diff options
Diffstat (limited to '')
-rw-r--r-- | library/Cube/CubeRenderer.php | 406 | ||||
-rw-r--r-- | library/Cube/CubeRenderer/HostStatusCubeRenderer.php | 105 | ||||
-rw-r--r-- | library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php | 126 |
3 files changed, 637 insertions, 0 deletions
diff --git a/library/Cube/CubeRenderer.php b/library/Cube/CubeRenderer.php new file mode 100644 index 0000000..3f8c80d --- /dev/null +++ b/library/Cube/CubeRenderer.php @@ -0,0 +1,406 @@ +<?php + +// Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2 + +namespace Icinga\Module\Cube; + +use Icinga\Web\View; + +/** + * CubeRenderer base class + * + * Every Cube Renderer must extend this class. + * + * TODO: Should we introduce DimensionRenderer, FactRenderer and SummaryHelper + * instead? + * + * @package Icinga\Module\Cube + */ +abstract class CubeRenderer +{ + /** @var View */ + protected $view; + + /** @var Cube */ + protected $cube; + + /** @var array Our dimensions */ + protected $dimensions; + + /** @var array Our dimensions in regular order */ + protected $dimensionOrder; + + /** @var array Our dimensions in reversed order as a quick lookup source */ + protected $reversedDimensions; + + /** @var array Level (deepness) for each dimension (0, 1, 2...) */ + protected $dimensionLevels; + + protected $facts; + + /** @var object The row before the current one */ + protected $lastRow; + + /** + * Current summaries + * + * This is an object of objects, with dimension names being the keys and + * a facts row containing current (rollup) summaries for that dimension + * being it's value + * + * @var object + */ + protected $summaries; + + protected $started; + + /** + * CubeRenderer constructor. + * + * @param Cube $cube + */ + public function __construct(Cube $cube) + { + $this->cube = $cube; + } + + /** + * Render the given facts + * + * @param $facts + * @return string + */ + abstract public function renderFacts($facts); + + /** + * Returns the base url for the details action + * + * @return string + */ + abstract protected function getDetailsBaseUrl(); + + /** + * Initialize all we need + */ + protected function initialize() + { + $this->started = false; + $this->initializeDimensions() + ->initializeFacts() + ->initializeLastRow() + ->initializeSummaries(); + } + + /** + * @return $this + */ + protected function initializeLastRow() + { + $object = (object) array(); + foreach ($this->dimensions as $dimension) { + $object->{$dimension->getName()} = null; + } + + $this->lastRow = $object; + + return $this; + } + + /** + * @return $this + */ + protected function initializeDimensions() + { + $this->dimensions = $this->cube->listDimensions(); + + $min = 3; + $cnt = count($this->dimensions); + if ($cnt < $min) { + $pos = 0; + $diff = $min - $cnt; + $this->dimensionOrder = []; + foreach ($this->dimensions as $name => $_) { + $this->dimensionOrder[$pos++ + $diff] = $name; + } + } else { + $this->dimensionOrder = array_keys($this->dimensions); + } + + $this->reversedDimensions = array_reverse($this->dimensionOrder); + $this->dimensionLevels = array_flip($this->dimensionOrder); + return $this; + } + + /** + * @return $this + */ + protected function initializeFacts() + { + $this->facts = $this->cube->listFacts(); + return $this; + } + + /** + * @return $this + */ + protected function initializeSummaries() + { + $this->summaries = (object) array(); + return $this; + } + + /** + * @param object $row + * @return bool + */ + protected function startsDimension($row) + { + foreach ($this->dimensionOrder as $name) { + if ($row->$name === null) { + $this->summaries->$name = $this->extractFacts($row); + return true; + } + } + + return false; + } + + /** + * @param $row + * @return object + */ + protected function extractFacts($row) + { + $res = (object) array(); + + foreach ($this->facts as $fact) { + $res->$fact = $row->$fact; + } + + return $res; + } + + public function render(View $view) + { + $this->view = $view; + $this->initialize(); + $htm = $this->beginContainer(); + foreach ($this->cube->fetchAll() as $row) { + $htm .= $this->renderRow($row); + } + + return $htm . $this->closeDimensions() . $this->endContainer(); + } + + protected function renderRow($row) + { + $htm = ''; + if ($dimension = $this->startsDimension($row)) { + return $htm; + } + + $htm .= $this->closeDimensionsForRow($row); + $htm .= $this->beginDimensionsForRow($row); + $htm .= $this->renderFacts($row); + $this->lastRow = $row; + return $htm; + } + + protected function beginDimensionsForRow($row) + { + $last = $this->lastRow; + foreach ($this->dimensionOrder as $name) { + if ($last->$name !== $row->$name) { + return $this->beginDimensionsUpFrom($name, $row); + } + } + + return ''; + } + + protected function beginDimensionsUpFrom($dimension, $row) + { + $htm = ''; + $found = false; + + foreach ($this->dimensionOrder as $name) { + if ($name === $dimension) { + $found = true; + } + + if ($found) { + $htm .= $this->beginDimension($name, $row); + } + } + + return $htm; + } + + protected function closeDimensionsForRow($row) + { + $last = $this->lastRow; + foreach ($this->dimensionOrder as $name) { + if ($last->$name !== $row->$name) { + return $this->closeDimensionsDownTo($name); + } + } + + return ''; + } + + protected function closeDimensionsDownTo($name) + { + $htm = ''; + + foreach ($this->reversedDimensions as $dimension) { + $htm .= $this->closeDimension($dimension); + + if ($name === $dimension) { + break; + } + } + + return $htm; + } + + protected function closeDimensions() + { + $htm = ''; + foreach ($this->reversedDimensions as $name) { + $htm .= $this->closeDimension($name); + } + + return $htm; + } + + protected function closeDimension($name) + { + if (! $this->started) { + return ''; + } + + $indent = $this->getIndent($name); + return $indent . ' </div>' . "\n" . $indent . "</div><!-- $name -->\n"; + } + + protected function getIndent($name) + { + return str_repeat(' ', $this->getLevel($name)); + } + + protected function beginDimension($name, $row) + { + $indent = $this->getIndent($name); + if (! $this->started) { + $this->started = true; + } + $view = $this->view; + $dimension = $this->cube->getDimension($name); + + return + $indent . '<div class="' + . $this->getDimensionClassString($name, $row) + . '">' . "\n" + . $indent . ' <div class="header"><a href="' + . $this->getDetailsUrl($name, $row) + . '" title="' . $view->escape(sprintf('Show details for %s: %s', $dimension->getLabel(), $row->$name)) . '"' + . ' data-base-target="_next">' + . $this->renderDimensionLabel($name, $row) + . '</a><a class="icon-filter" href="' + . $this->getSliceUrl($name, $row) + . '" title="' . $view->escape('Slice this cube') . '"></a></div>' . "\n" + . $indent . ' <div class="body">' . "\n"; + } + + /** + * Render the label for a given dimension name + * + * To have some context available, also + * + * @param $name + * @param $row + * @return string + */ + protected function renderDimensionLabel($name, $row) + { + $caption = $row->$name; + if (empty($caption)) { + $caption = '_'; + } + + return $this->view->escape($caption); + } + + protected function getDetailsUrl($name, $row) + { + $cube = $this->cube; + + $dimensions = array_merge(array_keys($cube->listDimensions()), $cube->listSlices()); + $params = [ + 'dimensions' => DimensionParams::update($dimensions)->getParams() + ]; + + foreach ($this->cube->listDimensionsUpTo($name) as $dimensionName) { + $params[$dimensionName] = $row->$dimensionName; + } + + foreach ($this->cube->getSlices() as $key => $val) { + $params[$key] = $val; + } + + return $this->view->url( + $this->getDetailsBaseUrl(), + $params + ); + } + + protected function getSliceUrl($name, $row) + { + return $this->view->url() + ->setParam($name, $row->$name); + } + + protected function isOuterDimension($name) + { + return $this->reversedDimensions[0] !== $name; + } + + protected function getDimensionClassString($name, $row) + { + return implode(' ', $this->getDimensionClasses($name, $row)); + } + + protected function getDimensionClasses($name, $row) + { + return array('cube-dimension' . $this->getLevel($name)); + } + + protected function getLevel($name) + { + return $this->dimensionLevels[$name]; + } + + /** + * @return string + */ + protected function beginContainer() + { + return '<div class="cube">' . "\n"; + } + + /** + * @return string + */ + protected function endContainer() + { + return '</div>' . "\n"; + } + + /** + * Well... just to be on the safe side + */ + public function __destruct() + { + unset($this->cube); + } +} diff --git a/library/Cube/CubeRenderer/HostStatusCubeRenderer.php b/library/Cube/CubeRenderer/HostStatusCubeRenderer.php new file mode 100644 index 0000000..89322cb --- /dev/null +++ b/library/Cube/CubeRenderer/HostStatusCubeRenderer.php @@ -0,0 +1,105 @@ +<?php + +// Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2 + +namespace Icinga\Module\Cube\CubeRenderer; + +use Icinga\Module\Cube\CubeRenderer; + +class HostStatusCubeRenderer extends CubeRenderer +{ + protected function renderDimensionLabel($name, $row) + { + $htm = parent::renderDimensionLabel($name, $row); + + if (($next = $this->cube->getDimensionAfter($name)) && isset($this->summaries->{$next->getName()})) { + $htm .= ' <span class="sum">(' . $this->summaries->{$next->getName()}->hosts_cnt . ')</span>'; + } + + return $htm; + } + + protected function getDimensionClasses($name, $row) + { + $classes = parent::getDimensionClasses($name, $row); + + $sums = $row; + if ($sums->hosts_down > 0) { + $classes[] = 'critical'; + if ((int) $sums->hosts_unhandled_down === 0) { + $classes[] = 'handled'; + } + } elseif ($sums->hosts_unreachable > 0) { + $classes[] = 'unreachable'; + if ((int) $sums->hosts_unhandled_unreachable === 0) { + $classes[] = 'handled'; + } + } else { + $classes[] = 'ok'; + } + + return $classes; + } + + public function renderFacts($facts) + { + $indent = str_repeat(' ', 3); + $parts = array(); + + if ($facts->hosts_unhandled_down > 0) { + $parts['critical'] = $facts->hosts_unhandled_down; + } + + if ($facts->hosts_down > 0 && $facts->hosts_down > $facts->hosts_unhandled_down) { + $parts['critical handled'] = $facts->hosts_down - $facts->hosts_unhandled_down; + } + + if ($facts->hosts_unhandled_unreachable > 0) { + $parts['unreachable'] = $facts->hosts_unhandled_unreachable; + } + + if ($facts->hosts_unreachable > 0 && $facts->hosts_unreachable > $facts->hosts_unhandled_unreachable) { + $parts['unreachable handled'] = $facts->hosts_unreachable - $facts->hosts_unhandled_unreachable; + } + + if ($facts->hosts_cnt > $facts->hosts_down && $facts->hosts_cnt > $facts->hosts_unreachable) { + $parts['ok'] = $facts->hosts_cnt - $facts->hosts_down - $facts->hosts_unreachable; + } + + $main = ''; + $sub = ''; + foreach ($parts as $class => $count) { + if ($main === '') { + $main = $this->makeBadgeHtml($class, $count); + } else { + $sub .= $this->makeBadgeHtml($class, $count); + } + } + if ($sub !== '') { + $sub = $indent + . '<span class="others">' + . "\n " + . $sub + . $indent + . "</span>\n"; + } + + return $main . $sub; + } + + protected function makeBadgeHtml($class, $count) + { + $indent = str_repeat(' ', 3); + return sprintf( + '%s<span class="%s">%s</span>', + $indent, + $class, + $count + ) . "\n"; + } + + protected function getDetailsBaseUrl() + { + return 'cube/hosts/details'; + } +} diff --git a/library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php b/library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php new file mode 100644 index 0000000..33caa84 --- /dev/null +++ b/library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php @@ -0,0 +1,126 @@ +<?php + +// Icinga Web 2 Cube Module | (c) 2019 Icinga GmbH | GPLv2 + +namespace Icinga\Module\Cube\CubeRenderer; + +use Icinga\Module\Cube\CubeRenderer; + +class ServiceStatusCubeRenderer extends CubeRenderer +{ + public function renderFacts($facts) + { + $indent = str_repeat(' ', 3); + $parts = []; + + if ($facts->services_unhandled_critical > 0) { + $parts['critical'] = $facts->services_unhandled_critical; + } + + if ($facts->services_critical > 0 && $facts->services_critical > $facts->services_unhandled_critical) { + $parts['critical handled'] = $facts->services_critical - $facts->services_unhandled_critical; + } + + if ($facts->services_unhandled_warning > 0) { + $parts['warning'] = $facts->services_unhandled_warning; + } + + if ($facts->services_warning > 0 && $facts->services_warning > $facts->services_unhandled_warning) { + $parts['warning handled'] = $facts->services_warning - $facts->services_unhandled_warning; + } + + if ($facts->services_unhandled_unknown > 0) { + $parts['unknown'] = $facts->services_unhandled_unknown; + } + + if ($facts->services_unknown > 0 && $facts->services_unknown > $facts->services_unhandled_unknown) { + $parts['unknown handled'] = $facts->services_unknown - $facts->services_unhandled_unknown; + } + + if ( + $facts->services_cnt > $facts->services_critical && $facts->services_cnt > $facts->services_warning + && $facts->services_cnt > $facts->services_unknown + ) { + $parts['ok'] = $facts->services_cnt - $facts->services_critical - $facts->services_warning - + $facts->services_unknown; + } + + $main = ''; + $sub = ''; + foreach ($parts as $class => $count) { + if ($main === '') { + $main = $this->makeBadgeHtml($class, $count); + } else { + $sub .= $this->makeBadgeHtml($class, $count); + } + } + if ($sub !== '') { + $sub = $indent + . '<span class="others">' + . "\n " + . $sub + . $indent + . "</span>\n"; + } + + return $main . $sub; + } + + /** + * @inheritdoc + */ + protected function renderDimensionLabel($name, $row) + { + $htm = parent::renderDimensionLabel($name, $row); + + if (($next = $this->cube->getDimensionAfter($name)) && isset($this->summaries->{$next->getName()})) { + $htm .= ' <span class="sum">(' . $this->summaries->{$next->getName()}->services_cnt . ')</span>'; + } + + return $htm; + } + + protected function getDimensionClasses($name, $row) + { + $classes = parent::getDimensionClasses($name, $row); + + $sums = $row; + if ($sums->services_critical > 0) { + $classes[] = 'critical'; + if ((int) $sums->services_unhandled_critical === 0) { + $classes[] = 'handled'; + } + } elseif ($sums->services_warning > 0) { + $classes[] = 'warning'; + if ((int) $sums->services_unhandled_warning === 0) { + $classes[] = 'handled'; + } + } elseif ($sums->services_unknown > 0) { + $classes[] = 'unknown'; + if ((int) $sums->services_unhandled_unknown === 0) { + $classes[] = 'handled'; + } + } else { + $classes[] = 'ok'; + } + + return $classes; + } + + protected function makeBadgeHtml($class, $count) + { + $indent = str_repeat(' ', 3); + + return sprintf( + '%s<span class="%s">%s</span>', + $indent, + $class, + $count + ) . "\n"; + } + + protected function getDetailsBaseUrl() + { + return 'cube/services/details'; + } +} |