From 87a87656d5ef1338a37ca4a151475be2a817c226 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Tue, 7 Oct 2014 17:13:57 +0200 Subject: [PATCH 1/7] Prototyping alert history refs #4074 --- .../controllers/AlertsummaryController.php | 219 ++++++++++++++++++ .../views/helpers/MonitoringState.php | 9 + .../scripts/alertsummary/defectimage.phtml | 7 + .../views/scripts/alertsummary/index.phtml | 37 +++ modules/monitoring/configuration.php | 12 + 5 files changed, 284 insertions(+) create mode 100644 modules/monitoring/application/controllers/AlertsummaryController.php create mode 100644 modules/monitoring/application/views/scripts/alertsummary/defectimage.phtml create mode 100644 modules/monitoring/application/views/scripts/alertsummary/index.phtml diff --git a/modules/monitoring/application/controllers/AlertsummaryController.php b/modules/monitoring/application/controllers/AlertsummaryController.php new file mode 100644 index 000000000..bc4f41115 --- /dev/null +++ b/modules/monitoring/application/controllers/AlertsummaryController.php @@ -0,0 +1,219 @@ +view->intervalBox = $this->createIntervalBox(); + $this->view->recentAlerts = $this->createRecentAlerts(); + $this->view->interval = $this->getInterval(); + } + + public function defectimageAction() + { + $gridChart = new GridChart(); + $interval = $this->getInterval(); + + $gridChart->alignTopLeft(); + $gridChart->setAxisLabel('', t('Services')) + ->setXAxis(new StaticAxis()) + ->setAxisMin(null, 0) + ->setYAxis(new \Icinga\Chart\Unit\LinearUnit(10)); + + $query = $this->backend->select()->from('notification', array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + )); + + $query->setFilter( + new Icinga\Data\Filter\FilterExpression( + 'n.start_time', + '>=', + $this->getBeginDate($interval)->format('Y-m-d H:i:s') + ) + ); + + $query->order('notification_start_time', 'asc'); + + + $records = $query->paginate(10000); + $data = array(); + $defects = array(); + $period = $this->createPeriod($interval); + + foreach ($period as $entry) { + $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); + $data[$id] = array($id, 0); + $defects[$id] = array($id, 0); + } + + foreach ($records as $item) { + $id = $this->getPeriodFormat($interval, $item->notification_start_time); + if (empty($data[$id])) { + $data[$id] = array($id, 0); + } + + $data[$id][1]++; + } + + $gridChart->drawBars( + array( + 'label' => $this->translate('Notifications'), + 'color' => 'green', + 'data' => $data, + 'showPoints' => true + ) + ); + + $query = null; + $records = null; + $item = null; + + $query = $this->backend->select()->from('eventhistory', array( + 'host_name', + 'service_description', + 'object_type', + 'timestamp', + 'state', + 'attempt', + 'max_attempts', + 'output', + 'type', + 'host', + 'service', + 'service_host_name' + )); + + $query->addFilter( + new Icinga\Data\Filter\FilterExpression( + 'timestamp', + '>=', + $this->getBeginDate($interval)->getTimestamp() + ) + ); + + $query->addFilter( + new Icinga\Data\Filter\FilterExpression( + 'state', + '>', + 0 + ) + ); + + $records = $query->paginate(10000); + + foreach ($records as $item) { + $id = $this->getPeriodFormat($interval, $item->timestamp); + if (empty($data[$id])) { + $defects[$id] = array($id, 0); + } + + $defects[$id][1]++; + } + + $gridChart->drawLines( + array( + 'label' => $this->translate('Defects'), + 'color' => 'red', + 'data' => $defects, + 'showPoints' => true + ) + ); + + $this->view->chart = $gridChart; + } + + private function createRecentAlerts() + { + $query = $this->backend->select()->from('notification', array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + )); + + $query->order('notification_start_time', 'desc'); + + return $query->paginate(5); + } + + private function createIntervalBox() + { + $box = new SelectBox( + 'intervalBox', + array( + '1d' => t('One day'), + '1w' => t('One week'), + '1m' => t('One month'), + '1y' => t('One year') + ), + t('Report interval'), + 'interval' + ); + $box->applyRequest($this->getRequest()); + return $box; + } + + private function getPeriodFormat($interval, $timestamp) + { + $format = ''; + if ($interval === '1d') { + $format = '%H:00:00'; + } elseif ($interval === '1w') { + $format = '%Y-%m-%d'; + } elseif ($interval === '1m') { + $format = '%Y-%m-%d'; + } elseif ($interval === '1y') { + $format = '%Y-%m'; + } + + return strftime($format, $timestamp); + } + + private function createPeriod($interval) + { + if ($interval === '1d') { + return new DatePeriod($this->getBeginDate($interval), new DateInterval('PT1H'), 24); + } elseif ($interval === '1w') { + return new DatePeriod($this->getBeginDate($interval), new DateInterval('P1D'), 7); + } elseif ($interval === '1m') { + return new DatePeriod($this->getBeginDate($interval), new DateInterval('P1D'), 30); + } elseif ($interval === '1y') { + return new DatePeriod($this->getBeginDate($interval), new DateInterval('P1M'), 12); + } + } + + private function getBeginDate($interval) + { + $new = new DateTime(); + if ($interval === '1d') { + return $new->sub(new DateInterval('P1D')); + } elseif ($interval === '1w') { + return $new->sub(new DateInterval('P1W')); + } elseif ($interval === '1m') { + return $new->sub(new DateInterval('P1M')); + } elseif ($interval === '1y') { + return $new->sub(new DateInterval('P1Y')); + } + + return null; + } + + private function getInterval() + { + return $this->getParam('interval', '1d'); + } +} \ No newline at end of file diff --git a/modules/monitoring/application/views/helpers/MonitoringState.php b/modules/monitoring/application/views/helpers/MonitoringState.php index c20c08c96..871e8ad08 100644 --- a/modules/monitoring/application/views/helpers/MonitoringState.php +++ b/modules/monitoring/application/views/helpers/MonitoringState.php @@ -22,6 +22,15 @@ class Zend_View_Helper_MonitoringState extends Zend_View_Helper_Abstract } } + public function monitoringStateById($id, $type = 'service') + { + if ($type === 'service') { + return $this->servicestates[$id]; + } elseif ($type === 'host') { + return $this->hoststates[$id]; + } + } + /** * @deprecated Monitoring colors are clustered. */ diff --git a/modules/monitoring/application/views/scripts/alertsummary/defectimage.phtml b/modules/monitoring/application/views/scripts/alertsummary/defectimage.phtml new file mode 100644 index 000000000..91f20a837 --- /dev/null +++ b/modules/monitoring/application/views/scripts/alertsummary/defectimage.phtml @@ -0,0 +1,7 @@ +compact) { ?> +
+ render(); ?> +
+ + render(); ?> + diff --git a/modules/monitoring/application/views/scripts/alertsummary/index.phtml b/modules/monitoring/application/views/scripts/alertsummary/index.phtml new file mode 100644 index 000000000..9fd3350c3 --- /dev/null +++ b/modules/monitoring/application/views/scripts/alertsummary/index.phtml @@ -0,0 +1,37 @@ +getHelper('MonitoringState'); +?> +
+
+ +
+
+ +
+

