cloud-8.x-2.0-beta1/modules/cloud_service_providers/aws_cloud/src/Service/Pricing/PricingService.php

modules/cloud_service_providers/aws_cloud/src/Service/Pricing/PricingService.php
<?php

namespace Drupal\aws_cloud\Service\Pricing;

use Drupal\cloud\Entity\CloudConfig;
use Drupal\cloud\Plugin\cloud\config\CloudConfigPluginManagerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use GuzzleHttp\Client;

/**
 * Service PricingService.
 */
class PricingService implements PricingServiceInterface {

  use StringTranslationTrait;

  const DEFAULT_ENDPOINT = 'https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current';

  /**
   * The Messenger service.
   *
   * @var \Drupal\Core\Messenger\Messenger
   */
  protected $messenger;

  /**
   * The cloud service provider plugin manager (CloudConfigPluginManager).
   *
   * @var \Drupal\cloud\Plugin\cloud\config\CloudConfigPluginManagerInterface
   */
  protected $cloudConfigPluginManager;

  /**
   * Cloud context string.
   *
   * @var string
   */
  private $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;

  /**
   * Guzzle http client.
   *
   * @var \GuzzleHttp\Client
   */
  protected $httpClient;

  /**
   * TRUE to run the operation, FALSE to run the operation in validation mode.
   *
   * @var bool
   */
  private $dryRun;

  /**
   * The cloud service provider (CloudConfig) entity.
   *
   * @var \Drupal\cloud\Entity\CloudConfig
   */
  private $cloudConfigEntity = FALSE;

  /**
   * Constructs a new Ec2Service object.
   *
   * @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\cloud\Plugin\cloud\config\CloudConfigPluginManagerInterface $cloud_config_plugin_manager
   *   The cloud service provider plugin manager (CloudConfigPluginManager).
   * @param \GuzzleHttp\Client $http_client
   *   The Guzzle Http client.
   */
  public function __construct(
    LoggerChannelFactoryInterface $logger_factory,
    ConfigFactoryInterface $config_factory,
    Messenger $messenger,
    TranslationInterface $string_translation,
    CloudConfigPluginManagerInterface $cloud_config_plugin_manager,
    Client $http_client
  ) {
    $this->logger = $logger_factory->get('aws_pricing_service');

    // Setup the configuration factory.  Not really needed at this point.
    // Could be useful at a later date.
    $this->configFactory = $config_factory;

    // Setup the dryRun flag.
    $this->dryRun = (bool) $this->configFactory->get('aws_cloud.settings')->get('aws_cloud_test_mode');
    $this->messenger = $messenger;
    $this->stringTranslation = $string_translation;
    $this->cloudConfigPluginManager = $cloud_config_plugin_manager;
    $this->httpClient = $http_client;
  }

  /**
   * {@inheritdoc}
   */
  public function setCloudConfigEntity(CloudConfig $cloud_config_entity) {
    $this->cloudConfigEntity = $cloud_config_entity;
  }

  /**
   * {@inheritdoc}
   */
  public function setCloudContext($cloud_context) {
    $this->cloudContext = $cloud_context;
  }

  /**
   * Generate the full pricing endpoint.
   *
   * @return bool|string
   *   The full pricing endpoint.
   */
  private function getPricingEndpoint() {
    $endpoint = FALSE;
    if ($this->cloudConfigEntity != FALSE) {
      $pricing_endpoint = $this->configFactory
        ->get('aws_cloud.settings')
        ->get('aws_cloud_ec2_pricing_endpoint');

      if (empty($pricing_endpoint)) {
        $pricing_endpoint = self::DEFAULT_ENDPOINT;
      }

      $endpoint = "$pricing_endpoint/{$this->cloudConfigEntity->get('field_region')->value}/index.json";
    }
    return $endpoint;
  }

