You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2471 lines
66 KiB
2471 lines
66 KiB
<?php
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Convert windows events type constant in to the string representation
|
|
*
|
|
* @param int $logtype
|
|
* @return string
|
|
*/
|
|
function get_item_logtype_description($logtype) {
|
|
switch ($logtype) {
|
|
case ITEM_LOGTYPE_INFORMATION:
|
|
return _('Information');
|
|
case ITEM_LOGTYPE_WARNING:
|
|
return _('Warning');
|
|
case ITEM_LOGTYPE_ERROR:
|
|
return _('Error');
|
|
case ITEM_LOGTYPE_FAILURE_AUDIT:
|
|
return _('Failure Audit');
|
|
case ITEM_LOGTYPE_SUCCESS_AUDIT:
|
|
return _('Success Audit');
|
|
case ITEM_LOGTYPE_CRITICAL:
|
|
return _('Critical');
|
|
case ITEM_LOGTYPE_VERBOSE:
|
|
return _('Verbose');
|
|
default:
|
|
return _('Unknown');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert windows events type constant in to the CSS style name
|
|
*
|
|
* @param int $logtype
|
|
* @return string
|
|
*/
|
|
function get_item_logtype_style($logtype) {
|
|
switch ($logtype) {
|
|
case ITEM_LOGTYPE_INFORMATION:
|
|
case ITEM_LOGTYPE_SUCCESS_AUDIT:
|
|
case ITEM_LOGTYPE_VERBOSE:
|
|
return ZBX_STYLE_LOG_INFO_BG;
|
|
|
|
case ITEM_LOGTYPE_WARNING:
|
|
return ZBX_STYLE_LOG_WARNING_BG;
|
|
|
|
case ITEM_LOGTYPE_ERROR:
|
|
case ITEM_LOGTYPE_FAILURE_AUDIT:
|
|
return ZBX_STYLE_LOG_HIGH_BG;
|
|
|
|
case ITEM_LOGTYPE_CRITICAL:
|
|
return ZBX_STYLE_LOG_DISASTER_BG;
|
|
|
|
default:
|
|
return ZBX_STYLE_LOG_NA_BG;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get item type string name by item type number, or array of all item types if null passed.
|
|
*
|
|
* @param int|null $type
|
|
*
|
|
* @return array|string
|
|
*/
|
|
function item_type2str($type = null) {
|
|
$types = [
|
|
ITEM_TYPE_ZABBIX => _('Zabbix agent'),
|
|
ITEM_TYPE_ZABBIX_ACTIVE => _('Zabbix agent (active)'),
|
|
ITEM_TYPE_SIMPLE => _('Simple check'),
|
|
ITEM_TYPE_SNMP => _('SNMP agent'),
|
|
ITEM_TYPE_SNMPTRAP => _('SNMP trap'),
|
|
ITEM_TYPE_INTERNAL => _('Zabbix internal'),
|
|
ITEM_TYPE_TRAPPER => _('Zabbix trapper'),
|
|
ITEM_TYPE_EXTERNAL => _('External check'),
|
|
ITEM_TYPE_DB_MONITOR => _('Database monitor'),
|
|
ITEM_TYPE_HTTPAGENT => _('HTTP agent'),
|
|
ITEM_TYPE_IPMI => _('IPMI agent'),
|
|
ITEM_TYPE_SSH => _('SSH agent'),
|
|
ITEM_TYPE_TELNET => _('TELNET agent'),
|
|
ITEM_TYPE_JMX => _('JMX agent'),
|
|
ITEM_TYPE_CALCULATED => _('Calculated'),
|
|
ITEM_TYPE_HTTPTEST => _('Web monitoring'),
|
|
ITEM_TYPE_DEPENDENT => _('Dependent item'),
|
|
ITEM_TYPE_SCRIPT => _('Script')
|
|
];
|
|
|
|
if ($type === null) {
|
|
return $types;
|
|
}
|
|
|
|
return array_key_exists($type, $types) ? $types[$type] : _('Unknown');
|
|
}
|
|
|
|
/**
|
|
* Returns label for value type.
|
|
*
|
|
* @param int $value_type
|
|
*
|
|
* @return string
|
|
*/
|
|
function itemValueTypeString($value_type): string {
|
|
switch ($value_type) {
|
|
case ITEM_VALUE_TYPE_UINT64:
|
|
return _('Numeric (unsigned)');
|
|
case ITEM_VALUE_TYPE_FLOAT:
|
|
return _('Numeric (float)');
|
|
case ITEM_VALUE_TYPE_STR:
|
|
return _('Character');
|
|
case ITEM_VALUE_TYPE_LOG:
|
|
return _('Log');
|
|
case ITEM_VALUE_TYPE_TEXT:
|
|
return _('Text');
|
|
case ITEM_VALUE_TYPE_BINARY:
|
|
return _('Binary');
|
|
}
|
|
|
|
return _('Unknown');
|
|
}
|
|
|
|
function item_status2str($type = null) {
|
|
if (is_null($type)) {
|
|
return [ITEM_STATUS_ACTIVE => _('Enabled'), ITEM_STATUS_DISABLED => _('Disabled')];
|
|
}
|
|
|
|
return ($type == ITEM_STATUS_ACTIVE) ? _('Enabled') : _('Disabled');
|
|
}
|
|
|
|
/**
|
|
* Returns the names of supported item states.
|
|
*
|
|
* If the $state parameter is passed, returns the name of the specific state, otherwise - returns an array of all
|
|
* supported states.
|
|
*
|
|
* @param string $state
|
|
*
|
|
* @return array|string
|
|
*/
|
|
function itemState($state = null) {
|
|
$states = [
|
|
ITEM_STATE_NORMAL => _('Normal'),
|
|
ITEM_STATE_NOTSUPPORTED => _('Not supported')
|
|
];
|
|
|
|
if ($state === null) {
|
|
return $states;
|
|
}
|
|
elseif (isset($states[$state])) {
|
|
return $states[$state];
|
|
}
|
|
else {
|
|
return _('Unknown');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the text indicating the items status and state. If the $state parameter is not given, only the status of
|
|
* the item will be taken into account.
|
|
*
|
|
* @param int $status
|
|
* @param int $state
|
|
*
|
|
* @return string
|
|
*/
|
|
function itemIndicator($status, $state = null) {
|
|
if ($status == ITEM_STATUS_ACTIVE) {
|
|
return ($state == ITEM_STATE_NOTSUPPORTED) ? _('Not supported') : _('Enabled');
|
|
}
|
|
|
|
return _('Disabled');
|
|
}
|
|
|
|
/**
|
|
* Returns the CSS class for the items status and state indicator. If the $state parameter is not given, only the status of
|
|
* the item will be taken into account.
|
|
*
|
|
* @param int $status
|
|
* @param int $state
|
|
*
|
|
* @return string
|
|
*/
|
|
function itemIndicatorStyle($status, $state = null) {
|
|
if ($status == ITEM_STATUS_ACTIVE) {
|
|
return ($state == ITEM_STATE_NOTSUPPORTED) ?
|
|
ZBX_STYLE_GREY :
|
|
ZBX_STYLE_GREEN;
|
|
}
|
|
|
|
return ZBX_STYLE_RED;
|
|
}
|
|
|
|
/**
|
|
* Order items by keep history.
|
|
*
|
|
* @param array $items
|
|
* @param string $items['history']
|
|
* @param string $sortorder
|
|
*/
|
|
function orderItemsByHistory(array &$items, $sortorder){
|
|
$simple_interval_parser = new CSimpleIntervalParser();
|
|
|
|
foreach ($items as &$item) {
|
|
$item['history_sort'] = ($simple_interval_parser->parse($item['history']) == CParser::PARSE_SUCCESS)
|
|
? timeUnitToSeconds($item['history'])
|
|
: $item['history'];
|
|
}
|
|
unset($item);
|
|
|
|
order_result($items, 'history_sort', $sortorder);
|
|
|
|
foreach ($items as &$item) {
|
|
unset($item['history_sort']);
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* Order items by keep trends.
|
|
*
|
|
* @param array $items
|
|
* @param int $items['value_type']
|
|
* @param string $items['trends']
|
|
* @param string $sortorder
|
|
*/
|
|
function orderItemsByTrends(array &$items, $sortorder){
|
|
$simple_interval_parser = new CSimpleIntervalParser();
|
|
|
|
foreach ($items as &$item) {
|
|
if (in_array($item['value_type'], [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])) {
|
|
$item['trends_sort'] = '';
|
|
}
|
|
else {
|
|
$item['trends_sort'] = ($simple_interval_parser->parse($item['trends']) == CParser::PARSE_SUCCESS)
|
|
? timeUnitToSeconds($item['trends'])
|
|
: $item['trends'];
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
order_result($items, 'trends_sort', $sortorder);
|
|
|
|
foreach ($items as &$item) {
|
|
unset($item['trends_sort']);
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* Order items by update interval.
|
|
*
|
|
* @param array $items
|
|
* @param int $items['type']
|
|
* @param string $items['delay']
|
|
* @param string $items['key_']
|
|
* @param string $sortorder
|
|
* @param array $options
|
|
* @param bool $options['usermacros']
|
|
* @param bool $options['lldmacros']
|
|
*/
|
|
function orderItemsByDelay(array &$items, $sortorder, array $options){
|
|
$update_interval_parser = new CUpdateIntervalParser($options);
|
|
|
|
foreach ($items as &$item) {
|
|
if (in_array($item['type'], [ITEM_TYPE_TRAPPER, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT])
|
|
|| ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE && strncmp($item['key_'], 'mqtt.get', 8) === 0)) {
|
|
$item['delay_sort'] = '';
|
|
}
|
|
elseif ($update_interval_parser->parse($item['delay']) == CParser::PARSE_SUCCESS) {
|
|
$item['delay_sort'] = $update_interval_parser->getDelay();
|
|
|
|
if ($item['delay_sort'][0] !== '{') {
|
|
$item['delay_sort'] = timeUnitToSeconds($item['delay_sort']);
|
|
}
|
|
}
|
|
else {
|
|
$item['delay_sort'] = $item['delay'];
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
order_result($items, 'delay_sort', $sortorder);
|
|
|
|
foreach ($items as &$item) {
|
|
unset($item['delay_sort']);
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* Orders items by both status and state. Items are sorted in the following order: enabled, disabled, not supported.
|
|
*
|
|
* Keep in sync with orderTriggersByStatus().
|
|
*
|
|
* @param array $items
|
|
* @param string $sortorder
|
|
*/
|
|
function orderItemsByStatus(array &$items, $sortorder = ZBX_SORT_UP) {
|
|
$sort = [];
|
|
|
|
foreach ($items as $key => $item) {
|
|
if ($item['status'] == ITEM_STATUS_ACTIVE) {
|
|
$sort[$key] = ($item['state'] == ITEM_STATE_NOTSUPPORTED) ? 2 : 0;
|
|
}
|
|
else {
|
|
$sort[$key] = 1;
|
|
}
|
|
}
|
|
|
|
if ($sortorder == ZBX_SORT_UP) {
|
|
asort($sort);
|
|
}
|
|
else {
|
|
arsort($sort);
|
|
}
|
|
|
|
$sortedItems = [];
|
|
foreach ($sort as $key => $val) {
|
|
$sortedItems[$key] = $items[$key];
|
|
}
|
|
$items = $sortedItems;
|
|
}
|
|
|
|
/**
|
|
* Returns the name of the given interface type. Items "status" and "state" properties must be defined.
|
|
*
|
|
* @param int $type
|
|
*
|
|
* @return ?string
|
|
*/
|
|
function interfaceType2str($type) {
|
|
$interfaceGroupLabels = [
|
|
INTERFACE_TYPE_AGENT => _('Agent'),
|
|
INTERFACE_TYPE_SNMP => _('SNMP'),
|
|
INTERFACE_TYPE_JMX => _('JMX'),
|
|
INTERFACE_TYPE_IPMI => _('IPMI')
|
|
];
|
|
|
|
return array_key_exists($type, $interfaceGroupLabels) ? $interfaceGroupLabels[$type] : null;
|
|
}
|
|
|
|
function itemTypeInterface($type = null) {
|
|
static $types = [
|
|
ITEM_TYPE_SNMP => INTERFACE_TYPE_SNMP,
|
|
ITEM_TYPE_SNMPTRAP => INTERFACE_TYPE_SNMP,
|
|
ITEM_TYPE_IPMI => INTERFACE_TYPE_IPMI,
|
|
ITEM_TYPE_ZABBIX => INTERFACE_TYPE_AGENT,
|
|
ITEM_TYPE_SIMPLE => INTERFACE_TYPE_OPT,
|
|
ITEM_TYPE_EXTERNAL => INTERFACE_TYPE_OPT,
|
|
ITEM_TYPE_SSH => INTERFACE_TYPE_OPT,
|
|
ITEM_TYPE_TELNET => INTERFACE_TYPE_OPT,
|
|
ITEM_TYPE_JMX => INTERFACE_TYPE_JMX,
|
|
ITEM_TYPE_HTTPAGENT => INTERFACE_TYPE_OPT
|
|
];
|
|
|
|
if (is_null($type)) {
|
|
return $types;
|
|
}
|
|
elseif (array_key_exists($type, $types)) {
|
|
return $types[$type];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Convert a list of interfaces to an array of interface type => interfaceids.
|
|
*
|
|
* @param array $interfaces List of (host) interfaces.
|
|
*
|
|
* @return array Interface IDs grouped by type.
|
|
*/
|
|
|
|
function interfaceIdsByType(array $interfaces) {
|
|
$interface_ids_by_type = [];
|
|
|
|
foreach ($interfaces as $interface) {
|
|
$interface_ids_by_type[$interface['type']][] = $interface['interfaceid'];
|
|
}
|
|
|
|
return $interface_ids_by_type;
|
|
}
|
|
|
|
/**
|
|
* Get parent templates for each given item.
|
|
*
|
|
* @param array $items An array of items.
|
|
* @param string $items[]['itemid'] ID of an item.
|
|
* @param string $items[]['templateid'] ID of parent template item.
|
|
* @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE,
|
|
* ZBX_FLAG_DISCOVERY_PROTOTYPE).
|
|
*
|
|
* @return array
|
|
*/
|
|
function getItemParentTemplates(array $items, $flag) {
|
|
$parent_itemids = [];
|
|
$data = [
|
|
'links' => [],
|
|
'templates' => []
|
|
];
|
|
|
|
foreach ($items as $item) {
|
|
if ($item['templateid'] != 0) {
|
|
$parent_itemids[$item['templateid']] = true;
|
|
$data['links'][$item['itemid']] = ['itemid' => $item['templateid']];
|
|
}
|
|
}
|
|
|
|
if (!$parent_itemids) {
|
|
return $data;
|
|
}
|
|
|
|
$all_parent_itemids = [];
|
|
$hostids = [];
|
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
$lld_ruleids = [];
|
|
}
|
|
|
|
do {
|
|
if ($flag == ZBX_FLAG_DISCOVERY_RULE) {
|
|
$db_items = API::DiscoveryRule()->get([
|
|
'output' => ['itemid', 'hostid', 'templateid'],
|
|
'itemids' => array_keys($parent_itemids)
|
|
]);
|
|
}
|
|
elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
$db_items = API::ItemPrototype()->get([
|
|
'output' => ['itemid', 'hostid', 'templateid'],
|
|
'itemids' => array_keys($parent_itemids),
|
|
'selectDiscoveryRule' => ['itemid']
|
|
]);
|
|
}
|
|
// ZBX_FLAG_DISCOVERY_NORMAL
|
|
else {
|
|
$db_items = API::Item()->get([
|
|
'output' => ['itemid', 'hostid', 'templateid'],
|
|
'itemids' => array_keys($parent_itemids),
|
|
'webitems' => true
|
|
]);
|
|
}
|
|
|
|
$all_parent_itemids += $parent_itemids;
|
|
$parent_itemids = [];
|
|
|
|
foreach ($db_items as $db_item) {
|
|
$data['templates'][$db_item['hostid']] = [];
|
|
$hostids[$db_item['itemid']] = $db_item['hostid'];
|
|
|
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
$lld_ruleids[$db_item['itemid']] = $db_item['discoveryRule']['itemid'];
|
|
}
|
|
|
|
if ($db_item['templateid'] != 0) {
|
|
if (!array_key_exists($db_item['templateid'], $all_parent_itemids)) {
|
|
$parent_itemids[$db_item['templateid']] = true;
|
|
}
|
|
|
|
$data['links'][$db_item['itemid']] = ['itemid' => $db_item['templateid']];
|
|
}
|
|
}
|
|
}
|
|
while ($parent_itemids);
|
|
|
|
foreach ($data['links'] as &$parent_item) {
|
|
$parent_item['hostid'] = array_key_exists($parent_item['itemid'], $hostids)
|
|
? $hostids[$parent_item['itemid']]
|
|
: 0;
|
|
|
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
$parent_item['lld_ruleid'] = array_key_exists($parent_item['itemid'], $lld_ruleids)
|
|
? $lld_ruleids[$parent_item['itemid']]
|
|
: 0;
|
|
}
|
|
}
|
|
unset($parent_item);
|
|
|
|
$db_templates = $data['templates']
|
|
? API::Template()->get([
|
|
'output' => ['name'],
|
|
'templateids' => array_keys($data['templates']),
|
|
'preservekeys' => true
|
|
])
|
|
: [];
|
|
|
|
$rw_templates = $db_templates
|
|
? API::Template()->get([
|
|
'output' => [],
|
|
'templateids' => array_keys($db_templates),
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
])
|
|
: [];
|
|
|
|
$data['templates'][0] = [];
|
|
|
|
foreach ($data['templates'] as $hostid => &$template) {
|
|
$template = array_key_exists($hostid, $db_templates)
|
|
? [
|
|
'hostid' => $hostid,
|
|
'name' => $db_templates[$hostid]['name'],
|
|
'permission' => array_key_exists($hostid, $rw_templates) ? PERM_READ_WRITE : PERM_READ
|
|
]
|
|
: [
|
|
'hostid' => $hostid,
|
|
'name' => _('Inaccessible template'),
|
|
'permission' => PERM_DENY
|
|
];
|
|
}
|
|
unset($template);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Returns a template prefix for selected item.
|
|
*
|
|
* @param string $itemid
|
|
* @param array $parent_templates The list of the templates, prepared by getItemParentTemplates() function.
|
|
* @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE,
|
|
* ZBX_FLAG_DISCOVERY_PROTOTYPE).
|
|
* @param bool $provide_links If this parameter is false, prefix will not contain links.
|
|
*
|
|
* @return array|null
|
|
*/
|
|
function makeItemTemplatePrefix($itemid, array $parent_templates, $flag, bool $provide_links) {
|
|
if (!array_key_exists($itemid, $parent_templates['links'])) {
|
|
return null;
|
|
}
|
|
|
|
while (array_key_exists($parent_templates['links'][$itemid]['itemid'], $parent_templates['links'])) {
|
|
$itemid = $parent_templates['links'][$itemid]['itemid'];
|
|
}
|
|
|
|
$template = $parent_templates['templates'][$parent_templates['links'][$itemid]['hostid']];
|
|
|
|
if ($provide_links && $template['permission'] == PERM_READ_WRITE) {
|
|
if ($flag == ZBX_FLAG_DISCOVERY_RULE) {
|
|
$url = (new CUrl('host_discovery.php'))
|
|
->setArgument('filter_set', '1')
|
|
->setArgument('filter_hostids', [$template['hostid']])
|
|
->setArgument('context', 'template');
|
|
}
|
|
elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
$url = (new CUrl('disc_prototypes.php'))
|
|
->setArgument('parent_discoveryid', $parent_templates['links'][$itemid]['lld_ruleid'])
|
|
->setArgument('context', 'template');
|
|
}
|
|
// ZBX_FLAG_DISCOVERY_NORMAL
|
|
else {
|
|
$url = (new CUrl('items.php'))
|
|
->setArgument('filter_set', '1')
|
|
->setArgument('filter_hostids', [$template['hostid']])
|
|
->setArgument('context', 'template');
|
|
}
|
|
|
|
$name = (new CLink($template['name'], $url))->addClass(ZBX_STYLE_LINK_ALT);
|
|
}
|
|
else {
|
|
$name = new CSpan($template['name']);
|
|
}
|
|
|
|
return [$name->addClass(ZBX_STYLE_GREY), NAME_DELIMITER];
|
|
}
|
|
|
|
/**
|
|
* Returns a list of item templates.
|
|
*
|
|
* @param string $itemid
|
|
* @param array $parent_templates The list of the templates, prepared by getItemParentTemplates() function.
|
|
* @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE,
|
|
* ZBX_FLAG_DISCOVERY_PROTOTYPE).
|
|
* @param bool $provide_links If this parameter is false, prefix will not contain links.
|
|
*
|
|
* @return array
|
|
*/
|
|
function makeItemTemplatesHtml($itemid, array $parent_templates, $flag, bool $provide_links) {
|
|
$list = [];
|
|
|
|
while (array_key_exists($itemid, $parent_templates['links'])) {
|
|
$template = $parent_templates['templates'][$parent_templates['links'][$itemid]['hostid']];
|
|
|
|
if ($provide_links && $template['permission'] == PERM_READ_WRITE) {
|
|
if ($flag == ZBX_FLAG_DISCOVERY_RULE) {
|
|
$url = (new CUrl('host_discovery.php'))
|
|
->setArgument('form', 'update')
|
|
->setArgument('itemid', $parent_templates['links'][$itemid]['itemid'])
|
|
->setArgument('context', 'template');
|
|
}
|
|
elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
$url = (new CUrl('disc_prototypes.php'))
|
|
->setArgument('form', 'update')
|
|
->setArgument('itemid', $parent_templates['links'][$itemid]['itemid'])
|
|
->setArgument('parent_discoveryid', $parent_templates['links'][$itemid]['lld_ruleid'])
|
|
->setArgument('context', 'template');
|
|
}
|
|
// ZBX_FLAG_DISCOVERY_NORMAL
|
|
else {
|
|
$url = (new CUrl('items.php'))
|
|
->setArgument('form', 'update')
|
|
->setArgument('itemid', $parent_templates['links'][$itemid]['itemid'])
|
|
->setArgument('context', 'template');
|
|
}
|
|
|
|
$name = new CLink($template['name'], $url);
|
|
}
|
|
else {
|
|
$name = (new CSpan($template['name']))->addClass(ZBX_STYLE_GREY);
|
|
}
|
|
|
|
array_unshift($list, $name, [NBSP(), RARR(), NBSP()]);
|
|
|
|
$itemid = $parent_templates['links'][$itemid]['itemid'];
|
|
}
|
|
|
|
if ($list) {
|
|
array_pop($list);
|
|
}
|
|
|
|
return $list;
|
|
}
|
|
|
|
/**
|
|
* Collect latest value and actual severity value for each item of Data overview table.
|
|
*
|
|
* @param array $db_items
|
|
* @param array $data
|
|
* @param int $show_suppressed
|
|
*
|
|
* @return array
|
|
*/
|
|
function getDataOverviewCellData(array $db_items, array $data, int $show_suppressed): array {
|
|
$history = Manager::History()->getLastValues($db_items, 1,
|
|
timeUnitToSeconds(CSettingsHelper::get(CSettingsHelper::HISTORY_PERIOD))
|
|
);
|
|
|
|
$db_triggers = getTriggersWithActualSeverity([
|
|
'output' => ['triggerid', 'priority', 'value'],
|
|
'selectItems' => ['itemid'],
|
|
'itemids' => array_keys($db_items),
|
|
'monitored' => true,
|
|
'preservekeys' => true
|
|
], ['show_suppressed' => $show_suppressed]);
|
|
|
|
$itemid_to_triggerids = [];
|
|
foreach ($db_triggers as $triggerid => $db_trigger) {
|
|
foreach ($db_trigger['items'] as $item) {
|
|
if (!array_key_exists($item['itemid'], $itemid_to_triggerids)) {
|
|
$itemid_to_triggerids[$item['itemid']] = [];
|
|
}
|
|
$itemid_to_triggerids[$item['itemid']][] = $triggerid;
|
|
}
|
|
}
|
|
|
|
// Apply values and trigger severity to each $data cell.
|
|
foreach ($data as &$data_clusters) {
|
|
foreach ($data_clusters as &$data_cluster) {
|
|
foreach ($data_cluster as &$item) {
|
|
$itemid = $item['itemid'];
|
|
|
|
if (array_key_exists($itemid, $itemid_to_triggerids)) {
|
|
$max_priority = -1;
|
|
$max_priority_triggerid = -1;
|
|
foreach ($itemid_to_triggerids[$itemid] as $triggerid) {
|
|
$trigger = $db_triggers[$triggerid];
|
|
|
|
// Bump lower priority triggers of value "true" ahead of triggers with value "false".
|
|
$multiplier = ($trigger['value'] == TRIGGER_VALUE_TRUE) ? TRIGGER_SEVERITY_COUNT : 0;
|
|
if ($trigger['priority'] + $multiplier > $max_priority) {
|
|
$max_priority_triggerid = $triggerid;
|
|
$max_priority = $trigger['priority'] + $multiplier;
|
|
}
|
|
}
|
|
$trigger = $db_triggers[$max_priority_triggerid];
|
|
}
|
|
else {
|
|
$trigger = null;
|
|
}
|
|
|
|
$item += [
|
|
'value' => array_key_exists($itemid, $history) ? $history[$itemid][0]['value'] : null,
|
|
'trigger' => $trigger
|
|
];
|
|
}
|
|
}
|
|
}
|
|
unset($data_clusters, $data_cluster, $item);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* @param array $groupids
|
|
* @param array $hostids
|
|
* @param array $tags
|
|
* @param int $evaltype
|
|
*
|
|
* @return array
|
|
*/
|
|
function getDataOverviewItems(?array $groupids, ?array $hostids, ?array $tags, int $evaltype): array {
|
|
if ($hostids === null) {
|
|
$limit = CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT);
|
|
$db_hosts = API::Host()->get([
|
|
'output' => ['name'],
|
|
'groupids' => $groupids,
|
|
'monitored_hosts' => true,
|
|
'with_monitored_items' => true,
|
|
'sortfield' => ['name'],
|
|
'limit' => $limit,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
CArrayHelper::sort($db_hosts, ['name']);
|
|
$db_hosts = array_slice($db_hosts, 0, CSettingsHelper::get(CSettingsHelper::MAX_OVERVIEW_TABLE_SIZE) + 1, true);
|
|
|
|
$hostids = array_keys($db_hosts);
|
|
}
|
|
|
|
$db_items = API::Item()->get([
|
|
'output' => ['itemid', 'hostid', 'name', 'value_type', 'units', 'valuemapid'],
|
|
'selectHosts' => ['name'],
|
|
'selectValueMap' => ['mappings'],
|
|
'hostids' => $hostids,
|
|
'groupids' => $groupids,
|
|
'evaltype' => $evaltype,
|
|
'tags' => $tags,
|
|
'monitored' => true,
|
|
'webitems' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
CArrayHelper::sort($db_items, [
|
|
['field' => 'name', 'order' => ZBX_SORT_UP],
|
|
['field' => 'itemid', 'order' => ZBX_SORT_UP]
|
|
]);
|
|
|
|
return $db_items;
|
|
}
|
|
|
|
/**
|
|
* @param array $groupids
|
|
* @param array $hostids
|
|
* @param array $filter
|
|
* @param array $filter['tags']
|
|
* @param int $filter['evaltype']
|
|
* @param int $filter['show_suppressed']
|
|
*
|
|
* @return array
|
|
*/
|
|
function getDataOverview(?array $groupids, ?array $hostids, array $filter): array {
|
|
$tags = (array_key_exists('tags', $filter) && $filter['tags']) ? $filter['tags'] : null;
|
|
$evaltype = array_key_exists('evaltype', $filter) ? $filter['evaltype'] : TAG_EVAL_TYPE_AND_OR;
|
|
|
|
$db_items = getDataOverviewItems($groupids, $hostids, $tags, $evaltype);
|
|
|
|
$data = [];
|
|
$item_counter = [];
|
|
$db_hosts = [];
|
|
|
|
foreach ($db_items as $db_item) {
|
|
$item_name = $db_item['name'];
|
|
$host_name = $db_item['hosts'][0]['name'];
|
|
$db_hosts[$db_item['hostid']] = $db_item['hosts'][0];
|
|
|
|
if (!array_key_exists($host_name, $item_counter)) {
|
|
$item_counter[$host_name] = [];
|
|
}
|
|
|
|
if (!array_key_exists($item_name, $item_counter[$host_name])) {
|
|
$item_counter[$host_name][$item_name] = 0;
|
|
}
|
|
|
|
$item_place = $item_counter[$host_name][$item_name];
|
|
$item_counter[$host_name][$item_name]++;
|
|
|
|
$item = [
|
|
'itemid' => $db_item['itemid'],
|
|
'value_type' => $db_item['value_type'],
|
|
'units' => $db_item['units'],
|
|
'valuemap' => $db_item['valuemap'],
|
|
'acknowledged' => array_key_exists('acknowledged', $db_item) ? $db_item['acknowledged'] : 0
|
|
];
|
|
|
|
if (array_key_exists('triggerid', $db_item)) {
|
|
$item += [
|
|
'triggerid' => $db_item['triggerid'],
|
|
'severity' => $db_item['priority'],
|
|
'tr_value' => $db_item['value']
|
|
];
|
|
}
|
|
else {
|
|
$item += [
|
|
'triggerid' => null,
|
|
'severity' => null,
|
|
'tr_value' => null
|
|
];
|
|
}
|
|
|
|
$data[$item_name][$item_place][$host_name] = $item;
|
|
}
|
|
|
|
CArrayHelper::sort($db_hosts, [
|
|
['field' => 'name', 'order' => ZBX_SORT_UP]
|
|
]);
|
|
|
|
$data_display_limit = (int) CSettingsHelper::get(CSettingsHelper::MAX_OVERVIEW_TABLE_SIZE);
|
|
$has_hidden_data = count($data) > $data_display_limit || count($db_hosts) > $data_display_limit;
|
|
$db_hosts = array_slice($db_hosts, 0, $data_display_limit, true);
|
|
$host_names = array_column($db_hosts, 'name', 'name');
|
|
|
|
$itemids = [];
|
|
$items_left = $data_display_limit;
|
|
|
|
foreach ($data as &$item_columns) {
|
|
if ($items_left != 0) {
|
|
$item_columns = array_slice($item_columns, 0, min($data_display_limit, $items_left));
|
|
$items_left -= count($item_columns);
|
|
}
|
|
else {
|
|
$item_columns = null;
|
|
break;
|
|
}
|
|
|
|
foreach ($item_columns as &$item_column) {
|
|
CArrayHelper::ksort($item_column);
|
|
$item_column = array_slice($item_column, 0, $data_display_limit, true);
|
|
|
|
foreach ($item_column as $host_name => $item) {
|
|
if (array_key_exists($host_name, $host_names)) {
|
|
$itemids[$item['itemid']] = true;
|
|
}
|
|
else {
|
|
unset($item_column[$host_name]);
|
|
}
|
|
}
|
|
}
|
|
unset($item_column);
|
|
|
|
$item_columns = array_filter($item_columns);
|
|
}
|
|
unset($item_columns);
|
|
|
|
$data = array_filter($data);
|
|
$data = array_slice($data, 0, $data_display_limit, true);
|
|
|
|
$has_hidden_data = $has_hidden_data || count($db_items) != count($itemids);
|
|
|
|
$db_items = array_intersect_key($db_items, $itemids);
|
|
$data = getDataOverviewCellData($db_items, $data, $filter['show_suppressed']);
|
|
|
|
return [$data, $db_hosts, $has_hidden_data];
|
|
}
|
|
|
|
/**
|
|
* Prepares interfaces select element with options.
|
|
*
|
|
* @param array $interfaces
|
|
*
|
|
* @return CSelect
|
|
*/
|
|
function getInterfaceSelect(array $interfaces): CSelect {
|
|
$interface_select = new CSelect('interfaceid');
|
|
|
|
/** @var CSelectOption[] $options_by_type */
|
|
$options_by_type = [];
|
|
|
|
$interface_select->addOption(new CSelectOption(0, _('None')));
|
|
|
|
foreach ($interfaces as $interface) {
|
|
$option = new CSelectOption($interface['interfaceid'], getHostInterface($interface));
|
|
|
|
if ($interface['type'] == INTERFACE_TYPE_SNMP) {
|
|
$option->setExtra('description', getSnmpInterfaceDescription($interface));
|
|
}
|
|
|
|
$options_by_type[$interface['type']][] = $option;
|
|
}
|
|
|
|
foreach (CItem::INTERFACE_TYPES_BY_PRIORITY as $interface_type) {
|
|
if (array_key_exists($interface_type, $options_by_type)) {
|
|
$interface_group = new CSelectOptionGroup((string) interfaceType2str($interface_type));
|
|
|
|
if ($interface_type == INTERFACE_TYPE_SNMP) {
|
|
$interface_group->setOptionTemplate('#{label}'.(new CDiv('#{description}'))->addClass('description'));
|
|
}
|
|
|
|
$interface_group->addOptions($options_by_type[$interface_type]);
|
|
|
|
$interface_select->addOptionGroup($interface_group);
|
|
}
|
|
}
|
|
|
|
return $interface_select;
|
|
}
|
|
|
|
/**
|
|
* Creates SNMP interface description.
|
|
*
|
|
* @param array $interface
|
|
* @param int $interface['details']['version'] Interface SNMP version.
|
|
* @param int $interface['details']['contextname'] Interface context name for SNMP version 3.
|
|
* @param int $interface['details']['community'] Interface community for SNMP non version 3 interface.
|
|
* @param int $interface['details']['securitylevel'] Security level for SNMP version 3 interface.
|
|
* @param int $interface['details']['authprotocol'] Authentication protocol for SNMP version 3 interface.
|
|
* @param int $interface['details']['privprotocol'] Privacy protocol for SNMP version 3 interface.
|
|
*
|
|
* @return string
|
|
*/
|
|
function getSnmpInterfaceDescription(array $interface): string {
|
|
$snmp_desc = [
|
|
_s('SNMPv%1$d', $interface['details']['version'])
|
|
];
|
|
|
|
if ($interface['details']['version'] == SNMP_V3) {
|
|
$snmp_desc[] = _('Context name').': '.$interface['details']['contextname'];
|
|
|
|
if ($interface['details']['securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV) {
|
|
[$interface['details']['authprotocol'] => $auth_protocol] = getSnmpV3AuthProtocols();
|
|
[$interface['details']['privprotocol'] => $priv_protocol] = getSnmpV3PrivProtocols();
|
|
|
|
$snmp_desc[] = '(priv: '.$priv_protocol.', auth: '.$auth_protocol.')';
|
|
}
|
|
elseif ($interface['details']['securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV) {
|
|
[$interface['details']['authprotocol'] => $auth_protocol] = getSnmpV3AuthProtocols();
|
|
|
|
$snmp_desc[] = '(auth: '.$auth_protocol.')';
|
|
}
|
|
|
|
} else {
|
|
$snmp_desc[] = _x('Community', 'SNMP Community').': '.$interface['details']['community'];
|
|
}
|
|
|
|
return implode(', ', $snmp_desc);
|
|
}
|
|
|
|
/**
|
|
* Named SNMPv3 authentication protocols.
|
|
*
|
|
* @return array
|
|
*/
|
|
function getSnmpV3AuthProtocols(): array {
|
|
return [
|
|
ITEM_SNMPV3_AUTHPROTOCOL_MD5 => 'MD5',
|
|
ITEM_SNMPV3_AUTHPROTOCOL_SHA1 => 'SHA1',
|
|
ITEM_SNMPV3_AUTHPROTOCOL_SHA224 => 'SHA224',
|
|
ITEM_SNMPV3_AUTHPROTOCOL_SHA256 => 'SHA256',
|
|
ITEM_SNMPV3_AUTHPROTOCOL_SHA384 => 'SHA384',
|
|
ITEM_SNMPV3_AUTHPROTOCOL_SHA512 => 'SHA512'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Named SNMPv3 privacy protocols.
|
|
*
|
|
* @return array
|
|
*/
|
|
function getSnmpV3PrivProtocols(): array {
|
|
return [
|
|
ITEM_SNMPV3_PRIVPROTOCOL_DES => 'DES',
|
|
ITEM_SNMPV3_PRIVPROTOCOL_AES128 => 'AES128',
|
|
ITEM_SNMPV3_PRIVPROTOCOL_AES192 => 'AES192',
|
|
ITEM_SNMPV3_PRIVPROTOCOL_AES256 => 'AES256',
|
|
ITEM_SNMPV3_PRIVPROTOCOL_AES192C => 'AES192C',
|
|
ITEM_SNMPV3_PRIVPROTOCOL_AES256C => 'AES256C'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array $item
|
|
* @param array $trigger
|
|
*
|
|
* @return CCol
|
|
*/
|
|
function getItemDataOverviewCell(array $item, ?array $trigger = null): CCol {
|
|
$ack = null;
|
|
$css = '';
|
|
$value = UNKNOWN_VALUE;
|
|
|
|
if ($trigger && $trigger['value'] == TRIGGER_VALUE_TRUE) {
|
|
$css = CSeverityHelper::getStyle((int) $trigger['priority']);
|
|
|
|
if ($trigger['problem']['acknowledged'] == 1) {
|
|
$ack = [' ', (new CSpan())->addClass(ZBX_ICON_CHECK)];
|
|
}
|
|
}
|
|
|
|
if ($item['value'] !== null) {
|
|
$value = $item['value_type'] == ITEM_VALUE_TYPE_BINARY
|
|
? italic(_('binary value'))->addClass(ZBX_STYLE_GREY)
|
|
: formatHistoryValue($item['value'], $item);
|
|
}
|
|
|
|
$col = (new CCol([$value, $ack]))
|
|
->addClass($css)
|
|
->addClass(ZBX_STYLE_NOWRAP)
|
|
->setMenuPopup(CMenuPopupHelper::getHistory($item['itemid']))
|
|
->addClass(ZBX_STYLE_CURSOR_POINTER);
|
|
|
|
return $col;
|
|
}
|
|
|
|
/**
|
|
* Prepare item value for displaying, apply value map and/or convert units.
|
|
*
|
|
* @see formatHistoryValueRaw
|
|
*
|
|
* @param int|float|string $value
|
|
* @param array $item
|
|
* @param bool $trim Whether to trim non-numeric value to a length of 20 characters.
|
|
* @param array $convert_options Options for unit conversion. See @convertUnitsRaw.
|
|
*
|
|
* @return string
|
|
*/
|
|
function formatHistoryValue($value, array $item, bool $trim = true, array $convert_options = []): string {
|
|
$formatted_value = formatHistoryValueRaw($value, $item, $trim, $convert_options);
|
|
|
|
return $formatted_value['value'].($formatted_value['units'] !== '' ? ' '.$formatted_value['units'] : '');
|
|
}
|
|
|
|
/**
|
|
* Prepare item value for displaying, apply value map and/or convert units.
|
|
*
|
|
* @param int|float|string $value
|
|
* @param array $item
|
|
* @param bool $trim Whether to trim non-numeric value to a length of 20 characters.
|
|
* @param array $convert_options Options for unit conversion. See @convertUnitsRaw.
|
|
*
|
|
* $item = [
|
|
* 'value_type' => (int) ITEM_VALUE_TYPE_FLOAT | ITEM_VALUE_TYPE_UINT64, ...
|
|
* 'units' => (string) Item units.
|
|
* 'valuemap' => (array) Item value map.
|
|
* ]
|
|
*
|
|
* @return array
|
|
*/
|
|
function formatHistoryValueRaw($value, array $item, bool $trim = true, array $convert_options = []): array {
|
|
$mapped_value = in_array($item['value_type'], [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_STR])
|
|
? CValueMapHelper::getMappedValue($item['value_type'], $value, $item['valuemap'])
|
|
: false;
|
|
|
|
switch ($item['value_type']) {
|
|
case ITEM_VALUE_TYPE_FLOAT:
|
|
case ITEM_VALUE_TYPE_UINT64:
|
|
if ($mapped_value !== false) {
|
|
return [
|
|
'value' => $mapped_value.' ('.$value.')',
|
|
'units' => '',
|
|
'is_mapped' => true
|
|
];
|
|
}
|
|
|
|
$converted_value = convertUnitsRaw([
|
|
'value' => $value,
|
|
'units' => $item['units']
|
|
] + $convert_options);
|
|
|
|
return [
|
|
'value' => $converted_value['value'],
|
|
'units' => $converted_value['units'],
|
|
'is_mapped' => false
|
|
];
|
|
|
|
case ITEM_VALUE_TYPE_STR:
|
|
case ITEM_VALUE_TYPE_TEXT:
|
|
case ITEM_VALUE_TYPE_LOG:
|
|
if ($trim && mb_strlen($value) > 20) {
|
|
$value = mb_substr($value, 0, 20).'...';
|
|
}
|
|
|
|
if ($mapped_value !== false) {
|
|
$value = $mapped_value.' ('.$value.')';
|
|
}
|
|
|
|
return [
|
|
'value' => $value,
|
|
'units' => '',
|
|
'is_mapped' => $mapped_value !== false
|
|
];
|
|
|
|
case ITEM_VALUE_TYPE_BINARY:
|
|
return [
|
|
'value' => _('binary value'),
|
|
'units' => '',
|
|
'is_mapped' => false
|
|
];
|
|
|
|
default:
|
|
return [
|
|
'value' => _('Unknown value type'),
|
|
'units' => '',
|
|
'is_mapped' => false
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check whether the unit of an item is binary or not.
|
|
*
|
|
* @param string $units
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isBinaryUnits(string $units): bool {
|
|
return $units === 'B' || $units === 'Bps';
|
|
}
|
|
|
|
/**
|
|
* Retrieves from DB historical data for items and applies functional calculations.
|
|
* If fails for some reason, returns null.
|
|
*
|
|
* @param array $item
|
|
* @param string $item['itemid'] ID of item
|
|
* @param string $item['value_type'] type of item, allowed: ITEM_VALUE_TYPE_FLOAT and ITEM_VALUE_TYPE_UINT64
|
|
* @param string $function function to apply to time period from param, allowed: min, max and avg
|
|
* @param string $parameter formatted parameter for function, example: "2w" meaning 2 weeks
|
|
*
|
|
* @return string|null item functional value from history
|
|
*/
|
|
function getItemFunctionalValue($item, $function, $parameter) {
|
|
// Check whether function is allowed and parameter is specified.
|
|
if (!in_array($function, ['min', 'max', 'avg']) || $parameter === '') {
|
|
return null;
|
|
}
|
|
|
|
// Check whether item type is allowed for min, max and avg functions.
|
|
if ($item['value_type'] != ITEM_VALUE_TYPE_FLOAT && $item['value_type'] != ITEM_VALUE_TYPE_UINT64) {
|
|
return null;
|
|
}
|
|
|
|
$number_parser = new CNumberParser(['with_size_suffix' => true, 'with_time_suffix' => true]);
|
|
|
|
if ($number_parser->parse($parameter) != CParser::PARSE_SUCCESS) {
|
|
return null;
|
|
}
|
|
|
|
$parameter = $number_parser->calcValue();
|
|
|
|
$time_from = time() - $parameter;
|
|
|
|
if ($time_from < 0 || $time_from > ZBX_MAX_DATE) {
|
|
return null;
|
|
}
|
|
|
|
return Manager::History()->getAggregatedValue($item, $function, $time_from);
|
|
}
|
|
|
|
/**
|
|
* Check if current time is within the given period.
|
|
*
|
|
* @param string $period time period format: "wd[-wd2],hh:mm-hh:mm"
|
|
* @param int $now current timestamp
|
|
*
|
|
* @return bool true - within period, false - out of period
|
|
*/
|
|
function checkTimePeriod($period, $now) {
|
|
if (sscanf($period, '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) {
|
|
if (sscanf($period, '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) {
|
|
// delay period format is wrong - skip
|
|
return false;
|
|
}
|
|
$d2 = $d1;
|
|
}
|
|
|
|
$tm = localtime($now, true);
|
|
$day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday'];
|
|
$sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec'];
|
|
|
|
$sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1;
|
|
$sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2;
|
|
|
|
return $d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2;
|
|
}
|
|
|
|
/**
|
|
* Get item minimum delay.
|
|
*
|
|
* @param string $delay
|
|
* @param array $flexible_intervals
|
|
*
|
|
* @return string
|
|
*/
|
|
function getItemDelay($delay, array $flexible_intervals) {
|
|
$delay = timeUnitToSeconds($delay);
|
|
|
|
if ($delay != 0 || !$flexible_intervals) {
|
|
return $delay;
|
|
}
|
|
|
|
$min_delay = SEC_PER_YEAR;
|
|
|
|
foreach ($flexible_intervals as $flexible_interval) {
|
|
$flexible_interval_parts = explode('/', $flexible_interval);
|
|
$flexible_delay = timeUnitToSeconds($flexible_interval_parts[0]);
|
|
|
|
$min_delay = min($min_delay, $flexible_delay);
|
|
}
|
|
|
|
return $min_delay;
|
|
}
|
|
|
|
/**
|
|
* Return delay value that is currently applicable
|
|
*
|
|
* @param int $delay default delay
|
|
* @param array $flexible_intervals array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm"
|
|
* @param int $now current timestamp
|
|
*
|
|
* @return int delay for a current timestamp
|
|
*/
|
|
function getCurrentDelay($delay, array $flexible_intervals, $now) {
|
|
if (!$flexible_intervals) {
|
|
return $delay;
|
|
}
|
|
|
|
$current_delay = -1;
|
|
|
|
foreach ($flexible_intervals as $flexible_interval) {
|
|
list($flexible_delay, $flexible_period) = explode('/', $flexible_interval);
|
|
$flexible_delay = (int) $flexible_delay;
|
|
|
|
if (($current_delay == -1 || $flexible_delay < $current_delay) && checkTimePeriod($flexible_period, $now)) {
|
|
$current_delay = $flexible_delay;
|
|
}
|
|
}
|
|
|
|
if ($current_delay == -1) {
|
|
return $delay;
|
|
}
|
|
|
|
return $current_delay;
|
|
}
|
|
|
|
/**
|
|
* Return time of next flexible interval
|
|
*
|
|
* @param array $flexible_intervals array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm"
|
|
* @param int $now current timestamp
|
|
* @param int $next_interval timestamp of a next interval
|
|
*
|
|
* @return bool false if no flexible intervals defined
|
|
*/
|
|
function getNextDelayInterval(array $flexible_intervals, $now, &$next_interval) {
|
|
if (!$flexible_intervals) {
|
|
return false;
|
|
}
|
|
|
|
$next = 0;
|
|
$tm = localtime($now, true);
|
|
$day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday'];
|
|
$sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec'];
|
|
|
|
foreach ($flexible_intervals as $flexible_interval) {
|
|
$flexible_interval_parts = explode('/', $flexible_interval);
|
|
|
|
if (sscanf($flexible_interval_parts[1], '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) {
|
|
if (sscanf($flexible_interval_parts[1], '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) {
|
|
continue;
|
|
}
|
|
$d2 = $d1;
|
|
}
|
|
|
|
$sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1;
|
|
$sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2;
|
|
|
|
// current period
|
|
if ($d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2) {
|
|
if ($next == 0 || $next > $now - $sec + $sec2) {
|
|
// the next second after the current interval's upper bound
|
|
$next = $now - $sec + $sec2;
|
|
}
|
|
}
|
|
// will be active today
|
|
elseif ($d1 <= $day && $d2 >= $day && $sec < $sec1) {
|
|
if ($next == 0 || $next > $now - $sec + $sec1) {
|
|
$next = $now - $sec + $sec1;
|
|
}
|
|
}
|
|
else {
|
|
$nextDay = ($day + 1 <= 7) ? $day + 1 : 1;
|
|
|
|
// will be active tomorrow
|
|
if ($d1 <= $nextDay && $nextDay <= $d2) {
|
|
if ($next == 0 || $next > $now - $sec + SEC_PER_DAY + $sec1) {
|
|
$next = $now - $sec + SEC_PER_DAY + $sec1;
|
|
}
|
|
}
|
|
// later in the future
|
|
else {
|
|
$dayDiff = -1;
|
|
|
|
if ($day < $d1) {
|
|
$dayDiff = $d1 - $day;
|
|
}
|
|
if ($day >= $d2) {
|
|
$dayDiff = ($d1 + 7) - $day;
|
|
}
|
|
if ($d1 <= $day && $day < $d2) {
|
|
// should never happen, could not deduce day difference
|
|
$dayDiff = -1;
|
|
}
|
|
if ($dayDiff != -1 && ($next == 0 || $next > $now - $sec + SEC_PER_DAY * $dayDiff + $sec1)) {
|
|
$next = $now - $sec + SEC_PER_DAY * $dayDiff + $sec1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($next != 0) {
|
|
$next_interval = $next;
|
|
}
|
|
|
|
return ($next != 0);
|
|
}
|
|
|
|
/**
|
|
* Calculate nextcheck timestamp for an item using flexible intervals.
|
|
*
|
|
* the parameter $flexible_intervals is an array if strings that are in the following format:
|
|
*
|
|
* +------------[;]<----------+
|
|
* | |
|
|
* ->+-[d/wd[-wd2],hh:mm-hh:mm]-+
|
|
*
|
|
* d - delay (0-n)
|
|
* wd, wd2 - day of week (1-7)
|
|
* hh - hours (0-24)
|
|
* mm - minutes (0-59)
|
|
*
|
|
* @param int $seed seed value applied to delay to spread item checks over the delay period
|
|
* @param string $delay default delay, can be overridden
|
|
* @param array $flexible_intervals array of flexible intervals
|
|
* @param int $now current timestamp
|
|
*
|
|
* @return int
|
|
*/
|
|
function calculateItemNextCheck($seed, $delay, $flexible_intervals, $now) {
|
|
/*
|
|
* Try to find the nearest 'nextcheck' value with condition 'now' < 'nextcheck' < 'now' + SEC_PER_YEAR
|
|
* If it is not possible to check the item within a year, fail.
|
|
*/
|
|
|
|
$t = $now;
|
|
$tMax = $now + SEC_PER_YEAR;
|
|
$try = 0;
|
|
|
|
while ($t < $tMax) {
|
|
// Calculate 'nextcheck' value for the current interval.
|
|
$currentDelay = getCurrentDelay($delay, $flexible_intervals, $t);
|
|
|
|
if ($currentDelay != 0) {
|
|
$nextCheck = $currentDelay * floor($t / $currentDelay) + ($seed % $currentDelay);
|
|
|
|
if ($try == 0) {
|
|
while ($nextCheck <= $t) {
|
|
$nextCheck += $currentDelay;
|
|
}
|
|
}
|
|
else {
|
|
while ($nextCheck < $t) {
|
|
$nextCheck += $currentDelay;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$nextCheck = ZBX_JAN_2038;
|
|
}
|
|
|
|
/*
|
|
* Is 'nextcheck' < end of the current interval and the end of the current interval
|
|
* is the beginning of the next interval - 1.
|
|
*/
|
|
if (getNextDelayInterval($flexible_intervals, $t, $nextInterval) && $nextCheck >= $nextInterval) {
|
|
// 'nextcheck' is beyond the current interval.
|
|
$t = $nextInterval;
|
|
$try++;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $nextCheck;
|
|
}
|
|
|
|
/*
|
|
* Description:
|
|
* Function returns true if http items exists in the $items array.
|
|
* The array should contain a field 'type'
|
|
*/
|
|
function httpItemExists($items) {
|
|
foreach ($items as $item) {
|
|
if ($item['type'] == ITEM_TYPE_HTTPTEST) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function getParamFieldNameByType($itemType) {
|
|
switch ($itemType) {
|
|
case ITEM_TYPE_SCRIPT:
|
|
return 'script';
|
|
case ITEM_TYPE_SSH:
|
|
case ITEM_TYPE_TELNET:
|
|
case ITEM_TYPE_JMX:
|
|
return 'params_es';
|
|
case ITEM_TYPE_DB_MONITOR:
|
|
return 'params_ap';
|
|
case ITEM_TYPE_CALCULATED:
|
|
return 'params_f';
|
|
default:
|
|
return 'params';
|
|
}
|
|
}
|
|
|
|
function getParamFieldLabelByType($itemType) {
|
|
switch ($itemType) {
|
|
case ITEM_TYPE_SCRIPT:
|
|
return _('Script');
|
|
case ITEM_TYPE_SSH:
|
|
case ITEM_TYPE_TELNET:
|
|
case ITEM_TYPE_JMX:
|
|
return _('Executed script');
|
|
case ITEM_TYPE_DB_MONITOR:
|
|
return _('SQL query');
|
|
case ITEM_TYPE_CALCULATED:
|
|
return _('Formula');
|
|
default:
|
|
return 'params';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get either one or all item preprocessing types.
|
|
* If $grouped set to true, returns group labels. Returns empty string if no specific type is found.
|
|
*
|
|
* Usage examples:
|
|
* - get_preprocessing_types(null, true, [5, 4, 2]) Returns array as defined.
|
|
* - get_preprocessing_types(4, true, [5, 4, 2]) Returns string: 'Trim'.
|
|
* - get_preprocessing_types(<wrong type>, true, [5, 4, 2]) Returns an empty string: ''.
|
|
* - get_preprocessing_types(null, false, [5, 12, 15, 16, 20]) Returns subarrays in one array maintaining index:
|
|
* [5] => Regular expression
|
|
* [12] => JSONPath
|
|
* [15] => Does not match regular expression
|
|
* [16] => Check for error in JSON
|
|
* [20] => Discard unchanged with heartbeat
|
|
*
|
|
* @param int $type Item preprocessing type.
|
|
* @param bool $grouped Group label flag. If specific type is given, this parameter does not matter.
|
|
* @param array $supported_types Array of supported pre-processing types. If none are given, empty array is returned.
|
|
*
|
|
* @return array|string
|
|
*/
|
|
function get_preprocessing_types($type = null, $grouped = true, array $supported_types = []) {
|
|
$types = [
|
|
ZBX_PREPROC_REGSUB => [
|
|
'group' => _('Text'),
|
|
'name' => _('Regular expression')
|
|
],
|
|
ZBX_PREPROC_STR_REPLACE => [
|
|
'group' => _('Text'),
|
|
'name' => _('Replace')
|
|
],
|
|
ZBX_PREPROC_TRIM => [
|
|
'group' => _('Text'),
|
|
'name' => _('Trim')
|
|
],
|
|
ZBX_PREPROC_RTRIM => [
|
|
'group' => _('Text'),
|
|
'name' => _('Right trim')
|
|
],
|
|
ZBX_PREPROC_LTRIM => [
|
|
'group' => _('Text'),
|
|
'name' => _('Left trim')
|
|
],
|
|
ZBX_PREPROC_XPATH => [
|
|
'group' => _('Structured data'),
|
|
'name' => _('XML XPath')
|
|
],
|
|
ZBX_PREPROC_JSONPATH => [
|
|
'group' => _('Structured data'),
|
|
'name' => _('JSONPath')
|
|
],
|
|
ZBX_PREPROC_CSV_TO_JSON => [
|
|
'group' => _('Structured data'),
|
|
'name' => _('CSV to JSON')
|
|
],
|
|
ZBX_PREPROC_XML_TO_JSON => [
|
|
'group' => _('Structured data'),
|
|
'name' => _('XML to JSON')
|
|
],
|
|
ZBX_PREPROC_SNMP_WALK_VALUE => [
|
|
'group' => _('SNMP'),
|
|
'name' => _('SNMP walk value')
|
|
],
|
|
ZBX_PREPROC_SNMP_WALK_TO_JSON => [
|
|
'group' => _('SNMP'),
|
|
'name' => _('SNMP walk to JSON')
|
|
],
|
|
ZBX_PREPROC_MULTIPLIER => [
|
|
'group' => _('Arithmetic'),
|
|
'name' => _('Custom multiplier')
|
|
],
|
|
ZBX_PREPROC_DELTA_VALUE => [
|
|
'group' => _x('Change', 'noun'),
|
|
'name' => _('Simple change')
|
|
],
|
|
ZBX_PREPROC_DELTA_SPEED => [
|
|
'group' => _x('Change', 'noun'),
|
|
'name' => _('Change per second')
|
|
],
|
|
ZBX_PREPROC_BOOL2DEC => [
|
|
'group' => _('Numeral systems'),
|
|
'name' => _('Boolean to decimal')
|
|
],
|
|
ZBX_PREPROC_OCT2DEC => [
|
|
'group' => _('Numeral systems'),
|
|
'name' => _('Octal to decimal')
|
|
],
|
|
ZBX_PREPROC_HEX2DEC => [
|
|
'group' => _('Numeral systems'),
|
|
'name' => _('Hexadecimal to decimal')
|
|
],
|
|
ZBX_PREPROC_SCRIPT => [
|
|
'group' => _('Custom scripts'),
|
|
'name' => _('JavaScript')
|
|
],
|
|
ZBX_PREPROC_VALIDATE_RANGE => [
|
|
'group' => _('Validation'),
|
|
'name' => _('In range')
|
|
],
|
|
ZBX_PREPROC_VALIDATE_REGEX => [
|
|
'group' => _('Validation'),
|
|
'name' => _('Matches regular expression')
|
|
],
|
|
ZBX_PREPROC_VALIDATE_NOT_REGEX => [
|
|
'group' => _('Validation'),
|
|
'name' => _('Does not match regular expression')
|
|
],
|
|
ZBX_PREPROC_ERROR_FIELD_JSON => [
|
|
'group' => _('Validation'),
|
|
'name' => _('Check for error in JSON')
|
|
],
|
|
ZBX_PREPROC_ERROR_FIELD_XML => [
|
|
'group' => _('Validation'),
|
|
'name' => _('Check for error in XML')
|
|
],
|
|
ZBX_PREPROC_ERROR_FIELD_REGEX => [
|
|
'group' => _('Validation'),
|
|
'name' => _('Check for error using regular expression')
|
|
],
|
|
ZBX_PREPROC_VALIDATE_NOT_SUPPORTED => [
|
|
'group' => _('Validation'),
|
|
'name' => _('Check for not supported value')
|
|
],
|
|
ZBX_PREPROC_THROTTLE_VALUE => [
|
|
'group' => _('Throttling'),
|
|
'name' => _('Discard unchanged')
|
|
],
|
|
ZBX_PREPROC_THROTTLE_TIMED_VALUE => [
|
|
'group' => _('Throttling'),
|
|
'name' => _('Discard unchanged with heartbeat')
|
|
],
|
|
ZBX_PREPROC_PROMETHEUS_PATTERN => [
|
|
'group' => _('Prometheus'),
|
|
'name' => _('Prometheus pattern')
|
|
],
|
|
ZBX_PREPROC_PROMETHEUS_TO_JSON => [
|
|
'group' => _('Prometheus'),
|
|
'name' => _('Prometheus to JSON')
|
|
]
|
|
];
|
|
|
|
$filtered_types = [];
|
|
|
|
foreach ($types as $_type => $data) {
|
|
if (in_array($_type, $supported_types)) {
|
|
$filtered_types[$data['group']][$_type] = $data['name'];
|
|
}
|
|
}
|
|
|
|
$groups = [];
|
|
|
|
foreach ($filtered_types as $label => $types) {
|
|
$groups[] = [
|
|
'label' => $label,
|
|
'types' => $types
|
|
];
|
|
}
|
|
|
|
if ($type !== null) {
|
|
foreach ($groups as $group) {
|
|
if (array_key_exists($type, $group['types'])) {
|
|
return $group['types'][$type];
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
elseif ($grouped) {
|
|
return $groups;
|
|
}
|
|
else {
|
|
$types = [];
|
|
|
|
foreach ($groups as $group) {
|
|
$types += $group['types'];
|
|
}
|
|
|
|
return $types;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Quoting $param if it contain special characters.
|
|
*
|
|
* @param string $param
|
|
* @param bool $forced
|
|
*
|
|
* @return string
|
|
*/
|
|
function quoteItemKeyParam($param, $forced = false) {
|
|
if (!$forced) {
|
|
if (!isset($param[0]) || ($param[0] != '"' && false === strpbrk($param, ',]'))) {
|
|
return $param;
|
|
}
|
|
}
|
|
|
|
return '"'.str_replace('"', '\\"', $param).'"';
|
|
}
|
|
|
|
/**
|
|
* Expands item name and for dependent item master item name.
|
|
*
|
|
* @param array $items Array of items.
|
|
* @param string $data_source 'items' or 'itemprototypes'.
|
|
*
|
|
* @return array
|
|
*/
|
|
function expandItemNamesWithMasterItems($items, $data_source) {
|
|
$itemids = [];
|
|
$master_itemids = [];
|
|
|
|
foreach ($items as $item_index => &$item) {
|
|
if ($item['type'] == ITEM_TYPE_DEPENDENT) {
|
|
$master_itemids[$item['master_itemid']] = true;
|
|
}
|
|
|
|
// The "source" is required to tell the frontend where the link should point at - item or item prototype.
|
|
$item['source'] = $data_source;
|
|
$itemids[$item_index] = $item['itemid'];
|
|
}
|
|
unset($item);
|
|
|
|
$master_itemids = array_diff(array_keys($master_itemids), $itemids);
|
|
|
|
if ($master_itemids) {
|
|
$options = [
|
|
'output' => ['itemid', 'type', 'name'],
|
|
'itemids' => $master_itemids,
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
];
|
|
$master_items = API::Item()->get($options + ['webitems' => true]);
|
|
|
|
foreach ($master_items as &$master_item) {
|
|
$master_item['source'] = 'items';
|
|
}
|
|
unset($master_item);
|
|
|
|
$master_item_prototypes = API::ItemPrototype()->get($options);
|
|
|
|
foreach ($master_item_prototypes as &$master_item_prototype) {
|
|
$master_item_prototype['source'] = 'itemprototypes';
|
|
}
|
|
unset($master_item_prototype);
|
|
|
|
$master_items += $master_item_prototypes;
|
|
}
|
|
|
|
foreach ($items as &$item) {
|
|
if ($item['type'] == ITEM_TYPE_DEPENDENT) {
|
|
$master_itemid = $item['master_itemid'];
|
|
$items_index = array_search($master_itemid, $itemids);
|
|
$item['master_item'] = array_fill_keys(['name', 'type', 'source'], '');
|
|
$item['master_item'] = ($items_index === false)
|
|
? array_intersect_key($master_items[$master_itemid], $item['master_item'])
|
|
: array_intersect_key($items[$items_index], $item['master_item']);
|
|
$item['master_item']['itemid'] = $master_itemid;
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of allowed item types for "Check now" functionality.
|
|
*
|
|
* @return array
|
|
*/
|
|
function checkNowAllowedTypes() {
|
|
return [
|
|
ITEM_TYPE_ZABBIX,
|
|
ITEM_TYPE_SIMPLE,
|
|
ITEM_TYPE_INTERNAL,
|
|
ITEM_TYPE_EXTERNAL,
|
|
ITEM_TYPE_DB_MONITOR,
|
|
ITEM_TYPE_IPMI,
|
|
ITEM_TYPE_SSH,
|
|
ITEM_TYPE_TELNET,
|
|
ITEM_TYPE_CALCULATED,
|
|
ITEM_TYPE_JMX,
|
|
ITEM_TYPE_DEPENDENT,
|
|
ITEM_TYPE_HTTPAGENT,
|
|
ITEM_TYPE_SNMP,
|
|
ITEM_TYPE_SCRIPT
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Validates update interval for items, item prototypes and low-level discovery rules and their overrides.
|
|
*
|
|
* @param CUpdateIntervalParser $parser [IN] Parser used for delay validation.
|
|
* @param string $value [IN] Update interval to parse and validate.
|
|
* @param string $field_name [IN] Frontend or API field name in the error
|
|
* @param string $error [OUT] Returned error string if delay validation fails.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function validateDelay(CUpdateIntervalParser $parser, $field_name, $value, &$error) {
|
|
if ($parser->parse($value) != CParser::PARSE_SUCCESS) {
|
|
$error = _s('Incorrect value for field "%1$s": %2$s.', $field_name, _('invalid delay'));
|
|
|
|
return false;
|
|
}
|
|
|
|
$delay = $parser->getDelay();
|
|
|
|
if ($delay[0] !== '{') {
|
|
$delay_sec = timeUnitToSeconds($delay);
|
|
$intervals = $parser->getIntervals();
|
|
$flexible_intervals = $parser->getIntervals(ITEM_DELAY_FLEXIBLE);
|
|
$has_scheduling_intervals = (bool) $parser->getIntervals(ITEM_DELAY_SCHEDULING);
|
|
$has_macros = false;
|
|
|
|
foreach ($intervals as $interval) {
|
|
if (strpos($interval['interval'], '{') !== false) {
|
|
$has_macros = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If delay is 0, there must be at least one either flexible or scheduling interval.
|
|
if ($delay_sec == 0 && !$intervals) {
|
|
$error = _('Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.');
|
|
|
|
return false;
|
|
}
|
|
elseif ($delay_sec < 0 || $delay_sec > SEC_PER_DAY) {
|
|
$error = _('Item will not be refreshed. Update interval should be between 1s and 1d. Also Scheduled/Flexible intervals can be used.');
|
|
|
|
return false;
|
|
}
|
|
|
|
// If there are scheduling intervals or intervals with macros, skip the next check calculation.
|
|
if (!$has_macros && !$has_scheduling_intervals && $flexible_intervals
|
|
&& calculateItemNextCheck(0, $delay_sec, $flexible_intervals, time()) == ZBX_JAN_2038) {
|
|
$error = _('Item will not be refreshed. Please enter a correct update interval.');
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Normalizes item preprocessing step parameters after item preprocessing form submit.
|
|
*
|
|
* @param array $preprocessing Array of item preprocessing steps, as received from form submit.
|
|
*
|
|
* @return array
|
|
*/
|
|
function normalizeItemPreprocessingSteps(array $preprocessing): array {
|
|
foreach ($preprocessing as &$step) {
|
|
switch ($step['type']) {
|
|
case ZBX_PREPROC_MULTIPLIER:
|
|
case ZBX_PREPROC_PROMETHEUS_TO_JSON:
|
|
$step['params'] = trim($step['params'][0]);
|
|
break;
|
|
|
|
case ZBX_PREPROC_RTRIM:
|
|
case ZBX_PREPROC_LTRIM:
|
|
case ZBX_PREPROC_TRIM:
|
|
case ZBX_PREPROC_XPATH:
|
|
case ZBX_PREPROC_JSONPATH:
|
|
case ZBX_PREPROC_VALIDATE_REGEX:
|
|
case ZBX_PREPROC_VALIDATE_NOT_REGEX:
|
|
case ZBX_PREPROC_ERROR_FIELD_JSON:
|
|
case ZBX_PREPROC_ERROR_FIELD_XML:
|
|
case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
|
|
case ZBX_PREPROC_SCRIPT:
|
|
$step['params'] = $step['params'][0];
|
|
break;
|
|
|
|
case ZBX_PREPROC_SNMP_WALK_VALUE:
|
|
case ZBX_PREPROC_VALIDATE_RANGE:
|
|
foreach ($step['params'] as &$param) {
|
|
$param = trim($param);
|
|
}
|
|
unset($param);
|
|
|
|
$step['params'] = implode("\n", $step['params']);
|
|
break;
|
|
|
|
case ZBX_PREPROC_PROMETHEUS_PATTERN:
|
|
foreach ($step['params'] as &$param) {
|
|
$param = trim($param);
|
|
}
|
|
unset($param);
|
|
|
|
if (in_array($step['params'][1], [ZBX_PREPROC_PROMETHEUS_SUM, ZBX_PREPROC_PROMETHEUS_MIN,
|
|
ZBX_PREPROC_PROMETHEUS_MAX, ZBX_PREPROC_PROMETHEUS_AVG, ZBX_PREPROC_PROMETHEUS_COUNT])) {
|
|
$step['params'][2] = $step['params'][1];
|
|
$step['params'][1] = ZBX_PREPROC_PROMETHEUS_FUNCTION;
|
|
}
|
|
|
|
if (!array_key_exists(2, $step['params'])) {
|
|
$step['params'][2] = '';
|
|
}
|
|
|
|
$step['params'] = implode("\n", $step['params']);
|
|
break;
|
|
|
|
case ZBX_PREPROC_REGSUB:
|
|
case ZBX_PREPROC_ERROR_FIELD_REGEX:
|
|
case ZBX_PREPROC_STR_REPLACE:
|
|
$step['params'] = implode("\n", $step['params']);
|
|
break;
|
|
|
|
case ZBX_PREPROC_CSV_TO_JSON:
|
|
if (!array_key_exists(2, $step['params'])) {
|
|
$step['params'][2] = ZBX_PREPROC_CSV_NO_HEADER;
|
|
}
|
|
$step['params'] = implode("\n", $step['params']);
|
|
break;
|
|
|
|
case ZBX_PREPROC_SNMP_WALK_TO_JSON:
|
|
$step['params'] = array_values($step['params']);
|
|
|
|
$step['params'] = implode("\n", array_map(function (string $value): string {
|
|
return trim($value);
|
|
}, $step['params']));
|
|
break;
|
|
|
|
default:
|
|
$step['params'] = '';
|
|
}
|
|
|
|
$step += [
|
|
'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
|
|
'error_handler_params' => ''
|
|
];
|
|
|
|
// Remove fictional fields that don't belong to DB and API.
|
|
unset($step['sortorder'], $step['on_fail']);
|
|
}
|
|
unset($step);
|
|
|
|
return $preprocessing;
|
|
}
|
|
|
|
/**
|
|
* Check that the given key is not equal to the example value presented for a specific type.
|
|
*
|
|
* @param int $type
|
|
* @param string $key
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isItemExampleKey(int $type, string $key): bool {
|
|
if (($type == ITEM_TYPE_DB_MONITOR && $key === ZBX_DEFAULT_KEY_DB_MONITOR)
|
|
|| ($type == ITEM_TYPE_SSH && $key === ZBX_DEFAULT_KEY_SSH)
|
|
|| ($type == ITEM_TYPE_TELNET && $key === ZBX_DEFAULT_KEY_TELNET)) {
|
|
error(_('Check the key, please. Default example was passed.'));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check the format of the given custom intervals. Unset the custom intervals with empty values.
|
|
*
|
|
* @param array $delay_flex
|
|
* @param bool $lldmacros
|
|
*/
|
|
function isValidCustomIntervals(array &$delay_flex, bool $lldmacros = false): bool {
|
|
if (!$delay_flex) {
|
|
return true;
|
|
}
|
|
|
|
$simple_interval_parser = new CSimpleIntervalParser([
|
|
'usermacros' => true,
|
|
'lldmacros' => $lldmacros
|
|
]);
|
|
|
|
$time_period_parser = new CTimePeriodParser([
|
|
'usermacros' => true,
|
|
'lldmacros' => $lldmacros
|
|
]);
|
|
|
|
$scheduling_interval_parser = new CSchedulingIntervalParser([
|
|
'usermacros' => true,
|
|
'lldmacros' => $lldmacros
|
|
]);
|
|
|
|
foreach ($delay_flex as $i => $interval) {
|
|
if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
|
|
if ($interval['delay'] === '' && $interval['period'] === '') {
|
|
unset($delay_flex[$i]);
|
|
continue;
|
|
}
|
|
|
|
if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
|
|
error(_s('Invalid interval "%1$s".', $interval['delay']));
|
|
|
|
return false;
|
|
}
|
|
elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
|
|
error(_s('Invalid interval "%1$s".', $interval['period']));
|
|
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
if ($interval['schedule'] === '') {
|
|
unset($delay_flex[$i]);
|
|
continue;
|
|
}
|
|
|
|
if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
|
|
error(_s('Invalid interval "%1$s".', $interval['schedule']));
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get all given delay intervals as string in API format.
|
|
*
|
|
* @param string $delay
|
|
* @param array $delay_flex
|
|
*
|
|
* @return string
|
|
*/
|
|
function getDelayWithCustomIntervals(string $delay, array $delay_flex): string {
|
|
foreach ($delay_flex as $interval) {
|
|
if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
|
|
$delay .= ';'.$interval['delay'].'/'.$interval['period'];
|
|
}
|
|
else {
|
|
$delay .= ';'.$interval['schedule'];
|
|
}
|
|
}
|
|
|
|
return $delay;
|
|
}
|
|
|
|
/**
|
|
* Format tags received via form for API input.
|
|
*
|
|
* @param array $tags Array of item tags, as received from form submit.
|
|
*
|
|
* @return array
|
|
*/
|
|
function prepareItemTags(array $tags): array {
|
|
foreach ($tags as $key => $tag) {
|
|
if ($tag['tag'] === '' && $tag['value'] === '') {
|
|
unset($tags[$key]);
|
|
}
|
|
elseif (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) {
|
|
unset($tags[$key]);
|
|
}
|
|
else {
|
|
unset($tags[$key]['type']);
|
|
}
|
|
}
|
|
|
|
return $tags;
|
|
}
|
|
|
|
/**
|
|
* Format LLD macro paths received via form for API input.
|
|
*
|
|
* @param array $macro_paths Array of LLD macro paths, as received from form submit.
|
|
*
|
|
* @return array
|
|
*/
|
|
function prepareLldMacroPaths(array $macro_paths): array {
|
|
foreach ($macro_paths as $i => &$macro_path) {
|
|
if ($macro_path['lld_macro'] === '' && $macro_path['path'] === '') {
|
|
unset($macro_paths[$i]);
|
|
continue;
|
|
}
|
|
|
|
$macro_path['lld_macro'] = mb_strtoupper($macro_path['lld_macro']);
|
|
}
|
|
unset($macro_path);
|
|
|
|
return array_values($macro_paths);
|
|
}
|
|
|
|
/**
|
|
* Format LLD rule filter data received via form for API input.
|
|
*
|
|
* @param array $filter Array of LLD filters, as received from form submit.
|
|
*
|
|
* @return array
|
|
*/
|
|
function prepareLldFilter(array $filter): array {
|
|
foreach ($filter['conditions'] as $i => &$condition) {
|
|
if ($condition['macro'] === '' && $condition['value'] === '') {
|
|
unset($filter['conditions'][$i]);
|
|
continue;
|
|
}
|
|
|
|
$condition['macro'] = mb_strtoupper($condition['macro']);
|
|
|
|
if ($filter['evaltype'] != CONDITION_EVAL_TYPE_EXPRESSION) {
|
|
$condition['formulaid'] = '';
|
|
}
|
|
}
|
|
unset($condition);
|
|
|
|
$filter['conditions'] = array_values($filter['conditions']);
|
|
|
|
if ($filter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION && count($filter['conditions']) <= 1) {
|
|
$filter['evaltype'] = CONDITION_EVAL_TYPE_AND_OR;
|
|
$filter['formula'] = '';
|
|
|
|
if ($filter['conditions']) {
|
|
$filter['conditions'][0]['formulaid'] = '';
|
|
}
|
|
}
|
|
|
|
if ($filter['evaltype'] != CONDITION_EVAL_TYPE_EXPRESSION) {
|
|
$filter['formula'] = '';
|
|
}
|
|
|
|
return $filter;
|
|
}
|
|
|
|
/**
|
|
* Format LLD rule overrides data received via form for API input.
|
|
*
|
|
* @param array $overrides Array of LLD overrides, as received from form submit.
|
|
* @param array|null $db_item
|
|
* @param array $db_item['overrides']
|
|
*
|
|
* @return array
|
|
*/
|
|
function prepareLldOverrides(array $overrides, ?array $db_item): array {
|
|
$db_overrides = $db_item !== null && $overrides ? array_column($db_item['overrides'], null, 'step') : [];
|
|
|
|
foreach ($overrides as &$override) {
|
|
if (!array_key_exists($override['step'], $db_overrides)
|
|
&& !array_key_exists('conditions', $override['filter'])) {
|
|
unset($override['filter']);
|
|
}
|
|
elseif (!array_key_exists('conditions', $override['filter'])) {
|
|
$override['filter']['conditions'] = [];
|
|
}
|
|
|
|
if (array_key_exists('filter', $override)) {
|
|
$override['filter'] = prepareLldFilter([
|
|
'evaltype' => $override['filter']['evaltype'],
|
|
'formula' => $override['filter']['formula'],
|
|
'conditions' => $override['filter']['conditions']
|
|
]);
|
|
}
|
|
|
|
if (!array_key_exists('operations', $override)) {
|
|
$override['operations'] = [];
|
|
}
|
|
}
|
|
unset($override);
|
|
|
|
return $overrides;
|
|
}
|
|
|
|
/**
|
|
* Format query fields received via form for API input.
|
|
*
|
|
* @param array $query_fields
|
|
*
|
|
* @return array
|
|
*/
|
|
function prepareItemQueryFields(array $query_fields): array {
|
|
if ($query_fields) {
|
|
$_query_fields = [];
|
|
|
|
foreach ($query_fields['name'] as $index => $key) {
|
|
$value = $query_fields['value'][$index];
|
|
$sortorder = $query_fields['sortorder'][$index];
|
|
|
|
if ($key !== '' || $value !== '') {
|
|
$_query_fields[$sortorder] = [$key => $value];
|
|
}
|
|
}
|
|
|
|
ksort($_query_fields);
|
|
$query_fields = array_values($_query_fields);
|
|
}
|
|
|
|
return $query_fields;
|
|
}
|
|
|
|
/**
|
|
* Format headers field received via form for API input.
|
|
*
|
|
* @param array $headers
|
|
*
|
|
* @return array
|
|
*/
|
|
function prepareItemHeaders(array $headers): array {
|
|
if ($headers) {
|
|
$_headers = [];
|
|
|
|
foreach ($headers['name'] as $i => $name) {
|
|
$value = $headers['value'][$i];
|
|
|
|
if ($name === '' && $value === '') {
|
|
continue;
|
|
}
|
|
|
|
$_headers[$name] = $value;
|
|
}
|
|
|
|
$headers = $_headers;
|
|
}
|
|
|
|
return $headers;
|
|
}
|
|
|
|
/**
|
|
* Format parameters field received via form for API input.
|
|
*
|
|
* @param array $parameters
|
|
*
|
|
* @return array
|
|
*/
|
|
function prepareItemParameters(array $parameters): array {
|
|
$_parameters = [];
|
|
|
|
if (is_array($parameters) && array_key_exists('name', $parameters)
|
|
&& array_key_exists('value', $parameters)) {
|
|
foreach ($parameters['name'] as $index => $name) {
|
|
if (array_key_exists($index, $parameters['value'])
|
|
&& ($name !== '' || $parameters['value'][$index] !== '')) {
|
|
$_parameters[] = [
|
|
'name' => $name,
|
|
'value' => $parameters['value'][$index]
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $_parameters;
|
|
}
|
|
|
|
/**
|
|
* Get sanitized item fields of given input.
|
|
*
|
|
* @param array $input
|
|
* @param string $input['templateid']
|
|
* @param int $input['flags']
|
|
* @param int $input['type']
|
|
* @param string $input['key_']
|
|
* @param int $input['value_type']
|
|
* @param int $input['authtype']
|
|
* @param int $input['allow_traps']
|
|
* @param int $input['hosts'][0]['status']
|
|
*
|
|
* @return array
|
|
*/
|
|
function getSanitizedItemFields(array $input): array {
|
|
$field_names = getMainItemFieldNames($input);
|
|
|
|
if ($input['flags'] != ZBX_FLAG_DISCOVERY_CREATED) {
|
|
$field_names = array_merge($field_names, getTypeItemFieldNames($input));
|
|
$field_names = getConditionalItemFieldNames($field_names, $input);
|
|
}
|
|
|
|
return array_intersect_key($input, array_flip($field_names));
|
|
}
|
|
|
|
/**
|
|
* Get main item fields of given input.
|
|
*
|
|
* @param array $input
|
|
* @param string $input['templateid']
|
|
* @param int $input['flags']
|
|
*
|
|
* @return array
|
|
*/
|
|
function getMainItemFieldNames(array $input): array {
|
|
switch ($input['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
if ($input['templateid'] == 0) {
|
|
return ['name', 'type', 'key_', 'value_type', 'units', 'history', 'trends', 'valuemapid',
|
|
'inventory_link', 'logtimefmt', 'description', 'status', 'tags', 'preprocessing'
|
|
];
|
|
}
|
|
else {
|
|
return ['history', 'trends', 'inventory_link', 'description', 'status', 'tags'];
|
|
}
|
|
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
if ($input['templateid'] == 0) {
|
|
$field_names = ['name', 'type', 'key_', 'lifetime', 'description', 'status', 'preprocessing',
|
|
'lld_macro_paths', 'overrides'
|
|
];
|
|
}
|
|
else {
|
|
$field_names = ['lifetime', 'description', 'status'];
|
|
}
|
|
|
|
if (array_key_exists('itemid', $input) || $input['filter']['conditions']) {
|
|
$field_names[] = 'filter';
|
|
}
|
|
|
|
return $field_names;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
if ($input['templateid'] == 0) {
|
|
return ['name', 'type', 'key_', 'value_type', 'units', 'history', 'trends', 'valuemapid', 'logtimefmt',
|
|
'description', 'status', 'discover', 'tags', 'preprocessing'
|
|
];
|
|
}
|
|
else {
|
|
return ['history', 'trends', 'description', 'status', 'discover', 'tags'];
|
|
}
|
|
|
|
case ZBX_FLAG_DISCOVERY_CREATED:
|
|
return ['status'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get item field names of the given type and template ID.
|
|
*
|
|
* @param array $input
|
|
* @param string $input['templateid']
|
|
* @param int $input['type']
|
|
*/
|
|
function getTypeItemFieldNames(array $input): array {
|
|
switch ($input['type']) {
|
|
case ITEM_TYPE_ZABBIX:
|
|
return ['interfaceid', 'delay'];
|
|
|
|
case ITEM_TYPE_TRAPPER:
|
|
return ['trapper_hosts'];
|
|
|
|
case ITEM_TYPE_SIMPLE:
|
|
return ['interfaceid', 'username', 'password', 'delay'];
|
|
|
|
case ITEM_TYPE_INTERNAL:
|
|
return ['delay'];
|
|
|
|
case ITEM_TYPE_ZABBIX_ACTIVE:
|
|
return ['delay'];
|
|
|
|
case ITEM_TYPE_EXTERNAL:
|
|
return ['interfaceid', 'delay'];
|
|
|
|
case ITEM_TYPE_DB_MONITOR:
|
|
return ['username', 'password', 'params', 'delay'];
|
|
|
|
case ITEM_TYPE_IPMI:
|
|
if ($input['templateid'] == 0) {
|
|
return ['interfaceid', 'ipmi_sensor', 'delay'];
|
|
}
|
|
else {
|
|
return ['interfaceid', 'delay'];
|
|
}
|
|
|
|
case ITEM_TYPE_SSH:
|
|
return ['interfaceid', 'authtype', 'username', 'publickey', 'privatekey', 'password', 'params', 'delay'];
|
|
|
|
case ITEM_TYPE_TELNET:
|
|
return ['interfaceid', 'username', 'password', 'params', 'delay'];
|
|
|
|
case ITEM_TYPE_CALCULATED:
|
|
return ['params', 'delay'];
|
|
|
|
case ITEM_TYPE_JMX:
|
|
if ($input['templateid'] == 0) {
|
|
return ['interfaceid', 'jmx_endpoint', 'username', 'password', 'delay'];
|
|
}
|
|
else {
|
|
return ['interfaceid', 'username', 'password', 'delay'];
|
|
}
|
|
|
|
case ITEM_TYPE_SNMPTRAP:
|
|
return ['interfaceid'];
|
|
|
|
case ITEM_TYPE_DEPENDENT:
|
|
if ($input['templateid'] == 0) {
|
|
return ['master_itemid'];
|
|
}
|
|
|
|
return [];
|
|
|
|
case ITEM_TYPE_HTTPAGENT:
|
|
if ($input['templateid'] == 0) {
|
|
return ['url', 'query_fields', 'request_method', 'post_type', 'posts', 'headers', 'status_codes',
|
|
'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy', 'interfaceid', 'authtype',
|
|
'username', 'password', 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file',
|
|
'ssl_key_password', 'timeout', 'delay', 'allow_traps', 'trapper_hosts'
|
|
];
|
|
}
|
|
else {
|
|
return ['interfaceid', 'delay', 'allow_traps', 'trapper_hosts'];
|
|
}
|
|
|
|
case ITEM_TYPE_SNMP:
|
|
if ($input['templateid'] == 0) {
|
|
return ['interfaceid', 'snmp_oid', 'delay'];
|
|
}
|
|
else {
|
|
return ['interfaceid', 'delay'];
|
|
}
|
|
|
|
case ITEM_TYPE_SCRIPT:
|
|
if ($input['templateid'] == 0) {
|
|
return ['parameters', 'params', 'timeout', 'delay'];
|
|
}
|
|
else {
|
|
return ['delay'];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get item field names excluding those that don't match a specific conditions.
|
|
*
|
|
* @param array $field_names
|
|
* @param array $input
|
|
* @param int $input['type']
|
|
* @param string $input['key_']
|
|
* @param int $input['value_type']
|
|
* @param int $input['authtype']
|
|
* @param int $input['allow_traps']
|
|
* @param int $input['hosts'][0]['status']
|
|
*
|
|
* @return array
|
|
*/
|
|
function getConditionalItemFieldNames(array $field_names, array $input): array {
|
|
return array_filter($field_names, static function ($field_name) use ($input): bool {
|
|
switch ($field_name) {
|
|
case 'units':
|
|
case 'trends':
|
|
return in_array($input['value_type'], [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64]);
|
|
|
|
case 'valuemapid':
|
|
return in_array($input['value_type'],
|
|
[ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64]
|
|
);
|
|
|
|
case 'inventory_link':
|
|
return in_array($input['value_type'],
|
|
[ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT]
|
|
);
|
|
|
|
case 'logtimefmt':
|
|
return $input['value_type'] == ITEM_VALUE_TYPE_LOG;
|
|
|
|
case 'interfaceid':
|
|
return in_array($input['hosts'][0]['status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
|
|
|
|
case 'username':
|
|
case 'password':
|
|
return $input['type'] != ITEM_TYPE_HTTPAGENT || in_array($input['authtype'],
|
|
[ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS, ZBX_HTTP_AUTH_DIGEST]
|
|
);
|
|
|
|
case 'delay':
|
|
return $input['type'] != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($input['key_'], 'mqtt.get', 8) != 0;
|
|
|
|
case 'trapper_hosts':
|
|
return $input['type'] != ITEM_TYPE_HTTPAGENT || $input['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_ON;
|
|
|
|
case 'publickey':
|
|
case 'privatekey':
|
|
return $input['authtype'] == ITEM_AUTHTYPE_PUBLICKEY;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Apply sorting for discovery rule filter or override filter conditions, if appropriate.
|
|
* Prioritization by non/exist operator applied between matching macros.
|
|
*
|
|
* @param array $conditions
|
|
* @param int $evaltype
|
|
*
|
|
* @return array
|
|
*/
|
|
function sortLldRuleFilterConditions(array $conditions, int $evaltype): array {
|
|
switch ($evaltype) {
|
|
case CONDITION_EVAL_TYPE_AND_OR:
|
|
case CONDITION_EVAL_TYPE_AND:
|
|
case CONDITION_EVAL_TYPE_OR:
|
|
usort($conditions, static function(array $condition_a, array $condition_b): int {
|
|
$comparison = strnatcasecmp($condition_a['macro'], $condition_b['macro']);
|
|
|
|
if ($comparison != 0) {
|
|
return $comparison;
|
|
}
|
|
|
|
$exist_operators = [CONDITION_OPERATOR_NOT_EXISTS, CONDITION_OPERATOR_EXISTS];
|
|
|
|
$comparison = (int) in_array($condition_b['operator'], $exist_operators)
|
|
- (int) in_array($condition_a['operator'], $exist_operators);
|
|
|
|
if ($comparison != 0) {
|
|
return $comparison;
|
|
}
|
|
|
|
return strnatcasecmp($condition_a['value'], $condition_b['value']);
|
|
});
|
|
|
|
foreach ($conditions as $i => &$condition) {
|
|
$condition['formulaid'] = num2letter($i);
|
|
}
|
|
unset($condition);
|
|
break;
|
|
|
|
case CONDITION_EVAL_TYPE_EXPRESSION:
|
|
CArrayHelper::sort($conditions, ['formulaid']);
|
|
break;
|
|
}
|
|
|
|
return array_values($conditions);
|
|
}
|