Alert summary

+ +

Notifications

+
+ action('defectimage', 'alertsummary', 'monitoring', array('interval' => $this->interval)); ?> +
+ + recentAlerts): ?> +

translate('Top 5 recent alerts'); ?>

+ + + + + + + + recentAlerts as $alert): ?> + + + + + + + +
translate('Host'); ?>translate('Service'); ?>translate('State'); ?>translate('Timestamp'); ?>
host; ?>service; ?>monitoringStateById($alert->notification_state, $alert->service ? 'service' : 'host')) ?>prefixedTimeSince($alert->notification_start_time, true) ?>
+ +
\ No newline at end of file diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index b40a8c282..edcd37a24 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -122,6 +122,18 @@ $section->add($this->translate('Events'), array( )); $section->add($this->translate('Timeline'))->setUrl('monitoring/timeline'); +/* + * Reporting Section + */ +$section = $this->menuSection($this->translate('Reporting'), array( + 'icon' => 'img/icons/hostgroup.png', + 'priority' => 100 +)); + +$section->add($this->translate('Alert Summary'), array( + 'url' => 'monitoring/alertsummary/index' +)); + /* * System Section */ From c48c7f41b7e53db8010c7203c07b6ffe746d8dc2 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Wed, 8 Oct 2014 13:33:04 +0200 Subject: [PATCH 2/7] AlertSummary: Decouple components and use partials refs #4078 --- .../controllers/AlertsummaryController.php | 219 ++++++++++++++++-- .../controllers/ListController.php | 4 + .../scripts/alertsummary/defectimage.phtml | 7 - .../views/scripts/alertsummary/index.phtml | 64 +++-- .../views/scripts/list/notifications.phtml | 7 +- 5 files changed, 249 insertions(+), 52 deletions(-) diff --git a/modules/monitoring/application/controllers/AlertsummaryController.php b/modules/monitoring/application/controllers/AlertsummaryController.php index bc4f41115..238dd59d2 100644 --- a/modules/monitoring/application/controllers/AlertsummaryController.php +++ b/modules/monitoring/application/controllers/AlertsummaryController.php @@ -6,27 +6,68 @@ use Icinga\Chart\GridChart; use Icinga\Chart\Unit\StaticAxis; use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Web\Widget\SelectBox; +use Icinga\Web\Url; class Monitoring_AlertsummaryController extends Controller { + protected $url; + + private $notificationData; + + private $problemData; + + public function init() + { + $tabs = $this->getTabs(); + if (in_array($this->_request->getActionName(), array( + 'alertsummary', + ))) { + $tabs->extend(new OutputFormat())->extend(new DashboardAction()); + } + + $this->url = Url::fromRequest(); + + $this->notificationData = $this->createNotificationData(); + $this->problemData = $this->createProblemData(); + } + + protected function addTitleTab($action, $title = false) + { + $title = $title ?: ucfirst($action); + $this->getTabs()->add($action, array( + 'title' => $title, + // 'url' => Url::fromPath('monitoring/list/' . $action) + 'url' => $this->url + ))->activate($action); + $this->view->title = $title; + } + public function indexAction() { + $this->addTitleTab('alertsummary'); $this->view->intervalBox = $this->createIntervalBox(); $this->view->recentAlerts = $this->createRecentAlerts(); $this->view->interval = $this->getInterval(); + $this->view->defectChart = $this->createDefectImage(); + $this->view->perf = $this->createNotificationPerfdata(); + $this->view->trend = $this->createTrendInformation(); + + $this->setAutorefreshInterval(15); + $query = $this->backend->select()->from('notification', array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + )); + + $this->view->notifications = $query->paginate(); } - public function defectimageAction() - { - $gridChart = new GridChart(); + private function createNotificationData() { $interval = $this->getInterval(); - $gridChart->alignTopLeft(); - $gridChart->setAxisLabel('', t('Services')) - ->setXAxis(new StaticAxis()) - ->setAxisMin(null, 0) - ->setYAxis(new \Icinga\Chart\Unit\LinearUnit(10)); - $query = $this->backend->select()->from('notification', array( 'host', 'service', @@ -46,16 +87,13 @@ class Monitoring_AlertsummaryController extends Controller $query->order('notification_start_time', 'asc'); - $records = $query->paginate(10000); $data = array(); - $defects = array(); $period = $this->createPeriod($interval); foreach ($period as $entry) { $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); $data[$id] = array($id, 0); - $defects[$id] = array($id, 0); } foreach ($records as $item) { @@ -67,18 +105,118 @@ class Monitoring_AlertsummaryController extends Controller $data[$id][1]++; } - $gridChart->drawBars( - array( - 'label' => $this->translate('Notifications'), - 'color' => 'green', - 'data' => $data, - 'showPoints' => true + return $data; + } + + private function createTrendInformation() + { + $date = new DateTime(); + + $beginDate = $date->sub(new DateInterval('P3D')); + $query = $this->backend->select()->from('notification', array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + )); + + $query->setFilter( + new Icinga\Data\Filter\FilterExpression( + 'n.start_time', + '>=', + $beginDate->format('Y-m-d H:i:s') ) ); - $query = null; - $records = null; - $item = null; + $query->order('notification_start_time', 'asc'); + + $records = $query->paginate(10000); + $slots = array(); + + $period = new DatePeriod($beginDate, new DateInterval('P1D'), 2, DatePeriod::EXCLUDE_START_DATE); + foreach ($period as $entry) { + $slots[$entry->format('Y-m-d')] = 0; + } + + foreach ($records as $item) { + $id = strftime('%Y-%m-%d', $item->notification_start_time); + if (isset($slots[$id])) { + $slots[$id]++; + } + } + + $yesterday = array_shift($slots); + $today = array_shift($slots); + + $out = new stdClass(); + if ($yesterday === $today) { + $out->trend = 'unchanged'; + } elseif ($yesterday > $today) { + $out->trend = 'down'; + } else { + $out->trend = 'up'; + } + + if ($yesterday <= 0) { + $out->percent = 100; + } elseif ($yesterday === $today) { + $out->percent = 0; + } else { + $out->percent = 100 - + ((100/($yesterday > $today ? $yesterday : $today)) * ($yesterday > $today ? $today : $yesterday)); + } + + return $out; + } + + private function createNotificationPerfdata() + { + $interval = $this->getInterval(); + + $query = $this->backend->select()->from('notification', array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + )); + + $query->setFilter( + new Icinga\Data\Filter\FilterExpression( + 'n.start_time', + '>=', + $this->getBeginDate($interval)->format('Y-m-d H:i:s') + ) + ); + + $query->order('notification_start_time', 'desc'); + + $records = $query->paginate(10000); + $slots = array(); + + foreach ($records as $item) { + $id = strftime('%Y-%m-%d %H:%I:00', $item->notification_start_time); + + if (empty($slots[$id])) { + $slots[$id] = 0; + } + + $slots[$id]++; + } + + $out = new stdClass(); + $out->avg = sprintf('%.2f', array_sum($slots) / count($slots)); + $out->last = array_shift($slots); + + return $out; + } + + private function createProblemData() + { + $interval = $this->getInterval(); $query = $this->backend->select()->from('eventhistory', array( 'host_name', @@ -111,27 +249,58 @@ class Monitoring_AlertsummaryController extends Controller ) ); + $defects = array(); $records = $query->paginate(10000); + $period = $this->createPeriod($interval); + + foreach ($period as $entry) { + $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); + $defects[$id] = array($id, 0); + } foreach ($records as $item) { $id = $this->getPeriodFormat($interval, $item->timestamp); - if (empty($data[$id])) { + if (empty($defects[$id])) { $defects[$id] = array($id, 0); } - $defects[$id][1]++; } + return $defects; + } + + public function createDefectImage() + { + $gridChart = new GridChart(); + $interval = $this->getInterval(); + + $gridChart->alignTopLeft(); + $gridChart->setAxisLabel('', t('Services')) + ->setXAxis(new StaticAxis()) + ->setAxisMin(null, 0) + ->setYAxis(new \Icinga\Chart\Unit\LinearUnit(10)); + + + + $gridChart->drawBars( + array( + 'label' => $this->translate('Notifications'), + 'color' => 'green', + 'data' => $this->notificationData, + 'showPoints' => true + ) + ); + $gridChart->drawLines( array( 'label' => $this->translate('Defects'), 'color' => 'red', - 'data' => $defects, + 'data' => $this->problemData, 'showPoints' => true ) ); - $this->view->chart = $gridChart; + return $gridChart; } private function createRecentAlerts() diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 9648145b7..1c8f671a0 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -32,6 +32,10 @@ class Monitoring_ListController extends Controller { $this->createTabs(); $this->view->compact = $this->_request->getParam('view') === 'compact'; + if ($this->_request->getParam('view') === 'inline') { + $this->view->compact = true; + $this->view->inline = true; + } $this->url = Url::fromRequest(); } diff --git a/modules/monitoring/application/views/scripts/alertsummary/defectimage.phtml b/modules/monitoring/application/views/scripts/alertsummary/defectimage.phtml index 91f20a837..e69de29bb 100644 --- a/modules/monitoring/application/views/scripts/alertsummary/defectimage.phtml +++ b/modules/monitoring/application/views/scripts/alertsummary/defectimage.phtml @@ -1,7 +0,0 @@ -compact) { ?> -
- render(); ?> -
- - render(); ?> - diff --git a/modules/monitoring/application/views/scripts/alertsummary/index.phtml b/modules/monitoring/application/views/scripts/alertsummary/index.phtml index 9fd3350c3..44a22eaa4 100644 --- a/modules/monitoring/application/views/scripts/alertsummary/index.phtml +++ b/modules/monitoring/application/views/scripts/alertsummary/index.phtml @@ -2,36 +2,62 @@ $helper = $this->getHelper('MonitoringState'); ?>
+ tabs ?>
+ widget('limiter') ?> + paginationControl($notifications, null, null, array('preserve' => $this->preserve)) ?>
-

