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

Issue #3317494 by Ryo Yamashita, yas: Add REST API to edit the OpenStack...

Issue #3317494 by Ryo Yamashita, yas: Add REST API to edit the OpenStack launch template in the Cloud module
parent 0abcf088
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -1033,6 +1033,20 @@ entity.openstack_volume.instances:
  requirements:
    _custom_access: '\Drupal\openstack\Controller\ApiController::access'

entity.openstack_cloud_launch_template.edit:
  path: '/cloud_dashboard/openstack/{cloud_context}/cloud_launch_template/{entity_id}/edit'
  defaults:
    _controller: '\Drupal\openstack\Controller\ApiController::operateEntity'
    entity_type_id: cloud_launch_template
    command: edit
  methods: [POST]
  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 cloud server templates+edit own cloud server templates'

entity.openstack_floating_ip.network_interface_ids:
  path: '/cloud_dashboard/openstack/{cloud_context}/network_interface_ids'
  defaults:
+1 −1
Original line number Diff line number Diff line
@@ -24,4 +24,4 @@ services:

  openstack.operations:
    class: Drupal\openstack\Service\OpenStackOperationsService
    arguments: ['@aws_cloud.openstack.operations', '@plugin.manager.cloud_config_plugin', '@cloud', '@openstack.ec2', '@entity_type.manager', '@messenger', '@openstack.factory', '@request_stack', '@entity.link_renderer']
    arguments: ['@aws_cloud.openstack.operations', '@plugin.manager.cloud_config_plugin', '@cloud', '@openstack.ec2', '@entity_type.manager', '@messenger', '@openstack.factory', '@request_stack', '@current_route_match', '@entity.link_renderer']
+4 −0
Original line number Diff line number Diff line
@@ -713,6 +713,10 @@ class ApiController extends ControllerBase implements ApiControllerInterface {
        $method_name = 'detachOpenStackVolume';
        break;

      case 'edit_openstack_cloud_launch_template':
        $method_name = 'editOpenStackCloudLaunchTemplate';
        break;

    }

    // Execute the process.
+232 −13
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ use Drupal\cloud\Plugin\cloud\config\CloudConfigPluginManagerInterface;
use Drupal\cloud\Service\CloudServiceInterface;
use Drupal\cloud\Service\EntityLinkRendererInterface;
use Drupal\cloud\Entity\CloudContentEntityBase;
use Drupal\cloud\Entity\CloudLaunchTemplateInterface;
use Drupal\cloud\Traits\CloudContentEntityTrait;
use Drupal\Core\Entity\EntityDeleteFormTrait;
use Drupal\Core\Entity\EntityInterface;
@@ -24,6 +25,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Render\Markup;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\openstack\Entity\OpenStackNetworkExInterface;
use Drupal\openstack\Entity\OpenStackPortInterface;
use Drupal\openstack\Entity\OpenStackRouterInterface;
@@ -106,6 +108,13 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
   */
  protected $requestStack;

  /**
   * Route match object.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * OpenStackOperationsService constructor.
   *
@@ -125,6 +134,8 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
   *   Object for interfacing with OpenStack Service.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   Request stack object.
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   Route match object.
   * @param \Drupal\cloud\Service\EntityLinkRendererInterface $entity_link_renderer
   *   The entity link render service.
   */
@@ -137,6 +148,7 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
    Messenger $messenger,
    OpenStackServiceFactoryInterface $openstack_service_factory,
    RequestStack $request_stack,
    RouteMatchInterface $route_match,
    EntityLinkRendererInterface $entity_link_renderer
  ) {
    $this->awsCloudOperationsService = $aws_cloud_operations_service;
@@ -147,6 +159,7 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
    $this->messenger = $messenger;
    $this->openStackServiceFactory = $openstack_service_factory;
    $this->requestStack = $request_stack;
    $this->routeMatch = $route_match;
    $this->entityLinkRenderer = $entity_link_renderer;
  }

