cloud-8.x-2.0-beta1/modules/cloud_service_providers/k8s/src/Controller/ApiController.php
modules/cloud_service_providers/k8s/src/Controller/ApiController.php
<?php
namespace Drupal\k8s\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Database\Connection;
use Drupal\Component\Serialization\Yaml;
use Drupal\k8s\Service\K8sServiceInterface;
use Drupal\k8s\Entity\K8sNode;
use Drupal\k8s\Entity\K8sPod;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Controller responsible for "update" urls.
*
* This class is mainly responsible for
* updating the aws entities from urls.
*/
class ApiController extends ControllerBase implements ApiControllerInterface {
/**
* Drupal\Core\Entity\EntityTypeManagerInterface definition.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The K8s Service.
*
* @var \Drupal\k8s\Service\K8sServiceInterface
*/
private $k8sService;
/**
* The Messenger service.
*
* @var \Drupal\Core\Messenger\Messenger
*/
protected $messenger;
/**
* Request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
private $requestStack;
/**
* Renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
private $renderer;
/**
* The database service.
*
* @var \Drupal\Core\Database\Connection
*/
private $database;
/**
* ApiController constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\k8s\Service\K8sServiceInterface $k8s_service
* Object for interfacing with K8s API.
* @param \Drupal\Core\Messenger\Messenger $messenger
* Messanger Object.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* Request stack object.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
* @param \Drupal\Core\Database\Connection $database
* The database service.
*/
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
K8sServiceInterface $k8s_service,
Messenger $messenger,
RequestStack $request_stack,
RendererInterface $renderer,
Connection $database
) {
$this->entityTypeManager = $entity_type_manager;
$this->k8sService = $k8s_service;
$this->messenger = $messenger;
$this->requestStack = $request_stack;
$this->renderer = $renderer;
$this->database = $database;
}
/**
* Dependency Injection.
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('k8s'),
$container->get('messenger'),
$container->get('request_stack'),
$container->get('renderer'),
$container->get('database')
);
}
/**
* {@inheritdoc}
*/
public function updateAllResources($cloud_context) {
k8s_update_resources($cloud_context);
return $this->redirect("view.k8s_node.list", [
'cloud_context' => $cloud_context,
]);
}
/**
* {@inheritdoc}
*/
public function updateNodeList($cloud_context) {
return $this->updateEntityList('node', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateNamespaceList($cloud_context) {
return $this->updateEntityList('namespace', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updatePodList($cloud_context) {
return $this->updateEntityList('pod', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateNetworkPolicyList($cloud_context) {
return $this->updateEntityList('network_policy', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateDeploymentList($cloud_context) {
return $this->updateEntityList('deployment', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateReplicaSetList($cloud_context) {
return $this->updateEntityList('replica_set', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateServiceList($cloud_context) {
return $this->updateEntityList('service', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateCronJobList($cloud_context) {
return $this->updateEntityList('cron_job', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateJobList($cloud_context) {
return $this->updateEntityList('job', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateResourceQuotaList($cloud_context) {
return $this->updateEntityList('resource_quota', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateLimitRangeList($cloud_context) {
return $this->updateEntityList('limit_range', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateSecretList($cloud_context) {
return $this->updateEntityList('secret', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function updateConfigMapList($cloud_context) {
return $this->updateEntityList('config_map', $cloud_context);
}
/**
* {@inheritdoc}
*/
public function getNodeMetrics($cloud_context, K8sNode $k8s_node) {
$node_name = $k8s_node->getName();
$result = $this->database
->select('watchdog', 'w')
->fields('w', ['message', 'timestamp'])
->condition('type', 'k8s_metrics')
->condition('timestamp', time() - 7 * 24 * 60 * 60, '>=')
->condition('message', "%$node_name%", 'LIKE')
->orderBy('timestamp')
->execute();
$data = [];
foreach ($result as $record) {
$message = Yaml::decode($record->message);
if (empty($message[$cloud_context])) {
continue;
}
$cpu = NULL;
if (empty($message[$cloud_context]['nodes'][$node_name])) {
continue;
}
$cpu = $message[$cloud_context]['nodes'][$node_name]['cpu'];
$memory = $message[$cloud_context]['nodes'][$node_name]['memory'] ?? 0;
$data[] = [
'timestamp' => $record->timestamp,
'cpu' => $cpu,
'memory' => $memory,
];
}
return new JsonResponse($data);
}
/**
* {@inheritdoc}
*/
public function getPodMetrics($cloud_context, K8sPod $k8s_pod) {
$pod_name = $k8s_pod->getName();
$pod_namespace = $k8s_pod->getNamespace();
$result = $this->database
->select('watchdog', 'w')
->fields('w', ['message', 'timestamp'])
->condition('type', 'k8s_metrics')
->condition('timestamp', time() - 7 * 24 * 60 * 60, '>=')
->condition('message', "%$pod_namespace:$pod_name%", 'LIKE')
->orderBy('timestamp')
->execute();
$data = [];
foreach ($result as $record) {
$message = Yaml::decode($record->message);
if (empty($message[$cloud_context])) {
continue;
}
$cpu = NULL;
$key = "$pod_namespace:$pod_name";
if (empty($message[$cloud_context]['pods'][$key])) {
continue;
}
$cpu = $message[$cloud_context]['pods'][$key]['cpu'];
$memory = $message[$cloud_context]['pods'][$key]['memory'] ?? 0;
$data[] = [
'timestamp' => $record->timestamp,
'cpu' => $cpu,
'memory' => $memory,
];
}
return new JsonResponse($data);
}
/**
* {@inheritdoc}
*/
public function getNodeAllocatedResourcesList($cloud_context, K8sNode $k8s_node = NULL) {
return $this->getNodeAllocatedResources($cloud_context);
}
/**
* {@inheritdoc}
*/
public function getNodeAllocatedResources($cloud_context, K8sNode $k8s_node = NULL) {
$response = [];
$nodes = isset($k8s_node)
? $this->entityTypeManager->getStorage('k8s_node')
->loadByProperties([
'cloud_context' => $cloud_context,
'id' => $k8s_node->id(),
])
: $this->entityTypeManager->getStorage('k8s_node')
->loadByProperties([
'cloud_context' => $cloud_context,
]);
foreach ($nodes ?: [] as $node) {
if (isset($node)) {
$node_name = $node->getName();
$pod_resources = [];
$pods = $this->entityTypeManager->getStorage('k8s_pod')
->loadByProperties([
'cloud_context' => $cloud_context,
'node_name' => $node_name,
]);
foreach ($pods ?: [] as $pod) {
if (isset($pod)) {
$pod_resources[] = [
'name' => $pod->getName() ?: 'Unknown Pod Name',
'cpuUsage' => $pod->getCpuUsage(),
'memoryUsage' => $pod->getMemoryUsage(),
];
}
}
// Return a JSON object of K8s Node names, Pods capacity and Pods
// allocation to construct a Node heatmap to display the Pods allocation
// status by D3.js.
$response[] = [
// Node Name.
'name' => $node_name ?: 'Unknown Node Name',
// CPU usage info.
'cpuCapacity' => $node->getCpuCapacity(),
'cpuRequest' => $node->getCpuRequest(),
'cpuLimit' => $node->getCpuLimit(),
// Memory usage info.
'memoryCapacity' => $node->getMemoryCapacity(),
'memoryRequest' => $node->getMemoryRequest(),
'memoryLimit' => $node->getMemoryLimit(),
// Pods usage info.
'podsCapacity' => $node->getPodsCapacity(),
'podsAllocation' => $node->getPodsAllocation(),
'pods' => $pod_resources,
];
}
}
return new JsonResponse($response);
}
/**
* Helper method to update entities.
*
* @param string $entity_type_name
* The entity type name.
* @param string $cloud_context
* The cloud context.
*/
private function updateEntityList($entity_type_name, $cloud_context) {
$entity_type_name_capital = str_replace('_', '', ucwords($entity_type_name, '_'));
$entity_type_name_capital_plural = (preg_match('/y$/', $entity_type_name_capital))
? preg_replace('/y$/', 'ies', $entity_type_name_capital)
: ((preg_match('/s$|x$|ch$|sh$/', $entity_type_name_capital))
? $entity_type_name_capital . 'es'
: $entity_type_name_capital . 's');
$this->k8sService->setCloudContext($cloud_context);
$update_method_name = 'update' . $entity_type_name_capital_plural;
$updated = $this->k8sService->$update_method_name();
if ($updated !== FALSE) {
$this->messageUser($this->t('Updated @name.', ['@name' => $entity_type_name_capital_plural]));
}
else {
$this->messageUser($this->t('Unable to update @name.', ['@name' => $entity_type_name_capital_plural]), 'error');
}
return $this->redirect("view.k8s_$entity_type_name.list", [
'cloud_context' => $cloud_context,
]);
}
/**
* Helper method to add messages for the end user.
*
* @param string $message
* The message.
* @param string $type
* The message type: error or message.
*/
private function messageUser($message, $type = 'message') {
switch ($type) {
case 'error':
$this->messenger->addError($message);
break;
case 'message':
$this->messenger->addMessage($message);
default:
break;
}
}
}
