From 8ca6cc32b2c789a3149861159ad258f2cb9491e3 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:39:39 +0200 Subject: Adding upstream version 2.11.4. Signed-off-by: Daniel Baumann --- .../application/controllers/ActionsController.php | 135 ++++ .../application/controllers/CommentController.php | 91 +++ .../application/controllers/CommentsController.php | 108 +++ .../application/controllers/ConfigController.php | 298 ++++++++ .../application/controllers/DowntimeController.php | 108 +++ .../controllers/DowntimesController.php | 108 +++ .../application/controllers/EventController.php | 551 ++++++++++++++ .../application/controllers/HealthController.php | 196 +++++ .../application/controllers/HostController.php | 185 +++++ .../application/controllers/HostsController.php | 260 +++++++ .../application/controllers/ListController.php | 808 +++++++++++++++++++++ .../application/controllers/ServiceController.php | 147 ++++ .../application/controllers/ServicesController.php | 262 +++++++ .../application/controllers/ShowController.php | 101 +++ .../application/controllers/TacticalController.php | 128 ++++ .../application/controllers/TimelineController.php | 325 +++++++++ 16 files changed, 3811 insertions(+) create mode 100644 modules/monitoring/application/controllers/ActionsController.php create mode 100644 modules/monitoring/application/controllers/CommentController.php create mode 100644 modules/monitoring/application/controllers/CommentsController.php create mode 100644 modules/monitoring/application/controllers/ConfigController.php create mode 100644 modules/monitoring/application/controllers/DowntimeController.php create mode 100644 modules/monitoring/application/controllers/DowntimesController.php create mode 100644 modules/monitoring/application/controllers/EventController.php create mode 100644 modules/monitoring/application/controllers/HealthController.php create mode 100644 modules/monitoring/application/controllers/HostController.php create mode 100644 modules/monitoring/application/controllers/HostsController.php create mode 100644 modules/monitoring/application/controllers/ListController.php create mode 100644 modules/monitoring/application/controllers/ServiceController.php create mode 100644 modules/monitoring/application/controllers/ServicesController.php create mode 100644 modules/monitoring/application/controllers/ShowController.php create mode 100644 modules/monitoring/application/controllers/TacticalController.php create mode 100644 modules/monitoring/application/controllers/TimelineController.php (limited to 'modules/monitoring/application/controllers') diff --git a/modules/monitoring/application/controllers/ActionsController.php b/modules/monitoring/application/controllers/ActionsController.php new file mode 100644 index 0000000..bc13e21 --- /dev/null +++ b/modules/monitoring/application/controllers/ActionsController.php @@ -0,0 +1,135 @@ +params); + if ($filter->isEmpty()) { + $this->getResponse()->json() + ->setFailData(array('filter' => 'Filter is required and must not be empty')) + ->sendResponse(); + } + return $filter; + } + + /** + * Schedule host downtimes + */ + public function scheduleHostDowntimeAction() + { + $filter = $this->getFilterOrExitIfEmpty(); + $hostList = new HostList($this->backend); + $hostList + ->applyFilter($this->getRestriction('monitoring/filter/objects')) + ->applyFilter($filter); + if (! $hostList->count()) { + $this->getResponse()->json() + ->setFailData(array('filter' => 'No hosts found matching the filter')) + ->sendResponse(); + } + $form = new ScheduleHostDowntimeCommandForm(); + $form + ->setIsApiTarget(true) + ->setBackend($this->backend) + ->setObjects($hostList->fetch()) + ->handleRequest($this->getRequest()); + } + + /** + * Remove host downtimes + */ + public function removeHostDowntimeAction() + { + $filter = $this->getFilterOrExitIfEmpty(); + $downtimes = $this->backend + ->select() + ->from('downtime', array('host_name', 'id' => 'downtime_internal_id', 'name' => 'downtime_name')) + ->where('object_type', 'host') + ->applyFilter($this->getRestriction('monitoring/filter/objects')) + ->applyFilter($filter); + if (! $downtimes->count()) { + $this->getResponse()->json() + ->setFailData(array('filter' => 'No downtimes found matching the filter')) + ->sendResponse(); + } + $form = new DeleteDowntimesCommandForm(); + $form + ->setIsApiTarget(true) + ->setDowntimes($downtimes->fetchAll()) + ->handleRequest($this->getRequest()); + // @TODO(el): Respond w/ the downtimes deleted instead of the notifiaction added by + // DeleteDowntimesCommandForm::onSuccess(). + } + + /** + * Schedule service downtimes + */ + public function scheduleServiceDowntimeAction() + { + $filter = $this->getFilterOrExitIfEmpty(); + $serviceList = new ServiceList($this->backend); + $serviceList + ->applyFilter($this->getRestriction('monitoring/filter/objects')) + ->applyFilter($filter); + if (! $serviceList->count()) { + $this->getResponse()->json() + ->setFailData(array('filter' => 'No services found matching the filter')) + ->sendResponse(); + } + $form = new ScheduleServiceDowntimeCommandForm(); + $form + ->setIsApiTarget(true) + ->setBackend($this->backend) + ->setObjects($serviceList->fetch()) + ->handleRequest($this->getRequest()); + } + + /** + * Remove service downtimes + */ + public function removeServiceDowntimeAction() + { + $filter = $this->getFilterOrExitIfEmpty(); + $downtimes = $this->backend + ->select() + ->from( + 'downtime', + array('host_name', 'service_description', 'id' => 'downtime_internal_id', 'name' => 'downtime_name') + ) + ->where('object_type', 'service') + ->applyFilter($this->getRestriction('monitoring/filter/objects')) + ->applyFilter($filter); + if (! $downtimes->count()) { + $this->getResponse()->json() + ->setFailData(array('filter' => 'No downtimes found matching the filter')) + ->sendResponse(); + } + $form = new DeleteDowntimesCommandForm(); + $form + ->setIsApiTarget(true) + ->setDowntimes($downtimes->fetchAll()) + ->handleRequest($this->getRequest()); + // @TODO(el): Respond w/ the downtimes deleted instead of the notifiaction added by + // DeleteDowntimesCommandForm::onSuccess(). + } +} diff --git a/modules/monitoring/application/controllers/CommentController.php b/modules/monitoring/application/controllers/CommentController.php new file mode 100644 index 0000000..e50473f --- /dev/null +++ b/modules/monitoring/application/controllers/CommentController.php @@ -0,0 +1,91 @@ +params->getRequired('comment_id'); + + $query = $this->backend->select()->from('comment', array( + 'id' => 'comment_internal_id', + 'objecttype' => 'object_type', + 'comment' => 'comment_data', + 'author' => 'comment_author_name', + 'timestamp' => 'comment_timestamp', + 'type' => 'comment_type', + 'persistent' => 'comment_is_persistent', + 'expiration' => 'comment_expiration', + 'name' => 'comment_name', + 'host_name', + 'service_description', + 'host_display_name', + 'service_display_name' + ))->where('comment_internal_id', $commentId); + $this->applyRestriction('monitoring/filter/objects', $query); + + if (false === $this->comment = $query->fetchRow()) { + $this->httpNotFound($this->translate('Comment not found')); + } + + $this->getTabs()->add( + 'comment', + array( + 'icon' => 'comment-empty', + 'label' => $this->translate('Comment'), + 'title' => $this->translate('Display detailed information about a comment.'), + 'url' =>'monitoring/comments/show' + ) + )->activate('comment')->extend(new DashboardAction())->extend(new MenuAction()); + + if (Hook::has('ticket')) { + $this->view->tickets = Hook::first('ticket'); + } + } + + /** + * Display comment detail view + */ + public function showAction() + { + $this->view->comment = $this->comment; + $this->view->title = $this->translate('Comments'); + + if ($this->hasPermission('monitoring/command/comment/delete')) { + $listUrl = Url::fromPath('monitoring/list/comments') + ->setQueryString('comment_type=comment|comment_type=ack'); + $form = new DeleteCommentCommandForm(); + $form + ->populate(array( + 'comment_id' => $this->comment->id, + 'comment_is_service' => isset($this->comment->service_description), + 'comment_name' => $this->comment->name, + 'redirect' => $listUrl + )) + ->handleRequest(); + $this->view->delCommentForm = $form; + } + } +} diff --git a/modules/monitoring/application/controllers/CommentsController.php b/modules/monitoring/application/controllers/CommentsController.php new file mode 100644 index 0000000..9de19a0 --- /dev/null +++ b/modules/monitoring/application/controllers/CommentsController.php @@ -0,0 +1,108 @@ +filter = Filter::fromQueryString(str_replace( + 'comment_id', + 'comment_internal_id', + (string) $this->params + )); + $query = $this->backend->select()->from('comment', array( + 'id' => 'comment_internal_id', + 'objecttype' => 'object_type', + 'comment' => 'comment_data', + 'author' => 'comment_author_name', + 'timestamp' => 'comment_timestamp', + 'type' => 'comment_type', + 'persistent' => 'comment_is_persistent', + 'expiration' => 'comment_expiration', + 'name' => 'comment_name', + 'host_name', + 'service_description', + 'host_display_name', + 'service_display_name' + ))->addFilter($this->filter); + $this->applyRestriction('monitoring/filter/objects', $query); + + $this->comments = $query; + + $this->view->title = $this->translate('Comments'); + $this->getTabs()->add( + 'comments', + array( + 'icon' => 'comment-empty', + 'label' => $this->translate('Comments') . sprintf(' (%d)', $query->count()), + 'title' => $this->translate( + 'Display detailed information about multiple comments.' + ), + 'url' =>'monitoring/comments/show' + ) + )->activate('comments'); + } + + /** + * Display the detail view for a comment list + */ + public function showAction() + { + $this->view->comments = $this->comments; + $this->view->listAllLink = Url::fromPath('monitoring/list/comments') + ->setQueryString($this->filter->toQueryString()); + $this->view->removeAllLink = Url::fromPath('monitoring/comments/delete-all') + ->setParams($this->params); + } + + /** + * Display the form for removing a comment list + */ + public function deleteAllAction() + { + $this->assertPermission('monitoring/command/comment/delete'); + + $listCommentsLink = Url::fromPath('monitoring/list/comments') + ->setQueryString('comment_type=(comment|ack)'); + $delCommentForm = new DeleteCommentsCommandForm(); + $delCommentForm->setTitle($this->view->translate('Remove all Comments')); + $delCommentForm->addDescription(sprintf( + $this->translate('Confirm removal of %d comments.'), + $this->comments->count() + )); + $delCommentForm->setComments($this->comments->fetchAll()) + ->setRedirectUrl($listCommentsLink) + ->handleRequest(); + $this->view->delCommentForm = $delCommentForm; + $this->view->comments = $this->comments; + $this->view->listAllLink = Url::fromPath('monitoring/list/comments') + ->setQueryString($this->filter->toQueryString()); + } +} diff --git a/modules/monitoring/application/controllers/ConfigController.php b/modules/monitoring/application/controllers/ConfigController.php new file mode 100644 index 0000000..b8ca0a1 --- /dev/null +++ b/modules/monitoring/application/controllers/ConfigController.php @@ -0,0 +1,298 @@ +assertPermission('config/modules'); + $this->view->title = $this->translate('Backends'); + $this->view->defaultTitle = 'monitoring :: ' . $this->view->defaultTitle; + parent::init(); + } + + /** + * Display a list of available backends and command transports + */ + public function indexAction() + { + $this->view->commandTransportReorderForm = $form = new TransportReorderForm(); + $form->handleRequest(); + + $this->view->backendsConfig = $this->Config('backends'); + $this->view->tabs = $this->Module()->getConfigTabs()->activate('backends'); + } + + /** + * Edit a monitoring backend + */ + public function editbackendAction() + { + $backendName = $this->params->getRequired('backend-name'); + + $form = new BackendConfigForm(); + $form->setRedirectUrl('monitoring/config'); + $form->setTitle(sprintf($this->translate('Edit Monitoring Backend %s'), $backendName)); + $form->setIniConfig($this->Config('backends')); + $form->setResourceConfig(ResourceFactory::getResourceConfigs()); + $form->setOnSuccess(function (BackendConfigForm $form) use ($backendName) { + try { + $form->edit($backendName, array_map( + function ($v) { + return $v !== '' ? $v : null; + }, + $form->getValues() + )); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($form->save()) { + Notification::success(sprintf(t('Monitoring backend "%s" successfully updated'), $backendName)); + return true; + } + + return false; + }); + + try { + $form->load($backendName); + $form->handleRequest(); + } catch (NotFoundError $_) { + $this->httpNotFound(sprintf($this->translate('Monitoring backend "%s" not found'), $backendName)); + } + + $this->view->form = $form; + $this->render('form'); + } + + /** + * Create a new monitoring backend + */ + public function createbackendAction() + { + $form = new BackendConfigForm(); + $form->setRedirectUrl('monitoring/config'); + $form->setTitle($this->translate('Create New Monitoring Backend')); + $form->setIniConfig($this->Config('backends')); + + try { + $form->setResourceConfig(ResourceFactory::getResourceConfigs()); + } catch (ConfigurationError $e) { + if ($this->hasPermission('config/resources')) { + Notification::error($e->getMessage()); + $this->redirectNow('config/createresource'); + } + + throw $e; // No permission for resource configuration, show the error + } + + $form->setOnSuccess(function (BackendConfigForm $form) { + try { + $form->add($form::transformEmptyValuesToNull($form->getValues())); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($form->save()) { + Notification::success(t('Monitoring backend successfully created')); + return true; + } + + return false; + }); + $form->handleRequest(); + + $this->view->form = $form; + $this->render('form'); + } + + /** + * Display a confirmation form to remove the backend identified by the 'backend' parameter + */ + public function removebackendAction() + { + $backendName = $this->params->getRequired('backend-name'); + + $backendForm = new BackendConfigForm(); + $backendForm->setIniConfig($this->Config('backends')); + $form = new ConfirmRemovalForm(); + $form->setRedirectUrl('monitoring/config'); + $form->setTitle(sprintf($this->translate('Remove Monitoring Backend %s'), $backendName)); + $form->setOnSuccess(function (ConfirmRemovalForm $form) use ($backendName, $backendForm) { + try { + $backendForm->delete($backendName); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($backendForm->save()) { + Notification::success(sprintf(t('Monitoring backend "%s" successfully removed'), $backendName)); + return true; + } + + return false; + }); + $form->handleRequest(); + + $this->view->form = $form; + $this->render('form'); + } + + /** + * Remove a command transport + */ + public function removetransportAction() + { + $transportName = $this->params->getRequired('transport'); + + $transportForm = new TransportConfigForm(); + $transportForm->setIniConfig($this->Config('commandtransports')); + $form = new ConfirmRemovalForm(); + $form->setRedirectUrl('monitoring/config'); + $form->setTitle(sprintf($this->translate('Remove Command Transport %s'), $transportName)); + $form->info( + $this->translate( + 'If you still have any environments or views referring to this transport, ' + . 'you won\'t be able to send commands anymore after deletion.' + ), + false + ); + $form->setOnSuccess(function (ConfirmRemovalForm $form) use ($transportName, $transportForm) { + try { + $transportForm->delete($transportName); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($transportForm->save()) { + Notification::success(sprintf(t('Command transport "%s" successfully removed'), $transportName)); + return true; + } + + return false; + }); + $form->handleRequest(); + + $this->view->form = $form; + $this->render('form'); + } + + /** + * Edit a command transport + */ + public function edittransportAction() + { + $transportName = $this->params->getRequired('transport'); + + $form = new TransportConfigForm(); + $form->setRedirectUrl('monitoring/config'); + $form->setTitle(sprintf($this->translate('Edit Command Transport %s'), $transportName)); + $form->setIniConfig($this->Config('commandtransports')); + $form->setInstanceNames( + MonitoringBackend::instance()->select()->from('instance', array('instance_name'))->fetchColumn() + ); + $form->setOnSuccess(function (TransportConfigForm $form) use ($transportName) { + try { + $form->edit($transportName, array_map( + function ($v) { + return $v !== '' ? $v : null; + }, + $form->getValues() + )); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($form->save()) { + Notification::success(sprintf(t('Command transport "%s" successfully updated'), $transportName)); + return true; + } + + return false; + }); + + try { + $form->load($transportName); + $form->handleRequest(); + } catch (NotFoundError $_) { + $this->httpNotFound(sprintf($this->translate('Command transport "%s" not found'), $transportName)); + } + + $this->view->form = $form; + $this->render('form'); + } + + /** + * Create a new command transport + */ + public function createtransportAction() + { + $form = new TransportConfigForm(); + $form->setRedirectUrl('monitoring/config'); + $form->setTitle($this->translate('Create New Command Transport')); + $form->setIniConfig($this->Config('commandtransports')); + $form->setInstanceNames( + MonitoringBackend::instance()->select()->from('instance', array('instance_name'))->fetchColumn() + ); + $form->setOnSuccess(function (TransportConfigForm $form) { + try { + $form->add($form::transformEmptyValuesToNull($form->getValues())); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($form->save()) { + Notification::success(t('Command transport successfully created')); + return true; + } + + return false; + }); + $form->handleRequest(); + + $this->view->form = $form; + $this->render('form'); + } + + /** + * Display a form to adjust security relevant settings + */ + public function securityAction() + { + $form = new SecurityConfigForm(); + $form->setIniConfig($this->Config()); + $form->handleRequest(); + + $this->view->form = $form; + $this->view->title = $this->translate('Security'); + $this->view->tabs = $this->Module()->getConfigTabs()->activate('security'); + } +} diff --git a/modules/monitoring/application/controllers/DowntimeController.php b/modules/monitoring/application/controllers/DowntimeController.php new file mode 100644 index 0000000..83c03dd --- /dev/null +++ b/modules/monitoring/application/controllers/DowntimeController.php @@ -0,0 +1,108 @@ +params->getRequired('downtime_id'); + + $query = $this->backend->select()->from('downtime', array( + 'id' => 'downtime_internal_id', + 'objecttype' => 'object_type', + 'comment' => 'downtime_comment', + 'author_name' => 'downtime_author_name', + 'start' => 'downtime_start', + 'scheduled_start' => 'downtime_scheduled_start', + 'scheduled_end' => 'downtime_scheduled_end', + 'end' => 'downtime_end', + 'duration' => 'downtime_duration', + 'is_flexible' => 'downtime_is_flexible', + 'is_fixed' => 'downtime_is_fixed', + 'is_in_effect' => 'downtime_is_in_effect', + 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', + 'host_state', + 'service_state', + 'host_name', + 'service_description', + 'host_display_name', + 'service_display_name' + ))->where('downtime_internal_id', $downtimeId); + $this->applyRestriction('monitoring/filter/objects', $query); + + if (false === $this->downtime = $query->fetchRow()) { + $this->httpNotFound($this->translate('Downtime not found')); + } + + $this->getTabs()->add( + 'downtime', + array( + + 'icon' => 'plug', + 'label' => $this->translate('Downtime'), + 'title' => $this->translate('Display detailed information about a downtime.'), + 'url' =>'monitoring/downtimes/show' + ) + )->activate('downtime')->extend(new DashboardAction())->extend(new MenuAction()); + + if (Hook::has('ticket')) { + $this->view->tickets = Hook::first('ticket'); + } + } + + /** + * Display the detail view for a downtime + */ + public function showAction() + { + $isService = isset($this->downtime->service_description); + $this->view->downtime = $this->downtime; + $this->view->isService = $isService; + $this->view->listAllLink = Url::fromPath('monitoring/list/downtimes'); + $this->view->showHostLink = Url::fromPath('monitoring/host/show')->setParam('host', $this->downtime->host_name); + $this->view->showServiceLink = Url::fromPath('monitoring/service/show') + ->setParam('host', $this->downtime->host_name) + ->setParam('service', $this->downtime->service_description); + $this->view->stateName = $isService ? Service::getStateText($this->downtime->service_state) + : Host::getStateText($this->downtime->host_state); + + $this->view->title = $this->translate('Downtimes'); + if ($this->hasPermission('monitoring/command/downtime/delete')) { + $form = new DeleteDowntimeCommandForm(); + $form + ->populate(array( + 'downtime_id' => $this->downtime->id, + 'downtime_is_service' => $isService, + 'downtime_name' => $this->downtime->name, + 'redirect' => Url::fromPath('monitoring/list/downtimes'), + )) + ->handleRequest(); + $this->view->delDowntimeForm = $form; + } + } +} diff --git a/modules/monitoring/application/controllers/DowntimesController.php b/modules/monitoring/application/controllers/DowntimesController.php new file mode 100644 index 0000000..4891203 --- /dev/null +++ b/modules/monitoring/application/controllers/DowntimesController.php @@ -0,0 +1,108 @@ +filter = Filter::fromQueryString(str_replace( + 'downtime_id', + 'downtime_internal_id', + (string) $this->params + )); + $query = $this->backend->select()->from('downtime', array( + 'id' => 'downtime_internal_id', + 'objecttype' => 'object_type', + 'comment' => 'downtime_comment', + 'author_name' => 'downtime_author_name', + 'start' => 'downtime_start', + 'scheduled_start' => 'downtime_scheduled_start', + 'scheduled_end' => 'downtime_scheduled_end', + 'end' => 'downtime_end', + 'duration' => 'downtime_duration', + 'is_flexible' => 'downtime_is_flexible', + 'is_fixed' => 'downtime_is_fixed', + 'is_in_effect' => 'downtime_is_in_effect', + 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', + 'host_state', + 'service_state', + 'host_name', + 'service_description', + 'host_display_name', + 'service_display_name' + ))->addFilter($this->filter); + $this->applyRestriction('monitoring/filter/objects', $query); + + $this->downtimes = $query; + + $this->view->title = $this->translate('Downtimes'); + $this->getTabs()->add( + 'downtimes', + array( + 'icon' => 'plug', + 'label' => $this->translate('Downtimes') . sprintf(' (%d)', $query->count()), + 'title' => $this->translate('Display detailed information about multiple downtimes.'), + 'url' =>'monitoring/downtimes/show' + ) + )->activate('downtimes'); + } + + /** + * Display the detail view for a downtime list + */ + public function showAction() + { + $this->view->downtimes = $this->downtimes; + $this->view->listAllLink = Url::fromPath('monitoring/list/downtimes') + ->setQueryString($this->filter->toQueryString()); + $this->view->removeAllLink = Url::fromPath('monitoring/downtimes/delete-all')->setParams($this->params); + } + + /** + * Display the form for removing a downtime list + */ + public function deleteAllAction() + { + $this->assertPermission('monitoring/command/downtime/delete'); + $this->view->downtimes = $this->downtimes; + $this->view->listAllLink = Url::fromPath('monitoring/list/downtimes') + ->setQueryString($this->filter->toQueryString()); + $delDowntimeForm = new DeleteDowntimesCommandForm(); + $delDowntimeForm->setTitle($this->view->translate('Remove all Downtimes')); + $delDowntimeForm->addDescription(sprintf( + $this->translate('Confirm removal of %d downtimes.'), + $this->downtimes->count() + )); + $delDowntimeForm->setRedirectUrl(Url::fromPath('monitoring/list/downtimes')); + $delDowntimeForm->setDowntimes($this->downtimes->fetchAll())->handleRequest(); + $this->view->delAllDowntimeForm = $delDowntimeForm; + } +} diff --git a/modules/monitoring/application/controllers/EventController.php b/modules/monitoring/application/controllers/EventController.php new file mode 100644 index 0000000..08ab1bc --- /dev/null +++ b/modules/monitoring/application/controllers/EventController.php @@ -0,0 +1,551 @@ + 'notificationevent', + 'comment' => 'commentevent', + 'comment_deleted' => 'commentevent', + 'ack' => 'commentevent', + 'ack_deleted' => 'commentevent', + 'dt_comment' => 'commentevent', + 'dt_comment_deleted' => 'commentevent', + 'flapping' => 'flappingevent', + 'flapping_deleted' => 'flappingevent', + 'hard_state' => 'statechangeevent', + 'soft_state' => 'statechangeevent', + 'dt_start' => 'downtimeevent', + 'dt_end' => 'downtimeevent' + ); + + public function init() + { + if (Hook::has('ticket')) { + $this->view->tickets = Hook::first('ticket'); + } + } + + public function showAction() + { + $type = $this->params->shiftRequired('type'); + $id = $this->params->shiftRequired('id'); + + if (! isset($this->dataViewsByType[$type]) + || $this->applyRestriction( + 'monitoring/filter/objects', + $this->backend->select()->from('eventhistory', array('id'))->where('id', $id) + )->fetchRow() === false + ) { + $this->httpNotFound($this->translate('Event not found')); + } + + $event = $this->query($type, $id)->fetchRow(); + + if ($event === false) { + $this->httpNotFound($this->translate('Event not found')); + } + + $this->view->object = $object = $event->service_description === null + ? new Host($this->backend, $event->host_name) + : new Service($this->backend, $event->host_name, $event->service_description); + $object->fetch(); + + list($icon, $label) = $this->getIconAndLabel($type); + + $this->view->details = array_merge( + array(array($this->view->escape($this->translate('Type')), $label)), + $this->getDetails($type, $event) + ); + + $this->view->extensionsHtml = array(); + /** @var EventDetailsExtensionHook $hook */ + foreach (Hook::all('Monitoring\\EventDetailsExtension') as $hook) { + try { + $html = $hook->getHtmlForEvent($event); + } catch (\Exception $e) { + $html = $this->view->escape($e->getMessage()); + } + + if ($html) { + $module = $this->view->escape($hook->getModule()->getName()); + $this->view->extensionsHtml[] = + '
' + . $html + . '
'; + } + } + + $this->view->title = $this->translate('Event Overview'); + $this->getTabs() + ->add('event', array( + 'title' => $label, + 'label' => $label, + 'url' => Url::fromRequest(), + 'active' => true + )) + ->extend(new OutputFormat()) + ->extend(new DashboardAction()) + ->extend(new MenuAction()); + } + + /** + * Return translated and escaped 'Yes' if the given condition is true, 'No' otherwise, 'N/A' if NULL + * + * @param bool|null $condition + * + * @return string + */ + protected function yesOrNo($condition) + { + if ($condition === null) { + return $this->view->escape($this->translate('N/A')); + } + + return $this->view->escape($condition ? $this->translate('Yes') : $this->translate('No')); + } + + /** + * Render the given duration in seconds as human readable HTML or 'N/A' if NULL + * + * @param int|null $seconds + * + * @return string + */ + protected function duration($seconds) + { + return $this->view->escape( + $seconds === null ? $this->translate('N/A') : DateFormatter::formatDuration($seconds) + ); + } + + /** + * Render the given percent number as human readable HTML or 'N/A' if NULL + * + * @param float|null $percent + * + * @return string + */ + protected function percent($percent) + { + return $this->view->escape( + $percent === null ? $this->translate('N/A') : sprintf($this->translate('%.2f%%'), $percent) + ); + } + + /** + * Render the given comment message as HTML or 'N/A' if NULL + * + * @param string|null $message + * + * @return string + */ + protected function comment($message) + { + return $this->view->nl2br($this->view->createTicketLinks($this->view->markdown($message))); + } + + /** + * Render a link to the given contact or 'N/A' if NULL + * + * @param string|null $name + * + * @return string + */ + protected function contact($name) + { + return $name === null + ? $this->view->escape($this->translate('N/A')) + : $this->view->qlink($name, Url::fromPath('monitoring/show/contact', array('contact_name' => $name))); + } + + /** + * Render the given monitored object state as human readable HTML or 'N/A' if NULL + * + * @param bool $isService + * @param int|null $state + * + * @return string + */ + protected function state($isService, $state) + { + if ($state === null) { + return $this->view->escape($this->translate('N/A')); + } + + try { + $stateText = $isService + ? Service::getStateText($state, true) + : Host::getStateText($state, true); + } catch (InvalidArgumentException $e) { + return $this->view->escape($this->translate('N/A')); + } + + return ' ' . $this->view->escape($stateText) . ''; + } + + /** + * Render the given plugin output as human readable HTML + * + * @param string $output + * + * @return string + */ + protected function pluginOutput($output) + { + return $this->view->getHelper('PluginOutput')->pluginOutput($output); + } + + /** + * Return the icon and the label for the given event type + * + * @param string $eventType + * + * @return string[] + */ + protected function getIconAndLabel($eventType) + { + switch ($eventType) { + case 'notify': + return array('bell', $this->translate('Notification', 'tooltip')); + case 'comment': + return array('comment-empty', $this->translate('Comment', 'tooltip')); + case 'comment_deleted': + return array('cancel', $this->translate('Comment removed', 'tooltip')); + case 'ack': + return array('ok', $this->translate('Acknowledged', 'tooltip')); + case 'ack_deleted': + return array('ok', $this->translate('Acknowledgement removed', 'tooltip')); + case 'dt_comment': + return array('plug', $this->translate('Downtime scheduled', 'tooltip')); + case 'dt_comment_deleted': + return array('plug', $this->translate('Downtime removed', 'tooltip')); + case 'flapping': + return array('flapping', $this->translate('Flapping started', 'tooltip')); + case 'flapping_deleted': + return array('flapping', $this->translate('Flapping stopped', 'tooltip')); + case 'hard_state': + return array('warning-empty', $this->translate('Hard state change')); + case 'soft_state': + return array('spinner', $this->translate('Soft state change')); + case 'dt_start': + return array('plug', $this->translate('Downtime started', 'tooltip')); + case 'dt_end': + return array('plug', $this->translate('Downtime ended', 'tooltip')); + } + } + + /** + * Return a query for the given event ID of the given type + * + * @param string $type + * @param int $id + * + * @return Queryable + */ + protected function query($type, $id) + { + switch ($this->dataViewsByType[$type]) { + case 'downtimeevent': + return $this->backend->select() + ->from('downtimeevent', array( + 'entry_time' => 'downtimeevent_entry_time', + 'author_name' => 'downtimeevent_author_name', + 'comment_data' => 'downtimeevent_comment_data', + 'is_fixed' => 'downtimeevent_is_fixed', + 'scheduled_start_time' => 'downtimeevent_scheduled_start_time', + 'scheduled_end_time' => 'downtimeevent_scheduled_end_time', + 'was_started' => 'downtimeevent_was_started', + 'actual_start_time' => 'downtimeevent_actual_start_time', + 'actual_end_time' => 'downtimeevent_actual_end_time', + 'was_cancelled' => 'downtimeevent_was_cancelled', + 'is_in_effect' => 'downtimeevent_is_in_effect', + 'trigger_time' => 'downtimeevent_trigger_time', + 'host_name', + 'service_description' + )) + ->where('downtimeevent_id', $id); + case 'commentevent': + return $this->backend->select() + ->from('commentevent', array( + 'entry_type' => 'commentevent_entry_type', + 'comment_time' => 'commentevent_comment_time', + 'author_name' => 'commentevent_author_name', + 'comment_data' => 'commentevent_comment_data', + 'is_persistent' => 'commentevent_is_persistent', + 'comment_source' => 'commentevent_comment_source', + 'expires' => 'commentevent_expires', + 'expiration_time' => 'commentevent_expiration_time', + 'deletion_time' => 'commentevent_deletion_time', + 'host_name', + 'service_description' + )) + ->where('commentevent_id', $id); + case 'flappingevent': + return $this->backend->select() + ->from('flappingevent', array( + 'event_time' => 'flappingevent_event_time', + 'reason_type' => 'flappingevent_reason_type', + 'percent_state_change' => 'flappingevent_percent_state_change', + 'low_threshold' => 'flappingevent_low_threshold', + 'high_threshold' => 'flappingevent_high_threshold', + 'host_name', + 'service_description' + )) + ->where('flappingevent_id', $id) + ->where('flappingevent_event_type', $type); + case 'notificationevent': + return $this->backend->select() + ->from('notificationevent', array( + 'notification_reason' => 'notificationevent_reason', + 'start_time' => 'notificationevent_start_time', + 'end_time' => 'notificationevent_end_time', + 'state' => 'notificationevent_state', + 'output' => 'notificationevent_output', + 'long_output' => 'notificationevent_long_output', + 'escalated' => 'notificationevent_escalated', + 'contacts_notified' => 'notificationevent_contacts_notified', + 'host_name', + 'service_description' + )) + ->where('notificationevent_id', $id); + case 'statechangeevent': + return $this->backend->select() + ->from('statechangeevent', array( + 'state_time' => 'statechangeevent_state_time', + 'state' => 'statechangeevent_state', + 'current_check_attempt' => 'statechangeevent_current_check_attempt', + 'max_check_attempts' => 'statechangeevent_max_check_attempts', + 'last_state' => 'statechangeevent_last_state', + 'last_hard_state' => 'statechangeevent_last_hard_state', + 'output' => 'statechangeevent_output', + 'long_output' => 'statechangeevent_long_output', + 'check_source' => 'statechangeevent_check_source', + 'host_name', + 'service_description' + )) + ->where('statechangeevent_id', $id) + ->where('statechangeevent_state_change', 1) + ->where('statechangeevent_state_type', $type); + } + } + + /** + * Return the given event's data prepared for a name-value table + * + * @param string $type + * @param \stdClass $event + * + * @return string[][] + */ + protected function getDetails($type, $event) + { + switch ($type) { + case 'dt_start': + case 'dt_end': + $details = array(array( + array($this->translate('Entry time'), DateFormatter::formatDateTime($event->entry_time)), + array($this->translate('Is fixed'), $this->yesOrNo($event->is_fixed)), + array($this->translate('Is in effect'), $this->yesOrNo($event->is_in_effect)), + array($this->translate('Was started'), $this->yesOrNo($event->was_started)) + )); + + if ($type === 'dt_end') { + $details[] = array( + array($this->translate('Was cancelled'), $this->yesOrNo($event->was_cancelled)) + ); + } + + $details[] = array( + array($this->translate('Trigger time'), DateFormatter::formatDateTime($event->trigger_time)), + array( + $this->translate('Scheduled start time'), + DateFormatter::formatDateTime($event->scheduled_start_time) + ), + array( + $this->translate('Actual start time'), + DateFormatter::formatDateTime($event->actual_start_time) + ), + array( + $this->translate('Scheduled end time'), + DateFormatter::formatDateTime($event->scheduled_end_time) + ) + ); + + if ($type === 'dt_end') { + $details[] = array( + array( + $this->translate('Actual end time'), + DateFormatter::formatDateTime($event->actual_end_time) + ) + ); + } + + $details[] = array( + array($this->translate('Author'), $this->contact($event->author_name)), + array($this->translate('Comment'), $this->comment($event->comment_data)) + ); + + return call_user_func_array('array_merge', $details); + case 'comment': + case 'comment_deleted': + case 'ack': + case 'ack_deleted': + case 'dt_comment': + case 'dt_comment_deleted': + switch ($event->entry_type) { + case 'comment': + $entryType = $this->translate('User comment'); + break; + case 'downtime': + $entryType = $this->translate('Scheduled downtime'); + break; + case 'flapping': + $entryType = $this->translate('Flapping'); + break; + case 'ack': + $entryType = $this->translate('Acknowledgement'); + break; + default: + $entryType = $this->translate('N/A'); + } + + switch ($event->comment_source) { + case 'icinga': + $commentSource = $this->translate('Icinga'); + break; + case 'user': + $commentSource = $this->translate('User'); + break; + default: + $commentSource = $this->translate('N/A'); + } + + return array( + array($this->translate('Time'), DateFormatter::formatDateTime($event->comment_time)), + array($this->translate('Source'), $this->view->escape($commentSource)), + array($this->translate('Entry type'), $this->view->escape($entryType)), + array($this->translate('Author'), $this->contact($event->author_name)), + array($this->translate('Is persistent'), $this->yesOrNo($event->is_persistent)), + array($this->translate('Expires'), $this->yesOrNo($event->expires)), + array($this->translate('Expiration time'), DateFormatter::formatDateTime($event->expiration_time)), + array($this->translate('Deletion time'), DateFormatter::formatDateTime($event->deletion_time)), + array($this->translate('Message'), $this->comment($event->comment_data)) + ); + case 'flapping': + case 'flapping_deleted': + switch ($event->reason_type) { + case 'stopped': + $reasonType = $this->translate('Flapping stopped normally'); + break; + case 'disabled': + $reasonType = $this->translate('Flapping was disabled'); + break; + default: + $reasonType = $this->translate('N/A'); + } + + return array( + array($this->translate('Event time'), DateFormatter::formatDateTime($event->event_time)), + array($this->translate('Reason'), $this->view->escape($reasonType)), + array($this->translate('State change'), $this->percent($event->percent_state_change)), + array($this->translate('Low threshold'), $this->percent($event->low_threshold)), + array($this->translate('High threshold'), $this->percent($event->high_threshold)) + ); + case 'notify': + switch ($event->notification_reason) { + case 'normal_notification': + $notificationReason = $this->translate('Normal notification'); + break; + case 'ack': + $notificationReason = $this->translate('Problem acknowledgement'); + break; + case 'flapping_started': + $notificationReason = $this->translate('Flapping started'); + break; + case 'flapping_stopped': + $notificationReason = $this->translate('Flapping stopped'); + break; + case 'flapping_disabled': + $notificationReason = $this->translate('Flapping was disabled'); + break; + case 'dt_start': + $notificationReason = $this->translate('Downtime started'); + break; + case 'dt_end': + $notificationReason = $this->translate('Downtime ended'); + break; + case 'dt_cancel': + $notificationReason = $this->translate('Downtime was cancelled'); + break; + case 'custom_notification': + $notificationReason = $this->translate('Custom notification'); + break; + default: + $notificationReason = $this->translate('N/A'); + } + + $details = array( + array($this->translate('Start time'), DateFormatter::formatDateTime($event->start_time)), + array($this->translate('End time'), DateFormatter::formatDateTime($event->end_time)), + array($this->translate('Reason'), $this->view->escape($notificationReason)), + array( + $this->translate('State'), + $this->state($event->service_description !== null, $event->state) + ), + array($this->translate('Escalated'), $this->yesOrNo($event->escalated)), + array($this->translate('Contacts notified'), (int) $event->contacts_notified), + array( + $this->translate('Output'), + $this->pluginOutput($event->output) . $this->pluginOutput($event->long_output) + ) + ); + + return $details; + case 'hard_state': + case 'soft_state': + $isService = $event->service_description !== null; + + $details = array( + array($this->translate('State time'), DateFormatter::formatDateTime($event->state_time)), + array($this->translate('State'), $this->state($isService, $event->state)), + array($this->translate('Check source'), $event->check_source), + array($this->translate('Check attempt'), $this->view->escape(sprintf( + $this->translate('%d of %d'), + (int) $event->current_check_attempt, + (int) $event->max_check_attempts + ))), + array($this->translate('Last state'), $this->state($isService, $event->last_state)), + array($this->translate('Last hard state'), $this->state($isService, $event->last_hard_state)), + array( + $this->translate('Output'), + $this->pluginOutput($event->output) . $this->pluginOutput($event->long_output) + ) + ); + + return $details; + } + } +} diff --git a/modules/monitoring/application/controllers/HealthController.php b/modules/monitoring/application/controllers/HealthController.php new file mode 100644 index 0000000..48dd580 --- /dev/null +++ b/modules/monitoring/application/controllers/HealthController.php @@ -0,0 +1,196 @@ +getTabs() + ->add( + 'info', + array( + 'title' => $this->translate( + 'Show information about the current monitoring instance\'s process' + . ' and it\'s performance as well as available features' + ), + 'label' => $this->translate('Process Information'), + 'url' =>'monitoring/health/info' + ) + ) + ->add( + 'stats', + array( + 'title' => $this->translate( + 'Show statistics about the monitored objects' + ), + 'label' => $this->translate('Stats'), + 'url' =>'monitoring/health/stats' + ) + ) + ->extend(new DashboardAction())->extend(new MenuAction()); + } + + /** + * Display process information and program-wide commands + */ + public function infoAction() + { + $this->view->title = $this->translate('Process Information'); + $this->getTabs()->activate('info'); + $this->setAutorefreshInterval(10); + $this->view->backendName = $this->backend->getName(); + $programStatus = $this->backend + ->select() + ->from( + 'programstatus', + array( + 'is_currently_running', + 'process_id', + 'endpoint_name', + 'program_start_time', + 'status_update_time', + 'program_version', + 'last_command_check', + 'last_log_rotation', + 'global_service_event_handler', + 'global_host_event_handler', + 'notifications_enabled', + 'disable_notif_expire_time', + 'active_service_checks_enabled', + 'passive_service_checks_enabled', + 'active_host_checks_enabled', + 'passive_host_checks_enabled', + 'event_handlers_enabled', + 'obsess_over_services', + 'obsess_over_hosts', + 'flap_detection_enabled', + 'process_performance_data' + ) + ) + ->getQuery(); + $this->handleFormatRequest($programStatus); + $programStatus = $programStatus->fetchRow(); + if ($programStatus === false) { + return $this->render('not-running', true, null); + } + $this->view->programStatus = $programStatus; + $toggleFeaturesForm = new ToggleInstanceFeaturesCommandForm(); + $toggleFeaturesForm + ->setBackend($this->backend) + ->setStatus($programStatus) + ->load($programStatus) + ->handleRequest(); + $this->view->toggleFeaturesForm = $toggleFeaturesForm; + + $this->view->runtimevariables = (object) $this->backend->select() + ->from('runtimevariables', array('varname', 'varvalue')) + ->getQuery()->fetchPairs(); + + $this->view->checkperformance = $this->backend->select() + ->from('runtimesummary') + ->getQuery()->fetchAll(); + } + + /** + * Display stats about current checks and monitored objects + */ + public function statsAction() + { + $this->view->title = $this->translate('Stats'); + $this->getTabs()->activate('stats'); + + $servicestats = $this->backend->select()->from('servicestatussummary', array( + 'services_critical', + 'services_critical_handled', + 'services_critical_unhandled', + 'services_ok', + 'services_pending', + 'services_total', + 'services_unknown', + 'services_unknown_handled', + 'services_unknown_unhandled', + 'services_warning', + 'services_warning_handled', + 'services_warning_unhandled' + )); + $this->applyRestriction('monitoring/filter/objects', $servicestats); + $this->view->servicestats = $servicestats->fetchRow(); + $this->view->unhandledServiceProblems = $this->view->servicestats->services_critical_unhandled + + $this->view->servicestats->services_unknown_unhandled + + $this->view->servicestats->services_warning_unhandled; + + $hoststats = $this->backend->select()->from('hoststatussummary', array( + 'hosts_total', + 'hosts_up', + 'hosts_down', + 'hosts_down_handled', + 'hosts_down_unhandled', + 'hosts_unreachable', + 'hosts_unreachable_handled', + 'hosts_unreachable_unhandled', + 'hosts_pending', + )); + $this->applyRestriction('monitoring/filter/objects', $hoststats); + $this->view->hoststats = $hoststats->fetchRow(); + $this->view->unhandledhostProblems = $this->view->hoststats->hosts_down_unhandled + + $this->view->hoststats->hosts_unreachable_unhandled; + + $this->view->unhandledProblems = $this->view->unhandledhostProblems + + $this->view->unhandledServiceProblems; + + $this->view->runtimevariables = (object) $this->backend->select() + ->from('runtimevariables', array('varname', 'varvalue')) + ->getQuery()->fetchPairs(); + + $this->view->checkperformance = $this->backend->select() + ->from('runtimesummary') + ->getQuery()->fetchAll(); + } + + /** + * Disable notifications w/ an optional expire time + */ + public function disableNotificationsAction() + { + $this->assertPermission('monitoring/command/feature/instance'); + $this->view->title = $this->translate('Disable Notifications'); + $programStatus = $this->backend + ->select() + ->from( + 'programstatus', + array( + 'notifications_enabled', + 'disable_notif_expire_time' + ) + ) + ->getQuery() + ->fetchRow(); + $this->view->programStatus = $programStatus; + if ((bool) $programStatus->notifications_enabled === false) { + return; + } else { + $form = new DisableNotificationsExpireCommandForm(); + $form + ->setRedirectUrl('monitoring/health/info') + ->handleRequest(); + $this->view->form = $form; + } + } +} diff --git a/modules/monitoring/application/controllers/HostController.php b/modules/monitoring/application/controllers/HostController.php new file mode 100644 index 0000000..94f1a60 --- /dev/null +++ b/modules/monitoring/application/controllers/HostController.php @@ -0,0 +1,185 @@ +backend, $this->params->getRequired('host')); + $this->applyRestriction('monitoring/filter/objects', $host); + if ($host->fetch() === false) { + $this->httpNotFound($this->translate('Host not found')); + } + $this->object = $host; + $this->createTabs(); + $this->getTabs()->activate('host'); + $this->view->title = $host->host_display_name; + $this->view->defaultTitle = $this->translate('Hosts') . ' :: ' . $this->view->defaultTitle; + } + + /** + * Get host actions from hook + * + * @return Navigation + */ + protected function getHostActions() + { + $navigation = new Navigation(); + foreach (Hook::all('Monitoring\\HostActions') as $hook) { + $navigation->merge($hook->getNavigation($this->object)); + } + + return $navigation; + } + + /** + * Show a host + */ + public function showAction() + { + $this->view->actions = $this->getHostActions(); + 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_address6', + '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_active_checks_enabled', + 'service_passive_checks_enabled', + 'current_check_attempt' => 'service_current_check_attempt', + 'max_check_attempts' => 'service_max_check_attempts', + 'service_check_command', + 'service_next_update' + )); + $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 + */ + public function acknowledgeProblemAction() + { + $this->assertPermission('monitoring/command/acknowledge-problem'); + + $form = new AcknowledgeProblemCommandForm(); + $form->setTitle($this->translate('Acknowledge Host Problem')); + $this->handleCommandForm($form); + } + + /** + * Add a host comment + */ + public function addCommentAction() + { + $this->assertPermission('monitoring/command/comment/add'); + + $form = new AddCommentCommandForm(); + $form->setTitle($this->translate('Add Host Comment')); + $this->handleCommandForm($form); + } + + /** + * Reschedule a host check + */ + public function rescheduleCheckAction() + { + $this->assertPermission('monitoring/command/schedule-check'); + + $form = new ScheduleHostCheckCommandForm(); + $form->setTitle($this->translate('Reschedule Host Check')); + $this->handleCommandForm($form); + } + + /** + * Schedule a host downtime + */ + public function scheduleDowntimeAction() + { + $this->assertPermission('monitoring/command/downtime/schedule'); + + $form = new ScheduleHostDowntimeCommandForm(); + $form->setTitle($this->translate('Schedule Host Downtime')); + $this->handleCommandForm($form); + } + + /** + * Submit a passive host check result + */ + public function processCheckResultAction() + { + $this->assertPermission('monitoring/command/process-check-result'); + + $form = new ProcessCheckResultCommandForm(); + $form->setTitle($this->translate('Submit Passive Host Check Result')); + $this->handleCommandForm($form); + } + + /** + * Send a custom notification for host + */ + public function sendCustomNotificationAction() + { + $this->assertPermission('monitoring/command/send-custom-notification'); + + $form = new SendCustomNotificationCommandForm(); + $form->setTitle($this->translate('Send Custom Host Notification')); + $this->handleCommandForm($form); + } +} diff --git a/modules/monitoring/application/controllers/HostsController.php b/modules/monitoring/application/controllers/HostsController.php new file mode 100644 index 0000000..9219df8 --- /dev/null +++ b/modules/monitoring/application/controllers/HostsController.php @@ -0,0 +1,260 @@ +backend); + $this->applyRestriction('monitoring/filter/objects', $hostList); + $hostList->addFilter(Filter::fromQueryString((string) $this->params)); + $this->hostList = $hostList; + $this->hostList->setColumns(array( + 'host_acknowledged', + 'host_active_checks_enabled', + 'host_display_name', + 'host_event_handler_enabled', + 'host_flap_detection_enabled', + 'host_handled', + 'host_in_downtime', + 'host_is_flapping', + 'host_last_state_change', + 'host_name', + 'host_notifications_enabled', + 'host_obsessing', + 'host_passive_checks_enabled', + 'host_problem', + 'host_state', + 'instance_name' + )); + $this->view->baseFilter = $this->hostList->getFilter(); + $this->getTabs()->add( + 'show', + array( + 'label' => $this->translate('Hosts') . sprintf(' (%d)', count($this->hostList)), + 'title' => sprintf( + $this->translate('Show summarized information for %u hosts'), + count($this->hostList) + ), + 'url' => Url::fromRequest() + ) + )->extend(new DashboardAction())->extend(new MenuAction())->activate('show'); + $this->view->listAllLink = Url::fromRequest()->setPath('monitoring/list/hosts'); + $this->view->title = $this->translate('Hosts'); + } + + protected function handleCommandForm(ObjectsCommandForm $form) + { + $form + ->setBackend($this->backend) + ->setObjects($this->hostList) + ->setRedirectUrl(Url::fromPath('monitoring/hosts/show')->setParams( + $this->params->without('host_active_checks_enabled') + )) + ->handleRequest(); + + $this->view->form = $form; + $this->view->objects = $this->hostList; + $this->view->stats = $this->hostList->getStateSummary(); + $this->_helper->viewRenderer('partials/command/objects-command-form', null, true); + return $form; + } + + public function showAction() + { + $this->setAutorefreshInterval(15); + $activeChecksEnabled = $this->hostList->getFeatureStatus()['active_checks_enabled'] !== 0; + if ($this->Auth()->hasPermission('monitoring/command/schedule-check') + || ($this->Auth()->hasPermission('monitoring/command/schedule-check/active-only') + && $activeChecksEnabled + ) + ) { + $checkNowForm = new CheckNowCommandForm(); + $checkNowForm + ->setObjects($this->hostList) + ->handleRequest(); + $this->view->checkNowForm = $checkNowForm; + } + + $acknowledgedObjects = $this->hostList->getAcknowledgedObjects(); + if (! empty($acknowledgedObjects)) { + $removeAckForm = new RemoveAcknowledgementCommandForm(); + $removeAckForm + ->setObjects($acknowledgedObjects) + ->handleRequest(); + $this->view->removeAckForm = $removeAckForm; + } + + $featureStatus = $this->hostList->getFeatureStatus(); + $toggleFeaturesForm = new ToggleObjectFeaturesCommandForm(array( + 'backend' => $this->backend, + 'objects' => $this->hostList + )); + $toggleFeaturesForm + ->load((object) $featureStatus) + ->handleRequest(); + $this->view->toggleFeaturesForm = $toggleFeaturesForm; + + $hostStates = $this->hostList->getStateSummary(); + + if ($activeChecksEnabled) { + $this->view->rescheduleAllLink = Url::fromRequest() + ->setPath('monitoring/hosts/reschedule-check') + ->addParams(['host_active_checks_enabled' => true]); + } + + $this->view->downtimeAllLink = Url::fromRequest()->setPath('monitoring/hosts/schedule-downtime'); + $this->view->processCheckResultAllLink = Url::fromRequest()->setPath('monitoring/hosts/process-check-result'); + $this->view->addCommentLink = Url::fromRequest()->setPath('monitoring/hosts/add-comment'); + $this->view->stats = $hostStates; + $this->view->objects = $this->hostList; + $this->view->unhandledObjects = $this->hostList->getUnhandledObjects(); + $this->view->problemObjects = $this->hostList->getProblemObjects(); + $this->view->acknowledgeUnhandledLink = Url::fromPath('monitoring/hosts/acknowledge-problem') + ->setQueryString($this->hostList->getUnhandledObjects()->objectsFilter()->toQueryString()); + $this->view->downtimeUnhandledLink = Url::fromPath('monitoring/hosts/schedule-downtime') + ->setQueryString($this->hostList->getUnhandledObjects()->objectsFilter()->toQueryString()); + $this->view->downtimeLink = Url::fromPath('monitoring/hosts/schedule-downtime') + ->setQueryString($this->hostList->getProblemObjects()->objectsFilter()->toQueryString()); + $this->view->acknowledgedObjects = $this->hostList->getAcknowledgedObjects(); + $this->view->acknowledgeLink = Url::fromPath('monitoring/hosts/acknowledge-problem') + ->setQueryString($this->hostList->getUnacknowledgedObjects()->objectsFilter()->toQueryString()); + $this->view->unacknowledgedObjects = $this->hostList->getUnacknowledgedObjects(); + $this->view->objectsInDowntime = $this->hostList->getObjectsInDowntime(); + $this->view->inDowntimeLink = Url::fromPath('monitoring/list/hosts') + ->setQueryString( + $this->hostList + ->getObjectsInDowntime() + ->objectsFilter() + ->toQueryString() + ); + $this->view->showDowntimesLink = Url::fromPath('monitoring/list/downtimes') + ->setQueryString( + $this->hostList + ->objectsFilter() + ->andFilter(FilterEqual::where('object_type', 'host')) + ->toQueryString() + ); + $this->view->commentsLink = Url::fromRequest()->setPath('monitoring/list/comments'); + $this->view->sendCustomNotificationLink = Url::fromRequest() + ->setPath('monitoring/hosts/send-custom-notification'); + + $this->view->extensionsHtml = array(); + foreach (Hook::all('Monitoring\DetailviewExtension') as $hook) { + /** @var DetailviewExtensionHook $hook */ + try { + $html = $hook->setView($this->view)->getHtmlForObjects($this->hostList); + } catch (Exception $e) { + $html = $this->view->escape($e->getMessage()); + } + + if ($html) { + $module = $this->view->escape($hook->getModule()->getName()); + $this->view->extensionsHtml[] = + '
' + . $html + . '
'; + } + } + } + + /** + * Add a host comments + */ + public function addCommentAction() + { + $this->assertPermission('monitoring/command/comment/add'); + + $form = new AddCommentCommandForm(); + $form->setTitle($this->translate('Add Host Comments')); + $this->handleCommandForm($form); + } + + /** + * Acknowledge host problems + */ + public function acknowledgeProblemAction() + { + $this->assertPermission('monitoring/command/acknowledge-problem'); + + $form = new AcknowledgeProblemCommandForm(); + $form->setTitle($this->translate('Acknowledge Host Problems')); + $this->handleCommandForm($form); + } + + /** + * Reschedule host checks + */ + public function rescheduleCheckAction() + { + $this->assertPermission('monitoring/command/schedule-check'); + + $form = new ScheduleHostCheckCommandForm(); + $form->setTitle($this->translate('Reschedule Host Checks')); + $this->handleCommandForm($form); + } + + /** + * Schedule host downtimes + */ + public function scheduleDowntimeAction() + { + $this->assertPermission('monitoring/command/downtime/schedule'); + + $form = new ScheduleHostDowntimeCommandForm(); + $form->setTitle($this->translate('Schedule Host Downtimes')); + $this->handleCommandForm($form); + } + + /** + * Submit passive host check results + */ + public function processCheckResultAction() + { + $this->assertPermission('monitoring/command/process-check-result'); + + $form = new ProcessCheckResultCommandForm(); + $form->setTitle($this->translate('Submit Passive Host Check Results')); + $this->handleCommandForm($form); + } + + /** + * Send a custom notification for hosts + */ + public function sendCustomNotificationAction() + { + $this->assertPermission('monitoring/command/send-custom-notification'); + + $form = new SendCustomNotificationCommandForm(); + $form->setTitle($this->translate('Send Custom Host Notification')); + $this->handleCommandForm($form); + } +} diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php new file mode 100644 index 0000000..0ccff99 --- /dev/null +++ b/modules/monitoring/application/controllers/ListController.php @@ -0,0 +1,808 @@ +createTabs(); + } + + /** + * Overwrite the backend to use (used for testing) + * + * @param MonitoringBackend $backend The Backend that should be used for querying + */ + public function setBackend($backend) + { + $this->backend = $backend; + } + + /** + * List hosts + */ + public function hostsAction() + { + $this->addTitleTab( + 'hosts', + $this->translate('Hosts'), + $this->translate('List hosts') + ); + + $this->setAutorefreshInterval(10); + + // Handle soft and hard states + if (strtolower($this->params->shift('stateType', 'soft')) === 'hard') { + $stateColumn = 'host_hard_state'; + $stateChangeColumn = 'host_last_hard_state_change'; + } else { + $stateColumn = 'host_state'; + $stateChangeColumn = 'host_last_state_change'; + } + + $hosts = $this->backend->select()->from('hoststatus', array_merge(array( + 'host_icon_image', + 'host_icon_image_alt', + 'host_name', + 'host_display_name', + 'host_state' => $stateColumn, + 'host_acknowledged', + 'host_output', + 'host_attempt', + 'host_in_downtime', + 'host_is_flapping', + 'host_state_type', + 'host_handled', + 'host_last_state_change' => $stateChangeColumn, + 'host_notifications_enabled', + 'host_active_checks_enabled', + 'host_passive_checks_enabled', + 'host_check_command', + 'host_next_update' + ), $this->addColumns())); + + $this->setupPaginationControl($hosts); + $this->setupSortControl(array( + 'host_severity' => $this->translate('Severity'), + 'host_state' => $this->translate('Current State'), + 'host_display_name' => $this->translate('Hostname'), + 'host_address' => $this->translate('Address'), + 'host_last_check' => $this->translate('Last Check'), + 'host_last_state_change' => $this->translate('Last State Change') + ), $hosts); + $this->filterQuery($hosts); + $this->setupLimitControl(); + + $stats = $this->backend->select()->from('hoststatussummary', array( + 'hosts_total', + 'hosts_up', + 'hosts_down', + 'hosts_down_handled', + 'hosts_down_unhandled', + 'hosts_unreachable', + 'hosts_unreachable_handled', + 'hosts_unreachable_unhandled', + 'hosts_pending', + )); + $this->applyRestriction('monitoring/filter/objects', $stats); + + $this->view->hosts = $hosts; + $this->view->stats = $stats; + } + + /** + * List services + */ + public function servicesAction() + { + $this->addTitleTab( + 'services', + $this->translate('Services'), + $this->translate('List services') + ); + + // Handle soft and hard states + if (strtolower($this->params->shift('stateType', 'soft')) === 'hard') { + $stateColumn = 'service_hard_state'; + $stateChangeColumn = 'service_last_hard_state_change'; + } else { + $stateColumn = 'service_state'; + $stateChangeColumn = 'service_last_state_change'; + } + + $this->setAutorefreshInterval(10); + + $services = $this->backend->select()->from('servicestatus', array_merge(array( + 'host_name', + 'host_display_name', + 'host_state', + 'service_description', + 'service_display_name', + 'service_state' => $stateColumn, + 'service_in_downtime', + 'service_acknowledged', + 'service_handled', + 'service_output', + 'service_perfdata', + 'service_attempt', + 'service_last_state_change' => $stateChangeColumn, + 'service_icon_image', + 'service_icon_image_alt', + 'service_is_flapping', + 'service_state_type', + 'service_handled', + 'service_severity', + 'service_notifications_enabled', + 'service_active_checks_enabled', + 'service_passive_checks_enabled', + 'service_check_command', + 'service_next_update' + ), $this->addColumns())); + + $this->setupPaginationControl($services); + $this->setupSortControl(array( + 'service_severity' => $this->translate('Service Severity'), + 'service_state' => $this->translate('Current Service State'), + 'service_display_name' => $this->translate('Service Name'), + 'service_last_check' => $this->translate('Last Service Check'), + 'service_last_state_change' => $this->translate('Last State Change'), + 'host_severity' => $this->translate('Host Severity'), + 'host_state' => $this->translate('Current Host State'), + 'host_display_name' => $this->translate('Hostname'), + 'host_address' => $this->translate('Host Address'), + 'host_last_check' => $this->translate('Last Host Check') + ), $services); + $this->filterQuery($services); + $this->setupLimitControl(); + + $stats = $this->backend->select()->from('servicestatussummary', array( + 'services_critical', + 'services_critical_handled', + 'services_critical_unhandled', + 'services_ok', + 'services_pending', + 'services_total', + 'services_unknown', + 'services_unknown_handled', + 'services_unknown_unhandled', + 'services_warning', + 'services_warning_handled', + 'services_warning_unhandled' + )); + $this->applyRestriction('monitoring/filter/objects', $stats); + + $this->view->services = $services; + $this->view->stats = $stats; + if (strpos($this->params->get('host_name', '*'), '*') === false) { + $this->view->showHost = false; + } else { + $this->view->showHost = true; + } + } + + /** + * List downtimes + */ + public function downtimesAction() + { + $this->addTitleTab( + 'downtimes', + $this->translate('Downtimes'), + $this->translate('List downtimes') + ); + + $this->setAutorefreshInterval(12); + + $downtimes = $this->backend->select()->from('downtime', array( + 'id' => 'downtime_internal_id', + 'objecttype' => 'object_type', + 'comment' => 'downtime_comment', + 'author_name' => 'downtime_author_name', + 'start' => 'downtime_start', + 'scheduled_start' => 'downtime_scheduled_start', + 'scheduled_end' => 'downtime_scheduled_end', + 'end' => 'downtime_end', + 'duration' => 'downtime_duration', + 'is_flexible' => 'downtime_is_flexible', + 'is_fixed' => 'downtime_is_fixed', + 'is_in_effect' => 'downtime_is_in_effect', + 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', + 'host_state', + 'service_state', + 'host_name', + 'service_description', + 'host_display_name', + 'service_display_name' + )); + + $this->setupPaginationControl($downtimes); + $this->setupSortControl(array( + 'downtime_is_in_effect' => $this->translate('Is In Effect'), + 'host_display_name' => $this->translate('Host'), + 'service_display_name' => $this->translate('Service'), + 'downtime_entry_time' => $this->translate('Entry Time'), + 'downtime_author' => $this->translate('Author'), + 'downtime_start' => $this->translate('Start Time'), + 'downtime_end' => $this->translate('End Time'), + 'downtime_scheduled_start' => $this->translate('Scheduled Start'), + 'downtime_scheduled_end' => $this->translate('Scheduled End'), + 'downtime_duration' => $this->translate('Duration') + ), $downtimes); + $this->filterQuery($downtimes); + $this->setupLimitControl(); + + $this->view->downtimes = $downtimes; + + if ($this->Auth()->hasPermission('monitoring/command/downtime/delete')) { + $this->view->delDowntimeForm = new DeleteDowntimeCommandForm(); + $this->view->delDowntimeForm->handleRequest(); + } + } + + /** + * List notifications + */ + public function notificationsAction() + { + $this->addTitleTab( + 'notifications', + $this->translate('Notifications'), + $this->translate('List notifications') + ); + + $this->setAutorefreshInterval(15); + + $notifications = $this->backend->select()->from('notification', array( + 'id', + 'host_display_name', + 'host_name', + 'notification_contact_name', + 'notification_output', + 'notification_state', + 'notification_timestamp', + 'service_description', + 'service_display_name' + )); + + $this->setupPaginationControl($notifications); + $this->setupSortControl(array( + 'notification_timestamp' => $this->translate('Notification Start') + ), $notifications); + $this->filterQuery($notifications); + $this->setupLimitControl(); + + $this->view->notifications = $notifications; + } + + /** + * List contacts + */ + public function contactsAction() + { + if (! $this->hasPermission('*') && $this->hasPermission('no-monitoring/contacts')) { + throw new SecurityException('No permission for %s', 'monitoring/contacts'); + } + + $this->addTitleTab( + 'contacts', + $this->translate('Contacts'), + $this->translate('List contacts') + ); + + $contacts = $this->backend->select()->from('contact', array( + 'contact_name', + 'contact_alias', + 'contact_email', + 'contact_pager', + 'contact_notify_service_timeperiod', + 'contact_notify_host_timeperiod' + )); + + $this->setupPaginationControl($contacts); + $this->setupSortControl(array( + 'contact_name' => $this->translate('Name'), + 'contact_alias' => $this->translate('Alias'), + 'contact_email' => $this->translate('Email'), + 'contact_pager' => $this->translate('Pager Address / Number') + ), $contacts); + $this->filterQuery($contacts); + $this->setupLimitControl(); + + $this->view->contacts = $contacts; + } + + public function eventgridAction() + { + $this->addTitleTab('eventgrid', $this->translate('Event Grid'), $this->translate('Show the Event Grid')); + + $form = new StatehistoryForm(); + $form->setEnctype(Zend_Form::ENCTYPE_URLENCODED); + $form->setMethod('get'); + $form->setTokenDisabled(); + $form->setUidDisabled(); + $form->render(); + $this->view->form = $form; + + $this->params + ->remove('showCompact') + ->remove('format'); + $orientation = $this->params->shift('vertical', 0) ? 'vertical' : 'horizontal'; +/* + $orientationBox = new SelectBox( + 'orientation', + array( + '0' => mt('monitoring', 'Vertical'), + '1' => mt('monitoring', 'Horizontal') + ), + mt('monitoring', 'Orientation'), + 'horizontal' + ); + $orientationBox->applyRequest($this->getRequest()); +*/ + $objectType = $form->getValue('objecttype'); + $from = $form->getValue('from'); + $query = $this->backend->select()->from( + 'eventgrid' . $objectType, + array('day', $form->getValue('state')) + ); + $this->params->remove(array('objecttype', 'from', 'to', 'state', 'btn_submit')); + $this->view->filter = Filter::fromQuerystring((string) $this->params); + $query->applyFilter($this->view->filter); + $query->applyFilter(Filter::fromQuerystring('timestamp>=' . $from)); + $this->applyRestriction('monitoring/filter/objects', $query); + $this->view->summary = $query; + $this->view->column = $form->getValue('state'); +// $this->view->orientationBox = $orientationBox; + $this->view->orientation = $orientation; + } + + /** + * List contact groups + */ + public function contactgroupsAction() + { + if (! $this->hasPermission('*') && $this->hasPermission('no-monitoring/contacts')) { + throw new SecurityException('No permission for %s', 'monitoring/contacts'); + } + + $this->addTitleTab( + 'contactgroups', + $this->translate('Contact Groups'), + $this->translate('List contact groups') + ); + + $contactGroups = $this->backend->select()->from('contactgroup', array( + 'contactgroup_name', + 'contactgroup_alias', + 'contact_count' + )); + + $this->setupPaginationControl($contactGroups); + $this->setupSortControl(array( + 'contactgroup_name' => $this->translate('Contactgroup Name'), + 'contactgroup_alias' => $this->translate('Contactgroup Alias') + ), $contactGroups); + $this->filterQuery($contactGroups); + $this->setupLimitControl(); + + $this->view->contactGroups = $contactGroups; + } + + /** + * List all comments + */ + public function commentsAction() + { + $this->addTitleTab( + 'comments', + $this->translate('Comments'), + $this->translate('List comments') + ); + + $this->setAutorefreshInterval(12); + + $comments = $this->backend->select()->from('comment', array( + 'id' => 'comment_internal_id', + 'objecttype' => 'object_type', + 'comment' => 'comment_data', + 'author' => 'comment_author_name', + 'timestamp' => 'comment_timestamp', + 'type' => 'comment_type', + 'persistent' => 'comment_is_persistent', + 'expiration' => 'comment_expiration', + 'name' => 'comment_name', + 'host_name', + 'service_description', + 'host_display_name', + 'service_display_name' + )); + + $this->setupPaginationControl($comments); + $this->setupSortControl( + array( + 'comment_timestamp' => $this->translate('Comment Timestamp'), + 'host_display_name' => $this->translate('Host'), + 'service_display_name' => $this->translate('Service'), + 'comment_type' => $this->translate('Comment Type'), + 'comment_expiration' => $this->translate('Expiration') + ), + $comments + ); + $this->filterQuery($comments); + $this->setupLimitControl(); + + $this->view->comments = $comments; + + if ($this->Auth()->hasPermission('monitoring/command/comment/delete')) { + $this->view->delCommentForm = new DeleteCommentCommandForm(); + $this->view->delCommentForm->handleRequest(); + } + } + + /** + * List service groups + */ + public function servicegroupsAction() + { + $this->addTitleTab( + 'servicegroups', + $this->translate('Service Groups'), + $this->translate('List service groups') + ); + + $this->setAutorefreshInterval(12); + + $serviceGroups = $this->backend->select()->from('servicegroupsummary', array( + 'servicegroup_alias', + 'servicegroup_name', + 'services_critical_handled', + 'services_critical_unhandled', + 'services_ok', + 'services_pending', + 'services_total', + 'services_unknown_handled', + 'services_unknown_unhandled', + 'services_warning_handled', + 'services_warning_unhandled' + )); + + $this->setupPaginationControl($serviceGroups); + $this->setupSortControl(array( + 'servicegroup_alias' => $this->translate('Service Group Name'), + 'services_severity' => $this->translate('Severity'), + 'services_total' => $this->translate('Total Services') + ), $serviceGroups); + $this->filterQuery($serviceGroups); + $this->setupLimitControl(); + + $this->view->serviceGroups = $serviceGroups; + } + + /** + * List service groups + */ + public function servicegroupGridAction() + { + $this->addTitleTab( + 'servicegroup-grid', + $this->translate('Service Group Grid'), + $this->translate('Show the Service Group Grid') + ); + + $this->setAutorefreshInterval(15); + + $serviceGroups = $this->backend->select()->from('servicegroupsummary', array( + 'servicegroup_alias', + 'servicegroup_name', + 'services_critical_handled', + 'services_critical_unhandled', + 'services_ok', + 'services_pending', + 'services_total', + 'services_unknown_handled', + 'services_unknown_unhandled', + 'services_warning_handled', + 'services_warning_unhandled' + )); + $this->filterQuery($serviceGroups); + + $this->setupSortControl(array( + 'servicegroup_alias' => $this->translate('Service Group Name'), + 'services_severity' => $this->translate('Severity'), + 'services_total' => $this->translate('Total Services') + ), $serviceGroups, ['services_severity' => 'desc']); + + $this->view->serviceGroups = $serviceGroups; + } + + /** + * List host groups + */ + public function hostgroupsAction() + { + $this->addTitleTab( + 'hostgroups', + $this->translate('Host Groups'), + $this->translate('List host groups') + ); + + $this->setAutorefreshInterval(12); + + $hostGroups = $this->backend->select()->from('hostgroupsummary', array( + 'hostgroup_alias', + 'hostgroup_name', + 'hosts_down_handled', + 'hosts_down_unhandled', + 'hosts_pending', + 'hosts_total', + 'hosts_unreachable_handled', + 'hosts_unreachable_unhandled', + 'hosts_up', + 'services_critical_handled', + 'services_critical_unhandled', + 'services_ok', + 'services_pending', + 'services_total', + 'services_unknown_handled', + 'services_unknown_unhandled', + 'services_warning_handled', + 'services_warning_unhandled' + )); + + $this->setupPaginationControl($hostGroups); + $this->setupSortControl(array( + 'hostgroup_alias' => $this->translate('Host Group Name'), + 'hosts_severity' => $this->translate('Severity'), + 'hosts_total' => $this->translate('Total Hosts'), + 'services_total' => $this->translate('Total Services') + ), $hostGroups); + $this->filterQuery($hostGroups); + $this->setupLimitControl(); + + $this->view->hostGroups = $hostGroups; + } + + /** + * List host groups + */ + public function hostgroupGridAction() + { + $this->addTitleTab( + 'hostgroup-grid', + $this->translate('Host Group Grid'), + $this->translate('Show the Host Group Grid') + ); + + $this->setAutorefreshInterval(15); + + $hostGroups = $this->backend->select()->from('hostgroupsummary', [ + 'hostgroup_alias', + 'hostgroup_name', + 'hosts_down_handled', + 'hosts_down_unhandled', + 'hosts_pending', + 'hosts_total', + 'hosts_unreachable_handled', + 'hosts_unreachable_unhandled', + 'hosts_up' + ]); + $this->filterQuery($hostGroups); + + $this->setupSortControl([ + 'hosts_severity' => $this->translate('Severity'), + 'hostgroup_alias' => $this->translate('Host Group Name'), + 'hosts_total' => $this->translate('Total Hosts'), + 'services_total' => $this->translate('Total Services') + ], $hostGroups, ['hosts_severity' => 'desc']); + + $this->view->hostGroups = $hostGroups; + } + + public function eventhistoryAction() + { + $this->addTitleTab( + 'eventhistory', + $this->translate('Event Overview'), + $this->translate('List event records') + ); + + $query = $this->backend->select()->from('eventhistory', array( + 'id', + 'host_name', + 'host_display_name', + 'service_description', + 'service_display_name', + 'object_type', + 'timestamp', + 'state', + 'output', + 'type' + )); + + $this->view->history = $query; + + $this->setupSortControl(array( + 'timestamp' => $this->translate('Occurence') + ), $query); + $this->filterQuery($query); + $this->setupLimitControl(); + } + + public function servicegridAction() + { + if ($this->params->has('noscript_apply')) { + $this->redirectNow($this->getRequest()->getUrl()->without('noscript_apply')); + } + + $this->addTitleTab('servicegrid', $this->translate('Service Grid'), $this->translate('Show the Service Grid')); + $this->setAutorefreshInterval(15); + $query = $this->backend->select()->from('servicestatus', array( + 'host_display_name', + 'host_name', + 'service_description', + 'service_display_name', + 'service_handled', + 'service_output', + 'service_state' + )); + $this->filterQuery($query); + $filter = (bool) $this->params->shift('problems', false) ? Filter::where('service_problem', 1) : null; + + $this->view->problemToggle = $problemToggle = new Form(['method' => 'GET']); + $problemToggle->setUidDisabled(); + $problemToggle->setTokenDisabled(); + $problemToggle->setAttrib('class', 'filter-toggle inline icinga-controls'); + $problemToggle->addElement('checkbox', 'problems', [ + 'disableHidden' => true, + 'autosubmit' => true, + 'value' => $filter !== null, + 'label' => $this->translate('Problems Only'), + 'decorators' => ['ViewHelper', ['Label', ['placement' => 'APPEND']]] + ]); + + if ($this->params->get('flipped', false)) { + $pivot = $query + ->pivot( + 'host_name', + 'service_description', + $filter, + $filter ? clone $filter : null + ) + ->setYAxisHeader('service_display_name') + ->setXAxisHeader('host_display_name'); + } else { + $pivot = $query + ->pivot( + 'service_description', + 'host_name', + $filter, + $filter ? clone $filter : null + ) + ->setXAxisHeader('service_display_name') + ->setYAxisHeader('host_display_name'); + } + $this->setupSortControl(array( + 'host_display_name' => $this->translate('Hostname'), + 'service_display_name' => $this->translate('Service Name') + ), $pivot); + $this->view->horizontalPaginator = $pivot->paginateXAxis(); + $this->view->verticalPaginator = $pivot->paginateYAxis(); + list($pivotData, $pivotHeader) = $pivot->toArray(); + $this->view->pivotData = $pivotData; + $this->view->pivotHeader = $pivotHeader; + if ($this->params->get('flipped', false)) { + $this->render('servicegrid-flipped'); + } + } + + /** + * Apply filters on a DataView + * + * @param DataView $dataView The DataView to apply filters on + * + * @return DataView $dataView + */ + protected function filterQuery(DataView $dataView) + { + $this->setupFilterControl($dataView, null, null, array( + 'format', // handleFormatRequest() + 'stateType', // hostsAction() and servicesAction() + 'addColumns', // addColumns() + 'problems', // servicegridAction() + 'flipped' // servicegridAction() + )); + + if ($this->params->get('format') !== 'sql' || $this->hasPermission('config/authentication/roles/show')) { + $this->applyRestriction('monitoring/filter/objects', $dataView); + } + + $this->handleFormatRequest($dataView); + + return $dataView; + } + + /** + * Get columns to be added from URL parameter 'addColumns' + * and assign to $this->view->addColumns (as array) + * + * @return array + */ + protected function addColumns() + { + $columns = preg_split( + '~,~', + $this->params->shift('addColumns', ''), + -1, + PREG_SPLIT_NO_EMPTY + ); + + $customVars = []; + $additionalCols = []; + foreach ($columns as $column) { + if (preg_match('~^_(host|service)_([a-zA-Z0-9_]+)$~', $column, $m)) { + $customVars[$m[1]]['vars'][$m[2]] = null; + } else { + $additionalCols[] = $column; + } + } + + if (! empty($customVars)) { + $blacklistedProperties = new GlobFilter( + $this->getRestrictions('monitoring/blacklist/properties') + ); + $customVars = $blacklistedProperties->removeMatching($customVars); + foreach ($customVars as $type => $vars) { + foreach ($vars['vars'] as $var => $_) { + $additionalCols[] = '_' . $type . '_' . $var; + } + } + } + + $this->view->addColumns = $additionalCols; + return $additionalCols; + } + + protected function addTitleTab($action, $title, $tip) + { + $this->getTabs()->add($action, array( + 'title' => $tip, + 'label' => $title, + 'url' => Url::fromRequest() + ))->activate($action); + $this->view->title = $title; + } + + /** + * Return all tabs for this controller + * + * @return Tabs + */ + private function createTabs() + { + $this->getTabs()->extend(new OutputFormat())->extend(new DashboardAction())->extend(new MenuAction()); + } +} diff --git a/modules/monitoring/application/controllers/ServiceController.php b/modules/monitoring/application/controllers/ServiceController.php new file mode 100644 index 0000000..d3eeb1c --- /dev/null +++ b/modules/monitoring/application/controllers/ServiceController.php @@ -0,0 +1,147 @@ +backend, + $this->params->getRequired('host'), + $this->params->getRequired('service') + ); + + $this->applyRestriction('monitoring/filter/objects', $service); + + if ($service->fetch() === false) { + $this->httpNotFound($this->translate('Service not found')); + } + $this->object = $service; + $this->createTabs(); + $this->getTabs()->activate('service'); + $this->view->title = $service->service_display_name; + $this->view->defaultTitle = join(' :: ', [ + $service->host_display_name, + $this->translate('Services'), + $this->view->defaultTitle + ]); + } + + /** + * Get service actions from hook + * + * @return Navigation + */ + protected function getServiceActions() + { + $navigation = new Navigation(); + foreach (Hook::all('Monitoring\\ServiceActions') as $hook) { + $navigation->merge($hook->getNavigation($this->object)); + } + + return $navigation; + } + + /** + * Show a service + */ + public function showAction() + { + $this->view->actions = $this->getServiceActions(); + parent::showAction(); + } + + + /** + * Acknowledge a service problem + */ + public function acknowledgeProblemAction() + { + $this->assertPermission('monitoring/command/acknowledge-problem'); + + $form = new AcknowledgeProblemCommandForm(); + $form->setTitle($this->translate('Acknowledge Service Problem')); + $this->handleCommandForm($form); + } + + /** + * Add a service comment + */ + public function addCommentAction() + { + $this->assertPermission('monitoring/command/comment/add'); + + $form = new AddCommentCommandForm(); + $form->setTitle($this->translate('Add Service Comment')); + $this->handleCommandForm($form); + } + + /** + * Reschedule a service check + */ + public function rescheduleCheckAction() + { + $this->assertPermission('monitoring/command/schedule-check'); + + $form = new ScheduleServiceCheckCommandForm(); + $form->setTitle($this->translate('Reschedule Service Check')); + $this->handleCommandForm($form); + } + + /** + * Schedule a service downtime + */ + public function scheduleDowntimeAction() + { + $this->assertPermission('monitoring/command/downtime/schedule'); + + $form = new ScheduleServiceDowntimeCommandForm(); + $form->setTitle($this->translate('Schedule Service Downtime')); + $this->handleCommandForm($form); + } + + /** + * Submit a passive service check result + */ + public function processCheckResultAction() + { + $this->assertPermission('monitoring/command/process-check-result'); + + $form = new ProcessCheckResultCommandForm(); + $form->setTitle($this->translate('Submit Passive Service Check Result')); + $this->handleCommandForm($form); + } + + /** + * Send a custom notification for a service + */ + public function sendCustomNotificationAction() + { + $this->assertPermission('monitoring/command/send-custom-notification'); + + $form = new SendCustomNotificationCommandForm(); + $form->setTitle($this->translate('Send Custom Service Notification')); + $this->handleCommandForm($form); + } +} diff --git a/modules/monitoring/application/controllers/ServicesController.php b/modules/monitoring/application/controllers/ServicesController.php new file mode 100644 index 0000000..6c65592 --- /dev/null +++ b/modules/monitoring/application/controllers/ServicesController.php @@ -0,0 +1,262 @@ +backend); + $this->applyRestriction('monitoring/filter/objects', $serviceList); + $serviceList->addFilter(Filter::fromQueryString( + (string) $this->params->without(array('service_problem', 'service_handled', 'showCompact')) + )); + $this->serviceList = $serviceList; + $this->serviceList->setColumns(array( + 'host_display_name', + 'host_handled', + 'host_name', + 'host_problem', + 'host_state', + 'instance_name', + 'service_acknowledged', + 'service_active_checks_enabled', + 'service_description', + 'service_display_name', + 'service_event_handler_enabled', + 'service_flap_detection_enabled', + 'service_handled', + 'service_in_downtime', + 'service_is_flapping', + 'service_last_state_change', + 'service_notifications_enabled', + 'service_obsessing', + 'service_passive_checks_enabled', + 'service_problem', + 'service_state' + )); + $this->view->baseFilter = $this->serviceList->getFilter(); + $this->view->listAllLink = Url::fromRequest()->setPath('monitoring/list/services'); + $this->getTabs()->add( + 'show', + array( + 'label' => $this->translate('Services') . sprintf(' (%d)', count($this->serviceList)), + 'title' => sprintf( + $this->translate('Show summarized information for %u services'), + count($this->serviceList) + ), + 'url' => Url::fromRequest() + ) + )->extend(new DashboardAction())->extend(new MenuAction())->activate('show'); + $this->view->title = $this->translate('Services'); + } + + protected function handleCommandForm(ObjectsCommandForm $form) + { + $form + ->setBackend($this->backend) + ->setObjects($this->serviceList) + ->setRedirectUrl(Url::fromPath('monitoring/services/show')->setParams( + $this->params->without('service_active_checks_enabled') + )) + ->handleRequest(); + + $this->view->form = $form; + $this->view->objects = $this->serviceList; + $this->view->stats = $this->serviceList->getServiceStateSummary(); + $this->view->serviceStates = true; + $this->_helper->viewRenderer('partials/command/objects-command-form', null, true); + return $form; + } + + public function showAction() + { + $this->setAutorefreshInterval(15); + $activeChecksEnabled = $this->serviceList->getFeatureStatus()['active_checks_enabled'] !== 0; + if ($this->Auth()->hasPermission('monitoring/command/schedule-check') + || ($this->Auth()->hasPermission('monitoring/command/schedule-check/active-only') + && $activeChecksEnabled + ) + ) { + $checkNowForm = new CheckNowCommandForm(); + $checkNowForm + ->setObjects($this->serviceList) + ->handleRequest(); + $this->view->checkNowForm = $checkNowForm; + } + + $acknowledgedObjects = $this->serviceList->getAcknowledgedObjects(); + if (! empty($acknowledgedObjects)) { + $removeAckForm = new RemoveAcknowledgementCommandForm(); + $removeAckForm + ->setObjects($acknowledgedObjects) + ->handleRequest(); + $this->view->removeAckForm = $removeAckForm; + } + + $featureStatus = $this->serviceList->getFeatureStatus(); + $toggleFeaturesForm = new ToggleObjectFeaturesCommandForm(array( + 'backend' => $this->backend, + 'objects' => $this->serviceList + )); + $toggleFeaturesForm + ->load((object) $featureStatus) + ->handleRequest(); + $this->view->toggleFeaturesForm = $toggleFeaturesForm; + + if ($activeChecksEnabled) { + $this->view->rescheduleAllLink = Url::fromRequest() + ->setPath('monitoring/services/reschedule-check') + ->addParams(['service_active_checks_enabled' => true]); + } + + $this->view->downtimeAllLink = Url::fromRequest()->setPath('monitoring/services/schedule-downtime'); + $this->view->processCheckResultAllLink = Url::fromRequest()->setPath( + 'monitoring/services/process-check-result' + ); + $this->view->addCommentLink = Url::fromRequest()->setPath('monitoring/services/add-comment'); + $this->view->deleteCommentLink = Url::fromRequest()->setPath('monitoring/services/delete-comment'); + $this->view->stats = $this->serviceList->getServiceStateSummary(); + $this->view->objects = $this->serviceList; + $this->view->unhandledObjects = $this->serviceList->getUnhandledObjects(); + $this->view->problemObjects = $this->serviceList->getProblemObjects(); + $this->view->downtimeUnhandledLink = Url::fromPath('monitoring/services/schedule-downtime') + ->setQueryString($this->serviceList->getUnhandledObjects()->objectsFilter()->toQueryString()); + $this->view->downtimeLink = Url::fromPath('monitoring/services/schedule-downtime') + ->setQueryString($this->serviceList->getProblemObjects()->objectsFilter()->toQueryString()); + $this->view->acknowledgedObjects = $acknowledgedObjects; + $this->view->acknowledgeLink = Url::fromPath('monitoring/services/acknowledge-problem') + ->setQueryString($this->serviceList->getUnacknowledgedObjects()->objectsFilter()->toQueryString()); + $this->view->unacknowledgedObjects = $this->serviceList->getUnacknowledgedObjects(); + $this->view->objectsInDowntime = $this->serviceList->getObjectsInDowntime(); + $this->view->inDowntimeLink = Url::fromPath('monitoring/list/services') + ->setQueryString($this->serviceList->getObjectsInDowntime() + ->objectsFilter(array('host' => 'host_name', 'service' => 'service_description'))->toQueryString()); + $this->view->showDowntimesLink = Url::fromPath('monitoring/downtimes/show') + ->setQueryString( + $this->serviceList->getObjectsInDowntime() + ->objectsFilter()->andFilter(Filter::where('object_type', 'service'))->toQueryString() + ); + $this->view->commentsLink = Url::fromRequest() + ->setPath('monitoring/list/comments'); + $this->view->sendCustomNotificationLink = Url::fromRequest()->setPath( + 'monitoring/services/send-custom-notification' + ); + + $this->view->extensionsHtml = array(); + foreach (Hook::all('Monitoring\DetailviewExtension') as $hook) { + /** @var DetailviewExtensionHook $hook */ + try { + $html = $hook->setView($this->view)->getHtmlForObjects($this->serviceList); + } catch (Exception $e) { + $html = $this->view->escape($e->getMessage()); + } + + if ($html) { + $module = $this->view->escape($hook->getModule()->getName()); + $this->view->extensionsHtml[] = + '
' + . $html + . '
'; + } + } + } + + /** + * Add a service comment + */ + public function addCommentAction() + { + $this->assertPermission('monitoring/command/comment/add'); + + $form = new AddCommentCommandForm(); + $form->setTitle($this->translate('Add Service Comments')); + $this->handleCommandForm($form); + } + + /** + * Acknowledge service problems + */ + public function acknowledgeProblemAction() + { + $this->assertPermission('monitoring/command/acknowledge-problem'); + + $form = new AcknowledgeProblemCommandForm(); + $form->setTitle($this->translate('Acknowledge Service Problems')); + $this->handleCommandForm($form); + } + + /** + * Reschedule service checks + */ + public function rescheduleCheckAction() + { + $this->assertPermission('monitoring/command/schedule-check'); + + $form = new ScheduleServiceCheckCommandForm(); + $form->setTitle($this->translate('Reschedule Service Checks')); + $this->handleCommandForm($form); + } + + /** + * Schedule service downtimes + */ + public function scheduleDowntimeAction() + { + $this->assertPermission('monitoring/command/downtime/schedule'); + + $form = new ScheduleServiceDowntimeCommandForm(); + $form->setTitle($this->translate('Schedule Service Downtimes')); + $this->handleCommandForm($form); + } + + /** + * Submit passive service check results + */ + public function processCheckResultAction() + { + $this->assertPermission('monitoring/command/process-check-result'); + + $form = new ProcessCheckResultCommandForm(); + $form->setTitle($this->translate('Submit Passive Service Check Results')); + $this->handleCommandForm($form); + } + + /** + * Send a custom notification for services + */ + public function sendCustomNotificationAction() + { + $this->assertPermission('monitoring/command/send-custom-notification'); + + $form = new SendCustomNotificationCommandForm(); + $form->setTitle($this->translate('Send Custom Service Notification')); + $this->handleCommandForm($form); + } +} diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php new file mode 100644 index 0000000..f1da561 --- /dev/null +++ b/modules/monitoring/application/controllers/ShowController.php @@ -0,0 +1,101 @@ +view->defaultTitle = $this->translate('Contacts') . ' :: ' . $this->view->defaultTitle; + + parent::init(); + } + + public function contactAction() + { + if (! $this->hasPermission('*') && $this->hasPermission('no-monitoring/contacts')) { + throw new SecurityException('No permission for %s', 'monitoring/contacts'); + } + + $contactName = $this->params->getRequired('contact_name'); + + $this->getTabs()->add('contact-detail', [ + 'title' => $this->translate('Contact details'), + 'label' => $this->translate('Contact'), + 'url' => Url::fromRequest(), + 'active' => true + ]); + + $query = $this->backend->select()->from('contact', array( + 'contact_name', + 'contact_id', + 'contact_alias', + 'contact_email', + 'contact_pager', + 'contact_notify_service_timeperiod', + 'contact_notify_service_recovery', + 'contact_notify_service_warning', + 'contact_notify_service_critical', + 'contact_notify_service_unknown', + 'contact_notify_service_flapping', + 'contact_notify_service_downtime', + 'contact_notify_host_timeperiod', + 'contact_notify_host_recovery', + 'contact_notify_host_down', + 'contact_notify_host_unreachable', + 'contact_notify_host_flapping', + 'contact_notify_host_downtime', + )); + $this->applyRestriction('monitoring/filter/objects', $query); + $query->whereEx(new FilterEqual('contact_name', '=', $contactName)); + $contact = $query->getQuery()->fetchRow(); + + if ($contact) { + $commands = $this->backend->select()->from('command', array( + 'command_line', + 'command_name' + ))->where('contact_id', $contact->contact_id); + + $this->view->commands = $commands; + + $notifications = $this->backend->select()->from('notification', array( + 'id', + 'host_name', + 'service_description', + 'notification_output', + 'notification_contact_name', + 'notification_timestamp', + 'notification_state', + 'host_display_name', + 'service_display_name' + )); + + $notifications->where('notification_contact_name', $contactName); + $this->applyRestriction('monitoring/filter/objects', $notifications); + $this->view->notifications = $notifications; + $this->setupLimitControl(); + $this->setupPaginationControl($this->view->notifications); + $this->view->title = $contact->contact_name; + } + + $this->view->contact = $contact; + $this->view->contactName = $contactName; + } +} diff --git a/modules/monitoring/application/controllers/TacticalController.php b/modules/monitoring/application/controllers/TacticalController.php new file mode 100644 index 0000000..b147d45 --- /dev/null +++ b/modules/monitoring/application/controllers/TacticalController.php @@ -0,0 +1,128 @@ +setAutorefreshInterval(15); + + $this->view->title = $this->translate('Tactical Overview'); + $this->getTabs()->add( + 'tactical_overview', + array( + 'title' => $this->translate( + 'Show an overview of all hosts and services, their current' + . ' states and monitoring feature utilisation' + ), + 'label' => $this->translate('Tactical Overview'), + 'url' => Url::fromRequest() + ) + )->extend(new DashboardAction())->extend(new MenuAction())->activate('tactical_overview'); + + $stats = $this->backend->select()->from( + 'statussummary', + array( + 'hosts_up', + 'hosts_down_handled', + 'hosts_down_unhandled', + 'hosts_unreachable_handled', + 'hosts_unreachable_unhandled', + 'hosts_pending', + 'hosts_pending_not_checked', + 'hosts_not_checked', + + 'services_ok', + 'services_warning_handled', + 'services_warning_unhandled', + 'services_critical_handled', + 'services_critical_unhandled', + 'services_unknown_handled', + 'services_unknown_unhandled', + 'services_pending', + 'services_pending_not_checked', + 'services_not_checked', + ) + ); + $this->applyRestriction('monitoring/filter/objects', $stats); + + $this->setupFilterControl($stats, null, ['host', 'service'], ['format']); + $this->view->setHelperFunction('filteredUrl', function ($path, array $params) { + $filter = clone $this->view->filterEditor->getFilter(); + + return $this->view->url($path)->setParams($params)->addFilter($filter); + }); + + $this->handleFormatRequest($stats); + $summary = $stats->fetchRow(); + + // Correct pending counts. Done here instead of in the query for compatibility reasons. + $summary->hosts_pending -= $summary->hosts_pending_not_checked; + $summary->services_pending -= $summary->services_pending_not_checked; + + $hostSummaryChart = new Donut(); + $hostSummaryChart + ->addSlice($summary->hosts_up, array('class' => 'slice-state-ok')) + ->addSlice($summary->hosts_down_handled, array('class' => 'slice-state-critical-handled')) + ->addSlice($summary->hosts_down_unhandled, array('class' => 'slice-state-critical')) + ->addSlice($summary->hosts_unreachable_handled, array('class' => 'slice-state-unreachable-handled')) + ->addSlice($summary->hosts_unreachable_unhandled, array('class' => 'slice-state-unreachable')) + ->addSlice($summary->hosts_pending, array('class' => 'slice-state-pending')) + ->addSlice($summary->hosts_pending_not_checked, array('class' => 'slice-state-not-checked')) + ->setLabelBig($summary->hosts_down_unhandled) + ->setLabelBigEyeCatching($summary->hosts_down_unhandled > 0) + ->setLabelSmall($this->translate('Hosts Down')); + + $serviceSummaryChart = new Donut(); + $serviceSummaryChart + ->addSlice($summary->services_ok, array('class' => 'slice-state-ok')) + ->addSlice($summary->services_warning_handled, array('class' => 'slice-state-warning-handled')) + ->addSlice($summary->services_warning_unhandled, array('class' => 'slice-state-warning')) + ->addSlice($summary->services_critical_handled, array('class' => 'slice-state-critical-handled')) + ->addSlice($summary->services_critical_unhandled, array('class' => 'slice-state-critical')) + ->addSlice($summary->services_unknown_handled, array('class' => 'slice-state-unknown-handled')) + ->addSlice($summary->services_unknown_unhandled, array('class' => 'slice-state-unknown')) + ->addSlice($summary->services_pending, array('class' => 'slice-state-pending')) + ->addSlice($summary->services_pending_not_checked, array('class' => 'slice-state-not-checked')) + ->setLabelBig($summary->services_critical_unhandled ?: $summary->services_unknown_unhandled) + ->setLabelBigState($summary->services_critical_unhandled > 0 ? 'critical' : ( + $summary->services_unknown_unhandled > 0 ? 'unknown' : null + )) + ->setLabelSmall($summary->services_critical_unhandled > 0 || $summary->services_unknown_unhandled < 1 + ? $this->translate('Services Critical') + : $this->translate('Services Unknown')); + + $this->view->hostStatusSummaryChart = $hostSummaryChart + ->setLabelBigUrl($this->view->filteredUrl( + 'monitoring/list/hosts', + array( + 'host_state' => 1, + 'host_handled' => 0, + 'sort' => 'host_last_check', + 'dir' => 'asc' + ) + )) + ->render(); + $this->view->serviceStatusSummaryChart = $serviceSummaryChart + ->setLabelBigUrl($this->view->filteredUrl( + 'monitoring/list/services', + array( + 'service_state' => $summary->services_critical_unhandled > 0 + || ! $summary->services_unknown_unhandled ? 2 : 3, + 'service_handled' => 0, + 'sort' => 'service_last_check', + 'dir' => 'asc' + ) + )) + ->render(); + $this->view->statusSummary = $summary; + } +} diff --git a/modules/monitoring/application/controllers/TimelineController.php b/modules/monitoring/application/controllers/TimelineController.php new file mode 100644 index 0000000..deeeb36 --- /dev/null +++ b/modules/monitoring/application/controllers/TimelineController.php @@ -0,0 +1,325 @@ +getTabs()->add( + 'timeline', + array( + 'title' => $this->translate('Show the number of historical event records grouped by time and type'), + 'label' => $this->translate('Timeline'), + 'url' => Url::fromRequest() + ) + )->extend(new DashboardAction())->extend(new MenuAction())->activate('timeline'); + $this->view->title = $this->translate('Timeline'); + + // TODO: filter for hard_states (precedence adjustments necessary!) + $this->setupIntervalBox(); + list($displayRange, $forecastRange) = $this->buildTimeRanges(); + + $detailUrl = Url::fromPath('monitoring/list/eventhistory'); + + $timeline = new TimeLine( + $this->applyRestriction( + 'monitoring/filter/objects', + $this->backend->select()->from( + 'eventhistory', + array( + 'name' => 'type', + 'time' => 'timestamp' + ) + ) + ), + array( + 'notification_ack' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_flapping' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_flapping_end' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_dt_start' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_dt_end' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_custom' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_state' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'hard_state' => array( + 'class' => 'timeline-hard-state', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Hard state changes') + ), + 'comment' => array( + 'class' => 'timeline-comment', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Comments') + ), + 'ack' => array( + 'class' => 'timeline-ack', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Acknowledgements') + ), + 'dt_start' => array( + 'class' => 'timeline-downtime-start', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Started downtimes') + ), + 'dt_end' => array( + 'class' => 'timeline-downtime-end', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Ended downtimes') + ) + ) + ); + $timeline->setMaximumCircleWidth('6em'); + $timeline->setMinimumCircleWidth('0.3em'); + $timeline->setDisplayRange($displayRange); + $timeline->setForecastRange($forecastRange); + $beingExtended = $this->getRequest()->getParam('extend') == 1; + $timeline->setSession($this->Window()->getSessionNamespace('timeline', !$beingExtended)); + + $this->view->timeline = $timeline; + $this->view->nextRange = $forecastRange; + $this->view->beingExtended = $beingExtended; + $this->view->intervalFormat = $this->getIntervalFormat(); + $oldBase = $timeline->getCalculationBase(false); + $this->view->switchedContext = $oldBase !== null && $oldBase !== $timeline->getCalculationBase(true); + } + + /** + * Create a select box the user can choose the timeline interval from + */ + private function setupIntervalBox() + { + $box = new SelectBox( + 'intervalBox', + array( + '4h' => mt('monitoring', '4 Hours'), + '1d' => mt('monitoring', 'One day'), + '1w' => mt('monitoring', 'One week'), + '1m' => mt('monitoring', 'One month'), + '1y' => mt('monitoring', 'One year') + ), + mt('monitoring', 'TimeLine interval'), + 'interval' + ); + $box->applyRequest($this->getRequest()); + $this->view->intervalBox = $box; + } + + /** + * Return the chosen interval + * + * @return DateInterval The chosen interval + */ + private function getTimelineInterval() + { + switch ($this->view->intervalBox->getInterval()) { + case '1d': + return new DateInterval('P1D'); + case '1w': + return new DateInterval('P1W'); + case '1m': + return new DateInterval('P1M'); + case '1y': + return new DateInterval('P1Y'); + default: + return new DateInterval('PT4H'); + } + } + + /** + * Get an appropriate datetime format string for the chosen interval + * + * @return string + */ + private function getIntervalFormat() + { + switch ($this->view->intervalBox->getInterval()) { + case '1d': + return $this->getDateFormat(); + case '1w': + return '\W\e\ek W\\of Y'; + case '1m': + return 'F Y'; + case '1y': + return 'Y'; + default: + return $this->getDateFormat() . '\' . $this->getTimeFormat(); + } + } + + /** + * Return a preload interval based on the chosen timeline interval and the given date and time + * + * @param DateTime $dateTime The date and time to use + * + * @return DateInterval The interval to pre-load + */ + private function getPreloadInterval(DateTime $dateTime) + { + switch ($this->view->intervalBox->getInterval()) { + case '1d': + return DateInterval::createFromDateString('1 week -1 second'); + case '1w': + return DateInterval::createFromDateString('8 weeks -1 second'); + case '1m': + $dateCopy = clone $dateTime; + for ($i = 0; $i < 6; $i++) { + $dateCopy->sub(new DateInterval('PT' . Format::secondsByMonth($dateCopy) . 'S')); + } + return $dateCopy->add(new DateInterval('PT1S'))->diff($dateTime); + case '1y': + $dateCopy = clone $dateTime; + for ($i = 0; $i < 4; $i++) { + $dateCopy->sub(new DateInterval('PT' . Format::secondsByYear($dateCopy) . 'S')); + } + return $dateCopy->add(new DateInterval('PT1S'))->diff($dateTime); + default: + return DateInterval::createFromDateString('1 day -1 second'); + } + } + + /** + * Extrapolate the given datetime based on the chosen timeline interval + * + * @param DateTime $dateTime The datetime to extrapolate + */ + private function extrapolateDateTime(DateTime &$dateTime) + { + switch ($this->view->intervalBox->getInterval()) { + case '1d': + $dateTime->setTimestamp(strtotime('tomorrow', $dateTime->getTimestamp()) - 1); + break; + case '1w': + $dateTime->setTimestamp(strtotime('next monday', $dateTime->getTimestamp()) - 1); + break; + case '1m': + $dateTime->setTimestamp( + strtotime( + 'last day of this month', + strtotime( + 'tomorrow', + $dateTime->getTimestamp() + ) - 1 + ) + ); + break; + case '1y': + $dateTime->setTimestamp(strtotime('1 january next year', $dateTime->getTimestamp()) - 1); + break; + default: + $hour = $dateTime->format('G'); + $end = $hour < 4 ? 4 : ($hour < 8 ? 8 : ($hour < 12 ? 12 : ($hour < 16 ? 16 : ($hour < 20 ? 20 : 24)))); + $dateTime = DateTime::createFromFormat( + 'd/m/y G:i:s', + $dateTime->format('d/m/y') . ($end - 1) . ':59:59' + ); + } + } + + /** + * Return a display- and forecast time range + * + * Assembles a time range each for display and forecast purposes based on the start- and + * end time if given in the current request otherwise based on the current time and a + * end time that is calculated based on the chosen timeline interval. + * + * @return array The resulting time ranges + */ + private function buildTimeRanges() + { + $startTime = new DateTime(); + $startParam = $this->_request->getParam('start'); + $startTimestamp = is_numeric($startParam) ? intval($startParam) : strtotime($startParam ?? ''); + if ($startTimestamp !== false) { + $startTime->setTimestamp($startTimestamp); + } else { + $this->extrapolateDateTime($startTime); + } + + $endTime = clone $startTime; + $endParam = $this->_request->getParam('end'); + $endTimestamp = is_numeric($endParam) ? intval($endParam) : strtotime($endParam ?? ''); + if ($endTimestamp !== false) { + $endTime->setTimestamp($endTimestamp); + } else { + $endTime->sub($this->getPreloadInterval($startTime)); + } + + $forecastStart = clone $endTime; + $forecastStart->sub(new DateInterval('PT1S')); + $forecastEnd = clone $forecastStart; + $forecastEnd->sub($this->getPreloadInterval($forecastStart)); + + $timelineInterval = $this->getTimelineInterval(); + return array( + new TimeRange($startTime, $endTime, $timelineInterval), + new TimeRange($forecastStart, $forecastEnd, $timelineInterval) + ); + } + + /** + * Get the user's preferred time format or the application's default + * + * @return string + */ + private function getTimeFormat() + { + return 'H:i'; + } + + /** + * Get the user's preferred date format or the application's default + * + * @return string + */ + private function getDateFormat() + { + return 'Y-m-d'; + } +} -- cgit v1.2.3