mirror of
https://github.com/Icinga/icingadb-web.git
synced 2026-04-11 20:06:14 -04:00
- Only the CheckAttempt visual must be skipped in case $layout is minimal|header, not the whole condition body.
427 lines
16 KiB
PHP
427 lines
16 KiB
PHP
<?php
|
|
|
|
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
|
|
|
namespace Icinga\Module\Icingadb\View;
|
|
|
|
use Icinga\Date\DateFormatter;
|
|
use Icinga\Module\Icingadb\Common\HostLink;
|
|
use Icinga\Module\Icingadb\Common\HostStates;
|
|
use Icinga\Module\Icingadb\Common\Icons;
|
|
use Icinga\Module\Icingadb\Common\Links;
|
|
use Icinga\Module\Icingadb\Common\ServiceLink;
|
|
use Icinga\Module\Icingadb\Common\ServiceStates;
|
|
use Icinga\Module\Icingadb\Common\TicketLinks;
|
|
use Icinga\Module\Icingadb\Model\History;
|
|
use Icinga\Module\Icingadb\Util\PluginOutput;
|
|
use Icinga\Module\Icingadb\Widget\CheckAttempt;
|
|
use Icinga\Module\Icingadb\Widget\MarkdownLine;
|
|
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
|
use Icinga\Module\Icingadb\Widget\StateChange;
|
|
use ipl\Html\Attributes;
|
|
use ipl\Html\HtmlDocument;
|
|
use ipl\Html\HtmlElement;
|
|
use ipl\Html\Text;
|
|
use ipl\I18n\Translation;
|
|
use ipl\Web\Common\ItemRenderer;
|
|
use ipl\Web\Widget\EmptyState;
|
|
use ipl\Web\Widget\Icon;
|
|
use ipl\Web\Widget\Link;
|
|
use ipl\Web\Widget\StateBall;
|
|
use ipl\Web\Widget\TimeAgo;
|
|
|
|
/** @implements ItemRenderer<History> */
|
|
class EventRenderer implements ItemRenderer
|
|
{
|
|
use Translation;
|
|
use TicketLinks;
|
|
use HostLink;
|
|
use ServiceLink;
|
|
|
|
/** @var NotificationRenderer To render NotificationHistory event */
|
|
protected $notificationRenderer;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->notificationRenderer = new NotificationRenderer();
|
|
}
|
|
|
|
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
|
{
|
|
$attributes->get('class')->addValue('history');
|
|
}
|
|
|
|
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
|
{
|
|
$ballSize = StateBall::SIZE_LARGE;
|
|
if ($layout === 'minimal' || $layout === 'header') {
|
|
$ballSize = StateBall::SIZE_BIG;
|
|
}
|
|
|
|
switch ($item->event_type) {
|
|
case 'comment_add':
|
|
$visual->addHtml(
|
|
HtmlElement::create(
|
|
'div',
|
|
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
|
new Icon(Icons::COMMENT)
|
|
)
|
|
);
|
|
|
|
break;
|
|
case 'comment_remove':
|
|
case 'downtime_end':
|
|
case 'ack_clear':
|
|
$visual->addHtml(
|
|
HtmlElement::create(
|
|
'div',
|
|
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
|
new Icon(Icons::REMOVE)
|
|
)
|
|
);
|
|
|
|
break;
|
|
case 'downtime_start':
|
|
$visual->addHtml(
|
|
HtmlElement::create(
|
|
'div',
|
|
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
|
new Icon(Icons::IN_DOWNTIME)
|
|
)
|
|
);
|
|
|
|
break;
|
|
case 'ack_set':
|
|
$visual->addHtml(
|
|
HtmlElement::create(
|
|
'div',
|
|
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
|
new Icon(Icons::IS_ACKNOWLEDGED)
|
|
)
|
|
);
|
|
|
|
break;
|
|
case 'flapping_end':
|
|
case 'flapping_start':
|
|
$visual->addHtml(
|
|
HtmlElement::create(
|
|
'div',
|
|
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
|
new Icon(Icons::IS_FLAPPING)
|
|
)
|
|
);
|
|
|
|
break;
|
|
case 'notification':
|
|
$visual->addHtml(
|
|
HtmlElement::create(
|
|
'div',
|
|
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
|
new Icon(Icons::NOTIFICATION)
|
|
)
|
|
);
|
|
|
|
break;
|
|
case 'state_change':
|
|
if ($item->state->state_type === 'soft') {
|
|
$stateType = 'soft_state';
|
|
$previousStateType = 'previous_soft_state';
|
|
|
|
if ($item->state->previous_soft_state === 0) {
|
|
$previousStateType = 'hard_state';
|
|
}
|
|
|
|
if ($layout !== 'minimal' && $layout !== 'header') {
|
|
$visual->addHtml(
|
|
new CheckAttempt(
|
|
(int) $item->state->check_attempt,
|
|
(int) $item->state->max_check_attempts
|
|
)
|
|
);
|
|
}
|
|
} else {
|
|
$stateType = 'hard_state';
|
|
$previousStateType = 'previous_hard_state';
|
|
|
|
if ($item->state->hard_state === $item->state->previous_hard_state) {
|
|
$previousStateType = 'previous_soft_state';
|
|
}
|
|
}
|
|
|
|
if ($item->object_type === 'host') {
|
|
$state = HostStates::text($item->state->$stateType);
|
|
$previousState = HostStates::text($item->state->$previousStateType);
|
|
} else {
|
|
$state = ServiceStates::text($item->state->$stateType);
|
|
$previousState = ServiceStates::text($item->state->$previousStateType);
|
|
}
|
|
|
|
$stateChange = new StateChange($state, $previousState);
|
|
if ($stateType === 'soft_state') {
|
|
$stateChange->setCurrentStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
|
}
|
|
|
|
if ($previousStateType === 'previous_soft_state') {
|
|
$stateChange->setPreviousStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
|
if ($stateType === 'soft_state') {
|
|
$visual->getAttributes()->add('class', 'small-state-change');
|
|
}
|
|
}
|
|
|
|
$visual->prependHtml($stateChange);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
|
{
|
|
if ($item->event_type === 'notification' && isset($item->notification->id)) {
|
|
$item->notification->history = $item;
|
|
$item->notification->host = $item->host;
|
|
$item->notification->service = $item->service;
|
|
|
|
$this->notificationRenderer->assembleTitle($item->notification, $title, $layout);
|
|
|
|
return;
|
|
}
|
|
|
|
switch ($item->event_type) {
|
|
case 'comment_add':
|
|
$subjectLabel = $this->translate('Comment added');
|
|
|
|
break;
|
|
case 'comment_remove':
|
|
if (! empty($item->comment->removed_by)) {
|
|
if ($item->comment->removed_by !== $item->comment->author) {
|
|
$subjectLabel = sprintf(
|
|
$this->translate('Comment removed by %s', '..<username>'),
|
|
$item->comment->removed_by
|
|
);
|
|
} else {
|
|
$subjectLabel = $this->translate('Comment removed by author');
|
|
}
|
|
} elseif (isset($item->comment->expire_time)) {
|
|
$subjectLabel = $this->translate('Comment expired');
|
|
} else {
|
|
$subjectLabel = $this->translate('Comment removed');
|
|
}
|
|
|
|
break;
|
|
case 'downtime_end':
|
|
if (! empty($item->downtime->cancelled_by)) {
|
|
if ($item->downtime->cancelled_by !== $item->downtime->author) {
|
|
$subjectLabel = sprintf(
|
|
$this->translate('Downtime cancelled by %s', '..<username>'),
|
|
$item->downtime->cancelled_by
|
|
);
|
|
} else {
|
|
$subjectLabel = $this->translate('Downtime cancelled by author');
|
|
}
|
|
} elseif ($item->downtime->has_been_cancelled === 'y') {
|
|
$subjectLabel = $this->translate('Downtime cancelled');
|
|
} else {
|
|
$subjectLabel = $this->translate('Downtime ended');
|
|
}
|
|
|
|
break;
|
|
case 'downtime_start':
|
|
$subjectLabel = $this->translate('Downtime started');
|
|
|
|
break;
|
|
case 'flapping_start':
|
|
$subjectLabel = $this->translate('Flapping started');
|
|
|
|
break;
|
|
case 'flapping_end':
|
|
$subjectLabel = $this->translate('Flapping stopped');
|
|
|
|
break;
|
|
case 'ack_set':
|
|
$subjectLabel = $this->translate('Acknowledgement set');
|
|
|
|
break;
|
|
case 'ack_clear':
|
|
if (! empty($item->acknowledgement->cleared_by)) {
|
|
if ($item->acknowledgement->cleared_by !== $item->acknowledgement->author) {
|
|
$subjectLabel = sprintf(
|
|
$this->translate('Acknowledgement cleared by %s', '..<username>'),
|
|
$item->acknowledgement->cleared_by
|
|
);
|
|
} else {
|
|
$subjectLabel = $this->translate('Acknowledgement cleared by author');
|
|
}
|
|
} elseif (isset($item->acknowledgement->expire_time)) {
|
|
$subjectLabel = $this->translate('Acknowledgement expired');
|
|
} else {
|
|
$subjectLabel = $this->translate('Acknowledgement cleared');
|
|
}
|
|
|
|
break;
|
|
case 'state_change':
|
|
$state = $item->state->state_type === 'hard'
|
|
? $item->state->hard_state
|
|
: $item->state->soft_state;
|
|
if ($state === 0) {
|
|
if ($item->object_type === 'service') {
|
|
$subjectLabel = $this->translate('Service recovered');
|
|
} else {
|
|
$subjectLabel = $this->translate('Host recovered');
|
|
}
|
|
} else {
|
|
if ($item->state->state_type === 'hard') {
|
|
$subjectLabel = $this->translate('Hard state changed');
|
|
} else {
|
|
$subjectLabel = $this->translate('Soft state changed');
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
$subjectLabel = $item->event_type;
|
|
|
|
break;
|
|
}
|
|
|
|
if ($layout === 'header') {
|
|
$title->addHtml(HtmlElement::create('span', ['class' => 'subject'], $subjectLabel));
|
|
} else {
|
|
$title->addHtml(new Link($subjectLabel, Links::event($item), ['class' => 'subject']));
|
|
}
|
|
|
|
if ($item->object_type === 'host' && isset($item->host->id)) {
|
|
$link = $this->createHostLink($item->host, true);
|
|
} elseif (isset($item->host->id, $item->service->id)) {
|
|
$link = $this->createServiceLink($item->service, $item->host, true);
|
|
}
|
|
|
|
$title->addHtml(Text::create(' '));
|
|
if (isset($link)) {
|
|
$title->addHtml($link);
|
|
}
|
|
}
|
|
|
|
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
|
{
|
|
if ($item->event_type === 'notification') {
|
|
$item->notification->host = $item->host;
|
|
$item->notification->service = $item->service;
|
|
|
|
$this->notificationRenderer->assembleCaption($item->notification, $caption, $layout);
|
|
|
|
return;
|
|
}
|
|
|
|
switch ($item->event_type) {
|
|
case 'comment_add':
|
|
case 'comment_remove':
|
|
$markdownLine = new MarkdownLine($this->createTicketLinks($item->comment->comment));
|
|
$caption->getAttributes()->add($markdownLine->getAttributes());
|
|
$caption->add([
|
|
new Icon(Icons::USER),
|
|
$item->comment->author,
|
|
': '
|
|
])->addFrom($markdownLine);
|
|
|
|
break;
|
|
case 'downtime_end':
|
|
case 'downtime_start':
|
|
$markdownLine = new MarkdownLine($this->createTicketLinks($item->downtime->comment));
|
|
$caption->getAttributes()->add($markdownLine->getAttributes());
|
|
$caption->add([
|
|
new Icon(Icons::USER),
|
|
$item->downtime->author,
|
|
': '
|
|
])->addFrom($markdownLine);
|
|
|
|
break;
|
|
case 'flapping_start':
|
|
$caption
|
|
->add(
|
|
sprintf(
|
|
$this->translate('State Change Rate: %.2f%%; Start Threshold: %.2f%%'),
|
|
$item->flapping->percent_state_change_start,
|
|
$item->flapping->flapping_threshold_high
|
|
)
|
|
)
|
|
->getAttributes()
|
|
->add('class', 'plugin-output');
|
|
|
|
break;
|
|
case 'flapping_end':
|
|
$caption
|
|
->add(
|
|
sprintf(
|
|
$this->translate('State Change Rate: %.2f%%; End Threshold: %.2f%%; Flapping for %s'),
|
|
$item->flapping->percent_state_change_end,
|
|
$item->flapping->flapping_threshold_low,
|
|
isset($item->flapping->end_time)
|
|
? DateFormatter::formatDuration(
|
|
$item->flapping->end_time->getTimestamp()
|
|
- $item->flapping->start_time->getTimestamp()
|
|
)
|
|
: $this->translate('n. a.')
|
|
)
|
|
)
|
|
->getAttributes()
|
|
->add('class', 'plugin-output');
|
|
|
|
break;
|
|
case 'ack_clear':
|
|
case 'ack_set':
|
|
if (! isset($item->acknowledgement->comment) && ! isset($item->acknowledgement->author)) {
|
|
$caption->addHtml(
|
|
new EmptyState(
|
|
$this->translate('This acknowledgement was set before Icinga DB history recording')
|
|
)
|
|
);
|
|
} else {
|
|
$markdownLine = new MarkdownLine($this->createTicketLinks($item->acknowledgement->comment));
|
|
$caption->getAttributes()->add($markdownLine->getAttributes());
|
|
$caption->add([
|
|
new Icon(Icons::USER),
|
|
$item->acknowledgement->author,
|
|
': '
|
|
])->addFrom($markdownLine);
|
|
}
|
|
|
|
break;
|
|
case 'state_change':
|
|
$commandName = $item->object_type === 'host'
|
|
? $item->host->checkcommand_name
|
|
: $item->service->checkcommand_name;
|
|
if (isset($commandName)) {
|
|
if (empty($item->state->output)) {
|
|
$caption->addHtml(new EmptyState($this->translate('Output unavailable.')));
|
|
} else {
|
|
$caption->addHtml(
|
|
new PluginOutputContainer(
|
|
(new PluginOutput($item->state->output))
|
|
->setCommandName($commandName)
|
|
)
|
|
);
|
|
}
|
|
} else {
|
|
$caption->addHtml(
|
|
new EmptyState($this->translate('Waiting for Icinga DB to synchronize the config.'))
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
|
{
|
|
$info->addHtml(new TimeAgo($item->event_time->getTimestamp()));
|
|
}
|
|
|
|
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
|
{
|
|
}
|
|
|
|
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
|
{
|
|
return false; // no custom sections
|
|
}
|
|
}
|