Alert summary

+

translate('Alert summary'); ?>

+ +

translate('Notifications'); ?>

+ +
+ render(); ?> +
+ +

translate('Trend'); ?>

-

Notifications

- action('defectimage', 'alertsummary', 'monitoring', array('interval' => $this->interval)); ?> + translate('Average') ?> + perf->avg; ?> + translate('notifications per hour'); ?>, + perf->last; ?> + translate('in the last hour'); ?>. +
+ +
+ translate('Trend, problems in 24h'); ?> + (trend->percent; ?>% + translate($this->trend->trend); ?>) + + + trend->trend === 'up'): ?> + icon('up.png'); ?> + trend->trend === 'unchanged'): ?> + icon('next.png'); ?> + + icon('down.png'); ?> + +
recentAlerts): ?>

translate('Top 5 recent alerts'); ?>

- - - - - - - - recentAlerts as $alert): ?> - - - - - - - -
translate('Host'); ?>translate('Service'); ?>translate('State'); ?>translate('Timestamp'); ?>
host; ?>service; ?>monitoringStateById($alert->notification_state, $alert->service ? 'service' : 'host')) ?>prefixedTimeSince($alert->notification_start_time, true) ?>
+ partial('list/notifications.phtml', array( + 'notifications' => $this->recentAlerts, + 'compact' => true, + 'inline' => true + )); ?> + +

