Merge branch 'master' into feature/user-and-group-management-8826

This commit is contained in:
Johannes Meyer 2015-05-18 11:28:07 +02:00
commit 524c449649
25 changed files with 604 additions and 185 deletions

View File

@ -128,8 +128,7 @@ class ConfigController extends Controller
$this->view->modules = Icinga::app()->getModuleManager()->select()
->from('modules')
->order('enabled', 'desc')
->order('name')
->paginate();
->order('name');
$this->setupLimitControl();
$this->setupPaginationControl($this->view->modules);
// TODO: Not working

View File

@ -51,7 +51,7 @@ class ListController extends Controller
. 'T[0-9]{2}(?::[0-9]{2}){2}(?:[\+\-][0-9]{2}:[0-9]{2})?)' // time
. ' - (?<loglevel>[A-Za-z]+) - (?<message>.*)(?!.)/msS' // loglevel, message
)));
$this->view->logData = $resource->select()->order('DESC')->paginate();
$this->view->logData = $resource->select()->order('DESC');
$this->setupLimitControl();
$this->setupPaginationControl($this->view->logData);

View File

@ -1,20 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Data;
/**
* Interface for browsing data
*/
interface Browsable
{
/**
* Paginate data
*
* @param int $itemsPerPage Number of items per page
* @param int $pageNumber Current page number
*
* @return Zend_Paginator
*/
public function paginate($itemsPerPage = null, $pageNumber = null);
}

View File

@ -5,4 +5,4 @@ namespace Icinga\Data;
use Countable;
interface QueryInterface extends Browsable, Fetchable, Filterable, Limitable, Sortable, Countable {};
interface QueryInterface extends Fetchable, Filterable, Limitable, Sortable, Countable {};

View File

