diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:39:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:39:39 +0000 |
commit | 8ca6cc32b2c789a3149861159ad258f2cb9491e3 (patch) | |
tree | 2492de6f1528dd44eaa169a5c1555026d9cb75ec /modules/monitoring/application | |
parent | Initial commit. (diff) | |
download | icingaweb2-upstream.tar.xz icingaweb2-upstream.zip |
Adding upstream version 2.11.4.upstream/2.11.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
153 files changed, 16128 insertions, 0 deletions
diff --git a/modules/monitoring/application/clicommands/ListCommand.php b/modules/monitoring/application/clicommands/ListCommand.php new file mode 100644 index 0000000..6dc4193 --- /dev/null +++ b/modules/monitoring/application/clicommands/ListCommand.php @@ -0,0 +1,400 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Clicommands; + +use Icinga\Module\Monitoring\Backend\MonitoringBackend; +use Icinga\Module\Monitoring\Cli\CliUtils; +use Icinga\Date\DateFormatter; +use Icinga\Cli\Command; +use Icinga\File\Csv; +use Icinga\Module\Monitoring\Plugin\PerfdataSet; +use Exception; +use Icinga\Util\Json; + +/** + * Icinga monitoring objects + * + * This module is your interface to the Icinga monitoring application. + */ +class ListCommand extends Command +{ + protected $backend; + protected $dumpSql; + protected $defaultActionName = 'status'; + + public function init() + { + $this->backend = MonitoringBackend::instance($this->params->shift('backend')); + $this->dumpSql = $this->params->shift('showsql'); + } + + protected function getQuery($table, $columns) + { + $limit = $this->params->shift('limit'); + $format = $this->params->shift('format'); + if ($format !== null) { + if ($this->params->has('columns')) { + $columnParams = preg_split( + '/,/', + $this->params->shift('columns') + ); + $columns = array(); + foreach ($columnParams as $col) { + if (false !== ($pos = strpos($col, '='))) { + $columns[substr($col, 0, $pos)] = substr($col, $pos + 1); + } else { + $columns[] = $col; + } + } + } + } + + $query = $this->backend->select()->from($table, $columns); + if ($limit) { + $query->limit($limit, $this->params->shift('offset')); + } + foreach ($this->params->getParams() as $col => $filter) { + $query->where($col, $filter); + } + // $query->applyFilters($this->params->getParams()); + if ($this->dumpSql) { + echo wordwrap($query->dump(), 72); + exit; + } + + if ($format !== null) { + $this->showFormatted($query, $format, $columns); + } + + return $query; + } + + protected function showFormatted($query, $format, $columns) + { + $query = $query->getQuery(); + switch ($format) { + case 'json': + echo Json::sanitize($query->fetchAll()); + break; + case 'csv': + Csv::fromQuery($query)->dump(); + break; + default: + preg_match_all('~\$([a-z0-9_-]+)\$~', $format, $m); + $words = array(); + foreach ($columns as $key => $col) { + if (is_numeric($key)) { + if (in_array($col, $m[1])) { + $words[] = $col; + } + } else { + if (in_array($key, $m[1])) { + $words[] = $key; + } + } + } + foreach ($query->fetchAll() as $row) { + $output = $format; + foreach ($words as $word) { + $output = preg_replace( + '~\$' . $word . '\$~', + $row->{$word}, + $output + ); + } + echo $output . "\n"; + } + } + exit; + } + + /** + * List and filter hosts + * + * This command allows you to search and visualize your hosts in + * different ways. + * + * USAGE + * + * icingacli monitoring list hosts [options] + * + * OPTIONS + * + * --verbose Show detailled output + * --showsql Dump generated SQL query (DB backend only) + * + * --format=<csv|json|<custom>> + * Dump columns in the given format. <custom> format allows $column$ + * placeholders, e.g. --format='$host$: $service$'. This requires + * that the columns are specified within the --columns parameter. + * + * --<column>[=filter] + * Filter given column by optional filter. Boolean (1/0) columns are + * true if no filter value is given. + * + * --problems + * Only show unhandled problems (HARD state and not acknowledged/in downtime). + * + * --columns='<comma separated list of host/service columns>' + * Add a limited set of columns to the output. The following host + * attributes can be fetched: state, handled, output, acknowledged, in_downtime, perfdata last_state_change + * + * EXAMPLES + * + * icingacli monitoring list hosts --problems + * icingacli monitoring list hosts --problems --host_state_type 0 + * icingacli monitoring list hosts --host=local* + * icingacli monitoring list hosts --columns 'host,host_output' \ + * --format='$host$ ($host_output$)' + */ + public function hostsAction() + { + $columns = array( + 'host_name', + 'host_state', + 'host_output', + 'host_handled', + 'host_acknowledged', + 'host_in_downtime' + ); + $query = $this->getQuery('hoststatus', $columns) + ->order('host_name'); + echo $this->renderStatusQuery($query); + } + + /** + * List and filter services + * + * This command allows you to search and visualize your services in + * different ways. + * + * USAGE + * + * icingacli monitoring list services [options] + * + * OPTIONS + * + * --verbose Show detailled output + * --showsql Dump generated SQL query (DB backend only) + * + * --format=<csv|json|<custom>> + * Dump columns in the given format. <custom> format allows $column$ + * placeholders, e.g. --format='$host$: $service$'. This requires + * that the columns are specified within the --columns parameter. + * + * --<column>[=filter] + * Filter given column by optional filter. Boolean (1/0) columns are + * true if no filter value is given. + * + * --problems + * Only show unhandled problems (HARD state and not acknowledged/in downtime). + * + * --columns='<comma separated list of host/service columns>' + * Add a limited set of columns to the output. The following service + * attributes can be fetched: state, handled, output, acknowledged, in_downtime, perfdata last_state_change + * + * EXAMPLES + * + * icingacli monitoring list services --problems + * icingacli monitoring list services --problems --service_state_type 0 + * icingacli monitoring list services --host=local* --service=*disk* + * icingacli monitoring list services --columns 'host,service,service_output' \ + * --format='$host$: $service$ ($service_output$)' + */ + public function servicesAction() + { + $columns = array( + 'host_name', + 'host_state', + 'host_output', + 'host_handled', + 'host_acknowledged', + 'host_in_downtime', + 'service_description', + 'service_state', + 'service_acknowledged', + 'service_in_downtime', + 'service_handled', + 'service_output', + 'service_perfdata', + 'service_last_state_change' + ); + $query = $this->getQuery('servicestatus', $columns) + ->order('host_name'); + echo $this->renderStatusQuery($query); + } + + protected function renderStatusQuery($query) + { + $out = ''; + $last_host = null; + $screen = $this->screen; + $utils = new CliUtils($screen); + $maxCols = $screen->getColumns(); + $query = $query->getQuery(); + $rows = $query->fetchAll(); + $count = $query->count(); + $count = count($rows); + + for ($i = 0; $i < $count; $i++) { + $row = & $rows[$i]; + + $utils->setHostState($row->host_state); + if (! array_key_exists($i + 1, $rows) + || $row->host_name !== $rows[$i + 1]->host_name + ) { + $lastService = true; + } else { + $lastService = false; + } + + $hostUnhandled = ! ($row->host_state == 0 || $row->host_handled); + + if ($row->host_name !== $last_host) { + if (isset($row->service_description)) { + $out .= "\n"; + } + + $hostTxt = $utils->shortHostState(); + if ($hostUnhandled) { + $out .= $utils->hostStateBackground( + sprintf(' %s ', $utils->shortHostState()) + ); + } else { + $out .= sprintf( + '%s %s ', + $utils->hostStateBackground(' '), + $utils->shortHostState() + ); + } + $out .= sprintf( + " %s%s: %s\n", + $screen->underline($row->host_name), + $screen->colorize($utils->objectStateFlags('host', $row), 'lightblue'), + $row->host_output + ); + + if (isset($row->services_ok)) { + $out .= sprintf( + "%d services, %d problems (%d unhandled), %d OK\n", + $row->services_cnt, + $row->services_problem, + $row->services_problem_unhandled, + $row->services_ok + ); + } + } + + $last_host = $row->host_name; + if (! isset($row->service_description)) { + continue; + } + + $utils->setServiceState($row->service_state); + $serviceUnhandled = ! ( + $row->service_state == 0 || $row->service_handled + ); + + if ($lastService) { + $straight = ' '; + $leaf = '└'; + } else { + $straight = '│'; + $leaf = '├'; + } + $out .= $utils->hostStateBackground(' '); + + if ($serviceUnhandled) { + $out .= $utils->serviceStateBackground( + sprintf(' %s ', $utils->shortServiceState()) + ); + $emptyBg = ' '; + $emptySpace = ''; + } else { + $out .= sprintf( + '%s %s ', + $utils->serviceStateBackground(' '), + $utils->shortServiceState() + ); + $emptyBg = ' '; + $emptySpace = ' '; + } + + $emptyLine = "\n" + . $utils->hostStateBackground(' ') + . $utils->serviceStateBackground($emptyBg) + . $emptySpace + . ' ' . $straight . ' '; + + $perf = ''; + try { + $pset = PerfdataSet::fromString($row->service_perfdata); + $perfs = array(); + foreach ($pset as $p) { + if ($percent = $p->getPercentage()) { + if ($percent < 0 || $percent > 100) { + continue; + } + $perfs[] = ' ' + . $p->getLabel() + . ': ' + . $this->getPercentageSign($percent) + . ' ' + . number_format($percent, 2, ',', '.') + . '%'; + } + } + if (! empty($perfs)) { + $perf = ', ' . implode($perfs); + } + // TODO: fix wordwarp, then remove this line: + $perf = ''; + } catch (Exception $e) { + // Ignoring perfdata errors right now, we could show some hint + } + + $wrappedOutput = wordwrap( + preg_replace('~\@{3,}~', '@@@', $row->service_output), + $maxCols - 13 + ) . "\n"; + $out .= sprintf( + " %1s─ %s%s (%s)", + $leaf, + $screen->underline($row->service_description), + $screen->colorize($utils->objectStateFlags('service', $row) . $perf, 'lightblue'), + ucfirst(DateFormatter::timeSince($row->service_last_state_change)) + ); + if ($this->isVerbose) { + $out .= $emptyLine . preg_replace( + '/\n/', + $emptyLine, + $wrappedOutput + ) . "\n"; + } else { + $out .= "\n"; + } + } + + $out .= "\n"; + return $out; + } + + protected function getPercentageSign($percent) + { + $circles = array( + 0 => '○', + 15 => '◔', + 40 => '◑', + 65 => '◕', + 90 => '●', + ); + $last = $circles[0]; + foreach ($circles as $cur => $circle) { + if ($percent < $cur) { + return $last; + } + $last = $circle; + } + } +} diff --git a/modules/monitoring/application/clicommands/NrpeCommand.php b/modules/monitoring/application/clicommands/NrpeCommand.php new file mode 100644 index 0000000..fe82322 --- /dev/null +++ b/modules/monitoring/application/clicommands/NrpeCommand.php @@ -0,0 +1,58 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Clicommands; + +use Icinga\Protocol\Nrpe\Connection; +use Icinga\Cli\Command; +use Exception; + +/** + * NRPE + */ +class NrpeCommand extends Command +{ + protected $defaultActionName = 'check'; + + /** + * Execute an NRPE command + * + * This command will execute an NRPE check, fire it against the given host + * and also pass through all your parameters. Output will be shown, exit + * code respected. + * + * USAGE + * + * icingacli monitoring nrpe <host> <command> [--ssl] [nrpe options] + * + * EXAMPLE + * + * icingacli monitoring nrpe 127.0.0.1 CheckMEM --ssl --MaxWarn=80% \ + * --MaxCrit=90% --type=physical + */ + public function checkAction() + { + $host = $this->params->shift(); + if (! $host) { + echo $this->showUsage(); + exit(3); + } + $command = $this->params->shift(null, '_NRPE_CHECK'); + $port = $this->params->shift('port', 5666); + try { + $nrpe = new Connection($host, $port); + if ($this->params->shift('ssl')) { + $nrpe->useSsl(); + } + $args = array(); + foreach ($this->params->getParams() as $k => $v) { + $args[] = $k . '=' . $v; + } + echo $nrpe->sendCommand($command, $args) . "\n"; + exit($nrpe->getLastReturnCode()); + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + exit(3); + } + } +} 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)) + . '"> </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'; + } +} diff --git a/modules/monitoring/application/forms/Command/CommandForm.php b/modules/monitoring/application/forms/Command/CommandForm.php new file mode 100644 index 0000000..34391cf --- /dev/null +++ b/modules/monitoring/application/forms/Command/CommandForm.php @@ -0,0 +1,92 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command; + +use Icinga\Exception\ConfigurationError; +use Icinga\Web\Form; +use Icinga\Web\Request; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; +use Icinga\Module\Monitoring\Command\Transport\CommandTransport; +use Icinga\Module\Monitoring\Command\Transport\CommandTransportInterface; + +/** + * Base class for command forms + */ +abstract class CommandForm extends Form +{ + /** + * Monitoring backend + * + * @var MonitoringBackend + */ + protected $backend; + + /** + * Set the monitoring backend + * + * @param MonitoringBackend $backend + * + * @return $this + */ + public function setBackend(MonitoringBackend $backend) + { + $this->backend = $backend; + return $this; + } + + /** + * Get the monitoring backend + * + * @return MonitoringBackend + */ + public function getBackend() + { + return $this->backend; + } + + /** + * Get the transport used to send commands + * + * @param Request $request + * + * @return CommandTransportInterface + * + * @throws ConfigurationError + */ + public function getTransport(Request $request) + { + if (($transportName = $request->getParam('transport')) !== null) { + $config = CommandTransport::getConfig(); + if ($config->hasSection($transportName)) { + $transport = CommandTransport::createTransport($config->getSection($transportName)); + } else { + throw new ConfigurationError(sprintf( + mt('monitoring', 'Command transport "%s" not found.'), + $transportName + )); + } + } else { + $transport = new CommandTransport(); + } + + return $transport; + } + + /** + * {@inheritdoc} + */ + public function getRedirectUrl() + { + $redirectUrl = parent::getRedirectUrl(); + // TODO(el): Forms should provide event handling. This is quite hackish + $formData = $this->getRequestData(); + if ($this->wasSent($formData) + && (! $this->getSubmitLabel() || $this->isSubmitted()) + && $this->isValid($formData) + ) { + $this->getResponse()->setAutoRefreshInterval(1); + } + return $redirectUrl; + } +} diff --git a/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php b/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php new file mode 100644 index 0000000..ee49962 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php @@ -0,0 +1,64 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Instance; + +use DateTime; +use DateInterval; +use Icinga\Module\Monitoring\Command\Instance\DisableNotificationsExpireCommand; +use Icinga\Module\Monitoring\Forms\Command\CommandForm; +use Icinga\Web\Notification; + +/** + * Form for disabling host and service notifications w/ an optional expire date and time on an Icinga instance + */ +class DisableNotificationsExpireCommandForm extends CommandForm +{ + /** + * (non-PHPDoc) + * @see \Zend_Form::init() For the method documentation. + */ + public function init() + { + $this->setRequiredCue(null); + $this->setSubmitLabel($this->translate('Disable Notifications')); + $this->addDescription($this->translate( + 'This command is used to disable host and service notifications for a specific time.' + )); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $expireTime = new DateTime(); + $expireTime->add(new DateInterval('PT1H')); + $this->addElement( + 'dateTimePicker', + 'expire_time', + array( + 'required' => true, + 'label' => $this->translate('Expire Time'), + 'description' => $this->translate('Set the expire time.'), + 'value' => $expireTime + ) + ); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + $disableNotifications = new DisableNotificationsExpireCommand(); + $disableNotifications + ->setExpireTime($this->getElement('expire_time')->getValue()->getTimestamp()); + $this->getTransport($this->request)->send($disableNotifications); + Notification::success($this->translate('Disabling host and service notifications..')); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php b/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php new file mode 100644 index 0000000..8b01399 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php @@ -0,0 +1,279 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Instance; + +use Icinga\Module\Monitoring\Command\Instance\ToggleInstanceFeatureCommand; +use Icinga\Module\Monitoring\Forms\Command\CommandForm; +use Icinga\Web\Notification; + +/** + * Form for enabling or disabling features of Icinga instances + */ +class ToggleInstanceFeaturesCommandForm extends CommandForm +{ + /** + * Instance status + * + * @var object + */ + protected $status; + + /** + * (non-PHPDoc) + * @see \Zend_Form::init() For the method documentation. + */ + public function init() + { + $this->setUseFormAutosubmit(); + $this->setAttrib('class', self::DEFAULT_CLASSES . ' instance-features'); + } + + /** + * Set the instance status + * + * @param object $status + * + * @return $this + */ + public function setStatus($status) + { + $this->status = (object) $status; + return $this; + } + + /** + * Get the instance status + * + * @return object + */ + public function getStatus() + { + return $this->status; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $notificationDescription = null; + $isIcinga2 = $this->getBackend()->isIcinga2($this->status->program_version); + + if (! $isIcinga2) { + if ((bool) $this->status->notifications_enabled) { + if ($this->hasPermission('monitoring/command/feature/instance')) { + $notificationDescription = sprintf( + '<a aria-label="%1$s" class="action-link" title="%1$s"' + . ' href="%2$s" data-base-target="_next">%3$s</a>', + $this->translate('Disable notifications for a specific time on a program-wide basis'), + $this->getView()->href('monitoring/health/disable-notifications'), + $this->translate('Disable temporarily') + ); + } else { + $notificationDescription = null; + } + } elseif ($this->status->disable_notif_expire_time) { + $notificationDescription = sprintf( + $this->translate('Notifications will be re-enabled in <strong>%s</strong>'), + $this->getView()->timeUntil($this->status->disable_notif_expire_time) + ); + } + } + + $toggleDisabled = $this->hasPermission('monitoring/command/feature/instance') ? null : ''; + + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS, + array( + 'label' => $this->translate('Active Host Checks'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS, + array( + 'label' => $this->translate('Active Service Checks'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS, + array( + 'label' => $this->translate('Event Handlers'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION, + array( + 'label' => $this->translate('Flap Detection'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS, + array( + 'label' => $this->translate('Notifications'), + 'autosubmit' => true, + 'description' => $notificationDescription, + 'decorators' => array( + array('Label', array('tag'=>'span', 'separator' => '', 'class' => 'control-label')), + array( + 'Description', + array('tag' => 'span', 'class' => 'description', 'escape' => false) + ), + array(array('labelWrap' => 'HtmlTag'), array('tag' => 'div', 'class' => 'control-label-group')), + array('ViewHelper', array('separator' => '')), + array('Errors', array('separator' => '')), + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group')) + ), + 'disabled' => $toggleDisabled + ) + ); + + if (! $isIcinga2) { + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING, + array( + 'label' => $this->translate('Obsessing Over Hosts'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_SERVICE_OBSESSING, + array( + 'label' => $this->translate('Obsessing Over Services'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_PASSIVE_HOST_CHECKS, + array( + 'label' => $this->translate('Passive Host Checks'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_PASSIVE_SERVICE_CHECKS, + array( + 'label' => $this->translate('Passive Service Checks'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + } + + $this->addElement( + 'checkbox', + ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA, + array( + 'label' => $this->translate('Performance Data'), + 'autosubmit' => true, + 'disabled' => $toggleDisabled + ) + ); + } + + /** + * Load feature status + * + * @param object $instanceStatus + * + * @return $this + */ + public function load($instanceStatus) + { + $this->create(); + foreach ($this->getValues() as $feature => $enabled) { + $this->getElement($feature)->setChecked($instanceStatus->{$feature}); + } + + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + $this->assertPermission('monitoring/command/feature/instance'); + + $notifications = array( + ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS => array( + $this->translate('Enabling active host checks..'), + $this->translate('Disabling active host checks..') + ), + ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS => array( + $this->translate('Enabling active service checks..'), + $this->translate('Disabling active service checks..') + ), + ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS => array( + $this->translate('Enabling event handlers..'), + $this->translate('Disabling event handlers..') + ), + ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION => array( + $this->translate('Enabling flap detection..'), + $this->translate('Disabling flap detection..') + ), + ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS => array( + $this->translate('Enabling notifications..'), + $this->translate('Disabling notifications..') + ), + ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING => array( + $this->translate('Enabling obsessing over hosts..'), + $this->translate('Disabling obsessing over hosts..') + ), + ToggleInstanceFeatureCommand::FEATURE_SERVICE_OBSESSING => array( + $this->translate('Enabling obsessing over services..'), + $this->translate('Disabling obsessing over services..') + ), + ToggleInstanceFeatureCommand::FEATURE_PASSIVE_HOST_CHECKS => array( + $this->translate('Enabling passive host checks..'), + $this->translate('Disabling passive host checks..') + ), + ToggleInstanceFeatureCommand::FEATURE_PASSIVE_SERVICE_CHECKS => array( + $this->translate('Enabling passive service checks..'), + $this->translate('Disabling passive service checks..') + ), + ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA => array( + $this->translate('Enabling performance data..'), + $this->translate('Disabling performance data..') + ) + ); + + foreach ($this->getValues() as $feature => $enabled) { + if ((bool) $this->status->{$feature} !== (bool) $enabled) { + $toggleFeature = new ToggleInstanceFeatureCommand(); + $toggleFeature + ->setFeature($feature) + ->setEnabled($enabled); + $this->getTransport($this->request)->send($toggleFeature); + + Notification::success( + $notifications[$feature][$enabled ? 0 : 1] + ); + } + } + + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php new file mode 100644 index 0000000..c7caf5d --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php @@ -0,0 +1,172 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use DateTime; +use DateInterval; +use Icinga\Application\Config; +use Icinga\Module\Monitoring\Command\Object\AcknowledgeProblemCommand; +use Icinga\Web\Notification; + +/** + * Form for acknowledging host or service problems + */ +class AcknowledgeProblemCommandForm extends ObjectsCommandForm +{ + /** + * Initialize this form + */ + public function init() + { + $this->addDescription($this->translate( + 'This command is used to acknowledge host or service problems. When a problem is acknowledged,' + . ' future notifications about problems are temporarily disabled until the host or service' + . ' recovers.' + )); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::getSubmitLabel() For the method documentation. + */ + public function getSubmitLabel() + { + return $this->translatePlural('Acknowledge problem', 'Acknowledge problems', count($this->objects)); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $config = Config::module('monitoring'); + + $acknowledgeExpire = (bool) $config->get('settings', 'acknowledge_expire', false); + + $this->addElements(array( + array( + 'textarea', + 'comment', + array( + 'required' => true, + 'label' => $this->translate('Comment'), + 'description' => $this->translate( + 'If you work with other administrators, you may find it useful to share information about' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ), + 'attribs' => array('class' => 'autofocus') + ) + ), + array( + 'checkbox', + 'persistent', + array( + 'label' => $this->translate('Persistent Comment'), + 'value' => (bool) $config->get('settings', 'acknowledge_persistent', false), + 'description' => $this->translate( + 'If you would like the comment to remain even when the acknowledgement is removed, check this' + . ' option.' + ) + ) + ), + array( + 'checkbox', + 'expire', + array( + 'label' => $this->translate('Use Expire Time'), + 'value' => $acknowledgeExpire, + 'description' => $this->translate( + 'If the acknowledgement should expire, check this option.' + ), + 'autosubmit' => true + ) + ) + )); + $expire = isset($formData['expire']) ? $formData['expire'] : $acknowledgeExpire; + if ($expire) { + $expireTime = new DateTime(); + $expireTime->add(new DateInterval($config->get('settings', 'acknowledge_expire_time', 'PT1H'))); + $this->addElement( + 'dateTimePicker', + 'expire_time', + array( + 'label' => $this->translate('Expire Time'), + 'value' => $expireTime, + 'description' => $this->translate( + 'Enter the expire date and time for this acknowledgement here. Icinga will delete the' + . ' acknowledgement after this time expired.' + ) + ) + ); + $this->addDisplayGroup( + array('expire', 'expire_time'), + 'expire-expire_time', + array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div')) + ) + ) + ); + } + $this->addElements(array( + array( + 'checkbox', + 'sticky', + array( + 'label' => $this->translate('Sticky Acknowledgement'), + 'value' => (bool) $config->get('settings', 'acknowledge_sticky', false), + 'description' => $this->translate( + 'If you want the acknowledgement to remain until the host or service recovers even if the host' + . ' or service changes state, check this option.' + ) + ) + ), + array( + 'checkbox', + 'notify', + array( + 'label' => $this->translate('Send Notification'), + 'value' => (bool) $config->get('settings', 'acknowledge_notify', true), + 'description' => $this->translate( + 'If you do not want an acknowledgement notification to be sent out to the appropriate contacts,' + . ' uncheck this option.' + ) + ) + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $ack = new AcknowledgeProblemCommand(); + $ack + ->setObject($object) + ->setComment($this->getElement('comment')->getValue()) + ->setAuthor($this->request->getUser()->getUsername()) + ->setPersistent($this->getElement('persistent')->isChecked()) + ->setSticky($this->getElement('sticky')->isChecked()) + ->setNotify($this->getElement('notify')->isChecked()); + if ($this->getElement('expire')->isChecked()) { + $ack->setExpireTime($this->getElement('expire_time')->getValue()->getTimestamp()); + } + $this->getTransport($this->request)->send($ack); + } + Notification::success($this->translatePlural( + 'Acknowledging problem..', + 'Acknowledging problems..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php new file mode 100644 index 0000000..72133a0 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php @@ -0,0 +1,148 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use DateInterval; +use DateTime; +use Icinga\Application\Config; +use Icinga\Module\Monitoring\Command\Object\AddCommentCommand; +use Icinga\Web\Notification; + +/** + * Form for adding host or service comments + */ +class AddCommentCommandForm extends ObjectsCommandForm +{ + /** + * Initialize this form + */ + public function init() + { + $this->addDescription($this->translate('This command is used to add host or service comments.')); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::getSubmitLabel() For the method documentation. + */ + public function getSubmitLabel() + { + return $this->translatePlural('Add comment', 'Add comments', count($this->objects)); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addElement( + 'textarea', + 'comment', + array( + 'required' => true, + 'label' => $this->translate('Comment'), + 'description' => $this->translate( + 'If you work with other administrators, you may find it useful to share information about' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ), + 'attribs' => array('class' => 'autofocus') + ) + ); + if (! $this->getBackend()->isIcinga2()) { + $this->addElement( + 'checkbox', + 'persistent', + array( + 'label' => $this->translate('Persistent'), + 'value' => (bool) Config::module('monitoring')->get('settings', 'comment_persistent', true), + 'description' => $this->translate( + 'If you uncheck this option, the comment will automatically be deleted the next time Icinga is' + . ' restarted.' + ) + ) + ); + } + + if (version_compare($this->getBackend()->getProgramVersion(), '2.13.0', '>=')) { + $config = Config::module('monitoring'); + $commentExpire = (bool) $config->get('settings', 'comment_expire', false); + + $this->addElement( + 'checkbox', + 'expire', + [ + 'label' => $this->translate('Use Expire Time'), + 'value' => $commentExpire, + 'description' => $this->translate('If the comment should expire, check this option.'), + 'autosubmit' => true + ] + ); + + if (isset($formData['expire']) ? $formData['expire'] : $commentExpire) { + $expireTime = new DateTime(); + $expireTime->add(new DateInterval($config->get('settings', 'comment_expire_time', 'PT1H'))); + + $this->addElement( + 'dateTimePicker', + 'expire_time', + [ + 'label' => $this->translate('Expire Time'), + 'value' => $expireTime, + 'description' => $this->translate( + 'Enter the expire date and time for this comment here. Icinga will delete the' + . ' comment after this time expired.' + ) + ] + ); + + $this->addDisplayGroup( + ['expire', 'expire_time'], + 'expire-expire_time', + [ + 'decorators' => [ + 'FormElements', + ['HtmlTag', ['tag' => 'div']] + ] + ] + ); + } + } + + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $comment = new AddCommentCommand(); + $comment->setObject($object); + $comment->setComment($this->getElement('comment')->getValue()); + $comment->setAuthor($this->request->getUser()->getUsername()); + if (($persistent = $this->getElement('persistent')) !== null) { + $comment->setPersistent($persistent->isChecked()); + } + + $expire = $this->getElement('expire'); + + if ($expire !== null && $expire->isChecked()) { + $comment->setExpireTime($this->getElement('expire_time')->getValue()->getTimestamp()); + } + + $this->getTransport($this->request)->send($comment); + } + Notification::success($this->translatePlural( + 'Adding comment..', + 'Adding comments..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php new file mode 100644 index 0000000..a586d2f --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php @@ -0,0 +1,87 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Module\Monitoring\Command\Object\ScheduleHostCheckCommand; +use Icinga\Module\Monitoring\Command\Object\ScheduleServiceCheckCommand; +use Icinga\Web\Notification; + +/** + * Form for immediately checking hosts or services + */ +class CheckNowCommandForm extends ObjectsCommandForm +{ + /** + * (non-PHPDoc) + * @see \Zend_Form::init() For the method documentation. + */ + public function init() + { + $this->setAttrib('class', 'inline'); + $this->setSubmitLabel($this->translate('Check now')); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::addSubmitButton() For the method documentation. + */ + public function addSubmitButton() + { + $this->addElements(array( + array( + 'button', + 'btn_submit', + array( + 'class' => 'link-button spinner', + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls')) + ), + 'escape' => false, + 'ignore' => true, + 'label' => $this->getView()->icon('arrows-cw') . $this->translate('Check now'), + 'type' => 'submit', + 'title' => $this->translate('Schedule the next active check to run immediately'), + 'value' => $this->translate('Check now') + ) + ) + )); + + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + if (! $object->active_checks_enabled + && ! $this->Auth()->hasPermission('monitoring/command/schedule-check') + ) { + continue; + } + + if ($object->getType() === $object::TYPE_HOST) { + $check = new ScheduleHostCheckCommand(); + } else { + $check = new ScheduleServiceCheckCommand(); + } + $check + ->setObject($object) + ->setForced() + ->setCheckTime(time()); + $this->getTransport($this->request)->send($check); + } + Notification::success(mtp( + 'monitoring', + 'Scheduling check..', + 'Scheduling checks..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php new file mode 100644 index 0000000..cd15b19 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php @@ -0,0 +1,109 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Module\Monitoring\Command\Object\DeleteCommentCommand; +use Icinga\Module\Monitoring\Forms\Command\CommandForm; +use Icinga\Web\Notification; + +/** + * Form for deleting host or service comments + */ +class DeleteCommentCommandForm extends CommandForm +{ + /** + * {@inheritdoc} + */ + public function init() + { + $this->setAttrib('class', 'inline'); + } + + /** + * {@inheritdoc} + */ + public function addSubmitButton() + { + $this->addElement( + 'button', + 'btn_submit', + array( + 'class' => 'link-button spinner', + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls')) + ), + 'escape' => false, + 'ignore' => true, + 'label' => $this->getView()->icon('cancel'), + 'title' => $this->translate('Delete this comment'), + 'type' => 'submit' + ) + ); + return $this; + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements( + array( + array( + 'hidden', + 'comment_id', + array( + 'required' => true, + 'validators' => array('NotEmpty'), + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'comment_is_service', + array( + 'filters' => array('Boolean'), + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'comment_name', + array( + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'redirect', + array( + 'decorators' => array('ViewHelper') + ) + ) + ) + ); + return $this; + } + + /** + * {@inheritdoc} + */ + public function onSuccess() + { + $cmd = new DeleteCommentCommand(); + $cmd + ->setAuthor($this->Auth()->getUser()->getUsername()) + ->setCommentId($this->getElement('comment_id')->getValue()) + ->setCommentName($this->getElement('comment_name')->getValue()) + ->setIsService($this->getElement('comment_is_service')->getValue()); + $this->getTransport($this->request)->send($cmd); + $redirect = $this->getElement('redirect')->getValue(); + if (! empty($redirect)) { + $this->setRedirectUrl($redirect); + } + Notification::success($this->translate('Deleting comment..')); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php new file mode 100644 index 0000000..70ea7b8 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php @@ -0,0 +1,89 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Module\Monitoring\Command\Object\DeleteCommentCommand; +use Icinga\Module\Monitoring\Forms\Command\CommandForm; +use Icinga\Web\Notification; + +/** + * Form for deleting host or service comments + */ +class DeleteCommentsCommandForm extends CommandForm +{ + /** + * The comments to delete + * + * @var array + */ + protected $comments; + + /** + * {@inheritdoc} + */ + public function init() + { + $this->setAttrib('class', 'inline'); + } + + /** + * Set the comments to delete + * + * @param iterable $comments + * + * @return $this + */ + public function setComments($comments) + { + $this->comments = $comments; + return $this; + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + array( + 'hidden', + 'redirect', + array('decorators' => array('ViewHelper')) + ) + )); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getSubmitLabel() + { + return $this->translatePlural('Remove', 'Remove All', count($this->comments)); + } + + /** + * {@inheritdoc} + */ + public function onSuccess() + { + foreach ($this->comments as $comment) { + $cmd = new DeleteCommentCommand(); + $cmd + ->setCommentId($comment->id) + ->setCommentName($comment->name) + ->setAuthor($this->Auth()->getUser()->getUsername()) + ->setIsService(isset($comment->service_description)); + $this->getTransport($this->request)->send($cmd); + } + $redirect = $this->getElement('redirect')->getValue(); + if (! empty($redirect)) { + $this->setRedirectUrl($redirect); + } + Notification::success( + $this->translatePlural('Deleting comment..', 'Deleting comments..', count($this->comments)) + ); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php new file mode 100644 index 0000000..79700cb --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php @@ -0,0 +1,129 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Module\Monitoring\Command\Object\DeleteDowntimeCommand; +use Icinga\Module\Monitoring\Exception\CommandTransportException; +use Icinga\Module\Monitoring\Forms\Command\CommandForm; +use Icinga\Web\Notification; + +/** + * Form for deleting host or service downtimes + */ +class DeleteDowntimeCommandForm extends CommandForm +{ + /** + * {@inheritdoc} + */ + public function init() + { + $this->setAttrib('class', 'inline'); + } + + /** + * {@inheritdoc} + */ + public function addSubmitButton() + { + $this->addElement( + 'button', + 'btn_submit', + array( + 'class' => 'link-button spinner', + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls')) + ), + 'escape' => false, + 'ignore' => true, + 'label' => $this->getView()->icon('cancel'), + 'title' => $this->translate('Delete this downtime'), + 'type' => 'submit' + ) + ); + return $this; + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements( + array( + array( + 'hidden', + 'downtime_id', + array( + 'decorators' => array('ViewHelper'), + 'required' => true, + 'validators' => array('NotEmpty') + ) + ), + array( + 'hidden', + 'downtime_is_service', + array( + 'decorators' => array('ViewHelper'), + 'filters' => array('Boolean') + ) + ), + array( + 'hidden', + 'downtime_name', + array( + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'redirect', + array( + 'decorators' => array('ViewHelper') + ) + ) + ) + ); + return $this; + } + + /** + * {@inheritdoc} + */ + public function onSuccess() + { + $cmd = new DeleteDowntimeCommand(); + $cmd + ->setAuthor($this->Auth()->getUser()->getUsername()) + ->setDowntimeId($this->getElement('downtime_id')->getValue()) + ->setDowntimeName($this->getElement('downtime_name')->getValue()) + ->setIsService($this->getElement('downtime_is_service')->getValue()); + + $errorMsg = null; + + try { + $this->getTransport($this->request)->send($cmd); + } catch (CommandTransportException $e) { + $errorMsg = $e->getMessage(); + } + + if (! $errorMsg) { + $redirect = $this->getElement('redirect')->getValue(); + Notification::success($this->translate('Deleting downtime.')); + } else { + if (! $this->getIsApiTarget()) { + $redirect = $this->getRequest()->getUrl(); + } + + Notification::error($errorMsg); + } + + if (! empty($redirect)) { + $this->setRedirectUrl($redirect); + return true; + } + + return false; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php new file mode 100644 index 0000000..d4ee803 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php @@ -0,0 +1,89 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Module\Monitoring\Command\Object\DeleteDowntimeCommand; +use Icinga\Module\Monitoring\Forms\Command\CommandForm; +use Icinga\Web\Notification; + +/** + * Form for deleting host or service downtimes + */ +class DeleteDowntimesCommandForm extends CommandForm +{ + /** + * The downtimes to delete + * + * @var array + */ + protected $downtimes; + + /** + * {@inheritdoc} + */ + public function init() + { + $this->setAttrib('class', 'inline'); + } + + /** + * Set the downtimes to delete + * + * @param iterable $downtimes + * + * @return $this + */ + public function setDowntimes($downtimes) + { + $this->downtimes = $downtimes; + return $this; + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + array( + 'hidden', + 'redirect', + array('decorators' => array('ViewHelper')) + ) + )); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getSubmitLabel() + { + return $this->translatePlural('Remove', 'Remove All', count($this->downtimes)); + } + + /** + * {@inheritdoc} + */ + public function onSuccess() + { + foreach ($this->downtimes as $downtime) { + $delDowntime = new DeleteDowntimeCommand(); + $delDowntime + ->setDowntimeId($downtime->id) + ->setDowntimeName($downtime->name) + ->setAuthor($this->Auth()->getUser()->getUsername()) + ->setIsService(isset($downtime->service_description)); + $this->getTransport($this->request)->send($delDowntime); + } + $redirect = $this->getElement('redirect')->getValue(); + if (! empty($redirect)) { + $this->setRedirectUrl($redirect); + } + Notification::success( + $this->translatePlural('Deleting downtime..', 'Deleting downtimes..', count($this->downtimes)) + ); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ObjectsCommandForm.php b/modules/monitoring/application/forms/Command/Object/ObjectsCommandForm.php new file mode 100644 index 0000000..928c365 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ObjectsCommandForm.php @@ -0,0 +1,47 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Module\Monitoring\Forms\Command\CommandForm; +use Icinga\Module\Monitoring\Object\MonitoredObject; + +/** + * Base class for Icinga object command forms + */ +abstract class ObjectsCommandForm extends CommandForm +{ + /** + * Involved Icinga objects + * + * @var array|\Traversable|\ArrayAccess + */ + protected $objects; + + /** + * Set the involved Icinga objects + * + * @param $objects MonitoredObject|array|\Traversable|\ArrayAccess + * + * @return $this + */ + public function setObjects($objects) + { + if ($objects instanceof MonitoredObject) { + $this->objects = array($objects); + } else { + $this->objects = $objects; + } + return $this; + } + + /** + * Get the involved Icinga objects + * + * @return array|\ArrayAccess|\Traversable + */ + public function getObjects() + { + return $this->objects; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php b/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php new file mode 100644 index 0000000..ab46071 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php @@ -0,0 +1,139 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Web\Notification; +use Icinga\Module\Monitoring\Command\Object\ProcessCheckResultCommand; + +/** + * Form for submitting a passive host or service check result + */ +class ProcessCheckResultCommandForm extends ObjectsCommandForm +{ + /** + * Initialize this form + */ + public function init() + { + $this->addDescription($this->translate( + 'This command is used to submit passive host or service check results.' + )); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::getSubmitLabel() For the method documentation. + */ + public function getSubmitLabel() + { + return $this->translatePlural( + 'Submit Passive Check Result', + 'Submit Passive Check Results', + count($this->objects) + ); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData) + { + foreach ($this->getObjects() as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + // Nasty, but as getObjects() returns everything but an object with a real + // iterator interface this is the only way to fetch just the first element + break; + } + + $this->addElement( + 'select', + 'status', + array( + 'required' => true, + 'label' => $this->translate('Status'), + 'description' => $this->translate('The state this check result should report'), + 'multiOptions' => $object->getType() === $object::TYPE_HOST ? $this->getHostMultiOptions() : array( + ProcessCheckResultCommand::SERVICE_OK => $this->translate('OK', 'icinga.state'), + ProcessCheckResultCommand::SERVICE_WARNING => $this->translate('WARNING', 'icinga.state'), + ProcessCheckResultCommand::SERVICE_CRITICAL => $this->translate('CRITICAL', 'icinga.state'), + ProcessCheckResultCommand::SERVICE_UNKNOWN => $this->translate('UNKNOWN', 'icinga.state') + ) + ) + ); + $this->addElement( + 'text', + 'output', + array( + 'required' => true, + 'label' => $this->translate('Output'), + 'description' => $this->translate('The plugin output of this check result') + ) + ); + $this->addElement( + 'text', + 'perfdata', + array( + 'allowEmpty' => true, + 'label' => $this->translate('Performance Data'), + 'description' => $this->translate( + 'The performance data of this check result. Leave empty' + . ' if this check result has no performance data' + ) + ) + ); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + if (! $object->passive_checks_enabled) { + continue; + } + + $command = new ProcessCheckResultCommand(); + $command->setObject($object); + $command->setStatus($this->getValue('status')); + $command->setOutput($this->getValue('output')); + + if ($perfdata = $this->getValue('perfdata')) { + $command->setPerformanceData($perfdata); + } + + $this->getTransport($this->request)->send($command); + } + + Notification::success($this->translatePlural( + 'Processing check result..', + 'Processing check results..', + count($this->objects) + )); + + return true; + } + + /** + * Returns the available host options based on the program version + * + * @return array + */ + protected function getHostMultiOptions() + { + $options = array( + ProcessCheckResultCommand::HOST_UP => $this->translate('UP', 'icinga.state'), + ProcessCheckResultCommand::HOST_DOWN => $this->translate('DOWN', 'icinga.state') + ); + + if (! $this->getBackend()->isIcinga2()) { + $options[ProcessCheckResultCommand::HOST_UNREACHABLE] = $this->translate('UNREACHABLE', 'icinga.state'); + } + + return $options; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php b/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php new file mode 100644 index 0000000..e45a055 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php @@ -0,0 +1,122 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Module\Monitoring\Command\Object\RemoveAcknowledgementCommand; +use Icinga\Web\Notification; + +/** + * Form for removing host or service problem acknowledgements + */ +class RemoveAcknowledgementCommandForm extends ObjectsCommandForm +{ + /** + * Whether to show the submit label next to the remove icon + * + * The submit label is disabled in detail views but should be enabled in multi-select views. + * + * @var bool + */ + protected $labelEnabled = false; + + /** + * Whether to show the submit label next to the remove icon + * + * @return bool + */ + public function isLabelEnabled() + { + return $this->labelEnabled; + } + + /** + * Set whether to show the submit label next to the remove icon + * + * @param bool $labelEnabled + * + * @return $this + */ + public function setLabelEnabled($labelEnabled) + { + $this->labelEnabled = (bool) $labelEnabled; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function init() + { + $this->setAttrib('class', 'inline'); + } + + /** + * {@inheritdoc} + */ + public function addSubmitButton() + { + $this->addElement( + 'button', + 'btn_submit', + array( + 'class' => 'link-button spinner', + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls')) + ), + 'escape' => false, + 'ignore' => true, + 'label' => $this->getSubmitLabel(), + 'title' => $this->translatePlural( + 'Remove acknowledgement', + 'Remove acknowledgements', + count($this->objects) + ), + 'type' => 'submit' + ) + ); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getSubmitLabel() + { + $label = $this->getView()->icon('cancel'); + if ($this->isLabelEnabled()) { + $label .= $this->translatePlural( + 'Remove acknowledgement', + 'Remove acknowledgements', + count($this->objects) + ); + } + + return $label; + } + + /** + * {@inheritdoc} + */ + public function onSuccess() + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $removeAck = new RemoveAcknowledgementCommand(); + $removeAck->setObject($object); + $removeAck->setAuthor($this->Auth()->getUser()->getUsername()); + $this->getTransport($this->request)->send($removeAck); + } + Notification::success(mtp( + 'monitoring', + 'Removing acknowledgement..', + 'Removing acknowledgements..', + count($this->objects) + )); + + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php new file mode 100644 index 0000000..55b044f --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php @@ -0,0 +1,67 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Application\Config; +use Icinga\Module\Monitoring\Command\Object\ScheduleHostCheckCommand; +use Icinga\Web\Notification; + +/** + * Form for scheduling host checks + */ +class ScheduleHostCheckCommandForm extends ScheduleServiceCheckCommandForm +{ + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $config = Config::module('monitoring'); + + parent::createElements($formData); + $this->addElements(array( + array( + 'checkbox', + 'all_services', + array( + 'label' => $this->translate('All Services'), + 'value' => (bool) $config->get('settings', 'hostcheck_all_services', false), + 'description' => $this->translate( + 'Schedule check for all services on the hosts and the hosts themselves.' + ) + ) + ) + )); + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + if (! $object->active_checks_enabled + && ! $this->Auth()->hasPermission('monitoring/command/schedule-check') + ) { + continue; + } + + $check = new ScheduleHostCheckCommand(); + $check + ->setObject($object) + ->setOfAllServices($this->getElement('all_services')->isChecked()); + $this->scheduleCheck($check, $this->request); + } + Notification::success($this->translatePlural( + 'Scheduling host check..', + 'Scheduling host checks..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php new file mode 100644 index 0000000..89db1ce --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php @@ -0,0 +1,178 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use DateInterval; +use DateTime; +use Icinga\Application\Config; +use Icinga\Module\Monitoring\Command\Object\ApiScheduleHostDowntimeCommand; +use Icinga\Module\Monitoring\Command\Object\PropagateHostDowntimeCommand; +use Icinga\Module\Monitoring\Command\Object\ScheduleHostDowntimeCommand; +use Icinga\Module\Monitoring\Command\Object\ScheduleServiceDowntimeCommand; +use Icinga\Module\Monitoring\Command\Transport\ApiCommandTransport; +use Icinga\Module\Monitoring\Command\Transport\CommandTransport; +use Icinga\Web\Notification; + +/** + * Form for scheduling host downtimes + */ +class ScheduleHostDowntimeCommandForm extends ScheduleServiceDowntimeCommandForm +{ + /** @var bool */ + protected $hostDowntimeAllServices; + + public function init() + { + $this->start = new DateTime(); + $config = Config::module('monitoring'); + $this->commentText = $config->get('settings', 'hostdowntime_comment_text'); + + $this->hostDowntimeAllServices = (bool) $config->get('settings', 'hostdowntime_all_services', false); + + $fixedEnd = clone $this->start; + $fixed = $config->get('settings', 'hostdowntime_end_fixed', 'PT1H'); + $this->fixedEnd = $fixedEnd->add(new DateInterval($fixed)); + + $flexibleEnd = clone $this->start; + $flexible = $config->get('settings', 'hostdowntime_end_flexible', 'PT1H'); + $this->flexibleEnd = $flexibleEnd->add(new DateInterval($flexible)); + + $flexibleDuration = $config->get('settings', 'hostdowntime_flexible_duration', 'PT2H'); + $this->flexibleDuration = new DateInterval($flexibleDuration); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + parent::createElements($formData); + + $this->addElement( + 'checkbox', + 'all_services', + array( + 'description' => $this->translate( + 'Schedule downtime for all services on the hosts and the hosts themselves.' + ), + 'label' => $this->translate('All Services'), + 'value' => $this->hostDowntimeAllServices + ) + ); + + if (! $this->getBackend()->isIcinga2() + || version_compare($this->getBackend()->getProgramVersion(), '2.6.0', '>=') + ) { + $this->addElement( + 'select', + 'child_hosts', + array( + 'description' => $this->translate( + 'Define what should be done with the child hosts of the hosts.' + ), + 'label' => $this->translate('Child Hosts'), + 'multiOptions' => array( + 0 => $this->translate('Do nothing with child hosts'), + 1 => $this->translate('Schedule triggered downtime for all child hosts'), + 2 => $this->translate('Schedule non-triggered downtime for all child hosts') + ), + 'value' => 0 + ) + ); + } + + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + $end = $this->getValue('end')->getTimestamp(); + if ($end <= $this->getValue('start')->getTimestamp()) { + $endElement = $this->_elements['end']; + $endElement->setValue($endElement->getValue()->format($endElement->getFormat())); + $endElement->addError($this->translate('The end time must be greater than the start time')); + return false; + } + + $now = new DateTime; + if ($end <= $now->getTimestamp()) { + $endElement = $this->_elements['end']; + $endElement->setValue($endElement->getValue()->format($endElement->getFormat())); + $endElement->addError($this->translate('A downtime must not be in the past')); + return false; + } + + // Send all_services API parameter if Icinga is equal to or greater than 2.11.0 + $allServicesNative = version_compare($this->getBackend()->getProgramVersion(), '2.11.0', '>='); + // Use ApiScheduleHostDowntimeCommand only when Icinga is equal to or greater than 2.11.0 and + // when an API command transport is requested or only API command transports are configured: + $useApiDowntime = $allServicesNative; + if ($useApiDowntime) { + $transport = $this->getTransport($this->getRequest()); + if ($transport instanceof CommandTransport) { + foreach ($transport::getConfig() as $config) { + if (strtolower($config->transport) !== 'api') { + $useApiDowntime = false; + break; + } + } + } elseif (! $transport instanceof ApiCommandTransport) { + $useApiDowntime = false; + } + } + + foreach ($this->objects as $object) { + if ($useApiDowntime) { + $hostDowntime = (new ApiScheduleHostDowntimeCommand()) + ->setForAllServices($this->getElement('all_services')->isChecked()) + ->setChildOptions((int) $this->getElement('child_hosts')->getValue()); + // Code duplicated for readability and scope + $hostDowntime->setObject($object); + $this->scheduleDowntime($hostDowntime, $this->request); + + continue; + } + + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + if (($childHostsEl = $this->getElement('child_hosts')) !== null) { + $childHosts = (int) $childHostsEl->getValue(); + } else { + $childHosts = 0; + } + $allServices = $this->getElement('all_services')->isChecked(); + if ($childHosts === 0) { + $hostDowntime = (new ScheduleHostDowntimeCommand()) + ->setForAllServicesNative($allServicesNative); + if ($allServices === true) { + $hostDowntime->setForAllServices(); + }; + } else { + $hostDowntime = new PropagateHostDowntimeCommand(); + if ($childHosts === 1) { + $hostDowntime->setTriggered(); + } + if ($allServices === true) { + foreach ($object->services as $service) { + $serviceDowntime = new ScheduleServiceDowntimeCommand(); + $serviceDowntime->setObject($service); + $this->scheduleDowntime($serviceDowntime, $this->request); + } + } + } + $hostDowntime->setObject($object); + $this->scheduleDowntime($hostDowntime, $this->request); + } + Notification::success($this->translatePlural( + 'Scheduling host downtime..', + 'Scheduling host downtimes..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php new file mode 100644 index 0000000..f65aea8 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php @@ -0,0 +1,112 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use DateTime; +use DateInterval; +use Icinga\Module\Monitoring\Command\Object\ScheduleServiceCheckCommand; +use Icinga\Web\Notification; +use Icinga\Web\Request; + +/** + * Form for scheduling service checks + */ +class ScheduleServiceCheckCommandForm extends ObjectsCommandForm +{ + /** + * Initialize this form + */ + public function init() + { + $this->addDescription($this->translate( + 'This command is used to schedule the next check of hosts or services. Icinga will re-queue the' + . ' hosts or services to be checked at the time you specify.' + )); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::getSubmitLabel() For the method documentation. + */ + public function getSubmitLabel() + { + return $this->translatePlural('Schedule check', 'Schedule checks', count($this->objects)); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $checkTime = new DateTime(); + $checkTime->add(new DateInterval('PT1H')); + $this->addElements(array( + array( + 'dateTimePicker', + 'check_time', + array( + 'required' => true, + 'label' => $this->translate('Check Time'), + 'description' => $this->translate( + 'Set the date and time when the check should be scheduled.' + ), + 'value' => $checkTime + ) + ), + array( + 'checkbox', + 'force_check', + array( + 'label' => $this->translate('Force Check'), + 'description' => $this->translate( + 'If you select this option, Icinga will force a check regardless of both what time the' + . ' scheduled check occurs and whether or not checks are enabled.' + ) + ) + ) + )); + return $this; + } + + /** + * Schedule a check + * + * @param ScheduleServiceCheckCommand $check + * @param Request $request + */ + public function scheduleCheck(ScheduleServiceCheckCommand $check, Request $request) + { + $check + ->setForced($this->getElement('force_check')->isChecked()) + ->setCheckTime($this->getElement('check_time')->getValue()->getTimestamp()); + $this->getTransport($request)->send($check); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + if (! $object->active_checks_enabled + && ! $this->Auth()->hasPermission('monitoring/command/schedule-check') + ) { + continue; + } + + $check = new ScheduleServiceCheckCommand(); + $check->setObject($object); + $this->scheduleCheck($check, $this->request); + } + Notification::success($this->translatePlural( + 'Scheduling service check..', + 'Scheduling service checks..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php new file mode 100644 index 0000000..90d50d4 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php @@ -0,0 +1,263 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use DateTime; +use DateInterval; +use Icinga\Application\Config; +use Icinga\Module\Monitoring\Command\Object\ScheduleServiceDowntimeCommand; +use Icinga\Web\Notification; +use Icinga\Web\Request; + +/** + * Form for scheduling service downtimes + */ +class ScheduleServiceDowntimeCommandForm extends ObjectsCommandForm +{ + /** + * Fixed downtime + */ + const FIXED = 'fixed'; + + /** + * Flexible downtime + */ + const FLEXIBLE = 'flexible'; + + /** @var DateTime downtime start */ + protected $start; + + /** @var DateTime fixed downtime end */ + protected $fixedEnd; + + /** @var DateTime flexible downtime end */ + protected $flexibleEnd; + + /** @var DateInterval flexible downtime duration */ + protected $flexibleDuration; + + /** @var mixed Comment text */ + protected $commentText; + + /** + * Initialize this form + */ + public function init() + { + $this->start = new DateTime(); + + $config = Config::module('monitoring'); + + $this->commentText = $config->get('settings', 'servicedowntime_comment_text'); + $fixedEnd = clone $this->start; + $fixed = $config->get('settings', 'servicedowntime_end_fixed', 'PT1H'); + $this->fixedEnd = $fixedEnd->add(new DateInterval($fixed)); + + $flexibleEnd = clone $this->start; + $flexible = $config->get('settings', 'servicedowntime_end_flexible', 'PT1H'); + $this->flexibleEnd = $flexibleEnd->add(new DateInterval($flexible)); + + $flexibleDuration = $config->get('settings', 'servicedowntime_flexible_duration', 'PT2H'); + $this->flexibleDuration = new DateInterval($flexibleDuration); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::getSubmitLabel() For the method documentation. + */ + public function getSubmitLabel() + { + return $this->translatePlural('Schedule downtime', 'Schedule downtimes', count($this->objects)); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addDescription($this->translate( + 'This command is used to schedule host and service downtimes. During the specified downtime,' + . ' Icinga will not send notifications out about the hosts and services. When the scheduled' + . ' downtime expires, Icinga will send out notifications for the hosts and services as it' + . ' normally would. Scheduled downtimes are preserved across program shutdowns and' + . ' restarts.' + )); + + $isFlexible = (bool) isset($formData['type']) && $formData['type'] === self::FLEXIBLE; + + $this->addElements(array( + array( + 'textarea', + 'comment', + array( + 'required' => true, + 'label' => $this->translate('Comment'), + 'description' => $this->translate( + 'If you work with other administrators, you may find it useful to share information about' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ), + 'attribs' => array('class' => 'autofocus'), + 'value' => $this->commentText + ) + ), + array( + 'dateTimePicker', + 'start', + array( + 'required' => true, + 'label' => $this->translate('Start Time'), + 'description' => $this->translate('Set the start date and time for the downtime.'), + 'value' => $this->start + ) + ), + array( + 'dateTimePicker', + 'end', + array( + 'required' => true, + 'label' => $this->translate('End Time'), + 'description' => $this->translate('Set the end date and time for the downtime.'), + 'preserveDefault' => true, + 'value' => $isFlexible ? $this->flexibleEnd : $this->fixedEnd + ) + ), + array( + 'select', + 'type', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => $this->translate('Type'), + 'description' => $this->translate( + 'If you select the fixed option, the downtime will be in effect between the start and end' + . ' times you specify whereas a flexible downtime starts when the host or service enters a' + . ' problem state sometime between the start and end times you specified and lasts as long' + . ' as the duration time you enter. The duration fields do not apply for fixed downtimes.' + ), + 'multiOptions' => array( + self::FIXED => $this->translate('Fixed'), + self::FLEXIBLE => $this->translate('Flexible') + ), + 'validators' => array( + array( + 'InArray', + true, + array(array(self::FIXED, self::FLEXIBLE)) + ) + ) + ) + ) + )); + $this->addDisplayGroup( + array('start', 'end'), + 'start-end', + array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div')) + ) + ) + ); + if ($isFlexible) { + $this->addElements(array( + array( + 'number', + 'hours', + array( + 'required' => true, + 'label' => $this->translate('Hours'), + 'value' => $this->flexibleDuration->h, + 'min' => -1 + ) + ), + array( + 'number', + 'minutes', + array( + 'required' => true, + 'label' => $this->translate('Minutes'), + 'value' => $this->flexibleDuration->m, + 'min' => -1 + ) + ) + )); + $this->addDisplayGroup( + array('hours', 'minutes'), + 'duration', + array( + 'legend' => $this->translate('Flexible Duration'), + 'description' => $this->translate( + 'Enter here the duration of the downtime. The downtime will be automatically deleted after this' + . ' time expired.' + ), + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div')), + array( + 'Description', + array('tag' => 'span', 'class' => 'description', 'placement' => 'prepend') + ), + 'Fieldset' + ) + ) + ); + } + return $this; + } + + public function scheduleDowntime(ScheduleServiceDowntimeCommand $downtime, Request $request) + { + $downtime + ->setComment($this->getElement('comment')->getValue()) + ->setAuthor($request->getUser()->getUsername()) + ->setStart($this->getElement('start')->getValue()->getTimestamp()) + ->setEnd($this->getElement('end')->getValue()->getTimestamp()); + if ($this->getElement('type')->getValue() === self::FLEXIBLE) { + $downtime->setFixed(false); + $downtime->setDuration( + (float) $this->getElement('hours')->getValue() * 3600 + + (float) $this->getElement('minutes')->getValue() * 60 + ); + } + $this->getTransport($request)->send($downtime); + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + $end = $this->getValue('end')->getTimestamp(); + if ($end <= $this->getValue('start')->getTimestamp()) { + $endElement = $this->_elements['end']; + $endElement->setValue($endElement->getValue()->format($endElement->getFormat())); + $endElement->addError($this->translate('The end time must be greater than the start time')); + return false; + } + + $now = new DateTime; + if ($end <= $now->getTimestamp()) { + $endElement = $this->_elements['end']; + $endElement->setValue($endElement->getValue()->format($endElement->getFormat())); + $endElement->addError($this->translate('A downtime must not be in the past')); + return false; + } + + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $downtime = new ScheduleServiceDowntimeCommand(); + $downtime->setObject($object); + $this->scheduleDowntime($downtime, $this->request); + } + Notification::success($this->translatePlural( + 'Scheduling service downtime..', + 'Scheduling service downtimes..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php new file mode 100644 index 0000000..0d1c393 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php @@ -0,0 +1,110 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Application\Config; +use Icinga\Module\Monitoring\Command\Object\SendCustomNotificationCommand; +use Icinga\Web\Notification; + +/** + * Form to send custom notifications + */ +class SendCustomNotificationCommandForm extends ObjectsCommandForm +{ + /** + * Initialize this form + */ + public function init() + { + $this->addDescription( + $this->translate('This command is used to send custom notifications about hosts or services.') + ); + } + + /** + * {@inheritdoc} + */ + public function getSubmitLabel() + { + return $this->translatePlural('Send custom notification', 'Send custom notifications', count($this->objects)); + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $config = Config::module('monitoring'); + + $this->addElements(array( + array( + 'textarea', + 'comment', + array( + 'required' => true, + 'label' => $this->translate('Comment'), + 'description' => $this->translate( + 'If you work with other administrators, you may find it useful to share information about' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ) + ) + ), + array( + 'checkbox', + 'forced', + array( + 'label' => $this->translate('Forced'), + 'value' => (bool) $config->get('settings', 'custom_notification_forced', false), + 'description' => $this->translate( + 'If you check this option, the notification is sent out regardless of time restrictions and' + . ' whether or not notifications are enabled.' + ) + ) + ) + )); + + if (! $this->getBackend()->isIcinga2()) { + $this->addElement( + 'checkbox', + 'broadcast', + array( + 'label' => $this->translate('Broadcast'), + 'value' => (bool) $config->get('settings', 'custom_notification_broadcast', false), + 'description' => $this->translate( + 'If you check this option, the notification is sent out to all normal and escalated contacts.' + ) + ) + ); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function onSuccess() + { + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + $notification = new SendCustomNotificationCommand(); + $notification + ->setObject($object) + ->setComment($this->getElement('comment')->getValue()) + ->setAuthor($this->request->getUser()->getUsername()) + ->setForced($this->getElement('forced')->isChecked()); + if (($broadcast = $this->getElement('broadcast')) !== null) { + $notification->setBroadcast($broadcast->isChecked()); + } + $this->getTransport($this->request)->send($notification); + } + Notification::success($this->translatePlural( + 'Sending custom notification..', + 'Sending custom notifications..', + count($this->objects) + )); + return true; + } +} diff --git a/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php b/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php new file mode 100644 index 0000000..e4aabb2 --- /dev/null +++ b/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php @@ -0,0 +1,187 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Command\Object; + +use Icinga\Module\Monitoring\Command\Object\ToggleObjectFeatureCommand; +use Icinga\Module\Monitoring\Object\MonitoredObject; +use Icinga\Web\Notification; + +/** + * Form for enabling or disabling features of Icinga objects, i.e. hosts or services + */ +class ToggleObjectFeaturesCommandForm extends ObjectsCommandForm +{ + /** + * Feature to feature spec map + * + * @var string[] + */ + protected $features; + + /** + * Feature to feature status map + * + * @var int[] + */ + protected $featureStatus; + + /** + * {@inheritdoc} + */ + public function init() + { + $this->setUseFormAutosubmit(); + $this->setAttrib('class', self::DEFAULT_CLASSES . ' object-features'); + $features = array( + ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS => array( + 'label' => $this->translate('Active Checks'), + 'permission' => 'monitoring/command/feature/object/active-checks' + ), + ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS => array( + 'label' => $this->translate('Passive Checks'), + 'permission' => 'monitoring/command/feature/object/passive-checks' + ), + ToggleObjectFeatureCommand::FEATURE_OBSESSING => array( + 'label' => $this->translate('Obsessing'), + 'permission' => 'monitoring/command/feature/object/obsessing' + ), + ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS => array( + 'label' => $this->translate('Notifications'), + 'permission' => 'monitoring/command/feature/object/notifications' + ), + ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER => array( + 'label' => $this->translate('Event Handler'), + 'permission' => 'monitoring/command/feature/object/event-handler' + ), + ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION => array( + 'label' => $this->translate('Flap Detection'), + 'permission' => 'monitoring/command/feature/object/flap-detection' + ) + ); + if ($this->getBackend()->isIcinga2()) { + unset($features[ToggleObjectFeatureCommand::FEATURE_OBSESSING]); + } + $this->features = $features; + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + foreach ($this->features as $feature => $spec) { + $options = array( + 'autosubmit' => true, + 'disabled' => $this->hasPermission($spec['permission']) ? null : 'disabled', + 'label' => $spec['label'] + ); + if ($formData[$feature . '_changed']) { + $options['description'] = $this->translate('changed'); + } + if ($formData[$feature] === 2) { + $this->addElement('select', $feature, $options + [ + 'description' => $this->translate('Multiple Values'), + 'filters' => [['Null', ['type' => \Zend_Filter_Null::STRING]]], + 'multiOptions' => [ + '' => $this->translate('Leave Unchanged'), + $this->translate('Disable All'), + $this->translate('Enable All') + ], + 'decorators' => array_merge( + array_slice(static::$defaultElementDecorators, 0, 3), + [['Description', ['tag' => 'span']]], + array_slice(static::$defaultElementDecorators, 4, 1), + [['HtmlTag', ['tag' => 'div', 'class' => 'control-group indeterminate']]] + ) + ]); + } else { + $options['value'] = $formData[$feature]; + $this->addElement('checkbox', $feature, $options); + } + } + } + + /** + * Load feature status + * + * @param MonitoredObject|object $object + * + * @return $this + */ + public function load($object) + { + $featureStatus = array(); + foreach (array_keys($this->features) as $feature) { + $featureStatus[$feature] = $object->{$feature}; + if (isset($object->{$feature . '_changed'})) { + $featureStatus[$feature . '_changed'] = (bool) $object->{$feature . '_changed'}; + } else { + $featureStatus[$feature . '_changed'] = false; + } + } + $this->create($featureStatus); + $this->featureStatus = $featureStatus; + + return $this; + } + + /** + * (non-PHPDoc) + * @see \Icinga\Web\Form::onSuccess() For the method documentation. + */ + public function onSuccess() + { + $notifications = array( + ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS => array( + $this->translate('Enabling active checks..'), + $this->translate('Disabling active checks..') + ), + ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS => array( + $this->translate('Enabling passive checks..'), + $this->translate('Disabling passive checks..') + ), + ToggleObjectFeatureCommand::FEATURE_OBSESSING => array( + $this->translate('Enabling obsessing..'), + $this->translate('Disabling obsessing..') + ), + ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS => array( + $this->translate('Enabling notifications..'), + $this->translate('Disabling notifications..') + ), + ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER => array( + $this->translate('Enabling event handler..'), + $this->translate('Disabling event handler..') + ), + ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION => array( + $this->translate('Enabling flap detection..'), + $this->translate('Disabling flap detection..') + ) + ); + + foreach ($this->getValues() as $feature => $enabled) { + if ($this->getElement($feature)->getAttrib('disabled') !== null + || $enabled === null + || (int) $enabled === (int) $this->featureStatus[$feature] + ) { + continue; + } + foreach ($this->objects as $object) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + if ((bool) $object->{$feature} !== (bool) $enabled) { + $toggleFeature = new ToggleObjectFeatureCommand(); + $toggleFeature + ->setFeature($feature) + ->setObject($object) + ->setEnabled($enabled); + $this->getTransport($this->request)->send($toggleFeature); + } + } + Notification::success( + $notifications[$feature][$enabled ? 0 : 1] + ); + } + + return true; + } +} diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php new file mode 100644 index 0000000..5ed42e1 --- /dev/null +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -0,0 +1,367 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Config; + +use Exception; +use InvalidArgumentException; +use Icinga\Application\Config; +use Icinga\Data\ConfigObject; +use Icinga\Data\ResourceFactory; +use Icinga\Exception\ConfigurationError; +use Icinga\Exception\IcingaException; +use Icinga\Exception\NotFoundError; +use Icinga\Forms\ConfigForm; +use Icinga\Web\Form; + +/** + * Form for managing monitoring backends + */ +class BackendConfigForm extends ConfigForm +{ + /** + * The available monitoring backend resources split by type + * + * @var array + */ + protected $resources; + + /** + * The backend to load when displaying the form for the first time + * + * @var string + */ + protected $backendToLoad; + + /** + * Initialize this form + */ + public function init() + { + $this->setName('form_config_monitoring_backends'); + $this->setSubmitLabel($this->translate('Save Changes')); + } + + /** + * Set the resource configuration to use + * + * @param Config $resourceConfig The resource configuration + * + * @return $this + * + * @throws ConfigurationError In case there are no valid monitoring backend resources + */ + public function setResourceConfig(Config $resourceConfig) + { + $resources = array(); + foreach ($resourceConfig as $name => $resource) { + if ($resource->type === 'db') { + $resources['ido'][$name] = $name; + } + } + + if (empty($resources)) { + throw new ConfigurationError($this->translate( + 'Could not find any valid monitoring backend resources. Please configure a database resource first.' + )); + } + + $this->resources = $resources; + return $this; + } + + /** + * Populate the form with the given backend's config + * + * @param string $name + * + * @return $this + * + * @throws NotFoundError In case no backend with the given name is found + */ + public function load($name) + { + if (! $this->config->hasSection($name)) { + throw new NotFoundError('No monitoring backend called "%s" found', $name); + } + + $this->backendToLoad = $name; + return $this; + } + + /** + * Add a new monitoring backend + * + * The backend to add is identified by the array-key `name'. + * + * @param array $data + * + * @return $this + * + * @throws InvalidArgumentException In case $data does not contain a backend name + * @throws IcingaException In case a backend with the same name already exists + */ + public function add(array $data) + { + if (! isset($data['name'])) { + throw new InvalidArgumentException('Key \'name\' missing'); + } + + $backendName = $data['name']; + if ($this->config->hasSection($backendName)) { + throw new IcingaException( + $this->translate('A monitoring backend with the name "%s" does already exist'), + $backendName + ); + } + + unset($data['name']); + $this->config->setSection($backendName, $data); + return $this; + } + + /** + * Edit a monitoring backend + * + * @param string $name + * @param array $data + * + * @return $this + * + * @throws NotFoundError In case no backend with the given name is found + */ + public function edit($name, array $data) + { + if (! $this->config->hasSection($name)) { + throw new NotFoundError('No monitoring backend called "%s" found', $name); + } + + $backendConfig = $this->config->getSection($name); + if (isset($data['name'])) { + if ($data['name'] !== $name) { + $this->config->removeSection($name); + $name = $data['name']; + } + + unset($data['name']); + } + + $backendConfig->merge($data); + $this->config->setSection($name, $backendConfig); + return $this; + } + + /** + * Remove a monitoring backend + * + * @param string $name + * + * @return $this + */ + public function delete($name) + { + $this->config->removeSection($name); + return $this; + } + + /** + * Create and add elements to this form + * + * @param array $formData + */ + public function createElements(array $formData) + { + $this->addElement( + 'checkbox', + 'disabled', + array( + 'label' => $this->translate('Disable This Backend') + ) + ); + $this->addElement( + 'text', + 'name', + array( + 'required' => true, + 'label' => $this->translate('Backend Name'), + 'description' => $this->translate( + 'The name of this monitoring backend that is used to differentiate it from others' + ) + ) + ); + + $resourceType = isset($formData['type']) ? $formData['type'] : null; + + $resourceTypes = array(); + if ($resourceType === 'ido' || array_key_exists('ido', $this->resources)) { + $resourceTypes['ido'] = 'IDO Backend'; + } + + if ($resourceType === null) { + $resourceType = key($resourceTypes); + } + + $this->addElement( + 'select', + 'type', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => $this->translate('Backend Type'), + 'description' => $this->translate( + 'The type of data source used for retrieving monitoring information' + ), + 'multiOptions' => $resourceTypes + ) + ); + + $this->addElement( + 'select', + 'resource', + array( + 'required' => true, + 'label' => $this->translate('Resource'), + 'description' => $this->translate('The resource to use'), + 'multiOptions' => $this->resources[$resourceType], + 'value' => current($this->resources[$resourceType]), + 'autosubmit' => true + ) + ); + $resourceName = isset($formData['resource']) ? $formData['resource'] : $this->getValue('resource'); + $this->addElement( + 'note', + 'resource_note', + array( + 'escape' => false, + 'value' => sprintf( + '<a href="%1$s" data-base-target="_next" title="%2$s" aria-label="%2$s">%3$s</a>', + $this->getView()->url('config/editresource', array('resource' => $resourceName)), + sprintf($this->translate('Show the configuration of the %s resource'), $resourceName), + $this->translate('Show resource configuration') + ) + ) + ); + + if (isset($formData['skip_validation']) && $formData['skip_validation']) { + // In case another error occured and the checkbox was displayed before + $this->addSkipValidationCheckbox(); + } + } + + /** + * Populate the configuration of the backend to load + */ + public function onRequest() + { + if ($this->backendToLoad) { + $data = $this->config->getSection($this->backendToLoad)->toArray(); + $data['name'] = $this->backendToLoad; + $this->populate($data); + } + } + + /** + * Return whether the given values are valid + * + * @param array $formData The data to validate + * + * @return bool + */ + public function isValid($formData) + { + if (! parent::isValid($formData)) { + return false; + } + + if (($el = $this->getElement('skip_validation')) === null || false === $el->isChecked()) { + $resourceConfig = ResourceFactory::getResourceConfig($this->getValue('resource')); + if (! self::isValidIdoSchema($this, $resourceConfig) + || (! $this->getElement('disabled')->isChecked() + && ! self::isValidIdoInstance($this, $resourceConfig)) + ) { + if ($el === null) { + $this->addSkipValidationCheckbox(); + } + + return false; + } + } + + return true; + } + + /** + * Add a checkbox to the form by which the user can skip the schema validation + */ + protected function addSkipValidationCheckbox() + { + $this->addElement( + 'checkbox', + 'skip_validation', + array( + 'order' => 0, + 'ignore' => true, + 'label' => $this->translate('Skip Validation'), + 'description' => $this->translate( + 'Check this to not to validate the IDO schema of the chosen resource.' + ) + ) + ); + } + + /** + * Return whether the given resource contains a valid IDO schema + * + * @param Form $form + * @param ConfigObject $resourceConfig + * + * @return bool + */ + public static function isValidIdoSchema(Form $form, ConfigObject $resourceConfig) + { + try { + $db = ResourceFactory::createResource($resourceConfig); + $db->select()->from('icinga_dbversion', array('version'))->fetchOne(); + } catch (Exception $_) { + $form->error($form->translate( + 'Cannot find the IDO schema. Please verify that the given database ' + . 'contains the schema and that the configured user has access to it.' + )); + return false; + } + + return true; + } + + /** + * Return whether a single icinga instance is writing to the given resource + * + * @param Form $form + * @param ConfigObject $resourceConfig + * + * @return bool True if it's a single instance, false if none + * or multiple instances are writing to it + */ + public static function isValidIdoInstance(Form $form, ConfigObject $resourceConfig) + { + $db = ResourceFactory::createResource($resourceConfig); + $rowCount = $db->select()->from('icinga_instances')->count(); + + if ($rowCount === 0) { + $form->warning($form->translate( + 'There is currently no icinga instance writing to the IDO. Make sure ' + . 'that a icinga instance is configured and able to write to the IDO.' + )); + return false; + } elseif ($rowCount > 1) { + $form->warning($form->translate( + 'There is currently more than one icinga instance writing to the IDO. You\'ll see all objects from all' + . ' instances without any differentation. If this is not desired, consider setting up a separate IDO' + . ' for each instance.' + )); + return false; + } + + return true; + } +} diff --git a/modules/monitoring/application/forms/Config/SecurityConfigForm.php b/modules/monitoring/application/forms/Config/SecurityConfigForm.php new file mode 100644 index 0000000..d57f985 --- /dev/null +++ b/modules/monitoring/application/forms/Config/SecurityConfigForm.php @@ -0,0 +1,75 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Config; + +use Icinga\Web\Notification; +use Icinga\Forms\ConfigForm; + +/** + * Form for modifying security relevant settings + */ +class SecurityConfigForm extends ConfigForm +{ + /** + * Initialize this form + */ + public function init() + { + $this->setName('form_config_monitoring_security'); + $this->setSubmitLabel($this->translate('Save Changes')); + } + + /** + * @see Form::onSuccess() + */ + public function onSuccess() + { + $this->config->setSection('security', $this->getValues()); + + if ($this->save()) { + Notification::success($this->translate('New security configuration has successfully been stored')); + } else { + return false; + } + } + + /** + * @see Form::onRequest() + */ + public function onRequest() + { + $this->populate($this->config->getSection('security')->toArray()); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'protected_customvars', + array( + 'allowEmpty' => true, + 'attribs' => array('placeholder' => $this->getDefaultProtectedCustomvars()), + 'label' => $this->translate('Protected Custom Variables'), + 'description' => $this->translate( + 'Comma separated case insensitive list of protected custom variables.' + . ' Use * as a placeholder for zero or more wildcard characters.' + . ' Existence of those custom variables will be shown, but their values will be masked.' + ) + ) + ); + } + + /** + * Return the customvars to suggest to protect when none are protected + * + * @return string + */ + public function getDefaultProtectedCustomvars() + { + return '*pw*,*pass*,community'; + } +} diff --git a/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php b/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php new file mode 100644 index 0000000..3d501e0 --- /dev/null +++ b/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php @@ -0,0 +1,75 @@ +<?php +/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Config\Transport; + +use Icinga\Web\Form; + +class ApiTransportForm extends Form +{ + /** + * {@inheritdoc} + */ + public function init() + { + $this->setName('form_config_command_transport_api'); + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + array( + 'text', + 'host', + array( + 'required' => true, + 'label' => $this->translate('Host'), + 'description' => $this->translate( + 'Hostname or address of the remote Icinga instance' + ) + ) + ), + array( + 'number', + 'port', + array( + 'required' => true, + 'preserveDefault' => true, + 'label' => $this->translate('Port'), + 'description' => $this->translate('SSH port to connect to on the remote Icinga instance'), + 'value' => 5665 + ) + ), + array( + 'text', + 'username', + array( + 'required' => true, + 'label' => $this->translate('API Username'), + 'description' => $this->translate( + 'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be' + . ' possible for this user' + ) + ) + ), + array( + 'password', + 'password', + array( + 'required' => true, + 'label' => $this->translate('API Password'), + 'description' => $this->translate( + 'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be' + . ' possible for this user' + ), + 'renderPassword' => true + ) + ) + )); + + return $this; + } +} diff --git a/modules/monitoring/application/forms/Config/Transport/LocalTransportForm.php b/modules/monitoring/application/forms/Config/Transport/LocalTransportForm.php new file mode 100644 index 0000000..15c7357 --- /dev/null +++ b/modules/monitoring/application/forms/Config/Transport/LocalTransportForm.php @@ -0,0 +1,37 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Config\Transport; + +use Icinga\Web\Form; + +class LocalTransportForm extends Form +{ + /** + * (non-PHPDoc) + * @see Form::init() For the method documentation. + */ + public function init() + { + $this->setName('form_config_command_transport_local'); + } + + /** + * (non-PHPDoc) + * @see Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $this->addElement( + 'text', + 'path', + array( + 'required' => true, + 'label' => $this->translate('Command File'), + 'value' => '/var/run/icinga2/cmd/icinga2.cmd', + 'description' => $this->translate('Path to the local Icinga command file') + ) + ); + return $this; + } +} diff --git a/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php b/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php new file mode 100644 index 0000000..7beeacf --- /dev/null +++ b/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php @@ -0,0 +1,185 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Config\Transport; + +use Icinga\Data\ResourceFactory; +use Icinga\Exception\ConfigurationError; +use Icinga\Web\Form; + +class RemoteTransportForm extends Form +{ + /** + * The available resources split by type + * + * @var array + */ + protected $resources; + + /** + * (non-PHPDoc) + * @see Form::init() For the method documentation. + */ + public function init() + { + $this->setName('form_config_command_transport_remote'); + } + + /** + * Load all available ssh identity resources + * + * @return $this + * + * @throws \Icinga\Exception\ConfigurationError + */ + public function loadResources() + { + $resourceConfig = ResourceFactory::getResourceConfigs(); + + $resources = array(); + foreach ($resourceConfig as $name => $resource) { + if ($resource->type === 'ssh') { + $resources['ssh'][$name] = $name; + } + } + + if (empty($resources)) { + throw new ConfigurationError($this->translate('Could not find any valid SSH resources')); + } + + $this->resources = $resources; + + return $this; + } + + /** + * Check whether ssh identity resources exists or not + * + * @return boolean + */ + public function hasResources() + { + $resourceConfig = ResourceFactory::getResourceConfigs(); + + foreach ($resourceConfig as $name => $resource) { + if ($resource->type === 'ssh') { + return true; + } + } + return false; + } + + /** + * (non-PHPDoc) + * @see Form::createElements() For the method documentation. + */ + public function createElements(array $formData = array()) + { + $useResource = false; + + if ($this->hasResources()) { + $useResource = isset($formData['use_resource']) + ? $formData['use_resource'] : $this->getValue('use_resource'); + + $this->addElement( + 'checkbox', + 'use_resource', + array( + 'label' => $this->translate('Use SSH Identity'), + 'description' => $this->translate('Make use of the ssh identity resource'), + 'autosubmit' => true, + 'ignore' => true + ) + ); + } + + if ($useResource) { + $this->loadResources(); + + $decorators = static::$defaultElementDecorators; + array_pop($decorators); // Removes the HtmlTag decorator + + $this->addElement( + 'select', + 'resource', + array( + 'required' => true, + 'label' => $this->translate('SSH Identity'), + 'description' => $this->translate('The resource to use'), + 'decorators' => $decorators, + 'multiOptions' => $this->resources['ssh'], + 'value' => current($this->resources['ssh']), + 'autosubmit' => false + ) + ); + $resourceName = isset($formData['resource']) ? $formData['resource'] : $this->getValue('resource'); + $this->addElement( + 'note', + 'resource_note', + array( + 'escape' => false, + 'decorators' => $decorators, + 'value' => sprintf( + '<a href="%1$s" data-base-target="_next" title="%2$s" aria-label="%2$s">%3$s</a>', + $this->getView()->url('config/editresource', array('resource' => $resourceName)), + sprintf($this->translate('Show the configuration of the %s resource'), $resourceName), + $this->translate('Show resource configuration') + ) + ) + ); + } + + $this->addElements(array( + array( + 'text', + 'host', + array( + 'required' => true, + 'label' => $this->translate('Host'), + 'description' => $this->translate( + 'Hostname or address of the remote Icinga instance' + ) + ) + ), + array( + 'number', + 'port', + array( + 'required' => true, + 'preserveDefault' => true, + 'label' => $this->translate('Port'), + 'description' => $this->translate('SSH port to connect to on the remote Icinga instance'), + 'value' => 22 + ) + ) + )); + + if (! $useResource) { + $this->addElement( + 'text', + 'user', + array( + 'required' => true, + 'label' => $this->translate('User'), + 'description' => $this->translate( + 'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be' + . ' possible for this user' + ) + ) + ); + } + + $this->addElement( + 'text', + 'path', + array( + 'required' => true, + 'label' => $this->translate('Command File'), + 'value' => '/var/run/icinga2/cmd/icinga2.cmd', + 'description' => $this->translate('Path to the Icinga command file on the remote Icinga instance') + ) + ); + + return $this; + } +} diff --git a/modules/monitoring/application/forms/Config/TransportConfigForm.php b/modules/monitoring/application/forms/Config/TransportConfigForm.php new file mode 100644 index 0000000..c68e63d --- /dev/null +++ b/modules/monitoring/application/forms/Config/TransportConfigForm.php @@ -0,0 +1,392 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Config; + +use Icinga\Data\ConfigObject; +use Icinga\Module\Monitoring\Command\Transport\CommandTransport; +use Icinga\Module\Monitoring\Exception\CommandTransportException; +use InvalidArgumentException; +use Icinga\Application\Platform; +use Icinga\Exception\IcingaException; +use Icinga\Exception\NotFoundError; +use Icinga\Forms\ConfigForm; +use Icinga\Module\Monitoring\Command\Transport\ApiCommandTransport; +use Icinga\Module\Monitoring\Command\Transport\LocalCommandFile; +use Icinga\Module\Monitoring\Command\Transport\RemoteCommandFile; +use Icinga\Module\Monitoring\Forms\Config\Transport\ApiTransportForm; +use Icinga\Module\Monitoring\Forms\Config\Transport\LocalTransportForm; +use Icinga\Module\Monitoring\Forms\Config\Transport\RemoteTransportForm; + +/** + * Form for managing command transports + */ +class TransportConfigForm extends ConfigForm +{ + /** + * The transport to load when displaying the form for the first time + * + * @var string + */ + protected $transportToLoad; + + /** + * The names of all available Icinga instances + * + * @var array + */ + protected $instanceNames; + + /** + * @var bool + */ + protected $validatePartial = true; + + /** + * Initialize this form + */ + public function init() + { + $this->setName('form_config_command_transports'); + $this->setSubmitLabel($this->translate('Save Changes')); + } + + /** + * Set the names of all available Icinga instances + * + * @param array $names + * + * @return $this + */ + public function setInstanceNames(array $names) + { + $this->instanceNames = $names; + return $this; + } + + /** + * Return the names of all available Icinga instances + * + * @return array + */ + public function getInstanceNames() + { + return $this->instanceNames ?: array(); + } + + /** + * Return a form object for the given transport type + * + * @param string $type The transport type for which to return a form + * + * @return \Icinga\Web\Form + * + * @throws InvalidArgumentException In case the given transport type is invalid + */ + public function getTransportForm($type) + { + switch (strtolower($type)) { + case LocalCommandFile::TRANSPORT: + return new LocalTransportForm(); + case RemoteCommandFile::TRANSPORT: + return new RemoteTransportForm(); + case ApiCommandTransport::TRANSPORT: + return new ApiTransportForm(); + default: + throw new InvalidArgumentException( + sprintf($this->translate('Invalid command transport type "%s" given'), $type) + ); + } + } + + /** + * Populate the form with the given transport's config + * + * @param string $name + * + * @return $this + * + * @throws NotFoundError In case no transport with the given name is found + */ + public function load($name) + { + if (! $this->config->hasSection($name)) { + throw new NotFoundError('No command transport called "%s" found', $name); + } + + $this->transportToLoad = $name; + return $this; + } + + /** + * Add a new command transport + * + * The transport to add is identified by the array-key `name'. + * + * @param array $data + * + * @return $this + * + * @throws InvalidArgumentException In case $data does not contain a transport name + * @throws IcingaException In case a transport with the same name already exists + */ + public function add(array $data) + { + if (! isset($data['name'])) { + throw new InvalidArgumentException('Key \'name\' missing'); + } + + $transportName = $data['name']; + if ($this->config->hasSection($transportName)) { + throw new IcingaException( + $this->translate('A command transport with the name "%s" does already exist'), + $transportName + ); + } + + unset($data['name']); + $this->config->setSection($transportName, $data); + return $this; + } + + /** + * Edit an existing command transport + * + * @param string $name + * @param array $data + * + * @return $this + * + * @throws NotFoundError In case no transport with the given name is found + */ + public function edit($name, array $data) + { + if (! $this->config->hasSection($name)) { + throw new NotFoundError('No command transport called "%s" found', $name); + } + + $transportConfig = $this->config->getSection($name); + if (isset($data['name'])) { + if ($data['name'] !== $name) { + $this->config->removeSection($name); + $name = $data['name']; + } + + unset($data['name']); + } + + $transportConfig->merge($data); + $this->config->setSection($name, $transportConfig); + return $this; + } + + /** + * Remove a command transport + * + * @param string $name + * + * @return $this + */ + public function delete($name) + { + $this->config->removeSection($name); + return $this; + } + + /** + * Create and add elements to this form + * + * @param array $formData + */ + public function createElements(array $formData) + { + $instanceNames = $this->getInstanceNames(); + if (count($instanceNames) > 1) { + $options = array('none' => $this->translate('None', 'command transport instance association')); + $this->addElement( + 'select', + 'instance', + array( + 'label' => $this->translate('Instance Link'), + 'description' => $this->translate( + 'The name of the Icinga instance this transport should exclusively transfer commands to.' + ), + 'multiOptions' => array_merge($options, array_combine($instanceNames, $instanceNames)) + ) + ); + } + + $this->addElement( + 'text', + 'name', + array( + 'required' => true, + 'label' => $this->translate('Transport Name'), + 'description' => $this->translate( + 'The name of this command transport that is used to differentiate it from others' + ) + ) + ); + + $transportTypes = array( + ApiCommandTransport::TRANSPORT => $this->translate('Icinga 2 API'), + LocalCommandFile::TRANSPORT => $this->translate('Local Command File'), + RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File') + ); + if (! Platform::extensionLoaded('curl')) { + unset($transportTypes[ApiCommandTransport::TRANSPORT]); + } + + $transportType = isset($formData['transport']) ? $formData['transport'] : null; + if ($transportType === null) { + $transportType = key($transportTypes); + } + + $this->addElements(array( + array( + 'select', + 'transport', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => $this->translate('Transport Type'), + 'multiOptions' => $transportTypes + ) + ) + )); + + $this->addSubForm($this->getTransportForm($transportType)->create($formData), 'transport_form'); + } + + /** + * Add a submit button to this form and one to manually validate the configuration + * + * Calls parent::addSubmitButton() to add the submit button. + * + * @return $this + */ + public function addSubmitButton() + { + parent::addSubmitButton(); + + if ($this->getSubForm('transport_form') instanceof ApiTransportForm) { + $btnSubmit = $this->getElement('btn_submit'); + + if ($btnSubmit !== null) { + // In the setup wizard $this is being used as a subform which doesn't have a submit button. + $this->addElement( + 'submit', + 'transport_validation', + array( + 'ignore' => true, + 'label' => $this->translate('Validate Configuration'), + 'data-progress-label' => $this->translate('Validation In Progress'), + 'decorators' => array('ViewHelper') + ) + ); + + $this->setAttrib('data-progress-element', 'transport-progress'); + $this->addElement( + 'note', + 'transport-progress', + array( + 'decorators' => array( + 'ViewHelper', + array('Spinner', array('id' => 'transport-progress')) + ) + ) + ); + + $elements = array('transport_validation', 'transport-progress'); + + $btnSubmit->setDecorators(array('ViewHelper')); + array_unshift($elements, 'btn_submit'); + + $this->addDisplayGroup( + $elements, + 'submit_validation', + array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls')) + ) + ) + ); + } + } + + return $this; + } + + /** + * Populate the configuration of the transport to load + */ + public function onRequest() + { + if ($this->transportToLoad) { + $data = $this->config->getSection($this->transportToLoad)->toArray(); + $data['name'] = $this->transportToLoad; + $this->populate($data); + } + } + + /** + * {@inheritdoc} + */ + public function isValidPartial(array $formData) + { + $isValidPartial = parent::isValidPartial($formData); + + $transportValidation = $this->getElement('transport_validation'); + if ($transportValidation !== null && $transportValidation->isChecked() && $this->isValid($formData)) { + $this->info($this->translate('The configuration has been successfully validated.')); + } + + return $isValidPartial; + } + + /** + * {@inheritdoc} + */ + public function isValid($formData) + { + if (! parent::isValid($formData)) { + return false; + } + + if ($this->getSubForm('transport_form') instanceof ApiTransportForm) { + if (! isset($formData['transport_validation']) + && isset($formData['force_creation']) && $formData['force_creation'] + ) { + // ignore any validation result + return true; + } + + try { + CommandTransport::createTransport(new ConfigObject($this->getValues()))->probe(); + } catch (CommandTransportException $e) { + $this->error(sprintf( + $this->translate('Failed to successfully validate the configuration: %s'), + $e->getMessage() + )); + + $this->addElement( + 'checkbox', + 'force_creation', + array( + 'order' => 0, + 'ignore' => true, + 'label' => $this->translate('Force Changes'), + 'description' => $this->translate( + 'Check this box to enforce changes without connectivity validation' + ) + ) + ); + + return false; + } + } + + return true; + } +} diff --git a/modules/monitoring/application/forms/Config/TransportReorderForm.php b/modules/monitoring/application/forms/Config/TransportReorderForm.php new file mode 100644 index 0000000..f3efe4c --- /dev/null +++ b/modules/monitoring/application/forms/Config/TransportReorderForm.php @@ -0,0 +1,87 @@ +<?php +/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Config; + +use Icinga\Application\Config; +use Icinga\Web\Form; +use Icinga\Web\Notification; + +/** + * Form for reordering command transports + */ +class TransportReorderForm extends Form +{ + /** + * Initialize this form + */ + public function init() + { + $this->setName('form_reorder_command_transports'); + $this->setViewScript('form/reorder-command-transports.phtml'); + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData) + { + // This adds just a dummy element to be able to utilize Form::getValue as part of onSuccess() + $this->addElement( + 'hidden', + 'transport_newpos', + array( + 'required' => true, + 'validators' => array( + array( + 'validator' => 'regex', + 'options' => array( + 'pattern' => '/\A\d+\|/' + ) + ) + ) + ) + ); + } + + /** + * Update the command transport order and save the configuration + */ + public function onSuccess() + { + list($position, $transportName) = explode('|', $this->getValue('transport_newpos'), 2); + $config = $this->getConfig(); + if (! $config->hasSection($transportName)) { + Notification::error(sprintf($this->translate('Command transport "%s" not found'), $transportName)); + return false; + } + + if ($config->count() > 1) { + $sections = $config->keys(); + array_splice($sections, array_search($transportName, $sections, true), 1); + array_splice($sections, $position, 0, array($transportName)); + + $sectionsInNewOrder = array(); + foreach ($sections as $section) { + $sectionsInNewOrder[$section] = $config->getSection($section); + $config->removeSection($section); + } + foreach ($sectionsInNewOrder as $name => $options) { + $config->setSection($name, $options); + } + + $config->saveIni(); + Notification::success($this->translate('Command transport order updated')); + } + } + + /** + * Get the command transports config + * + * @return Config + */ + public function getConfig() + { + return Config::module('monitoring', 'commandtransports'); + } +} diff --git a/modules/monitoring/application/forms/EventOverviewForm.php b/modules/monitoring/application/forms/EventOverviewForm.php new file mode 100644 index 0000000..db1511c --- /dev/null +++ b/modules/monitoring/application/forms/EventOverviewForm.php @@ -0,0 +1,157 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms; + +use Icinga\Web\Url; +use Icinga\Web\Form; +use Icinga\Data\Filter\Filter; + +/** + * Configure the filter for the event overview + */ +class EventOverviewForm extends Form +{ + /** + * {@inheritdoc} + */ + public function init() + { + $this->setName('form_event_overview'); + $this->setDecorators(array( + 'FormElements', + array('HtmlTag', array('tag' => 'div', 'class' => 'hbox')), + 'Form' + )); + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData) + { + $decorators = array( + array('Label', array('class' => 'optional')), + 'ViewHelper', + array('HtmlTag', array('tag' => 'div', 'class' => 'hbox-item optionbox')), + ); + + $url = Url::fromRequest()->getAbsoluteUrl(); + $this->addElement( + 'checkbox', + 'statechange', + array( + 'label' => $this->translate('State Changes'), + 'class' => 'autosubmit', + 'decorators' => $decorators, + 'value' => strpos($url, $this->stateChangeFilter()->toQueryString()) === false ? 0 : 1 + ) + ); + $this->addElement( + 'checkbox', + 'downtime', + array( + 'label' => $this->translate('Downtimes'), + 'class' => 'autosubmit', + 'decorators' => $decorators, + 'value' => strpos($url, $this->downtimeFilter()->toQueryString()) === false ? 0 : 1 + ) + ); + $this->addElement( + 'checkbox', + 'comment', + array( + 'label' => $this->translate('Comments'), + 'class' => 'autosubmit', + 'decorators' => $decorators, + 'value' => strpos($url, $this->commentFilter()->toQueryString()) === false ? 0 : 1 + ) + ); + $this->addElement( + 'checkbox', + 'notification', + array( + 'label' => $this->translate('Notifications'), + 'class' => 'autosubmit', + 'decorators' => $decorators, + 'value' => strpos($url, $this->notificationFilter()->toQueryString()) === false ? 0 : 1 + ) + ); + $this->addElement( + 'checkbox', + 'flapping', + array( + 'label' => $this->translate('Flapping'), + 'class' => 'autosubmit', + 'decorators' => $decorators, + 'value' => strpos($url, $this->flappingFilter()->toQueryString()) === false ? 0 : 1 + ) + ); + } + + /** + * Return the corresponding filter-object + * + * @returns Filter + */ + public function getFilter() + { + $filters = array(); + if ($this->getValue('statechange', 1)) { + $filters[] = $this->stateChangeFilter(); + } + if ($this->getValue('comment', 1)) { + $filters[] = $this->commentFilter(); + } + if ($this->getValue('notification', 1)) { + $filters[] = $this->notificationFilter(); + } + if ($this->getValue('downtime', 1)) { + $filters[] = $this->downtimeFilter(); + } + if ($this->getValue('flapping', 1)) { + $filters[] = $this->flappingFilter(); + } + return Filter::matchAny($filters); + } + + public function stateChangeFilter() + { + return Filter::matchAny( + Filter::expression('type', '=', 'hard_state'), + Filter::expression('type', '=', 'soft_state') + ); + } + + public function commentFilter() + { + return Filter::matchAny( + Filter::expression('type', '=', 'comment'), + Filter::expression('type', '=', 'comment_deleted'), + Filter::expression('type', '=', 'dt_comment'), + Filter::expression('type', '=', 'dt_comment_deleted'), + Filter::expression('type', '=', 'ack') + ); + } + + public function notificationFilter() + { + return Filter::expression('type', '=', 'notify'); + } + + public function downtimeFilter() + { + return Filter::matchAny( + Filter::expression('type', '=', 'downtime_start'), + Filter::expression('type', '=', 'downtime_end') + ); + } + + public function flappingFilter() + { + return Filter::matchAny( + Filter::expression('type', '=', 'flapping'), + Filter::expression('type', '=', 'flapping_deleted') + ); + } +} diff --git a/modules/monitoring/application/forms/Navigation/ActionForm.php b/modules/monitoring/application/forms/Navigation/ActionForm.php new file mode 100644 index 0000000..81d5588 --- /dev/null +++ b/modules/monitoring/application/forms/Navigation/ActionForm.php @@ -0,0 +1,79 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Navigation; + +use Icinga\Data\Filter\Filter; +use Icinga\Exception\QueryException; +use Icinga\Forms\Navigation\NavigationItemForm; + +class ActionForm extends NavigationItemForm +{ + /** + * {@inheritdoc} + */ + public function createElements(array $formData) + { + parent::createElements($formData); + + $this->addElement( + 'text', + 'filter', + array( + 'allowEmpty' => true, + 'label' => $this->translate('Filter'), + 'description' => $this->translate( + 'Display this action only for objects matching this filter. Leave it blank' + . ' if you want this action being displayed regardless of the object' + ) + ) + ); + } + + /** + * {@inheritdoc} + */ + public function isValid($formData) + { + if (! parent::isValid($formData)) { + return false; + } + + if (($filterString = $this->getValue('filter')) !== null) { + $filter = Filter::matchAll(); + $filter->setAllowedFilterColumns(array( + 'host_name', + 'hostgroup_name', + 'instance_name', + 'service_description', + 'servicegroup_name', + 'contact_name', + 'contactgroup_name', + function ($c) { + return preg_match('/^_(?:host|service)_/', $c); + } + )); + + try { + $filter->addFilter(Filter::fromQueryString($filterString)); + } catch (QueryException $_) { + $this->getElement('filter')->addError(sprintf( + $this->translate('Invalid filter provided. You can only use the following columns: %s'), + implode(', ', array( + 'instance_name', + 'host_name', + 'hostgroup_name', + 'service_description', + 'servicegroup_name', + 'contact_name', + 'contactgroup_name', + '_(host|service)_<customvar-name>' + )) + )); + return false; + } + } + + return true; + } +} diff --git a/modules/monitoring/application/forms/Navigation/HostActionForm.php b/modules/monitoring/application/forms/Navigation/HostActionForm.php new file mode 100644 index 0000000..da237d4 --- /dev/null +++ b/modules/monitoring/application/forms/Navigation/HostActionForm.php @@ -0,0 +1,8 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Navigation; + +class HostActionForm extends ActionForm +{ +} diff --git a/modules/monitoring/application/forms/Navigation/ServiceActionForm.php b/modules/monitoring/application/forms/Navigation/ServiceActionForm.php new file mode 100644 index 0000000..68314d1 --- /dev/null +++ b/modules/monitoring/application/forms/Navigation/ServiceActionForm.php @@ -0,0 +1,8 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Navigation; + +class ServiceActionForm extends ActionForm +{ +} diff --git a/modules/monitoring/application/forms/Setup/BackendPage.php b/modules/monitoring/application/forms/Setup/BackendPage.php new file mode 100644 index 0000000..d5c7efb --- /dev/null +++ b/modules/monitoring/application/forms/Setup/BackendPage.php @@ -0,0 +1,51 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Setup; + +use Icinga\Web\Form; +use Icinga\Application\Platform; + +class BackendPage extends Form +{ + public function init() + { + $this->setName('setup_monitoring_backend'); + $this->setTitle($this->translate('Monitoring Backend', 'setup.page.title')); + $this->addDescription($this->translate( + 'Please configure below how Icinga Web 2 should retrieve monitoring information.' + )); + } + + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'name', + array( + 'required' => true, + 'value' => 'icinga', + 'label' => $this->translate('Backend Name'), + 'description' => $this->translate('The identifier of this backend') + ) + ); + + $resourceTypes = array(); + if (Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) { + $resourceTypes['ido'] = 'IDO'; + } + + $this->addElement( + 'select', + 'type', + array( + 'required' => true, + 'label' => $this->translate('Backend Type'), + 'description' => $this->translate( + 'The data source used for retrieving monitoring information' + ), + 'multiOptions' => $resourceTypes + ) + ); + } +} diff --git a/modules/monitoring/application/forms/Setup/IdoResourcePage.php b/modules/monitoring/application/forms/Setup/IdoResourcePage.php new file mode 100644 index 0000000..d648579 --- /dev/null +++ b/modules/monitoring/application/forms/Setup/IdoResourcePage.php @@ -0,0 +1,188 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Setup; + +use Icinga\Data\ConfigObject; +use Icinga\Forms\Config\ResourceConfigForm; +use Icinga\Forms\Config\Resource\DbResourceForm; +use Icinga\Web\Form; +use Icinga\Module\Monitoring\Forms\Config\BackendConfigForm; +use Icinga\Module\Setup\Utils\DbTool; + +class IdoResourcePage extends Form +{ + /** + * Initialize this form + */ + public function init() + { + $this->setName('setup_monitoring_ido'); + $this->setTitle($this->translate('Monitoring IDO Resource', 'setup.page.title')); + $this->addDescription($this->translate( + 'Please fill out the connection details below to access the IDO database of your monitoring environment.' + )); + $this->setValidatePartial(true); + } + + /** + * Create and add elements to this form + * + * @param array $formData + */ + public function createElements(array $formData) + { + $this->addElement( + 'hidden', + 'type', + array( + 'required' => true, + 'value' => 'db' + ) + ); + + if (isset($formData['skip_validation']) && $formData['skip_validation']) { + // In case another error occured and the checkbox was displayed before + $this->addSkipValidationCheckbox(); + } else { + $this->addElement( + 'hidden', + 'skip_validation', + array( + 'required' => true, + 'value' => 0 + ) + ); + } + + $dbResourceForm = new DbResourceForm(); + $this->addElements($dbResourceForm->createElements($formData)->getElements()); + $this->getElement('name')->setValue('icinga_ido'); + } + + /** + * Return whether the given values are valid + * + * @param array $formData The data to validate + * + * @return bool + */ + public function isValid($formData) + { + if (! parent::isValid($formData)) { + return false; + } + + if (! isset($formData['skip_validation']) || !$formData['skip_validation']) { + if (! $this->validateConfiguration()) { + $this->addSkipValidationCheckbox(); + return false; + } + } + + return true; + } + + /** + * Run the configured backend's inspection checks and show the result, if necessary + * + * This will only run any validation if the user pushed the 'backend_validation' button. + * + * @param array $formData + * + * @return bool + */ + public function isValidPartial(array $formData) + { + if (isset($formData['backend_validation']) && parent::isValid($formData)) { + if (! $this->validateConfiguration(true)) { + return false; + } + + $this->info($this->translate('The configuration has been successfully validated.')); + } elseif (! isset($formData['backend_validation'])) { + // This is usually done by isValid(Partial), but as we're not calling any of these... + $this->populate($formData); + } + + return true; + } + + /** + * Return whether the configuration is valid + * + * @param bool $showLog Whether to show the validation log + * + * @return bool + */ + protected function validateConfiguration($showLog = false) + { + $inspection = ResourceConfigForm::inspectResource($this); + if ($inspection !== null) { + if ($showLog) { + $join = function ($e) use (&$join) { + return is_string($e) ? $e : join("\n", array_map($join, $e)); + }; + $this->addElement( + 'note', + 'inspection_output', + array( + 'order' => 0, + 'value' => '<strong>' . $this->translate('Validation Log') . "</strong>\n\n" + . join("\n", array_map($join, $inspection->toArray())), + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'pre', 'class' => 'log-output')), + ) + ) + ); + } + + if ($inspection->hasError()) { + $this->error(sprintf( + $this->translate('Failed to successfully validate the configuration: %s'), + $inspection->getError() + )); + return false; + } + } + + $configObject = new ConfigObject($this->getValues()); + if (! BackendConfigForm::isValidIdoSchema($this, $configObject) + || !BackendConfigForm::isValidIdoInstance($this, $configObject) + ) { + return false; + } + + if ($this->getValue('db') === 'pgsql') { + $db = new DbTool($this->getValues()); + $version = $db->connectToDb()->getServerVersion(); + if (version_compare($version, '9.1', '<')) { + $this->error(sprintf( + $this->translate('The server\'s version %s is too old. The minimum required version is %s.'), + $version, + '9.1' + )); + return false; + } + } + + return true; + } + + /** + * Add a checkbox to the form by which the user can skip the configuration validation + */ + protected function addSkipValidationCheckbox() + { + $this->addElement( + 'checkbox', + 'skip_validation', + array( + 'required' => true, + 'label' => $this->translate('Skip Validation'), + 'description' => $this->translate('Check this to not to validate the configuration') + ) + ); + } +} diff --git a/modules/monitoring/application/forms/Setup/SecurityPage.php b/modules/monitoring/application/forms/Setup/SecurityPage.php new file mode 100644 index 0000000..999103c --- /dev/null +++ b/modules/monitoring/application/forms/Setup/SecurityPage.php @@ -0,0 +1,27 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Setup; + +use Icinga\Web\Form; +use Icinga\Module\Monitoring\Forms\Config\SecurityConfigForm; + +class SecurityPage extends Form +{ + public function init() + { + $this->setName('setup_monitoring_security'); + $this->setTitle($this->translate('Monitoring Security', 'setup.page.title')); + $this->addDescription($this->translate( + 'To protect your monitoring environment against prying eyes please fill out the settings below.' + )); + } + + public function createElements(array $formData) + { + $securityConfigForm = new SecurityConfigForm(); + $securityConfigForm->createElements($formData); + $this->addElements($securityConfigForm->getElements()); + $this->getElement('protected_customvars')->setValue($securityConfigForm->getDefaultProtectedCustomvars()); + } +} diff --git a/modules/monitoring/application/forms/Setup/TransportPage.php b/modules/monitoring/application/forms/Setup/TransportPage.php new file mode 100644 index 0000000..9d0760a --- /dev/null +++ b/modules/monitoring/application/forms/Setup/TransportPage.php @@ -0,0 +1,55 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Setup; + +use Icinga\Web\Form; +use Icinga\Module\Monitoring\Forms\Config\TransportConfigForm; + +class TransportPage extends Form +{ + public function init() + { + $this->setName('setup_command_transport'); + $this->setTitle($this->translate('Command Transport', 'setup.page.title')); + $this->addDescription($this->translate( + 'Please define below how you want to send commands to your monitoring instance.' + )); + $this->setValidatePartial(true); + } + + public function createElements(array $formData) + { + $transportConfigForm = new TransportConfigForm(); + $this->addSubForm($transportConfigForm, 'transport_form'); + $transportConfigForm->create($formData); + $transportConfigForm->removeElement('instance'); + $transportConfigForm->getElement('name')->setValue('icinga2'); + } + + public function getValues($suppressArrayNotation = false) + { + return $this->getSubForm('transport_form')->getValues($suppressArrayNotation); + } + + /** + * Run the configured backend's inspection checks and show the result, if necessary + * + * This will only run any validation if the user pushed the 'transport_validation' button. + * + * @param array $formData + * + * @return bool + */ + public function isValidPartial(array $formData) + { + if (isset($formData['transport_validation']) && parent::isValid($formData)) { + $this->info($this->translate('The configuration has been successfully validated.')); + } elseif (! isset($formData['transport_validation'])) { + // This is usually done by isValid(Partial), but as we're not calling any of these... + $this->populate($formData); + } + + return true; + } +} diff --git a/modules/monitoring/application/forms/Setup/WelcomePage.php b/modules/monitoring/application/forms/Setup/WelcomePage.php new file mode 100644 index 0000000..aa78db5 --- /dev/null +++ b/modules/monitoring/application/forms/Setup/WelcomePage.php @@ -0,0 +1,63 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms\Setup; + +use Icinga\Web\Form; + +class WelcomePage extends Form +{ + public function init() + { + $this->setName('setup_monitoring_welcome'); + } + + public function createElements(array $formData) + { + $this->addElement( + 'note', + 'welcome', + array( + 'value' => $this->translate( + 'Welcome to the configuration of the monitoring module for Icinga Web 2!' + ), + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'h2')) + ) + ) + ); + + $this->addElement( + 'note', + 'core_hint', + array( + 'value' => $this->translate('This is the core module for Icinga Web 2.'), + 'decorators' => array('ViewHelper') + ) + ); + + $this->addElement( + 'note', + 'description', + array( + 'value' => $this->translate( + 'It offers various status and reporting views with powerful filter capabilities that allow' + . ' you to keep track of the most important events in your monitoring environment.' + ), + 'decorators' => array('ViewHelper') + ) + ); + + $this->addDisplayGroup( + array('core_hint', 'description'), + 'info', + array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div', 'class' => 'info')) + ) + ) + ); + } +} diff --git a/modules/monitoring/application/forms/StatehistoryForm.php b/modules/monitoring/application/forms/StatehistoryForm.php new file mode 100644 index 0000000..3a7c10d --- /dev/null +++ b/modules/monitoring/application/forms/StatehistoryForm.php @@ -0,0 +1,141 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Monitoring\Forms; + +use Icinga\Web\Form; +use Icinga\Data\Filter\Filter; + +/** + * Configure the filter for the event grid + */ +class StatehistoryForm extends Form +{ + /** + * {@inheritdoc} + */ + public function init() + { + $this->setName('form_event_overview'); + $this->setSubmitLabel($this->translate('Apply')); + } + + /** + * Return the corresponding filter-object + * + * @returns Filter + */ + public function getFilter() + { + $baseFilter = Filter::matchAny( + Filter::expression('type', '=', 'hard_state') + ); + + if ($this->getValue('objecttype', 'hosts') === 'hosts') { + $objectTypeFilter = Filter::expression('object_type', '=', 'host'); + } else { + $objectTypeFilter = Filter::expression('object_type', '=', 'service'); + } + + $states = array( + 'cnt_down_hard' => Filter::expression('state', '=', '1'), + 'cnt_unreachable_hard' => Filter::expression('state', '=', '2'), + 'cnt_up' => Filter::expression('state', '=', '0'), + 'cnt_critical_hard' => Filter::expression('state', '=', '2'), + 'cnt_warning_hard' => Filter::expression('state', '=', '1'), + 'cnt_unknown_hard' => Filter::expression('state', '=', '3'), + 'cnt_ok' => Filter::expression('state', '=', '0') + ); + $state = $this->getValue('state', 'cnt_critical_hard'); + $stateFilter = $states[$state]; + if (in_array($state, array('cnt_ok', 'cnt_up'))) { + return Filter::matchAll($objectTypeFilter, $stateFilter); + } + return Filter::matchAll($baseFilter, $objectTypeFilter, $stateFilter); + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData) + { + $this->addElement( + 'select', + 'from', + array( + 'label' => $this->translate('From'), + 'value' => $this->getRequest()->getParam('from', strtotime('3 months ago')), + 'multiOptions' => array( + strtotime('midnight 3 months ago') => $this->translate('3 Months'), + strtotime('midnight 4 months ago') => $this->translate('4 Months'), + strtotime('midnight 8 months ago') => $this->translate('8 Months'), + strtotime('midnight 12 months ago') => $this->translate('1 Year'), + strtotime('midnight 24 months ago') => $this->translate('2 Years') + ) + ) + ); + $this->addElement( + 'select', + 'to', + array( + 'label' => $this->translate('To'), + 'value' => $this->getRequest()->getParam('to', time()), + 'multiOptions' => array( + time() => $this->translate('Today') + ) + ) + ); + + $objectType = $this->getRequest()->getParam('objecttype', 'services'); + $this->addElement( + 'select', + 'objecttype', + array( + 'label' => $this->translate('Object type'), + 'value' => $objectType, + 'multiOptions' => array( + 'services' => $this->translate('Services'), + 'hosts' => $this->translate('Hosts') + ) + ) + ); + if ($objectType === 'services') { + $serviceState = $this->getRequest()->getParam('state', 'cnt_critical_hard'); + if (in_array($serviceState, array('cnt_down_hard', 'cnt_unreachable_hard', 'cnt_up'))) { + $serviceState = 'cnt_critical_hard'; + } + $this->addElement( + 'select', + 'state', + array( + 'label' => $this->translate('State'), + 'value' => $serviceState, + 'multiOptions' => array( + 'cnt_critical_hard' => $this->translate('Critical'), + 'cnt_warning_hard' => $this->translate('Warning'), + 'cnt_unknown_hard' => $this->translate('Unknown'), + 'cnt_ok' => $this->translate('Ok') + ) + ) + ); + } else { + $hostState = $this->getRequest()->getParam('state', 'cnt_down_hard'); + if (in_array($hostState, array('cnt_ok', 'cnt_critical_hard', 'cnt_warning', 'cnt_unknown'))) { + $hostState = 'cnt_down_hard'; + } + $this->addElement( + 'select', + 'state', + array( + 'label' => $this->translate('State'), + 'value' => $hostState, + 'multiOptions' => array( + 'cnt_up' => $this->translate('Up'), + 'cnt_down_hard' => $this->translate('Down'), + 'cnt_unreachable_hard' => $this->translate('Unreachable') + ) + ) + ); + } + } +} diff --git a/modules/monitoring/application/views/helpers/CheckPerformance.php b/modules/monitoring/application/views/helpers/CheckPerformance.php new file mode 100644 index 0000000..feac4d8 --- /dev/null +++ b/modules/monitoring/application/views/helpers/CheckPerformance.php @@ -0,0 +1,50 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +/** + * Convert check summary data into a simple usable stdClass + */ +class Zend_View_Helper_CheckPerformance extends Zend_View_Helper_Abstract +{ + /** + * Create dispatch instance + * + * @return $this + */ + public function checkPerformance() + { + return $this; + } + + /** + * Create a condensed row of object data + * + * @param array $results Array of stdClass + * + * @return stdClass Condensed row + */ + public function create(array $results) + { + $out = new stdClass(); + $out->host_passive_count = 0; + $out->host_passive_latency_avg = 0; + $out->host_passive_execution_avg = 0; + $out->service_passive_count = 0; + $out->service_passive_latency_avg = 0; + $out->service_passive_execution_avg = 0; + $out->service_active_count = 0; + $out->service_active_latency_avg = 0; + $out->service_active_execution_avg = 0; + $out->host_active_count = 0; + $out->host_active_latency_avg = 0; + $out->host_active_execution_avg = 0; + + foreach ($results as $row) { + $key = $row->object_type . '_' . $row->check_type . '_'; + $out->{$key . 'count'} = $row->object_count; + $out->{$key . 'latency_avg'} = $row->latency / $row->object_count; + $out->{$key . 'execution_avg'} = $row->execution_time / $row->object_count; + } + return $out; + } +} diff --git a/modules/monitoring/application/views/helpers/ContactFlags.php b/modules/monitoring/application/views/helpers/ContactFlags.php new file mode 100644 index 0000000..858c726 --- /dev/null +++ b/modules/monitoring/application/views/helpers/ContactFlags.php @@ -0,0 +1,46 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +class Zend_View_Helper_ContactFlags extends Zend_View_Helper_Abstract +{ + /** + * Get the human readable flag name for the given contact notification option + * + * @param string $tableName The name of the option table + * + * @return string + */ + public function getNotificationOptionName($tableName) + { + $exploded = explode('_', $tableName); + $name = end($exploded); + return ucfirst($name); + } + + /** + * Build all active notification options to a readable string + * + * @param object $contact The contact retrieved from a backend + * @param string $type Whether to display the flags for 'host' or 'service' + * @param string $glue The symbol to use to concatenate the flag names + * + * @return string A string that contains a human readable list of active options + */ + public function contactFlags($contact, $type, $glue = ', ') + { + $optionName = 'contact_' . $type . '_notification_options'; + if (isset($contact->$optionName)) { + return $contact->$optionName; + } + $out = array(); + foreach ($contact as $key => $value) { + if (preg_match('/^contact_notify_' . $type . '_.*/', $key) && $value == true) { + $option = $this->getNotificationOptionName($key); + if (strtolower($option) != 'timeperiod') { + array_push($out, $option); + } + } + } + return implode($glue, $out); + } +} diff --git a/modules/monitoring/application/views/helpers/Customvar.php b/modules/monitoring/application/views/helpers/Customvar.php new file mode 100644 index 0000000..8bfdc3a --- /dev/null +++ b/modules/monitoring/application/views/helpers/Customvar.php @@ -0,0 +1,62 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +class Zend_View_Helper_Customvar extends Zend_View_Helper_Abstract +{ + /** + * Create dispatch instance + * + * @return $this + */ + public function checkPerformance() + { + return $this; + } + + public function customvar($struct) + { + if (is_scalar($struct)) { + return nl2br($this->view->escape( + is_string($struct) + ? $struct + : var_export($struct, true) + ), false); + } elseif (is_array($struct)) { + return $this->renderArray($struct); + } elseif (is_object($struct)) { + return $this->renderObject($struct); + } + } + + protected function renderArray($array) + { + if (empty($array)) { + return '[]'; + } + $out = "<ul>\n"; + + foreach ($array as $val) { + $out .= '<li>' . $this->customvar($val) . "</li>\n"; + } + + return $out . "</ul>\n"; + } + + protected function renderObject($object) + { + if (0 === count((array) $object)) { + return '{}'; + } + $out = "{<ul>\n"; + + foreach ($object as $key => $val) { + $out .= '<li>' + . $this->view->escape($key) + . ' => ' + . $this->customvar($val) + . "</li>\n"; + } + + return $out . "</ul>}"; + } +} diff --git a/modules/monitoring/application/views/helpers/EscapeComment.php b/modules/monitoring/application/views/helpers/EscapeComment.php new file mode 100644 index 0000000..be85a22 --- /dev/null +++ b/modules/monitoring/application/views/helpers/EscapeComment.php @@ -0,0 +1,38 @@ +<?php +/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */ + +/** + * Helper for escaping comments, but preserving links + */ +class Zend_View_Helper_EscapeComment extends Zend_View_Helper_Abstract +{ + /** + * The purifier to use for escaping + * + * @var HTMLPurifier + */ + protected static $purifier; + + /** + * Escape any comment for being placed inside HTML, but preserve simple links (<a href="...">). + * + * @param string $comment + * + * @return string + */ + public function escapeComment($comment) + { + if (self::$purifier === null) { + require_once 'HTMLPurifier/Bootstrap.php'; + require_once 'HTMLPurifier.php'; + require_once 'HTMLPurifier.autoload.php'; + + $config = HTMLPurifier_Config::createDefault(); + $config->set('Core.EscapeNonASCIICharacters', true); + $config->set('HTML.Allowed', 'a[href]'); + $config->set('Cache.DefinitionImpl', null); + self::$purifier = new HTMLPurifier($config); + } + return self::$purifier->purify($comment); + } +} diff --git a/modules/monitoring/application/views/helpers/HostFlags.php b/modules/monitoring/application/views/helpers/HostFlags.php new file mode 100644 index 0000000..81d8ebc --- /dev/null +++ b/modules/monitoring/application/views/helpers/HostFlags.php @@ -0,0 +1,33 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +class Zend_View_Helper_HostFlags extends Zend_View_Helper_Abstract +{ + public function hostFlags($host) + { + $icons = array(); + if (! $host->host_handled && $host->host_state > 0) { + $icons[] = $this->view->icon('attention-alt', $this->view->translate('Unhandled')); + } + if ($host->host_acknowledged) { + $icons[] = $this->view->icon('ok', $this->view->translate('Acknowledged')); + } + if ($host->host_is_flapping) { + $icons[] = $this->view->icon('flapping', $this->view->translate('Flapping')); + } + if (! $host->host_notifications_enabled) { + $icons[] = $this->view->icon('bell-off-empty', $this->view->translate('Notifications Disabled')); + } + if ($host->host_in_downtime) { + $icons[] = $this->view->icon('plug', $this->view->translate('In Downtime')); + } + if (! $host->host_active_checks_enabled) { + if (! $host->host_passive_checks_enabled) { + $icons[] = $this->view->icon('eye-off', $this->view->translate('Active And Passive Checks Disabled')); + } else { + $icons[] = $this->view->icon('eye-off', $this->view->translate('Active Checks Disabled')); + } + } + return implode(' ', $icons); + } +} diff --git a/modules/monitoring/application/views/helpers/IconImage.php b/modules/monitoring/application/views/helpers/IconImage.php new file mode 100644 index 0000000..3c0ca43 --- /dev/null +++ b/modules/monitoring/application/views/helpers/IconImage.php @@ -0,0 +1,64 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +use Icinga\Module\Monitoring\Object\Macro; + +/** + * Generate icons to describe a given hosts state + */ +class Zend_View_Helper_IconImage extends Zend_View_Helper_Abstract +{ + /** + * Create dispatch instance + * + * @return \Zend_View_Helper_IconImage + */ + public function iconImage() + { + return $this; + } + + /** + * Display the image_icon of a MonitoredObject + * + * @param MonitoredObject|stdClass $object The host or service + * @return string + */ + public function host($object) + { + if ($object->host_icon_image && ! preg_match('/[\'"]/', $object->host_icon_image)) { + return $this->view->icon( + Macro::resolveMacros($object->host_icon_image, $object), + null, + array( + 'alt' => $object->host_icon_image_alt, + 'class' => 'host-icon-image', + 'title' => $object->host_icon_image_alt + ) + ); + } + return ''; + } + + /** + * Display the image_icon of a MonitoredObject + * + * @param MonitoredObject|stdClass $object The host or service + * @return string + */ + public function service($object) + { + if ($object->service_icon_image && ! preg_match('/[\'"]/', $object->service_icon_image)) { + return $this->view->icon( + Macro::resolveMacros($object->service_icon_image, $object), + null, + array( + 'alt' => $object->service_icon_image_alt, + 'class' => 'service-icon-image', + 'title' => $object->service_icon_image_alt + ) + ); + } + return ''; + } +} diff --git a/modules/monitoring/application/views/helpers/Link.php b/modules/monitoring/application/views/helpers/Link.php new file mode 100644 index 0000000..c5443a4 --- /dev/null +++ b/modules/monitoring/application/views/helpers/Link.php @@ -0,0 +1,72 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +/** + * Helper for generating frequently used jump links + * + * Most of the monitoring overviews link to detail information, e.g. the full information of the involved monitored + * object. Instead of reintroducing link generation and translation in those views, this helper contains most + * frequently used jump links. + */ +class Zend_View_Helper_Link extends Zend_View_Helper_Abstract +{ + /** + * Helper entry point + * + * @return $this + */ + public function link() + { + return $this; + } + + /** + * Create a host link + * + * @param string $host Hostname + * @param string $linkText Link text, e.g. the host's display name + * + * @return string + */ + public function host($host, $linkText) + { + return $this->view->qlink( + $linkText, + 'monitoring/host/show', + array('host' => $host), + array('title' => sprintf($this->view->translate('Show detailed information for host %s'), $linkText)) + ); + } + + /** + * Create a service link + * + * @param string $service Service name + * @param string $serviceLinkText Text for the service link, e.g. the service's display name + * @param string $host Hostname + * @param string $hostLinkText Text for the host link, e.g. the host's display name + * @param string $class An optional class to use for this link + * + * @return string + */ + public function service($service, $serviceLinkText, $host, $hostLinkText, $class = null) + { + return sprintf( + '%s: %s', + $this->host($host, $hostLinkText), + $this->view->qlink( + $serviceLinkText, + 'monitoring/service/show', + array('host' => $host, 'service' => $service), + array( + 'title' => sprintf( + $this->view->translate('Show detailed information for service %s on host %s'), + $serviceLinkText, + $hostLinkText + ), + 'class' => $class + ) + ) + ); + } +} diff --git a/modules/monitoring/application/views/helpers/MonitoringFlags.php b/modules/monitoring/application/views/helpers/MonitoringFlags.php new file mode 100644 index 0000000..8509e5b --- /dev/null +++ b/modules/monitoring/application/views/helpers/MonitoringFlags.php @@ -0,0 +1,40 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +/* use Icinga\Module\Monitoring\Object\MonitoredObject; */ + +/** + * Rendering helper for object's properties which may be either enabled or disabled + */ +class Zend_View_Helper_MonitoringFlags extends Zend_View_Helper_Abstract +{ + /** + * Object's properties which may be either enabled or disabled and their human readable description + * + * @var string[] + */ + private static $flags = array( + 'passive_checks_enabled' => 'Passive Checks', + 'active_checks_enabled' => 'Active Checks', + 'obsessing' => 'Obsessing', + 'notifications_enabled' => 'Notifications', + 'event_handler_enabled' => 'Event Handler', + 'flap_detection_enabled' => 'Flap Detection', + ); + + /** + * Retrieve flags as array with either true or false as value + * + * @param MonitoredObject $object + * + * @return array + */ + public function monitoringFlags(/*MonitoredObject*/ $object) + { + $flags = array(); + foreach (self::$flags as $column => $description) { + $flags[$description] = (bool) $object->{$column}; + } + return $flags; + } +} diff --git a/modules/monitoring/application/views/helpers/Perfdata.php b/modules/monitoring/application/views/helpers/Perfdata.php new file mode 100644 index 0000000..e7bc72b --- /dev/null +++ b/modules/monitoring/application/views/helpers/Perfdata.php @@ -0,0 +1,116 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +use Icinga\Module\Monitoring\Plugin\Perfdata; +use Icinga\Module\Monitoring\Plugin\PerfdataSet; +use Icinga\Util\StringHelper; + +class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract +{ + /** + * Display the given perfdata string to the user + * + * @param string $perfdataStr The perfdata string + * @param bool $compact Whether to display the perfdata in compact mode + * @param int $limit Max labels to show; 0 for no limit + * @param string $color The color indicating the perfdata state + * + * @return string + */ + public function perfdata($perfdataStr, $compact = false, $limit = 0, $color = Perfdata::PERFDATA_OK) + { + $pieChartData = PerfdataSet::fromString($perfdataStr)->asArray(); + uasort( + $pieChartData, + function ($a, $b) { + return $a->worseThan($b) ? -1 : ($b->worseThan($a) ? 1 : 0); + } + ); + $results = array(); + $keys = array('', 'label', 'value', 'min', 'max', 'warn', 'crit'); + $columns = array(); + $labels = array_combine( + $keys, + array( + '', + $this->view->translate('Label'), + $this->view->translate('Value'), + $this->view->translate('Min'), + $this->view->translate('Max'), + $this->view->translate('Warning'), + $this->view->translate('Critical') + ) + ); + foreach ($pieChartData as $perfdata) { + if ($perfdata->isVisualizable()) { + $columns[''] = ''; + } + foreach ($perfdata->toArray() as $column => $value) { + if (empty($value) || + $column === 'min' && floatval($value) === 0.0 || + $column === 'max' && $perfdata->isPercentage() && floatval($value) === 100) { + continue; + } + $columns[$column] = $labels[$column]; + } + } + // restore original column array sorting + $headers = array(); + foreach ($keys as $column) { + if (isset($columns[$column])) { + $headers[$column] = $labels[$column]; + } + } + $table = array('<thead><tr><th>' . implode('</th><th>', $headers) . '</th></tr></thead><tbody>'); + foreach ($pieChartData as $perfdata) { + if ($compact && $perfdata->isVisualizable()) { + $results[] = $perfdata->asInlinePie($color)->render(); + } else { + $data = array(); + if ($perfdata->isVisualizable()) { + $data []= $perfdata->asInlinePie($color)->render(); + } elseif (isset($columns[''])) { + $data []= ''; + } + if (! $compact) { + foreach ($perfdata->toArray() as $column => $value) { + if (! isset($columns[$column])) { + continue; + } + $text = $this->view->escape(empty($value) ? '-' : $value); + $data []= sprintf( + '<span title="%s">%s</span>', + $text, + $text + ); + } + } + $table []= '<tr><td class="sparkline-col">' . implode('</td><td>', $data) . '</td></tr>'; + } + } + $table[] = '</tbody>'; + if ($limit > 0) { + $count = $compact ? count($results) : count($table); + if ($count > $limit) { + if ($compact) { + $results = array_slice($results, 0, $limit); + $title = sprintf($this->view->translate('%d more ...'), $count - $limit); + $results[] = '<span aria-hidden="true" title="' . $title . '">...</span>'; + } else { + $table = array_slice($table, 0, $limit); + } + } + } + if ($compact) { + return join('', $results); + } else { + if (empty($table)) { + return ''; + } + return sprintf( + '<table class="performance-data-table collapsible" data-visible-rows="6">%s</table>', + implode("\n", $table) + ); + } + } +} diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php new file mode 100644 index 0000000..bcd3d9e --- /dev/null +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -0,0 +1,199 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +use Icinga\Web\Dom\DomNodeIterator; +use Icinga\Web\View; +use Icinga\Web\Helper\HtmlPurifier; + +/** + * Plugin output renderer + */ +class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract +{ + /** + * Patterns to be replaced in plain text plugin output + * + * @var array + */ + protected static $txtPatterns = array( + '~\\\t~', + '~\\\n~', + '~(\[|\()OK(\]|\))~', + '~(\[|\()WARNING(\]|\))~', + '~(\[|\()CRITICAL(\]|\))~', + '~(\[|\()UNKNOWN(\]|\))~', + '~(\[|\()UP(\]|\))~', + '~(\[|\()DOWN(\]|\))~', + '~\@{6,}~' + ); + + /** + * Replacements for $txtPatterns + * + * @var array + */ + protected static $txtReplacements = array( + "\t", + "\n", + '<span class="state-ok">$1OK$2</span>', + '<span class="state-warning">$1WARNING$2</span>', + '<span class="state-critical">$1CRITICAL$2</span>', + '<span class="state-unknown">$1UNKNOWN$2</span>', + '<span class="state-up">$1UP$2</span>', + '<span class="state-down">$1DOWN$2</span>', + '@@@@@@', + ); + + /** + * Patterns to be replaced in html plugin output + * + * @var array + */ + protected static $htmlPatterns = array( + '~\\\t~', + '~\\\n~', + '~<table~' + ); + + /** + * Replacements for $htmlPatterns + * + * @var array + */ + protected static $htmlReplacements = array( + "\t", + "\n", + '<table style="font-size: 0.75em"' + ); + + /** @var \Icinga\Module\Monitoring\Web\Helper\PluginOutputHookRenderer */ + protected $hookRenderer; + + public function __construct() + { + $this->hookRenderer = (new \Icinga\Module\Monitoring\Web\Helper\PluginOutputHookRenderer())->registerHooks(); + } + + /** + * Render plugin output + * + * @param string $output + * @param bool $raw + * @param string $command Check command + * + * @return string + */ + public function pluginOutput($output, $raw = false, $command = null) + { + if (empty($output)) { + return ''; + } + + if ($command !== null) { + $output = $this->hookRenderer->render($command, $output, ! $raw); + } + + if (preg_match('~<\w+(?>\s\w+=[^>]*)?>~', $output)) { + // HTML + $output = HtmlPurifier::process(preg_replace( + self::$htmlPatterns, + self::$htmlReplacements, + $output + )); + $isHtml = true; + } else { + // Plaintext + $output = preg_replace( + self::$txtPatterns, + self::$txtReplacements, + // Not using the view here to escape this. The view sets `double_encode` to true + htmlspecialchars($output, ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5, View::CHARSET, false) + ); + $isHtml = false; + } + + $output = trim($output); + // Add zero-width space after commas which are not followed by a whitespace character + // in oder to help browsers to break words in plugin output + $output = preg_replace('/,(?=[^\s])/', ',​', $output); + if (! $raw) { + if ($isHtml) { + $output = $this->processHtml($output); + $output = '<div class="plugin-output">' . $output . '</div>'; + } else { + $output = '<div class="plugin-output preformatted">' . $output . '</div>'; + } + } + + return $output; + } + + /** + * Replace classic Icinga CGI links with Icinga Web 2 links and color state information, if any + * + * @param string $html + * + * @return string + */ + protected function processHtml($html) + { + $pattern = '/[([](OK|WARNING|CRITICAL|UNKNOWN|UP|DOWN)[)\]]/'; + $doc = new DOMDocument(); + $doc->loadXML('<div>' . $html . '</div>', LIBXML_NOERROR | LIBXML_NOWARNING); + $dom = new RecursiveIteratorIterator(new DomNodeIterator($doc), RecursiveIteratorIterator::SELF_FIRST); + $nodesToRemove = array(); + foreach ($dom as $node) { + /** @var \DOMNode $node */ + if ($node->nodeType === XML_TEXT_NODE) { + $start = 0; + while (preg_match($pattern, $node->nodeValue, $match, PREG_OFFSET_CAPTURE, $start)) { + $offsetLeft = $match[0][1]; + $matchLength = strlen($match[0][0]); + $leftLength = $offsetLeft - $start; + // if there is text before the match + if ($leftLength) { + // create node for leading text + $text = new DOMText(substr($node->nodeValue, $start, $leftLength)); + $node->parentNode->insertBefore($text, $node); + } + // create the new element for the match + $span = $doc->createElement('span', $match[0][0]); + $span->setAttribute('class', 'state-' . strtolower($match[1][0])); + $node->parentNode->insertBefore($span, $node); + + // start for next match + $start = $offsetLeft + $matchLength; + } + if ($start) { + // is there text left? + if (strlen($node->nodeValue) > $start) { + // create node for trailing text + $text = new DOMText(substr($node->nodeValue, $start)); + $node->parentNode->insertBefore($text, $node); + } + // delete the old node later + $nodesToRemove[] = $node; + } + } elseif ($node->nodeType === XML_ELEMENT_NODE) { + /** @var \DOMElement $node */ + if ($node->tagName === 'a' + && preg_match('~^/cgi\-bin/status\.cgi\?(.+)$~', $node->getAttribute('href'), $match) + ) { + parse_str($match[1], $params); + if (isset($params['host'])) { + $node->setAttribute( + 'href', + $this->view->baseUrl('/monitoring/host/show?host=' . urlencode($params['host'])) + ); + } + } + } + } + foreach ($nodesToRemove as $node) { + /** @var \DOMNode $node */ + $node->parentNode->removeChild($node); + } + + return substr($doc->saveHTML(), 5, -7); + } +} diff --git a/modules/monitoring/application/views/helpers/RuntimeVariables.php b/modules/monitoring/application/views/helpers/RuntimeVariables.php new file mode 100644 index 0000000..e80e8aa --- /dev/null +++ b/modules/monitoring/application/views/helpers/RuntimeVariables.php @@ -0,0 +1,50 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +/** + * Convert runtime summary data into a simple usable stdClass + */ +class Zend_View_Helper_RuntimeVariables extends Zend_View_Helper_Abstract +{ + /** + * Create dispatch instance + * + * @return $this + */ + public function runtimeVariables() + { + return $this; + } + + /** + * Create a condensed row of object data + * + * @param $result stdClass + * + * @return stdClass Condensed row + */ + public function create(stdClass $result) + { + $out = new stdClass(); + $out->total_hosts = isset($result->total_hosts) + ? $result->total_hosts + : 0; + $out->total_scheduled_hosts = isset($result->total_scheduled_hosts) + ? $result->total_scheduled_hosts + : 0; + $out->total_services = isset($result->total_services) + ? $result->total_services + : 0; + $out->total_scheduled_services = isset($result->total_scheduled_services) + ? $result->total_scheduled_services + : 0; + $out->average_services_per_host = $out->total_hosts > 0 + ? $out->total_services / $out->total_hosts + : 0; + $out->average_scheduled_services_per_host = $out->total_scheduled_hosts > 0 + ? $out->total_scheduled_services / $out->total_scheduled_hosts + : 0; + + return $out; + } +} diff --git a/modules/monitoring/application/views/helpers/ServiceFlags.php b/modules/monitoring/application/views/helpers/ServiceFlags.php new file mode 100644 index 0000000..47a351c --- /dev/null +++ b/modules/monitoring/application/views/helpers/ServiceFlags.php @@ -0,0 +1,33 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +class Zend_View_Helper_ServiceFlags extends Zend_View_Helper_Abstract +{ + public function serviceFlags($service) + { + $icons = array(); + if (! $service->service_handled && $service->service_state > 0) { + $icons[] = $this->view->icon('attention-alt', $this->view->translate('Unhandled')); + } + if ($service->service_acknowledged) { + $icons[] = $this->view->icon('ok', $this->view->translate('Acknowledged')); + } + if ($service->service_is_flapping) { + $icons[] = $this->view->icon('flapping', $this->view->translate('Flapping')); + } + if (! $service->service_notifications_enabled) { + $icons[] = $this->view->icon('bell-off-empty', $this->view->translate('Notifications Disabled')); + } + if ($service->service_in_downtime) { + $icons[] = $this->view->icon('plug', $this->view->translate('In Downtime')); + } + if (! $service->service_active_checks_enabled) { + if (! $service->service_passive_checks_enabled) { + $icons[] = $this->view->icon('eye-off', $this->view->translate('Active And Passive Checks Disabled')); + } else { + $icons[] = $this->view->icon('eye-off', $this->view->translate('Active Checks Disabled')); + } + } + return implode(' ', $icons); + } +} diff --git a/modules/monitoring/application/views/scripts/comment/remove.phtml b/modules/monitoring/application/views/scripts/comment/remove.phtml new file mode 100644 index 0000000..73f8c68 --- /dev/null +++ b/modules/monitoring/application/views/scripts/comment/remove.phtml @@ -0,0 +1,11 @@ +<div class="controls"> + + <?php if (! $this->compact): ?> + <?= $this->tabs; ?> + <?php endif ?> + + <?= $this->render('partials/downtime/downtime-header.phtml'); ?> +</div> +<div class="content object-command"> + <?= $delDowntimeForm; ?> +</div> diff --git a/modules/monitoring/application/views/scripts/comment/show.phtml b/modules/monitoring/application/views/scripts/comment/show.phtml new file mode 100644 index 0000000..3cbfb76 --- /dev/null +++ b/modules/monitoring/application/views/scripts/comment/show.phtml @@ -0,0 +1,86 @@ +<div class="controls"> + <?php if (! $this->compact): ?> + <?= $this->tabs; ?> + <?php endif ?> + + <div data-base-target='_next'> + <?= $this->render('partials/comment/comment-header.phtml'); ?> + </div> +</div> +<div class="content"> + +<h2><?= $this->translate('Comment detail information') ?></h2> +<table class="name-value-table"> + <tbody> + <tr> + <?php if ($this->comment->objecttype === 'service'): ?> + <th> <?= $this->translate('Service') ?> </th> + <td> + <?= $this->icon('service', $this->translate('Service')); ?> + <?= $this->link()->service( + $this->comment->service_description, + $this->comment->service_display_name, + $this->comment->host_name, + $this->comment->host_display_name + ); + ?> + </td> + <?php else: ?> + <th> <?= $this->translate('Host') ?> </th> + <td> + <?= $this->icon('host', $this->translate('Host')); ?> + <?= $this->link()->host( + $this->comment->host_name, + $this->comment->host_display_name + ); + ?> + </td> + <?php endif ?> + </tr> + + <tr> + <th><?= $this->translate('Author') ?></th> + <td><?= $this->icon('user', $this->translate('User')) ?> <?= $this->escape($this->comment->author) ?></td> + </tr> + + <tr> + <th><?= $this->translate('Persistent') ?></th> + <td><?= $this->escape($this->comment->persistent) ? $this->translate('Yes') : $this->translate('No') ?></td> + </tr> + + <tr> + <th><?= $this->translate('Created') ?></th> + <td><?= $this->formatDateTime($this->comment->timestamp) ?></td> + </tr> + + <tr> + <th><?= $this->translate('Expires') ?></th> + <td> + <?= $this->comment->expiration ? sprintf( + $this->translate('This comment expires on %s at %s.'), + $this->formatDate($this->comment->expiration), + $this->formatTime($this->comment->expiration) + ) : $this->translate('This comment does not expire.'); + ?> + </td> + </tr> + + <tr> + <th><?= $this->translate('Comment') ?></th> + <td><?= $this->nl2br($this->createTicketLinks($this->markdown($comment->comment))) ?></td> + </tr> + + <?php if (isset($delCommentForm)): // Form is unset if the current user lacks the respective permission ?> + <tr class="newsection"> + <th><?= $this->translate('Commands') ?></th> + <td> + <?= $delCommentForm ?> + </td> + </tr> + <?php endif ?> + + </tbody> +</table> + +</div> + diff --git a/modules/monitoring/application/views/scripts/comments/delete-all.phtml b/modules/monitoring/application/views/scripts/comments/delete-all.phtml new file mode 100644 index 0000000..698c4ee --- /dev/null +++ b/modules/monitoring/application/views/scripts/comments/delete-all.phtml @@ -0,0 +1,12 @@ +<div class="controls"> + + <?php if (! $this->compact): ?> + <?= $this->tabs; ?> + <?php endif ?> + + <?= $this->render('partials/comment/comments-header.phtml'); ?> +</div> + +<div class="content object-command"> + <?= $delCommentForm ?> +</div> diff --git a/modules/monitoring/application/views/scripts/comments/show.phtml b/modules/monitoring/application/views/scripts/comments/show.phtml new file mode 100644 index 0000000..67e1c6b --- /dev/null +++ b/modules/monitoring/application/views/scripts/comments/show.phtml @@ -0,0 +1,19 @@ +<div class="controls"> +<?php if (! $this->compact): ?> + <?= $this->tabs ?> +<?php endif ?> + <?= $this->render('partials/comment/comments-header.phtml') ?> +</div> + +<div class="content multi-commands"> + <h2><?= $this->translate('Commands') ?></h2> + <?= $this->qlink( + sprintf($this->translate('Remove %d comments'), $comments->count()), + $removeAllLink, + null, + array( + 'icon' => 'trash', + 'title' => $this->translate('Remove all selected comments') + ) + ) ?> +</div> diff --git a/modules/monitoring/application/views/scripts/config/form.phtml b/modules/monitoring/application/views/scripts/config/form.phtml new file mode 100644 index 0000000..cbf0659 --- /dev/null +++ b/modules/monitoring/application/views/scripts/config/form.phtml @@ -0,0 +1,6 @@ +<div class="controls"> + <?= $tabs->showOnlyCloseButton(); ?> +</div> +<div class="content"> + <?= $form; ?> +</div>
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/config/index.phtml b/modules/monitoring/application/views/scripts/config/index.phtml new file mode 100644 index 0000000..a1264c2 --- /dev/null +++ b/modules/monitoring/application/views/scripts/config/index.phtml @@ -0,0 +1,78 @@ +<div class="controls"> + <?= $tabs ?> +</div> + +<div class="content" data-base-target="_next"> + <div> + <h2><?= $this->translate('Monitoring Backends') ?></h2> + <?= $this->qlink( + $this->translate('Create a New Monitoring Backend') , + 'monitoring/config/createbackend', + null, + array( + 'class' => 'button-link', + 'icon' => 'plus', + 'title' => $this->translate('Create a new monitoring backend') + ) + ) ?> + <table class="table-row-selectable common-table"> + <thead> + <tr> + <th><?= $this->translate('Monitoring Backend') ?></th> + <th></th> + </tr> + </thead> + <tbody> + <?php foreach ($this->backendsConfig as $backendName => $config): ?> + <tr> + <td> + <?= $this->qlink( + $backendName, + 'monitoring/config/editbackend', + array('backend-name' => $backendName), + array( + 'icon' => 'edit', + 'title' => sprintf($this->translate('Edit monitoring backend %s'), $backendName) + ) + ) ?> + <span class="config-label-meta">(<?= sprintf( + $this->translate('Type: %s'), + $this->escape($config->type === 'ido' ? 'IDO' : ucfirst($config->type)) + ) ?>) + </span> + </td> + <td class="text-right"> + <?= $this->qlink( + '', + 'monitoring/config/removebackend', + array('backend-name' => $backendName), + array( + 'class' => 'action-link', + 'icon' => 'cancel', + 'title' => sprintf($this->translate('Remove monitoring backend %s'), $backendName) + ) + ) ?> + </td> + </tr> + <?php endforeach ?> + </tbody> + </table> + </div> + <div> + <h2><?= $this->translate('Command Transports') ?></h2> + <?= $this->qlink( + $this->translate('Create a New Command Transport') , + 'monitoring/config/createtransport', + null, + array( + 'class' => 'button-link', + 'icon' => 'plus', + 'title' => $this->translate('Create a new command transport') + ) + ) ?> + <?php + /** @var \Icinga\Module\Monitoring\Forms\Config\TransportReorderForm $commandTransportReorderForm */ + echo $commandTransportReorderForm; + ?> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/config/security.phtml b/modules/monitoring/application/views/scripts/config/security.phtml new file mode 100644 index 0000000..3801678 --- /dev/null +++ b/modules/monitoring/application/views/scripts/config/security.phtml @@ -0,0 +1,6 @@ +<div class="controls"> + <?= $tabs; ?> +</div> +<div class="content"> + <?= $form; ?> +</div>
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/downtime/remove.phtml b/modules/monitoring/application/views/scripts/downtime/remove.phtml new file mode 100644 index 0000000..34a7dbd --- /dev/null +++ b/modules/monitoring/application/views/scripts/downtime/remove.phtml @@ -0,0 +1,13 @@ +<div class="controls"> + + <?php if (! $this->compact): ?> + <?= $this->tabs; ?> + <?php endif ?> + + <table> + <tr> <?= $this->render('partials/downtime/downtime-header.phtml') ?> </tr> + </table> +</div> +<div class="content object-command"> + <?= $delDowntimeForm; ?> +</div>
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/downtime/show.phtml b/modules/monitoring/application/views/scripts/downtime/show.phtml new file mode 100644 index 0000000..4db03cb --- /dev/null +++ b/modules/monitoring/application/views/scripts/downtime/show.phtml @@ -0,0 +1,173 @@ +<div class="controls"> + <?php if (! $this->compact): ?> + <?= $this->tabs; ?> + <?php endif ?> + + <table> + <tr> <?= $this->render('partials/downtime/downtime-header.phtml'); ?> </tr> + </table> +</div> +<div class="content"><h2><?= $this->translate('Details') ?></h2> + <table class="name-value-table"> + <tbody> + <tr> + <th> + <?= $this->isService ? $this->translate('Service') : $this->translate('Host') ?> + </th> + <td data-base-target="_next"> + <?php if ($this->isService): ?> + <?php + $link = $this->link()->service( + $downtime->service_description, + $downtime->service_display_name, + $downtime->host_name, + $downtime->host_display_name + ); + $icon = $this->icon('service', $this->translate('Service')); + ?> + <?php else: ?> + <?php + $icon = $this->icon('host', $this->translate('Host')); + $link = $this->link()->host($downtime->host_name, $downtime->host_display_name) + ?> + <?php endif ?> + <?= $icon ?> + <?= $link ?> + </td> + </tr> + <tr title="<?= $this->translate('The name of the person who scheduled this downtime'); ?>"> + <th><?= $this->translate('Author') ?></th> + <td><?= $this->icon('user', $this->translate('User')) ?> <?= $this->escape($this->downtime->author_name) ?></td> + </tr> + <tr title="<?= $this->translate('Date and time this downtime was entered'); ?>"> + <th><?= $this->translate('Entry Time') ?></th> + <td><?= $this->formatDateTime($this->downtime->entry_time) ?></td> + </tr> + <tr title="<?= $this->translate('A comment, as entered by the author, associated with the scheduled downtime'); ?>"> + <th><?= $this->translate('Comment') ?></th> + <td><?= $this->nl2br($this->createTicketLinks($this->markdown($downtime->comment))) ?></td> + </tr> + </tbody> + </table> + + <h2> <?= $this->translate('Duration') ?> </h2> + + <table class="name-value-table"> + <tbody> + <tr class="newsection"> + <th><?= $this->escape( + $this->downtime->is_flexible ? + $this->translate('Flexible') : $this->translate('Fixed') + ); ?> + <?= $this->icon('info-circled', $this->downtime->is_flexible ? + $this->translate('Flexible downtimes have a hard start and end time,' + . ' but also an additional restriction on the duration in which ' + . ' the host or service may actually be down.') : + $this->translate('Fixed downtimes have a static start and end time.')) ?> + </th> + <td> + <?php if ($downtime->is_flexible): ?> + <?php if ($downtime->is_in_effect): ?> + <?= sprintf( + $isService + ? $this->translate('This flexible service downtime was started on %s at %s and lasts for %s until %s at %s.') + : $this->translate('This flexible host downtime was started on %s at %s and lasts for %s until %s at %s.'), + $this->formatDate($downtime->start), + $this->formatTime($downtime->start), + $this->formatDuration($downtime->duration), + $this->formatDate($downtime->end), + $this->formatTime($downtime->end) + ) ?> + <?php else: ?> + <?= sprintf( + $isService + ? $this->translate('This flexible service downtime has been scheduled to start between %s - %s and to last for %s.') + : $this->translate('This flexible host downtime has been scheduled to start between %s - %s and to last for %s.'), + $this->formatDateTime($downtime->scheduled_start), + $this->formatDateTime($downtime->scheduled_end), + $this->formatDuration($downtime->duration) + ) ?> + <?php endif ?> + <?php else: ?> + <?php if ($downtime->is_in_effect): ?> + <?= sprintf( + $isService + ? $this->translate('This fixed service downtime was started on %s at %s and expires on %s at %s.') + : $this->translate('This fixed host downtime was started on %s at %s and expires on %s at %s.'), + $this->formatDate($downtime->start), + $this->formatTime($downtime->start), + $this->formatDate($downtime->end), + $this->formatTime($downtime->end) + ) ?> + <?php else: ?> + <?= sprintf( + $isService + ? $this->translate('This fixed service downtime has been scheduled to start on %s at %s and to end on %s at %s.') + : $this->translate('This fixed host downtime has been scheduled to start on %s at %s and to end on %s at %s.'), + $this->formatDate($downtime->start), + $this->formatTime($downtime->start), + $this->formatDate($downtime->end), + $this->formatTime($downtime->end) + ) ?> + <?php endif ?> + <?php endif ?> + </td> + </tr> + <tr title="<?= $this->translate('The date/time the scheduled downtime is' + . ' supposed to start. If this is a flexible (non-fixed) downtime, ' + . 'this refers to the earliest possible time that the downtime' + . ' can start'); ?>"> + <th><?= $this->translate('Scheduled start') ?></th> + <td><?= $this->formatDateTime($this->downtime->scheduled_start) ?></td> + </tr> + <tr title="<?= $this->translate('The date/time the scheduled downtime is ' + . 'supposed to end. If this is a flexible (non-fixed) downtime, ' + . 'this refers to the last possible time that the downtime can ' + . 'start'); ?>"> + <th><?= $this->translate('Scheduled end') ?></th> + <td><?= $this->formatDateTime($this->downtime->scheduled_end) ?></td> + </tr> + <?php if ($this->downtime->is_flexible): ?> + <tr title="<?= $this->translate('Indicates the number of seconds that the ' + . 'scheduled downtime should last. This is usually only needed if' + . ' this is a flexible downtime, which can start at a variable ' + . 'time, but lasts for the specified duration'); ?>"> + <th tit><?= $this->translate('Duration') ?></th> + <td><?= $this->formatDuration($this->downtime->duration) ?></td> + </tr> + <tr title="<?= $this->translate('he date/time the scheduled downtime was' + . ' actually started'); ?>"> + <th><?= $this->translate('Actual start time') ?></th> + <td><?= $this->formatDateTime($downtime->start) ?></td> + </tr> + <tr title="<?= $this->translate('The date/time the scheduled downtime ' + . 'actually ended'); ?>"> + <th><?= $this->translate('Actual end time') ?></th> + <td><?= $this->formatDateTime($downtime->end) ?></td> + </tr> + <?php endif; ?> + + <tr class="newsection"> + <th><?= $this->translate('In effect') ?></th> + <td> + <?= $this->escape( + $this->downtime->is_in_effect ? + $this->translate('Yes') : $this->translate('No') + ); + ?> + </td> + </tr> + + <?php if (isset($delDowntimeForm)): // Form is unset if the current user lacks the respective permission ?> + <tr class="newsection"> + <th><?= $this->translate('Commands') ?></th> + <td> + <?= $delDowntimeForm ?> + </td> + </tr> + <?php endif ?> + </tbody> + </table> + +</div> + diff --git a/modules/monitoring/application/views/scripts/downtimes/delete-all.phtml b/modules/monitoring/application/views/scripts/downtimes/delete-all.phtml new file mode 100644 index 0000000..e6435fe --- /dev/null +++ b/modules/monitoring/application/views/scripts/downtimes/delete-all.phtml @@ -0,0 +1,12 @@ +<div class="controls"> + + <?php if (! $this->compact): ?> + <?= $this->tabs; ?> + <?php endif ?> + + <?= $this->render('partials/downtime/downtimes-header.phtml'); ?> +</div> + +<div class="content object-command"> + <?= $delAllDowntimeForm ?> +</div>
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/downtimes/show.phtml b/modules/monitoring/application/views/scripts/downtimes/show.phtml new file mode 100644 index 0000000..73d9bf6 --- /dev/null +++ b/modules/monitoring/application/views/scripts/downtimes/show.phtml @@ -0,0 +1,19 @@ +<div class="controls"> +<?php if (! $this->compact): ?> + <?= $this->tabs ?> +<?php endif ?> + <?= $this->render('partials/downtime/downtimes-header.phtml') ?> +</div> + +<div class="content multi-commands"> + <h2> <?= $this->translate('Commands') ?> </h2> + <?= $this->qlink( + sprintf($this->translate('Remove all %d scheduled downtimes'), $downtimes->count()), + $removeAllLink, + null, + array( + 'icon' => 'trash', + 'title' => $this->translate('Remove all selected downtimes') + ) + ) ?> +</div> diff --git a/modules/monitoring/application/views/scripts/event/show.phtml b/modules/monitoring/application/views/scripts/event/show.phtml new file mode 100644 index 0000000..c844a6f --- /dev/null +++ b/modules/monitoring/application/views/scripts/event/show.phtml @@ -0,0 +1,34 @@ +<?php +use Icinga\Module\Monitoring\Object\Service; + +/** @var string[][] $details */ +/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ +/** @var \Icinga\Web\View $this */ +?> +<div class="controls"> +<?php +if (! $this->compact) { + echo $this->tabs; +} + +echo $object instanceof Service + ? '<h2>' . $this->translate('Current Service State') . '</h2>' . $this->render('partials/object/service-header.phtml') + : '<h2>' . $this->translate('Current Host State') . '</h2>' . $this->render('partials/object/host-header.phtml'); +?> +</div> +<div class="content"> + <?php + foreach ($extensionsHtml as $extensionHtml) { + echo $extensionHtml; + } + ?> + + <h2><?= $this->escape($this->translate('Event Details')) ?></h2> + <table class="event-details name-value-table" data-base-target="_next"> + <?php + foreach ($details as $detail) { + echo '<tr><th>' . $this->escape($detail[0]) . '</th><td>' . $detail[1] . '</td></tr>'; + } + ?> + </table> +</div> diff --git a/modules/monitoring/application/views/scripts/form/reorder-command-transports.phtml b/modules/monitoring/application/views/scripts/form/reorder-command-transports.phtml new file mode 100644 index 0000000..2f81610 --- /dev/null +++ b/modules/monitoring/application/views/scripts/form/reorder-command-transports.phtml @@ -0,0 +1,93 @@ +<?php +/** @var \Icinga\Web\View $this */ +/** @var \Icinga\Module\Monitoring\Forms\Config\TransportReorderForm $form */ +?> +<form id="<?= +$this->escape($form->getId()) +?>" name="<?= +$this->escape($form->getName()) +?>" enctype="<?= +$this->escape($form->getEncType()) +?>" method="<?= +$this->escape($form->getMethod()) +?>" action="<?= +$this->escape($form->getAction()) +?>"> + <table class="table-row-selectable common-table" data-base-target="_next"> + <thead> + <tr> + <th><?= $this->translate('Transport') ?></th> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <?php + $i = -1; + $transportConfig = $form->getConfig(); + $total = $transportConfig->count(); + foreach ($transportConfig as $transportName => $config): + ++$i; + ?> + <tr> + <td> + <?= $this->qlink( + $transportName, + 'monitoring/config/edittransport', + array('transport' => $transportName), + array( + 'icon' => 'edit', + 'title' => sprintf($this->translate('Edit command transport %s'), $transportName) + ) + ); ?> + <span class="config-label-meta">(<?= sprintf( + $this->translate('Type: %s'), + ucfirst($config->get('transport', 'local')) + ) ?>) + </span> + </td> + <td class="text-right"> + <?= $this->qlink( + '', + 'monitoring/config/removetransport', + array('transport' => $transportName), + array( + 'class' => 'action-link', + 'icon' => 'cancel', + 'title' => sprintf($this->translate('Remove command transport %s'), $transportName) + ) + ); ?> + </td> + <td class="icon-col text-right" data-base-target="_self"> + <?php if ($i > 0): ?> + <button type="submit" name="transport_newpos" class="link-button icon-only animated move-up" value="<?= $this->escape( + ($i - 1) . '|' . $transportName + ) ?>" title="<?= $this->translate( + 'Move up in order' + ) ?>" aria-label="<?= $this->escape(sprintf( + $this->translate('Move command transport %s upwards'), + $transportName + )) ?>"><?= + $this->icon('up-small') + ?></button> + <?php endif ?> + <?php if ($i + 1 < $total): ?> + <button type="submit" name="transport_newpos" class="link-button icon-only animated move-down" value="<?= $this->escape( + ($i + 1) . '|' . $transportName + ) ?>" title="<?= $this->translate( + 'Move down in order' + ) ?>" aria-label="<?= $this->escape(sprintf( + $this->translate('Move command transport %s downwards'), + $transportName + )) ?>"><?= + $this->icon('down-small') + ?></button> + <?php endif ?> + </td> + </tr> + <?php endforeach ?> + </tbody> + </table> + <?= $form->getElement($form->getTokenElementName()) ?> + <?= $form->getElement($form->getUidElementName()) ?> +</form> diff --git a/modules/monitoring/application/views/scripts/health/disable-notifications.phtml b/modules/monitoring/application/views/scripts/health/disable-notifications.phtml new file mode 100644 index 0000000..e8c75e5 --- /dev/null +++ b/modules/monitoring/application/views/scripts/health/disable-notifications.phtml @@ -0,0 +1,20 @@ +<?php if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs->showOnlyCloseButton(); ?> +</div> +<?php endif ?> +<div class="content"> + <h1><?= $title; ?></h1> + <?php if ((bool) $programStatus->notifications_enabled === false): ?> + <div> + <?= $this->translate('Host and service notifications are already disabled.') ?> + <?php if ($this->programStatus->disable_notif_expire_time): ?> + <?= sprintf( + $this->translate('Notifications will be re-enabled in <strong>%s</strong>.'), + $this->timeUntil($this->programStatus->disable_notif_expire_time)); ?> + <?php endif; ?> + </div> + <?php else: ?> + <?= $form; ?> + <?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/health/info.phtml b/modules/monitoring/application/views/scripts/health/info.phtml new file mode 100644 index 0000000..76d9ee3 --- /dev/null +++ b/modules/monitoring/application/views/scripts/health/info.phtml @@ -0,0 +1,87 @@ +<?php +$rv = $this->runtimeVariables()->create($this->runtimevariables); +$cp = $this->checkPerformance()->create($this->checkperformance); + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs; ?> +</div> +<?php endif ?> + +<div class="content processinfo"> + <div class="boxview"> + <div class="box process"> + <h2 tabindex="0"><?= $this->translate('Process Info') ?></h2> + <table class="name-value-table"> + <tbody> + <tr> + <th><?= $this->translate('Program Version') ?></th> + <td><?= $this->programStatus->program_version + ? $this->programStatus->program_version + : $this->translate('N/A') ?></td> + </tr> + <tr> + <th><?= $this->translate('Program Start Time') ?></th> + <td><?= $this->formatDateTime($this->programStatus->program_start_time) ?></td> + </tr> + <tr> + <th><?= $this->translate('Last Status Update'); ?></th> + <td><?= $this->timeAgo($this->programStatus->status_update_time); ?></td> + </tr> + <tr> + <th><?= $this->translate('Last External Command Check'); ?></th> + <td><?= $this->timeAgo($this->programStatus->last_command_check); ?></td> + </tr> + <tr> + <th><?= $this->translate('Last Log File Rotation'); ?></th> + <td><?= $this->programStatus->last_log_rotation + ? $this->timeSince($this->programStatus->last_log_rotation) + : $this->translate('N/A') ?></td> + </tr> + <tr> + <th><?= $this->translate('Global Service Event Handler'); ?></th> + <td><?= $this->programStatus->global_service_event_handler + ? $this->programStatus->global_service_event_handler + : $this->translate('N/A'); ?></td> + </tr> + <tr> + <th><?= $this->translate('Global Host Event Handler'); ?></th> + <td><?= $this->programStatus->global_host_event_handler + ? $this->programStatus->global_host_event_handler + : $this->translate('N/A'); ?></td> + </tr> + <tr> + <th><?= $this->translate('Active Endpoint'); ?></th> + <td><?= $this->programStatus->endpoint_name + ? $this->programStatus->endpoint_name + : $this->translate('N/A') ?></td> + </tr> + <tr> + <th><?= $this->translate('Active Icinga Web 2 Endpoint'); ?></th> + <td><?= gethostname() ?: $this->translate('N/A') ?></td> + </tr> + </tbody> + </table> + <?php if ((bool) $this->programStatus->is_currently_running === true): ?> + <div class="backend-running"> + <?= sprintf( + $this->translate( + '%1$s has been up and running with PID %2$d %3$s', + 'Last format parameter represents the time running' + ), + $this->backendName, + $this->programStatus->process_id, + $this->timeSince($this->programStatus->program_start_time)) ?> + </div> + <?php else: ?> + <div class="backend-not-running"> + <?= sprintf($this->translate('Backend %s is not running'), $this->backendName) ?> + </div> + <?php endif ?> + </div> + <div class="box features"> + <h2 tabindex="0"><?= $this->translate('Feature Commands') ?></h2> + <?= $this->toggleFeaturesForm ?> + </div> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/health/not-running.phtml b/modules/monitoring/application/views/scripts/health/not-running.phtml new file mode 100644 index 0000000..8439fc4 --- /dev/null +++ b/modules/monitoring/application/views/scripts/health/not-running.phtml @@ -0,0 +1,8 @@ +<?php if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs; ?> +</div> +<?php endif ?> +<div class="content"> + <?= sprintf($this->translate('%s is currently not up and running'), $this->backendName) ?> +</div> diff --git a/modules/monitoring/application/views/scripts/health/stats.phtml b/modules/monitoring/application/views/scripts/health/stats.phtml new file mode 100644 index 0000000..5cfb8f9 --- /dev/null +++ b/modules/monitoring/application/views/scripts/health/stats.phtml @@ -0,0 +1,150 @@ +<?php +$rv = $this->runtimeVariables()->create($this->runtimevariables); +$cp = $this->checkPerformance()->create($this->checkperformance); + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> +</div> +<?php endif ?> + +<div class="content stats"> + <div class="boxview"> + <div class="box stats"> + <h2 tabindex="0"><?= $this->unhandledProblems ?> <?= $this->translate('Unhandled Problems:') ?></h2> + <table class="name-value-table"> + <thead> + <th></th> + <th colspan="3"></th> + </thead> + <tbody> + <tr> + <th><?= $this->translate('Service Problems:') ?></th> + <td colspan="3"> + <span class="badge state-critical"> + <?= + $this->qlink( + $this->unhandledServiceProblems, + 'monitoring/list/services?service_problem=1&service_handled=0&sort=service_severity', + null, + array('data-base-target' => '_next') + ) + ?> + </span> + </td> + </tr> + <tr> + <th><?= $this->translate('Host Problems:') ?></th> + <td colspan="3"> + <span class="badge state-critical"> + <?= + $this->qlink( + $this->unhandledhostProblems, + 'monitoring/list/hosts?host_problem=1&host_handled=0', + null, + array('data-base-target' => '_next') + ) + ?> + </span> + </td> + </tr> + </tbody> + </table> + + <h2 tabindex="0" class="tinystatesummary" data-base-target="_next"> + <?php $this->stats = $hoststats ?> + <?= $this->render('list/components/hostssummary.phtml') ?> + </h2> + <table class="name-value-table"> + <thead> + <tr> + <th><?= $this->translate('Runtime Variables') ?></th> + <th colspan="3"><?= $this->translate('Host Checks') ?></th> + </tr> + </thead> + <tbody> + <tr> + <th><?= $this->translate('Total') ?></th> + <td><?= $rv->total_scheduled_hosts ?></td> + </tr> + <tr> + <th><?= $this->translate('Scheduled') ?></th> + <td><?= $rv->total_scheduled_hosts ?></td> + </tr> + </tbody> + </table> + + <h2 class="tinystatesummary" data-base-target="_next"> + <?php $this->stats = $servicestats ?> + <?= $this->render('list/components/servicesummary.phtml') ?> + </h2> + <table class="name-value-table"> + <thead> + <tr> + <th><?= $this->translate('Runtime Variables') ?></th> + <th><?= $this->translate('Service Checks') ?></th> + <th colspan="2"><?= $this->translate('Per Host') ?></th> + </tr> + </thead> + <tbody> + <tr> + <th><?= $this->translate('Total') ?></th> + <td><?= $rv->total_services ?></td> + <td><?= sprintf('%.2f', $rv->average_services_per_host) ?></td> + </tr> + <tr> + <th><?= $this->translate('Scheduled') ?></th> + <td><?= $rv->total_scheduled_services ?></td> + <td><?= sprintf('%.2f', $rv->average_scheduled_services_per_host) ?></td> + </tr> + </tbody> + </table> + + <h2><?= $this->translate('Active checks') ?></h2> + <table class="name-value-table"> + <thead> + <tr> + <th><?= $this->translate('Check Performance') ?></th> + <th><?= $this->translate('Checks') ?></th> + <th><?= $this->translate('Latency') ?></th> + <th><?= $this->translate('Execution time') ?></th> + </tr> + </thead> + <tbody> + <tr> + <th><?= $this->translate('Host Checks') ?></th> + <td><?= $cp->host_active_count; ?></td> + <td><?= sprintf('%.3f', $cp->host_active_latency_avg) ?>s</td> + <td><?= sprintf('%.3f', $cp->host_active_execution_avg) ?>s</td> + </tr> + <tr> + <th><?= $this->translate('Service Checks') ?></th> + <td><?= $cp->service_active_count; ?></td> + <td><?= sprintf('%.3f', $cp->service_active_latency_avg) ?>s</td> + <td><?= sprintf('%.3f', $cp->service_active_execution_avg) ?>s</td> + </tr> + </tbody> + </table> + + <h2><?= $this->translate('Passive checks') ?></h2> + <table class="name-value-table"> + <thead> + <tr> + <th><?= $this->translate('Check Performance') ?></th> + <th colspan="3"><?= $this->translate('Passive Checks') ?></th> + </tr> + </thead> + <tbody> + <tr> + <th><?= $this->translate('Host Checks') ?></th> + <td><?= $cp->host_passive_count ?></td> + </tr> + <tr> + <th><?= $this->translate('Service Checks') ?></th> + <td><?= $cp->service_passive_count ?></td> + </tr> + </tbody> + </table> + </div> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/host/services.phtml b/modules/monitoring/application/views/scripts/host/services.phtml new file mode 100644 index 0000000..ac1dc5b --- /dev/null +++ b/modules/monitoring/application/views/scripts/host/services.phtml @@ -0,0 +1,23 @@ +<?php use Icinga\Data\Filter\Filter; ?> + +<div class="controls"> + <?php if (! $this->compact): ?> + <?= $this->tabs; ?> + <?php endif ?> + <?= $this->render('partials/object/host-header.phtml') ?> + <?php + $this->baseFilter = Filter::where('host', $object->host_name); + $this->stats = $object->stats; + echo $this->render('list/components/servicesummary.phtml'); + ?> +</div> +<?= $this->partial( + 'list/services.phtml', + 'monitoring', + array( + 'compact' => true, + 'showHost' => false, + 'services' => $services, + 'addColumns' => array() + ) +); ?> diff --git a/modules/monitoring/application/views/scripts/host/show.phtml b/modules/monitoring/application/views/scripts/host/show.phtml new file mode 100644 index 0000000..72f5af4 --- /dev/null +++ b/modules/monitoring/application/views/scripts/host/show.phtml @@ -0,0 +1,14 @@ +<?php use Icinga\Data\Filter\Filter; ?> +<div class="controls controls-separated"> +<?php if (! $this->compact): ?> + <?= $this->tabs ?> +<?php endif ?> + <?= $this->render('partials/object/host-header.phtml') ?> +<?php + $this->stats = $object->stats; + $this->baseFilter = Filter::where('host', $object->host_name); + echo $this->render('list/components/servicesummary.phtml'); +?> + <?= $this->render('partials/object/quick-actions.phtml') ?> +</div> +<?= $this->render('partials/object/detail-content.phtml') ?> diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml new file mode 100644 index 0000000..97b8434 --- /dev/null +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -0,0 +1,206 @@ +<div class="controls"> + <?php if (! $this->compact): ?> + <?= $tabs; ?> + <?php endif ?> + <?= $this->render('list/components/hostssummary.phtml') ?> + <?= $this->render('partials/host/objects-header.phtml'); ?> + <?php + $hostCount = count($objects); + $unhandledCount = count($unhandledObjects); + $problemCount = count($problemObjects); + $unackCount = count($unacknowledgedObjects); + $scheduledDowntimeCount = count($objects->getScheduledDowntimes()); + ?> +</div> + +<div class="content"> + <?php if ($hostCount === 0): ?> + <?= $this->translate('No hosts found matching the filter'); ?> + <?php else: ?> + <?= $this->render('show/components/extensions.phtml') ?> + <h2><?= $this->translate('Problem Handling') ?></h2> + <table class="name-value-table"> + <tbody> + <?php + + if ($unackCount > 0): ?> + <tr> + <th> <?= sprintf($this->translate('%d unhandled problems'), $unackCount) ?> </th> + <td> + <?= $this->qlink( + $this->translate('Acknowledge'), + $acknowledgeLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'check' + ) + ) ?> + </td> + </tr> + <?php endif; ?> + + <?php if (($acknowledgedCount = count($acknowledgedObjects)) > 0): ?> + <tr> + <th> <?= sprintf( + $this->translatePlural( + '%s acknowledgement', + '%s acknowledgements', + $acknowledgedCount + ), + '<b>' . $acknowledgedCount . '</b>' + ); ?> </th> + <td> + <?= $removeAckForm->setLabelEnabled(true) ?> + </td> + </tr> + <?php endif ?> + + <tr> + <th> <?= $this->translate('Comments') ?> </th> + <td> + <?= $this->qlink( + $this->translate('Add comments'), + $addCommentLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'comment-empty' + ) + ) ?> + </td> + </tr> + + <?php if (($commentCount = count($objects->getComments())) > 0): ?> + <tr> + <th></th> + <td> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%s comment', + '%s comments', + $commentCount + ), + $commentCount + ), + $commentsLink, + null, + array('data-base-target' => '_next') + ); ?> + </td> + </tr> + <?php endif ?> + + <tr> + <th> + <?= $this->translate('Downtimes') ?> + </th> + <td> + <?= $this->qlink( + $this->translate('Schedule downtimes'), + $downtimeAllLink, + null, + array( + 'icon' => 'plug', + 'class' => 'action-link' + ) + ) ?> + </td> + </tr> + + <?php if ($scheduledDowntimeCount > 0): ?> + <tr> + <th></th> + <td> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%d scheduled downtime', + '%d scheduled downtimes', + $scheduledDowntimeCount + ), + $scheduledDowntimeCount + ), + $showDowntimesLink, + null, + array( + 'data-base-target' => '_next' + ) + ) ?> + </td> + </tr> + <?php endif ?> + </tbody> + </table> + + <?php if ($this->hasPermission('monitoring/command/send-custom-notification')): ?> + <h2> <?= $this->translate('Notifications') ?> </h2> + <table class="name-value-table"> + <tbody> + <tr> + <th> <?= $this->translate('Notifications') ?> </th> + <td> + <?= $this->qlink( + $this->translate('Send notifications'), + $sendCustomNotificationLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'bell' + ) + ) ?> + </td> + </tr> + </tbody> + </table> + <?php endif ?> + + <h2> <?= $this->translate('Check Execution') ?> </h2> + + <table class="name-value-table"> + <tbody> + <tr> + <th> <?= $this->translate('Command') ?> </th> + <td> + <?= $this->qlink( + $this->translate('Process check result'), + $processCheckResultAllLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'edit' + ) + ) ?> + </td> + </tr> + + <?php if (isset($checkNowForm)): // Form is unset if the current user lacks the respective permission ?> + <tr> + <th> <?= $this->translate('Schedule Check') ?> </th> + <td> <?= $checkNowForm ?> </td> + </tr> + <?php endif ?> + + <?php if (isset($rescheduleAllLink)): ?> + <tr> + <th></th> + <td> + <?= $this->qlink( + $this->translate('Reschedule'), + $rescheduleAllLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'calendar-empty' + ) + ) ?> + </td> + </tr> + <?php endif ?> + </tbody> + </table> + <h2><?= $this->translate('Feature Commands') ?></h2> + <?= $toggleFeaturesForm ?> + <?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/comments.phtml b/modules/monitoring/application/views/scripts/list/comments.phtml new file mode 100644 index 0000000..c7fb86a --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/comments.phtml @@ -0,0 +1,61 @@ +<?php if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->render('list/components/selectioninfo.phtml') ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content"> +<?php if (! $comments->hasResult()): ?> + <p><?= $this->translate('No comments found matching the filter') ?></p> +</div> +<?php return; endif ?> + <table data-base-target="_next" + class="table-row-selectable common-table multiselect" + data-icinga-multiselect-url="<?= $this->href('monitoring/comments/show') ?>" + data-icinga-multiselect-related="<?= $this->href("monitoring/comments") ?>" + data-icinga-multiselect-data="comment_id"> + <thead class="print-only"> + <tr> + <th><?= $this->translate('Type') ?></th> + <th><?= $this->translate('Comment') ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($comments->peekAhead($this->compact) as $comment): ?> + <tr href="<?= $this->href('monitoring/comment/show', array('comment_id' => $comment->id)) ?>"> + <td class="icon-col"> + <?= $this->partial('partials/comment/comment-description.phtml', array('comment' => $comment)) ?> + </td> + <td> + <?= $this->partial( + 'partials/comment/comment-detail.phtml', + array( + 'comment' => $comment, + 'delCommentForm' => isset($delCommentForm) ? $delCommentForm : null + // Form is unset if the current user lacks the respective permission + )) ?> + </td> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php if ($comments->hasMore()): ?> + <div class="dont-print action-links"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url()->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ) ?> + </div> +<?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/components/hostssummary.phtml b/modules/monitoring/application/views/scripts/list/components/hostssummary.phtml new file mode 100644 index 0000000..4b9f1cd --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/components/hostssummary.phtml @@ -0,0 +1,92 @@ +<?php +use Icinga\Module\Monitoring\Web\Widget\StateBadges; +use Icinga\Web\Url; + +// Don't fetch rows until they are actually needed to improve dashlet performance +if (! $stats instanceof stdClass) { + $stats = $stats->fetchRow(); +} +?> +<div class="hosts-summary dont-print"> + <span class="hosts-link"><?= $this->qlink( + sprintf($this->translatePlural('%u Host', '%u Hosts', $stats->hosts_total), $stats->hosts_total), + // @TODO(el): Fix that + Url::fromPath('monitoring/list/hosts')->setParams(isset($baseFilter) ? $baseFilter->getUrlParams() : array()), + null, + array('title' => sprintf( + $this->translatePlural('List %u host', 'List all %u hosts', $stats->hosts_total), + $stats->hosts_total + )) + ) ?>:</span> +<?php +$stateBadges = new StateBadges(); +$stateBadges + ->setBaseFilter(isset($baseFilter) ? $baseFilter : null) + ->setUrl('monitoring/list/hosts') + ->add( + StateBadges::STATE_UP, + $stats->hosts_up, + array( + 'host_state' => 0 + ), + 'List %u host that is currently in state UP', + 'List %u hosts which are currently in state UP', + array($stats->hosts_up) + ) + ->add( + StateBadges::STATE_DOWN, + $stats->hosts_down_unhandled, + array( + 'host_state' => 1, + 'host_handled' => 0 + ), + 'List %u host that is currently in state DOWN', + 'List %u hosts which are currently in state DOWN', + array($stats->hosts_down_unhandled) + ) + ->add( + StateBadges::STATE_DOWN_HANDLED, + $stats->hosts_down_handled, + array( + 'host_state' => 1, + 'host_handled' => 1 + ), + 'List %u host that is currently in state DOWN (Acknowledged)', + 'List %u hosts which are currently in state DOWN (Acknowledged)', + array($stats->hosts_down_handled) + ) + ->add( + StateBadges::STATE_UNREACHABLE, + $stats->hosts_unreachable_unhandled, + array( + 'host_state' => 2, + 'host_handled' => 0 + ), + 'List %u host that is currently in state UNREACHABLE', + 'List %u hosts which are currently in state UNREACHABLE', + array($stats->hosts_unreachable_unhandled) + ) + ->add( + StateBadges::STATE_UNREACHABLE_HANDLED, + $stats->hosts_unreachable_handled, + array( + 'host_state' => 2, + 'host_handled' => 1 + ), + 'List %u host that is currently in state UNREACHABLE (Acknowledged)', + 'List %u hosts which are currently in state UNREACHABLE (Acknowledged)', + array($stats->hosts_unreachable_handled) + ) + ->add( + StateBadges::STATE_PENDING, + $stats->hosts_pending, + array( + 'host_state' => 99 + ), + 'List %u host that is currently in state PENDING', + 'List %u hosts which are currently in state PENDING', + array($stats->hosts_pending) + ); +echo $stateBadges->render(); +?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml b/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml new file mode 100644 index 0000000..ec0fb85 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml @@ -0,0 +1,15 @@ +<?php +$helpMessage = $this->translate( + 'Press and hold the Ctrl key while clicking on rows to select multiple rows or press and hold the Shift key to' + .' select a range of rows', + 'Multi-selection help' +); +?> +<div class="selection-info" title="<?= $this->escape($helpMessage) ?>"> + <?= sprintf( + /// TRANSLATORS: Please leave %s as it is because the selection counter is wrapped in a span tag for updating + /// the counter via JavaScript + $this->translate('%s row(s) selected', 'Multi-selection count'), + '<span class="selection-info-count">0</span>' + ) ?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/components/servicesummary.phtml b/modules/monitoring/application/views/scripts/list/components/servicesummary.phtml new file mode 100644 index 0000000..73a3b57 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/components/servicesummary.phtml @@ -0,0 +1,118 @@ +<?php +use Icinga\Module\Monitoring\Web\Widget\StateBadges; +use Icinga\Web\Url; + +// Don't fetch rows until they are actually needed, to improve dashlet performance +if (! $stats instanceof stdClass) { + $stats = $stats->fetchRow(); +} +?> +<div class="services-summary dont-print"> + <span class="services-link"><?= $this->qlink( + sprintf($this->translatePlural( + '%u Service', '%u Services', $stats->services_total), + $stats->services_total + ), + // @TODO(el): Fix that + Url::fromPath('monitoring/list/services')->setParams(isset($baseFilter) ? $baseFilter->getUrlParams() : array()), + null, + array('title' => sprintf( + $this->translatePlural('List %u service', 'List all %u services', $stats->services_total), + $stats->services_total + )) + ) ?>:</span> +<?php +$stateBadges = new StateBadges(); +$stateBadges + ->setBaseFilter(isset($baseFilter) ? $baseFilter : null) + ->setUrl('monitoring/list/services') + ->add( + StateBadges::STATE_OK, + $stats->services_ok, + array( + 'service_state' => 0 + ), + 'List %u service that is currently in state OK', + 'List %u services which are currently in state OK', + array($stats->services_ok) + ) + ->add( + StateBadges::STATE_CRITICAL, + $stats->services_critical_unhandled, + array( + 'service_state' => 2, + 'service_handled' => 0 + ), + 'List %u service that is currently in state CRITICAL', + 'List %u services which are currently in state CRITICAL', + array($stats->services_critical_unhandled) + ) + ->add( + StateBadges::STATE_CRITICAL_HANDLED, + $stats->services_critical_handled, + array( + 'service_state' => 2, + 'service_handled' => 1 + ), + 'List %u handled service that is currently in state CRITICAL', + 'List %u handled services which are currently in state CRITICAL', + array($stats->services_critical_handled) + ) + ->add( + StateBadges::STATE_UNKNOWN, + $stats->services_unknown_unhandled, + array( + 'service_state' => 3, + 'service_handled' => 0 + ), + 'List %u service that is currently in state UNKNOWN', + 'List %u services which are currently in state UNKNOWN', + array($stats->services_unknown_unhandled) + ) + ->add( + StateBadges::STATE_UNKNOWN_HANDLED, + $stats->services_unknown_handled, + array( + 'service_state' => 3, + 'service_handled' => 1 + ), + 'List %u handled service that is currently in state UNKNOWN', + 'List %u handled services which are currently in state UNKNOWN', + array($stats->services_unknown_handled) + + ) + ->add( + StateBadges::STATE_WARNING, + $stats->services_warning_unhandled, + array( + 'service_state' => 1, + 'service_handled' => 0 + ), + 'List %u service that is currently in state WARNING', + 'List %u services which are currently in state WARNING', + array($stats->services_warning_unhandled) + ) + ->add( + StateBadges::STATE_WARNING_HANDLED, + $stats->services_warning_handled, + array( + 'service_state' => 1, + 'service_handled' => 1 + ), + 'List %u handled service that is currently in state WARNING', + 'List %u handled services which are currently in state WARNING', + array($stats->services_warning_handled) + ) + ->add( + StateBadges::STATE_PENDING, + $stats->services_pending, + array( + 'service_state' => 99 + ), + 'List %u handled service that is currently in state PENDING', + 'List %u handled services which are currently in state PENDING', + array($stats->services_pending) + ); +echo $stateBadges->render(); +?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/contactgroups.phtml b/modules/monitoring/application/views/scripts/list/contactgroups.phtml new file mode 100644 index 0000000..125aeea --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/contactgroups.phtml @@ -0,0 +1,53 @@ +<?php + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content"> +<?php if (! $contactGroups->hasResult()): ?> + <p><?= $this->translate('No contact groups found matching the filter') ?></p> +</div> +<?php return; endif ?> + <table class="common-table table-row-selectable" data-base-target="_next"> + <thead> + <tr> + <th></th> + <th><?= $this->translate('Contact Group') ?></th> + <th><?= $this->translate('Alias') ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($contactGroups as $contactGroup): ?> + <tr> + <td class="count-col"> + <span class="badge"><?= $contactGroup->contact_count ?></span> + </td> + <th> + <?= $this->qlink( + $contactGroup->contactgroup_name, + 'monitoring/list/contacts', + array('contactgroup_name' => $contactGroup->contactgroup_name), + array('title' => sprintf( + $this->translate('Show detailed information about %s'), + $contactGroup->contactgroup_name + )) + ) ?> + </th> + <td> + <?php if ($contactGroup->contactgroup_name !== $contactGroup->contactgroup_alias): ?> + <?= $contactGroup->contactgroup_alias ?> + <?php endif ?> + </td> + </tr> + <?php endforeach ?> + </tbody> + </table> +</div> diff --git a/modules/monitoring/application/views/scripts/list/contacts.phtml b/modules/monitoring/application/views/scripts/list/contacts.phtml new file mode 100644 index 0000000..42ec778 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/contacts.phtml @@ -0,0 +1,83 @@ +<?php if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content"> +<?php if (! $contacts->hasResult()): ?> + <p><?= $this->translate('No contacts found matching the filter') ?></p> +</div> +<?php return; endif ?> + <table class="common-table table-row-selectable" data-base-target="_next"> + <thead> + <tr> + <th><?= $this->translate('Name') ?></th> + <th><?= $this->translate('Email') ?></th> + <th><?= $this->translate('Pager') ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($contacts->peekAhead($this->compact) as $contact): ?> + <tr> + <th> + <?= $this->qlink( + $contact->contact_name, + 'monitoring/show/contact', + array('contact_name' => $contact->contact_name), + array( + 'title' => sprintf( + $this->translate('Show detailed information about %s'), + $contact->contact_alias + ) + ) + ) ?> + </th> + <td> + <?= $this->translate('Email') ?>: + <a href="mailto:<?= $contact->contact_email ?>" + title="<?= sprintf($this->translate('Send a mail to %s'), $contact->contact_alias) ?>" + aria-label="<?= sprintf($this->translate('Send a mail to %s'), $contact->contact_alias) ?>"> + <?= $this->escape($contact->contact_email) ?> + </a> + </td> + <td> + <?php if ($contact->contact_pager): ?> + <?= $this->escape($contact->contact_pager) ?> + <?php endif ?> + </td> + + <?php if ($contact->contact_notify_service_timeperiod): ?> + <td> + <?= $this->escape($contact->contact_notify_service_timeperiod) ?> + </td> + <?php endif ?> + + <?php if ($contact->contact_notify_host_timeperiod): ?> + <td> + <?= $this->escape($contact->contact_notify_host_timeperiod) ?> + </td> + <?php endif ?> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php if ($contacts->hasMore()): ?> + <div class="dont-print action-links"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url()->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ) ?> + </div> +<?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/downtimes.phtml b/modules/monitoring/application/views/scripts/list/downtimes.phtml new file mode 100644 index 0000000..46ce0bb --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/downtimes.phtml @@ -0,0 +1,64 @@ +<?php +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Module\Monitoring\Object\Service; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->render('list/components/selectioninfo.phtml') ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content"> +<?php if (! $downtimes->hasResult()): ?> + <p><?= $this->translate('No downtimes found matching the filter.') ?></p> +</div> +<?php return; endif ?> + <table class="common-table state-table table-row-selectable multiselect" + data-base-target="_next" + data-icinga-multiselect-url="<?= $this->href('monitoring/downtimes/show') ?>" + data-icinga-multiselect-controllers="<?= $this->href("monitoring/downtimes") ?>" + data-icinga-multiselect-data="downtime_id"> + <thead class="print-only"> + <tr> + <th><?= $this->translate('State') ?></th> + <th><?= $this->translate('Downtime') ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($downtimes->peekAhead($this->compact) as $downtime): + if (isset($downtime->service_description)) { + $this->isService = true; + $this->stateName = Service::getStateText($downtime->service_state); + } else { + $this->isService = false; + $this->stateName = Host::getStateText($downtime->host_state); + } + // Set downtime for partials + $this->downtime = $downtime; + ?> + <tr href="<?= $this->href('monitoring/downtime/show', array('downtime_id' => $downtime->id)) ?>"> + <?= $this->render('partials/downtime/downtime-header.phtml') ?> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php if ($downtimes->hasMore()): ?> + <div class="dont-print action-links"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url()->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ) ?> + </div> +<?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/eventgrid.phtml b/modules/monitoring/application/views/scripts/list/eventgrid.phtml new file mode 100644 index 0000000..8809c53 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/eventgrid.phtml @@ -0,0 +1,123 @@ +<?php +use Icinga\Data\Filter\Filter; +use Icinga\Web\Widget\Chart\HistoryColorGrid; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->form ?> +</div> +<?php endif ?> +<div class="content" data-base-target="_next"> +<?php + +$settings = array( + 'cnt_up' => array( + 'tooltip' => $this->translate('%d hosts ok on %s'), + 'color' => '#49DF96', + 'opacity' => '0.55' + ), + 'cnt_unreachable_hard' => array( + 'tooltip' => $this->translate('%d hosts unreachable on %s'), + 'color' => '#77AAFF', + 'opacity' => '0.55' + ), + 'cnt_critical_hard' => array( + 'tooltip' => $this->translate('%d services critical on %s'), + 'color' => '#ff5566', + 'opacity' => '0.9' + ), + + 'cnt_warning_hard' => array( + 'tooltip' => $this->translate('%d services warning on %s'), + 'color' => '#ffaa44', + 'opacity' => '1.0' + ), + + 'cnt_down_hard' => array( + 'tooltip' => $this->translate('%d hosts down on %s'), + 'color' => '#ff5566', + 'opacity' => '0.9' + ), + 'cnt_unknown_hard' => array( + 'tooltip' => $this->translate('%d services unknown on %s'), + 'color' => '#cc77ff', + 'opacity' => '0.7' + ), + 'cnt_ok' => array( + 'tooltip' => $this->translate('%d services ok on %s'), + 'color' => '#49DF96', + 'opacity' => '0.55' + ) +); + +$data = array(); +foreach ($summary as $entry) { + $day = $entry->day; + $value = $entry->$column; + $caption = sprintf( + $settings[$column]['tooltip'], + $value, + $this->formatDate(strtotime($day ?? '')) + ); + $linkFilter = Filter::matchAll( + Filter::expression('timestamp', '<', strtotime($day . ' 23:59:59')), + Filter::expression('timestamp', '>', strtotime($day . ' 00:00:00')), + $form->getFilter(), + $filter + ); + $data[$day] = array( + 'value' => $value, + 'caption' => $caption, + 'url' => $this->href('monitoring/list/eventhistory?' . $linkFilter->toQueryString()) + ); +} + +if (! $summary->hasResult()) { + echo $this->translate('No state changes in the selected time period.') . '</div>'; + return; +} + +$from = intval($form->getValue('from', strtotime('3 months ago'))); +$to = intval($form->getValue('to', time())); + +// don't display more than ten years, or else this will get really slow +if ($to - $from > 315360000) { + $from = $to - 315360000; +} + +$f = new DateTime(); +$f->setTimestamp($from); +$t = new DateTime(); +$t->setTimestamp($to); +$diff = $t->diff($f); +$step = 124; + +for ($i = 0; $i < $diff->days; $i += $step) { + $end = clone $f; + if ($diff->days - $i > $step) { + // full range, move last day to next chunk + $end->add(new DateInterval('P' . ($step - 1) . 'D')); + } else { + // include last day + $end->add(new DateInterval('P' . ($diff->days - $i) . 'D')); + } + $grid = new HistoryColorGrid(null, $f->getTimestamp(), $end->getTimestamp()); + $grid->setColor($settings[$column]['color']); + $grid->opacity = $settings[$column]['opacity']; + $grid->orientation = $orientation; + $grid->setData($data); + $grids[] = $grid; + + $f->add(new DateInterval('P' . $step . 'D')); +} +?> +<div style="width: 33.5em;"> +<?php foreach (array_reverse($grids) as $key => $grid): ?> + <div style=" <?= $this->orientation === 'horizontal' ? '' : 'display: inline-block; vertical-align: top; top; margin: 0.5em;' ?>"> + <?= $grid; ?> + <?= $this->orientation === 'horizontal' ? '<br />' : '' ?> + </div> +<?php endforeach ?> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/list/eventhistory.phtml b/modules/monitoring/application/views/scripts/list/eventhistory.phtml new file mode 100644 index 0000000..0573e8a --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/eventhistory.phtml @@ -0,0 +1,22 @@ +<?php + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<?= $this->partial( + 'partials/event-history.phtml', + array( + 'compact' => $this->compact, + 'history' => $history, + 'isOverview' => true, + 'translationDomain' => $this->translationDomain + ) +) ?> + diff --git a/modules/monitoring/application/views/scripts/list/hostgroup-grid.phtml b/modules/monitoring/application/views/scripts/list/hostgroup-grid.phtml new file mode 100644 index 0000000..34498d0 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/hostgroup-grid.phtml @@ -0,0 +1,173 @@ +<?php if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <div class="sort-controls-container"> + <?= $this->sortBox ?> + <a href="<?= $this->href('monitoring/list/hostgroups')->addFilter($this->filterEditor->getFilter()) ?>" class="grid-toggle-link" + title="<?= $this->translate('Toogle grid view mode') ?>"> + <?= $this->icon('th-list', null, ['class' => '-inactive']) ?> + <?= $this->icon('th-thumb-empty', null, ['class' => '-active']) ?> + </a> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content" data-base-target="_next"> +<?php /** @var \Icinga\Module\Monitoring\DataView\Hostgroup $hostGroups */ +if (! $hostGroups->hasResult()): ?> + <p><?= $this->translate('No host groups found matching the filter.') ?></p> +</div> +<?php return; endif ?> +<div class="group-grid"> +<?php foreach ($hostGroups as $hostGroup): ?> + <div class="group-grid-cell"> + <?php if ($hostGroup->hosts_down_unhandled > 0): ?> + <?= $this->qlink( + $hostGroup->hosts_down_unhandled, + $this->url('monitoring/list/hosts')->addFilter($this->filterEditor->getFilter()), + [ + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'host_handled' => 0, + 'host_state' => 1 + ], + [ + 'class' => 'state-down', + 'title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state DOWN in the host group "%s"', + 'List %u hosts which are currently in state DOWN in the host group "%s"', + $hostGroup->hosts_down_unhandled + ), + $hostGroup->hosts_down_unhandled, + $hostGroup->hostgroup_alias + ) + ] + ) ?> + <?php elseif ($hostGroup->hosts_unreachable_unhandled > 0): ?> + <?= $this->qlink( + $hostGroup->hosts_unreachable_unhandled, + $this->url('monitoring/list/hosts')->addFilter($this->filterEditor->getFilter()), + [ + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'host_handled' => 0, + 'host_state' => 2 + ], + [ + 'class' => 'state-unreachable', + 'title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state UNREACHABLE in the host group "%s"', + 'List %u hosts which are currently in state UNREACHABLE in the host group "%s"', + $hostGroup->hosts_unreachable_unhandled + ), + $hostGroup->hosts_unreachable_unhandled, + $hostGroup->hostgroup_alias + ) + ] + ) ?> + <?php elseif ($hostGroup->hosts_down_handled > 0): ?> + <?= $this->qlink( + $hostGroup->hosts_down_handled, + $this->url('monitoring/list/hosts')->addFilter($this->filterEditor->getFilter()), + [ + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'host_handled' => 1, + 'host_state' => 1 + ], + [ + 'class' => 'state-down handled', + 'title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state DOWN (Acknowledged) in the host group "%s"', + 'List %u hosts which are currently in state DOWN (Acknowledged) in the host group "%s"', + $hostGroup->hosts_down_handled + ), + $hostGroup->hosts_down_handled, + $hostGroup->hostgroup_alias + ) + ] + ) ?> + <?php elseif ($hostGroup->hosts_unreachable_handled > 0): ?> + <?= $this->qlink( + $hostGroup->hosts_unreachable_handled, + $this->url('monitoring/list/hosts')->addFilter($this->filterEditor->getFilter()), + [ + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'host_handled' => 0, + 'host_state' => 2 + ], + [ + 'class' => 'state-unreachable handled', + 'title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state UNREACHABLE (Acknowledged) in the host group "%s"', + 'List %u hosts which are currently in state UNREACHABLE (Acknowledged) in the host group "%s"', + $hostGroup->hosts_unreachable_handled + ), + $hostGroup->hosts_unreachable_handled, + $hostGroup->hostgroup_alias + ) + ] + ) ?> + <?php elseif ($hostGroup->hosts_pending > 0): ?> + <?= $this->qlink( + $hostGroup->hosts_pending, + $this->url('monitoring/list/hosts')->addFilter($this->filterEditor->getFilter()), + [ + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'host_state' => 99 + ], + [ + 'class' => 'state-pending', + 'title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state PENDING in the host group "%s"', + 'List %u hosts which are currently in state PENDING in the host group "%s"', + $hostGroup->hosts_pending + ), + $hostGroup->hosts_pending, + $hostGroup->hostgroup_alias + ) + ] + ) ?> + <?php elseif ($hostGroup->hosts_up > 0): ?> + <?= $this->qlink( + $hostGroup->hosts_up, + $this->url('monitoring/list/hosts')->addFilter($this->filterEditor->getFilter()), + [ + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'host_state' => 0 + ], + [ + 'class' => 'state-up', + 'title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state UP in the host group "%s"', + 'List %u hosts which are currently in state UP in the host group "%s"', + $hostGroup->hosts_up + ), + $hostGroup->hosts_up, + $hostGroup->hostgroup_alias + ) + ] + ) ?> + <?php else: ?> + <div class="state-none"> + 0 + </div> + <?php endif ?> + <?= $this->qlink( + $hostGroup->hostgroup_alias, + $this->url('monitoring/list/hosts')->addFilter($this->filterEditor->getFilter()), + ['hostgroup_name' => $hostGroup->hostgroup_name], + [ + 'title' => sprintf( + $this->translate('List all hosts in the group "%s"'), + $hostGroup->hostgroup_alias + ) + ] + ) ?> + </div> +<?php endforeach ?> +</div> +</div> diff --git a/modules/monitoring/application/views/scripts/list/hostgroups.phtml b/modules/monitoring/application/views/scripts/list/hostgroups.phtml new file mode 100644 index 0000000..a0592c8 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/hostgroups.phtml @@ -0,0 +1,296 @@ +<?php + +use Icinga\Module\Monitoring\Web\Widget\StateBadges; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + <a href="<?= $this->href('monitoring/list/hostgroup-grid')->addFilter(clone $this->filterEditor->getFilter()) ?>" class="grid-toggle-link" + title="<?= $this->translate('Toogle grid view mode') ?>"> + <?= $this->icon('th-list', null, ['class' => '-active']) ?> + <?= $this->icon('th-thumb-empty', null, ['class' => '-inactive']) ?> + </a> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> + +<div class="content"> +<?php /** @var \Icinga\Module\Monitoring\DataView\Hostgroup $hostGroups */ if (! $hostGroups->hasResult()): ?> + <p><?= $this->translate('No host groups found matching the filter.') ?></p> +</div> +<?php return; endif ?> + <table class="common-table table-row-selectable" data-base-target="_next"> + <thead> + <tr> + <th></th> + <th><?= $this->translate('Host Group') ?></th> + <th><?= $this->translate('Host States') ?></th> + <th></th> + <th><?= $this->translate('Service States') ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($hostGroups->peekAhead($this->compact) as $hostGroup): ?> + <tr> + <td class="count-col"> + <span class="badge"><?= $hostGroup->hosts_total ?></span> + </td> + <th> + <?= $this->qlink( + $hostGroup->hostgroup_alias, + $this + ->url('monitoring/list/hosts') + ->setParams(['hostgroup_name' => $hostGroup->hostgroup_name]) + ->addFilter($this->filterEditor->getFilter()), + ['sort' => 'host_severity'], + ['title' => sprintf( + $this->translate('List all hosts in the group "%s"'), + $hostGroup->hostgroup_alias + )] + ) ?> + </th> + <td> + <?php + $stateBadges = new StateBadges(); + $stateBadges + ->setUrl('monitoring/list/hosts') + ->setBaseFilter($this->filterEditor->getFilter()) + ->add( + StateBadges::STATE_UP, + $hostGroup->hosts_up, + array( + 'host_state' => 0, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'host_severity' + ), + 'List %u host that is currently in state UP in the host group "%s"', + 'List %u hosts which are currently in state UP in the host group "%s"', + array($hostGroup->hosts_up, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_DOWN, + $hostGroup->hosts_down_unhandled, + array( + 'host_state' => 1, + 'host_acknowledged' => 0, + 'host_in_downtime' => 0, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'host_severity' + ), + 'List %u host that is currently in state DOWN in the host group "%s"', + 'List %u hosts which are currently in state DOWN in the host group "%s"', + array($hostGroup->hosts_down_unhandled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_DOWN_HANDLED, + $hostGroup->hosts_down_handled, + array( + 'host_state' => 1, + 'host_handled' => 1, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'host_severity' + ), + 'List %u host that is currently in state DOWN (Acknowledged) in the host group "%s"', + 'List %u hosts which are currently in state DOWN (Acknowledged) in the host group "%s"', + array($hostGroup->hosts_down_handled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_UNREACHABLE, + $hostGroup->hosts_unreachable_unhandled, + array( + 'host_state' => 2, + 'host_acknowledged' => 0, + 'host_in_downtime' => 0, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'host_severity' + ), + 'List %u host that is currently in state UNREACHABLE in the host group "%s"', + 'List %u hosts which are currently in state UNREACHABLE in the host group "%s"', + array($hostGroup->hosts_unreachable_unhandled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_UNREACHABLE_HANDLED, + $hostGroup->hosts_unreachable_handled, + array( + 'host_state' => 2, + 'host_handled' => 1, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'host_severity' + ), + 'List %u host that is currently in state UNREACHABLE (Acknowledged) in the host group "%s"', + 'List %u hosts which are currently in state UNREACHABLE (Acknowledged) in the host group "%s"', + array($hostGroup->hosts_unreachable_handled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_PENDING, + $hostGroup->hosts_pending, + array( + 'host_state' => 99, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'host_severity' + ), + 'List %u host that is currently in state PENDING in the host group "%s"', + 'List %u hosts which are currently in state PENDING in the host group "%s"', + array($hostGroup->hosts_pending, $hostGroup->hostgroup_alias) + ); + echo $stateBadges->render(); + ?> + </td> + <td class="count-col"> + <?= $this->qlink( + $hostGroup->services_total, + $this + ->url('monitoring/list/services') + ->setParams(['hostgroup_name' => $hostGroup->hostgroup_name]) + ->addFilter($this->filterEditor->getFilter()), + ['sort' => 'service_severity'], + [ + 'title' => sprintf( + $this->translate('List all services of all hosts in host group "%s"'), + $hostGroup->hostgroup_alias + ), + 'class' => 'badge' + ] + ) ?> + </td> + <td> + <?php + $stateBadges = new StateBadges(); + $stateBadges + ->setUrl('monitoring/list/services') + ->setBaseFilter($this->filterEditor->getFilter()) + ->add( + StateBadges::STATE_OK, + $hostGroup->services_ok, + array( + 'service_state' => 0, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'service_severity' + ), + 'List %u service that is currently in state OK on hosts in the host group "%s"', + 'List %u services which are currently in state OK on hosts in the host group "%s"', + array($hostGroup->services_ok, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_CRITICAL, + $hostGroup->services_critical_unhandled, + array( + 'service_state' => 2, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0, + 'host_problem' => 0, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'service_severity' + ), + 'List %u service that is currently in state CRITICAL on hosts in the host group "%s"', + 'List %u services which are currently in state CRITICAL on hosts in the host group "%s"', + array($hostGroup->services_critical_unhandled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_CRITICAL_HANDLED, + $hostGroup->services_critical_handled, + array( + 'service_state' => 2, + 'service_handled' => 1, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'service_severity' + ), + 'List %u service that is currently in state CRITICAL (Acknowledged) on hosts in the host group "%s"', + 'List %u services which are currently in state CRITICAL (Acknowledged) on hosts in the host group "%s"', + array($hostGroup->services_critical_handled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_UNKNOWN, + $hostGroup->services_unknown_unhandled, + array( + 'service_state' => 3, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0, + 'host_problem' => 0, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'service_severity' + ), + 'List %u service that is currently in state UNKNOWN on hosts in the host group "%s"', + 'List %u services which are currently in state UNKNOWN on hosts in the host group "%s"', + array($hostGroup->services_unknown_unhandled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_UNKNOWN_HANDLED, + $hostGroup->services_unknown_handled, + array( + 'service_state' => 3, + 'service_handled' => 1, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'service_severity' + ), + 'List %u service that is currently in state UNKNOWN (Acknowledged) on hosts in the host group "%s"', + 'List %u services which are currently in state UNKNOWN (Acknowledged) on hosts in the host group "%s"', + array($hostGroup->services_unknown_handled, $hostGroup->hostgroup_alias) + + ) + ->add( + StateBadges::STATE_WARNING, + $hostGroup->services_warning_unhandled, + array( + 'service_state' => 1, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0, + 'host_problem' => 0, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'service_severity' + ), + 'List %u service that is currently in state WARNING on hosts in the host group "%s"', + 'List %u services which are currently in state WARNING on hosts in the host group "%s"', + array($hostGroup->services_warning_unhandled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_WARNING_HANDLED, + $hostGroup->services_warning_handled, + array( + 'service_state' => 1, + 'service_handled' => 1, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'service_severity' + ), + 'List %u service that is currently in state WARNING (Acknowledged) on hosts in the host group "%s"', + 'List %u services which are currently in state WARNING (Acknowledged) on hosts in the host group "%s"', + array($hostGroup->services_warning_handled, $hostGroup->hostgroup_alias) + ) + ->add( + StateBadges::STATE_PENDING, + $hostGroup->services_pending, + array( + 'service_state' => 99, + 'hostgroup_name' => $hostGroup->hostgroup_name, + 'sort' => 'service_severity' + ), + 'List %u service that is currently in state PENDING on hosts in the host group "%s"', + 'List %u services which are currently in state PENDING on hosts in the host group "%s"', + array($hostGroup->services_pending, $hostGroup->hostgroup_alias) + ); + echo $stateBadges->render(); + ?> + </td> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php if ($hostGroups->hasMore()): ?> + <div class="dont-print action-links"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url()->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ) ?> + </div> +<?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml new file mode 100644 index 0000000..6d7674e --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/hosts.phtml @@ -0,0 +1,106 @@ +<?php +use Icinga\Date\DateFormatter; +use Icinga\Module\Monitoring\Object\Host; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content"> +<?php if (! $hosts->hasResult()): ?> + <p><?= $this->translate('No hosts found matching the filter.') ?></p> +</div> +<?php return; endif ?> + <table data-base-target="_next" + class="table-row-selectable state-table multiselect" + data-icinga-multiselect-url="<?= $this->href('monitoring/hosts/show') ?>" + data-icinga-multiselect-controllers="<?= $this->href("monitoring/hosts") ?>" + data-icinga-multiselect-data="host"> + <thead class="print-only"> + <tr> + <th><?= $this->translate('State') ?></th> + <th><?= $this->translate('Host') ?></th> + <?php foreach($this->addColumns as $col): ?> + <th><?= $this->escape($col) ?></th> + <?php endforeach ?> + </tr> + </thead> + <tbody> + <?php foreach($hosts->peekAhead($this->compact) as $host): + $hostStateName = Host::getStateText($host->host_state); + $hostLink = $this->href('monitoring/host/show', array('host' => $host->host_name)); + $hostCheckOverdue = $host->host_next_update < time();?> + <tr<?= $hostCheckOverdue ? ' class="state-outdated"' : '' ?>> + <td class="state-col state-<?= $hostStateName ?><?= $host->host_handled ? ' handled' : '' ?>"> + <div class="state-label"> + <?php if ($hostCheckOverdue): ?> + <?= $this->icon('clock', sprintf($this->translate('Overdue %s'), DateFormatter::timeSince($host->host_next_update))) ?> + <?php endif ?> + <?= Host::getStateText($host->host_state, true) ?> + </div> + <?php if ((int) $host->host_state !== 99): ?> + <div class="state-meta"> + <?= $this->timeSince($host->host_last_state_change, $this->compact) ?> + <?php if ((int) $host->host_state > 0 && (int) $host->host_state_type === 0): ?> + <div><?= $this->translate('Soft', 'Soft state') ?> <?= $host->host_attempt ?></div> + <?php endif ?> + </div> + <?php endif ?> + </td> + <td> + <div class="state-header"> + <?= $this->iconImage()->host($host) ?> + <?= $this->qlink( + $host->host_display_name, + $hostLink, + null, + array( + 'title' => sprintf( + $this->translate('Show detailed information for host %s'), + $host->host_display_name + ), + 'class' => 'rowaction' + ) + ) ?> + <span class="state-icons"><?= $this->hostFlags($host) ?></span> + </div> + <p class="overview-plugin-output"><?= $this->pluginOutput($this->ellipsis($host->host_output, 10000), true, $host->host_check_command) ?></p> + </td> + <?php foreach($this->addColumns as $col): ?> + <?php if ($host->$col && preg_match('~^_(host|service)_([a-zA-Z0-9_]+)$~', $col, $m)): ?> + <td><?= $this->escape(\Icinga\Module\Monitoring\Object\MonitoredObject::protectCustomVars([$m[2] => $host->$col])[$m[2]]) ?></td> + <?php else: ?> + <td><?= $this->escape($host->$col) ?></td> + <?php endif ?> + <?php endforeach ?> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php if ($hosts->hasMore()): ?> + <div class="dont-print action-links"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url()->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ) ?> + </div> +<?php endif ?> +</div> +<?php if (! $this->compact): ?> +<div class="monitoring-statusbar dont-print"> + <?= $this->render('list/components/hostssummary.phtml') ?> + <?= $this->render('list/components/selectioninfo.phtml') ?> +</div> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/list/notifications.phtml b/modules/monitoring/application/views/scripts/list/notifications.phtml new file mode 100644 index 0000000..51ef432 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/notifications.phtml @@ -0,0 +1,124 @@ +<?php +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Module\Monitoring\Object\Service; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content"> +<?php if (! $notifications->hasResult()): ?> + <p><?= $this->translate('No notifications found matching the filter.') ?></p> +</div> +<?php return; endif ?> + <table data-base-target="_next" class="table-row-selectable state-table"> + <tbody> + <?php foreach ($notifications->peekAhead($this->compact) as $notification): + if (isset($notification->service_description)) { + $isService = true; + $stateLabel = Service::getStateText($notification->notification_state, true); + $stateName = Service::getStateText($notification->notification_state); + } else { + $isService = false; + $stateLabel = Host::getStateText($notification->notification_state, true); + $stateName = Host::getStateText($notification->notification_state); + } + ?> + <tr href="<?= $this->href('monitoring/event/show', ['id' => $notification->id, 'type' => 'notify']) ?>"> + <td class="state-col state-<?= $stateName ?>"> + <div class="state-label"><?= $stateLabel ?></div> + <div class="state-meta"> + <?= $this->formatDateTime($notification->notification_timestamp) ?> + </div> + </td> + <td> + <div class="state-header"> + <?php if ($isService) { + echo '<span class="service-on">'; + echo sprintf( + $this->translate('%s on %s', 'service on host'), + $this->qlink( + $notification->service_display_name, + 'monitoring/service/show', + [ + 'host' => $notification->host_name, + 'service' => $notification->service_description + ], + [ + 'title' => sprintf( + $this->translate('Show detailed information for service %s on host %s'), + $notification->service_display_name, + $notification->host_display_name + ) + ] + ), + $this->qlink( + $notification->host_display_name, + 'monitoring/host/show', + ['host' => $notification->host_name], + [ + 'title' => sprintf( + $this->translate('Show detailed information for host %s'), + $notification->host_display_name + ) + ] + ) + ); + echo '</span>'; + } else { + echo $this->qlink( + $notification->host_display_name, + 'monitoring/host/show', + ['host' => $notification->host_name], + [ + 'title' => sprintf( + $this->translate('Show detailed information for host %s'), + $notification->host_display_name + ) + ] + ); + } ?> + <?php if (! $this->contact): ?> + <div class="notification-recipient"> + <?php if ($notification->notification_contact_name): ?> + <?= sprintf( + $this->translate('Sent to %s'), + $this->qlink( + $notification->notification_contact_name, + 'monitoring/show/contact', + array('contact_name' => $notification->notification_contact_name) + ) + ) ?> + <?php else: ?> + <?= $this->translate('Not sent out to any contact') ?> + <?php endif ?> + </div> + <?php endif ?> + </div> + <p class="overview-plugin-output"><?= $this->pluginOutput($this->ellipsis($notification->notification_output, 10000), true) ?></p> + </td> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php if ($notifications->hasMore()): ?> + <div class="action-links"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url(isset($notificationsUrl) ? $notificationsUrl : null)->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ); ?> + </div> +<?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/servicegrid-flipped.phtml b/modules/monitoring/application/views/scripts/list/servicegrid-flipped.phtml new file mode 100644 index 0000000..d7b4c78 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/servicegrid-flipped.phtml @@ -0,0 +1,144 @@ +<?php +use Icinga\Data\Filter\Filter; +use Icinga\Module\Monitoring\Object\Service; +use Icinga\Web\Url; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->problemToggle ?> + <div class="sort-controls-container"> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content" data-base-target="_next"> + <?php if (empty($pivotData)): ?> + <p><?= $this->translate('No services found matching the filter.') ?></p> +</div> +<?php return; endif; +$serviceFilter = Filter::matchAny(); +foreach ($pivotData as $serviceDescription => $_) { + $serviceFilter->orFilter(Filter::where('service_description', $serviceDescription)); +} +?> +<table class="service-grid-table"> + <thead> + <tr> + <th><?= $this->partial( + 'joystickPagination.phtml', + 'default', + array( + 'flippable' => true, + 'xAxisPaginator' => $horizontalPaginator, + 'yAxisPaginator' => $verticalPaginator + ) + ) ?></th> + <?php foreach ($pivotHeader['cols'] as $hostName => $hostAlias): ?> + <th class="rotate-45"><div><span><?= $this->qlink( + $this->ellipsis($hostAlias, 24), + Url::fromPath('monitoring/list/services')->addFilter( + Filter::matchAll($serviceFilter, Filter::where('host_name', $hostName)) + ), + null, + array('title' => sprintf($this->translate('List all reported services on host %s'), $hostAlias)), + false + ) ?></span></div></th> + <?php endforeach ?> + </tr> + </thead> + <tbody> + + <?php $i = 0 ?> + <?php foreach ($pivotHeader['rows'] as $serviceDescription => $serviceDisplayName): ?> + <tr> + <th><?php + $hostFilter = Filter::matchAny(); + foreach ($pivotData[$serviceDescription] as $hostName => $_) { + $hostFilter->orFilter(Filter::where('host_name', $hostName)); + } + echo $this->qlink( + $serviceDisplayName, + Url::fromPath('monitoring/list/services')->addFilter( + Filter::matchAll($hostFilter, Filter::where('service_description', $serviceDescription)) + ), + null, + array('title' => sprintf( + $this->translate('List all services with the name "%s" on all reported hosts'), + $serviceDisplayName + )) + ); + ?></th> + <?php foreach (array_keys($pivotHeader['cols']) as $hostName): ?> + <td><?php + $service = $pivotData[$serviceDescription][$hostName]; + if ($service === null): ?> + <span aria-hidden="true">·</span> + <?php continue; endif ?> + <?php $ariaDescribedById = $this->protectId($service->host_name . '_' . $service->service_description . '_desc') ?> + <span class="sr-only" id="<?= $ariaDescribedById ?>"> + <?= $this->escape($service->service_output) ?> + </span> + <?= $this->qlink( + '', + 'monitoring/service/show', + array( + 'host' => $hostName, + 'service' => $serviceDescription + ), + array( + 'aria-describedby' => $ariaDescribedById, + 'aria-label' => sprintf( + $this->translate('Show detailed information for service %s on host %s'), + $service->service_display_name, + $service->host_display_name + ), + 'class' => 'service-grid-link state-' . Service::getStateText($service->service_state) . ($service->service_handled ? ' handled' : ''), + 'title' => $service->service_output + ) + ) ?> + </td> + <?php endforeach ?> + <?php if (! $this->compact && $this->horizontalPaginator->getPages()->pageCount > 1): ?> + <td> + <?php $expandLink = $this->qlink( + $this->translate('Load more'), + Url::fromRequest(), + array( + 'limit' => ($this->horizontalPaginator->getItemCountPerPage() + 20) + . ',' + . $this->verticalPaginator->getItemCountPerPage() + ), + array( + 'class' => 'action-link', + 'data-base-target' => '_self' + ) + ) ?> + <?= ++$i === (int) ceil(count($pivotHeader['rows']) / 2) ? $expandLink : '' ?> + </td> + <?php endif ?> + </tr> + <?php endforeach ?> + <?php if (! $this->compact && $this->verticalPaginator->getPages()->pageCount > 1): ?> + <tr> + <td colspan="<?= count($pivotHeader['cols']) + 1?>" class="service-grid-table-more"> + <?php echo $this->qlink( + $this->translate('Load more'), + Url::fromRequest(), + array( + 'limit' => $this->horizontalPaginator->getItemCountPerPage() + . ',' + . ($this->verticalPaginator->getItemCountPerPage() + 20) + ), + array( + 'class' => 'action-link', + 'data-base-target' => '_self' + ) + ) ?> + </td> + </tr> + <?php endif ?> + </tbody> +</table> +</div> diff --git a/modules/monitoring/application/views/scripts/list/servicegrid.phtml b/modules/monitoring/application/views/scripts/list/servicegrid.phtml new file mode 100644 index 0000000..d0ed4bc --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/servicegrid.phtml @@ -0,0 +1,144 @@ +<?php +use Icinga\Data\Filter\Filter; +use Icinga\Module\Monitoring\Object\Service; +use Icinga\Web\Url; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->problemToggle ?> + <div class="sort-controls-container"> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content" data-base-target="_next"> +<?php if (empty($pivotData)): ?> + <p><?= $this->translate('No services found matching the filter.') ?></p> +</div> +<?php return; endif; +$hostFilter = Filter::matchAny(); +foreach ($pivotData as $hostName => $_) { + $hostFilter->orFilter(Filter::where('host_name', $hostName)); +} +?> + <table class="service-grid-table"> + <thead> + <tr> + <th><?= $this->partial( + 'joystickPagination.phtml', + 'default', + array( + 'flippable' => true, + 'xAxisPaginator' => $horizontalPaginator, + 'yAxisPaginator' => $verticalPaginator + ) + ) ?></th> + <?php foreach ($pivotHeader['cols'] as $serviceDescription => $serviceDisplayName): ?> + <th class="rotate-45"><div><span><?= $this->qlink( + $this->ellipsis($serviceDisplayName, 24), + Url::fromPath('monitoring/list/services')->addFilter( + Filter::matchAll($hostFilter, Filter::where('service_description', $serviceDescription)) + ), + null, + array('title' => sprintf( + $this->translate('List all services with the name "%s" on all reported hosts'), + $serviceDisplayName + )), + false + ) ?></span></div></th> + <?php endforeach ?> + </tr> + </thead> + <tbody> + + <?php $i = 0 ?> + <?php foreach ($pivotHeader['rows'] as $hostName => $hostDisplayName): ?> + <tr> + <th><?php + $serviceFilter = Filter::matchAny(); + foreach ($pivotData[$hostName] as $serviceName => $_) { + $serviceFilter->orFilter(Filter::where('service_description', $serviceName)); + } + echo $this->qlink( + $hostDisplayName, + Url::fromPath('monitoring/list/services')->addFilter( + Filter::matchAll($serviceFilter, Filter::where('host_name', $hostName)) + ), + null, + array('title' => sprintf($this->translate('List all reported services on host %s'), $hostDisplayName)) + ); + ?></th> + <?php foreach (array_keys($pivotHeader['cols']) as $serviceDescription): ?> + <td> + <?php + $service = $pivotData[$hostName][$serviceDescription]; + if ($service === null): ?> + <span aria-hidden="true">·</span> + <?php continue; endif ?> + <?php $ariaDescribedById = $this->protectId($service->host_name . '_' . $service->service_description . '_desc') ?> + <span class="sr-only" id="<?= $ariaDescribedById ?>"> + <?= $this->escape($service->service_output) ?> + </span> + <?= $this->qlink( + '', + 'monitoring/service/show', + array( + 'host' => $hostName, + 'service' => $serviceDescription + ), + array( + 'aria-describedby' => $ariaDescribedById, + 'aria-label' => sprintf( + $this->translate('Show detailed information for service %s on host %s'), + $service->service_display_name, + $service->host_display_name + ), + 'class' => 'service-grid-link state-' . Service::getStateText($service->service_state) . ($service->service_handled ? ' handled' : ''), + 'title' => $service->service_output + ) + ) ?> + </td> + <?php endforeach ?> + <?php if (! $this->compact && $this->horizontalPaginator->getPages()->pageCount > 1): ?> + <td> + <?php $expandLink = $this->qlink( + $this->translate('Load more'), + Url::fromRequest(), + array( + 'limit' => ( + $this->horizontalPaginator->getItemCountPerPage() + 20) . ',' + . $this->verticalPaginator->getItemCountPerPage() + ), + array( + 'class' => 'action-link', + 'data-base-target' => '_self' + ) + ) ?> + <?= ++$i === (int) (count($pivotHeader['rows']) / 2) ? $expandLink : '' ?> + </td> + <?php endif ?> + </tr> + <?php endforeach ?> + <?php if (! $this->compact && $this->verticalPaginator->getPages()->pageCount > 1): ?> + <tr> + <td colspan="<?= count($pivotHeader['cols']) + 1?>" class="service-grid-table-more"> + <?php echo $this->qlink( + $this->translate('Load more'), + Url::fromRequest(), + array( + 'limit' => $this->horizontalPaginator->getItemCountPerPage() . ',' . + ($this->verticalPaginator->getItemCountPerPage() + 20) + ), + array( + 'class' => 'action-link', + 'data-base-target' => '_self' + ) + ) ?> + </td> + </tr> + <?php endif ?> + </tbody> + </table> +</div> diff --git a/modules/monitoring/application/views/scripts/list/servicegroup-grid.phtml b/modules/monitoring/application/views/scripts/list/servicegroup-grid.phtml new file mode 100644 index 0000000..5ea6d17 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/servicegroup-grid.phtml @@ -0,0 +1,217 @@ +<?php if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <div class="sort-controls-container"> + <?= $this->sortBox ?> + <a href="<?= $this->href('monitoring/list/servicegroups')->addFilter($this->filterEditor->getFilter()) ?>" class="grid-toggle-link" + title="<?= $this->translate('Toogle grid view mode') ?>"> + <?= $this->icon('th-list', null, ['class' => '-inactive']) ?> + <?= $this->icon('th-thumb-empty', null, ['class' => '-active']) ?> + </a> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content" data-base-target="_next"> +<?php /** @var \Icinga\Module\Monitoring\DataView\Servicegroup $serviceGroups */ +if (! $serviceGroups->hasResult()): ?> + <p><?= $this->translate('No service groups found matching the filter.') ?></p> +</div> +<?php return; endif ?> +<div class="group-grid"> +<?php foreach ($serviceGroups as $serviceGroup): ?> + <div class="group-grid-cell"> + <?php if ($serviceGroup->services_critical_unhandled > 0): ?> + <?= $this->qlink( + $serviceGroup->services_critical_unhandled, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + [ + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'service_handled' => 0, + 'service_state' => 2 + ], + [ + 'class' => 'state-critical', + 'title' => sprintf( + $this->translatePlural( + 'List %s service that is currently in state CRITICAL in service group "%s"', + 'List %s services which are currently in state CRITICAL in service group "%s"', + $serviceGroup->services_critical_unhandled + ), + $serviceGroup->services_critical_unhandled, + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + <?php elseif ($serviceGroup->services_warning_unhandled > 0): ?> + <?= $this->qlink( + $serviceGroup->services_warning_unhandled, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + [ + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'service_handled' => 0, + 'service_state' => 1 + ], + [ + 'class' => 'state-warning', + 'title' => sprintf( + $this->translatePlural( + 'List %s service that is currently in state WARNING in service group "%s"', + 'List %s services which are currently in state WARNING in service group "%s"', + $serviceGroup->services_warning_unhandled + ), + $serviceGroup->services_warning_unhandled, + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + <?php elseif ($serviceGroup->services_unknown_unhandled > 0): ?> + <?= $this->qlink( + $serviceGroup->services_unknown_unhandled, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + [ + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'service_handled' => 0, + 'service_state' => 3 + ], + [ + 'class' => 'state-unknown', + 'title' => sprintf( + $this->translatePlural( + 'List %s service that is currently in state UNKNOWN in service group "%s"', + 'List %s services which are currently in state UNKNOWN in service group "%s"', + $serviceGroup->services_unknown_unhandled + ), + $serviceGroup->services_unknown_unhandled, + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + <?php elseif ($serviceGroup->services_critical_handled > 0): ?> + <?= $this->qlink( + $serviceGroup->services_critical_handled, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + [ + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'service_handled' => 1, + 'service_state' => 2 + ], + [ + 'class' => 'state-critical handled', + 'title' => sprintf( + $this->translatePlural( + 'List %s service that is currently in state CRITICAL (Acknowledged) in service group "%s"', + 'List %s services which are currently in state CRITICAL (Acknowledged) in service group "%s"', + $serviceGroup->services_critical_handled + ), + $serviceGroup->services_critical_handled, + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + <?php elseif ($serviceGroup->services_warning_handled > 0): ?> + <?= $this->qlink( + $serviceGroup->services_warning_handled, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + [ + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'service_handled' => 1, + 'service_state' => 1 + ], + [ + 'class' => 'state-warning handled', + 'title' => sprintf( + $this->translatePlural( + 'List %s service that is currently in state WARNING (Acknowledged) in service group "%s"', + 'List %s services which are currently in state WARNING (Acknowledged) in service group "%s"', + $serviceGroup->services_warning_handled + ), + $serviceGroup->services_warning_handled, + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + <?php elseif ($serviceGroup->services_unknown_handled > 0): ?> + <?= $this->qlink( + $serviceGroup->services_unknown_handled, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + [ + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'service_handled' => 1, + 'service_state' => 3 + ], + [ + 'class' => 'state-unknown handled', + 'title' => sprintf( + $this->translatePlural( + 'List %s service that is currently in state UNKNOWN (Acknowledged) in service group "%s"', + 'List %s services which are currently in state UNKNOWN (Acknowledged) in service group "%s"', + $serviceGroup->services_unknown_handled + ), + $serviceGroup->services_unknown_handled, + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + <?php elseif ($serviceGroup->services_pending > 0): ?> + <?= $this->qlink( + $serviceGroup->services_pending, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + [ + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'service_state' => 99 + ], + [ + 'class' => 'state-pending', + 'title' => sprintf( + $this->translatePlural( + 'List %s service that is currenlty in state PENDING in service group "%s"', + 'List %s services which are currently in state PENDING in service group "%s"', + $serviceGroup->services_pending + ), + $serviceGroup->services_pending, + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + <?php elseif ($serviceGroup->services_ok > 0): ?> + <?= $this->qlink( + $serviceGroup->services_ok, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + [ + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'service_state' => 0 + ], + [ + 'class' => 'state-ok', + 'title' => sprintf( + $this->translatePlural( + 'List %s service that is currently in state OK in service group "%s"', + 'List %s services which are currently in state OK in service group "%s"', + $serviceGroup->services_ok + ), + $serviceGroup->services_ok, + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + <?php else: ?> + <div class="state-none"> + 0 + </div> + <?php endif ?> + <?= $this->qlink( + $serviceGroup->servicegroup_alias, + $this->url('monitoring/list/servicegrid')->addFilter($this->filterEditor->getFilter()), + ['servicegroup_name' => $serviceGroup->servicegroup_name], + [ + 'title' => sprintf( + $this->translate('List all services in the group "%s"'), + $serviceGroup->servicegroup_alias + ) + ] + ) ?> + </div> +<?php endforeach ?> +</div> +</div> diff --git a/modules/monitoring/application/views/scripts/list/servicegroups.phtml b/modules/monitoring/application/views/scripts/list/servicegroups.phtml new file mode 100644 index 0000000..c915b30 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/servicegroups.phtml @@ -0,0 +1,184 @@ +<?php use Icinga\Module\Monitoring\Web\Widget\StateBadges; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + <a href="<?= $this->href('monitoring/list/servicegroup-grid')->addFilter(clone $this->filterEditor->getFilter()) ?>" class="grid-toggle-link" + title="<?= $this->translate('Toogle grid view mode') ?>"> + <?= $this->icon('th-list', null, ['class' => '-active']) ?> + <?= $this->icon('th-thumb-empty', null, ['class' => '-inactive']) ?> + </a> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content"> +<?php if (! $serviceGroups->hasResult()): ?> + <p><?= $this->translate('No service groups found matching the filter.') ?></p> +</div> +<?php return; endif ?> + <table class="table-row-selectable common-table" data-base-target="_next"> + <thead> + <tr> + <th></th> + <th><?= $this->translate('Service Group') ?></th> + <th><?= $this->translate('Service States') ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($serviceGroups->peekAhead($this->compact) as $serviceGroup): ?> + <tr> + <td class="count-col"> + <span class="badge"><?= $serviceGroup->services_total ?></span> + </td> + <th> + <?= $this->qlink( + $serviceGroup->servicegroup_alias, + $this + ->url('monitoring/list/services') + ->setParams(['servicegroup_name' => $serviceGroup->servicegroup_name]) + ->addFilter($this->filterEditor->getFilter()), + ['sort' => 'service_severity'], + ['title' => sprintf($this->translate('List all services in the group "%s"'), $serviceGroup->servicegroup_alias)] + ) ?> + </th> + <td> + <?php + $stateBadges = new StateBadges(); + $stateBadges + ->setUrl('monitoring/list/services') + ->setBaseFilter($this->filterEditor->getFilter()) + ->add( + StateBadges::STATE_OK, + $serviceGroup->services_ok, + array( + 'service_state' => 0, + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'sort' => 'service_severity' + ), + 'List %s service that is currently in state OK in service group "%s"', + 'List %s services which are currently in state OK in service group "%s"', + array($serviceGroup->services_ok, $serviceGroup->servicegroup_alias) + ) + ->add( + StateBadges::STATE_CRITICAL, + $serviceGroup->services_critical_unhandled, + array( + 'service_state' => 2, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0, + 'host_problem' => 0, + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'sort' => 'service_severity' + ), + 'List %s service that is currently in state CRITICAL in service group "%s"', + 'List %s services which are currently in state CRITICAL in service group "%s"', + array($serviceGroup->services_critical_unhandled, $serviceGroup->servicegroup_alias) + ) + ->add( + StateBadges::STATE_CRITICAL_HANDLED, + $serviceGroup->services_critical_handled, + array( + 'service_state' => 2, + 'service_handled' => 1, + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'sort' => 'service_severity' + ), + 'List %s service that is currently in state CRITICAL (Acknowledged) in service group "%s"', + 'List %s services which are currently in state CRITICAL (Acknowledged) in service group "%s"', + array($serviceGroup->services_critical_handled, $serviceGroup->servicegroup_alias) + ) + ->add( + StateBadges::STATE_UNKNOWN, + $serviceGroup->services_unknown_unhandled, + array( + 'service_state' => 3, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0, + 'host_problem' => 0, + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'sort' => 'service_severity' + ), + 'List %s service that is currently in state UNKNOWN in service group "%s"', + 'List %s services which are currently in state UNKNOWN in service group "%s"', + array($serviceGroup->services_unknown_unhandled, $serviceGroup->servicegroup_alias) + ) + ->add( + StateBadges::STATE_UNKNOWN_HANDLED, + $serviceGroup->services_unknown_handled, + array( + 'service_state' => 3, + 'service_handled' => 1, + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'sort' => 'service_severity' + ), + 'List %s service that is currently in state UNKNOWN (Acknowledged) in service group "%s"', + 'List %s services which are currently in state UNKNOWN (Acknowledged) in service group "%s"', + array($serviceGroup->services_unknown_handled, $serviceGroup->servicegroup_alias) + + ) + ->add( + StateBadges::STATE_WARNING, + $serviceGroup->services_warning_unhandled, + array( + 'service_state' => 1, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0, + 'host_problem' => 0, + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'sort' => 'service_severity' + ), + 'List %s service that is currently in state WARNING in service group "%s"', + 'List %s services which are currently in state WARNING in service group "%s"', + array($serviceGroup->services_warning_unhandled, $serviceGroup->servicegroup_alias) + ) + ->add( + StateBadges::STATE_WARNING_HANDLED, + $serviceGroup->services_warning_handled, + array( + 'service_state' => 1, + 'service_handled' => 1, + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'sort' => 'service_severity' + ), + 'List %s service that is currently in state WARNING (Acknowledged) in service group "%s"', + 'List %s services which are currently in state WARNING (Acknowledged) in service group "%s"', + array($serviceGroup->services_warning_handled, $serviceGroup->servicegroup_alias) + ) + ->add( + StateBadges::STATE_PENDING, + $serviceGroup->services_pending, + array( + 'service_state' => 99, + 'servicegroup_name' => $serviceGroup->servicegroup_name, + 'sort' => 'service_severity' + ), + 'List %s service that is currenlty in state PENDING in service group "%s"', + 'List %s services which are currently in state PENDING in service group "%s"', + array($serviceGroup->services_pending, $serviceGroup->servicegroup_alias) + ); + echo $stateBadges->render(); + ?> + </td> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php if ($serviceGroups->hasMore()): ?> +<div class="dont-print action-links"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url()->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ) ?> +</div> +<?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/list/services.phtml b/modules/monitoring/application/views/scripts/list/services.phtml new file mode 100644 index 0000000..b2088e9 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/services.phtml @@ -0,0 +1,161 @@ +<?php +use Icinga\Date\DateFormatter; +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Module\Monitoring\Object\Service; + +if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->paginator ?> + <div class="sort-controls-container"> + <?= $this->limiter ?> + <?= $this->sortBox ?> + </div> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content"> +<?php if (! $services->hasResult()): ?> + <p><?= $this->translate('No services found matching the filter.') ?></p> +</div> +<?php return; endif ?> + <table data-base-target="_next" + class="table-row-selectable state-table multiselect<?php if ($this->compact): ?> compact<?php endif ?>" + data-icinga-multiselect-url="<?= $this->href('monitoring/services/show') ?>" + data-icinga-multiselect-controllers="<?= $this->href('monitoring/services') ?>" + data-icinga-multiselect-data="service,host"> + <thead class="print-only"> + <tr> + <th><?= $this->translate('State') ?></th> + <th><?= $this->translate('Service') ?></th> + <?php foreach($this->addColumns as $col): ?> + <th><?= $this->escape($col) ?></th> + <?php endforeach ?> + </tr> + </thead> + <tbody> + <?php foreach ($services->peekAhead($this->compact) as $service): + $serviceLink = $this->href( + 'monitoring/service/show', + array( + 'host' => $service->host_name, + 'service' => $service->service_description + ) + ); + $hostLink = $this->href( + 'monitoring/host/show', + array( + 'host' => $service->host_name, + ) + ); + $serviceStateName = Service::getStateText($service->service_state); + $serviceCheckOverdue = $service->service_next_update < time(); ?> + <tr<?= $serviceCheckOverdue ? ' class="state-outdated"' : '' ?>> + <td class="state-col state-<?= $serviceStateName ?><?= $service->service_handled ? ' handled' : '' ?>"> + <div class="state-label"> + <?php if ($serviceCheckOverdue): ?> + <?= $this->icon('clock', sprintf($this->translate('Overdue %s'), DateFormatter::timeSince($service->service_next_update))) ?> + <?php endif ?> + <?= Service::getStateText($service->service_state, true) ?> + </div> + <?php if ((int) $service->service_state !== 99): ?> + <div class="state-meta"> + <?= $this->timeSince($service->service_last_state_change, $this->compact) ?> + <?php if ((int) $service->service_state > 0 && (int) $service->service_state_type === 0): ?> + <div><?= $this->translate('Soft', 'Soft state') ?> <?= $service->service_attempt ?></div> + <?php endif ?> + </div> + <?php endif ?> + </td> + + <td> + <div class="state-header"> + <span class="service-on"> + <?= $this->iconImage()->service($service) ?> + <?php + if ($this->showHost) { + echo sprintf( + $this->translate('%s on %s', 'service on host'), + $this->qlink( + $service->service_display_name, + $serviceLink, + null, + array( + 'title' => sprintf( + $this->translate('Show detailed information for service %s on host %s'), + $service->service_display_name, + $service->host_display_name + ), + 'class' => 'rowaction' + ) + ), + $this->qlink( + $service->host_display_name + . ($service->host_state != 0 ? ' (' . Host::getStateText($service->host_state, true) . ')' : ''), + $hostLink, + null, + [ + 'title' => sprintf( + $this->translate('Show detailed information for host %s'), + $service->host_display_name + ) + ] + ) + ); + } else { + echo $this->qlink( + $service->service_display_name, + $serviceLink, + null, + array( + 'title' => sprintf( + $this->translate('Show detailed information for service %s on host %s'), + $service->service_display_name, + $service->host_display_name + ), + 'class' => 'rowaction' + ) + ); + } + ?> + </span> + <span class="state-icons"><?= $this->serviceFlags($service) ?></span> + </div> + <div class="overview-plugin-output-container"> + <div class="overview-performance-data"> + <?= $this->perfdata($service->service_perfdata, true, 5) ?> + </div> + <p class="overview-plugin-output"><?= $this->pluginOutput($this->ellipsis($service->service_output, 10000), true, $service->service_check_command) ?></p> + </div> + </td> + <?php foreach($this->addColumns as $col): ?> + <?php if ($service->$col && preg_match('~^_(host|service)_([a-zA-Z0-9_]+)$~', $col, $m)): ?> + <td><?= $this->escape(\Icinga\Module\Monitoring\Object\MonitoredObject::protectCustomVars([$m[2] => $service->$col])[$m[2]]) ?></td> + <?php else: ?> + <td><?= $this->escape($service->$col) ?></td> + <?php endif ?> + <?php endforeach ?> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php if ($services->hasMore()): ?> +<div class="dont-print action-links"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url()->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ) ?> +</div> +<?php endif ?> +</div> +<?php if (! $this->compact): ?> +<div class="monitoring-statusbar dont-print"> + <?= $this->render('list/components/servicesummary.phtml') ?> + <?= $this->render('list/components/selectioninfo.phtml') ?> +</div> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/object/detail-history.phtml b/modules/monitoring/application/views/scripts/object/detail-history.phtml new file mode 100644 index 0000000..692d3e4 --- /dev/null +++ b/modules/monitoring/application/views/scripts/object/detail-history.phtml @@ -0,0 +1,13 @@ +<?php + +if (! $this->compact): ?> +<div class="controls separated"> + <?= $this->tabs ?> +<?php if ($object->type === 'service') { + echo $this->render('partials/object/service-header.phtml'); +} else { + echo $this->render('partials/object/host-header.phtml'); +} ?> +</div> +<?php endif ?> +<?= $this->render('partials/event-history.phtml') ?> diff --git a/modules/monitoring/application/views/scripts/object/detail-tabhook.phtml b/modules/monitoring/application/views/scripts/object/detail-tabhook.phtml new file mode 100644 index 0000000..abcfcc1 --- /dev/null +++ b/modules/monitoring/application/views/scripts/object/detail-tabhook.phtml @@ -0,0 +1,21 @@ +<?php + +if (! $this->compact): ?> +<div class="controls separated"> + <?= $this->tabs ?> +<?php +if ($this->header === true) { + if ($object->type === 'service') { + echo $this->render('partials/object/service-header.phtml'); + } else { + echo $this->render('partials/object/host-header.phtml'); + } +} elseif ($this->header !== false) { + echo $this->header; +} +?> +</div> +<?php endif ?> +<div class="content"> + <?= $this->content ?> +</div>
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/partials/command/object-command-form.phtml b/modules/monitoring/application/views/scripts/partials/command/object-command-form.phtml new file mode 100644 index 0000000..b4e5a9c --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/command/object-command-form.phtml @@ -0,0 +1,18 @@ +<?php use Icinga\Data\Filter\Filter; ?> +<div class="controls"> +<?php if (! $this->compact): ?> + <?= $this->tabs ?> +<?php endif ?> +<?php if ($object->getType() === $object::TYPE_HOST) { + echo $this->render('partials/object/host-header.phtml'); + $this->baseFilter = Filter::where('host', $object->host_name); + $this->stats = $object->stats; + echo $this->render('list/components/servicesummary.phtml'); +} else { + echo $this->render('partials/object/service-header.phtml'); +} ?> +<?= $this->render('partials/object/quick-actions.phtml') ?> +</div> +<div class="content object-command"> + <?= $form ?> +</div> diff --git a/modules/monitoring/application/views/scripts/partials/command/objects-command-form.phtml b/modules/monitoring/application/views/scripts/partials/command/objects-command-form.phtml new file mode 100644 index 0000000..8d241ee --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/command/objects-command-form.phtml @@ -0,0 +1,15 @@ +<div class="controls"> +<?php if (! $this->compact): ?> + <?= $tabs ?> +<?php endif ?> +<?php if (isset($serviceStates)): ?> + <?= $this->render('list/components/servicesummary.phtml') ?> + <?= $this->render('partials/service/objects-header.phtml') ?> +<?php else: ?> + <?= $this->render('list/components/hostssummary.phtml') ?> + <?= $this->render('partials/host/objects-header.phtml') ?> +<?php endif ?> +</div> +<div class="content objects-command"> + <?= $form ?> +</div> diff --git a/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml b/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml new file mode 100644 index 0000000..f35680c --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml @@ -0,0 +1,24 @@ +<?php +switch ($comment->type) { + case 'flapping': + $icon = 'flapping'; + $title = $this->translate('Flapping'); + $tooltip = $this->translate('Comment was caused by a flapping host or service'); + break; + case 'comment': + $icon = 'user'; + $title = $this->translate('User Comment'); + $tooltip = $this->translate('Comment was created by an user'); + break; + case 'downtime': + $icon = 'plug'; + $title = $this->translate('Downtime'); + $tooltip = $this->translate('Comment was caused by a downtime'); + break; + case 'ack': + $icon = 'ok'; + $title = $this->translate('Acknowledgement'); + $tooltip = $this->translate('Comment was caused by an acknowledgement'); + break; +} +echo $this->icon($icon, $tooltip, array('class' => 'large-icon')); diff --git a/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml b/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml new file mode 100644 index 0000000..c603d3c --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml @@ -0,0 +1,82 @@ +<div class="comment-author"> +<?php if ($comment->objecttype === 'service') { + echo '<span class="service-on">'; + echo sprintf( + $this->translate('%s on %s', 'service on host'), + $this->qlink( + $comment->service_display_name, + 'monitoring/service/show', + [ + 'host' => $comment->host_name, + 'service' => $comment->service_description + ], + [ + 'title' => sprintf( + $this->translate('Show detailed information for service %s on host %s'), + $comment->service_display_name, + $comment->host_display_name + ) + ] + ), + $this->qlink( + $comment->host_display_name, + 'monitoring/host/show', + ['host' => $comment->host_name], + [ + 'title' => sprintf( + $this->translate('Show detailed information for host %s'), + $comment->host_display_name + ) + ] + ) + ); + echo '</span>'; +} else { + echo $this->qlink( + $comment->host_display_name, + 'monitoring/host/show', + array('host' => $comment->host_name), + array( + 'title' => sprintf( + $this->translate('Show detailed information for this comment about host %s'), + $comment->host_display_name + ) + ) + ); +} ?> + <span class="comment-time"> + <?= $this->translate('by') ?> + <?= $this->escape($comment->author) ?> + <?= $this->timeAgo($comment->timestamp) ?> + </span> + <span class="comment-icons" data-base-target="_self"> + <?= $comment->persistent ? $this->icon('attach', 'This comment is persistent') : '' ?> + <?= $comment->expiration ? $this->icon('clock', sprintf( + $this->translate('This comment expires on %s at %s'), + $this->formatDate($comment->expiration), + $this->formatTime($comment->expiration) + )) : '' ?> + <?php if (isset($delCommentForm)) { + // Form is unset if the current user lacks the respective permission + $uniqId = uniqid(); + $buttonId = 'delete-comment-' . $uniqId; + $textId = 'comment-' . $uniqId; + $deleteButton = clone $delCommentForm; + /** @var \Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm $deleteButton */ + $deleteButton->setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action dont-print'); + $deleteButton->populate( + array( + 'comment_id' => $comment->id, + 'comment_is_service' => isset($comment->service_description), + 'comment_name' => $comment->name + ) + ); + $deleteButton->getElement('btn_submit') + ->setAttrib('aria-label', $this->translate('Delete comment')) + ->setAttrib('id', $buttonId) + ->setAttrib('aria-describedby', $buttonId . ' ' . $textId); + echo $deleteButton; + } ?> + </span> +</div> +<?= $this->nl2br($this->markdownLine($comment->comment, isset($textId) ? ['id' => $textId, 'class' => 'caption'] : [ 'class' => 'caption'])) ?> diff --git a/modules/monitoring/application/views/scripts/partials/comment/comment-header.phtml b/modules/monitoring/application/views/scripts/partials/comment/comment-header.phtml new file mode 100644 index 0000000..4472479 --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/comment/comment-header.phtml @@ -0,0 +1,10 @@ +<table> + <tr> + <td class="icon-col"> + <?= $this->render('partials/comment/comment-description.phtml') ?> + </td> + <td> + <?= $this->render('partials/comment/comment-detail.phtml') ?> + </td> + </tr> +</table> diff --git a/modules/monitoring/application/views/scripts/partials/comment/comments-header.phtml b/modules/monitoring/application/views/scripts/partials/comment/comments-header.phtml new file mode 100644 index 0000000..c4c92da --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/comment/comments-header.phtml @@ -0,0 +1,32 @@ +<table> + <tbody> + <?php + foreach ($comments as $i => $comment): + if ($i === 5) { + break; + } + ?> + <tr> + <td class="icon-col"> + <?= $this->partial('partials/comment/comment-description.phtml', array('comment' => $comment)) ?> + </td> + <td> + <?= $this->partial('partials/comment/comment-detail.phtml', array('comment' => $comment)) ?> + </td> + </tr> + <?php endforeach ?> + </tbody> +</table> +<?php if ($comments->count() > 5): ?> +<p> + <?= $this->qlink( + sprintf($this->translate('List all %d comments'), $comments->count()), + $listAllLink, + null, + array( + 'data-base-target' => '_next', + 'icon' => 'down-open' + ) + ) ?> +</p> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml b/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml new file mode 100644 index 0000000..dae6caa --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml @@ -0,0 +1,101 @@ +<td class="state-col state-<?= $stateName; ?><?= $downtime->is_in_effect ? ' handled' : ''; ?>"> + <?php if ($downtime->start <= time() && ! $downtime->is_in_effect): ?> + <div class="state-label"><?= $this->translate('ENDS', 'Downtime status'); ?></div> + <div class="state-meta"><?= $this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, $this->compact, true) ?></div> + <?php else: ?> + <div class="state-label"><?= $downtime->is_in_effect ? $this->translate('EXPIRES', 'Downtime status') : $this->translate('STARTS', 'Downtime status'); ?></div> + <div class="state-meta"><?= $this->timeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start, $this->compact, true) ?></div> + <?php endif; ?> +</td> +<td> + <div class="comment-author"> + <?php if ($isService) { + echo '<span class="service-on">'; + echo sprintf( + $this->translate('%s on %s', 'service on host'), + $this->qlink( + $downtime->service_display_name, + 'monitoring/service/show', + [ + 'host' => $downtime->host_name, + 'service' => $downtime->service_description + ], + [ + 'title' => sprintf( + $this->translate('Show detailed information for service %s on host %s'), + $downtime->service_display_name, + $downtime->host_display_name + ) + ] + ), + $this->qlink( + $downtime->host_display_name, + 'monitoring/host/show', + ['host' => $downtime->host_name], + [ + 'title' => sprintf( + $this->translate('Show detailed information for host %s'), + $downtime->host_display_name + ) + ] + ) + ); + echo '</span>'; + } else { + echo $this->qlink( + $downtime->host_display_name, + 'monitoring/host/show', + array('host' => $downtime->host_name, 'downtime_id' => $downtime->id), + array( + 'title' => sprintf( + $this->translate('Show detailed information for this downtime scheduled for host %s'), + $downtime->host_display_name + ) + ) + ); + } ?> + <span class="comment-time"> + <?= $this->escape(sprintf( + $downtime->is_flexible + ? $this->translate('Flexible downtime by %s') + : $this->translate('Fixed downtime by %s'), + $downtime->author_name + )) ?> + </span> + <?php if (! $downtime->is_in_effect && $downtime->start >= time()): ?> + <span><?= sprintf($this->translate('expires %s'), $this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, false, true)) ?></span> + <?php endif ?> + <span class="comment-icons"> + <?php if ($downtime->is_flexible): ?> + <?= $this->icon('magic', $this->translate('This downtime is flexible')); ?> + <?php endif ?> + + <?php if ($downtime->is_in_effect): ?> + <?= $this->icon('plug', $this->translate('This downtime is in effect')); ?> + <?php endif ?> + + <?php if (isset($delDowntimeForm)) { + // Form is unset if the current user lacks the respective permission + $uniqId = uniqid(); + $buttonId = 'delete-downtime-' . $uniqId; + $textId = 'downtime-' . $uniqId; + $deleteButton = clone $delDowntimeForm; + /** @var \Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm $deleteButton */ + $deleteButton->setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action dont-print'); + $deleteButton->populate( + array( + 'downtime_id' => $downtime->id, + 'downtime_is_service' => isset($downtime->service_description), + 'downtime_name' => $downtime->name + ) + ); + $deleteButton->getElement('btn_submit') + ->setAttrib('aria-label', $this->translate('Delete downtime')) + ->setAttrib('id', $buttonId) + ->setAttrib('aria-describedby', $buttonId . ' ' . $textId); + echo $deleteButton; + } ?> + </span> + </div> + <?= $this->nl2br($this->markdown($downtime->comment, isset($textId) ? ['id' => $textId] : null)) ?> +</td> diff --git a/modules/monitoring/application/views/scripts/partials/downtime/downtimes-header.phtml b/modules/monitoring/application/views/scripts/partials/downtime/downtimes-header.phtml new file mode 100644 index 0000000..e2582c1 --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/downtime/downtimes-header.phtml @@ -0,0 +1,40 @@ +<?php +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Module\Monitoring\Object\Service; +?> +<table class="state-table common-table" data-base-target="_next"> + <tbody> + <?php + foreach ($this->downtimes as $i => $downtime): + if ($i > 5) { + break; + } + if ($downtime->objecttype === 'service') { + $this->isService = true; + $this->stateName = Service::getStateText($downtime->service_state); + } else { + $this->isService = false; + $this->stateName = Host::getStateText($downtime->host_state); + } + $this->downtime = $downtime; + $this->displayComment = false; + ?> + <tr> + <?= $this->render('partials/downtime/downtime-header.phtml') ?> + </tr> + <?php endforeach ?> + </tbody> +</table> +<?php if ($downtimes->count() > 5): ?> +<p> + <?= $this->qlink( + sprintf($this->translate('List all %d downtimes'), $downtimes->count()), + $listAllLink, + null, + array( + 'data-base-target' => '_next', + 'icon' => 'down-open' + ) + ) ?> +</p> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/partials/event-history.phtml b/modules/monitoring/application/views/scripts/partials/event-history.phtml new file mode 100644 index 0000000..b81c95d --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/event-history.phtml @@ -0,0 +1,267 @@ +<?php +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Module\Monitoring\Object\Service; +use Icinga\Web\Url; +use Icinga\Web\UrlParams; + +function contactsLink($match, $view) { + $links = array(); + foreach (preg_split('/,\s/', $match[1]) as $contact) { + $links[] = $view->qlink( + $contact, + 'monitoring/show/contact', + array('contact_name' => $contact), + array('title' => sprintf($view->translate('Show detailed information about %s'), $contact)) + ); + } + return '[' . implode(', ', $links) . ']'; +} + +$self = $this; + +$url = $this->url(); +$limit = (int) $url->getParam('limit', 25); +if (! $url->hasParam('page') || ($page = (int) $url->getParam('page')) < 1) { + $page = 1; +} + +/** @var \Icinga\Module\Monitoring\DataView\EventHistory $history */ +$history->limit($limit * $page); +?> +<div class="content"> +<?php +$dateFormatter = new IntlDateFormatter(setlocale(LC_TIME, 0), IntlDateFormatter::FULL, IntlDateFormatter::NONE); +$lastDate = null; +$flappingMsg = $this->translate('Flapping with a %.2f%% state change rate'); +$rowAction = Url::fromPath('monitoring/event/show'); +?> + <?php foreach ($history->peekAhead() as $event): ?> +<?php if ($lastDate === null): ?> + <table class="table-row-selectable state-table" data-base-target="_next"> + <tbody> +<?php endif; + $icon = ''; + $iconTitle = null; + $isService = isset($event->service_description); + $msg = $event->output; + $stateName = 'no-state'; + + $rowAction->setParams(new UrlParams())->addParams(array( + 'type' => $event->type, + 'id' => $event->id + )); + switch ($event->type) { + case substr($event->type, 0, 13) === 'notification_': + $rowAction->setParam('type', 'notify'); + $icon = 'bell'; + switch (substr($event->type, 13)) { + case 'state': + $iconTitle = $this->translate('State notification', 'tooltip'); + $label = $this->translate('NOTIFICATION'); + $stateName = $isService ? Service::getStateText($event->state) : Host::getStateText($event->state); + break; + case 'ack': + $iconTitle = $this->translate('Ack Notification', 'tooltip'); + $label = $this->translate('ACK NOTIFICATION'); + break; + case 'dt_start': + $iconTitle = $this->translate('Downtime start notification', 'tooltip'); + $label = $this->translate('DOWNTIME START NOTIFICATION'); + break; + case 'dt_end': + $iconTitle = $this->translate('Downtime end notification', 'tooltip'); + $label = $this->translate('DOWNTIME END NOTIFICATION'); + break; + case 'flapping': + $iconTitle = $this->translate('Flapping notification', 'tooltip'); + $label = $this->translate('FLAPPING NOTIFICATION'); + break; + case 'flapping_end': + $iconTitle = $this->translate('Flapping end notification', 'tooltip'); + $label = $this->translate('FLAPPING END NOTIFICATION'); + break; + case 'custom': + $iconTitle = $this->translate('Custom notification', 'tooltip'); + $label = $this->translate('CUSTOM NOTIFICATION'); + break; + } + $msg = $msg ? preg_replace_callback( + '/^\[([^\]]+)\]/', + function($match) use ($self) { return contactsLink($match, $self); }, + $msg + ) : $this->translate('This notification was not sent out to any contact.'); + break; + case 'comment': + $icon = 'comment-empty'; + $iconTitle = $this->translate('Comment', 'tooltip'); + $label = $this->translate('COMMENT'); + break; + case 'comment_deleted': + $icon = 'cancel'; + $iconTitle = $this->translate('Comment removed', 'tooltip'); + $label = $this->translate('COMMENT DELETED'); + break; + case 'ack': + $icon = 'ok'; + $iconTitle = $this->translate('Acknowledged', 'tooltip'); + $label = $this->translate('ACKNOWLEDGED'); + break; + case 'ack_deleted': + $icon = 'ok'; + $iconTitle = $this->translate('Acknowledgement removed', 'tooltip'); + $label = $this->translate('ACKNOWLEDGEMENT REMOVED'); + break; + case 'dt_comment': + $icon = 'plug'; + $iconTitle = $this->translate('Downtime scheduled', 'tooltip'); + $label = $this->translate('SCHEDULED DOWNTIME'); + break; + case 'dt_comment_deleted': + $icon = 'plug'; + $iconTitle = $this->translate('Downtime removed', 'tooltip'); + $label = $this->translate('DOWNTIME DELETED'); + break; + case 'flapping': + $icon = 'flapping'; + $iconTitle = $this->translate('Flapping started', 'tooltip'); + $label = $this->translate('FLAPPING'); + $msg = sprintf($flappingMsg, $msg); + break; + case 'flapping_deleted': + $icon = 'flapping'; + $iconTitle = $this->translate('Flapping stopped', 'tooltip'); + $label = $this->translate('FLAPPING STOPPED'); + $msg = sprintf($flappingMsg, $msg); + break; + case 'hard_state': + if ((int) $event->state === 0) { + $icon = 'thumbs-up'; + } else { + $icon = 'warning-empty'; + } + $iconTitle = $this->translate('Hard state', 'tooltip'); + $label = $isService ? Service::getStateText($event->state, true) : Host::getStateText($event->state, true); + $stateName = $isService ? Service::getStateText($event->state) : Host::getStateText($event->state); + break; + case 'soft_state': + $icon = 'spinner'; + $iconTitle = $this->translate('Soft state', 'tooltip'); + $label = $isService ? Service::getStateText($event->state, true) : Host::getStateText($event->state, true); + $stateName = $isService ? Service::getStateText($event->state) : Host::getStateText($event->state); + break; + case 'dt_start': + $icon = 'plug'; + $iconTitle = $this->translate('Downtime started', 'tooltip'); + $label = $this->translate('DOWNTIME START'); + break; + case 'dt_end': + $icon = 'plug'; + $iconTitle = $this->translate('Downtime ended', 'tooltip'); + $label = $this->translate('DOWNTIME END'); + break; + } ?> + <?php + $currentDate = $dateFormatter->format($event->timestamp); + if ($currentDate !== $lastDate): + $lastDate = $currentDate; + ?> + <tr> + <th colspan="2"><?= $currentDate ?></th> + </tr> + <?php endif ?> + <tr href="<?= $rowAction ?>"> + <td class="state-col state-<?= $stateName ?>"> + <?php if ($history->getIteratorPosition() % $limit === 0): ?> + <a id="page-<?= $history->getIteratorPosition() / $limit + 1 ?>"></a> + <?php endif ?> + <div class="state-label"><?= $this->escape($label) ?></div> + <div class="state-meta"><?= $this->formatTime($event->timestamp) ?></div> + </td> + <td> + <div class="history-message-container"> + <?php if ($icon): ?> + <div class="history-message-icon"> + <?= $this->icon($icon, $iconTitle) ?> + </div> + <?php endif ?> + <div class="history-message-output"> + <?php if ($this->isOverview): ?> + <?php if ($isService) { + echo '<span class="service-on">'; + echo sprintf( + $this->translate('%s on %s', 'service on host'), + $this->qlink( + $event->service_display_name, + 'monitoring/service/show', + [ + 'host' => $event->host_name, + 'service' => $event->service_description + ], + [ + 'title' => sprintf( + $this->translate('Show detailed information for service %s on host %s'), + $event->service_display_name, + $event->host_display_name + ) + ] + ), + $this->qlink( + $event->host_display_name, + 'monitoring/host/show', + ['host' => $event->host_name], + [ + 'title' => sprintf( + $this->translate('Show detailed information for host %s'), + $event->host_display_name + ) + ] + ) + ); + echo '</span>'; + } else { + echo $this->qlink( + $event->host_display_name, + 'monitoring/host/show', + ['host' => $event->host_name], + [ + 'title' => sprintf( + $this->translate('Show detailed information for host %s'), + $event->host_display_name + ) + ] + ); + } ?> + <?php endif ?> + <?= $this->nl2br($this->createTicketLinks($this->markdown($msg, ['class' => 'overview-plugin-output']))) ?> + </div> + </div> + </td> + </tr> + <?php endforeach ?> +<?php if ($lastDate !== null): ?> + </tbody> + </table> +<?php endif ?> +<?php if ($history->hasMore()): ?> + <div class="action-links"> + <?php if ($this->compact) { + echo $this->qlink( + $this->translate('Show More'), + $url->without(array('showCompact', 'limit')), + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_next' + ) + ); + } else { + echo $this->qlink( + $this->translate('Load More'), + $url->setAnchor('page-' . ($page + 1)), + array('page' => $page + 1,), + array('class' => 'action-link') + ); + } ?> + </div> +<?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml b/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml new file mode 100644 index 0000000..48141e2 --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml @@ -0,0 +1,41 @@ +<?php +use Icinga\Module\Monitoring\Object\Host; + +if (! ($hostCount = count($objects))): return; endif ?> +<table class="state-table host-detail-state"> +<tbody> +<?php foreach ($objects as $i => $host): /** @var Host $host */ + if ($i === 5) { + break; + } ?> + <tr> + <td class="state-col state-<?= Host::getStateText($host->host_state); ?><?= $host->host_handled ? ' handled' : '' ?>"> + <span class="sr-only"><?= Host::getStateText($host->host_state) ?></span> + <div class="state-meta"> + <?= $this->timeSince($host->host_last_state_change, $this->compact) ?> + </div> + </td> + <td> + <?= $this->link()->host( + $host->host_name, + $host->host_display_name + ) ?> + <?= $this->hostFlags($host) ?> + </td> + </tr> +<?php endforeach ?> +</tbody> +</table> +<?php if ($hostCount > 5): ?> +<div class="hosts-link"> + <?= $this->qlink( + sprintf($this->translate('List all %d hosts'), $hostCount), + $this->url()->setPath('monitoring/list/hosts'), + null, + array( + 'data-base-target' => '_next', + 'icon' => 'forward' + ) + ) ?> +</div> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml b/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml new file mode 100644 index 0000000..62bfd2c --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml @@ -0,0 +1,53 @@ +<div class="content" data-base-target="_next"> + <?= $this->render('show/components/output.phtml') ?> + <?= $this->render('show/components/grapher.phtml') ?> + <?= $this->render('show/components/extensions.phtml') ?> + + <h2><?= $this->translate('Problem handling') ?></h2> + <table class="name-value-table"> + <tbody> + <?= $this->render('show/components/acknowledgement.phtml') ?> + <?= $this->render('show/components/comments.phtml') ?> + <?= $this->render('show/components/downtime.phtml') ?> + <?= $this->render('show/components/notes.phtml') ?> + <?= $this->render('show/components/actions.phtml') ?> + <?= $this->render('show/components/flapping.phtml') ?> + <?php if ($object->type === 'service'): ?> + <?= $this->render('show/components/servicegroups.phtml') ?> + <?php else: ?> + <?= $this->render('show/components/hostgroups.phtml') ?> + <?php endif ?> + </tbody> + </table> + + <?= $this->render('show/components/perfdata.phtml') ?> + + <h2><?= $this->translate('Notifications') ?></h2> + <table class="name-value-table"> + <tbody> + <?= $this->render('show/components/notifications.phtml') ?> + <?php if ($this->hasPermission('*') || ! $this->hasPermission('no-monitoring/contacts')): ?> + <?= $this->render('show/components/contacts.phtml') ?> + <?php endif ?> + </tbody> + </table> + + <h2><?= $this->translate('Check execution') ?></h2> + <table class="name-value-table"> + <tbody> + <?= $this->render('show/components/command.phtml') ?> + <?= $this->render('show/components/checksource.phtml') ?> + <?= $this->render('show/components/reachable.phtml') ?> + <?= $this->render('show/components/checkstatistics.phtml') ?> + <?= $this->render('show/components/checktimeperiod.phtml') ?> + </tbody> + </table> + + <?php if (! empty($object->customvars)): ?> + <h2><?= $this->translate('Custom Variables') ?></h2> + <div id="<?= $object->type ?>-customvars" data-visible-height="200" class="collapsible"> + <?= (new \Icinga\Module\Monitoring\Web\Widget\CustomVarTable($object->customvarsWithOriginalNames, $object)) ?> + </div> + <?php endif ?> + <?= $this->render('show/components/flags.phtml') ?> +</div> diff --git a/modules/monitoring/application/views/scripts/partials/object/host-header.phtml b/modules/monitoring/application/views/scripts/partials/object/host-header.phtml new file mode 100644 index 0000000..4de4a01 --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/object/host-header.phtml @@ -0,0 +1,51 @@ +<?php +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Web\Url; + +/** @var Host $object */ + +$url = Url::fromRequest(); +$linkHostName = ! ($url->getPath() === 'monitoring/host/show' && $url->getParam('host') === $object->host_name); +?> +<table class="state-table host-detail-state"> + <tr> + <td class="state-col state-<?= Host::getStateText($object->host_state) ?><?= $object->host_handled ? ' handled' : '' ?>"> + <div class="state-header"><?= Host::getStateText($object->host_state, true) ?></div> + <div class="state-meta"> + <?= $this->timeSince($object->host_last_state_change) ?> + <?php if ((int) $object->host_state > 0 && (int) $object->host_state_type === 0): ?> + <div><?= $this->translate('Soft', 'Soft state') ?> <?= $object->host_attempt ?></div> + <?php endif ?> + </div> + </td> + <td> + <?= $this->iconImage()->host($object) ?> + <?php + if ($linkHostName) { + echo '<a href="' . Url::fromPath('monitoring/host/show', array('host' => $object->host_name)) . '">'; + } + ?> + <span class="selectable"><strong><?= $this->escape($object->host_display_name) ?></strong></span> + <?php if ($object->host_display_name !== $object->host_name): ?> + <span class="selectable host-meta">(<?= $this->escape($object->host_name) ?>)</span> + <?php endif ?> + <?php + if ($linkHostName) { + echo '</a>'; + } + ?> + <?php if ($object->host_alias !== $object->host_display_name && $object->host_alias !== $object->host_name): ?> + <div class="selectable host-meta"> + <?= $this->escape($this->translate('Alias', 'host') . ': ' . $object->host_alias) ?> + </div> + <?php endif ?> + <?= $this->hostFlags($object) ?> + <?php if ($object->host_address6 && $object->host_address6 !== $object->host_name): ?> + <div class="selectable host-meta" title="<?= $this->translate('IPv6 address') ?>"><?= $this->escape($object->host_address6) ?></div> + <?php endif ?> + <?php if ($object->host_address && $object->host_address !== $object->host_name): ?> + <div class="selectable host-meta" title="<?= $this->translate('IPv4 address') ?>"><?= $this->escape($object->host_address) ?></div> + <?php endif ?> + </td> + </tr> +</table> diff --git a/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml new file mode 100644 index 0000000..fe05a84 --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml @@ -0,0 +1,144 @@ +<div class="quick-actions"> + <ul class="nav tab-nav"> + <?php if (isset($removeAckForm)): ?> + <li> + <?php + $removeAckForm = clone $removeAckForm; + $removeAckForm->setAttrib('id', 'quickAction_' . $removeAckForm->getName()); // Avoids id duplication + $removeAckForm->setLabelEnabled(true); + echo $removeAckForm; + ?> + </li> + <?php elseif /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ ($this->hasPermission('monitoring/command/acknowledge-problem') && ! (in_array((int) $object->state, array(0, 99))) ): ?> + <li> + <?php if ($object->getType() === $object::TYPE_HOST) { + echo $this->qlink( + $this->translate('Acknowledge'), + 'monitoring/host/acknowledge-problem', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'edit', + 'title' => $this->translate( + 'Acknowledge this problem, suppress all future notifications for it and tag it as being handled' + ) + ) + ); + } else { + echo $this->qlink( + $this->translate('Acknowledge'), + 'monitoring/service/acknowledge-problem', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'edit', + 'title' => $this->translate( + 'Acknowledge this problem, suppress all future notifications for it and tag it as being handled' + ) + ) + ); + } ?> + </li> + <?php endif ?> + <?php if (isset($checkNowForm)): // Form is unset if the current user lacks the respective permission ?> + <?php ($checkNowForm = clone $checkNowForm)->setAttrib('id', 'quickAction_' . $checkNowForm->getName()); // Avoids id duplication ?> + <li><?= $checkNowForm ?></li> + <?php endif ?> + <?php if ($this->hasPermission('monitoring/command/comment/add')): ?> + <li> + <?php if ($object->getType() === $object::TYPE_HOST) { + echo $this->qlink( + $this->translate('Comment'), + 'monitoring/host/add-comment', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'comment-empty', + 'title' => $this->translate('Add a new comment to this host') + ) + ); + } else { + echo $this->qlink( + $this->translate('Comment'), + 'monitoring/service/add-comment', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'comment-empty', + 'title' => $this->translate('Add a new comment to this service') + ) + ); + } ?> + </li> + <?php endif ?> + <?php if ($this->hasPermission('monitoring/command/send-custom-notification')): ?> + <li> + <?php if ($object->getType() === $object::TYPE_HOST) { + echo $this->qlink( + $this->translate('Notification'), + 'monitoring/host/send-custom-notification', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'bell', + 'title' => $this->translate( + 'Send a custom notification to contacts responsible for this host' + ) + ) + ); + } else { + echo $this->qlink( + $this->translate('Notification'), + 'monitoring/service/send-custom-notification', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'bell', + 'title' => $this->translate( + 'Send a custom notification to contacts responsible for this service' + ) + ) + ); + } ?> + </li> + <?php endif ?> + <?php if ($this->hasPermission('monitoring/command/downtime/schedule')): ?> + <li><?php if ($object->getType() === $object::TYPE_HOST) { + echo $this->qlink( + $this->translate('Downtime'), + 'monitoring/host/schedule-downtime', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'plug', + 'title' => $this->translate( + 'Schedule a downtime to suppress all problem notifications within a specific period of time' + ) + ) + ); + } else { + echo $this->qlink( + $this->translate('Downtime'), + 'monitoring/service/schedule-downtime', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'plug', + 'title' => $this->translate( + 'Schedule a downtime to suppress all problem notifications within a specific period of time' + ) + ) + ); + } ?> + </li> + <?php endif ?> + </ul> +</div> diff --git a/modules/monitoring/application/views/scripts/partials/object/service-header.phtml b/modules/monitoring/application/views/scripts/partials/object/service-header.phtml new file mode 100644 index 0000000..318fe49 --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/object/service-header.phtml @@ -0,0 +1,72 @@ +<?php +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Module\Monitoring\Object\Service; +use Icinga\Web\Url; + +/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + +$url = Url::fromRequest(); +$linkServiceName = ! ($url->getPath() === 'monitoring/service/show' && $url->getParam('service') === $object->service_description); +?> +<table class="state-table service-detail-state"> + <tr> + <td class="state-col state-<?= Host::getStateText($object->host_state) ?><?= $object->host_handled ? ' handled' : '' ?>"> + <div class="state-label"><?= Host::getStateText($object->host_state, true) ?></div> + <div class="state-meta"> + <?= $this->timeSince($object->host_last_state_change) ?> + <?php if ((int) $object->host_state > 0 && (int) $object->host_state_type === 0): ?> + <div><?= $this->translate('Soft', 'Soft state') ?> <?= $object->host_attempt ?></div> + <?php endif ?> + </div> + </td> + <td> + <?= $this->iconImage()->host($object) ?> + <a href="<?= Url::fromPath('monitoring/host/show', array('host' => $object->host_name)) ?>"> + <span class="selectable"><strong><?= $this->escape($object->host_display_name) ?></strong></span> + <?php if ($object->host_display_name !== $object->host_name): ?> + <span class="selectable host-meta">(<?= $this->escape($object->host_name) ?>)</span> + <?php endif ?> + </a> + <?= $this->hostFlags($object) ?> + <?php if ($object->host_address6 && $object->host_address6 !== $object->host_name): ?> + <div class="selectable host-meta" title="<?= $this->translate('IPv6 address') ?>"><?= $this->escape($object->host_address6) ?></div> + <?php endif ?> + <?php if ($object->host_address && $object->host_address !== $object->host_name): ?> + <div class="selectable host-meta" title="<?= $this->translate('IPv4 address') ?>"><?= $this->escape($object->host_address) ?></div> + <?php endif ?> + </td> + </tr> + <tr> + <td class="state-col state-<?= Service::getStateText($object->service_state) ?><?= $object->service_handled ? ' handled' : '' ?>"> + <div class="state-label"><?= Service::getStateText($object->service_state, true) ?></div> + <div class="state-meta"> + <?= $this->timeSince($object->service_last_state_change) ?> + <?php if ((int) $object->service_state > 0 && (int) $object->service_state_type === 0): ?> + <div><?= $this->translate('Soft', 'Soft state') ?> <?= $object->service_attempt ?></div> + <?php endif ?> + </div> + </td> + <td> + <?= $this->iconImage()->service($object) ?> + <?= $this->translate('Service') ?>: + <?php + if ($linkServiceName) { + echo '<a href="' . Url::fromPath('monitoring/service/show', array( + 'host' => $object->host_name, + 'service' => $object->service_description + )) . '">'; + } + ?> + <span class="selectable"><strong><?= $this->escape($object->service_display_name) ?></strong></span> + <?php if ($object->service_display_name !== $object->service_description): ?> + <span class="selectable service-meta">(<?= $this->escape($object->service_description) ?>)</span> + <?php endif ?> + <?php + if ($linkServiceName) { + echo '</a>'; + } + ?> + <?= $this->serviceFlags($object) ?> + </td> + </tr> +</table> diff --git a/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml b/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml new file mode 100644 index 0000000..d342d87 --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml @@ -0,0 +1,45 @@ +<?php +use Icinga\Module\Monitoring\Object\Host; +use Icinga\Module\Monitoring\Object\Service; + +if (! ($serviceCount = count($objects))): return; endif ?> +<table class="state-table service-detail-state"> +<tbody> +<?php foreach ($objects as $i => $service): /** @var Service $service */ + if ($i === 5) { + break; + } ?> + <tr> + <td class="state-col state-<?= Service::getStateText($service->service_state) ?><?= $service->service_handled ? ' handled' : '' ?>"> + <span class="sr-only"><?= Service::getStateText($service->service_state) ?></span> + <div class="state-meta"> + <?= $this->timeSince($service->service_last_state_change, $this->compact) ?> + </div> + </td> + <td> + <?= $this->link()->service( + $service->service_description, + $service->service_display_name, + $service->host_name, + $service->host_display_name + . ($service->host_state != 0 ? ' (' . Host::getStateText($service->host_state, true) . ')' : '') + ) ?> + <?= $this->serviceFlags($service) ?> + </td> + </tr> +<?php endforeach ?> +</tbody> +</table> +<?php if ($serviceCount > 5): ?> +<div class="services-link"> + <?= $this->qlink( + sprintf($this->translate('List all %d services'), $serviceCount), + $this->url()->setPath('monitoring/list/services'), + null, + array( + 'data-base-target' => '_next', + 'icon' => 'forward' + ) + ) ?> +</div> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/partials/show-more.phtml b/modules/monitoring/application/views/scripts/partials/show-more.phtml new file mode 100644 index 0000000..fd6a99d --- /dev/null +++ b/modules/monitoring/application/views/scripts/partials/show-more.phtml @@ -0,0 +1,15 @@ +<?php +/** @var \Icinga\Module\Monitoring\DataView\DataView $dataView */ +if ($dataView->hasMore()): ?> +<div class="text-right"> + <?= $this->qlink( + $this->translate('Show More'), + $this->url()->without(array('showCompact', 'limit')), + null, + array( + 'data-base-target' => '_next', + 'class' => 'action-link' + ) + ) ?> +</div> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/service/show.phtml b/modules/monitoring/application/views/scripts/service/show.phtml new file mode 100644 index 0000000..bc9c612 --- /dev/null +++ b/modules/monitoring/application/views/scripts/service/show.phtml @@ -0,0 +1,8 @@ +<div class="controls controls-separated"> +<?php if (! $this->compact): ?> + <?= $this->tabs ?> +<?php endif ?> + <?= $this->render('partials/object/service-header.phtml') ?> + <?= $this->render('partials/object/quick-actions.phtml') ?> +</div> +<?= $this->render('partials/object/detail-content.phtml') ?> diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml new file mode 100644 index 0000000..e9fb56f --- /dev/null +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -0,0 +1,208 @@ +<div class="controls"> + + <?php if (! $this->compact): ?> + <?= $tabs ?> + <?php endif ?> + <?= $this->render('list/components/servicesummary.phtml') ?> + <?= $this->render('partials/service/objects-header.phtml') ?> + <?php + $serviceCount = count($objects); + $unhandledCount = count($unhandledObjects); + $problemCount = count($problemObjects); + $unackCount = count($unacknowledgedObjects); + $scheduledDowntimeCount = count($objects->getScheduledDowntimes()); + ?> +</div> + +<div class="content"> + + <?php if ($serviceCount === 0): ?> + <?= $this->translate('No services found matching the filter') ?> + <?php else: ?> + <?= $this->render('show/components/extensions.phtml') ?> + <h2> <?= $this->translate('Problem handling') ?> </h2> + <table class="name-value-table"> + <tbody> + <?php if ($unackCount > 0): ?> + <tr> + <th> <?= sprintf($this->translate('%d unhandled problems'), $unackCount) ?> </th> + <td> <?= $this->qlink( + $this->translate('Acknowledge'), + $acknowledgeLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'check' + ) + ) ?> </td> + </tr> + <?php endif; ?> + + <?php if (($acknowledgedCount = count($acknowledgedObjects)) > 0): ?> + <tr> + <th> <?= sprintf( + $this->translatePlural( + '%s acknowledgement', + '%s acknowledgements', + $acknowledgedCount + ), + '<b>' . $acknowledgedCount . '</b>' + ) ?> + </th> + <td> + <?= $removeAckForm->setLabelEnabled(true) ?> + </td> + </tr> + <?php endif ?> + + <tr> + <th> <?= $this->translate('Comments') ?> </th> + <td> + <?= $this->qlink( + $this->translate('Add comments'), + $addCommentLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'comment-empty' + ) + ) ?> + </td> + </tr> + + <?php if (($commentCount = count($objects->getComments())) > 0): ?> + <tr> + <th></th> + <td> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%s comment', + '%s comments', + $commentCount + ), + $commentCount + ), + $commentsLink, + null, + array('data-base-target' => '_next') + ) ?> + </td> + </tr> + <?php endif ?> + + <tr> + <th> + <?= $this->translate('Downtimes') ?> + </th> + <td> + <?= $this->qlink( + $this->translate('Schedule downtimes'), + $downtimeAllLink, + null, + array( + 'icon' => 'plug', + 'class' => 'action-link' + ) + ) ?> + </td> + </tr> + + <?php if ($scheduledDowntimeCount > 0): ?> + <tr> + <th></th> + <td> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%d scheduled downtime', + '%d scheduled downtimes', + $scheduledDowntimeCount + ), + $scheduledDowntimeCount + ), + $showDowntimesLink, + null, + array( + 'data-base-target' => '_next' + ) + ) ?> + </td> + </tr> + <?php endif ?> + + </tbody> + </table> + + <?php if ($this->hasPermission('monitoring/command/send-custom-notification')): ?> + + <h2> <?= $this->translate('Notifications') ?> </h2> + + <table class="name-value-table"> + <tbody> + <tr> + <th> <?= $this->translate('Notifications') ?> </th> + <td> + <?= $this->qlink( + $this->translate('Send notifications'), + $sendCustomNotificationLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'bell' + ) + ) ?> + </td> + </tr> + </tbody> + </table> + <?php endif ?> + + <h2> <?= $this->translate('Check Execution') ?> </h2> + + <table class="name-value-table"> + <tbody> + <tr> + <th> <?= $this->translate('Command') ?> </th> + <td> + <?= $this->qlink( + $this->translate('Process check result'), + $processCheckResultAllLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'edit' + ) + ) ?> + </td> + </tr> + + <?php if (isset($checkNowForm)): // Form is unset if the current user lacks the respective permission ?> + <tr> + <th> <?= $this->translate('Schedule Check') ?> </th> + <td> <?= $checkNowForm ?> </td> + </tr> + <?php endif ?> + + <?php if (isset($rescheduleAllLink)): ?> + <tr> + <th></th> + <td> + <?= $this->qlink( + $this->translate('Reschedule'), + $rescheduleAllLink, + null, + array( + 'class' => 'action-link', + 'icon' => 'calendar-empty' + ) + ) ?> + </td> + </tr> + <?php endif ?> + </tbody> + </table> + <h2><?= $this->translate('Feature Commands') ?></h2> + <?= $toggleFeaturesForm ?> + <?php endif ?> +</div> diff --git a/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml b/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml new file mode 100644 index 0000000..fd7f6bb --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml @@ -0,0 +1,94 @@ +<?php + +/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + +if (in_array((int) $object->state, array(0, 99))) { + // Ignore this markup if the object is in a non-problem state or pending + return; +} + +if ($object->acknowledged): +$acknowledgement = $object->acknowledgement; +/** @var \Icinga\Module\Monitoring\Object\Acknowledgement $acknowledgement */ +?> +<tr> + <th><?= $this->translate('Acknowledged') ?></th> + <td data-base-target="_self"> + <?php if ($acknowledgement): ?> + <dl class="comment-list"> + <dt> + <?= $this->escape($acknowledgement->getAuthor()) ?> + <span class="comment-time"> + <?= $this->translate('acknowledged') ?> + <?= $this->timeAgo($acknowledgement->getEntryTime()) ?> + <?php if ($acknowledgement->expires()): ?> + <span aria-hidden="true">ǀ</span> + <?= sprintf( + $this->translate('Expires %s'), + $this->timeUntil($acknowledgement->getExpirationTime()) + ) ?> + <?php endif ?> + </span> + <?php if ($acknowledgement->getSticky()): ?> + <?= $this->icon('pin', sprintf( + $this->translate( + 'Acknowledgement remains until the %1$s recovers even if the %1$s changes state' + ), + $object->getType(true) + )) ?> + <?php endif ?> + <?php if (isset($removeAckForm)) { + // Form is unset if the current user lacks the respective permission + $removeAckForm->setAttrib('class', $removeAckForm->getAttrib('class') . ' remove-action'); + echo $removeAckForm; + } ?> + </dt> + <dd> + <?= $this->nl2br($this->createTicketLinks($this->markdown($acknowledgement->getComment()))) ?> + </dd> + </dl> + <?php elseif (isset($removeAckForm)): ?> + <?= $removeAckForm ?> + <?php endif ?> + </td> +</tr> +<?php else: ?> +<tr> + <th><?= $this->translate('Not acknowledged') ?></th> + <td> + <?php if ($this->hasPermission('monitoring/command/acknowledge-problem')) { + if ($object->getType() === $object::TYPE_HOST) { + $ackLink = $this->href( + 'monitoring/host/acknowledge-problem', + array('host' => $object->getName()), + null, + array('class' => 'action-link') + ); + } else { + $ackLink = $this->href( + 'monitoring/service/acknowledge-problem', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + null, + array('class' => 'action-link') + ); + } + ?> + <?= $this->qlink( + $this->translate('Acknowledge'), + $ackLink, + null, + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'edit', + 'title' => $this->translate( + 'Acknowledge this problem, suppress all future notifications for it and tag it as being handled' + ) + ) + ) ?> + <?php } else { + echo '-'; + } // endif ?> + </td> +</tr> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/show/components/actions.phtml b/modules/monitoring/application/views/scripts/show/components/actions.phtml new file mode 100644 index 0000000..938ab2a --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/actions.phtml @@ -0,0 +1,43 @@ +<?php + +use Icinga\Web\Navigation\Navigation; + +$navigation = new Navigation(); +$navigation->load($object->getType() . '-action'); +foreach ($navigation as $item) { + $item->setObject($object); +} + +foreach ($object->getActionUrls() as $i => $link) { + $navigation->addItem( + + // add warning to links that open in new tabs to improve accessibility, as recommended by WCAG20 G201 + $this->icon( + 'forward', + $this->translate('Link opens in new window'), + array('aria-label' => $this->translate('Link opens in new window')) + ) . ' Action ' . ($i + 1), + array( + 'url' => $link, + 'target' => '_blank', + 'renderer' => array( + 'NavigationItemRenderer', + 'escape_label' => false + ) + ) + ); +} + +if (isset($this->actions)) { + $navigation->merge($this->actions); +} + +if ($navigation->isEmpty() || ! $navigation->hasRenderableItems()) { + return; +} + +?> +<tr> + <th><?= $this->translate('Actions'); ?></th> + <?= $navigation->getRenderer()->setElementTag('td')->setCssClass('actions go-ahead'); ?> +</tr> diff --git a/modules/monitoring/application/views/scripts/show/components/checksource.phtml b/modules/monitoring/application/views/scripts/show/components/checksource.phtml new file mode 100644 index 0000000..ac9799f --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/checksource.phtml @@ -0,0 +1,6 @@ +<?php if ($object->check_source !== null): ?> +<tr> + <th><?= $this->translate('Check Source') ?></th> + <td><?= $this->escape($object->check_source) ?></td> +</tr> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml new file mode 100644 index 0000000..e37e30a --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml @@ -0,0 +1,85 @@ +<?php +/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ +$activeChecksEnabled = (bool) $object->active_checks_enabled; +?> + +<tr> + <th><?= $activeChecksEnabled ? $this->translate('Last check') : $this->translate('Last update') ?></th> + <td data-base-target="_self"> +<?php if ((int) $object->state !== 99): ?> + <?= $this->timeAgo($object->last_check) ?> + <?php if ($object->next_update < time()): ?> + <?= $this->icon('circle', $this->translate('Check result is late'), array('class' => 'icon-stateful state-critical')) ?> + <?php endif ?> +<?php endif ?> + <?php if (isset($checkNowForm)) { // Form is unset if the current user lacks the respective permission + echo $checkNowForm; + } ?> + </td> +</tr> + +<tr> + <th><?= $activeChecksEnabled ? $this->translate('Next check') : $this->translate('Next update') ?></th> + <td> + <?php if ((int) $object->state !== 99) { + if ($activeChecksEnabled) { + echo $this->timeUntil($object->next_check); + } else { + echo sprintf($this->translate('expected %s'), $this->timeUntil($object->next_update)); + } + } ?> + <?php if ($activeChecksEnabled && $this->hasPermission('monitoring/command/schedule-check')) { + if ($object->getType() === $object::TYPE_SERVICE) { + echo $this->qlink( + $this->translate('Reschedule'), + 'monitoring/service/reschedule-check', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'calendar-empty', + 'title' => $this->translate( + 'Schedule the next active check at a different time than the current one' + ) + ) + ); + } else { + echo $this->qlink( + $this->translate('Reschedule'), + 'monitoring/host/reschedule-check', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'calendar-empty', + 'title' => $this->translate( + 'Schedule the next active check at a different time than the current one' + ) + ) + ); + } + } ?> + </td> +</tr> + +<tr> + <th><?= $this->translate('Check attempts') ?></th> + <td> + <?= $object->attempt ?> + (<?= (int) $object->state_type === 0 ? $this->translate('soft state') : $this->translate('hard state') ?>) + </td> +</tr> + +<?php if ($object->check_execution_time): ?> +<tr> + <th><?= $this->translate('Check execution time') ?></th> + <td><?= round((float) $object->check_execution_time, 3) ?>s</td> +</tr> +<?php endif ?> + +<?php if ($object->check_latency): ?> +<tr> + <th><?= $this->translate('Check latency') ?></th> + <td><?= $object->check_latency ?>s</td> +</tr> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/show/components/checktimeperiod.phtml b/modules/monitoring/application/views/scripts/show/components/checktimeperiod.phtml new file mode 100644 index 0000000..34c4eb9 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/checktimeperiod.phtml @@ -0,0 +1,21 @@ +<?php if (isset($object->service_check_timeperiod)): ?> + +<tr> + <th><?= $this->translate('Check Timeperiod') ?></th> + <td> + <?= $object->service_check_timeperiod ?> + </td> +</tr> + +<?php endif ?> + +<?php if (isset($object->host_check_timeperiod)): ?> + + <tr> + <th><?= $this->translate('Check Timeperiod') ?></th> + <td> + <?= $object->host_check_timeperiod ?> + </td> + </tr> + +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/show/components/command.phtml b/modules/monitoring/application/views/scripts/show/components/command.phtml new file mode 100644 index 0000000..9b51458 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/command.phtml @@ -0,0 +1,52 @@ +<?php +$parts = explode('!', $object->check_command); +$command = array_shift($parts); + +if ($showInstance): ?> +<tr> + <th><?= $this->translate('Instance') ?></th> + <td><?= $this->escape($object->instance_name) ?></td> +</tr> +<?php endif ?> +<tr> + <th><?= $this->translate('Command') ?></th> + <td> + <?= $this->escape($command) ?> + <?php if ($this->hasPermission('monitoring/command/process-check-result') && $object->passive_checks_enabled) { + $title = sprintf( + $this->translate('Submit a one time or so called passive result for the %s check'), $command + ); + if ($object->getType() === $object::TYPE_HOST) { + echo $this->qlink( + $this->translate('Process check result'), + 'monitoring/host/process-check-result', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'edit', + 'title' => $title + ) + ); + } else { + echo $this->qlink( + $this->translate('Process check result'), + 'monitoring/service/process-check-result', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'edit', + 'title' => $title + ) + ); + } + } ?> + </td> +</tr> + +<?php +$row = "<tr>\n <th>%s</th>\n <td>%s</td>\n</tr>\n"; +for ($i = 0; $i < count($parts); $i++) { + printf($row, '$ARG' . ($i + 1) . '$', $this->escape($parts[$i])); +} diff --git a/modules/monitoring/application/views/scripts/show/components/comments.phtml b/modules/monitoring/application/views/scripts/show/components/comments.phtml new file mode 100644 index 0000000..fd980ee --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/comments.phtml @@ -0,0 +1,86 @@ +<?php +$addLink = false; +if ($this->hasPermission('monitoring/command/comment/add')) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + if ($object->getType() === $object::TYPE_HOST) { + $addLink = $this->qlink( + $this->translate('Add comment'), + 'monitoring/host/add-comment', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'comment-empty', + 'title' => $this->translate('Add a new comment to this host') + ) + ); + } else { + $addLink = $this->qlink( + $this->translate('Add comment'), + 'monitoring/service/add-comment', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'comment-empty', + 'title' => $this->translate('Add a new comment to this service') + ) + ); + } +} +if (empty($object->comments) && ! $addLink) { + return; +} +?> +<tr> + <th><?php + echo $this->translate('Comments'); + if (! empty($object->comments) && $addLink) { + echo '<br>' . $addLink; + } + ?></th> + <td data-base-target="_self"> + <?php if (empty($object->comments)): + echo $addLink; + else: ?> + <dl class="comment-list"> + <?php foreach ($object->comments as $comment): ?> + <dt> + <a data-base-target="_next" href="<?= $this->href('monitoring/comment/show', array('comment_id' => $comment->id)) ?>"> + <?= $this->escape($comment->author) ?> + <span class="comment-time"> + <?= $this->translate('commented') ?> + <?= $this->timeAgo($comment->timestamp) ?> + <?php if ($comment->expiration): ?> + <span aria-hidden="true">ǀ</span> + <?= sprintf( + $this->translate('Expires %s'), + $this->timeUntil($comment->expiration) + ) ?> + <?php endif ?> + </span> + </a> + <?= $comment->persistent ? $this->icon('attach', 'This comment is persistent.') : '' ?> + <?php if (isset($delCommentForm)) { + // Form is unset if the current user lacks the respective permission + $deleteButton = clone($delCommentForm); + /** @var \Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm $deleteButton */ + $deleteButton->setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action'); + $deleteButton->populate( + array( + 'comment_id' => $comment->id, + 'comment_is_service' => isset($comment->service_description), + 'comment_name' => $comment->name + ) + ); + echo $deleteButton; + } ?> + </dt> + <dd> + <?= $this->nl2br($this->createTicketLinks($this->markdownLine($comment->comment, [ 'class' => 'caption']))) ?> + </dd> + <?php endforeach ?> + </dl> + <?php endif ?> + </td> +</tr> diff --git a/modules/monitoring/application/views/scripts/show/components/contacts.phtml b/modules/monitoring/application/views/scripts/show/components/contacts.phtml new file mode 100644 index 0000000..5661c1a --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/contacts.phtml @@ -0,0 +1,38 @@ +<?php + +if ($object->contacts->hasResult()) { + + $list = array(); + foreach ($object->contacts as $contact) { + $list[] = $this->qlink( + $contact->contact_alias, + 'monitoring/show/contact', + array('contact_name' => $contact->contact_name), + array('title' => sprintf($this->translate('Show detailed information about %s'), $contact->contact_alias)) + ); + } + + printf( + "<tr><th>%s</th><td class=\"go-ahead\">%s</td></tr>\n", + $this->translate('Contacts'), + implode(', ', $list) + ); +} + +if ($object->contactgroups->hasResult()) { + $list = array(); + foreach ($object->contactgroups as $contactgroup) { + $list[] = $this->qlink( + $contactgroup->contactgroup_alias, + 'monitoring/list/contactgroups', + array('contactgroup_name' => $contactgroup->contactgroup_name), + array('title' => sprintf($this->translate('List contacts in contact-group "%s"'), $contactgroup->contactgroup_alias)) + ); + } + + printf( + "<tr><th>%s</th><td class=\"go-ahead\">%s</td></tr>\n", + $this->translate('Contactgroups'), + implode(', ', $list) + ); +} diff --git a/modules/monitoring/application/views/scripts/show/components/downtime.phtml b/modules/monitoring/application/views/scripts/show/components/downtime.phtml new file mode 100644 index 0000000..618d4d9 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/downtime.phtml @@ -0,0 +1,109 @@ +<?php +$addLink = false; +if ($this->hasPermission('monitoring/command/downtime/schedule')) { + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + if ($object->getType() === $object::TYPE_HOST) { + $addLink = $this->qlink( + $this->translate('Schedule downtime'), + 'monitoring/host/schedule-downtime', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'plug', + 'title' => $this->translate( + 'Schedule a downtime to suppress all problem notifications within a specific period of time' + ) + ) + ); + } else { + $addLink = $this->qlink( + $this->translate('Schedule downtime'), + 'monitoring/service/schedule-downtime', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'plug', + 'title' => $this->translate( + 'Schedule a downtime to suppress all problem notifications within a specific period of time' + ) + ) + ); + } +} +if (empty($object->downtimes) && ! $addLink) { + return; +} +?> +<tr> + <th><?php + echo $this->translate('Downtimes'); + if (! empty($object->downtimes) && $addLink) { + echo '<br>' . $addLink; + } + ?></th> + <td data-base-target="_self"> + <?php if (empty($object->downtimes)): + echo $addLink; + else: ?> + <dl class="comment-list"> + <?php foreach ($object->downtimes as $downtime): + if ((bool) $downtime->is_in_effect) { + $state = sprintf( + $this->translate('expires %s', 'Last format parameter represents the downtime expire time'), + $this->timeUntil($downtime->end, false, true) + ); + } else { + if ($downtime->start <= time()) { + $state = sprintf( + $this->translate('ends %s', 'Last format parameter represents the end time'), + $this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, false, true) + ); + } else { + $state = sprintf( + $this->translate('scheduled %s', 'Last format parameter represents the time scheduled'), + $this->timeUntil($downtime->start, false, true) + ) . ' ' . sprintf( + $this->translate('expires %s', 'Last format parameter represents the downtime expire time'), + $this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, false, true) + ); + } + } + ?> + <dt> + <?= $this->escape(sprintf( + $downtime->is_flexible + ? $this->translate('Flexible downtime by %s') + : $this->translate('Fixed downtime by %s'), + $downtime->author_name + )) ?> + <span class="comment-time"> + <?= $state ?> + <span aria-hidden="true">ǀ</span> + <?= $this->translate('created') ?> + <?= $this->timeAgo($downtime->entry_time) ?> + </span> + <?php if (isset($delDowntimeForm)) { + // Form is unset if the current user lacks the respective permission + $deleteButton = clone($delDowntimeForm); + /** @var \Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm $deleteButton */ + $deleteButton->setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action'); + $deleteButton->populate( + array( + 'downtime_id' => $downtime->id, + 'downtime_is_service' => $object->getType() === $object::TYPE_SERVICE, + 'downtime_name' => $downtime->name + ) + ); + echo $deleteButton; + } ?> + </dt> + <dd> + <?= $this->nl2br($this->createTicketLinks($this->markdown($downtime->comment))) ?> + </dd> + <?php endforeach ?> + </dl> + <?php endif ?> + </td> +</tr> diff --git a/modules/monitoring/application/views/scripts/show/components/extensions.phtml b/modules/monitoring/application/views/scripts/show/components/extensions.phtml new file mode 100644 index 0000000..263b7e4 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/extensions.phtml @@ -0,0 +1,4 @@ +<?php +foreach ($extensionsHtml as $extensionHtml) { + echo $extensionHtml; +} diff --git a/modules/monitoring/application/views/scripts/show/components/flags.phtml b/modules/monitoring/application/views/scripts/show/components/flags.phtml new file mode 100644 index 0000000..871a4dd --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/flags.phtml @@ -0,0 +1,4 @@ +<div data-base-target="_self"> + <h2><?= $this->translate('Feature Commands') ?></h2> + <?= $toggleFeaturesForm ?> +</div> diff --git a/modules/monitoring/application/views/scripts/show/components/flapping.phtml b/modules/monitoring/application/views/scripts/show/components/flapping.phtml new file mode 100644 index 0000000..f09b107 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/flapping.phtml @@ -0,0 +1,14 @@ +<?php + +if ($object->is_flapping) { + printf( + "<tr><th>%s</th><td>%s %s</td></tr>\n", + 'Flapping', + $this->icon('flapping', 'Flapping'), + sprintf( + 'Currently flapping with a %.2f%% state change rate', + $object->percent_state_change + ) + ); +} + diff --git a/modules/monitoring/application/views/scripts/show/components/grapher.phtml b/modules/monitoring/application/views/scripts/show/components/grapher.phtml new file mode 100644 index 0000000..0b49e63 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/grapher.phtml @@ -0,0 +1,6 @@ +<?php if (isset($graphers)) { + foreach ($graphers as $grapher) { + echo $grapher->getPreviewHtml($object); + } +} ?> + diff --git a/modules/monitoring/application/views/scripts/show/components/hostgroups.phtml b/modules/monitoring/application/views/scripts/show/components/hostgroups.phtml new file mode 100644 index 0000000..377b56f --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/hostgroups.phtml @@ -0,0 +1,19 @@ +<?php + +if (empty($object->hostgroups)) return; + +$list = array(); +foreach ($object->hostgroups as $name => $alias) { + $list[] = $this->qlink( + $alias, + 'monitoring/list/hosts', + array('hostgroup_name' => $name), + array('title' => sprintf($this->translate('List all hosts in the group "%s"'), $alias)) + ); +} +printf( + "<tr><th>%s</th><td class=\"go-ahead\">%s</td></tr>\n", + $this->translate('Hostgroups'), + implode(', ', $list) +); + diff --git a/modules/monitoring/application/views/scripts/show/components/notes.phtml b/modules/monitoring/application/views/scripts/show/components/notes.phtml new file mode 100644 index 0000000..c868c95 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/notes.phtml @@ -0,0 +1,48 @@ +<?php + +use Icinga\Web\Navigation\Navigation; + +/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + +$navigation = new Navigation(); +$notes = trim($object->notes); + +$links = $object->getNotesUrls(); +if (! empty($links)) { + foreach ($links as $link) { + $navigation->addItem( + // add warning to links that open in new tabs to improve accessibility, as recommended by WCAG20 G201 + $this->icon( + 'forward', + $this->translate('Link opens in new window'), + array('aria-label' => $this->translate('Link opens in new window')) + ) . ' ' . $this->escape($link), + array( + 'url' => $link, + 'target' => '_blank', + 'renderer' => array( + 'NavigationItemRenderer', + 'escape_label' => false + ) + ) + ); + } +} + +if (($navigation->isEmpty() || ! $navigation->hasRenderableItems()) && $notes === '') { + return; +} +?> +<tr> + <th><?= $this->translate('Notes') ?></th> + <td> + <?= $navigation->getRenderer() ?> + <?php if ($notes !== ''): ?> + <?= $this->markdown($notes, [ + 'id' => $object->type . '-notes', + 'class' => 'collapsible', + 'data-visible-height' => 200 + ]) ?> + <?php endif ?> + </td> +</tr>
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/show/components/notifications.phtml b/modules/monitoring/application/views/scripts/show/components/notifications.phtml new file mode 100644 index 0000000..3e8c665 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/notifications.phtml @@ -0,0 +1,68 @@ +<tr> + <th><?= $this->translate('Notifications') ?></th> + <td> + <?php + /** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ + if ($this->hasPermission('monitoring/command/send-custom-notification')) { + if ($object->getType() === $object::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + echo $this->qlink( + $this->translate('Send notification'), + 'monitoring/host/send-custom-notification', + array('host' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'bell', + 'title' => $this->translate( + 'Send a custom notification to contacts responsible for this host' + ) + ) + ); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + echo $this->qlink( + $this->translate('Send notification'), + 'monitoring/service/send-custom-notification', + array('host' => $object->getHost()->getName(), 'service' => $object->getName()), + array( + 'class' => 'action-link', + 'data-base-target' => '_self', + 'icon' => 'bell', + 'title' => $this->translate( + 'Send a custom notification to contacts responsible for this service' + ) + ) + ); + } + if (! in_array((int) $object->state, array(0, 99))) { + echo '<br>'; + } + } elseif (in_array((int) $object->state, array(0, 99))) { + echo '-'; + } + // We are not interested in notifications for OK or pending objects + if (! in_array((int) $object->state, array(0, 99))) { + if ($object->current_notification_number > 0) { + if ((int) $object->current_notification_number === 1) { + $msg = sprintf( + $this->translate('A notification has been sent for this issue %s.'), + $this->timeAgo($object->last_notification) + ); + } else { + $msg = sprintf( + $this->translate('%d notifications have been sent for this issue.'), + $object->current_notification_number + ) . '<br>' . sprintf( + $this->translate('The last one was sent %s.'), + $this->timeAgo($object->last_notification) + ); + } + } else { + $msg = $this->translate('No notification has been sent for this issue.'); + } + echo $msg; + } + ?> + </td> +</tr> diff --git a/modules/monitoring/application/views/scripts/show/components/output.phtml b/modules/monitoring/application/views/scripts/show/components/output.phtml new file mode 100644 index 0000000..34d8268 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/output.phtml @@ -0,0 +1,5 @@ +<h2><?= $this->translate('Plugin Output') ?></h2> +<div id="check-output-<?= $this->escape(str_replace(' ', '-', $object->check_command)) ?>" class="collapsible" data-visible-height="100"> + <?= $this->pluginOutput($object->output, false, $object->check_command) ?> + <?= $this->pluginOutput($object->long_output, false, $object->check_command) ?> +</div> diff --git a/modules/monitoring/application/views/scripts/show/components/perfdata.phtml b/modules/monitoring/application/views/scripts/show/components/perfdata.phtml new file mode 100644 index 0000000..78ea6d2 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/perfdata.phtml @@ -0,0 +1,4 @@ +<?php if ($object->perfdata): ?> +<h2><?= $this->translate('Performance data') ?></h2> +<div id="check-perfdata-<?= $this->escape(str_replace(' ', '-', $object->check_command)) ?>"><?= $this->perfdata($object->perfdata) ?></div> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/show/components/reachable.phtml b/modules/monitoring/application/views/scripts/show/components/reachable.phtml new file mode 100644 index 0000000..8d55e84 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/reachable.phtml @@ -0,0 +1,15 @@ +<?php if ($object->is_reachable !== null): ?> +<tr> + <th> + <?= $this->translate('Reachable') ?> + </th> + <td> + <span class="check-source-meta"><?= (bool) $object->is_reachable ? $this->translate('yes') : $this->translate('no') ?></span> + <?php if ((bool) $object->is_reachable) { + echo $this->icon('circle', $this->translate('Is reachable'), array('class' => 'icon-stateful state-ok')); + } else { + echo $this->icon('circle', $this->translate('Not reachable'), array('class' => 'icon-stateful state-critical')); + } ?> + </td> +</tr> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/show/components/servicegroups.phtml b/modules/monitoring/application/views/scripts/show/components/servicegroups.phtml new file mode 100644 index 0000000..09ff248 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/servicegroups.phtml @@ -0,0 +1,20 @@ +<?php + +if (empty($object->servicegroups)) return; + +$list = array(); +foreach ($object->servicegroups as $name => $alias) { + $list[] = $this->qlink( + $alias, + 'monitoring/list/services', + array('servicegroup_name' => $name), + array('title' => sprintf($this->translate('List all services in the group "%s"'), $alias)) + ); +} + +printf( + "<tr><th>%s</th><td class=\"go-ahead\">%s</td></tr>\n", + $this->translate('Servicegroups'), + implode(', ', $list) +); + diff --git a/modules/monitoring/application/views/scripts/show/components/status.phtml b/modules/monitoring/application/views/scripts/show/components/status.phtml new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/status.phtml diff --git a/modules/monitoring/application/views/scripts/show/contact.phtml b/modules/monitoring/application/views/scripts/show/contact.phtml new file mode 100644 index 0000000..aa448ae --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/contact.phtml @@ -0,0 +1,70 @@ +<?php $contactHelper = $this->getHelper('ContactFlags') ?> +<div class="controls"> + <?php if (! $this->compact): ?> + <?= $this->tabs; ?> + <?php endif ?> + <h1><?= $this->translate('Contact details') ?></h1> + <div class="circular" style="background-image: url('<?= + $this->href('static/gravatar', array('email' => $contact->contact_email)) + ?>';width:120px;height:120px;)"></div> + +<?php if (! $contact): ?> + <?= $this->translate('No such contact') ?>: <?= $contactName ?> +</div> +<?php return; endif ?> + + <table class="name-value-table"> + <tbody> + <tr> + <th style="width: 20%"></th> + <td><strong><?= $this->escape($contact->contact_alias) ?></strong> (<?= $contact->contact_name ?>)</td> + </tr> +<?php if ($contact->contact_email): ?> + <tr> + <th><?= $this->translate('Email') ?></th> + <td> + <a href="mailto:<?= $contact->contact_email; ?>" title="<?= sprintf($this->translate('Send a mail to %s'), $contact->contact_alias); ?>" aria-label="<?= sprintf($this->translate('Send a mail to %s'), $contact->contact_alias); ?>"> + <?= $this->escape($contact->contact_email); ?> + </a> + </td> + </tr> +<?php endif ?> +<?php if ($contact->contact_pager): ?> + <tr> + <th><?= $this->translate('Pager') ?></th> + <td><?= $this->escape($contact->contact_pager) ?></td> + </tr> +<?php endif ?> + <tr> + <th><?= $this->translate('Hosts') ?></th> + <td><?= $this->escape($contactHelper->contactFlags($contact, 'host')) ?><br /> + <?= $this->escape($contact->contact_notify_host_timeperiod) ?></td> + </tr> + <tr> + <th><?= $this->translate('Services') ?></th> + <td><?= $this->escape($contactHelper->contactFlags($contact, 'service')) ?><br /> + <?= $this->escape($contact->contact_notify_service_timeperiod) ?></td> + </tr> + </tbody> + </table> + <?php if (count($commands)): ?> + <h1><?= $this->translate('Commands') ?>:</h1> + <ul> + <?php foreach ($commands as $command): ?> + <li><?= $command->command_name ?></li> + <?php endforeach ?> + </ul> + <?php endif ?> + <h1><?= $this->translate('Notifications sent to this contact') ?></h1> + <?= $this->limiter; ?> + <?= $this->paginator; ?> +</div> + +<?php if (count($notifications)): ?> +<?= $this->partial('list/notifications.phtml', array( + 'notifications' => $notifications, + 'compact' => true +)); ?> +<?php else: ?> +<div class="content"><?= $this->translate('No notifications have been sent for this contact') ?></div> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml b/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml new file mode 100644 index 0000000..e6dc0be --- /dev/null +++ b/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml @@ -0,0 +1,131 @@ +<div class="box hostservicechecks col-1-2"> + <div class="box header"> + <h2><?= $this->translate('Host and Service Checks'); ?></h2> + </div> + <div class="box contents"> + <table> + <thead> + <tr> + <th><?= $this->translate('Hosts'); ?></th> + <th><?= $this->translate('Services'); ?></th> + </tr> + </thead> + <tbody> + <tr> + <td> +<?php if ($this->statusSummary->hosts_active): ?> + <div class="box entry"><?= $this->qlink( + sprintf( + $this->translatePlural('%u Active', '%u Active', $this->statusSummary->hosts_active), + $this->statusSummary->hosts_active + ), + 'monitoring/list/hosts', + array('host_active_checks_enabled' => 1), + array('title' => sprintf( + $this->translatePlural( + 'List %u actively checked host', + 'List %u actively checked hosts', + $this->statusSummary->hosts_active + ), + $this->statusSummary->hosts_active + )) + ); ?></div> +<?php endif ?> +<?php if ($this->statusSummary->hosts_passive): ?> + <div class="box entry"><?= $this->qlink( + sprintf( + $this->translatePlural('%d Passive', '%d Passive', $this->statusSummary->hosts_passive), + $this->statusSummary->hosts_passive + ), + 'monitoring/list/hosts', + array('host_active_checks_enabled' => 0, 'host_passive_checks_enabled' => 1), + array('title' => sprintf( + $this->translatePlural( + 'List %u passively checked host', + 'List %u passively checked hosts', + $this->statusSummary->hosts_passive + ), + $this->statusSummary->hosts_passive + )) + ); ?></div> +<?php endif ?> +<?php if ($this->statusSummary->hosts_not_checked): ?> + <div class="box entry"><?= $this->qlink( + sprintf( + $this->translatePlural('%d Disabled', '%d Disabled', $this->statusSummary->hosts_not_checked), + $this->statusSummary->hosts_not_checked + ), + 'monitoring/list/hosts', + array('host_active_checks_enabled' => 0, 'host_passive_checks_enabled' => 0), + array('title' => sprintf( + $this->translatePlural( + 'List %u host that is not being checked at all', + 'List %u hosts which are not being checked at all', + $this->statusSummary->hosts_not_checked + ), + $this->statusSummary->hosts_not_checked + )) + ); ?></div> +<?php endif ?> + </td> + <td> +<?php if ($this->statusSummary->services_active): ?> + <div class="box entry"><?= $this->qlink( + sprintf( + $this->translatePlural('%d Active', '%d Active', $this->statusSummary->services_active), + $this->statusSummary->services_active + ), + 'monitoring/list/services', + array('service_active_checks_enabled' => 1), + array('title' => sprintf( + $this->translatePlural( + 'List %u actively checked service', + 'List %u actively checked services', + $this->statusSummary->services_active + ), + $this->statusSummary->services_active + )) + ); ?></div> +<?php endif ?> +<?php if ($this->statusSummary->services_passive): ?> + <div class="box entry"><?= $this->qlink( + sprintf( + $this->translatePlural('%d Passive', '%d Passive', $this->statusSummary->services_passive), + $this->statusSummary->services_passive + ), + 'monitoring/list/services', + array('service_active_checks_enabled' => 0, 'service_passive_checks_enabled' => 1), + array('title' => sprintf( + $this->translatePlural( + 'List %u passively checked service', + 'List %u passively checked services', + $this->statusSummary->services_passive + ), + $this->statusSummary->services_passive + )) + ); ?></div> +<?php endif ?> +<?php if ($this->statusSummary->services_not_checked): ?> + <div class="box entry"><?= $this->qlink( + sprintf( + $this->translatePlural('%d Disabled', '%d Disabled', $this->statusSummary->services_not_checked), + $this->statusSummary->services_not_checked + ), + 'monitoring/list/services', + array('service_active_checks_enabled' => 0, 'service_passive_checks_enabled' => 0), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is not being checked at all', + 'List %u services which are not being checked at all', + $this->statusSummary->services_not_checked + ), + $this->statusSummary->services_not_checked + )) + ); ?></div> +<?php endif ?> + </td> + </tr> + </tbody> + </table> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml b/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml new file mode 100644 index 0000000..eeeec16 --- /dev/null +++ b/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml @@ -0,0 +1,287 @@ +<div class="box monitoringfeatures col-1-2"> + <div class="box header"> + <h2><?= $this->translate('Monitoring Features'); ?></h2> + </div> + <div class="box contents"> +<?php if ($this->statusSummary->hosts_without_flap_detection || $this->statusSummary->services_without_flap_detection || + $this->statusSummary->hosts_flapping || $this->statusSummary->services_flapping): ?> + <div class="box-separator badge feature-highlight"><?= $this->translate('Flap Detection'); ?></div> +<?php else: ?> + <div class="box-separator badge"><?= $this->translate('Flap Detection'); ?></div> +<?php endif ?> + <table> + <tbody> + <tr> + <td> + <div class="box entry"> +<?php if ($this->statusSummary->hosts_without_flap_detection): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural('%u Host Disabled', '%u Hosts Disabled', $this->statusSummary->hosts_without_flap_detection), + $this->statusSummary->hosts_without_flap_detection + ), + 'monitoring/list/hosts', + array('host_flap_detection_enabled' => 0), + array( + 'class' => 'feature-highlight', + 'title' => sprintf( + $this->translatePlural( + 'List %u host for which flap detection has been disabled', + 'List %u hosts for which flap detection has been disabled', + $this->statusSummary->hosts_without_flap_detection + ), + $this->statusSummary->hosts_without_flap_detection + ) + ) + ); ?> +<?php else: ?> + <?= $this->qlink( + $this->translate('All Hosts Enabled'), + 'monitoring/list/hosts', + array('host_flap_detection_enabled' => 1), + array('title' => $this->translate( + 'List all hosts, for which flap detection is enabled entirely' + )) + ); ?> +<?php endif ?> +<?php if ($this->statusSummary->hosts_flapping): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural('%u Host Flapping', '%u Hosts Flapping', $this->statusSummary->hosts_flapping), + $this->statusSummary->hosts_flapping + ), + 'monitoring/list/hosts', + array('host_is_flapping' => 1), + array( + 'class' => 'feature-highlight', + 'title' => sprintf( + $this->translatePlural( + 'List %u host that is currently flapping', + 'List %u hosts which are currently flapping', + $this->statusSummary->hosts_flapping + ), + $this->statusSummary->hosts_flapping + ) + ) + ); ?> +<?php endif ?> + </div> + </td> + <td> + <div class="box entry"> +<?php if ($this->statusSummary->services_without_flap_detection): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural('%u Service Disabled', '%u Services Disabled', $this->statusSummary->services_without_flap_detection), + $this->statusSummary->services_without_flap_detection + ), + 'monitoring/list/services', + array('service_flap_detection_enabled' => 0), + array( + 'class' => 'feature-highlight', + 'title' => sprintf( + $this->translatePlural( + 'List %u service for which flap detection has been disabled', + 'List %u services for which flap detection has been disabled', + $this->statusSummary->services_without_flap_detection + ), + $this->statusSummary->services_without_flap_detection + ) + ) + ); ?> +<?php else: ?> + <?= $this->qlink( + $this->translate('All Services Enabled'), + 'monitoring/list/services', + array('service_flap_detection_enabled' => 1), + array('title' => $this->translate( + 'List all services, for which flap detection is enabled entirely' + )) + ); ?> +<?php endif ?> +<?php if ($this->statusSummary->services_flapping): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural('%u Service Flapping', '%u Services Flapping', $this->statusSummary->services_flapping), + $this->statusSummary->services_flapping + ), + 'monitoring/list/services', + array('service_is_flapping' => 1), + array( + 'class' => 'feature-highlight', + 'title' => sprintf( + $this->translatePlural( + 'List %u service that is currently flapping', + 'List %u services which are currently flapping', + $this->statusSummary->services_flapping + ), + $this->statusSummary->services_flapping + ) + ) + ); ?> +<?php endif ?> + </div> + </td> + </tr> + </tbody> + </table> +<?php if ($this->statusSummary->hosts_not_triggering_notifications || $this->statusSummary->services_not_triggering_notifications): ?> + <div class="box-separator badge feature-highlight"><?= $this->translate('Notifications'); ?></div> +<?php else: ?> + <div class="box-separator badge"><?= $this->translate('Notifications'); ?></div> +<?php endif ?> + <table> + <tbody> + <tr> + <td> + <div class="box entry"> +<?php if ($this->statusSummary->hosts_not_triggering_notifications): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural('%u Host Disabled', '%u Hosts Disabled', $this->statusSummary->hosts_not_triggering_notifications), + $this->statusSummary->hosts_not_triggering_notifications + ), + 'monitoring/list/hosts', + array('host_notifications_enabled' => 0), + array( + 'class' => 'feature-highlight', + 'title' => sprintf( + $this->translatePlural( + 'List %u host for which notifications are suppressed', + 'List %u hosts for which notifications are suppressed', + $this->statusSummary->hosts_not_triggering_notifications + ), + $this->statusSummary->hosts_not_triggering_notifications + ) + ) + ); ?> +<?php else: ?> + <?= $this->qlink( + $this->translate('All Hosts Enabled'), + 'monitoring/list/hosts', + array('host_notifications_enabled' => 1), + array('title' => $this->translate( + 'List all hosts, for which notifications are enabled entirely' + )) + ); ?> +<?php endif ?> + </div> + </td> + <td> + <div class="box entry"> +<?php if ($this->statusSummary->services_not_triggering_notifications): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural('%u Service Disabled', '%u Services Disabled', $this->statusSummary->services_not_triggering_notifications), + $this->statusSummary->services_not_triggering_notifications + ), + 'monitoring/list/services', + array('service_notifications_enabled' => 0), + array( + 'class' => 'feature-highlight', + 'title' => sprintf( + $this->translatePlural( + 'List %u service for which notifications are suppressed', + 'List %u services for which notifications are suppressed', + $this->statusSummary->services_not_triggering_notifications + ), + $this->statusSummary->services_not_triggering_notifications + ) + ) + ); ?> +<?php else: ?> + <?= $this->qlink( + $this->translate('All Services Enabled'), + 'monitoring/list/services', + array('service_notifications_enabled' => 1), + array('title' => $this->translate( + 'List all services, for which notifications are enabled entirely' + )) + ); ?> +<?php endif ?> + </div> + </td> + </tr> + </tbody> + </table> +<?php if ($this->statusSummary->hosts_not_processing_event_handlers || $this->statusSummary->services_not_processing_event_handlers): ?> + <div class="box-separator badge feature-highlight"><?= $this->translate('Event Handlers'); ?></div> +<?php else: ?> + <div class="box-separator badge"><?= $this->translate('Event Handlers'); ?></div> +<?php endif ?> + <table> + <tbody> + <tr> + <td> + <div class="box entry"> +<?php if ($this->statusSummary->hosts_not_processing_event_handlers): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural('%u Host Disabled', '%u Hosts Disabled', $this->statusSummary->hosts_not_processing_event_handlers), + $this->statusSummary->hosts_not_processing_event_handlers + ), + 'monitoring/list/hosts', + array('host_event_handler_enabled' => 0), + array( + 'class' => 'feature-highlight', + 'title' => sprintf( + $this->translatePlural( + 'List %u host that is not processing any event handlers', + 'List %u hosts which are not processing any event handlers', + $this->statusSummary->hosts_not_processing_event_handlers + ), + $this->statusSummary->hosts_not_processing_event_handlers + ) + ) + ); ?> +<?php else: ?> + <?= $this->qlink( + $this->translate('All Hosts Enabled'), + 'monitoring/list/hosts', + array('host_event_handler_enabled' => 1), + array('title' => $this->translate( + 'List all hosts, which are processing event handlers entirely' + )) + ); ?> +<?php endif ?> + </div> + </td> + <td> + <div class="box entry"> +<?php if ($this->statusSummary->services_not_processing_event_handlers): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural('%u Service Disabled', '%u Services Disabled', $this->statusSummary->services_not_processing_event_handlers), + $this->statusSummary->services_not_processing_event_handlers + ), + 'monitoring/list/services', + array('service_event_handler_enabled' => 0), + array( + 'class' => 'feature-highlight', + 'title' => sprintf( + $this->translatePlural( + 'List %u service that is not processing any event handlers', + 'List %u services which are not processing any event handlers', + $this->statusSummary->services_not_processing_event_handlers + ), + $this->statusSummary->services_not_processing_event_handlers + ) + ) + ); ?> +<?php else: ?> + <?= $this->qlink( + $this->translate('All Services Enabled'), + 'monitoring/list/services', + array('service_event_handler_enabled' => 1), + array('title' => $this->translate( + 'List all services, which are processing event handlers entirely' + )) + ); ?> +<?php endif ?> + </div> + </td> + </tr> + </tbody> + </table> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml b/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml new file mode 100644 index 0000000..05ffd29 --- /dev/null +++ b/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml @@ -0,0 +1,81 @@ +<?php +$service_problems = ( + $this->statusSummary->services_warning_handled_on_ok_hosts || + $this->statusSummary->services_warning_unhandled_on_ok_hosts || + $this->statusSummary->services_critical_handled_on_ok_hosts || + $this->statusSummary->services_critical_unhandled_on_ok_hosts || + $this->statusSummary->services_unknown_handled_on_ok_hosts || + $this->statusSummary->services_unknown_unhandled_on_ok_hosts +); +?> +<div class="box ok_hosts state_<?= $this->statusSummary->hosts_up ? 'up' : 'pending'; ?> col-1-2"> + <div class="box header"> + <?php if ($this->statusSummary->hosts_up): ?> + <h2><?= $this->qlink( + sprintf( + $this->translatePlural('%u Host UP', '%u Hosts UP', $this->statusSummary->hosts_up), + $this->statusSummary->hosts_up + ), + 'monitoring/list/hosts', + array('host_state' => 0), + array('title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state UP', + 'List %u hosts which are currently in state UP', + $this->statusSummary->hosts_up + ), + $this->statusSummary->hosts_up + )) + ); ?></h2> + <?php endif ?> + <?php if ($this->statusSummary->hosts_pending): ?> + <h2><?= $this->qlink( + sprintf( + $this->translatePlural('%u Host PENDING', '%u Hosts PENDING', $this->statusSummary->hosts_pending), + $this->statusSummary->hosts_pending + ), + 'monitoring/list/hosts', + array('host_state' => 99), + array('title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state PENDING', + 'List %u hosts which are currently in state PENDING', + $this->statusSummary->hosts_pending + ), + $this->statusSummary->hosts_pending + )) + ); ?></h2> + <?php endif ?> + </div> +<?php if ($service_problems || $this->statusSummary->hosts_down || $this->statusSummary->hosts_unreachable): ?> + <div class="box contents"> + <?= $this->partial( + 'tactical/components/parts/servicestatesummarybyhoststate.phtml', + array( + 'translationDomain' => $this->translationDomain, + 'host_problem' => 0, + 'services_ok' => $this->statusSummary->services_ok_on_ok_hosts, + 'services_ok_not_checked' => $this->statusSummary->services_ok_not_checked_on_ok_hosts, + 'services_pending' => $this->statusSummary->services_pending_on_ok_hosts, + 'services_pending_not_checked' => $this->statusSummary->services_pending_not_checked_on_ok_hosts, + 'services_warning_handled' => $this->statusSummary->services_warning_handled_on_ok_hosts, + 'services_warning_unhandled' => $this->statusSummary->services_warning_unhandled_on_ok_hosts, + 'services_warning_passive' => $this->statusSummary->services_warning_passive_on_ok_hosts, + 'services_warning_not_checked' => $this->statusSummary->services_warning_not_checked_on_ok_hosts, + 'services_critical_handled' => $this->statusSummary->services_critical_handled_on_ok_hosts, + 'services_critical_unhandled' => $this->statusSummary->services_critical_unhandled_on_ok_hosts, + 'services_critical_passive' => $this->statusSummary->services_critical_passive_on_ok_hosts, + 'services_critical_not_checked' => $this->statusSummary->services_critical_not_checked_on_ok_hosts, + 'services_unknown_handled' => $this->statusSummary->services_unknown_handled_on_ok_hosts, + 'services_unknown_unhandled' => $this->statusSummary->services_unknown_unhandled_on_ok_hosts, + 'services_unknown_passive' => $this->statusSummary->services_unknown_passive_on_ok_hosts, + 'services_unknown_not_checked' => $this->statusSummary->services_unknown_not_checked_on_ok_hosts + ) + ); ?> +<?php else: ?> + <div class="box contents zero"> + <h3>0</h3> + <span><?= $this->translate('Service Problems'); ?></span> +<?php endif ?> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml b/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml new file mode 100644 index 0000000..4f32daf --- /dev/null +++ b/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml @@ -0,0 +1,394 @@ +<?php + +use Icinga\Module\Monitoring\Object\Service; + +?> +<?php if ($services_critical_handled || $services_critical_unhandled): ?> +<div class="box badge entry state-<?= Service::getStateText(2); ?> <?= $services_critical_unhandled ? '' : 'handled'; ?>"> +<?php if ($services_critical_unhandled): ?> + <?= $this->qlink( + $services_critical_unhandled . ' ' . Service::getStateText(2, true), + 'monitoring/list/services', + array( + 'host_problem' => $host_problem, + 'service_state' => 2, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state CRITICAL', + 'List %u services which are currently in state CRITICAL', + $services_critical_unhandled + ), + $services_critical_unhandled + )) + ); ?> +<?php endif ?> +<?php if ($services_critical_handled): ?> + <?= $this->qlink( + $services_critical_handled . ' ' . ( + $services_critical_unhandled ? $this->translate('Handled') : Service::getStateText(2, true) + ), + 'monitoring/list/services', + array( + 'host_problem' => $host_problem, + 'service_state' => 2, + 'service_handled' => 1 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state CRITICAL (Handled)', + 'List %u services which are currently in state CRITICAL (Handled)', + $services_critical_handled + ), + $services_critical_handled + )) + ); ?> +<?php endif ?> +<?php if ($services_critical_passive): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%u is passively checked', + '%u are passively checked', + $services_critical_passive + ), + $services_critical_passive + ), + 'monitoring/list/services', + array( + 'service_state' => 2, + 'host_problem' => $host_problem, + 'service_active_checks_enabled' => 0, + 'service_passive_checks_enabled' => 1 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state CRITICAL and passively checked', + 'List %u services which are currently in state CRITICAL and passively checked', + $services_critical_passive + ), + $services_critical_passive + )) + ); ?> +<?php endif ?> +<?php if ($services_critical_not_checked): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%u is not checked at all', + '%u are not checked at all', + $services_critical_not_checked + ), + $services_critical_not_checked + ), + 'monitoring/list/services', + array( + 'service_state' => 2, + 'host_problem' => $host_problem, + 'service_active_checks_enabled' => 0, + 'service_passive_checks_enabled' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state CRITICAL and not checked at all', + 'List %u services which are currently in state CRITICAL and not checked at all', + $services_critical_not_checked + ), + $services_critical_not_checked + )) + ); ?> +<?php endif ?> +</div> +<?php endif ?> +<?php if ($services_warning_handled || $services_warning_unhandled): ?> +<div class="box badge entry state-<?= Service::getStateText(1); ?> <?= $services_warning_unhandled ? '' : 'handled'; ?>"> +<?php if ($services_warning_unhandled): ?> + <?= $this->qlink( + $services_warning_unhandled . ' ' . Service::getStateText(1, true), + 'monitoring/list/services', + array( + 'host_problem' => $host_problem, + 'service_state' => 1, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state WARNING', + 'List %u services which are currently in state WARNING', + $services_warning_unhandled + ), + $services_warning_unhandled + )) + ); ?> +<?php endif ?> +<?php if ($services_warning_handled): ?> + <?= $this->qlink( + $services_warning_handled . ' ' . ( + $services_warning_unhandled ? $this->translate('Handled') : Service::getStateText(1, true) + ), + 'monitoring/list/services', + array( + 'host_problem' => $host_problem, + 'service_state' => 1, + 'service_handled' => 1 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state WARNING (Handled)', + 'List %u services which are currently in state WARNING (Handled)', + $services_warning_handled + ), + $services_warning_handled + )) + ); ?> +<?php endif ?> +<?php if ($services_warning_passive): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%u is passively checked', + '%u are passively checked', + $services_warning_passive + ), + $services_warning_passive + ), + 'monitoring/list/services', + array( + 'service_state' => 1, + 'host_problem' => $host_problem, + 'service_active_checks_enabled' => 0, + 'service_passive_checks_enabled' => 1 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state WARNING and passively checked', + 'List %u services which are currently in state WARNING and passively checked', + $services_warning_passive + ), + $services_warning_passive + )) + ); ?> +<?php endif ?> +<?php if ($services_warning_not_checked): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%u is not checked at all', + '%u are not checked at all', + $services_warning_not_checked + ), + $services_warning_not_checked + ), + 'monitoring/list/services', + array( + 'service_state' => 1, + 'host_problem' => $host_problem, + 'service_active_checks_enabled' => 0, + 'service_passive_checks_enabled' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state WARNING and not checked at all', + 'List %u services which are currently in state WARNING and not checked at all', + $services_warning_not_checked + ), + $services_warning_not_checked + )) + ); ?> +<?php endif ?> +</div> +<?php endif ?> +<?php if ($services_unknown_handled || $services_unknown_unhandled): ?> +<div class="box badge entry state-<?= Service::getStateText(3); ?> <?= $services_unknown_unhandled ? '' : 'handled'; ?>"> +<?php if ($services_unknown_unhandled): ?> + <?= $this->qlink( + $services_unknown_unhandled . ' ' . Service::getStateText(3, true), + 'monitoring/list/services', + array( + 'host_problem' => $host_problem, + 'service_state' => 3, + 'service_acknowledged' => 0, + 'service_in_downtime' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state UNKNOWN', + 'List %u services which are currently in state UNKNOWN', + $services_unknown_unhandled + ), + $services_unknown_unhandled + )) + ); ?> +<?php endif ?> +<?php if ($services_unknown_handled): ?> + <?= $this->qlink( + $services_unknown_handled . ' ' . ( + $services_unknown_unhandled ? $this->translate('Handled') : Service::getStateText(3, true) + ), + 'monitoring/list/services', + array( + 'host_problem' => $host_problem, + 'service_state' => 3, + 'service_handled' => 1 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state UNKNOWN (Handled)', + 'List %u services which are currently in state UNKNOWN (Handled)', + $services_unknown_handled + ), + $services_unknown_handled + )) + ); ?> +<?php endif ?> +<?php if ($services_unknown_passive): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%u is passively checked', + '%u are passively checked', + $services_unknown_passive + ), + $services_unknown_passive + ), + 'monitoring/list/services', + array( + 'service_state' => 3, + 'host_problem' => $host_problem, + 'service_active_checks_enabled' => 0, + 'service_passive_checks_enabled' => 1 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state UNKNOWN and passively checked', + 'List %u services which are currently in state UNKNOWN and passively checked', + $services_unknown_passive + ), + $services_unknown_passive + )) + ); ?> +<?php endif ?> +<?php if ($services_unknown_not_checked): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%u is not checked at all', + '%u are not checked at all', + $services_unknown_not_checked + ), + $services_unknown_not_checked + ), + 'monitoring/list/services', + array( + 'service_state' => 3, + 'host_problem' => $host_problem, + 'service_active_checks_enabled' => 0, + 'service_passive_checks_enabled' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state UNKNOWN and not checked at all', + 'List %u services which are currently in state UNKNOWN and not checked at all', + $services_unknown_not_checked + ), + $services_unknown_not_checked + )) + ); ?> +<?php endif ?> +</div> +<?php endif ?> +<?php if ($services_ok): ?> +<div class="box badge entry state-<?= Service::getStateText(0); ?>"> + <?= $this->qlink( + $services_ok . ' ' . Service::getStateText(0, true), + 'monitoring/list/services', + array( + 'host_problem' => $host_problem, + 'service_state' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state OK', + 'List %u services which are currently in state OK', + $services_ok + ), + $services_ok + )) + ); ?> +<?php if ($services_ok_not_checked): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%u is not checked at all', + '%u are not checked at all', + $services_ok_not_checked + ), + $services_ok_not_checked + ), + 'monitoring/list/services', + array( + 'service_state' => 0, + 'host_problem' => $host_problem, + 'service_active_checks_enabled' => 0, + 'service_passive_checks_enabled' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state OK and not checked at all', + 'List %u services which are currently in state OK and not checked at all', + $services_ok_not_checked + ), + $services_ok_not_checked + )) + ); ?> +<?php endif ?> +</div> +<?php endif ?> +<?php if ($services_pending): ?> +<div class="box badge entry state-<?= Service::getStateText(99); ?>"> + <?= $this->qlink( + $services_pending . ' ' . Service::getStateText(99, true), + 'monitoring/list/services', + array( + 'service_state' => 99 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state PENDING', + 'List %u services which are currently in state PENDING', + $services_pending + ), + $services_pending + )) + ); ?> +<?php if ($services_pending_not_checked): ?> + <?= $this->qlink( + sprintf( + $this->translatePlural( + '%u is not checked at all', + '%u are not checked at all', + $services_pending_not_checked + ), + $services_pending_not_checked + ), + 'monitoring/list/services', + array( + 'service_state' => 99, + 'service_active_checks_enabled' => 0, + 'service_passive_checks_enabled' => 0 + ), + array('title' => sprintf( + $this->translatePlural( + 'List %u service that is currently in state PENDING and not checked at all', + 'List %u services which are currently in state PENDING and not checked at all', + $services_pending_not_checked + ), + $services_pending_not_checked + )) + ); ?> +<?php endif ?> +</div> +<?php endif ?> diff --git a/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml b/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml new file mode 100644 index 0000000..6374ff8 --- /dev/null +++ b/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml @@ -0,0 +1,74 @@ +<div class="box problem_hosts <?php + echo $this->statusSummary->hosts_down ? 'state_down' : 'state_unreachable'; + if (!$this->statusSummary->hosts_down_unhandled && !$this->statusSummary->hosts_unreachable_unhandled) { + echo ' handled'; + } +?> col-1-2"> + <div class="box header"> + <?php if ($this->statusSummary->hosts_down): ?> + <h2><?= $this->qlink( + sprintf( + $this->translatePlural('%u Host DOWN', '%u Hosts DOWN', $this->statusSummary->hosts_down), + $this->statusSummary->hosts_down + ), + 'monitoring/list/hosts', + array('host_state' => 1), + array('title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state DOWN', + 'List %u hosts which are currently in state DOWN', + $this->statusSummary->hosts_down + ), + $this->statusSummary->hosts_down + )) + ); ?></h2> + <?php endif ?> + <?php if ($this->statusSummary->hosts_unreachable): ?> + <h2><?= $this->qlink( + sprintf( + $this->translatePlural( + '%u Host UNREACHABLE', + '%u Hosts UNREACHABLE', + $this->statusSummary->hosts_unreachable + ), + $this->statusSummary->hosts_unreachable + ), + 'monitoring/list/hosts', + array('host_state' => 2), + array('title' => sprintf( + $this->translatePlural( + 'List %u host that is currently in state UNREACHABLE', + 'List %u hosts which are currently in state UNREACHABLE', + $this->statusSummary->hosts_unreachable + ), + $this->statusSummary->hosts_unreachable + )) + ); ?></h2> + <?php endif ?> + </div> + <div class="box contents"> + <?= $this->partial( + 'tactical/components/parts/servicestatesummarybyhoststate.phtml', + array( + 'translationDomain' => $this->translationDomain, + 'host_problem' => 1, + 'services_ok' => $this->statusSummary->services_ok_on_problem_hosts, + 'services_ok_not_checked' => $this->statusSummary->services_ok_not_checked_on_problem_hosts, + 'services_pending' => $this->statusSummary->services_pending_on_problem_hosts, + 'services_pending_not_checked' => $this->statusSummary->services_pending_not_checked_on_problem_hosts, + 'services_warning_handled' => $this->statusSummary->services_warning_handled_on_problem_hosts, + 'services_warning_unhandled' => $this->statusSummary->services_warning_unhandled_on_problem_hosts, + 'services_warning_passive' => $this->statusSummary->services_warning_passive_on_problem_hosts, + 'services_warning_not_checked' => $this->statusSummary->services_warning_not_checked_on_problem_hosts, + 'services_critical_handled' => $this->statusSummary->services_critical_handled_on_problem_hosts, + 'services_critical_unhandled' => $this->statusSummary->services_critical_unhandled_on_problem_hosts, + 'services_critical_passive' => $this->statusSummary->services_critical_passive_on_problem_hosts, + 'services_critical_not_checked' => $this->statusSummary->services_critical_not_checked_on_problem_hosts, + 'services_unknown_handled' => $this->statusSummary->services_unknown_handled_on_problem_hosts, + 'services_unknown_unhandled' => $this->statusSummary->services_unknown_unhandled_on_problem_hosts, + 'services_unknown_passive' => $this->statusSummary->services_unknown_passive_on_problem_hosts, + 'services_unknown_not_checked' => $this->statusSummary->services_unknown_not_checked_on_problem_hosts + ) + ); ?> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/tactical/index.phtml b/modules/monitoring/application/views/scripts/tactical/index.phtml new file mode 100644 index 0000000..12f4bc5 --- /dev/null +++ b/modules/monitoring/application/views/scripts/tactical/index.phtml @@ -0,0 +1,145 @@ +<?php +use Icinga\Data\Filter\Filter; +?> +<?php if (! $this->compact): ?> +<div class="controls"> + <?= $this->tabs ?> + <?= $this->filterEditor ?> +</div> +<?php endif ?> +<div class="content tactical grid"> +<?php if (! count(array_filter((array) $statusSummary))): ?> + <p><?= $this->translate('No results found matching the filter.') ?></p> +</div> +<?php return; endif ?> + <div class="boxview" data-base-target="_next"> + <div class="donut-container"> + <h2 aria-label="<?= $this->translate('Host Summary') ?>"><?= $this->translate('Host Summary') ?></h2> + <div class="donut"> + <?= $hostStatusSummaryChart ?> + </div> + <ul class="donut-legend"> + <?php if ($statusSummary->hosts_up): ?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/hosts', array('host_state' => 0, 'sort' => 'host_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-ok badge"><?= $statusSummary->hosts_up ?></span><?= $this->translate('Up') ?> + </a> + </li> + <?php endif ?> + <?php if ($statusSummary->hosts_down_handled): ?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/hosts', array('host_state' => 1, 'host_handled' => 1, 'sort' => 'host_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-critical handled badge"><?= $statusSummary->hosts_down_handled ?></span><?= $this->translate('Down') ?> (<?= $this->translate('Handled') ?>) + </a> + </li> + <?php endif ?> + <?php if ($statusSummary->hosts_down_unhandled): ?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/hosts', array('host_state' => 1, 'host_handled' => 0, 'sort' => 'host_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-critical badge"><?= $statusSummary->hosts_down_unhandled ?></span><?= $this->translate('Down') ?> + </a> + </li> + <?php endif ?> + <?php if ($statusSummary->hosts_unreachable_handled): ?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/hosts', array('host_state' => 2, 'host_handled' => 1, 'sort' => 'host_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-unreachable handled badge"><?= $statusSummary->hosts_unreachable_handled ?></span><?= $this->translate('Unreachable') ?> (<?= $this->translate('Handled') ?>) + </a> + </li> + <?php endif ?> + <?php if ($statusSummary->hosts_unreachable_unhandled): ?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/hosts', array('host_state' => 2, 'host_handled' => 0, 'sort' => 'host_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-unreachable badge"><?= $statusSummary->hosts_unreachable_unhandled ?></span><?= $this->translate('Unreachable') ?> + </a> + </li> + <?php endif ?> + <?php if ($statusSummary->hosts_pending): ?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/hosts', array('host_state' => 99, 'sort' => 'host_last_check', 'dir' => 'asc'))->addFilter(Filter::not(Filter::where('host_active_checks_enabled', 0), Filter::where('host_passive_checks_enabled', 0))) ?>"> + <span class="state state-pending badge"><?= $statusSummary->hosts_pending ?></span><?= $this->translate('Pending') ?> + </a> + </li> + <?php endif ?> + <?php if ($statusSummary->hosts_pending_not_checked): ?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/hosts', array('host_state' => 99, 'host_active_checks_enabled' => 0, 'host_passive_checks_enabled' => 0, 'sort' => 'host_last_check', 'dir' => 'asc')) ?>"> + <span class="state slice-state-not-checked badge"><?= $statusSummary->hosts_pending_not_checked ?></span><?= $this->translate('Not Checked') ?> + </a> + </li> + <?php endif ?> + </ul> + </div> + <div class="donut-container"> + <h2 aria-label="<?= $this->translate('Service Summary') ?>"><?= $this->translate('Service Summary') ?></h2> + <div class="donut"> + <?= $serviceStatusSummaryChart ?> + </div> + <ul class="donut-legend"> + <?php if ($statusSummary->services_ok):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 0, 'sort' => 'service_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-ok badge"><?= $statusSummary->services_ok ?></span><?= $this->translate('Ok') ?> + </a> + </li> + <?php endif; + if ($statusSummary->services_warning_handled):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 1, 'service_handled' => 1, 'sort' => 'service_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-warning handled badge"><?= $statusSummary->services_warning_handled ?></span><?= $this->translate('Warning') ?> (<?= $this->translate('Handled') ?>) + </a> + </li> + <?php endif; + if ($statusSummary->services_warning_unhandled):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 1, 'service_handled' => 0, 'sort' => 'service_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-warning badge"><?= $statusSummary->services_warning_unhandled ?></span><?= $this->translate('Warning') ?> + </a> + </li> + <?php endif; + if ($statusSummary->services_critical_handled):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 2, 'service_handled' => 1, 'sort' => 'service_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-critical handled badge"><?= $statusSummary->services_critical_handled ?></span><?= $this->translate('Critical') ?> (<?= $this->translate('Handled') ?>) + </a> + </li> + <?php endif; + if ($statusSummary->services_critical_unhandled):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 2, 'service_handled' => 0, 'sort' => 'service_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-critical badge"><?= $statusSummary->services_critical_unhandled ?></span><?= $this->translate('Critical') ?> + </a> + </li> + <?php endif; + if ($statusSummary->services_unknown_handled):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 3, 'service_handled' => 1, 'sort' => 'service_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-unknown handled badge"><?= $statusSummary->services_unknown_handled ?></span><?= $this->translate('Unknown') ?> (<?= $this->translate('Handled') ?>) + </a> + </li> + <?php endif; + if ($statusSummary->services_unknown_unhandled):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 3, 'service_handled' => 0, 'sort' => 'service_last_check', 'dir' => 'asc')) ?>"> + <span class="state state-unknown badge"><?= $statusSummary->services_unknown_unhandled ?></span><?= $this->translate('Unknown') ?> + </a> + </li> + <?php endif; + if ($statusSummary->services_pending):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 99, 'sort' => 'service_last_check', 'dir' => 'asc'))->addFilter(Filter::not(Filter::where('service_active_checks_enabled', 0), Filter::where('service_passive_checks_enabled', 0))) ?>"> + <span class="state state-pending badge"><?= $statusSummary->services_pending ?></span><?= $this->translate('Pending') ?> + </a> + </li> + <?php endif; + if ($statusSummary->services_pending_not_checked):?> + <li> + <a href="<?= $this->filteredUrl('monitoring/list/services', array('service_state' => 99, 'service_active_checks_enabled' => 0, 'service_passive_checks_enabled' => 0, 'sort' => 'service_last_check', 'dir' => 'asc')) ?>"> + <span class="state slice-state-not-checked badge"><?= $statusSummary->services_pending_not_checked ?></span><?= $this->translate('Not Checked') ?> + </a> + </li> + <?php endif?> + </ul> + </div> + </div> +</div> diff --git a/modules/monitoring/application/views/scripts/timeline/index.phtml b/modules/monitoring/application/views/scripts/timeline/index.phtml new file mode 100644 index 0000000..af3b406 --- /dev/null +++ b/modules/monitoring/application/views/scripts/timeline/index.phtml @@ -0,0 +1,145 @@ +<?php +use Icinga\Web\Url; +use Icinga\Util\Color; + +$groupInfo = $timeline->getGroupInfo(); +$firstRow = ! $beingExtended; + +if (! $beingExtended && !$this->compact): ?> +<div class="controls"> + <?= $this->tabs; ?> + <div class="dontprint"> + <?= $intervalBox; ?> + </div> + <div class="timeline-legend"> + <h2><?= $this->translate('Legend'); ?></h2> +<?php foreach ($groupInfo as $labelAndClass): ?> + <span class="<?= $labelAndClass['class'] ?>"> + <span><?= $labelAndClass['label']; ?></span> + </span> +<?php endforeach ?> + </div> +</div> +<?php endif ?> +<?php if (! $beingExtended): ?> +<div class="content" data-base-target="_next"> + <div class="timeline"> +<?php endif ?> +<?php if ($switchedContext): ?> + <hr> +<?php endif ?> +<?php foreach ($timeline as $timeInfo): + switch ($intervalBox->getInterval()) { + case '1d': + $titleTime = sprintf( + $this->translate('on %s', 'timeline.link.title.time'), + $timeInfo[0]->end->format('d/m/Y') + ); + break; + case '1w': + $titleTime = sprintf( + $this->translate('in week %s of %s', 'timeline.link.title.week.and.year'), + $timeInfo[0]->end->format('W'), + $timeInfo[0]->end->format('Y') + ); + break; + case '1m': + $titleTime = sprintf( + $this->translate('in %s', 'timeline.link.title.month.and.year'), + $timeInfo[0]->end->format('F Y') + ); + break; + case '1y': + $titleTime = sprintf( + $this->translate('in %s', 'timeline.link.title.year'), + $timeInfo[0]->end->format('Y') + ); + break; + default: + $titleTime = sprintf( + $this->translate('between %s and %s', 'timeline.link.title.datetime.twice'), + $timeInfo[0]->end->format('d/m/Y g:i A'), + $timeInfo[0]->start->format('d/m/Y g:i A') + ); + } ?> + <div class="timeframe"> + <span><?= $this->qlink( + $timeInfo[0]->end->format($intervalFormat), + 'monitoring/list/eventhistory', + array( + 'timestamp<' => $timeInfo[0]->start->getTimestamp(), + 'timestamp>' => $timeInfo[0]->end->getTimestamp() + ), + array('title' => sprintf( + $this->translate('List all event records registered %s', 'timeline.link.title'), + $titleTime + )), + false + ); ?></span> +<?php foreach ($groupInfo as $groupName => $labelAndColor): ?> +<?php if (array_key_exists($groupName, $timeInfo[1])): ?> +<?php +$circleWidth = $timeline->calculateCircleWidth($timeInfo[1][$groupName], 2); +$extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$groupName], 2); +?> +<?php if ($firstRow && $extrapolatedCircleWidth !== $circleWidth): ?> + <div class="circle-box" style="width: <?= $extrapolatedCircleWidth; ?>;"> + <div class="outer-circle extrapolated <?= $timeInfo[1][$groupName]->getClass() ?>" style="<?= sprintf( + 'width: %2$s; height: %2$s; margin-top: -%1$Fem;', + (float) substr($extrapolatedCircleWidth, 0, -2) / 2, + $extrapolatedCircleWidth + ); ?>"> +<?php else: ?> + <div class="circle-box" style="width: <?= $circleWidth; ?>;"> + <div class="outer-circle" style="<?= sprintf( + 'width: %2$s; height: %2$s; margin-top: -%1$Fem;', + (float) substr($circleWidth, 0, -2) / 2, + $circleWidth + ); ?>"> +<?php endif ?> + <?= $this->qlink( + '', + $timeInfo[1][$groupName]->getDetailUrl(), + array( + 'type' => $groupName, + 'timestamp<' => $timeInfo[0]->start->getTimestamp(), + 'timestamp>' => $timeInfo[0]->end->getTimestamp() + ), + array( + 'title' => sprintf( + $this->translate('List %u %s registered %s', 'timeline.link.title'), + $timeInfo[1][$groupName]->getValue(), + strtolower($labelAndColor['label']), + $titleTime + ), + 'class' => 'inner-circle ' . $timeInfo[1][$groupName]->getClass(), + 'style' => sprintf( + 'width: %2$s; height: %2$s; margin-top: -%1$Fem; margin-left: -%1$Fem;', + (float) substr($circleWidth, 0, -2) / 2, + (string) $circleWidth + ) + ) + ); ?> + </div> + </div> +<?php endif ?> +<?php endforeach ?> + </div> + <?php $firstRow = false; ?> +<?php endforeach ?> + <a aria-hidden="true" id="end" href="<?= Url::fromRequest()->remove( + array( + 'timestamp<', + 'timestamp>' + ) + )->overwriteParams( + array( + 'start' => $nextRange->getStart()->getTimestamp(), + 'end' => $nextRange->getEnd()->getTimestamp(), + 'extend' => 1 + ) + ); ?>"></a> +<?php if (!$beingExtended): ?> + </div> +</div> +<?php endif ?> |