translate('History'); ?>

+ partial('list/notifications.phtml', array( + 'notifications' => $this->notifications, + 'compact' => true, + 'inline' => true + )); ?>
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/list/notifications.phtml b/modules/monitoring/application/views/scripts/list/notifications.phtml index 826254fd8..fcfea9055 100644 --- a/modules/monitoring/application/views/scripts/list/notifications.phtml +++ b/modules/monitoring/application/views/scripts/list/notifications.phtml @@ -1,4 +1,4 @@ - +compact); ?> compact): ?>
tabs ?> @@ -10,7 +10,10 @@
+inline): ?>
+ + notifications)) { @@ -75,4 +78,6 @@ foreach ($notifications as $notification): +inline): ?>
+ From 0825b2f0711e1f206a411592e829728b55cf4f7f Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Wed, 8 Oct 2014 15:01:24 +0200 Subject: [PATCH 3/7] AlertSummary: Add healing chart refs #4076 --- .../controllers/AlertsummaryController.php | 138 ++++++++++++++++-- .../views/scripts/alertsummary/index.phtml | 8 +- .../views/scripts/list/notifications.phtml | 1 - .../Backend/Ido/Query/NotificationQuery.php | 17 ++- 4 files changed, 152 insertions(+), 12 deletions(-) diff --git a/modules/monitoring/application/controllers/AlertsummaryController.php b/modules/monitoring/application/controllers/AlertsummaryController.php index 238dd59d2..df430c975 100644 --- a/modules/monitoring/application/controllers/AlertsummaryController.php +++ b/modules/monitoring/application/controllers/AlertsummaryController.php @@ -3,6 +3,7 @@ // {{{ICINGA_LICENSE_HEADER}}} use Icinga\Chart\GridChart; +use Icinga\Chart\Unit\LinearUnit; use Icinga\Chart\Unit\StaticAxis; use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Web\Widget\SelectBox; @@ -49,6 +50,7 @@ class Monitoring_AlertsummaryController extends Controller $this->view->recentAlerts = $this->createRecentAlerts(); $this->view->interval = $this->getInterval(); $this->view->defectChart = $this->createDefectImage(); + $this->view->healingChart = $this->createHealingChart(); $this->view->perf = $this->createNotificationPerfdata(); $this->view->trend = $this->createTrendInformation(); @@ -87,10 +89,12 @@ class Monitoring_AlertsummaryController extends Controller $query->order('notification_start_time', 'asc'); - $records = $query->paginate(10000); + $records = $query->getQuery()->fetchAll(); $data = array(); $period = $this->createPeriod($interval); + + foreach ($period as $entry) { $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); $data[$id] = array($id, 0); @@ -132,7 +136,7 @@ class Monitoring_AlertsummaryController extends Controller $query->order('notification_start_time', 'asc'); - $records = $query->paginate(10000); + $records = $query->getQuery()->fetchAll(); $slots = array(); $period = new DatePeriod($beginDate, new DateInterval('P1D'), 2, DatePeriod::EXCLUDE_START_DATE); @@ -194,7 +198,7 @@ class Monitoring_AlertsummaryController extends Controller $query->order('notification_start_time', 'desc'); - $records = $query->paginate(10000); + $records = $query->getQuery()->fetchAll(); $slots = array(); foreach ($records as $item) { @@ -250,7 +254,7 @@ class Monitoring_AlertsummaryController extends Controller ); $defects = array(); - $records = $query->paginate(10000); + $records = $query->getQuery()->fetchAll(); $period = $this->createPeriod($interval); foreach ($period as $entry) { @@ -269,18 +273,134 @@ class Monitoring_AlertsummaryController extends Controller return $defects; } + public function createHealingChart() + { + $gridChart = new GridChart(); + + $gridChart->alignTopLeft(); + $gridChart->setAxisLabel('', t('Notifications')) + ->setXAxis(new StaticAxis()) + ->setAxisMin(null, 0) + ->setYAxis(new LinearUnit(10)); + + $interval = $this->getInterval(); + + $query = $this->backend->select()->from('notification', array( + 'host', + 'service', + 'notification_object_id', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state', + 'acknowledgement_entry_time' + )); + + $query->setFilter( + new Icinga\Data\Filter\FilterExpression( + 'n.start_time', + '>=', + $this->getBeginDate($interval)->format('Y-m-d H:i:s') + ) + ); + + $query->order('notification_start_time', 'asc'); + + $records = $query->getQuery()->fetchAll(); + + $interval = $this->getInterval(); + $period = $this->createPeriod($interval); + $dAvg = array(); + $dMax = array(); + $notifications = array(); + $rData = array(); + + foreach ($period as $entry) { + $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); + $dMax[$id] = array($id, 0); + $dAvg[$id] = array($id, 0, 0); + $notifications[$id] = array($id, 0); + } + + foreach ($records as $item) { + $id = $this->getPeriodFormat($interval, $item->notification_start_time); + + if ($item->notification_state == '0' && isset($rData[$item->notification_object_id])) { + $rData[$item->notification_object_id]['recover'] = $item->notification_start_time - $rData[$item->notification_object_id]['entry']; + } elseif ($item->notification_state !== '0') { + $recover = 0; + if ($item->acknowledgement_entry_time) { + $recover = $item->acknowledgement_entry_time - $item->notification_start_time; + } + $rData[$item->notification_object_id] = array( + 'id' => $id, + 'entry' => $item->notification_start_time, + 'recover' => $recover + ); + } + } + + foreach ($rData as $item) { + $notifications[$item['id']][1]++; + + if ($item['recover'] > $dMax[$item['id']][1]) { + $dMax[$item['id']][1] = (int) $item['recover']; + } + + $dAvg[$item['id']][1] += (int) $item['recover']; + $dAvg[$item['id']][2]++; + } + + foreach ($dAvg as &$item) { + if ($item[2] > 0) { + $item[1] = ($item[1]/$item[2])/60/60; + } + } + + foreach ($dMax as &$item) { + $item[1] = $item[1]/60/60; + } + + + $gridChart->drawBars( + array( + 'label' => $this->translate('Notifications'), + 'color' => 'blue', + 'data' => $notifications, + 'showPoints' => true + ) + ); + + $gridChart->drawLines( + array( + 'label' => $this->translate('Avg (min)'), + 'color' => 'orange', + 'data' => $dAvg, + 'showPoints' => true + ) + ); + + $gridChart->drawLines( + array( + 'label' => $this->translate('Max (min)'), + 'color' => 'red', + 'data' => $dMax, + 'showPoints' => true + ) + ); + + return $gridChart; + } + public function createDefectImage() { $gridChart = new GridChart(); - $interval = $this->getInterval(); $gridChart->alignTopLeft(); - $gridChart->setAxisLabel('', t('Services')) + $gridChart->setAxisLabel('', t('Notifications')) ->setXAxis(new StaticAxis()) ->setAxisMin(null, 0) - ->setYAxis(new \Icinga\Chart\Unit\LinearUnit(10)); - - + ->setYAxis(new LinearUnit(10)); $gridChart->drawBars( array( diff --git a/modules/monitoring/application/views/scripts/alertsummary/index.phtml b/modules/monitoring/application/views/scripts/alertsummary/index.phtml index 44a22eaa4..9b253dad8 100644 --- a/modules/monitoring/application/views/scripts/alertsummary/index.phtml +++ b/modules/monitoring/application/views/scripts/alertsummary/index.phtml @@ -13,12 +13,18 @@ $helper = $this->getHelper('MonitoringState');

