Merge remote-tracking branch 'origin/master' into feature/refine-ui-for-rc1-9361

Conflicts:
	modules/monitoring/application/views/scripts/host/history.phtml
	modules/monitoring/application/views/scripts/list/services.phtml
This commit is contained in:
Thomas Gelf 2015-06-18 11:26:51 +02:00
commit 3462f0ee9a
23 changed files with 425 additions and 228 deletions

View File

@ -86,12 +86,8 @@ class ConfigController extends Controller
if ($this->firstAllowedAction === null) {
throw new SecurityException($this->translate('No permission for application configuration'));
}
$action = $this->getTabs()->get($this->firstAllowedAction);
if (substr($action->getUrl()->getPath(), 0, 7) === 'config/') {
$this->forward($this->firstAllowedAction);
} else {
$this->redirectNow($action->getUrl());
}
$this->redirectNow($this->getTabs()->get($this->firstAllowedAction)->getUrl());
}
/**

View File

@ -1,7 +1,6 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use \Exception;
use Icinga\Exception\ProgrammingError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Dashboard\DashletForm;

View File

@ -1,7 +1,6 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use \Exception;
use Icinga\Application\Logger;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\Data\Reducible;

View File

@ -1,7 +1,6 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use \Exception;
use Icinga\Application\Logger;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;

View File

@ -1,7 +1,6 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use \Exception;
use Icinga\Application\Config;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm;

View File

@ -5,29 +5,20 @@ use Icinga\Web\Notification;
use Icinga\Authentication\Manager as Auth;
if (Auth::getInstance()->isAuthenticated()): ?>
<div id="header">
<ul id="notifications"><?php
$moduleName = $this->layout()->moduleName;
if ($moduleName) {
$moduleClass = ' icinga-module module-' . $moduleName;
} else {
$moduleClass = '';
}
$refresh = '';
if ($this->layout()->autorefreshInterval) {
$refresh = ' data-icinga-refresh="' . $this->layout()->autorefreshInterval . '"';
}
$notifications = Notification::getInstance();
if ($notifications->hasMessages()) {
foreach ($notifications->getMessages() as $m) {
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
if (Auth::getInstance()->isAuthenticated()):
$moduleName = $this->layout()->moduleName;
if ($moduleName) {
$moduleClass = ' icinga-module module-' . $moduleName;
} else {
$moduleClass = '';
}
}
?></ul>
$refresh = '';
if ($this->layout()->autorefreshInterval) {
$refresh = ' data-icinga-refresh="' . $this->layout()->autorefreshInterval . '"';
}
?>
<div id="header">
<div id="logo" data-base-target="_main">
<?= $this->qlink(
'',
@ -56,6 +47,16 @@ if ($notifications->hasMessages()) {
<div id="col3" class="container">
</div>
</div><!-- END of main -->
<div id="footer">
<ul id="notifications"><?php
$notifications = Notification::getInstance();
if ($notifications->hasMessages()) {
foreach ($notifications->getMessages() as $m) {
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
}
}
?></ul>
</div>
<?php else: ?>
<?= $this->render('inline.phtml') ?>
<?php endif ?>

View File

@ -1,8 +1,6 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use \Zend_View_Helper_FormElement;
/**
* Render number input controls
*/

View File

@ -18,6 +18,7 @@ class StyleSheet
'css/icinga/layout-structure.less',
'css/icinga/menu.less',
'css/icinga/header-elements.less',
'css/icinga/footer-elements.less',
'css/icinga/main-content.less',
'css/icinga/tabs.less',
'css/icinga/forms.less',

View File

@ -374,9 +374,29 @@ class UrlParams
}
}
public function toArray()
/**
* Return the parameters of this url as sequenced or associative array
*
* @param bool $sequenced
*
* @return array
*/
public function toArray($sequenced = true)
{
return $this->params;
if ($sequenced) {
return $this->params;
}
$params = array();
foreach ($this->params as $param) {
if ($param[1] === true) {
$params[] = $param[0];
} else {
$params[$param[0]] = $param[1];
}
}
return $params;
}
public function toString($separator = null)

View File