@ -3,14 +3,12 @@
namespace Icinga\Data;
use Icinga\Application\Icinga;
use ArrayIterator;
use IteratorAggregate;
use Icinga\Data\Filter\Filter;
use Icinga\Web\Paginator\Adapter\QueryAdapter;
use Zend_Paginator;
use Exception;
use Icinga\Exception\IcingaException;
class SimpleQuery implements QueryInterface, Queryable
class SimpleQuery implements QueryInterface, Queryable, IteratorAggregate
{
/**
* Query data source
@ -102,6 +100,16 @@ class SimpleQuery implements QueryInterface, Queryable
*/
protected function init() {}
/**
* Return a iterable for this query's result
*
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->fetchAll());
}
/**
* Get the data source
*
@ -343,35 +351,6 @@ class SimpleQuery implements QueryInterface, Queryable
return $this->limitOffset;
}
/**
* Paginate data
*
* Auto-detects pagination parameters from request when unset
*
* @param int $itemsPerPage Number of items per page
* @param int $pageNumber Current page number
*
* @return Zend_Paginator
*/
public function paginate($itemsPerPage = null, $pageNumber = null)
{
if ($itemsPerPage === null || $pageNumber === null) {
// Detect parameters from request
$request = Icinga::app()->getFrontController()->getRequest();
if ($itemsPerPage === null) {
$itemsPerPage = $request->getParam('limit', 25);
}
if ($pageNumber === null) {
$pageNumber = $request->getParam('page', 0);
}
}
$this->limit($itemsPerPage, $pageNumber * $itemsPerPage);
$paginator = new Zend_Paginator(new QueryAdapter($this));
$paginator->setItemCountPerPage($itemsPerPage);
$paginator->setCurrentPageNumber($pageNumber);
return $paginator;
}
/**
* Retrieve an array containing all rows of the result set
*

View File

@ -3,15 +3,13 @@
namespace Icinga\File;
use Icinga\Data\Browsable;
class Csv
{
protected $query;
protected function __construct() {}
public static function fromQuery(Browsable $query)
public static function fromQuery($query)
{
$csv = new Csv();
$csv->query = $query;

View File

@ -4,10 +4,12 @@
namespace Icinga\Web;
use Zend_Paginator;
use Icinga\Web\Controller\ModuleActionController;
use Icinga\Web\Widget\SortBox;
use Icinga\Web\Widget\Limiter;
use Icinga\Data\Sortable;
use Icinga\Data\QueryInterface;
use Icinga\Web\Paginator\Adapter\QueryAdapter;
use Icinga\Web\Controller\ModuleActionController;
use Icinga\Web\Widget\Limiter;
use Icinga\Web\Widget\SortBox;
/**
* This is the controller all modules should inherit from
@ -22,44 +24,56 @@ class Controller extends ModuleActionController
public function init()
{
parent::init();
$this->handleSortControlSubmit();
}
/**
* Check whether the sort control has been submitted and redirect using GET parameters
*/
protected function handleSortControlSubmit()
{
$request = $this->getRequest();
$url = Url::fromRequest();
if (! $request->isPost()) {
return;
}
if ($request->isPost() && ($sort = $request->getPost('sort'))) {
if (($sort = $request->getPost('sort'))) {
$url = Url::fromRequest();
$url->setParam('sort', $sort);
if ($dir = $request->getPost('dir')) {
if (($dir = $request->getPost('dir'))) {
$url->setParam('dir', $dir);
} else {
$url->removeParam('dir');
}
$this->redirectNow($url);
}
}
/**
* Create a SortBox widget at the `sortBox' view property
* Create a SortBox widget and apply its sort rules on the given query
*
* In case the current view has been requested as compact this method does nothing.
* The widget is set on the `sortBox' view property only if the current view has not been requested as compact
*
* @param array $columns An array containing the sort columns, with the
* submit value as the key and the label as the value
* @param Sortable $query Query to set on the newly created SortBox
* @param Sortable $query Query to apply the user chosen sort rules on
*
* @return $this
*/
protected function setupSortControl(array $columns, Sortable $query = null)
{
$request = $this->getRequest();
$sortBox = SortBox::create('sortbox-' . $request->getActionName(), $columns);
$sortBox->setRequest($request);
if ($query) {
$sortBox->setQuery($query);
$sortBox->handleRequest($request);
}
if (! $this->view->compact) {
$req = $this->getRequest();
$this->view->sortBox = $sortBox = SortBox::create(
'sortbox-' . $req->getActionName(),
$columns
)->setRequest($req);
if ($query !== null) {
$sortBox->setQuery($query);
}
$sortBox->handleRequest();
$this->view->sortBox = $sortBox;
}
return $this;
@ -70,29 +84,43 @@ class Controller extends ModuleActionController
*
* In case the current view has been requested as compact this method does nothing.
*
* @param int $itemsPerPage Default number of items per page
*
* @return $this
*/
protected function setupLimitControl()
protected function setupLimitControl($itemsPerPage = 25)
{
if (! $this->view->compact) {
$this->view->limiter = new Limiter();
$this->view->limiter->setDefaultLimit($itemsPerPage);
}
return $this;
}
/**
* Set the view property `paginator' to the given Zend_Paginator
* Apply the given page limit and number on the given query and setup a paginator for it
*
* In case the current view has been requested as compact this method does nothing.
* The $itemsPerPage and $pageNumber parameters are only applied if not available in the current request.
* The paginator is set on the `paginator' view property only if the current view has not been requested as compact.
*
* @param Zend_Paginator $paginator The Zend_Paginator for which to show a pagination control
* @param QueryInterface $query The query to create a paginator for
* @param int $itemsPerPage Default number of items per page
* @param int $pageNumber Default page number
*
* @return $this
*/
protected function setupPaginationControl(Zend_Paginator $paginator)
protected function setupPaginationControl(QueryInterface $query, $itemsPerPage = 25, $pageNumber = 0)
{
$request = $this->getRequest();
$limit = $request->getParam('limit', $itemsPerPage);
$page = $request->getParam('page', $pageNumber);
$query->limit($limit, $page * $limit);
if (! $this->view->compact) {
$paginator = new Zend_Paginator(new QueryAdapter($query));
$paginator->setItemCountPerPage($limit);
$paginator->setCurrentPageNumber($page);
$this->view->paginator = $paginator;
}

View File

@ -38,6 +38,28 @@ class Form extends Zend_Form
*/
const DEFAULT_SUFFIX = '_default';
/**
* The type of the notification for the error
*/
const NOTIFICATION_ERROR = 0;
/**
* The type of the notification for the warning
*/
const NOTIFICATION_WARNING = 2;
/**
* The type of the notification for the info
*/
const NOTIFICATION_INFO = 4;
/**
* The notifications of the form
*
* @var array
*/
protected $notifications = array();
/**
* Whether this form has been created
*
@ -1009,6 +1031,7 @@ class Form extends Zend_Form
}
$this->addDecorator('FormErrors', array('onlyCustomFormErrors' => true))
->addDecorator('FormNotifications')
->addDecorator('FormDescriptions')
->addDecorator('FormElements')
//->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form'))
@ -1208,4 +1231,57 @@ class Form extends Zend_Form
throw new SecurityException('No permission for %s', $permission);
}
}
/**
* Return all form notifications
*
* @return array
*/
public function getNotifications()
{
return $this->notifications;
}
/**
* Add a typed message to the notifications
*
* @param string $message The message which would be displayed to the user
*
* @param int $type The type of the message notification
*/
public function addNotification($message, $type = self::NOTIFICATION_ERROR)
{
$this->notifications[$message] = $type;
$this->markAsError();
}
/**
* Add a error message to notifications
*
* @param string $message
*/
public function error($message)
{
$this->addNotification($message, $type = self::NOTIFICATION_ERROR);
}
/**
* Add a warning message to notifications
*
* @param string $message
*/
public function warning($message)
{
$this->addNotification($message, $type = self::NOTIFICATION_WARNING);
}
/**
* Add a info message to notifications
*
* @param string $message
*/
public function info($message)
{
$this->addNotification($message, $type = self::NOTIFICATION_INFO);
}
}

View File

@ -0,0 +1,95 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Form\Decorator;
use Zend_Form_Decorator_Abstract;
use Icinga\Web\Form as Form;
/**
* Decorator to add a list of notifications at the top of a form
*/
class FormNotifications extends Zend_Form_Decorator_Abstract
{
/**
* Render form descriptions
*
* @param string $content The html rendered so far
*
* @return string The updated html
*/
public function render($content = '')
{
$form = $this->getElement();
if (! $form instanceof Form) {
return $content;
}
$view = $form->getView();
if ($view === null) {
return $content;
}
$notifications = $this->recurseForm($form);
if (empty($notifications)) {
return $content;
}
$html = '<ul class="form-notifications">';
asort($notifications);
foreach ($notifications as $message => $type) {
$html .= '<li class="'.self::getNotificationTypeName($type).'">' . $view->escape($message) . '</li>';
}
switch ($this->getPlacement()) {
case self::APPEND:
return $content . $html . '</ul>';
case self::PREPEND:
return $html . '</ul>' . $content;
}
}
/**
* Recurse the given form and return the notifications for it and all of its subforms
*
* @param Form $form The form to recurse
*
* @return array
*/
protected function recurseForm(Form $form)
{
$notifications = $form->getNotifications();
foreach ($form->getSubForms() as $subForm) {
$notifications = $notifications + $this->recurseForm($subForm);
}
return $notifications;
}
/**
* Get the readable type name of the notification
*
* @param $type Type of the message
*
* @return string
*/
public static function getNotificationTypeName($type)
{
switch ($type) {
case Form::NOTIFICATION_ERROR:
return 'error';
break;
case Form::NOTIFICATION_WARNING:
return 'warning';
break;
case Form::NOTIFICATION_INFO:
return 'info';
break;
default:
return 'unknown';
}
}
}

View File

@ -21,6 +21,8 @@ class Limiter extends AbstractWidget
private $pages;
private $default;
public function setUrl(Url $url)
{
$this->url = $url;
@ -39,13 +41,19 @@ class Limiter extends AbstractWidget
return $this;
}
public function setDefaultLimit($limit)
{
$this->default = $limit;
return $this;
}
public function render()
{
if ($this->url === null) {
$this->url = Url::fromRequest();
}
$currentLimit = (int) $this->url->getParam('limit', 25); // Default??
$currentLimit = (int) $this->url->getParam('limit', $this->default);
$availableLimits = array(
10 => '10',
25 => '25',

View File

@ -70,7 +70,7 @@ class Monitoring_AlertsummaryController extends Controller
'notification_state'
)
);
$this->view->notifications = $query->paginate();
$this->view->notifications = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->notifications);
@ -493,7 +493,7 @@ class Monitoring_AlertsummaryController extends Controller
$query->order('notification_start_time', 'desc');
return $query->paginate(5);
return $query->limit(5);
}
/**

View File

@ -97,7 +97,7 @@ class Monitoring_ListController extends Controller
), $this->extraColumns()));
$this->filterQuery($query);
$this->applyRestriction('monitoring/hosts/filter', $query);
$this->view->hosts = $query->paginate();
$this->view->hosts = $query;
$this->view->stats = $this->backend->select()->from('statusSummary', array(
'hosts_total',
@ -181,7 +181,7 @@ class Monitoring_ListController extends Controller
$query = $this->backend->select()->from('serviceStatus', $columns);
$this->filterQuery($query);
$this->applyRestriction('monitoring/services/filter', $query);
$this->view->services = $query->paginate();
$this->view->services = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->services);
@ -246,7 +246,7 @@ class Monitoring_ListController extends Controller
'service_display_name'
));
$this->filterQuery($query);
$this->view->downtimes = $query->paginate();
$this->view->downtimes = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->downtimes);
@ -292,7 +292,7 @@ class Monitoring_ListController extends Controller
'service_display_name'
));
$this->filterQuery($query);
$this->view->notifications = $query->paginate();
$this->view->notifications = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->notifications);
@ -326,7 +326,7 @@ class Monitoring_ListController extends Controller
'contact_notify_host_downtime',
));
$this->filterQuery($query);
$this->view->contacts = $query->paginate();
$this->view->contacts = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->contacts);
@ -438,7 +438,7 @@ class Monitoring_ListController extends Controller
'service_display_name'
));
$this->filterQuery($query);
$this->view->comments = $query->paginate();
$this->view->comments = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->comments);
@ -498,7 +498,7 @@ class Monitoring_ListController extends Controller
// TODO(el): Can't default to the sort rules of the data view because it's meant for both host groups and
// service groups. We should separate them.
$this->filterQuery($query);
$this->view->servicegroups = $query->paginate();
$this->view->servicegroups = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->servicegroups);
@ -528,6 +528,12 @@ class Monitoring_ListController extends Controller
'hosts_down_handled',
'hosts_down_unhandled',
'hosts_pending',
'hosts_up_last_state_change',
'hosts_pending_last_state_change',
'hosts_down_last_state_change_handled',
'hosts_unreachable_last_state_change_handled',
'hosts_down_last_state_change_unhandled',
'hosts_unreachable_last_state_change_unhandled',
'services_ok',
'services_unknown_handled',
'services_unknown_unhandled',
@ -549,7 +555,7 @@ class Monitoring_ListController extends Controller
// TODO(el): Can't default to the sort rules of the data view because it's meant for both host groups and
// service groups. We should separate them.
$this->filterQuery($query);
$this->view->hostgroups = $query->paginate();
$this->view->hostgroups = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->hostgroups);
@ -588,7 +594,7 @@ class Monitoring_ListController extends Controller
));
$this->filterQuery($query);
$this->view->history = $query->paginate();
$this->view->history = $query;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->history);

View File

@ -70,12 +70,12 @@ class Monitoring_ShowController extends Controller
{
$this->getTabs()->activate('history');
$this->view->object->fetchEventHistory();
$this->view->history = $this->view->object->eventhistory->getQuery()->paginate($this->params->get('limit', 50));
$this->view->history = $this->view->object->eventhistory;
$this->handleFormatRequest($this->view->object->eventhistory);
$this->fetchHostStats();
$this->setupLimitControl();
$this->setupPaginationControl($this->view->history);
$this->setupLimitControl(50);
$this->setupPaginationControl($this->view->history, 50);
}
public function servicesAction()
@ -154,7 +154,7 @@ class Monitoring_ShowController extends Controller
'command_name'
))->where('contact_id', $contact->contact_id);
$this->view->commands = $commands->paginate();
$this->view->commands = $commands;
$notifications = $this->backend->select()->from('notification', array(
'host_name',
@ -168,7 +168,7 @@ class Monitoring_ShowController extends Controller
));
$notifications->where('contact_object_id', $contact->contact_object_id);
$this->view->notifications = $notifications->paginate();
$this->view->notifications = $notifications;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->notifications);
}

View File

@ -215,8 +215,14 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
->setFeature($feature)
->setEnabled($enabled);
$this->getTransport($this->request)->send($toggleFeature);
if ($this->status->{$feature} != $enabled) {
Notification::success($enabled
? $this->translate('Enabling feature..')
: $this->translate('Disabling feature..')
);
}
}
Notification::success($this->translate('Toggling feature..'));
return true;
}
}

View File

@ -3,6 +3,10 @@
namespace Icinga\Module\Monitoring\Forms\Config;
use Exception;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Web\Form;
use InvalidArgumentException;
use Icinga\Application\Config;
use Icinga\Exception\ConfigurationError;
@ -271,4 +275,63 @@ class BackendConfigForm extends ConfigForm
)
);
}
/**
* Validate the ido instance schema resource
*
* @param Form $form
* @param ConfigObject $resourceConfig
*
* @return bool Whether validation succeeded or not
*/
public static function isValidIdoSchema(Form $form, ConfigObject $resourceConfig)
{
try {
$resource = ResourceFactory::createResource($resourceConfig);
$result = $resource->select()->from('icinga_dbversion', array('version'));
$result->fetchOne();
} catch (Exception $e) {
$form->addError(
$form->translate(
'IDO schema validation failed, it looks like that the IDO schema is missing in the given database.'
)
);
return false;
}
return true;
}
/**
* Validate the ido instance availability
*
* @param Form $form
* @param ConfigObject $resourceConfig
*
* @return bool Whether validation succeeded or not
*/
public static function isValidIdoInstance(Form $form, ConfigObject $resourceConfig)
{
$resource = ResourceFactory::createResource($resourceConfig);
$result = $resource->select()->from('icinga_instances', array('instance_name'));
$instances = $result->fetchAll();
if (count($instances) === 1) {
return true;
} elseif (count($instances) > 1) {
$form->warning(
$form->translate(
'IDO instance validation failed, because there are multiple instances available.'
)
);
return false;
}
$form->error(
$form->translate(
'IDO instance validation failed, because there is no IDO instance available.'
)
);
return false;
}
}

View File

@ -3,6 +3,8 @@
namespace Icinga\Module\Monitoring\Forms\Setup;
use Icinga\Data\ConfigObject;
use Icinga\Module\Monitoring\Forms\Config\BackendConfigForm;
use Icinga\Web\Form;
use Icinga\Forms\Config\Resource\DbResourceForm;
@ -53,8 +55,21 @@ class IdoResourcePage extends Form
}
if (false === isset($data['skip_validation']) || $data['skip_validation'] == 0) {
if (false === DbResourceForm::isValidResource($this)) {
$this->addSkipValidationCheckbox();
$configObject = new ConfigObject($this->getValues());
if (false === DbResourceForm::isValidResource($this, $configObject)) {
$this->addSkipValidationCheckbox($this->translate(
'Check this to not to validate connectivity with the given database server'
));
return false;
} elseif (false === BackendConfigForm::isValidIdoSchema($this, $configObject)) {
$this->addSkipValidationCheckbox($this->translate(
'Check this to not to validate the ido schema'
));
return false;
} elseif (false === BackendConfigForm::isValidIdoInstance($this, $configObject)) {
$this->addSkipValidationCheckbox($this->translate(
'Check this to not to validate the ido instance'
));
return false;
}
}
@ -65,17 +80,21 @@ class IdoResourcePage extends Form
/**
* Add a checkbox to the form by which the user can skip the connection validation
*/
protected function addSkipValidationCheckbox()
protected function addSkipValidationCheckbox($description = '')
{
if (empty($description)) {
$description = $this->translate(
'Proceed without any further (custom) validation'
);
}
$this->addElement(
'checkbox',
'skip_validation',
array(
'required' => true,
'label' => $this->translate('Skip Validation'),
'description' => $this->translate(
'Check this to not to validate connectivity with the given database server'
)
'description' => $description
)
);
}

View File

@ -5,7 +5,7 @@
<?= $this->render('list/components/selectioninfo.phtml'); ?>
</div>
<div class="tinystatesummary">
<?= $comments->getTotalItemCount() ?> <?= $this->translate('Comments') ?>:
<?= count($comments) ?> <?= $this->translate('Comments') ?>:
</div>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>

View File

@ -9,7 +9,7 @@ if (! $this->compact): ?>
<?= $this->render('list/components/selectioninfo.phtml'); ?>
</div>
<div class="tinystatesummary">
<?= $downtimes->getTotalItemCount() ?> <?= $this->translate('Downtimes') ?>
<?= count($downtimes) ?> <?= $this->translate('Downtimes') ?>
</div>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>

View File

@ -1,4 +1,7 @@
<?php if (! $this->compact): ?>
<?php
use Icinga\Module\Monitoring\Object\Host;
if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
<?= $this->sortBox; ?>
@ -15,7 +18,7 @@ if (count($hostgroups) === 0) {
return;
}
?>
<table class="groupview" data-base-target="_next">
<table class="groupview action" data-base-target="_next">
<thead>
<th><?= $this->translate('Last Problem'); ?></th>
<th><?= $this->translate('Host Group'); ?></th>
@ -23,57 +26,40 @@ if (count($hostgroups) === 0) {
<th><?= $this->translate('Service States'); ?></th>
</thead>
<tbody>
<?php foreach ($hostgroups as $h): ?>
<tr href="<?= $this->href('monitoring/list/hosts', array('hostgroup_name' => $h->hostgroup_name)) ?>">
<?php if ($h->services_critical_last_state_change_unhandled): ?>
<td class="state change critical unhandled">
<strong><?= $this->translate('CRITICAL'); ?></strong>
<?php foreach ($hostgroups as $h):
if ($h->hosts_down_unhandled) {
$handled = false;
$state = Host::STATE_DOWN;
$lastStateChange = $h->hosts_down_last_state_change_unhandled;
} elseif ($h->hosts_unreachable_unhandled) {
$handled = false;
$state = Host::STATE_UNREACHABLE;
$lastStateChange = $h->hosts_unreachable_last_state_change_unhandled;
} else {
$handled = true;
if ($h->hosts_down_handled) {
$state = Host::STATE_DOWN;
$lastStateChange = $h->hosts_down_last_state_change_handled;
} elseif ($h->hosts_unreachable_handled) {
$state = Host::STATE_UNREACHABLE;
$lastStateChange = $h->hosts_unreachable_last_state_change_handled;
} elseif ($h->hosts_up) {
$state = Host::STATE_UP;
$lastStateChange = $h->hosts_up_last_state_change;
} else {
$state = Host::STATE_PENDING;
$lastStateChange = $h->hosts_pending_last_state_change;
}
}
?>
<tr class="state <?= Host::getStateText($state) ?><?= $handled ? ' handled' : '' ?>" href="<?=
$this->href('monitoring/list/hosts', array('hostgroup_name' => $h->hostgroup_name))
?>">
<td class="state">
<strong><?= Host::getStateText($state, true); ?></strong>
<br>
<?= $this->prefixedTimeSince($h->services_critical_last_state_change_unhandled); ?>
<?= $this->prefixedTimeSince($lastStateChange); ?>
</td>
<?php elseif ($h->services_unknown_last_state_change_unhandled): ?>
<td class="state change unknown unhandled">
<strong><?= $this->translate('UNKNOWN'); ?></strong>
<br>
<?= $this->prefixedTimeSince($h->services_unknown_last_state_change_unhandled); ?>
</td>
<?php elseif ($h->services_warning_last_state_change_unhandled): ?>
<td class="state change warning unhandled">
<strong><?= $this->translate('WARNING'); ?></strong>
<br>
<?= $this->prefixedTimeSince($h->services_warning_last_state_change_unhandled); ?>
</td>
<?php elseif ($h->services_critical_last_state_change_handled): ?>
<td class="state change critical">
<strong><?= $this->translate('CRITICAL'); ?></strong>
<br>
<?= $this->prefixedTimeSince($h->services_critical_last_state_change_handled); ?>
</td>
<?php elseif ($h->services_unknown_last_state_change_handled): ?>
<td class="state change unknown">
<strong><?= $this->translate('UNKNOWN'); ?></strong>
<br>
<?= $this->prefixedTimeSince($h->services_unknown_last_state_change_handled); ?>
</td>
<?php elseif ($h->services_warning_last_state_change_handled): ?>
<td class="state change warning">
<strong><?= $this->translate('WARNING'); ?></strong>
<br>
<?= $this->prefixedTimeSince($h->services_warning_last_state_change_handled); ?>
</td>
<?php elseif ($h->services_ok_last_state_change): ?>
<td class="state change ok">
<strong><?= $this->translate('OK'); ?></strong>
<br>
<?= $this->prefixedTimeSince($h->services_ok_last_state_change); ?>
</td>
<?php else: ?>
<td class="state change pending">
<strong><?= $this->translate('PENDING'); ?></strong>
<br>
<?= $this->prefixedTimeSince($h->services_pending_last_state_change); ?>
</td>
<?php endif ?>
<td class="groupname">
<?= $this->qlink(
$h->hostgroup_alias,
@ -93,7 +79,7 @@ if (count($hostgroups) === 0) {
))
); ?>
</td>
<td class="state">
<td>
<?php if ($h->services_ok): ?>
<span class="state ok">
<?= $this->qlink(

View File

@ -68,7 +68,18 @@ if ($object->getType() === $object::TYPE_HOST) {
<?php if ($object->check_execution_time): ?>
<tr>
<th><?= $this->translate('Check execution time') ?></th>
<td><?= $object->check_execution_time ?>s</td>
<td><?php
$matches = array();
if (preg_match(
'/(?<!.)([0-9]+\.[0-9]{4,})(?!.)/ms',
$object->check_execution_time,
$matches
)) {
printf('%.3f', (float) $matches[1]);
} else {
echo $object->check_execution_time;
}
?>s</td>
</tr>
<?php endif ?>
<?php if ($object->check_latency): ?>

View File

@ -11,17 +11,23 @@ class GroupSummaryQuery extends IdoQuery
protected $columnMap = array(
'hoststatussummary' => array(
'hosts_up' => 'SUM(CASE WHEN object_type = \'host\' AND state = 0 THEN 1 ELSE 0 END)',
'hosts_unreachable' => 'SUM(CASE WHEN object_type = \'host\' AND state = 2 THEN 1 ELSE 0 END)',
'hosts_unreachable_handled' => 'SUM(CASE WHEN object_type = \'host\' AND state = 2 AND acknowledged + in_downtime != 0 THEN 1 ELSE 0 END)',
'hosts_unreachable_unhandled' => 'SUM(CASE WHEN object_type = \'host\' AND state = 2 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)',
'hosts_down' => 'SUM(CASE WHEN object_type = \'host\' AND state = 1 THEN 1 ELSE 0 END)',
'hosts_down_handled' => 'SUM(CASE WHEN object_type = \'host\' AND state = 1 AND acknowledged + in_downtime != 0 THEN 1 ELSE 0 END)',
'hosts_down_unhandled' => 'SUM(CASE WHEN object_type = \'host\' AND state = 1 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)',
'hosts_pending' => 'SUM(CASE WHEN object_type = \'host\' AND state = 99 THEN 1 ELSE 0 END)',
'hostgroup_name' => 'hostgroup_name',
'hostgroup_alias' => 'hostgroup_alias',
'hostgroup' => 'hostgroup'
'hosts_up' => 'SUM(CASE WHEN object_type = \'host\' AND state = 0 THEN 1 ELSE 0 END)',
'hosts_unreachable' => 'SUM(CASE WHEN object_type = \'host\' AND state = 2 THEN 1 ELSE 0 END)',
'hosts_unreachable_handled' => 'SUM(CASE WHEN object_type = \'host\' AND state = 2 AND acknowledged + in_downtime != 0 THEN 1 ELSE 0 END)',
'hosts_unreachable_unhandled' => 'SUM(CASE WHEN object_type = \'host\' AND state = 2 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)',
'hosts_down' => 'SUM(CASE WHEN object_type = \'host\' AND state = 1 THEN 1 ELSE 0 END)',
'hosts_down_handled' => 'SUM(CASE WHEN object_type = \'host\' AND state = 1 AND acknowledged + in_downtime != 0 THEN 1 ELSE 0 END)',
'hosts_down_unhandled' => 'SUM(CASE WHEN object_type = \'host\' AND state = 1 AND acknowledged + in_downtime = 0 THEN 1 ELSE 0 END)',
'hosts_pending' => 'SUM(CASE WHEN object_type = \'host\' AND state = 99 THEN 1 ELSE 0 END)',
'hosts_up_last_state_change' => 'MAX(CASE WHEN object_type = \'host\' AND state = 0 THEN state_change ELSE 0 END)',
'hosts_pending_last_state_change' => 'MAX(CASE WHEN object_type = \'host\' AND state = 99 THEN state_change ELSE 0 END)',
'hosts_down_last_state_change_handled' => 'MAX(CASE WHEN object_type = \'host\' AND state = 1 AND acknowledged + in_downtime != 0 THEN state_change ELSE 0 END)',
'hosts_unreachable_last_state_change_handled' => 'MAX(CASE WHEN object_type = \'host\' AND state = 2 AND acknowledged + in_downtime != 0 THEN state_change ELSE 0 END)',
'hosts_down_last_state_change_unhandled' => 'MAX(CASE WHEN object_type = \'host\' AND state = 1 AND acknowledged + in_downtime = 0 THEN state_change ELSE 0 END)',
'hosts_unreachable_last_state_change_unhandled' => 'MAX(CASE WHEN object_type = \'host\' AND state = 2 AND acknowledged + in_downtime = 0 THEN state_change ELSE 0 END)',
'hostgroup_name' => 'hostgroup_name',
'hostgroup_alias' => 'hostgroup_alias',
'hostgroup' => 'hostgroup'
),
'servicestatussummary' => array(
'services_total' => 'SUM(CASE WHEN object_type = \'service\' THEN 1 ELSE 0 END)',

View File

@ -3,14 +3,13 @@
namespace Icinga\Module\Monitoring\DataView;
use Countable;
use ArrayIterator;
use IteratorAggregate;
use Icinga\Data\QueryInterface;
use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterMatch;
use Icinga\Data\Browsable;
use Icinga\Data\PivotTable;
use Icinga\Data\Sortable;
use Icinga\Data\ConnectionInterface;
use Icinga\Data\Filterable;
use Icinga\Exception\QueryException;
use Icinga\Web\Request;
use Icinga\Web\Url;
@ -19,12 +18,12 @@ use Icinga\Module\Monitoring\Backend\MonitoringBackend;
/**
* A read-only view of an underlying query
*/
abstract class DataView implements Browsable, Countable, Filterable, Sortable
abstract class DataView implements QueryInterface, IteratorAggregate
{
/**
* The query used to populate the view
*
* @var \Icinga\Data\SimpleQuery
* @var QueryInterface
*/
protected $query;
@ -59,6 +58,16 @@ abstract class DataView implements Browsable, Countable, Filterable, Sortable
{
}
/**
* Return a iterator for all rows of the result set
*
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->fetchAll());
}
/**
* Get the query name this data view relies on
*
@ -238,11 +247,11 @@ abstract class DataView implements Browsable, Countable, Filterable, Sortable
};
}
$order = $order === null ? (isset($sortColumns['order']) ? $sortColumns['order'] : static::SORT_ASC) : $order;
$order = (strtoupper($order) === static::SORT_ASC) ? 'ASC' : 'DESC';
$globalDefaultOrder = isset($sortColumns['order']) ? $sortColumns['order'] : static::SORT_ASC;
$globalDefaultOrder = (strtoupper($globalDefaultOrder) === static::SORT_ASC) ? 'ASC' : 'DESC';
foreach ($sortColumns['columns'] as $column) {
list($column, $direction) = $this->query->splitOrder($column);
list($column, $specificDefaultOrder) = $this->query->splitOrder($column);
if (! $this->isValidFilterTarget($column)) {
throw new QueryException(
mt('monitoring', 'The sort column "%s" is not allowed in "%s".'),
@ -250,7 +259,10 @@ abstract class DataView implements Browsable, Countable, Filterable, Sortable
get_class($this)
);
}
$this->query->order($column, $direction !== null ? $direction : $order);
$this->query->order(
$column,
$order === null && $specificDefaultOrder !== null ? $specificDefaultOrder : $globalDefaultOrder
);
}
$this->isSorted = true;
return $this;
@ -400,6 +412,114 @@ abstract class DataView implements Browsable, Countable, Filterable, Sortable
*/
public function count()
{
return count($this->query);
return $this->query->count();
}
/**
* Set a limit count and offset
*
* @param int $count Number of rows to return
* @param int $offset Start returning after this many rows
*
* @return self
*/
public function limit($count = null, $offset = null)
{
$this->query->limit($count, $offset);
return $this;
}
/**
* Whether a limit is set
*
* @return bool
*/
public function hasLimit()
{
return $this->query->hasLimit();
}
/**
* Get the limit if any
*
* @return int|null
*/
public function getLimit()
{
return $this->query->getLimit();
}
/**
* Whether an offset is set
*
* @return bool
*/
public function hasOffset()
{
return $this->query->hasOffset();
}
/**
* Get the offset if any
*
* @return int|null
*/
public function getOffset()
{
return $this->query->hasOffset();
}
/**
* Retrieve an array containing all rows of the result set
*
* @return array
*/
public function fetchAll()
{
return $this->getQuery()->fetchAll();
}
/**
* Fetch the first row of the result set
*
* @return mixed
*/
public function fetchRow()
{
return $this->getQuery()->fetchRow();
}
/**
* Fetch a column of all rows of the result set as an array
*
* @param int $columnIndex Index of the column to fetch
*
* @return array
*/
public function fetchColumn($columnIndex = 0)
{
return $this->getQuery()->fetchColumn($columnIndex);
}
/**
* Fetch the first column of the first row of the result set
*
* @return string
*/
public function fetchOne()
{
return $this->getQuery()->fetchOne();
}
/**
* Fetch all rows of the result set as an array of key-value pairs
*
* The first column is the key, the second column is the value.
*
* @return array
*/
public function fetchPairs()
{
return $this->getQuery()->fetchPairs();
}
}

View File

@ -25,6 +25,12 @@ class Groupsummary extends DataView
'hosts_down_handled',
'hosts_down_unhandled',
'hosts_pending',
'hosts_up_last_state_change',
'hosts_pending_last_state_change',
'hosts_down_last_state_change_handled',
'hosts_unreachable_last_state_change_handled',
'hosts_down_last_state_change_unhandled',
'hosts_unreachable_last_state_change_unhandled',
'services_total',
'services_ok',
'services_unknown',

View File

@ -23,6 +23,9 @@
@colorUnreachableHandled: #cc77ff;
@colorPending: #77aaff;
@colorInvalid: #999;
@colorFormNotificationInfo: #77aaff;
@colorFormNotificationWarning: #ffaa44;
@colorFormNotificationError: #ff5566;
/* Mixins */

View File

@ -87,6 +87,10 @@ button::-moz-focus-inner {
outline: 0;
}
button {
cursor: pointer;
}
select::-moz-focus-inner {
border: 0;
outline: 0;
@ -165,6 +169,32 @@ form ul.form-errors {
}
}
form ul.form-notifications {
.non-list-like-list;
margin-bottom: 1em;
padding: 0em;
li.info {
background: @colorFormNotificationInfo;
}
li.warning {
background: @colorFormNotificationWarning;
}
li.error {
background: @colorFormNotificationError;
}
li {
color: white;
font-weight: bold;
line-height: 1.5em;
padding: 0.5em;
margin-bottom: 0.5em;
}
}
form div.element {
margin: 0.5em 0;
}