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.
2780 lines
86 KiB
2780 lines
86 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.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Class containing methods for operations with item general.
|
|
*/
|
|
abstract class CItemGeneral extends CApiService {
|
|
|
|
public const ACCESS_RULES = [
|
|
'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER],
|
|
'create' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN],
|
|
'update' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN],
|
|
'delete' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN]
|
|
];
|
|
|
|
public const INTERFACE_TYPES_BY_PRIORITY = [
|
|
INTERFACE_TYPE_AGENT,
|
|
INTERFACE_TYPE_SNMP,
|
|
INTERFACE_TYPE_JMX,
|
|
INTERFACE_TYPE_IPMI
|
|
];
|
|
|
|
/**
|
|
* A list of supported preprocessing types.
|
|
*
|
|
* @var array
|
|
*/
|
|
public const SUPPORTED_PREPROCESSING_TYPES = [];
|
|
|
|
/**
|
|
* A list of preprocessing types that supports the "params" field.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected const PREPROC_TYPES_WITH_PARAMS = [
|
|
ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
|
|
ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX,
|
|
ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML,
|
|
ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
|
|
ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON,
|
|
ZBX_PREPROC_STR_REPLACE, ZBX_PREPROC_SNMP_WALK_VALUE, ZBX_PREPROC_SNMP_WALK_TO_JSON
|
|
];
|
|
|
|
/**
|
|
* A list of preprocessing types that supports the error handling.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected const PREPROC_TYPES_WITH_ERR_HANDLING = [
|
|
ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_REGSUB, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
|
|
ZBX_PREPROC_DELTA_VALUE, ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH,
|
|
ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX,
|
|
ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX,
|
|
ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON,
|
|
ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON, ZBX_PREPROC_SNMP_WALK_VALUE,
|
|
ZBX_PREPROC_SNMP_WALK_TO_JSON
|
|
];
|
|
|
|
/**
|
|
* A list of supported item types.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected const SUPPORTED_ITEM_TYPES = [];
|
|
|
|
/**
|
|
* A list of field names for each of value types.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected const VALUE_TYPE_FIELD_NAMES = [];
|
|
|
|
/**
|
|
* Maximum number of inheritable items per iteration.
|
|
*
|
|
* @var int
|
|
*/
|
|
protected const INHERIT_CHUNK_SIZE = 1000;
|
|
|
|
/**
|
|
* @abstract
|
|
*
|
|
* @param array $options
|
|
*
|
|
* @return array
|
|
*/
|
|
abstract public function get($options = []);
|
|
|
|
/**
|
|
* @param array $field_names
|
|
* @param array $items
|
|
* @param array|null $db_items
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function validateByType(array $field_names, array &$items, array $db_items = null): void {
|
|
$checked_fields = array_fill_keys($field_names, ['type' => API_ANY]);
|
|
|
|
foreach ($items as $i => &$item) {
|
|
$api_input_rules = ['type' => API_OBJECT, 'fields' => $checked_fields];
|
|
$db_item = ($db_items === null) ? null : $db_items[$item['itemid']];
|
|
$item_type = CItemTypeFactory::getObject($item['type']);
|
|
|
|
if ($db_item === null) {
|
|
$api_input_rules['fields'] += $item_type::getCreateValidationRules($item);
|
|
}
|
|
elseif ($db_item['templateid'] != 0) {
|
|
if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
|
|
$item += array_intersect_key($db_item, array_flip(['allow_traps']));
|
|
}
|
|
elseif ($item['type'] == ITEM_TYPE_SSH) {
|
|
$item += array_intersect_key($db_item, array_flip(['authtype']));
|
|
}
|
|
|
|
if ($item['type'] === ITEM_TYPE_SSH && $item['authtype'] == ITEM_AUTHTYPE_PUBLICKEY
|
|
&& $db_item['authtype'] != ITEM_AUTHTYPE_PUBLICKEY) {
|
|
$item += array_intersect_key($db_item, array_flip(['publickey', 'privatekey']));
|
|
}
|
|
|
|
$api_input_rules['fields'] += $item_type::getUpdateValidationRulesInherited($db_item);
|
|
}
|
|
elseif ($db_item['flags'] == ZBX_FLAG_DISCOVERY_CREATED) {
|
|
$api_input_rules['fields'] += $item_type::getUpdateValidationRulesDiscovered();
|
|
}
|
|
else {
|
|
if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
|
|
$item += array_intersect_key($db_item, array_flip(
|
|
['request_method', 'post_type', 'authtype', 'allow_traps']
|
|
));
|
|
}
|
|
elseif ($item['type'] == ITEM_TYPE_SSH) {
|
|
$item += array_intersect_key($db_item, array_flip(['authtype']));
|
|
}
|
|
|
|
$interfaceid_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_IPMI,
|
|
ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_HTTPAGENT,
|
|
ITEM_TYPE_SNMP
|
|
];
|
|
|
|
if (in_array($item['type'], $interfaceid_types)) {
|
|
$opt_interface_types = [ITEM_TYPE_SIMPLE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
|
|
ITEM_TYPE_HTTPAGENT
|
|
];
|
|
|
|
if (in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
|
|
&& (!in_array($db_item['type'], $interfaceid_types)
|
|
|| (in_array($item['type'], array_diff($interfaceid_types, $opt_interface_types))
|
|
&& in_array($db_item['type'], $opt_interface_types)
|
|
&& $db_item['interfaceid'] == 0))) {
|
|
$item += array_intersect_key($db_item, array_flip(['interfaceid']));
|
|
}
|
|
}
|
|
|
|
$username_types = [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
|
|
ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT
|
|
];
|
|
|
|
if (in_array($item['type'], [ITEM_TYPE_SSH, ITEM_TYPE_TELNET])) {
|
|
$opt_username_types = array_diff($username_types, [ITEM_TYPE_SSH, ITEM_TYPE_TELNET]);
|
|
|
|
if (!in_array($db_item['type'], $username_types)
|
|
|| (in_array($db_item['type'], $opt_username_types) && $db_item['username'] === '')) {
|
|
$item += array_intersect_key($db_item, array_flip(['username']));
|
|
}
|
|
}
|
|
|
|
$params_types = [ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED,
|
|
ITEM_TYPE_SCRIPT
|
|
];
|
|
|
|
if (in_array($item['type'], $params_types) && !in_array($db_item['type'], $params_types)) {
|
|
$item += array_intersect_key($db_item, array_flip(['params']));
|
|
}
|
|
|
|
$delay_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
|
|
ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
|
|
ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
|
|
];
|
|
|
|
if (in_array($item['type'], $delay_types)) {
|
|
if (!in_array($db_item['type'], $delay_types)
|
|
|| ($db_item['type'] == ITEM_TYPE_ZABBIX_ACTIVE
|
|
&& strncmp($db_item['key_'], 'mqtt.get', 8) === 0)) {
|
|
$item += array_intersect_key($db_item, array_flip(['delay']));
|
|
}
|
|
}
|
|
|
|
if ($item['type'] == ITEM_TYPE_DEPENDENT && $db_item['type'] != ITEM_TYPE_DEPENDENT) {
|
|
$item += array_intersect_key($db_item, array_flip(['master_itemid']));
|
|
}
|
|
|
|
if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
|
|
if ($db_item['type'] != ITEM_TYPE_HTTPAGENT) {
|
|
$item += array_intersect_key($db_item, array_flip(['url']));
|
|
}
|
|
|
|
$post_types = [ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML];
|
|
|
|
if (in_array($item['post_type'], $post_types) && !in_array($db_item['post_type'], $post_types)) {
|
|
$item += array_intersect_key($db_item, array_flip(['posts']));
|
|
}
|
|
}
|
|
|
|
if ($item['type'] == ITEM_TYPE_IPMI
|
|
&& ($db_item['type'] != ITEM_TYPE_IPMI
|
|
|| ($item['key_'] !== $db_item['key_'] && $db_item['key_'] === 'ipmi.get'))) {
|
|
$item += array_intersect_key($db_item, array_flip(['ipmi_sensor']));
|
|
}
|
|
|
|
if ($item['type'] == ITEM_TYPE_JMX && $db_item['type'] != ITEM_TYPE_JMX) {
|
|
$item += array_intersect_key($db_item, array_flip(['jmx_endpoint']));
|
|
}
|
|
|
|
if ($item['type'] == ITEM_TYPE_SNMP && $db_item['type'] != ITEM_TYPE_SNMP) {
|
|
$item += array_intersect_key($db_item, array_flip(['snmp_oid']));
|
|
}
|
|
|
|
if ($item['type'] === ITEM_TYPE_SSH && $item['authtype'] == ITEM_AUTHTYPE_PUBLICKEY
|
|
&& $db_item['authtype'] != ITEM_AUTHTYPE_PUBLICKEY) {
|
|
$item += array_intersect_key($db_item, array_flip(['publickey', 'privatekey']));
|
|
}
|
|
|
|
$api_input_rules['fields'] += $item_type::getUpdateValidationRules($db_item);
|
|
}
|
|
|
|
$api_input_rules['fields'] += CItemType::getDefaultValidationRules();
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
if ($item['type'] == ITEM_TYPE_JMX) {
|
|
if (array_key_exists('username', $item) || array_key_exists('password', $item)
|
|
|| ($db_item !== null && $db_item['type'] != ITEM_TYPE_JMX)) {
|
|
$_item = array_intersect_key($item, array_flip(['username', 'password']));
|
|
|
|
if ($db_item === null) {
|
|
$_item += array_fill_keys(['username', 'password'], '');
|
|
}
|
|
else {
|
|
$_item += array_intersect_key($db_item, array_flip(['username', 'password']));
|
|
}
|
|
|
|
if (($_item['username'] === '') !== ($_item['password'] === '')) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', '/'.($i + 1),
|
|
_('both username and password should be either present or empty')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (array_key_exists('query_fields', $item)) {
|
|
foreach ($item['query_fields'] as $query_field) {
|
|
if (count($query_field) != 1 || key($query_field) === '') {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/query_fields', _('nonempty key and value pair expected'))
|
|
);
|
|
}
|
|
}
|
|
|
|
$item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
|
|
|
|
if (strlen($item['query_fields']) > DB::getFieldLength('items', 'query_fields')) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/query_fields', _('value is too long')
|
|
));
|
|
}
|
|
}
|
|
|
|
if (array_key_exists('headers', $item)) {
|
|
foreach ($item['headers'] as $name => $value) {
|
|
if (trim($name) === '' || !is_string($value) || $value === '') {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/headers', _('nonempty key and value pair expected')
|
|
));
|
|
}
|
|
}
|
|
|
|
$item['headers'] = self::headersArrayToString($item['headers']);
|
|
|
|
if (strlen($item['headers']) > DB::getFieldLength('items', 'headers')) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/headers', _('value is too long')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
*/
|
|
protected static function validateUniqueness(array &$items): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['uuid'], ['hostid', 'key_']], 'fields' => [
|
|
'uuid' => ['type' => API_ANY],
|
|
'hostid' => ['type' => API_ANY],
|
|
'key_' => ['type' => API_ANY]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validateUniqueness($api_input_rules, $items, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
protected static function getTagsValidationRules(): array {
|
|
return ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['tag', 'value']], 'fields' => [
|
|
'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_tag', 'tag')],
|
|
'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('item_tag', 'value')]
|
|
]];
|
|
}
|
|
|
|
/**
|
|
* @param int $flags
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getPreprocessingValidationRules(int $flags = 0x00): array {
|
|
return [
|
|
'type' => API_OBJECTS,
|
|
'flags' => API_NORMALIZE,
|
|
'uniq_by_values' => [
|
|
['type' => [ZBX_PREPROC_DELTA_VALUE, ZBX_PREPROC_DELTA_SPEED]],
|
|
['type' => [ZBX_PREPROC_THROTTLE_VALUE, ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
|
|
['type' => [ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON]],
|
|
['type' => [ZBX_PREPROC_VALIDATE_NOT_SUPPORTED]]
|
|
],
|
|
'fields' => [
|
|
'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', static::SUPPORTED_PREPROCESSING_TYPES)],
|
|
'params' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => implode(',', static::PREPROC_TYPES_WITH_PARAMS)], 'type' => API_PREPROC_PARAMS, 'flags' => API_REQUIRED | API_ALLOW_USER_MACRO | ($flags & API_ALLOW_LLD_MACRO), 'preproc_type' => ['field' => 'type'], 'length' => DB::getFieldLength('item_preproc', 'params')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('item_preproc', 'params')]
|
|
]],
|
|
'error_handler' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => implode(',', array_diff(static::PREPROC_TYPES_WITH_ERR_HANDLING, [ZBX_PREPROC_VALIDATE_NOT_SUPPORTED]))], 'type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_FAIL_DEFAULT, ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR])],
|
|
['if' => ['field' => 'type', 'in' => ZBX_PREPROC_VALIDATE_NOT_SUPPORTED], 'type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR])],
|
|
['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('item_preproc', 'error_handler')]
|
|
]],
|
|
'error_handler_params' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function (array $data): bool {
|
|
return array_key_exists('error_handler', $data) && $data['error_handler'] == ZBX_PREPROC_FAIL_SET_VALUE;
|
|
}, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_preproc', 'error_handler_params')],
|
|
['if' => static function (array $data): bool {
|
|
return array_key_exists('error_handler', $data) && $data['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR;
|
|
}, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_preproc', 'error_handler_params')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('item_preproc', 'error_handler_params')]
|
|
]]
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check that host IDs of given items are valid.
|
|
* If host IDs are valid, $db_hosts and $db_templates parameters will be filled with found hosts and templates.
|
|
*
|
|
* @param array $items
|
|
* @param array|null $db_hosts
|
|
* @param array|null $db_templates
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function checkHostsAndTemplates(array $items, array &$db_hosts = null,
|
|
array &$db_templates = null): void {
|
|
$hostids = array_unique(array_column($items, 'hostid'));
|
|
|
|
$db_templates = API::Template()->get([
|
|
'output' => [],
|
|
'templateids' => $hostids,
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$_hostids = array_diff($hostids, array_keys($db_templates));
|
|
|
|
$db_hosts = $_hostids
|
|
? API::Host()->get([
|
|
'output' => ['status'],
|
|
'hostids' => $_hostids,
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
])
|
|
: [];
|
|
|
|
if (count($db_templates) + count($db_hosts) != count($hostids)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add host_status property to given items in accordance to statuses of given hosts and templates.
|
|
*
|
|
* @param array $items
|
|
* @param array $db_hosts
|
|
* @param array $db_templates
|
|
*/
|
|
protected static function addHostStatus(array &$items, array $db_hosts, array $db_templates): void {
|
|
foreach ($items as &$item) {
|
|
$item['host_status'] = array_key_exists($item['hostid'], $db_templates)
|
|
? HOST_STATUS_TEMPLATE
|
|
: $db_hosts[$item['hostid']]['status'];
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* Add flags property to given items with the given flags value.
|
|
*
|
|
* @param array $items
|
|
* @param int $flags
|
|
*/
|
|
protected static function addFlags(array &$items, int $flags): void {
|
|
foreach ($items as &$item) {
|
|
$item['flags'] = $flags;
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* Add the UUID to those of the given items that belong to a template and don't have the 'uuid' parameter set.
|
|
*
|
|
* @param array $items
|
|
*/
|
|
protected static function addUuid(array &$items): void {
|
|
foreach ($items as &$item) {
|
|
if ($item['host_status'] == HOST_STATUS_TEMPLATE && !array_key_exists('uuid', $item)) {
|
|
$item['uuid'] = generateUuidV4();
|
|
}
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* Verify host prototype UUIDs are not repeated.
|
|
*
|
|
* @param array $items
|
|
* @param array|null $db_items
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function checkUuidDuplicates(array $items, array $db_items = null): void {
|
|
$item_indexes = [];
|
|
|
|
foreach ($items as $i => $item) {
|
|
if (!array_key_exists('uuid', $item)) {
|
|
continue;
|
|
}
|
|
|
|
if ($db_items === null || $item['uuid'] !== $db_items[$item['itemid']]['uuid']) {
|
|
$item_indexes[$item['uuid']] = $i;
|
|
}
|
|
}
|
|
|
|
if (!$item_indexes) {
|
|
return;
|
|
}
|
|
|
|
$flags = $items[reset($item_indexes)]['flags'];
|
|
|
|
$duplicates = DB::select('items', [
|
|
'output' => ['uuid'],
|
|
'filter' => [
|
|
'flags' => $flags,
|
|
'uuid' => array_keys($item_indexes)
|
|
],
|
|
'limit' => 1
|
|
]);
|
|
|
|
if ($duplicates) {
|
|
switch ($flags) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
$error = _s('Invalid parameter "%1$s": %2$s.', '/'.($item_indexes[$duplicates[0]['uuid']] + 1),
|
|
_('item with the same UUID already exists')
|
|
);
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
$error = _s('Invalid parameter "%1$s": %2$s.', '/'.($item_indexes[$duplicates[0]['uuid']] + 1),
|
|
_('LLD rule with the same UUID already exists')
|
|
);
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
$error = _s('Invalid parameter "%1$s": %2$s.', '/'.($item_indexes[$duplicates[0]['uuid']] + 1),
|
|
_('item prototype with the same UUID already exists')
|
|
);
|
|
break;
|
|
}
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array|null $hostids
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getTemplateLinks(array $items, ?array $hostids): array {
|
|
if ($hostids !== null) {
|
|
$db_hosts = DB::select('hosts', [
|
|
'output' => ['hostid', 'status'],
|
|
'hostids' => $hostids,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$tpl_links = [];
|
|
|
|
foreach ($items as $item) {
|
|
$tpl_links[$item['hostid']] = $db_hosts;
|
|
}
|
|
}
|
|
else {
|
|
$templateids = [];
|
|
|
|
foreach ($items as $item) {
|
|
if ($item['host_status'] == HOST_STATUS_TEMPLATE) {
|
|
$templateids[$item['hostid']] = true;
|
|
}
|
|
}
|
|
|
|
if (!$templateids) {
|
|
return [];
|
|
}
|
|
|
|
$result = DBselect(
|
|
'SELECT ht.templateid,ht.hostid,h.status'.
|
|
' FROM hosts_templates ht,hosts h'.
|
|
' WHERE ht.hostid=h.hostid'.
|
|
' AND '.dbConditionId('ht.templateid', array_keys($templateids)).
|
|
' AND '.dbConditionInt('h.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED])
|
|
);
|
|
|
|
$tpl_links = [];
|
|
|
|
while ($row = DBfetch($result)) {
|
|
$tpl_links[$row['templateid']][$row['hostid']] = [
|
|
'hostid' => $row['hostid'],
|
|
'status' => $row['status']
|
|
];
|
|
}
|
|
}
|
|
|
|
return $tpl_links;
|
|
}
|
|
|
|
/**
|
|
* Filter out inheritable items from the given items.
|
|
*
|
|
* @param array $items
|
|
* @param array $db_items
|
|
* @param array $tpl_links
|
|
*/
|
|
protected static function filterObjectsToInherit(array &$items, array &$db_items, array $tpl_links): void {
|
|
foreach ($items as $i => $item) {
|
|
if (!array_key_exists($item['hostid'], $tpl_links)) {
|
|
unset($items[$i]);
|
|
|
|
if (array_key_exists($item['itemid'], $db_items)) {
|
|
unset($db_items[$item['itemid']]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that no items with repeating keys would be inherited to a single host or template.
|
|
*
|
|
* @param array $items
|
|
* @param array $db_items
|
|
* @param array $tpl_links
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function checkDoubleInheritedNames(array $items, array $db_items, array $tpl_links): void {
|
|
$item_indexes = [];
|
|
|
|
foreach ($items as $i => $item) {
|
|
if (array_key_exists($item['itemid'], $db_items) && $item['key_'] === $db_items[$item['itemid']]['key_']) {
|
|
continue;
|
|
}
|
|
|
|
$item_indexes[$item['key_']][] = $i;
|
|
}
|
|
|
|
foreach ($item_indexes as $key => $indexes) {
|
|
if (count($indexes) == 1) {
|
|
continue;
|
|
}
|
|
|
|
$hostids = [];
|
|
|
|
foreach ($indexes as $i) {
|
|
$templateid = $items[$i]['hostid'];
|
|
$same_hosts = array_intersect_key($tpl_links[$templateid], $hostids);
|
|
|
|
if ($same_hosts) {
|
|
$same_host = reset($same_hosts);
|
|
|
|
$templateid_first = $hostids[$same_host['hostid']];
|
|
$templateid_second = $templateid;
|
|
|
|
$hosts = DB::select('hosts', [
|
|
'output' => ['hostid', 'host'],
|
|
'hostids' => [$templateid_first, $templateid_second, $same_host['hostid']],
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$target_is_host = in_array($same_host['status'],
|
|
[HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]
|
|
);
|
|
|
|
switch ($items[$i]['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit items with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
|
|
: _('Cannot inherit items with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item prototypes with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
|
|
: _('Cannot inherit item prototypes with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit LDD rules with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
|
|
: _('Cannot inherit LDD rules with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
|
|
break;
|
|
}
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key,
|
|
$hosts[$templateid_first]['host'], $hosts[$templateid_second]['host'],
|
|
$hosts[$same_host['hostid']]['host']
|
|
));
|
|
}
|
|
|
|
$hostids += array_fill_keys(array_keys($tpl_links[$templateid]), $templateid);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get item chunks to inherit.
|
|
*
|
|
* @param array $items
|
|
* @param array $tpl_links
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getInheritChunks(array $items, array $tpl_links): array {
|
|
$chunks = [
|
|
[
|
|
'item_indexes' => [],
|
|
'hosts' => [],
|
|
'size' => 0
|
|
]
|
|
];
|
|
$last = 0;
|
|
|
|
foreach ($items as $i => $item) {
|
|
$hosts_chunks = array_chunk($tpl_links[$item['hostid']], self::INHERIT_CHUNK_SIZE, true);
|
|
|
|
foreach ($hosts_chunks as $hosts) {
|
|
if ($chunks[$last]['size'] < self::INHERIT_CHUNK_SIZE) {
|
|
$_hosts = array_slice($hosts, 0, self::INHERIT_CHUNK_SIZE - $chunks[$last]['size'], true);
|
|
|
|
$can_add_hosts = true;
|
|
|
|
foreach ($chunks[$last]['item_indexes'] as $_i) {
|
|
$new_hosts = array_diff_key($_hosts, $chunks[$last]['hosts']);
|
|
|
|
if (array_intersect_key($tpl_links[$items[$_i]['hostid']], $new_hosts)) {
|
|
$can_add_hosts = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($can_add_hosts) {
|
|
$chunks[$last]['item_indexes'][] = $i;
|
|
$chunks[$last]['hosts'] += $_hosts;
|
|
$chunks[$last]['size'] += count($_hosts);
|
|
|
|
$hosts = array_diff_key($hosts, $_hosts);
|
|
}
|
|
}
|
|
|
|
if ($hosts) {
|
|
$chunks[++$last] = [
|
|
'item_indexes' => [$i],
|
|
'hosts' => $hosts,
|
|
'size' => count($hosts)
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $chunks;
|
|
}
|
|
|
|
/**
|
|
* @param array $item
|
|
* @param array $upd_db_item
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function showObjectMismatchError(array $item, array $upd_db_item): void {
|
|
$target_is_host = in_array($upd_db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
|
|
|
|
$hosts = DB::select('hosts', [
|
|
'output' => ['host'],
|
|
'hostids' => [$item['hostid'], $upd_db_item['hostid']],
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$error = '';
|
|
|
|
switch ($item['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
switch ($upd_db_item['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an LLD rule with the same key already exists.')
|
|
: _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an LLD rule with the same key already exists.');
|
|
break 2;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an item prototype with the same key already exists.')
|
|
: _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an item prototype with the same key already exists.');
|
|
break 2;
|
|
|
|
case ZBX_FLAG_DISCOVERY_CREATED:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
|
|
: _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
|
|
break 2;
|
|
}
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
switch ($upd_db_item['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key already exists.')
|
|
: _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key already exists.');
|
|
break 2;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because an item prototype with the same key already exists.')
|
|
: _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because an item prototype with the same key already exists.');
|
|
break 2;
|
|
|
|
case ZBX_FLAG_DISCOVERY_CREATED:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
|
|
: _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
|
|
break 2;
|
|
}
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
switch ($upd_db_item['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key already exists.')
|
|
: _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key already exists.');
|
|
break 2;
|
|
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because an LLD rule with the same key already exists.')
|
|
: _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because an LLD rule with the same key already exists.');
|
|
break 2;
|
|
|
|
case ZBX_FLAG_DISCOVERY_CREATED:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
|
|
: _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
|
|
break 2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ($error) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $upd_db_item['key_'],
|
|
$hosts[$item['hostid']]['host'], $hosts[$upd_db_item['hostid']]['host']
|
|
));
|
|
}
|
|
|
|
if ($upd_db_item['templateid'] == 0) {
|
|
return;
|
|
}
|
|
|
|
$template = DBfetch(DBselect(
|
|
'SELECT h.host'.
|
|
' FROM items i,hosts h'.
|
|
' WHERE i.hostid=h.hostid'.
|
|
' AND '.dbConditionId('i.itemid', [$upd_db_item['templateid']])
|
|
));
|
|
|
|
switch ($item['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key is already inherited from template "%4$s".')
|
|
: _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key is already inherited from template "%4$s".');
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because an LLD rule with the same key is already inherited from template "%4$s".')
|
|
: _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because an LLD rule with the same key is already inherited from template "%4$s".');
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
$error = $target_is_host
|
|
? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because an item prototype with the same key is already inherited from template "%4$s".')
|
|
: _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because an item prototype with the same key is already inherited from template "%4$s".');
|
|
break;
|
|
}
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $upd_db_item['key_'], $hosts[$item['hostid']]['host'],
|
|
$hosts[$upd_db_item['hostid']]['host'], $template['host']
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @param array $item
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function unsetNestedObjectIds(array $item): array {
|
|
if (array_key_exists('tags', $item)) {
|
|
foreach ($item['tags'] as &$tag) {
|
|
unset($tag['itemtagid']);
|
|
}
|
|
unset($tag);
|
|
}
|
|
|
|
if (array_key_exists('preprocessing', $item)) {
|
|
foreach ($item['preprocessing'] as &$preprocessing) {
|
|
unset($preprocessing['item_preprocid']);
|
|
}
|
|
unset($preprocessing);
|
|
}
|
|
|
|
if (array_key_exists('parameters', $item)) {
|
|
foreach ($item['parameters'] as &$parameter) {
|
|
unset($parameter['item_parameterid']);
|
|
}
|
|
unset($parameter);
|
|
}
|
|
|
|
return $item;
|
|
}
|
|
|
|
/**
|
|
* Update relation to master item for inherited dependent items.
|
|
*
|
|
* @param array $upd_items
|
|
* @param array $ins_items
|
|
* @param array $hostids
|
|
*/
|
|
protected static function setChildMasterItemIds(array &$upd_items, array &$ins_items, array $hostids): void {
|
|
$upd_item_indexes = [];
|
|
$ins_item_indexes = [];
|
|
|
|
foreach ($upd_items as $i => $upd_item) {
|
|
if ($upd_item['type'] == ITEM_TYPE_DEPENDENT && array_key_exists('master_itemid', $upd_item)) {
|
|
$upd_item_indexes[$upd_item['master_itemid']][$upd_item['hostid']][] = $i;
|
|
}
|
|
}
|
|
|
|
foreach ($ins_items as $i => $ins_item) {
|
|
if ($ins_item['type'] == ITEM_TYPE_DEPENDENT) {
|
|
$ins_item_indexes[$ins_item['master_itemid']][$ins_item['hostid']][] = $i;
|
|
}
|
|
}
|
|
|
|
if (!$upd_item_indexes && !$ins_item_indexes) {
|
|
return;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['itemid', 'hostid', 'templateid'],
|
|
'filter' => [
|
|
'templateid' => array_keys($ins_item_indexes + $upd_item_indexes),
|
|
'hostid' => $hostids
|
|
]
|
|
];
|
|
$result = DBselect(DB::makeSql('items', $options));
|
|
|
|
while ($row = DBfetch($result)) {
|
|
if (array_key_exists($row['templateid'], $upd_item_indexes)
|
|
&& array_key_exists($row['hostid'], $upd_item_indexes[$row['templateid']])) {
|
|
foreach ($upd_item_indexes[$row['templateid']][$row['hostid']] as $i) {
|
|
$upd_items[$i]['master_itemid'] = $row['itemid'];
|
|
}
|
|
}
|
|
|
|
if (array_key_exists($row['templateid'], $ins_item_indexes)
|
|
&& array_key_exists($row['hostid'], $ins_item_indexes[$row['templateid']])) {
|
|
foreach ($ins_item_indexes[$row['templateid']][$row['hostid']] as $i) {
|
|
$ins_items[$i]['master_itemid'] = $row['itemid'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $upd_items
|
|
* @param array $upd_db_items
|
|
* @param array $ins_items
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function addInterfaceIds(array &$upd_items, array $upd_db_items, array &$ins_items): void {
|
|
$upd_item_indexes = [];
|
|
$ins_item_indexes = [];
|
|
$interface_types = [];
|
|
|
|
$upd_item_indexes_by_interfaceid = [];
|
|
|
|
foreach ($upd_items as $i => $upd_item) {
|
|
if (!in_array($upd_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])) {
|
|
continue;
|
|
}
|
|
|
|
$interface_type = itemTypeInterface($upd_item['type']);
|
|
|
|
if ($interface_type === false) {
|
|
continue;
|
|
}
|
|
|
|
if ($upd_db_items[$upd_item['itemid']]['interfaceid'] != 0) {
|
|
$db_interface_type = itemTypeInterface($upd_db_items[$upd_item['itemid']]['type']);
|
|
|
|
if ($interface_type != $db_interface_type) {
|
|
if ($db_interface_type == INTERFACE_TYPE_OPT) {
|
|
$upd_item_indexes_by_interfaceid[$upd_db_items[$upd_item['itemid']]['interfaceid']][] = $i;
|
|
}
|
|
elseif ($interface_type != INTERFACE_TYPE_OPT) {
|
|
$upd_item_indexes[$upd_item['hostid']][$interface_type][] = $i;
|
|
|
|
if ($interface_types !== null) {
|
|
$interface_types[$interface_type] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$upd_item_indexes[$upd_item['hostid']][$interface_type][] = $i;
|
|
|
|
if ($interface_types !== null) {
|
|
if ($interface_type == INTERFACE_TYPE_OPT) {
|
|
$interface_types = null;
|
|
}
|
|
else {
|
|
$interface_types[$interface_type] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($upd_item_indexes_by_interfaceid) {
|
|
$options = [
|
|
'output' => ['interfaceid', 'type'],
|
|
'interfaceids' => array_keys($upd_item_indexes_by_interfaceid)
|
|
];
|
|
$result = DBselect(DB::makeSql('interface', $options));
|
|
|
|
while ($row = DBfetch($result)) {
|
|
foreach ($upd_item_indexes_by_interfaceid[$row['interfaceid']] as $i) {
|
|
$upd_item = $upd_items[$i];
|
|
$interface_type = itemTypeInterface($upd_item['type']);
|
|
|
|
if ($interface_type != $row['type']) {
|
|
$upd_item_indexes[$upd_item['hostid']][$interface_type][] = $i;
|
|
|
|
if ($interface_types !== null) {
|
|
$interface_types[$interface_type] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($ins_items as $i => $ins_item) {
|
|
if (!in_array($ins_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])) {
|
|
continue;
|
|
}
|
|
|
|
$interface_type = itemTypeInterface($ins_item['type']);
|
|
|
|
if ($interface_type === false) {
|
|
continue;
|
|
}
|
|
|
|
$ins_item_indexes[$ins_item['hostid']][$interface_type][] = $i;
|
|
|
|
if ($interface_types !== null) {
|
|
if ($interface_type == INTERFACE_TYPE_OPT) {
|
|
$interface_types = null;
|
|
}
|
|
else {
|
|
$interface_types[$interface_type] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$upd_item_indexes && !$ins_item_indexes) {
|
|
return;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['interfaceid', 'hostid', 'type'],
|
|
'filter' => [
|
|
'hostid' => array_keys($upd_item_indexes + $ins_item_indexes),
|
|
'main' => INTERFACE_PRIMARY
|
|
]
|
|
];
|
|
|
|
if ($interface_types !== null) {
|
|
$options['filter']['type'] = array_keys($interface_types);
|
|
}
|
|
|
|
$result = DBselect(DB::makeSql('interface', $options));
|
|
|
|
$priority_interfaces = [];
|
|
|
|
while ($row = DBfetch($result)) {
|
|
$has_opt_type_items = false;
|
|
|
|
if (array_key_exists($row['hostid'], $upd_item_indexes)) {
|
|
if (array_key_exists(INTERFACE_TYPE_OPT, $upd_item_indexes[$row['hostid']])) {
|
|
$has_opt_type_items = true;
|
|
}
|
|
|
|
if (array_key_exists($row['type'], $upd_item_indexes[$row['hostid']])) {
|
|
foreach ($upd_item_indexes[$row['hostid']][$row['type']] as $_i => $i) {
|
|
$upd_items[$i]['interfaceid'] = $row['interfaceid'];
|
|
|
|
unset($upd_item_indexes[$row['hostid']][$row['type']][$_i]);
|
|
}
|
|
|
|
if (!$upd_item_indexes[$row['hostid']][$row['type']]) {
|
|
unset($upd_item_indexes[$row['hostid']][$row['type']]);
|
|
}
|
|
|
|
if (!$upd_item_indexes[$row['hostid']]) {
|
|
unset($upd_item_indexes[$row['hostid']]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (array_key_exists($row['hostid'], $ins_item_indexes)) {
|
|
if (array_key_exists(INTERFACE_TYPE_OPT, $ins_item_indexes[$row['hostid']])) {
|
|
$has_opt_type_items = true;
|
|
}
|
|
|
|
if (array_key_exists($row['type'], $ins_item_indexes[$row['hostid']])) {
|
|
foreach ($ins_item_indexes[$row['hostid']][$row['type']] as $_i => $i) {
|
|
$ins_items[$i]['interfaceid'] = $row['interfaceid'];
|
|
|
|
unset($ins_item_indexes[$row['hostid']][$row['type']][$_i]);
|
|
}
|
|
|
|
if (!$ins_item_indexes[$row['hostid']][$row['type']]) {
|
|
unset($ins_item_indexes[$row['hostid']][$row['type']]);
|
|
}
|
|
|
|
if (!$ins_item_indexes[$row['hostid']]) {
|
|
unset($ins_item_indexes[$row['hostid']]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($has_opt_type_items) {
|
|
$priority_index = array_search($row['type'], self::INTERFACE_TYPES_BY_PRIORITY);
|
|
|
|
if (!array_key_exists($row['hostid'], $priority_interfaces)
|
|
|| $priority_index < $priority_interfaces[$row['hostid']]['priority_index']) {
|
|
$priority_interfaces[$row['hostid']] = [
|
|
'interfaceid' => $row['interfaceid'],
|
|
'type' => $row['type'],
|
|
'priority_index' => $priority_index
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($upd_item_indexes as $hostid => $item_indexes) {
|
|
if (!array_key_exists(INTERFACE_TYPE_OPT, $item_indexes)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($item_indexes[INTERFACE_TYPE_OPT] as $i) {
|
|
if (array_key_exists($hostid, $priority_interfaces)) {
|
|
$upd_items[$i]['interfaceid'] = $priority_interfaces[$hostid]['interfaceid'];
|
|
}
|
|
}
|
|
|
|
unset($upd_item_indexes[$hostid][INTERFACE_TYPE_OPT]);
|
|
|
|
if (!$upd_item_indexes[$hostid]) {
|
|
unset($upd_item_indexes[$hostid]);
|
|
}
|
|
}
|
|
|
|
foreach ($ins_item_indexes as $hostid => $item_indexes) {
|
|
if (!array_key_exists(INTERFACE_TYPE_OPT, $item_indexes)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($item_indexes[INTERFACE_TYPE_OPT] as $i) {
|
|
if (array_key_exists($hostid, $priority_interfaces)) {
|
|
$ins_items[$i]['interfaceid'] = $priority_interfaces[$hostid]['interfaceid'];
|
|
}
|
|
}
|
|
|
|
unset($ins_item_indexes[$hostid][INTERFACE_TYPE_OPT]);
|
|
|
|
if (!$ins_item_indexes[$hostid]) {
|
|
unset($ins_item_indexes[$hostid]);
|
|
}
|
|
}
|
|
|
|
$item = null;
|
|
|
|
if ($upd_item_indexes) {
|
|
$hostid = key($upd_item_indexes);
|
|
$interface_type = key($upd_item_indexes[$hostid]);
|
|
$i = reset($upd_item_indexes[$hostid][$interface_type]);
|
|
|
|
$item = $upd_items[$i];
|
|
}
|
|
elseif ($ins_item_indexes) {
|
|
$hostid = key($ins_item_indexes);
|
|
$interface_type = key($ins_item_indexes[$hostid]);
|
|
$i = reset($ins_item_indexes[$hostid][$interface_type]);
|
|
|
|
$item = $ins_items[$i];
|
|
}
|
|
|
|
if ($item === null) {
|
|
return;
|
|
}
|
|
|
|
$templates = DBfetchArray(DBselect(
|
|
'SELECT h.host'.
|
|
' FROM items i,hosts h'.
|
|
' WHERE i.hostid=h.hostid'.
|
|
' AND '.dbConditionId('i.itemid', [$item['templateid']])
|
|
));
|
|
|
|
$hosts = DB::select('hosts', [
|
|
'output' => ['host'],
|
|
'hostids' => $item['hostid']
|
|
]);
|
|
|
|
switch ($item['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
$error = _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
$error = _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
$error = _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
|
|
break;
|
|
}
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $item['key_'], $templates[0]['host'],
|
|
$hosts[0]['host'], interfaceType2str($interface_type)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Inherit dependent items in nesting order.
|
|
*
|
|
* @param array $dep_items_to_link[<master item index>][<dependent item index>]
|
|
* @param array $items_to_link
|
|
* @param array $hostids
|
|
*/
|
|
protected static function inheritDependentItems(array $dep_items_to_link, array $items_to_link,
|
|
array $hostids): void {
|
|
while ($dep_items_to_link) {
|
|
$items = [];
|
|
|
|
foreach ($dep_items_to_link as $i => $_items) {
|
|
if (array_key_exists($i, $items_to_link)) {
|
|
$items += $_items;
|
|
unset($dep_items_to_link[$i]);
|
|
}
|
|
}
|
|
|
|
static::inherit(array_values($items), [], $hostids, true);
|
|
|
|
$items_to_link = $items;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array $db_items
|
|
* @param array|null $hostids
|
|
* @param bool $is_dep_items Inherit called for dependent items.
|
|
*/
|
|
abstract protected static function inherit(array $items, array $db_items = [], array $hostids = null,
|
|
bool $is_dep_items = false): void;
|
|
|
|
/**
|
|
* Add default values for fields that became unnecessary as the result of the change of the type fields.
|
|
*
|
|
* @param array $items
|
|
* @param array $db_items
|
|
*/
|
|
protected static function addFieldDefaultsByType(array &$items, array $db_items): void {
|
|
$type_field_defaults = [
|
|
// The fields used for multiple item types.
|
|
'interfaceid' => 0,
|
|
'authtype' => DB::getDefault('items', 'authtype'),
|
|
'username' => DB::getDefault('items', 'username'),
|
|
'password' => DB::getDefault('items', 'password'),
|
|
'params' => DB::getDefault('items', 'params'),
|
|
'timeout' => DB::getDefault('items', 'timeout'),
|
|
'delay' => DB::getDefault('items', 'delay'),
|
|
'trapper_hosts' => DB::getDefault('items', 'trapper_hosts'),
|
|
|
|
// Dependent item type specific fields.
|
|
'master_itemid' => 0,
|
|
|
|
// HTTP Agent item type specific fields.
|
|
'url' => DB::getDefault('items', 'url'),
|
|
'query_fields' => DB::getDefault('items', 'query_fields'),
|
|
'request_method' => DB::getDefault('items', 'request_method'),
|
|
'post_type' => DB::getDefault('items', 'post_type'),
|
|
'posts' => DB::getDefault('items', 'posts'),
|
|
'headers' => DB::getDefault('items', 'headers'),
|
|
'status_codes' => DB::getDefault('items', 'status_codes'),
|
|
'follow_redirects' => DB::getDefault('items', 'follow_redirects'),
|
|
'retrieve_mode' => DB::getDefault('items', 'retrieve_mode'),
|
|
'output_format' => DB::getDefault('items', 'output_format'),
|
|
'http_proxy' => DB::getDefault('items', 'http_proxy'),
|
|
'verify_peer' => DB::getDefault('items', 'verify_peer'),
|
|
'verify_host' => DB::getDefault('items', 'verify_host'),
|
|
'ssl_cert_file' => DB::getDefault('items', 'ssl_cert_file'),
|
|
'ssl_key_file' => DB::getDefault('items', 'ssl_key_file'),
|
|
'ssl_key_password' => DB::getDefault('items', 'ssl_key_password'),
|
|
'allow_traps' => DB::getDefault('items', 'allow_traps'),
|
|
|
|
// IPMI item type specific fields.
|
|
'ipmi_sensor' => DB::getDefault('items', 'ipmi_sensor'),
|
|
|
|
// JMX item type specific fields.
|
|
'jmx_endpoint' => DB::getDefault('items', 'jmx_endpoint'),
|
|
|
|
// Script item type specific fields.
|
|
'parameters' => [],
|
|
|
|
// SNMP item type specific fields.
|
|
'snmp_oid' => DB::getDefault('items', 'snmp_oid'),
|
|
|
|
// SSH item type specific fields.
|
|
'publickey' => DB::getDefault('items', 'publickey'),
|
|
'privatekey' => DB::getDefault('items', 'privatekey')
|
|
];
|
|
|
|
$value_type_field_defaults = [
|
|
'units' => DB::getDefault('items', 'units'),
|
|
'trends' => DB::getDefault('items', 'trends'),
|
|
'valuemapid' => 0,
|
|
'logtimefmt' => DB::getDefault('items', 'logtimefmt'),
|
|
'inventory_link' => DB::getDefault('items', 'inventory_link')
|
|
];
|
|
|
|
foreach ($items as &$item) {
|
|
if (!array_key_exists('type', $db_items[$item['itemid']])) {
|
|
continue;
|
|
}
|
|
|
|
$db_item = $db_items[$item['itemid']];
|
|
|
|
if ($item['type'] != $db_item['type']) {
|
|
$type_field_names = CItemTypeFactory::getObject($item['type'])::FIELD_NAMES;
|
|
$db_type_field_names = CItemTypeFactory::getObject($db_item['type'])::FIELD_NAMES;
|
|
|
|
$field_names = array_flip(array_diff($db_type_field_names, $type_field_names));
|
|
|
|
if ($item['host_status'] == HOST_STATUS_TEMPLATE && array_key_exists('interfaceid', $field_names)) {
|
|
unset($field_names['interfaceid']);
|
|
}
|
|
|
|
$item += array_intersect_key($type_field_defaults, $field_names);
|
|
}
|
|
|
|
if ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE) {
|
|
if (($item['type'] != $db_item['type'] || $item['key_'] !== $db_item['key_'])
|
|
&& strncmp($item['key_'], 'mqtt.get', 8) == 0) {
|
|
$item += array_intersect_key($type_field_defaults, array_flip(['delay']));
|
|
}
|
|
}
|
|
elseif ($item['type'] == ITEM_TYPE_SSH) {
|
|
if ($item['type'] != $db_item['type']) {
|
|
if ($db_item['type'] == ITEM_TYPE_HTTPAGENT) {
|
|
$item += array_intersect_key($type_field_defaults, array_flip(['authtype']));
|
|
}
|
|
}
|
|
elseif (array_key_exists('authtype', $item) && $item['authtype'] !== $db_item['authtype']
|
|
&& $item['authtype'] == ITEM_AUTHTYPE_PASSWORD) {
|
|
$item += array_intersect_key($type_field_defaults, array_flip(['publickey', 'privatekey']));
|
|
}
|
|
}
|
|
elseif ($item['type'] == ITEM_TYPE_HTTPAGENT) {
|
|
if ($item['type'] != $db_item['type']) {
|
|
if (!array_key_exists('authtype', $item)) {
|
|
$item += array_intersect_key($type_field_defaults, array_flip(['authtype']));
|
|
}
|
|
|
|
if ($item['authtype'] == ZBX_HTTP_AUTH_NONE) {
|
|
$item += array_intersect_key($type_field_defaults, array_flip(['username', 'password']));
|
|
}
|
|
|
|
if (!array_key_exists('allow_traps', $item) || $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF) {
|
|
$item += array_intersect_key($type_field_defaults, array_flip(['trapper_hosts']));
|
|
}
|
|
}
|
|
else {
|
|
if (array_key_exists('request_method', $item)
|
|
&& $item['request_method'] != $db_item['request_method']
|
|
&& $item['request_method'] == HTTPCHECK_REQUEST_HEAD) {
|
|
$item += ['retrieve_mode' => HTTPTEST_STEP_RETRIEVE_MODE_HEADERS];
|
|
}
|
|
|
|
if (array_key_exists('authtype', $item) && $item['authtype'] != $db_item['authtype']
|
|
&& $item['authtype'] == ZBX_HTTP_AUTH_NONE) {
|
|
$item += array_intersect_key($type_field_defaults, array_flip(['username', 'password']));
|
|
}
|
|
|
|
if (array_key_exists('allow_traps', $item) && $item['allow_traps'] != $db_item['allow_traps']
|
|
&& $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF) {
|
|
$item += array_intersect_key($type_field_defaults, array_flip(['trapper_hosts']));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (array_key_exists('value_type', $item) && $item['value_type'] != $db_item['value_type']) {
|
|
$type_field_names = static::VALUE_TYPE_FIELD_NAMES[$item['value_type']];
|
|
$db_type_field_names = static::VALUE_TYPE_FIELD_NAMES[$db_item['value_type']];
|
|
|
|
$field_names = array_flip(array_diff($db_type_field_names, $type_field_names));
|
|
|
|
if (array_key_exists('trends', $field_names)) {
|
|
$item += ['trends' => 0];
|
|
}
|
|
|
|
$item += array_intersect_key($value_type_field_defaults, $field_names);
|
|
}
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array|null $db_items
|
|
* @param array|null $upd_itemids
|
|
*/
|
|
protected static function updateParameters(array &$items, array &$db_items = null,
|
|
array &$upd_itemids = null): void {
|
|
$ins_item_parameters = [];
|
|
$upd_item_parameters = [];
|
|
$del_item_parameterids = [];
|
|
|
|
foreach ($items as $i => &$item) {
|
|
$update = false;
|
|
|
|
if ($db_items === null) {
|
|
if ($item['type'] == ITEM_TYPE_SCRIPT && array_key_exists('parameters', $item) && $item['parameters']) {
|
|
$update = true;
|
|
}
|
|
}
|
|
else {
|
|
if (!array_key_exists('type', $db_items[$item['itemid']])) {
|
|
continue;
|
|
}
|
|
|
|
if ($item['type'] == ITEM_TYPE_SCRIPT) {
|
|
if (array_key_exists('parameters', $item)) {
|
|
$update = true;
|
|
}
|
|
}
|
|
elseif ($db_items[$item['itemid']]['type'] == ITEM_TYPE_SCRIPT
|
|
&& $db_items[$item['itemid']]['parameters']) {
|
|
$update = true;
|
|
}
|
|
}
|
|
|
|
if (!$update) {
|
|
continue;
|
|
}
|
|
|
|
$changed = false;
|
|
$db_item_parameters = ($db_items !== null)
|
|
? array_column($db_items[$item['itemid']]['parameters'], null, 'name')
|
|
: [];
|
|
|
|
foreach ($item['parameters'] as &$item_parameter) {
|
|
if (array_key_exists($item_parameter['name'], $db_item_parameters)) {
|
|
$db_item_parameter = $db_item_parameters[$item_parameter['name']];
|
|
$item_parameter['item_parameterid'] = $db_item_parameter['item_parameterid'];
|
|
unset($db_item_parameters[$db_item_parameter['name']]);
|
|
|
|
$upd_item_parameter = DB::getUpdatedValues('item_parameter', $item_parameter, $db_item_parameter);
|
|
|
|
if ($upd_item_parameter) {
|
|
$upd_item_parameters[] = [
|
|
'values' => $upd_item_parameter,
|
|
'where' => ['item_parameterid' => $db_item_parameter['item_parameterid']]
|
|
];
|
|
$changed = true;
|
|
}
|
|
}
|
|
else {
|
|
$ins_item_parameters[] = ['itemid' => $item['itemid']] + $item_parameter;
|
|
$changed = true;
|
|
}
|
|
}
|
|
unset($item_parameter);
|
|
|
|
if ($db_item_parameters) {
|
|
$del_item_parameterids =
|
|
array_merge($del_item_parameterids, array_column($db_item_parameters, 'item_parameterid'));
|
|
$changed = true;
|
|
}
|
|
|
|
if ($db_items !== null) {
|
|
if ($changed) {
|
|
$upd_itemids[$i] = $item['itemid'];
|
|
}
|
|
else {
|
|
unset($item['parameters'], $db_items[$item['itemid']]['parameters']);
|
|
}
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
if ($del_item_parameterids) {
|
|
DB::delete('item_parameter', ['item_parameterid' => $del_item_parameterids]);
|
|
}
|
|
|
|
if ($upd_item_parameters) {
|
|
DB::update('item_parameter', $upd_item_parameters);
|
|
}
|
|
|
|
if ($ins_item_parameters) {
|
|
$item_parameterids = DB::insert('item_parameter', $ins_item_parameters);
|
|
}
|
|
|
|
foreach ($items as &$item) {
|
|
if (!array_key_exists('parameters', $item)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($item['parameters'] as &$item_parameter) {
|
|
if (!array_key_exists('item_parameterid', $item_parameter)) {
|
|
$item_parameter['item_parameterid'] = array_shift($item_parameterids);
|
|
}
|
|
}
|
|
unset($item_parameter);
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array|null $db_items
|
|
* @param array|null $upd_itemids
|
|
*/
|
|
protected static function updatePreprocessing(array &$items, array &$db_items = null,
|
|
array &$upd_itemids = null): void {
|
|
$ins_item_preprocs = [];
|
|
$upd_item_preprocs = [];
|
|
$del_item_preprocids = [];
|
|
|
|
foreach ($items as $i => &$item) {
|
|
if (!array_key_exists('preprocessing', $item)) {
|
|
continue;
|
|
}
|
|
|
|
$changed = false;
|
|
$db_item_preprocs = ($db_items !== null)
|
|
? array_column($db_items[$item['itemid']]['preprocessing'], null, 'step')
|
|
: [];
|
|
|
|
$step = 1;
|
|
|
|
foreach ($item['preprocessing'] as &$item_preproc) {
|
|
$item_preproc['step'] = ($item_preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++;
|
|
|
|
if (array_key_exists($item_preproc['step'], $db_item_preprocs)) {
|
|
$db_item_preproc = $db_item_preprocs[$item_preproc['step']];
|
|
$item_preproc['item_preprocid'] = $db_item_preproc['item_preprocid'];
|
|
unset($db_item_preprocs[$db_item_preproc['step']]);
|
|
|
|
$upd_item_preproc = DB::getUpdatedValues('item_preproc', $item_preproc, $db_item_preproc);
|
|
|
|
if ($upd_item_preproc) {
|
|
$upd_item_preprocs[] = [
|
|
'values' => $upd_item_preproc,
|
|
'where' => ['item_preprocid' => $db_item_preproc['item_preprocid']]
|
|
];
|
|
$changed = true;
|
|
}
|
|
}
|
|
else {
|
|
$ins_item_preprocs[] = ['itemid' => $item['itemid']] + $item_preproc;
|
|
$changed = true;
|
|
}
|
|
}
|
|
unset($item_preproc);
|
|
|
|
if ($db_item_preprocs) {
|
|
$del_item_preprocids =
|
|
array_merge($del_item_preprocids, array_column($db_item_preprocs, 'item_preprocid'));
|
|
$changed = true;
|
|
}
|
|
|
|
if ($db_items !== null) {
|
|
if ($changed) {
|
|
$upd_itemids[$i] = $item['itemid'];
|
|
}
|
|
else {
|
|
unset($item['preprocessing'], $db_items[$item['itemid']]['preprocessing']);
|
|
}
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
if ($del_item_preprocids) {
|
|
DB::delete('item_preproc', ['item_preprocid' => $del_item_preprocids]);
|
|
}
|
|
|
|
if ($upd_item_preprocs) {
|
|
DB::update('item_preproc', $upd_item_preprocs);
|
|
}
|
|
|
|
if ($ins_item_preprocs) {
|
|
$item_preprocids = DB::insert('item_preproc', $ins_item_preprocs);
|
|
}
|
|
|
|
foreach ($items as &$item) {
|
|
if (!array_key_exists('preprocessing', $item)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($item['preprocessing'] as &$item_preproc) {
|
|
if (!array_key_exists('item_preprocid', $item_preproc)) {
|
|
$item_preproc['item_preprocid'] = array_shift($item_preprocids);
|
|
}
|
|
}
|
|
unset($item_preproc);
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array|null $db_items
|
|
* @param array|null $upd_itemids
|
|
*/
|
|
protected static function updateTags(array &$items, array &$db_items = null, array &$upd_itemids = null): void {
|
|
$ins_tags = [];
|
|
$del_itemtagids = [];
|
|
|
|
foreach ($items as $i => &$item) {
|
|
if (!array_key_exists('tags', $item)) {
|
|
continue;
|
|
}
|
|
|
|
$changed = false;
|
|
$db_tags = ($db_items !== null) ? $db_items[$item['itemid']]['tags'] : [];
|
|
|
|
foreach ($item['tags'] as &$tag) {
|
|
$db_itemtagid = key(array_filter($db_tags, static function (array $db_tag) use ($tag): bool {
|
|
return $tag['tag'] === $db_tag['tag']
|
|
&& (!array_key_exists('value', $tag) || $tag['value'] === $db_tag['value']);
|
|
}));
|
|
|
|
if ($db_itemtagid !== null) {
|
|
$tag['itemtagid'] = $db_itemtagid;
|
|
unset($db_tags[$db_itemtagid]);
|
|
}
|
|
else {
|
|
$ins_tags[] = ['itemid' => $item['itemid']] + $tag;
|
|
$changed = true;
|
|
}
|
|
}
|
|
unset($tag);
|
|
|
|
if ($db_tags) {
|
|
$del_itemtagids = array_merge($del_itemtagids, array_keys($db_tags));
|
|
$changed = true;
|
|
}
|
|
|
|
if ($db_items !== null) {
|
|
if ($changed) {
|
|
$upd_itemids[$i] = $item['itemid'];
|
|
}
|
|
else {
|
|
unset($item['tags'], $db_items[$item['itemid']]['tags']);
|
|
}
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
if ($del_itemtagids) {
|
|
DB::delete('item_tag', ['itemtagid' => $del_itemtagids]);
|
|
}
|
|
|
|
if ($ins_tags) {
|
|
$itemtagids = DB::insert('item_tag', $ins_tags);
|
|
}
|
|
|
|
foreach ($items as &$item) {
|
|
if (!array_key_exists('tags', $item)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($item['tags'] as &$tag) {
|
|
if (!array_key_exists('itemtagid', $tag)) {
|
|
$tag['itemtagid'] = array_shift($itemtagids);
|
|
}
|
|
}
|
|
unset($tag);
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
/**
|
|
* Check for unique item keys.
|
|
*
|
|
* @param array $items
|
|
* @param array|null $db_items
|
|
*
|
|
* @throws APIException if item keys are not unique.
|
|
*/
|
|
protected static function checkDuplicates(array $items, array $db_items = null): void {
|
|
$host_keys = [];
|
|
|
|
foreach ($items as $item) {
|
|
if ($db_items === null || $item['key_'] !== $db_items[$item['itemid']]['key_']) {
|
|
$host_keys[$item['hostid']][] = $item['key_'];
|
|
}
|
|
}
|
|
|
|
if (!$host_keys) {
|
|
return;
|
|
}
|
|
|
|
$where = [];
|
|
foreach ($host_keys as $hostid => $keys) {
|
|
$where[] = '('.dbConditionId('i.hostid', [$hostid]).' AND '.dbConditionString('i.key_', $keys).')';
|
|
}
|
|
|
|
$duplicates = DBfetchArray(DBselect(
|
|
'SELECT i.key_,i.flags,h.host,h.status'.
|
|
' FROM items i,hosts h'.
|
|
' WHERE i.hostid=h.hostid'.
|
|
' AND ('.implode(' OR ', $where).')',
|
|
1
|
|
));
|
|
|
|
if ($duplicates) {
|
|
$target_is_template = ($duplicates[0]['status'] == HOST_STATUS_TEMPLATE);
|
|
|
|
switch ($duplicates[0]['flags']) {
|
|
case ZBX_FLAG_DISCOVERY_NORMAL:
|
|
case ZBX_FLAG_DISCOVERY_CREATED:
|
|
$error = $target_is_template
|
|
? _('An item with key "%1$s" already exists on the template "%2$s".')
|
|
: _('An item with key "%1$s" already exists on the host "%2$s".');
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_PROTOTYPE:
|
|
$error = $target_is_template
|
|
? _('An item prototype with key "%1$s" already exists on the template "%2$s".')
|
|
: _('An item prototype with key "%1$s" already exists on the host "%2$s".');
|
|
break;
|
|
|
|
case ZBX_FLAG_DISCOVERY_RULE:
|
|
$error = $target_is_template
|
|
? _('An LLD rule with key "%1$s" already exists on the template "%2$s".')
|
|
: _('An LLD rule with key "%1$s" already exists on the host "%2$s".');
|
|
break;
|
|
}
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $duplicates[0]['key_'], $duplicates[0]['host']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array|null $db_items
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function checkHostInterfaces(array $items, array $db_items = null): void {
|
|
foreach ($items as $i => &$item) {
|
|
$interface_type = itemTypeInterface($item['type']);
|
|
|
|
if (!in_array($item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
|
|
|| $interface_type === false) {
|
|
unset($items[$i]);
|
|
continue;
|
|
}
|
|
|
|
$check = false;
|
|
|
|
if ($db_items === null) {
|
|
if (array_key_exists('interfaceid', $item)) {
|
|
if ($item['interfaceid'] != 0) {
|
|
$check = true;
|
|
}
|
|
elseif ($interface_type != INTERFACE_TYPE_OPT) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$db_item = $db_items[$item['itemid']];
|
|
|
|
if ($item['type'] == $db_item['type']) {
|
|
if (array_key_exists('interfaceid', $item)) {
|
|
if ($item['interfaceid'] != 0) {
|
|
if (bccomp($item['interfaceid'], $db_item['interfaceid']) != 0) {
|
|
$check = true;
|
|
}
|
|
}
|
|
elseif ($interface_type != INTERFACE_TYPE_OPT) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$db_interface_type = itemTypeInterface($db_item['type']);
|
|
|
|
if (array_key_exists('interfaceid', $item)) {
|
|
if ($item['interfaceid'] != 0) {
|
|
if (bccomp($item['interfaceid'], $db_item['interfaceid']) != 0
|
|
|| ($interface_type != INTERFACE_TYPE_OPT
|
|
&& $interface_type != $db_interface_type)) {
|
|
$check = true;
|
|
}
|
|
}
|
|
elseif ($interface_type != INTERFACE_TYPE_OPT) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
|
|
));
|
|
}
|
|
}
|
|
else {
|
|
if ($db_item['interfaceid'] != 0) {
|
|
if ($interface_type != INTERFACE_TYPE_OPT && $interface_type != $db_interface_type) {
|
|
$item += ['interfaceid' => $db_item['interfaceid']];
|
|
|
|
$check = true;
|
|
}
|
|
}
|
|
elseif ($interface_type != INTERFACE_TYPE_OPT) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1), _s('the parameter "%1$s" is missing', 'interfaceid')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$check) {
|
|
unset($items[$i]);
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
if (!$items) {
|
|
return;
|
|
}
|
|
|
|
$db_interfaces = DB::select('interface', [
|
|
'output' => ['interfaceid', 'hostid', 'type'],
|
|
'interfaceids' => array_unique(array_column($items, 'interfaceid')),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
foreach ($items as $i => $item) {
|
|
if (!array_key_exists($item['interfaceid'], $db_interfaces)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
|
|
));
|
|
}
|
|
|
|
if (bccomp($db_interfaces[$item['interfaceid']]['hostid'], $item['hostid']) != 0) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/interfaceid', _('cannot be the host interface ID from another host')
|
|
));
|
|
}
|
|
|
|
$interface_type = itemTypeInterface($item['type']);
|
|
|
|
if ($interface_type != INTERFACE_TYPE_OPT
|
|
&& $db_interfaces[$item['interfaceid']]['type'] != $interface_type) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/interfaceid',
|
|
_s('the host interface ID of type "%1$s" is expected', interfaceType2str($interface_type))
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function addRelatedObjects(array $options, array $result) {
|
|
$result = parent::addRelatedObjects($options, $result);
|
|
|
|
// adding hosts
|
|
if ($options['selectHosts'] !== null && $options['selectHosts'] != API_OUTPUT_COUNT) {
|
|
$relationMap = $this->createRelationMap($result, 'itemid', 'hostid');
|
|
$hosts = API::Host()->get([
|
|
'hostids' => $relationMap->getRelatedIds(),
|
|
'templated_hosts' => true,
|
|
'output' => $options['selectHosts'],
|
|
'nopermissions' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
$result = $relationMap->mapMany($result, $hosts, 'hosts');
|
|
}
|
|
|
|
// adding preprocessing
|
|
if ($options['selectPreprocessing'] !== null && $options['selectPreprocessing'] != API_OUTPUT_COUNT) {
|
|
$db_item_preproc = API::getApiService()->select('item_preproc', [
|
|
'output' => $this->outputExtend($options['selectPreprocessing'], ['itemid', 'step']),
|
|
'filter' => ['itemid' => array_keys($result)]
|
|
]);
|
|
|
|
CArrayHelper::sort($db_item_preproc, ['step']);
|
|
|
|
foreach ($result as &$item) {
|
|
$item['preprocessing'] = [];
|
|
}
|
|
unset($item);
|
|
|
|
foreach ($db_item_preproc as $step) {
|
|
$itemid = $step['itemid'];
|
|
unset($step['item_preprocid'], $step['itemid'], $step['step']);
|
|
|
|
if (array_key_exists($itemid, $result)) {
|
|
$result[$itemid]['preprocessing'][] = $step;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add value mapping.
|
|
if (($this instanceof CItemPrototype || $this instanceof CItem) && $options['selectValueMap'] !== null) {
|
|
if ($options['selectValueMap'] === API_OUTPUT_EXTEND) {
|
|
$options['selectValueMap'] = ['valuemapid', 'name', 'mappings'];
|
|
}
|
|
|
|
foreach ($result as &$item) {
|
|
$item['valuemap'] = [];
|
|
}
|
|
unset($item);
|
|
|
|
$valuemaps = DB::select('valuemap', [
|
|
'output' => array_diff($this->outputExtend($options['selectValueMap'], ['valuemapid', 'hostid']),
|
|
['mappings']
|
|
),
|
|
'filter' => ['valuemapid' => array_keys(array_flip(array_column($result, 'valuemapid')))],
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if ($this->outputIsRequested('mappings', $options['selectValueMap']) && $valuemaps) {
|
|
$params = [
|
|
'output' => ['valuemapid', 'type', 'value', 'newvalue'],
|
|
'filter' => ['valuemapid' => array_keys($valuemaps)],
|
|
'sortfield' => ['sortorder']
|
|
];
|
|
$query = DBselect(DB::makeSql('valuemap_mapping', $params));
|
|
|
|
while ($mapping = DBfetch($query)) {
|
|
$valuemaps[$mapping['valuemapid']]['mappings'][] = [
|
|
'type' => $mapping['type'],
|
|
'value' => $mapping['value'],
|
|
'newvalue' => $mapping['newvalue']
|
|
];
|
|
}
|
|
}
|
|
|
|
foreach ($result as &$item) {
|
|
if (array_key_exists('valuemapid', $item) && array_key_exists($item['valuemapid'], $valuemaps)) {
|
|
$item['valuemap'] = array_intersect_key($valuemaps[$item['valuemapid']],
|
|
array_flip($options['selectValueMap'])
|
|
);
|
|
}
|
|
}
|
|
unset($item);
|
|
}
|
|
|
|
if (!$options['countOutput'] && $this->outputIsRequested('parameters', $options['output'])) {
|
|
$item_parameters = DBselect(
|
|
'SELECT ip.itemid,ip.name,ip.value'.
|
|
' FROM item_parameter ip'.
|
|
' WHERE '.dbConditionInt('ip.itemid', array_keys($result))
|
|
);
|
|
|
|
foreach ($result as &$item) {
|
|
$item['parameters'] = [];
|
|
}
|
|
unset($item);
|
|
|
|
while ($row = DBfetch($item_parameters)) {
|
|
$result[$row['itemid']]['parameters'][] = [
|
|
'name' => $row['name'],
|
|
'value' => $row['value']
|
|
];
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Check that dependent items of given items are valid.
|
|
*
|
|
* @param array $items
|
|
* @param array $db_items
|
|
* @param bool $inherited
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function checkDependentItems(array $items, array $db_items = [], bool $inherited = false): void {
|
|
$del_links = [];
|
|
|
|
foreach ($items as $i => $item) {
|
|
$check = false;
|
|
|
|
if ($item['type'] == ITEM_TYPE_DEPENDENT) {
|
|
if (!array_key_exists('itemid', $item)) {
|
|
if ($item['master_itemid'] != 0) {
|
|
$check = true;
|
|
}
|
|
else {
|
|
$error = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
|
|
? _('an item/item prototype ID is expected')
|
|
: _('an item ID is expected');
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/master_itemid', $error
|
|
));
|
|
}
|
|
}
|
|
else {
|
|
if (array_key_exists('master_itemid', $item)) {
|
|
if ($item['master_itemid'] != 0) {
|
|
if (bccomp($item['master_itemid'], $db_items[$item['itemid']]['master_itemid']) != 0) {
|
|
$check = true;
|
|
|
|
if ($db_items[$item['itemid']]['master_itemid'] != 0) {
|
|
$del_links[$item['itemid']] = $db_items[$item['itemid']]['master_itemid'];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$error = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
|
|
? _('an item/item prototype ID is expected')
|
|
: _('an item ID is expected');
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/master_itemid', $error
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
elseif (array_key_exists('itemid', $item) && $db_items[$item['itemid']]['type'] == ITEM_TYPE_DEPENDENT) {
|
|
$del_links[$item['itemid']] = $db_items[$item['itemid']]['master_itemid'];
|
|
}
|
|
|
|
if (!$check) {
|
|
unset($items[$i]);
|
|
}
|
|
}
|
|
|
|
if (!$items) {
|
|
return;
|
|
}
|
|
|
|
if (!$inherited) {
|
|
self::checkMasterItems($items, $db_items);
|
|
}
|
|
|
|
$dep_item_links = self::getDependentItemLinks($items, $del_links);
|
|
|
|
if (!$inherited && $db_items) {
|
|
self::checkCircularDependencies($items, $dep_item_links);
|
|
}
|
|
|
|
$root_itemids = [];
|
|
|
|
foreach ($dep_item_links as $itemid => $master_itemid) {
|
|
if ($master_itemid == 0) {
|
|
$root_itemids[] = $itemid;
|
|
}
|
|
}
|
|
|
|
$master_item_links = self::getMasterItemLinks($items, $root_itemids, $del_links);
|
|
|
|
foreach ($root_itemids as $root_itemid) {
|
|
if (self::maxDependencyLevelExceeded($master_item_links, $root_itemid, $links_path)) {
|
|
[$flags, $key, $master_flags, $master_key, $is_template, $host] =
|
|
self::getProblemCausedItemData($links_path, $items);
|
|
|
|
$error = self::getDependentItemError($flags, $master_flags, $is_template);
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key, $master_key, $host,
|
|
_('allowed count of dependency levels would be exceeded')
|
|
));
|
|
}
|
|
|
|
if (self::maxDependentItemCountExceeded($master_item_links, $root_itemid, $links_path)) {
|
|
[$flags, $key, $master_flags, $master_key, $is_template, $host] =
|
|
self::getProblemCausedItemData($links_path, $items);
|
|
|
|
$error = self::getDependentItemError($flags, $master_flags, $is_template);
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key, $master_key, $host,
|
|
_('allowed count of dependent items would be exceeded')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that master item IDs of given dependent items are valid.
|
|
*
|
|
* @param array $items
|
|
* @param array $db_items
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function checkMasterItems(array $items, array $db_items): void {
|
|
$master_itemids = array_unique(array_column($items, 'master_itemid'));
|
|
$flags = $items[key($items)]['flags'];
|
|
|
|
if ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
$db_master_items = DBfetchArrayAssoc(DBselect(
|
|
'SELECT i.itemid,i.hostid,i.master_itemid,i.flags,id.parent_itemid AS ruleid'.
|
|
' FROM items i'.
|
|
' LEFT JOIN item_discovery id ON i.itemid=id.itemid'.
|
|
' WHERE '.dbConditionId('i.itemid', $master_itemids).
|
|
' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_PROTOTYPE])
|
|
), 'itemid');
|
|
}
|
|
else {
|
|
$db_master_items = DB::select('items', [
|
|
'output' => ['itemid', 'hostid', 'master_itemid'],
|
|
'itemids' => $master_itemids,
|
|
'filter' => [
|
|
'flags' => ZBX_FLAG_DISCOVERY_NORMAL
|
|
],
|
|
'preservekeys' => true
|
|
]);
|
|
}
|
|
|
|
foreach ($items as $i => $item) {
|
|
if (!array_key_exists($item['master_itemid'], $db_master_items)) {
|
|
$error = $flags == ZBX_FLAG_DISCOVERY_PROTOTYPE
|
|
? _('an item/item prototype ID is expected')
|
|
: _('an item ID is expected');
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/master_itemid', $error
|
|
));
|
|
}
|
|
|
|
$db_master_item = $db_master_items[$item['master_itemid']];
|
|
|
|
if (bccomp($db_master_item['hostid'], $item['hostid']) != 0) {
|
|
$error = $flags == ZBX_FLAG_DISCOVERY_PROTOTYPE
|
|
? _('cannot be an item/item prototype ID from another host or template')
|
|
: _('cannot be an item ID from another host or template');
|
|
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/master_itemid', $error
|
|
));
|
|
}
|
|
|
|
if ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE && $db_master_item['ruleid'] != 0) {
|
|
$item_ruleid = array_key_exists('itemid', $item)
|
|
? $db_items[$item['itemid']]['ruleid']
|
|
: $item['ruleid'];
|
|
|
|
if (bccomp($db_master_item['ruleid'], $item_ruleid) != 0) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/master_itemid', _('cannot be an item prototype ID from another LLD rule')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get dependent item links starting from the given dependent items and till the highest dependency level.
|
|
*
|
|
* @param array $items
|
|
* @param array $del_links
|
|
*
|
|
* @return array Array of the links where each key contain the ID of dependent item and value contain the
|
|
* appropriate ID of the master item.
|
|
*/
|
|
private static function getDependentItemLinks(array $items, array $del_links): array {
|
|
$links = array_column($items, 'master_itemid', 'itemid');
|
|
$master_itemids = array_flip(array_column($items, 'master_itemid'));
|
|
|
|
while ($master_itemids) {
|
|
$options = [
|
|
'output' => ['itemid', 'hostid', 'master_itemid'],
|
|
'itemids' => array_keys($master_itemids)
|
|
];
|
|
$db_master_items = DBselect(DB::makeSql('items', $options));
|
|
|
|
$master_itemids = [];
|
|
|
|
while ($db_master_item = DBfetch($db_master_items)) {
|
|
if (array_key_exists($db_master_item['itemid'], $del_links)
|
|
&& bccomp($db_master_item['master_itemid'], $del_links[$db_master_item['itemid']]) == 0) {
|
|
$links[$db_master_item['itemid']] = 0;
|
|
continue;
|
|
}
|
|
|
|
$links[$db_master_item['itemid']] = $db_master_item['master_itemid'];
|
|
|
|
if ($db_master_item['master_itemid'] != 0) {
|
|
$master_itemids[$db_master_item['master_itemid']] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $links;
|
|
}
|
|
|
|
/**
|
|
* Check that the changed master item IDs of dependent items do not create a circular dependencies.
|
|
*
|
|
* @param array $items
|
|
* @param array $dep_item_links
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function checkCircularDependencies(array $items, array $dep_item_links): void {
|
|
foreach ($items as $i => $item) {
|
|
if ($item['flags'] == ZBX_FLAG_DISCOVERY_RULE) {
|
|
continue;
|
|
}
|
|
|
|
$master_itemid = $item['master_itemid'];
|
|
|
|
while ($master_itemid != 0) {
|
|
if (bccomp($master_itemid, $item['itemid']) == 0) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/master_itemid', _('circular item dependency is not allowed')
|
|
));
|
|
}
|
|
|
|
$master_itemid = $dep_item_links[$master_itemid];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get master item links starting from the given master items and till the lowest level master items.
|
|
*
|
|
* @param array $items
|
|
* @param array $master_itemids
|
|
* @param array $del_links
|
|
*
|
|
* @return array Array of the links where each key contain the ID of master item and value contain the array of
|
|
* appropriate dependent item IDs.
|
|
*/
|
|
private static function getMasterItemLinks(array $items, array $master_itemids, array $del_links): array {
|
|
$ins_links = [];
|
|
$upd_item_links = [];
|
|
|
|
foreach ($items as $item) {
|
|
if (array_key_exists('itemid', $item)) {
|
|
$upd_item_links[$item['master_itemid']][] = $item['itemid'];
|
|
}
|
|
else {
|
|
$ins_links[$item['master_itemid']][] = 0;
|
|
}
|
|
}
|
|
|
|
$links = [];
|
|
|
|
do {
|
|
$options = [
|
|
'output' => ['master_itemid', 'itemid'],
|
|
'filter' => [
|
|
'master_itemid' => $master_itemids
|
|
]
|
|
];
|
|
$db_items = DBselect(DB::makeSql('items', $options));
|
|
|
|
$_master_itemids = [];
|
|
|
|
while ($db_item = DBfetch($db_items)) {
|
|
if (array_key_exists($db_item['itemid'], $del_links)
|
|
&& bccomp($db_item['master_itemid'], $del_links[$db_item['itemid']]) == 0) {
|
|
continue;
|
|
}
|
|
|
|
$links[$db_item['master_itemid']][] = $db_item['itemid'];
|
|
$_master_itemids[] = $db_item['itemid'];
|
|
}
|
|
|
|
foreach ($master_itemids as $master_itemid) {
|
|
if (array_key_exists($master_itemid, $upd_item_links)) {
|
|
foreach ($upd_item_links[$master_itemid] as $itemid) {
|
|
$_master_itemids[] = $itemid;
|
|
$links[$master_itemid][] = $itemid;
|
|
}
|
|
}
|
|
}
|
|
|
|
$master_itemids = $_master_itemids;
|
|
} while ($master_itemids);
|
|
|
|
foreach ($ins_links as $master_itemid => $ins_items) {
|
|
$links[$master_itemid] = array_key_exists($master_itemid, $links)
|
|
? array_merge($links[$master_itemid], $ins_items)
|
|
: $ins_items;
|
|
}
|
|
|
|
return $links;
|
|
}
|
|
|
|
/**
|
|
* Check whether maximum number of dependency levels is exceeded.
|
|
*
|
|
* @param array $master_item_links
|
|
* @param string $master_itemid
|
|
* @param array|null $links_path
|
|
* @param int $level
|
|
*
|
|
* @return bool
|
|
*/
|
|
private static function maxDependencyLevelExceeded(array $master_item_links, string $master_itemid,
|
|
array &$links_path = null, int $level = 0): bool {
|
|
if (!array_key_exists($master_itemid, $master_item_links)) {
|
|
return false;
|
|
}
|
|
|
|
if ($links_path === null) {
|
|
$links_path = [];
|
|
}
|
|
|
|
$links_path[] = $master_itemid;
|
|
$level++;
|
|
|
|
if ($level > ZBX_DEPENDENT_ITEM_MAX_LEVELS) {
|
|
return true;
|
|
}
|
|
|
|
foreach ($master_item_links[$master_itemid] as $itemid) {
|
|
$_links_path = $links_path;
|
|
|
|
if (self::maxDependencyLevelExceeded($master_item_links, $itemid, $_links_path, $level)) {
|
|
$links_path = $_links_path;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check whether maximum count of dependent items is exceeded.
|
|
*
|
|
* @param array $master_item_links
|
|
* @param string $master_itemid
|
|
* @param array|null $links_path
|
|
* @param int $count
|
|
*
|
|
* @return bool
|
|
*/
|
|
private static function maxDependentItemCountExceeded(array $master_item_links, string $master_itemid,
|
|
array &$links_path = null, int &$count = 0): bool {
|
|
if (!array_key_exists($master_itemid, $master_item_links)) {
|
|
return false;
|
|
}
|
|
|
|
if ($links_path === null) {
|
|
$links_path = [];
|
|
}
|
|
|
|
$links_path[] = $master_itemid;
|
|
$count += count($master_item_links[$master_itemid]);
|
|
|
|
if ($count > ZBX_DEPENDENT_ITEM_MAX_COUNT) {
|
|
return true;
|
|
}
|
|
|
|
foreach ($master_item_links[$master_itemid] as $itemid) {
|
|
$_links_path = $links_path;
|
|
|
|
if (self::maxDependentItemCountExceeded($master_item_links, $itemid, $_links_path, $count)) {
|
|
$links_path = $_links_path;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get data for a dependent item that causes a problem, based on the given path where the problem was detected.
|
|
*
|
|
* @param array $links_path
|
|
* @param array $items
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function getProblemCausedItemData(array $links_path, array $items): array {
|
|
foreach ($items as $item) {
|
|
if (in_array($item['master_itemid'], $links_path)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$master_item_data = DBfetch(DBselect(
|
|
'SELECT i.flags,i.key_,h.host'.
|
|
' FROM items i,hosts h'.
|
|
' WHERE i.hostid=h.hostid'.
|
|
' AND '.dbConditionId('i.itemid', [$item['master_itemid']])
|
|
));
|
|
|
|
$flags = $item['flags'];
|
|
$key = $item['key_'];
|
|
$master_flags = $master_item_data['flags'];
|
|
$master_key = $master_item_data['key_'];
|
|
$is_template = $item['host_status'] == HOST_STATUS_TEMPLATE;
|
|
$host = $master_item_data['host'];
|
|
|
|
return [$flags, $key, $master_flags, $master_key, $is_template, $host];
|
|
}
|
|
|
|
/**
|
|
* Get the error message about problem with dependent item according to given data.
|
|
*
|
|
* @param int $flags
|
|
* @param int $master_flags
|
|
* @param bool $is_template
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function getDependentItemError(int $flags, int $master_flags, bool $is_template): string {
|
|
if ($flags == ZBX_FLAG_DISCOVERY_NORMAL) {
|
|
return $is_template
|
|
? _('Cannot set dependency for item with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
|
|
: _('Cannot set dependency for item with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
|
|
}
|
|
elseif ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
if ($master_flags == ZBX_FLAG_DISCOVERY_NORMAL) {
|
|
return $is_template
|
|
? _('Cannot set dependency for item prototype with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
|
|
: _('Cannot set dependency for item prototype with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
|
|
}
|
|
else {
|
|
return $is_template
|
|
? _('Cannot set dependency for item prototype with key "%1$s" on the master item prototype with key "%2$s" on the template "%3$s": %4$s.')
|
|
: _('Cannot set dependency for item prototype with key "%1$s" on the master item prototype with key "%2$s" on the host "%3$s": %4$s.');
|
|
}
|
|
}
|
|
elseif ($flags == ZBX_FLAG_DISCOVERY_RULE) {
|
|
return $is_template
|
|
? _('Cannot set dependency for LLD rule with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
|
|
: _('Cannot set dependency for LLD rule with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts headers field text to hash with header name as key.
|
|
*
|
|
* @param string $headers Headers string, one header per line, line delimiter "\r\n".
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function headersStringToArray(string $headers): array {
|
|
$result = [];
|
|
|
|
foreach (explode("\r\n", $headers) as $header) {
|
|
$header = explode(': ', $header, 2);
|
|
|
|
if (count($header) == 2) {
|
|
$result[$header[0]] = $header[1];
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Converts headers fields hash to string.
|
|
*
|
|
* @param array $headers Array of headers where key is header name.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected static function headersArrayToString(array $headers): string {
|
|
$result = [];
|
|
|
|
foreach ($headers as $k => $v) {
|
|
$result[] = $k.': '.$v;
|
|
}
|
|
|
|
return implode("\r\n", $result);
|
|
}
|
|
|
|
/**
|
|
* Remove NCLOB value type fields from resulting query SELECT part if DISTINCT will be used.
|
|
*
|
|
* @param string $table_name Table name.
|
|
* @param string $table_alias Table alias value.
|
|
* @param array $options Array of query options.
|
|
* @param array $sql_parts Array of query parts already initialized from $options.
|
|
*
|
|
* @return array The resulting SQL parts array.
|
|
*/
|
|
protected function applyQueryOutputOptions($table_name, $table_alias, array $options, array $sql_parts) {
|
|
if (!$options['countOutput'] && self::dbDistinct($sql_parts)) {
|
|
$schema = $this->getTableSchema();
|
|
$nclob_fields = [];
|
|
|
|
foreach ($schema['fields'] as $field_name => $field) {
|
|
if ($field['type'] == DB::FIELD_TYPE_NCLOB
|
|
&& $this->outputIsRequested($field_name, $options['output'])) {
|
|
$nclob_fields[] = $field_name;
|
|
}
|
|
}
|
|
|
|
if ($nclob_fields) {
|
|
$output = ($options['output'] === API_OUTPUT_EXTEND)
|
|
? array_keys($schema['fields'])
|
|
: $options['output'];
|
|
|
|
$options['output'] = array_diff($output, $nclob_fields);
|
|
}
|
|
}
|
|
|
|
return parent::applyQueryOutputOptions($table_name, $table_alias, $options, $sql_parts);
|
|
}
|
|
|
|
/**
|
|
* Add NCLOB type fields if there was DISTINCT in query.
|
|
*
|
|
* @param array $options Array of query options.
|
|
* @param array $result Query results.
|
|
*
|
|
* @return array The result array with added NCLOB fields.
|
|
*/
|
|
protected function addNclobFieldValues(array $options, array $result): array {
|
|
$schema = $this->getTableSchema();
|
|
$nclob_fields = [];
|
|
|
|
foreach ($schema['fields'] as $field_name => $field) {
|
|
if ($field['type'] == DB::FIELD_TYPE_NCLOB && $this->outputIsRequested($field_name, $options['output'])) {
|
|
$nclob_fields[] = $field_name;
|
|
}
|
|
}
|
|
|
|
if (!$nclob_fields) {
|
|
return $result;
|
|
}
|
|
|
|
$pk = $schema['key'];
|
|
$options = [
|
|
'output' => $nclob_fields,
|
|
'filter' => [$pk => array_keys($result)]
|
|
];
|
|
|
|
$db_items = DBselect(DB::makeSql($this->tableName, $options));
|
|
|
|
while ($db_item = DBfetch($db_items)) {
|
|
$result[$db_item[$pk]] += $db_item;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Check that valuemap belong to same host as item.
|
|
*
|
|
* @param array $items
|
|
* @param array|null $db_items
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected static function checkValueMaps(array $items, array $db_items = null): void {
|
|
$item_indexes = [];
|
|
|
|
foreach ($items as $i => $item) {
|
|
if (array_key_exists('valuemapid', $item) && $item['valuemapid'] != 0
|
|
&& ($db_items === null
|
|
|| bccomp($item['valuemapid'], $db_items[$item['itemid']]['valuemapid']) != 0)) {
|
|
$item_indexes[$item['valuemapid']][] = $i;
|
|
}
|
|
}
|
|
|
|
if (!$item_indexes) {
|
|
return;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['valuemapid', 'hostid'],
|
|
'valuemapids' => array_keys($item_indexes)
|
|
];
|
|
$db_valuemaps = DBselect(DB::makeSql('valuemap', $options));
|
|
|
|
while ($db_valuemap = DBfetch($db_valuemaps)) {
|
|
foreach ($item_indexes[$db_valuemap['valuemapid']] as $i) {
|
|
if (bccomp($db_valuemap['hostid'], $items[$i]['hostid']) != 0) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
'/'.($i + 1).'/valuemapid', _('cannot be a value map ID from another host or template')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add the internally used fields to the given $db_items.
|
|
*
|
|
* @param array $db_items
|
|
*/
|
|
protected static function addInternalFields(array &$db_items): void {
|
|
$result = DBselect(
|
|
'SELECT i.itemid,i.hostid,i.templateid,i.flags,h.status AS host_status'.
|
|
' FROM items i,hosts h'.
|
|
' WHERE i.hostid=h.hostid'.
|
|
' AND '.dbConditionId('i.itemid', array_keys($db_items))
|
|
);
|
|
|
|
while ($row = DBfetch($result)) {
|
|
$db_items[$row['itemid']] += $row;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Note: instances may override this to add e.g. tags.
|
|
*
|
|
* @param array $items
|
|
* @param array $db_items
|
|
*/
|
|
protected static function addAffectedObjects(array $items, array &$db_items): void {
|
|
self::addAffectedTags($items, $db_items);
|
|
self::addAffectedPreprocessing($items, $db_items);
|
|
self::addAffectedParameters($items, $db_items);
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array $db_items
|
|
*/
|
|
protected static function addAffectedTags(array $items, array &$db_items): void {
|
|
$itemids = [];
|
|
|
|
foreach ($items as $item) {
|
|
if (array_key_exists('tags', $item)) {
|
|
$itemids[] = $item['itemid'];
|
|
$db_items[$item['itemid']]['tags'] = [];
|
|
}
|
|
}
|
|
|
|
if (!$itemids) {
|
|
return;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['itemtagid', 'itemid', 'tag', 'value'],
|
|
'filter' => ['itemid' => $itemids]
|
|
];
|
|
$db_item_tags = DBselect(DB::makeSql('item_tag', $options));
|
|
|
|
while ($db_item_tag = DBfetch($db_item_tags)) {
|
|
$db_items[$db_item_tag['itemid']]['tags'][$db_item_tag['itemtagid']] =
|
|
array_diff_key($db_item_tag, array_flip(['itemid']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array $db_items
|
|
*/
|
|
protected static function addAffectedPreprocessing(array $items, array &$db_items): void {
|
|
$itemids = [];
|
|
|
|
foreach ($items as $item) {
|
|
if (array_key_exists('preprocessing', $item)) {
|
|
$itemids[] = $item['itemid'];
|
|
$db_items[$item['itemid']]['preprocessing'] = [];
|
|
}
|
|
}
|
|
|
|
if (!$itemids) {
|
|
return;
|
|
}
|
|
|
|
$options = [
|
|
'output' => [
|
|
'item_preprocid', 'itemid', 'step', 'type', 'params', 'error_handler', 'error_handler_params'
|
|
],
|
|
'filter' => ['itemid' => $itemids]
|
|
];
|
|
$db_item_preprocs = DBselect(DB::makeSql('item_preproc', $options));
|
|
|
|
while ($db_item_preproc = DBfetch($db_item_preprocs)) {
|
|
$db_items[$db_item_preproc['itemid']]['preprocessing'][$db_item_preproc['item_preprocid']] =
|
|
array_diff_key($db_item_preproc, array_flip(['itemid']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $items
|
|
* @param array $db_items
|
|
*/
|
|
protected static function addAffectedParameters(array $items, array &$db_items): void {
|
|
$itemids = [];
|
|
|
|
foreach ($items as $item) {
|
|
$db_type = $db_items[$item['itemid']]['type'];
|
|
|
|
if ((array_key_exists('parameters', $item) && $item['type'] == ITEM_TYPE_SCRIPT)
|
|
|| ($item['type'] != $db_type && $db_type == ITEM_TYPE_SCRIPT)) {
|
|
$itemids[] = $item['itemid'];
|
|
$db_items[$item['itemid']]['parameters'] = [];
|
|
}
|
|
elseif (array_key_exists('parameters', $item)) {
|
|
$db_items[$item['itemid']]['parameters'] = [];
|
|
}
|
|
}
|
|
|
|
if (!$itemids) {
|
|
return;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['item_parameterid', 'itemid', 'name', 'value'],
|
|
'filter' => ['itemid' => $itemids]
|
|
];
|
|
$db_item_parameters = DBselect(DB::makeSql('item_parameter', $options));
|
|
|
|
while ($db_item_parameter = DBfetch($db_item_parameters)) {
|
|
$db_items[$db_item_parameter['itemid']]['parameters'][$db_item_parameter['item_parameterid']] =
|
|
array_diff_key($db_item_parameter, array_flip(['itemid']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add the inherited items of the given items to the given item array.
|
|
*
|
|
* @param array $db_items
|
|
*/
|
|
public static function addInheritedItems(array &$db_items): void {
|
|
$templateids = array_keys($db_items);
|
|
|
|
do {
|
|
$options = [
|
|
'output' => ['itemid', 'name'],
|
|
'filter' => ['templateid' => $templateids]
|
|
];
|
|
$result = DBselect(DB::makeSql('items', $options));
|
|
|
|
$templateids = [];
|
|
|
|
while ($row = DBfetch($result)) {
|
|
if (!array_key_exists($row['itemid'], $db_items)) {
|
|
$templateids[] = $row['itemid'];
|
|
|
|
$db_items[$row['itemid']] = $row;
|
|
}
|
|
}
|
|
} while ($templateids);
|
|
}
|
|
|
|
/**
|
|
* Reset the MIN and MAX values of Y axis in the graphs, if such are calculated using the given items.
|
|
*
|
|
* @param array $del_itemids
|
|
*/
|
|
protected static function resetGraphsYAxis(array $del_itemids): void {
|
|
DB::update('graphs', [
|
|
'values' => [
|
|
'ymin_type' => GRAPH_YAXIS_TYPE_CALCULATED,
|
|
'ymin_itemid' => null
|
|
],
|
|
'where' => ['ymin_itemid' => $del_itemids]
|
|
]);
|
|
|
|
DB::update('graphs', [
|
|
'values' => [
|
|
'ymax_type' => GRAPH_YAXIS_TYPE_CALCULATED,
|
|
'ymax_itemid' => null
|
|
],
|
|
'where' => ['ymax_itemid' => $del_itemids]
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Delete triggers and trigger prototypes, which contain the given items in the expression.
|
|
*
|
|
* @param array $del_itemids
|
|
*/
|
|
protected static function deleteAffectedTriggers(array $del_itemids): void {
|
|
$result = DBselect(
|
|
'SELECT DISTINCT f.triggerid,t.flags'.
|
|
' FROM functions f,triggers t'.
|
|
' WHERE f.triggerid=t.triggerid'.
|
|
' AND '.dbConditionInt('f.itemid', $del_itemids)
|
|
);
|
|
|
|
$del_trigger_prototypeids = [];
|
|
$del_triggerids = [];
|
|
|
|
while ($row = DBfetch($result)) {
|
|
if ($row['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
|
$del_trigger_prototypeids[] = $row['triggerid'];
|
|
}
|
|
else {
|
|
$del_triggerids[] = $row['triggerid'];
|
|
}
|
|
}
|
|
|
|
if ($del_triggerids) {
|
|
CTriggerManager::delete($del_triggerids);
|
|
}
|
|
|
|
if ($del_trigger_prototypeids) {
|
|
CTriggerPrototypeManager::delete($del_trigger_prototypeids);
|
|
}
|
|
}
|
|
}
|