  /**
   * {@inheritdoc}
   */
  public function getInstanceTypes() {
    $mock_data = $this->configFactory
      ->get('aws_cloud.settings')
      ->get('aws_cloud_mock_instance_types');
    if ($this->dryRun && $mock_data) {
      return json_decode($mock_data, TRUE);
    }

    $price_rate = $this->configFactory
      ->get('aws_cloud.settings')
      ->get('aws_cloud_price_rate_ec2') ?: 100;
    $price_rate /= 100.0;

    // Give max memory for json decoding.
    ini_set('memory_limit', '-1');
    $instance_types = [];
    try {
      $url = $this->getPricingEndpoint();
      if ($url != FALSE) {
        $response = $this->httpClient->get($url, [
          'timeout' => 0,
        ]);
        $data = (string) $response->getBody();
        if (!empty($data)) {
          $instance_products = [];
          $pricing = \GuzzleHttp\json_decode($data);
          foreach ($pricing->products as $product) {
            if ($product->productFamily == 'Compute Instance'
              && $product->attributes->operatingSystem == 'Linux'
              && $product->attributes->tenancy != 'Dedicated'
              && $product->attributes->preInstalledSw == 'NA'
              && isset($product->attributes->instancesku)
            ) {
              $instance_products[$product->attributes->instancesku] = $product->attributes;
            }
          }

          // Add on-demand price.
          foreach ($pricing->terms->OnDemand as $term) {
            $items = array_values((array) $term);

            foreach ($items as $item) {
              $prices = array_values((array) $item->priceDimensions);

              $found = FALSE;
              foreach ($prices as $price) {
                if ($price
                  && $price->unit == 'Hrs'
                  && $price->pricePerUnit->USD
                  && $this->isOnDemandInstance($price)
                ) {
                  if (isset($instance_products[$item->sku])) {
                    $instance_products[$item->sku]->price = floatval($price->pricePerUnit->USD);
                    $found = TRUE;
                    break;
                  }
                }
              }

              if ($found) {
                break;
              }
            }
          }

          // Add reserved instance price.
          foreach ($pricing->terms->Reserved as $term) {

            $items = array_values((array) $term);
            foreach ($items as $item) {
              if (!isset($item->termAttributes->LeaseContractLength)) {
                continue;
              }

              if ($item->termAttributes->LeaseContractLength != '1yr'
                && $item->termAttributes->LeaseContractLength != '3yr'
              ) {
                continue;
              }

              if ($item->termAttributes->OfferingClass != 'standard'
                || $item->termAttributes->PurchaseOption != 'All Upfront'
              ) {
                continue;
              }

              $prices = array_values((array) $item->priceDimensions);

              $found = FALSE;
              foreach ($prices as $price) {
                if ($price
                  && $price->unit == 'Quantity'
                  && $price->pricePerUnit->USD
                ) {
                  if (isset($instance_products[$item->sku])) {
                    if ($item->termAttributes->LeaseContractLength == '1yr') {
                      $property_name = 'one_year_price';
                    }
                    else {
                      $property_name = 'three_year_price';
                    }
                    $instance_products[$item->sku]->$property_name = floatval($price->pricePerUnit->USD);

                    break;
                  }
                }
              }
            }
          }

          uasort($instance_products, function ($a, $b) {
            $a_type = explode('.', $a->instanceType)[0];
            $b_type = explode('.', $b->instanceType)[0];
            if ($a_type < $b_type) {
              return -1;
            }
            elseif ($a_type > $b_type) {
              return 1;
            }

            return floatval($a->price) < floatval($b->price) ? -1 : 1;
          });

          foreach ($instance_products as $product) {
            $instance_types[$product->instanceType] = sprintf(
              '%s:%s:%s:%s:%s:%s:%s',
              $product->instanceType,
              $product->vcpu,
              $product->ecu,
              $product->memory,
              isset($product->price) ? $product->price * $price_rate : '',
              isset($product->one_year_price) ? $product->one_year_price * $price_rate : '',
              isset($product->three_year_price) ? $product->three_year_price * $price_rate : ''
            );
          }
        }
      }
      else {
        $this->messenger
          ->addError(t('Unable to set pricing endpoint for @cloud_context.  Cannot retrieve instance types.', [
            '@cloud_context' => $this->cloudContext,
          ]));
      }
    }
    catch (RequestException $e) {
      $this->messenger->addError(t('Error retrieving instance types.'));
      // Empty out the array so it does not get cached with empty values.
      $instance_types = [];
    }

    return $instance_types;
  }

  /**
   * Helper function to check whether the instance is on demand.
   *
   * @param object $price
   *   The price object.
   *
   * @return bool
   *   The result of check.
   */
  private function isOnDemandInstance($price) {
    $forbidden_words = ['Windows', 'Reservation', 'Dedicated'];
    foreach ($forbidden_words as $word) {
      if (strpos($price->description, $word) !== FALSE) {
        return FALSE;
      }
    }

    return TRUE;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc