cloud-8.x-2.0-beta1/modules/cloud_service_providers/aws_cloud/src/Service/Ec2/Ec2Service.php
modules/cloud_service_providers/aws_cloud/src/Service/Ec2/Ec2Service.php
<?php
namespace Drupal\aws_cloud\Service\Ec2;
use Aws\Api\DateTimeResult;
use Aws\Credentials\AssumeRoleCredentialProvider;
use Aws\Credentials\CredentialProvider;
use Aws\Ec2\Ec2Client;
use Aws\Ec2\Exception\Ec2Exception;
use Aws\Endpoint\EndpointProvider;
use Aws\MockHandler;
use Aws\Result;
use Aws\Sts\StsClient;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\cloud\Plugin\cloud\config\CloudConfigPluginManagerInterface;
use Drupal\cloud\Traits\EntityTrait;
use Drupal\aws_cloud\Entity\Ec2\SecurityGroup;
use Drupal\aws_cloud\Entity\Ec2\Instance;
/**
* Ec2Service interacts with the Amazon EC2 API.
*/
class Ec2Service implements Ec2ServiceInterface {
use StringTranslationTrait;
use EntityTrait;
/**
* The Messenger service.
*
* @var \Drupal\Core\Messenger\Messenger
*/
protected $messenger;
/**
* Drupal\Core\Entity\EntityTypeManagerInterface definition.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Cloud context string.
*
* @var string
*/
protected $cloudContext;
/**
* A logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The config factory.
*
* Subclasses should use the self::config() method, which may be overridden to
* address specific needs when loading config, rather than this property
* directly. See \Drupal\Core\Form\ConfigFormBase::config() for an example of
* this.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The cloud service provider plugin manager (CloudConfigPluginManager).
*
* @var \Drupal\cloud\Plugin\cloud\config\CloudConfigPluginManagerInterface
*/
protected $cloudConfigPluginManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Field type plugin manager.
*
* @var \Drupal\core\Field\FieldTypePluginManagerInterface
*/
protected $fieldTypePluginManager;
/**
* Entity field manager interface.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* TRUE to run the operation. FALSE to run the operation in validation mode.
*
* @var bool
*/
private $dryRun;
/**
* The lock interface.
*
* @var \Drupal\Core\Lock\LockBackendInterface
*/
protected $lock;
/**
* Constructs a new Ec2Service object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* An entity type manager instance.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* A logger instance.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* A configuration factory.
* @param \Drupal\Core\Messenger\Messenger $messenger
* The messenger service.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
* @param \Drupal\cloud\Plugin\cloud\config\CloudConfigPluginManagerInterface $cloud_config_plugin_manager
* The cloud service provider plugin manager (CloudConfigPluginManager).
* @param \Drupal\core\Field\FieldTypePluginManagerInterface $field_type_plugin_manager
* The field type plugin manager.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Lock\LockBackendInterface $lock
* The lock interface.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, LoggerChannelFactoryInterface $logger_factory, ConfigFactoryInterface $config_factory, Messenger $messenger, TranslationInterface $string_translation, AccountInterface $current_user, CloudConfigPluginManagerInterface $cloud_config_plugin_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, EntityFieldManagerInterface $entity_field_manager, LockBackendInterface $lock) {
// Setup the entity type manager for querying entities.
$this->entityTypeManager = $entity_type_manager;
// Setup the logger.
$this->logger = $logger_factory->get('aws_ec2_service');
// Setup the configuration factory.
$this->configFactory = $config_factory;
// Setup the dryRun flag.
$this->dryRun = (bool) $this->configFactory->get('aws_cloud.settings')->get('aws_cloud_test_mode');
// Setup the messenger.
$this->messenger = $messenger;
// Setup the $this->t()
$this->stringTranslation = $string_translation;
$this->currentUser = $current_user;
$this->cloudConfigPluginManager = $cloud_config_plugin_manager;
$this->fieldTypePluginManager = $field_type_plugin_manager;
$this->entityFieldManager = $entity_field_manager;
$this->lock = $lock;
}
/**
* {@inheritdoc}
*/
public function setCloudContext($cloud_context) {
$this->cloudContext = $cloud_context;
$this->cloudConfigPluginManager->setCloudContext($cloud_context);
}
/**
* Load and return an Ec2Client.
*/
protected function getEc2Client() {
$credentials = $this->cloudConfigPluginManager->loadCredentials();
try {
$ec2_params = [
'region' => $credentials['region'],
'version' => $credentials['version'],
];
$provider = FALSE;
// Load credentials if needed.
if ($credentials['use_instance_credentials'] == FALSE) {
$provider = CredentialProvider::ini('default', $credentials['ini_file']);
$provider = CredentialProvider::memoize($provider);
}
if ($credentials['assume_role'] == TRUE) {
$sts_params = [
'region' => $credentials['region'],
'version' => $credentials['version'],
];
if ($provider != FALSE) {
$sts_params['credentials'] = $provider;
}
$assumeRoleCredentials = new AssumeRoleCredentialProvider([
'client' => new StsClient($sts_params),
'assume_role_params' => [
'RoleArn' => $credentials['role_arn'],
'RoleSessionName' => 'ec2_client_assume_role',
],
]);
// Memoize takes care of re-authenticating when the tokens expire.
$assumeRoleCredentials = CredentialProvider::memoize($assumeRoleCredentials);
$ec2_params['credentials'] = $assumeRoleCredentials;
// If switch role is enabled, execute one more assume role.
if ($credentials['switch_role'] == TRUE) {
$switch_sts_params = [
'region' => $credentials['region'],
'version' => $credentials['version'],
'credentials' => $assumeRoleCredentials,
];
$switchRoleCredentials = new AssumeRoleCredentialProvider([
'client' => new StsClient($switch_sts_params),
'assume_role_params' => [
'RoleArn' => $credentials['switch_role_arn'],
'RoleSessionName' => 'ec2_client_switch_role',
],
]);
$switchRoleCredentials = CredentialProvider::memoize($switchRoleCredentials);
$ec2_params['credentials'] = $switchRoleCredentials;
}
}
elseif ($provider != FALSE) {
$ec2_params['credentials'] = $provider;
}
$ec2_client = new Ec2Client($ec2_params);
}
catch (\Exception $e) {
$ec2_client = NULL;
$this->logger->error($e->getMessage());
}
$this->addMockHandler($ec2_client);
return $ec2_client;
}
/**
* Add a mock handler of aws sdk for testing.
*
* The mock data of aws response is saved
* in configuration "aws_cloud_mock_data".
*
* @param \Aws\Ec2\Ec2Client $ec2_client
* The ec2 client.
*
* @see https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_handlers-and-middleware.html
*/
private function addMockHandler(Ec2Client $ec2_client) {
$mock_data = $this->configFactory->get('aws_cloud.settings')->get('aws_cloud_mock_data');
if ($this->dryRun && $mock_data) {
$func = function ($command, $request) {
$mock_data = \Drupal::service('config.factory')
->get('aws_cloud.settings')
->get('aws_cloud_mock_data');
$mock_data = json_decode($mock_data, TRUE);
// If the mock data of a command is defined.
$command_name = $command->getName();
if (isset($mock_data[$command_name])) {
$result_data = $mock_data[$command_name];
// Because launch time is special,
// we need to convert it from string to DateTimeResult.
if ($command_name === 'DescribeInstances') {
foreach ($result_data['Reservations'] as &$reservation) {
foreach ($reservation['Instances'] as &$instance) {
if (!empty($instance['LaunchTime'])) {
$instance['LaunchTime'] = new DateTimeResult($instance['LaunchTime']);
}
}
}
}
return new Result($result_data);
}
elseif ($command_name == 'DescribeAccountAttributes') {
// Return an empty array so testing doesn't error out.
$result_data = [
'AccountAttributes' => [
[
'AttributeName' => 'supported-platforms',
'AttributeValues' => [
[
'AttributeValue' => 'VPC',
],
],
],
],
];
return new Result($result_data);
}
else {
return new Result();
}
};
// Set mock handler.
$ec2_client->getHandlerList()->setHandler(new MockHandler([$func, $func]));
}
}
/**
* Execute the API of AWS Cloud EC2 Service.
*
* @param string $operation
* The operation to perform.
* @param array $params
* An array of parameters.
*
* @return array
* Array of execution result or NULL if there is an error.
*
* @throws \Drupal\aws_cloud\Service\Ec2\Ec2ServiceException
* If the $params is empty or EC2 Client is null.
*/
private function execute($operation, array $params = []) {
$results = NULL;
$ec2_client = $this->getEc2Client();
if (empty($ec2_client)) {
throw new Ec2ServiceException('No EC2 Client found. Cannot perform API operations');
}
try {
// Let other modules alter the parameters
// before they are sent through the API.
\Drupal::moduleHandler()->invokeAll('aws_cloud_pre_execute_alter', [
&$params,
$operation,
$this->cloudContext,
]);
$command = $ec2_client->getCommand($operation, $params);
$results = $ec2_client->execute($command);
// Let other modules alter the results before the module processes it.
\Drupal::moduleHandler()->invokeAll('aws_cloud_post_execute_alter', [
&$results,
$operation,
$this->cloudContext,
]);
}
catch (Ec2Exception $e) {
$this->messenger->addError($this->t('Error: The operation "@operation" could not be performed.', [
'@operation' => $operation,
]));
$this->messenger->addError($this->t('Error Info: @error_info', [
'@error_info' => $e->getAwsErrorCode(),
]));
$this->messenger->addError($this->t('Error from: @error_type-side', [
'@error_type' => $e->getAwsErrorType(),
]));
$this->messenger->addError($this->t('Status Code: @status_code', [
'@status_code' => $e->getStatusCode(),
]));
$this->messenger->addError($this->t('Message: @msg', ['@msg' => $e->getAwsErrorMessage()]));
}
catch (\Exception $e) {
$this->messenger->addError($e->getMessage());
}
return $results;
}
/**
* {@inheritdoc}
*/
public function associateAddress(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('AssociateAddress', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function allocateAddress(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('AllocateAddress', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function associateIamInstanceProfile(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('AssociateIamInstanceProfile', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeAccountAttributes(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeAccountAttributes', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function disassociateIamInstanceProfile(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DisassociateIamInstanceProfile', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function replaceIamInstanceProfileAssociation(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('ReplaceIamInstanceProfileAssociation', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeIamInstanceProfileAssociations(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeIamInstanceProfileAssociations', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function authorizeSecurityGroupIngress(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('AuthorizeSecurityGroupIngress', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function authorizeSecurityGroupEgress(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('AuthorizeSecurityGroupEgress', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createImage(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateImage', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function modifyImageAttribute(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('ModifyImageAttribute', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createKeyPair(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateKeyPair', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createNetworkInterface(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateNetworkInterface', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function modifyNetworkInterfaceAttribute(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('ModifyNetworkInterfaceAttribute', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createTags(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateTags', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteTags(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteTags', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createVolume(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateVolume', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function modifyVolume(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('ModifyVolume', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createSnapshot(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateSnapshot', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createVpc(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateVpc', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createFlowLogs(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateFlowLogs', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createVpcPeeringConnection(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateVpcPeeringConnection', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function acceptVpcPeeringConnection(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('AcceptVpcPeeringConnection', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeVpcPeeringConnections(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeVpcPeeringConnections', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeFlowLogs(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeFlowLogs', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createSecurityGroup(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateSecurityGroup', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deregisterImage(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeregisterImage', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeInstances(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeInstances', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeInstanceAttribute(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeInstanceAttribute', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeImages(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeImages', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeSecurityGroups(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeSecurityGroups', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeNetworkInterfaces(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeNetworkInterfaces', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeAddresses(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeAddresses', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeSnapshots(array $params = []) {
$params += $this->getDefaultParameters();
$cloud_config = $this->cloudConfigPluginManager->loadConfigEntity();
$account_id = $cloud_config->get('field_account_id')->value;
$assume_role = $cloud_config->get('field_assume_role')->value;
$switch_role = $cloud_config->get('field_switch_role')->value;
if (isset($assume_role) && isset($switch_role) && $switch_role == TRUE && $assume_role == TRUE) {
$account_id = trim($cloud_config->get('field_switch_role_account_id')->value);
}
$params['RestorableByUserIds'] = [$account_id];
$results = $this->execute('DescribeSnapshots', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeKeyPairs(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeKeyPairs', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeVolumes(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeVolumes', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeAvailabilityZones(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeAvailabilityZones', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeVpcs(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeVpcs', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function associateVpcCidrBlock(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('AssociateVpcCidrBlock', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function disassociateVpcCidrBlock(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DisassociateVpcCidrBlock', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeSubnets(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeSubnets', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createSubnet(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateSubnet', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function getRegions() {
$regions = EndpointProvider::defaultProvider()
->getPartition($region = '', 'ec2')['regions'];
foreach ($regions as $region => $region_name) {
$item[$region] = $region_name['description'];
}
return $item;
}
/**
* {@inheritdoc}
*/
public function getEndpointUrls() {
// The $endpoints will be an array like ['us-east-1' => [], 'us-east-2' =>
// [], ...].
$endpoints = EndpointProvider::defaultProvider()
->getPartition('', 'ec2')['services']['ec2']['endpoints'];
$urls = [];
foreach ($endpoints as $endpoint => $item) {
$url = "https://ec2.$endpoint.amazonaws.com";
$urls[$endpoint] = $url;
}
return $urls;
}
/**
* {@inheritdoc}
*/
public function importKeyPair(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('ImportKeyPair', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function terminateInstance(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('TerminateInstances', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteSecurityGroup(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteSecurityGroup', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteNetworkInterface(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteNetworkInterface', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function disassociateAddress(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DisassociateAddress', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function releaseAddress(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('ReleaseAddress', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteKeyPair(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteKeyPair', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteVolume(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteVolume', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function attachVolume(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('AttachVolume', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function detachVolume(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DetachVolume', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteSnapshot(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteSnapshot', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteVpc(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteVpc', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteFlowLogs(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteFlowLogs', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteSubnet(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteSubnet', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function revokeSecurityGroupIngress(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('RevokeSecurityGroupIngress', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function revokeSecurityGroupEgress(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('RevokeSecurityGroupEgress', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function runInstances(array $params = [], array $tags = []) {
$params += $this->getDefaultParameters();
// Add meta tags to identify where the instance was launched from.
$params['TagSpecifications'] = [
[
'ResourceType' => 'instance',
'Tags' => [
[
'Key' => Instance::TAG_LAUNCH_ORIGIN,
'Value' => \Drupal::request()->getHost(),
],
[
'Key' => Instance::TAG_LAUNCH_SOFTWARE,
'Value' => 'Drupal 8 Cloud Orchestrator',
],
[
'Key' => Instance::TAG_LAUNCHED_BY,
'Value' => $this->currentUser->getAccountName(),
],
[
'Key' => Instance::TAG_LAUNCHED_BY_UID,
'Value' => $this->currentUser->id(),
],
],
],
];
// If there are tags, add them to the Tags array.
foreach ($tags as $tag) {
$params['TagSpecifications'][0]['Tags'][] = $tag;
}
$results = $this->execute('RunInstances', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function stopInstances(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('StopInstances', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function startInstances(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('StartInstances', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function modifyInstanceAttribute(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('ModifyInstanceAttribute', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function rebootInstances(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('RebootInstances', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createLaunchTemplate(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateLaunchTemplate', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function modifyLaunchTemplate(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('ModifyLaunchTemplate', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteLaunchTemplate(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteLaunchTemplate', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeLaunchTemplates(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeLaunchTemplates', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function createLaunchTemplateVersion(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('CreateLaunchTemplateVersion', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function deleteLaunchTemplateVersions(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DeleteLaunchTemplateVersions', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function describeLaunchTemplateVersions(array $params = []) {
$params += $this->getDefaultParameters();
$results = $this->execute('DescribeLaunchTemplateVersions', $params);
return $results;
}
/**
* {@inheritdoc}
*/
public function updateInstances(array $params = [], $clear = TRUE) {
$updated = FALSE;
$entity_type = 'aws_cloud_instance';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
// Call the API and get all instances.
$result = $this->describeInstances($params);
if (isset($result)) {
$all_instances = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the instances by setting up
// the array with the instance_id.
foreach ($all_instances as $instance) {
$stale[$instance->getInstanceId()] = $instance;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Instance Update');
// Loop through the reservations and store each one as an Instance entity.
foreach ($result['Reservations'] ?: [] as $reservation) {
foreach ($reservation['Instances'] ?: [] as $instance) {
// Keep track of instances that do not exist anymore
// delete them after saving the rest of the instances.
if (isset($stale[$instance['InstanceId']])) {
unset($stale[$instance['InstanceId']]);
}
// Store the Reservation OwnerId in instance so batch
// callback has access.
$instance['reservation_ownerid'] = $reservation['OwnerId'];
$instance['reservation_id'] = $reservation['ReservationId'];
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateInstance',
], [$this->cloudContext, $instance]);
}
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, $clear]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateInstancesWithoutBatch(array $params = [], $clear = FALSE) {
$updated = FALSE;
$entity_type = 'aws_cloud_instance';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
// Load all entities by cloud_context.
$instance_entities = $this->entityTypeManager
->getStorage($entity_type)
->loadByProperties(
['cloud_context' => $this->cloudContext]
);
$result = $this->describeInstances($params);
if (isset($result)) {
$stale = [];
// Make it easier to lookup the images by setting up
// the array with the image_id.
foreach ($instance_entities as $instance) {
$stale[$instance->getInstanceId()] = $instance;
}
foreach ($result['Reservations'] ?: [] as $reservation) {
foreach ($reservation['Instances'] ?: [] as $instance) {
// Keep track of images that do not exist anymore
// delete them after saving the rest of the images.
if (isset($stale[$instance['InstanceId']])) {
unset($stale[$instance['InstanceId']]);
}
// Store the Reservation OwnerId in instance so batch
// callback has access.
$instance['reservation_ownerid'] = $reservation['OwnerId'];
$instance['reservation_id'] = $reservation['ReservationId'];
Ec2BatchOperations::updateInstance($this->cloudContext, $instance);
}
}
if (count($stale) && $clear == TRUE) {
$this->entityTypeManager->getStorage($entity_type)->delete($stale);
}
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateImages(array $params = [], $clear = FALSE) {
$updated = FALSE;
$entity_type = 'aws_cloud_image';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
// Load all entities by cloud_context.
$image_entities = $this->entityTypeManager->getStorage($entity_type)->loadByProperties(
['cloud_context' => $this->cloudContext]
);
$result = $this->describeImages($params);
if (isset($result)) {
$stale = [];
// Make it easier to lookup the images by setting up
// the array with the image_id.
foreach ($image_entities as $image) {
$stale[$image->getImageId()] = $image;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Image Update');
foreach ($result['Images'] ?: [] as $image) {
// Keep track of images that do not exist anymore
// delete them after saving the rest of the images.
if (isset($stale[$image['ImageId']])) {
unset($stale[$image['ImageId']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateImage',
], [$this->cloudContext, $image]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, $clear]);
$this->runBatch($batch_builder);
$updated = count($result['Images']);
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateImagesWithoutBatch(array $params = [], $clear = FALSE) {
$updated = FALSE;
$entity_type = 'aws_cloud_image';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
// Load all entities by cloud_context.
$image_entities = $this->entityTypeManager
->getStorage($entity_type)
->loadByProperties(
['cloud_context' => $this->cloudContext]
);
$result = $this->describeImages($params);
if (isset($result)) {
$stale = [];
// Make it easier to lookup the images by setting up
// the array with the image_id.
foreach ($image_entities as $image) {
$stale[$image->getImageId()] = $image;
}
foreach ($result['Images'] ?: [] as $image) {
// Keep track of images that do not exist anymore
// delete them after saving the rest of the images.
if (isset($stale[$image['ImageId']])) {
unset($stale[$image['ImageId']]);
}
Ec2BatchOperations::updateImage($this->cloudContext, $image);
}
if (count($stale) && $clear == TRUE) {
$this->entityTypeManager->getStorage($entity_type)->delete($stale);
}
$updated = count($result['Images'] ?: []);
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateSecurityGroups(array $params = [], $clear = TRUE) {
$updated = FALSE;
$entity_type = 'aws_cloud_security_group';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
$result = $this->describeSecurityGroups($params);
if (isset($result)) {
$all_groups = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the groups by setting up
// the array with the group_id.
foreach ($all_groups as $group) {
$stale[$group->getGroupId()] = $group;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Security Group Update');
foreach ($result['SecurityGroups'] ?: [] as $security_group) {
// Keep track of instances that do not exist anymore
// delete them after saving the rest of the instances.
if (isset($stale[$security_group['GroupId']])) {
unset($stale[$security_group['GroupId']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateSecurityGroup',
], [$this->cloudContext, $security_group]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, $clear]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateCloudServerTemplates(array $params = [], $clear = TRUE) {
$updated = FALSE;
$entity_type = 'cloud_server_template';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
// Load all entities by cloud_context.
$entities = $this->entityTypeManager
->getStorage($entity_type)
->loadByProperties(
[
'type' => 'aws_cloud',
'cloud_context' => $this->cloudContext,
]
);
$result = $this->describeLaunchTemplates($params);
if (isset($result)) {
$stale = [];
foreach ($entities as $entity) {
$stale[$entity->getName()] = $entity;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Cloud Server Template Update');
foreach ($result['LaunchTemplates'] ?: [] as $template) {
if (isset($stale[$template['LaunchTemplateName']])) {
unset($stale[$template['LaunchTemplateName']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateCloudServerTemplate',
], [$this->cloudContext, $template]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, $clear]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function setupIpPermissions(SecurityGroup &$security_group, $field, array $ec2_permissions) {
// Permissions are always overwritten with the latest from
// EC2. The reason is that there is no way to guarantee a 1
// to 1 mapping from the $security_group['IpPermissions'] array.
// There is no IP permission ID coming back from EC2.
// Clear out all items before re-adding them.
$i = $security_group->$field->count() - 1;
while ($i >= 0) {
if ($security_group->$field->get($i)) {
$security_group->$field->removeItem($i);
}
$i--;
}
// Setup all permission objects.
$count = 0;
foreach ($ec2_permissions as $permissions) {
$permission_objects = $this->setupIpPermissionObject($permissions);
// Loop through the permission objects and add them to the
// security group ip_permission field.
foreach ($permission_objects as $permission) {
$security_group->$field->set($count, $permission);
$count++;
}
}
}
/**
* {@inheritdoc}
*/
public function setupIpPermissionObject(array $ec2_permission) {
$ip_permissions = [];
// Get the field definition for an IpPermission object.
$definition = $this->entityFieldManager->getBaseFieldDefinitions('aws_cloud_security_group');
// Setup the more global attributes.
$from_port = isset($ec2_permission['FromPort']) ? "{$ec2_permission['FromPort']}" : NULL;
$to_port = isset($ec2_permission['ToPort']) ? "{$ec2_permission['ToPort']}" : NULL;
$ip_protocol = $ec2_permission['IpProtocol'];
// To keep things consistent, if ip_protocol is -1,
// set from_port and to_port as 0-65535.
if ($ip_protocol == -1) {
$from_port = "0";
$to_port = "65535";
}
if (isset($ec2_permission['IpRanges']) && count($ec2_permission['IpRanges'])) {
// Create a IPv4 permission object.
foreach ($ec2_permission['IpRanges'] ?: [] as $ip_range) {
$ip_range_permission = $this->fieldTypePluginManager->createInstance('ip_permission', [
'field_definition' => $definition['ip_permission'],
'parent' => NULL,
'name' => NULL,
]);
// Source is an internal identifier. Doesn't come from EC2.
$ip_range_permission->source = 'ip4';
$ip_range_permission->cidr_ip = $ip_range['CidrIp'];
$ip_range_permission->from_port = $from_port;
$ip_range_permission->to_port = $to_port;
$ip_range_permission->ip_protocol = $ip_protocol;
$ip_permissions[] = $ip_range_permission;
}
}
if (isset($ec2_permission['Ipv6Ranges']) && count($ec2_permission['Ipv6Ranges'])) {
// Create IPv6 permissions object.
foreach ($ec2_permission['Ipv6Ranges'] ?: [] as $ip_range) {
$ip_v6_permission = $this->fieldTypePluginManager->createInstance('ip_permission', [
'field_definition' => $definition['ip_permission'],
'parent' => NULL,
'name' => NULL,
]);
// Source is an internal identifier. Doesn't come from EC2.
$ip_v6_permission->source = 'ip6';
$ip_v6_permission->cidr_ip_v6 = $ip_range['CidrIpv6'];
$ip_v6_permission->from_port = $from_port;
$ip_v6_permission->to_port = $to_port;
$ip_v6_permission->ip_protocol = $ip_protocol;
$ip_permissions[] = $ip_v6_permission;
}
}
if (isset($ec2_permission['UserIdGroupPairs']) && count($ec2_permission['UserIdGroupPairs'])) {
// Create Group permissions object.
foreach ($ec2_permission['UserIdGroupPairs'] ?: [] as $group) {
$group_permission = $this->fieldTypePluginManager->createInstance('ip_permission', [
'field_definition' => $definition['ip_permission'],
'parent' => NULL,
'name' => NULL,
]);
// Source is an internal identifier. Doesn't come from EC2.
$group_permission->source = 'group';
$group_permission->group_id = isset($group['GroupId']) ? $group['GroupId'] : NULL;
$group_permission->group_name = isset($group['GroupName']) ? $group['GroupName'] : NULL;
$group_permission->user_id = isset($group['UserId']) ? $group['UserId'] : NULL;
$group_permission->peering_status = isset($group['PeeringStatus']) ? $group['PeeringStatus'] : NULL;
$group_permission->vpc_id = isset($group['VpcId']) ? $group['VpcId'] : NULL;
$group_permission->peering_connection_id = isset($group['VpcPeeringConnectionId']) ? $group['VpcPeeringConnectionId'] : NULL;
$group_permission->from_port = $from_port;
$group_permission->to_port = $to_port;
$group_permission->ip_protocol = $ip_protocol;
$ip_permissions[] = $group_permission;
}
}
return $ip_permissions;
}
/**
* {@inheritdoc}
*/
public function updateNetworkInterfaces(array $params = [], $clear = TRUE) {
$updated = FALSE;
$entity_type = 'aws_cloud_network_interface';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
$result = $this->describeNetworkInterfaces($params);
if (isset($result)) {
$all_interfaces = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the groups by setting up
// the array with the group_id.
foreach ($all_interfaces as $interface) {
$stale[$interface->getNetworkInterfaceId()] = $interface;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Network Interface Update');
foreach ($result['NetworkInterfaces'] ?: [] as $network_interface) {
// Keep track of network interfaces that do not exist anymore
// delete them after saving the rest of the network interfaces.
if (isset($stale[$network_interface['NetworkInterfaceId']])) {
unset($stale[$network_interface['NetworkInterfaceId']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateNetworkInterface',
], [$this->cloudContext, $network_interface]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, $clear]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateElasticIp() {
$updated = FALSE;
$entity_type = 'aws_cloud_elastic_ip';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
$result = $this->describeAddresses();
if (isset($result)) {
$all_ips = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the groups by setting up
// the array with the group_id.
foreach ($all_ips as $ip) {
$stale[$ip->getPublicIp()] = $ip;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('ElasticIp Update');
foreach ($result['Addresses'] ?: [] as $elastic_ip) {
// Keep track of IPs that do not exist anymore
// delete them after saving the rest of the IPs.
if (isset($stale[$elastic_ip['PublicIp']])) {
unset($stale[$elastic_ip['PublicIp']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateElasticIp',
], [$this->cloudContext, $elastic_ip]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, TRUE]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateKeyPairs() {
$updated = FALSE;
$entity_type = 'aws_cloud_key_pair';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
$result = $this->describeKeyPairs();
if (isset($result)) {
$all_keys = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the groups by setting up
// the array with the group_id.
foreach ($all_keys as $key) {
$stale[$key->getKeyPairName()] = $key;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Keypair Update');
foreach ($result['KeyPairs'] ?: [] as $key_pair) {
// Keep track of key pair that do not exist anymore
// delete them after saving the rest of the key pair.
if (isset($stale[$key_pair['KeyName']])) {
unset($stale[$key_pair['KeyName']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateKeyPair',
], [$this->cloudContext, $key_pair]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, TRUE]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateVolumes() {
$updated = FALSE;
$entity_type = 'aws_cloud_volume';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
$result = $this->describeVolumes();
if (isset($result)) {
$all_volumes = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the groups by setting up
// the array with the group_id.
foreach ($all_volumes as $volume) {
$stale[$volume->getVolumeId()] = $volume;
}
$snapshot_id_name_map = $this->getSnapshotIdNameMap($result['Volumes'] ?: []);
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Volume Update');
foreach ($result['Volumes'] ?: [] as $volume) {
// Keep track of network interfaces that do not exist anymore
// delete them after saving the rest of the network interfaces.
if (isset($stale[$volume['VolumeId']])) {
unset($stale[$volume['VolumeId']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateVolume',
], [$this->cloudContext, $volume, $snapshot_id_name_map]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, TRUE]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateSnapshots() {
$updated = FALSE;
$entity_type = 'aws_cloud_snapshot';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
$result = $this->describeSnapshots();
if (isset($result)) {
$all_snapshots = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the snapshot by setting up
// the array with the snapshot_id.
foreach ($all_snapshots as $snapshot) {
$stale[$snapshot->getSnapshotId()] = $snapshot;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Snapshot Update');
foreach ($result['Snapshots'] ?: [] as $snapshot) {
// Keep track of snapshot that do not exist anymore
// delete them after saving the rest of the snapshots.
if (isset($stale[$snapshot['SnapshotId']])) {
unset($stale[$snapshot['SnapshotId']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateSnapshot',
], [$this->cloudContext, $snapshot]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, TRUE]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateVpcs(array $params = [], $clear = TRUE) {
$updated = FALSE;
$entity_type = 'aws_cloud_vpc';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
$result = $this->describeVpcs($params);
if (isset($result)) {
$all_vpcs = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the snapshot by setting up
// the array with the vpc_id.
foreach ($all_vpcs as $vpc) {
$stale[$vpc->getVpcId()] = $vpc;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('VPC Update');
foreach ($result['Vpcs'] ?: [] as $vpc) {
// Keep track of snapshot that do not exist anymore
// delete them after saving the rest of the snapshots.
if (isset($stale[$vpc['VpcId']])) {
unset($stale[$vpc['VpcId']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateVpc',
], [$this->cloudContext, $vpc]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, $clear]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function updateSubnets(array $params = [], $clear = TRUE) {
$updated = FALSE;
$entity_type = 'aws_cloud_subnet';
$lock_name = $this->getLockKey($entity_type);
if (!$this->lock->acquire($lock_name)) {
return FALSE;
}
$result = $this->describeSubnets($params);
if (isset($result)) {
$all_subnets = $this->loadAllEntities($entity_type);
$stale = [];
// Make it easier to lookup the subnet by setting up
// the array with the subnet_id.
foreach ($all_subnets as $subnet) {
$stale[$subnet->getSubnetId()] = $subnet;
}
/* @var \Drupal\Core\Batch\BatchBuilder $batch_builder */
$batch_builder = $this->initBatch('Subnet Update');
foreach ($result['Subnets'] ?: [] as $subnet) {
// Keep track of subnet that do not exist anymore
// delete them after saving the rest of the snapshots.
if (isset($stale[$subnet['SubnetId']])) {
unset($stale[$subnet['SubnetId']]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'updateSubnet',
], [$this->cloudContext, $subnet]);
}
$batch_builder->addOperation([
'\Drupal\aws_cloud\Service\Ec2\Ec2BatchOperations',
'finished',
], [$entity_type, $stale, $clear]);
$this->runBatch($batch_builder);
$updated = TRUE;
}
$this->lock->release($lock_name);
return $updated;
}
/**
* {@inheritdoc}
*/
public function getVpcs() {
$vpcs = [];
$result = $this->describeVpcs();
if (isset($result)) {
foreach (array_column($result['Vpcs'] ?: [], 'VpcId') as $key => $vpc) {
$vpcs[$vpc] = $result['Vpcs'][$key]['CidrBlock'] . " ($vpc)";
}
}
return $vpcs;
}
/**
* {@inheritdoc}
*/
public function getAvailabilityZones() {
$zones = [];
$result = $this->describeAvailabilityZones();
if (isset($result)) {
foreach (array_column($result['AvailabilityZones'] ?: [], 'ZoneName') as $availability_zone) {
$zones[$availability_zone] = $availability_zone;
}
}
return $zones;
}
/**
* {@inheritdoc}
*/
public function getSupportedPlatforms() {
$platforms = [];
$result = $this->describeAccountAttributes([
'AttributeNames' => [
'supported-platforms',
],
]);
if (isset($result)) {
foreach ($result['AccountAttributes'] ?: [] as $attribute) {
if ($attribute['AttributeName'] === 'supported-platforms') {
foreach ($attribute['AttributeValues'] ?: [] as $value) {
$platforms[] = $value['AttributeValue'];
}
}
}
}
return $platforms;
}
/**
* {@inheritdoc}
*/
public function clearAllEntities() {
$timestamp = $this->getTimestamp();
$this->clearEntities('aws_cloud_instance', $timestamp);
$this->clearEntities('aws_cloud_security_group', $timestamp);
$this->clearEntities('aws_cloud_image', $timestamp);
$this->clearEntities('aws_cloud_network_interface', $timestamp);
$this->clearEntities('aws_cloud_elastic_ip', $timestamp);
$this->clearEntities('aws_cloud_key_pair', $timestamp);
$this->clearEntities('aws_cloud_volume', $timestamp);
$this->clearEntities('aws_cloud_snapshot', $timestamp);
}
/**
* Helper method to get the current timestamp.
*
* @return int
* The current timestamp.
*/
private function getTimestamp() {
return time();
}
/**
* Setup the default parameters that all API calls will need.
*
* @return array
* Array of default parameters.
*/
protected function getDefaultParameters() {
return [
'DryRun' => $this->dryRun,
];
}
/**
* {@inheritdoc}
*/
public function getSnapshotIdNameMap(array $volumes = []) {
$snapshot_ids = array_filter(array_column($volumes, 'SnapshotId'));
if (empty($snapshot_ids)) {
return [];
}
$map = [];
foreach ($snapshot_ids as $snapshot_id) {
$map[$snapshot_id] = '';
}
$result = $this->describeSnapshots();
if (isset($result)) {
foreach ($result['Snapshots'] ?: [] as $snapshot) {
$snapshot_id = $snapshot['SnapshotId'];
if (!array_key_exists($snapshot_id, $map)) {
continue;
}
$map[$snapshot_id] = $this->getTagName($snapshot, '');
}
}
return $map;
}
/**
* {@inheritdoc}
*/
public function getTagName(array $aws_obj, $default_value) {
$name = $default_value;
if (!isset($aws_obj['Tags'])) {
return $name;
}
foreach ($aws_obj['Tags'] ?: [] as $tag) {
if ($tag['Key'] == 'Name' && !empty($tag['Value'])) {
$name = $tag['Value'];
break;
}
}
return $name;
}
/**
* {@inheritdoc}
*/
public function getPrivateIps(array $network_interfaces) {
$ip_string = FALSE;
$private_ips = [];
foreach ($network_interfaces as $interface) {
$private_ips[] = $interface['PrivateIpAddress'];
}
if (count($private_ips)) {
$ip_string = implode(', ', $private_ips);
}
return $ip_string;
}
/**
* {@inheritdoc}
*/
public function calculateInstanceCost(array $instance, array $instance_types) {
$cost = NULL;
if ($instance['State']['Name'] == 'stopped') {
return $cost;
}
$instance_type = $instance['InstanceType'];
if (isset($instance_types[$instance_type])) {
$parts = explode(':', $instance_types[$instance_type]);
$hourly_rate = $parts[4];
$launch_time = strtotime($instance['LaunchTime']->__toString());
$cost = round((time() - $launch_time) / 3600 * $hourly_rate, 2);
}
return $cost;
}
/**
* {@inheritdoc}
*/
public function getUidTagValue(array $tags_array, $key) {
$uid = 0;
if (isset($tags_array['Tags'])) {
foreach ($tags_array['Tags'] ?: [] as $tag) {
if ($tag['Key'] == $key) {
$uid = $tag['Value'];
break;
}
}
}
return $uid;
}
/**
* {@inheritdoc}
*/
public function getInstanceUid($instance_id) {
$uid = 0;
$instance = $this->entityTypeManager
->getStorage('aws_cloud_instance')
->loadByProperties([
'instance_id' => $instance_id,
]);
if (count($instance) > 0) {
$instance = array_shift($instance);
$uid = $instance->getOwnerId();
}
return $uid;
}
/**
* Helper method to load an entity using parameters.
*
* @param string $entity_type
* Entity Type.
* @param string $id_field
* Entity ID field.
* @param string $id_value
* Entity ID value.
* @param array $extra_conditions
* Extra conditions.
*
* @return int
* Entity ID.
*/
public function getEntityId($entity_type, $id_field, $id_value, array $extra_conditions = []) {
$query = $this->entityTypeManager
->getStorage($entity_type)
->getQuery()
->condition($id_field, $id_value)
->condition('cloud_context', $this->cloudContext);
foreach ($extra_conditions as $key => $value) {
$query->condition($key, $value);
}
$entities = $query->execute();
return array_shift($entities);
}
/**
* Initialize a new batch builder.
*
* @param string $batch_name
* The batch name.
*
* @return \Drupal\Core\Batch\BatchBuilder
* The initialized batch object.
*/
protected function initBatch($batch_name) {
return (new BatchBuilder())
->setTitle($batch_name);
}
/**
* Run the batch job to process entities.
*
* @param \Drupal\Core\Batch\BatchBuilder $batch_builder
* The batch builder object.
*/
protected function runBatch(BatchBuilder $batch_builder) {
// Log the start time.
$start = $this->getTimestamp();
$batch_array = $batch_builder->toArray();
batch_set($batch_array);
// Reset the progressive so batch works with out a web head.
$batch = &batch_get();
$batch['progressive'] = FALSE;
batch_process();
// Log the end time.
$end = $this->getTimestamp();
$this->logger->info(
$this->t('@updater - @cloud_context: Batch operation took @time seconds.',
[
'@cloud_context' => $this->cloudContext,
'@updater' => $batch_array['title'],
'@time' => $end - $start,
]));
}
/**
* Generate a lock key based on entity name.
*
* @param string $name
* The entity name.
*
* @return string
* The lock key.
*/
protected function getLockKey($name) {
return $this->cloudContext . '_' . $name;
}
}
