Skip to content
Snippets Groups Projects
Commit 22e0103a authored by xiaohua guan's avatar xiaohua guan Committed by Yas Naoi
Browse files

Issue #3063599 by Xiaohua Guan, yas: Stop multiple Instances at once

parent 77b39c7a
No related branches found
No related tags found
No related merge requests found
Showing
with 350 additions and 105 deletions
......@@ -975,6 +975,20 @@ function aws_cloud_update_8155() {
cloud_update_yml_definitions($files, 'aws_cloud');
}
/**
* Add multiple instance stop action.
*
* Update view aws_cloud_instance_all,
* add aws_cloud_instance_delete_action.
*/
function aws_cloud_update_8156() {
$files = [
'views.view.aws_cloud_instance_all.yml',
'system.action.aws_cloud_instance_stop_action.yml',
];
cloud_update_yml_definitions($files, 'aws_cloud');
}
/**
* Helper function to add fields to the entity type.
*
......
......@@ -219,6 +219,19 @@ entity.aws_cloud_instance.associate_elastic_ip_form:
requirements:
_entity_access: 'aws_cloud_instance.associate_elastic_ip'
entity.aws_cloud_instance.stop_multiple_form:
path: '/clouds/aws_cloud/{cloud_context}/instance/stop_multiple'
defaults:
entity_type_id: 'aws_cloud_instance'
_form: 'Drupal\aws_cloud\Form\Ec2\InstanceStopMultipleForm'
_title: 'Stop AWS Cloud Instances(s)'
requirements:
# Use custom access that will check for cloud_context and the desired permission.
# Desired permission is passed as an option in the "perm" variable
_custom_access: '\Drupal\cloud\Controller\CloudConfigController::access'
options:
perm: 'edit any aws cloud instance+edit own aws cloud instance'
# AWS Cloud Images Routes
entity.aws_cloud_image.canonical:
......
langcode: en
status: true
dependencies:
module:
- aws_cloud
id: aws_cloud_instance_stop_action
label: 'Stop instance(s)'
type: aws_cloud_instance
plugin: aws_cloud_instance_stop_action
configuration: { }
......@@ -79,15 +79,22 @@ display:
summary: ''
description: ''
columns:
instance_bulk_form: instance_bulk_form
name: name
cloud_context: cloud_context
public_ip: public_ip
instance_state: instance_state
instance_type: instance_type
availability_zone: availability_zone
cost: cost
created: created
operations: operations
info:
instance_bulk_form:
align: ''
separator: ''
empty_column: false
responsive: ''
name:
sortable: true
default_sort_order: asc
......@@ -137,6 +144,13 @@ display:
separator: ''
empty_column: false
responsive: ''
cost:
sortable: true
default_sort_order: asc
align: views-align-right
separator: ''
empty_column: false
responsive: ''
operations:
align: ''
separator: ''
......@@ -147,6 +161,22 @@ display:
row:
type: fields
fields:
instance_bulk_form:
id: instance_bulk_form
table: aws_cloud_instance
field: instance_bulk_form
label: ''
exclude: false
alter:
alter_text: false
element_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
plugin_id: instance_bulk_form
entity_type: aws_cloud_instance
cloud_context:
id: cloud_context
table: aws_cloud_instance
......
......@@ -5,3 +5,7 @@ action.configuration.aws_cloud_elastic_ip_delete_action:
action.configuration.aws_cloud_elastic_ip_disassociate_action:
type: action_configuration_default
label: 'Disassociate the selected Elastic IP(s) configuration'
action.configuration.aws_cloud_instance_stop_action:
type: action_configuration_default
label: 'Stop the selected Instance(s) configuration'
<?php
namespace Drupal\aws_cloud\Form\Ec2;
use Drupal\cloud\Entity\CloudContentEntityBase;
/**
* Provides an entities stop confirmation form.
*/
class InstanceStopMultipleForm extends AwsCloudProcessMultipleForm {
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->formatPlural(count($this->selection),
'Are you sure you want to stop this @item?',
'Are you sure you want to stop these @items?', [
'@item' => $this->entityType->getSingularLabel(),
'@items' => $this->entityType->getPluralLabel(),
]
);
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Stop');
}
/**
* {@inheritdoc}
*/
protected function processAwsResource(CloudContentEntityBase $entity) {
$this->awsEc2Service->setCloudContext($entity->getCloudContext());
return $this->awsEc2Service->stopInstances([
'InstanceIds' => [$entity->getInstanceId()],
]);
}
/**
* {@inheritdoc}
*/
protected function processEntity(CloudContentEntityBase $entity) {}
/**
* Process an entity and related AWS resource.
*
* @param \Drupal\cloud\Entity\CloudContentEntityBase $entity
* An entity object.
*
* @return bool
* Succeeded or failed.
*/
protected function process(CloudContentEntityBase $entity) {
if ($this->processAwsResource($entity)) {
$message = $this->t('The @type "@label" has been stopped.', [
'@type' => $entity->getEntityType()->getLabel(),
'@label' => $entity->label(),
]);
$this->processEntity($entity);
$this->messenger->addMessage($message);
return TRUE;
}
else {
$message = $this->t('The @type "@label" couldn\'t be stopped.', [
'@type' => $entity->getEntityType()->getLabel(),
'@label' => $entity->label(),
]);
$this->messenger->addError($message);
return FALSE;
}
}
/**
* Returns the message to show the user after an item was processed.
*
* @param int $count
* Count of processed translations.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
* The item processed message.
*/
protected function getProcessedMessage($count) {
$this->awsEc2Service->updateInstances();
return $this->formatPlural($count, 'Stopped @count item.', 'Stopped @count items.');
}
}
......@@ -2,12 +2,6 @@
namespace Drupal\aws_cloud\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Disassociate selected Elastic IP(s).
*
......@@ -19,97 +13,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* = "entity.aws_cloud_elastic_ip.disassociate_multiple_form"
* )
*/
class DisassociateElasticIp extends ActionBase implements ContainerFactoryPluginInterface {
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The tempstore.
*
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
/**
* The temp store key name.
*
* @var string
*/
protected $tempStoreKey;
/**
* Constructs a CancelUser object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param \Drupal\Core\Session\AccountInterface $current_user
* Current user.
*/
public function __construct(array $configuration,
$plugin_id,
$plugin_definition,
PrivateTempStoreFactory $temp_store_factory,
AccountInterface $current_user) {
$this->currentUser = $current_user;
$this->tempStoreKey = $this->currentUser->id() . ':' . $plugin_definition['type'];
$this->tempStore = $temp_store_factory->get($this->tempStoreKey);
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
class DisassociateElasticIp extends OperateAction {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('tempstore.private'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function executeMultiple(array $entities) {
/** @var \Drupal\Core\Entity\EntityInterface[] $entities */
$selection = [];
foreach ($entities as $entity) {
$langcode = $entity->language()->getId();
$selection[$entity->id()][$langcode] = $langcode;
}
$this->tempStore->set($this->tempStoreKey, $selection);
}
/**
* {@inheritdoc}
*/
public function execute($object = NULL) {
$this->executeMultiple([$object]);
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\user\UserInterface $object */
return $object->access('disassociate', $account, $return_as_object);
protected function getOperation() {
return 'disassociate';
}
}
<?php
namespace Drupal\aws_cloud\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Operate selected Entity(s).
*/
abstract class OperateAction extends ActionBase implements ContainerFactoryPluginInterface {
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The tempstore.
*
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
/**
* The temp store key name.
*
* @var string
*/
protected $tempStoreKey;
/**
* Constructs a CancelUser object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param \Drupal\Core\Session\AccountInterface $current_user
* Current user.
*/
public function __construct(array $configuration,
$plugin_id,
$plugin_definition,
PrivateTempStoreFactory $temp_store_factory,
AccountInterface $current_user) {
$this->currentUser = $current_user;
$this->tempStoreKey = $this->currentUser->id() . ':' . $plugin_definition['type'];
$this->tempStore = $temp_store_factory->get($this->tempStoreKey);
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('tempstore.private'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function executeMultiple(array $entities) {
/** @var \Drupal\Core\Entity\EntityInterface[] $entities */
$selection = [];
foreach ($entities as $entity) {
$langcode = $entity->language()->getId();
$selection[$entity->id()][$langcode] = $langcode;
}
$this->tempStore->set($this->tempStoreKey, $selection);
}
/**
* {@inheritdoc}
*/
public function execute($object = NULL) {
$this->executeMultiple([$object]);
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\user\UserInterface $object */
return $object->access($this->getOperation(), $account, $return_as_object);
}
/**
* Get operation.
*
* @return string
* The operation.
*/
abstract protected function getOperation();
}
<?php
namespace Drupal\aws_cloud\Plugin\Action;
/**
* Disassociate selected Instance(s).
*
* @Action(
* id = "aws_cloud_instance_stop_action",
* label = @Translation("Stop Instance"),
* type = "aws_cloud_instance",
* confirm_form_route_name
* = "entity.aws_cloud_instance.stop_multiple_form"
* )
*/
class StopInstance extends OperateAction {
/**
* {@inheritdoc}
*/
protected function getOperation() {
return 'stop';
}
}
......@@ -25,6 +25,7 @@ class InstanceTest extends AwsCloudTestCase {
'edit own aws cloud instance',
'delete own aws cloud instance',
'view own aws cloud instance',
'edit any aws cloud instance',
'list cloud server template',
'launch server template',
......@@ -903,7 +904,7 @@ class InstanceTest extends AwsCloudTestCase {
/**
* Tests deleting instances with bulk operation.
*/
public function testInstanceBulk() {
public function testDeleteInstanceBulk() {
$cloud_context = $this->cloudContext;
for ($i = 0; $i < self::AWS_CLOUD_INSTANCE_REPEAT_COUNT; $i++) {
......@@ -918,6 +919,25 @@ class InstanceTest extends AwsCloudTestCase {
}
}
/**
* Tests stopping instances with bulk operation.
*/
public function testStopInstanceBulk() {
$cloud_context = $this->cloudContext;
for ($i = 0; $i < self::AWS_CLOUD_INSTANCE_REPEAT_COUNT; $i++) {
// Create instances.
$instances = $this->createRandomInstancesData();
$instance_count = count($instances);
$num = 1;
foreach ($instances as $instance) {
$this->createTestInstance($num++, NULL, $instance['Name'], $instance['InstanceId']);
}
$this->doTestEntityBulk('instance', $instances, 'stop', 'stopped');
}
}
/**
* Create instance test data.
*
......
......@@ -53,11 +53,13 @@ trait AwsCloudTestTrait {
* The name.
* @param string $instance_id
* The instance id.
* @param string $instance_state
* The instance state.
*
* @return object
* The Instance Object.
*/
protected function createTestInstance($num, $public_ip = NULL, $name = NULL, $instance_id = NULL) {
protected function createTestInstance($num, $public_ip = NULL, $name = NULL, $instance_id = NULL, $instance_state = 'running') {
if (!isset($public_ip)) {
$public_ip = Utils::getRandomPublicIp();
}
......@@ -93,6 +95,7 @@ trait AwsCloudTestTrait {
'subnet_id' => 'subnet-' . $this->getRandomAwsId(),
'reason' => $this->random->string(16, TRUE),
'instance_id' => $instance_id ?: 'i-' . $this->getRandomAwsId(),
'instance_state' => $instance_state,
'uid' => $this->loggedInUser->id(),
]);
$instance->save();
......@@ -391,8 +394,12 @@ trait AwsCloudTestTrait {
* The name of the entity type. For example, instance.
* @param array $entities_data
* The data of entities.
* @param string $operation
* The operation.
* @param string $operation_passive
* The passive voice of operation.
*/
protected function doTestEntityBulk($entity_type_name, array $entities_data) {
protected function doTestEntityBulk($entity_type_name, array $entities_data, $operation = 'delete', $operation_passive = 'deleted') {
$cloud_context = $this->cloudContext;
......@@ -405,7 +412,7 @@ trait AwsCloudTestTrait {
$this->drupalGet("/clouds/aws_cloud/$cloud_context/$entity_type_name");
$data = [];
$data['action'] = "aws_cloud_${entity_type_name}_delete_action";
$data['action'] = "aws_cloud_${entity_type_name}_${operation}_action";
$checkboxes = $this->cssSelect('input[type=checkbox]');
foreach ($checkboxes as $checkbox) {
......@@ -424,9 +431,9 @@ trait AwsCloudTestTrait {
);
$this->assertResponse(200);
$message = "Are you sure you want to delete these aws cloud $resource_name entities?";
$message = "Are you sure you want to $operation these aws cloud $resource_name entities?";
if ($entity_count == 1) {
$message = "Are you sure you want to delete this aws cloud $resource_name?";
$message = "Are you sure you want to $operation this aws cloud $resource_name?";
}
$this->assertText($message);
foreach ($entities_data as $entity_data) {
......@@ -434,23 +441,25 @@ trait AwsCloudTestTrait {
$this->assertText($entity_name);
}
// Delete.
// Operation.
$operation_upper = ucfirst($operation);
$this->drupalPostForm(
"/clouds/aws_cloud/$cloud_context/$entity_type_name/delete_multiple",
"/clouds/aws_cloud/$cloud_context/$entity_type_name/${operation}_multiple",
[],
t('Delete')
$operation_upper
);
$this->assertResponse(200);
$operation_passive_upper = ucfirst($operation_passive);
if ($entity_count == 1) {
$this->assertText("Deleted $entity_count item.");
$this->assertText("$operation_passive_upper $entity_count item.");
}
else {
$this->assertText("Deleted $entity_count items.");
$this->assertText("$operation_passive_upper $entity_count items.");
}
foreach ($entities_data as $entity_data) {
$entity_name = $entity_data['Name'];
$this->assertText("The AWS Cloud $entity_type_label \"$entity_name\" has been deleted.");
$this->assertText("The AWS Cloud $entity_type_label \"$entity_name\" has been $operation_passive.");
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment