Merge branch 'feature/display-graph-in-detailview-27' into next

fixes #27
fixes #29
fixes #30
fixes #32
fixes #34
This commit is contained in:
Alexander A. Klimov 2017-09-20 11:33:51 +02:00
commit 47195e1108
18 changed files with 508 additions and 81 deletions

View file

@ -0,0 +1,101 @@
<?php
namespace Icinga\Module\Graphite\Controllers;
use Icinga\Module\Graphite\Forms\TimeRangePicker\TimeRangePickerTrait as TimeRangePicker;
use Icinga\Module\Graphite\Web\Controller\TimeRangePickerTrait;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\DataView\DataView;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
class ListController extends Controller
{
use TimeRangePickerTrait;
public function init()
{
parent::init();
$this->getTabs()->extend(new DashboardAction())->extend(new MenuAction());
}
public function hostsAction()
{
$this->addTitleTab(
'hosts',
mt('monitoring', 'Hosts'),
mt('monitoring', 'List hosts')
);
$this->view->hosts = $hosts = $this->backend->select()->from('hoststatus', ['host_name', 'host_display_name']);
$this->applyRestriction('monitoring/filter/objects', $hosts);
$this->filterQuery($hosts);
$this->setupPaginationControl($hosts);
$this->setupLimitControl();
$this->setupSortControl(['host_display_name' => mt('monitoring', 'Hostname')], $hosts);
$this->handleTimeRangePickerRequest();
$this->view->timeRangePicker = $this->renderTimeRangePicker($this->view);
}
public function servicesAction()
{
$this->addTitleTab(
'services',
mt('monitoring', 'Services'),
mt('monitoring', 'List services')
);
$this->view->services = $services = $this->backend->select()->from('servicestatus', [
'host_name',
'host_display_name',
'service_description',
'service_display_name'
]);
$this->applyRestriction('monitoring/filter/objects', $services);
$this->filterQuery($services);
$this->setupPaginationControl($services);
$this->setupLimitControl();
$this->setupSortControl([
'service_display_name' => mt('monitoring', 'Service Name'),
'host_display_name' => mt('monitoring', 'Hostname')
], $services);
$this->handleTimeRangePickerRequest();
$this->view->timeRangePicker = $this->renderTimeRangePicker($this->view);
}
/**
* Apply filters on a DataView
*
* @param DataView $dataView The DataView to apply filters on
*/
protected function filterQuery(DataView $dataView)
{
$this->setupFilterControl(
$dataView,
null,
null,
array_merge(['format', 'stateType', 'addColumns', 'problems'], TimeRangePicker::getAllParameters())
);
$this->handleFormatRequest($dataView);
}
/**
* Add title tab
*
* @param string $action
* @param string $title
* @param string $tip
*/
protected function addTitleTab($action, $title, $tip)
{
$this->getTabs()->add($action, [
'title' => $tip,
'label' => $title,
'url' => Url::fromRequest(),
'active' => true
]);
}
}

View file

