From 3e02d5aff85babc3ffbfcf52313f2108e313aa23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 13:46:43 +0200 Subject: Adding upstream version 2.12.1. Signed-off-by: Daniel Baumann --- .../application/controllers/TimelineController.php | 325 +++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 modules/monitoring/application/controllers/TimelineController.php (limited to 'modules/monitoring/application/controllers/TimelineController.php') diff --git a/modules/monitoring/application/controllers/TimelineController.php b/modules/monitoring/application/controllers/TimelineController.php new file mode 100644 index 0000000..deeeb36 --- /dev/null +++ b/modules/monitoring/application/controllers/TimelineController.php @@ -0,0 +1,325 @@ +getTabs()->add( + 'timeline', + array( + 'title' => $this->translate('Show the number of historical event records grouped by time and type'), + 'label' => $this->translate('Timeline'), + 'url' => Url::fromRequest() + ) + )->extend(new DashboardAction())->extend(new MenuAction())->activate('timeline'); + $this->view->title = $this->translate('Timeline'); + + // TODO: filter for hard_states (precedence adjustments necessary!) + $this->setupIntervalBox(); + list($displayRange, $forecastRange) = $this->buildTimeRanges(); + + $detailUrl = Url::fromPath('monitoring/list/eventhistory'); + + $timeline = new TimeLine( + $this->applyRestriction( + 'monitoring/filter/objects', + $this->backend->select()->from( + 'eventhistory', + array( + 'name' => 'type', + 'time' => 'timestamp' + ) + ) + ), + array( + 'notification_ack' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_flapping' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_flapping_end' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_dt_start' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_dt_end' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_custom' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'notification_state' => array( + 'class' => 'timeline-notification', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Notifications'), + 'groupBy' => 'notification_*' + ), + 'hard_state' => array( + 'class' => 'timeline-hard-state', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Hard state changes') + ), + 'comment' => array( + 'class' => 'timeline-comment', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Comments') + ), + 'ack' => array( + 'class' => 'timeline-ack', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Acknowledgements') + ), + 'dt_start' => array( + 'class' => 'timeline-downtime-start', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Started downtimes') + ), + 'dt_end' => array( + 'class' => 'timeline-downtime-end', + 'detailUrl' => $detailUrl, + 'label' => mt('monitoring', 'Ended downtimes') + ) + ) + ); + $timeline->setMaximumCircleWidth('6em'); + $timeline->setMinimumCircleWidth('0.3em'); + $timeline->setDisplayRange($displayRange); + $timeline->setForecastRange($forecastRange); + $beingExtended = $this->getRequest()->getParam('extend') == 1; + $timeline->setSession($this->Window()->getSessionNamespace('timeline', !$beingExtended)); + + $this->view->timeline = $timeline; + $this->view->nextRange = $forecastRange; + $this->view->beingExtended = $beingExtended; + $this->view->intervalFormat = $this->getIntervalFormat(); + $oldBase = $timeline->getCalculationBase(false); + $this->view->switchedContext = $oldBase !== null && $oldBase !== $timeline->getCalculationBase(true); + } + + /** + * Create a select box the user can choose the timeline interval from + */ + private function setupIntervalBox() + { + $box = new SelectBox( + 'intervalBox', + array( + '4h' => mt('monitoring', '4 Hours'), + '1d' => mt('monitoring', 'One day'), + '1w' => mt('monitoring', 'One week'), + '1m' => mt('monitoring', 'One month'), + '1y' => mt('monitoring', 'One year') + ), + mt('monitoring', 'TimeLine interval'), + 'interval' + ); + $box->applyRequest($this->getRequest()); + $this->view->intervalBox = $box; + } + + /** + * Return the chosen interval + * + * @return DateInterval The chosen interval + */ + private function getTimelineInterval() + { + switch ($this->view->intervalBox->getInterval()) { + case '1d': + return new DateInterval('P1D'); + case '1w': + return new DateInterval('P1W'); + case '1m': + return new DateInterval('P1M'); + case '1y': + return new DateInterval('P1Y'); + default: + return new DateInterval('PT4H'); + } + } + + /** + * Get an appropriate datetime format string for the chosen interval + * + * @return string + */ + private function getIntervalFormat() + { + switch ($this->view->intervalBox->getInterval()) { + case '1d': + return $this->getDateFormat(); + case '1w': + return '\W\e\ek W\\of Y'; + case '1m': + return 'F Y'; + case '1y': + return 'Y'; + default: + return $this->getDateFormat() . '\' . $this->getTimeFormat(); + } + } + + /** + * Return a preload interval based on the chosen timeline interval and the given date and time + * + * @param DateTime $dateTime The date and time to use + * + * @return DateInterval The interval to pre-load + */ + private function getPreloadInterval(DateTime $dateTime) + { + switch ($this->view->intervalBox->getInterval()) { + case '1d': + return DateInterval::createFromDateString('1 week -1 second'); + case '1w': + return DateInterval::createFromDateString('8 weeks -1 second'); + case '1m': + $dateCopy = clone $dateTime; + for ($i = 0; $i < 6; $i++) { + $dateCopy->sub(new DateInterval('PT' . Format::secondsByMonth($dateCopy) . 'S')); + } + return $dateCopy->add(new DateInterval('PT1S'))->diff($dateTime); + case '1y': + $dateCopy = clone $dateTime; + for ($i = 0; $i < 4; $i++) { + $dateCopy->sub(new DateInterval('PT' . Format::secondsByYear($dateCopy) . 'S')); + } + return $dateCopy->add(new DateInterval('PT1S'))->diff($dateTime); + default: + return DateInterval::createFromDateString('1 day -1 second'); + } + } + + /** + * Extrapolate the given datetime based on the chosen timeline interval + * + * @param DateTime $dateTime The datetime to extrapolate + */ + private function extrapolateDateTime(DateTime &$dateTime) + { + switch ($this->view->intervalBox->getInterval()) { + case '1d': + $dateTime->setTimestamp(strtotime('tomorrow', $dateTime->getTimestamp()) - 1); + break; + case '1w': + $dateTime->setTimestamp(strtotime('next monday', $dateTime->getTimestamp()) - 1); + break; + case '1m': + $dateTime->setTimestamp( + strtotime( + 'last day of this month', + strtotime( + 'tomorrow', + $dateTime->getTimestamp() + ) - 1 + ) + ); + break; + case '1y': + $dateTime->setTimestamp(strtotime('1 january next year', $dateTime->getTimestamp()) - 1); + break; + default: + $hour = $dateTime->format('G'); + $end = $hour < 4 ? 4 : ($hour < 8 ? 8 : ($hour < 12 ? 12 : ($hour < 16 ? 16 : ($hour < 20 ? 20 : 24)))); + $dateTime = DateTime::createFromFormat( + 'd/m/y G:i:s', + $dateTime->format('d/m/y') . ($end - 1) . ':59:59' + ); + } + } + + /** + * Return a display- and forecast time range + * + * Assembles a time range each for display and forecast purposes based on the start- and + * end time if given in the current request otherwise based on the current time and a + * end time that is calculated based on the chosen timeline interval. + * + * @return array The resulting time ranges + */ + private function buildTimeRanges() + { + $startTime = new DateTime(); + $startParam = $this->_request->getParam('start'); + $startTimestamp = is_numeric($startParam) ? intval($startParam) : strtotime($startParam ?? ''); + if ($startTimestamp !== false) { + $startTime->setTimestamp($startTimestamp); + } else { + $this->extrapolateDateTime($startTime); + } + + $endTime = clone $startTime; + $endParam = $this->_request->getParam('end'); + $endTimestamp = is_numeric($endParam) ? intval($endParam) : strtotime($endParam ?? ''); + if ($endTimestamp !== false) { + $endTime->setTimestamp($endTimestamp); + } else { + $endTime->sub($this->getPreloadInterval($startTime)); + } + + $forecastStart = clone $endTime; + $forecastStart->sub(new DateInterval('PT1S')); + $forecastEnd = clone $forecastStart; + $forecastEnd->sub($this->getPreloadInterval($forecastStart)); + + $timelineInterval = $this->getTimelineInterval(); + return array( + new TimeRange($startTime, $endTime, $timelineInterval), + new TimeRange($forecastStart, $forecastEnd, $timelineInterval) + ); + } + + /** + * Get the user's preferred time format or the application's default + * + * @return string + */ + private function getTimeFormat() + { + return 'H:i'; + } + + /** + * Get the user's preferred date format or the application's default + * + * @return string + */ + private function getDateFormat() + { + return 'Y-m-d'; + } +} -- cgit v1.2.3