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.

319 lines
9.1 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\Geomap\Actions;
use API,
CControllerDashboardWidgetView,
CControllerResponseData,
CGeomapCoordinatesParser,
CParser,
CProfile,
CSettingsHelper,
CSeverityHelper;
class WidgetView extends CControllerDashboardWidgetView {
private const NO_PROBLEMS_MARKER_COLOR = '#009900';
protected string $widgetid;
/**
* Global geomap configuration.
*
* @param array
*/
protected array $geomap_config;
protected function init(): void {
parent::init();
$this->addValidationRules([
'initial_load' => 'in 0,1',
'widgetid' => 'db widget.widgetid',
'unique_id' => 'required|string',
'dynamic_hostid' => 'db hosts.hostid'
]);
}
protected function doAction(): void {
$this->widgetid = $this->getInput('widgetid', 0);
$data = [
'name' => $this->getInput('name', $this->widget->getDefaultName()),
'hosts' => self::convertToRFC7946($this->getHosts()),
'user' => [
'debug_mode' => $this->getDebugMode()
],
'unique_id' => $this->getInput('unique_id')
];
if ($this->getInput('initial_load', 0)) {
$this->geomap_config = self::getMapConfig();
$data['config'] = $this->geomap_config + $this->getMapCenter() + [
'filter' => $this->getUserProfileFilter(),
'colors' => self::getSeveritySettings()
];
}
$this->setResponse(new CControllerResponseData($data));
}
/**
* Get hosts and their properties to show on the map as markers.
*/
private function getHosts(): array {
if ($this->isTemplateDashboard()) {
if ($this->hasInput('dynamic_hostid')) {
$hosts = API::Host()->get([
'output' => ['hostid', 'name'],
'selectInventory' => ['location_lat', 'location_lon'],
'hostids' => [$this->getInput('dynamic_hostid')],
'filter' => [
'inventory_mode' => [HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC]
],
'monitored_hosts' => true,
'preservekeys' => true
]);
}
else {
return [];
}
}
else {
$filter_groupids = $this->fields_values['groupids'] ? getSubGroups($this->fields_values['groupids']) : null;
$hosts = API::Host()->get([
'output' => ['hostid', 'name'],
'selectInventory' => ['location_lat', 'location_lon'],
'groupids' => $filter_groupids,
'hostids' => $this->fields_values['hostids'] ?: null,
'evaltype' => $this->fields_values['evaltype'],
'tags' => $this->fields_values['tags'],
'filter' => [
'inventory_mode' => [HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC]
],
'monitored_hosts' => true,
'preservekeys' => true
]);
}
$hosts = array_filter($hosts, static function ($host) {
$lat = $host['inventory']['location_lat'];
$lng = $host['inventory']['location_lon'];
return (is_numeric($lat) && $lat >= GEOMAP_LAT_MIN && $lat <= GEOMAP_LAT_MAX
&& is_numeric($lng) && $lng >= GEOMAP_LNG_MIN && $lng <= GEOMAP_LNG_MAX);
});
// Get triggers.
$triggers = API::Trigger()->get([
'output' => [],
'selectHosts' => ['hostid'],
'hostids' => array_keys($hosts),
'filter' => [
'value' => TRIGGER_VALUE_TRUE
],
'monitored' => true,
'preservekeys' => true
]);
// Get problems.
$problems = API::Problem()->get([
'output' => ['objectid', 'severity'],
'objectids' => array_keys($triggers),
'symptom' => false
]);
// Group problems by hosts.
$problems_by_host = [];
foreach ($problems as $problem) {
foreach ($triggers[$problem['objectid']]['hosts'] as $trigger_host) {
if (!array_key_exists($trigger_host['hostid'], $problems_by_host)) {
$problems_by_host[$trigger_host['hostid']] = [
TRIGGER_SEVERITY_DISASTER => 0,
TRIGGER_SEVERITY_HIGH => 0,
TRIGGER_SEVERITY_AVERAGE => 0,
TRIGGER_SEVERITY_WARNING => 0,
TRIGGER_SEVERITY_INFORMATION => 0,
TRIGGER_SEVERITY_NOT_CLASSIFIED => 0
];
}
$problems_by_host[$trigger_host['hostid']][$problem['severity']]++;
}
}
// Filter hosts by severity filter.
$severity_filter = CProfile::get('web.dashboard.widget.geomap.severity_filter', '', $this->widgetid);
$severity_filter = ($severity_filter !== '') ? array_flip(explode(',', $severity_filter)) : [];
if ($severity_filter && count($severity_filter) != 7) {
$hosts = array_filter($hosts, static function ($host) use ($severity_filter, $problems_by_host) {
return array_key_exists($host['hostid'], $problems_by_host)
? (bool) array_intersect_key(array_filter($problems_by_host[$host['hostid']]), $severity_filter)
: array_key_exists(-1, $severity_filter);
});
}
$result_hosts = [];
foreach ($hosts as $host) {
$problems = array_key_exists($host['hostid'], $problems_by_host)
? $problems_by_host[$host['hostid']]
: [
TRIGGER_SEVERITY_DISASTER => 0,
TRIGGER_SEVERITY_HIGH => 0,
TRIGGER_SEVERITY_AVERAGE => 0,
TRIGGER_SEVERITY_WARNING => 0,
TRIGGER_SEVERITY_INFORMATION => 0,
TRIGGER_SEVERITY_NOT_CLASSIFIED => 0
];
$result_hosts[] = $host + ['problems' => $problems];
}
return $result_hosts;
}
/**
* Get initial map center point, zoom level and coordinates to center when clicking on navigate home button.
*/
private function getMapCenter(): array {
$geoloc_parser = new CGeomapCoordinatesParser();
$home_coords = [];
$center = [];
$user_default_view = CProfile::get('web.dashboard.widget.geomap.default_view', '', $this->widgetid);
if ($user_default_view !== '' && $geoloc_parser->parse($user_default_view) == CParser::PARSE_SUCCESS) {
$home_coords['default'] = true;
$center = $geoloc_parser->result;
$center['zoom'] = min($this->geomap_config['max_zoom'], $center['zoom']);
}
if (array_key_exists('default_view', $this->fields_values)
&& $this->fields_values['default_view'] !== ''
&& $geoloc_parser->parse($this->fields_values['default_view']) == CParser::PARSE_SUCCESS) {
$initial_view = $geoloc_parser->result;
if (array_key_exists('zoom', $initial_view)) {
$initial_view['zoom'] = min($this->geomap_config['max_zoom'], $initial_view['zoom']);
}
else {
$initial_view['zoom'] = ceil($this->geomap_config['max_zoom'] / 2);
}
$home_coords['initial'] = $initial_view;
if (!$center) {
$center = $home_coords['initial'];
}
}
$defaults = [
'latitude' => 0,
'longitude' => 0,
'zoom' => 1
];
return [
'center' => $center ?: $defaults,
'home_coords' => $home_coords
];
}
private function getUserProfileFilter(): array {
return [
'severity' => CProfile::get('web.dashboard.widget.geomap.severity_filter', [], $this->widgetid)
];
}
/**
* Get global map configuration.
*/
private static function getMapConfig(): array {
if (CSettingsHelper::get(CSettingsHelper::GEOMAPS_TILE_PROVIDER) === '') {
return [
'tile_url' => CSettingsHelper::get(CSettingsHelper::GEOMAPS_TILE_URL),
'max_zoom' => CSettingsHelper::get(CSettingsHelper::GEOMAPS_MAX_ZOOM),
'attribution' => htmlspecialchars(CSettingsHelper::get(CSettingsHelper::GEOMAPS_ATTRIBUTION),
ENT_NOQUOTES, 'UTF-8'
)
];
}
$tile_provider = getTileProviders()[CSettingsHelper::get(CSettingsHelper::GEOMAPS_TILE_PROVIDER)];
return [
'tile_url' => $tile_provider['geomaps_tile_url'],
'max_zoom' => $tile_provider['geomaps_max_zoom'],
'attribution' => $tile_provider['geomaps_attribution']
];
}
/**
* Create an array of problem severity colors.
*/
private static function getSeveritySettings(): array {
$severity_config = [
-1 => self::NO_PROBLEMS_MARKER_COLOR
];
for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
$severity_config[$severity] = '#'.CSeverityHelper::getColor($severity);
}
return $severity_config;
}
/**
* Convert array of hosts to valid GeoJSON (RFC7946) object.
*/
private static function convertToRFC7946(array $hosts) : array {
$geo_json = [];
foreach ($hosts as $host) {
$problems = array_filter($host['problems']);
$severities = array_keys($problems);
$top_severity = reset($severities);
$geo_json[] = [
'type' => 'Feature',
'geometry' => [
'type' => 'Point',
'coordinates' => [
$host['inventory']['location_lon'],
$host['inventory']['location_lat'],
0
]
],
'properties' => [
'hostid' => $host['hostid'],
'name' => $host['name'],
'severity' => ($top_severity === false) ? -1 : $top_severity,
'problems' => $problems
]
];
}
return $geo_json;
}
}