1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22function sysmap_element_types($type = null) { 23 $types = [ 24 SYSMAP_ELEMENT_TYPE_HOST => _('Host'), 25 SYSMAP_ELEMENT_TYPE_HOST_GROUP => _('Host group'), 26 SYSMAP_ELEMENT_TYPE_TRIGGER => _('Trigger'), 27 SYSMAP_ELEMENT_TYPE_MAP => _('Map'), 28 SYSMAP_ELEMENT_TYPE_IMAGE => _('Image') 29 ]; 30 31 if (is_null($type)) { 32 natsort($types); 33 return $types; 34 } 35 elseif (isset($types[$type])) { 36 return $types[$type]; 37 } 38 else { 39 return _('Unknown'); 40 } 41} 42 43function sysmapElementLabel($label = null) { 44 $labels = [ 45 MAP_LABEL_TYPE_LABEL => _('Label'), 46 MAP_LABEL_TYPE_IP => _('IP address'), 47 MAP_LABEL_TYPE_NAME => _('Element name'), 48 MAP_LABEL_TYPE_STATUS => _('Status only'), 49 MAP_LABEL_TYPE_NOTHING => _('Nothing'), 50 MAP_LABEL_TYPE_CUSTOM => _('Custom label') 51 ]; 52 53 if (is_null($label)) { 54 return $labels; 55 } 56 elseif (isset($labels[$label])) { 57 return $labels[$label]; 58 } 59 else { 60 return false; 61 } 62} 63 64/** 65 * Get actions (data for popup menu) for map elements. 66 * 67 * @param array $sysmap 68 * @param array $sysmap['selements'] 69 * @param array $options Options used to retrieve actions. 70 * @param int $options['severity_min'] Minimal severity used. 71 * 72 * @return array 73 */ 74function getActionsBySysmap(array $sysmap, array $options = []) { 75 $actions = []; 76 $severity_min = array_key_exists('severity_min', $options) 77 ? $options['severity_min'] 78 : TRIGGER_SEVERITY_NOT_CLASSIFIED; 79 80 foreach ($sysmap['selements'] as $selementid => $elem) { 81 if ($elem['permission'] < PERM_READ) { 82 continue; 83 } 84 85 $hostid = ($elem['elementtype_orig'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP 86 && $elem['elementsubtype_orig'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) 87 ? $elem['elements'][0]['hostid'] 88 : 0; 89 90 $map = CMenuPopupHelper::getMapElement($sysmap['sysmapid'], $elem, $severity_min, $hostid); 91 92 $actions[$selementid] = json_encode($map); 93 } 94 95 return $actions; 96} 97 98function get_png_by_selement($info) { 99 $image = get_image_by_imageid($info['iconid']); 100 101 return $image['image'] ? imagecreatefromstring($image['image']) : get_default_image(); 102} 103 104function get_map_elements($db_element, &$elements) { 105 switch ($db_element['elementtype']) { 106 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 107 $elements['hosts_groups'][] = $db_element['elements'][0]['groupid']; 108 break; 109 case SYSMAP_ELEMENT_TYPE_HOST: 110 $elements['hosts'][] = $db_element['elements'][0]['hostid']; 111 break; 112 case SYSMAP_ELEMENT_TYPE_TRIGGER: 113 foreach ($db_element['elements'] as $db_element) { 114 $elements['triggers'][] = $db_element['triggerid']; 115 } 116 break; 117 case SYSMAP_ELEMENT_TYPE_MAP: 118 $map = API::Map()->get([ 119 'output' => [], 120 'selectSelements' => ['selementid', 'elements', 'elementtype'], 121 'sysmapids' => $db_element['elements'][0]['sysmapid'], 122 'nopermissions' => true 123 ]); 124 125 if ($map) { 126 $map = reset($map); 127 128 foreach ($map['selements'] as $db_mapelement) { 129 get_map_elements($db_mapelement, $elements); 130 } 131 } 132 break; 133 } 134} 135 136/** 137 * Adds names to elements. Adds expression for SYSMAP_ELEMENT_TYPE_TRIGGER elements. 138 * 139 * @param array $selements 140 * @param array $selements[]['elements'] 141 * @param int $selements[]['elementtype'] 142 * @param int $selements[]['iconid_off'] 143 * @param int $selements[]['permission'] 144 */ 145function addElementNames(array &$selements) { 146 $hostids = []; 147 $triggerids = []; 148 $sysmapids = []; 149 $groupids = []; 150 $imageids = []; 151 152 foreach ($selements as $selement) { 153 if ($selement['permission'] < PERM_READ) { 154 continue; 155 } 156 157 switch ($selement['elementtype']) { 158 case SYSMAP_ELEMENT_TYPE_HOST: 159 $hostids[$selement['elements'][0]['hostid']] = $selement['elements'][0]['hostid']; 160 break; 161 162 case SYSMAP_ELEMENT_TYPE_MAP: 163 $sysmapids[$selement['elements'][0]['sysmapid']] = $selement['elements'][0]['sysmapid']; 164 break; 165 166 case SYSMAP_ELEMENT_TYPE_TRIGGER: 167 foreach ($selement['elements'] as $element) { 168 $triggerids[$element['triggerid']] = $element['triggerid']; 169 } 170 break; 171 172 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 173 $groupids[$selement['elements'][0]['groupid']] = $selement['elements'][0]['groupid']; 174 break; 175 176 case SYSMAP_ELEMENT_TYPE_IMAGE: 177 $imageids[$selement['iconid_off']] = $selement['iconid_off']; 178 break; 179 } 180 } 181 182 $hosts = $hostids 183 ? API::Host()->get([ 184 'output' => ['name'], 185 'hostids' => $hostids, 186 'preservekeys' => true 187 ]) 188 : []; 189 190 $maps = $sysmapids 191 ? API::Map()->get([ 192 'output' => ['name'], 193 'sysmapids' => $sysmapids, 194 'preservekeys' => true 195 ]) 196 : []; 197 198 $triggers = $triggerids 199 ? API::Trigger()->get([ 200 'output' => ['description', 'expression', 'priority'], 201 'selectHosts' => ['name'], 202 'triggerids' => $triggerids, 203 'preservekeys' => true 204 ]) 205 : []; 206 $triggers = CMacrosResolverHelper::resolveTriggerNames($triggers); 207 208 $groups = $groupids 209 ? API::HostGroup()->get([ 210 'output' => ['name'], 211 'hostgroupids' => $groupids, 212 'preservekeys' => true 213 ]) 214 : []; 215 216 $images = $imageids 217 ? API::Image()->get([ 218 'output' => ['name'], 219 'imageids' => $imageids, 220 'preservekeys' => true 221 ]) 222 : []; 223 224 foreach ($selements as $snum => &$selement) { 225 if ($selement['permission'] < PERM_READ) { 226 continue; 227 } 228 229 switch ($selement['elementtype']) { 230 case SYSMAP_ELEMENT_TYPE_HOST: 231 $selements[$snum]['elements'][0]['elementName'] = $hosts[$selement['elements'][0]['hostid']]['name']; 232 break; 233 234 case SYSMAP_ELEMENT_TYPE_MAP: 235 $selements[$snum]['elements'][0]['elementName'] = $maps[$selement['elements'][0]['sysmapid']]['name']; 236 break; 237 238 case SYSMAP_ELEMENT_TYPE_TRIGGER: 239 foreach ($selement['elements'] as $enum => &$element) { 240 if (array_key_exists($element['triggerid'], $triggers)) { 241 $trigger = $triggers[$element['triggerid']]; 242 $element['elementName'] = $trigger['hosts'][0]['name'].NAME_DELIMITER.$trigger['description']; 243 $element['priority'] = $trigger['priority']; 244 } 245 else { 246 unset($selement['elements'][$enum]); 247 } 248 } 249 unset($element); 250 $selement['elements'] = array_values($selement['elements']); 251 break; 252 253 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 254 $selements[$snum]['elements'][0]['elementName'] = $groups[$selement['elements'][0]['groupid']]['name']; 255 break; 256 257 case SYSMAP_ELEMENT_TYPE_IMAGE: 258 if (array_key_exists($selement['iconid_off'], $images)) { 259 $selements[$snum]['elements'][0]['elementName'] = $images[$selement['iconid_off']]['name']; 260 } 261 break; 262 } 263 } 264 unset($selement); 265} 266 267/** 268 * Returns selement icon rendering parameters. 269 * 270 * @param array $i 271 * @param int $i['elementtype'] Element type. Possible values: 272 * SYSMAP_ELEMENT_TYPE_HOST, SYSMAP_ELEMENT_TYPE_MAP, 273 * SYSMAP_ELEMENT_TYPE_TRIGGER, SYSMAP_ELEMENT_TYPE_HOST_GROUP, 274 * SYSMAP_ELEMENT_TYPE_IMAGE. 275 * @param int $i['disabled'] The number of disabled hosts. 276 * @param int $i['maintenance'] The number of hosts in maintenance. 277 * @param int $i['problem'] The number of problems. 278 * @param int $i['problem_unack'] The number of unacknowledged problems. 279 * @param int $i['iconid_off'] Icon ID for element without problems. 280 * @param int $i['iconid_on'] Icon ID for element with problems. 281 * @param int $i['iconid_maintenance'] Icon ID for element with hosts in maintenance. 282 * @param int $i['iconid_disabled'] Icon ID for disabled element. 283 * @param bool $i['latelyChanged'] Whether trigger status has changed recently. 284 * @param int $i['priority'] Problem severity. Possible values: 285 * TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_INFORMATION, 286 * TRIGGER_SEVERITY_WARNING, TRIGGER_SEVERITY_AVERAGE, TRIGGER_SEVERITY_HIGH, 287 * TRIGGER_SEVERITY_DISASTER. 288 * @param int $i['expandproblem'] Map "Display problems" option. Possible values: 289 * SYSMAP_SINGLE_PROBLEM, SYSMAP_PROBLEMS_NUMBER, 290 * SYSMAP_PROBLEMS_NUMBER_CRITICAL. 291 * @param string $i['problem_title'] (optional) The name of the most critical problem. 292 * @param int|null $show_unack (optional) Map "Problem display" option. Possible values: 293 * EXTACK_OPTION_ALL, EXTACK_OPTION_UNACK, EXTACK_OPTION_BOTH. 294 * 295 * @return array 296 */ 297function getSelementInfo(array $i, ?int $show_unack = null): array { 298 if ($i['elementtype'] == SYSMAP_ELEMENT_TYPE_IMAGE) { 299 return [ 300 'iconid' => $i['iconid_off'], 301 'icon_type' => SYSMAP_ELEMENT_ICON_OFF, 302 'name' => _('Image'), 303 'latelyChanged' => false 304 ]; 305 } 306 307 $info = [ 308 'latelyChanged' => $i['latelyChanged'], 309 'ack' => !$i['problem_unack'], 310 'priority' => $i['priority'], 311 'info' => [], 312 'iconid' => $i['iconid_off'], 313 'aria_label' => '' 314 ]; 315 316 if ($i['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST && $i['disabled']) { 317 $info['iconid'] = $i['iconid_disabled']; 318 $info['icon_type'] = SYSMAP_ELEMENT_ICON_DISABLED; 319 $info['info']['status'] = [ 320 'msg' => _('Disabled'), 321 'color' => '960000' 322 ]; 323 324 return $info; 325 } 326 327 $has_problem = false; 328 329 if ($i['problem']) { 330 if ($show_unack == EXTACK_OPTION_ALL || $show_unack == EXTACK_OPTION_BOTH) { 331 $msg = ''; 332 333 // Expand single problem. 334 if ($i['expandproblem'] == SYSMAP_SINGLE_PROBLEM) { 335 $msg = ($i['problem'] == 1) ? $i['problem_title'] : _n('%1$s problem', '%1$s problems', $i['problem']); 336 } 337 // Number of problems. 338 elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) { 339 $msg = _n('%1$s problem', '%1$s problems', $i['problem']); 340 } 341 // Number of problems and expand most critical one. 342 elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL) { 343 $msg = $i['problem_title']; 344 345 if ($i['problem'] > 1) { 346 $msg .= "\n"._n('%1$s problem', '%1$s problems', $i['problem']); 347 } 348 } 349 350 $info['info']['problem'] = [ 351 'msg' => $msg, 352 'color' => getSelementLabelColor(true, !$i['problem_unack']) 353 ]; 354 } 355 356 if ($i['problem_unack'] && ($show_unack == EXTACK_OPTION_UNACK || $show_unack == EXTACK_OPTION_BOTH)) { 357 $msg = ''; 358 359 if ($show_unack == EXTACK_OPTION_UNACK) { 360 if ($i['expandproblem'] == SYSMAP_SINGLE_PROBLEM) { 361 $msg = ($i['problem_unack'] == 1) 362 ? $i['problem_title'] 363 : _n('%1$s unacknowledged problem', '%1$s unacknowledged problems', $i['problem_unack']); 364 } 365 elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) { 366 $msg = _n('%1$s unacknowledged problem', '%1$s unacknowledged problems', $i['problem_unack']); 367 } 368 elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL) { 369 $msg = $i['problem_title']; 370 371 if ($i['problem_unack'] > 1) { 372 $msg .= "\n". 373 _n('%1$s unacknowledged problem', '%1$s unacknowledged problems', $i['problem_unack']); 374 } 375 } 376 } 377 elseif ($show_unack == EXTACK_OPTION_BOTH) { 378 $msg = _n('%1$s unacknowledged problem', '%1$s unacknowledged problems', $i['problem_unack']); 379 } 380 381 $info['info']['unack'] = [ 382 'msg' => $msg, 383 'color' => getSelementLabelColor(true, false) 384 ]; 385 } 386 387 // Set element to problem state if it has problem events. 388 if ($info['info']) { 389 $info['iconid'] = $i['iconid_on']; 390 $info['icon_type'] = SYSMAP_ELEMENT_ICON_ON; 391 $has_problem = true; 392 } 393 394 $info['aria_label'] = ($i['problem'] > 1) 395 ? _n('%1$s problem', '%1$s problems', $i['problem']) 396 : $i['problem_title']; 397 } 398 399 if ($i['maintenance']) { 400 $info['iconid'] = $i['iconid_maintenance']; 401 $info['icon_type'] = SYSMAP_ELEMENT_ICON_MAINTENANCE; 402 $info['info']['maintenance'] = [ 403 'msg' => ($i['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST) 404 ? _('In maintenance') 405 : _n('%1$s host in maintenance', '%1$s hosts in maintenance', $i['maintenance']), 406 'color' => 'EE9600' 407 ]; 408 } 409 410 if (!$has_problem) { 411 if (!$i['maintenance']) { 412 $info['iconid'] = $i['iconid_off']; 413 $info['icon_type'] = SYSMAP_ELEMENT_ICON_OFF; 414 } 415 416 $info['info']['ok'] = [ 417 'msg' => _('OK'), 418 'color' => getSelementLabelColor(false, $info['ack']) 419 ]; 420 } 421 422 return $info; 423} 424 425/** 426 * Prepare map elements data. 427 * Calculate problem triggers and priorities. Populate map elements with automatic icon mapping, acknowledging and 428 * recent change markers. 429 * 430 * @param array $sysmap 431 * @param array $options 432 * @param int $options['severity_min'] Minimum severity, default value is maximal (Disaster) 433 * 434 * @return array 435 */ 436function getSelementsInfo(array $sysmap, array $options = []): array { 437 if (!isset($options['severity_min'])) { 438 $options['severity_min'] = TRIGGER_SEVERITY_NOT_CLASSIFIED; 439 } 440 441 $triggerIdToSelementIds = []; 442 $subSysmapTriggerIdToSelementIds = []; 443 $hostGroupIdToSelementIds = []; 444 $hostIdToSelementIds = []; 445 446 if ($sysmap['sysmapid']) { 447 $iconMap = API::IconMap()->get([ 448 'output' => API_OUTPUT_EXTEND, 449 'selectMappings' => API_OUTPUT_EXTEND, 450 'sysmapids' => $sysmap['sysmapid'] 451 ]); 452 $iconMap = reset($iconMap); 453 } 454 $hostsToGetInventories = []; 455 456 $selements = $sysmap['selements']; 457 $selementIdToSubSysmaps = []; 458 foreach ($selements as $selementId => &$selement) { 459 $selement['hosts'] = []; 460 $selement['triggers'] = []; 461 462 if ($selement['permission'] < PERM_READ) { 463 continue; 464 } 465 466 switch ($selement['elementtype']) { 467 case SYSMAP_ELEMENT_TYPE_MAP: 468 $sysmapIds = [$selement['elements'][0]['sysmapid']]; 469 470 while (!empty($sysmapIds)) { 471 $subSysmaps = API::Map()->get([ 472 'output' => ['sysmapid'], 473 'selectSelements' => ['elementtype', 'elements', 'application'], 474 'sysmapids' => $sysmapIds, 475 'preservekeys' => true 476 ]); 477 478 if(!isset($selementIdToSubSysmaps[$selementId])) { 479 $selementIdToSubSysmaps[$selementId] = []; 480 } 481 $selementIdToSubSysmaps[$selementId] += $subSysmaps; 482 483 $sysmapIds = []; 484 foreach ($subSysmaps as $subSysmap) { 485 foreach ($subSysmap['selements'] as $subSysmapSelement) { 486 switch ($subSysmapSelement['elementtype']) { 487 case SYSMAP_ELEMENT_TYPE_MAP: 488 $sysmapIds[] = $subSysmapSelement['elements'][0]['sysmapid']; 489 break; 490 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 491 $hostGroupIdToSelementIds[$subSysmapSelement['elements'][0]['groupid']][$selementId] 492 = $selementId; 493 break; 494 case SYSMAP_ELEMENT_TYPE_HOST: 495 $hostIdToSelementIds[$subSysmapSelement['elements'][0]['hostid']][$selementId] 496 = $selementId; 497 break; 498 case SYSMAP_ELEMENT_TYPE_TRIGGER: 499 foreach ($subSysmapSelement['elements'] as $element) { 500 $subSysmapTriggerIdToSelementIds[$element['triggerid']][$selementId] 501 = $selementId; 502 } 503 break; 504 } 505 } 506 } 507 } 508 break; 509 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 510 $hostGroupId = $selement['elements'][0]['groupid']; 511 $hostGroupIdToSelementIds[$hostGroupId][$selementId] = $selementId; 512 break; 513 case SYSMAP_ELEMENT_TYPE_HOST: 514 $hostId = $selement['elements'][0]['hostid']; 515 $hostIdToSelementIds[$hostId][$selementId] = $selementId; 516 517 /** 518 * If we have icon map applied, we need to get inventories for all hosts, where automatic icon 519 * selection is enabled. 520 */ 521 if ($sysmap['iconmapid'] && $selement['use_iconmap']) { 522 $hostsToGetInventories[] = $hostId; 523 } 524 break; 525 case SYSMAP_ELEMENT_TYPE_TRIGGER: 526 foreach ($selement['elements'] as $element) { 527 $triggerIdToSelementIds[$element['triggerid']][$selementId] = $selementId; 528 } 529 break; 530 } 531 } 532 unset($selement); 533 534 // get host inventories 535 if ($sysmap['iconmapid']) { 536 $host_inventories = API::Host()->get([ 537 'output' => ['hostid', 'inventory_mode'], 538 'selectInventory' => API_OUTPUT_EXTEND, 539 'hostids' => $hostsToGetInventories, 540 'preservekeys' => true 541 ]); 542 } 543 544 $allHosts = []; 545 if ($hostIdToSelementIds) { 546 $allHosts = API::Host()->get([ 547 'output' => ['name', 'status', 'maintenance_status'], 548 'hostids' => array_keys($hostIdToSelementIds), 549 'preservekeys' => true 550 ]); 551 552 foreach ($allHosts as $hostId => $host) { 553 foreach ($hostIdToSelementIds[$hostId] as $selementId) { 554 $selements[$selementId]['hosts'][$hostId] = $hostId; 555 } 556 } 557 } 558 559 $hostsFromHostGroups = []; 560 if ($hostGroupIdToSelementIds) { 561 $hostsFromHostGroups = API::Host()->get([ 562 'output' => ['name', 'status', 'maintenance_status'], 563 'selectGroups' => ['groupid'], 564 'groupids' => array_keys($hostGroupIdToSelementIds), 565 'preservekeys' => true 566 ]); 567 568 foreach ($hostsFromHostGroups as $hostId => $host) { 569 foreach ($host['groups'] as $group) { 570 $groupId = $group['groupid']; 571 572 if (isset($hostGroupIdToSelementIds[$groupId])) { 573 foreach ($hostGroupIdToSelementIds[$groupId] as $selementId) { 574 $selement =& $selements[$selementId]; 575 576 $selement['hosts'][$hostId] = $hostId; 577 578 // Add hosts to hosts_map for trigger selection. 579 if (!isset($hostIdToSelementIds[$hostId])) { 580 $hostIdToSelementIds[$hostId] = []; 581 } 582 $hostIdToSelementIds[$hostId][$selementId] = $selementId; 583 584 unset($selement); 585 } 586 } 587 } 588 } 589 590 $allHosts = zbx_array_merge($allHosts, $hostsFromHostGroups); 591 } 592 593 // Get triggers data, triggers from current map and from submaps, select all. 594 if ($triggerIdToSelementIds || $subSysmapTriggerIdToSelementIds) { 595 $all_triggerid_to_selementids = []; 596 597 foreach ([$triggerIdToSelementIds, $subSysmapTriggerIdToSelementIds] as $triggerid_to_selementids) { 598 foreach ($triggerid_to_selementids as $triggerid => $selementids) { 599 if (!array_key_exists($triggerid, $all_triggerid_to_selementids)) { 600 $all_triggerid_to_selementids[$triggerid] = $selementids; 601 } 602 else { 603 $all_triggerid_to_selementids[$triggerid] += $selementids; 604 } 605 } 606 } 607 608 $triggers = API::Trigger()->get([ 609 'output' => ['triggerid', 'status', 'value', 'priority', 'description', 'expression'], 610 'selectHosts' => ['hostid', 'maintenance_status'], 611 'triggerids' => array_keys($all_triggerid_to_selementids), 612 'filter' => ['state' => null], 613 'preservekeys' => true 614 ]); 615 616 $monitored_triggers = API::Trigger()->get([ 617 'output' => [], 618 'triggerids' => array_keys($triggers), 619 'monitored' => true, 620 'skipDependent' => true, 621 'preservekeys' => true 622 ]); 623 624 foreach ($triggers as $triggerid => $trigger) { 625 if (!array_key_exists($triggerid, $monitored_triggers)) { 626 $trigger['status'] = TRIGGER_STATUS_DISABLED; 627 } 628 629 $trigger['source'][SYSMAP_ELEMENT_TYPE_TRIGGER] = true; 630 631 if (array_key_exists($triggerid, $all_triggerid_to_selementids)) { 632 foreach ($all_triggerid_to_selementids[$triggerid] as $selementid) { 633 $selements[$selementid]['triggers'][$triggerid] = $trigger; 634 } 635 } 636 } 637 unset($triggers, $monitored_triggers); 638 } 639 640 $monitored_hostids = []; 641 foreach ($allHosts as $hostid => $host) { 642 if ($host['status'] == HOST_STATUS_MONITORED) { 643 $monitored_hostids[$hostid] = true; 644 } 645 } 646 647 // triggers from all hosts/hostgroups, skip dependent 648 if ($monitored_hostids) { 649 $triggers = API::Trigger()->get([ 650 'output' => ['triggerid', 'status', 'value', 'priority', 'description', 'expression'], 651 'selectHosts' => ['hostid', 'maintenance_status'], 652 'selectItems' => ['itemid'], 653 'hostids' => array_keys($monitored_hostids), 654 'filter' => ['state' => null], 655 'monitored' => true, 656 'skipDependent' => true, 657 'only_true' => true, 658 'preservekeys' => true 659 ]); 660 661 foreach ($triggers as $triggerid => $trigger) { 662 $trigger['source'][SYSMAP_ELEMENT_TYPE_HOST] = true; 663 664 foreach ($trigger['hosts'] as $host) { 665 if (array_key_exists($host['hostid'], $hostIdToSelementIds)) { 666 foreach ($hostIdToSelementIds[$host['hostid']] as $selementid) { 667 if (!array_key_exists($triggerid, $selements[$selementid]['triggers'])) { 668 $selements[$selementid]['triggers'][$triggerid] = $trigger; 669 } 670 else { 671 $selements[$selementid]['triggers'][$triggerid]['status'] = $trigger['status']; 672 $selements[$selementid]['triggers'][$triggerid]['source'] += $trigger['source']; 673 $selements[$selementid]['triggers'][$triggerid]['items'] = $trigger['items']; 674 } 675 } 676 } 677 } 678 } 679 680 $subSysmapHostApplicationFilters = getSelementHostApplicationFilters($selements, $selementIdToSubSysmaps, 681 $hostsFromHostGroups 682 ); 683 $selements = filterSysmapTriggers($selements, $subSysmapHostApplicationFilters, $triggers, 684 $subSysmapTriggerIdToSelementIds 685 ); 686 } 687 688 // Get problems by triggerids. 689 $triggerids = []; 690 foreach ($selements as $selement) { 691 foreach ($selement['triggers'] as $trigger) { 692 if ($trigger['status'] == TRIGGER_STATUS_ENABLED) { 693 $triggerids[$trigger['triggerid']] = true; 694 } 695 } 696 } 697 698 $problems = API::Problem()->get([ 699 'output' => ['eventid', 'objectid', 'name', 'acknowledged', 'clock', 'r_clock', 'severity'], 700 'objectids' => array_keys($triggerids), 701 'acknowledged' => ($sysmap['show_unack'] == EXTACK_OPTION_UNACK) ? false : null, 702 'severities' => range($options['severity_min'], TRIGGER_SEVERITY_COUNT - 1), 703 'suppressed' => ($sysmap['show_suppressed'] == ZBX_PROBLEM_SUPPRESSED_FALSE) ? false : null, 704 'recent' => true 705 ]); 706 707 foreach ($selements as $snum => $selement) { 708 foreach ($problems as $problem) { 709 if (array_key_exists($problem['objectid'], $selement['triggers'])) { 710 $selements[$snum]['triggers'][$problem['objectid']]['problems'][] = $problem; 711 } 712 } 713 } 714 715 $config = select_config(); 716 717 $info = []; 718 foreach ($selements as $selementId => $selement) { 719 $i = [ 720 'elementtype' => $selement['elementtype'], 721 'disabled' => 0, 722 'maintenance' => 0, 723 'problem' => 0, 724 'problem_unack' => 0, 725 'priority' => 0, 726 'latelyChanged' => false, 727 'expandproblem' => $sysmap['expandproblem'] 728 ]; 729 730 /* 731 * If user has no rights to see the details of particular selement, add only info that is needed to render map 732 * icons. 733 */ 734 if (PERM_READ > $selement['permission']) { 735 $info[$selementId] = getSelementInfo($i + ['iconid_off' => $selement['iconid_off']]); 736 737 continue; 738 } 739 740 if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER 741 || $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) { 742 $trigger_hosts = []; 743 foreach ($selement['triggers'] as $trigger) { 744 foreach ($trigger['hosts'] as $host) { 745 if (!array_key_exists($host['hostid'], $trigger_hosts) 746 && !array_key_exists($host['hostid'], $selement['hosts'])) { 747 $trigger_hosts[$host['hostid']] = true; 748 749 if ($host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON 750 && ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER 751 || ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP 752 && array_key_exists(SYSMAP_ELEMENT_TYPE_TRIGGER, $trigger['source'])))) { 753 $i['maintenance']++; 754 } 755 } 756 } 757 } 758 } 759 760 foreach ($selement['hosts'] as $hostId) { 761 $host = $allHosts[$hostId]; 762 763 if ($host['status'] == HOST_STATUS_NOT_MONITORED) { 764 $i['disabled']++; 765 } 766 elseif ($host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) { 767 $i['maintenance']++; 768 } 769 } 770 771 $critical_problem = []; 772 $trigger_order = ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) 773 ? zbx_objectValues($selement['elements'], 'triggerid') 774 : []; 775 $lately_changed = 0; 776 777 foreach ($selement['triggers'] as $trigger) { 778 if ($trigger['status'] == TRIGGER_STATUS_DISABLED) { 779 continue; 780 } 781 782 if (array_key_exists('problems', $trigger)) { 783 foreach ($trigger['problems'] as $problem) { 784 if ($problem['r_clock'] == 0) { 785 $i['problem']++; 786 787 if ($problem['acknowledged'] == EVENT_NOT_ACKNOWLEDGED) { 788 $i['problem_unack']++; 789 } 790 791 if (!$critical_problem || $critical_problem['severity'] < $problem['severity']) { 792 $critical_problem = $problem; 793 } 794 elseif ($critical_problem['severity'] === $problem['severity']) { 795 if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { 796 if ($critical_problem['objectid'] === $problem['objectid'] 797 && $critical_problem['eventid'] < $problem['eventid']) { 798 $critical_problem = $problem; 799 } 800 elseif (array_search($critical_problem['objectid'], $trigger_order) 801 > array_search($problem['objectid'], $trigger_order)) { 802 $critical_problem = $problem; 803 } 804 } 805 elseif ($critical_problem['eventid'] < $problem['eventid']) { 806 $critical_problem = $problem; 807 } 808 } 809 } 810 811 if ($problem['r_clock'] > $lately_changed) { 812 $lately_changed = $problem['r_clock']; 813 } 814 elseif ($problem['clock'] > $lately_changed) { 815 $lately_changed = $problem['clock']; 816 } 817 } 818 } 819 820 $i['latelyChanged'] |= ((time() - $lately_changed) < timeUnitToSeconds($config['blink_period'])); 821 } 822 823 if ($critical_problem) { 824 $i['priority'] = $critical_problem['severity']; 825 $i['problem_title'] = $critical_problem['name']; 826 } 827 828 // replace default icons 829 if (!$selement['iconid_on']) { 830 $selement['iconid_on'] = $selement['iconid_off']; 831 } 832 if (!$selement['iconid_maintenance']) { 833 $selement['iconid_maintenance'] = $selement['iconid_off']; 834 } 835 if (!$selement['iconid_disabled']) { 836 $selement['iconid_disabled'] = $selement['iconid_off']; 837 } 838 839 $i['iconid_off'] = $selement['iconid_off']; 840 $i['iconid_on'] = $selement['iconid_on']; 841 $i['iconid_maintenance'] = $selement['iconid_maintenance']; 842 $i['iconid_disabled'] = $selement['iconid_disabled']; 843 844 $info[$selementId] = getSelementInfo($i, $sysmap['show_unack']); 845 846 if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST && $sysmap['iconmapid'] && $selement['use_iconmap']) { 847 $host_inventory = $host_inventories[$selement['elements'][0]['hostid']]; 848 $info[$selementId]['iconid'] = getIconByMapping($iconMap, $host_inventory); 849 } 850 851 $info[$selementId]['problems_total'] = $i['problem']; 852 } 853 854 if ($sysmap['label_format'] == SYSMAP_LABEL_ADVANCED_OFF) { 855 $hlabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME); 856 $hglabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME); 857 $tlabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME); 858 $mlabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME); 859 } 860 else { 861 $hlabel = ($sysmap['label_type_host'] == MAP_LABEL_TYPE_NAME); 862 $hglabel = ($sysmap['label_type_hostgroup'] == MAP_LABEL_TYPE_NAME); 863 $tlabel = ($sysmap['label_type_trigger'] == MAP_LABEL_TYPE_NAME); 864 $mlabel = ($sysmap['label_type_map'] == MAP_LABEL_TYPE_NAME); 865 } 866 867 // get names if needed 868 $elems = separateMapElements($sysmap); 869 870 if ($elems['sysmaps'] && $mlabel) { 871 $sysmapids = []; 872 873 foreach ($elems['sysmaps'] as $selement) { 874 if ($selement['permission'] >= PERM_READ) { 875 $sysmapids[$selement['elements'][0]['sysmapid']] = true; 876 } 877 } 878 879 $db_sysmaps = API::Map()->get([ 880 'output' => ['name'], 881 'sysmapids' => array_keys($sysmapids), 882 'preservekeys' => true 883 ]); 884 885 foreach ($elems['sysmaps'] as $selement) { 886 if ($selement['permission'] >= PERM_READ) { 887 $info[$selement['selementid']]['name'] = 888 array_key_exists($selement['elements'][0]['sysmapid'], $db_sysmaps) 889 ? $db_sysmaps[$selement['elements'][0]['sysmapid']]['name'] 890 : ''; 891 } 892 } 893 } 894 895 if ($elems['hostgroups'] && $hglabel) { 896 $groupids = []; 897 898 foreach ($elems['hostgroups'] as $selement) { 899 if ($selement['permission'] >= PERM_READ) { 900 $groupids[$selement['elements'][0]['groupid']] = true; 901 } 902 } 903 904 $db_groups = $groupids 905 ? API::HostGroup()->get([ 906 'output' => ['name'], 907 'groupids' => array_keys($groupids), 908 'preservekeys' => true 909 ]) 910 : []; 911 912 foreach ($elems['hostgroups'] as $selement) { 913 if ($selement['permission'] >= PERM_READ) { 914 $info[$selement['selementid']]['name'] = 915 array_key_exists($selement['elements'][0]['groupid'], $db_groups) 916 ? $db_groups[$selement['elements'][0]['groupid']]['name'] 917 : ''; 918 } 919 } 920 } 921 922 if ($elems['triggers'] && $tlabel) { 923 $selements = zbx_toHash($selements, 'selementid'); 924 foreach ($elems['triggers'] as $selementid => $selement) { 925 foreach ($selement['elements'] as $element) { 926 if ($selement['permission'] >= PERM_READ) { 927 $trigger = array_key_exists($element['triggerid'], $selements[$selementid]['triggers']) 928 ? $selements[$selementid]['triggers'][$element['triggerid']] 929 : null; 930 $info[$selement['selementid']]['name'] = ($trigger != null) 931 ? CMacrosResolverHelper::resolveTriggerName($trigger) 932 : ''; 933 } 934 } 935 } 936 } 937 938 if ($elems['hosts'] && $hlabel) { 939 foreach ($elems['hosts'] as $selement) { 940 if ($selement['permission'] >= PERM_READ) { 941 $info[$selement['selementid']]['name'] = array_key_exists($selement['elements'][0]['hostid'], $allHosts) 942 ? $allHosts[$selement['elements'][0]['hostid']]['name'] 943 : []; 944 } 945 } 946 } 947 948 return $info; 949} 950 951/** 952 * Takes sysmap selements array, applies filtering by application to triggers and returns sysmap selements array. 953 * 954 * @param array $selements selements of current sysmap 955 * @param array $selementHostApplicationFilters a list of application filters applied to each host under each element 956 * @see getSelementHostApplicationFilters() 957 * @param array $triggersFromMonitoredHosts triggers that are relevant to filtering 958 * @param array $subSysmapTriggerIdToSelementIds a map of triggers in sysmaps to selement IDs 959 * 960 * @return array 961 */ 962function filterSysmapTriggers(array $selements, array $selementHostApplicationFilters, 963 array $triggersFromMonitoredHosts, array $subSysmapTriggerIdToSelementIds) { 964 // pick only host, host group or map selements 965 $filterableSelements = []; 966 foreach ($selements as $selementId => $selement) { 967 if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST 968 || $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP 969 || $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) { 970 $filterableSelements[$selementId] = $selement; 971 } 972 } 973 // calculate list of triggers that might get removed from $selement['triggers'] 974 $triggersToFilter = []; 975 foreach ($filterableSelements as $selementId => $selement) { 976 foreach ($selement['triggers'] as $trigger) { 977 if (!isset($triggersFromMonitoredHosts[$trigger['triggerid']])) { 978 continue; 979 } 980 $trigger = $triggersFromMonitoredHosts[$trigger['triggerid']]; 981 foreach ($trigger['hosts'] as $host) { 982 $hostId = $host['hostid']; 983 if (isset($selementHostApplicationFilters[$selementId][$hostId])) { 984 $triggersToFilter[$trigger['triggerid']] = $trigger; 985 } 986 } 987 } 988 } 989 990 // if there are no triggers to filter 991 if (!$triggersToFilter) { 992 return $selements; 993 } 994 995 // produce mapping of trigger to application names it is related to and produce mapping of host to triggers 996 $itemIds = []; 997 foreach ($triggersToFilter as $trigger) { 998 foreach ($trigger['items'] as $item) { 999 $itemIds[$item['itemid']] = $item['itemid']; 1000 } 1001 } 1002 $items = API::Item()->get([ 1003 'output' => ['itemid'], 1004 'selectApplications' => ['name'], 1005 'itemids' => $itemIds, 1006 'webitems' => true, 1007 'preservekeys' => true 1008 ]); 1009 1010 $triggerApplications = []; 1011 $hostIdToTriggers = []; 1012 foreach ($triggersToFilter as $trigger) { 1013 $triggerId = $trigger['triggerid']; 1014 1015 foreach ($trigger['items'] as $item) { 1016 foreach ($items[$item['itemid']]['applications'] as $application) { 1017 $triggerApplications[$triggerId][$application['name']] = true; 1018 } 1019 } 1020 1021 foreach ($trigger['hosts'] as $host) { 1022 $hostIdToTriggers[$host['hostid']][$triggerId] = $trigger; 1023 } 1024 } 1025 1026 foreach ($filterableSelements as $selementId => &$selement) { 1027 // walk through each host of a submap and apply its filters to all its triggers 1028 foreach ($selement['hosts'] as $hostId) { 1029 // skip hosts that don't have any filters or triggers to filter 1030 if (!isset($hostIdToTriggers[$hostId]) || !isset($selementHostApplicationFilters[$selementId][$hostId])) { 1031 continue; 1032 } 1033 1034 // remove the triggers that don't have applications or don't match the filter 1035 $filteredApplicationNames = $selementHostApplicationFilters[$selementId][$hostId]; 1036 foreach ($hostIdToTriggers[$hostId] as $trigger) { 1037 $triggerId = $trigger['triggerid']; 1038 1039 // skip if this trigger is standalone trigger and those are not filtered 1040 if (isset($subSysmapTriggerIdToSelementIds[$triggerId]) 1041 && isset($subSysmapTriggerIdToSelementIds[$triggerId][$selementId])) { 1042 continue; 1043 } 1044 1045 $applicationNamesForTrigger = isset($triggerApplications[$triggerId]) 1046 ? array_keys($triggerApplications[$triggerId]) 1047 : []; 1048 1049 if (!array_intersect($applicationNamesForTrigger, $filteredApplicationNames)) { 1050 unset($selement['triggers'][$triggerId]); 1051 } 1052 } 1053 } 1054 } 1055 unset($selement); 1056 1057 // put back updated selements 1058 foreach ($filterableSelements as $selementId => $selement) { 1059 $selements[$selementId] = $selement; 1060 } 1061 1062 return $selements; 1063} 1064 1065/** 1066 * Returns a list of application filters applied to each host under each element. 1067 * 1068 * @param array $selements selements of current sysmap 1069 * @param array $selementIdToSubSysmaps all sub-sysmaps used in current sysmap, indexed by selementId 1070 * @param array $hostsFromHostGroups collection of hosts that get included via host groups 1071 * 1072 * @return array a two-dimensional array with selement IDs as the primary key, host IDs as the secondary key 1073 * application names as values 1074 */ 1075function getSelementHostApplicationFilters(array $selements, array $selementIdToSubSysmaps, 1076 array $hostsFromHostGroups) { 1077 $hostIdsForHostGroupId = []; 1078 foreach ($hostsFromHostGroups as $host) { 1079 $hostId = $host['hostid']; 1080 foreach ($host['groups'] as $group) { 1081 $hostIdsForHostGroupId[$group['groupid']][$hostId] = $hostId; 1082 } 1083 } 1084 1085 $selementHostApplicationFilters = []; 1086 foreach ($selements as $selementId => $selement) { 1087 switch ($selement['elementtype']) { 1088 case SYSMAP_ELEMENT_TYPE_HOST: 1089 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 1090 // skip host and host group elements with an empty filter 1091 if ($selement['application'] === '') { 1092 continue 2; 1093 } 1094 1095 foreach ($selement['hosts'] as $hostId) { 1096 $selementHostApplicationFilters[$selementId][$hostId][] = $selement['application']; 1097 } 1098 1099 break; 1100 1101 case SYSMAP_ELEMENT_TYPE_MAP: 1102 if (array_key_exists($selementId, $selementIdToSubSysmaps)) { 1103 foreach ($selementIdToSubSysmaps[$selementId] as $subSysmap) { 1104 // add all filters set for host elements 1105 foreach ($subSysmap['selements'] as $subSysmapSelement) { 1106 if ($subSysmapSelement['elementtype'] != SYSMAP_ELEMENT_TYPE_HOST 1107 || $subSysmapSelement['application'] === '') { 1108 1109 continue; 1110 } 1111 1112 $hostId = $subSysmapSelement['elements'][0]['hostid']; 1113 $selementHostApplicationFilters[$selementId][$hostId][] = $subSysmapSelement['application']; 1114 } 1115 1116 // Find all selements with host groups and sort them into two arrays: 1117 // - with application filter 1118 // - without application filter 1119 $hostGroupSelementsWithApplication = []; 1120 $hostGroupSelementsWithoutApplication = []; 1121 foreach ($subSysmap['selements'] as $subSysmapSelement) { 1122 if ($subSysmapSelement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP) { 1123 if ($subSysmapSelement['application'] !== '') { 1124 $hostGroupSelementsWithApplication[] = $subSysmapSelement; 1125 } 1126 else { 1127 $hostGroupSelementsWithoutApplication[] = $subSysmapSelement; 1128 } 1129 } 1130 } 1131 1132 // Combine application filters for hosts from host group selements with 1133 // application filters set. 1134 foreach ($hostGroupSelementsWithApplication as $hostGroupSelement) { 1135 $hostGroupId = $hostGroupSelement['elements'][0]['groupid']; 1136 1137 if (isset($hostIdsForHostGroupId[$hostGroupId])) { 1138 foreach ($hostIdsForHostGroupId[$hostGroupId] as $hostId) { 1139 $selementHostApplicationFilters[$selementId][$hostId][] 1140 = $hostGroupSelement['application']; 1141 } 1142 } 1143 } 1144 1145 // Unset all application filters for hosts in host group selements without any filters. 1146 // This might reset application filters set by previous foreach. 1147 foreach ($hostGroupSelementsWithoutApplication AS $hostGroupSelement) { 1148 $hostGroupId = $hostGroupSelement['elements'][0]['groupid']; 1149 1150 if (isset($hostIdsForHostGroupId[$hostGroupId])) { 1151 foreach ($hostIdsForHostGroupId[$hostGroupId] as $hostId) { 1152 unset($selementHostApplicationFilters[$selementId][$hostId]); 1153 } 1154 } 1155 } 1156 } 1157 } 1158 break; 1159 } 1160 } 1161 1162 return $selementHostApplicationFilters; 1163} 1164 1165function separateMapElements($sysmap) { 1166 $elements = [ 1167 'sysmaps' => [], 1168 'hostgroups' => [], 1169 'hosts' => [], 1170 'triggers' => [], 1171 'images' => [] 1172 ]; 1173 1174 foreach ($sysmap['selements'] as $selement) { 1175 switch ($selement['elementtype']) { 1176 case SYSMAP_ELEMENT_TYPE_MAP: 1177 $elements['sysmaps'][$selement['selementid']] = $selement; 1178 break; 1179 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 1180 $elements['hostgroups'][$selement['selementid']] = $selement; 1181 break; 1182 case SYSMAP_ELEMENT_TYPE_HOST: 1183 $elements['hosts'][$selement['selementid']] = $selement; 1184 break; 1185 case SYSMAP_ELEMENT_TYPE_TRIGGER: 1186 $elements['triggers'][$selement['selementid']] = $selement; 1187 break; 1188 case SYSMAP_ELEMENT_TYPE_IMAGE: 1189 default: 1190 $elements['images'][$selement['selementid']] = $selement; 1191 } 1192 } 1193 return $elements; 1194} 1195 1196/** 1197 * Calculates coordinates from elements inside areas 1198 * 1199 * @param array $map 1200 * @param array $areas 1201 * @param array $mapInfo 1202 */ 1203function processAreasCoordinates(array &$map, array $areas, array $mapInfo) { 1204 foreach ($areas as $area) { 1205 $rowPlaceCount = ceil(sqrt(count($area['selementids']))); 1206 1207 // offset from area borders 1208 $area['x'] += 5; 1209 $area['y'] += 5; 1210 $area['width'] -= 5; 1211 $area['height'] -= 5; 1212 1213 $xOffset = floor($area['width'] / $rowPlaceCount); 1214 $yOffset = floor($area['height'] / $rowPlaceCount); 1215 1216 $colNum = 0; 1217 $rowNum = 0; 1218 // some offset is required so that icon highlights are not drawn outside area 1219 $borderOffset = 20; 1220 foreach ($area['selementids'] as $selementId) { 1221 $selement = $map['selements'][$selementId]; 1222 1223 $image = get_png_by_selement($mapInfo[$selementId]); 1224 $iconX = imagesx($image); 1225 $iconY = imagesy($image); 1226 1227 $labelLocation = (is_null($selement['label_location']) || ($selement['label_location'] < 0)) 1228 ? $map['label_location'] : $selement['label_location']; 1229 switch ($labelLocation) { 1230 case MAP_LABEL_LOC_TOP: 1231 $newX = $area['x'] + ($xOffset / 2) - ($iconX / 2); 1232 $newY = $area['y'] + $yOffset - $iconY - ($iconY >= $iconX ? 0 : abs($iconX - $iconY) / 2) - $borderOffset; 1233 break; 1234 case MAP_LABEL_LOC_LEFT: 1235 $newX = $area['x'] + $xOffset - $iconX - $borderOffset; 1236 $newY = $area['y'] + ($yOffset / 2) - ($iconY / 2); 1237 break; 1238 case MAP_LABEL_LOC_RIGHT: 1239 $newX = $area['x'] + $borderOffset; 1240 $newY = $area['y'] + ($yOffset / 2) - ($iconY / 2); 1241 break; 1242 case MAP_LABEL_LOC_BOTTOM: 1243 $newX = $area['x'] + ($xOffset / 2) - ($iconX / 2); 1244 $newY = $area['y'] + abs($iconX - $iconY) / 2 + $borderOffset; 1245 break; 1246 } 1247 1248 $map['selements'][$selementId]['x'] = $newX + ($colNum * $xOffset); 1249 $map['selements'][$selementId]['y'] = $newY + ($rowNum * $yOffset); 1250 1251 $colNum++; 1252 if ($colNum == $rowPlaceCount) { 1253 $colNum = 0; 1254 $rowNum++; 1255 } 1256 } 1257 } 1258} 1259 1260/** 1261 * Calculates area connector point on area perimeter 1262 * 1263 * @param int $ax x area coordinate 1264 * @param int $ay y area coordinate 1265 * @param int $aWidth area width 1266 * @param int $aHeight area height 1267 * @param int $x2 x coordinate of connector second element 1268 * @param int $y2 y coordinate of connector second element 1269 * 1270 * @return array contains two values, x and y coordinates of new area connector point 1271 */ 1272function calculateMapAreaLinkCoord($ax, $ay, $aWidth, $aHeight, $x2, $y2) { 1273 $dY = abs($y2 - $ay); 1274 $dX = abs($x2 - $ax); 1275 1276 $halfHeight = $aHeight / 2; 1277 $halfWidth = $aWidth / 2; 1278 1279 if ($dY == 0) { 1280 $ay = $y2; 1281 $ax = ($x2 < $ax) ? $ax - $halfWidth : $ax + $halfWidth; 1282 } 1283 elseif ($dX == 0) { 1284 $ay = ($y2 > $ay) ? $ay + $halfHeight : $ay - $halfHeight; 1285 $ax = $x2; 1286 } 1287 else { 1288 $koef = $halfHeight / $dY; 1289 1290 $c = $dX * $koef; 1291 1292 // If point is further than area diagonal, we should use calculations with width instead of height. 1293 if (($halfHeight / $c) > ($halfHeight / $halfWidth)) { 1294 $ay = ($y2 > $ay) ? $ay + $halfHeight : $ay - $halfHeight; 1295 $ax = ($x2 < $ax) ? $ax - $c : $ax + $c; 1296 } 1297 else { 1298 $koef = $halfWidth / $dX; 1299 1300 $c = $dY * $koef; 1301 1302 $ay = ($y2 > $ay) ? $ay + $c : $ay - $c; 1303 $ax = ($x2 < $ax) ? $ax - $halfWidth : $ax + $halfWidth; 1304 } 1305 } 1306 1307 return [$ax, $ay]; 1308} 1309 1310/** 1311 * Get icon id by mapping. 1312 * 1313 * @param array $icon_map 1314 * @param array $host 1315 * @param int $host['inventory_mode'] 1316 * @param array $host['inventory'] 1317 * 1318 * @return int 1319 */ 1320function getIconByMapping(array $icon_map, array $host) { 1321 if ($host['inventory_mode'] == HOST_INVENTORY_DISABLED) { 1322 return $icon_map['default_iconid']; 1323 } 1324 1325 $inventories = getHostInventories(); 1326 1327 foreach ($icon_map['mappings'] as $mapping) { 1328 try { 1329 $expr = new CGlobalRegexp($mapping['expression']); 1330 if ($expr->match($host['inventory'][$inventories[$mapping['inventory_link']]['db_field']])) { 1331 return $mapping['iconid']; 1332 } 1333 } 1334 catch(Exception $e) { 1335 continue; 1336 } 1337 } 1338 1339 return $icon_map['default_iconid']; 1340} 1341 1342/** 1343 * Get parent maps for current map. 1344 * 1345 * @param int $sysmapid 1346 * 1347 * @return array 1348 */ 1349function get_parent_sysmaps($sysmapid) { 1350 $db_sysmaps_elements = DBselect( 1351 'SELECT DISTINCT se.sysmapid'. 1352 ' FROM sysmaps_elements se'. 1353 ' WHERE '.dbConditionInt('se.elementtype', [SYSMAP_ELEMENT_TYPE_MAP]). 1354 ' AND '.dbConditionInt('se.elementid', [$sysmapid]) 1355 ); 1356 1357 $sysmapids = []; 1358 1359 while ($db_sysmaps_element = DBfetch($db_sysmaps_elements)) { 1360 $sysmapids[] = $db_sysmaps_element['sysmapid']; 1361 } 1362 1363 if ($sysmapids) { 1364 $sysmaps = API::Map()->get([ 1365 'output' => ['sysmapid', 'name'], 1366 'sysmapids' => $sysmapids 1367 ]); 1368 1369 CArrayHelper::sort($sysmaps, ['name']); 1370 1371 return $sysmaps; 1372 } 1373 1374 return []; 1375} 1376 1377/** 1378 * Get labels for map elements. 1379 * 1380 * @param array $map Sysmap data array. 1381 * @param array $map_info Array of selements (@see getSelementsInfo). 1382 * 1383 * @return array 1384 */ 1385function getMapLabels($map, $map_info) { 1386 $selements = $map['selements']; 1387 1388 // Collect labels for each map element and apply appropriate values. 1389 $labels = []; 1390 foreach ($selements as $selementId => $selement) { 1391 if ($selement['permission'] < PERM_READ) { 1392 continue; 1393 } 1394 elseif ($selement['label_type'] == MAP_LABEL_TYPE_NOTHING) { 1395 $labels[$selementId] = []; 1396 continue; 1397 } 1398 1399 $label_lines = []; 1400 $msgs = explode("\n", $selement['label']); 1401 foreach ($msgs as $msg) { 1402 $label_lines[] = ['content' => $msg]; 1403 } 1404 1405 $status_lines = []; 1406 $element_info = $map_info[$selementId]; 1407 if (array_key_exists('info', $element_info)) { 1408 foreach (['problem', 'unack', 'maintenance', 'ok', 'status'] as $caption) { 1409 if (array_key_exists($caption, $element_info['info']) 1410 && $element_info['info'][$caption]['msg'] !== '') { 1411 $msgs = explode("\n", $element_info['info'][$caption]['msg']); 1412 foreach ($msgs as $msg) { 1413 $status_lines[] = [ 1414 'content' => $msg, 1415 'attributes' => [ 1416 'fill' => '#'.$element_info['info'][$caption]['color'] 1417 ] 1418 ]; 1419 } 1420 } 1421 } 1422 } 1423 1424 if ($selement['label_type'] == MAP_LABEL_TYPE_IP && $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST) { 1425 $label = array_merge([['content' => $selement['label']]], $status_lines); 1426 } 1427 elseif ($selement['label_type'] == MAP_LABEL_TYPE_STATUS) { 1428 $label = $status_lines; 1429 } 1430 elseif ($selement['label_type'] == MAP_LABEL_TYPE_NAME) { 1431 $label = array_merge([['content' => $element_info['name']]], $status_lines); 1432 } 1433 else { 1434 $label = array_merge($label_lines, $status_lines); 1435 } 1436 1437 $labels[$selementId] = $label; 1438 } 1439 1440 return $labels; 1441} 1442 1443/** 1444 * Get map element highlights (information about elements with marks or background). 1445 * 1446 * @param array $map Sysmap data array. 1447 * @param array $map_info Array of selements (@see getSelementsInfo). 1448 * 1449 * @return array 1450 */ 1451function getMapHighligts(array $map, array $map_info) { 1452 $highlights = []; 1453 1454 foreach ($map['selements'] as $id => $selement) { 1455 if ((($map['highlight'] % 2) != SYSMAP_HIGHLIGHT_ON) || (array_key_exists('elementtype', $selement) 1456 && $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP 1457 && array_key_exists('elementsubtype', $selement) 1458 && $selement['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS)) { 1459 $highlights[$id] = null; 1460 continue; 1461 } 1462 1463 $hl_color = null; 1464 $st_color = null; 1465 $element_info = $map_info[$id]; 1466 1467 switch ($element_info['icon_type']) { 1468 case SYSMAP_ELEMENT_ICON_ON: 1469 $hl_color = getSeverityColor($element_info['priority']); 1470 break; 1471 1472 case SYSMAP_ELEMENT_ICON_MAINTENANCE: 1473 $st_color = 'FF9933'; 1474 break; 1475 1476 case SYSMAP_ELEMENT_ICON_DISABLED: 1477 $st_color = '999999'; 1478 break; 1479 } 1480 1481 if (array_key_exists('elementtype', $selement) 1482 && ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP 1483 || $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) && $hl_color !== null) { 1484 $st_color = null; 1485 } 1486 elseif ($st_color !== null) { 1487 $hl_color = null; 1488 } 1489 1490 $highlights[$id] = [ 1491 'st' => $st_color, 1492 'hl' => $hl_color, 1493 'ack' => ($hl_color !== null && array_key_exists('ack', $element_info) && $element_info['ack']) 1494 ]; 1495 } 1496 1497 return $highlights; 1498} 1499 1500/** 1501 * Get trigger data for all linktriggers. 1502 * 1503 * @param array $sysmap 1504 * @param array $sysmap['show_suppressed'] Whether to show suppressed problems. 1505 * @param array $sysmap['show_unack'] Property specified in sysmap's 'Problem display' field. Used to determine 1506 * whether to show unacknowledged problems only. 1507 * @param array $options Options used to retrieve actions. 1508 * @param int $options['severity_min'] Minimal severity used. 1509 * 1510 * @return array 1511 */ 1512function getMapLinkTriggerInfo($sysmap, $options) { 1513 if (!array_key_exists('severity_min', $options)) { 1514 $options['severity_min'] = TRIGGER_SEVERITY_NOT_CLASSIFIED; 1515 } 1516 1517 $triggerids = []; 1518 1519 foreach ($sysmap['links'] as $link) { 1520 foreach ($link['linktriggers'] as $linktrigger) { 1521 $triggerids[$linktrigger['triggerid']] = true; 1522 } 1523 } 1524 1525 $trigger_options = [ 1526 'output' => ['status', 'value', 'priority'], 1527 'triggerids' => array_keys($triggerids), 1528 'monitored' => true, 1529 'preservekeys' => true 1530 ]; 1531 1532 $problem_options = [ 1533 'show_suppressed' => $sysmap['show_suppressed'], 1534 'acknowledged' => ($sysmap['show_unack'] == EXTACK_OPTION_UNACK) ? false : null 1535 ]; 1536 1537 return getTriggersWithActualSeverity($trigger_options, $problem_options); 1538} 1539 1540/** 1541 * Get map selement label color based on problem and acknowledgement state 1542 * as well as taking custom event status color settings into account. 1543 * 1544 * @throws APIException if the given table does not exist 1545 * 1546 * @param bool $is_problem 1547 * @param bool $is_ack 1548 * 1549 * @return string 1550 */ 1551function getSelementLabelColor($is_problem, $is_ack) { 1552 static $config = null; 1553 static $schema = null; 1554 1555 if ($config === null) { 1556 $config = select_config(); 1557 $schema = DB::getSchema('config'); 1558 } 1559 1560 $ack_unack = $is_ack ? 'ack' : 'unack'; 1561 $ok_problem = $is_problem ? 'problem' : 'ok'; 1562 1563 if ($config['custom_color'] === '1') { 1564 return $config[$ok_problem . '_' . $ack_unack . '_color']; 1565 } 1566 1567 return $schema['fields'][$ok_problem . '_' . $ack_unack . '_color']['default']; 1568} 1569