cloud-8.x-2.0-beta1/modules/cloud_service_providers/k8s/src/Plugin/Block/K8sNamespaceCostsBlock.php

modules/cloud_service_providers/k8s/src/Plugin/Block/K8sNamespaceCostsBlock.php
<?php

namespace Drupal\k8s\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Url;

use Drupal\k8s\Service\CostFieldsRendererInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a block of the costs.
 *
 * @Block(
 *   id = "k8s_namespace_costs",
 *   admin_label = @Translation("K8s Namespace Costs"),
 *   category = @Translation("K8s")
 * )
 */
class K8sNamespaceCostsBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * The entity.manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The route match service.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * The page cache kill switch service.
   *
   * @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch
   */
  protected $killSwitch;

  /**
   * The module handler service.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The cost fields renderer.
   *
   * @var \Drupal\k8s\Service\CostFieldsRendererInterface
   */
  protected $costFieldsRenderer;

  /**
   * Constructs a new K8sNodeHeatmapBlock instance.
   *
   * @param array $configuration
   *   The plugin configuration, i.e. an array with configuration values keyed
   *   by configuration option name. The special key 'context' may be used to
   *   initialize the defined contexts by setting it to an array of context
   *   values keyed by context names.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity.manager service.
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The route match service.
   * @param \Drupal\Core\PageCache\ResponsePolicy\KillSwitch $kill_switch
   *   The page cache kill switch service.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler service.
   * @param \Drupal\k8s\Service\CostFieldsRendererInterface $cost_fields_renderer
   *   The cost fields renderer.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    EntityTypeManagerInterface $entity_type_manager,
    RouteMatchInterface $route_match,
    KillSwitch $kill_switch,
    ModuleHandlerInterface $module_handler,
    CostFieldsRendererInterface $cost_fields_renderer
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);

    $this->entityTypeManager = $entity_type_manager;
    $this->routeMatch = $route_match;
    $this->killSwitch = $kill_switch;
    $this->moduleHandler = $module_handler;
    $this->costFieldsRenderer = $cost_fields_renderer;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition
  ) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_type.manager'),
      $container->get('current_route_match'),
      $container->get('page_cache_kill_switch'),
      $container->get('module_handler'),
      $container->get('k8s.cost_fields_renderer')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      'aws_cloud_ec2_cost_type' => 'ri_one_year',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function blockForm($form, FormStateInterface $form_state) {
    $options = [
      'on_demand_yearly'  => $this->t('On-demand Yearly ($)'),
      'ri_one_year'       => $this->t('RI 1 Year ($)'),
      'ri_three_year'     => $this->t('RI 3 Year ($)'),
    ];
    $form['aws_cloud_ec2_cost_type'] = [
      '#type' => 'select',
      '#title' => $this->t('AWS Cloud EC2 Cost Type'),
      '#options' => $options,
      '#default_value' => $this->configuration['aws_cloud_ec2_cost_type'],
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['aws_cloud_ec2_cost_type']
      = $form_state->getValue('aws_cloud_ec2_cost_type');
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $build = [];

    $cloud_context = $this->routeMatch->getParameter('cloud_context');
    if (empty($cloud_context)) {
      return $build;
    }

    $nodes = $this->entityTypeManager
      ->getStorage('k8s_node')->loadByProperties([
        'cloud_context' => $cloud_context,
      ]
    );

    $total_costs = $this->getTotalCosts($nodes);

    // $cpu_capacity = $this->getCpuCapacity($nodes);
    $cpu_capacity = array_sum(array_map(function($node) {
      return $node->getCpuCapacity();
    }, $nodes));

    $memory_capacity = array_sum(array_map(function($node) {
      return $node->getMemoryCapacity();
    }, $nodes));

    $pod_capacity = array_sum(array_map(function($node) {
      return $node->getPodsCapacity();
    }, $nodes));

    $namespaces = $this->entityTypeManager
      ->getStorage('k8s_namespace')->loadByProperties([
        'cloud_context' => $cloud_context,
      ]
    );

    // Get row data.
    $rows = [];
    foreach ($namespaces as $namespace) {
      $pods = $this->entityTypeManager
        ->getStorage('k8s_pod')->loadByProperties([
          'cloud_context' => $namespace->getCloudContext(),
          'namespace' => $namespace->getName(),
        ]
      );

      $row['namespace'] = [
        'data' => [
          '#type' => 'link',
          '#title' => $namespace->getName(),
          '#url' => Url::fromRoute(
            'entity.k8s_namespace.canonical',
            [
              'cloud_context' => $cloud_context,
              'k8s_namespace' => $namespace->id(),
            ]
          ),
        ]
      ];

      $cpu_usage = array_sum(array_map(function($pod) {
        return $pod->getCpuUsage();
      }, $pods));

      $memory_usage = array_sum(array_map(function($pod) {
        return $pod->getMemoryUsage();
      }, $pods));

      $pod_usage = count($pods);

      $costs = ($cpu_usage / $cpu_capacity + $memory_usage / $memory_capacity + $pod_usage / $pod_capacity) / 3 * $total_costs;

      $row['costs'] = $this->formatCosts($costs, $total_costs);
      $row['cpu_usage'] = $this->formatCpuUsage($cpu_usage, $cpu_capacity);
      $row['memory_usage'] = $this->formatMemoryUsage($memory_usage, $memory_capacity);
      $row['pod_usage'] = $this->formatPodUsage($pod_usage, $pod_capacity);

      $rows[] = $row;
    }

    $table = [
      '#theme' => 'table',
      '#header' => [
        $this->t('Namespace'),
        $this->t('Total Cost ($)'),
        $this->t('CPU (Usage)'),
        $this->t('Memory (Usage)'),
        $this->t('Pods Allocation'),
      ],
      '#rows' => $rows,
    ];

    $build['k8s_namespace_costs'] = [
      '#type' => 'details',
      '#title' => $this->t('Namespace Costs'),
      '#open' => TRUE,
    ];

    $build['k8s_namespace_costs'][] = $table;

    return $build;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheMaxAge() {
    $this->killSwitch->trigger();
    // BigPipe/#cache/max-age is breaking my block javascript
    // https://www.drupal.org/forum/support/module-development-and-code-questions/2016-07-17/bigpipecachemax-age-is-breaking-my
    // "a slight delay of a second or two before the charts are built.
    // That seems to work, though it is a janky solution.".
    return $this->moduleHandler->moduleExists('big_pipe') ? 1 : 0;
  }

  /**
   * Format costs.
   *
   * @param int $costs
   *   The costs.
   * @param int $total_costs
   *   The total costs.
   *
   * @return string
   *   The formatted costs string.
   */
  private function formatCosts($costs, $total_costs) {
    $costs_str = round($costs, 2);
    $percentage = round($costs / $total_costs * 100, 2);
    return "$costs_str ($percentage%)";
  }

  /**
   * Format cpu usage.
   *
   * @param float $cpu_usage
   *   The cpu usage.
   * @param float $cpu_capacity
   *   The cpu capacity.
   *
   * @return string
   *   The formatted cpu usage string.
   */
  private function formatCpuUsage($cpu_usage, $cpu_capacity) {
    $cpu_str = round($cpu_usage, 2);
    $percentage = round($cpu_usage / $cpu_capacity * 100, 2);
    return "$cpu_str ($percentage%)";
  }

  /**
   * Format memory usage.
   *
   * @param int $memory_usage
   *   The memory usage.
   * @param int $memory_capacity
   *   The memory capacity.
   *
   * @return string
   *   The formatted memory usage string.
   */
  private function formatMemoryUsage($memory_usage, $memory_capacity) {
    $memory_str = k8s_format_memory($memory_usage);
    $percentage = round($memory_usage / $memory_capacity * 100, 2);
    return "$memory_str ($percentage%)";
  }

  /**
   * Format pod usage.
   *
   * @param int $pod_usage
   *   The pod usage.
   * @param int $pod_capacity
   *   The pod capacity.
   *
   * @return string
   *   The formatted pod usage string.
   */
  private function formatPodUsage($pod_usage, $pod_capacity) {
    $percentage = round($pod_usage / $pod_capacity * 100, 2);
    return "$pod_usage/$pod_capacity ($percentage%)";
  }

  /**
   * Get total costs of nodes.
   *
   * @param array $nodes
   *   The k8s_node entities.
   *
   * @return int
   *   The total costs of nodes.
   */
  private function getTotalCosts(array $nodes) {
    $costs = 0;

    if (!$this->moduleHandler->moduleExists('aws_cloud')) {
      return $costs;
    }

    $price_date_provider = \Drupal::service('aws_cloud.instance_type_price_data_provider');

    foreach ($nodes as $node) {
      // Get instance type and region.
      $region = NULL;
      $instance_type = NULL;
      foreach ($node->get('labels') as $item) {
        if ($item->getItemKey() === 'beta.kubernetes.io/instance-type') {
          $instance_type = $item->getItemValue();
        }
        elseif ($item->getItemKey() === 'failure-domain.beta.kubernetes.io/region') {
          $region = $item->getItemValue();
        }
      }

      if (empty($instance_type) || empty($region)) {
        continue;
      }

      $price_data = $price_date_provider->getDataByRegion($region);
      foreach ($price_data as $item) {
        if ($item['instance_type'] == $instance_type) {
          $cost_type = $this->configuration['aws_cloud_ec2_cost_type'];
          if (!empty($item[$cost_type])) {
            if ($cost_type == 'ri_three_year') {
              $costs += $item[$cost_type] / 3;
            }
            else {
              $costs += $item[$cost_type];
            }
          }
          break;
        }
      }
    }

    return $costs;
  }

}

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

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