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 22/** 23 * Convert windows events type constant in to the string representation 24 * 25 * @param int $logtype 26 * @return string 27 */ 28function get_item_logtype_description($logtype) { 29 switch ($logtype) { 30 case ITEM_LOGTYPE_INFORMATION: 31 return _('Information'); 32 case ITEM_LOGTYPE_WARNING: 33 return _('Warning'); 34 case ITEM_LOGTYPE_ERROR: 35 return _('Error'); 36 case ITEM_LOGTYPE_FAILURE_AUDIT: 37 return _('Failure Audit'); 38 case ITEM_LOGTYPE_SUCCESS_AUDIT: 39 return _('Success Audit'); 40 case ITEM_LOGTYPE_CRITICAL: 41 return _('Critical'); 42 case ITEM_LOGTYPE_VERBOSE: 43 return _('Verbose'); 44 default: 45 return _('Unknown'); 46 } 47} 48 49/** 50 * Convert windows events type constant in to the CSS style name 51 * 52 * @param int $logtype 53 * @return string 54 */ 55function get_item_logtype_style($logtype) { 56 switch ($logtype) { 57 case ITEM_LOGTYPE_INFORMATION: 58 case ITEM_LOGTYPE_SUCCESS_AUDIT: 59 case ITEM_LOGTYPE_VERBOSE: 60 return ZBX_STYLE_LOG_INFO_BG; 61 62 case ITEM_LOGTYPE_WARNING: 63 return ZBX_STYLE_LOG_WARNING_BG; 64 65 case ITEM_LOGTYPE_ERROR: 66 case ITEM_LOGTYPE_FAILURE_AUDIT: 67 return ZBX_STYLE_LOG_HIGH_BG; 68 69 case ITEM_LOGTYPE_CRITICAL: 70 return ZBX_STYLE_LOG_DISASTER_BG; 71 72 default: 73 return ZBX_STYLE_LOG_NA_BG; 74 } 75} 76 77/** 78 * Get item type string name by item type number, or array of all item types if null passed. 79 * 80 * @param int|null $type 81 * 82 * @return array|string 83 */ 84function item_type2str($type = null) { 85 $types = [ 86 ITEM_TYPE_ZABBIX => _('Zabbix agent'), 87 ITEM_TYPE_ZABBIX_ACTIVE => _('Zabbix agent (active)'), 88 ITEM_TYPE_SIMPLE => _('Simple check'), 89 ITEM_TYPE_SNMP => _('SNMP agent'), 90 ITEM_TYPE_SNMPTRAP => _('SNMP trap'), 91 ITEM_TYPE_INTERNAL => _('Zabbix internal'), 92 ITEM_TYPE_TRAPPER => _('Zabbix trapper'), 93 ITEM_TYPE_EXTERNAL => _('External check'), 94 ITEM_TYPE_DB_MONITOR => _('Database monitor'), 95 ITEM_TYPE_HTTPAGENT => _('HTTP agent'), 96 ITEM_TYPE_IPMI => _('IPMI agent'), 97 ITEM_TYPE_SSH => _('SSH agent'), 98 ITEM_TYPE_TELNET => _('TELNET agent'), 99 ITEM_TYPE_JMX => _('JMX agent'), 100 ITEM_TYPE_CALCULATED => _('Calculated'), 101 ITEM_TYPE_HTTPTEST => _('Web monitoring'), 102 ITEM_TYPE_DEPENDENT => _('Dependent item'), 103 ITEM_TYPE_SCRIPT => _('Script') 104 ]; 105 106 if ($type === null) { 107 return $types; 108 } 109 110 return array_key_exists($type, $types) ? $types[$type] : _('Unknown'); 111} 112 113/** 114 * Returns human readable an item value type 115 * 116 * @param int $valueType 117 * 118 * @return string 119 */ 120function itemValueTypeString($valueType) { 121 switch ($valueType) { 122 case ITEM_VALUE_TYPE_UINT64: 123 return _('Numeric (unsigned)'); 124 case ITEM_VALUE_TYPE_FLOAT: 125 return _('Numeric (float)'); 126 case ITEM_VALUE_TYPE_STR: 127 return _('Character'); 128 case ITEM_VALUE_TYPE_LOG: 129 return _('Log'); 130 case ITEM_VALUE_TYPE_TEXT: 131 return _('Text'); 132 } 133 return _('Unknown'); 134} 135 136function item_status2str($type = null) { 137 if (is_null($type)) { 138 return [ITEM_STATUS_ACTIVE => _('Enabled'), ITEM_STATUS_DISABLED => _('Disabled')]; 139 } 140 141 return ($type == ITEM_STATUS_ACTIVE) ? _('Enabled') : _('Disabled'); 142} 143 144/** 145 * Returns the names of supported item states. 146 * 147 * If the $state parameter is passed, returns the name of the specific state, otherwise - returns an array of all 148 * supported states. 149 * 150 * @param string $state 151 * 152 * @return array|string 153 */ 154function itemState($state = null) { 155 $states = [ 156 ITEM_STATE_NORMAL => _('Normal'), 157 ITEM_STATE_NOTSUPPORTED => _('Not supported') 158 ]; 159 160 if ($state === null) { 161 return $states; 162 } 163 elseif (isset($states[$state])) { 164 return $states[$state]; 165 } 166 else { 167 return _('Unknown'); 168 } 169} 170 171/** 172 * Returns the text indicating the items status and state. If the $state parameter is not given, only the status of 173 * the item will be taken into account. 174 * 175 * @param int $status 176 * @param int $state 177 * 178 * @return string 179 */ 180function itemIndicator($status, $state = null) { 181 if ($status == ITEM_STATUS_ACTIVE) { 182 return ($state == ITEM_STATE_NOTSUPPORTED) ? _('Not supported') : _('Enabled'); 183 } 184 185 return _('Disabled'); 186} 187 188/** 189 * Returns the CSS class for the items status and state indicator. If the $state parameter is not given, only the status of 190 * the item will be taken into account. 191 * 192 * @param int $status 193 * @param int $state 194 * 195 * @return string 196 */ 197function itemIndicatorStyle($status, $state = null) { 198 if ($status == ITEM_STATUS_ACTIVE) { 199 return ($state == ITEM_STATE_NOTSUPPORTED) ? 200 ZBX_STYLE_GREY : 201 ZBX_STYLE_GREEN; 202 } 203 204 return ZBX_STYLE_RED; 205} 206 207/** 208 * Order items by keep history. 209 * 210 * @param array $items 211 * @param string $items['history'] 212 * @param string $sortorder 213 */ 214function orderItemsByHistory(array &$items, $sortorder){ 215 $simple_interval_parser = new CSimpleIntervalParser(); 216 217 foreach ($items as &$item) { 218 $item['history_sort'] = ($simple_interval_parser->parse($item['history']) == CParser::PARSE_SUCCESS) 219 ? timeUnitToSeconds($item['history']) 220 : $item['history']; 221 } 222 unset($item); 223 224 order_result($items, 'history_sort', $sortorder); 225 226 foreach ($items as &$item) { 227 unset($item['history_sort']); 228 } 229 unset($item); 230} 231 232/** 233 * Order items by keep trends. 234 * 235 * @param array $items 236 * @param int $items['value_type'] 237 * @param string $items['trends'] 238 * @param string $sortorder 239 */ 240function orderItemsByTrends(array &$items, $sortorder){ 241 $simple_interval_parser = new CSimpleIntervalParser(); 242 243 foreach ($items as &$item) { 244 if (in_array($item['value_type'], [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])) { 245 $item['trends_sort'] = ''; 246 } 247 else { 248 $item['trends_sort'] = ($simple_interval_parser->parse($item['trends']) == CParser::PARSE_SUCCESS) 249 ? timeUnitToSeconds($item['trends']) 250 : $item['trends']; 251 } 252 } 253 unset($item); 254 255 order_result($items, 'trends_sort', $sortorder); 256 257 foreach ($items as &$item) { 258 unset($item['trends_sort']); 259 } 260 unset($item); 261} 262 263/** 264 * Order items by update interval. 265 * 266 * @param array $items 267 * @param int $items['type'] 268 * @param string $items['delay'] 269 * @param string $items['key_'] 270 * @param string $sortorder 271 * @param array $options 272 * @param bool $options['usermacros'] 273 * @param bool $options['lldmacros'] 274 */ 275function orderItemsByDelay(array &$items, $sortorder, array $options){ 276 $update_interval_parser = new CUpdateIntervalParser($options); 277 278 foreach ($items as &$item) { 279 if (in_array($item['type'], [ITEM_TYPE_TRAPPER, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT]) 280 || ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE && strncmp($item['key_'], 'mqtt.get', 8) === 0)) { 281 $item['delay_sort'] = ''; 282 } 283 elseif ($update_interval_parser->parse($item['delay']) == CParser::PARSE_SUCCESS) { 284 $item['delay_sort'] = $update_interval_parser->getDelay(); 285 286 if ($item['delay_sort'][0] !== '{') { 287 $item['delay_sort'] = timeUnitToSeconds($item['delay_sort']); 288 } 289 } 290 else { 291 $item['delay_sort'] = $item['delay']; 292 } 293 } 294 unset($item); 295 296 order_result($items, 'delay_sort', $sortorder); 297 298 foreach ($items as &$item) { 299 unset($item['delay_sort']); 300 } 301 unset($item); 302} 303 304/** 305 * Orders items by both status and state. Items are sorted in the following order: enabled, disabled, not supported. 306 * 307 * Keep in sync with orderTriggersByStatus(). 308 * 309 * @param array $items 310 * @param string $sortorder 311 */ 312function orderItemsByStatus(array &$items, $sortorder = ZBX_SORT_UP) { 313 $sort = []; 314 315 foreach ($items as $key => $item) { 316 if ($item['status'] == ITEM_STATUS_ACTIVE) { 317 $sort[$key] = ($item['state'] == ITEM_STATE_NOTSUPPORTED) ? 2 : 0; 318 } 319 else { 320 $sort[$key] = 1; 321 } 322 } 323 324 if ($sortorder == ZBX_SORT_UP) { 325 asort($sort); 326 } 327 else { 328 arsort($sort); 329 } 330 331 $sortedItems = []; 332 foreach ($sort as $key => $val) { 333 $sortedItems[$key] = $items[$key]; 334 } 335 $items = $sortedItems; 336} 337 338/** 339 * Returns the name of the given interface type. Items "status" and "state" properties must be defined. 340 * 341 * @param int $type 342 * 343 * @return null 344 */ 345function interfaceType2str($type) { 346 $interfaceGroupLabels = [ 347 INTERFACE_TYPE_AGENT => _('Agent'), 348 INTERFACE_TYPE_SNMP => _('SNMP'), 349 INTERFACE_TYPE_JMX => _('JMX'), 350 INTERFACE_TYPE_IPMI => _('IPMI') 351 ]; 352 353 return isset($interfaceGroupLabels[$type]) ? $interfaceGroupLabels[$type] : null; 354} 355 356function itemTypeInterface($type = null) { 357 $types = [ 358 ITEM_TYPE_SNMP => INTERFACE_TYPE_SNMP, 359 ITEM_TYPE_SNMPTRAP => INTERFACE_TYPE_SNMP, 360 ITEM_TYPE_IPMI => INTERFACE_TYPE_IPMI, 361 ITEM_TYPE_ZABBIX => INTERFACE_TYPE_AGENT, 362 ITEM_TYPE_SIMPLE => INTERFACE_TYPE_ANY, 363 ITEM_TYPE_EXTERNAL => INTERFACE_TYPE_ANY, 364 ITEM_TYPE_SSH => INTERFACE_TYPE_ANY, 365 ITEM_TYPE_TELNET => INTERFACE_TYPE_ANY, 366 ITEM_TYPE_JMX => INTERFACE_TYPE_JMX, 367 ITEM_TYPE_HTTPAGENT => INTERFACE_TYPE_ANY 368 ]; 369 if (is_null($type)) { 370 return $types; 371 } 372 elseif (isset($types[$type])) { 373 return $types[$type]; 374 } 375 else { 376 return false; 377 } 378} 379 380/** 381 * Copies the given items to the given hosts or templates. 382 * 383 * @param array $src_itemids Items which will be copied to $dst_hostids. 384 * @param array $dst_hostids Hosts and templates to whom add items. 385 * 386 * @return bool 387 */ 388function copyItemsToHosts($src_itemids, $dst_hostids) { 389 $items = API::Item()->get([ 390 'output' => ['type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type', 391 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username', 392 'password', 'publickey', 'privatekey', 'flags', 'description', 'inventory_link', 'jmx_endpoint', 393 'master_itemid', 'timeout', 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects', 394 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format', 'ssl_cert_file', 395 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps', 'parameters' 396 ], 397 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'], 398 'selectTags' => ['tag', 'value'], 399 'itemids' => $src_itemids, 400 'preservekeys' => true 401 ]); 402 403 // Check if dependent items have master items in same selection. If not, those could be web items. 404 $master_itemids = []; 405 406 foreach ($items as $itemid => $item) { 407 if ($item['type'] == ITEM_TYPE_DEPENDENT && !array_key_exists($item['master_itemid'], $items)) { 408 $master_itemids[$item['master_itemid']] = true; 409 } 410 } 411 412 // Find same master items (that includes web items) on destination host. 413 $dst_master_items = []; 414 415 foreach (array_keys($master_itemids) as $master_itemid) { 416 $same_master_item = get_same_item_for_host(['itemid' => $master_itemid], $dst_hostids); 417 418 if ($same_master_item) { 419 $dst_master_items[$master_itemid] = $same_master_item; 420 } 421 } 422 423 $create_order = []; 424 $src_itemid_to_key = []; 425 426 // Calculate dependency level between items so that master items are created before dependent items. 427 foreach ($items as $itemid => $item) { 428 $dependency_level = 0; 429 $master_item = $item; 430 $src_itemid_to_key[$itemid] = $item['key_']; 431 432 while ($master_item['type'] == ITEM_TYPE_DEPENDENT) { 433 if (!array_key_exists($master_item['master_itemid'], $items)) { 434 break; 435 } 436 437 $master_item = $items[$master_item['master_itemid']]; 438 ++$dependency_level; 439 } 440 441 $create_order[$itemid] = $dependency_level; 442 } 443 444 asort($create_order); 445 446 $dstHosts = API::Host()->get([ 447 'output' => ['hostid', 'host', 'status'], 448 'selectInterfaces' => ['interfaceid', 'type', 'main'], 449 'hostids' => $dst_hostids, 450 'preservekeys' => true, 451 'nopermissions' => true, 452 'templated_hosts' => true 453 ]); 454 455 $src_valuemapids = array_column($items, 'valuemapid', 'valuemapid'); 456 unset($src_valuemapids[0]); 457 458 if ($src_valuemapids) { 459 $valuemapids_map = []; 460 $src_valuemaps = API::ValueMap()->get([ 461 'output' => ['name'], 462 'valuemapids' => $src_valuemapids, 463 'preservekeys' => true 464 ]); 465 $dst_valuemaps = API::ValueMap()->get([ 466 'output' => ['name', 'hostid'], 467 'hostids' => $dst_hostids, 468 'filter' => ['name' => array_column($src_valuemaps, 'name')], 469 'preservekeys' => true 470 ]); 471 472 foreach ($src_valuemaps as $src_valuemapid => $src_valuemap) { 473 foreach ($dst_valuemaps as $dst_valuemapid => $dst_valuemap) { 474 if ($dst_valuemap['name'] === $src_valuemap['name']) { 475 $valuemapids_map[$src_valuemapid][$dst_valuemap['hostid']] = $dst_valuemapid; 476 } 477 } 478 } 479 } 480 481 foreach ($dstHosts as $dstHost) { 482 $interfaceids = []; 483 484 foreach ($dstHost['interfaces'] as $interface) { 485 if ($interface['main'] == 1) { 486 $interfaceids[$interface['type']] = $interface['interfaceid']; 487 } 488 } 489 490 $itemkey_to_id = []; 491 $create_items = []; 492 $current_dependency = reset($create_order); 493 494 foreach ($create_order as $itemid => $dependency_level) { 495 if ($current_dependency != $dependency_level) { 496 $current_dependency = $dependency_level; 497 $created_itemids = API::Item()->create($create_items); 498 499 if (!$created_itemids) { 500 return false; 501 } 502 $created_itemids = $created_itemids['itemids']; 503 504 foreach ($create_items as $index => $created_item) { 505 $itemkey_to_id[$created_item['key_']] = $created_itemids[$index]; 506 } 507 508 $create_items = []; 509 } 510 511 $item = $items[$itemid]; 512 513 if ($dstHost['status'] != HOST_STATUS_TEMPLATE) { 514 $type = itemTypeInterface($item['type']); 515 516 if ($type == INTERFACE_TYPE_ANY) { 517 foreach ([INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI] as $itype) { 518 if (isset($interfaceids[$itype])) { 519 $item['interfaceid'] = $interfaceids[$itype]; 520 break; 521 } 522 } 523 } 524 elseif ($type !== false) { 525 if (!isset($interfaceids[$type])) { 526 error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], 527 $item['key_'] 528 )); 529 return false; 530 } 531 $item['interfaceid'] = $interfaceids[$type]; 532 } 533 } 534 unset($item['itemid']); 535 536 if ($item['valuemapid'] != 0) { 537 if (array_key_exists($item['valuemapid'], $valuemapids_map) 538 && array_key_exists($dstHost['hostid'], $valuemapids_map[$item['valuemapid']])) { 539 $item['valuemapid'] = $valuemapids_map[$item['valuemapid']][$dstHost['hostid']]; 540 } 541 else { 542 $item['valuemapid'] = 0; 543 } 544 } 545 546 $item['hostid'] = $dstHost['hostid']; 547 548 if ($item['type'] == ITEM_TYPE_DEPENDENT) { 549 if (array_key_exists($item['master_itemid'], $items)) { 550 $src_item_key = $src_itemid_to_key[$item['master_itemid']]; 551 $item['master_itemid'] = $itemkey_to_id[$src_item_key]; 552 } 553 else { 554 $item_found = false; 555 556 if (array_key_exists($item['master_itemid'], $dst_master_items)) { 557 foreach ($dst_master_items[$item['master_itemid']] as $dst_master_item) { 558 if ($dst_master_item['hostid'] == $dstHost['hostid']) { 559 // A matching item on destination host has been found. 560 561 $item['master_itemid'] = $dst_master_item['itemid']; 562 $item_found = true; 563 } 564 } 565 } 566 567 // Master item does not exist on destination host or has not been selected for copying. 568 if (!$item_found) { 569 error(_s('Item "%1$s" cannot be copied without its master item.', $item['name'])); 570 571 return false; 572 } 573 } 574 } 575 else { 576 unset($item['master_itemid']); 577 } 578 579 $create_items[] = $item; 580 } 581 582 if ($create_items && !API::Item()->create($create_items)) { 583 return false; 584 } 585 } 586 587 return true; 588} 589 590function copyItems($srcHostId, $dstHostId) { 591 $srcItems = API::Item()->get([ 592 'output' => ['type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type', 593 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username', 594 'password', 'publickey', 'privatekey', 'flags', 'description', 'inventory_link', 'jmx_endpoint', 595 'master_itemid', 'templateid', 'url', 'query_fields', 'timeout', 'posts', 'status_codes', 596 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 597 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 598 'allow_traps', 'parameters' 599 ], 600 'selectTags' => ['tag', 'value'], 601 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'], 602 'selectValueMap' => ['name'], 603 'hostids' => $srcHostId, 604 'webitems' => true, 605 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL], 606 'preservekeys' => true 607 ]); 608 $dstHosts = API::Host()->get([ 609 'output' => ['hostid', 'host', 'status'], 610 'selectInterfaces' => ['interfaceid', 'type', 'main'], 611 'hostids' => $dstHostId, 612 'preservekeys' => true, 613 'nopermissions' => true, 614 'templated_hosts' => true 615 ]); 616 $dstHost = reset($dstHosts); 617 $src_valuemap_names = []; 618 $valuemap_map = []; 619 620 foreach ($srcItems as $src_item) { 621 if ($src_item['valuemap'] && $src_item['templateid'] == 0) { 622 $src_valuemap_names[] = $src_item['valuemap']['name']; 623 } 624 } 625 626 if ($src_valuemap_names) { 627 $valuemap_map = array_column(API::ValueMap()->get([ 628 'output' => ['valuemapid', 'name'], 629 'hostids' => $dstHostId, 630 'filter' => ['name' => $src_valuemap_names] 631 ]), 'valuemapid', 'name'); 632 } 633 634 $create_order = []; 635 $src_itemid_to_key = []; 636 foreach ($srcItems as $itemid => $item) { 637 $dependency_level = 0; 638 $master_item = $item; 639 $src_itemid_to_key[$itemid] = $item['key_']; 640 641 while ($master_item['type'] == ITEM_TYPE_DEPENDENT) { 642 $master_item = $srcItems[$master_item['master_itemid']]; 643 ++$dependency_level; 644 } 645 646 $create_order[$itemid] = $dependency_level; 647 } 648 asort($create_order); 649 650 $itemkey_to_id = []; 651 $create_items = []; 652 $current_dependency = reset($create_order); 653 654 foreach ($create_order as $itemid => $dependency_level) { 655 $srcItem = $srcItems[$itemid]; 656 657 // Skip creating web items. Those were created before. 658 if ($srcItem['type'] == ITEM_TYPE_HTTPTEST) { 659 continue; 660 } 661 662 if ($current_dependency != $dependency_level && $create_items) { 663 $current_dependency = $dependency_level; 664 $created_itemids = API::Item()->create($create_items); 665 666 if (!$created_itemids) { 667 return false; 668 } 669 $created_itemids = $created_itemids['itemids']; 670 671 foreach ($create_items as $index => $created_item) { 672 $itemkey_to_id[$created_item['key_']] = $created_itemids[$index]; 673 } 674 675 $create_items = []; 676 } 677 678 if ($srcItem['templateid'] != 0) { 679 $srcItem = get_same_item_for_host($srcItem, $dstHost['hostid']); 680 681 if (!$srcItem) { 682 return false; 683 } 684 $itemkey_to_id[$srcItem['key_']] = $srcItem['itemid']; 685 continue; 686 } 687 else if ($srcItem['valuemapid'] != 0) { 688 $srcItem['valuemapid'] = array_key_exists($srcItem['valuemap']['name'], $valuemap_map) 689 ? $valuemap_map[$srcItem['valuemap']['name']] 690 : 0; 691 } 692 693 if ($dstHost['status'] != HOST_STATUS_TEMPLATE) { 694 // find a matching interface 695 $interface = CItem::findInterfaceForItem($srcItem['type'], $dstHost['interfaces']); 696 if ($interface) { 697 $srcItem['interfaceid'] = $interface['interfaceid']; 698 } 699 // no matching interface found, throw an error 700 elseif ($interface !== false) { 701 error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], $srcItem['key_'])); 702 } 703 } 704 unset($srcItem['itemid']); 705 unset($srcItem['templateid']); 706 $srcItem['hostid'] = $dstHostId; 707 708 if (!$srcItem['preprocessing']) { 709 unset($srcItem['preprocessing']); 710 } 711 712 if ($srcItem['type'] == ITEM_TYPE_DEPENDENT) { 713 if ($srcItems[$srcItem['master_itemid']]['type'] == ITEM_TYPE_HTTPTEST) { 714 // Web items are outside the scope and are created before regular items. 715 $web_item = get_same_item_for_host($srcItems[$srcItem['master_itemid']], $dstHost['hostid']); 716 $srcItem['master_itemid'] = $web_item['itemid']; 717 } 718 else { 719 $src_item_key = $src_itemid_to_key[$srcItem['master_itemid']]; 720 $srcItem['master_itemid'] = $itemkey_to_id[$src_item_key]; 721 } 722 } 723 else { 724 unset($srcItem['master_itemid']); 725 } 726 727 $create_items[] = $srcItem; 728 } 729 730 if ($create_items && !API::Item()->create($create_items)) { 731 return false; 732 } 733 734 return true; 735} 736 737function get_item_by_itemid($itemid) { 738 $db_items = DBfetch(DBselect('SELECT i.* FROM items i WHERE i.itemid='.zbx_dbstr($itemid))); 739 if ($db_items) { 740 return $db_items; 741 } 742 error(_s('No item with itemid="%1$s".', $itemid)); 743 return false; 744} 745 746/** 747 * Description: 748 * Replace items for specified host 749 * 750 * Comments: 751 * $error= true : rise Error if item doesn't exist (error generated), false: special processing (NO error generated) 752 */ 753function get_same_item_for_host($item, $dest_hostids) { 754 $return_array = is_array($dest_hostids); 755 zbx_value2array($dest_hostids); 756 757 if (!is_array($item)) { 758 $itemid = $item; 759 } 760 elseif (isset($item['itemid'])) { 761 $itemid = $item['itemid']; 762 } 763 764 $same_item = null; 765 $same_items = []; 766 767 if (isset($itemid)) { 768 $db_items = DBselect( 769 'SELECT src.*'. 770 ' FROM items src,items dest'. 771 ' WHERE dest.itemid='.zbx_dbstr($itemid). 772 ' AND src.key_=dest.key_'. 773 ' AND '.dbConditionInt('src.hostid', $dest_hostids) 774 ); 775 while ($db_item = DBfetch($db_items)) { 776 if (is_array($item)) { 777 $same_item = $db_item; 778 $same_items[$db_item['itemid']] = $db_item; 779 } 780 else { 781 $same_item = $db_item['itemid']; 782 $same_items[$db_item['itemid']] = $db_item['itemid']; 783 } 784 } 785 if ($return_array) { 786 return $same_items; 787 } 788 else { 789 return $same_item; 790 } 791 } 792 return false; 793} 794 795/** 796 * Get parent templates for each given item. 797 * 798 * @param array $items An array of items. 799 * @param string $items[]['itemid'] ID of an item. 800 * @param string $items[]['templateid'] ID of parent template item. 801 * @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, 802 * ZBX_FLAG_DISCOVERY_PROTOTYPE). 803 * 804 * @return array 805 */ 806function getItemParentTemplates(array $items, $flag) { 807 $parent_itemids = []; 808 $data = [ 809 'links' => [], 810 'templates' => [] 811 ]; 812 813 foreach ($items as $item) { 814 if ($item['templateid'] != 0) { 815 $parent_itemids[$item['templateid']] = true; 816 $data['links'][$item['itemid']] = ['itemid' => $item['templateid']]; 817 } 818 } 819 820 if (!$parent_itemids) { 821 return $data; 822 } 823 824 $all_parent_itemids = []; 825 $hostids = []; 826 if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 827 $lld_ruleids = []; 828 } 829 830 do { 831 if ($flag == ZBX_FLAG_DISCOVERY_RULE) { 832 $db_items = API::DiscoveryRule()->get([ 833 'output' => ['itemid', 'hostid', 'templateid'], 834 'itemids' => array_keys($parent_itemids) 835 ]); 836 } 837 elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 838 $db_items = API::ItemPrototype()->get([ 839 'output' => ['itemid', 'hostid', 'templateid'], 840 'itemids' => array_keys($parent_itemids), 841 'selectDiscoveryRule' => ['itemid'] 842 ]); 843 } 844 // ZBX_FLAG_DISCOVERY_NORMAL 845 else { 846 $db_items = API::Item()->get([ 847 'output' => ['itemid', 'hostid', 'templateid'], 848 'itemids' => array_keys($parent_itemids), 849 'webitems' => true 850 ]); 851 } 852 853 $all_parent_itemids += $parent_itemids; 854 $parent_itemids = []; 855 856 foreach ($db_items as $db_item) { 857 $data['templates'][$db_item['hostid']] = []; 858 $hostids[$db_item['itemid']] = $db_item['hostid']; 859 860 if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 861 $lld_ruleids[$db_item['itemid']] = $db_item['discoveryRule']['itemid']; 862 } 863 864 if ($db_item['templateid'] != 0) { 865 if (!array_key_exists($db_item['templateid'], $all_parent_itemids)) { 866 $parent_itemids[$db_item['templateid']] = true; 867 } 868 869 $data['links'][$db_item['itemid']] = ['itemid' => $db_item['templateid']]; 870 } 871 } 872 } 873 while ($parent_itemids); 874 875 foreach ($data['links'] as &$parent_item) { 876 $parent_item['hostid'] = array_key_exists($parent_item['itemid'], $hostids) 877 ? $hostids[$parent_item['itemid']] 878 : 0; 879 880 if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 881 $parent_item['lld_ruleid'] = array_key_exists($parent_item['itemid'], $lld_ruleids) 882 ? $lld_ruleids[$parent_item['itemid']] 883 : 0; 884 } 885 } 886 unset($parent_item); 887 888 $db_templates = $data['templates'] 889 ? API::Template()->get([ 890 'output' => ['name'], 891 'templateids' => array_keys($data['templates']), 892 'preservekeys' => true 893 ]) 894 : []; 895 896 $rw_templates = $db_templates 897 ? API::Template()->get([ 898 'output' => [], 899 'templateids' => array_keys($db_templates), 900 'editable' => true, 901 'preservekeys' => true 902 ]) 903 : []; 904 905 $data['templates'][0] = []; 906 907 foreach ($data['templates'] as $hostid => &$template) { 908 $template = array_key_exists($hostid, $db_templates) 909 ? [ 910 'hostid' => $hostid, 911 'name' => $db_templates[$hostid]['name'], 912 'permission' => array_key_exists($hostid, $rw_templates) ? PERM_READ_WRITE : PERM_READ 913 ] 914 : [ 915 'hostid' => $hostid, 916 'name' => _('Inaccessible template'), 917 'permission' => PERM_DENY 918 ]; 919 } 920 unset($template); 921 922 return $data; 923} 924 925/** 926 * Returns a template prefix for selected item. 927 * 928 * @param string $itemid 929 * @param array $parent_templates The list of the templates, prepared by getItemParentTemplates() function. 930 * @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, 931 * ZBX_FLAG_DISCOVERY_PROTOTYPE). 932 * @param bool $provide_links If this parameter is false, prefix will not contain links. 933 * 934 * @return array|null 935 */ 936function makeItemTemplatePrefix($itemid, array $parent_templates, $flag, bool $provide_links) { 937 if (!array_key_exists($itemid, $parent_templates['links'])) { 938 return null; 939 } 940 941 while (array_key_exists($parent_templates['links'][$itemid]['itemid'], $parent_templates['links'])) { 942 $itemid = $parent_templates['links'][$itemid]['itemid']; 943 } 944 945 $template = $parent_templates['templates'][$parent_templates['links'][$itemid]['hostid']]; 946 947 if ($provide_links && $template['permission'] == PERM_READ_WRITE) { 948 if ($flag == ZBX_FLAG_DISCOVERY_RULE) { 949 $url = (new CUrl('host_discovery.php')) 950 ->setArgument('filter_set', '1') 951 ->setArgument('filter_hostids', [$template['hostid']]) 952 ->setArgument('context', 'template'); 953 } 954 elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 955 $url = (new CUrl('disc_prototypes.php')) 956 ->setArgument('parent_discoveryid', $parent_templates['links'][$itemid]['lld_ruleid']) 957 ->setArgument('context', 'template'); 958 } 959 // ZBX_FLAG_DISCOVERY_NORMAL 960 else { 961 $url = (new CUrl('items.php')) 962 ->setArgument('filter_set', '1') 963 ->setArgument('filter_hostids', [$template['hostid']]) 964 ->setArgument('context', 'template'); 965 } 966 967 $name = (new CLink(CHtml::encode($template['name']), $url))->addClass(ZBX_STYLE_LINK_ALT); 968 } 969 else { 970 $name = new CSpan(CHtml::encode($template['name'])); 971 } 972 973 return [$name->addClass(ZBX_STYLE_GREY), NAME_DELIMITER]; 974} 975 976/** 977 * Returns a list of item templates. 978 * 979 * @param string $itemid 980 * @param array $parent_templates The list of the templates, prepared by getItemParentTemplates() function. 981 * @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, 982 * ZBX_FLAG_DISCOVERY_PROTOTYPE). 983 * @param bool $provide_links If this parameter is false, prefix will not contain links. 984 * 985 * @return array 986 */ 987function makeItemTemplatesHtml($itemid, array $parent_templates, $flag, bool $provide_links) { 988 $list = []; 989 990 while (array_key_exists($itemid, $parent_templates['links'])) { 991 $template = $parent_templates['templates'][$parent_templates['links'][$itemid]['hostid']]; 992 993 if ($provide_links && $template['permission'] == PERM_READ_WRITE) { 994 if ($flag == ZBX_FLAG_DISCOVERY_RULE) { 995 $url = (new CUrl('host_discovery.php')) 996 ->setArgument('form', 'update') 997 ->setArgument('itemid', $parent_templates['links'][$itemid]['itemid']) 998 ->setArgument('context', 'template'); 999 } 1000 elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 1001 $url = (new CUrl('disc_prototypes.php')) 1002 ->setArgument('form', 'update') 1003 ->setArgument('itemid', $parent_templates['links'][$itemid]['itemid']) 1004 ->setArgument('parent_discoveryid', $parent_templates['links'][$itemid]['lld_ruleid']) 1005 ->setArgument('context', 'template'); 1006 } 1007 // ZBX_FLAG_DISCOVERY_NORMAL 1008 else { 1009 $url = (new CUrl('items.php')) 1010 ->setArgument('form', 'update') 1011 ->setArgument('itemid', $parent_templates['links'][$itemid]['itemid']) 1012 ->setArgument('context', 'template'); 1013 } 1014 1015 $name = new CLink(CHtml::encode($template['name']), $url); 1016 } 1017 else { 1018 $name = (new CSpan(CHtml::encode($template['name'])))->addClass(ZBX_STYLE_GREY); 1019 } 1020 1021 array_unshift($list, $name, ' ⇒ '); 1022 1023 $itemid = $parent_templates['links'][$itemid]['itemid']; 1024 } 1025 1026 if ($list) { 1027 array_pop($list); 1028 } 1029 1030 return $list; 1031} 1032 1033/** 1034 * Collect latest value and actual severity value for each item of Data overview table. 1035 * 1036 * @param array $db_items 1037 * @param array $data 1038 * @param int $show_suppressed 1039 * 1040 * @return array 1041 */ 1042function getDataOverviewCellData(array $db_items, array $data, int $show_suppressed): array { 1043 $history = Manager::History()->getLastValues($db_items, 1, 1044 timeUnitToSeconds(CSettingsHelper::get(CSettingsHelper::HISTORY_PERIOD)) 1045 ); 1046 1047 $db_triggers = getTriggersWithActualSeverity([ 1048 'output' => ['triggerid', 'priority', 'value'], 1049 'selectItems' => ['itemid'], 1050 'itemids' => array_keys($db_items), 1051 'monitored' => true, 1052 'preservekeys' => true 1053 ], ['show_suppressed' => $show_suppressed]); 1054 1055 $itemid_to_triggerids = []; 1056 foreach ($db_triggers as $triggerid => $db_trigger) { 1057 foreach ($db_trigger['items'] as $item) { 1058 if (!array_key_exists($item['itemid'], $itemid_to_triggerids)) { 1059 $itemid_to_triggerids[$item['itemid']] = []; 1060 } 1061 $itemid_to_triggerids[$item['itemid']][] = $triggerid; 1062 } 1063 } 1064 1065 // Apply values and trigger severity to each $data cell. 1066 foreach ($data as &$data_clusters) { 1067 foreach ($data_clusters as &$data_cluster) { 1068 foreach ($data_cluster as &$item) { 1069 $itemid = $item['itemid']; 1070 1071 if (array_key_exists($itemid, $itemid_to_triggerids)) { 1072 $max_priority = -1; 1073 $max_priority_triggerid = -1; 1074 foreach ($itemid_to_triggerids[$itemid] as $triggerid) { 1075 $trigger = $db_triggers[$triggerid]; 1076 1077 // Bump lower priority triggers of value "true" ahead of triggers with value "false". 1078 $multiplier = ($trigger['value'] == TRIGGER_VALUE_TRUE) ? TRIGGER_SEVERITY_COUNT : 0; 1079 if ($trigger['priority'] + $multiplier > $max_priority) { 1080 $max_priority_triggerid = $triggerid; 1081 $max_priority = $trigger['priority'] + $multiplier; 1082 } 1083 } 1084 $trigger = $db_triggers[$max_priority_triggerid]; 1085 } 1086 else { 1087 $trigger = null; 1088 } 1089 1090 $item += [ 1091 'value' => array_key_exists($itemid, $history) ? $history[$itemid][0]['value'] : null, 1092 'trigger' => $trigger 1093 ]; 1094 } 1095 } 1096 } 1097 unset($data_clusters, $data_cluster, $item); 1098 1099 return $data; 1100} 1101 1102/** 1103 * @param array $groupids 1104 * @param array $hostids 1105 * @param array $tags 1106 * @param int $evaltype 1107 * 1108 * @return array 1109 */ 1110function getDataOverviewItems(?array $groupids = null, ?array $hostids = null, ?array $tags, 1111 int $evaltype = TAG_EVAL_TYPE_AND_OR): array { 1112 1113 if ($hostids === null) { 1114 $limit = (int) CSettingsHelper::get(CSettingsHelper::MAX_OVERVIEW_TABLE_SIZE) + 1; 1115 $db_hosts = API::Host()->get([ 1116 'output' => [], 1117 'groupids' => $groupids, 1118 'monitored_hosts' => true, 1119 'with_monitored_items' => true, 1120 'preservekeys' => true, 1121 'limit' => $limit 1122 ]); 1123 $hostids = array_keys($db_hosts); 1124 } 1125 1126 $db_items = API::Item()->get([ 1127 'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type', 'units', 'valuemapid'], 1128 'selectHosts' => ['name'], 1129 'selectValueMap' => ['mappings'], 1130 'hostids' => $hostids, 1131 'groupids' => $groupids, 1132 'evaltype' => $evaltype, 1133 'tags' => $tags, 1134 'monitored' => true, 1135 'webitems' => true, 1136 'preservekeys' => true 1137 ]); 1138 1139 $db_items = CMacrosResolverHelper::resolveItemNames($db_items); 1140 1141 CArrayHelper::sort($db_items, [ 1142 ['field' => 'name_expanded', 'order' => ZBX_SORT_UP], 1143 ['field' => 'itemid', 'order' => ZBX_SORT_UP] 1144 ]); 1145 1146 return [$db_items, $hostids]; 1147} 1148 1149/** 1150 * @param array $groupids 1151 * @param array $hostids 1152 * @param array $filter 1153 * @param array $filter['tags'] 1154 * @param int $filter['evaltype'] 1155 * @param int $filter['show_suppressed'] 1156 * 1157 * @return array 1158 */ 1159function getDataOverview(?array $groupids, ?array $hostids, array $filter): array { 1160 $tags = (array_key_exists('tags', $filter) && $filter['tags']) ? $filter['tags'] : null; 1161 $evaltype = array_key_exists('evaltype', $filter) ? $filter['evaltype'] : TAG_EVAL_TYPE_AND_OR; 1162 1163 [$db_items, $hostids] = getDataOverviewItems($groupids, $hostids, $tags, $evaltype); 1164 1165 $data = []; 1166 $item_counter = []; 1167 $db_hosts = []; 1168 1169 foreach ($db_items as $db_item) { 1170 $item_name = $db_item['name_expanded']; 1171 $host_name = $db_item['hosts'][0]['name']; 1172 $db_hosts[$db_item['hostid']] = $db_item['hosts'][0]; 1173 1174 if (!array_key_exists($host_name, $item_counter)) { 1175 $item_counter[$host_name] = []; 1176 } 1177 1178 if (!array_key_exists($item_name, $item_counter[$host_name])) { 1179 $item_counter[$host_name][$item_name] = 0; 1180 } 1181 1182 $item_place = $item_counter[$host_name][$item_name]; 1183 $item_counter[$host_name][$item_name]++; 1184 1185 $item = [ 1186 'itemid' => $db_item['itemid'], 1187 'value_type' => $db_item['value_type'], 1188 'units' => $db_item['units'], 1189 'valuemap' => $db_item['valuemap'], 1190 'acknowledged' => array_key_exists('acknowledged', $db_item) ? $db_item['acknowledged'] : 0 1191 ]; 1192 1193 if (array_key_exists('triggerid', $db_item)) { 1194 $item += [ 1195 'triggerid' => $db_item['triggerid'], 1196 'severity' => $db_item['priority'], 1197 'tr_value' => $db_item['value'] 1198 ]; 1199 } 1200 else { 1201 $item += [ 1202 'triggerid' => null, 1203 'severity' => null, 1204 'tr_value' => null 1205 ]; 1206 } 1207 1208 $data[$item_name][$item_place][$host_name] = $item; 1209 } 1210 1211 CArrayHelper::sort($db_hosts, [ 1212 ['field' => 'name', 'order' => ZBX_SORT_UP] 1213 ]); 1214 1215 $data_display_limit = (int) CSettingsHelper::get(CSettingsHelper::MAX_OVERVIEW_TABLE_SIZE); 1216 $has_hidden_hosts = (count($db_hosts) > $data_display_limit); 1217 $db_hosts = array_slice($db_hosts, 0, $data_display_limit, true); 1218 1219 $data = array_slice($data, 0, $data_display_limit, true); 1220 $items_left = $data_display_limit; 1221 $itemids = []; 1222 array_walk($data, function (array &$item_columns) use ($data_display_limit, &$itemids, &$items_left) { 1223 if ($items_left != 0) { 1224 $item_columns = array_slice($item_columns, 0, min($data_display_limit, $items_left)); 1225 $items_left -= count($item_columns); 1226 } 1227 else { 1228 $item_columns = null; 1229 return; 1230 } 1231 1232 array_walk($item_columns, function (array &$item_column) use ($data_display_limit, &$itemids) { 1233 $item_column = array_slice($item_column, 0, $data_display_limit, true); 1234 $itemids += array_column($item_column, 'itemid', 'itemid'); 1235 }); 1236 }); 1237 $data = array_filter($data); 1238 1239 $has_hidden_items = (count($db_items) != count($itemids)); 1240 1241 $db_items = array_intersect_key($db_items, $itemids); 1242 $data = getDataOverviewCellData($db_items, $data, $filter['show_suppressed']); 1243 1244 return [$data, $db_hosts, ($has_hidden_items || $has_hidden_hosts)]; 1245} 1246 1247/** 1248 * Prepares interfaces select element with options. 1249 * 1250 * @param array $interfaces 1251 * 1252 * @return CSelect 1253 */ 1254function getInterfaceSelect(array $interfaces): CSelect { 1255 $interface_select = new CSelect('interfaceid'); 1256 1257 /** @var CSelectOption[] $options_by_type */ 1258 $options_by_type = []; 1259 1260 foreach ($interfaces as $interface) { 1261 $option = new CSelectOption($interface['interfaceid'], getHostInterface($interface)); 1262 1263 if ($interface['type'] == INTERFACE_TYPE_SNMP) { 1264 $option->setExtra('description', getSnmpInterfaceDescription($interface)); 1265 } 1266 1267 $options_by_type[$interface['type']][] = $option; 1268 } 1269 1270 foreach ([INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI] as $interface_type) { 1271 if (array_key_exists($interface_type, $options_by_type)) { 1272 $interface_group = new CSelectOptionGroup((string) interfaceType2str($interface_type)); 1273 1274 if ($interface_type == INTERFACE_TYPE_SNMP) { 1275 $interface_group->setOptionTemplate('#{label}'.(new CDiv('#{description}'))->addClass('description')); 1276 } 1277 1278 $interface_group->addOptions($options_by_type[$interface_type]); 1279 1280 $interface_select->addOptionGroup($interface_group); 1281 } 1282 } 1283 1284 return $interface_select; 1285} 1286 1287/** 1288 * Creates SNMP interface description. 1289 * 1290 * @param array $interface 1291 * @param int $interface['details']['version'] Interface SNMP version. 1292 * @param int $interface['details']['contextname'] Interface context name for SNMP version 3. 1293 * @param int $interface['details']['community'] Interface community for SNMP non version 3 interface. 1294 * @param int $interface['details']['securitylevel'] Security level for SNMP version 3 interface. 1295 * @param int $interface['details']['authprotocol'] Authentication protocol for SNMP version 3 interface. 1296 * @param int $interface['details']['privprotocol'] Privacy protocol for SNMP version 3 interface. 1297 * 1298 * @return string 1299 */ 1300function getSnmpInterfaceDescription(array $interface): string { 1301 $snmp_desc = [ 1302 _s('SNMPv%1$d', $interface['details']['version']) 1303 ]; 1304 1305 if ($interface['details']['version'] == SNMP_V3) { 1306 $snmp_desc[] = _('Context name').': '.$interface['details']['contextname']; 1307 1308 if ($interface['details']['securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV) { 1309 [$interface['details']['authprotocol'] => $auth_protocol] = getSnmpV3AuthProtocols(); 1310 [$interface['details']['privprotocol'] => $priv_protocol] = getSnmpV3PrivProtocols(); 1311 1312 $snmp_desc[] = '(priv: '.$priv_protocol.', auth: '.$auth_protocol.')'; 1313 } 1314 elseif ($interface['details']['securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV) { 1315 [$interface['details']['authprotocol'] => $auth_protocol] = getSnmpV3AuthProtocols(); 1316 1317 $snmp_desc[] = '(auth: '.$auth_protocol.')'; 1318 } 1319 1320 } else { 1321 $snmp_desc[] = _x('Community', 'SNMP Community').': '.$interface['details']['community']; 1322 } 1323 1324 return implode(', ', $snmp_desc); 1325} 1326 1327/** 1328 * Named SNMPv3 authentication protocols. 1329 * 1330 * @return array 1331 */ 1332function getSnmpV3AuthProtocols(): array { 1333 return [ 1334 ITEM_SNMPV3_AUTHPROTOCOL_MD5 => 'MD5', 1335 ITEM_SNMPV3_AUTHPROTOCOL_SHA1 => 'SHA1', 1336 ITEM_SNMPV3_AUTHPROTOCOL_SHA224 => 'SHA224', 1337 ITEM_SNMPV3_AUTHPROTOCOL_SHA256 => 'SHA256', 1338 ITEM_SNMPV3_AUTHPROTOCOL_SHA384 => 'SHA384', 1339 ITEM_SNMPV3_AUTHPROTOCOL_SHA512 => 'SHA512' 1340 ]; 1341} 1342 1343/** 1344 * Named SNMPv3 privacy protocols. 1345 * 1346 * @return array 1347 */ 1348function getSnmpV3PrivProtocols(): array { 1349 return [ 1350 ITEM_SNMPV3_PRIVPROTOCOL_DES => 'DES', 1351 ITEM_SNMPV3_PRIVPROTOCOL_AES128 => 'AES128', 1352 ITEM_SNMPV3_PRIVPROTOCOL_AES192 => 'AES192', 1353 ITEM_SNMPV3_PRIVPROTOCOL_AES256 => 'AES256', 1354 ITEM_SNMPV3_PRIVPROTOCOL_AES192C => 'AES192C', 1355 ITEM_SNMPV3_PRIVPROTOCOL_AES256C => 'AES256C' 1356 ]; 1357} 1358 1359/** 1360 * @param array $item 1361 * @param array $trigger 1362 * 1363 * @return CCol 1364 */ 1365function getItemDataOverviewCell(array $item, ?array $trigger = null): CCol { 1366 $ack = null; 1367 $css = ''; 1368 $value = UNKNOWN_VALUE; 1369 1370 if ($trigger && $trigger['value'] == TRIGGER_VALUE_TRUE) { 1371 $css = getSeverityStyle($trigger['priority']); 1372 1373 if ($trigger['problem']['acknowledged'] == 1) { 1374 $ack = [' ', (new CSpan())->addClass(ZBX_STYLE_ICON_ACKN)]; 1375 } 1376 } 1377 1378 if ($item['value'] !== null) { 1379 $value = formatHistoryValue($item['value'], $item); 1380 } 1381 1382 $col = (new CCol([$value, $ack])) 1383 ->addClass($css) 1384 ->addClass(ZBX_STYLE_NOWRAP) 1385 ->setMenuPopup(CMenuPopupHelper::getHistory($item['itemid'])) 1386 ->addClass(ZBX_STYLE_CURSOR_POINTER); 1387 1388 return $col; 1389} 1390 1391/** 1392 * Format history value. 1393 * First format the value according to the configuration of the item. Then apply the value mapping to the formatted (!) 1394 * value. 1395 * 1396 * @param mixed $value 1397 * @param array $item 1398 * @param int $item['value_type'] type of the value: ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64, ... 1399 * @param string $item['units'] units of item 1400 * @param array $item['valuemap'] 1401 * @param bool $trim 1402 * 1403 * @return string 1404 */ 1405function formatHistoryValue($value, array $item, $trim = true) { 1406 $mapping = false; 1407 1408 // format value 1409 if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) { 1410 $value = convertUnits([ 1411 'value' => $value, 1412 'units' => $item['units'] 1413 ]); 1414 } 1415 elseif (!in_array($item['value_type'], [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_TEXT, ITEM_VALUE_TYPE_LOG])) { 1416 $value = _('Unknown value type'); 1417 } 1418 1419 // apply value mapping 1420 switch ($item['value_type']) { 1421 case ITEM_VALUE_TYPE_STR: 1422 $mapping = CValueMapHelper::getMappedValue($item['value_type'], $value, $item['valuemap']); 1423 // break; is not missing here 1424 1425 case ITEM_VALUE_TYPE_TEXT: 1426 case ITEM_VALUE_TYPE_LOG: 1427 if ($trim && mb_strlen($value) > 20) { 1428 $value = mb_substr($value, 0, 20).'...'; 1429 } 1430 1431 if ($mapping !== false) { 1432 $value = $mapping.' ('.$value.')'; 1433 } 1434 1435 break; 1436 1437 default: 1438 $value = CValueMapHelper::applyValueMap($item['value_type'], $value, $item['valuemap']); 1439 } 1440 1441 return $value; 1442} 1443 1444/** 1445 * Retrieves from DB historical data for items and applies functional calculations. 1446 * If fails for some reason, returns UNRESOLVED_MACRO_STRING. 1447 * 1448 * @param array $item 1449 * @param string $item['value_type'] type of item, allowed: ITEM_VALUE_TYPE_FLOAT and ITEM_VALUE_TYPE_UINT64 1450 * @param string $item['itemid'] ID of item 1451 * @param string $item['units'] units of item 1452 * @param string $function function to apply to time period from param, allowed: min, max and avg 1453 * @param string $parameter formatted parameter for function, example: "2w" meaning 2 weeks 1454 * 1455 * @return string item functional value from history 1456 */ 1457function getItemFunctionalValue($item, $function, $parameter) { 1458 // Check whether function is allowed and parameter is specified. 1459 if (!in_array($function, ['min', 'max', 'avg']) || $parameter === '') { 1460 return UNRESOLVED_MACRO_STRING; 1461 } 1462 1463 // Check whether item type is allowed for min, max and avg functions. 1464 if ($item['value_type'] != ITEM_VALUE_TYPE_FLOAT && $item['value_type'] != ITEM_VALUE_TYPE_UINT64) { 1465 return UNRESOLVED_MACRO_STRING; 1466 } 1467 1468 $number_parser = new CNumberParser(['with_suffix' => true]); 1469 1470 if ($number_parser->parse($parameter) != CParser::PARSE_SUCCESS) { 1471 return UNRESOLVED_MACRO_STRING; 1472 } 1473 1474 $parameter = $number_parser->calcValue(); 1475 1476 $time_from = time() - $parameter; 1477 1478 if ($time_from < 0 || $time_from > ZBX_MAX_DATE) { 1479 return UNRESOLVED_MACRO_STRING; 1480 } 1481 1482 $result = Manager::History()->getAggregatedValue($item, $function, $time_from); 1483 1484 if ($result !== null) { 1485 return convertUnits(['value' => $result, 'units' => $item['units']]); 1486 } 1487 else { 1488 return UNRESOLVED_MACRO_STRING; 1489 } 1490} 1491 1492/** 1493 * Check if current time is within the given period. 1494 * 1495 * @param string $period time period format: "wd[-wd2],hh:mm-hh:mm" 1496 * @param int $now current timestamp 1497 * 1498 * @return bool true - within period, false - out of period 1499 */ 1500function checkTimePeriod($period, $now) { 1501 if (sscanf($period, '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) { 1502 if (sscanf($period, '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) { 1503 // delay period format is wrong - skip 1504 return false; 1505 } 1506 $d2 = $d1; 1507 } 1508 1509 $tm = localtime($now, true); 1510 $day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday']; 1511 $sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec']; 1512 1513 $sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1; 1514 $sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2; 1515 1516 return $d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2; 1517} 1518 1519/** 1520 * Get item minimum delay. 1521 * 1522 * @param string $delay 1523 * @param array $flexible_intervals 1524 * 1525 * @return string 1526 */ 1527function getItemDelay($delay, array $flexible_intervals) { 1528 $delay = timeUnitToSeconds($delay); 1529 1530 if ($delay != 0 || !$flexible_intervals) { 1531 return $delay; 1532 } 1533 1534 $min_delay = SEC_PER_YEAR; 1535 1536 foreach ($flexible_intervals as $flexible_interval) { 1537 $flexible_interval_parts = explode('/', $flexible_interval); 1538 $flexible_delay = timeUnitToSeconds($flexible_interval_parts[0]); 1539 1540 $min_delay = min($min_delay, $flexible_delay); 1541 } 1542 1543 return $min_delay; 1544} 1545 1546/** 1547 * Return delay value that is currently applicable 1548 * 1549 * @param int $delay default delay 1550 * @param array $flexible_intervals array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm" 1551 * @param int $now current timestamp 1552 * 1553 * @return int delay for a current timestamp 1554 */ 1555function getCurrentDelay($delay, array $flexible_intervals, $now) { 1556 if (!$flexible_intervals) { 1557 return $delay; 1558 } 1559 1560 $current_delay = -1; 1561 1562 foreach ($flexible_intervals as $flexible_interval) { 1563 list($flexible_delay, $flexible_period) = explode('/', $flexible_interval); 1564 $flexible_delay = (int) $flexible_delay; 1565 1566 if (($current_delay == -1 || $flexible_delay < $current_delay) && checkTimePeriod($flexible_period, $now)) { 1567 $current_delay = $flexible_delay; 1568 } 1569 } 1570 1571 if ($current_delay == -1) { 1572 return $delay; 1573 } 1574 1575 return $current_delay; 1576} 1577 1578/** 1579 * Return time of next flexible interval 1580 * 1581 * @param array $flexible_intervals array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm" 1582 * @param int $now current timestamp 1583 * @param int $next_interval timestamp of a next interval 1584 * 1585 * @return bool false if no flexible intervals defined 1586 */ 1587function getNextDelayInterval(array $flexible_intervals, $now, &$next_interval) { 1588 if (!$flexible_intervals) { 1589 return false; 1590 } 1591 1592 $next = 0; 1593 $tm = localtime($now, true); 1594 $day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday']; 1595 $sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec']; 1596 1597 foreach ($flexible_intervals as $flexible_interval) { 1598 $flexible_interval_parts = explode('/', $flexible_interval); 1599 1600 if (sscanf($flexible_interval_parts[1], '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) { 1601 if (sscanf($flexible_interval_parts[1], '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) { 1602 continue; 1603 } 1604 $d2 = $d1; 1605 } 1606 1607 $sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1; 1608 $sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2; 1609 1610 // current period 1611 if ($d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2) { 1612 if ($next == 0 || $next > $now - $sec + $sec2) { 1613 // the next second after the current interval's upper bound 1614 $next = $now - $sec + $sec2; 1615 } 1616 } 1617 // will be active today 1618 elseif ($d1 <= $day && $d2 >= $day && $sec < $sec1) { 1619 if ($next == 0 || $next > $now - $sec + $sec1) { 1620 $next = $now - $sec + $sec1; 1621 } 1622 } 1623 else { 1624 $nextDay = ($day + 1 <= 7) ? $day + 1 : 1; 1625 1626 // will be active tomorrow 1627 if ($d1 <= $nextDay && $nextDay <= $d2) { 1628 if ($next == 0 || $next > $now - $sec + SEC_PER_DAY + $sec1) { 1629 $next = $now - $sec + SEC_PER_DAY + $sec1; 1630 } 1631 } 1632 // later in the future 1633 else { 1634 $dayDiff = -1; 1635 1636 if ($day < $d1) { 1637 $dayDiff = $d1 - $day; 1638 } 1639 if ($day >= $d2) { 1640 $dayDiff = ($d1 + 7) - $day; 1641 } 1642 if ($d1 <= $day && $day < $d2) { 1643 // should never happen, could not deduce day difference 1644 $dayDiff = -1; 1645 } 1646 if ($dayDiff != -1 && ($next == 0 || $next > $now - $sec + SEC_PER_DAY * $dayDiff + $sec1)) { 1647 $next = $now - $sec + SEC_PER_DAY * $dayDiff + $sec1; 1648 } 1649 } 1650 } 1651 } 1652 1653 if ($next != 0) { 1654 $next_interval = $next; 1655 } 1656 1657 return ($next != 0); 1658} 1659 1660/** 1661 * Calculate nextcheck timestamp for an item using flexible intervals. 1662 * 1663 * the parameter $flexible_intervals is an array if strings that are in the following format: 1664 * 1665 * +------------[;]<----------+ 1666 * | | 1667 * ->+-[d/wd[-wd2],hh:mm-hh:mm]-+ 1668 * 1669 * d - delay (0-n) 1670 * wd, wd2 - day of week (1-7) 1671 * hh - hours (0-24) 1672 * mm - minutes (0-59) 1673 * 1674 * @param int $seed seed value applied to delay to spread item checks over the delay period 1675 * @param string $delay default delay, can be overridden 1676 * @param array $flexible_intervals array of flexible intervals 1677 * @param int $now current timestamp 1678 * 1679 * @return int 1680 */ 1681function calculateItemNextCheck($seed, $delay, $flexible_intervals, $now) { 1682 /* 1683 * Try to find the nearest 'nextcheck' value with condition 'now' < 'nextcheck' < 'now' + SEC_PER_YEAR 1684 * If it is not possible to check the item within a year, fail. 1685 */ 1686 1687 $t = $now; 1688 $tMax = $now + SEC_PER_YEAR; 1689 $try = 0; 1690 1691 while ($t < $tMax) { 1692 // Calculate 'nextcheck' value for the current interval. 1693 $currentDelay = getCurrentDelay($delay, $flexible_intervals, $t); 1694 1695 if ($currentDelay != 0) { 1696 $nextCheck = $currentDelay * floor($t / $currentDelay) + ($seed % $currentDelay); 1697 1698 if ($try == 0) { 1699 while ($nextCheck <= $t) { 1700 $nextCheck += $currentDelay; 1701 } 1702 } 1703 else { 1704 while ($nextCheck < $t) { 1705 $nextCheck += $currentDelay; 1706 } 1707 } 1708 } 1709 else { 1710 $nextCheck = ZBX_JAN_2038; 1711 } 1712 1713 /* 1714 * Is 'nextcheck' < end of the current interval and the end of the current interval 1715 * is the beginning of the next interval - 1. 1716 */ 1717 if (getNextDelayInterval($flexible_intervals, $t, $nextInterval) && $nextCheck >= $nextInterval) { 1718 // 'nextcheck' is beyond the current interval. 1719 $t = $nextInterval; 1720 $try++; 1721 } 1722 else { 1723 break; 1724 } 1725 } 1726 1727 return $nextCheck; 1728} 1729 1730/* 1731 * Description: 1732 * Function returns true if http items exists in the $items array. 1733 * The array should contain a field 'type' 1734 */ 1735function httpItemExists($items) { 1736 foreach ($items as $item) { 1737 if ($item['type'] == ITEM_TYPE_HTTPTEST) { 1738 return true; 1739 } 1740 } 1741 return false; 1742} 1743 1744function getParamFieldNameByType($itemType) { 1745 switch ($itemType) { 1746 case ITEM_TYPE_SCRIPT: 1747 return 'script'; 1748 case ITEM_TYPE_SSH: 1749 case ITEM_TYPE_TELNET: 1750 case ITEM_TYPE_JMX: 1751 return 'params_es'; 1752 case ITEM_TYPE_DB_MONITOR: 1753 return 'params_ap'; 1754 case ITEM_TYPE_CALCULATED: 1755 return 'params_f'; 1756 default: 1757 return 'params'; 1758 } 1759} 1760 1761function getParamFieldLabelByType($itemType) { 1762 switch ($itemType) { 1763 case ITEM_TYPE_SCRIPT: 1764 return _('Script'); 1765 case ITEM_TYPE_SSH: 1766 case ITEM_TYPE_TELNET: 1767 case ITEM_TYPE_JMX: 1768 return _('Executed script'); 1769 case ITEM_TYPE_DB_MONITOR: 1770 return _('SQL query'); 1771 case ITEM_TYPE_CALCULATED: 1772 return _('Formula'); 1773 default: 1774 return 'params'; 1775 } 1776} 1777 1778/** 1779 * Get either one or all item preprocessing types. 1780 * If $grouped set to true, returns group labels. Returns empty string if no specific type is found. 1781 * 1782 * Usage examples: 1783 * - get_preprocessing_types(null, true, [5, 4, 2]) Returns array as defined. 1784 * - get_preprocessing_types(4, true, [5, 4, 2]) Returns string: 'Trim'. 1785 * - get_preprocessing_types(<wrong type>, true, [5, 4, 2]) Returns an empty string: ''. 1786 * - get_preprocessing_types(null, false, [5, 12, 15, 16, 20]) Returns subarrays in one array maintaining index: 1787 * [5] => Regular expression 1788 * [12] => JSONPath 1789 * [15] => Does not match regular expression 1790 * [16] => Check for error in JSON 1791 * [20] => Discard unchanged with heartbeat 1792 * 1793 * @param int $type Item preprocessing type. 1794 * @param bool $grouped Group label flag. If specific type is given, this parameter does not matter. 1795 * @param array $supported_types Array of supported pre-processing types. If none are given, empty array is returned. 1796 * 1797 * @return array|string 1798 */ 1799function get_preprocessing_types($type = null, $grouped = true, array $supported_types = []) { 1800 $types = [ 1801 ZBX_PREPROC_REGSUB => [ 1802 'group' => _('Text'), 1803 'name' => _('Regular expression') 1804 ], 1805 ZBX_PREPROC_STR_REPLACE => [ 1806 'group' => _('Text'), 1807 'name' => _('Replace') 1808 ], 1809 ZBX_PREPROC_TRIM => [ 1810 'group' => _('Text'), 1811 'name' => _('Trim') 1812 ], 1813 ZBX_PREPROC_RTRIM => [ 1814 'group' => _('Text'), 1815 'name' => _('Right trim') 1816 ], 1817 ZBX_PREPROC_LTRIM => [ 1818 'group' => _('Text'), 1819 'name' => _('Left trim') 1820 ], 1821 ZBX_PREPROC_XPATH => [ 1822 'group' => _('Structured data'), 1823 'name' => _('XML XPath') 1824 ], 1825 ZBX_PREPROC_JSONPATH => [ 1826 'group' => _('Structured data'), 1827 'name' => _('JSONPath') 1828 ], 1829 ZBX_PREPROC_CSV_TO_JSON => [ 1830 'group' => _('Structured data'), 1831 'name' => _('CSV to JSON') 1832 ], 1833 ZBX_PREPROC_XML_TO_JSON => [ 1834 'group' => _('Structured data'), 1835 'name' => _('XML to JSON') 1836 ], 1837 ZBX_PREPROC_MULTIPLIER => [ 1838 'group' => _('Arithmetic'), 1839 'name' => _('Custom multiplier') 1840 ], 1841 ZBX_PREPROC_DELTA_VALUE => [ 1842 'group' => _x('Change', 'noun'), 1843 'name' => _('Simple change') 1844 ], 1845 ZBX_PREPROC_DELTA_SPEED => [ 1846 'group' => _x('Change', 'noun'), 1847 'name' => _('Change per second') 1848 ], 1849 ZBX_PREPROC_BOOL2DEC => [ 1850 'group' => _('Numeral systems'), 1851 'name' => _('Boolean to decimal') 1852 ], 1853 ZBX_PREPROC_OCT2DEC => [ 1854 'group' => _('Numeral systems'), 1855 'name' => _('Octal to decimal') 1856 ], 1857 ZBX_PREPROC_HEX2DEC => [ 1858 'group' => _('Numeral systems'), 1859 'name' => _('Hexadecimal to decimal') 1860 ], 1861 ZBX_PREPROC_SCRIPT => [ 1862 'group' => _('Custom scripts'), 1863 'name' => _('JavaScript') 1864 ], 1865 ZBX_PREPROC_VALIDATE_RANGE => [ 1866 'group' => _('Validation'), 1867 'name' => _('In range') 1868 ], 1869 ZBX_PREPROC_VALIDATE_REGEX => [ 1870 'group' => _('Validation'), 1871 'name' => _('Matches regular expression') 1872 ], 1873 ZBX_PREPROC_VALIDATE_NOT_REGEX => [ 1874 'group' => _('Validation'), 1875 'name' => _('Does not match regular expression') 1876 ], 1877 ZBX_PREPROC_ERROR_FIELD_JSON => [ 1878 'group' => _('Validation'), 1879 'name' => _('Check for error in JSON') 1880 ], 1881 ZBX_PREPROC_ERROR_FIELD_XML => [ 1882 'group' => _('Validation'), 1883 'name' => _('Check for error in XML') 1884 ], 1885 ZBX_PREPROC_ERROR_FIELD_REGEX => [ 1886 'group' => _('Validation'), 1887 'name' => _('Check for error using regular expression') 1888 ], 1889 ZBX_PREPROC_VALIDATE_NOT_SUPPORTED => [ 1890 'group' => _('Validation'), 1891 'name' => _('Check for not supported value') 1892 ], 1893 ZBX_PREPROC_THROTTLE_VALUE => [ 1894 'group' => _('Throttling'), 1895 'name' => _('Discard unchanged') 1896 ], 1897 ZBX_PREPROC_THROTTLE_TIMED_VALUE => [ 1898 'group' => _('Throttling'), 1899 'name' => _('Discard unchanged with heartbeat') 1900 ], 1901 ZBX_PREPROC_PROMETHEUS_PATTERN => [ 1902 'group' => _('Prometheus'), 1903 'name' => _('Prometheus pattern') 1904 ], 1905 ZBX_PREPROC_PROMETHEUS_TO_JSON => [ 1906 'group' => _('Prometheus'), 1907 'name' => _('Prometheus to JSON') 1908 ] 1909 ]; 1910 1911 $filtered_types = []; 1912 1913 foreach ($types as $_type => $data) { 1914 if (in_array($_type, $supported_types)) { 1915 $filtered_types[$data['group']][$_type] = $data['name']; 1916 } 1917 } 1918 1919 $groups = []; 1920 1921 foreach ($filtered_types as $label => $types) { 1922 $groups[] = [ 1923 'label' => $label, 1924 'types' => $types 1925 ]; 1926 } 1927 1928 if ($type !== null) { 1929 foreach ($groups as $group) { 1930 if (array_key_exists($type, $group['types'])) { 1931 return $group['types'][$type]; 1932 } 1933 } 1934 1935 return ''; 1936 } 1937 elseif ($grouped) { 1938 return $groups; 1939 } 1940 else { 1941 $types = []; 1942 1943 foreach ($groups as $group) { 1944 $types += $group['types']; 1945 } 1946 1947 return $types; 1948 } 1949} 1950 1951/* 1952 * Quoting $param if it contain special characters. 1953 * 1954 * @param string $param 1955 * @param bool $forced 1956 * 1957 * @return string 1958 */ 1959function quoteItemKeyParam($param, $forced = false) { 1960 if (!$forced) { 1961 if (!isset($param[0]) || ($param[0] != '"' && false === strpbrk($param, ',]'))) { 1962 return $param; 1963 } 1964 } 1965 1966 return '"'.str_replace('"', '\\"', $param).'"'; 1967} 1968 1969/** 1970 * Expands item name and for dependent item master item name. 1971 * 1972 * @param array $items Array of items. 1973 * @param string $data_source 'items' or 'itemprototypes'. 1974 * 1975 * @return array 1976 */ 1977function expandItemNamesWithMasterItems($items, $data_source) { 1978 $items = CMacrosResolverHelper::resolveItemNames($items); 1979 $itemids = []; 1980 $master_itemids = []; 1981 1982 foreach ($items as $item_index => &$item) { 1983 if ($item['type'] == ITEM_TYPE_DEPENDENT) { 1984 $master_itemids[$item['master_itemid']] = true; 1985 } 1986 1987 // The "source" is required to tell the frontend where the link should point at - item or item prototype. 1988 $item['source'] = $data_source; 1989 $itemids[$item_index] = $item['itemid']; 1990 } 1991 unset($item); 1992 1993 $master_itemids = array_diff(array_keys($master_itemids), $itemids); 1994 1995 if ($master_itemids) { 1996 $options = [ 1997 'output' => ['itemid', 'type', 'hostid', 'name', 'key_'], 1998 'itemids' => $master_itemids, 1999 'editable' => true, 2000 'preservekeys' => true 2001 ]; 2002 $master_items = API::Item()->get($options + ['webitems' => true]); 2003 2004 foreach ($master_items as &$master_item) { 2005 $master_item['source'] = 'items'; 2006 } 2007 unset($master_item); 2008 2009 $master_item_prototypes = API::ItemPrototype()->get($options); 2010 2011 foreach ($master_item_prototypes as &$master_item_prototype) { 2012 $master_item_prototype['source'] = 'itemprototypes'; 2013 } 2014 unset($master_item_prototype); 2015 2016 $master_items = CMacrosResolverHelper::resolveItemNames($master_items + $master_item_prototypes); 2017 } 2018 2019 foreach ($items as &$item) { 2020 if ($item['type'] == ITEM_TYPE_DEPENDENT) { 2021 $master_itemid = $item['master_itemid']; 2022 $items_index = array_search($master_itemid, $itemids); 2023 2024 $item['master_item'] = [ 2025 'itemid' => $master_itemid, 2026 'name_expanded' => ($items_index === false) 2027 ? $master_items[$master_itemid]['name_expanded'] 2028 : $items[$items_index]['name_expanded'], 2029 'type' => ($items_index === false) 2030 ? $master_items[$master_itemid]['type'] 2031 : $items[$items_index]['type'], 2032 'source' => ($items_index === false) 2033 ? $master_items[$master_itemid]['source'] 2034 : $items[$items_index]['source'] 2035 ]; 2036 } 2037 } 2038 unset($item); 2039 2040 return $items; 2041} 2042 2043/** 2044 * Returns an array of allowed item types for "Check now" functionality. 2045 * 2046 * @return array 2047 */ 2048function checkNowAllowedTypes() { 2049 return [ 2050 ITEM_TYPE_ZABBIX, 2051 ITEM_TYPE_SIMPLE, 2052 ITEM_TYPE_INTERNAL, 2053 ITEM_TYPE_EXTERNAL, 2054 ITEM_TYPE_DB_MONITOR, 2055 ITEM_TYPE_IPMI, 2056 ITEM_TYPE_SSH, 2057 ITEM_TYPE_TELNET, 2058 ITEM_TYPE_CALCULATED, 2059 ITEM_TYPE_JMX, 2060 ITEM_TYPE_HTTPAGENT, 2061 ITEM_TYPE_SCRIPT, 2062 ITEM_TYPE_SNMP 2063 ]; 2064} 2065 2066/** 2067 * Validates update interval for items, item prototypes and low-level discovery rules and their overrides. 2068 * 2069 * @param CUpdateIntervalParser $parser [IN] Parser used for delay validation. 2070 * @param string $value [IN] Update interval to parse and validate. 2071 * @param string $field_name [IN] Frontend or API field name in the error 2072 * @param string $error [OUT] Returned error string if delay validation fails. 2073 * 2074 * @return bool 2075 */ 2076function validateDelay(CUpdateIntervalParser $parser, $field_name, $value, &$error) { 2077 if ($parser->parse($value) != CParser::PARSE_SUCCESS) { 2078 $error = _s('Incorrect value for field "%1$s": %2$s.', $field_name, _('invalid delay')); 2079 2080 return false; 2081 } 2082 2083 $delay = $parser->getDelay(); 2084 2085 if ($delay[0] !== '{') { 2086 $delay_sec = timeUnitToSeconds($delay); 2087 $intervals = $parser->getIntervals(); 2088 $flexible_intervals = $parser->getIntervals(ITEM_DELAY_FLEXIBLE); 2089 $has_scheduling_intervals = (bool) $parser->getIntervals(ITEM_DELAY_SCHEDULING); 2090 $has_macros = false; 2091 2092 foreach ($intervals as $interval) { 2093 if (strpos($interval['interval'], '{') !== false) { 2094 $has_macros = true; 2095 break; 2096 } 2097 } 2098 2099 // If delay is 0, there must be at least one either flexible or scheduling interval. 2100 if ($delay_sec == 0 && !$intervals) { 2101 $error = _('Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'); 2102 2103 return false; 2104 } 2105 elseif ($delay_sec < 0 || $delay_sec > SEC_PER_DAY) { 2106 $error = _('Item will not be refreshed. Update interval should be between 1s and 1d. Also Scheduled/Flexible intervals can be used.'); 2107 2108 return false; 2109 } 2110 2111 // If there are scheduling intervals or intervals with macros, skip the next check calculation. 2112 if (!$has_macros && !$has_scheduling_intervals && $flexible_intervals 2113 && calculateItemNextCheck(0, $delay_sec, $flexible_intervals, time()) == ZBX_JAN_2038) { 2114 $error = _('Item will not be refreshed. Please enter a correct update interval.'); 2115 2116 return false; 2117 } 2118 } 2119 2120 return true; 2121} 2122