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; $explicitWidth = false; if (array_key_exists("width", $urlParams)) { $actwidth = $urlParams["width"]->resolve(['width']); $explicitWidth = $actwidth; } if ($this->renderInline) { $chart->setFrom($this->start) ->setUntil($this->end) ->setWidth((int) $actwidth) ->setHeight((int) $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 = '\"\"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[] = "

{$view->qlink( sprintf($view->translate('Load all %d graphs'), $total), $url->setParam('graphs_limit', '-1'), null, ['class' => 'action-link'] )}

"; } } $classes = $this->classes; $classes[] = 'images'; array_unshift($result, '
'); $result[] = '
'; } 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 "

{$view->escape($e->getMessage())}

" . '

' . 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']) ) : ['', ''] ) . '

'; } $view = $this->view(); if ($result === '' && $this->getShowNoGraphsFound()) { $result = "

{$view->escape($view->translate('No graphs found'))}

"; } if (IPT::enabled()) { $result .= "

{$view->escape($view->translate('Graphs assembling process record'))}

" . "
{$view->escape(IPT::dump())}
"; } 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
s around the images * * @return string[] */ public function getClasses() { return $this->classes; } /** * Set additional CSS classes for the
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; } }