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 * @return array|string 82 */ 83function item_type2str($type = null) { 84 $types = [ 85 ITEM_TYPE_ZABBIX => _('Zabbix agent'), 86 ITEM_TYPE_ZABBIX_ACTIVE => _('Zabbix agent (active)'), 87 ITEM_TYPE_SIMPLE => _('Simple check'), 88 ITEM_TYPE_SNMPV1 => _('SNMPv1 agent'), 89 ITEM_TYPE_SNMPV2C => _('SNMPv2 agent'), 90 ITEM_TYPE_SNMPV3 => _('SNMPv3 agent'), 91 ITEM_TYPE_SNMPTRAP => _('SNMP trap'), 92 ITEM_TYPE_INTERNAL => _('Zabbix internal'), 93 ITEM_TYPE_TRAPPER => _('Zabbix trapper'), 94 ITEM_TYPE_AGGREGATE => _('Zabbix aggregate'), 95 ITEM_TYPE_EXTERNAL => _('External check'), 96 ITEM_TYPE_DB_MONITOR => _('Database monitor'), 97 ITEM_TYPE_HTTPAGENT => _('HTTP agent'), 98 ITEM_TYPE_IPMI => _('IPMI agent'), 99 ITEM_TYPE_SSH => _('SSH agent'), 100 ITEM_TYPE_TELNET => _('TELNET agent'), 101 ITEM_TYPE_JMX => _('JMX agent'), 102 ITEM_TYPE_CALCULATED => _('Calculated'), 103 ITEM_TYPE_HTTPTEST => _('Web monitoring'), 104 ITEM_TYPE_DEPENDENT => _('Dependent item') 105 ]; 106 if (is_null($type)) { 107 return $types; 108 } 109 elseif (isset($types[$type])) { 110 return $types[$type]; 111 } 112 else { 113 return _('Unknown'); 114 } 115} 116 117/** 118 * Returns human readable an item value type 119 * 120 * @param int $valueType 121 * 122 * @return string 123 */ 124function itemValueTypeString($valueType) { 125 switch ($valueType) { 126 case ITEM_VALUE_TYPE_UINT64: 127 return _('Numeric (unsigned)'); 128 case ITEM_VALUE_TYPE_FLOAT: 129 return _('Numeric (float)'); 130 case ITEM_VALUE_TYPE_STR: 131 return _('Character'); 132 case ITEM_VALUE_TYPE_LOG: 133 return _('Log'); 134 case ITEM_VALUE_TYPE_TEXT: 135 return _('Text'); 136 } 137 return _('Unknown'); 138} 139 140function item_status2str($type = null) { 141 if (is_null($type)) { 142 return [ITEM_STATUS_ACTIVE => _('Enabled'), ITEM_STATUS_DISABLED => _('Disabled')]; 143 } 144 145 return ($type == ITEM_STATUS_ACTIVE) ? _('Enabled') : _('Disabled'); 146} 147 148/** 149 * Returns the names of supported item states. 150 * 151 * If the $state parameter is passed, returns the name of the specific state, otherwise - returns an array of all 152 * supported states. 153 * 154 * @param string $state 155 * 156 * @return array|string 157 */ 158function itemState($state = null) { 159 $states = [ 160 ITEM_STATE_NORMAL => _('Normal'), 161 ITEM_STATE_NOTSUPPORTED => _('Not supported') 162 ]; 163 164 if ($state === null) { 165 return $states; 166 } 167 elseif (isset($states[$state])) { 168 return $states[$state]; 169 } 170 else { 171 return _('Unknown'); 172 } 173} 174 175/** 176 * Returns the text indicating the items status and state. If the $state parameter is not given, only the status of 177 * the item will be taken into account. 178 * 179 * @param int $status 180 * @param int $state 181 * 182 * @return string 183 */ 184function itemIndicator($status, $state = null) { 185 if ($status == ITEM_STATUS_ACTIVE) { 186 return ($state == ITEM_STATE_NOTSUPPORTED) ? _('Not supported') : _('Enabled'); 187 } 188 189 return _('Disabled'); 190} 191 192/** 193 * Returns the CSS class for the items status and state indicator. If the $state parameter is not given, only the status of 194 * the item will be taken into account. 195 * 196 * @param int $status 197 * @param int $state 198 * 199 * @return string 200 */ 201function itemIndicatorStyle($status, $state = null) { 202 if ($status == ITEM_STATUS_ACTIVE) { 203 return ($state == ITEM_STATE_NOTSUPPORTED) ? 204 ZBX_STYLE_GREY : 205 ZBX_STYLE_GREEN; 206 } 207 208 return ZBX_STYLE_RED; 209} 210 211/** 212 * Order items by keep history. 213 * 214 * @param array $items 215 * @param string $items['history'] 216 * @param string $sortorder 217 */ 218function orderItemsByHistory(array &$items, $sortorder){ 219 $simple_interval_parser = new CSimpleIntervalParser(); 220 221 foreach ($items as &$item) { 222 $item['history_sort'] = ($simple_interval_parser->parse($item['history']) == CParser::PARSE_SUCCESS) 223 ? timeUnitToSeconds($item['history']) 224 : $item['history']; 225 } 226 unset($item); 227 228 order_result($items, 'history_sort', $sortorder); 229 230 foreach ($items as &$item) { 231 unset($item['history_sort']); 232 } 233 unset($item); 234} 235 236/** 237 * Order items by keep trends. 238 * 239 * @param array $items 240 * @param int $items['value_type'] 241 * @param string $items['trends'] 242 * @param string $sortorder 243 */ 244function orderItemsByTrends(array &$items, $sortorder){ 245 $simple_interval_parser = new CSimpleIntervalParser(); 246 247 foreach ($items as &$item) { 248 if (in_array($item['value_type'], [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])) { 249 $item['trends_sort'] = ''; 250 } 251 else { 252 $item['trends_sort'] = ($simple_interval_parser->parse($item['trends']) == CParser::PARSE_SUCCESS) 253 ? timeUnitToSeconds($item['trends']) 254 : $item['trends']; 255 } 256 } 257 unset($item); 258 259 order_result($items, 'trends_sort', $sortorder); 260 261 foreach ($items as &$item) { 262 unset($item['trends_sort']); 263 } 264 unset($item); 265} 266 267/** 268 * Order items by update interval. 269 * 270 * @param array $items 271 * @param int $items['type'] 272 * @param string $items['delay'] 273 * @param string $sortorder 274 * @param array $options 275 * @param bool $options['usermacros'] 276 * @param bool $options['lldmacros'] 277 */ 278function orderItemsByDelay(array &$items, $sortorder, array $options){ 279 $update_interval_parser = new CUpdateIntervalParser($options); 280 281 foreach ($items as &$item) { 282 if (in_array($item['type'], [ITEM_TYPE_TRAPPER, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT])) { 283 $item['delay_sort'] = ''; 284 } 285 elseif ($update_interval_parser->parse($item['delay']) == CParser::PARSE_SUCCESS) { 286 $item['delay_sort'] = $update_interval_parser->getDelay(); 287 288 if ($item['delay_sort'][0] !== '{') { 289 $item['delay_sort'] = timeUnitToSeconds($item['delay_sort']); 290 } 291 } 292 else { 293 $item['delay_sort'] = $item['delay']; 294 } 295 } 296 unset($item); 297 298 order_result($items, 'delay_sort', $sortorder); 299 300 foreach ($items as &$item) { 301 unset($item['delay_sort']); 302 } 303 unset($item); 304} 305 306/** 307 * Orders items by both status and state. Items are sorted in the following order: enabled, disabled, not supported. 308 * 309 * Keep in sync with orderTriggersByStatus(). 310 * 311 * @param array $items 312 * @param string $sortorder 313 */ 314function orderItemsByStatus(array &$items, $sortorder = ZBX_SORT_UP) { 315 $sort = []; 316 317 foreach ($items as $key => $item) { 318 if ($item['status'] == ITEM_STATUS_ACTIVE) { 319 $sort[$key] = ($item['state'] == ITEM_STATE_NOTSUPPORTED) ? 2 : 0; 320 } 321 else { 322 $sort[$key] = 1; 323 } 324 } 325 326 if ($sortorder == ZBX_SORT_UP) { 327 asort($sort); 328 } 329 else { 330 arsort($sort); 331 } 332 333 $sortedItems = []; 334 foreach ($sort as $key => $val) { 335 $sortedItems[$key] = $items[$key]; 336 } 337 $items = $sortedItems; 338} 339 340/** 341 * Returns the name of the given interface type. Items "status" and "state" properties must be defined. 342 * 343 * @param int $type 344 * 345 * @return null 346 */ 347function interfaceType2str($type) { 348 $interfaceGroupLabels = [ 349 INTERFACE_TYPE_AGENT => _('Agent'), 350 INTERFACE_TYPE_SNMP => _('SNMP'), 351 INTERFACE_TYPE_JMX => _('JMX'), 352 INTERFACE_TYPE_IPMI => _('IPMI') 353 ]; 354 355 return isset($interfaceGroupLabels[$type]) ? $interfaceGroupLabels[$type] : null; 356} 357 358function itemTypeInterface($type = null) { 359 $types = [ 360 ITEM_TYPE_SNMPV1 => INTERFACE_TYPE_SNMP, 361 ITEM_TYPE_SNMPV2C => INTERFACE_TYPE_SNMP, 362 ITEM_TYPE_SNMPV3 => INTERFACE_TYPE_SNMP, 363 ITEM_TYPE_SNMPTRAP => INTERFACE_TYPE_SNMP, 364 ITEM_TYPE_IPMI => INTERFACE_TYPE_IPMI, 365 ITEM_TYPE_ZABBIX => INTERFACE_TYPE_AGENT, 366 ITEM_TYPE_SIMPLE => INTERFACE_TYPE_ANY, 367 ITEM_TYPE_EXTERNAL => INTERFACE_TYPE_ANY, 368 ITEM_TYPE_SSH => INTERFACE_TYPE_ANY, 369 ITEM_TYPE_TELNET => INTERFACE_TYPE_ANY, 370 ITEM_TYPE_JMX => INTERFACE_TYPE_JMX, 371 ITEM_TYPE_HTTPAGENT => INTERFACE_TYPE_ANY 372 ]; 373 if (is_null($type)) { 374 return $types; 375 } 376 elseif (isset($types[$type])) { 377 return $types[$type]; 378 } 379 else { 380 return false; 381 } 382} 383 384/** 385 * Copies the given items to the given hosts or templates. 386 * 387 * @param array $src_itemids Items which will be copied to $dst_hostids. 388 * @param array $dst_hostids Hosts and templates to whom add items. 389 * 390 * @return bool 391 */ 392function copyItemsToHosts($src_itemids, $dst_hostids) { 393 $items = API::Item()->get([ 394 'output' => ['type', 'snmp_community', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 395 'value_type', 'trapper_hosts', 'units', 'snmpv3_contextname', 'snmpv3_securityname', 'snmpv3_securitylevel', 396 'snmpv3_authprotocol', 'snmpv3_authpassphrase', 'snmpv3_privprotocol', 'snmpv3_privpassphrase', 397 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username', 'password', 'publickey', 398 'privatekey', 'flags', 'port', 'description', 'inventory_link', 'jmx_endpoint', 'master_itemid', 'timeout', 399 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects', 'post_type', 'http_proxy', 'headers', 400 'retrieve_mode', 'request_method', 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 401 'verify_peer', 'verify_host', 'allow_traps' 402 ], 403 'selectApplications' => ['applicationid'], 404 'selectPreprocessing' => ['type', 'params'], 405 'itemids' => $src_itemids, 406 'preservekeys' => true 407 ]); 408 409 // Check if dependent items have master items in same selection. If not, those could be web items. 410 $master_itemids = []; 411 412 foreach ($items as $itemid => $item) { 413 if ($item['type'] == ITEM_TYPE_DEPENDENT && !array_key_exists($item['master_itemid'], $items)) { 414 $master_itemids[$item['master_itemid']] = true; 415 } 416 } 417 418 // Find same master items (that includes web items) on destination host. 419 $dst_master_items = []; 420 421 foreach (array_keys($master_itemids) as $master_itemid) { 422 $same_master_item = get_same_item_for_host(['itemid' => $master_itemid], $dst_hostids); 423 424 if ($same_master_item) { 425 $dst_master_items[$master_itemid] = $same_master_item; 426 } 427 } 428 429 $create_order = []; 430 $src_itemid_to_key = []; 431 432 // Calculate dependency level between items so that master items are created before dependent items. 433 foreach ($items as $itemid => $item) { 434 $dependency_level = 0; 435 $master_item = $item; 436 $src_itemid_to_key[$itemid] = $item['key_']; 437 438 while ($master_item['type'] == ITEM_TYPE_DEPENDENT) { 439 if (!array_key_exists($master_item['master_itemid'], $items)) { 440 break; 441 } 442 443 $master_item = $items[$master_item['master_itemid']]; 444 ++$dependency_level; 445 } 446 447 $create_order[$itemid] = $dependency_level; 448 } 449 450 asort($create_order); 451 452 $dstHosts = API::Host()->get([ 453 'output' => ['hostid', 'host', 'status'], 454 'selectInterfaces' => ['interfaceid', 'type', 'main'], 455 'hostids' => $dst_hostids, 456 'preservekeys' => true, 457 'nopermissions' => true, 458 'templated_hosts' => true 459 ]); 460 461 foreach ($dstHosts as $dstHost) { 462 $interfaceids = []; 463 464 foreach ($dstHost['interfaces'] as $interface) { 465 if ($interface['main'] == 1) { 466 $interfaceids[$interface['type']] = $interface['interfaceid']; 467 } 468 } 469 470 $itemkey_to_id = []; 471 $create_items = []; 472 $current_dependency = reset($create_order); 473 474 foreach ($create_order as $itemid => $dependency_level) { 475 if ($current_dependency != $dependency_level) { 476 $current_dependency = $dependency_level; 477 $created_itemids = API::Item()->create($create_items); 478 479 if (!$created_itemids) { 480 return false; 481 } 482 $created_itemids = $created_itemids['itemids']; 483 484 foreach ($create_items as $index => $created_item) { 485 $itemkey_to_id[$created_item['key_']] = $created_itemids[$index]; 486 } 487 488 $create_items = []; 489 } 490 491 $item = $items[$itemid]; 492 493 if ($dstHost['status'] != HOST_STATUS_TEMPLATE) { 494 $type = itemTypeInterface($item['type']); 495 496 if ($type == INTERFACE_TYPE_ANY) { 497 foreach ([INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI] as $itype) { 498 if (isset($interfaceids[$itype])) { 499 $item['interfaceid'] = $interfaceids[$itype]; 500 break; 501 } 502 } 503 } 504 elseif ($type !== false) { 505 if (!isset($interfaceids[$type])) { 506 error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], 507 $item['key_'] 508 )); 509 return false; 510 } 511 $item['interfaceid'] = $interfaceids[$type]; 512 } 513 } 514 unset($item['itemid']); 515 $item['hostid'] = $dstHost['hostid']; 516 $item['applications'] = get_same_applications_for_host( 517 zbx_objectValues($item['applications'], 'applicationid'), 518 $dstHost['hostid'] 519 ); 520 521 if ($item['type'] == ITEM_TYPE_DEPENDENT) { 522 if (array_key_exists($item['master_itemid'], $items)) { 523 $src_item_key = $src_itemid_to_key[$item['master_itemid']]; 524 $item['master_itemid'] = $itemkey_to_id[$src_item_key]; 525 } 526 else { 527 $item_found = false; 528 529 if (array_key_exists($item['master_itemid'], $dst_master_items)) { 530 foreach ($dst_master_items[$item['master_itemid']] as $dst_master_item) { 531 if ($dst_master_item['hostid'] == $dstHost['hostid']) { 532 // A matching item on destination host has been found. 533 534 $item['master_itemid'] = $dst_master_item['itemid']; 535 $item_found = true; 536 } 537 } 538 } 539 540 // Master item does not exist on destination host or has not been selected for copying. 541 if (!$item_found) { 542 error(_s('Item "%1$s" has master item and cannot be copied.', $item['name'])); 543 544 return false; 545 } 546 } 547 } 548 else { 549 unset($item['master_itemid']); 550 } 551 552 $create_items[] = $item; 553 } 554 555 if ($create_items && !API::Item()->create($create_items)) { 556 return false; 557 } 558 } 559 560 return true; 561} 562 563function copyItems($srcHostId, $dstHostId) { 564 $srcItems = API::Item()->get([ 565 'output' => ['type', 'snmp_community', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 566 'value_type', 'trapper_hosts', 'units', 'snmpv3_contextname', 'snmpv3_securityname', 'snmpv3_securitylevel', 567 'snmpv3_authprotocol', 'snmpv3_authpassphrase', 'snmpv3_privprotocol', 'snmpv3_privpassphrase', 568 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username', 'password', 'publickey', 569 'privatekey', 'flags', 'port', 'description', 'inventory_link', 'jmx_endpoint', 'master_itemid', 570 'templateid', 'url', 'query_fields', 'timeout', 'posts', 'status_codes', 'follow_redirects', 'post_type', 571 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format', 'ssl_cert_file', 572 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps' 573 ], 574 'selectApplications' => ['applicationid'], 575 'selectPreprocessing' => ['type', 'params'], 576 'hostids' => $srcHostId, 577 'webitems' => true, 578 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL], 579 'preservekeys' => true 580 ]); 581 $dstHosts = API::Host()->get([ 582 'output' => ['hostid', 'host', 'status'], 583 'selectInterfaces' => ['interfaceid', 'type', 'main'], 584 'hostids' => $dstHostId, 585 'preservekeys' => true, 586 'nopermissions' => true, 587 'templated_hosts' => true 588 ]); 589 $dstHost = reset($dstHosts); 590 591 $create_order = []; 592 $src_itemid_to_key = []; 593 foreach ($srcItems as $itemid => $item) { 594 $dependency_level = 0; 595 $master_item = $item; 596 $src_itemid_to_key[$itemid] = $item['key_']; 597 598 while ($master_item['type'] == ITEM_TYPE_DEPENDENT) { 599 $master_item = $srcItems[$master_item['master_itemid']]; 600 ++$dependency_level; 601 } 602 603 $create_order[$itemid] = $dependency_level; 604 } 605 asort($create_order); 606 607 $itemkey_to_id = []; 608 $create_items = []; 609 $current_dependency = reset($create_order); 610 611 foreach ($create_order as $itemid => $dependency_level) { 612 $srcItem = $srcItems[$itemid]; 613 614 // Skip creating web items. Those were created before. 615 if ($srcItem['type'] == ITEM_TYPE_HTTPTEST) { 616 continue; 617 } 618 619 if ($current_dependency != $dependency_level && $create_items) { 620 $current_dependency = $dependency_level; 621 $created_itemids = API::Item()->create($create_items); 622 623 if (!$created_itemids) { 624 return false; 625 } 626 $created_itemids = $created_itemids['itemids']; 627 628 foreach ($create_items as $index => $created_item) { 629 $itemkey_to_id[$created_item['key_']] = $created_itemids[$index]; 630 } 631 632 $create_items = []; 633 } 634 635 if ($srcItem['templateid']) { 636 $srcItem = get_same_item_for_host($srcItem, $dstHost['hostid']); 637 638 if (!$srcItem) { 639 return false; 640 } 641 $itemkey_to_id[$srcItem['key_']] = $srcItem['itemid']; 642 continue; 643 } 644 645 if ($dstHost['status'] != HOST_STATUS_TEMPLATE) { 646 // find a matching interface 647 $interface = CItem::findInterfaceForItem($srcItem['type'], $dstHost['interfaces']); 648 if ($interface) { 649 $srcItem['interfaceid'] = $interface['interfaceid']; 650 } 651 // no matching interface found, throw an error 652 elseif ($interface !== false) { 653 error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], $srcItem['key_'])); 654 } 655 } 656 unset($srcItem['itemid']); 657 unset($srcItem['templateid']); 658 $srcItem['hostid'] = $dstHostId; 659 $srcItem['applications'] = get_same_applications_for_host(zbx_objectValues($srcItem['applications'], 'applicationid'), $dstHostId); 660 661 if (!$srcItem['preprocessing']) { 662 unset($srcItem['preprocessing']); 663 } 664 665 if ($srcItem['type'] == ITEM_TYPE_DEPENDENT) { 666 if ($srcItems[$srcItem['master_itemid']]['type'] == ITEM_TYPE_HTTPTEST) { 667 // Web items are outside the scope and are created before regular items. 668 $web_item = get_same_item_for_host($srcItems[$srcItem['master_itemid']], $dstHost['hostid']); 669 $srcItem['master_itemid'] = $web_item['itemid']; 670 } 671 else { 672 $src_item_key = $src_itemid_to_key[$srcItem['master_itemid']]; 673 $srcItem['master_itemid'] = $itemkey_to_id[$src_item_key]; 674 } 675 } 676 else { 677 unset($srcItem['master_itemid']); 678 } 679 680 $create_items[] = $srcItem; 681 } 682 683 if ($create_items && !API::Item()->create($create_items)) { 684 return false; 685 } 686 687 return true; 688} 689 690/** 691 * Copy applications to a different host. 692 * 693 * @param string $source_hostid 694 * @param string $destination_hostid 695 * 696 * @return bool 697 */ 698function copyApplications($source_hostid, $destination_hostid) { 699 $applications_to_create = API::Application()->get([ 700 'output' => ['name'], 701 'hostids' => [$source_hostid], 702 'inherited' => false, 703 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL] 704 ]); 705 706 if (!$applications_to_create) { 707 return true; 708 } 709 710 foreach ($applications_to_create as &$application) { 711 $application['hostid'] = $destination_hostid; 712 unset($application['applicationid'], $application['templateid']); 713 } 714 unset($application); 715 716 return (bool) API::Application()->create($applications_to_create); 717} 718 719function get_item_by_itemid($itemid) { 720 $db_items = DBfetch(DBselect('SELECT i.* FROM items i WHERE i.itemid='.zbx_dbstr($itemid))); 721 if ($db_items) { 722 return $db_items; 723 } 724 error(_s('No item with itemid="%1$s".', $itemid)); 725 return false; 726} 727 728/** 729 * Description: 730 * Replace items for specified host 731 * 732 * Comments: 733 * $error= true : rise Error if item doesn't exist (error generated), false: special processing (NO error generated) 734 */ 735function get_same_item_for_host($item, $dest_hostids) { 736 $return_array = is_array($dest_hostids); 737 zbx_value2array($dest_hostids); 738 739 if (!is_array($item)) { 740 $itemid = $item; 741 } 742 elseif (isset($item['itemid'])) { 743 $itemid = $item['itemid']; 744 } 745 746 $same_item = null; 747 $same_items = []; 748 749 if (isset($itemid)) { 750 $db_items = DBselect( 751 'SELECT src.*'. 752 ' FROM items src,items dest'. 753 ' WHERE dest.itemid='.zbx_dbstr($itemid). 754 ' AND src.key_=dest.key_'. 755 ' AND '.dbConditionInt('src.hostid', $dest_hostids) 756 ); 757 while ($db_item = DBfetch($db_items)) { 758 if (is_array($item)) { 759 $same_item = $db_item; 760 $same_items[$db_item['itemid']] = $db_item; 761 } 762 else { 763 $same_item = $db_item['itemid']; 764 $same_items[$db_item['itemid']] = $db_item['itemid']; 765 } 766 } 767 if ($return_array) { 768 return $same_items; 769 } 770 else { 771 return $same_item; 772 } 773 } 774 return false; 775} 776 777/** 778 * Get parent templates for each given item. 779 * 780 * @param array $items An array of items. 781 * @param string $items[]['itemid'] ID of an item. 782 * @param string $items[]['templateid'] ID of parent template item. 783 * @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, 784 * ZBX_FLAG_DISCOVERY_PROTOTYPE). 785 * 786 * @return array 787 */ 788function getItemParentTemplates(array $items, $flag) { 789 $parent_itemids = []; 790 $data = [ 791 'links' => [], 792 'templates' => [] 793 ]; 794 795 foreach ($items as $item) { 796 if ($item['templateid'] != 0) { 797 $parent_itemids[$item['templateid']] = true; 798 $data['links'][$item['itemid']] = ['itemid' => $item['templateid']]; 799 } 800 } 801 802 if (!$parent_itemids) { 803 return $data; 804 } 805 806 $all_parent_itemids = []; 807 $hostids = []; 808 if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 809 $lld_ruleids = []; 810 } 811 812 do { 813 if ($flag == ZBX_FLAG_DISCOVERY_RULE) { 814 $db_items = API::DiscoveryRule()->get([ 815 'output' => ['itemid', 'hostid', 'templateid'], 816 'itemids' => array_keys($parent_itemids) 817 ]); 818 } 819 elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 820 $db_items = API::ItemPrototype()->get([ 821 'output' => ['itemid', 'hostid', 'templateid'], 822 'itemids' => array_keys($parent_itemids), 823 'selectDiscoveryRule' => ['itemid'] 824 ]); 825 } 826 // ZBX_FLAG_DISCOVERY_NORMAL 827 else { 828 $db_items = API::Item()->get([ 829 'output' => ['itemid', 'hostid', 'templateid'], 830 'itemids' => array_keys($parent_itemids), 831 'webitems' => true 832 ]); 833 } 834 835 $all_parent_itemids += $parent_itemids; 836 $parent_itemids = []; 837 838 foreach ($db_items as $db_item) { 839 $data['templates'][$db_item['hostid']] = []; 840 $hostids[$db_item['itemid']] = $db_item['hostid']; 841 842 if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 843 $lld_ruleids[$db_item['itemid']] = $db_item['discoveryRule']['itemid']; 844 } 845 846 if ($db_item['templateid'] != 0) { 847 if (!array_key_exists($db_item['templateid'], $all_parent_itemids)) { 848 $parent_itemids[$db_item['templateid']] = true; 849 } 850 851 $data['links'][$db_item['itemid']] = ['itemid' => $db_item['templateid']]; 852 } 853 } 854 } 855 while ($parent_itemids); 856 857 foreach ($data['links'] as &$parent_item) { 858 $parent_item['hostid'] = array_key_exists($parent_item['itemid'], $hostids) 859 ? $hostids[$parent_item['itemid']] 860 : 0; 861 862 if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 863 $parent_item['lld_ruleid'] = array_key_exists($parent_item['itemid'], $lld_ruleids) 864 ? $lld_ruleids[$parent_item['itemid']] 865 : 0; 866 } 867 } 868 unset($parent_item); 869 870 $db_templates = $data['templates'] 871 ? API::Template()->get([ 872 'output' => ['name'], 873 'templateids' => array_keys($data['templates']), 874 'preservekeys' => true 875 ]) 876 : []; 877 878 $rw_templates = $db_templates 879 ? API::Template()->get([ 880 'output' => [], 881 'templateids' => array_keys($db_templates), 882 'editable' => true, 883 'preservekeys' => true 884 ]) 885 : []; 886 887 $data['templates'][0] = []; 888 889 foreach ($data['templates'] as $hostid => &$template) { 890 $template = array_key_exists($hostid, $db_templates) 891 ? [ 892 'hostid' => $hostid, 893 'name' => $db_templates[$hostid]['name'], 894 'permission' => array_key_exists($hostid, $rw_templates) ? PERM_READ_WRITE : PERM_READ 895 ] 896 : [ 897 'hostid' => $hostid, 898 'name' => _('Inaccessible template'), 899 'permission' => PERM_DENY 900 ]; 901 } 902 unset($template); 903 904 return $data; 905} 906 907/** 908 * Returns a template prefix for selected item. 909 * 910 * @param string $itemid 911 * @param array $parent_templates The list of the templates, prepared by getItemParentTemplates() function. 912 * @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, 913 * ZBX_FLAG_DISCOVERY_PROTOTYPE). 914 * 915 * @return array|null 916 */ 917function makeItemTemplatePrefix($itemid, array $parent_templates, $flag) { 918 if (!array_key_exists($itemid, $parent_templates['links'])) { 919 return null; 920 } 921 922 while (array_key_exists($parent_templates['links'][$itemid]['itemid'], $parent_templates['links'])) { 923 $itemid = $parent_templates['links'][$itemid]['itemid']; 924 } 925 926 $template = $parent_templates['templates'][$parent_templates['links'][$itemid]['hostid']]; 927 928 if ($template['permission'] == PERM_READ_WRITE) { 929 if ($flag == ZBX_FLAG_DISCOVERY_RULE) { 930 $url = (new CUrl('host_discovery.php'))->setArgument('hostid', $template['hostid']); 931 } 932 elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 933 $url = (new CUrl('disc_prototypes.php')) 934 ->setArgument('parent_discoveryid', $parent_templates['links'][$itemid]['lld_ruleid']); 935 } 936 // ZBX_FLAG_DISCOVERY_NORMAL 937 else { 938 $url = (new CUrl('items.php')) 939 ->setArgument('hostid', $template['hostid']) 940 ->setArgument('filter_set', 1); 941 } 942 943 $name = (new CLink(CHtml::encode($template['name']), $url))->addClass(ZBX_STYLE_LINK_ALT); 944 } 945 else { 946 $name = new CSpan(CHtml::encode($template['name'])); 947 } 948 949 return [$name->addClass(ZBX_STYLE_GREY), NAME_DELIMITER]; 950} 951 952/** 953 * Returns a list of item templates. 954 * 955 * @param string $itemid 956 * @param array $parent_templates The list of the templates, prepared by getItemParentTemplates() function. 957 * @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, 958 * ZBX_FLAG_DISCOVERY_PROTOTYPE). 959 * 960 * @return array 961 */ 962function makeItemTemplatesHtml($itemid, array $parent_templates, $flag) { 963 $list = []; 964 965 while (array_key_exists($itemid, $parent_templates['links'])) { 966 $template = $parent_templates['templates'][$parent_templates['links'][$itemid]['hostid']]; 967 968 if ($template['permission'] == PERM_READ_WRITE) { 969 if ($flag == ZBX_FLAG_DISCOVERY_RULE) { 970 $url = (new CUrl('host_discovery.php')) 971 ->setArgument('form', 'update') 972 ->setArgument('itemid', $parent_templates['links'][$itemid]['itemid']); 973 } 974 elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) { 975 $url = (new CUrl('disc_prototypes.php')) 976 ->setArgument('form', 'update') 977 ->setArgument('itemid', $parent_templates['links'][$itemid]['itemid']) 978 ->setArgument('parent_discoveryid', $parent_templates['links'][$itemid]['lld_ruleid']); 979 } 980 // ZBX_FLAG_DISCOVERY_NORMAL 981 else { 982 $url = (new CUrl('items.php')) 983 ->setArgument('form', 'update') 984 ->setArgument('itemid', $parent_templates['links'][$itemid]['itemid']); 985 } 986 987 $name = new CLink(CHtml::encode($template['name']), $url); 988 } 989 else { 990 $name = (new CSpan(CHtml::encode($template['name'])))->addClass(ZBX_STYLE_GREY); 991 } 992 993 array_unshift($list, $name, ' ⇒ '); 994 995 $itemid = $parent_templates['links'][$itemid]['itemid']; 996 } 997 998 if ($list) { 999 array_pop($list); 1000 } 1001 1002 return $list; 1003} 1004 1005/** 1006 * Retrieve overview table object for items. 1007 * 1008 * @param array $groupids 1009 * @param string $application IDs of applications to filter items by. 1010 * @param int $viewMode 1011 * @param int $show_suppressed Whether to show suppressed problems. 1012 * 1013 * @return CTableInfo 1014 */ 1015 1016function getItemsDataOverview(array $groupids, $application, $viewMode, 1017 $show_suppressed = ZBX_PROBLEM_SUPPRESSED_TRUE) { 1018 // application filter 1019 if ($application !== '') { 1020 $applicationids = array_keys(API::Application()->get([ 1021 'output' => [], 1022 'groupids' => $groupids ? $groupids : null, 1023 'search' => ['name' => $application], 1024 'preservekeys' => true 1025 ])); 1026 $groupids = []; 1027 } 1028 else { 1029 $applicationids = null; 1030 } 1031 1032 $db_items = API::Item()->get([ 1033 'output' => ['itemid', 'hostid', 'key_', 'name', 'value_type', 'units', 'valuemapid'], 1034 'selectHosts' => ['name'], 1035 'groupids' => $groupids ? $groupids : null, 1036 'applicationids' => $applicationids, 1037 'monitored' => true, 1038 'webitems' => true, 1039 'preservekeys' => true 1040 ]); 1041 1042 $db_triggers = getTriggersWithActualSeverity([ 1043 'output' => ['triggerid', 'priority', 'value'], 1044 'selectItems' => ['itemid'], 1045 'groupids' => $groupids ? $groupids : null, 1046 'applicationids' => $applicationids, 1047 'monitored' => true, 1048 'preservekeys' => true 1049 ], ['show_suppressed' => $show_suppressed]); 1050 1051 foreach ($db_triggers as $db_trigger) { 1052 foreach ($db_trigger['items'] as $item) { 1053 if (array_key_exists($item['itemid'], $db_items)) { 1054 $db_item = &$db_items[$item['itemid']]; 1055 1056 // a little tricky check for attempt to overwrite active trigger (value=1) with 1057 // inactive or active trigger with lower priority. 1058 if (!array_key_exists('triggerid', $db_item) 1059 || ($db_item['value'] == TRIGGER_VALUE_FALSE && $db_trigger['value'] == TRIGGER_VALUE_TRUE) 1060 || (($db_item['value'] == TRIGGER_VALUE_FALSE || $db_trigger['value'] == TRIGGER_VALUE_TRUE) 1061 && $db_item['priority'] < $db_trigger['priority'])) { 1062 $db_item['triggerid'] = $db_trigger['triggerid']; 1063 $db_item['priority'] = $db_trigger['priority']; 1064 $db_item['value'] = $db_trigger['value']; 1065 } 1066 1067 $db_item['acknowledged'] = $db_trigger['problem']['acknowledged']; 1068 1069 unset($db_item); 1070 } 1071 } 1072 } 1073 1074 $db_items = CMacrosResolverHelper::resolveItemNames($db_items); 1075 1076 CArrayHelper::sort($db_items, [ 1077 ['field' => 'name_expanded', 'order' => ZBX_SORT_UP], 1078 ['field' => 'itemid', 'order' => ZBX_SORT_UP] 1079 ]); 1080 1081 // fetch latest values 1082 $history = Manager::History()->getLastValues(zbx_toHash($db_items, 'itemid'), 1, ZBX_HISTORY_PERIOD); 1083 1084 // fetch data for the host JS menu 1085 $hosts = API::Host()->get([ 1086 'output' => ['name', 'hostid'], 1087 'monitored_hosts' => true, 1088 'groupids' => $groupids ? $groupids : null, 1089 'applicationids' => $applicationids, 1090 'with_monitored_items' => true, 1091 'preservekeys' => true 1092 ]); 1093 1094 $items = []; 1095 $item_counter = []; 1096 $host_items = []; 1097 $host_names = []; 1098 1099 foreach ($db_items as $db_item) { 1100 $item_name = $db_item['name_expanded']; 1101 $host_name = $db_item['hosts'][0]['name']; 1102 $host_names[$db_item['hostid']] = $host_name; 1103 1104 if (!array_key_exists($host_name, $item_counter)) { 1105 $item_counter[$host_name] = []; 1106 } 1107 1108 if (!array_key_exists($item_name, $item_counter[$host_name])) { 1109 $item_counter[$host_name][$item_name] = 0; 1110 } 1111 1112 if (!array_key_exists($item_name, $host_items) || !array_key_exists($host_name, $host_items[$item_name])) { 1113 $host_items[$item_name][$host_name] = []; 1114 } 1115 1116 if (!array_key_exists($db_item['itemid'], $host_items[$item_name][$host_name])) { 1117 if (array_key_exists($db_item['itemid'], $host_items[$item_name][$host_name])) { 1118 $item_place = $host_items[$item_name][$host_name][$db_item['itemid']]['item_place']; 1119 } 1120 else { 1121 $item_place = $item_counter[$host_name][$item_name]; 1122 $item_counter[$host_name][$item_name]++; 1123 } 1124 1125 $item = [ 1126 'itemid' => $db_item['itemid'], 1127 'value_type' => $db_item['value_type'], 1128 'value' => isset($history[$db_item['itemid']]) ? $history[$db_item['itemid']][0]['value'] : null, 1129 'units' => $db_item['units'], 1130 'valuemapid' => $db_item['valuemapid'], 1131 'item_place' => $item_place, 1132 'acknowledged' => array_key_exists('acknowledged', $db_item) ? $db_item['acknowledged'] : 0 1133 ]; 1134 1135 if (array_key_exists('triggerid', $db_item)) { 1136 $item += [ 1137 'triggerid' => $db_item['triggerid'], 1138 'severity' => $db_item['priority'], 1139 'tr_value' => $db_item['value'] 1140 ]; 1141 } 1142 else { 1143 $item += [ 1144 'triggerid' => null, 1145 'severity' => null, 1146 'tr_value' => null 1147 ]; 1148 } 1149 1150 $items[$item_name][$item_place][$host_name] = $item; 1151 1152 $host_items[$item_name][$host_name][$db_item['itemid']] = $items[$item_name][$item_place][$host_name]; 1153 } 1154 } 1155 1156 $table = (new CTableInfo())->setHeadingColumn(0); 1157 if (!$host_names) { 1158 return $table; 1159 } 1160 $table->makeVerticalRotation(); 1161 1162 order_result($host_names); 1163 1164 if ($viewMode == STYLE_TOP) { 1165 $header = [_('Items')]; 1166 foreach ($host_names as $host_name) { 1167 $header[] = (new CColHeader($host_name)) 1168 ->addClass('vertical_rotation') 1169 ->setTitle($host_name); 1170 } 1171 $table->setHeader($header); 1172 1173 foreach ($items as $item_name => $item_data) { 1174 foreach ($item_data as $ithosts) { 1175 $tableRow = [(new CColHeader($item_name))->addClass(ZBX_STYLE_NOWRAP)]; 1176 foreach ($host_names as $host_name) { 1177 $tableRow = getItemDataOverviewCells($tableRow, $ithosts, $host_name); 1178 } 1179 $table->addRow($tableRow); 1180 } 1181 } 1182 } 1183 else { 1184 $header = [_('Hosts')]; 1185 foreach ($items as $item_name => $item_data) { 1186 foreach ($item_data as $ithosts) { 1187 $header[] = (new CColHeader($item_name)) 1188 ->addClass('vertical_rotation') 1189 ->setTitle($item_name); 1190 } 1191 } 1192 $table->setHeader($header); 1193 1194 foreach ($host_names as $hostId => $host_name) { 1195 $host = $hosts[$hostId]; 1196 1197 $name = (new CLinkAction($host['name']))->setMenuPopup(CMenuPopupHelper::getHost($hostId)); 1198 1199 $tableRow = [(new CColHeader($name))->addClass(ZBX_STYLE_NOWRAP)]; 1200 foreach ($items as $item_data) { 1201 foreach ($item_data as $ithosts) { 1202 $tableRow = getItemDataOverviewCells($tableRow, $ithosts, $host_name); 1203 } 1204 } 1205 $table->addRow($tableRow); 1206 } 1207 } 1208 1209 return $table; 1210} 1211 1212function getItemDataOverviewCells($tableRow, $ithosts, $hostName) { 1213 $ack = null; 1214 $css = ''; 1215 $value = UNKNOWN_VALUE; 1216 1217 if (isset($ithosts[$hostName])) { 1218 $item = $ithosts[$hostName]; 1219 1220 if ($item['tr_value'] == TRIGGER_VALUE_TRUE) { 1221 $css = getSeverityStyle($item['severity']); 1222 $ack = ($item['acknowledged'] == 1) ? [' ', (new CSpan())->addClass(ZBX_STYLE_ICON_ACKN)] : null; 1223 } 1224 1225 if ($item['value'] !== null) { 1226 $value = formatHistoryValue($item['value'], $item); 1227 } 1228 } 1229 1230 if ($value != UNKNOWN_VALUE) { 1231 $value = $value; 1232 } 1233 1234 $column = (new CCol([$value, $ack]))->addClass($css); 1235 1236 if (isset($ithosts[$hostName])) { 1237 $column 1238 ->setMenuPopup(CMenuPopupHelper::getHistory($item['itemid'])) 1239 ->addClass(ZBX_STYLE_CURSOR_POINTER) 1240 ->addClass(ZBX_STYLE_NOWRAP); 1241 } 1242 1243 $tableRow[] = $column; 1244 1245 return $tableRow; 1246} 1247 1248/** 1249 * Get same application IDs on destination host and return array with keys as source application IDs 1250 * and values as destination application IDs. 1251 * 1252 * Comments: !!! Don't forget sync code with C !!! 1253 * 1254 * @param array $applicationIds 1255 * @param string $hostId 1256 * 1257 * @return array 1258 */ 1259function get_same_applications_for_host(array $applicationIds, $hostId) { 1260 $applications = []; 1261 1262 $dbApplications = DBselect( 1263 'SELECT a1.applicationid AS dstappid,a2.applicationid AS srcappid'. 1264 ' FROM applications a1,applications a2'. 1265 ' WHERE a1.name=a2.name'. 1266 ' AND a1.hostid='.zbx_dbstr($hostId). 1267 ' AND '.dbConditionInt('a2.applicationid', $applicationIds) 1268 ); 1269 1270 while ($dbApplication = DBfetch($dbApplications)) { 1271 $applications[$dbApplication['srcappid']] = $dbApplication['dstappid']; 1272 } 1273 1274 return $applications; 1275} 1276 1277/****************************************************************************** 1278 * * 1279 * Comments: !!! Don't forget sync code with C !!! * 1280 * * 1281 ******************************************************************************/ 1282function get_applications_by_itemid($itemids, $field = 'applicationid') { 1283 zbx_value2array($itemids); 1284 $result = []; 1285 $db_applications = DBselect( 1286 'SELECT DISTINCT app.'.$field.' AS result'. 1287 ' FROM applications app,items_applications ia'. 1288 ' WHERE app.applicationid=ia.applicationid'. 1289 ' AND '.dbConditionInt('ia.itemid', $itemids) 1290 ); 1291 while ($db_application = DBfetch($db_applications)) { 1292 array_push($result, $db_application['result']); 1293 } 1294 1295 return $result; 1296} 1297 1298/** 1299 * Format history value. 1300 * First format the value according to the configuration of the item. Then apply the value mapping to the formatted (!) 1301 * value. 1302 * 1303 * @param mixed $value 1304 * @param array $item 1305 * @param int $item['value_type'] type of the value: ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64, ... 1306 * @param string $item['units'] units of item 1307 * @param int $item['valuemapid'] id of mapping set of values 1308 * @param bool $trim 1309 * 1310 * @return string 1311 */ 1312function formatHistoryValue($value, array $item, $trim = true) { 1313 $mapping = false; 1314 1315 // format value 1316 if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) { 1317 $value = convert_units([ 1318 'value' => $value, 1319 'units' => $item['units'] 1320 ]); 1321 } 1322 elseif ($item['value_type'] != ITEM_VALUE_TYPE_STR 1323 && $item['value_type'] != ITEM_VALUE_TYPE_TEXT 1324 && $item['value_type'] != ITEM_VALUE_TYPE_LOG) { 1325 1326 $value = _('Unknown value type'); 1327 } 1328 1329 // apply value mapping 1330 switch ($item['value_type']) { 1331 case ITEM_VALUE_TYPE_STR: 1332 $mapping = getMappedValue($value, $item['valuemapid']); 1333 // break; is not missing here 1334 case ITEM_VALUE_TYPE_TEXT: 1335 case ITEM_VALUE_TYPE_LOG: 1336 if ($trim && mb_strlen($value) > 20) { 1337 $value = mb_substr($value, 0, 20).'...'; 1338 } 1339 1340 if ($mapping !== false) { 1341 $value = $mapping.' ('.$value.')'; 1342 } 1343 break; 1344 default: 1345 $value = applyValueMap($value, $item['valuemapid']); 1346 } 1347 1348 return $value; 1349} 1350 1351/** 1352 * Retrieves from DB historical data for items and applies functional calculations. 1353 * If fails for some reason, returns UNRESOLVED_MACRO_STRING. 1354 * 1355 * @param array $item 1356 * @param string $item['value_type'] type of item, allowed: ITEM_VALUE_TYPE_FLOAT and ITEM_VALUE_TYPE_UINT64 1357 * @param string $item['itemid'] ID of item 1358 * @param string $item['units'] units of item 1359 * @param string $function function to apply to time period from param, allowed: min, max and avg 1360 * @param string $parameter formatted parameter for function, example: "2w" meaning 2 weeks 1361 * 1362 * @return string item functional value from history 1363 */ 1364function getItemFunctionalValue($item, $function, $parameter) { 1365 // check whether function is allowed 1366 if (!in_array($function, ['min', 'max', 'avg']) || $parameter === '') { 1367 return UNRESOLVED_MACRO_STRING; 1368 } 1369 1370 $parameter = convertFunctionValue($parameter); 1371 1372 if (bccomp($parameter, 0) == 0) { 1373 return UNRESOLVED_MACRO_STRING; 1374 } 1375 1376 // allowed item types for min, max and avg function 1377 $history_tables = [ITEM_VALUE_TYPE_FLOAT => 'history', ITEM_VALUE_TYPE_UINT64 => 'history_uint']; 1378 1379 if (!array_key_exists($item['value_type'], $history_tables)) { 1380 return UNRESOLVED_MACRO_STRING; 1381 } 1382 else { 1383 $result = Manager::History()->getAggregatedValue($item, $function, (time() - $parameter)); 1384 1385 if ($result !== null) { 1386 return convert_units(['value' => $result, 'units' => $item['units']]); 1387 } 1388 // no data in history 1389 else { 1390 return UNRESOLVED_MACRO_STRING; 1391 } 1392 } 1393} 1394 1395/** 1396 * Check if current time is within the given period. 1397 * 1398 * @param string $period time period format: "wd[-wd2],hh:mm-hh:mm" 1399 * @param int $now current timestamp 1400 * 1401 * @return bool true - within period, false - out of period 1402 */ 1403function checkTimePeriod($period, $now) { 1404 if (sscanf($period, '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) { 1405 if (sscanf($period, '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) { 1406 // delay period format is wrong - skip 1407 return false; 1408 } 1409 $d2 = $d1; 1410 } 1411 1412 $tm = localtime($now, true); 1413 $day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday']; 1414 $sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec']; 1415 1416 $sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1; 1417 $sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2; 1418 1419 return $d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2; 1420} 1421 1422/** 1423 * Get item minimum delay. 1424 * 1425 * @param string $delay 1426 * @param array $flexible_intervals 1427 * 1428 * @return string 1429 */ 1430function getItemDelay($delay, array $flexible_intervals) { 1431 $delay = timeUnitToSeconds($delay); 1432 1433 if ($delay != 0 || !$flexible_intervals) { 1434 return $delay; 1435 } 1436 1437 $min_delay = SEC_PER_YEAR; 1438 1439 foreach ($flexible_intervals as $flexible_interval) { 1440 $flexible_interval_parts = explode('/', $flexible_interval); 1441 $flexible_delay = timeUnitToSeconds($flexible_interval_parts[0]); 1442 1443 $min_delay = min($min_delay, $flexible_delay); 1444 } 1445 1446 return $min_delay; 1447} 1448 1449/** 1450 * Return delay value that is currently applicable 1451 * 1452 * @param int $delay default delay 1453 * @param array $flexible_intervals array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm" 1454 * @param int $now current timestamp 1455 * 1456 * @return int delay for a current timestamp 1457 */ 1458function getCurrentDelay($delay, array $flexible_intervals, $now) { 1459 if (!$flexible_intervals) { 1460 return $delay; 1461 } 1462 1463 $current_delay = -1; 1464 1465 foreach ($flexible_intervals as $flexible_interval) { 1466 list($flexible_delay, $flexible_period) = explode('/', $flexible_interval); 1467 $flexible_delay = (int) $flexible_delay; 1468 1469 if (($current_delay == -1 || $flexible_delay < $current_delay) && checkTimePeriod($flexible_period, $now)) { 1470 $current_delay = $flexible_delay; 1471 } 1472 } 1473 1474 if ($current_delay == -1) { 1475 return $delay; 1476 } 1477 1478 return $current_delay; 1479} 1480 1481/** 1482 * Return time of next flexible interval 1483 * 1484 * @param array $flexible_intervals array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm" 1485 * @param int $now current timestamp 1486 * @param int $next_interval timestamp of a next interval 1487 * 1488 * @return bool false if no flexible intervals defined 1489 */ 1490function getNextDelayInterval(array $flexible_intervals, $now, &$next_interval) { 1491 if (!$flexible_intervals) { 1492 return false; 1493 } 1494 1495 $next = 0; 1496 $tm = localtime($now, true); 1497 $day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday']; 1498 $sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec']; 1499 1500 foreach ($flexible_intervals as $flexible_interval) { 1501 $flexible_interval_parts = explode('/', $flexible_interval); 1502 1503 if (sscanf($flexible_interval_parts[1], '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) { 1504 if (sscanf($flexible_interval_parts[1], '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) { 1505 continue; 1506 } 1507 $d2 = $d1; 1508 } 1509 1510 $sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1; 1511 $sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2; 1512 1513 // current period 1514 if ($d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2) { 1515 if ($next == 0 || $next > $now - $sec + $sec2) { 1516 // the next second after the current interval's upper bound 1517 $next = $now - $sec + $sec2; 1518 } 1519 } 1520 // will be active today 1521 elseif ($d1 <= $day && $d2 >= $day && $sec < $sec1) { 1522 if ($next == 0 || $next > $now - $sec + $sec1) { 1523 $next = $now - $sec + $sec1; 1524 } 1525 } 1526 else { 1527 $nextDay = ($day + 1 <= 7) ? $day + 1 : 1; 1528 1529 // will be active tomorrow 1530 if ($d1 <= $nextDay && $nextDay <= $d2) { 1531 if ($next == 0 || $next > $now - $sec + SEC_PER_DAY + $sec1) { 1532 $next = $now - $sec + SEC_PER_DAY + $sec1; 1533 } 1534 } 1535 // later in the future 1536 else { 1537 $dayDiff = -1; 1538 1539 if ($day < $d1) { 1540 $dayDiff = $d1 - $day; 1541 } 1542 if ($day >= $d2) { 1543 $dayDiff = ($d1 + 7) - $day; 1544 } 1545 if ($d1 <= $day && $day < $d2) { 1546 // should never happen, could not deduce day difference 1547 $dayDiff = -1; 1548 } 1549 if ($dayDiff != -1 && ($next == 0 || $next > $now - $sec + SEC_PER_DAY * $dayDiff + $sec1)) { 1550 $next = $now - $sec + SEC_PER_DAY * $dayDiff + $sec1; 1551 } 1552 } 1553 } 1554 } 1555 1556 if ($next != 0) { 1557 $next_interval = $next; 1558 } 1559 1560 return ($next != 0); 1561} 1562 1563/** 1564 * Calculate nextcheck timestamp for an item using flexible intervals. 1565 * 1566 * the parameter $flexible_intervals is an array if strings that are in the following format: 1567 * 1568 * +------------[;]<----------+ 1569 * | | 1570 * ->+-[d/wd[-wd2],hh:mm-hh:mm]-+ 1571 * 1572 * d - delay (0-n) 1573 * wd, wd2 - day of week (1-7) 1574 * hh - hours (0-24) 1575 * mm - minutes (0-59) 1576 * 1577 * @param int $seed seed value applied to delay to spread item checks over the delay period 1578 * @param string $delay default delay, can be overridden 1579 * @param array $flexible_intervals array of flexible intervals 1580 * @param int $now current timestamp 1581 * 1582 * @return int 1583 */ 1584function calculateItemNextCheck($seed, $delay, $flexible_intervals, $now) { 1585 /* 1586 * Try to find the nearest 'nextcheck' value with condition 'now' < 'nextcheck' < 'now' + SEC_PER_YEAR 1587 * If it is not possible to check the item within a year, fail. 1588 */ 1589 1590 $t = $now; 1591 $tMax = $now + SEC_PER_YEAR; 1592 $try = 0; 1593 1594 while ($t < $tMax) { 1595 // Calculate 'nextcheck' value for the current interval. 1596 $currentDelay = getCurrentDelay($delay, $flexible_intervals, $t); 1597 1598 if ($currentDelay != 0) { 1599 $nextCheck = $currentDelay * floor($t / $currentDelay) + ($seed % $currentDelay); 1600 1601 if ($try == 0) { 1602 while ($nextCheck <= $t) { 1603 $nextCheck += $currentDelay; 1604 } 1605 } 1606 else { 1607 while ($nextCheck < $t) { 1608 $nextCheck += $currentDelay; 1609 } 1610 } 1611 } 1612 else { 1613 $nextCheck = ZBX_JAN_2038; 1614 } 1615 1616 /* 1617 * Is 'nextcheck' < end of the current interval and the end of the current interval 1618 * is the beginning of the next interval - 1. 1619 */ 1620 if (getNextDelayInterval($flexible_intervals, $t, $nextInterval) && $nextCheck >= $nextInterval) { 1621 // 'nextcheck' is beyond the current interval. 1622 $t = $nextInterval; 1623 $try++; 1624 } 1625 else { 1626 break; 1627 } 1628 } 1629 1630 return $nextCheck; 1631} 1632 1633/* 1634 * Description: 1635 * Function returns true if http items exists in the $items array. 1636 * The array should contain a field 'type' 1637 */ 1638function httpItemExists($items) { 1639 foreach ($items as $item) { 1640 if ($item['type'] == ITEM_TYPE_HTTPTEST) { 1641 return true; 1642 } 1643 } 1644 return false; 1645} 1646 1647function getParamFieldNameByType($itemType) { 1648 switch ($itemType) { 1649 case ITEM_TYPE_SSH: 1650 case ITEM_TYPE_TELNET: 1651 case ITEM_TYPE_JMX: 1652 return 'params_es'; 1653 case ITEM_TYPE_DB_MONITOR: 1654 return 'params_ap'; 1655 case ITEM_TYPE_CALCULATED: 1656 return 'params_f'; 1657 default: 1658 return 'params'; 1659 } 1660} 1661 1662function getParamFieldLabelByType($itemType) { 1663 switch ($itemType) { 1664 case ITEM_TYPE_SSH: 1665 case ITEM_TYPE_TELNET: 1666 case ITEM_TYPE_JMX: 1667 return _('Executed script'); 1668 case ITEM_TYPE_DB_MONITOR: 1669 return _('SQL query'); 1670 case ITEM_TYPE_CALCULATED: 1671 return _('Formula'); 1672 default: 1673 return 'params'; 1674 } 1675} 1676 1677/** 1678 * Get either one or all item preprocessing types. If grouped set to true, returns group labels. Returns empty string if 1679 * no specific type is found. 1680 * 1681 * Usage examples: 1682 * - get_preprocessing_types() Returns array as defined. 1683 * - get_preprocessing_types(4) Returns string: 'Trim'. 1684 * - get_preprocessing_types(<wrong type>) Returns an empty string: ''. 1685 * - get_preprocessing_types(null, false) Returns subarrays in one array maintaining index: 1686 * [5] => Regular expression 1687 * [4] => Trim 1688 * [2] => Right trim 1689 * [3] => Left trim 1690 * [11] => XML XPath 1691 * [12] => JSON Path 1692 * [1] => Custom multiplier 1693 * [9] => Simple change 1694 * [10] => Speed per second 1695 * [6] => Boolean to decimal 1696 * [7] => Octal to decimal 1697 * [8] => Hexadecimal to decimal 1698 * 1699 * @param int $type Item preprocessing type. 1700 * @param bool $grouped Group label flag. 1701 * 1702 * @return mixed 1703 */ 1704function get_preprocessing_types($type = null, $grouped = true) { 1705 $groups = [ 1706 [ 1707 'label' => _('Text'), 1708 'types' => [ 1709 ZBX_PREPROC_REGSUB => _('Regular expression'), 1710 ZBX_PREPROC_TRIM => _('Trim'), 1711 ZBX_PREPROC_RTRIM => _('Right trim'), 1712 ZBX_PREPROC_LTRIM => _('Left trim') 1713 ] 1714 ], 1715 [ 1716 'label' => _('Structured data'), 1717 'types' => [ 1718 ZBX_PREPROC_XPATH => _('XML XPath'), 1719 ZBX_PREPROC_JSONPATH => _('JSON Path') 1720 ] 1721 ], 1722 [ 1723 'label' => _('Arithmetic'), 1724 'types' => [ 1725 ZBX_PREPROC_MULTIPLIER => _('Custom multiplier') 1726 ] 1727 ], 1728 [ 1729 'label' => _x('Change', 'noun'), 1730 'types' => [ 1731 ZBX_PREPROC_DELTA_VALUE => _('Simple change'), 1732 ZBX_PREPROC_DELTA_SPEED => _('Change per second') 1733 ] 1734 ], 1735 [ 1736 'label' => _('Numeral systems'), 1737 'types' => [ 1738 ZBX_PREPROC_BOOL2DEC => _('Boolean to decimal'), 1739 ZBX_PREPROC_OCT2DEC => _('Octal to decimal'), 1740 ZBX_PREPROC_HEX2DEC => _('Hexadecimal to decimal') 1741 ] 1742 ] 1743 ]; 1744 1745 if ($type !== null) { 1746 foreach ($groups as $group) { 1747 if (array_key_exists($type, $group['types'])) { 1748 return $group['types'][$type]; 1749 } 1750 } 1751 1752 return ''; 1753 } 1754 elseif ($grouped) { 1755 return $groups; 1756 } 1757 else { 1758 $types = []; 1759 1760 foreach ($groups as $group) { 1761 $types += $group['types']; 1762 } 1763 1764 return $types; 1765 } 1766} 1767 1768/* 1769 * Quoting $param if it contain special characters. 1770 * 1771 * @param string $param 1772 * @param bool $forced 1773 * 1774 * @return string 1775 */ 1776function quoteItemKeyParam($param, $forced = false) { 1777 if (!$forced) { 1778 if (!isset($param[0]) || ($param[0] != '"' && false === strpbrk($param, ',]'))) { 1779 return $param; 1780 } 1781 } 1782 1783 return '"'.str_replace('"', '\\"', $param).'"'; 1784} 1785 1786/** 1787 * Expands item name and for dependent item master item name. 1788 * 1789 * @param array $items Array of items. 1790 * @param string $data_source 'items' or 'itemprototypes'. 1791 * 1792 * @return array 1793 */ 1794function expandItemNamesWithMasterItems($items, $data_source) { 1795 $items = CMacrosResolverHelper::resolveItemNames($items); 1796 $itemids = []; 1797 $master_itemids = []; 1798 1799 foreach ($items as $item_index => &$item) { 1800 if ($item['type'] == ITEM_TYPE_DEPENDENT) { 1801 $master_itemids[$item['master_itemid']] = true; 1802 } 1803 1804 // The "source" is required to tell the frontend where the link should point at - item or item prototype. 1805 $item['source'] = $data_source; 1806 $itemids[$item_index] = $item['itemid']; 1807 } 1808 unset($item); 1809 1810 $master_itemids = array_diff(array_keys($master_itemids), $itemids); 1811 1812 if ($master_itemids) { 1813 $options = [ 1814 'output' => ['itemid', 'type', 'hostid', 'name', 'key_'], 1815 'itemids' => $master_itemids, 1816 'editable' => true, 1817 'preservekeys' => true 1818 ]; 1819 $master_items = API::Item()->get($options + ['webitems' => true]); 1820 1821 foreach ($master_items as &$master_item) { 1822 $master_item['source'] = 'items'; 1823 } 1824 unset($master_item); 1825 1826 $master_item_prototypes = API::ItemPrototype()->get($options); 1827 1828 foreach ($master_item_prototypes as &$master_item_prototype) { 1829 $master_item_prototype['source'] = 'itemprototypes'; 1830 } 1831 unset($master_item_prototype); 1832 1833 $master_items = CMacrosResolverHelper::resolveItemNames($master_items + $master_item_prototypes); 1834 } 1835 1836 foreach ($items as &$item) { 1837 if ($item['type'] == ITEM_TYPE_DEPENDENT) { 1838 $master_itemid = $item['master_itemid']; 1839 $items_index = array_search($master_itemid, $itemids); 1840 1841 $item['master_item'] = [ 1842 'itemid' => $master_itemid, 1843 'name_expanded' => ($items_index === false) 1844 ? $master_items[$master_itemid]['name_expanded'] 1845 : $items[$items_index]['name_expanded'], 1846 'type' => ($items_index === false) 1847 ? $master_items[$master_itemid]['type'] 1848 : $items[$items_index]['type'], 1849 'source' => ($items_index === false) 1850 ? $master_items[$master_itemid]['source'] 1851 : $items[$items_index]['source'] 1852 ]; 1853 } 1854 } 1855 unset($item); 1856 1857 return $items; 1858} 1859 1860/** 1861 * Returns an array of allowed item types for "Check now" functionality. 1862 * 1863 * @return array 1864 */ 1865function checkNowAllowedTypes() { 1866 return [ 1867 ITEM_TYPE_ZABBIX, 1868 ITEM_TYPE_SNMPV1, 1869 ITEM_TYPE_SIMPLE, 1870 ITEM_TYPE_SNMPV2C, 1871 ITEM_TYPE_INTERNAL, 1872 ITEM_TYPE_SNMPV3, 1873 ITEM_TYPE_AGGREGATE, 1874 ITEM_TYPE_EXTERNAL, 1875 ITEM_TYPE_DB_MONITOR, 1876 ITEM_TYPE_IPMI, 1877 ITEM_TYPE_SSH, 1878 ITEM_TYPE_TELNET, 1879 ITEM_TYPE_CALCULATED, 1880 ITEM_TYPE_JMX, 1881 ITEM_TYPE_HTTPAGENT 1882 ]; 1883} 1884