diff options
Diffstat (limited to 'ui/widgets/navtree/actions/WidgetView.php')
-rw-r--r-- | ui/widgets/navtree/actions/WidgetView.php | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/ui/widgets/navtree/actions/WidgetView.php b/ui/widgets/navtree/actions/WidgetView.php new file mode 100644 index 00000000000..706789994fc --- /dev/null +++ b/ui/widgets/navtree/actions/WidgetView.php @@ -0,0 +1,519 @@ +<?php declare(strict_types = 0); +/* +** Zabbix +** Copyright (C) 2001-2022 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', + '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 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), + '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_to_add = $problems_per_trigger[$triggerid]; + $problems_counted[$triggerid] = true; + + // 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; + $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; + + 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; + $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; + $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; + } +} |