['updateExisting' => false, 'createMissing' => false], 'templates' => ['updateExisting' => false, 'createMissing' => false], 'host_groups' => ['updateExisting' => false, 'createMissing' => false], 'hosts' => ['updateExisting' => false, 'createMissing' => false], 'templateDashboards' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 'templateLinkage' => ['createMissing' => false, 'deleteMissing' => false], 'items' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 'discoveryRules' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 'triggers' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 'graphs' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 'httptests' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 'maps' => ['updateExisting' => false, 'createMissing' => false], 'images' => ['updateExisting' => false, 'createMissing' => false], 'mediaTypes' => ['updateExisting' => false, 'createMissing' => false], 'valueMaps' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false] ]; $options += $default_options; foreach ($default_options as $entity => $rules) { $options[$entity] += $rules; } $object_options = ( $options['templateLinkage']['createMissing'] || $options['templateLinkage']['deleteMissing'] || $options['items']['updateExisting'] || $options['items']['createMissing'] || $options['items']['deleteMissing'] || $options['discoveryRules']['updateExisting'] || $options['discoveryRules']['createMissing'] || $options['discoveryRules']['deleteMissing'] || $options['triggers']['deleteMissing'] || $options['graphs']['deleteMissing'] || $options['httptests']['updateExisting'] || $options['httptests']['createMissing'] || $options['httptests']['deleteMissing'] ); $options['process_templates'] = ( !$options['templates']['updateExisting'] && ($object_options || $options['templateDashboards']['updateExisting'] || $options['templateDashboards']['createMissing'] || $options['templateDashboards']['deleteMissing'] ) ); $options['process_hosts'] = (!$options['hosts']['updateExisting'] && $object_options); $this->options = $options; $this->referencer = $referencer; $this->importedObjectContainer = $importedObjectContainer; } /** * Import configuration data. * * @param CImportDataAdapter $adapter an object to provide access to the imported data * * @return bool * * @throws Exception */ public function import(CImportDataAdapter $adapter): bool { $this->adapter = $adapter; // Parse all import for references to resolve them all together with less sql count. $this->gatherReferences(); $this->processTemplateGroups(); $this->processTemplates(); $this->processHostGroups(); $this->processHosts(); // Delete missing objects from processed hosts and templates. $this->deleteMissingHttpTests(); $this->deleteMissingTemplateDashboards(); $this->deleteMissingDiscoveryRules(); $this->deleteMissingTriggers(); $this->deleteMissingGraphs(); $this->deleteMissingItems(); // Import objects. $this->processHttpTests(); $this->processItems(); $this->processTriggers(); $this->processDiscoveryRules(); $this->processGraphs(); $this->processImages(); $this->processMaps(); $this->processTemplateDashboards(); $this->processMediaTypes(); return true; } /** * Parse all import data and collect references to objects. * For host objects it collects host names, for items - host name and item key, etc. * Collected references are added and resolved via the $this->referencer object. * * @see CImportReferencer */ protected function gatherReferences(): void { $template_groups_refs = []; $templates_refs = []; $host_groups_refs = []; $hosts_refs = []; $items_refs = []; $valuemaps_refs = []; $triggers_refs = []; $graphs_refs = []; $iconmaps_refs = []; $images_refs = []; $maps_refs = []; $services_refs = []; $slas_refs = []; $users_refs = []; $actions_refs = []; $media_types_refs = []; $template_dashboards_refs = []; $template_macros_refs = []; $host_macros_refs = []; $host_prototype_macros_refs = []; $proxy_refs = []; $host_prototypes_refs = []; $httptests_refs = []; $httpsteps_refs = []; foreach ($this->getFormattedTemplateGroups() as $group) { $template_groups_refs[$group['name']] = ['uuid' => $group['uuid']]; } foreach ($this->getFormattedTemplates() as $template) { $templates_refs[$template['host']] = ['uuid' => $template['uuid']]; foreach ($template['groups'] as $group) { $template_groups_refs += [$group['name'] => []]; } if (array_key_exists('macros', $template)) { foreach ($template['macros'] as $macro) { $template_macros_refs[$template['uuid']][] = $macro['macro']; } } if ($template['templates']) { foreach ($template['templates'] as $linked_template) { $templates_refs += [$linked_template['name'] => []]; } } } foreach ($this->getFormattedHostGroups() as $group) { $host_groups_refs[$group['name']] = ['uuid' => $group['uuid']]; } foreach ($this->getFormattedHosts() as $host) { $hosts_refs[$host['host']] = []; foreach ($host['groups'] as $group) { $host_groups_refs += [$group['name'] => []]; } if (array_key_exists('macros', $host)) { foreach ($host['macros'] as $macro) { $host_macros_refs[$host['host']][] = $macro['macro']; } } if ($host['templates']) { foreach ($host['templates'] as $linked_template) { $templates_refs += [$linked_template['name'] => []]; } } if ($host['proxy']) { $proxy_refs[$host['proxy']['name']] = []; } } foreach ($this->getFormattedItems() as $host => $items) { foreach ($items as $item) { $items_refs[$host][$item['key_']] = array_key_exists('uuid', $item) ? ['uuid' => $item['uuid']] : []; if ($item['valuemap']) { $valuemaps_refs[$host][$item['valuemap']['name']] = []; } } } foreach ($this->getFormattedDiscoveryRules() as $host => $discovery_rules) { foreach ($discovery_rules as $discovery_rule) { $items_refs[$host][$discovery_rule['key_']] = array_key_exists('uuid', $discovery_rule) ? ['uuid' => $discovery_rule['uuid']] : []; foreach ($discovery_rule['item_prototypes'] as $item_prototype) { $items_refs[$host][$item_prototype['key_']] = array_key_exists('uuid', $item_prototype) ? ['uuid' => $item_prototype['uuid']] : []; if (!empty($item_prototype['valuemap'])) { $valuemaps_refs[$host][$item_prototype['valuemap']['name']] = []; } } foreach ($discovery_rule['trigger_prototypes'] as $trigger) { $description = $trigger['description']; $expression = $trigger['expression']; $recovery_expression = $trigger['recovery_expression']; $triggers_refs[$description][$expression][$recovery_expression] = array_key_exists('uuid', $trigger) ? ['uuid' => $trigger['uuid']] : []; if (array_key_exists('dependencies', $trigger)) { foreach ($trigger['dependencies'] as $dependency) { $name = $dependency['name']; $expression = $dependency['expression']; $recovery_expression = $dependency['recovery_expression']; if (!array_key_exists($name, $triggers_refs) || !array_key_exists($expression, $triggers_refs[$name]) || !array_key_exists($recovery_expression, $triggers_refs[$name][$expression])) { $triggers_refs[$name][$expression][$recovery_expression] = []; } } } } foreach ($discovery_rule['graph_prototypes'] as $graph) { if ($graph['ymin_item_1']) { $item_host = $graph['ymin_item_1']['host']; $item_key = $graph['ymin_item_1']['key']; if (!array_key_exists($item_host, $items_refs) || !array_key_exists($item_key, $items_refs[$item_host])) { $items_refs[$item_host][$item_key] = []; } } if ($graph['ymax_item_1']) { $item_host = $graph['ymax_item_1']['host']; $item_key = $graph['ymax_item_1']['key']; if (!array_key_exists($item_host, $items_refs) || !array_key_exists($item_key, $items_refs[$item_host])) { $items_refs[$item_host][$item_key] = []; } } foreach ($graph['gitems'] as $gitem) { $item_host = $gitem['item']['host']; $item_key = $gitem['item']['key']; if (!array_key_exists($item_host, $templates_refs)) { $hosts_refs[$item_host] = []; } if (!array_key_exists($item_host, $items_refs) || !array_key_exists($item_key, $items_refs[$item_host])) { $items_refs[$item_host][$item_key] = []; } $graphs_refs[$item_host][$graph['name']] = array_key_exists('uuid', $graph) ? ['uuid' => $graph['uuid']] : []; } } foreach ($discovery_rule['host_prototypes'] as $host_prototype) { if (array_key_exists('uuid', $host_prototype)) { $host_prototypes_refs['uuid'][$host][$discovery_rule['uuid']][] = $host_prototype['uuid']; } else { $host_prototypes_refs['host'][$host][$discovery_rule['key_']][] = $host_prototype['host']; } foreach ($host_prototype['group_links'] as $group_prototype) { $host_groups_refs += [$group_prototype['group']['name'] => []]; } if (array_key_exists('macros', $host_prototype)) { foreach ($host_prototype['macros'] as $macro) { if (array_key_exists('uuid', $host_prototype)) { $host_prototype_macros_refs['uuid'][$host][$discovery_rule['key_']] [$host_prototype['uuid']][] = $macro['macro']; } else { $host_prototype_macros_refs['host'][$host][$discovery_rule['key_']] [$host_prototype['host']][] = $macro['macro']; } } } foreach ($host_prototype['templates'] as $template) { $templates_refs += [$template['name'] => []]; } } if ($discovery_rule['overrides']) { foreach ($discovery_rule['overrides'] as $override) { foreach ($override['operations'] as $operation) { if ($operation['operationobject'] == OPERATION_OBJECT_HOST_PROTOTYPE && array_key_exists('optemplate', $operation)) { foreach ($operation['optemplate'] as $template) { $templates_refs += [$template['name'] => []]; } } } } } } } foreach ($this->getFormattedGraphs() as $graph) { if ($graph['ymin_item_1']) { $item_host = $graph['ymin_item_1']['host']; $item_key = $graph['ymin_item_1']['key']; if (!array_key_exists($item_host, $templates_refs)) { $hosts_refs[$item_host] = []; } if (!array_key_exists($item_host, $items_refs) || !array_key_exists($item_key, $items_refs[$item_host])) { $items_refs[$item_host][$item_key] = []; } } if ($graph['ymax_item_1']) { $item_host = $graph['ymax_item_1']['host']; $item_key = $graph['ymax_item_1']['key']; if (!array_key_exists($item_host, $templates_refs)) { $hosts_refs[$item_host] = []; } if (!array_key_exists($item_host, $items_refs) || !array_key_exists($item_key, $items_refs[$item_host])) { $items_refs[$item_host][$item_key] = []; } } if (array_key_exists('gitems', $graph) && $graph['gitems']) { foreach ($graph['gitems'] as $gitem) { $item_host = $gitem['item']['host']; $item_key = $gitem['item']['key']; if (!array_key_exists($item_host, $templates_refs)) { $hosts_refs[$item_host] = []; } if (!array_key_exists($item_host, $items_refs) || !array_key_exists($item_key, $items_refs[$item_host])) { $items_refs[$item_host][$item_key] = []; } $graphs_refs[$gitem['item']['host']][$graph['name']] = array_key_exists('uuid', $graph) ? ['uuid' => $graph['uuid']] : []; } } } foreach ($this->getFormattedTriggers() as $trigger) { $triggers_refs[$trigger['description']][$trigger['expression']][$trigger['recovery_expression']] = array_key_exists('uuid', $trigger) ? ['uuid' => $trigger['uuid']] : []; if (array_key_exists('dependencies', $trigger)) { foreach ($trigger['dependencies'] as $dependency) { $name = $dependency['name']; $expression = $dependency['expression']; $recovery_expression = $dependency['recovery_expression']; if (!array_key_exists($name, $triggers_refs) || !array_key_exists($expression, $triggers_refs[$name]) || !array_key_exists($recovery_expression, $triggers_refs[$name][$expression])) { $triggers_refs[$name][$expression][$recovery_expression] = []; } } } } foreach ($this->getFormattedMaps() as $map) { $maps_refs[$map['name']] = []; if ($map['iconmap'] && array_key_exists('name', $map['iconmap']) && $map['iconmap']['name'] !== '') { $iconmaps_refs[$map['iconmap']['name']] = []; } if ($map['background'] && array_key_exists('name', $map['background']) && $map['background']['name'] !== '') { $images_refs[$map['background']['name']] = []; } if (array_key_exists('selements', $map)) { foreach ($map['selements'] as $selement) { switch ($selement['elementtype']) { case SYSMAP_ELEMENT_TYPE_MAP: $maps_refs[$selement['elements'][0]['name']] = []; break; case SYSMAP_ELEMENT_TYPE_HOST_GROUP: $host_groups_refs += [$selement['elements'][0]['name'] => []]; break; case SYSMAP_ELEMENT_TYPE_HOST: $hosts_refs += [$selement['elements'][0]['host'] => []]; break; case SYSMAP_ELEMENT_TYPE_TRIGGER: foreach ($selement['elements'] as $element) { $description = $element['description']; $expression = $element['expression']; $recovery_expression = $element['recovery_expression']; if (!array_key_exists($description, $triggers_refs) || !array_key_exists($expression, $triggers_refs[$description]) || !array_key_exists($recovery_expression, $triggers_refs[$description][$expression])) { $triggers_refs[$description][$expression][$recovery_expression] = []; } } break; } } } if (array_key_exists('links', $map)) { foreach ($map['links'] as $link) { if (array_key_exists('linktriggers', $link)) { foreach ($link['linktriggers'] as $link_trigger) { $description = $link_trigger['trigger']['description']; $expression = $link_trigger['trigger']['expression']; $recovery_expression = $link_trigger['trigger']['recovery_expression']; if (!array_key_exists($description, $triggers_refs) || !array_key_exists($expression, $triggers_refs[$description]) || !array_key_exists($recovery_expression, $triggers_refs[$description][$expression])) { $triggers_refs[$description][$expression][$recovery_expression] = []; } } } } } } foreach ($this->getFormattedTemplateDashboards() as $host => $dashboards) { foreach ($dashboards as $dashboard) { $template_dashboards_refs[$dashboard['uuid']]['name'] = $dashboard['name']; if (!$dashboard['pages']) { continue; } foreach ($dashboard['pages'] as $dashboard_page) { if (!$dashboard_page['widgets']) { continue; } foreach ($dashboard_page['widgets'] as $widget) { foreach ($widget['fields'] as $field) { $value = $field['value']; switch ($field['type']) { case ZBX_WIDGET_FIELD_TYPE_ITEM: case ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE: $templates_refs += [$value['host'] => []]; if (!array_key_exists($value['host'], $items_refs) || !array_key_exists($value['key'], $items_refs[$value['host']])) { $items_refs[$value['host']][$value['key']] = []; } break; case ZBX_WIDGET_FIELD_TYPE_GRAPH: case ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE: $templates_refs += [$value['host'] => []]; if (!array_key_exists($value['host'], $graphs_refs) || !array_key_exists($value['name'], $graphs_refs[$value['host']])) { $graphs_refs[$value['host']][$value['name']] = []; } break; case ZBX_WIDGET_FIELD_TYPE_MAP: if (!array_key_exists($value['name'], $maps_refs)) { $maps_refs[$value['name']] = []; } break; case ZBX_WIDGET_FIELD_TYPE_SERVICE: if (!array_key_exists($value['name'], $services_refs)) { $services_refs[$value['name']] = []; } break; case ZBX_WIDGET_FIELD_TYPE_SLA: if (!array_key_exists($value['name'], $slas_refs)) { $slas_refs[$value['name']] = []; } break; case ZBX_WIDGET_FIELD_TYPE_USER: if (!array_key_exists($value['username'], $users_refs)) { $users_refs[$value['username']] = []; } break; case ZBX_WIDGET_FIELD_TYPE_ACTION: if (!array_key_exists($value['name'], $actions_refs)) { $actions_refs[$value['name']] = []; } break; case ZBX_WIDGET_FIELD_TYPE_MEDIA_TYPE: if (!array_key_exists($value['name'], $media_types_refs)) { $media_types_refs[$value['name']] = []; } break; } } } } } } foreach ($this->getFormattedHttpTests() as $host => $httptests) { foreach ($httptests as $httptest) { $httptests_refs[$host][$httptest['name']] = array_key_exists('uuid', $httptest) ? ['uuid' => $httptest['uuid']] : []; } } foreach ($this->getFormattedHttpSteps() as $host => $httptests) { foreach ($httptests as $httptest_name => $httpsteps) { foreach ($httpsteps as $httpstep) { $httpsteps_refs[$host][$httptest_name][$httpstep['name']] = []; } } } foreach ($this->getFormattedImages() as $image) { $images_refs[$image['name']] = []; } $this->referencer->addTemplateGroups($template_groups_refs); $this->referencer->addTemplates($templates_refs); $this->referencer->addHostGroups($host_groups_refs); $this->referencer->addHosts($hosts_refs); $this->referencer->addItems($items_refs); $this->referencer->addValuemaps($valuemaps_refs); $this->referencer->addTriggers($triggers_refs); $this->referencer->addGraphs($graphs_refs); $this->referencer->addIconmaps($iconmaps_refs); $this->referencer->addImages($images_refs); $this->referencer->addMaps($maps_refs); $this->referencer->addServices($services_refs); $this->referencer->addSlas($slas_refs); $this->referencer->addUsers($users_refs); $this->referencer->addActions($actions_refs); $this->referencer->addMediaTypes($media_types_refs); $this->referencer->addTemplateDashboards($template_dashboards_refs); $this->referencer->addTemplateMacros($template_macros_refs); $this->referencer->addHostMacros($host_macros_refs); $this->referencer->addHostPrototypeMacros($host_prototype_macros_refs); $this->referencer->addProxies($proxy_refs); $this->referencer->addHostPrototypes($host_prototypes_refs); $this->referencer->addHttpTests($httptests_refs); $this->referencer->addHttpSteps($httpsteps_refs); } /** * Import template groups. */ protected function processTemplateGroups(): void { if (!$this->options['template_groups']['createMissing'] && !$this->options['template_groups']['updateExisting']) { return; } $groups_to_create = []; $groups_to_update = []; foreach ($this->getFormattedTemplateGroups() as $group) { $groupid = $this->referencer->findTemplateGroupidByUuid($group['uuid']); if ($groupid === null) { $groupid = $this->referencer->findTemplateGroupidByName($group['name']); } if ($groupid !== null) { $groups_to_update[] = $group + ['groupid' => $groupid]; } else { $groups_to_create[] = $group; } } if ($this->options['template_groups']['updateExisting'] && $groups_to_update) { API::TemplateGroup()->update($groups_to_update); foreach ($groups_to_update as $group) { $this->referencer->setDbTemplateGroup($group['groupid'], $group); } } if ($this->options['template_groups']['createMissing'] && $groups_to_create) { $created_groups = API::TemplateGroup()->create($groups_to_create); foreach ($created_groups['groupids'] as $index => $groupid) { $this->referencer->setDbTemplateGroup($groupid, $groups_to_create[$index]); } } } /** * Import host groups. */ protected function processHostGroups(): void { if (!$this->options['host_groups']['createMissing'] && !$this->options['host_groups']['updateExisting']) { return; } $groups_to_create = []; $groups_to_update = []; foreach ($this->getFormattedHostGroups() as $group) { $groupid = $this->referencer->findHostGroupidByUuid($group['uuid']); if ($groupid === null) { $groupid = $this->referencer->findHostGroupidByName($group['name']); } if ($groupid !== null) { $groups_to_update[] = $group + ['groupid' => $groupid]; } else { $groups_to_create[] = $group; } } if ($this->options['host_groups']['updateExisting'] && $groups_to_update) { API::HostGroup()->update($groups_to_update); foreach ($groups_to_update as $group) { $this->referencer->setDbHostGroup($group['groupid'], $group); } } if ($this->options['host_groups']['createMissing'] && $groups_to_create) { $created_groups = API::HostGroup()->create($groups_to_create); foreach ($created_groups['groupids'] as $index => $groupid) { $this->referencer->setDbHostGroup($groupid, $groups_to_create[$index]); } } } /** * Import templates. * * @throws Exception */ protected function processTemplates(): void { if ($this->options['templates']['updateExisting'] || $this->options['templates']['createMissing'] || $this->options['process_templates']) { $templates = $this->getFormattedTemplates(); if ($templates) { $template_importer = new CTemplateImporter($this->options, $this->referencer, $this->importedObjectContainer ); $template_importer->import($templates); // Get list of imported template IDs and add them processed template ID list. $templateids = $template_importer->getProcessedTemplateids(); $this->importedObjectContainer->addTemplateIds($templateids); } } } /** * Import hosts. * * @throws Exception */ protected function processHosts(): void { if ($this->options['hosts']['updateExisting'] || $this->options['hosts']['createMissing'] || $this->options['process_hosts']) { $hosts = $this->getFormattedHosts(); if ($hosts) { $host_importer = new CHostImporter($this->options, $this->referencer, $this->importedObjectContainer); $host_importer->import($hosts); // Get list of imported host IDs and add them processed host ID list. $hostids = $host_importer->getProcessedHostIds(); $this->importedObjectContainer->addHostIds($hostids); } } } /** * Import items. * * @throws Exception */ protected function processItems(): void { if (!$this->options['items']['createMissing'] && !$this->options['items']['updateExisting']) { return; } $master_item_key = 'master_item'; $order_tree = $this->getItemsOrder($master_item_key); $items_to_create = []; $items_to_update = []; $levels = []; foreach ($this->getFormattedItems() as $host => $items) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); if ($hostid === null || (!$this->importedObjectContainer->isHostProcessed($hostid) && !$this->importedObjectContainer->isTemplateProcessed($hostid))) { continue; } foreach ($order_tree[$host] as $item_key => $level) { $item = $items[$item_key]; $item['hostid'] = $hostid; unset($item['triggers']); $levels[$level] = true; $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)) { unset($item['delay']); } if (array_key_exists('interface_ref', $item) && $item['interface_ref']) { $interfaceid = $this->referencer->findInterfaceidByRef($hostid, $item['interface_ref']); if ($interfaceid === null) { throw new Exception(_s('Cannot find interface "%1$s" used for item "%2$s" on "%3$s".', $item['interface_ref'], $item['name'], $host )); } $item['interfaceid'] = $interfaceid; } unset($item['interface_ref']); if (array_key_exists('valuemap', $item) && $item['valuemap']) { $valuemapid = $this->referencer->findValuemapidByName($hostid, $item['valuemap']['name']); if ($valuemapid === null) { throw new Exception(_s( 'Cannot find value map "%1$s" used for item "%2$s" on "%3$s".', $item['valuemap']['name'], $item['name'], $host )); } $item['valuemapid'] = $valuemapid; } unset($item['valuemap']); if ($item['type'] == ITEM_TYPE_DEPENDENT) { if (!array_key_exists('key', $item[$master_item_key])) { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _('cannot be empty') )); } $master_itemid = $this->referencer->findItemidByKey($hostid, $item[$master_item_key]['key'], true); if ($master_itemid !== null) { $item['master_itemid'] = $master_itemid; unset($item[$master_item_key]); } } else { unset($item[$master_item_key]); } if ($item['type'] == ITEM_TYPE_HTTPAGENT) { $headers = []; foreach ($item['headers'] as $header) { $headers[$header['name']] = $header['value']; } $item['headers'] = $headers; $query_fields = []; foreach ($item['query_fields'] as $query_field) { $query_fields[] = [$query_field['name'] => $query_field['value']]; } $item['query_fields'] = $query_fields; } foreach ($item['preprocessing'] as &$preprocessing_step) { $preprocessing_step['params'] = implode("\n", $preprocessing_step['parameters']); unset($preprocessing_step['parameters']); } unset($preprocessing_step); $itemid = null; if (array_key_exists('uuid', $item)) { $itemid = $this->referencer->findItemidByUuid($item['uuid']); } if ($itemid === null) { $itemid = $this->referencer->findItemidByKey($hostid, $item['key_']); } if ($itemid !== null) { $item['itemid'] = $itemid; $items_to_update[$level][] = $item; } else { $items_to_create[$level][] = $item; } } } ksort($levels); foreach (array_keys($levels) as $level) { if ($this->options['items']['updateExisting'] && array_key_exists($level, $items_to_update)) { $this->updateItemsWithDependency([$items_to_update[$level]], $master_item_key, API::Item()); } if ($this->options['items']['createMissing'] && array_key_exists($level, $items_to_create)) { $this->createItemsWithDependency([$items_to_create[$level]], $master_item_key, API::Item()); } } // Refresh items because templated ones can be inherited to host and used in triggers, graphs, etc. $this->referencer->refreshItems(); } /** * Create CItem or CItemPrototype with dependency. * * @param array $items_by_level Associative array of entities where key is entity dependency * level and value is array of entities for this level. * @param string $master_item_key Master entity array key in xml parsed data. * @param CItem|CItemPrototype $api_service Entity service which is capable to proceed with entity create. * * @throws Exception if entity master entity can not be resolved. */ protected function createItemsWithDependency(array $items_by_level, string $master_item_key, CItemGeneral $api_service): void { foreach ($items_by_level as $items_to_create) { foreach ($items_to_create as &$item) { if (array_key_exists($master_item_key, $item)) { $item['master_itemid'] = $this->referencer->findItemidByKey($item['hostid'], $item[$master_item_key]['key'], true ); if ($item['master_itemid'] === null) { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _s('value "%1$s" not found', $item[$master_item_key]['key']) )); } unset($item[$master_item_key]); } } unset($item); $created_items = $api_service->create($items_to_create); foreach ($items_to_create as $index => $item) { $this->referencer->setDbItem($created_items['itemids'][$index], $item); } } } /** * Update CItem or CItemPrototype with dependency. * * @param array $items_by_level Associative array of entities where key is entity dependency * level and value is array of entities for this level. * @param string $master_item_key Master entity array key in xml parsed data. * @param CItem|CItemPrototype $api_service Entity service which is capable to proceed with entity update. * * @throws Exception if entity master entity can not be resolved. */ protected function updateItemsWithDependency(array $items_by_level, string $master_item_key, CItemGeneral $api_service): void { foreach ($items_by_level as $items_to_update) { $hostids = []; foreach ($items_to_update as &$item) { if (array_key_exists($master_item_key, $item)) { $item['master_itemid'] = $this->referencer->findItemidByKey($item['hostid'], $item[$master_item_key]['key'], true ); if ($item['master_itemid'] === null) { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _s('value "%1$s" not found', $item[$master_item_key]['key']) )); } unset($item[$master_item_key]); } $hostids[] = $item['hostid']; unset($item['hostid']); } unset($item); $updated_items = $api_service->update($items_to_update); foreach ($items_to_update as $index => $item) { $item['hostid'] = array_shift($hostids); $this->referencer->setDbItem($updated_items['itemids'][$index], $item); } } } /** * Import discovery rules. * * @throws Exception */ protected function processDiscoveryRules(): void { if (!$this->options['discoveryRules']['createMissing'] && !$this->options['discoveryRules']['updateExisting']) { return; } $master_item_key = 'master_item'; $discovery_rules_by_hosts = $this->getFormattedDiscoveryRules(); if (!$discovery_rules_by_hosts) { return; } // Unset rules that are related to hosts we did not process. foreach ($discovery_rules_by_hosts as $host => $discovery_rules) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); if ($hostid === null || (!$this->importedObjectContainer->isHostProcessed($hostid) && !$this->importedObjectContainer->isTemplateProcessed($hostid))) { unset($discovery_rules_by_hosts[$host]); } } if ($this->options['discoveryRules']['updateExisting']) { $this->deleteMissingPrototypes($discovery_rules_by_hosts); } $discovery_rules_to_create = []; $discovery_rules_to_update = []; $upd_discovery_rule_hostids = []; /* * It's possible that some LLD rules use master items which are web items. They don't reside in item * references at this point. For items and item prototypes web items are found while processing the order of * them, but for LLD rules there is no ordering, so this is done independently. So due to the nature of constant * item refreshing after each entity type is processed, it's safer to collect web items once more here where it * is necessary. Collect host IDs and master item keys that cannot be resolved and then find web items and add * references to item list. */ $unresolved_master_items = []; $hostids = []; foreach ($discovery_rules_by_hosts as $host => $discovery_rules) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); $hostids[$hostid] = true; foreach ($discovery_rules as $discovery_rule) { if ($discovery_rule['type'] == ITEM_TYPE_DEPENDENT) { if (!array_key_exists('key', $discovery_rule[$master_item_key])) { throw new Exception( _s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _('cannot be empty') )); } // if key cannot be resolved if ($this->referencer->findItemidByKey($hostid, $discovery_rule[$master_item_key]['key']) === null) { $unresolved_master_items[$discovery_rule[$master_item_key]['key']] = true; } } } } if ($unresolved_master_items) { $items = API::Item()->get([ 'output' => ['hostid', 'itemid', 'key_'], 'hostids' => array_keys($hostids), 'filter' => ['key_' => array_keys($unresolved_master_items)], 'webitems' => true ]); foreach ($items as $item) { $this->referencer->setDbItem($item['itemid'], $item); } } foreach ($discovery_rules_by_hosts as $host => $discovery_rules) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); foreach ($discovery_rules as $discovery_rule) { $discovery_rule['hostid'] = $hostid; $itemid = null; if (array_key_exists('uuid', $discovery_rule)) { $itemid = $this->referencer->findItemidByUuid($discovery_rule['uuid']); } if ($itemid === null) { $itemid = $this->referencer->findItemidByKey($hostid, $discovery_rule['key_']); } unset($discovery_rule['item_prototypes'], $discovery_rule['trigger_prototypes'], $discovery_rule['graph_prototypes'], $discovery_rule['host_prototypes'] ); $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_JMX, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT ]; if (!in_array($discovery_rule['type'], $delay_types)) { unset($discovery_rule['delay']); } if (array_key_exists('interface_ref', $discovery_rule) && $discovery_rule['interface_ref']) { $interfaceid = $this->referencer->findInterfaceidByRef($hostid, $discovery_rule['interface_ref']); if ($interfaceid === null) { throw new Exception(_s('Cannot find interface "%1$s" used for discovery rule "%2$s" on "%3$s".', $discovery_rule['interface_ref'], $discovery_rule['name'], $host )); } $discovery_rule['interfaceid'] = $interfaceid; } unset($discovery_rule['interface_ref']); if ($discovery_rule['type'] == ITEM_TYPE_HTTPAGENT) { $headers = []; foreach ($discovery_rule['headers'] as $header) { $headers[$header['name']] = $header['value']; } $discovery_rule['headers'] = $headers; $query_fields = []; foreach ($discovery_rule['query_fields'] as $query_field) { $query_fields[] = [$query_field['name'] => $query_field['value']]; } $discovery_rule['query_fields'] = $query_fields; } if ($discovery_rule['type'] == ITEM_TYPE_DEPENDENT) { if (!array_key_exists('key', $discovery_rule[$master_item_key])) { throw new Exception( _s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _('cannot be empty') )); } $discovery_rule['master_itemid'] = $this->referencer->findItemidByKey($hostid, $discovery_rule[$master_item_key]['key'], true ); } unset($discovery_rule[$master_item_key]); if ($discovery_rule['overrides']) { foreach ($discovery_rule['overrides'] as &$override) { foreach ($override['operations'] as &$operation) { if ($operation['operationobject'] == OPERATION_OBJECT_HOST_PROTOTYPE && array_key_exists('optemplate', $operation)) { foreach ($operation['optemplate'] as &$template) { $templateid = $this->referencer->findTemplateidByHost($template['name']); if ($templateid === null) { throw new Exception(_s( 'Cannot find template "%1$s" for override "%2$s" of discovery rule "%3$s" on "%4$s".', $template['name'], $override['name'], $discovery_rule['name'], $host )); } $template['templateid'] = $templateid; unset($template['name']); } unset($template); } } unset($operation); } unset($override); } foreach ($discovery_rule['preprocessing'] as &$preprocessing_step) { $preprocessing_step['params'] = implode("\n", $preprocessing_step['parameters']); unset($preprocessing_step['parameters']); } unset($preprocessing_step); if (array_key_exists('filter', $discovery_rule)) { foreach ($discovery_rule['filter']['conditions'] as &$condition) { if ($discovery_rule['filter']['evaltype'] != CONDITION_EVAL_TYPE_EXPRESSION) { unset($condition['formulaid']); } } unset($condition); } foreach ($discovery_rule['overrides'] as &$override) { if (!array_key_exists('filter', $override)) { continue; } foreach ($override['filter']['conditions'] as &$condition) { if ($override['filter']['evaltype'] != CONDITION_EVAL_TYPE_EXPRESSION) { unset($condition['formulaid']); } } unset($condition); } unset($override); if ($itemid !== null) { $discovery_rule['itemid'] = $itemid; $discovery_rules_to_update[] = array_diff_key($discovery_rule, array_flip(['hostid'])); $upd_discovery_rule_hostids[] = $discovery_rule['hostid']; } else { /* * The array key "lld_macro_paths" must exist at this point. It is processed by chain conversion. * Unlike discoveryrule.update method, discoveryrule.create does not allow "lld_macro_paths" * to be empty. */ if (!$discovery_rule['lld_macro_paths']) { unset($discovery_rule['lld_macro_paths']); } $discovery_rules_to_create[] = $discovery_rule; } } } $processed_discovery_rules = []; if ($this->options['discoveryRules']['createMissing'] && $discovery_rules_to_create) { API::DiscoveryRule()->create($discovery_rules_to_create); foreach ($discovery_rules_to_create as $discovery_rule) { $processed_discovery_rules[$discovery_rule['hostid']][$discovery_rule['key_']] = 1; } } if ($this->options['discoveryRules']['updateExisting'] && $discovery_rules_to_update) { API::DiscoveryRule()->update($discovery_rules_to_update); foreach ($discovery_rules_to_update as $discovery_rule) { $hostid = array_shift($upd_discovery_rule_hostids); $processed_discovery_rules[$hostid][$discovery_rule['key_']] = 1; } } // Refresh discovery rules because templated ones can be inherited to host and used for prototypes. $this->referencer->refreshItems(); $order_tree = $this->getDiscoveryRulesItemsOrder($master_item_key); // process prototypes $item_prototypes_to_update = []; $item_prototypes_to_create = []; $host_prototypes_to_update = []; $host_prototypes_to_create = []; $levels = []; foreach ($discovery_rules_by_hosts as $host => $discovery_rules) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); foreach ($discovery_rules as $discovery_rule) { // if rule was not processed we should not create/update any of its prototypes if (!array_key_exists($hostid, $processed_discovery_rules) || !array_key_exists($discovery_rule['key_'], $processed_discovery_rules[$hostid])) { continue; } $itemid = $this->referencer->findItemidByKey($hostid, $discovery_rule['key_']); // prototypes $item_prototypes = $discovery_rule['item_prototypes'] ? $order_tree[$host][$discovery_rule['key_']] : []; foreach ($item_prototypes as $index => $level) { $item_prototype = $discovery_rule['item_prototypes'][$index]; $item_prototype['hostid'] = $hostid; unset($item_prototype['trigger_prototypes']); $levels[$level] = true; $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_prototype['type'], $delay_types)) { unset($item_prototype['delay']); } if (array_key_exists('interface_ref', $item_prototype) && $item_prototype['interface_ref']) { $interfaceid = $this->referencer->findInterfaceidByRef($hostid, $item_prototype['interface_ref'] ); if ($interfaceid !== null) { $item_prototype['interfaceid'] = $interfaceid; } else { throw new Exception(_s( 'Cannot find interface "%1$s" used for item prototype "%2$s" of discovery rule "%3$s" on "%4$s".', $item_prototype['interface_ref'], $item_prototype['name'], $discovery_rule['name'], $host )); } } unset($item_prototype['interface_ref']); if ($item_prototype['valuemap']) { $valuemapid = $this->referencer->findValuemapidByName($hostid, $item_prototype['valuemap']['name'] ); if ($valuemapid === null) { throw new Exception(_s( 'Cannot find value map "%1$s" used for item prototype "%2$s" of discovery rule "%3$s" on "%4$s".', $item_prototype['valuemap']['name'], $item_prototype['name'], $discovery_rule['name'], $host )); } $item_prototype['valuemapid'] = $valuemapid; } unset($item_prototype['valuemap']); if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT) { if (!array_key_exists('key', $item_prototype[$master_item_key])) { throw new Exception( _s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _('cannot be empty') )); } $master_item_prototypeid = $this->referencer->findItemidByKey($hostid, $item_prototype[$master_item_key]['key'], true ); if ($master_item_prototypeid !== null) { $item_prototype['master_itemid'] = $master_item_prototypeid; unset($item_prototype[$master_item_key]); } } else { unset($item_prototype[$master_item_key]); } if ($item_prototype['type'] == ITEM_TYPE_HTTPAGENT) { $headers = []; foreach ($item_prototype['headers'] as $header) { $headers[$header['name']] = $header['value']; } $item_prototype['headers'] = $headers; $query_fields = []; foreach ($item_prototype['query_fields'] as $query_field) { $query_fields[] = [$query_field['name'] => $query_field['value']]; } $item_prototype['query_fields'] = $query_fields; } $item_prototypeid = null; if (array_key_exists('uuid', $item_prototype)) { $item_prototypeid = $this->referencer->findItemidByUuid($item_prototype['uuid']); } if ($item_prototypeid === null) { $item_prototypeid = $this->referencer->findItemidByKey($hostid, $item_prototype['key_']); } if ($item_prototypeid === null) { $item_prototype['ruleid'] = $itemid; } foreach ($item_prototype['preprocessing'] as &$preprocessing_step) { $preprocessing_step['params'] = implode("\n", $preprocessing_step['parameters']); unset($preprocessing_step['parameters']); } unset($preprocessing_step); if ($item_prototypeid !== null) { if (!array_key_exists($level, $item_prototypes_to_update)) { $item_prototypes_to_update[$level] = []; } $item_prototype['itemid'] = $item_prototypeid; $item_prototypes_to_update[$level][] = $item_prototype; } else { if (!array_key_exists($level, $item_prototypes_to_create)) { $item_prototypes_to_create[$level] = []; } $item_prototypes_to_create[$level][] = $item_prototype; } } foreach ($discovery_rule['host_prototypes'] as $host_prototype) { // Resolve group prototypes. $group_links = []; foreach ($host_prototype['group_links'] as $group_link) { $groupid = $this->referencer->findHostGroupidByName($group_link['group']['name']); if ($groupid === null) { throw new Exception(_s( 'Cannot find host group "%1$s" for host prototype "%2$s" of discovery rule "%3$s" on "%4$s".', $group_link['group']['name'], $host_prototype['name'], $discovery_rule['name'], $host )); } $group_links[] = ['groupid' => $groupid]; } $host_prototype['groupLinks'] = $group_links; $host_prototype['groupPrototypes'] = $host_prototype['group_prototypes']; unset($host_prototype['group_links'], $host_prototype['group_prototypes']); // Resolve templates. $templates = []; foreach ($host_prototype['templates'] as $template) { $templateid = $this->referencer->findTemplateidByHost($template['name']); if ($templateid === null) { throw new Exception(_s( 'Cannot find template "%1$s" for host prototype "%2$s" of discovery rule "%3$s" on "%4$s".', $template['name'], $host_prototype['name'], $discovery_rule['name'], $host )); } $templates[] = ['templateid' => $templateid]; } $host_prototype['templates'] = $templates; $host_prototypeid = null; if (array_key_exists('uuid', $host_prototype)) { $host_prototypeid = $this->referencer->findHostPrototypeidByUuid($host_prototype['uuid']); } if ($host_prototypeid === null) { $host_prototypeid = $this->referencer->findHostPrototypeidByHost($hostid, $itemid, $host_prototype['host'] ); } if ($host_prototypeid !== null) { if (array_key_exists('macros', $host_prototype)) { foreach ($host_prototype['macros'] as &$macro) { $hostmacroid = $this->referencer->findHostPrototypeMacroid($host_prototypeid, $macro['macro'] ); if ($hostmacroid !== null) { $macro['hostmacroid'] = $hostmacroid; } } unset($macro); } $host_prototype['hostid'] = $host_prototypeid; $host_prototypes_to_update[] = $host_prototype; } else { $host_prototype['ruleid'] = $itemid; $host_prototypes_to_create[] = $host_prototype; } } } } ksort($levels); foreach (array_keys($levels) as $level) { if (array_key_exists($level, $item_prototypes_to_update) && $item_prototypes_to_update[$level]) { $this->updateItemsWithDependency([$item_prototypes_to_update[$level]], $master_item_key, API::ItemPrototype() ); } if (array_key_exists($level, $item_prototypes_to_create) && $item_prototypes_to_create[$level]) { $this->createItemsWithDependency([$item_prototypes_to_create[$level]], $master_item_key, API::ItemPrototype() ); } } if ($host_prototypes_to_update) { API::HostPrototype()->update($host_prototypes_to_update); } if ($host_prototypes_to_create) { API::HostPrototype()->create($host_prototypes_to_create); } // Refresh prototypes because templated ones can be inherited to host and used in triggers prototypes or graph // prototypes. $this->referencer->refreshItems(); // First we need to create item prototypes and only then trigger and graph prototypes. $triggers_to_create = []; $triggers_to_update = []; $graphs_to_create = []; $graphs_to_update = []; // The list of triggers to process dependencies. $triggers = []; foreach ($discovery_rules_by_hosts as $host => $discovery_rules) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); foreach ($discovery_rules as $discovery_rule) { // If rule was not processed we should not create/update any of its prototypes. if (array_key_exists($hostid, $processed_discovery_rules) && !array_key_exists($discovery_rule['key_'], $processed_discovery_rules[$hostid])) { continue; } foreach ($discovery_rule['trigger_prototypes'] as $trigger) { $triggerid = null; if (array_key_exists('uuid', $trigger)) { $triggerid = $this->referencer->findTriggeridByUuid($trigger['uuid']); } if ($triggerid === null) { $triggerid = $this->referencer->findTriggeridByName($trigger['description'], $trigger['expression'], $trigger['recovery_expression'] ); } $triggers[] = $trigger; unset($trigger['dependencies']); if ($triggerid !== null) { $trigger['triggerid'] = $triggerid; $triggers_to_update[] = $trigger; } else { $triggers_to_create[] = $trigger; } } foreach ($discovery_rule['graph_prototypes'] as $graph) { if ($graph['ymin_item_1']) { $hostid = $this->referencer->findTemplateidOrHostidByHost($graph['ymin_item_1']['host']); $itemid = ($hostid !== null) ? $this->referencer->findItemidByKey($hostid, $graph['ymin_item_1']['key'], true) : null; if ($itemid === null) { throw new Exception(_s( 'Cannot find item "%1$s" on "%2$s" used as the Y axis MIN value for graph prototype "%3$s" of discovery rule "%4$s" on "%5$s".', $graph['ymin_item_1']['key'], $graph['ymin_item_1']['host'], $graph['name'], $discovery_rule['name'], $host )); } $graph['ymin_itemid'] = $itemid; } if ($graph['ymax_item_1']) { $hostid = $this->referencer->findTemplateidOrHostidByHost($graph['ymax_item_1']['host']); $itemid = ($hostid !== null) ? $this->referencer->findItemidByKey($hostid, $graph['ymax_item_1']['key'], true) : null; if ($itemid === null) { throw new Exception(_s( 'Cannot find item "%1$s" on "%2$s" used as the Y axis MAX value for graph prototype "%3$s" of discovery rule "%4$s" on "%5$s".', $graph['ymax_item_1']['key'], $graph['ymax_item_1']['host'], $graph['name'], $discovery_rule['name'], $host )); } $graph['ymax_itemid'] = $itemid; } foreach ($graph['gitems'] as &$item) { $hostid = $this->referencer->findTemplateidOrHostidByHost($item['item']['host']); $item['itemid'] = ($hostid !== null) ? $this->referencer->findItemidByKey($hostid, $item['item']['key'], true) : null; if ($item['itemid'] === null) { throw new Exception(_s( 'Cannot find item "%1$s" on "%2$s" used in graph prototype "%3$s" of discovery rule "%4$s" on "%5$s".', $item['item']['key'], $item['item']['host'], $graph['name'], $discovery_rule['name'], $host )); } } unset($item); $graphid = null; if (array_key_exists('uuid', $graph)) { $graphid = $this->referencer->findGraphidByUuid($graph['uuid']); } if ($graphid === null) { $graphid = $this->referencer->findGraphidByName($hostid, $graph['name']); } if ($graphid !== null) { $graph['graphid'] = $graphid; $graphs_to_update[] = $graph; } else { $graphs_to_create[] = $graph; } } } } if ($triggers_to_update) { $updated_triggers = API::TriggerPrototype()->update($triggers_to_update); foreach ($updated_triggers['triggerids'] as $index => $triggerid) { $trigger = $triggers_to_update[$index]; $this->referencer->setDbTrigger($triggerid, $trigger); } } if ($triggers_to_create) { $created_triggers = API::TriggerPrototype()->create($triggers_to_create); foreach ($created_triggers['triggerids'] as $index => $triggerid) { $trigger = $triggers_to_create[$index]; $this->referencer->setDbTrigger($triggerid, $trigger); } } if ($graphs_to_update) { API::GraphPrototype()->update($graphs_to_update); $this->referencer->refreshGraphs(); } if ($graphs_to_create) { API::GraphPrototype()->create($graphs_to_create); $this->referencer->refreshGraphs(); } $this->processTriggerPrototypeDependencies($triggers); } /** * Update trigger prototype dependencies. * * @param array $triggers * * @throws Exception */ protected function processTriggerPrototypeDependencies(array $triggers): void { $trigger_dependencies = []; foreach ($triggers as $trigger) { if (!array_key_exists('dependencies', $trigger)) { continue; } $dependencies = []; $triggerid = $this->referencer->findTriggeridByName($trigger['description'], $trigger['expression'], $trigger['recovery_expression'] ); foreach ($trigger['dependencies'] as $dependency) { $dependent_triggerid = $this->referencer->findTriggeridByName($dependency['name'], $dependency['expression'], $dependency['recovery_expression'], true ); if ($dependent_triggerid === null) { throw new Exception(_s('Trigger prototype "%1$s" depends on trigger "%2$s", which does not exist.', $trigger['description'], $dependency['name'] )); } $dependencies[] = ['triggerid' => $dependent_triggerid]; } $trigger_dependencies[] = [ 'triggerid' => $triggerid, 'dependencies' => $dependencies ]; } if ($trigger_dependencies) { API::TriggerPrototype()->update($trigger_dependencies); } } /** * Import web scenarios. * * @throws APIException */ protected function processHttpTests(): void { if (!$this->options['httptests']['createMissing'] && !$this->options['httptests']['updateExisting']) { return; } $httptests_to_create = []; $httptests_to_update = []; foreach ($this->getFormattedHttpTests() as $host => $httptests) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); if ($hostid === null || (!$this->importedObjectContainer->isHostProcessed($hostid) && !$this->importedObjectContainer->isTemplateProcessed($hostid))) { continue; } foreach ($httptests as $httptest) { $httptestid = null; if (array_key_exists('uuid', $httptest)) { $httptestid = $this->referencer->findHttpTestidByUuid($httptest['uuid']); } if ($httptestid === null) { $httptestid = $this->referencer->findHttpTestidByName($hostid, $httptest['name']); } if ($httptestid !== null) { foreach ($httptest['steps'] as &$httpstep) { $httpstepid = $this->referencer->findHttpStepidByName($hostid, $httptestid, $httpstep['name']); if ($httpstepid !== null) { $httpstep['httpstepid'] = $httpstepid; } } unset($httpstep); $httptest['httptestid'] = $httptestid; $httptests_to_update[] = $httptest; } else { $httptest['hostid'] = $hostid; $httptests_to_create[] = $httptest; } } } if ($this->options['httptests']['updateExisting'] && $httptests_to_update) { API::HttpTest()->update($httptests_to_update); } if ($this->options['httptests']['createMissing'] && $httptests_to_create) { API::HttpTest()->create($httptests_to_create); } $this->referencer->refreshHttpTests(); } /** * Import graphs. * * @throws Exception */ protected function processGraphs(): void { if (!$this->options['graphs']['createMissing'] && !$this->options['graphs']['updateExisting']) { return; } $graphs_to_create = []; $graphs_to_update = []; foreach ($this->getFormattedGraphs() as $graph) { if ($graph['ymin_item_1']) { $hostid = $this->referencer->findTemplateidOrHostidByHost($graph['ymin_item_1']['host']); $itemid = ($hostid !== null) ? $this->referencer->findItemidByKey($hostid, $graph['ymin_item_1']['key'], true) : null; if ($itemid === null) { throw new Exception(_s( 'Cannot find item "%1$s" on "%2$s" used as the Y axis MIN value for graph "%3$s".', $graph['ymin_item_1']['key'], $graph['ymin_item_1']['host'], $graph['name'] )); } $graph['ymin_itemid'] = $itemid; } if ($graph['ymax_item_1']) { $hostid = $this->referencer->findTemplateidOrHostidByHost($graph['ymax_item_1']['host']); $itemid = ($hostid !== null) ? $this->referencer->findItemidByKey($hostid, $graph['ymax_item_1']['key'], true) : null; if ($itemid === null) { throw new Exception(_s( 'Cannot find item "%1$s" on "%2$s" used as the Y axis MAX value for graph "%3$s".', $graph['ymax_item_1']['key'], $graph['ymax_item_1']['host'], $graph['name'] )); } $graph['ymax_itemid'] = $itemid; } $hostid = null; foreach ($graph['gitems'] as &$item) { $hostid = $this->referencer->findTemplateidOrHostidByHost($item['item']['host']); $item['itemid'] = ($hostid !== null) ? $this->referencer->findItemidByKey($hostid, $item['item']['key'], true) : null; if ($item['itemid'] === null) { throw new Exception(_s( 'Cannot find item "%1$s" on "%2$s" used in graph "%3$s".', $item['item']['key'], $item['item']['host'], $graph['name'] )); } } unset($item); if ($this->isTemplateGraph($graph)) { $graphid = $this->referencer->findGraphidByUuid($graph['uuid']); if ($graphid === null) { $graphid = $this->referencer->findGraphidByName($hostid, $graph['name']); } } else { unset($graph['uuid']); $graphid = $this->referencer->findGraphidByName($hostid, $graph['name']); } if ($graphid !== null) { $graph['graphid'] = $graphid; $graphs_to_update[] = $graph; } else { $graphs_to_create[] = $graph; } } if ($this->options['graphs']['updateExisting'] && $graphs_to_update) { API::Graph()->update($graphs_to_update); } if ($this->options['graphs']['createMissing'] && $graphs_to_create) { API::Graph()->create($graphs_to_create); } $this->referencer->refreshGraphs(); } private function isTemplateGraph(array $graph): bool { if ($graph['ymin_item_1'] && $this->referencer->findTemplateidByHost($graph['ymin_item_1']['host'])) { return true; } if ($graph['ymax_item_1'] && $this->referencer->findTemplateidByHost($graph['ymax_item_1']['host'])) { return true; } if (array_key_exists('gitems', $graph) && $graph['gitems']) { foreach ($graph['gitems'] as $gitem) { if ($this->referencer->findTemplateidByHost($gitem['item']['host'])) { return true; } } } return false; } /** * Import triggers. * * @throws Exception */ protected function processTriggers(): void { if (!$this->options['triggers']['createMissing'] && !$this->options['triggers']['updateExisting']) { return; } $triggers_to_create = []; $triggers_to_update = []; $triggers_to_process_dependencies = []; foreach ($this->getFormattedTriggers() as $trigger) { $triggerid = null; $is_template_trigger = $this->isTemplateTrigger($trigger); if ($is_template_trigger && array_key_exists('uuid', $trigger)) { $triggerid = $this->referencer->findTriggeridByUuid($trigger['uuid']); if ($triggerid === null) { $triggerid = $this->referencer->findTriggeridByName($trigger['description'], $trigger['expression'], $trigger['recovery_expression'] ); } } elseif (!$is_template_trigger) { unset($trigger['uuid']); $triggerid = $this->referencer->findTriggeridByName($trigger['description'], $trigger['expression'], $trigger['recovery_expression'] ); } if ($triggerid !== null) { if ($this->options['triggers']['updateExisting']) { $triggers_to_process_dependencies[] = $trigger; $trigger['triggerid'] = $triggerid; unset($trigger['dependencies']); $triggers_to_update[] = $trigger; } } else { if ($this->options['triggers']['createMissing']) { $triggers_to_process_dependencies[] = $trigger; unset($trigger['dependencies']); $triggers_to_create[] = $trigger; } } } if ($triggers_to_update) { API::Trigger()->update($triggers_to_update); } if ($triggers_to_create) { API::Trigger()->create($triggers_to_create); } // Refresh triggers because template triggers can be inherited to host and used in maps. $this->referencer->refreshTriggers(); $this->processTriggerDependencies($triggers_to_process_dependencies); } private function isTemplateTrigger(array $trigger): bool { $expression_parser = new CExpressionParser(['usermacros' => true]); if ($expression_parser->parse($trigger['expression']) != CParser::PARSE_SUCCESS) { return false; } foreach ($expression_parser->getResult()->getHosts() as $host) { $host = $this->referencer->findTemplateidByHost($host); if ($host !== null) { return true; } } if ($trigger['recovery_expression'] === '' || $expression_parser->parse($trigger['recovery_expression']) != CParser::PARSE_SUCCESS) { return false; } foreach ($expression_parser->getResult()->getHosts() as $host) { $host = $this->referencer->findTemplateidByHost($host); if ($host !== null) { return true; } } return false; } /** * Update trigger dependencies * * @param array $triggers * * @throws Exception */ protected function processTriggerDependencies(array $triggers): void { $trigger_dependencies = []; foreach ($triggers as $trigger) { if (!array_key_exists('dependencies', $trigger)) { continue; } $triggerid = $this->referencer->findTriggeridByName($trigger['description'], $trigger['expression'], $trigger['recovery_expression'] ); $dependencies = []; foreach ($trigger['dependencies'] as $dependency) { $dependent_triggerid = $this->referencer->findTriggeridByName($dependency['name'], $dependency['expression'], $dependency['recovery_expression'], true ); if ($dependent_triggerid === null) { throw new Exception(_s('Trigger "%1$s" depends on trigger "%2$s", which does not exist.', $trigger['description'], $dependency['name'] )); } $dependencies[] = ['triggerid' => $dependent_triggerid]; } $trigger_dependencies[] = [ 'triggerid' => $triggerid, 'dependencies' => $dependencies ]; } if ($trigger_dependencies) { API::Trigger()->update($trigger_dependencies); } } /** * Import images. * * @throws Exception */ protected function processImages(): void { if (!$this->options['images']['updateExisting'] && !$this->options['images']['createMissing']) { return; } $images_to_import = $this->getFormattedImages(); if (!$images_to_import) { return; } $images_to_update = []; $images_to_create = []; foreach ($images_to_import as $image) { $imageid = $this->referencer->findImageidByName($image['name']); if ($imageid !== null) { $image['imageid'] = $imageid; unset($image['imagetype']); $images_to_update[] = $image; } else { $images_to_create[] = $image; } } if ($this->options['images']['updateExisting'] && $images_to_update) { API::Image()->update($images_to_update); } if ($this->options['images']['createMissing'] && $images_to_create) { $created_images = API::Image()->create($images_to_create); foreach ($images_to_create as $index => $image) { $this->referencer->setDbImage($created_images['imageids'][$index], $image); } } } /** * Import maps. * * @throws Exception */ protected function processMaps(): void { if ($this->options['maps']['updateExisting'] || $this->options['maps']['createMissing']) { $maps = $this->getFormattedMaps(); if ($maps) { $map_importer = new CMapImporter($this->options, $this->referencer, $this->importedObjectContainer); $map_importer->import($maps); } } } /** * Import template dashboards. */ protected function processTemplateDashboards(): void { if ($this->options['templateDashboards']['updateExisting'] || $this->options['templateDashboards']['createMissing']) { $dashboards = $this->getFormattedTemplateDashboards(); if ($dashboards) { $dashboard_importer = new CTemplateDashboardImporter($this->options, $this->referencer, $this->importedObjectContainer ); $dashboard_importer->import($dashboards); } } } /** * Import media types. */ protected function processMediaTypes(): void { if (!$this->options['mediaTypes']['updateExisting'] && !$this->options['mediaTypes']['createMissing']) { return; } $media_types_to_import = $this->getFormattedMediaTypes(); if (!$media_types_to_import) { return; } $media_types_to_import = zbx_toHash($media_types_to_import, 'name'); $db_media_types = API::MediaType()->get([ 'output' => ['mediatypeid', 'name'], 'filter' => ['name' => array_keys($media_types_to_import)] ]); $db_media_types = zbx_toHash($db_media_types, 'name'); $media_types_to_update = []; $media_types_to_create = []; foreach ($media_types_to_import as $name => $media_type) { if (array_key_exists($name, $db_media_types)) { $media_type['mediatypeid'] = $db_media_types[$name]['mediatypeid']; $media_types_to_update[] = $media_type; } else { $media_types_to_create[] = $media_type; } } if ($this->options['mediaTypes']['updateExisting'] && $media_types_to_update) { API::MediaType()->update($media_types_to_update); } if ($this->options['mediaTypes']['createMissing'] && $media_types_to_create) { API::MediaType()->create($media_types_to_create); } } /** * Deletes items from DB that are missing in import file. */ protected function deleteMissingItems(): void { if (!$this->options['items']['deleteMissing']) { return; } $processed_hostids = array_merge( $this->importedObjectContainer->getHostids(), $this->importedObjectContainer->getTemplateids() ); if (!$processed_hostids) { return; } $itemids = []; foreach ($this->getFormattedItems() as $host => $items) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); if ($hostid === null) { continue; } foreach ($items as $item) { $itemid = null; if (array_key_exists('uuid', $item)) { $itemid = $this->referencer->findItemidByUuid($item['uuid']); } if ($itemid === null) { $itemid = $this->referencer->findItemidByKey($hostid, $item['key_']); } if ($itemid !== null) { $itemids[$itemid] = []; } } } $db_itemids = API::Item()->get([ 'output' => ['itemid'], 'hostids' => $processed_hostids, 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL], 'inherited' => false, 'preservekeys' => true, 'nopermissions' => true ]); $items_to_delete = array_diff_key($db_itemids, $itemids); if ($items_to_delete) { API::Item()->delete(array_keys($items_to_delete)); } $this->referencer->refreshItems(); } /** * Deletes triggers from DB that are missing in import file. */ protected function deleteMissingTriggers(): void { if (!$this->options['triggers']['deleteMissing']) { return; } $processed_hostids = array_merge( $this->importedObjectContainer->getHostids(), $this->importedObjectContainer->getTemplateids() ); if (!$processed_hostids) { return; } $triggerids = []; foreach ($this->getFormattedTriggers() as $trigger) { $triggerid = null; if (array_key_exists('uuid', $trigger)) { $triggerid = $this->referencer->findTriggeridByUuid($trigger['uuid']); } // In import file host trigger can have UUID assigned after conversion, such should be searched by name. if ($triggerid === null) { $triggerid = $this->referencer->findTriggeridByName($trigger['description'], $trigger['expression'], $trigger['recovery_expression'] ); // Template triggers should only be searched by UUID. if ($triggerid !== null && array_key_exists('uuid', $trigger)) { $db_trigger = $this->referencer->findTriggerById($triggerid); if ($db_trigger['uuid'] !== '' && $db_trigger['uuid'] !== $trigger['uuid']) { $triggerid = null; } } } if ($triggerid !== null) { $triggerids[$triggerid] = []; } } $db_triggerids = API::Trigger()->get([ 'output' => [], 'selectHosts' => ['hostid'], 'hostids' => $processed_hostids, 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL], 'inherited' => false, 'preservekeys' => true, 'nopermissions' => true ]); // Check that potentially deletable trigger belongs to same hosts that are in the import file. // If some triggers belong to more hosts than import file contains, don't delete them. $triggerids_to_delete = []; $processed_hostids = array_flip($processed_hostids); foreach (array_diff_key($db_triggerids, $triggerids) as $triggerid => $trigger) { $trigger_hostids = array_flip(array_column($trigger['hosts'], 'hostid')); if (!array_diff_key($trigger_hostids, $processed_hostids)) { $triggerids_to_delete[] = $triggerid; } } if ($triggerids_to_delete) { API::Trigger()->delete($triggerids_to_delete); } // refresh triggers because template triggers can be inherited to host and used in maps $this->referencer->refreshTriggers(); } /** * Deletes graphs from DB that are missing in import file. */ protected function deleteMissingGraphs(): void { if (!$this->options['graphs']['deleteMissing']) { return; } $processed_hostids = array_merge( $this->importedObjectContainer->getHostids(), $this->importedObjectContainer->getTemplateids() ); if (!$processed_hostids) { return; } $graphids = []; foreach ($this->getFormattedGraphs() as $graph) { $graphid = null; if (array_key_exists('uuid', $graph)) { $graphid = $this->referencer->findGraphidByUuid($graph['uuid']); } if ($graphid !== null) { $graphids[$graphid] = []; } elseif (array_key_exists('gitems', $graph)) { // In import file host graph can have UUID assigned after conversion, such should be searched by name. foreach ($graph['gitems'] as $gitem) { $gitem_hostid = $this->referencer->findTemplateidOrHostidByHost($gitem['item']['host']); if ($gitem_hostid !== null) { $graphid = $this->referencer->findGraphidByName($gitem_hostid, $graph['name']); } if ($graphid !== null) { $graphids[$graphid] = []; } } } } $db_graphids = API::Graph()->get([ 'output' => ['graphid'], 'hostids' => $processed_hostids, 'selectHosts' => ['hostid'], 'preservekeys' => true, 'nopermissions' => true, 'inherited' => false, 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL] ]); // check that potentially deletable graph belongs to same hosts that are in XML // if some graphs belong to more hosts than current XML contains, don't delete them $graphids_to_delete = []; $processed_hostids = array_flip($processed_hostids); foreach (array_diff_key($db_graphids, $graphids) as $graphid => $graph) { $graph_hostids = array_flip(array_column($graph['hosts'], 'hostid')); if (!array_diff_key($graph_hostids, $processed_hostids)) { $graphids_to_delete[] = $graphid; } } if ($graphids_to_delete) { API::Graph()->delete($graphids_to_delete); } $this->referencer->refreshGraphs(); } /** * Deletes prototypes from DB that are missing in import file. * * @param array $discovery_rules_by_hosts * * @throws APIException */ protected function deleteMissingPrototypes(array $discovery_rules_by_hosts): void { $discovery_ruleids = []; $host_prototypeids = []; $trigger_prototypeids = []; $graph_prototypeids = []; $item_prototypeids = []; foreach ($discovery_rules_by_hosts as $host => $discovery_rules) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); foreach ($discovery_rules as $discovery_rule) { $discoveryid = null; if (array_key_exists('uuid', $discovery_rule)) { $discoveryid = $this->referencer->findItemidByUuid($discovery_rule['uuid']); } if ($discoveryid === null) { $discoveryid = $this->referencer->findItemidByKey($hostid, $discovery_rule['key_']); } if ($discoveryid === null) { continue; } $discovery_ruleids[$discoveryid] = []; foreach ($discovery_rule['host_prototypes'] as $host_prototype) { $host_prototypeid = null; if (array_key_exists('uuid', $host_prototype)) { $host_prototypeid = $this->referencer->findHostPrototypeidByUuid($host_prototype['uuid']); } if ($host_prototypeid === null) { $host_prototypeid = $this->referencer->findHostPrototypeidByHost($hostid, $discoveryid, $host_prototype['host'] ); } if ($host_prototypeid !== null) { $host_prototypeids[$host_prototypeid] = []; } } foreach ($discovery_rule['trigger_prototypes'] as $trigger_prototype) { $trigger_prototypeid = null; if (array_key_exists('uuid', $trigger_prototype)) { $trigger_prototypeid = $this->referencer->findTriggeridByUuid($trigger_prototype['uuid']); } if ($trigger_prototypeid === null) { $trigger_prototypeid = $this->referencer->findTriggeridByName($trigger_prototype['description'], $trigger_prototype['expression'], $trigger_prototype['recovery_expression'] ); } if ($trigger_prototypeid !== null) { $trigger_prototypeids[$trigger_prototypeid] = []; } } foreach ($discovery_rule['graph_prototypes'] as $graph_prototype) { $graph_prototypeid = null; if (array_key_exists('uuid', $graph_prototype)) { $graph_prototypeid = $this->referencer->findGraphidByUuid($graph_prototype['uuid']); } if ($graph_prototypeid === null) { $graph_prototypeid = $this->referencer->findGraphidByName($hostid, $graph_prototype['name']); } if ($graph_prototypeid !== null) { $graph_prototypeids[$graph_prototypeid] = []; } } foreach ($discovery_rule['item_prototypes'] as $item_prototype) { $item_prototypeid = null; if (array_key_exists('uuid', $item_prototype)) { $item_prototypeid = $this->referencer->findItemidByUuid($item_prototype['uuid']); } if ($item_prototypeid === null) { $item_prototypeid = $this->referencer->findItemidByKey($hostid, $item_prototype['key_']); } if ($item_prototypeid !== null) { $item_prototypeids[$item_prototypeid] = []; } } } } $db_host_prototypes = API::HostPrototype()->get([ 'output' => [], 'discoveryids' => array_keys($discovery_ruleids), 'preservekeys' => true, 'nopermissions' => true, 'inherited' => false ]); $host_prototypes_to_delete = array_diff_key($db_host_prototypes, $host_prototypeids); if ($host_prototypes_to_delete) { API::HostPrototype()->delete(array_keys($host_prototypes_to_delete)); } $db_trigger_prototypes = API::TriggerPrototype()->get([ 'output' => [], 'discoveryids' => array_keys($discovery_ruleids), 'preservekeys' => true, 'nopermissions' => true, 'inherited' => false ]); $trigger_prototypes_to_delete = array_diff_key($db_trigger_prototypes, $trigger_prototypeids); // Unlike triggers that belong to multiple hosts, trigger prototypes do not, so we just delete them. if ($trigger_prototypes_to_delete) { API::TriggerPrototype()->delete(array_keys($trigger_prototypes_to_delete)); $this->referencer->refreshTriggers(); } $db_graph_prototypes = API::GraphPrototype()->get([ 'output' => [], 'discoveryids' => array_keys($discovery_ruleids), 'preservekeys' => true, 'nopermissions' => true, 'inherited' => false ]); $graph_prototypes_to_delete = array_diff_key($db_graph_prototypes, $graph_prototypeids); // Unlike graphs that belong to multiple hosts, graph prototypes do not, so we just delete them. if ($graph_prototypes_to_delete) { API::GraphPrototype()->delete(array_keys($graph_prototypes_to_delete)); $this->referencer->refreshGraphs(); } $db_item_prototypes = API::ItemPrototype()->get([ 'output' => [], 'discoveryids' => array_keys($discovery_ruleids), 'preservekeys' => true, 'nopermissions' => true, 'inherited' => false ]); $item_prototypes_to_delete = array_diff_key($db_item_prototypes, $item_prototypeids); if ($item_prototypes_to_delete) { API::ItemPrototype()->delete(array_keys($item_prototypes_to_delete)); $this->referencer->refreshItems(); } } /** * Deletes web scenarios from DB that are missing in import file. */ protected function deleteMissingHttpTests(): void { if (!$this->options['httptests']['deleteMissing']) { return; } $processed_hostids = array_merge( $this->importedObjectContainer->getHostids(), $this->importedObjectContainer->getTemplateids() ); if (!$processed_hostids) { return; } $httptestids = []; foreach ($this->getFormattedHttpTests() as $host => $httptests) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); if ($hostid === null) { continue; } foreach ($httptests as $httptest) { $httptestid = null; if (array_key_exists('uuid', $httptest)) { $httptestid = $this->referencer->findHttpTestidByUuid($httptest['uuid']); } if ($httptestid === null) { $httptestid = $this->referencer->findHttpTestidByName($hostid, $httptest['name']); } if ($httptestid !== null) { $httptestids[$httptestid] = []; } } } $db_httptestids = API::HttpTest()->get([ 'output' => [], 'hostids' => $processed_hostids, 'inherited' => false, 'preservekeys' => true, 'nopermissions' => true ]); $httptestids_to_delete = array_diff_key($db_httptestids, $httptestids); if ($httptestids_to_delete) { API::HttpTest()->delete(array_keys($httptestids_to_delete)); } $this->referencer->refreshHttpTests(); } /** * Deletes template dashboards from DB that are missing in import file. * * @throws APIException */ protected function deleteMissingTemplateDashboards(): void { if (!$this->options['templateDashboards']['deleteMissing']) { return; } $dashboard_importer = new CTemplateDashboardImporter($this->options, $this->referencer, $this->importedObjectContainer ); $dashboard_importer->delete($this->getFormattedTemplateDashboards()); } /** * Deletes discovery rules from DB that are missing in import file. */ protected function deleteMissingDiscoveryRules(): void { if (!$this->options['discoveryRules']['deleteMissing']) { return; } $processed_hostids = array_merge( $this->importedObjectContainer->getHostids(), $this->importedObjectContainer->getTemplateids() ); if (!$processed_hostids) { return; } $discovery_ruleids = []; foreach ($this->getFormattedDiscoveryRules() as $host => $discovery_rules) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); if ($hostid === null) { continue; } foreach ($discovery_rules as $discovery_rule) { $discovery_ruleid = null; if (array_key_exists('uuid', $discovery_rule)) { $discovery_ruleid = $this->referencer->findItemidByUuid($discovery_rule['uuid']); } if ($discovery_ruleid === null) { $discovery_ruleid = $this->referencer->findItemidByKey($hostid, $discovery_rule['key_']); } if ($discovery_ruleid !== null) { $discovery_ruleids[$discovery_ruleid] = []; } } } $db_discovery_ruleids = API::DiscoveryRule()->get([ 'output' => ['itemid'], 'hostids' => $processed_hostids, 'inherited' => false, 'preservekeys' => true, 'nopermissions' => true ]); $discovery_ruleids_to_delete = array_diff_key($db_discovery_ruleids, $discovery_ruleids); if ($discovery_ruleids_to_delete) { API::DiscoveryRule()->delete(array_keys($discovery_ruleids_to_delete)); } $this->referencer->refreshItems(); } /** * Get formatted template groups. * * @return array */ protected function getFormattedTemplateGroups(): array { if (!array_key_exists('template_groups', $this->formattedData)) { $this->formattedData['template_groups'] = $this->adapter->getTemplateGroups(); } return $this->formattedData['template_groups']; } /** * Get formatted host groups. * * @return array */ protected function getFormattedHostGroups(): array { if (!array_key_exists('host_groups', $this->formattedData)) { $this->formattedData['host_groups'] = $this->adapter->getHostGroups(); } return $this->formattedData['host_groups']; } /** * Get formatted templates. * * @return array */ public function getFormattedTemplates(): array { if (!array_key_exists('templates', $this->formattedData)) { $this->formattedData['templates'] = $this->adapter->getTemplates(); } return $this->formattedData['templates']; } /** * Get formatted hosts. * * @return array */ public function getFormattedHosts(): array { if (!array_key_exists('hosts', $this->formattedData)) { $this->formattedData['hosts'] = $this->adapter->getHosts(); } return $this->formattedData['hosts']; } /** * Get formatted items. * * @return array */ protected function getFormattedItems(): array { if (!array_key_exists('items', $this->formattedData)) { $this->formattedData['items'] = $this->adapter->getItems(); } return $this->formattedData['items']; } /** * Get formatted discovery rules. * * @return array */ protected function getFormattedDiscoveryRules(): array { if (!array_key_exists('discoveryRules', $this->formattedData)) { $this->formattedData['discoveryRules'] = $this->adapter->getDiscoveryRules(); } return $this->formattedData['discoveryRules']; } /** * Get formatted web scenarios. * * @return array */ protected function getFormattedHttpTests(): array { if (!array_key_exists('httptests', $this->formattedData)) { $this->formattedData['httptests'] = $this->adapter->getHttpTests(); } return $this->formattedData['httptests']; } /** * Get formatted web scenario steps. * * @return array */ protected function getFormattedHttpSteps(): array { if (!array_key_exists('httpsteps', $this->formattedData)) { $this->formattedData['httpsteps'] = $this->adapter->getHttpSteps(); } return $this->formattedData['httpsteps']; } /** * Get formatted triggers. * * @return array */ protected function getFormattedTriggers(): array { if (!array_key_exists('triggers', $this->formattedData)) { $this->formattedData['triggers'] = $this->adapter->getTriggers(); } return $this->formattedData['triggers']; } /** * Get formatted graphs. * * @return array */ protected function getFormattedGraphs(): array { if (!array_key_exists('graphs', $this->formattedData)) { $this->formattedData['graphs'] = $this->adapter->getGraphs(); } return $this->formattedData['graphs']; } /** * Get formatted images. * * @return array */ protected function getFormattedImages(): array { if (!array_key_exists('images', $this->formattedData)) { $this->formattedData['images'] = $this->adapter->getImages(); } return $this->formattedData['images']; } /** * Get formatted maps. * * @return array */ protected function getFormattedMaps(): array { if (!array_key_exists('maps', $this->formattedData)) { $this->formattedData['maps'] = $this->adapter->getMaps(); } return $this->formattedData['maps']; } /** * Get formatted template dashboards. * * @return array */ protected function getFormattedTemplateDashboards(): array { if (!array_key_exists('templateDashboards', $this->formattedData)) { $this->formattedData['templateDashboards'] = $this->adapter->getTemplateDashboards(); } return $this->formattedData['templateDashboards']; } /** * Get formatted media types. * * @return array */ protected function getFormattedMediaTypes(): array { if (!array_key_exists('mediaTypes', $this->formattedData)) { $this->formattedData['mediaTypes'] = $this->adapter->getMediaTypes(); } return $this->formattedData['mediaTypes']; } /** * Get items keys order tree, to ensure that master item will be inserted or updated before any of it dependent * item. Returns associative array where key is item index and value is item dependency level. * * @param string $master_item_key String containing master key name used to identify item master. * * @return array * * @throws Exception */ protected function getItemsOrder(string $master_item_key): array { $entities = $this->getFormattedItems(); return $this->getEntitiesOrder($entities, $master_item_key); } /** * Get discovery rules items prototypes keys order tree, to ensure that master item will be inserted or updated * before any of it dependent item. Returns associative array where key is item prototype index and value is item * prototype dependency level. * * @param string $master_item_key String containing master key name used to identify item master. * * @return array * * @throws Exception */ protected function getDiscoveryRulesItemsOrder(string $master_item_key): array { $discovery_rules_by_hosts = $this->getFormattedDiscoveryRules(); $entities_order = []; foreach ($discovery_rules_by_hosts as $host => $discovery_rules) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host); if ($hostid === null) { continue; } foreach ($discovery_rules as $discovery_rule) { if ($discovery_rule['item_prototypes']) { $item_prototypes = [$host => $discovery_rule['item_prototypes']]; $item_prototypes = $this->getEntitiesOrder($item_prototypes, $master_item_key, true); $entities_order[$host][$discovery_rule['key_']] = $item_prototypes[$host]; } } } return $entities_order; } /** * Generic method to get entities order tree, to ensure that master entity will be inserted or updated before any * of it dependent entities. * Returns associative array where key is entity index in source array grouped by host key and value is entity * dependency level. * * @param array $items_by_hosts Associative array of host key and host items. * @param string $master_item_key String containing master key name to identify item master. * @param bool $get_prototypes Option to get also master item prototypes not found in supplied input. * * @return array * * @throws Exception if data is invalid. */ protected function getEntitiesOrder(array $items_by_hosts, string $master_item_key, bool $get_prototypes = false): array { $parent_item_hostids = []; $parent_item_keys = []; $resolved_masters_cache = []; $host_name_to_hostid = []; foreach ($items_by_hosts as $host_name => $items) { $hostid = $this->referencer->findTemplateidOrHostidByHost($host_name); if ($hostid === null) { unset($items_by_hosts[$host_name]); continue; } $host_name_to_hostid[$host_name] = $hostid; // Cache input array entities. foreach ($items as $item) { $resolved_masters_cache[$host_name][$item['key_']] = [ 'type' => $item['type'], $master_item_key => $item[$master_item_key] ]; if ($item['type'] == ITEM_TYPE_DEPENDENT && array_key_exists('key', $item[$master_item_key])) { $parent_item_hostids[$hostid] = true; $parent_item_keys[$item[$master_item_key]['key']] = true; } } } // There are entities to resolve from database, resolve and cache them recursively. if ($parent_item_keys) { /* * For existing items, 'referencer' should be initialized before 'setDbItem' method will be used. * Registering reference when property 'db_items' is empty, will not allow first call of * 'findValueMapidByName' method update references to existing items. */ $this->referencer->initItemsReferences(); $options = [ 'output' => ['itemid', 'uuid', 'key_', 'type', 'hostid', 'master_itemid'], 'hostids' => array_keys($parent_item_hostids), 'filter' => ['key_' => array_keys($parent_item_keys)], 'preservekeys' => true ]; $db_items = API::Item()->get($options + ['webitems' => true]); if ($get_prototypes) { $db_items += API::ItemPrototype()->get($options); } $resolve_entity_keys = []; $itemid_to_item_key_by_hosts = []; for ($level = 0; $level < ZBX_DEPENDENT_ITEM_MAX_LEVELS; $level++) { $missing_master_itemids = []; foreach ($db_items as $itemid => $item) { $host_name = array_search($item['hostid'], $host_name_to_hostid); $this->referencer->setDbItem($itemid, $item); $item['key'] = $item['key_']; unset($item['key_']); $itemid_to_item_key_by_hosts[$host_name][$itemid] = $item['key']; $cache_entity = [ 'type' => $item['type'] ]; if ($item['type'] == ITEM_TYPE_DEPENDENT) { $master_itemid = $item['master_itemid']; if (array_key_exists($master_itemid, $itemid_to_item_key_by_hosts[$host_name])) { $cache_entity[$master_item_key] = [ 'key' => $itemid_to_item_key_by_hosts[$host_name][$master_itemid] ]; } else { $missing_master_itemids[] = $item['master_itemid']; $resolve_entity_keys[] = [ 'host' => $host_name, 'key' => $item['key'], 'master_itemid' => $item['master_itemid'] ]; } } $resolved_masters_cache[$host_name][$item['key']] = $cache_entity; } if ($missing_master_itemids) { $options = [ 'output' => ['uuid', 'key_', 'type', 'hostid', 'master_itemid'], 'itemids' => $missing_master_itemids, 'preservekeys' => true ]; $db_items = API::Item()->get($options + ['webitems' => true]); if ($get_prototypes) { $db_items += API::ItemPrototype()->get($options); } } else { break; } } if ($missing_master_itemids) { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _('maximum number of dependency levels reached') )); } foreach ($resolve_entity_keys as $item) { $master_itemid = $item['master_itemid']; if (!array_key_exists($item['host'], $itemid_to_item_key_by_hosts) || !array_key_exists($master_itemid, $itemid_to_item_key_by_hosts[$item['host']])) { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _s('value "%1$s" not found', $master_itemid) )); } $master_key = $itemid_to_item_key_by_hosts[$item['host']][$master_itemid]; $resolved_masters_cache[$item['host']][$item['key']] += [ $master_item_key => ['key' => $master_key] ]; } unset($resolve_entity_keys, $itemid_to_item_key_by_hosts); } // Resolve every entity dependency level. $tree = []; foreach ($items_by_hosts as $host_name => $items) { $hostid = $host_name_to_hostid[$host_name]; $host_items_tree = []; foreach ($items as $index => $item) { $level = 0; $traversal_path = [$item['key_']]; while ($item && $item['type'] == ITEM_TYPE_DEPENDENT) { if (!array_key_exists('key', $item[$master_item_key])) { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _('cannot be empty') )); } $master_key = $item[$master_item_key]['key']; if (array_key_exists($host_name, $resolved_masters_cache) && array_key_exists($master_key, $resolved_masters_cache[$host_name])) { $item = $resolved_masters_cache[$host_name][$master_key]; if (($item['type'] == ITEM_TYPE_DEPENDENT && $item[$master_item_key] && $master_key === $item[$master_item_key]['key']) || in_array($master_key, $traversal_path)) { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _('circular item dependency is not allowed') )); } $traversal_path[] = $master_key; $level++; } else { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _s('value "%1$s" not found', $master_key) )); } if ($level > ZBX_DEPENDENT_ITEM_MAX_LEVELS) { throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'master_itemid', _('maximum number of dependency levels reached') )); } } $host_items_tree[$index] = $level; } $tree[$host_name] = $host_items_tree; } // Order item indexes in descending order by nesting level. foreach ($tree as &$item_indexes) { asort($item_indexes); } unset($item_indexes); return $tree; } }