Commit f98541f1 authored by Ryo Yamashita's avatar Ryo Yamashita Committed by Yas Naoi
Browse files

Issue #3291392 by Ryo Yamashita, yas: Add the function to edit associate...

Issue #3291392 by Ryo Yamashita, yas: Add the function to edit associate Elastic IP for AWS Cloud instance in the SPA
parent 3d83aa87
Loading
Loading
Loading
Loading
+7 −15
Changes for modules/cloud_dashboard/cloud_dashboard.routing.yml: 7 added lines, 15 removed lines.
Original line number Diff line number Diff line
@@ -3,12 +3,10 @@ cloud_dashboard.login:
  defaults:
    _title: Cloud Dashboard
    _form: '\Drupal\cloud_dashboard\Form\React\CloudDashboardForm'
    cloud_context: ''
  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: 'access dashboard'
    # Public access.
    _access: 'TRUE'

# Ex.
# * /clouds/dashboard/callback
@@ -83,11 +81,8 @@ cloud_dashboard.callback_uri:
    _controller: '\Drupal\cloud_dashboard\Controller\CloudDashboardConfigController::getCallbackUri'
  methods: [GET]
  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: 'access dashboard'
    # Public access.
    _access: 'TRUE'

# OAuth2 client ID for Cloud Admin Settings.
cloud_dashboard.client_id:
@@ -96,11 +91,8 @@ cloud_dashboard.client_id:
    _controller: '\Drupal\cloud_dashboard\Controller\CloudDashboardConfigController::getClientId'
  methods: [GET]
  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: 'access dashboard'
    # Public access.
    _access: 'TRUE'

# JSON:API server URI for Cloud Admin Settings.
cloud_dashboard.jsonapi_server_uri:
+24 −1
Changes for modules/cloud_dashboard/cloud_dashboard/src/constant/form_template/aws_cloud/instance.ts: 24 added lines, 1 removed line.
Original line number Diff line number Diff line
import EntityFormTemplate from 'model/EntityFormTemplate';

