mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-07-27 07:44:04 +02:00
parent
e21d288f5b
commit
18b5f715c5
@ -1,72 +1,47 @@
|
|||||||
<?php
|
<?php
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
use \DateTime;
|
use \DateTime;
|
||||||
use \Exception;
|
|
||||||
use \DateInterval;
|
use \DateInterval;
|
||||||
use Icinga\Web\Hook;
|
use \Zend_Config;
|
||||||
use Icinga\Application\Config;
|
use Icinga\Application\Config;
|
||||||
use Icinga\Exception\ProgrammingError;
|
|
||||||
use Icinga\Web\Controller\ActionController;
|
use Icinga\Web\Controller\ActionController;
|
||||||
use Icinga\Module\Monitoring\Timeline\TimeLine;
|
use Icinga\Module\Monitoring\Timeline\TimeLine;
|
||||||
use Icinga\Module\Monitoring\Timeline\TimeEntry;
|
|
||||||
use Icinga\Module\Monitoring\Timeline\TimeRange;
|
use Icinga\Module\Monitoring\Timeline\TimeRange;
|
||||||
use Icinga\Module\Monitoring\Web\Widget\TimelineIntervalBox;
|
use Icinga\Module\Monitoring\Web\Widget\TimelineIntervalBox;
|
||||||
use Icinga\Module\Monitoring\DataView\EventHistory as EventHistoryView;
|
use Icinga\Module\Monitoring\DataView\EventHistory as EventHistoryView;
|
||||||
|
|
||||||
class Monitoring_TimelineController extends ActionController
|
class Monitoring_TimelineController extends ActionController
|
||||||
{
|
{
|
||||||
public function showAction()
|
public function indexAction()
|
||||||
{
|
{
|
||||||
$this->setupIntervalBox();
|
$this->setupIntervalBox();
|
||||||
$timeline = new TimeLine();
|
list($displayRange, $forecastRange) = $this->buildTimeRanges();
|
||||||
$timeline->setConfiguration(Config::app());
|
|
||||||
//$timeline->setAttrib('data-icinga-component', 'monitoring/timelineComponent');
|
|
||||||
list($displayRange, $forecastRange) = $this->buildTimeRanges($this->getTimelineInterval());
|
|
||||||
$timeline->setTimeRange($displayRange);
|
|
||||||
$timeline->setDisplayData($this->loadData($displayRange));
|
|
||||||
$timeline->setForecastData($this->loadData($forecastRange));
|
|
||||||
$this->view->timeline = $timeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function extendAction()
|
$timeline = new TimeLine(
|
||||||
{
|
EventHistoryView::fromRequest(
|
||||||
$this->setupIntervalBox();
|
$this->getRequest(),
|
||||||
$timeline = new TimeLine();
|
array(
|
||||||
$timeline->setConfiguration(Config::app());
|
'name' => 'type',
|
||||||
list($displayRange, $forecastRange) = $this->buildTimeRanges($this->getTimelineInterval());
|
'time' => 'raw_timestamp'
|
||||||
$timeline->setTimeRange($displayRange);
|
)
|
||||||
$timeline->setDisplayData($this->loadData($displayRange));
|
),
|
||||||
$timeline->setForecastData($this->loadData($forecastRange));
|
array(
|
||||||
$this->view->timeline = $timeline;
|
'notify' => array('label' => t('Notifications'), 'color' => 'red'),
|
||||||
|
'hard_state' => array('label' => t('Hard state changes'), 'color' => 'green'),
|
||||||
|
'comment' => array('label' => t('Comments'), 'color' => 'blue'),
|
||||||
|
'ack' => array('label' => t('Acknowledgements'), 'color' => 'black'),
|
||||||
|
'dt_start' => array('label' => t('Started downtimes'), 'color' => 'grey'),
|
||||||
|
'dt_end' => array('label' => t('Ended downtimes'), 'color' => 'white')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$timeline->setDisplayRange($displayRange);
|
||||||
|
$timeline->setForecastRange($forecastRange);
|
||||||
|
|
||||||
// Disable layout as this is an AJAX request
|
$this->view->timeline = $timeline;
|
||||||
$this->_helper->layout()->disableLayout();
|
$this->view->intervalFormat = $this->getIntervalFormat();
|
||||||
|
$this->view->switchedContext = $timeline->getCalculationBase(false) !== $timeline->getCalculationBase(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,59 +86,65 @@ class Monitoring_TimelineController extends ActionController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a new display- and forecast time range
|
* Get an appropriate datetime format string for the chosen interval
|
||||||
*
|
*
|
||||||
* Assembles a time range each for display and forecast purposes based on the start- and
|
* @return string
|
||||||
* end time if given in the current request otherwise based on the current time and a
|
|
||||||
* end time that is calculated based on the given interval.
|
|
||||||
*
|
|
||||||
* @param DateInterval $interval The interval by which to part the time range
|
|
||||||
* @return TimeRange The resulting time range
|
|
||||||
* @throws Exception If a start time is given in the request but no end time
|
|
||||||
*/
|
*/
|
||||||
private function buildTimeRanges(DateInterval $interval)
|
private function getIntervalFormat()
|
||||||
{
|
{
|
||||||
$startTime = DateTime::createFromFormat('Y-m-d_G-i-s', $this->_request->getParam('start'));
|
switch ($this->view->intervalBox->getInterval())
|
||||||
$endTime = DateTime::createFromFormat('Y-m-d_G-i-s', $this->_request->getParam('end'));
|
{
|
||||||
|
case '1d':
|
||||||
if (!$startTime) {
|
return $this->getDateFormat();
|
||||||
$startTime = $this->extrapolateDateTime(new DateTime(), $interval);
|
case '1w':
|
||||||
$endTime = clone $startTime;
|
return '\W\e\ek #W \of Y';
|
||||||
$endTime->sub($this->getPreloadInterval($interval));
|
case '1m':
|
||||||
} elseif (!$endTime) {
|
return 'F Y';
|
||||||
throw new Exception('Missing end time in request');
|
case '1y':
|
||||||
|
return 'Y';
|
||||||
|
default:
|
||||||
|
return $this->getDateFormat() . ' ' . $this->getTimeFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
$forecastStart = clone $endTime;
|
|
||||||
$forecastStart->sub(new DateInterval('PT1S'));
|
|
||||||
$forecastEnd = clone $forecastStart;
|
|
||||||
$forecastEnd->sub($endTime->diff($startTime));
|
|
||||||
|
|
||||||
return array(
|
|
||||||
new TimeRange($startTime, $endTime, $interval),
|
|
||||||
new TimeRange($forecastStart, $forecastEnd, $interval)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extrapolate the given datetime based on the given interval
|
* Return a preload interval based on the chosen timeline interval
|
||||||
|
*
|
||||||
|
* @return DateInterval The interval to pre-load
|
||||||
|
*/
|
||||||
|
private function getPreloadInterval()
|
||||||
|
{
|
||||||
|
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':
|
||||||
|
return DateInterval::createFromDateString('6 months -1 second');
|
||||||
|
case '1y':
|
||||||
|
return DateInterval::createFromDateString('4 years -1 second');
|
||||||
|
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
|
* @param DateTime $dateTime The datetime to extrapolate
|
||||||
* @param DateInterval $interval The interval by which to part a time range
|
|
||||||
* @return DateTime The extrapolated datetime
|
|
||||||
* @throws Exception If the given interval is invalid
|
|
||||||
*/
|
*/
|
||||||
private function extrapolateDateTime(DateTime $dateTime, DateInterval $interval)
|
private function extrapolateDateTime(DateTime &$dateTime)
|
||||||
{
|
{
|
||||||
if ($interval->h == 4) {
|
switch ($this->view->intervalBox->getInterval())
|
||||||
$hour = $dateTime->format('G');
|
{
|
||||||
$end = $hour < 4 ? 4 : ($hour < 8 ? 8 : ($hour < 12 ? 12 : ($hour < 16 ? 16 : ($hour < 20 ? 20 : 24))));
|
case '1d':
|
||||||
$dateTime = DateTime::createFromFormat('d/m/y G:i:s', $dateTime->format('d/m/y') . ($end - 1) . ':59:59');
|
|
||||||
} elseif ($interval->d == 1) {
|
|
||||||
$dateTime->setTimestamp(strtotime('tomorrow', $dateTime->getTimestamp()) - 1);
|
$dateTime->setTimestamp(strtotime('tomorrow', $dateTime->getTimestamp()) - 1);
|
||||||
} elseif ($interval->d == 7) {
|
break;
|
||||||
|
case '1w':
|
||||||
$dateTime->setTimestamp(strtotime('next monday', $dateTime->getTimestamp()) - 1);
|
$dateTime->setTimestamp(strtotime('next monday', $dateTime->getTimestamp()) - 1);
|
||||||
} elseif ($interval->m == 1) {
|
break;
|
||||||
|
case '1m':
|
||||||
$dateTime->setTimestamp(
|
$dateTime->setTimestamp(
|
||||||
strtotime(
|
strtotime(
|
||||||
'last day of this month',
|
'last day of this month',
|
||||||
@ -173,317 +154,95 @@ class Monitoring_TimelineController extends ActionController
|
|||||||
) - 1
|
) - 1
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} elseif ($interval->y == 1) {
|
break;
|
||||||
|
case '1y':
|
||||||
$dateTime->setTimestamp(strtotime('1 january next year', $dateTime->getTimestamp()) - 1);
|
$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();
|
||||||
|
$startTimestamp = strtotime($this->_request->getParam('start'));
|
||||||
|
if ($startTimestamp !== false) {
|
||||||
|
$startTime->setTimestamp($startTimestamp);
|
||||||
|
}
|
||||||
|
$this->extrapolateDateTime($startTime);
|
||||||
|
|
||||||
|
$endTime = clone $startTime;
|
||||||
|
$endTimestamp = strtotime($this->_request->getParam('end'));
|
||||||
|
if ($endTimestamp !== false) {
|
||||||
|
$endTime->setTimestamp($endTimestamp);
|
||||||
} else {
|
} else {
|
||||||
throw new Exception('Invalid interval given. Valid intervals are: 4 hours, 1 day, 1 week, 1 month, 1 year');
|
$endTime->sub($this->getPreloadInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $dateTime;
|
$forecastStart = clone $endTime;
|
||||||
}
|
$forecastStart->sub(new DateInterval('PT1S'));
|
||||||
|
$forecastEnd = clone $forecastStart;
|
||||||
|
$forecastEnd->sub($endTime->diff($startTime));
|
||||||
|
|
||||||
/**
|
$timelineInterval = $this->getTimelineInterval();
|
||||||
* Return a new preload interval
|
return array(
|
||||||
*
|
new TimeRange($startTime, $endTime, $timelineInterval),
|
||||||
* Examine the given interval and return a new one that defines how much data should be loaded
|
new TimeRange($forecastStart, $forecastEnd, $timelineInterval)
|
||||||
*
|
|
||||||
* @param DateInterval $interval The interval by which to part a time range
|
|
||||||
* @return DateInterval The interval to load
|
|
||||||
* @throws Exception If the given interval is invalid
|
|
||||||
*/
|
|
||||||
private function getPreloadInterval(DateInterval $interval)
|
|
||||||
{
|
|
||||||
if ($interval->h == 4) {
|
|
||||||
return DateInterval::createFromDateString('1 day -1 second');
|
|
||||||
} elseif ($interval->d == 1) {
|
|
||||||
return DateInterval::createFromDateString('1 week -1 second');
|
|
||||||
} elseif ($interval->d == 7) {
|
|
||||||
return DateInterval::createFromDateString('8 weeks -1 second');
|
|
||||||
} elseif ($interval->m == 1) {
|
|
||||||
return DateInterval::createFromDateString('6 months -1 second');
|
|
||||||
} elseif ($interval->y == 1) {
|
|
||||||
return DateInterval::createFromDateString('4 years -1 second');
|
|
||||||
} else {
|
|
||||||
throw new Exception('Invalid interval given. Valid intervals are: 4 hours, 1 day, 1 week, 1 month, 1 year');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Groups a set of elements based on a specific range of time
|
|
||||||
*
|
|
||||||
* @param TimeRange $range The range of time represented by the timeline
|
|
||||||
* @param array $elements The elements to group. Each element need to have a ´time´ property
|
|
||||||
* that defines its position in the given range of time
|
|
||||||
* @param array $attributes The attributes to set on each event group. Need to contain at least
|
|
||||||
* a ´name´ and a ´detailUrl´. The detailUrl need also to contain
|
|
||||||
* placeholders for both the start- and end time of a specific timeframe
|
|
||||||
* @return array A list of event groups suitable to pass to the timeline
|
|
||||||
* @throws ProgrammingError If an element is found that does not match the given range of time
|
|
||||||
* or one of the required attributes is missing
|
|
||||||
*/
|
|
||||||
private function groupResults(TimeRange $range, array $elements, array $attributes)
|
|
||||||
{
|
|
||||||
$groupCounts = array();
|
|
||||||
foreach ($elements as $element) {
|
|
||||||
$elementTime = new DateTime();
|
|
||||||
$elementTime->setTimestamp($element->time);
|
|
||||||
$timeframeIdentifier = $range->findTimeframe($elementTime, true);
|
|
||||||
|
|
||||||
if ($timeframeIdentifier === null) {
|
|
||||||
$format = 'd/m/y G:i:s';
|
|
||||||
throw new ProgrammingError(
|
|
||||||
'Event result does not match any timeframe in the given range of time: ' .
|
|
||||||
$elementTime->format($format) . ' not in ' . $range->getStart()->format($format) .
|
|
||||||
' -> ' . $range->getEnd()->format($format)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists($timeframeIdentifier, $groupCounts)) {
|
|
||||||
$groupCounts[$timeframeIdentifier] += 1;
|
|
||||||
} else {
|
|
||||||
$groupCounts[$timeframeIdentifier] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!array_key_exists('name', $attributes) || !array_key_exists('detailUrl', $attributes)) {
|
|
||||||
throw new ProgrammingError('Missing required event group attribute. Either ´name´ or ´detailUrl´');
|
|
||||||
}
|
|
||||||
|
|
||||||
$groups = array();
|
|
||||||
$urlTemplate = $attributes['detailUrl'];
|
|
||||||
foreach ($groupCounts as $timeframeIdentifier => $groupCount) {
|
|
||||||
$timeframe = $range->getTimeframe($timeframeIdentifier);
|
|
||||||
$attributes['dateTime'] = $timeframe->start;
|
|
||||||
$attributes['value'] = $groupCount;
|
|
||||||
$attributes['detailUrl'] = sprintf(
|
|
||||||
$urlTemplate,
|
|
||||||
$timeframe->start->getTimestamp(),
|
|
||||||
$timeframe->end->getTimestamp()
|
|
||||||
);
|
|
||||||
$groups[] = TimeEntry::fromArray($attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the event groups that the timeline should display
|
|
||||||
*
|
|
||||||
* @param TimeRange $timeRange The range of time represented by the timeline
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function loadData(TimeRange $timeRange)
|
|
||||||
{
|
|
||||||
$entries = array_merge(
|
|
||||||
$this->loadInitiatedDowntimes($timeRange),
|
|
||||||
$this->loadFinishedDowntimes($timeRange),
|
|
||||||
$this->loadAcknowledgements($timeRange),
|
|
||||||
$this->loadNotifications($timeRange),
|
|
||||||
$this->loadStateChanges($timeRange),
|
|
||||||
$this->loadComments($timeRange)
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach (Hook::all('timeline') as $timelineProvider) {
|
|
||||||
$entries = array_merge(
|
|
||||||
$entries,
|
|
||||||
$timelineProvider->fetchTimeEntries($timeRange, $this->_request)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aggregate all problem notifications sent out in the given range of time
|
|
||||||
*
|
|
||||||
* @param TimeRange $range The range of time represented by the timeline
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function loadNotifications(TimeRange $range)
|
|
||||||
{
|
|
||||||
$query = EventHistoryView::fromRequest(
|
|
||||||
$this->_request,
|
|
||||||
array(
|
|
||||||
'time' => 'timestamp'
|
|
||||||
)
|
|
||||||
)->getQuery();
|
|
||||||
|
|
||||||
$result = $query->where('timestamp <= ' . $range->getStart()->getTimestamp())
|
|
||||||
->where('timestamp > ' . $range->getEnd()->getTimestamp())
|
|
||||||
->where('type = notify')
|
|
||||||
->where('state != 0')
|
|
||||||
->fetchAll();
|
|
||||||
|
|
||||||
return $this->groupResults(
|
|
||||||
$range,
|
|
||||||
$result,
|
|
||||||
array(
|
|
||||||
'name' => t('Notifications'),
|
|
||||||
'detailUrl' => $this->view->baseUrl(
|
|
||||||
'monitoring/list/eventhistory?timestamp<=%s×tamp>=%s&type=notify&state>0'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aggregate all status changes occured in the given range of time
|
* Get the application's global configuration or an empty one
|
||||||
*
|
*
|
||||||
* @param TimeRange $range The range of time represented by the timeline
|
* @return Zend_Config
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
private function loadStateChanges(TimeRange $range)
|
private function getGlobalConfiguration()
|
||||||
{
|
{
|
||||||
$query = EventHistoryView::fromRequest(
|
$globalConfig = Config::app()->global;
|
||||||
$this->_request,
|
|
||||||
array(
|
|
||||||
'time' => 'timestamp'
|
|
||||||
)
|
|
||||||
)->getQuery();
|
|
||||||
|
|
||||||
$result = $query->where('timestamp <= ' . $range->getStart()->getTimestamp())
|
if ($globalConfig === null) {
|
||||||
->where('timestamp > ' . $range->getEnd()->getTimestamp())
|
$globalConfig = new Zend_Config(array());
|
||||||
->where('type = hard_state')
|
}
|
||||||
->where('state != 0')
|
|
||||||
->fetchAll();
|
|
||||||
|
|
||||||
return $this->groupResults(
|
return $globalConfig;
|
||||||
$range,
|
|
||||||
$result,
|
|
||||||
array(
|
|
||||||
'name' => t('Hard states'),
|
|
||||||
'detailUrl' => $this->view->baseUrl(
|
|
||||||
'monitoring/list/eventhistory?timestamp<=%s×tamp>=%s&type=hard_state&state>0'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aggregate all comments made in the given range of time
|
* Get the user's preferred time format or the application's default
|
||||||
*
|
*
|
||||||
* @param TimeRange $range The range of time represented by the timeline
|
* @return string
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
private function loadComments(TimeRange $range)
|
private function getTimeFormat()
|
||||||
{
|
{
|
||||||
$query = EventHistoryView::fromRequest(
|
$globalConfig = $this->getGlobalConfiguration();
|
||||||
$this->_request,
|
$preferences = $this->getRequest()->getUser()->getPreferences();
|
||||||
array(
|
return $preferences->get('app.timeFormat', $globalConfig->get('timeFormat', 'g:i A'));
|
||||||
'time' => 'timestamp'
|
|
||||||
)
|
|
||||||
)->getQuery();
|
|
||||||
|
|
||||||
$result = $query->where('timestamp <= ' . $range->getStart()->getTimestamp())
|
|
||||||
->where('timestamp > ' . $range->getEnd()->getTimestamp())
|
|
||||||
->where('type = comment')
|
|
||||||
->fetchAll();
|
|
||||||
|
|
||||||
return $this->groupResults(
|
|
||||||
$range,
|
|
||||||
$result,
|
|
||||||
array(
|
|
||||||
'name' => t('Comments'),
|
|
||||||
'detailUrl' => $this->view->baseUrl(
|
|
||||||
'monitoring/list/eventhistory?timestamp<=%s×tamp>=%s&type=comment'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aggregate all acknowledgements placed in the given range of time
|
* Get the user's preferred date format or the application's default
|
||||||
*
|
*
|
||||||
* @param TimeRange $range The range of time represented by the timeline
|
* @return string
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
private function loadAcknowledgements(TimeRange $range)
|
private function getDateFormat()
|
||||||
{
|
{
|
||||||
$query = EventHistoryView::fromRequest(
|
$globalConfig = $this->getGlobalConfiguration();
|
||||||
$this->_request,
|
$preferences = $this->getRequest()->getUser()->getPreferences();
|
||||||
array(
|
return $preferences->get('app.dateFormat', $globalConfig->get('dateFormat', 'd/m/Y'));
|
||||||
'time' => 'timestamp'
|
|
||||||
)
|
|
||||||
)->getQuery();
|
|
||||||
|
|
||||||
$result = $query->where('timestamp <= ' . $range->getStart()->getTimestamp())
|
|
||||||
->where('timestamp > ' . $range->getEnd()->getTimestamp())
|
|
||||||
->where('type = ack')
|
|
||||||
->fetchAll();
|
|
||||||
|
|
||||||
return $this->groupResults(
|
|
||||||
$range,
|
|
||||||
$result,
|
|
||||||
array(
|
|
||||||
'name' => t('Acknowledgements'),
|
|
||||||
'detailUrl' => $this->view->baseUrl(
|
|
||||||
'monitoring/list/eventhistory?timestamp<=%s×tamp>=%s&type=ack'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aggregate all downtimes that were initiated in the given range of time
|
|
||||||
*
|
|
||||||
* @param TimeRange $range The range of time represented by the timeline
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function loadInitiatedDowntimes(TimeRange $range)
|
|
||||||
{
|
|
||||||
$query = EventHistoryView::fromRequest(
|
|
||||||
$this->_request,
|
|
||||||
array(
|
|
||||||
'time' => 'timestamp'
|
|
||||||
)
|
|
||||||
)->getQuery();
|
|
||||||
|
|
||||||
$result = $query->where('timestamp <= ' . $range->getStart()->getTimestamp())
|
|
||||||
->where('timestamp > ' . $range->getEnd()->getTimestamp())
|
|
||||||
->where('type = dt_start')
|
|
||||||
->fetchAll();
|
|
||||||
|
|
||||||
return $this->groupResults(
|
|
||||||
$range,
|
|
||||||
$result,
|
|
||||||
array(
|
|
||||||
'name' => t('Initiated downtimes'),
|
|
||||||
'detailUrl' => $this->view->baseUrl(
|
|
||||||
'monitoring/list/eventhistory?timestamp<=%s×tamp>=%s&type=dt_start'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aggregate all downtimes that were finished in the given range of time
|
|
||||||
*
|
|
||||||
* @param TimeRange $range The range of time represented by the timeline
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function loadFinishedDowntimes(TimeRange $range)
|
|
||||||
{
|
|
||||||
$query = EventHistoryView::fromRequest(
|
|
||||||
$this->_request,
|
|
||||||
array(
|
|
||||||
'time' => 'timestamp'
|
|
||||||
)
|
|
||||||
)->getQuery();
|
|
||||||
|
|
||||||
$result = $query->where('timestamp <= ' . $range->getStart()->getTimestamp())
|
|
||||||
->where('timestamp > ' . $range->getEnd()->getTimestamp())
|
|
||||||
->where('type = dt_end')
|
|
||||||
->fetchAll();
|
|
||||||
|
|
||||||
return $this->groupResults(
|
|
||||||
$range,
|
|
||||||
$result,
|
|
||||||
array(
|
|
||||||
'name' => t('Finished downtimes'),
|
|
||||||
'detailUrl' => $this->view->baseUrl(
|
|
||||||
'monitoring/list/eventhistory?timestamp<=%s×tamp>=%s&type=dt_end'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<?= $this->timeline ?>
|
|
@ -1,29 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
|
||||||
* This file is part of Icinga 2 Web.
|
|
||||||
*
|
|
||||||
* Icinga 2 Web - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
namespace Icinga\Module\Monitoring\Timeline;
|
namespace Icinga\Module\Monitoring\Timeline;
|
||||||
@ -41,42 +17,49 @@ class TimeEntry
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $name;
|
protected $name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of events that are part of this group
|
* The amount of events that are part of this group
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $value;
|
protected $value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The date and time of this group
|
* The date and time of this group
|
||||||
*
|
*
|
||||||
* @var DateTime
|
* @var DateTime
|
||||||
*/
|
*/
|
||||||
private $dateTime;
|
protected $dateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The url to this group's detail view
|
* The url to this group's detail view
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $detailUrl;
|
protected $detailUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The weight of this group
|
* The weight of this group
|
||||||
*
|
*
|
||||||
* @var float
|
* @var float
|
||||||
*/
|
*/
|
||||||
private $weight = 1.0;
|
protected $weight = 1.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The label of this group
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $label;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The color of this group
|
* The color of this group
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $color;
|
protected $color;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a new TimeEntry object with the given attributes being set
|
* Return a new TimeEntry object with the given attributes being set
|
||||||
@ -136,12 +119,11 @@ class TimeEntry
|
|||||||
/**
|
/**
|
||||||
* Return the amount of events in this group
|
* Return the amount of events in this group
|
||||||
*
|
*
|
||||||
* @param bool $raw Whether to ignore the set weight
|
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function getValue($raw = false)
|
public function getValue()
|
||||||
{
|
{
|
||||||
return $raw ? $this->value : $this->value * $this->weight;
|
return $this->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -204,6 +186,26 @@ class TimeEntry
|
|||||||
return $this->weight;
|
return $this->weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this group's label
|
||||||
|
*
|
||||||
|
* @param string $label The label to set
|
||||||
|
*/
|
||||||
|
public function setLabel($label)
|
||||||
|
{
|
||||||
|
$this->label = $label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the label of this group
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLabel()
|
||||||
|
{
|
||||||
|
return $this->label;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this group's color
|
* Set this group's color
|
||||||
*
|
*
|
||||||
|
@ -1,116 +1,135 @@
|
|||||||
<?php
|
<?php
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
namespace Icinga\Module\Monitoring\Timeline;
|
namespace Icinga\Module\Monitoring\Timeline;
|
||||||
|
|
||||||
use \Zend_Config;
|
use \DateTime;
|
||||||
|
use \Exception;
|
||||||
|
use \ArrayIterator;
|
||||||
|
use \IteratorAggregate;
|
||||||
|
use Icinga\Web\Hook;
|
||||||
|
use Icinga\Module\Monitoring\DataView\DataView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a set of events in a specific time range
|
* Represents a set of events in a specific range of time
|
||||||
*/
|
*/
|
||||||
class TimeLine
|
class TimeLine implements IteratorAggregate
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The range of time represented by this timeline
|
* The resultset returned by the dataview
|
||||||
*
|
|
||||||
* @var TimeRange
|
|
||||||
*/
|
|
||||||
private $range;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The event groups this timeline will display
|
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $displayData;
|
private $resultset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event groups this timeline uses to calculate forecasts
|
* The groups this timeline uses for display purposes
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $forecastData;
|
private $displayGroups;
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum diameter each circle can have
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $circleDiameter = 250;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The unit of a circle's diameter
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $diameterUnit = 'px';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base that is used to calculate each circle's diameter
|
* The base that is used to calculate each circle's diameter
|
||||||
*
|
*
|
||||||
* @var float
|
* @var float
|
||||||
*/
|
*/
|
||||||
private $calculationBase;
|
protected $calculationBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the range of time to represent
|
* The dataview to fetch entries from
|
||||||
*
|
*
|
||||||
* @param TimeRange $range The range of time to represent
|
* @var DataView
|
||||||
*/
|
*/
|
||||||
public function setTimeRange(TimeRange $range)
|
protected $dataview;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The names by which to group entries
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $identifiers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The range of time for which to display entries
|
||||||
|
*
|
||||||
|
* @var TimeRange
|
||||||
|
*/
|
||||||
|
protected $displayRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The range of time for which to calculate forecasts
|
||||||
|
*
|
||||||
|
* @var TimeRange
|
||||||
|
*/
|
||||||
|
protected $forecastRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum diameter each circle can have
|
||||||
|
*
|
||||||
|
* @var float
|
||||||
|
*/
|
||||||
|
protected $circleDiameter = 100.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unit of a circle's diameter
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $diameterUnit = 'px';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a iterator for this timeline
|
||||||
|
*
|
||||||
|
* @return ArrayIterator
|
||||||
|
*/
|
||||||
|
public function getIterator()
|
||||||
{
|
{
|
||||||
$this->range = $range;
|
return new ArrayIterator($this->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the groups this timeline should display
|
* Create a new timeline
|
||||||
*
|
*
|
||||||
* @param array $entries The TimeEntry objects
|
* The given dataview must provide the following columns:
|
||||||
|
* - name A string identifying an entry (Corresponds to the keys of "$identifiers")
|
||||||
|
* - time A unix timestamp that defines where to place an entry on the timeline
|
||||||
|
*
|
||||||
|
* @param DataView $dataview The dataview to fetch entries from
|
||||||
|
* @param array $identifiers The names by which to group entries
|
||||||
*/
|
*/
|
||||||
public function setDisplayData(array $entries)
|
public function __construct(DataView $dataview, array $identifiers)
|
||||||
{
|
{
|
||||||
$this->displayData = $entries;
|
$this->dataview = $dataview;
|
||||||
|
$this->identifiers = $identifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the groups this timeline should use to calculate forecasts
|
* Set the range of time for which to display elements
|
||||||
*
|
*
|
||||||
* @param array $entries The TimeEntry objects
|
* @param TimeRange $range The range of time for which to display elements
|
||||||
*/
|
*/
|
||||||
public function setForecastData(array $entries)
|
public function setDisplayRange(TimeRange $range)
|
||||||
{
|
{
|
||||||
$this->forecastData = $entries;
|
$this->displayRange = $range;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the range of time for which to calculate forecasts
|
||||||
|
*
|
||||||
|
* @param TimeRange $range The range of time for which to calculate forecasts
|
||||||
|
*/
|
||||||
|
public function setForecastRange(TimeRange $range)
|
||||||
|
{
|
||||||
|
$this->forecastRange = $range;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the maximum diameter each circle can have
|
* Set the maximum diameter each circle can have
|
||||||
*
|
*
|
||||||
* @param string $width The diameter to set, suffixed with its unit
|
* @param string $width The diameter to set, suffixed with its unit
|
||||||
|
*
|
||||||
* @throws Exception If the given diameter is invalid
|
* @throws Exception If the given diameter is invalid
|
||||||
*/
|
*/
|
||||||
public function setMaximumCircleWidth($width)
|
public function setMaximumCircleWidth($width)
|
||||||
@ -124,6 +143,247 @@ class TimeLine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the circle's diameter for the given event group
|
||||||
|
*
|
||||||
|
* @param TimeEntry $group The group for which to return a circle width
|
||||||
|
* @param int $precision Amount of decimal places to preserve
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function calculateCircleWidth(TimeEntry $group, $precision = 0)
|
||||||
|
{
|
||||||
|
$base = $this->getCalculationBase(true);
|
||||||
|
$factor = log($group->getValue() * $group->getWeight(), $base) / 100;
|
||||||
|
return sprintf('%.' . $precision . 'F%s', $this->circleDiameter * $factor, $this->diameterUnit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an extrapolated circle width for the given event group
|
||||||
|
*
|
||||||
|
* @param TimeEntry $group The event group for which to return an extrapolated circle width
|
||||||
|
* @param int $precision Amount of decimal places to preserve
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExtrapolatedCircleWidth(TimeEntry $group, $precision = 0)
|
||||||
|
{
|
||||||
|
$eventCount = 0;
|
||||||
|
foreach ($this->displayGroups as $groups) {
|
||||||
|
if (array_key_exists($group->getName(), $groups)) {
|
||||||
|
$eventCount += $groups[$group->getName()]->getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$extrapolatedCount = (int) $eventCount / count($this->displayGroups);
|
||||||
|
if ($extrapolatedCount < $group->getValue()) {
|
||||||
|
return $this->calculateCircleWidth($group, $precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->calculateCircleWidth(
|
||||||
|
TimeEntry::fromArray(
|
||||||
|
array(
|
||||||
|
'value' => $extrapolatedCount,
|
||||||
|
'weight' => $group->getWeight()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$precision
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the base that should be used to calculate circle widths
|
||||||
|
*
|
||||||
|
* @param bool $create Whether to generate a new base if none is known yet
|
||||||
|
*
|
||||||
|
* @return float|null
|
||||||
|
*/
|
||||||
|
public function getCalculationBase($create)
|
||||||
|
{
|
||||||
|
if ($this->calculationBase === null) {
|
||||||
|
// TODO: get base from session
|
||||||
|
if ($create) {
|
||||||
|
$new = $this->generateCalculationBase();
|
||||||
|
if ($new > $this->calculationBase) {
|
||||||
|
// TODO: save base in session
|
||||||
|
$this->calculationBase = $new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->calculationBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new base to calculate circle widths with
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
protected function generateCalculationBase()
|
||||||
|
{
|
||||||
|
$allEntries = $this->groupEntries(
|
||||||
|
array_merge(
|
||||||
|
$this->fetchEntries(),
|
||||||
|
$this->fetchForecasts()
|
||||||
|
),
|
||||||
|
new TimeRange(
|
||||||
|
$this->displayRange->getStart(),
|
||||||
|
$this->forecastRange->getEnd(),
|
||||||
|
$this->displayRange->getInterval()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$highestValue = 0;
|
||||||
|
foreach ($allEntries as $groups) {
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
if ($group->getValue() * $group->getWeight() > $highestValue) {
|
||||||
|
$highestValue = $group->getValue() * $group->getWeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pow($highestValue, 1 / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all entries and forecasts by using the dataview associated with this timeline
|
||||||
|
*
|
||||||
|
* @return array The dataview's result
|
||||||
|
*/
|
||||||
|
private function fetchResults()
|
||||||
|
{
|
||||||
|
$hookResults = array();
|
||||||
|
foreach (Hook::all('timeline') as $timelineProvider) {
|
||||||
|
$hookResults = array_merge(
|
||||||
|
$hookResults,
|
||||||
|
$timelineProvider->fetchEntries($this->displayRange),
|
||||||
|
$timelineProvider->fetchForecasts($this->forecastRange)
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($timelineProvider->getIdentifiers() as $identifier => $attributes) {
|
||||||
|
if (!array_key_exists($identifier, $this->identifiers)) {
|
||||||
|
$this->identifiers[$identifier] = $attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $this->dataview->getQuery();
|
||||||
|
$queryColumns = $query->getColumns();
|
||||||
|
$query->where(
|
||||||
|
$query->isValidFilterTarget('name') ? 'name' : $queryColumns['name'],
|
||||||
|
array_keys($this->identifiers)
|
||||||
|
)->where('raw_timestamp <= ?', $this->displayRange->getStart()->getTimestamp())
|
||||||
|
->where('raw_timestamp > ?', $this->forecastRange->getEnd()->getTimestamp());
|
||||||
|
|
||||||
|
return array_merge($query->fetchAll(), $hookResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all entries
|
||||||
|
*
|
||||||
|
* @return array The entries to display on the timeline
|
||||||
|
*/
|
||||||
|
protected function fetchEntries()
|
||||||
|
{
|
||||||
|
if ($this->resultset === null) {
|
||||||
|
$this->resultset = $this->fetchResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
$range = $this->displayRange;
|
||||||
|
return array_filter(
|
||||||
|
$this->resultset,
|
||||||
|
function ($e) use ($range) { return $range->validateTime($e->time); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all forecasts
|
||||||
|
*
|
||||||
|
* @return array The entries to calculate forecasts with
|
||||||
|
*/
|
||||||
|
protected function fetchForecasts()
|
||||||
|
{
|
||||||
|
if ($this->resultset === null) {
|
||||||
|
$this->resultset = $this->fetchResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
$range = $this->forecastRange;
|
||||||
|
return array_filter(
|
||||||
|
$this->resultset,
|
||||||
|
function ($e) use ($range) { return $range->validateTime($e->time); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the given entries grouped together
|
||||||
|
*
|
||||||
|
* @param array $entries The entries to group
|
||||||
|
* @param TimeRange $timeRange The range of time to group by
|
||||||
|
*
|
||||||
|
* @return array displayGroups The grouped entries
|
||||||
|
*/
|
||||||
|
protected function groupEntries(array $entries, TimeRange $timeRange)
|
||||||
|
{
|
||||||
|
$counts = array();
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$entryTime = new DateTime();
|
||||||
|
$entryTime->setTimestamp($entry->time);
|
||||||
|
$timestamp = $timeRange->findTimeframe($entryTime, true);
|
||||||
|
|
||||||
|
if ($timestamp !== null) {
|
||||||
|
if (array_key_exists($entry->name, $counts)) {
|
||||||
|
if (array_key_exists($timestamp, $counts[$entry->name])) {
|
||||||
|
$counts[$entry->name][$timestamp] += 1;
|
||||||
|
} else {
|
||||||
|
$counts[$entry->name][$timestamp] = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$counts[$entry->name][$timestamp] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$groups = array();
|
||||||
|
foreach ($counts as $name => $data) {
|
||||||
|
foreach ($data as $timestamp => $count) {
|
||||||
|
$dateTime = new DateTime();
|
||||||
|
$dateTime->setTimestamp($timestamp);
|
||||||
|
$groups[$timestamp][$name] = TimeEntry::fromArray(
|
||||||
|
array_merge(
|
||||||
|
$this->identifiers[$name],
|
||||||
|
array(
|
||||||
|
'name' => $name,
|
||||||
|
'value' => $count,
|
||||||
|
'dateTime' => $dateTime
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the contents of this timeline as array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function toArray()
|
||||||
|
{
|
||||||
|
$this->displayGroups = $this->groupEntries($this->fetchEntries(), $this->displayRange);
|
||||||
|
|
||||||
|
$array = array();
|
||||||
|
foreach ($this->displayRange as $timestamp => $timeframe) {
|
||||||
|
$array[] = array(
|
||||||
|
$timeframe,
|
||||||
|
array_key_exists($timestamp, $this->displayGroups) ? $this->displayGroups[$timestamp] : array()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the legend
|
* Build the legend
|
||||||
*/
|
*/
|
||||||
@ -225,173 +485,4 @@ class TimeLine
|
|||||||
$elements[] = '<span id="TimelineEnd"></span>';
|
$elements[] = '<span id="TimelineEnd"></span>';
|
||||||
return implode('', $elements);
|
return implode('', $elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return contextless attributes of all available distinct group types
|
|
||||||
*
|
|
||||||
* Returns an associative array where each key refers to the name
|
|
||||||
* and the value to the attributes of a specific group type.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getGroups()
|
|
||||||
{
|
|
||||||
$groups = array();
|
|
||||||
foreach (array_merge($this->displayData, $this->forecastData) as $group) {
|
|
||||||
if (!array_key_exists($group->getName(), $groups)) {
|
|
||||||
$groups[$group->getName()] = array(
|
|
||||||
'color' => $group->getColor(),
|
|
||||||
'weight' => $group->getWeight()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the circle's diameter for the given amount of events
|
|
||||||
*
|
|
||||||
* @param int $eventCount The amount of events represented by the circle
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private function calculateCircleWidth($eventCount)
|
|
||||||
{
|
|
||||||
if (!isset($this->calculationBase)) {
|
|
||||||
$highestValue = max(
|
|
||||||
array_map(
|
|
||||||
function ($g) { return $g->getValue(); },
|
|
||||||
array_merge($this->displayData, $this->forecastData)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->calculationBase = 1;//$this->getRequest()->getParam('calculationBase', 1);
|
|
||||||
while (log($highestValue, $this->calculationBase) > 100) {
|
|
||||||
$this->calculationBase += 0.01;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*$this->addElement(
|
|
||||||
'hidden',
|
|
||||||
'calculationBase',
|
|
||||||
array(
|
|
||||||
'value' => $this->calculationBase
|
|
||||||
)
|
|
||||||
);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return intval($this->circleDiameter * (log($eventCount, $this->calculationBase) / 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an extrapolated event count for the given event group
|
|
||||||
*
|
|
||||||
* @param TimeEntry $eventGroup The event group for which to return an extrapolated event count
|
|
||||||
* @param int $offset The amount of intervals to consider for the extrapolation
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private function extrapolateEventCount(TimeEntry $eventGroup, $offset)
|
|
||||||
{
|
|
||||||
$start = $eventGroup->getDateTime();
|
|
||||||
$end = clone $start;
|
|
||||||
|
|
||||||
for ($i = 0; $i < $offset; $i++) {
|
|
||||||
$end->sub($this->range->getInterval());
|
|
||||||
}
|
|
||||||
|
|
||||||
$eventCount = 0;
|
|
||||||
foreach ($this->displayData as $group) {
|
|
||||||
if ($group->getName() === $eventGroup->getName() &&
|
|
||||||
$group->getDateTime() <= $start && $group->getDateTime() > $end) {
|
|
||||||
$eventCount += $group->getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$extrapolatedCount = (int) $eventCount / $offset;
|
|
||||||
return $extrapolatedCount > $eventGroup->getValue() ? $extrapolatedCount : $eventGroup->getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a random generated CSS color hex code
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function getRandomCssColor()
|
|
||||||
{
|
|
||||||
return '#' . str_pad(dechex(rand(256,16777215)), 6, '0', STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an appropriate datetime format string for the current interval
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function getIntervalFormat()
|
|
||||||
{
|
|
||||||
$interval = $this->range->getInterval();
|
|
||||||
|
|
||||||
if ($interval->h == 4) {
|
|
||||||
return $this->getDateFormat() . ' ' . $this->getTimeFormat();
|
|
||||||
} elseif ($interval->d == 1) {
|
|
||||||
return $this->getDateFormat();
|
|
||||||
} elseif ($interval->d == 7) {
|
|
||||||
return '\W\e\ek #W \of Y';
|
|
||||||
} elseif ($interval->m == 1) {
|
|
||||||
return 'F Y';
|
|
||||||
} else { // $interval->y == 1
|
|
||||||
return 'Y';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setConfiguration($config)
|
|
||||||
{
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConfiguration()
|
|
||||||
{
|
|
||||||
return $this->config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the application's global configuration or an empty one
|
|
||||||
*
|
|
||||||
* @return Zend_Config
|
|
||||||
*/
|
|
||||||
private function getGlobalConfiguration()
|
|
||||||
{
|
|
||||||
$config = $this->getConfiguration();
|
|
||||||
$global = $config->global;
|
|
||||||
|
|
||||||
if ($global === null) {
|
|
||||||
$global = new Zend_Config(array());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $global;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the user's preferred time format or the application's default
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function getTimeFormat()
|
|
||||||
{
|
|
||||||
return 'g:i A';
|
|
||||||
$globalConfig = $this->getGlobalConfiguration();
|
|
||||||
$preferences = $this->getUserPreferences();
|
|
||||||
return $preferences->get('app.timeFormat', $globalConfig->get('timeFormat', 'g:i A'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the user's preferred date format or the application's default
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function getDateFormat()
|
|
||||||
{
|
|
||||||
return 'd/m/Y';
|
|
||||||
$globalConfig = $this->getGlobalConfiguration();
|
|
||||||
$preferences = $this->getUserPreferences();
|
|
||||||
return $preferences->get('app.dateFormat', $globalConfig->get('dateFormat', 'd/m/Y'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
namespace Icinga\Module\Monitoring\Timeline;
|
namespace Icinga\Module\Monitoring\Timeline;
|
||||||
@ -45,35 +21,35 @@ class TimeRange implements Iterator
|
|||||||
*
|
*
|
||||||
* @var DateTime
|
* @var DateTime
|
||||||
*/
|
*/
|
||||||
private $start;
|
protected $start;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The end of this time range
|
* The end of this time range
|
||||||
*
|
*
|
||||||
* @var DateTime
|
* @var DateTime
|
||||||
*/
|
*/
|
||||||
private $end;
|
protected $end;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interval by which this time range is split
|
* The interval by which this time range is split
|
||||||
*
|
*
|
||||||
* @var DateInterval
|
* @var DateInterval
|
||||||
*/
|
*/
|
||||||
private $interval;
|
protected $interval;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current date in the iteration
|
* The current date in the iteration
|
||||||
*
|
*
|
||||||
* @var DateTime
|
* @var DateTime
|
||||||
*/
|
*/
|
||||||
private $current;
|
protected $current;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the date iteration is negative
|
* Whether the date iteration is negative
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $negative;
|
protected $negative;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a new time range
|
* Initialize a new time range
|
||||||
@ -87,6 +63,7 @@ class TimeRange implements Iterator
|
|||||||
$this->interval = $interval;
|
$this->interval = $interval;
|
||||||
$this->start = $start;
|
$this->start = $start;
|
||||||
$this->end = $end;
|
$this->end = $end;
|
||||||
|
$this->negative = $this->start > $this->end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,6 +116,24 @@ class TimeRange implements Iterator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the given time is within this range of time
|
||||||
|
*
|
||||||
|
* @param int|DateTime $time The timestamp or date and time to check
|
||||||
|
*/
|
||||||
|
public function validateTime($time)
|
||||||
|
{
|
||||||
|
if ($time instanceof DateTime) {
|
||||||
|
$dateTime = $time;
|
||||||
|
} else {
|
||||||
|
$dateTime = new DateTime();
|
||||||
|
$dateTime->setTimestamp($time);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($this->negative && ($dateTime <= $this->start && $dateTime >= $this->end)) ||
|
||||||
|
(!$this->negative && ($dateTime >= $this->start && $dateTime <= $this->end));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the appropriate timeframe for the given timeframe start
|
* Return the appropriate timeframe for the given timeframe start
|
||||||
*
|
*
|
||||||
@ -148,7 +143,7 @@ class TimeRange implements Iterator
|
|||||||
public function getTimeframe($time)
|
public function getTimeframe($time)
|
||||||
{
|
{
|
||||||
if ($time instanceof DateTime) {
|
if ($time instanceof DateTime) {
|
||||||
$startTime = $time;
|
$startTime = clone $time;
|
||||||
} else {
|
} else {
|
||||||
$startTime = new DateTime();
|
$startTime = new DateTime();
|
||||||
$startTime->setTimestamp($time);
|
$startTime->setTimestamp($time);
|
||||||
@ -174,7 +169,7 @@ class TimeRange implements Iterator
|
|||||||
* @param DateTime $end The end of the timeframe
|
* @param DateTime $end The end of the timeframe
|
||||||
* @return StdClass
|
* @return StdClass
|
||||||
*/
|
*/
|
||||||
private function buildTimeframe(DateTime $start, DateTime $end)
|
protected function buildTimeframe(DateTime $start, DateTime $end)
|
||||||
{
|
{
|
||||||
$timeframe = new StdClass();
|
$timeframe = new StdClass();
|
||||||
$timeframe->start = $start;
|
$timeframe->start = $start;
|
||||||
@ -188,7 +183,6 @@ class TimeRange implements Iterator
|
|||||||
public function rewind()
|
public function rewind()
|
||||||
{
|
{
|
||||||
$this->current = clone $this->start;
|
$this->current = clone $this->start;
|
||||||
$this->negative = $this->start > $this->end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,47 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
namespace Icinga\Module\Monitoring\Web\Hook;
|
namespace Icinga\Module\Monitoring\Web\Hook;
|
||||||
|
|
||||||
use \Zend_Controller_Request_Abstract;
|
|
||||||
use Icinga\Module\Monitoring\Timeline\TimeRange;
|
use Icinga\Module\Monitoring\Timeline\TimeRange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for TimeEntry providers
|
* Base class for TimeLine providers
|
||||||
*/
|
*/
|
||||||
abstract class TimelineProvider
|
abstract class TimelineProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Return a set of TimeEntry objects for the given range of time
|
* Return the names by which to group entries
|
||||||
|
*
|
||||||
|
* @return array An array with the names as keys and their attribute-lists as values
|
||||||
|
*/
|
||||||
|
abstract public function getIdentifiers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the visible entries supposed to be shown on the timeline
|
||||||
*
|
*
|
||||||
* @param TimeRange $range The range of time for which to fetch entries
|
* @param TimeRange $range The range of time for which to fetch entries
|
||||||
* @param Zend_Controller_Request_Abstract $request The current request
|
*
|
||||||
* @return array
|
* @return array The entries to display on the timeline
|
||||||
*/
|
*/
|
||||||
abstract public function fetchTimeEntries(TimeRange $range, Zend_Controller_Request_Abstract $request);
|
abstract public function fetchEntries(TimeRange $range);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the entries supposed to be used to calculate forecasts
|
||||||
|
*
|
||||||
|
* @param TimeRange $range The range of time for which to fetch forecasts
|
||||||
|
*
|
||||||
|
* @return array The entries to calculate forecasts with
|
||||||
|
*/
|
||||||
|
abstract public function fetchForecasts(TimeRange $range);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user