@ -4,6 +4,7 @@ namespace Icinga\Module\Graphite\Controllers;
use DirectoryIterator;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Graphite\Forms\TimeRangePicker\TimeRangePickerTrait;
use Icinga\Module\Graphite\GraphiteChart;
use Icinga\Module\Graphite\GraphiteUtil;
use Icinga\Module\Graphite\GraphiteWeb;
@ -11,6 +12,7 @@ use Icinga\Module\Graphite\GraphiteWebClient;
use Icinga\Module\Graphite\GraphTemplate;
use Icinga\Module\Graphite\TemplateStore;
use Icinga\Web\Controller;
use Icinga\Web\UrlParams;
use Icinga\Web\Widget;
class ShowController extends Controller
@ -168,15 +170,14 @@ class ShowController extends Controller
foreach ($set->loadTemplates() as $key => $template) {
if (strpos($template->getFilterString(), '$service') !== false) continue;
$imgParams = array(
'template' => $key,
'start' => $view->start,
'width' => $view->width,
'height' => $view->height
);
$imgParams = (new UrlParams())
->set('template', $key)
->set('start', $view->start)
->set('width', $view->width)
->set('height', $view->height);
if ($this->view->disabledDatasources) {
$imgParams['disabled'] = $this->view->disabledDatasources;
$imgParams->set('disabled', $this->view->disabledDatasources);
foreach ($this->view->disabledDatasources as $dis) {
if ($template->hasDatasource($dis)) {
$template->getDatasource($dis)->disable();
@ -190,7 +191,7 @@ class ShowController extends Controller
->select()
->from($template->getFilterString())
->where('hostname', $hostname)
->getWrappedImageLinks($template, $imgParams);
->getWrappedImageLinks($template, TimeRangePickerTrait::copyAllRangeParameters($imgParams));
}
}
@ -228,15 +229,14 @@ class ShowController extends Controller
foreach ($set->loadTemplates() as $key => $template) {
if (strpos($template->getFilterString(), '$service') === false) continue;
$imgParams = array(
'template' => $key,
'start' => $view->start,
'width' => $view->width,
'height' => $view->height
);
$imgParams = (new UrlParams())
->set('template', $key)
->set('start', $view->start)
->set('width', $view->width)
->set('height', $view->height);;
if ($this->view->disabledDatasources) {
$imgParams['disabled'] = $this->view->disabledDatasources;
$imgParams->set('disabled', $this->view->disabledDatasources);
foreach ($this->view->disabledDatasources as $dis) {
if ($template->hasDatasource($dis)) {
$template->getDatasource($dis)->disable();
@ -252,7 +252,7 @@ class ShowController extends Controller
->from($template->getFilterString())
->where('hostname', $hostname)
->where('service', $service)
->getWrappedImageLinks($template, $imgParams);
->getWrappedImageLinks($template, TimeRangePickerTrait::copyAllRangeParameters($imgParams));
}
}
@ -329,12 +329,29 @@ class ShowController extends Controller
$this->view->disabledDatasources = $this->params->getValues('disabled');
}
/**
* Get time range parameters for Graphite from the URL
*
* @return string[]
*/
protected function getRangeFromTimeRangePicker()
{
$params = $this->getRequest()->getUrl()->getParams();
$relative = $params->get(TimeRangePickerTrait::getRelativeRangeParameter());
if ($relative !== null) {
return ["-{$relative}s", null];
}
$absolute = TimeRangePickerTrait::getAbsoluteRangeParameters();
return [$params->get($absolute['start'], '-1hours'), $params->get($absolute['end'])];
}
protected function handleGraphParams()
{
if ($this->handledGraphParams === false) {
$this->handledGraphParams = true;
$view = $this->view;
$view->start = $this->params->shift('start', '-1hours');
list($view->start, $view->end) = $this->getRangeFromTimeRangePicker();
$view->width = $this->params->shift('width', '300');
$view->height = $this->params->shift('height', '150');
}
@ -347,6 +364,7 @@ class ShowController extends Controller
$this->handleGraphParams();
$view = $this->view;
$chart->setStart($view->start)
->setUntil($view->end)
->setWidth($view->width)
->setHeight($view->height)
// TODO: handle before

View file

@ -36,6 +36,7 @@ class CommonForm extends Form
public function init()
{
$this->setName('form_timerangepickercommon_graphite');
$this->setAttrib('data-base-target', '_self');
}
public function createElements(array $formData)

View file

@ -40,6 +40,7 @@ class CustomForm extends Form
public function init()
{
$this->setName('form_timerangepickercustom_graphite');
$this->setAttrib('data-base-target', '_self');
}
public function createElements(array $formData)

View file

@ -2,6 +2,7 @@
namespace Icinga\Module\Graphite\Forms\TimeRangePicker;
use Icinga\Web\Url;
use Icinga\Web\UrlParams;
trait TimeRangePickerTrait
@ -46,6 +47,33 @@ trait TimeRangePickerTrait
return array_values(array_merge(static::getAllRangeParameters(), [static::getRangeCustomizationParameter()]));
}
/**
* Copy {@link getAllRangeParameters()} from one {@link UrlParams} instance to another
*
* @param UrlParams|null $copy Defaults to a new instance
* @param UrlParams|null $origin Defaults to the current request's params
*
* @return UrlParams The copy
*/
public static function copyAllRangeParameters(UrlParams $copy = null, UrlParams $origin = null)
{
if ($origin === null) {
$origin = Url::fromRequest()->getParams();
}
if ($copy === null) {
$copy = new UrlParams();
}
foreach (TimeRangePickerTrait::getAllRangeParameters() as $param) {
$value = $origin->get($param);
if ($value !== null) {
$copy->set($param, $value);
}
}
return $copy;
}
/**
* Extract the relative time range (if any) from the given URL parameters
*

View file

@ -0,0 +1,64 @@
<?php
use Icinga\Module\Graphite\EmbedGraphs;
use Icinga\Web\Url;
/** @var \Icinga\Web\View $this */
/** @var \Icinga\Web\Widget\FilterEditor $filterEditor */
/** @var \Icinga\Module\Monitoring\DataView\DataView $hosts */
if (! $compact): ?>
<div class="controls">
<?= $tabs ?>
<?= $paginator ?>
<div class="sort-controls-container">
<?= $limiter ?>
<?= $sortBox ?>
</div>
<?= $filterEditor ?>
<?= $timeRangePicker ?>
</div>
<?php endif ?>
<div class="content">
<?php
if ($filterEditor->getFilter()->isEmpty()) {
echo '<p>' . $this->escape($this->translate('Please specify a filter')) . '</p>';
} elseif ($hosts->hasResult()) {
foreach ($hosts->peekAhead($compact) as $host) {
echo '<div class="graphs-host">';
if (! $compact) {
echo '<h2>'
. $this->qlink(
$host->host_name === $host->host_display_name
? $host->host_display_name
: $host->host_display_name . ' (' . $this->escape($host->host_name) . ')',
Url::fromPath('monitoring/host/show', ['host' => $host->host_name]),
null,
['data-base-target' => '_next']
)
. '</h2>';
}
echo EmbedGraphs::host($host->host_name);
echo '</div>';
}
if (! $compact && $hosts->hasMore()) {
echo '<div class="action-links">'
. $this->qlink(
mt('monitoring', 'Show More'),
$this->url()->without(array('view', 'limit')),
null,
[
'class' => 'action-link',
'data-base-target' => '_next'
]
)
. '</div>';
}
} else {
echo '<p>' . $this->escape(mt('monitoring', 'No hosts found matching the filter.')) . '</p>';
}
?>
</div>

View file

@ -0,0 +1,72 @@
<?php
use Icinga\Module\Graphite\EmbedGraphs;
use Icinga\Web\Url;
/** @var \Icinga\Web\View $this */
/** @var \Icinga\Web\Widget\FilterEditor $filterEditor */
/** @var \Icinga\Module\Monitoring\DataView\DataView $services */
if (! $compact): ?>
<div class="controls">
<?= $tabs ?>
<?= $paginator ?>
<div class="sort-controls-container">
<?= $limiter ?>
<?= $sortBox ?>
</div>
<?= $filterEditor ?>
<?= $timeRangePicker ?>
</div>
<?php endif ?>
<div class="content">
<?php
if ($filterEditor->getFilter()->isEmpty()) {
echo '<p>' . $this->escape($this->translate('Please specify a filter')) . '</p>';
} elseif ($services->hasResult()) {
foreach ($services->peekAhead($compact) as $service) {
if (! $compact) {
echo '<h2>'
. $this->qlink(
$service->host_name === $service->host_display_name
? $service->host_display_name
: $service->host_display_name . ' (' . $this->escape($service->host_name) . ')',
Url::fromPath('monitoring/host/show', ['host' => $service->host_name]),
null,
['data-base-target' => '_next']
)
. '&#58; '
. $this->qlink(
$service->service_description === $service->service_display_name
? $service->service_display_name
: $service->service_display_name . ' (' . $this->escape($service->service_description) . ')',
Url::fromPath('monitoring/service/show', [
'host' => $service->host_name,
'service' => $service->service_description
]),
null,
['data-base-target' => '_next']
)
. '</h2>';
}
echo EmbedGraphs::service($service->host_name, $service->service_description);
}
if (! $compact && $services->hasMore()) {
echo '<div class="action-links">'
. $this->qlink(
mt('monitoring', 'Show More'),
$this->url()->without(array('view', 'limit')),
null,
[
'class' => 'action-link',
'data-base-target' => '_next'
]
)
. '</div>';
}
} else {
echo '<p>' . $this->escape(mt('monitoring', 'No services found matching the filter.')) . '</p>';
}
?>
</div>

View file

@ -1,21 +1,30 @@
<?php
/** @var \Icinga\Web\View $this */
if (! $compact): ?>
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->hostname ?></h1>
</div>
<?php endif ?>
<div class="content">
<?php foreach ($this->images as $type => $imgs): ?>
<?php if (count($imgs) > 0): ?>
<div class="images"><h3><?= $this->escape(ucfirst($type)) ?></h3>
<?php $template = $this->templates[$type] ?>
<?= $this->partial(
'show/legend.phtml',
array(
'template' => $template,
'disabledDatasources' => $this->disabledDatasources
)
) ?>
<div class="images">
<?php
if (! $compact) {
echo "<h3>{$this->escape(ucfirst($type))}</h3>";
echo $this->partial(
'show/legend.phtml',
array(
'template' => $this->templates[$type],
'disabledDatasources' => $this->disabledDatasources
)
);
}
?>
<?php foreach ($imgs as $title => $url): ?>
<img src="<?= $url ?>" class="graphiteImg" alt="" width="<?= $this->width ?>" height="<?= $this->height ?>" />
<?php endforeach ?>
@ -23,4 +32,9 @@
<?php endif ?>
<?php endforeach ?>
<?php if (! isset($title)): ?>
<p><?= $this->translate('No graphs found') ?></p>
<?php endif ?>
</div>

View file

@ -1,21 +1,30 @@
<?php
/** @var \Icinga\Web\View $this */
if (! $compact): ?>
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->hostname ?>: <?= $this->service ?></h1>
</div>
<?php endif ?>
<div class="content">
<?php foreach ($this->images as $type => $imgs): ?>
<?php if (count($imgs) > 0): ?>
<div class="images"><h3><?= $this->escape(ucfirst($type)) ?></h3>
<?php $template = $this->templates[$type] ?>
<?= $this->partial(
'show/legend.phtml',
array(
'template' => $template,
'disabledDatasources' => $this->disabledDatasources
)
) ?>
<div class="images">
<?php
if (! $compact) {
echo "<h3>{$this->escape(ucfirst($type))}</h3>";
echo $this->partial(
'show/legend.phtml',
array(
'template' => $this->templates[$type],
'disabledDatasources' => $this->disabledDatasources
)
);
}
?>
<?php foreach ($imgs as $title => $url): ?>
<img src="<?= $url ?>" class="graphiteImg" alt="" width="<?= $this->width ?>" height="<?= $this->height ?>" />
<?php endforeach ?>
@ -23,4 +32,9 @@
<?php endif ?>
<?php endforeach ?>
<?php if (! isset($title)): ?>
<p><?= $this->translate('No graphs found') ?></p>
<?php endif ?>
</div>

View file

@ -2,7 +2,10 @@
/** @var \Icinga\Application\Modules\Module $this */
$this->menuSection(N_('Graphite'), ['icon' => 'chart-area'])->setUrl('graphite/show/overview');
/** @var \Icinga\Application\Modules\MenuItemContainer $section */
$section = $this->menuSection(N_('Graphite'), ['icon' => 'chart-area'])->setUrl('graphite/show/overview');
$section->add(N_('Hosts'), ['url' => 'graphite/list/hosts']);
$section->add(N_('Services'), ['url' => 'graphite/list/services']);
$this->provideConfigTab('backend', array(
'title' => $this->translate('Configure the Graphite Web backend'),

View file

@ -0,0 +1,66 @@
<?php
namespace Icinga\Module\Graphite;
use Icinga\Application\Icinga;
use Icinga\Module\Graphite\Forms\TimeRangePicker\TimeRangePickerTrait;
use Icinga\Web\Url;
use Icinga\Web\View;
class EmbedGraphs
{
/**
* Embed all graphs of the given host (but none of its services)
*
* @param string $host
*
* @return string
*/
public static function host($host)
{
return static::url(static::getView()->href('graphite/show/host', ['host' => $host]));
}
/**
* Embed all graphs of the given service of the given host
*
* @param string $host
* @param string $service
*
* @return string
*/
public static function service($host, $service)
{
return static::url(static::getView()->href('graphite/show/service', [
'host' => $host,
'service' => $service
]));
}
/**
* Return a <div/> which causes the framework JS to embed the given URL
*
* @param Url $url
*
* @return string
*/
protected static function url(Url $url)
{
TimeRangePickerTrait::copyAllRangeParameters($url->getParams());
// TODO(ak): EL says "<div class="container" data-icinga-url="..." /> is enough",
// but this seems not to work for me
return '<div class="container" data-base-target="_main" data-last-update="-1" data-icinga-refresh="15"'
. ' data-icinga-url="' . $url->setParam('view', 'compact') . '"></div>';
}
/**
* Get the current response view
*
* @return View
*/
protected static function getView()
{
return Icinga::app()->getViewRenderer()->view;
}
}

View file

@ -12,6 +12,11 @@ class GraphiteChart
protected $from = '-4hours';
/**
* @var string
*/
protected $until;
protected $showLegend = true;
protected $height = 200;
@ -70,9 +75,32 @@ class GraphiteChart
return $this->from;
}
/**
* Get {@link until}
*
* @return string
*/
public function getUntil()
{
return $this->until;
}
/**
* Set {@link until}
*
* @param string $until
*
* @return $this
*/
public function setUntil($until)
{
$this->until = $until;
return $this;
}
protected function getParams()
{
return array(
$params = [
'height' => $this->height,
'width' => $this->width,
'_salt' => time() . '.000',
@ -90,7 +118,13 @@ class GraphiteChart
// 'hideYAxis' => 'true',
// 'format' => 'svg',
// 'pieMode' => 'average',
);
];
if ($this->until !== null) {
$params['until'] = $this->until;
}
return $params;
}
public function getUrl()

View file

@ -0,0 +1,38 @@
<?php
namespace Icinga\Module\Graphite\ProvidedHook\Monitoring;
use Icinga\Module\Graphite\EmbedGraphs;
use Icinga\Module\Graphite\Web\Controller\TimeRangePickerTrait;
use Icinga\Module\Monitoring\Hook\DetailviewExtensionHook;
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\MonitoredObject;
use Icinga\Module\Monitoring\Object\Service;
class DetailviewExtension extends DetailviewExtensionHook
{
use TimeRangePickerTrait;
public function getHtmlForObject(MonitoredObject $object)
{
switch ($object->getType()) {
case 'host':
/** @var Host $object */
return $this->getGeneric() . EmbedGraphs::host($object->getName());
case 'service':
/** @var Service $object */
return $this->getGeneric() . EmbedGraphs::service($object->getHost()->getName(), $object->getName());
}
}
/**
* Get monitored object type independend HTML to use
*
* @return string
*/
protected function getGeneric()
{
$this->handleTimeRangePickerRequest();
return '<h2>' . mt('graphite', 'Graphs') . '</h2>' . $this->renderTimeRangePicker($this->getView());
}
}

View file

@ -1,17 +0,0 @@
<?php
namespace Icinga\Module\Graphite\ProvidedHook\Monitoring;
use Icinga\Module\Monitoring\Hook\HostActionsHook;
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Web\Url;
class HostActions extends HostActionsHook
{
public function getActionsForHost(Host $host)
{
return array(
'Graphite' => Url::fromPath('graphite/show/host', array('host' => $host->host_name))
);
}
}

View file

@ -1,22 +0,0 @@
<?php
namespace Icinga\Module\Graphite\ProvidedHook\Monitoring;
use Icinga\Module\Monitoring\Hook\ServiceActionsHook;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Web\Url;
class ServiceActions extends ServiceActionsHook
{
public function getActionsForService(Service $service)
{
return array(
'Graphite' => Url::fromPath(
'graphite/show/service',
array(
'host' => $service->host_name,
'service' => $service->service_description,
))
);
}
}

View file

@ -53,7 +53,10 @@ trait TimeRangePickerTrait
$view->translate('Custom', 'TimeRangePicker'),
$url->with(TimeRangePicker::getRangeCustomizationParameter(), '1'),
null,
['class' => 'button-link']
[
'class' => 'button-link',
'data-base-target' => '_self'
]
);
}

View file

@ -50,6 +50,16 @@ ul.legend {
}
}
div.graphs-host {
display: inline-block;
vertical-align: top;
margin-right: 2em;
> div.container[data-icinga-url] {
width: 300px;
}
}
form[name=form_timerangepickercommon_graphite] select {
width: 7.5em;
margin-right: 0.25em;

View file

@ -1,5 +1,4 @@
<?php
$this->provideHook('monitoring/HostActions');
$this->provideHook('monitoring/ServiceActions');
/** @var \Icinga\Application\Modules\Module $this */
$this->provideHook('monitoring/DetailviewExtension');