You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
543 lines
17 KiB
543 lines
17 KiB
<?php declare(strict_types = 0);
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** 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.
|
|
**/
|
|
|
|
|
|
namespace Widgets\NavTree\Actions;
|
|
|
|
use API,
|
|
CControllerDashboardWidgetView,
|
|
CControllerResponseData,
|
|
CProfile,
|
|
CSeverityHelper;
|
|
|
|
class WidgetView extends CControllerDashboardWidgetView {
|
|
|
|
private array $problems_per_severity_tpl;
|
|
|
|
protected function init(): void {
|
|
parent::init();
|
|
|
|
$this->addValidationRules([
|
|
'name' => 'string',
|
|
'widgetid' => 'db widget.widgetid',
|
|
'fields' => 'array'
|
|
]);
|
|
}
|
|
|
|
protected function doAction(): void {
|
|
// Get list of sysmapids.
|
|
$sysmapids = [];
|
|
$navtree_items = [];
|
|
|
|
foreach ($this->fields_values['navtree'] as $id => $navtree_item) {
|
|
$sysmapid = array_key_exists('sysmapid', $navtree_item) ? $navtree_item['sysmapid'] : 0;
|
|
|
|
if ($sysmapid != 0) {
|
|
$sysmapids[$sysmapid] = true;
|
|
}
|
|
|
|
$navtree_items[$id] = [
|
|
'parent' => $navtree_item['parent'],
|
|
'sysmapid' => $sysmapid,
|
|
'child_sysmapids' => []
|
|
];
|
|
}
|
|
|
|
// Propagate item mapids to all its parent items.
|
|
foreach ($navtree_items as $navtree_item) {
|
|
$parent = $navtree_item['parent'];
|
|
|
|
while (array_key_exists($parent, $navtree_items)) {
|
|
if ($navtree_item['sysmapid'] != 0) {
|
|
$navtree_items[$parent]['child_sysmapids'][$navtree_item['sysmapid']] = true;
|
|
}
|
|
$parent = $navtree_items[$parent]['parent'];
|
|
}
|
|
}
|
|
|
|
// Get severity levels and colors and select list of sysmapids to count problems per maps.
|
|
$this->problems_per_severity_tpl = [];
|
|
$severity_config = [];
|
|
|
|
$maps_accessible = $sysmapids
|
|
? API::Map()->get([
|
|
'output' => [],
|
|
'sysmapids' => array_keys($sysmapids),
|
|
'preservekeys' => true
|
|
])
|
|
: [];
|
|
|
|
for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
|
|
$this->problems_per_severity_tpl[$severity] = 0;
|
|
$severity_config[$severity] = [
|
|
'name' => CSeverityHelper::getName($severity),
|
|
'style_class' => CSeverityHelper::getStatusStyle($severity)
|
|
];
|
|
}
|
|
|
|
$widgetid = $this->getInput('widgetid', 0);
|
|
$navtree_item_selected = 0;
|
|
$navtree_items_opened = [];
|
|
|
|
if ($widgetid) {
|
|
$pattern = 'web.dashboard.widget.navtree.item-%.toggle';
|
|
$discard_from_start = strpos($pattern, '%');
|
|
$discard_from_end = strlen($pattern) - $discard_from_start - 1;
|
|
|
|
foreach (CProfile::findByIdxPattern($pattern, $widgetid) as $item_opened) {
|
|
$navtree_items_opened[] = substr($item_opened, $discard_from_start, -$discard_from_end);
|
|
}
|
|
|
|
$navtree_item_selected = CProfile::get('web.dashboard.widget.navtree.item.selected', 0, $widgetid);
|
|
}
|
|
|
|
$this->setResponse(new CControllerResponseData([
|
|
'name' => $this->getInput('name', $this->widget->getDefaultName()),
|
|
'navtree' => $this->fields_values['navtree'],
|
|
'navtree_item_selected' => $navtree_item_selected,
|
|
'navtree_items_opened' => $navtree_items_opened,
|
|
'problems' => $this->getNumberOfProblemsBySysmap($navtree_items),
|
|
'show_unavailable' => $this->fields_values['show_unavailable'],
|
|
'maps_accessible' => array_keys($maps_accessible),
|
|
'severity_config' => $severity_config,
|
|
'initial_load' => $this->getInput('initial_load', 0),
|
|
'user' => [
|
|
'debug_mode' => $this->getDebugMode()
|
|
]
|
|
]));
|
|
}
|
|
|
|
private function getNumberOfProblemsBySysmap(array $navtree_items = []): array {
|
|
$response = [];
|
|
$sysmapids = [];
|
|
|
|
foreach ($navtree_items as $navtree_item) {
|
|
$sysmapids[$navtree_item['sysmapid']] = true;
|
|
}
|
|
unset($sysmapids[0]);
|
|
|
|
$sysmaps = $sysmapids
|
|
? API::Map()->get([
|
|
'output' => ['sysmapid', 'severity_min'],
|
|
'selectLinks' => ['linktriggers', 'permission'],
|
|
'selectSelements' => ['elements', 'elementtype', 'permission'],
|
|
'sysmapids' => array_keys($sysmapids),
|
|
'preservekeys' => true
|
|
])
|
|
: [];
|
|
|
|
if ($sysmaps) {
|
|
$triggers_per_hosts = [];
|
|
$triggers_per_host_groups = [];
|
|
$problems_per_trigger = [];
|
|
$submaps_relations = [];
|
|
$submaps_found = [];
|
|
$host_groups = [];
|
|
$hosts = [];
|
|
|
|
// Gather submaps from all selected maps.
|
|
foreach ($sysmaps as $map) {
|
|
foreach ($map['selements'] as $selement) {
|
|
if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) {
|
|
if (($element = reset($selement['elements'])) !== false) {
|
|
$submaps_relations[$map['sysmapid']][] = $element['sysmapid'];
|
|
$submaps_found[] = $element['sysmapid'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Gather maps added as submaps for each of map in any depth.
|
|
$sysmaps_resolved = array_keys($sysmaps);
|
|
while ($diff = array_diff($submaps_found, $sysmaps_resolved)) {
|
|
$submaps = API::Map()->get([
|
|
'output' => ['sysmapid', 'severity_min'],
|
|
'selectLinks' => ['linktriggers', 'permission'],
|
|
'selectSelements' => ['elements', 'elementtype', 'permission'],
|
|
'sysmapids' => $diff,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$sysmaps_resolved = array_merge($sysmaps_resolved, $diff);
|
|
|
|
foreach ($submaps as $submap) {
|
|
$sysmaps[$submap['sysmapid']] = $submap;
|
|
|
|
foreach ($submap['selements'] as $selement) {
|
|
if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) {
|
|
$element = reset($selement['elements']);
|
|
if ($element) {
|
|
$submaps_relations[$submap['sysmapid']][] = $element['sysmapid'];
|
|
$submaps_found[] = $element['sysmapid'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Gather elements from all maps selected.
|
|
foreach ($sysmaps as $map) {
|
|
// Collect triggers from map links.
|
|
foreach ($map['links'] as $link) {
|
|
foreach ($link['linktriggers'] as $linktrigger) {
|
|
$problems_per_trigger[$linktrigger['triggerid']] = $this->problems_per_severity_tpl;
|
|
}
|
|
}
|
|
|
|
// Collect map elements.
|
|
foreach ($map['selements'] as $selement) {
|
|
switch ($selement['elementtype']) {
|
|
case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
|
|
if (($element = reset($selement['elements'])) !== false) {
|
|
$host_groups[$element['groupid']] = true;
|
|
}
|
|
break;
|
|
|
|
case SYSMAP_ELEMENT_TYPE_TRIGGER:
|
|
foreach (array_column($selement['elements'], 'triggerid') as $triggerid) {
|
|
$problems_per_trigger[$triggerid] = $this->problems_per_severity_tpl;
|
|
}
|
|
break;
|
|
|
|
case SYSMAP_ELEMENT_TYPE_HOST:
|
|
if (($element = reset($selement['elements'])) !== false) {
|
|
$hosts[$element['hostid']] = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Drop all disabled and inaccessible triggers.
|
|
if ($problems_per_trigger) {
|
|
$triggers = API::Trigger()->get([
|
|
'output' => [],
|
|
'triggerids' => array_keys($problems_per_trigger),
|
|
'monitored' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$problems_per_trigger = array_intersect_key($problems_per_trigger, $triggers);
|
|
|
|
unset($triggers);
|
|
}
|
|
|
|
// Select lowest severity to reduce amount of data returned by API.
|
|
$severity_min = min(array_column($sysmaps, 'severity_min'));
|
|
|
|
// Get triggers related to host groups.
|
|
if ($host_groups) {
|
|
$triggers = API::Trigger()->get([
|
|
'output' => ['triggerid'],
|
|
'selectHostGroups' => ['groupid'],
|
|
'groupids' => array_keys($host_groups),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
foreach ($triggers as $trigger) {
|
|
foreach ($trigger['hostgroups'] as $host_group) {
|
|
$triggers_per_host_groups[$host_group['groupid']][$trigger['triggerid']] = true;
|
|
}
|
|
$problems_per_trigger[$trigger['triggerid']] = $this->problems_per_severity_tpl;
|
|
}
|
|
|
|
unset($host_groups);
|
|
}
|
|
|
|
// Get triggers related to hosts.
|
|
if ($hosts) {
|
|
$triggers = API::Trigger()->get([
|
|
'output' => ['triggerid'],
|
|
'selectHosts' => ['hostid'],
|
|
'hostids' => array_keys($hosts),
|
|
'preservekeys' => true,
|
|
'monitored' => true
|
|
]);
|
|
|
|
foreach ($triggers as $trigger) {
|
|
if (($host = reset($trigger['hosts'])) !== false) {
|
|
$triggers_per_hosts[$host['hostid']][$trigger['triggerid']] = true;
|
|
$problems_per_trigger[$trigger['triggerid']] = $this->problems_per_severity_tpl;
|
|
}
|
|
}
|
|
|
|
unset($hosts);
|
|
}
|
|
|
|
// Count problems per trigger.
|
|
if ($problems_per_trigger) {
|
|
$problems = API::Problem()->get([
|
|
'output' => ['objectid', 'severity'],
|
|
'source' => EVENT_SOURCE_TRIGGERS,
|
|
'object' => EVENT_OBJECT_TRIGGER,
|
|
'objectids' => array_keys($problems_per_trigger),
|
|
'severities' => range($severity_min, TRIGGER_SEVERITY_COUNT - 1),
|
|
'symptom' => false,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if ($problems) {
|
|
foreach ($problems as $problem) {
|
|
$problems_per_trigger[$problem['objectid']][$problem['severity']]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count problems in each submap included in navigation tree:
|
|
foreach ($navtree_items as $id => $navtree_item) {
|
|
$maps_need_to_count_in = $navtree_item['child_sysmapids'];
|
|
if ($navtree_item['sysmapid'] != 0) {
|
|
$maps_need_to_count_in[$navtree_item['sysmapid']] = true;
|
|
}
|
|
|
|
$response[$id] = $this->problems_per_severity_tpl;
|
|
$problems_counted = [];
|
|
|
|
foreach (array_keys($maps_need_to_count_in) as $sysmapid) {
|
|
if (array_key_exists($sysmapid, $sysmaps)) {
|
|
$map = $sysmaps[$sysmapid];
|
|
|
|
// Count problems occurred in linked elements.
|
|
foreach ($map['selements'] as $selement) {
|
|
if ($selement['permission'] >= PERM_READ) {
|
|
$problems = $this->getElementProblems($selement, $problems_per_trigger, $sysmaps,
|
|
$submaps_relations, $map['severity_min'], $problems_counted, $triggers_per_hosts,
|
|
$triggers_per_host_groups
|
|
);
|
|
|
|
if ($problems !== null) {
|
|
$response[$id] = self::sumArrayValues($response[$id], $problems);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count problems occurred in triggers which are related to the links.
|
|
foreach ($map['links'] as $link) {
|
|
$uncounted_problem_triggers = array_diff_key(
|
|
array_flip(array_column($link['linktriggers'], 'triggerid')),
|
|
$problems_counted
|
|
);
|
|
|
|
foreach ($uncounted_problem_triggers as $triggerid => $var) {
|
|
$problems_counted[$triggerid] = true;
|
|
|
|
// Skip disabled and inaccessible triggers.
|
|
if (!array_key_exists($triggerid, $problems_per_trigger)) {
|
|
continue;
|
|
}
|
|
|
|
$problems_to_add = $problems_per_trigger[$triggerid];
|
|
|
|
// Remove problems which are less important than map's min-severity.
|
|
if ($map['severity_min'] > 0) {
|
|
foreach (array_keys($problems_to_add) as $severity) {
|
|
if ($map['severity_min'] > $severity) {
|
|
$problems_to_add[$severity] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
$response[$id] = self::sumArrayValues($response[$id], $problems_to_add);
|
|
}
|
|
unset($uncounted_problem_triggers);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($response as &$row) {
|
|
// Reduce the amount of data transferred over Ajax.
|
|
if ($row === $this->problems_per_severity_tpl) {
|
|
$row = 0;
|
|
}
|
|
}
|
|
unset($row);
|
|
|
|
return $response;
|
|
}
|
|
|
|
private function getElementProblems(array $selement, array $problems_per_trigger, array $sysmaps,
|
|
array $submaps_relations, $severity_min = 0, array &$problems_counted = [], array $triggers_per_hosts = [],
|
|
array $triggers_per_host_groups = []): ?array {
|
|
$problems = null;
|
|
|
|
switch ($selement['elementtype']) {
|
|
case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
|
|
$problems = $this->problems_per_severity_tpl;
|
|
|
|
if (($element = reset($selement['elements'])) !== false) {
|
|
if (array_key_exists($element['groupid'], $triggers_per_host_groups)) {
|
|
$uncounted_problem_triggers = array_diff_key($triggers_per_host_groups[$element['groupid']],
|
|
$problems_counted
|
|
);
|
|
foreach ($uncounted_problem_triggers as $triggerid => $var) {
|
|
$problems_counted[$triggerid] = true;
|
|
|
|
// Skip disabled and inaccessible triggers.
|
|
if (array_key_exists($triggerid, $problems_per_trigger)) {
|
|
$problems = self::sumArrayValues($problems, $problems_per_trigger[$triggerid]);
|
|
}
|
|
}
|
|
unset($uncounted_problem_triggers);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SYSMAP_ELEMENT_TYPE_TRIGGER:
|
|
$problems = $this->problems_per_severity_tpl;
|
|
$uncounted_problem_triggers = array_diff_key(
|
|
array_flip(array_column($selement['elements'], 'triggerid')),
|
|
$problems_counted
|
|
);
|
|
foreach ($uncounted_problem_triggers as $triggerid => $var) {
|
|
$problems_counted[$triggerid] = true;
|
|
|
|
// Skip disabled and inaccessible triggers.
|
|
if (array_key_exists($triggerid, $problems_per_trigger)) {
|
|
$problems = self::sumArrayValues($problems, $problems_per_trigger[$triggerid]);
|
|
}
|
|
}
|
|
unset($uncounted_problem_triggers);
|
|
break;
|
|
|
|
case SYSMAP_ELEMENT_TYPE_HOST:
|
|
$problems = $this->problems_per_severity_tpl;
|
|
|
|
if (($element = reset($selement['elements'])) !== false) {
|
|
if (array_key_exists($element['hostid'], $triggers_per_hosts)) {
|
|
$uncounted_problem_triggers = array_diff_key($triggers_per_hosts[$element['hostid']],
|
|
$problems_counted
|
|
);
|
|
foreach ($uncounted_problem_triggers as $triggerid => $var) {
|
|
$problems_counted[$triggerid] = true;
|
|
|
|
// Skip disabled and inaccessible triggers.
|
|
if (array_key_exists($triggerid, $problems_per_trigger)) {
|
|
$problems = self::sumArrayValues($problems, $problems_per_trigger[$triggerid]);
|
|
}
|
|
}
|
|
unset($uncounted_problem_triggers);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SYSMAP_ELEMENT_TYPE_MAP:
|
|
$problems = $this->problems_per_severity_tpl;
|
|
|
|
if (($submap_element = reset($selement['elements'])) !== false) {
|
|
// Recursively find all submaps in any depth and put them into an array.
|
|
$maps_to_process[$submap_element['sysmapid']] = false;
|
|
|
|
while (array_filter($maps_to_process, static function($item) {return !$item;})) {
|
|
foreach ($maps_to_process as $linked_map => $val) {
|
|
$maps_to_process[$linked_map] = true;
|
|
|
|
if (array_key_exists($linked_map, $submaps_relations)) {
|
|
foreach ($submaps_relations[$linked_map] as $submap) {
|
|
if (!array_key_exists($submap, $maps_to_process)) {
|
|
$maps_to_process[$submap] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count problems in each of selected submap.
|
|
foreach ($maps_to_process as $sysmapid => $val) {
|
|
// Count problems in elements assigned to selements.
|
|
if (array_key_exists($sysmapid, $sysmaps)) {
|
|
foreach ($sysmaps[$sysmapid]['selements'] as $submap_selement) {
|
|
if ($submap_selement['permission'] >= PERM_READ) {
|
|
$problems_in_submap = $this->getElementProblems($submap_selement,
|
|
$problems_per_trigger, $sysmaps, $submaps_relations,
|
|
$sysmaps[$sysmapid]['severity_min'], $problems_counted, $triggers_per_hosts,
|
|
$triggers_per_host_groups
|
|
);
|
|
|
|
if ($problems_in_submap !== null) {
|
|
$problems = self::sumArrayValues($problems, $problems_in_submap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count problems in triggers assigned to linked.
|
|
if (array_key_exists($sysmapid, $sysmaps)) {
|
|
foreach ($sysmaps[$sysmapid]['links'] as $link) {
|
|
if ($link['permission'] >= PERM_READ) {
|
|
$uncounted_problem_triggers = array_diff_key(
|
|
array_flip(array_column($link['linktriggers'], 'triggerid')),
|
|
$problems_counted
|
|
);
|
|
foreach ($uncounted_problem_triggers as $triggerid => $var) {
|
|
$problems_counted[$triggerid] = true;
|
|
|
|
// Skip disabled and inaccessible triggers.
|
|
if (array_key_exists($triggerid, $problems_per_trigger)) {
|
|
$problems = self::sumArrayValues($problems,
|
|
$problems_per_trigger[$triggerid]
|
|
);
|
|
}
|
|
}
|
|
unset($uncounted_problem_triggers);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Remove problems which are less important than $severity_min.
|
|
if ($problems !== null && $severity_min > 0) {
|
|
foreach (array_keys($problems) as $severity) {
|
|
if ($severity_min > $severity) {
|
|
$problems[$severity] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $problems;
|
|
}
|
|
|
|
/**
|
|
* Function is used to sum problems in 2 arrays.
|
|
*
|
|
* Example:
|
|
* $a1 = [1 => 0, 2 => 5, 3 => 10];
|
|
* $a2 = [1 => 1, 2 => 2, 3 => 3];
|
|
* self::sumArrayValues($a1, $a2); // returns [1 => 1, 2 => 7, 3 => 13]
|
|
*
|
|
* @param array $a1 Array containing severity as key and number of problems as value.
|
|
* @param array $a2 Array containing severity as key and number of problems as value.
|
|
*
|
|
* @return array Array containing problems in both arrays summed.
|
|
*/
|
|
private static function sumArrayValues(array $a1, array $a2): array {
|
|
foreach ($a1 as $key => &$value) {
|
|
$value += $a2[$key];
|
|
}
|
|
unset($value);
|
|
|
|
return $a1;
|
|
}
|
|
}
|