summaryrefslogtreecommitdiffstats
path: root/modules/monitoring/application/controllers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:39:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:39:39 +0000
commit8ca6cc32b2c789a3149861159ad258f2cb9491e3 (patch)
tree2492de6f1528dd44eaa169a5c1555026d9cb75ec /modules/monitoring/application/controllers
parentInitial commit. (diff)
downloadicingaweb2-upstream/2.11.4.tar.xz
icingaweb2-upstream/2.11.4.zip
Adding upstream version 2.11.4.upstream/2.11.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/monitoring/application/controllers')
-rw-r--r--modules/monitoring/application/controllers/ActionsController.php135
-rw-r--r--modules/monitoring/application/controllers/CommentController.php91
-rw-r--r--modules/monitoring/application/controllers/CommentsController.php108
-rw-r--r--modules/monitoring/application/controllers/ConfigController.php298
-rw-r--r--modules/monitoring/application/controllers/DowntimeController.php108
-rw-r--r--modules/monitoring/application/controllers/DowntimesController.php108
-rw-r--r--modules/monitoring/application/controllers/EventController.php551
-rw-r--r--modules/monitoring/application/controllers/HealthController.php196
-rw-r--r--modules/monitoring/application/controllers/HostController.php185
-rw-r--r--modules/monitoring/application/controllers/HostsController.php260
-rw-r--r--modules/monitoring/application/controllers/ListController.php808
-rw-r--r--modules/monitoring/application/controllers/ServiceController.php147
-rw-r--r--modules/monitoring/application/controllers/ServicesController.php262
-rw-r--r--modules/monitoring/application/controllers/ShowController.php101
-rw-r--r--modules/monitoring/application/controllers/TacticalController.php128
-rw-r--r--modules/monitoring/application/controllers/TimelineController.php325
16 files changed, 3811 insertions, 0 deletions
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 @@
+<?php
+/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Data\Filter\Filter;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimesCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm;
+use Icinga\Module\Monitoring\Object\HostList;
+use Icinga\Module\Monitoring\Object\ServiceList;
+
+/**
+ * Monitoring API
+ */
+class ActionsController extends Controller
+{
+ /**
+ * Get the filter from URL parameters or exit immediately if the filter is empty
+ *
+ * @return Filter
+ */
+ protected function getFilterOrExitIfEmpty()
+ {
+ $filter = Filter::fromQueryString((string) $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Application\Hook;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
+use Icinga\Web\Url;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+
+/**
+ * Display detailed information about a comment
+ */
+class CommentController extends Controller
+{
+ /**
+ * The fetched comment
+ *
+ * @var object
+ */
+ protected $comment;
+
+ /**
+ * Fetch the first comment with the given id and add tabs
+ */
+ public function init()
+ {
+ $commentId = $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Data\Filter\Filter;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentsCommandForm;
+use Icinga\Web\Url;
+
+/**
+ * Display detailed information about comments
+ */
+class CommentsController extends Controller
+{
+ /**
+ * The comments view
+ *
+ * @var \Icinga\Module\Monitoring\DataView\Comment
+ */
+ protected $comments;
+
+ /**
+ * Filter from request
+ *
+ * @var Filter
+ */
+ protected $filter;
+
+ /**
+ * Fetch all comments matching the current filter and add tabs
+ */
+ public function init()
+ {
+ $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Exception;
+use Icinga\Data\ResourceFactory;
+use Icinga\Exception\ConfigurationError;
+use Icinga\Exception\NotFoundError;
+use Icinga\Forms\ConfirmRemovalForm;
+use Icinga\Module\Monitoring\Backend\MonitoringBackend;
+use Icinga\Module\Monitoring\Forms\Config\TransportReorderForm;
+use Icinga\Web\Controller;
+use Icinga\Web\Notification;
+use Icinga\Module\Monitoring\Forms\Config\BackendConfigForm;
+use Icinga\Module\Monitoring\Forms\Config\SecurityConfigForm;
+use Icinga\Module\Monitoring\Forms\Config\TransportConfigForm;
+
+/**
+ * Configuration controller for editing monitoring resources
+ */
+class ConfigController extends Controller
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function init()
+ {
+ $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Application\Hook;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
+use Icinga\Module\Monitoring\Object\Host;
+use Icinga\Module\Monitoring\Object\Service;
+use Icinga\Web\Url;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+
+/**
+ * Display detailed information about a downtime
+ */
+class DowntimeController extends Controller
+{
+ /**
+ * The fetched downtime
+ *
+ * @var object
+ */
+ protected $downtime;
+
+ /**
+ * Fetch the downtime matching the given id and add tabs
+ */
+ public function init()
+ {
+ $downtimeId = $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Data\Filter\Filter;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimesCommandForm;
+use Icinga\Web\Url;
+
+/**
+ * Display detailed information about downtimes
+ */
+class DowntimesController extends Controller
+{
+ /**
+ * The downtimes view
+ *
+ * @var \Icinga\Module\Monitoring\DataView\Downtime
+ */
+ protected $downtimes;
+
+ /**
+ * Filter from request
+ *
+ * @var Filter
+ */
+ protected $filter;
+
+ /**
+ * Fetch all downtimes matching the current filter and add tabs
+ */
+ public function init()
+ {
+ $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use DateTime;
+use DateTimeZone;
+use Icinga\Module\Monitoring\Hook\EventDetailsExtensionHook;
+use Icinga\Application\Hook;
+use InvalidArgumentException;
+use Icinga\Data\Queryable;
+use Icinga\Date\DateFormatter;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Object\Host;
+use Icinga\Module\Monitoring\Object\Service;
+use Icinga\Util\TimezoneDetect;
+use Icinga\Web\Url;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+use Icinga\Web\Widget\Tabextension\OutputFormat;
+
+class EventController extends Controller
+{
+ /**
+ * @var string[]
+ */
+ protected $dataViewsByType = array(
+ 'notify' => '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[] =
+ '<div class="icinga-module module-' . $module . '" data-icinga-module="' . $module . '">'
+ . $html
+ . '</div>';
+ }
+ }
+
+ $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 '<span class="badge state-' . ($isService ? Service::getStateText($state) : Host::getStateText($state))
+ . '">&nbsp;</span><span class="state-label">' . $this->view->escape($stateText) . '</span>';
+ }
+
+ /**
+ * 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 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Forms\Command\Instance\DisableNotificationsExpireCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Instance\ToggleInstanceFeaturesCommandForm;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+
+/**
+ * Display process and performance information of the monitoring host and program-wide commands
+ */
+class HealthController extends Controller
+{
+ /**
+ * Add tabs
+ *
+ * @see \Icinga\Web\Controller\ActionController::init()
+ */
+ public function init()
+ {
+ $this
+ ->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 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostCheckCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
+use Icinga\Module\Monitoring\Object\Host;
+use Icinga\Module\Monitoring\Web\Controller\MonitoredObjectController;
+use Icinga\Web\Hook;
+use Icinga\Web\Navigation\Navigation;
+
+class HostController extends MonitoredObjectController
+{
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $commandRedirectUrl = 'monitoring/host/show';
+
+ /**
+ * Fetch the requested host from the monitoring backend
+ */
+ public function init()
+ {
+ $host = new Host($this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Exception;
+use Icinga\Data\Filter\Filter;
+use Icinga\Data\Filter\FilterEqual;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostCheckCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ToggleObjectFeaturesCommandForm;
+use Icinga\Module\Monitoring\Hook\DetailviewExtensionHook;
+use Icinga\Module\Monitoring\Object\HostList;
+use Icinga\Web\Hook;
+use Icinga\Web\Url;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+
+class HostsController extends Controller
+{
+ /**
+ * @var HostList
+ */
+ protected $hostList;
+
+ public function init()
+ {
+ $hostList = new HostList($this->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[] =
+ '<div class="icinga-module module-' . $module . '" data-icinga-module="' . $module . '">'
+ . $html
+ . '</div>';
+ }
+ }
+ }
+
+ /**
+ * 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 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Module\Monitoring\Backend\MonitoringBackend;
+use Icinga\Security\SecurityException;
+use Icinga\Util\GlobFilter;
+use Icinga\Web\Form;
+use Zend_Form;
+use Icinga\Data\Filter\Filter;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\DataView\DataView;
+use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
+use Icinga\Module\Monitoring\Forms\StatehistoryForm;
+use Icinga\Web\Url;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+use Icinga\Web\Widget\Tabextension\OutputFormat;
+use Icinga\Web\Widget\Tabs;
+
+class ListController extends Controller
+{
+ /**
+ * @see ActionController::init
+ */
+ public function init()
+ {
+ parent::init();
+ $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceCheckCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
+use Icinga\Module\Monitoring\Object\Service;
+use Icinga\Module\Monitoring\Web\Controller\MonitoredObjectController;
+use Icinga\Web\Hook;
+use Icinga\Web\Navigation\Navigation;
+
+class ServiceController extends MonitoredObjectController
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected $commandRedirectUrl = 'monitoring/service/show';
+
+ /**
+ * Fetch the requested service from the monitoring backend
+ */
+ public function init()
+ {
+ $service = new Service(
+ $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Exception;
+use Icinga\Data\Filter\Filter;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceCheckCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
+use Icinga\Module\Monitoring\Forms\Command\Object\ToggleObjectFeaturesCommandForm;
+use Icinga\Module\Monitoring\Hook\DetailviewExtensionHook;
+use Icinga\Module\Monitoring\Object\ServiceList;
+use Icinga\Web\Hook;
+use Icinga\Web\Url;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+
+class ServicesController extends Controller
+{
+ /**
+ * @var ServiceList
+ */
+ protected $serviceList;
+
+ public function init()
+ {
+ $serviceList = new ServiceList($this->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[] =
+ '<div class="icinga-module module-' . $module . '" data-icinga-module="' . $module . '">'
+ . $html
+ . '</div>';
+ }
+ }
+ }
+
+ /**
+ * 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 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Data\Filter\FilterEqual;
+use Icinga\Module\Monitoring\Backend\MonitoringBackend;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Security\SecurityException;
+use Icinga\Web\Url;
+
+/**
+ * Class Monitoring_ShowController
+ *
+ * Actions for show context
+ */
+class ShowController extends Controller
+{
+ /**
+ * @var MonitoringBackend
+ */
+ protected $backend;
+
+ public function init()
+ {
+ $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use Icinga\Chart\Donut;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Web\Url;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+
+class TacticalController extends Controller
+{
+ public function indexAction()
+ {
+ $this->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 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Monitoring\Controllers;
+
+use DateInterval;
+use DateTime;
+use Icinga\Module\Monitoring\Controller;
+use Icinga\Module\Monitoring\Timeline\TimeLine;
+use Icinga\Module\Monitoring\Timeline\TimeRange;
+use Icinga\Module\Monitoring\Web\Widget\SelectBox;
+use Icinga\Util\Format;
+use Icinga\Web\Url;
+use Icinga\Web\Widget\Tabextension\DashboardAction;
+use Icinga\Web\Widget\Tabextension\MenuAction;
+
+class TimelineController extends Controller
+{
+ public function indexAction()
+ {
+ $this->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\<b\r\>\of Y';
+ case '1m':
+ return 'F Y';
+ case '1y':
+ return 'Y';
+ default:
+ return $this->getDateFormat() . '\<b\r\>' . $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';
+ }
+}