@@ -776,7 +789,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
   * {@inheritdoc}
   */
  public function createOpenStackNetwork(OpenStackNetworkExInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $this->awsCloudOperationsService->trimTextfields($entity, $form, $form_state);
@@ -830,7 +845,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
   * {@inheritdoc}
   */
  public function createOpenStackSubnet(OpenStackSubnetInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $this->awsCloudOperationsService->trimTextfields($entity, $form, $form_state);
@@ -940,7 +957,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
   * {@inheritdoc}
   */
  public function createOpenStackPort(OpenStackPortInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $this->awsCloudOperationsService->trimTextfields($entity, $form, $form_state);
@@ -1023,7 +1042,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
   * {@inheritdoc}
   */
  public function createOpenStackRouter(OpenStackRouterInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $this->awsCloudOperationsService->trimTextfields($entity, $form, $form_state);
@@ -1088,7 +1109,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
   * {@inheritdoc}
   */
  public function addOpenStackRouterInterface(OpenStackRouterInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $form_state->setRedirect('view.openstack_port.router_interface', [
@@ -1102,6 +1125,7 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
        'cloud_context' => $entity->getCloudContext(),
        'subnet_id' => $form_state->getValue('subnet_id'),
      ]);
    /** @var \Drupal\openstack\Entity\OpenStackSubnet $subnet */
    $subnet = reset($subnets);

    $ip_address = $form_state->getValue('ip_address');
@@ -1230,7 +1254,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
  public function editOpenStackNetwork(OpenStackNetworkExInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->entity = $entity;

    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $this->awsCloudOperationsService->trimTextfields($entity, $form, $form_state);
@@ -1286,7 +1312,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
  public function editOpenStackSubnet(OpenStackSubnetInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->entity = $entity;

    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $this->awsCloudOperationsService->trimTextfields($entity, $form, $form_state);
@@ -1402,7 +1430,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
  public function editOpenStackPort(OpenStackPortInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->entity = $entity;

    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $this->awsCloudOperationsService->trimTextfields($entity, $form, $form_state);
@@ -1478,7 +1508,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
  public function editOpenStackRouter(OpenStackRouterInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->entity = $entity;

    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $this->awsCloudOperationsService->trimTextfields($entity, $form, $form_state);
@@ -1552,7 +1584,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
  public function deleteOpenStackNetwork(OpenStackNetworkExInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->entity = $entity;

    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $form_state->setRedirect("view.{$entity->getEntityTypeId()}.list", ['cloud_context' => $entity->getCloudContext()]);
@@ -1589,7 +1623,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
  public function deleteOpenStackSubnet(OpenStackSubnetInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->entity = $entity;

    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $form_state->setRedirect("view.{$entity->getEntityTypeId()}.list", ['cloud_context' => $entity->getCloudContext()]);
@@ -1626,7 +1662,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
  public function deleteOpenStackPort(OpenStackPortInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->entity = $entity;

    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $form_state->setRedirect("view.{$entity->getEntityTypeId()}.list", ['cloud_context' => $entity->getCloudContext()]);
@@ -1663,7 +1701,9 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
  public function deleteOpenStackRouter(OpenStackRouterInterface $entity, array &$form, FormStateInterface $form_state): bool {
    $this->entity = $entity;

    $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
    /** @var Drupal\openstack\Service\Rest\OpenStackService $ec2_service */
    $ec2_service = $this->openStackServiceFactory->get($entity->getCloudContext());
    $this->ec2Service = $ec2_service;
    $this->awsCloudOperationsService->setEc2Service($this->ec2Service);

    $form_state->setRedirect("view.{$entity->getEntityTypeId()}.list", ['cloud_context' => $entity->getCloudContext()]);
@@ -2834,4 +2874,183 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
    }
  }

  /**
   * {@inheritdoc}
   */
  public function editOpenStackCloudLaunchTemplate(CloudLaunchTemplateInterface $entity, array &$form, FormStateInterface $form_state): bool {
    try {
      $this->alterCloudLaunchTemplateEditForm($entity, $form, $form_state);
      return $this->submitCloudLaunchTemplateEditForm($entity, $form, $form_state);
    }
    catch (\Exception $e) {
      $this->handleException($e);
      $this->processOperationErrorStatus($entity, 'edited');
      return FALSE;
    }
  }

  /**
   * Common alter function for edit and add forms.
   *
   * @param \Drupal\cloud\Entity\CloudLaunchTemplateInterface $entity
   *   The CloudLaunchTemplate entity.
   * @param array $form
   *   Array of form object.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   */
  private function alterCloudLaunchTemplateCommonForm(CloudLaunchTemplateInterface $entity, array &$form, FormStateInterface $form_state) {
    $this->cloudService->reorderForm($form, openstack_launch_template_field_orders());

    $cloud_context = $this->routeMatch->getParameter('cloud_context');
    /** @var \Drupal\openstack\Service\OpenStackServiceInterface $openstack_service */
    $openstack_ec2_service = $this->openStackServiceFactory->isEc2ServiceType('cloud_config', $cloud_context);

    if (empty($openstack_ec2_service)) {
      $form['instance']['field_min_count']['#access'] = FALSE;
      $form['instance']['field_max_count']['#access'] = FALSE;
      $form['ami']['field_kernel_id']['#access'] = FALSE;
      $form['ami']['field_ram']['#access'] = FALSE;
      $form['network']['field_openstack_vpc']['#access'] = FALSE;
      $form['network']['field_openstack_subnet']['#access'] = FALSE;
      unset($form['options']);
    }

    $form['field_instance_type']['widget']['#default_value'] = ['m1.nano'];
    $form['field_instance_type']['#access'] = FALSE;

    if (!empty($openstack_ec2_service)) {
      $form['instance']['field_flavor']['widget']['#default_value'] = '1';
      $form['instance']['field_flavor']['#access'] = FALSE;

      $form['network']['field_openstack_vpc']['widget']['#ajax'] = [
        'callback' => 'openstack_ajax_callback_get_fields',
      ];

      $vpc_id = '_none';
      if (!empty($form['network']['field_openstack_vpc']['widget']['#default_value'])) {
        $vpc_id = $form['network']['field_openstack_vpc']['widget']['#default_value'][0];
      }

      // If validation happened, we should get vpc_id from user input.
      $user_input = $form_state->getUserInput();
      if (!empty($user_input['field_openstack_vpc'])) {
        $vpc_id = $user_input['field_openstack_vpc'];
      }

      /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
      $form_object = $form_state->getFormObject();
      $server_template = $entity->createDuplicate();
      $form_object->setEntity($server_template);
      $subnet_options = openstack_get_subnet_options_by_vpc_id($vpc_id, $form_object->getEntity());
      $form['#attached']['library'][] = 'openstack/openstack_form';
      $form['#attached']['drupalSettings']['openstack']['field_openstack_subnet_default_values']
        = array_keys($subnet_options);

      $security_group_options = openstack_get_security_group_options_by_vpc_id($vpc_id);
      $security_group_default_values = array_map(static function ($id) {
        return (string) $id;
      }, array_keys($security_group_options));

      $form['#attached']['drupalSettings']['openstack']['field_openstack_security_group_default_values']
        = $security_group_default_values;
    }

    // Hide labels of field_tags.
    $form['tags']['field_tags']['widget']['#title'] = NULL;
    if (empty($openstack_ec2_service)) {
      $form['#validate'][] = 'openstack_form_cloud_launch_template_form_validate';
    }
  }

  /**
   * Implements hook_form_FORM_ID_alter().
   *
   * @param \Drupal\cloud\Entity\CloudLaunchTemplateInterface $entity
   *   The CloudLaunchTemplate entity.
   * @param array $form
   *   Array of form object.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   */
  private function alterCloudLaunchTemplateEditForm(CloudLaunchTemplateInterface $entity, array &$form, FormStateInterface $form_state) {
    $this->alterCloudLaunchTemplateCommonForm($entity, $form, $form_state);

    // Hide new revision checkbox.
    $form['new_revision']['#access'] = FALSE;

    // Disable name field.
    $form['instance']['name']['#disabled'] = TRUE;

    /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
    $form_object = $form_state->getFormObject();
    /** @var \Drupal\cloud\Entity\CloudLaunchTemplateInterface $server_template */
    $server_template = $form_object->getEntity();
    $cloud_context = $server_template->getCloudContext();

    $openstack_service = $this->openStackServiceFactory->isEc2ServiceType('cloud_config', $cloud_context);
    if (empty($openstack_service)) {
      // Overwrite function ::save.
      $form['actions']['submit']['#submit'][1] = 'openstack_form_cloud_launch_template_openstack_edit_form_submit';
    }
  }

  /**
   * Submit function for form cloud_launch_template_aws_cloud_edit_form.
   *
   * @param \Drupal\cloud\Entity\CloudLaunchTemplateInterface $entity
   *   The CloudLaunchTemplate entity.
   * @param array $form
   *   Array of form object.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   *
   * @return bool
   *   TRUE when the process succeeds.
   */
  private function submitCloudLaunchTemplateEditForm(CloudLaunchTemplateInterface $entity, array &$form, FormStateInterface $form_state): bool {
    /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
    $form_object = $form_state->getFormObject();
    /** @var \Drupal\cloud\Entity\CloudLaunchTemplateInterface $server_template */
    $server_template = $form_object->getEntity();

    $cloud_context = $server_template->getCloudContext();

    $form_values = $form_state->getValues();
    $uid = $form_values['uid'][0]['target_id'] ?? 0;
    /** @var \Drupal\openstack\Service\OpenStackServiceInterface|\Drupal\cloud\Service\CloudResourceTagInterface $openstack_service */
    $openstack_service = $this->openStackServiceFactory->get($cloud_context);
    $uid_key_name = $openstack_service->getTagKeyCreatedByUid(
      'openstack',
      $cloud_context,
      intval($uid)
    );

    $form_tags = ($server_template->hasField('field_tags')
      && !$server_template->get('field_tags')->isEmpty())
      ? $server_template->get('field_tags')->getValue() : [];

    // Update tags if authored id is changed.
    foreach ($form_tags ?: [] as $form_tag) {
      $item_key = $form_tag['item_key'];
      if ($form_tag['item_key'] === $uid_key_name) {
        $tags[] = ['item_key' => $item_key, 'item_value' => $uid];
      }
    }

    $server_template->set('field_tags', $tags);
    $server_template->save();

    $this->processOperationStatus($server_template, 'updated');

    $form_state->setRedirect(
      'entity.cloud_launch_template.canonical',
      [
        'cloud_context' => $cloud_context,
        'cloud_launch_template' => $server_template->id(),
      ]
    );
    return TRUE;
  }

}
+16 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ use Drupal\aws_cloud\Entity\Ec2\NetworkInterfaceInterface;
use Drupal\aws_cloud\Entity\Ec2\SecurityGroupInterface;
use Drupal\aws_cloud\Entity\Ec2\SnapshotInterface;
use Drupal\aws_cloud\Entity\Ec2\VolumeInterface;
use Drupal\cloud\Entity\CloudLaunchTemplateInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\openstack\Entity\OpenStackNetworkExInterface;
use Drupal\openstack\Entity\OpenStackPortInterface;
@@ -730,4 +731,19 @@ interface OpenStackOperationsServiceInterface {
   */
  public function detachOpenStackVolume(VolumeInterface $entity, array &$form, FormStateInterface $form_state): bool;

  /**
   * Edit an OpenStack CloudLaunchTemplate.
   *
   * @param \Drupal\cloud\Entity\CloudLaunchTemplateInterface $entity
   *   The CloudLaunchTemplate entity.
   * @param array $form
   *   Array of form object.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   *
   * @return bool
   *   TRUE when the process succeeds.
   */
  public function editOpenStackCloudLaunchTemplate(CloudLaunchTemplateInterface $entity, array &$form, FormStateInterface $form_state): bool;

}