diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 370b8f5d0..77b5b66d9 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -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 diff --git a/application/controllers/ListController.php b/application/controllers/ListController.php index ca7265f0e..4271e385a 100644 --- a/application/controllers/ListController.php +++ b/application/controllers/ListController.php @@ -51,7 +51,7 @@ class ListController extends Controller . 'T[0-9]{2}(?::[0-9]{2}){2}(?:[\+\-][0-9]{2}:[0-9]{2})?)' // time . ' - (?[A-Za-z]+) - (?.*)(?!.)/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); diff --git a/library/Icinga/Data/Browsable.php b/library/Icinga/Data/Browsable.php deleted file mode 100644 index a734a2680..000000000 --- a/library/Icinga/Data/Browsable.php +++ /dev/null @@ -1,20 +0,0 @@ -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 * diff --git a/library/Icinga/File/Csv.php b/library/Icinga/File/Csv.php index ee88262c3..5d9975bc7 100644 --- a/library/Icinga/File/Csv.php +++ b/library/Icinga/File/Csv.php @@ -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; diff --git a/library/Icinga/Web/Controller.php b/library/Icinga/Web/Controller.php index b70dbd3fc..61e80173b 100644 --- a/library/Icinga/Web/Controller.php +++ b/library/Icinga/Web/Controller.php @@ -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; } diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index faf77770c..48f7acdb7 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -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); + } } diff --git a/library/Icinga/Web/Form/Decorator/FormNotifications.php b/library/Icinga/Web/Form/Decorator/FormNotifications.php new file mode 100644 index 000000000..ed725e625 --- /dev/null +++ b/library/Icinga/Web/Form/Decorator/FormNotifications.php @@ -0,0 +1,95 @@ +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 = ''; + case self::PREPEND: + return $html . '' . $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'; + } + } +} diff --git a/library/Icinga/Web/Widget/Limiter.php b/library/Icinga/Web/Widget/Limiter.php index 9febf6f63..1c6c82f62 100644 --- a/library/Icinga/Web/Widget/Limiter.php +++ b/library/Icinga/Web/Widget/Limiter.php @@ -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', diff --git a/modules/monitoring/application/controllers/AlertsummaryController.php b/modules/monitoring/application/controllers/AlertsummaryController.php index dcc805286..1dc3b137b 100644 --- a/modules/monitoring/application/controllers/AlertsummaryController.php +++ b/modules/monitoring/application/controllers/AlertsummaryController.php @@ -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); } /** diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index ece98d28c..27f02f3f9 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -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); diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php index c1522d2a1..a8a4583d1 100644 --- a/modules/monitoring/application/controllers/ShowController.php +++ b/modules/monitoring/application/controllers/ShowController.php @@ -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); } diff --git a/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php b/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php index ec58bb4b6..79d15f5c1 100644 --- a/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php +++ b/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php @@ -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; } } diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php index d89c6623f..1380b416d 100644 --- a/modules/monitoring/application/forms/Config/BackendConfigForm.php +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -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; + } } diff --git a/modules/monitoring/application/forms/Setup/IdoResourcePage.php b/modules/monitoring/application/forms/Setup/IdoResourcePage.php index f52601359..3f6bb1564 100644 --- a/modules/monitoring/application/forms/Setup/IdoResourcePage.php +++ b/modules/monitoring/application/forms/Setup/IdoResourcePage.php @@ -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 ) ); } diff --git a/modules/monitoring/application/views/scripts/list/comments.phtml b/modules/monitoring/application/views/scripts/list/comments.phtml index f05adff1f..f8c4383bd 100644 --- a/modules/monitoring/application/views/scripts/list/comments.phtml +++ b/modules/monitoring/application/views/scripts/list/comments.phtml @@ -5,7 +5,7 @@ render('list/components/selectioninfo.phtml'); ?>
- getTotalItemCount() ?> translate('Comments') ?>: + translate('Comments') ?>:
sortBox; ?> limiter; ?> diff --git a/modules/monitoring/application/views/scripts/list/downtimes.phtml b/modules/monitoring/application/views/scripts/list/downtimes.phtml index 0d35b77cd..79a0e1b92 100644 --- a/modules/monitoring/application/views/scripts/list/downtimes.phtml +++ b/modules/monitoring/application/views/scripts/list/downtimes.phtml @@ -9,7 +9,7 @@ if (! $this->compact): ?> render('list/components/selectioninfo.phtml'); ?>
- getTotalItemCount() ?> translate('Downtimes') ?> + translate('Downtimes') ?>
sortBox; ?> limiter; ?> diff --git a/modules/monitoring/application/views/scripts/list/hostgroups.phtml b/modules/monitoring/application/views/scripts/list/hostgroups.phtml index ef8cd78ea..90e96e660 100644 --- a/modules/monitoring/application/views/scripts/list/hostgroups.phtml +++ b/modules/monitoring/application/views/scripts/list/hostgroups.phtml @@ -1,4 +1,7 @@ -compact): ?> +compact): ?>
tabs; ?> sortBox; ?> @@ -15,7 +18,7 @@ if (count($hostgroups) === 0) { return; } ?> - +
@@ -23,57 +26,40 @@ if (count($hostgroups) === 0) { - - - services_critical_last_state_change_unhandled): ?> - + - services_unknown_last_state_change_unhandled): ?> - - services_warning_last_state_change_unhandled): ?> - - services_critical_last_state_change_handled): ?> - - services_unknown_last_state_change_handled): ?> - - services_warning_last_state_change_handled): ?> - - services_ok_last_state_change): ?> - - - - - - + check_latency): ?> diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php index 8b72bbf79..fd389562e 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php @@ -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)', diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index c9537eff8..6bed36fe1 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -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(); } } diff --git a/modules/monitoring/library/Monitoring/DataView/Groupsummary.php b/modules/monitoring/library/Monitoring/DataView/Groupsummary.php index 562bed1da..d29c1c418 100644 --- a/modules/monitoring/library/Monitoring/DataView/Groupsummary.php +++ b/modules/monitoring/library/Monitoring/DataView/Groupsummary.php @@ -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', diff --git a/public/css/icinga/defaults.less b/public/css/icinga/defaults.less index 3dc277300..e3e033eff 100644 --- a/public/css/icinga/defaults.less +++ b/public/css/icinga/defaults.less @@ -23,6 +23,9 @@ @colorUnreachableHandled: #cc77ff; @colorPending: #77aaff; @colorInvalid: #999; +@colorFormNotificationInfo: #77aaff; +@colorFormNotificationWarning: #ffaa44; +@colorFormNotificationError: #ff5566; /* Mixins */ diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 41525a4c7..c8b5df161 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -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; }
translate('Last Problem'); ?> translate('Host Group'); ?>translate('Service States'); ?>
- translate('CRITICAL'); ?> + 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; + } + } + ?> +
+
- prefixedTimeSince($h->services_critical_last_state_change_unhandled); ?> + prefixedTimeSince($lastStateChange); ?>
- translate('UNKNOWN'); ?> -
- prefixedTimeSince($h->services_unknown_last_state_change_unhandled); ?> -
- translate('WARNING'); ?> -
- prefixedTimeSince($h->services_warning_last_state_change_unhandled); ?> -
- translate('CRITICAL'); ?> -
- prefixedTimeSince($h->services_critical_last_state_change_handled); ?> -
- translate('UNKNOWN'); ?> -
- prefixedTimeSince($h->services_unknown_last_state_change_handled); ?> -
- translate('WARNING'); ?> -
- prefixedTimeSince($h->services_warning_last_state_change_handled); ?> -
- translate('OK'); ?> -
- prefixedTimeSince($h->services_ok_last_state_change); ?> -
- translate('PENDING'); ?> -
- prefixedTimeSince($h->services_pending_last_state_change); ?> -
qlink( $h->hostgroup_alias, @@ -93,7 +79,7 @@ if (count($hostgroups) === 0) { )) ); ?> + services_ok): ?> qlink( diff --git a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml index 2a4e4b033..b0699c799 100644 --- a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml +++ b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml @@ -68,7 +68,18 @@ if ($object->getType() === $object::TYPE_HOST) { check_execution_time): ?>
translate('Check execution time') ?>check_execution_time ?>scheck_execution_time, + $matches + )) { + printf('%.3f', (float) $matches[1]); + } else { + echo $object->check_execution_time; + } + ?>s