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