@ -309,13 +309,14 @@ EOT;
private function renderRefreshTab()
{
$url = Url::fromRequest()->without('renderLayout');
$tab = $this->get($this->getActiveName());
if ($tab !== null) {
$url = Url::fromRequest($tab->getUrl()->getParams()->toArray(false))->without('renderLayout');
$label = $this->view()->escape(
$tab->getLabel()
);
} else {
$url = Url::fromRequest()->without('renderLayout');
}
if (! empty($label)) {

View File

@ -1,7 +1,6 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use \Zend_Controller_Router_Route;
use Icinga\Application\Icinga;
if (Icinga::app()->isCli()) {

View File

@ -63,6 +63,54 @@ class Monitoring_HostController extends MonitoredObjectController
parent::showAction();
}
/**
* List a host's services
*/
public function servicesAction()
{
$this->setAutorefreshInterval(10);
$this->getTabs()->activate('services');
$query = $this->backend->select()->from('serviceStatus', array(
'host_name',
'host_display_name',
'host_state',
'host_state_type',
'host_last_state_change',
'host_address',
'host_handled',
'service_description',
'service_display_name',
'service_state',
'service_in_downtime',
'service_acknowledged',
'service_handled',
'service_output',
'service_perfdata',
'service_attempt',
'service_last_state_change',
'service_icon_image',
'service_icon_image_alt',
'service_is_flapping',
'service_state_type',
'service_handled',
'service_severity',
'service_last_check',
'service_notifications_enabled',
'service_action_url',
'service_notes_url',
'service_last_comment',
'service_last_ack',
'service_last_downtime',
'service_active_checks_enabled',
'service_passive_checks_enabled',
'current_check_attempt' => 'service_current_check_attempt',
'max_check_attempts' => 'service_max_check_attempts'
));
$this->applyRestriction('monitoring/filter/objects', $query);
$this->view->services = $query->where('host_name', $this->object->getName());
$this->view->object = $this->object;
}
/**
* Acknowledge a host problem
*/

View File

@ -1,12 +1,7 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Module\Monitoring\Object\MonitoredObject;
use Icinga\Web\Hook;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabs;
use Icinga\Web\Widget\Tabextension\OutputFormat;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Module\Monitoring\Backend;
use Icinga\Module\Monitoring\Controller;
@ -22,34 +17,6 @@ class Monitoring_ShowController extends Controller
*/
protected $backend;
/**
* @var Hook\GrapherHook
*/
protected $grapher;
/**
* Initialize the controller
*/
public function init()
{
$this->view->object = MonitoredObject::fromParams($this->params);
if ($this->view->object && $this->view->object->fetch() === false) {
throw new Zend_Controller_Action_Exception($this->translate('Host or service not found'));
}
if (Hook::has('ticket')) {
$this->view->tickets = Hook::first('ticket');
}
if (Hook::has('grapher')) {
$this->grapher = Hook::first('grapher');
if ($this->grapher && ! $this->grapher->hasPreviews()) {
$this->grapher = null;
}
}
$this->createTabs();
}
/**
* @deprecated
*/
@ -66,37 +33,16 @@ class Monitoring_ShowController extends Controller
$this->redirectNow(Url::fromRequest()->setPath('monitoring/host/show'));
}
/**
* @deprecated
*/
public function historyAction()
{
$this->getTabs()->activate('history');
$this->view->object->fetchEventHistory();
$this->view->history = $this->view->object->eventhistory;
$this->handleFormatRequest($this->view->object->eventhistory);
$this->fetchHostStats();
if ($this->params->has('service')) {
$this->redirectNow(Url::fromRequest()->setPath('monitoring/service/history'));
}
$this->setupLimitControl(50);
$this->setupPaginationControl($this->view->history, 50);
}
protected function fetchHostStats()
{
$this->view->stats = $this->backend->select()->from('statusSummary', array(
'services_total',
'services_ok',
'services_problem',
'services_problem_handled',
'services_problem_unhandled',
'services_critical',
'services_critical_unhandled',
'services_critical_handled',
'services_warning',
'services_warning_unhandled',
'services_warning_handled',
'services_unknown',
'services_unknown_unhandled',
'services_unknown_handled',
'services_pending',
))->where('service_host_name', $this->params->get('host'))->getQuery()->fetchRow();
$this->redirectNow(Url::fromRequest()->setPath('monitoring/host/history'));
}
public function contactAction()
@ -162,78 +108,4 @@ class Monitoring_ShowController extends Controller
$this->view->contact = $contact;
$this->view->contactName = $contactName;
}
/**
* Creating tabs for this controller
* @return Tabs
*/
protected function createTabs()
{
if (($object = $this->view->object) === null) {
return;
}
if ($object->getType() === $object::TYPE_HOST) {
$isService = false;
$params = array(
'host' => $object->getName()
);
} else {
$isService = true;
$params = array(
'host' => $object->getHost()->getName(),
'service' => $object->getName()
);
}
$tabs = $this->getTabs();
$tabs->add(
'host',
array(
'title' => sprintf(
$this->translate('Show detailed information for host %s'),
$isService ? $object->getHost()->getName() : $object->getName()
),
'label' => $this->translate('Host'),
'icon' => 'host',
'url' => 'monitoring/show/host',
'urlParams' => $params,
)
);
if ($isService) {
$tabs->add(
'service',
array(
'title' => sprintf(
$this->translate('Show detailed information for service %s on host %s'),
$object->getName(),
$object->getHost()->getName()
),
'label' => $this->translate('Service'),
'icon' => 'service',
'url' => 'monitoring/show/service',
'urlParams' => $params,
)
);
}
if ($this->backend->hasQuery('eventHistory')) {
$tabs->add(
'history',
array(
'title' => $isService
? sprintf(
$this->translate('Show all event records of service %s on host %s'),
$object->getName(),
$object->getHost()->getName()
)
: sprintf($this->translate('Show all event records of host %s'), $object->getName())
,
'label' => $this->translate('History'),
'icon' => 'rewind',
'url' => 'monitoring/show/history',
'urlParams' => $params,
)
);
}
$tabs->extend(new OutputFormat())
->extend(new DashboardAction());
}
}

