1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22/** 23 * Class for importing configuration data. 24 */ 25class CConfigurationImport { 26 27 /** 28 * @var CImportDataAdapter 29 */ 30 protected $adapter; 31 32 /** 33 * @var CImportReferencer 34 */ 35 protected $referencer; 36 37 /** 38 * @var CImportedObjectContainer 39 */ 40 protected $importedObjectContainer; 41 42 /** 43 * @var CTriggerExpression 44 */ 45 protected $triggerExpression; 46 47 /** 48 * @var array 49 */ 50 protected $options; 51 52 /** 53 * @var array with data read from source string 54 */ 55 protected $data; 56 57 /** 58 * @var array cached data from the adapter 59 */ 60 protected $formattedData = []; 61 62 /** 63 * Constructor. 64 * Source string must be suitable for reader class, 65 * i.e. if string contains json then reader should be able to read json. 66 * 67 * @param array $options import options "createMissing", "updateExisting" and "deleteMissing" 68 * @param CImportReferencer $referencer class containing all importable objects 69 * @param CImportedObjectContainer $importedObjectContainer class containing processed host and template IDs 70 * @param CTriggerExpression $triggerExpression class to parse trigger expression 71 */ 72 public function __construct(array $options = [], CImportReferencer $referencer, 73 CImportedObjectContainer $importedObjectContainer, CTriggerExpression $triggerExpression) { 74 $this->options = [ 75 'groups' => ['createMissing' => false], 76 'hosts' => ['updateExisting' => false, 'createMissing' => false], 77 'templates' => ['updateExisting' => false, 'createMissing' => false], 78 'templateScreens' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 79 'applications' => ['updateExisting' => false, 'createMissing' => false], 80 'templateLinkage' => ['createMissing' => false], 81 'items' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 82 'discoveryRules' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 83 'triggers' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 84 'graphs' => ['updateExisting' => false, 'createMissing' => false, 'deleteMissing' => false], 85 'screens' => ['updateExisting' => false, 'createMissing' => false], 86 'maps' => ['updateExisting' => false, 'createMissing' => false], 87 'images' => ['updateExisting' => false, 'createMissing' => false], 88 'valueMaps' => ['updateExisting' => false, 'createMissing' => false] 89 ]; 90 91 $this->options = array_merge($this->options, $options); 92 $this->referencer = $referencer; 93 $this->importedObjectContainer = $importedObjectContainer; 94 $this->triggerExpression = $triggerExpression; 95 } 96 97 /** 98 * Import configuration data. 99 * 100 * @param CImportDataAdapter $adapter an object to provide access to the imported data 101 * 102 * @return bool 103 */ 104 public function import(CImportDataAdapter $adapter) { 105 $this->adapter = $adapter; 106 107 // parse all import for references to resolve them all together with less sql count 108 $this->gatherReferences(); 109 110 $this->processGroups(); 111 $this->processTemplates(); 112 $this->processHosts(); 113 114 // delete missing objects from processed hosts and templates 115 $this->deleteMissingDiscoveryRules(); 116 $this->deleteMissingTriggers(); 117 $this->deleteMissingGraphs(); 118 $this->deleteMissingItems(); 119 $this->deleteMissingApplications(); 120 121 // import objects 122 $this->processApplications(); 123 $this->processValueMaps(); 124 $this->processItems(); 125 $this->processTriggers(); 126 $this->processDiscoveryRules(); 127 $this->processGraphs(); 128 $this->processImages(); 129 $this->processMaps(); 130 $this->processTemplateScreens(); 131 $this->processScreens(); 132 133 return true; 134 } 135 136 /** 137 * Parse all import data and collect references to objects. 138 * For host objects it collects host names, for items - host name and item key, etc. 139 * Collected references are added and resolved via the $this->referencer object. 140 * 141 * @see CImportReferencer 142 */ 143 protected function gatherReferences() { 144 $groupsRefs = []; 145 $templatesRefs = []; 146 $hostsRefs = []; 147 $applicationsRefs = []; 148 $itemsRefs = []; 149 $valueMapsRefs = []; 150 $triggersRefs = []; 151 $graphsRefs = []; 152 $iconMapsRefs = []; 153 $mapsRefs = []; 154 $screensRefs = []; 155 $templateScreensRefs = []; 156 $macrosRefs = []; 157 $proxyRefs = []; 158 $hostPrototypesRefs = []; 159 160 foreach ($this->getFormattedGroups() as $group) { 161 $groupsRefs[$group['name']] = $group['name']; 162 } 163 164 foreach ($this->getFormattedTemplates() as $template) { 165 $templatesRefs[$template['host']] = $template['host']; 166 167 foreach ($template['groups'] as $group) { 168 $groupsRefs[$group['name']] = $group['name']; 169 } 170 171 if (array_key_exists('macros', $template)) { 172 foreach ($template['macros'] as $macro) { 173 $macrosRefs[$template['host']][$macro['macro']] = $macro['macro']; 174 } 175 } 176 177 if (!empty($template['templates'])) { 178 foreach ($template['templates'] as $linkedTemplate) { 179 $templatesRefs[$linkedTemplate['name']] = $linkedTemplate['name']; 180 } 181 } 182 } 183 184 foreach ($this->getFormattedHosts() as $host) { 185 $hostsRefs[$host['host']] = $host['host']; 186 187 foreach ($host['groups'] as $group) { 188 $groupsRefs[$group['name']] = $group['name']; 189 } 190 191 if (array_key_exists('macros', $host)) { 192 foreach ($host['macros'] as $macro) { 193 $macrosRefs[$host['host']][$macro['macro']] = $macro['macro']; 194 } 195 } 196 197 if (!empty($host['templates'])) { 198 foreach ($host['templates'] as $linkedTemplate) { 199 $templatesRefs[$linkedTemplate['name']] = $linkedTemplate['name']; 200 } 201 } 202 203 if (!empty($host['proxy'])) { 204 $proxyRefs[$host['proxy']['name']] = $host['proxy']['name']; 205 } 206 } 207 208 foreach ($this->getFormattedApplications() as $host => $applications) { 209 foreach ($applications as $app) { 210 $applicationsRefs[$host][$app['name']] = $app['name']; 211 } 212 } 213 214 foreach ($this->getFormattedValueMaps() as $valuemap) { 215 $valueMapsRefs[$valuemap['name']] = $valuemap['name']; 216 } 217 218 foreach ($this->getFormattedItems() as $host => $items) { 219 foreach ($items as $item) { 220 $itemsRefs[$host][$item['key_']] = $item['key_']; 221 222 foreach ($item['applications'] as $app) { 223 $applicationsRefs[$host][$app['name']] = $app['name']; 224 } 225 226 if (!empty($item['valuemap'])) { 227 $valueMapsRefs[$item['valuemap']['name']] = $item['valuemap']['name']; 228 } 229 } 230 } 231 232 foreach ($this->getFormattedDiscoveryRules() as $host => $discoveryRules) { 233 foreach ($discoveryRules as $discoveryRule) { 234 $itemsRefs[$host][$discoveryRule['key_']] = $discoveryRule['key_']; 235 236 foreach ($discoveryRule['item_prototypes'] as $itemp) { 237 $itemsRefs[$host][$itemp['key_']] = $itemp['key_']; 238 239 foreach ($itemp['applications'] as $app) { 240 $applicationsRefs[$host][$app['name']] = $app['name']; 241 } 242 243 if (!empty($itemp['valuemap'])) { 244 $valueMapsRefs[$itemp['valuemap']['name']] = $itemp['valuemap']['name']; 245 } 246 } 247 248 foreach ($discoveryRule['trigger_prototypes'] as $trigger) { 249 $triggersRefs[$trigger['description']][$trigger['expression']] = $trigger['expression']; 250 251 // add found hosts and items to references from parsed trigger expressions 252 foreach ($trigger['parsedExpressions'] as $expression) { 253 $hostsRefs[$expression['host']] = $expression['host']; 254 $itemsRefs[$expression['host']][$expression['item']] = $expression['item']; 255 } 256 257 if (array_key_exists('dependencies', $trigger)) { 258 foreach ($trigger['dependencies'] as $dependency) { 259 $triggersRefs[$dependency['name']][$dependency['expression']] = $dependency['expression']; 260 } 261 } 262 } 263 264 foreach ($discoveryRule['graph_prototypes'] as $graph) { 265 if ($graph['ymin_item_1']) { 266 $yMinItem = $graph['ymin_item_1']; 267 $itemsRefs[$yMinItem['host']][$yMinItem['key']] = $yMinItem['key']; 268 } 269 270 if ($graph['ymax_item_1']) { 271 $yMaxItem = $graph['ymax_item_1']; 272 $itemsRefs[$yMaxItem['host']][$yMaxItem['key']] = $yMaxItem['key']; 273 } 274 275 foreach ($graph['gitems'] as $gitem) { 276 $gitemItem = $gitem['item']; 277 278 $hostsRefs[$gitemItem['host']] = $gitemItem['host']; 279 $itemsRefs[$gitemItem['host']][$gitemItem['key']] = $gitemItem['key']; 280 $graphsRefs[$gitemItem['host']][$graph['name']] = $graph['name']; 281 } 282 } 283 284 foreach ($discoveryRule['host_prototypes'] as $hostPrototype) { 285 $hostPrototypesRefs[$host][$discoveryRule['key_']][$hostPrototype['host']] = $hostPrototype['host']; 286 287 foreach ($hostPrototype['group_prototypes'] as $groupPrototype) { 288 if (isset($groupPrototype['group'])) { 289 $groupsRefs[$groupPrototype['group']['name']] = $groupPrototype['group']['name']; 290 } 291 } 292 293 foreach ($hostPrototype['templates'] as $template) { 294 $templatesRefs[$template['name']] = $template['name']; 295 } 296 } 297 } 298 } 299 300 foreach ($this->getFormattedGraphs() as $graph) { 301 if ($graph['ymin_item_1']) { 302 $yMinItem = $graph['ymin_item_1']; 303 304 $hostsRefs[$yMinItem['host']] = $yMinItem['host']; 305 $itemsRefs[$yMinItem['host']][$yMinItem['key']] = $yMinItem['key']; 306 } 307 308 if ($graph['ymax_item_1']) { 309 $yMaxItem = $graph['ymax_item_1']; 310 311 $hostsRefs[$yMaxItem['host']] = $yMaxItem['host']; 312 $itemsRefs[$yMaxItem['host']][$yMaxItem['key']] = $yMaxItem['key']; 313 } 314 315 if (isset($graph['gitems']) && $graph['gitems']) { 316 foreach ($graph['gitems'] as $gitem) { 317 $gitemItem = $gitem['item']; 318 319 $hostsRefs[$gitemItem['host']] = $gitemItem['host']; 320 $itemsRefs[$gitemItem['host']][$gitemItem['key']] = $gitemItem['key']; 321 $graphsRefs[$gitemItem['host']][$graph['name']] = $graph['name']; 322 } 323 } 324 } 325 326 foreach ($this->getFormattedTriggers() as $trigger) { 327 $triggersRefs[$trigger['description']][$trigger['expression']] = $trigger['expression']; 328 329 // add found hosts and items to references from parsed trigger expressions 330 foreach ($trigger['parsedExpressions'] as $expression) { 331 $hostsRefs[$expression['host']] = $expression['host']; 332 $itemsRefs[$expression['host']][$expression['item']] = $expression['item']; 333 } 334 335 if (array_key_exists('dependencies', $trigger)) { 336 foreach ($trigger['dependencies'] as $dependency) { 337 $triggersRefs[$dependency['name']][$dependency['expression']] = $dependency['expression']; 338 } 339 } 340 } 341 342 foreach ($this->getFormattedMaps() as $map) { 343 $mapsRefs[$map['name']] = $map['name']; 344 345 if (!empty($map['iconmap'])) { 346 $iconMapsRefs[$map['iconmap']['name']] = $map['iconmap']['name']; 347 } 348 349 if (isset($map['selements'])) { 350 foreach ($map['selements'] as $selement) { 351 switch ($selement['elementtype']) { 352 case SYSMAP_ELEMENT_TYPE_MAP: 353 $mapsRefs[$selement['element']['name']] = $selement['element']['name']; 354 break; 355 356 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 357 $groupsRefs[$selement['element']['name']] = $selement['element']['name']; 358 break; 359 360 case SYSMAP_ELEMENT_TYPE_HOST: 361 $hostsRefs[$selement['element']['host']] = $selement['element']['host']; 362 break; 363 364 case SYSMAP_ELEMENT_TYPE_TRIGGER: 365 $el = $selement['element']; 366 $triggersRefs[$el['description']][$el['expression']] = $el['expression']; 367 break; 368 } 369 } 370 } 371 372 if (isset($map['links'])) { 373 foreach ($map['links'] as $link) { 374 if (isset($link['linktriggers'])) { 375 foreach ($link['linktriggers'] as $linkTrigger) { 376 $t = $linkTrigger['trigger']; 377 $triggersRefs[$t['description']][$t['expression']] = $t['expression']; 378 } 379 } 380 } 381 } 382 } 383 384 foreach ($this->getFormattedScreens() as $screen) { 385 $screensRefs[$screen['name']] = $screen['name']; 386 387 if (!empty($screen['screenitems'])) { 388 foreach ($screen['screenitems'] as $screenItem) { 389 $resource = $screenItem['resource']; 390 391 if (empty($resource)) { 392 continue; 393 } 394 395 switch ($screenItem['resourcetype']) { 396 case SCREEN_RESOURCE_HOSTS_INFO: 397 case SCREEN_RESOURCE_TRIGGERS_INFO: 398 case SCREEN_RESOURCE_TRIGGERS_OVERVIEW: 399 case SCREEN_RESOURCE_DATA_OVERVIEW: 400 case SCREEN_RESOURCE_HOSTGROUP_TRIGGERS: 401 $groupsRefs[$resource['name']] = $resource['name']; 402 break; 403 404 case SCREEN_RESOURCE_HOST_TRIGGERS: 405 $hostsRefs[$resource['host']] = $resource['host']; 406 break; 407 408 case SCREEN_RESOURCE_GRAPH: 409 case SCREEN_RESOURCE_LLD_GRAPH: 410 $hostsRefs[$resource['host']] = $resource['host']; 411 $graphsRefs[$resource['host']][$resource['name']] = $resource['name']; 412 break; 413 414 case SCREEN_RESOURCE_CLOCK: 415 if ($screenItem['style'] != TIME_TYPE_HOST) { 416 break; 417 } 418 // break; is not missing here 419 420 case SCREEN_RESOURCE_SIMPLE_GRAPH: 421 case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH: 422 case SCREEN_RESOURCE_PLAIN_TEXT: 423 $hostsRefs[$resource['host']] = $resource['host']; 424 $itemsRefs[$resource['host']][$resource['key']] = $resource['key']; 425 break; 426 427 case SCREEN_RESOURCE_MAP: 428 $mapsRefs[$resource['name']] = $resource['name']; 429 break; 430 431 case SCREEN_RESOURCE_SCREEN: 432 $screensRefs[$resource['name']] = $resource['name']; 433 break; 434 } 435 } 436 } 437 } 438 439 foreach ($this->getFormattedTemplateScreens() as $screens) { 440 foreach ($screens as $screen) { 441 $templateScreensRefs[$screen['name']] = $screen['name']; 442 443 if (!empty($screen['screenitems'])) { 444 foreach ($screen['screenitems'] as $screenItem) { 445 $resource = $screenItem['resource']; 446 447 switch ($screenItem['resourcetype']) { 448 case SCREEN_RESOURCE_GRAPH: 449 case SCREEN_RESOURCE_LLD_GRAPH: 450 $hostsRefs[$resource['host']] = $resource['host']; 451 $graphsRefs[$resource['host']][$resource['name']] = $resource['name']; 452 break; 453 454 case SCREEN_RESOURCE_CLOCK: 455 if ($screenItem['style'] != TIME_TYPE_HOST) { 456 break; 457 } 458 // break; is not missing here 459 460 case SCREEN_RESOURCE_SIMPLE_GRAPH: 461 case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH: 462 case SCREEN_RESOURCE_PLAIN_TEXT: 463 $hostsRefs[$resource['host']] = $resource['host']; 464 $itemsRefs[$resource['host']][$resource['key']] = $resource['key']; 465 break; 466 } 467 } 468 } 469 } 470 } 471 472 $this->referencer->addGroups($groupsRefs); 473 $this->referencer->addTemplates($templatesRefs); 474 $this->referencer->addHosts($hostsRefs); 475 $this->referencer->addApplications($applicationsRefs); 476 $this->referencer->addItems($itemsRefs); 477 $this->referencer->addValueMaps($valueMapsRefs); 478 $this->referencer->addTriggers($triggersRefs); 479 $this->referencer->addGraphs($graphsRefs); 480 $this->referencer->addIconMaps($iconMapsRefs); 481 $this->referencer->addMaps($mapsRefs); 482 $this->referencer->addScreens($screensRefs); 483 $this->referencer->addTemplateScreens($templateScreensRefs); 484 $this->referencer->addMacros($macrosRefs); 485 $this->referencer->addProxies($proxyRefs); 486 $this->referencer->addHostPrototypes($hostPrototypesRefs); 487 } 488 489 /** 490 * Import groups. 491 * 492 * @return null 493 */ 494 protected function processGroups() { 495 if (!$this->options['groups']['createMissing']) { 496 return; 497 } 498 499 $groups = $this->getFormattedGroups(); 500 501 if (empty($groups)) { 502 return; 503 } 504 505 // skip the groups that already exist 506 foreach ($groups as $gnum => $group) { 507 if ($this->referencer->resolveGroup($group['name'])) { 508 unset($groups[$gnum]); 509 } 510 } 511 512 if ($groups) { 513 // reset indexing because ids from api does not preserve input array keys 514 $groups = array_values($groups); 515 $newGroups = API::HostGroup()->create($groups); 516 517 foreach ($newGroups['groupids'] as $gnum => $groupid) { 518 $this->referencer->addGroupRef($groups[$gnum]['name'], $groupid); 519 } 520 } 521 } 522 523 /** 524 * Import templates. 525 * 526 * @throws Exception 527 */ 528 protected function processTemplates() { 529 if ($this->options['templates']['updateExisting'] || $this->options['templates']['createMissing']) { 530 $templates = $this->getFormattedTemplates(); 531 if ($templates) { 532 $templateImporter = new CTemplateImporter($this->options, $this->referencer, 533 $this->importedObjectContainer 534 ); 535 $templateImporter->import($templates); 536 537 // get list of imported template IDs and add them processed template ID list 538 $templateIds = $templateImporter->getProcessedTemplateIds(); 539 $this->importedObjectContainer->addTemplateIds($templateIds); 540 } 541 } 542 } 543 544 /** 545 * Import hosts. 546 * 547 * @throws Exception 548 */ 549 protected function processHosts() { 550 if ($this->options['hosts']['updateExisting'] || $this->options['hosts']['createMissing']) { 551 $hosts = $this->getFormattedHosts(); 552 if ($hosts) { 553 $hostImporter = new CHostImporter($this->options, $this->referencer, $this->importedObjectContainer); 554 $hostImporter->import($hosts); 555 556 // get list of imported host IDs and add them processed host ID list 557 $hostIds = $hostImporter->getProcessedHostIds(); 558 $this->importedObjectContainer->addHostIds($hostIds); 559 } 560 } 561 } 562 563 /** 564 * Import applications. 565 */ 566 protected function processApplications() { 567 if (!$this->options['applications']['createMissing']) { 568 return; 569 } 570 571 $allApplications = $this->getFormattedApplications(); 572 573 if (!$allApplications) { 574 return; 575 } 576 577 $applicationsToCreate = []; 578 579 foreach ($allApplications as $host => $applications) { 580 $hostId = $this->referencer->resolveHostOrTemplate($host); 581 582 if (!$this->importedObjectContainer->isHostProcessed($hostId) 583 && !$this->importedObjectContainer->isTemplateProcessed($hostId)) { 584 continue; 585 } 586 587 foreach ($applications as $application) { 588 $application['hostid'] = $hostId; 589 $appId = $this->referencer->resolveApplication($hostId, $application['name']); 590 591 if (!$appId) { 592 $applicationsToCreate[] = $application; 593 } 594 } 595 } 596 597 if ($applicationsToCreate) { 598 API::Application()->create($applicationsToCreate); 599 } 600 601 // refresh applications because templated ones can be inherited to host and used in items 602 $this->referencer->refreshApplications(); 603 } 604 605 /** 606 * Import value maps. 607 */ 608 protected function processValueMaps() { 609 if (!$this->options['valueMaps']['createMissing'] && !$this->options['valueMaps']['updateExisting']) { 610 return; 611 } 612 613 $all_valuemaps = $this->getFormattedValueMaps(); 614 615 if (!$all_valuemaps) { 616 return; 617 } 618 619 $valuemaps_to_create = []; 620 $valuemaps_to_update = []; 621 622 foreach ($all_valuemaps as $valuemap) { 623 $valuemapid = $this->referencer->resolveValueMap($valuemap['name']); 624 625 if ($valuemapid) { 626 $valuemap['valuemapid'] = $valuemapid; 627 $valuemaps_to_update[] = $valuemap; 628 } 629 else { 630 $valuemaps_to_create[] = $valuemap; 631 } 632 } 633 634 if ($this->options['valueMaps']['createMissing'] && $valuemaps_to_create) { 635 $valuemapids = API::ValueMap()->create($valuemaps_to_create); 636 637 foreach ($valuemaps_to_create as $key => $valuemap) { 638 $this->referencer->addValueMapRef($valuemap['name'], $valuemapids['valuemapids'][$key]); 639 } 640 } 641 642 if ($this->options['valueMaps']['updateExisting'] && $valuemaps_to_update) { 643 API::ValueMap()->update($valuemaps_to_update); 644 } 645 } 646 647 /** 648 * Import items. 649 */ 650 protected function processItems() { 651 if (!$this->options['items']['createMissing'] && !$this->options['items']['updateExisting']) { 652 return; 653 } 654 655 $allItems = $this->getFormattedItems(); 656 657 if (!$allItems) { 658 return; 659 } 660 661 $itemsToCreate = []; 662 $itemsToUpdate = []; 663 664 foreach ($allItems as $host => $items) { 665 $hostId = $this->referencer->resolveHostOrTemplate($host); 666 667 if (!$this->importedObjectContainer->isHostProcessed($hostId) 668 && !$this->importedObjectContainer->isTemplateProcessed($hostId)) { 669 continue; 670 } 671 672 foreach ($items as $item) { 673 $item['hostid'] = $hostId; 674 675 if (isset($item['applications']) && $item['applications']) { 676 $applicationsIds = []; 677 678 foreach ($item['applications'] as $application) { 679 if ($applicationId = $this->referencer->resolveApplication($hostId, $application['name'])) { 680 $applicationsIds[] = $applicationId; 681 } 682 else { 683 throw new Exception(_s('Item "%1$s" on "%2$s": application "%3$s" does not exist.', 684 $item['name'], $host, $application['name'])); 685 } 686 } 687 688 $item['applications'] = $applicationsIds; 689 } 690 691 if (array_key_exists('interface_ref', $item) && $item['interface_ref']) { 692 $item['interfaceid'] = $this->referencer->interfacesCache[$hostId][$item['interface_ref']]; 693 } 694 695 if (isset($item['valuemap']) && $item['valuemap']) { 696 $valueMapId = $this->referencer->resolveValueMap($item['valuemap']['name']); 697 698 if (!$valueMapId) { 699 throw new Exception(_s( 700 'Cannot find value map "%1$s" used for item "%2$s" on "%3$s".', 701 $item['valuemap']['name'], 702 $item['name'], 703 $host 704 )); 705 } 706 707 $item['valuemapid'] = $valueMapId; 708 } 709 710 $itemsId = $this->referencer->resolveItem($hostId, $item['key_']); 711 712 if ($itemsId) { 713 $item['itemid'] = $itemsId; 714 $itemsToUpdate[] = $item; 715 } 716 else { 717 $itemsToCreate[] = $item; 718 } 719 } 720 } 721 722 // create/update the items and create a hash hostid->key_->itemid 723 if ($this->options['items']['createMissing'] && $itemsToCreate) { 724 API::Item()->create($itemsToCreate); 725 } 726 727 if ($this->options['items']['updateExisting'] && $itemsToUpdate) { 728 API::Item()->update($itemsToUpdate); 729 } 730 731 // refresh items because templated ones can be inherited to host and used in triggers, graphs, etc. 732 $this->referencer->refreshItems(); 733 } 734 735 /** 736 * Import discovery rules. 737 * 738 * @throws Exception 739 */ 740 protected function processDiscoveryRules() { 741 if (!$this->options['discoveryRules']['createMissing'] && !$this->options['discoveryRules']['updateExisting']) { 742 return; 743 } 744 745 $allDiscoveryRules = $this->getFormattedDiscoveryRules(); 746 747 if (!$allDiscoveryRules) { 748 return; 749 } 750 751 // unset rules that are related to hosts we did not process 752 foreach ($allDiscoveryRules as $host => $discoveryRules) { 753 $hostId = $this->referencer->resolveHostOrTemplate($host); 754 755 if (!$this->importedObjectContainer->isHostProcessed($hostId) 756 && !$this->importedObjectContainer->isTemplateProcessed($hostId)) { 757 unset($allDiscoveryRules[$host]); 758 } 759 } 760 761 $itemsToCreate = []; 762 $itemsToUpdate = []; 763 764 foreach ($allDiscoveryRules as $host => $discoveryRules) { 765 $hostId = $this->referencer->resolveHostOrTemplate($host); 766 767 foreach ($discoveryRules as $item) { 768 $item['hostid'] = $hostId; 769 770 if (array_key_exists('interface_ref', $item) && $item['interface_ref']) { 771 $item['interfaceid'] = $this->referencer->interfacesCache[$hostId][$item['interface_ref']]; 772 } 773 774 unset($item['item_prototypes']); 775 unset($item['trigger_prototypes']); 776 unset($item['graph_prototypes']); 777 unset($item['host_prototypes']); 778 779 $itemId = $this->referencer->resolveItem($hostId, $item['key_']); 780 781 if ($itemId) { 782 $item['itemid'] = $itemId; 783 $itemsToUpdate[] = $item; 784 } 785 else { 786 $itemsToCreate[] = $item; 787 } 788 } 789 } 790 791 // create/update discovery rules and add processed rules to array $processedRules 792 $processedRules = []; 793 794 if ($this->options['discoveryRules']['createMissing'] && $itemsToCreate) { 795 $newItemsIds = API::DiscoveryRule()->create($itemsToCreate); 796 797 foreach ($newItemsIds['itemids'] as $inum => $itemid) { 798 $item = $itemsToCreate[$inum]; 799 800 $this->referencer->addItemRef($item['hostid'], $item['key_'], $itemid); 801 } 802 803 foreach ($itemsToCreate as $item) { 804 $processedRules[$item['hostid']][$item['key_']] = 1; 805 } 806 } 807 808 if ($this->options['discoveryRules']['updateExisting'] && $itemsToUpdate) { 809 API::DiscoveryRule()->update($itemsToUpdate); 810 811 foreach ($itemsToUpdate as $item) { 812 $processedRules[$item['hostid']][$item['key_']] = 1; 813 } 814 } 815 816 // refresh discovery rules because templated ones can be inherited to host and used for prototypes 817 $this->referencer->refreshItems(); 818 819 // process prototypes 820 $prototypesToUpdate = []; 821 $prototypesToCreate = []; 822 $hostPrototypesToUpdate = []; 823 $hostPrototypesToCreate = []; 824 825 foreach ($allDiscoveryRules as $host => $discoveryRules) { 826 $hostId = $this->referencer->resolveHostOrTemplate($host); 827 828 foreach ($discoveryRules as $item) { 829 // if rule was not processed we should not create/update any of its prototypes 830 if (!isset($processedRules[$hostId][$item['key_']])) { 831 continue; 832 } 833 834 $item['hostid'] = $hostId; 835 $itemId = $this->referencer->resolveItem($hostId, $item['key_']); 836 837 // prototypes 838 foreach ($item['item_prototypes'] as $prototype) { 839 $prototype['hostid'] = $hostId; 840 841 $applicationsIds = []; 842 843 foreach ($prototype['applications'] as $application) { 844 $applicationsIds[] = $this->referencer->resolveApplication($hostId, $application['name']); 845 } 846 847 $prototype['applications'] = $applicationsIds; 848 849 if (array_key_exists('application_prototypes', $prototype)) { 850 $prototype['applicationPrototypes'] = $prototype['application_prototypes']; 851 } 852 853 if (array_key_exists('interface_ref', $prototype) && $prototype['interface_ref']) { 854 $prototype['interfaceid'] = $this->referencer->interfacesCache[$hostId][$prototype['interface_ref']]; 855 } 856 857 if ($prototype['valuemap']) { 858 $valueMapId = $this->referencer->resolveValueMap($prototype['valuemap']['name']); 859 860 if (!$valueMapId) { 861 throw new Exception(_s( 862 'Cannot find value map "%1$s" used for item prototype "%2$s" of discovery rule "%3$s" on "%4$s".', 863 $prototype['valuemap']['name'], 864 $prototype['name'], 865 $item['name'], 866 $host 867 )); 868 } 869 870 $prototype['valuemapid'] = $valueMapId; 871 } 872 873 $prototypeId = $this->referencer->resolveItem($hostId, $prototype['key_']); 874 $prototype['rule'] = ['hostid' => $hostId, 'key' => $item['key_']]; 875 876 if ($prototypeId) { 877 $prototype['itemid'] = $prototypeId; 878 $prototypesToUpdate[] = $prototype; 879 } 880 else { 881 $prototypesToCreate[] = $prototype; 882 } 883 } 884 885 // host prototype 886 foreach ($item['host_prototypes'] as $hostPrototype) { 887 // resolve group prototypes 888 $groupLinks = []; 889 890 foreach ($hostPrototype['group_links'] as $groupLink) { 891 $groupId = $this->referencer->resolveGroup($groupLink['group']['name']); 892 893 if (!$groupId) { 894 throw new Exception(_s( 895 'Cannot find host group "%1$s" for host prototype "%2$s" of discovery rule "%3$s" on "%4$s".', 896 $groupLink['group']['name'], 897 $hostPrototype['name'], 898 $item['name'], 899 $host 900 )); 901 } 902 903 $groupLinks[] = ['groupid' => $groupId]; 904 } 905 906 $hostPrototype['groupLinks'] = $groupLinks; 907 $hostPrototype['groupPrototypes'] = $hostPrototype['group_prototypes']; 908 unset($hostPrototype['group_links'], $hostPrototype['group_prototypes']); 909 910 // resolve templates 911 $templates = []; 912 913 foreach ($hostPrototype['templates'] as $template) { 914 $templateId = $this->referencer->resolveTemplate($template['name']); 915 916 if (!$templateId) { 917 throw new Exception(_s( 918 'Cannot find template "%1$s" for host prototype "%2$s" of discovery rule "%3$s" on "%4$s".', 919 $template['name'], 920 $hostPrototype['name'], 921 $item['name'], 922 $host 923 )); 924 } 925 926 $templates[] = ['templateid' => $templateId]; 927 } 928 929 $hostPrototype['templates'] = $templates; 930 931 $hostPrototypeId = $this->referencer->resolveHostPrototype($hostId, $itemId, $hostPrototype['host']); 932 933 if ($hostPrototypeId) { 934 $hostPrototype['hostid'] = $hostPrototypeId; 935 $hostPrototypesToUpdate[] = $hostPrototype; 936 } 937 else { 938 $hostPrototype['ruleid'] = $itemId; 939 $hostPrototypesToCreate[] = $hostPrototype; 940 } 941 } 942 943 if (array_key_exists('interface_ref', $item) && $item['interface_ref']) { 944 $item['interfaceid'] = $this->referencer->interfacesCache[$hostId][$item['interface_ref']]; 945 } 946 unset($item['item_prototypes']); 947 unset($item['trigger_prototypes']); 948 unset($item['graph_prototypes']); 949 unset($item['host_prototypes']); 950 951 $itemsId = $this->referencer->resolveItem($hostId, $item['key_']); 952 953 if ($itemsId) { 954 $item['itemid'] = $itemsId; 955 $itemsToUpdate[] = $item; 956 } 957 else { 958 $itemsToCreate[] = $item; 959 } 960 } 961 } 962 963 if ($prototypesToCreate) { 964 foreach ($prototypesToCreate as &$prototype) { 965 $prototype['ruleid'] = $this->referencer->resolveItem($prototype['rule']['hostid'], $prototype['rule']['key']); 966 } 967 unset($prototype); 968 969 $newPrototypeIds = API::ItemPrototype()->create($prototypesToCreate); 970 971 foreach ($newPrototypeIds['itemids'] as $inum => $itemid) { 972 $item = $prototypesToCreate[$inum]; 973 $this->referencer->addItemRef($item['hostid'], $item['key_'], $itemid); 974 } 975 } 976 977 if ($prototypesToUpdate) { 978 foreach ($prototypesToCreate as &$prototype) { 979 $prototype['ruleid'] = $this->referencer->resolveItem($prototype['rule']['hostid'], $prototype['rule']['key']); 980 } 981 unset($prototype); 982 983 API::ItemPrototype()->update($prototypesToUpdate); 984 } 985 986 if ($hostPrototypesToCreate) { 987 API::HostPrototype()->create($hostPrototypesToCreate); 988 } 989 if ($hostPrototypesToUpdate) { 990 API::HostPrototype()->update($hostPrototypesToUpdate); 991 } 992 993 // refresh prototypes because templated ones can be inherited to host and used in triggers prototypes or graph prototypes 994 $this->referencer->refreshItems(); 995 996 // first we need to create item prototypes and only then graph prototypes 997 $triggersToCreate = []; 998 $triggersToUpdate = []; 999 $graphsToCreate = []; 1000 $graphsToUpdate = []; 1001 // the list of triggers to process dependencies 1002 $triggers = []; 1003 1004 foreach ($allDiscoveryRules as $host => $discoveryRules) { 1005 $hostId = $this->referencer->resolveHostOrTemplate($host); 1006 1007 foreach ($discoveryRules as $item) { 1008 // if rule was not processed we should not create/update any of its prototypes 1009 if (!isset($processedRules[$hostId][$item['key_']])) { 1010 continue; 1011 } 1012 1013 // trigger prototypes 1014 foreach ($item['trigger_prototypes'] as $trigger) { 1015 // search for existing items in trigger prototype expressions 1016 foreach ($trigger['parsedExpressions'] as $expression) { 1017 $hostId = $this->referencer->resolveHostOrTemplate($expression['host']); 1018 $itemId = $hostId ? $this->referencer->resolveItem($hostId, $expression['item']) : false; 1019 1020 if (!$itemId) { 1021 throw new Exception(_s( 1022 'Cannot find item "%1$s" on "%2$s" used in trigger prototype "%3$s" of discovery rule "%4$s" on "%5$s".', 1023 $expression['item'], 1024 $expression['host'], 1025 $trigger['description'], 1026 $item['name'], 1027 $host 1028 )); 1029 } 1030 } 1031 1032 $triggerId = $this->referencer->resolveTrigger($trigger['description'], $trigger['expression']); 1033 1034 $triggers[] = $trigger; 1035 unset($trigger['dependencies']); 1036 1037 if ($triggerId) { 1038 $trigger['triggerid'] = $triggerId; 1039 $triggersToUpdate[] = $trigger; 1040 } 1041 else { 1042 $triggersToCreate[] = $trigger; 1043 } 1044 } 1045 1046 // graph prototypes 1047 foreach ($item['graph_prototypes'] as $graph) { 1048 if ($graph['ymin_item_1']) { 1049 $hostId = $this->referencer->resolveHostOrTemplate($graph['ymin_item_1']['host']); 1050 $itemId = $hostId 1051 ? $this->referencer->resolveItem($hostId, $graph['ymin_item_1']['key']) 1052 : false; 1053 1054 if (!$itemId) { 1055 throw new Exception(_s( 1056 '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".', 1057 $graph['ymin_item_1']['key'], 1058 $graph['ymin_item_1']['host'], 1059 $graph['name'], 1060 $item['name'], 1061 $host 1062 )); 1063 } 1064 1065 $graph['ymin_itemid'] = $itemId; 1066 } 1067 1068 if ($graph['ymax_item_1']) { 1069 $hostId = $this->referencer->resolveHostOrTemplate($graph['ymax_item_1']['host']); 1070 $itemId = $hostId 1071 ? $this->referencer->resolveItem($hostId, $graph['ymax_item_1']['key']) 1072 : false; 1073 1074 if (!$itemId) { 1075 throw new Exception(_s( 1076 '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".', 1077 $graph['ymax_item_1']['key'], 1078 $graph['ymax_item_1']['host'], 1079 $graph['name'], 1080 $item['name'], 1081 $host 1082 )); 1083 } 1084 1085 $graph['ymax_itemid'] = $itemId; 1086 } 1087 1088 foreach ($graph['gitems'] as &$gitem) { 1089 $gitemHostId = $this->referencer->resolveHostOrTemplate($gitem['item']['host']); 1090 $gitem['itemid'] = $gitemHostId 1091 ? $this->referencer->resolveItem($gitemHostId, $gitem['item']['key']) 1092 : false; 1093 1094 if (!$gitem['itemid']) { 1095 throw new Exception(_s( 1096 'Cannot find item "%1$s" on "%2$s" used in graph prototype "%3$s" of discovery rule "%4$s" on "%5$s".', 1097 $gitem['item']['key'], 1098 $gitem['item']['host'], 1099 $graph['name'], 1100 $item['name'], 1101 $host 1102 )); 1103 } 1104 } 1105 unset($gitem); 1106 1107 $graphId = $this->referencer->resolveGraph($gitemHostId, $graph['name']); 1108 if ($graphId) { 1109 $graph['graphid'] = $graphId; 1110 $graphsToUpdate[] = $graph; 1111 } 1112 else { 1113 $graphsToCreate[] = $graph; 1114 } 1115 } 1116 } 1117 } 1118 1119 if ($triggersToCreate) { 1120 $result = API::TriggerPrototype()->create($triggersToCreate); 1121 1122 foreach ($result['triggerids'] as $tnum => $triggerid) { 1123 $trigger = $triggersToCreate[$tnum]; 1124 $this->referencer->addTriggerRef($trigger['description'], $trigger['expression'], $triggerid); 1125 } 1126 } 1127 if ($triggersToUpdate) { 1128 API::TriggerPrototype()->update($triggersToUpdate); 1129 } 1130 if ($graphsToCreate) { 1131 API::GraphPrototype()->create($graphsToCreate); 1132 $this->referencer->refreshGraphs(); 1133 } 1134 if ($graphsToUpdate) { 1135 API::GraphPrototype()->update($graphsToUpdate); 1136 } 1137 1138 $this->processTriggerPrototypeDependencies($triggers); 1139 } 1140 1141 /** 1142 * Update trigger dependencies 1143 * 1144 * @param array $triggers 1145 * 1146 * @throws Exception 1147 */ 1148 protected function processTriggerPrototypeDependencies(array $triggers) { 1149 $dependencies = []; 1150 1151 foreach ($triggers as $trigger) { 1152 if (!array_key_exists('dependencies', $trigger)) { 1153 continue; 1154 } 1155 1156 $deps = []; 1157 $triggerid = $this->referencer->resolveTrigger($trigger['description'], $trigger['expression']); 1158 1159 foreach ($trigger['dependencies'] as $dependency) { 1160 $dep_triggerid = $this->referencer->resolveTrigger($dependency['name'], $dependency['expression']); 1161 1162 if (!$dep_triggerid) { 1163 throw new Exception(_s('Trigger prototype "%1$s" depends on trigger "%2$s", which does not exist.', 1164 $trigger['description'], 1165 $dependency['name'] 1166 )); 1167 } 1168 1169 $deps[] = ['triggerid' => $dep_triggerid]; 1170 } 1171 1172 $dependencies[] = [ 1173 'triggerid' => $triggerid, 1174 'dependencies' => $deps 1175 ]; 1176 } 1177 1178 if ($dependencies) { 1179 API::TriggerPrototype()->update($dependencies); 1180 } 1181 } 1182 1183 /** 1184 * Import graphs. 1185 * 1186 * @throws Exception 1187 */ 1188 protected function processGraphs() { 1189 if (!$this->options['graphs']['createMissing'] && !$this->options['graphs']['updateExisting']) { 1190 return; 1191 } 1192 1193 $allGraphs = $this->getFormattedGraphs(); 1194 1195 if (!$allGraphs) { 1196 return; 1197 } 1198 1199 $graphsToCreate = []; 1200 $graphsToUpdate = []; 1201 1202 foreach ($allGraphs as $graph) { 1203 if ($graph['ymin_item_1']) { 1204 $hostId = $this->referencer->resolveHostOrTemplate($graph['ymin_item_1']['host']); 1205 $itemId = $hostId ? $this->referencer->resolveItem($hostId, $graph['ymin_item_1']['key']) : false; 1206 1207 if (!$itemId) { 1208 throw new Exception(_s( 1209 'Cannot find item "%1$s" on "%2$s" used as the Y axis MIN value for graph "%3$s".', 1210 $graph['ymin_item_1']['key'], 1211 $graph['ymin_item_1']['host'], 1212 $graph['name'] 1213 )); 1214 } 1215 1216 $graph['ymin_itemid'] = $itemId; 1217 } 1218 1219 if ($graph['ymax_item_1']) { 1220 $hostId = $this->referencer->resolveHostOrTemplate($graph['ymax_item_1']['host']); 1221 $itemId = $hostId ? $this->referencer->resolveItem($hostId, $graph['ymax_item_1']['key']) : false; 1222 1223 if (!$itemId) { 1224 throw new Exception(_s( 1225 'Cannot find item "%1$s" on "%2$s" used as the Y axis MAX value for graph "%3$s".', 1226 $graph['ymax_item_1']['key'], 1227 $graph['ymax_item_1']['host'], 1228 $graph['name'] 1229 )); 1230 } 1231 1232 $graph['ymax_itemid'] = $itemId; 1233 } 1234 1235 if (isset($graph['gitems']) && $graph['gitems']) { 1236 foreach ($graph['gitems'] as &$gitem) { 1237 $gitemHostId = $this->referencer->resolveHostOrTemplate($gitem['item']['host']); 1238 $gitem['itemid'] = $gitemHostId 1239 ? $this->referencer->resolveItem($gitemHostId, $gitem['item']['key']) 1240 : false; 1241 1242 if (!$gitem['itemid']) { 1243 throw new Exception(_s( 1244 'Cannot find item "%1$s" on "%2$s" used in graph "%3$s".', 1245 $gitem['item']['key'], 1246 $gitem['item']['host'], 1247 $graph['name'] 1248 )); 1249 } 1250 } 1251 unset($gitem); 1252 1253 $graphId = $this->referencer->resolveGraph($gitemHostId, $graph['name']); 1254 1255 if ($graphId) { 1256 $graph['graphid'] = $graphId; 1257 $graphsToUpdate[] = $graph; 1258 } 1259 else { 1260 $graphsToCreate[] = $graph; 1261 } 1262 } 1263 } 1264 1265 if ($this->options['graphs']['createMissing'] && $graphsToCreate) { 1266 API::Graph()->create($graphsToCreate); 1267 } 1268 if ($this->options['graphs']['updateExisting'] && $graphsToUpdate) { 1269 API::Graph()->update($graphsToUpdate); 1270 } 1271 1272 $this->referencer->refreshGraphs(); 1273 } 1274 1275 /** 1276 * Import triggers. 1277 */ 1278 protected function processTriggers() { 1279 if (!$this->options['triggers']['createMissing'] && !$this->options['triggers']['updateExisting']) { 1280 return; 1281 } 1282 1283 $allTriggers = $this->getFormattedTriggers(); 1284 1285 if (!$allTriggers) { 1286 return; 1287 } 1288 1289 $triggersToCreate = []; 1290 $triggersToUpdate = []; 1291 // the list of triggers to process dependencies 1292 $triggers = []; 1293 1294 foreach ($allTriggers as $trigger) { 1295 // search for existing items in trigger expressions 1296 foreach ($trigger['parsedExpressions'] as $expression) { 1297 $hostId = $this->referencer->resolveHostOrTemplate($expression['host']); 1298 $itemId = $hostId ? $this->referencer->resolveItem($hostId, $expression['item']) : false; 1299 1300 if (!$itemId) { 1301 throw new Exception(_s('Cannot find item "%1$s" on "%2$s" used in trigger "%3$s".', 1302 $expression['item'], 1303 $expression['host'], 1304 $trigger['description'] 1305 )); 1306 } 1307 } 1308 1309 $triggerId = $this->referencer->resolveTrigger($trigger['description'], $trigger['expression']); 1310 1311 if ($triggerId) { 1312 if ($this->options['triggers']['updateExisting']) { 1313 $triggers[] = $trigger; 1314 1315 $trigger['triggerid'] = $triggerId; 1316 unset($trigger['dependencies']); 1317 $triggersToUpdate[] = $trigger; 1318 } 1319 } 1320 else { 1321 if ($this->options['triggers']['createMissing']) { 1322 $triggers[] = $trigger; 1323 1324 unset($trigger['dependencies']); 1325 $triggersToCreate[] = $trigger; 1326 } 1327 } 1328 } 1329 1330 if ($triggersToCreate) { 1331 $result = API::Trigger()->create($triggersToCreate); 1332 1333 foreach ($result['triggerids'] as $tnum => $triggerid) { 1334 $trigger = $triggersToCreate[$tnum]; 1335 $this->referencer->addTriggerRef($trigger['description'], $trigger['expression'], $triggerid); 1336 } 1337 } 1338 1339 if ($triggersToUpdate) { 1340 API::Trigger()->update($triggersToUpdate); 1341 } 1342 1343 // refresh triggers because template triggers can be inherited to host and used in maps 1344 $this->referencer->refreshTriggers(); 1345 1346 $this->processTriggerDependencies($triggers); 1347 } 1348 1349 /** 1350 * Update trigger dependencies 1351 * 1352 * @param array $triggers 1353 * 1354 * @throws Exception 1355 */ 1356 protected function processTriggerDependencies(array $triggers) { 1357 $dependencies = []; 1358 1359 foreach ($triggers as $trigger) { 1360 if (!array_key_exists('dependencies', $trigger)) { 1361 continue; 1362 } 1363 1364 $deps = []; 1365 $triggerid = $this->referencer->resolveTrigger($trigger['description'], $trigger['expression']); 1366 1367 foreach ($trigger['dependencies'] as $dependency) { 1368 $dep_triggerid = $this->referencer->resolveTrigger($dependency['name'], $dependency['expression']); 1369 1370 if (!$dep_triggerid) { 1371 throw new Exception(_s('Trigger "%1$s" depends on trigger "%2$s", which does not exist.', 1372 $trigger['description'], 1373 $dependency['name'] 1374 )); 1375 } 1376 1377 $deps[] = ['triggerid' => $dep_triggerid]; 1378 } 1379 1380 $dependencies[] = [ 1381 'triggerid' => $triggerid, 1382 'dependencies' => $deps 1383 ]; 1384 } 1385 1386 if ($dependencies) { 1387 API::Trigger()->update($dependencies); 1388 } 1389 } 1390 1391 /** 1392 * Import images. 1393 * 1394 * @throws Exception 1395 * 1396 * @return null 1397 */ 1398 protected function processImages() { 1399 if (!$this->options['images']['updateExisting'] && !$this->options['images']['createMissing']) { 1400 return; 1401 } 1402 1403 $allImages = $this->getFormattedImages(); 1404 1405 if (!$allImages) { 1406 return; 1407 } 1408 1409 $allImages = zbx_toHash($allImages, 'name'); 1410 1411 $dbImages = API::Image()->get([ 1412 'output' => ['imageid', 'name'], 1413 'filter' => ['name' => array_keys($allImages)] 1414 ]); 1415 $dbImages = zbx_toHash($dbImages, 'name'); 1416 1417 $imagesToUpdate = []; 1418 $imagesToCreate = []; 1419 1420 foreach ($allImages as $imageName => $image) { 1421 if (isset($dbImages[$imageName])) { 1422 $image['imageid'] = $dbImages[$imageName]['imageid']; 1423 unset($image['imagetype']); 1424 $imagesToUpdate[] = $image; 1425 } 1426 else { 1427 $imagesToCreate[] = $image; 1428 } 1429 } 1430 1431 if ($this->options['images']['createMissing'] && $imagesToCreate) { 1432 API::Image()->create($imagesToCreate); 1433 } 1434 1435 if ($this->options['images']['updateExisting'] && $imagesToUpdate) { 1436 API::Image()->update($imagesToUpdate); 1437 } 1438 } 1439 1440 /** 1441 * Import maps. 1442 */ 1443 protected function processMaps() { 1444 if ($this->options['maps']['updateExisting'] || $this->options['maps']['createMissing']) { 1445 $maps = $this->getFormattedMaps(); 1446 if ($maps) { 1447 $mapImporter = new CMapImporter($this->options, $this->referencer, $this->importedObjectContainer); 1448 $mapImporter->import($maps); 1449 } 1450 } 1451 } 1452 1453 /** 1454 * Import screens. 1455 */ 1456 protected function processScreens() { 1457 if ($this->options['screens']['updateExisting'] || $this->options['screens']['createMissing']) { 1458 $screens = $this->getFormattedScreens(); 1459 if ($screens) { 1460 $screenImporter = new CScreenImporter($this->options, $this->referencer, 1461 $this->importedObjectContainer 1462 ); 1463 $screenImporter->import($screens); 1464 } 1465 } 1466 } 1467 1468 /** 1469 * Import template screens. 1470 */ 1471 protected function processTemplateScreens() { 1472 if ($this->options['templateScreens']['updateExisting'] 1473 || $this->options['templateScreens']['createMissing'] 1474 || $this->options['templateScreens']['deleteMissing']) { 1475 $screens = $this->getFormattedTemplateScreens(); 1476 $screenImporter = new CTemplateScreenImporter($this->options, $this->referencer, 1477 $this->importedObjectContainer 1478 ); 1479 $screenImporter->delete($screens); 1480 $screenImporter->import($screens); 1481 } 1482 } 1483 1484 /** 1485 * Deletes items from DB that are missing in XML. 1486 * 1487 * @return null 1488 */ 1489 protected function deleteMissingItems() { 1490 if (!$this->options['items']['deleteMissing']) { 1491 return; 1492 } 1493 1494 $processedHostIds = $this->importedObjectContainer->getHostIds(); 1495 $processedTemplateIds = $this->importedObjectContainer->getTemplateIds(); 1496 1497 $processedHostIds = array_merge($processedHostIds, $processedTemplateIds); 1498 1499 // no hosts or templates have been processed 1500 if (!$processedHostIds) { 1501 return; 1502 } 1503 1504 $itemIdsXML = []; 1505 1506 $allItems = $this->getFormattedItems(); 1507 1508 if ($allItems) { 1509 foreach ($allItems as $host => $items) { 1510 $hostId = $this->referencer->resolveHostOrTemplate($host); 1511 1512 foreach ($items as $item) { 1513 $itemId = $this->referencer->resolveItem($hostId, $item['key_']); 1514 1515 if ($itemId) { 1516 $itemIdsXML[$itemId] = $itemId; 1517 } 1518 } 1519 } 1520 } 1521 1522 $dbItemIds = API::Item()->get([ 1523 'output' => ['itemid'], 1524 'hostids' => $processedHostIds, 1525 'preservekeys' => true, 1526 'nopermissions' => true, 1527 'inherited' => false, 1528 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL] 1529 ]); 1530 1531 $itemsToDelete = array_diff_key($dbItemIds, $itemIdsXML); 1532 1533 if ($itemsToDelete) { 1534 API::Item()->delete(array_keys($itemsToDelete)); 1535 } 1536 1537 $this->referencer->refreshItems(); 1538 } 1539 1540 /** 1541 * Deletes applications from DB that are missing in XML. 1542 * 1543 * @return null 1544 */ 1545 protected function deleteMissingApplications() { 1546 if (!$this->options['applications']['deleteMissing']) { 1547 return; 1548 } 1549 1550 $processedHostIds = $this->importedObjectContainer->getHostIds(); 1551 $processedTemplateIds = $this->importedObjectContainer->getTemplateIds(); 1552 1553 $processedHostIds = array_merge($processedHostIds, $processedTemplateIds); 1554 1555 // no hosts or templates have been processed 1556 if (!$processedHostIds) { 1557 return; 1558 } 1559 1560 $applicationIdsXML = []; 1561 1562 $allApplications = $this->getFormattedApplications(); 1563 1564 if ($allApplications) { 1565 foreach ($allApplications as $host => $applications) { 1566 $hostId = $this->referencer->resolveHostOrTemplate($host); 1567 1568 foreach ($applications as $application) { 1569 $applicationId = $this->referencer->resolveApplication($hostId, $application['name']); 1570 1571 if ($applicationId) { 1572 $applicationIdsXML[$applicationId] = $applicationId; 1573 } 1574 } 1575 } 1576 } 1577 1578 $dbApplicationIds = API::Application()->get([ 1579 'output' => ['applicationid'], 1580 'hostids' => $processedHostIds, 1581 'preservekeys' => true, 1582 'nopermissions' => true, 1583 'inherited' => false, 1584 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL] 1585 ]); 1586 1587 $applicationsToDelete = array_diff_key($dbApplicationIds, $applicationIdsXML); 1588 if ($applicationsToDelete) { 1589 API::Application()->delete(array_keys($applicationsToDelete)); 1590 } 1591 1592 // refresh applications because templated ones can be inherited to host and used in items 1593 $this->referencer->refreshApplications(); 1594 } 1595 1596 /** 1597 * Deletes triggers from DB that are missing in XML. 1598 * 1599 * @return null 1600 */ 1601 protected function deleteMissingTriggers() { 1602 if (!$this->options['triggers']['deleteMissing']) { 1603 return; 1604 } 1605 1606 $processedHostIds = $this->importedObjectContainer->getHostIds(); 1607 $processedTemplateIds = $this->importedObjectContainer->getTemplateIds(); 1608 1609 $processedHostIds = array_merge($processedHostIds, $processedTemplateIds); 1610 1611 // no hosts or templates have been processed 1612 if (!$processedHostIds) { 1613 return; 1614 } 1615 1616 $triggersXML = []; 1617 1618 $allTriggers = $this->getFormattedTriggers(); 1619 1620 if ($allTriggers) { 1621 foreach ($allTriggers as $trigger) { 1622 $triggerId = $this->referencer->resolveTrigger($trigger['description'], $trigger['expression']); 1623 1624 if ($triggerId) { 1625 $triggersXML[$triggerId] = $triggerId; 1626 } 1627 } 1628 } 1629 1630 $dbTriggerIds = API::Trigger()->get([ 1631 'output' => ['triggerid'], 1632 'hostids' => $processedHostIds, 1633 'selectHosts' => ['hostid'], 1634 'preservekeys' => true, 1635 'nopermissions' => true, 1636 'inherited' => false, 1637 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL] 1638 ]); 1639 1640 // check that potentially deletable trigger belongs to same hosts that are in XML 1641 // if some triggers belong to more hosts than current XML contains, don't delete them 1642 $triggersToDelete = array_diff_key($dbTriggerIds, $triggersXML); 1643 $triggerIdsToDelete = []; 1644 $processedHostIds = array_flip($processedHostIds); 1645 1646 foreach ($triggersToDelete as $triggerId => $trigger) { 1647 $triggerHostIds = array_flip(zbx_objectValues($trigger['hosts'], 'hostid')); 1648 if (!array_diff_key($triggerHostIds, $processedHostIds)) { 1649 $triggerIdsToDelete[] = $triggerId; 1650 } 1651 } 1652 1653 if ($triggerIdsToDelete) { 1654 API::Trigger()->delete($triggerIdsToDelete); 1655 } 1656 1657 // refresh triggers because template triggers can be inherited to host and used in maps 1658 $this->referencer->refreshTriggers(); 1659 } 1660 1661 /** 1662 * Deletes graphs from DB that are missing in XML. 1663 * 1664 * @return null 1665 */ 1666 protected function deleteMissingGraphs() { 1667 if (!$this->options['graphs']['deleteMissing']) { 1668 return; 1669 } 1670 1671 $processedHostIds = $this->importedObjectContainer->getHostIds(); 1672 $processedTemplateIds = $this->importedObjectContainer->getTemplateIds(); 1673 1674 $processedHostIds = array_merge($processedHostIds, $processedTemplateIds); 1675 1676 // no hosts or templates have been processed 1677 if (!$processedHostIds) { 1678 return; 1679 } 1680 1681 $graphsIdsXML = []; 1682 1683 // gather host IDs for graphs that exist in XML 1684 $allGraphs = $this->getFormattedGraphs(); 1685 1686 if ($allGraphs) { 1687 foreach ($allGraphs as $graph) { 1688 if (isset($graph['gitems']) && $graph['gitems']) { 1689 foreach ($graph['gitems'] as $gitem) { 1690 $gitemHostId = $this->referencer->resolveHostOrTemplate($gitem['item']['host']); 1691 $graphId = $this->referencer->resolveGraph($gitemHostId, $graph['name']); 1692 1693 if ($graphId) { 1694 $graphsIdsXML[$graphId] = $graphId; 1695 } 1696 } 1697 } 1698 } 1699 } 1700 1701 $dbGraphIds = API::Graph()->get([ 1702 'output' => ['graphid'], 1703 'hostids' => $processedHostIds, 1704 'selectHosts' => ['hostid'], 1705 'preservekeys' => true, 1706 'nopermissions' => true, 1707 'inherited' => false, 1708 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL] 1709 ]); 1710 1711 // check that potentially deletable graph belongs to same hosts that are in XML 1712 // if some graphs belong to more hosts than current XML contains, don't delete them 1713 $graphsToDelete = array_diff_key($dbGraphIds, $graphsIdsXML); 1714 $graphIdsToDelete = []; 1715 $processedHostIds = array_flip($processedHostIds); 1716 1717 foreach ($graphsToDelete as $graphId => $graph) { 1718 $graphHostIds = array_flip(zbx_objectValues($graph['hosts'], 'hostid')); 1719 1720 if (!array_diff_key($graphHostIds, $processedHostIds)) { 1721 $graphIdsToDelete[] = $graphId; 1722 } 1723 } 1724 1725 if ($graphIdsToDelete) { 1726 API::Graph()->delete($graphIdsToDelete); 1727 } 1728 1729 $this->referencer->refreshGraphs(); 1730 } 1731 1732 /** 1733 * Deletes discovery rules and prototypes from DB that are missing in XML. 1734 * 1735 * @return null 1736 */ 1737 protected function deleteMissingDiscoveryRules() { 1738 if (!$this->options['discoveryRules']['deleteMissing']) { 1739 return; 1740 } 1741 1742 $processedHostIds = $this->importedObjectContainer->getHostIds(); 1743 $processedTemplateIds = $this->importedObjectContainer->getTemplateIds(); 1744 1745 $processedHostIds = array_merge($processedHostIds, $processedTemplateIds); 1746 1747 // no hosts or templates have been processed 1748 if (!$processedHostIds) { 1749 return; 1750 } 1751 1752 $discoveryRuleIdsXML = []; 1753 1754 $allDiscoveryRules = $this->getFormattedDiscoveryRules(); 1755 1756 if ($allDiscoveryRules) { 1757 foreach ($allDiscoveryRules as $host => $discoveryRules) { 1758 $hostId = $this->referencer->resolveHostOrTemplate($host); 1759 1760 foreach ($discoveryRules as $discoveryRule) { 1761 $discoveryRuleId = $this->referencer->resolveItem($hostId, $discoveryRule['key_']); 1762 1763 if ($discoveryRuleId) { 1764 $discoveryRuleIdsXML[$discoveryRuleId] = $discoveryRuleId; 1765 } 1766 } 1767 } 1768 } 1769 1770 $dbDiscoveryRuleIds = API::DiscoveryRule()->get([ 1771 'output' => ['itemid'], 1772 'hostids' => $processedHostIds, 1773 'preservekeys' => true, 1774 'nopermissions' => true, 1775 'inherited' => false 1776 ]); 1777 1778 $discoveryRulesToDelete = array_diff_key($dbDiscoveryRuleIds, $discoveryRuleIdsXML); 1779 1780 if ($discoveryRulesToDelete) { 1781 API::DiscoveryRule()->delete(array_keys($discoveryRulesToDelete)); 1782 } 1783 1784 // refresh discovery rules because templated ones can be inherited to host and used for prototypes 1785 $this->referencer->refreshItems(); 1786 1787 $hostPrototypeIdsXML = []; 1788 $triggerPrototypeIdsXML = []; 1789 $itemPrototypeIdsXML = []; 1790 $graphPrototypeIdsXML = []; 1791 1792 foreach ($allDiscoveryRules as $host => $discoveryRules) { 1793 $hostId = $this->referencer->resolveHostOrTemplate($host); 1794 1795 foreach ($discoveryRules as $discoveryRule) { 1796 $discoveryRuleId = $this->referencer->resolveItem($hostId, $discoveryRule['key_']); 1797 1798 if ($discoveryRuleId) { 1799 // gather host prototype IDs to delete 1800 foreach ($discoveryRule['host_prototypes'] as $hostPrototype) { 1801 $hostPrototypeId = $this->referencer->resolveHostPrototype($hostId, $discoveryRuleId, 1802 $hostPrototype['host'] 1803 ); 1804 1805 if ($hostPrototypeId) { 1806 $hostPrototypeIdsXML[$hostPrototypeId] = $hostPrototypeId; 1807 } 1808 } 1809 1810 // gather trigger prototype IDs to delete 1811 foreach ($discoveryRule['trigger_prototypes'] as $triggerPrototype) { 1812 $triggerPrototypeId = $this->referencer->resolveTrigger($triggerPrototype['description'], 1813 $triggerPrototype['expression'] 1814 ); 1815 1816 if ($triggerPrototypeId) { 1817 $triggerPrototypeIdsXML[$triggerPrototypeId] = $triggerPrototypeId; 1818 } 1819 } 1820 1821 // gather graph prototype IDs to delete 1822 foreach ($discoveryRule['graph_prototypes'] as $graphPrototype) { 1823 $graphPrototypeId = $this->referencer->resolveGraph($hostId, $graphPrototype['name']); 1824 1825 if ($graphPrototypeId) { 1826 $graphPrototypeIdsXML[$graphPrototypeId] = $graphPrototypeId; 1827 } 1828 } 1829 1830 // gather item prototype IDs to delete 1831 foreach ($discoveryRule['item_prototypes'] as $itemPrototype) { 1832 $itemPrototypeId = $this->referencer->resolveItem($hostId, $itemPrototype['key_']); 1833 1834 if ($itemPrototypeId) { 1835 $itemPrototypeIdsXML[$itemPrototypeId] = $itemPrototypeId; 1836 } 1837 } 1838 } 1839 } 1840 } 1841 1842 // delete missing host prototypes 1843 $dbHostPrototypeIds = API::HostPrototype()->get([ 1844 'output' => ['hostid'], 1845 'discoveryids' => $discoveryRuleIdsXML, 1846 'preservekeys' => true, 1847 'nopermissions' => true, 1848 'inherited' => false 1849 ]); 1850 1851 $hostPrototypesToDelete = array_diff_key($dbHostPrototypeIds, $hostPrototypeIdsXML); 1852 1853 if ($hostPrototypesToDelete) { 1854 API::HostPrototype()->delete(array_keys($hostPrototypesToDelete)); 1855 } 1856 1857 // delete missing trigger prototypes 1858 $dbTriggerPrototypeIds = API::TriggerPrototype()->get([ 1859 'output' => ['triggerid'], 1860 'hostids' => $processedHostIds, 1861 'preservekeys' => true, 1862 'nopermissions' => true, 1863 'inherited' => false 1864 ]); 1865 1866 $triggerPrototypesToDelete = array_diff_key($dbTriggerPrototypeIds, $triggerPrototypeIdsXML); 1867 1868 // unlike triggers that belong to multiple hosts, trigger prototypes do not, so we just delete them 1869 if ($triggerPrototypesToDelete) { 1870 API::TriggerPrototype()->delete(array_keys($triggerPrototypesToDelete)); 1871 } 1872 1873 // delete missing graph prototypes 1874 $dbGraphPrototypeIds = API::GraphPrototype()->get([ 1875 'output' => ['graphid'], 1876 'hostids' => $processedHostIds, 1877 'preservekeys' => true, 1878 'nopermissions' => true, 1879 'inherited' => false 1880 ]); 1881 1882 $graphPrototypesToDelete = array_diff_key($dbGraphPrototypeIds, $graphPrototypeIdsXML); 1883 1884 // unlike graphs that belong to multiple hosts, graph prototypes do not, so we just delete them 1885 if ($graphPrototypesToDelete) { 1886 API::GraphPrototype()->delete(array_keys($graphPrototypesToDelete)); 1887 } 1888 1889 // delete missing item prototypes 1890 $dbItemPrototypeIds = API::ItemPrototype()->get([ 1891 'output' => ['itemid'], 1892 'hostids' => $processedHostIds, 1893 'preservekeys' => true, 1894 'nopermissions' => true, 1895 'inherited' => false 1896 ]); 1897 1898 $itemPrototypesToDelete = array_diff_key($dbItemPrototypeIds, $itemPrototypeIdsXML); 1899 1900 if ($itemPrototypesToDelete) { 1901 API::ItemPrototype()->delete(array_keys($itemPrototypesToDelete)); 1902 } 1903 } 1904 1905 /** 1906 * Get formatted groups. 1907 * 1908 * @return array 1909 */ 1910 protected function getFormattedGroups() { 1911 if (!isset($this->formattedData['groups'])) { 1912 $this->formattedData['groups'] = $this->adapter->getGroups(); 1913 } 1914 1915 return $this->formattedData['groups']; 1916 } 1917 1918 /** 1919 * Get formatted templates. 1920 * 1921 * @return array 1922 */ 1923 public function getFormattedTemplates() { 1924 if (!isset($this->formattedData['templates'])) { 1925 $this->formattedData['templates'] = $this->adapter->getTemplates(); 1926 } 1927 1928 return $this->formattedData['templates']; 1929 } 1930 1931 /** 1932 * Get formatted hosts. 1933 * 1934 * @return array 1935 */ 1936 public function getFormattedHosts() { 1937 if (!isset($this->formattedData['hosts'])) { 1938 $this->formattedData['hosts'] = $this->adapter->getHosts(); 1939 } 1940 1941 return $this->formattedData['hosts']; 1942 } 1943 1944 /** 1945 * Get formatted applications. 1946 * 1947 * @return array 1948 */ 1949 protected function getFormattedApplications() { 1950 if (!isset($this->formattedData['applications'])) { 1951 $this->formattedData['applications'] = $this->adapter->getApplications(); 1952 } 1953 1954 return $this->formattedData['applications']; 1955 } 1956 1957 /** 1958 * Get formatted value maps. 1959 * 1960 * @return array 1961 */ 1962 protected function getFormattedValueMaps() { 1963 if (!isset($this->formattedData['valueMaps'])) { 1964 $this->formattedData['valueMaps'] = $this->adapter->getValueMaps(); 1965 } 1966 1967 return $this->formattedData['valueMaps']; 1968 } 1969 1970 /** 1971 * Get formatted items. 1972 * 1973 * @return array 1974 */ 1975 protected function getFormattedItems() { 1976 if (!isset($this->formattedData['items'])) { 1977 $this->formattedData['items'] = $this->adapter->getItems(); 1978 } 1979 1980 return $this->formattedData['items']; 1981 } 1982 1983 /** 1984 * Get formatted discovery rules. 1985 * 1986 * @return array 1987 */ 1988 protected function getFormattedDiscoveryRules() { 1989 if (!isset($this->formattedData['discoveryRules'])) { 1990 $this->formattedData['discoveryRules'] = $this->adapter->getDiscoveryRules(); 1991 1992 foreach ($this->formattedData['discoveryRules'] as &$discoveryRules) { 1993 foreach ($discoveryRules as &$discoveryRule) { 1994 foreach ($discoveryRule['trigger_prototypes'] as &$triggerPrototype) { 1995 $triggerPrototype['parsedExpressions'] = $this->parseTriggerExpression($triggerPrototype['expression']); 1996 } 1997 unset($triggerPrototype); 1998 } 1999 unset($discoveryRule); 2000 } 2001 unset($discoveryRules); 2002 } 2003 2004 return $this->formattedData['discoveryRules']; 2005 } 2006 2007 /** 2008 * Get formatted triggers. 2009 * 2010 * @return array 2011 */ 2012 protected function getFormattedTriggers() { 2013 if (!isset($this->formattedData['triggers'])) { 2014 $this->formattedData['triggers'] = $this->adapter->getTriggers(); 2015 2016 foreach ($this->formattedData['triggers'] as &$trigger) { 2017 $trigger['parsedExpressions'] = $this->parseTriggerExpression($trigger['expression']); 2018 } 2019 unset($trigger); 2020 } 2021 2022 return $this->formattedData['triggers']; 2023 } 2024 2025 /** 2026 * Parses a trigger expression and returns an array of used hosts and items. 2027 * 2028 * @param string $expression 2029 * 2030 * @return array 2031 * 2032 * @throws Exception 2033 */ 2034 protected function parseTriggerExpression($expression) { 2035 $expressions = []; 2036 2037 $result = $this->triggerExpression->parse($expression); 2038 if (!$result) { 2039 throw new Exception($this->triggerExpression->error); 2040 } 2041 2042 foreach ($result->getTokensByType(CTriggerExpressionParserResult::TOKEN_TYPE_FUNCTION_MACRO) as $token) { 2043 $expressions[] = [ 2044 'host' => $token['data']['host'], 2045 'item' => $token['data']['item'], 2046 ]; 2047 } 2048 2049 return $expressions; 2050 } 2051 2052 /** 2053 * Get formatted graphs. 2054 * 2055 * @return array 2056 */ 2057 protected function getFormattedGraphs() { 2058 if (!isset($this->formattedData['graphs'])) { 2059 $this->formattedData['graphs'] = $this->adapter->getGraphs(); 2060 } 2061 2062 return $this->formattedData['graphs']; 2063 } 2064 2065 /** 2066 * Get formatted images. 2067 * 2068 * @return array 2069 */ 2070 protected function getFormattedImages() { 2071 if (!isset($this->formattedData['images'])) { 2072 $this->formattedData['images'] = $this->adapter->getImages(); 2073 } 2074 2075 return $this->formattedData['images']; 2076 } 2077 2078 /** 2079 * Get formatted maps. 2080 * 2081 * @return array 2082 */ 2083 protected function getFormattedMaps() { 2084 if (!isset($this->formattedData['maps'])) { 2085 $this->formattedData['maps'] = $this->adapter->getMaps(); 2086 } 2087 2088 return $this->formattedData['maps']; 2089 } 2090 2091 /** 2092 * Get formatted screens. 2093 * 2094 * @return array 2095 */ 2096 protected function getFormattedScreens() { 2097 if (!isset($this->formattedData['screens'])) { 2098 $this->formattedData['screens'] = $this->adapter->getScreens(); 2099 } 2100 2101 return $this->formattedData['screens']; 2102 } 2103 2104 /** 2105 * Get formatted template screens. 2106 * 2107 * @return array 2108 */ 2109 protected function getFormattedTemplateScreens() { 2110 if (!isset($this->formattedData['templateScreens'])) { 2111 $this->formattedData['templateScreens'] = $this->adapter->getTemplateScreens(); 2112 } 2113 2114 return $this->formattedData['templateScreens']; 2115 } 2116} 2117