const AWS_CLOUD_INSTANCE_TEMPLATE: EntityFormTemplate[] = [
  {
    cloudServiceProvider: 'aws_cloud',
    entityName: 'instance',
    actionType: 'associate_elastic_ip',
    entityRecords: [
      {
        type: 'panel',
        panelName: 'Select Elastic IP',
        keyValueRecords: [
          {
            type: 'select', labelName: 'Elastic IP', name: 'allocation_id',
            url: '/cloud_dashboard/aws_cloud/{cloud_context}/elastic_ips',
            defaultValue: ''
          },
          {
            type: 'select', labelName: 'Private IP', name: 'network_interface_id',
            url: '/cloud_dashboard/aws_cloud/aws_cloud_instance/{entity_id}/private_ips',
            defaultValue: ''
          },
        ]
      }
    ]
  },
  {
    cloudServiceProvider: 'aws_cloud',
    entityName: 'instance',
@@ -47,8 +70,8 @@ const AWS_CLOUD_INSTANCE_TEMPLATE: EntityFormTemplate[] = [
          { labelName: 'Private IPs', name: 'private_ips', type: 'default', defaultValue: '', readOnly: true },
          { labelName: 'Public DNS', name: 'public_dns', type: 'default', defaultValue: '', readOnly: true },
          {
            url: '/cloud_dashboard/aws_cloud/aws_cloud_instance/{entity_id}/security_groups_options',
            type: 'multi-select', labelName: 'Security groups', name: 'security_groups',
            url: '/cloud_dashboard/aws_cloud/aws_cloud_instance/{entity_id}/security_groups_options',
            defaultValue: []
          },
          {
+7 −6
Changes for modules/cloud_dashboard/cloud_dashboard/src/molecules/DataTableRowWithOperationLinks.tsx: 7 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ const getOperations = (dataRecord: DataRecord): string[] => {
      } else if (dataRecord.value['instance_state'] === 'stopped') {
        list.push('Start');
      }
      list.push('Associate Elastic IP');
      return list;
    }
    case 'aws_cloud_volume': {
@@ -123,12 +124,12 @@ const getOperations = (dataRecord: DataRecord): string[] => {
}

/**
 * Action logic of xxUD.
 * Action logic of updating and deleting.
 *
 * @param dataRecord Data record of the element.
 * @param action Type of CxUD.
 */
const xxudData = (dataRecord: DataRecord, action: string) => {
const updateAndDeleteData = (dataRecord: DataRecord, action: string) => {
  let urlParts = dataRecord.entityTypeId;
  const modules = ['aws_cloud', 'k8s', 'openstack', 'vmware'];
  for (const module of modules) {
@@ -186,7 +187,7 @@ const DataTableRowWithOperationLinks = ({ dataRecord, dataColumnList, className,
          <Dropdown as={ButtonGroup} className="custom-dropdown">
            <button type="button" className="dropdown-toggle links ripple-effect" onClick={(e) => {
              e.preventDefault();
              xxudData(dataRecord, 'edit');
              updateAndDeleteData(dataRecord, 'edit');
            }}>{t('Edit')}</button>

            <button type="button" className="uparrow" data-bs-toggle="dropdown" aria-expanded="false">
@@ -207,7 +208,7 @@ const DataTableRowWithOperationLinks = ({ dataRecord, dataColumnList, className,
                {/* eslint-disable-next-line  */}
                <a href="#" onClick={(e) => {
                  e.preventDefault();
                  xxudData(dataRecord, 'delete');
                  updateAndDeleteData(dataRecord, 'delete');
                }}>
                  <span className="delete glyphicon glyphicon-"></span>
                  {t('Delete')}
@@ -228,10 +229,10 @@ const DataTableRowWithOperationLinks = ({ dataRecord, dataColumnList, className,
                        // becomes part of the URL in CxUD. If it does not match,
                        // set it individually with a switch-case statement.
                        case 'Start':
                          xxudData(dataRecord, 'start');
                          updateAndDeleteData(dataRecord, 'start');
                          break;
                        default:
                          xxudData(dataRecord, menuInfo.toLowerCase());
                          updateAndDeleteData(dataRecord, menuInfo.toLowerCase().replaceAll(' ', '_'));
                          break;
                      }
                    }}>
+29 −1
Changes for modules/cloud_service_providers/aws_cloud/aws_cloud.routing.yml: 29 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -874,6 +874,7 @@ entity.aws_cloud_instance.security_groups_options:
  path: '/cloud_dashboard/aws_cloud/aws_cloud_instance/{entity_id}/security_groups_options'
  defaults:
    _controller: '\Drupal\aws_cloud\Controller\Ec2\ApiController::getSecurityGroupsOptions'
    cloud_context: ''
  methods: [GET]
  requirements:
    # Use custom access that will check for cloud_context and the desired permission.
@@ -886,6 +887,32 @@ entity.aws_cloud_instance.schedule_options:
  path: '/cloud_dashboard/schedule_options'
  defaults:
    _controller: '\Drupal\aws_cloud\Controller\Ec2\ApiController::getScheduleOptions'
    cloud_context: ''
  methods: [GET]
  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: 'access dashboard'

entity.aws_cloud_instance.elastic_ips:
  path: '/cloud_dashboard/aws_cloud/{cloud_context}/elastic_ips'
  defaults:
    _controller: '\Drupal\aws_cloud\Controller\Ec2\ApiController::getAvailableElasticIps'
  methods: [GET]
  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: 'access dashboard'

entity.aws_cloud_instance.private_ips:
  path: '/cloud_dashboard/aws_cloud/aws_cloud_instance/{entity_id}/private_ips'
  defaults:
    _controller: '\Drupal\aws_cloud\Controller\Ec2\ApiController::getAvailablePrivateIps'
    cloud_context: ''
  methods: [GET]
  requirements:
    # Use custom access that will check for cloud_context and the desired permission.
@@ -942,7 +969,8 @@ entity.aws_cloud_instance.create_image:
entity.aws_cloud_instance.associate_elastic_ip:
  path: '/cloud_dashboard/aws_cloud/{cloud_context}/aws_cloud_instance/{entity_id}/associate_elastic_ip'
  defaults:
    _controller: '\Drupal\aws_cloud\Controller\Ec2\ApiController::operateInstance'
    _controller: '\Drupal\aws_cloud\Controller\Ec2\ApiController::operateEntity'
    entity_type_id: aws_cloud_instance
    command: associate_elastic_ip
  methods: [POST]
  requirements:
+89 −44
Changes for modules/cloud_service_providers/aws_cloud/src/Controller/Ec2/ApiController.php: 89 added lines, 44 removed lines.
Original line number Diff line number Diff line
@@ -2,8 +2,10 @@

namespace Drupal\aws_cloud\Controller\Ec2;

use Drupal\aws_cloud\Entity\Ec2\ElasticIp;
use Drupal\aws_cloud\Entity\Ec2\Instance;
use Drupal\aws_cloud\Entity\Ec2\InstanceInterface;
use Drupal\aws_cloud\Entity\Ec2\NetworkInterface;
use Drupal\aws_cloud\Service\AwsCloud\AwsCloudOperationsServiceInterface;
use Drupal\aws_cloud\Service\CloudWatch\CloudWatchServiceInterface;
use Drupal\aws_cloud\Service\Ec2\Ec2OperationsServiceInterface;
@@ -797,38 +799,6 @@ class ApiController extends ControllerBase implements ApiControllerInterface {
        $command = 'createImageFrom';
        break;

      case 'associate_elastic_ip':
        /* JSON format in request body.
         * {
         *   allocationId: string;
         *   networkInterfaceId: string;
         * }
         */
        $parameter = json_decode($request->getContent(), TRUE);
        $command = 'associateElasticIpFor';
        break;

      case 'create':
        /* JSON format in request body.
         * {
         *   imageId: string;
         *   // integer.
         *   maxCount: number;
         *   // integer.
         *   minCount: number;
         *   keyPairName: string;
         *   isMonitoring: boolean;
         *   availabilityZone: string;
         *   securityGroups: string;
         *   instanceType: string;
         *   kernelId: string;
         *   ramdiskId: string;
         *   userData: string;
         * }
         */
        $parameter = json_decode($request->getContent(), TRUE);
        break;

      default:
        break;
    }
@@ -880,24 +850,26 @@ class ApiController extends ControllerBase implements ApiControllerInterface {
    $form_state = new IntermediateFormState();
    $method_name = '';
    switch ($command . '_' . $entity_type_id) {
      case 'associate_elastic_ip_aws_cloud_instance':
        $form_state->set('allocation_id', $request->get('allocation_id', ''));
        $form_state->set('network_interface_id', $request->get('network_interface_id', ''));

        $method_name = 'associateElasticIpForInstance';
        break;

      case 'edit_aws_cloud_instance':
        $original_entity->setName($request->get('name', ''));
        $original_entity->setIamRole($request->get('iam_role', ''));
        $original_entity->setSecurityGroups(json_decode($request->get('security_groups', '[]'), TRUE));
        $original_entity->setTags(json_decode($request->get('tags', '[]'), TRUE));
        $original_entity->setTerminationTimestamp($request->get('termination_timestamp', NULL));
        $original_entity->setUserData($request->get('user_data', NULL));
        $original_entity->setTerminationProtection(json_decode($request->get('termination_protection', '"false"'), TRUE));
        $original_entity->setSchedule($request->get('schedule', ''));

        $form_state->set('name', $request->get('name', ''));
        $form_state->set('iam_role', $request->get('iam_role', ''));
        $form_state->set('security_groups', json_decode($request->get('security_groups', '[]'), TRUE));
        $form_state->set('tags', json_decode($request->get('tags', '[]'), TRUE));
        $form_state->set('termination_timestamp', $request->get('termination_timestamp', NULL));
        $form_state->set('user_data', $request->get('user_data', NULL));
        $form_state->set('termination_protection', json_decode($request->get('termination_protection', '"false"'), TRUE));
        $form_state->set('schedule', $request->get('schedule', ''));
        $original_entity->setUserData($request->get('user_data', NULL));
        $original_entity->setTerminationProtection(json_decode($request->get('termination_protection', '"false"'), TRUE));
        $form_state->set('user_data_base64_encoded', json_decode($request->get('user_data_base64_encoded', '"false"'), TRUE));
        $original_entity->setSchedule($request->get('schedule', ''));
        $form_state->set('add_new_elastic_ip', $request->get('add_new_elastic_ip', ''));
        $form_state->set('current_allocation_id', $request->get('current_allocation_id', ''));
        $form_state->set('current_association_id', $request->get('current_association_id', ''));

        $method_name = 'editInstance';
        break;
@@ -1018,4 +990,77 @@ class ApiController extends ControllerBase implements ApiControllerInterface {
    return new JsonResponse($output, 200);
  }

  /**
   * {@inheritdoc}
   */
  public function getAvailableElasticIps(string $cloud_context): JsonResponse {
    $elastic_ips = [];
    $results = $this->entityTypeManager
      ->getStorage('aws_cloud_elastic_ip')
      ->getQuery()
      ->accessCheck(TRUE)
      ->condition('cloud_context', $cloud_context)
      ->notExists('association_id')
      ->execute();

    foreach ($results ?: [] as $result) {
      /** @var \Drupal\aws_cloud\Entity\Ec2\ElasticIp $elastic_ip */
      $elastic_ip = ElasticIp::load($result);
      $elastic_ips[$elastic_ip->getAllocationId()] = $elastic_ip->getPublicIp();
    }

    $output = [];
    foreach ($elastic_ips as $value => $label) {
      $output[] = [
        'value' => $value,
        'label' => $label,
      ];
    }

    return new JsonResponse($output, 200);
  }

  /**
   * {@inheritdoc}
   */
  public function getAvailablePrivateIps(string $entity_id): JsonResponse {
    /** @var \Drupal\aws_cloud\Entity\Ec2\Instance */
    $entity = $this->entityTypeManager
      ->getStorage('aws_cloud_instance')
      ->load($entity_id);
    if (empty($entity)) {
      return new JsonResponse([
        'result' => 'NG',
        'reason' => 'The instance has already been deleted.',
      ], 404);
    }

    $private_ips = [];
    $results = $this->entityTypeManager
      ->getStorage('aws_cloud_network_interface')
      ->getQuery()
      ->accessCheck(TRUE)
      ->condition('instance_id', $entity->getInstanceId())
      ->condition('cloud_context', $entity->getCloudContext())
      ->notExists('association_id')
      ->execute();
    foreach ($results ?: [] as $result) {
      $network_interface = NetworkInterface::load($result);
      if (empty($network_interface)) {
        continue;
      }
      $private_ips[$network_interface->getNetworkInterfaceId()] = $network_interface->getPrimaryPrivateIp();
    }

    $output = [];
    foreach ($private_ips as $value => $label) {
      $output[] = [
        'value' => $value,
        'label' => $label,
      ];
    }

    return new JsonResponse($output, 200);
  }

}
Loading