diff --git a/application/controllers/GraphController.php b/application/controllers/GraphController.php new file mode 100644 index 0000000..d50cba2 --- /dev/null +++ b/application/controllers/GraphController.php @@ -0,0 +1,117 @@ +service = false; + + $this->supplyImage(); + } + + public function serviceAction() + { + $this->supplyImage(); + } + + /** + * Do all monitored object type independend actions + */ + protected function supplyImage() + { + $this->filterParams = clone $this->getRequest()->getUrl()->getParams(); + + foreach ($this->geometryParamsNames as $paramName) { + $this->geometryParams[$paramName] = $this->filterParams->shift($paramName); + } + + $this->collectTemplates(); + $this->collectGraphiteQueries(); + + $charts = []; + foreach ($this->graphiteQueries as $templateName => $graphiteQuery) { + /** @var GraphiteQuery $graphiteQuery */ + + $charts = array_merge($charts, $graphiteQuery->getImages($this->templates[$templateName])); + if (count($charts) > 1) { + throw new HttpBadRequestException('%s', $this->translate( + 'Graphite Web yields more than one metric for the given filter.' + . ' Please specify a more precise filter.' + )); + } + } + + if (empty($charts)) { + throw new HttpNotFoundException('%s', $this->translate('No such graph')); + } + + $image = $charts[0] + ->setStart($this->geometryParams['start']) + ->setUntil($this->geometryParams['end']) + ->setWidth($this->geometryParams['width']) + ->setHeight($this->geometryParams['height']) + ->showLegend((bool) $this->geometryParams['legend']) + ->fetchImage(); + + $this->_helper->layout()->disableLayout(); + + header('Content-Type: image/png'); + header('Content-Disposition: inline; filename="graph.png"'); + echo $image; + exit; + } + + protected function includeTemplate(GraphTemplate $template) + { + return (strpos($template->getFilterString(), '$service') !== false) === $this->service; + } + + protected function filterGraphiteQuery(GraphiteQuery $query) + { + foreach ($this->filterParams->toArray() as list($key, $value)) { + $query->where($key, $value); + } + + return $query; + } +} diff --git a/application/controllers/ShowController.php b/application/controllers/ShowController.php index efdfdc2..5f6aabf 100644 --- a/application/controllers/ShowController.php +++ b/application/controllers/ShowController.php @@ -40,39 +40,6 @@ class ShowController extends Controller $this->params->shift('r'); } - public function graphAction() - { - $template = $this->loadTemplate(); - $title = $template->getTitle(); - if (false === strpos($title, '$')) { - $template->setTitle('$hostname'); - } else { - if (false === strpos($title, '$hostname')) { - $template->setTitle('$hostname: ' . $template->getTitle()); - } - } - - $query = $this->graphiteWeb - ->select() - ->from( - $template->getFilterString() - ); - - foreach ($this->params->toArray() as $val) { - $query->where($val[0], urldecode($val[1])); - } - - $img = $this->applyGraphParams(current($query->getImages($template))) - ->showLegend(false); - - $this->_helper->layout()->disableLayout(); - - $image = $img->fetchImage(); - header('Content-Type: image/png'); - echo $image; - exit; - } - protected function loadTemplate() { $this->handleTemplateParams(); @@ -160,7 +127,7 @@ class ShowController extends Controller $params->add('disabled', $key); } } - + $this->redirectNow($url); } } diff --git a/library/Graphite/Web/Widget/Graphs.php b/library/Graphite/Web/Widget/Graphs.php index fabb210..4825be6 100644 --- a/library/Graphite/Web/Widget/Graphs.php +++ b/library/Graphite/Web/Widget/Graphs.php @@ -5,36 +5,20 @@ namespace Icinga\Module\Graphite\Web\Widget; use Icinga\Application\Icinga; use Icinga\Module\Graphite\Forms\TimeRangePicker\TimeRangePickerTrait; use Icinga\Module\Graphite\GraphiteQuery; -use Icinga\Module\Graphite\GraphiteWeb; -use Icinga\Module\Graphite\GraphiteWebClient; -use Icinga\Module\Graphite\GraphTemplate; -use Icinga\Module\Graphite\TemplateSet; -use Icinga\Module\Graphite\TemplateStore; +use Icinga\Module\Graphite\GraphiteUtil; 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\UrlParams; +use Icinga\Web\Url; use Icinga\Web\View; use Icinga\Web\Widget\AbstractWidget; abstract class Graphs extends AbstractWidget { - /** - * [$setName => $set] - * - * @var TemplateSet[string] - */ - protected static $templateSets; - - /** - * [$setName => [$templateName => $template]] - * - * @var GraphTemplate[string][string] - */ - protected static $allTemplates = []; + use GraphsTrait; /** * Graph image width @@ -78,12 +62,7 @@ abstract class Graphs extends AbstractWidget * * @var string[string][string] */ - protected $images; - - /** - * @var GraphTemplate[string] - */ - protected $templates; + protected $images = []; /** * Factory, based on the given object @@ -132,22 +111,20 @@ abstract class Graphs extends AbstractWidget $rendered = ''; foreach ($this->images as $type => $images) { - if (count($images) > 0) { - $rendered .= '
'; + $rendered .= '
'; - if (! $this->compact) { - $rendered .= "

{$view->escape(ucfirst($type))}

{$view->partial( - 'show/legend.phtml', - ['template' => $this->templates[$type]] - )}"; - } - - foreach ($images as $title => $url) { - $rendered .= "\"\"width\" height=\"$this->height\" />"; - } - - $rendered .= '
'; + if (! $this->compact) { + $rendered .= "

{$view->escape(ucfirst($type))}

{$view->partial( + 'show/legend.phtml', + ['template' => $this->templates[$type]] + )}"; } + + foreach ($images as $url) { + $rendered .= "\"\"width\" height=\"$this->height\" />"; + } + + $rendered .= '
'; } return $rendered ?: "

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

"; @@ -186,75 +163,35 @@ abstract class Graphs extends AbstractWidget } /** - * Initialize {@link templates} + * Initialize {@link images} */ - protected function collectTemplates() + protected function collectImages() { - if (static::$templateSets === null) { - static::$templateSets = (new TemplateStore())->loadTemplateSets(); - } + $this->collectGraphiteQueries(); + $imageBaseUrl = $this->getImageBaseUrl(); - foreach (static::$templateSets as $setname => $set) { - /** @var TemplateSet $set */ + foreach ($this->graphiteQueries as $templateName => $graphiteQuery) { + /** @var GraphiteQuery $graphiteQuery */ - if (array_key_exists('icingaHost', $set->getBasePatterns())) { - if (! isset(static::$allTemplates[$setname])) { - static::$allTemplates[$setname] = $set->loadTemplates(); - } + $searchPattern = $graphiteQuery->getSearchPattern(); - foreach (static::$allTemplates[$setname] as $templateName => $template) { - /** @var GraphTemplate $template */ - - if ($this->includeTemplate($template)) { - $this->templates[$templateName] = $template; - } - } + foreach ($graphiteQuery->listMetrics() as $metric) { + $this->images[$templateName][] = $imageBaseUrl + ->with(GraphiteUtil::extractVars($metric, $searchPattern)) + ->setParam('start', $this->start) + ->setParam('end', $this->end) + ->setParam('width', $this->width) + ->setParam('height', $this->height); } } } /** - * Initialize {@link images} + * Get the base URL to a graph specifying just the monitored object kind + * + * @return Url */ - protected function collectImages() - { - $graphiteWeb = new GraphiteWeb(GraphiteWebClient::getInstance()); - foreach ($this->templates as $templateName => $template) { - /** @var GraphTemplate $template */ - - $this->images[$templateName] = $this->filterGraphiteQuery( - $graphiteWeb->select()->from($template->getFilterString()) - ) - ->getWrappedImageLinks( - $template, - TimeRangePickerTrait::copyAllRangeParameters( - (new UrlParams()) - ->set('template', $templateName) - ->set('start', $this->start) - ->set('width', $this->width) - ->set('height', $this->height) - ) - ); - } - } - - /** - * Add filters to the given query so that only specific graphs are shown - * - * @param GraphiteQuery $query - * - * @return GraphiteQuery The given query - */ - abstract protected function filterGraphiteQuery(GraphiteQuery $query); - - /** - * Return whether to use the given template - * - * @param GraphTemplate $template - * - * @return bool - */ - abstract protected function includeTemplate(GraphTemplate $template); + abstract protected function getImageBaseUrl(); /** * Get {@link compact} diff --git a/library/Graphite/Web/Widget/Graphs/Host.php b/library/Graphite/Web/Widget/Graphs/Host.php index 91c387b..fd16b63 100644 --- a/library/Graphite/Web/Widget/Graphs/Host.php +++ b/library/Graphite/Web/Widget/Graphs/Host.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Graphite\Web\Widget\Graphs; use Icinga\Module\Graphite\GraphiteQuery; use Icinga\Module\Graphite\GraphTemplate; use Icinga\Module\Graphite\Web\Widget\Graphs; +use Icinga\Web\Url; class Host extends Graphs { @@ -34,4 +35,9 @@ class Host extends Graphs { return strpos($template->getFilterString(), '$service') === false; } + + protected function getImageBaseUrl() + { + return Url::fromPath('graphite/graph/host'); + } } diff --git a/library/Graphite/Web/Widget/Graphs/Service.php b/library/Graphite/Web/Widget/Graphs/Service.php index 8aafb2a..a35ec9d 100644 --- a/library/Graphite/Web/Widget/Graphs/Service.php +++ b/library/Graphite/Web/Widget/Graphs/Service.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Graphite\Web\Widget\Graphs; use Icinga\Module\Graphite\GraphiteQuery; use Icinga\Module\Graphite\GraphTemplate; use Icinga\Module\Graphite\Web\Widget\Graphs; +use Icinga\Web\Url; class Service extends Graphs { @@ -45,4 +46,9 @@ class Service extends Graphs { return strpos($template->getFilterString(), '$service') !== false; } + + protected function getImageBaseUrl() + { + return Url::fromPath('graphite/graph/service'); + } } diff --git a/library/Graphite/Web/Widget/GraphsTrait.php b/library/Graphite/Web/Widget/GraphsTrait.php new file mode 100644 index 0000000..f94fb21 --- /dev/null +++ b/library/Graphite/Web/Widget/GraphsTrait.php @@ -0,0 +1,98 @@ + $set] + * + * @var TemplateSet[string] + */ + protected static $templateSets; + + /** + * [$setName => [$templateName => $template]] + * + * @var GraphTemplate[string][string] + */ + protected static $allTemplates = []; + + /** + * @var GraphTemplate[string] + */ + protected $templates; + + /** + * @var GraphiteQuery[string] + */ + protected $graphiteQueries; + + /** + * Initialize {@link templates} + */ + protected function collectTemplates() + { + if (static::$templateSets === null) { + static::$templateSets = (new TemplateStore())->loadTemplateSets(); + } + + foreach (static::$templateSets as $setname => $set) { + /** @var TemplateSet $set */ + + if (array_key_exists('icingaHost', $set->getBasePatterns())) { + if (! isset(static::$allTemplates[$setname])) { + static::$allTemplates[$setname] = $set->loadTemplates(); + } + + foreach (static::$allTemplates[$setname] as $templateName => $template) { + /** @var GraphTemplate $template */ + + if ($this->includeTemplate($template)) { + $this->templates[$templateName] = $template; + } + } + } + } + } + + /** + * Initialize {@link graphiteQueries} + */ + protected function collectGraphiteQueries() + { + $graphiteWeb = new GraphiteWeb(GraphiteWebClient::getInstance()); + foreach ($this->templates as $templateName => $template) { + /** @var GraphTemplate $template */ + + $this->graphiteQueries[$templateName] = $this->filterGraphiteQuery( + $graphiteWeb->select()->from($template->getFilterString()) + ); + } + } + + /** + * Return whether to use the given template + * + * @param GraphTemplate $template + * + * @return bool + */ + abstract protected function includeTemplate(GraphTemplate $template); + + /** + * Add filters to the given query so that only specific graphs are shown + * + * @param GraphiteQuery $query + * + * @return GraphiteQuery The given query + */ + abstract protected function filterGraphiteQuery(GraphiteQuery $query); +}