translate('Alert summary'); ?>

-

translate('Notifications'); ?>

+

translate('Notifications and problems'); ?>

render(); ?>
+

translate('Time to reaction (Ack, Recover)'); ?>

+ +
+ render(); ?> +
+

translate('Trend'); ?>

diff --git a/modules/monitoring/application/views/scripts/list/notifications.phtml b/modules/monitoring/application/views/scripts/list/notifications.phtml index fcfea9055..d1ff9cdad 100644 --- a/modules/monitoring/application/views/scripts/list/notifications.phtml +++ b/modules/monitoring/application/views/scripts/list/notifications.phtml @@ -1,4 +1,3 @@ -compact); ?> compact): ?>
tabs ?> diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php index 882600240..00b6957b8 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php @@ -18,7 +18,8 @@ class NotificationQuery extends IdoQuery 'notification' => array( 'notification_output' => 'n.output', 'notification_start_time' => 'UNIX_TIMESTAMP(n.start_time)', - 'notification_state' => 'n.state' + 'notification_state' => 'n.state', + 'notification_object_id' => 'n.object_id' ), 'objects' => array( 'host' => 'o.name1', @@ -30,6 +31,11 @@ class NotificationQuery extends IdoQuery ), 'command' => array( 'notification_command' => 'cmd_o.name1' + ), + 'acknowledgement' => array( + 'acknowledgement_entry_time' => 'UNIX_TIMESTAMP(a.entry_time)', + 'acknowledgement_author_name' => 'a.author_name', + 'acknowledgement_comment_data' => 'a.comment_data' ) ); @@ -95,4 +101,13 @@ class NotificationQuery extends IdoQuery array() ); } + + protected function joinAcknowledgement() + { + $this->select->joinLeft( + array('a' => $this->prefix . 'acknowledgements'), + 'n.object_id = a.object_id', + array() + ); + } } From 8f26f0dbf645cf12849c926ecbfa3425fe2c8702 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Wed, 8 Oct 2014 16:17:43 +0200 Subject: [PATCH 4/7] AlertSummary: Basic styling refs #4074 --- .../views/scripts/alertsummary/index.phtml | 91 +++++++++++-------- modules/monitoring/public/css/module.less | 24 +++++ 2 files changed, 76 insertions(+), 39 deletions(-) diff --git a/modules/monitoring/application/views/scripts/alertsummary/index.phtml b/modules/monitoring/application/views/scripts/alertsummary/index.phtml index 9b253dad8..8e8c08e1c 100644 --- a/modules/monitoring/application/views/scripts/alertsummary/index.phtml +++ b/modules/monitoring/application/views/scripts/alertsummary/index.phtml @@ -13,57 +13,70 @@ $helper = $this->getHelper('MonitoringState');

