icingaweb2/modules/monitoring/application/controllers/EventController.php

525 lines
22 KiB
PHP
Raw Normal View History

<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use DateTime;
use DateTimeZone;
use InvalidArgumentException;
use Icinga\Data\Queryable;
use Icinga\Date\DateFormatter;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Util\TimezoneDetect;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
use Icinga\Web\Widget\Tabextension\OutputFormat;
class EventController extends Controller
{
/**
* @var string[]
*/
protected $dataViewsByType = array(
'notify' => 'notificationevent',
'comment' => 'commentevent',
'comment_deleted' => 'commentevent',
'ack' => 'commentevent',
'ack_deleted' => 'commentevent',
'dt_comment' => 'commentevent',
'dt_comment_deleted' => 'commentevent',
'flapping' => 'flappingevent',
'flapping_deleted' => 'flappingevent',
'hard_state' => 'statechangeevent',
'soft_state' => 'statechangeevent',
'dt_start' => 'downtimeevent',
'dt_end' => 'downtimeevent'
);
public function showAction()
{
$type = $this->params->shiftRequired('type');
$id = $this->params->shiftRequired('id');
if (! isset($this->dataViewsByType[$type])
|| $this->applyRestriction(
'monitoring/filter/objects',
$this->backend->select()->from('eventhistory', array('id'))->where('id', $id)
)->fetchRow() === false
) {
$this->httpNotFound($this->translate('Event not found'));
}
$event = $this->query($type, $id)->fetchRow();
if ($event === false) {
$this->httpNotFound($this->translate('Event not found'));
}
$this->view->object = $object = $event->service_description === null
? new Host($this->backend, $event->host_name)
: new Service($this->backend, $event->host_name, $event->service_description);
$object->fetch();
list($icon, $label) = $this->getIconAndLabel($type);
$this->view->details = array_merge(
array(array($this->view->escape($this->translate('Type')), $label)),
$this->getDetails($type, $event)
);
$this->getTabs()
->add('event', array(
'title' => $label,
'label' => $label,
'icon' => $icon,
'url' => Url::fromRequest(),
'active' => true
))
->extend(new OutputFormat())
->extend(new DashboardAction())
->extend(new MenuAction());
}
/**
* Return translated and escaped 'Yes' if the given condition is true, 'No' otherwise, 'N/A' if NULL
*
* @param bool|null $condition
*
* @return string
*/
protected function yesOrNo($condition)
{
if ($condition === null) {
return $this->view->escape($this->translate('N/A'));
}
return $this->view->escape($condition ? $this->translate('Yes') : $this->translate('No'));
}
/**
* Render the given duration in seconds as human readable HTML or 'N/A' if NULL
*
* @param int|null $seconds
*
* @return string
*/
protected function duration($seconds)
{
return $this->view->escape(
$seconds === null ? $this->translate('N/A') : DateFormatter::formatDuration($seconds)
);
}
/**
* Render the given percent number as human readable HTML or 'N/A' if NULL
*
* @param float|null $percent
*
* @return string
*/
protected function percent($percent)
{
return $this->view->escape(
$percent === null ? $this->translate('N/A') : sprintf($this->translate('%.2f%%'), $percent)
);
}
/**
* Render the given comment message as HTML or 'N/A' if NULL
*
* @param string|null $message
*
* @return string
*/
protected function comment($message)
{
return $this->view->nl2br($this->view->createTicketLinks($this->view->escapeComment($message)));
}
/**
* Render a link to the given contact or 'N/A' if NULL
*
* @param string|null $name
*
* @return string
*/
protected function contact($name)
{
return $name === null
? $this->view->escape($this->translate('N/A'))
: $this->view->qlink($name, Url::fromPath('monitoring/show/contact', array('contact_name' => $name)));
}
/**
* Render the given monitored object state as human readable HTML or 'N/A' if NULL
*
* @param bool $isService
* @param int|null $state
*
* @return string
*/
protected function state($isService, $state)
{
if ($state === null) {
return $this->view->escape($this->translate('N/A'));
}
try {
$stateText = $isService
? Service::getStateText($state, true)
: Host::getStateText($state, true);
} catch (InvalidArgumentException $e) {
return $this->view->escape($this->translate('N/A'));
}
return '<span class="badge state-' . ($isService ? Service::getStateText($state) : Host::getStateText($state))
. '">&nbsp;</span><span class="state-label">' . $this->view->escape($stateText) . '</span>';
}
/**
* Render the given plugin output as human readable HTML
*
* @param string $output
*
* @return string
*/
protected function pluginOutput($output)
{
return $this->view->getHelper('PluginOutput')->pluginOutput($output);
}
/**
* Return the icon and the label for the given event type
*
* @param string $eventType
*
* @return string[]
*/
protected function getIconAndLabel($eventType)
{
switch ($eventType) {
case 'notify':
return array('bell', $this->translate('Notification', 'tooltip'));
case 'comment':
return array('comment-empty', $this->translate('Comment', 'tooltip'));
case 'comment_deleted':
return array('cancel', $this->translate('Comment removed', 'tooltip'));
case 'ack':
return array('ok', $this->translate('Acknowledged', 'tooltip'));
case 'ack_deleted':
return array('ok', $this->translate('Acknowledgement removed', 'tooltip'));
case 'dt_comment':
return array('plug', $this->translate('Downtime scheduled', 'tooltip'));
case 'dt_comment_deleted':
return array('plug', $this->translate('Downtime removed', 'tooltip'));
case 'flapping':
return array('flapping', $this->translate('Flapping started', 'tooltip'));
case 'flapping_deleted':
return array('flapping', $this->translate('Flapping stopped', 'tooltip'));
case 'hard_state':
return array('warning-empty', $this->translate('Hard state change'));
case 'soft_state':
return array('spinner', $this->translate('Soft state change'));
case 'dt_start':
return array('plug', $this->translate('Downtime started', 'tooltip'));
case 'dt_end':
return array('plug', $this->translate('Downtime ended', 'tooltip'));
}
}
/**
* Return a query for the given event ID of the given type
*
* @param string $type
* @param int $id
*
* @return Queryable
*/
protected function query($type, $id)
{
switch ($this->dataViewsByType[$type]) {
case 'downtimeevent':
return $this->backend->select()
->from('downtimeevent', array(
'entry_time' => 'downtimeevent_entry_time',
'author_name' => 'downtimeevent_author_name',
'comment_data' => 'downtimeevent_comment_data',
'is_fixed' => 'downtimeevent_is_fixed',
'scheduled_start_time' => 'downtimeevent_scheduled_start_time',
'scheduled_end_time' => 'downtimeevent_scheduled_end_time',
'was_started' => 'downtimeevent_was_started',
'actual_start_time' => 'downtimeevent_actual_start_time',
'actual_end_time' => 'downtimeevent_actual_end_time',
'was_cancelled' => 'downtimeevent_was_cancelled',
'is_in_effect' => 'downtimeevent_is_in_effect',
'trigger_time' => 'downtimeevent_trigger_time',
'host_name',
'service_description'
))
->where('downtimeevent_id', $id);
case 'commentevent':
return $this->backend->select()
->from('commentevent', array(
'entry_type' => 'commentevent_entry_type',
'comment_time' => 'commentevent_comment_time',
'author_name' => 'commentevent_author_name',
'comment_data' => 'commentevent_comment_data',
'is_persistent' => 'commentevent_is_persistent',
'comment_source' => 'commentevent_comment_source',
'expires' => 'commentevent_expires',
'expiration_time' => 'commentevent_expiration_time',
'deletion_time' => 'commentevent_deletion_time',
'host_name',
'service_description'
))
->where('commentevent_id', $id);
case 'flappingevent':
return $this->backend->select()
->from('flappingevent', array(
'event_time' => 'flappingevent_event_time',
'reason_type' => 'flappingevent_reason_type',
'percent_state_change' => 'flappingevent_percent_state_change',
'low_threshold' => 'flappingevent_low_threshold',
'high_threshold' => 'flappingevent_high_threshold',
'host_name',
'service_description'
))
->where('flappingevent_id', $id)
->where('flappingevent_event_type', $type);
case 'notificationevent':
return $this->backend->select()
->from('notificationevent', array(
'notification_reason' => 'notificationevent_reason',
'start_time' => 'notificationevent_start_time',
'end_time' => 'notificationevent_end_time',
'state' => 'notificationevent_state',
'output' => 'notificationevent_output',
'long_output' => 'notificationevent_long_output',
'escalated' => 'notificationevent_escalated',
'contacts_notified' => 'notificationevent_contacts_notified',
'host_name',
'service_description'
))
->where('notificationevent_id', $id);
case 'statechangeevent':
return $this->backend->select()
->from('statechangeevent', array(
'state_time' => 'statechangeevent_state_time',
'state' => 'statechangeevent_state',
'current_check_attempt' => 'statechangeevent_current_check_attempt',
'max_check_attempts' => 'statechangeevent_max_check_attempts',
'last_state' => 'statechangeevent_last_state',
'last_hard_state' => 'statechangeevent_last_hard_state',
'output' => 'statechangeevent_output',
'long_output' => 'statechangeevent_long_output',
'check_source' => 'statechangeevent_check_source',
'host_name',
'service_description'
))
->where('statechangeevent_id', $id)
->where('statechangeevent_state_change', 1)
->where('statechangeevent_state_type', $type);
}
}
/**
* Return the given event's data prepared for a name-value table
*
* @param string $type
* @param \stdClass $event
*
* @return string[][]
*/
protected function getDetails($type, $event)
{
switch ($type) {
case 'dt_start':
case 'dt_end':
$details = array(array(
array($this->translate('Entry time'), DateFormatter::formatTime($event->entry_time)),
array($this->translate('Is fixed'), $this->yesOrNo($event->is_fixed)),
array($this->translate('Is in effect'), $this->yesOrNo($event->is_in_effect)),
array($this->translate('Was started'), $this->yesOrNo($event->was_started))
));
if ($type === 'dt_end') {
$details[] = array(
array($this->translate('Was cancelled'), $this->yesOrNo($event->was_cancelled))
);
}
$details[] = array(
array($this->translate('Trigger time'), DateFormatter::formatTime($event->trigger_time)),
array(
$this->translate('Scheduled start time'),
DateFormatter::formatTime($event->scheduled_start_time)
),
array(
$this->translate('Actual start time'),
DateFormatter::formatTime($event->actual_start_time)
),
array(
$this->translate('Scheduled end time'),
DateFormatter::formatTime($event->scheduled_end_time)
)
);
if ($type === 'dt_end') {
$details[] = array(
array(
$this->translate('Actual end time'),
DateFormatter::formatTime($event->actual_end_time)
)
);
}
$details[] = array(
array($this->translate('Author'), $this->contact($event->author_name)),
array($this->translate('Comment'), $this->comment($event->comment_data))
);
return call_user_func_array('array_merge', $details);
case 'comment':
case 'comment_deleted':
case 'ack':
case 'ack_deleted':
case 'dt_comment':
case 'dt_comment_deleted':
switch ($event->entry_type) {
case 'comment':
$entryType = $this->translate('User comment');
break;
case 'downtime':
$entryType = $this->translate('Scheduled downtime');
break;
case 'flapping':
$entryType = $this->translate('Flapping');
break;
case 'ack':
$entryType = $this->translate('Acknowledgement');
break;
default:
$entryType = $this->translate('N/A');
}
switch ($event->comment_source) {
case 'icinga':
$commentSource = $this->translate('Icinga');
break;
case 'user':
$commentSource = $this->translate('User');
break;
default:
$commentSource = $this->translate('N/A');
}
return array(
array($this->translate('Time'), DateFormatter::formatTime($event->comment_time)),
array($this->translate('Source'), $this->view->escape($commentSource)),
array($this->translate('Entry type'), $this->view->escape($entryType)),
array($this->translate('Author'), $this->contact($event->author_name)),
array($this->translate('Is persistent'), $this->yesOrNo($event->is_persistent)),
array($this->translate('Expires'), $this->yesOrNo($event->expires)),
array($this->translate('Expiration time'), DateFormatter::formatTime($event->expiration_time)),
array($this->translate('Deletion time'), DateFormatter::formatTime($event->deletion_time)),
array($this->translate('Message'), $this->comment($event->comment_data))
);
case 'flapping':
case 'flapping_deleted':
switch ($event->reason_type) {
case 'stopped':
$reasonType = $this->translate('Flapping stopped normally');
break;
case 'disabled':
$reasonType = $this->translate('Flapping was disabled');
break;
default:
$reasonType = $this->translate('N/A');
}
return array(
array($this->translate('Event time'), DateFormatter::formatTime($event->event_time)),
array($this->translate('Reason'), $this->view->escape($reasonType)),
array($this->translate('State change'), $this->percent($event->percent_state_change)),
array($this->translate('Low threshold'), $this->percent($event->low_threshold)),
array($this->translate('High threshold'), $this->percent($event->high_threshold))
);
case 'notify':
switch ($event->notification_reason) {
case 'normal_notification':
$notificationReason = $this->translate('Normal notification');
break;
case 'ack':
$notificationReason = $this->translate('Problem acknowledgement');
break;
case 'flapping_started':
$notificationReason = $this->translate('Flapping started');
break;
case 'flapping_stopped':
$notificationReason = $this->translate('Flapping stopped');
break;
case 'flapping_disabled':
$notificationReason = $this->translate('Flapping was disabled');
break;
case 'dt_start':
$notificationReason = $this->translate('Downtime started');
break;
case 'dt_end':
$notificationReason = $this->translate('Downtime ended');
break;
case 'dt_cancel':
$notificationReason = $this->translate('Downtime was cancelled');
break;
case 'custom_notification':
$notificationReason = $this->translate('Custom notification');
break;
default:
$notificationReason = $this->translate('N/A');
}
$details = array(
array($this->translate('Start time'), DateFormatter::formatTime($event->start_time)),
array($this->translate('End time'), DateFormatter::formatTime($event->end_time)),
array($this->translate('Reason'), $this->view->escape($notificationReason)),
array(
$this->translate('State'),
$this->state($event->service_description !== null, $event->state)
),
array($this->translate('Escalated'), $this->yesOrNo($event->escalated)),
array($this->translate('Contacts notified'), (int) $event->contacts_notified),
array(
$this->translate('Output'),
$this->pluginOutput($event->output) . $this->pluginOutput($event->long_output)
)
);
return $details;
case 'hard_state':
case 'soft_state':
$isService = $event->service_description !== null;
$details = array(
array($this->translate('State time'), DateFormatter::formatTime($event->state_time)),
array($this->translate('State'), $this->state($isService, $event->state)),
array($this->translate('Check attempt'), $this->view->escape(sprintf(
$this->translate('%d of %d'),
(int) $event->current_check_attempt,
(int) $event->max_check_attempts
))),
array($this->translate('Last state'), $this->state($isService, $event->last_state)),
array($this->translate('Last hard state'), $this->state($isService, $event->last_hard_state)),
array($this->translate('Check source'), $event->check_source),
array(
$this->translate('Output'),
$this->pluginOutput($event->output) . $this->pluginOutput($event->long_output)
)
);
return $details;
}
}
}