diff options
Diffstat (limited to 'ui/include/classes/api/services/CDiscoveryRule.php')
-rw-r--r-- | ui/include/classes/api/services/CDiscoveryRule.php | 439 |
1 files changed, 281 insertions, 158 deletions
diff --git a/ui/include/classes/api/services/CDiscoveryRule.php b/ui/include/classes/api/services/CDiscoveryRule.php index 4a2b123e38a..55bd23e9044 100644 --- a/ui/include/classes/api/services/CDiscoveryRule.php +++ b/ui/include/classes/api/services/CDiscoveryRule.php @@ -22,7 +22,7 @@ /** * Class containing methods for operations with discovery rules. */ -class CDiscoveryRule extends CItemGeneral { +class CDiscoveryRule extends CItemGeneralOld { public const ACCESS_RULES = parent::ACCESS_RULES + [ 'copy' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN] @@ -712,6 +712,77 @@ class CDiscoveryRule extends CItemGeneral { } /** + * @param array $templateids + * @param array|null $hostids + */ + public static function unlinkTemplateObjects(array $templateids, array $hostids = null): void { + $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : ''; + + $result = DBselect( + 'SELECT ii.itemid,h.status AS host_status'. + ' FROM items i,items ii,hosts h'. + ' WHERE i.itemid=ii.templateid'. + ' AND ii.hostid=h.hostid'. + ' AND '.dbConditionId('i.hostid', $templateids). + ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_RULE]). + $hostids_condition + ); + + $upd_items = []; + $ruleids = []; + + while ($row = DBfetch($result)) { + $upd_item = [ + 'templateid' => 0, + 'valuemapid' => 0 + ]; + + if ($row['host_status'] == HOST_STATUS_TEMPLATE) { + $upd_item += ['uuid' => generateUuidV4()]; + } + + $upd_items[] = [ + 'values' => $upd_item, + 'where' => ['itemid' => $row['itemid']] + ]; + + $ruleids[] = $row['itemid']; + } + + if ($upd_items) { + DB::update('items', $upd_items); + + /* + * TODO: The trigger prototypes and graphs also should be updated here when new audit log will be added for + * them. + */ + CItemPrototype::unlinkTemplateObjects($ruleids); + CHostPrototype::unlinkTemplateObjects($ruleids); + } + } + + /** + * @param array $templateids + * @param array|null $hostids + */ + public static function clearTemplateObjects(array $templateids, array $hostids = null): void { + $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : ''; + + $ruleids = DBfetchColumn(DBselect( + 'SELECT ii.itemid'. + ' FROM items i,items ii'. + ' WHERE i.itemid=ii.templateid'. + ' AND '.dbConditionId('i.hostid', $templateids). + ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_RULE]). + $hostids_condition + ), 'itemid'); + + if ($ruleids) { + CDiscoveryRuleManager::delete($ruleids); + } + } + + /** * Copies all of the triggers from the source discovery to the target discovery rule. * * @param array $src_discovery The source discovery rule to copy from. @@ -2124,14 +2195,14 @@ class CDiscoveryRule extends CItemGeneral { // fetch source and destination hosts $hosts = API::Host()->get([ - 'hostids' => [$srcDiscovery['hostid'], $hostid], 'output' => ['hostid', 'host', 'name', 'status'], - 'selectInterfaces' => API_OUTPUT_EXTEND, + 'selectInterfaces' => ['interfaceid', 'main', 'type', 'useip', 'ip', 'dns', 'port', 'details'], + 'hostids' => [$srcDiscovery['hostid'], $hostid], 'templated_hosts' => true, 'preservekeys' => true ]); - $srcHost = $hosts[$srcDiscovery['hostid']]; - $dstHost = $hosts[$hostid]; + $src_host = $hosts[$srcDiscovery['hostid']]; + $dst_host = $hosts[$hostid]; $dstDiscovery = $srcDiscovery; $dstDiscovery['hostid'] = $hostid; @@ -2163,9 +2234,9 @@ class CDiscoveryRule extends CItemGeneral { } // if this is a plain host, map discovery interfaces - if ($srcHost['status'] != HOST_STATUS_TEMPLATE) { + if ($src_host['status'] != HOST_STATUS_TEMPLATE) { // find a matching interface - $interface = self::findInterfaceForItem($dstDiscovery['type'], $dstHost['interfaces']); + $interface = self::findInterfaceForItem($dstDiscovery['type'], $dst_host['interfaces']); if ($interface) { $dstDiscovery['interfaceid'] = $interface['interfaceid']; } @@ -2173,7 +2244,7 @@ class CDiscoveryRule extends CItemGeneral { elseif ($interface !== false) { self::exception(ZBX_API_ERROR_PARAMETERS, _s( 'Cannot find host interface on "%1$s" for item key "%2$s".', - $dstHost['name'], + $dst_host['name'], $dstDiscovery['key_'] )); } @@ -2203,22 +2274,21 @@ class CDiscoveryRule extends CItemGeneral { $dstDiscovery['itemid'] = $newDiscovery['itemids'][0]; // copy prototypes - $new_prototypeids = $this->copyItemPrototypes($srcDiscovery, $dstDiscovery, $dstHost); + $this->copyItemPrototypes($srcDiscovery['itemid'], $src_host, $dstDiscovery['itemid'], $dst_host); - // if there were prototypes defined, clone everything else - if ($new_prototypeids) { - // fetch new prototypes - $dstDiscovery['items'] = API::ItemPrototype()->get([ - 'output' => ['itemid', 'key_'], - 'itemids' => $new_prototypeids, - 'preservekeys' => true - ]); + // fetch new prototypes + $dstDiscovery['items'] = API::ItemPrototype()->get([ + 'output' => ['itemid', 'key_'], + 'discoveryids' => $dstDiscovery['itemid'], + 'preservekeys' => true + ]); + if ($dstDiscovery['items']) { // copy graphs $this->copyGraphPrototypes($srcDiscovery, $dstDiscovery); // copy triggers - $this->copyTriggerPrototypes($srcDiscovery, $srcHost, $dstHost); + $this->copyTriggerPrototypes($srcDiscovery, $src_host, $dst_host); } // copy host prototypes @@ -2228,203 +2298,256 @@ class CDiscoveryRule extends CItemGeneral { } /** - * Copies all of the item prototypes from the source discovery to the target - * discovery rule. Return array of created item prototype ids. + * Create copies of items prototypes from the given source LLD rule to the given destination host or template. * - * @throws APIException if prototype saving fails - * - * @param array $srcDiscovery The source discovery rule to copy from - * @param array $dstDiscovery The target discovery rule to copy to - * @param array $dstHost The target host to copy the discovery rule to + * @param string $src_ruleid + * @param array $src_host + * @param array $src_host['interfaces'] + * @param string $dst_ruleid + * @param array $dst_host + * @param string $dst_host['hostid'] + * @param string $dst_host['host'] + * @param array $dst_host['interfaces'] * - * @return array + * @throws APIException */ - protected function copyItemPrototypes(array $srcDiscovery, array $dstDiscovery, array $dstHost) { - $item_prototypes = API::ItemPrototype()->get([ - 'output' => ['itemid', 'type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', - 'value_type', 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', - 'username', 'password', 'publickey', 'privatekey', 'interfaceid', 'port', 'description', 'jmx_endpoint', - 'master_itemid', 'templateid', 'url', 'query_fields', 'timeout', 'posts', 'status_codes', - 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', - 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', - 'allow_traps', 'discover', 'parameters' + private static function copyItemPrototypes(string $src_ruleid, array $src_host, string $dst_ruleid, + array $dst_host): void { + $src_items = API::ItemPrototype()->get([ + 'output' => ['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends', + 'valuemapid', 'logtimefmt', 'description', 'status', 'discover', + + // Type fields. + // The fields used for multiple item types. + 'interfaceid', 'authtype', 'username', 'password', 'params', 'timeout', 'delay', 'trapper_hosts', + + // Dependent item type specific fields. + 'master_itemid', + + // HTTP Agent item type specific fields. + 'url', 'query_fields', 'request_method', 'post_type', 'posts', + 'headers', 'status_codes', 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy', + 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'allow_traps', + + // IPMI item type specific fields. + 'ipmi_sensor', + + // JMX item type specific fields. + 'jmx_endpoint', + + // Script item type specific fields. + 'parameters', + + // SNMP item type specific fields. + 'snmp_oid', + + // SSH item type specific fields. + 'publickey', 'privatekey' ], 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'], 'selectTags' => ['tag', 'value'], - 'selectValueMap' => ['name'], - 'discoveryids' => $srcDiscovery['itemid'], + 'discoveryids' => $src_ruleid, 'preservekeys' => true ]); - $new_itemids = []; - $itemkey_to_id = []; - $create_items = []; - $src_valuemap_names = []; - $valuemap_map = []; - foreach ($item_prototypes as $item_prototype) { - if ($item_prototype['valuemap']) { - $src_valuemap_names[] = $item_prototype['valuemap']['name']; - } + if (!$src_items) { + return; } - if ($src_valuemap_names) { - $valuemap_map = array_column(API::ValueMap()->get([ - 'output' => ['valuemapid', 'name'], - 'hostids' => $dstHost['hostid'], - 'filter' => ['name' => $src_valuemap_names] - ]), 'valuemapid', 'name'); - } + $src_itemids = array_fill_keys(array_keys($src_items), true); + $src_valuemapids = []; + $src_interfaceids = []; + $src_dep_items = []; + $dep_itemids = []; + + foreach ($src_items as $itemid => $item) { + if ($item['valuemapid'] != 0) { + $src_valuemapids[$item['valuemapid']] = true; + } - if ($item_prototypes) { - $create_order = []; - $src_itemid_to_key = []; - $unresolved_master_itemids = []; + if ($item['interfaceid'] != 0) { + $src_interfaceids[$item['interfaceid']] = true; + } + + if ($item['type'] == ITEM_TYPE_DEPENDENT) { + if (array_key_exists($item['master_itemid'], $src_itemids)) { + $src_dep_items[$item['master_itemid']][] = $item; - // Gather all master item IDs and check if master item IDs already belong to item prototypes. - foreach ($item_prototypes as $itemid => $item_prototype) { - if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT - && !array_key_exists($item_prototype['master_itemid'], $item_prototypes)) { - $unresolved_master_itemids[$item_prototype['master_itemid']] = true; + unset($src_items[$itemid]); + } + else { + $dep_itemids[$item['master_itemid']][] = $item['itemid']; } } + } - $items = []; + $valuemap_links = []; - // It's possible that master items are non-prototype items. - if ($unresolved_master_itemids) { - $items = API::Item()->get([ - 'output' => ['itemid'], - 'itemids' => array_keys($unresolved_master_itemids), - 'webitems' => true, - 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL], - 'preservekeys' => true - ]); + if ($src_valuemapids) { + $src_valuemaps = API::ValueMap()->get([ + 'output' => ['valuemapid', 'name'], + 'valuemapids' => array_keys($src_valuemapids) + ]); - foreach ($items as $item) { - if (array_key_exists($item['itemid'], $unresolved_master_itemids)) { - unset($unresolved_master_itemids[$item['itemid']]); + $dst_valuemaps = API::ValueMap()->get([ + 'output' => ['valuemapid', 'hostid', 'name'], + 'hostids' => $dst_host['hostid'], + 'filter' => ['name' => array_unique(array_column($src_valuemaps, 'name'))] + ]); + + $dst_valuemapids = []; + + foreach ($dst_valuemaps as $dst_valuemap) { + $dst_valuemapids[$dst_valuemap['name']][$dst_valuemap['hostid']] = $dst_valuemap['valuemapid']; + } + + foreach ($src_valuemaps as $src_valuemap) { + if (array_key_exists($src_valuemap['name'], $dst_valuemapids)) { + foreach ($dst_valuemapids[$src_valuemap['name']] as $dst_hostid => $dst_valuemapid) { + $valuemap_links[$src_valuemap['valuemapid']][$dst_hostid] = $dst_valuemapid; } } + } + } - // If still there are IDs left, there's nothing more we can do. - if ($unresolved_master_itemids) { - reset($unresolved_master_itemids); - self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.', - 'master_itemid', _s('Item "%1$s" does not exist or you have no access to this item', - key($unresolved_master_itemids) - ))); + $interface_links = []; + $dst_interfaceids = []; + + if ($src_interfaceids) { + $src_interfaces = []; + + foreach ($src_host['interfaces'] as $src_interface) { + if (array_key_exists($src_interface['interfaceid'], $src_interfaceids)) { + $src_interfaces[$src_interface['interfaceid']] = + array_diff_key($src_interface, array_flip(['interfaceid'])); } } - foreach ($item_prototypes as $itemid => $item_prototype) { - $dependency_level = 0; - $master_item_prototype = $item_prototype; - $src_itemid_to_key[$itemid] = $item_prototype['key_']; + foreach ($dst_host['interfaces'] as $dst_interface) { + $dst_interfaceid = $dst_interface['interfaceid']; + unset($dst_interface['interfaceid']); - while ($master_item_prototype['type'] == ITEM_TYPE_DEPENDENT) { - if (array_key_exists($master_item_prototype['master_itemid'], $item_prototypes)) { - $master_item_prototype = $item_prototypes[$master_item_prototype['master_itemid']]; - ++$dependency_level; - } - else { - break; + foreach ($src_interfaces as $src_interfaceid => $src_interface) { + if ($src_interface == $dst_interface) { + $interface_links[$src_interfaceid][$dst_host['hostid']] = $dst_interfaceid; } } - $create_order[$itemid] = $dependency_level; + if ($dst_interface['main'] == INTERFACE_PRIMARY) { + $dst_interfaceids[$dst_host['hostid']][$dst_interface['type']] = $dst_interfaceid; + } } - asort($create_order); + } - $current_dependency = reset($create_order); + $master_item_links = []; - foreach ($create_order as $key => $dependency_level) { - if ($current_dependency != $dependency_level && $create_items) { - $current_dependency = $dependency_level; - $created_itemids = API::ItemPrototype()->create($create_items); + if ($dep_itemids) { + $master_items = API::Item()->get([ + 'output' => ['itemid', 'key_'], + 'itemids' => array_keys($dep_itemids) + ]); - if (!$created_itemids) { - self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.')); - } + $options = $dst_host['status'] == HOST_STATUS_TEMPLATE + ? ['templateids' => $dst_host['hostid']] + : ['hostids' => $dst_host['hostid']]; - $created_itemids = $created_itemids['itemids']; - $new_itemids = array_merge($new_itemids, $created_itemids); + $dst_master_items = API::Item()->get([ + 'output' => ['itemid', 'hostid', 'key_'], + 'filter' => ['key_' => array_unique(array_column($master_items, 'key_'))] + ] + $options); - foreach ($create_items as $index => $created_item) { - $itemkey_to_id[$created_item['key_']] = $created_itemids[$index]; - } + $dst_master_itemids = []; - $create_items = []; - } + foreach ($dst_master_items as $item) { + $dst_master_itemids[$item['hostid']][$item['key_']] = $item['itemid']; + } - $item_prototype = $item_prototypes[$key]; - $item_prototype['ruleid'] = $dstDiscovery['itemid']; - $item_prototype['hostid'] = $dstDiscovery['hostid']; + foreach ($master_items as $item) { + if (array_key_exists($dst_host['hostid'], $dst_master_itemids) + && array_key_exists($item['key_'], $dst_master_itemids[$dst_host['hostid']])) { + $master_item_links[$item['itemid']][$dst_host['hostid']] = + $dst_master_itemids[$dst_host['hostid']][$item['key_']]; + } + else { + $src_itemid = reset($dep_itemids[$item['itemid']]); - if ($item_prototype['valuemapid'] != 0) { - $item_prototype['valuemapid'] = array_key_exists($item_prototype['valuemap']['name'], $valuemap_map) - ? $valuemap_map[$item_prototype['valuemap']['name']] - : 0; + self::exception(ZBX_API_ERROR_PARAMETERS, _s( + 'Cannot copy item prototype with key "%1$s" without its master item with key "%2$s".', + $src_items[$src_itemid]['key_'], $item['key_'] + )); } + } + } - // map prototype interfaces - if ($dstHost['status'] != HOST_STATUS_TEMPLATE) { - // find a matching interface - $interface = self::findInterfaceForItem($item_prototype['type'], $dstHost['interfaces']); - if ($interface) { - $item_prototype['interfaceid'] = $interface['interfaceid']; + do { + $dst_items = []; + + foreach ($src_items as $src_item) { + $dst_item = array_diff_key($src_item, array_flip(['itemid'])); + + if ($src_item['valuemapid'] != 0) { + if (array_key_exists($src_item['valuemapid'], $valuemap_links) + && array_key_exists($dst_host['hostid'], $valuemap_links[$src_item['valuemapid']])) { + $dst_item['valuemapid'] = $valuemap_links[$src_item['valuemapid']][$dst_host['hostid']]; } - // no matching interface found, throw an error - elseif ($interface !== false) { - self::exception(ZBX_API_ERROR_PARAMETERS, _s( - 'Cannot find host interface on "%1$s" for item key "%2$s".', - $dstHost['name'], - $item_prototype['key_'] - )); + else { + $dst_item['valuemapid'] = 0; } } - if (!$item_prototype['preprocessing']) { - unset($item_prototype['preprocessing']); - } - - if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT) { - $master_itemid = $item_prototype['master_itemid']; + $dst_item['interfaceid'] = 0; - if (array_key_exists($master_itemid, $src_itemid_to_key)) { - $src_item_key = $src_itemid_to_key[$master_itemid]; - $item_prototype['master_itemid'] = $itemkey_to_id[$src_item_key]; + if ($src_item['interfaceid'] != 0) { + if (array_key_exists($src_item['interfaceid'], $interface_links) + && array_key_exists($dst_host['hostid'], $interface_links[$src_item['interfaceid']])) { + $dst_item['interfaceid'] = $interface_links[$src_item['interfaceid']][$dst_host['hostid']]; } else { - // It's a non-prototype item, so look for it on destination host. - $dst_item = get_same_item_for_host($items[$master_itemid], $dstHost['hostid']); - - if (!$dst_item) { - self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.')); + $type = itemTypeInterface($src_item['type']); + + if (in_array($type, + [INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI] + )) { + if (array_key_exists($dst_host['hostid'], $dst_interfaceids) + && array_key_exists($type, $dst_interfaceids[$dst_host['hostid']])) { + $dst_item['interfaceid'] = $dst_interfaceids[$dst_host['hostid']][$type]; + } + else { + self::exception(ZBX_API_ERROR_PARAMETERS, _s( + 'Cannot find host interface on "%1$s" for item prototype with key "%2$s".', + $dst_host['host'], $src_item['key_'] + )); + } } - - $item_prototype['master_itemid'] = $dst_item['itemid']; } } - else { - unset($item_prototype['master_itemid']); + + if ($src_item['type'] == ITEM_TYPE_DEPENDENT) { + $dst_item['master_itemid'] = $master_item_links[$src_item['master_itemid']][$dst_host['hostid']]; } - unset($item_prototype['templateid']); - $create_items[] = $item_prototype; + $dst_items[] = ['hostid' => $dst_host['hostid'], 'ruleid' => $dst_ruleid] + $dst_item; } - if ($create_items) { - $created_itemids = API::ItemPrototype()->create($create_items); + $response = API::ItemPrototype()->create($dst_items); - if (!$created_itemids) { - self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.')); - } + $_src_items = []; + + if ($src_dep_items) { + foreach ($src_items as $src_item) { + $dst_itemid = array_shift($response['itemids']); + + if (array_key_exists($src_item['itemid'], $src_dep_items)) { + $master_item_links[$src_item['itemid']][$dst_host['hostid']] = $dst_itemid; - $new_itemids = array_merge($new_itemids, $created_itemids['itemids']); + $_src_items = array_merge($_src_items, $src_dep_items[$src_item['itemid']]); + unset($src_dep_items[$src_item['itemid']]); + } + } } - } - return $new_itemids; + $src_items = $_src_items; + } while ($src_items); } /** |