translate('Alert summary'); ?>

-

translate('Notifications and problems'); ?>

+
+
+

translate('Notifications and problems'); ?>

+
+ render(); ?> +
+
-
- render(); ?> -
- -

translate('Time to reaction (Ack, Recover)'); ?>

- -
- render(); ?> +
+

translate('Time to reaction (Ack, Recover)'); ?>

+
+ render(); ?> +
+

translate('Trend'); ?>

-
- translate('Average') ?> - perf->avg; ?> - translate('notifications per hour'); ?>, - perf->last; ?> - translate('in the last hour'); ?>. -
+
+
+ translate('Average') ?> + perf->avg; ?> + translate('notifications per hour'); ?>, + perf->last; ?> + translate('in the last hour'); ?>. -
- translate('Trend, problems in 24h'); ?> - (trend->percent; ?>% - translate($this->trend->trend); ?>) + translate('Trend for the last 24h'); ?> + (trend->percent; ?>% + translate($this->trend->trend); ?>) - - trend->trend === 'up'): ?> - icon('up.png'); ?> - trend->trend === 'unchanged'): ?> - icon('next.png'); ?> - - icon('down.png'); ?> - - + + trend->trend === 'up'): ?> + icon('up.png'); ?> + trend->trend === 'unchanged'): ?> + icon('next.png'); ?> + + icon('down.png'); ?> + + +
recentAlerts): ?>

translate('Top 5 recent alerts'); ?>

- partial('list/notifications.phtml', array( - 'notifications' => $this->recentAlerts, - 'compact' => true, - 'inline' => true - )); ?> + +
+
+ partial('list/notifications.phtml', array( + 'notifications' => $this->recentAlerts, + 'compact' => true, + 'inline' => true + )); ?> +
+

translate('History'); ?>

