diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:21:16 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:21:16 +0000 |
commit | 2e582fe0b8b6a8e67982ddb84935db1bd3b401fe (patch) | |
tree | dd511b321f308264952cffb005a4288ea4e478e6 /library/Graphite/Web/Widget | |
parent | Initial commit. (diff) | |
download | icingaweb2-module-graphite-2e582fe0b8b6a8e67982ddb84935db1bd3b401fe.tar.xz icingaweb2-module-graphite-2e582fe0b8b6a8e67982ddb84935db1bd3b401fe.zip |
Adding upstream version 1.2.2.upstream/1.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/Graphite/Web/Widget')
-rw-r--r-- | library/Graphite/Web/Widget/GraphImage.php | 143 | ||||
-rw-r--r-- | library/Graphite/Web/Widget/Graphs.php | 688 | ||||
-rw-r--r-- | library/Graphite/Web/Widget/Graphs/Host.php | 51 | ||||
-rw-r--r-- | library/Graphite/Web/Widget/Graphs/Icingadb/IcingadbHost.php | 61 | ||||
-rw-r--r-- | library/Graphite/Web/Widget/Graphs/Icingadb/IcingadbService.php | 71 | ||||
-rw-r--r-- | library/Graphite/Web/Widget/Graphs/Service.php | 56 | ||||
-rw-r--r-- | library/Graphite/Web/Widget/IcingadbGraphs.php | 106 | ||||
-rw-r--r-- | library/Graphite/Web/Widget/InlineGraphImage.php | 49 |
8 files changed, 1225 insertions, 0 deletions
diff --git a/library/Graphite/Web/Widget/GraphImage.php b/library/Graphite/Web/Widget/GraphImage.php new file mode 100644 index 0000000..af64e69 --- /dev/null +++ b/library/Graphite/Web/Widget/GraphImage.php @@ -0,0 +1,143 @@ +<?php + +namespace Icinga\Module\Graphite\Web\Widget; + +use Icinga\Module\Graphite\Graphing\Chart; +use Icinga\Web\Url; +use Icinga\Web\UrlParams; +use Icinga\Web\Widget\AbstractWidget; +use RuntimeException; + +class GraphImage extends AbstractWidget +{ + /** + * The chart to be rendered + * + * @var Chart + */ + protected $chart; + + /** + * The rendered PNG image + * + * @var string|null + */ + protected $rendered; + + /** + * Constructor + * + * @param Chart $chart The chart to be rendered + */ + public function __construct(Chart $chart) + { + $this->chart = $chart; + } + + /** + * Render the graph lazily + * + * @return string + */ + public function render() + { + if ($this->rendered === null) { + $now = time(); + + $from = (int) $this->chart->getFrom(); + if ($from < 0) { + $from += $now; + } + + $until = (string) $this->chart->getUntil(); + + if ($until === '') { + $until = $now; + } else { + $until = (int) $until; + if ($until < 0) { + $until += $now; + } + } + + $variables = $this->chart->getMetricVariables(); + $template = $this->chart->getTemplate(); + $graphiteWebClient = $this->chart->getGraphiteWebClient(); + $params = (new UrlParams())->addValues([ + 'from' => $from, + 'until' => $until, + 'bgcolor' => $this->chart->getBackgroundColor() ?? 'black', + 'fgcolor' => $this->chart->getForegroundColor() ?? 'white', + 'majorGridLineColor' => $this->chart->getMajorGridLineColor() ?? '0000003F', + 'minorGridLineColor' => $this->chart->getMinorGridLineColor() ?? 'black', + 'width' => $this->chart->getWidth(), + 'height' => $this->chart->getHeight(), + 'hideLegend' => (string) ! $this->chart->getShowLegend(), + 'tz' => date_default_timezone_get(), + '_salt' => "$now.000", + 'vTitle' => 'Percent', + 'lineMode' => 'connected', + 'drawNullAsZero' => 'false', + 'graphType' => 'line', + '_ext' => 'whatever.svg' + ]); + + foreach ($template->getUrlParams() as $key => $value) { + $params->set($key, $value->resolve($variables)); + } + + $metrics = $this->chart->getMetrics(); + $allVars = []; + + foreach ($template->getCurves() as $curveName => $curve) { + if (!isset($metrics[$curveName])) { + continue; + } + + $vars = $curve[0]->reverseResolve($metrics[$curveName]); + + if ($vars !== false) { + $allVars = array_merge($allVars, $vars); + } + } + + foreach ($metrics as $curveName => $metric) { + $allVars['metric'] = $metric; + $params->add('target', $template->getCurves()[$curveName][1]->resolve($allVars)); + } + + $url = Url::fromPath('/render')->setParams($params); + $headers = [ + 'Accept-language' => 'en', + 'Content-type' => 'application/x-www-form-urlencoded' + ]; + + for (;;) { + try { + $this->rendered = $graphiteWebClient->request($url, 'GET', $headers); + } catch (RuntimeException $e) { + if (preg_match('/\b500\b/', $e->getMessage())) { + // A 500 Internal Server Error, probably because of + // a division by zero because of a too low time range to render. + + $until = (int) $url->getParam('until'); + $diff = $until - (int) $url->getParam('from'); + + // Try to render a higher time range, but give up + // once our default (1h) has been reached (non successfully). + if ($diff < 3600) { + $url->setParam('from', $until - $diff * 2); + continue; + } + } + + throw $e; + } + + break; + } + } + + return $this->rendered; + } +} diff --git a/library/Graphite/Web/Widget/Graphs.php b/library/Graphite/Web/Widget/Graphs.php new file mode 100644 index 0000000..e18b8da --- /dev/null +++ b/library/Graphite/Web/Widget/Graphs.php @@ -0,0 +1,688 @@ +<?php + +namespace Icinga\Module\Graphite\Web\Widget; + +use Icinga\Application\Config; +use Icinga\Application\Icinga; +use Icinga\Authentication\Auth; +use Icinga\Exception\ConfigurationError; +use Icinga\Module\Graphite\Graphing\Chart; +use Icinga\Module\Graphite\Graphing\GraphingTrait; +use Icinga\Module\Graphite\Graphing\Template; +use Icinga\Module\Graphite\Util\InternalProcessTracker as IPT; +use Icinga\Module\Graphite\Util\TimeRangePickerTools; +use Icinga\Module\Graphite\Web\Widget\Graphs\Host as HostGraphs; +use Icinga\Module\Graphite\Web\Widget\Graphs\Service as ServiceGraphs; +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Module\Monitoring\Object\MonitoredObject; +use Icinga\Module\Monitoring\Object\Service; +use Icinga\Web\Request; +use Icinga\Web\Url; +use Icinga\Web\View; +use Icinga\Web\Widget\AbstractWidget; +use ipl\Orm\Model; +use Icinga\Module\Icingadb\Model\Host as IcingadbHost; +use Icinga\Module\Graphite\Web\Widget\Graphs\Icingadb\IcingadbHost as IcingadbHostGraphs; +use Icinga\Module\Graphite\Web\Widget\Graphs\Icingadb\IcingadbService as IcingadbServiceGraphs; + +abstract class Graphs extends AbstractWidget +{ + use GraphingTrait; + + /** + * The Icinga custom variable with the "real" check command (if any) of objects we display graphs for + * + * @var string + */ + protected static $obscuredCheckCommandCustomVar; + + /** + * The type of the object to render the graphs for + * + * @var string + */ + protected $objectType; + + /** + * The object to render the graphs for + * + * @var MonitoredObject|Model + */ + protected $object; + + /** + * Graph image width + * + * @var string + */ + protected $width = '350'; + + /** + * Graph image height + * + * @var string + */ + protected $height = '200'; + + /** + * Graph range start + * + * @var string + */ + protected $start; + + /** + * Graph range end + * + * @var string + */ + protected $end; + + /** + * Whether to render as compact as possible + * + * @var bool + */ + protected $compact = false; + + /** + * The check command of the monitored object we display graphs for + * + * @var string + */ + protected $checkCommand; + + /** + * The "real" check command (if any) of the monitored object we display graphs for + * + * E.g. the command executed remotely via check_by_ssh + * + * @var string|null + */ + protected $obscuredCheckCommand; + + /** + * Additional CSS classes for the <div/>s around the images + * + * @var string[] + */ + protected $classes = []; + + /** + * Whether to serve a transparent dummy image first and let the JS code load the actual graph + * + * @var bool + */ + protected $preloadDummy = false; + + /** + * Whether to render the graphs inline + * + * @var bool + */ + protected $renderInline; + + /** + * Whether to explicitly display that no graphs were found + * + * @var bool|null + */ + protected $showNoGraphsFound; + + /** + * Factory, based on the given monitoring object + * + * @param MonitoredObject $object + * + * @return static + */ + public static function forMonitoredObject(MonitoredObject $object) + { + switch ($object->getType()) { + case 'host': + /** @var Host $object */ + return new HostGraphs($object); + + case 'service': + /** @var Service $object */ + return new ServiceGraphs($object); + } + } + + /** + * Factory, based on the given icingadb object + * + * @param Model $object + * + * @return static + */ + public static function forIcingadbObject(Model $object) + { + if ($object instanceof IcingadbHost) { + return new IcingadbHostGraphs($object); + } + + return new IcingadbServiceGraphs($object); + } + + /** + * Get the Icinga custom variable with the "real" check command (if any) of monitored objects we display graphs for + * + * @return string + */ + public static function getObscuredCheckCommandCustomVar() + { + if (static::$obscuredCheckCommandCustomVar === null) { + static::$obscuredCheckCommandCustomVar = Config::module('graphite') + ->get('icinga', 'customvar_obscured_check_command', 'check_command'); + } + + return static::$obscuredCheckCommandCustomVar; + } + + /** + * Constructor + * + * @param MonitoredObject|Model $object The object to render the graphs for + */ + public function __construct($object) + { + $this->object = $object; + $this->renderInline = Url::fromRequest()->getParam('format') === 'pdf'; + + if ($object instanceof Model) { + $this->checkCommand = $object->checkcommand_name; + $this->obscuredCheckCommand = $object->vars[Graphs::getObscuredCheckCommandCustomVar()] ?? null; + } else { + $this->checkCommand = $object->{"{$this->objectType}_check_command"}; + $this->obscuredCheckCommand = $object->{ + "_{$this->objectType}_" . Graphs::getObscuredCheckCommandCustomVar() + }; + } + } + + /** + * Process the given request using this widget + * + * @param Request $request The request to be processed + * + * @return $this + */ + public function handleRequest(Request $request = null) + { + if ($request === null) { + $request = Icinga::app()->getRequest(); + } + + $params = $request->getUrl()->getParams(); + list($this->start, $this->end) = $this->getRangeFromTimeRangePicker($request); + $this->width = $params->shift('width', $this->width); + $this->height = $params->shift('height', $this->height); + + return $this; + } + + /** + * Render the graphs list + * + * @return string + */ + protected function getGraphsList() + { + $result = []; // kind of string builder + $imageBaseUrl = $this->getImageBaseUrl(); + $allTemplates = $this->getAllTemplates(); + $actualCheckCommand = $this->obscuredCheckCommand === null ? $this->checkCommand : $this->obscuredCheckCommand; + $concreteTemplates = $allTemplates->getTemplates($actualCheckCommand); + + $excludedMetrics = []; + + foreach ($concreteTemplates as $concreteTemplate) { + foreach ($concreteTemplate->getCurves() as $curve) { + $excludedMetrics[] = $curve[0]; + } + } + + IPT::recordf("Icinga check command: %s", $this->checkCommand); + IPT::recordf("Obscured check command: %s", $this->obscuredCheckCommand); + + foreach ( + [ + ['template', $concreteTemplates, []], + ['default_template', $allTemplates->getDefaultTemplates(), $excludedMetrics], + ] as $templateSet + ) { + list($urlParam, $templates, $excludeMetrics) = $templateSet; + + if ($urlParam === 'template') { + IPT::recordf('Applying templates for check command %s', $actualCheckCommand); + } else { + IPT::recordf('Applying default templates, excluding previously used metrics'); + } + + IPT::indent(); + + foreach ($templates as $templateName => $template) { + if ($this->designedForObjectType($template)) { + IPT::recordf('Applying template %s', $templateName); + IPT::indent(); + + $charts = $template->getCharts( + static::getMetricsDataSource(), + $this->object, + [], + $excludeMetrics + ); + + if (! empty($charts)) { + $currentGraphs = []; + + foreach ($charts as $chart) { + /** @var Chart $chart */ + + $metricVariables = $chart->getMetricVariables(); + $bestIntersect = -1; + $bestPos = count($result); + + foreach ($result as $graphPos => & $graph) { + $currentIntersect = count(array_intersect_assoc($graph[1], $metricVariables)); + + if ($currentIntersect >= $bestIntersect) { + $bestIntersect = $currentIntersect; + $bestPos = $graphPos + 1; + } + } + unset($graph); + + $urlParams = $template->getUrlParams(); + if (array_key_exists("height", $urlParams)) { + $actheight = $urlParams["height"]->resolve(['height']); + if ($actheight < $this->height) { + $actheight = $this->height; + } + } else { + $actheight = $this->height; + } + $actwidth = $this->width; + $actwidthfix = ""; + if (array_key_exists("width", $urlParams)) { + $actwidth = $urlParams["width"]->resolve(['width']); + $actwidthfix = "width: {$actwidth}px; "; + } + + if ($this->renderInline) { + $chart->setFrom($this->start) + ->setUntil($this->end) + ->setWidth($actwidth) + ->setHeight($actheight) + ->setBackgroundColor('white') + ->setForegroundColor('black') + ->setMajorGridLineColor('grey') + ->setMinorGridLineColor('white') + ->setShowLegend(! $this->compact); + + $img = new InlineGraphImage($chart); + } else { + $imageUrl = $this->filterImageUrl($imageBaseUrl->with($metricVariables)) + ->setParam($urlParam, $templateName) + ->setParam('start', $this->start) + ->setParam('end', $this->end) + ->setParam('width', $actwidth) + ->setParam('height', $actheight); + + if (! $this->compact) { + $imageUrl->setParam('legend', 1); + } + + if ($this->preloadDummy) { + $src = 'data:image/png;base64,' // 1x1 dummy + . 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRS' + . 'TlMAQObYZgAAAApJREFUeAFjZAAAAAQAAhq+CAMAAAAASUVORK5CYII='; + } else { + $src = $imageUrl; + } + + $img = '<img id="graphiteImg-' . md5((string) $imageUrl) . '"' + . " src=\"$src\" data-actualimageurl=\"$imageUrl\" class=\"detach graphiteImg\"" + . " alt=\"\" width=\"$actwidth\" height=\"$actheight\"" + . " style=\"min-width: {$actwidth}px; $actwidthfix min-height: {$actheight}px;\">"; + } + + $currentGraphs[] = [$img, $metricVariables, $bestPos]; + } + + foreach (array_reverse($currentGraphs) as $graph) { + list($img, $metricVariables, $bestPos) = $graph; + array_splice($result, $bestPos, 0, [[$img, $metricVariables]]); + } + } + + IPT::unindent(); + } else { + IPT::recordf('Not applying template %s', $templateName); + } + } + + IPT::unindent(); + } + + if (! empty($result)) { + foreach ($result as & $graph) { + $graph = $graph[0]; + } + unset($graph); + + $currentUrl = Icinga::app()->getRequest()->getUrl(); + $limit = (int) $currentUrl->getParam('graphs_limit', 2); + $total = count($result); + + if ($limit < 1) { + $limit = -1; + } + + if ($limit !== -1 && $total > $limit) { + $result = array_slice($result, 0, $limit); + + if (! $this->compact) { + /** @var View $view */ + $view = $this->view(); + + $url = $this->getGraphsListBaseUrl(); + TimeRangePickerTools::copyAllRangeParameters($url->getParams(), $currentUrl->getParams()); + + $result[] = "<p class='load-more'>{$view->qlink( + sprintf($view->translate('Load all %d graphs'), $total), + $url->setParam('graphs_limit', '-1'), + null, + ['class' => 'action-link'] + )}</p>"; + } + } + + $classes = $this->classes; + $classes[] = 'images'; + + array_unshift($result, '<div class="' . implode(' ', $classes) . '">'); + $result[] = '</div>'; + } + + if ($this->renderInline) { + foreach ($result as $html) { + if ($html instanceof InlineGraphImage) { + // Errors should occur now or not at all + $html->render(); + } + } + } + + return implode($result); + } + + public function render() + { + IPT::clear(); + + try { + $result = $this->getGraphsList(); + } catch (ConfigurationError $e) { + $view = $this->view(); + + return "<p>{$view->escape($e->getMessage())}</p>" + . '<p>' . vsprintf( + $view->escape($view->translate('Please %scorrect%s the configuration of the Graphite module.')), + Auth::getInstance()->hasPermission('config/modules') + ? explode( + '$LINK_TEXT$', + $view->qlink('$LINK_TEXT$', 'graphite/config/backend', null, ['class' => 'action-link']) + ) + : ['', ''] + ) . '</p>'; + } + + $view = $this->view(); + + if ($result === '' && $this->getShowNoGraphsFound()) { + $result = "<p>{$view->escape($view->translate('No graphs found'))}</p>"; + } + + if (IPT::enabled()) { + $result .= "<h3>{$view->escape($view->translate('Graphs assembling process record'))}</h3>" + . "<pre>{$view->escape(IPT::dump())}</pre>"; + } + + return $result; + } + + /** + * Get time range parameters for Graphite from the URL + * + * @param Request $request The request to be used + * + * @return string[] + */ + protected function getRangeFromTimeRangePicker(Request $request) + { + $params = $request->getUrl()->getParams(); + $relative = $params->get(TimeRangePickerTools::getRelativeRangeParameter()); + if ($relative !== null) { + return ["-$relative", null]; + } + + $absolute = TimeRangePickerTools::getAbsoluteRangeParameters(); + $start = $params->get($absolute['start']); + return [ + $start === null ? -TimeRangePickerTools::getDefaultRelativeTimeRange() : $start, + $params->get($absolute['end']) + ]; + } + + /** + * Return a identifier specifying the monitored object we display graphs for + * + * @return string + */ + abstract protected function getMonitoredObjectIdentifier(); + + /** + * Get the base URL to a graph specifying just the monitored object kind + * + * @return Url + */ + abstract protected function getImageBaseUrl(); + + /** + * Get the base URL to the monitored object's graphs list + * + * @return Url + */ + abstract protected function getGraphsListBaseUrl(); + + /** + * Extend the {@link getImageBaseUrl()}'s result's parameters with the concrete monitored object + * + * @param Url $url The URL to extend + * + * @return Url The given URL + */ + abstract protected function filterImageUrl(Url $url); + + /** + * Return whether the given template is designed for the type of the object we display graphs for + * + * @param Template $template + * + * @return bool + */ + abstract protected function designedForObjectType(Template $template); + + /** + * Get {@link compact} + * + * @return bool + */ + public function getCompact() + { + return $this->compact; + } + + /** + * Set {@link compact} + * + * @param bool $compact + * + * @return $this + */ + public function setCompact($compact = true) + { + $this->compact = $compact; + return $this; + } + + /** + * Get the graph image width + * + * @return string + */ + public function getWidth() + { + return $this->width; + } + + /** + * Set the graph image width + * + * @param string $width + * + * @return $this + */ + public function setWidth($width) + { + $this->width = $width; + + return $this; + } + + /** + * Get the graph image height + * + * @return string + */ + public function getHeight() + { + return $this->height; + } + + /** + * Set the graph image height + * + * @param string $height + * + * @return $this + */ + public function setHeight($height) + { + $this->height = $height; + + return $this; + } + + /** + * Get additional CSS classes for the <div/>s around the images + * + * @return string[] + */ + public function getClasses() + { + return $this->classes; + } + + /** + * Set additional CSS classes for the <div/>s around the images + * + * @param string[] $classes + * + * @return $this + */ + public function setClasses($classes) + { + $this->classes = $classes; + + return $this; + } + + /** + * Get whether to serve a transparent dummy image first and let the JS code load the actual graph + * + * @return bool + */ + public function getPreloadDummy() + { + return $this->preloadDummy; + } + + /** + * Set whether to serve a transparent dummy image first and let the JS code load the actual graph + * + * @param bool $preloadDummy + * + * @return $this + */ + public function setPreloadDummy($preloadDummy = true) + { + $this->preloadDummy = $preloadDummy; + + return $this; + } + + /** + * Get whether to render the graphs inline + * + * @return bool + */ + public function getRenderInline() + { + return $this->renderInline; + } + + /** + * Set whether to render the graphs inline + * + * @param bool $renderInline + * + * @return $this + */ + public function setRenderInline($renderInline = true) + { + $this->renderInline = $renderInline; + + return $this; + } + + /** + * Get whether to explicitly display that no graphs were found + * + * @return bool + */ + public function getShowNoGraphsFound() + { + if ($this->showNoGraphsFound === null) { + $this->showNoGraphsFound = ! Config::module('graphite')->get('ui', 'disable_no_graphs_found'); + } + + return $this->showNoGraphsFound; + } + + /** + * Set whether to explicitly display that no graphs were found + * + * @param bool $showNoGraphsFound + * + * @return $this + */ + public function setShowNoGraphsFound($showNoGraphsFound = true) + { + $this->showNoGraphsFound = $showNoGraphsFound; + + return $this; + } +} diff --git a/library/Graphite/Web/Widget/Graphs/Host.php b/library/Graphite/Web/Widget/Graphs/Host.php new file mode 100644 index 0000000..2247bcc --- /dev/null +++ b/library/Graphite/Web/Widget/Graphs/Host.php @@ -0,0 +1,51 @@ +<?php + +namespace Icinga\Module\Graphite\Web\Widget\Graphs; + +use Icinga\Module\Graphite\Graphing\Template; +use Icinga\Module\Graphite\Web\Widget\Graphs; +use Icinga\Module\Monitoring\Object\Host as MonitoredHost; +use Icinga\Web\Url; + +class Host extends Graphs +{ + protected $objectType = 'host'; + + /** + * The host to render the graphs of + * + * @var MonitoredHost + */ + protected $object; + + protected function getImageBaseUrl() + { + return Url::fromPath('graphite/graph/host'); + } + + protected function getGraphsListBaseUrl() + { + return Url::fromPath('graphite/list/hosts', ['host' => $this->object->getName()]); + } + + protected function filterImageUrl(Url $url) + { + return $url->setParam('host.name', $this->object->getName()); + } + + protected function getMonitoredObjectIdentifier() + { + return $this->object->getName(); + } + + protected function designedForObjectType(Template $template) + { + foreach ($template->getCurves() as $curve) { + if (in_array('host_name_template', $curve[0]->getMacros())) { + return true; + } + } + + return false; + } +} diff --git a/library/Graphite/Web/Widget/Graphs/Icingadb/IcingadbHost.php b/library/Graphite/Web/Widget/Graphs/Icingadb/IcingadbHost.php new file mode 100644 index 0000000..2b0a614 --- /dev/null +++ b/library/Graphite/Web/Widget/Graphs/Icingadb/IcingadbHost.php @@ -0,0 +1,61 @@ +<?php + +namespace Icinga\Module\Graphite\Web\Widget\Graphs\Icingadb; + +use Icinga\Module\Graphite\Graphing\Template; +use Icinga\Module\Graphite\Web\Widget\Graphs; +use Icinga\Web\Url; +use Icinga\Module\Icingadb\Model\Host; + +class IcingadbHost extends Graphs +{ + protected $objectType = 'host'; + + /** + * The Icingadb host to render the graphs for + * + * @var Host + */ + protected $object; + + protected function getGraphsListBaseUrl() + { + return Url::fromPath('graphite/hosts', ['host.name' => $this->object->name]); + } + + protected function filterImageUrl(Url $url) + { + return $url->setParam('host.name', $this->object->name); + } + + public function createHostTitle() + { + return $this->object->name; + } + + public function getObjectType() + { + return $this->objectType; + } + + protected function getMonitoredObjectIdentifier() + { + return $this->object->name; + } + + protected function getImageBaseUrl() + { + return Url::fromPath('graphite/graph/host'); + } + + protected function designedForObjectType(Template $template) + { + foreach ($template->getCurves() as $curve) { + if (in_array('host_name_template', $curve[0]->getMacros())) { + return true; + } + } + + return false; + } +} diff --git a/library/Graphite/Web/Widget/Graphs/Icingadb/IcingadbService.php b/library/Graphite/Web/Widget/Graphs/Icingadb/IcingadbService.php new file mode 100644 index 0000000..7827e86 --- /dev/null +++ b/library/Graphite/Web/Widget/Graphs/Icingadb/IcingadbService.php @@ -0,0 +1,71 @@ +<?php + +namespace Icinga\Module\Graphite\Web\Widget\Graphs\Icingadb; + +use Icinga\Module\Graphite\Graphing\Template; +use Icinga\Module\Graphite\Web\Widget\Graphs; +use Icinga\Web\Url; +use Icinga\Module\Icingadb\Model\Service; + +class IcingadbService extends Graphs +{ + protected $objectType = 'service'; + + /** + * The icingadb service to render the graphs for + * + * @var Service + */ + protected $object; + + protected function getGraphsListBaseUrl() + { + return Url::fromPath( + 'graphite/services', + ['service.name' => $this->object->name, 'host.name' => $this->object->host->name] + ); + } + + protected function filterImageUrl(Url $url) + { + return $url + ->setParam('host.name', $this->object->host->name) + ->setParam('service.name', $this->object->name); + } + + public function createHostTitle() + { + return $this->object->host->name; + } + + public function createServiceTitle() + { + return ' : ' . $this->object->name; + } + + public function getObjectType() + { + return $this->objectType; + } + + protected function getMonitoredObjectIdentifier() + { + return $this->object->host->name . ':' . $this->object->name; + } + + protected function getImageBaseUrl() + { + return Url::fromPath('graphite/graph/service'); + } + + protected function designedforObjectType(Template $template) + { + foreach ($template->getCurves() as $curve) { + if (in_array('service_name_template', $curve[0]->getMacros())) { + return true; + } + } + + return false; + } +} diff --git a/library/Graphite/Web/Widget/Graphs/Service.php b/library/Graphite/Web/Widget/Graphs/Service.php new file mode 100644 index 0000000..5fc0143 --- /dev/null +++ b/library/Graphite/Web/Widget/Graphs/Service.php @@ -0,0 +1,56 @@ +<?php + +namespace Icinga\Module\Graphite\Web\Widget\Graphs; + +use Icinga\Module\Graphite\Graphing\Template; +use Icinga\Module\Graphite\Web\Widget\Graphs; +use Icinga\Module\Monitoring\Object\Service as MonitoredService; +use Icinga\Web\Url; + +class Service extends Graphs +{ + protected $objectType = 'service'; + + /** + * The service to render the graphs for + * + * @var MonitoredService + */ + protected $object; + + protected function getImageBaseUrl() + { + return Url::fromPath('graphite/graph/service'); + } + + protected function getGraphsListBaseUrl() + { + return Url::fromPath( + 'graphite/list/services', + ['host' => $this->object->getHost()->getName(), 'service' => $this->object->getName()] + ); + } + + protected function filterImageUrl(Url $url) + { + return $url + ->setParam('host.name', $this->object->getHost()->getName()) + ->setParam('service.name', $this->object->getName()); + } + + protected function getMonitoredObjectIdentifier() + { + return $this->object->getHost()->getName() . ':' . $this->object->getName(); + } + + protected function designedForObjectType(Template $template) + { + foreach ($template->getCurves() as $curve) { + if (in_array('service_name_template', $curve[0]->getMacros())) { + return true; + } + } + + return false; + } +} diff --git a/library/Graphite/Web/Widget/IcingadbGraphs.php b/library/Graphite/Web/Widget/IcingadbGraphs.php new file mode 100644 index 0000000..e038e92 --- /dev/null +++ b/library/Graphite/Web/Widget/IcingadbGraphs.php @@ -0,0 +1,106 @@ +<?php + +/* Icinga Graphite Web | (c) 2022 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Graphite\Web\Widget; + +use Icinga\Data\Filter\Filter; +use Icinga\Module\Graphite\Web\Widget\Graphs\Icingadb\IcingadbHost; +use Icinga\Module\Graphite\Web\Widget\Graphs\Icingadb\IcingadbService; +use Icinga\Module\Icingadb\Common\Links; +use Icinga\Module\Icingadb\Widget\EmptyState; +use Icinga\Module\Icingadb\Model\Host; +use ipl\Html\BaseHtmlElement; +use ipl\Html\Html; +use ipl\Html\HtmlDocument; +use ipl\Html\HtmlString; +use ipl\Orm\ResultSet; +use ipl\Stdlib\BaseFilter; +use ipl\Web\Filter\QueryString; +use ipl\Web\Widget\Link; + +/** +* Class for creating graphs of icingadb objects +*/ +class IcingadbGraphs extends BaseHtmlElement +{ + use BaseFilter; + + protected $defaultAttributes = ['class' => 'grid']; + + /** @var Iterable */ + protected $objects; + + protected $tag = 'div'; + + /** + * Create a new Graph item + * + * @param ResultSet $objects + */ + public function __construct(ResultSet $objects) + { + $this->objects = $objects; + } + + protected function assemble() + { + if (! $this->objects->hasResult()) { + $this->add(new EmptyState(t('No items found.'))); + } + + foreach ($this->objects as $object) { + $this->add($this->createGridItem($object)); + } + + $document = new HtmlDocument(); + $document->addHtml(Html::tag('div', ['class' => 'graphite-graph-color-registry']), $this); + $this->prependWrapper($document); + } + + protected function createGridItem($object) + { + if ($object instanceof Host) { + $graph = new IcingadbHost($object); + $hostObj = $object; + } else { + $graph = new IcingadbService($object); + $hostObj = $object->host; + } + + $hostUrl = Links::host($hostObj); + + if ($this->hasBaseFilter()) { + $hostUrl->addFilter(Filter::fromQueryString(QueryString::render($this->getBaseFilter()))); + } + + $hostLink = new Link( + $graph->createHostTitle(), + $hostUrl, + ['data-base-target' => '_next'] + ); + + $serviceLink = null; + if ($graph->getObjectType() === 'service') { + $serviceUrl = Links::service($object, $hostObj); + + if ($this->hasBaseFilter()) { + $serviceUrl->addFilter(Filter::fromQueryString(QueryString::render($this->getBaseFilter()))); + } + + $serviceLink = new Link( + $graph->createServiceTitle(), + $serviceUrl, + ['data-base-target' => '_next'] + ); + } + + $gridItem = Html::tag('div', ['class' => 'grid-item']); + $header = Html::tag('h2'); + + $header->add([$hostLink, $serviceLink]); + $gridItem->add($header); + + return $gridItem->add(HtmlString::create($graph->setPreloadDummy()->handleRequest())); + } +} diff --git a/library/Graphite/Web/Widget/InlineGraphImage.php b/library/Graphite/Web/Widget/InlineGraphImage.php new file mode 100644 index 0000000..881384d --- /dev/null +++ b/library/Graphite/Web/Widget/InlineGraphImage.php @@ -0,0 +1,49 @@ +<?php + +namespace Icinga\Module\Graphite\Web\Widget; + +use Icinga\Module\Graphite\Graphing\Chart; +use Icinga\Web\Widget\AbstractWidget; + +class InlineGraphImage extends AbstractWidget +{ + /** + * The image to be rendered + * + * @var GraphImage + */ + protected $image; + + /** + * The rendered <img> + * + * @var string|null + */ + protected $rendered; + + /** + * Constructor + * + * @param Chart $chart The chart to be rendered + */ + public function __construct(Chart $chart) + { + $this->image = new GraphImage($chart); + } + + /** + * Render the graph lazily + * + * @return string + */ + public function render() + { + if ($this->rendered === null) { + $this->rendered = '<img src="data:image/png;base64,' + . implode("\n", str_split(base64_encode($this->image->render()), 76)) + . '">'; + } + + return $this->rendered; + } +} |