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

Issue #3280027 by Ryo Yamashita, yas, sekinet: Refactor OpenStackImageCreateForm

parent 55d8c57d
Loading
Loading
Loading
Loading
+27 −13
Changes for modules/cloud_service_providers/openstack/src/Controller/ApiController.php: 27 added lines, 13 removed lines.
Original line number Diff line number Diff line
@@ -503,8 +503,35 @@ class ApiController extends ControllerBase implements ApiControllerInterface {
      ], 404);
    }

    if ($command === 'create' && $entity_type_id === 'openstack_image') {
      /**
       * @var \Drupal\aws_cloud\Entity\Ec2\ImageInterface|\Drupal\aws_cloud\Entity\Ec2\Image
       */
      $original_entity = $entity;
      $form_state = new IntermediateFormState();

      $original_entity->setName($request->get('name', ''));
      $original_entity->setVisibility(!empty($request->get('visibility', '')) ? 'public' : 'private');
      $form_state->set('name', $request->get('name', ''));

      $result = $this->openStackOperationsService->createOpenStackImage(
        $original_entity,
        [],
        $form_state
      );
      return !empty($result)
        ? new JsonResponse([
          'result' => 'OK',
        ], 200)
        : new JsonResponse([
          'result' => 'NG',
          'reason' => 'Internal Server Error',
        ], 500);
    }

    if ($command === 'delete' && $entity_type_id === 'openstack_image') {
      $form_state = new IntermediateFormState();

      $result = $this->openStackOperationsService->deleteOpenStackImage(
        $entity,
        [],
@@ -523,19 +550,6 @@ class ApiController extends ControllerBase implements ApiControllerInterface {
    $method_name = '';
    $parameter = [];
    switch ($command) {
      case 'create':
        switch ($entity_type_id) {
          case 'openstack_image':
            $method_name = 'createImage';
            $parameter = [
              'name' => $request->get('name', ''),
            ];
            $entity->setName($request->get('name', ''));
            $entity->setVisibility(!empty($request->get('visibility', '')) ? 'public' : 'private');
            break;
        }
        break;

      case 'edit':
        switch ($entity_type_id) {
          case 'openstack_image':
+19 −17
Changes for modules/cloud_service_providers/openstack/src/Form/OpenStackImageCreateForm.php: 19 added lines, 17 removed lines.
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ use Drupal\Core\Render\Renderer;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\openstack\Service\Ec2\OpenStackEc2OperationsServiceInterface;
use Drupal\openstack\Service\OpenStackOperationsServiceInterface;
use Drupal\openstack\Service\OpenStackServiceFactoryInterface;
use Drupal\openstack\Service\Rest\OpenStackService as OpenStackRestService;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -45,6 +46,13 @@ class OpenStackImageCreateForm extends ImageCreateForm {
   */
  private $openStackEc2OperationsService;

  /**
   * The OpenStack operations Service.
   *
   * @var \Drupal\openstack\Service\OpenStackOperationsServiceInterface
   */
  protected $openStackOperationsService;

  /**
   * OpenStackImageCreateForm constructor.
   *
@@ -52,7 +60,7 @@ class OpenStackImageCreateForm extends ImageCreateForm {
   *   Object for interfacing with OpenStack Service.
   * @param \Drupal\aws_cloud\Service\Ec2\Ec2ServiceInterface $ec2_service
   *   The AWS Cloud or OpenStack EC2 Service.
   * @param \Drupal\openstack\Service\Ec2\OpenStackEc2OperationsServiceInterface $openstack_operations_service
   * @param \Drupal\openstack\Service\Ec2\OpenStackEc2OperationsServiceInterface $openstack_ec2_operations_service
   *   The OpenStack operation service.
   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
   *   The entity repository service.
@@ -82,11 +90,13 @@ class OpenStackImageCreateForm extends ImageCreateForm {
   *   The general renderer.
   * @param \Drupal\cloud\Service\CloudServiceInterface $cloud_service
   *   The Cloud service.
   * @param \Drupal\openstack\Service\OpenStackOperationsServiceInterface $openstack_operations_service
   *   The OpenStack Operations service.
   */
  public function __construct(
    OpenStackServiceFactoryInterface $openstack_service_factory,
    Ec2ServiceInterface $ec2_service,
    OpenStackEc2OperationsServiceInterface $openstack_operations_service,
    OpenStackEc2OperationsServiceInterface $openstack_ec2_operations_service,
    EntityRepositoryInterface $entity_repository,
    EntityTypeBundleInfoInterface $entity_type_bundle_info,
    TimeInterface $time,
@@ -100,7 +110,8 @@ class OpenStackImageCreateForm extends ImageCreateForm {
    RouteMatchInterface $route_match,
    DateFormatterInterface $date_formatter,
    Renderer $renderer,
    CloudServiceInterface $cloud_service
    CloudServiceInterface $cloud_service,
    OpenStackOperationsServiceInterface $openstack_operations_service
  ) {

    parent::__construct(
@@ -122,7 +133,8 @@ class OpenStackImageCreateForm extends ImageCreateForm {
    );

    $this->openStackServiceFactory = $openstack_service_factory;
    $this->openStackEc2OperationsService = $openstack_operations_service;
    $this->openStackEc2OperationsService = $openstack_ec2_operations_service;
    $this->openStackOperationsService = $openstack_operations_service;
  }

  /**
@@ -152,7 +164,8 @@ class OpenStackImageCreateForm extends ImageCreateForm {
      $container->get('current_route_match'),
      $container->get('date.formatter'),
      $container->get('renderer'),
      $container->get('cloud')
      $container->get('cloud'),
      $container->get('openstack.operations')
    );
  }

@@ -210,18 +223,7 @@ class OpenStackImageCreateForm extends ImageCreateForm {
   *   The current form state.
   */
  public function save(array $form, FormStateInterface $form_state): void {
    $this->trimTextfields($form, $form_state);

    $entity = $this->entity;
    $cloudContext = $entity->getCloudContext();
    $ec2Service = $this->openStackServiceFactory->get($cloudContext);

    $this->openStackEc2OperationsService->setEc2Service($ec2Service);
    if ($this->openStackEc2OperationsService->createImage($this->entity, [
      'name' => $form_state->getValue('name'),
    ])) {
      $form_state->setRedirect("view.{$entity->getEntityTypeId()}.list", ['cloud_context' => $entity->getCloudContext()]);
    }
    $this->openStackOperationsService->createOpenStackImage($this->entity, $form, $form_state);
  }

}
+242 −0
Changes for modules/cloud_service_providers/openstack/src/Service/OpenStackOperationsService.php: 242 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -3,15 +3,18 @@
namespace Drupal\openstack\Service;

use Drupal\aws_cloud\Entity\Ec2\ImageInterface;
use Drupal\aws_cloud\Service\Ec2\Ec2ServiceException;
use Drupal\aws_cloud\Service\Ec2\Ec2ServiceInterface;
use Drupal\aws_cloud\Traits\AwsCloudEntityCheckTrait;
use Drupal\cloud\Plugin\cloud\config\CloudConfigPluginManagerInterface;
use Drupal\cloud\Service\CloudServiceInterface;
use Drupal\cloud\Traits\CloudContentEntityTrait;
use Drupal\Core\Entity\EntityDeleteFormTrait;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\Messenger;
use Drupal\openstack\Service\Rest\OpenStackService as OpenStackRestService;

/**
 * Interacts with OpenStack using OpenStackService API.
@@ -103,6 +106,239 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
    return $this->entity;
  }

  /**
   * Trim white spaces in the values of textfields.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state object.
   */
  private function trimTextfields(array $form, FormStateInterface $form_state): void {
    $field_names = [];
    foreach ($form ?: [] as $name => $item) {
      if (!is_array($item)) {
        continue;
      }

      if (isset($item['#type'])
        && $item['#type'] === 'textfield'
      ) {
        $field_names[] = $name;
      }

      if (isset($item['#type']) && $item['#type'] === 'details') {
        foreach ($item ?: [] as $sub_item_name => $sub_item) {
          if (is_array($sub_item)
            && isset($sub_item['#type'])
            && $sub_item['#type'] === 'textfield'
          ) {
            $field_names[] = $sub_item_name;
          }
        }
      }
    }

    foreach ($field_names ?: [] as $field_name) {
      $value = $form_state->getValue($field_name);
      if ($value === NULL) {
        continue;
      }

      $value = trim($value);
      $form_state->setValue($field_name, $value);
      $this->entity->set($field_name, $value);
    }
  }

  /**
   * Check if current cloud config is remote or not.
   *
   * @return bool
   *   True if the current cloud config is remote.
   */
  private function isCloudConfigRemote(): bool {
    /**
     * The entity being used by this form.
     *
     * @var \Drupal\aws_cloud\Entity\Ec2\ImageInterface
     */
    $entity = $this->entity;

    $this->cloudConfigPluginManager->setCloudContext($entity->getCloudContext());
    $cloud_config = $this->cloudConfigPluginManager->loadConfigEntity();
    return $cloud_config->isRemote();
  }

  /**
   * Set the tags for the entity.
   *
   * @param string $resource_id
   *   The resource ID.  For example, instance_id, volume_id.
   * @param array $tag_map
   *   The map of tags.
   * @param string $entity_type
   *   The entity type.
   */
  private function setTagsInAws($resource_id, array $tag_map, $entity_type = ''): void {
    /**
     * The entity being used by this form.
     *
     * @var \Drupal\aws_cloud\Entity\Ec2\ImageInterface
     */
    $entity = $this->entity;

    $this->ec2Service->setCloudContext($entity->getCloudContext());
    $tags = [];
    foreach ($tag_map ?: [] as $key => $value) {
      $tags[] = [
        'Key' => $key,
        'Value' => $value,
      ];
    }

    if (!method_exists($this->ec2Service, 'createTags')) {
      return;
    }

    // Create Tags with different parameters for AWS and OpenStack.
    if (preg_match('[^aws_cloud]', $entity->getEntityTypeId()) === 1) {
      $this->ec2Service->createTags([
        'Resources' => [$resource_id],
        'Tags' => $tags,
      ]);
    }
    else {
      $this->ec2Service->createTags([
        'Resources' => [$resource_id],
        'Tags' => $tags,
        'EntityType' => $entity_type,
      ]);
    }
  }

  /**
   * Update name and created_by tags.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity.
   * @param string $aws_cloud_resource_id
   *   The AWS Cloud resource ID.
   */
  private function updateNameAndCreatedByTags(EntityInterface $entity, $aws_cloud_resource_id): void {
    /**
     * The entity being used by this form.
     *
     * @var \Drupal\aws_cloud\Entity\Ec2\ImageInterface|\Drupal\aws_cloud\Entity\Ec2\Image
     */
    $original_entity = $entity;

    $cloud_context = $original_entity->getCloudContext();
    if ($this->isCloudConfigRemote()) {
      // Get original cloud context.
      if (($pos = strrpos($cloud_context, '_')) !== FALSE) {
        $cloud_context = substr($cloud_context, 0, $pos);
      }
    }

    $bundle = str_contains($original_entity->bundle(), 'aws_cloud')
      ? 'aws_cloud' : 'openstack';
    $uid_key_name = $this->cloudService->getTagCreatedByUid($bundle, $cloud_context);
    $this->setTagsInAws($aws_cloud_resource_id, [
      $original_entity->getEntityTypeId() . '_' . $uid_key_name => !empty($original_entity->getOwner())
      ? $original_entity->getOwner()->id() : 0,
      'Name' => $original_entity->getName(),
    ],
    $original_entity->getEntityTypeId());
  }

  /**
   * Create an Image.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return bool
   *   TRUE when the process succeeds.
   *
   * @throws \Drupal\Core\Entity\EntityMalformedException
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  private function createImage(array $form, FormStateInterface $form_state): bool {
    $this->trimTextfields($form, $form_state);

    /** @var \Drupal\aws_cloud\Entity\Ec2\Image $entity */
    $entity = $this->entity;

    $result = $this->ec2Service->createImage([
      'InstanceId'  => $entity->getInstanceId(),
      'Name'        => $entity->getName(),
      'Description' => $entity->getDescription(),
      'Visibility'  => $entity->getVisibility(),
    ]);

    if (!empty($result['SendToWorker'])) {
      $this->processOperationStatus($entity, 'created remotely');
      $form_state->setRedirect("view.{$entity->getEntityTypeId()}.list", ['cloud_context' => $entity->getCloudContext()]);
      return TRUE;
    }

    $this->cloudConfigPluginManager->setCloudContext($entity->getCloudContext());
    $account_id = $this->cloudConfigPluginManager->loadConfigEntity()->get('field_account_id')->value;

    if (!empty($entity) && !empty($result['ImageId'])
      && ($entity->setName($form_state->getValue('name')))
      && ($entity->set('ami_name', $form_state->getValue('name')))
      && ($entity->setImageId($result['ImageId']))
      && ($entity->set('account_id', $account_id))
      && ($entity->save())) {

      $this->updateNameAndCreatedByTags($entity, $entity->getImageId());

      $this->processOperationStatus($entity, 'created');

      $form_state->setRedirect("view.{$entity->getEntityTypeId()}.list", ['cloud_context' => $entity->getCloudContext()]);
      $this->dispatchSaveEvent($entity);

      return TRUE;
    }
    else {
      $this->processOperationErrorStatus($entity, 'created');

      return FALSE;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function createOpenStackImage(ImageInterface $entity, array &$form, FormStateInterface $form_state): bool {
    try {
      $this->entity = $entity;

      // Switch OpenStack service based on $entity->getCloudContext().
      $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());

      $this->createImage($form, $form_state);

      if (!empty($result['SendToWorker'])) {
        return TRUE;
      }

      if ($this->ec2Service instanceof OpenStackRestService) {
        $this->updateNameAndCreatedByTags($entity, $entity->getImageId());
      }

      return TRUE;
    }
    catch (Ec2ServiceException $e) {
      $this->handleException($e);
      return FALSE;
    }
  }

  /**
   * Delete an Image.
   *
@@ -186,9 +422,15 @@ class OpenStackOperationsService implements OpenStackOperationsServiceInterface
   * {@inheritdoc}
   */
  public function deleteOpenStackImage(ImageInterface $entity, array &$form, FormStateInterface $form_state): bool {
    try {
      $this->entity = $entity;
      $this->ec2Service = $this->openStackServiceFactory->get($entity->getCloudContext());
      return $this->deleteImage($form, $form_state);
    }
    catch (Ec2ServiceException $e) {
      $this->handleException($e);
      return FALSE;
    }
  }

}
+15 −0
Changes for modules/cloud_service_providers/openstack/src/Service/OpenStackOperationsServiceInterface.php: 15 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -10,6 +10,21 @@ use Drupal\Core\Form\FormStateInterface;
 */
interface OpenStackOperationsServiceInterface {

  /**
   * Create an OpenStack image.
   *
   * @param \Drupal\aws_cloud\Entity\Ec2\ImageInterface $entity
   *   The OpenStack image 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 createOpenStackImage(ImageInterface $entity, array &$form, FormStateInterface $form_state): bool;

  /**
   * Delete an OpenStack image.
   *