View File

@ -20,6 +20,12 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract
public function perfdata($perfdataStr, $compact = false, $limit = 0, $color = Perfdata::PERFDATA_OK)
{
$pieChartData = PerfdataSet::fromString($perfdataStr)->asArray();
uasort(
$pieChartData,
function($a, $b) {
return $a->worseThan($b) ? -1 : ($b->worseThan($a) ? 1 : 0);
}
);
$results = array();
$keys = array('', 'label', 'value', 'min', 'max', 'warn', 'crit');
$columns = array();

View File

@ -3,17 +3,12 @@ use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
$self = $this;
$hostContext = $object->getType() === 'host';
if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
<?php if ($hostContext): ?>
<?= $this->render('partials/host/object-header.phtml'); ?>
<?php else: ?>
<?= $this->render('partials/service/object-header.phtml'); ?>
<?php endif ?>
<h1><?= $this->translate('This Object\'s Event History'); ?></h1>
<h1><?= $this->translate('This Host\'s Event History'); ?></h1>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
@ -24,7 +19,7 @@ if (! $this->compact): ?>
<?php
if (count($history) === 0) {
echo $this->translate('No history available for this object') . '</div>';
echo $this->translate('No history events found matching the filter') . '</div>';
return;
}
?>
@ -143,9 +138,8 @@ $output = $this->tickets ? preg_replace_callback(
?>
<?php if ($isService): ?>
<?= sprintf(
'%s: %s',
$event->host_display_name,
$hostContext ? $this->qlink(
$this->translate('%s on %s', 'Service running on host'),
$this->qlink(
$event->service_display_name,
'monitoring/show/service',
array(
@ -157,7 +151,8 @@ $output = $this->tickets ? preg_replace_callback(
$event->service_display_name,
$event->host_display_name
))
) : $this->escape($event->service_display_name)
),
$event->host_display_name
) ?>
<?php else: ?>
<?= $this->escape($event->host_name); ?>

View File

@ -0,0 +1,18 @@
<div class="controls">
<?php if (! $this->compact): ?>
<?= $this->tabs; ?>
<?php endif ?>
<?= $this->render('partials/host/object-header.phtml') ?>
<?= $this->render('partials/host/servicesummary.phtml') ?>
</div>
<?= $this->partial(
'list/services.phtml',
'monitoring',
array(
'compact' => true,
'showHost' => false,
'services' => $services,
'addColumns' => array(),
'baseTarget' => '_self'
)
); ?>

View File

@ -25,7 +25,7 @@ if (count($services) === 0) {
return;
}
?>
<table data-base-target="_next"
<table data-base-target="<?= isset($baseTarget) ? $baseTarget : '_next'; ?>"
class="action multiselect <?php if ($this->compact): ?> compact<?php endif ?>" style="table-layout: auto;"
data-icinga-multiselect-url="<?= $this->href("monitoring/services/show") ?>"
data-icinga-multiselect-data="service,host">
@ -81,7 +81,7 @@ if (count($services) === 0) {
'class' => 'rowaction'
)
) ?><br />
<div class="sparkline-box"><?= $this->perfdata($service->service_perfdata, true, 8) ?> </div>
<div class="sparkline-box"><?= $this->perfdata($service->service_perfdata, true, 5) ?> </div>
<p class="pluginoutput"><?= $this->escape($this->ellipsis($service->service_output, 10000)) ?></p>
</td>
<?php foreach($this->addColumns as $col): ?>

View File

@ -0,0 +1,151 @@
<?php
use Icinga\Module\Monitoring\Object\Service;
$self = $this;
if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
<?= $this->render('partials/service/object-header.phtml'); ?>
<h1><?= $this->translate('This Service\'s Event History'); ?></h1>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?= $this->filterEditor; ?>
</div>
<?php endif ?>
<div class="content">
<?php
if (count($history) === 0) {
echo $this->translate('No history events found matching the filter') . '</div>';
return;
}
?>
<?php
function contactsLink($match, $view) {
$links = array();
foreach (preg_split('/,\s/', $match[1]) as $contact) {
$links[] = $view->qlink(
$contact,
'monitoring/show/contact',
array('contact_name' => $contact),
array('title' => sprintf($view->translate('Show detailed information about %s'), $contact))
);
}
return '[' . implode(', ', $links) . ']';
}
?>
<table data-base-target="_next" class="action objecthistory">
<tbody>
<?php foreach ($history as $event): ?>
<?php
$stateClass = 'invalid';
switch ($event->type) {
case 'notify':
$icon = 'notification';
$title = $this->translate('Notification');
$stateClass = Service::getStateText($event->state);
$msg = preg_replace_callback(
'/^\[([^\]]+)\]/',
function($match) use ($self) { return contactsLink($match, $self); },
$this->escape($event->output)
);
break;
case 'comment':
$icon = 'comment';
$title = $this->translate('Comment');
$msg = $this->escape($event->output);
break;
case 'comment_deleted':
$icon = 'remove';
$title = $this->translate('Comment deleted');
$msg = $this->escape($event->output);
break;
case 'ack':
$icon = 'acknowledgement';
$title = $this->translate('Acknowledge');
$msg = $this->escape($event->output);
break;
case 'ack_deleted':
$icon = 'remove';
$title = $this->translate('Ack removed');
$msg = $this->escape($event->output);
break;
case 'dt_comment':
$icon = 'in_downtime';
$title = $this->translate('In Downtime');
$msg = $this->escape($event->output);
break;
case 'dt_comment_deleted':
$icon = 'remove';
$title = $this->translate('Downtime removed');
$msg = $this->escape($event->output);
break;
case 'flapping':
$icon = 'flapping';
$title = $this->translate('Flapping');
$msg = $this->escape($event->output);
break;
case 'flapping_deleted':
$icon = 'remove';
$title = $this->translate('Flapping stopped');
$msg = $this->escape($event->output);
break;
case 'hard_state':
$msg = '[ ' . $event->attempt . '/' . $event->max_attempts . ' ] ' . $this->escape($event->output);
$stateClass = Service::getStateText($event->state);
$icon = 'attention-alt';
$title = Service::getStateText($event->state);
break;
case 'soft_state':
$icon = 'spinner';
$msg = '[ ' . $event->attempt . '/' . $event->max_attempts . ' ] ' . $this->escape($event->output);
$stateClass = Service::getStateText($event->state);
$title = Service::getStateText($event->state);
break;
case 'dt_start':
$icon = 'downtime_start';
$title = $this->translate('Downtime Start');
$msg = $this->escape($event->output);
break;
case 'dt_end':
$icon = 'downtime_end';
$title = $this->translate('Downtime End');
$msg = $this->escape($event->output);
break;
}
?>
<tr class="state <?= $stateClass; ?>">
<td class="state">
<strong><?= $this->escape($title); ?></strong>
<br>
<?= date('d.m. H:i', $event->timestamp); ?>
</td>
<td><?php
$output = $this->tickets ? preg_replace_callback(
$this->tickets->getPattern(),
array($this->tickets, 'createLink'),
$msg
) : $msg;
?>
<?= sprintf(
$this->translate('%s on %s', 'Service running on host'),
$this->escape($event->service_display_name),
$event->host_display_name
) ?>
<br>
<div>
<?= $this->icon($icon, $title); ?> <?= empty($msg) ? '' : $msg; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -7,6 +7,7 @@ use Icinga\Util\Format;
use InvalidArgumentException;
use Icinga\Exception\ProgrammingError;
use Icinga\Web\Widget\Chart\InlinePie;
use Icinga\Module\Monitoring\Object\Service;
use Zend_Controller_Front;
class Perfdata
@ -453,4 +454,60 @@ class Perfdata
);
return $parts;
}
/**
* Return the state indicated by this perfdata
*
* @see Service
*
* @return int
*/
public function getState()
{
if ($this->value === null) {
return Service::STATE_UNKNOWN;
}
if (! ($this->criticalThreshold === null
|| $this->value < $this->criticalThreshold)) {
return Service::STATE_CRITICAL;
}
if (! ($this->warningThreshold === null
|| $this->value < $this->warningThreshold)) {
return Service::STATE_WARNING;
}
return Service::STATE_OK;
}
/**
* Return whether the state indicated by this perfdata is worse than
* the state indicated by the other perfdata
* CRITICAL > UNKNOWN > WARNING > OK
*
* @param Perfdata $rhs the other perfdata
*
* @return bool
*/
public function worseThan(Perfdata $rhs)
{
if (($state = $this->getState()) === ($rhsState = $rhs->getState())) {
return $this->getPercentage() > $rhs->getPercentage();
}
if ($state === Service::STATE_CRITICAL) {
return true;
}
if ($state === Service::STATE_UNKNOWN) {
return $rhsState !== Service::STATE_CRITICAL;
}
if ($state === Service::STATE_WARNING) {
return $rhsState === Service::STATE_OK;
}
return false;
}
}

