You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3154 lines
93 KiB

<?php
/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
/**
* Class for importing configuration data.
*/
class CConfigurationImport {
/**
* @var CImportDataAdapter
*/
protected $adapter;
/**
* @var CImportReferencer
*/
protected $referencer;
/**
* @var CImportedObjectContainer
*/
protected $importedObjectContainer;
/**
* @var array
*/
protected $options;
/**
* @var array with data read from source string
*/
protected $data;
/**
* @var array cached data from the adapter
*/
protected $formattedData = [];
/**
* Constructor.
* Source string must be suitable for reader class,
* i.e. if string contains json then reader should be able to read json.
*
* @param array $options import options "createMissing", "updateExisting" and "deleteMissing"
* @param CImportReferencer $referencer class containing all importable objects
* @param CImportedObjectContainer $importedObjectContainer class containing processed host and template IDs
*/
public function __construct(array $options, CImportReferencer $referencer,
CImportedObjectContainer $importedObjectContainer) {
$default_options = [
'template_groups' => ['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;
}
}