- partial('list/notifications.phtml', array( - 'notifications' => $this->notifications, - 'compact' => true, - 'inline' => true - )); ?> +
+
+ partial('list/notifications.phtml', array( + 'notifications' => $this->notifications, + 'compact' => true, + 'inline' => true + )); ?> +
+
\ No newline at end of file diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index 5ab6d9c82..7e734bb33 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -134,3 +134,27 @@ span.state.pending { form.instance-features span.description, form.object-features span.description { display: inline; } + +.alertsummary-flex-container { + display: -ms-Flexbox; + -ms-box-orient: horizontal; + + display: -webkit-flex; + display: -moz-flex; + display: -ms-flex; + display: flex; + + -webkit-flex-flow: row wrap; + -moz-flex-flow: row wrap; + -ms-flex-flow: row wrap; + flex-flow: row wrap; +} + +.alertsummary-flex { + flex: 1 1 auto; + overflow: auto; + border: 1px #333 solid; + padding: 5px; + margin: 0 10px 0 0; + border-spacing: 10px 10px; +} From 66220425395c202ea4f37e7b43ede1b38bdee9d2 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Wed, 8 Oct 2014 16:32:50 +0200 Subject: [PATCH 5/7] AlertSummary: Controller clean up refs #4074 --- .../controllers/AlertsummaryController.php | 268 +++++++++++++----- 1 file changed, 190 insertions(+), 78 deletions(-) diff --git a/modules/monitoring/application/controllers/AlertsummaryController.php b/modules/monitoring/application/controllers/AlertsummaryController.php index df430c975..e6d4f51d7 100644 --- a/modules/monitoring/application/controllers/AlertsummaryController.php +++ b/modules/monitoring/application/controllers/AlertsummaryController.php @@ -11,18 +11,28 @@ use Icinga\Web\Url; class Monitoring_AlertsummaryController extends Controller { + /** + * @var string + */ protected $url; + /** + * @var array + */ private $notificationData; + /** + * @var array + */ private $problemData; + /** + * Init data set + */ public function init() { $tabs = $this->getTabs(); - if (in_array($this->_request->getActionName(), array( - 'alertsummary', - ))) { + if (in_array($this->_request->getActionName(), array('alertsummary'))) { $tabs->extend(new OutputFormat())->extend(new DashboardAction()); } @@ -32,17 +42,26 @@ class Monitoring_AlertsummaryController extends Controller $this->problemData = $this->createProblemData(); } + /** + * @param string $action + * @param bool $title + */ protected function addTitleTab($action, $title = false) { $title = $title ?: ucfirst($action); - $this->getTabs()->add($action, array( - 'title' => $title, - // 'url' => Url::fromPath('monitoring/list/' . $action) - 'url' => $this->url - ))->activate($action); + $this->getTabs()->add( + $action, + array( + 'title' => $title, + 'url' => $this->url + ) + )->activate($action); $this->view->title = $title; } + /** + * Creat full report + */ public function indexAction() { $this->addTitleTab('alertsummary'); @@ -55,29 +74,41 @@ class Monitoring_AlertsummaryController extends Controller $this->view->trend = $this->createTrendInformation(); $this->setAutorefreshInterval(15); - $query = $this->backend->select()->from('notification', array( - 'host', - 'service', - 'notification_output', - 'notification_contact', - 'notification_start_time', - 'notification_state' - )); + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); $this->view->notifications = $query->paginate(); } - private function createNotificationData() { + /** + * Create data for charts + * + * @return array + */ + private function createNotificationData() + { $interval = $this->getInterval(); - $query = $this->backend->select()->from('notification', array( - 'host', - 'service', - 'notification_output', - 'notification_contact', - 'notification_start_time', - 'notification_state' - )); + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); $query->setFilter( new Icinga\Data\Filter\FilterExpression( @@ -93,8 +124,6 @@ class Monitoring_AlertsummaryController extends Controller $data = array(); $period = $this->createPeriod($interval); - - foreach ($period as $entry) { $id = $this->getPeriodFormat($interval, $entry->getTimestamp()); $data[$id] = array($id, 0); @@ -112,19 +141,27 @@ class Monitoring_AlertsummaryController extends Controller return $data; } + /** + * Trend information for notifications + * + * @return stdClass + */ private function createTrendInformation() { $date = new DateTime(); $beginDate = $date->sub(new DateInterval('P3D')); - $query = $this->backend->select()->from('notification', array( - 'host', - 'service', - 'notification_output', - 'notification_contact', - 'notification_start_time', - 'notification_state' - )); + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); $query->setFilter( new Icinga\Data\Filter\FilterExpression( @@ -175,18 +212,26 @@ class Monitoring_AlertsummaryController extends Controller return $out; } + /** + * Perfdata for notifications + * + * @return stdClass + */ private function createNotificationPerfdata() { $interval = $this->getInterval(); - $query = $this->backend->select()->from('notification', array( - 'host', - 'service', - 'notification_output', - 'notification_contact', - 'notification_start_time', - 'notification_state' - )); + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); $query->setFilter( new Icinga\Data\Filter\FilterExpression( @@ -218,24 +263,32 @@ class Monitoring_AlertsummaryController extends Controller return $out; } + /** + * Problems for notifications + * + * @return array + */ private function createProblemData() { $interval = $this->getInterval(); - $query = $this->backend->select()->from('eventhistory', array( - 'host_name', - 'service_description', - 'object_type', - 'timestamp', - 'state', - 'attempt', - 'max_attempts', - 'output', - 'type', - 'host', - 'service', - 'service_host_name' - )); + $query = $this->backend->select()->from( + 'eventhistory', + array( + 'host_name', + 'service_description', + 'object_type', + 'timestamp', + 'state', + 'attempt', + 'max_attempts', + 'output', + 'type', + 'host', + 'service', + 'service_host_name' + ) + ); $query->addFilter( new Icinga\Data\Filter\FilterExpression( @@ -273,6 +326,11 @@ class Monitoring_AlertsummaryController extends Controller return $defects; } + /** + * Healing svg image + * + * @return GridChart + */ public function createHealingChart() { $gridChart = new GridChart(); @@ -285,16 +343,19 @@ class Monitoring_AlertsummaryController extends Controller $interval = $this->getInterval(); - $query = $this->backend->select()->from('notification', array( - 'host', - 'service', - 'notification_object_id', - 'notification_output', - 'notification_contact', - 'notification_start_time', - 'notification_state', - 'acknowledgement_entry_time' - )); + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_object_id', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state', + 'acknowledgement_entry_time' + ) + ); $query->setFilter( new Icinga\Data\Filter\FilterExpression( @@ -326,7 +387,8 @@ class Monitoring_AlertsummaryController extends Controller $id = $this->getPeriodFormat($interval, $item->notification_start_time); if ($item->notification_state == '0' && isset($rData[$item->notification_object_id])) { - $rData[$item->notification_object_id]['recover'] = $item->notification_start_time - $rData[$item->notification_object_id]['entry']; + $rData[$item->notification_object_id]['recover'] = + $item->notification_start_time - $rData[$item->notification_object_id]['entry']; } elseif ($item->notification_state !== '0') { $recover = 0; if ($item->acknowledgement_entry_time) { @@ -392,6 +454,11 @@ class Monitoring_AlertsummaryController extends Controller return $gridChart; } + /** + * Notifications and defects + * + * @return GridChart + */ public function createDefectImage() { $gridChart = new GridChart(); @@ -423,22 +490,35 @@ class Monitoring_AlertsummaryController extends Controller return $gridChart; } + /** + * Top recent alerts + * + * @return mixed + */ private function createRecentAlerts() { - $query = $this->backend->select()->from('notification', array( - 'host', - 'service', - 'notification_output', - 'notification_contact', - 'notification_start_time', - 'notification_state' - )); + $query = $this->backend->select()->from( + 'notification', + array( + 'host', + 'service', + 'notification_output', + 'notification_contact', + 'notification_start_time', + 'notification_state' + ) + ); $query->order('notification_start_time', 'desc'); return $query->paginate(5); } + /** + * Interval selector box + * + * @return SelectBox + */ private function createIntervalBox() { $box = new SelectBox( @@ -456,6 +536,14 @@ class Monitoring_AlertsummaryController extends Controller return $box; } + /** + * Return reasonable date time format for an interval + * + * @param string $interval + * @param string $timestamp + * + * @return string + */ private function getPeriodFormat($interval, $timestamp) { $format = ''; @@ -472,6 +560,12 @@ class Monitoring_AlertsummaryController extends Controller return strftime($format, $timestamp); } + /** + * Create a reasonable period based in interval strings + * + * @param $interval + * @return DatePeriod + */ private function createPeriod($interval) { if ($interval === '1d') { @@ -485,6 +579,12 @@ class Monitoring_AlertsummaryController extends Controller } } + /** + * Return start timestamps based on interval strings + * + * @param $interval + * @return DateTime|null + */ private function getBeginDate($interval) { $new = new DateTime(); @@ -501,8 +601,20 @@ class Monitoring_AlertsummaryController extends Controller return null; } + /** + * Getter for interval + * + * @return string + * + * @throws Zend_Controller_Action_Exception + */ private function getInterval() { - return $this->getParam('interval', '1d'); + $interval = $this->getParam('interval', '1d'); + if (false === in_array($interval, array('1d', '1w', '1m', '1y'))) { + throw new Zend_Controller_Action_Exception($this->translate('Value for interval not valid')); + } + + return $interval; } -} \ No newline at end of file +} From cc61dc2b01a09ffbcd45d719ea5e8ce4a74bd004 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 8 Oct 2014 17:43:28 +0200 Subject: [PATCH 6/7] Form: Don't override Zend_Form::setDefaults() fixes #7315 --- library/Icinga/Web/Form.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index b32a03075..631b2eb07 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -14,6 +14,18 @@ use Icinga\Web\Form\Element\CsrfCounterMeasure; /** * Base class for forms providing CSRF protection, confirmation logic and auto submission + * + * @method $this setDefaults(array $defaults) { + * Use `Form::populate()' for setting default values for elements instead because `Form::setDefaults()' does not + * create the form via `Form::create()'. + * + * Due to a BC introduced with https://github.com/mhujer/zf1/commit/244e3d3f88a363ee0ca49cf63eee31f925f515cd + * we cannot override this function without running into a strict standards violation on Zend version 1.12.7. + * + * @param array $defaults + * + * @return $this + * } */ class Form extends Zend_Form { @@ -508,10 +520,10 @@ class Form extends Zend_Form * * @param array $defaults The values to populate the elements with */ - public function setDefaults(array $defaults) + public function populate(array $defaults) { $this->create($defaults); - return parent::setDefaults($defaults); + return parent::populate($defaults); } /** From c67e7c3ef0cc468b666a75d2bc4c727c3faa5c64 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Thu, 9 Oct 2014 09:31:03 +0200 Subject: [PATCH 7/7] AlertSummary: Format trend change percent value --- .../application/controllers/AlertsummaryController.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/controllers/AlertsummaryController.php b/modules/monitoring/application/controllers/AlertsummaryController.php index e6d4f51d7..8aeb27c76 100644 --- a/modules/monitoring/application/controllers/AlertsummaryController.php +++ b/modules/monitoring/application/controllers/AlertsummaryController.php @@ -205,8 +205,10 @@ class Monitoring_AlertsummaryController extends Controller } elseif ($yesterday === $today) { $out->percent = 0; } else { - $out->percent = 100 - - ((100/($yesterday > $today ? $yesterday : $today)) * ($yesterday > $today ? $today : $yesterday)); + $out->percent = sprintf( + '%.2f', + 100 - ((100/($yesterday > $today ? $yesterday : $today)) * ($yesterday > $today ? $today : $yesterday)) + ); } return $out;