View File

@ -93,6 +93,19 @@ abstract class MonitoredObjectController extends Controller
$this->view->object = $this->object;
}
/**
* Show the history for a host or service
*/
public function historyAction()
{
$this->getTabs()->activate('history');
$this->view->history = $this->object->fetchEventHistory()->eventhistory;
$this->setupLimitControl(50);
$this->setupPaginationControl($this->view->history, 50);
$this->view->object = $this->object;
}
/**
* Handle a command form
*
@ -145,6 +158,9 @@ abstract class MonitoredObjectController extends Controller
$params = array(
'host' => $object->getName()
);
if ($this->params->has('service')) {
$params['service'] = $this->params->get('service');
}
} else {
$isService = true;
$params = array(
@ -165,14 +181,14 @@ abstract class MonitoredObjectController extends Controller
'urlParams' => $params
)
);
if ($isService) {
if ($isService || $this->params->has('service')) {
$tabs->add(
'service',
array(
'title' => sprintf(
$this->translate('Show detailed information for service %s on host %s'),
$object->getName(),
$object->getHost()->getName()
$isService ? $object->getName() : $this->params->get('service'),
$isService ? $object->getHost()->getName() : $object->getName()
),
'label' => $this->translate('Service'),
'icon' => 'service',
@ -181,6 +197,19 @@ abstract class MonitoredObjectController extends Controller
)
);
}
$tabs->add(
'services',
array(
'title' => sprintf(
$this->translate('List all services on host %s'),
$isService ? $object->getHost()->getName() : $object->getName()
),
'label' => $this->translate('Services'),
'icon' => 'services',
'url' => 'monitoring/host/services',
'urlParams' => $params
)
);
if ($this->backend->hasQuery('eventHistory')) {
$tabs->add(
'history',
@ -195,7 +224,7 @@ abstract class MonitoredObjectController extends Controller
,
'label' => $this->translate('History'),
'icon' => 'rewind',
'url' => 'monitoring/show/history',
'url' => $isService ? 'monitoring/service/history' : 'monitoring/host/history',
'urlParams' => $params
)
);

View File

@ -0,0 +1,46 @@
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
div#footer {
position: fixed;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 9999;
}
/** Notifications **/
#notifications {
margin: 0;
padding: 0;
}
#notifications > li {
list-style-type: none;
display: block;
border-top: 1px solid #999;
color: white;
line-height: 2.5em;
padding-left: 3em;
background-repeat: no-repeat;
background-position: 1em center;
}
#notifications > li.info {
background-color: @colorFormNotificationInfo;
}
#notifications > li.warning {
background-color: @colorWarningHandled;
}
#notifications > li.error {
background-color: @colorCritical;
background-image: url(../img/icons/error_white.png);
}
#notifications > li.success {
background-color: #fe6;
background-image: url(../img/icons/success.png);
color: #333;
}
/** END of Notifications **/

View File

@ -23,43 +23,6 @@ img.icon {
border: none;
}
/** Notifications **/
#notifications {
margin: 0;
padding: 0;
}
#notifications > li {
list-style-type: none;
display: block;
border-bottom: 1px solid #999;
color: white;
line-height: 2.5em;
padding-left: 3em;
background-repeat: no-repeat;
background-position: 1em center;
}
#notifications > li.info {
background-color: @colorFormNotificationInfo;
}
#notifications > li.warning {
background-color: @colorWarningHandled;
}
#notifications > li.error {
background-color: @colorCritical;
background-image: url(../img/icons/error_white.png);
}
#notifications > li.success {
background-color: #fe6;
background-image: url(../img/icons/success.png);
color: #333;
}
/** END of Notifications **/
/* TODO: Remove once there is no more module container */
.container > div > pre {
margin: 1em;