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 containing methods for operations with host prototypes. 24 */ 25class CHostPrototype extends CHostBase { 26 27 protected $sortColumns = ['hostid', 'host', 'name', 'status']; 28 29 public function __construct() { 30 parent::__construct(); 31 32 $this->getOptions = array_merge($this->getOptions, [ 33 'discoveryids' => null, 34 'inherited' => null, 35 'selectDiscoveryRule' => null, 36 'selectGroupLinks' => null, 37 'selectGroupPrototypes' => null, 38 'selectParentHost' => null, 39 'selectTemplates' => null, 40 'selectInventory' => null, 41 'editable' => false, 42 'nopermissions' => null, 43 'sortfield' => '', 44 'sortorder' => '' 45 ]); 46 } 47 48 /** 49 * Get host prototypes. 50 * 51 * @param array $options 52 * 53 * @return array 54 */ 55 public function get(array $options) { 56 $options = zbx_array_merge($this->getOptions, $options); 57 $options['filter']['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE; 58 59 // build and execute query 60 $sql = $this->createSelectQuery($this->tableName(), $options); 61 $res = DBselect($sql, $options['limit']); 62 63 // fetch results 64 $result = []; 65 while ($row = DBfetch($res)) { 66 // a count query, return a single result 67 if ($options['countOutput']) { 68 if ($options['groupCount']) { 69 $result[] = $row; 70 } 71 else { 72 $result = $row['rowscount']; 73 } 74 } 75 // a normal select query 76 else { 77 $result[$row[$this->pk()]] = $row; 78 } 79 } 80 81 if ($options['countOutput']) { 82 return $result; 83 } 84 85 if ($result) { 86 $result = $this->addRelatedObjects($options, $result); 87 $result = $this->unsetExtraFields($result, ['triggerid'], $options['output']); 88 } 89 90 if (!$options['preservekeys']) { 91 $result = zbx_cleanHashes($result); 92 } 93 94 return $result; 95 } 96 97 /** 98 * Check for duplicated names. 99 * 100 * @param string $field_name 101 * @param array $names_by_ruleid 102 * 103 * @throws APIException if host prototype with same name already exists. 104 */ 105 private function checkDuplicates($field_name, array $names_by_ruleid) { 106 $sql_where = []; 107 foreach ($names_by_ruleid as $ruleid => $names) { 108 $sql_where[] = '(i.itemid='.$ruleid.' AND '.dbConditionString('h.'.$field_name, $names).')'; 109 } 110 111 $db_host_prototypes = DBfetchArray(DBselect( 112 'SELECT i.name AS rule,h.'.$field_name. 113 ' FROM items i,host_discovery hd,hosts h'. 114 ' WHERE i.itemid=hd.parent_itemid'. 115 ' AND hd.hostid=h.hostid'. 116 ' AND ('.implode(' OR ', $sql_where).')', 117 1 118 )); 119 120 if ($db_host_prototypes) { 121 $error = ($field_name === 'host') 122 ? _('Host prototype with host name "%1$s" already exists in discovery rule "%2$s".') 123 : _('Host prototype with visible name "%1$s" already exists in discovery rule "%2$s".'); 124 125 self::exception(ZBX_API_ERROR_PARAMETERS, 126 sprintf($error, $db_host_prototypes[0][$field_name], $db_host_prototypes[0]['rule']) 127 ); 128 } 129 } 130 131 /** 132 * Validates the input parameters for the create() method. 133 * 134 * @throws APIException if the input is invalid. 135 * 136 * @param array $host_prototypes 137 */ 138 protected function validateCreate(array &$host_prototypes) { 139 $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['ruleid', 'host'], ['ruleid', 'name']], 'fields' => [ 140 'ruleid' => ['type' => API_ID, 'flags' => API_REQUIRED], 141 'host' => ['type' => API_H_NAME, 'flags' => API_REQUIRED | API_REQUIRED_LLD_MACRO, 'length' => DB::getFieldLength('hosts', 'host')], 142 'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('hosts', 'name'), 'default_source' => 'host'], 143 'status' => ['type' => API_INT32, 'in' => implode(',', [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])], 144 'groupLinks' => ['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'uniq' => [['groupid']], 'fields' => [ 145 'groupid' => ['type' => API_ID, 'flags' => API_REQUIRED] 146 ]], 147 'groupPrototypes' => ['type' => API_OBJECTS, 'uniq' => [['name']], 'fields' => [ 148 'name' => ['type' => API_HG_NAME, 'flags' => API_REQUIRED | API_REQUIRED_LLD_MACRO, 'length' => DB::getFieldLength('hstgrp', 'name')] 149 ]], 150 'inventory' => ['type' => API_OBJECT, 'fields' => [ 151 'inventory_mode' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [HOST_INVENTORY_DISABLED, HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC])] 152 ]], 153 'templates' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [ 154 'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED] 155 ]] 156 ]]; 157 if (!CApiInputValidator::validate($api_input_rules, $host_prototypes, '/', $error)) { 158 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 159 } 160 161 $hosts_by_ruleid = []; 162 $names_by_ruleid = []; 163 $groupids = []; 164 165 foreach ($host_prototypes as $host_prototype) { 166 // Collect host group ID links for latter validation. 167 foreach ($host_prototype['groupLinks'] as $group_prototype) { 168 $groupids[$group_prototype['groupid']] = true; 169 } 170 171 $hosts_by_ruleid[$host_prototype['ruleid']][] = $host_prototype['host']; 172 $names_by_ruleid[$host_prototype['ruleid']][] = $host_prototype['name']; 173 } 174 175 $ruleids = array_unique(zbx_objectValues($host_prototypes, 'ruleid')); 176 $groupids = array_keys($groupids); 177 178 $this->checkDiscoveryRulePermissions($ruleids); 179 $this->checkHostGroupsPermissions($groupids); 180 181 // Check if the host is discovered. 182 $db_discovered_hosts = DBfetchArray(DBselect( 183 'SELECT h.host'. 184 ' FROM items i,hosts h'. 185 ' WHERE i.hostid=h.hostid'. 186 ' AND '.dbConditionInt('i.itemid', $ruleids). 187 ' AND h.flags='.ZBX_FLAG_DISCOVERY_CREATED, 188 1 189 )); 190 191 if ($db_discovered_hosts) { 192 self::exception(ZBX_API_ERROR_PARAMETERS, 193 _s('Cannot create a host prototype on a discovered host "%1$s".', $db_discovered_hosts[0]['host']) 194 ); 195 } 196 197 $this->checkDuplicates('host', $hosts_by_ruleid); 198 $this->checkDuplicates('name', $names_by_ruleid); 199 } 200 201 /** 202 * Creates the given host prototypes. 203 * 204 * @param array $host_prototypes 205 * 206 * @return array 207 */ 208 public function create(array $host_prototypes) { 209 // 'templateid' validation happens during linkage. 210 $this->validateCreate($host_prototypes); 211 212 // Merge groups into group prototypes. 213 foreach ($host_prototypes as &$host_prototype) { 214 $host_prototype['groupPrototypes'] = array_merge( 215 array_key_exists('groupPrototypes', $host_prototype) ? $host_prototype['groupPrototypes'] : [], 216 $host_prototype['groupLinks'] 217 ); 218 unset($host_prototype['groupLinks']); 219 } 220 unset($host_prototype); 221 222 $host_prototypes = $this->createReal($host_prototypes); 223 $this->inherit($host_prototypes); 224 225 $this->addAuditBulk(AUDIT_ACTION_ADD, AUDIT_RESOURCE_HOST_PROTOTYPE, $host_prototypes); 226 227 return ['hostids' => zbx_objectValues($host_prototypes, 'hostid')]; 228 } 229 230 /** 231 * Creates the host prototypes and inherits them to linked hosts and templates. 232 * 233 * @param array $hostPrototypes 234 * 235 * @return array an array of host prototypes with host IDs 236 */ 237 protected function createReal(array $hostPrototypes) { 238 foreach ($hostPrototypes as &$hostPrototype) { 239 $hostPrototype['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE; 240 } 241 unset($hostPrototype); 242 243 // save the host prototypes 244 $hostPrototypeIds = DB::insert($this->tableName(), $hostPrototypes); 245 246 $groupPrototypes = []; 247 $hostPrototypeDiscoveryRules = []; 248 $hostPrototypeInventory = []; 249 foreach ($hostPrototypes as $key => $hostPrototype) { 250 $hostPrototypes[$key]['hostid'] = $hostPrototype['hostid'] = $hostPrototypeIds[$key]; 251 252 // save group prototypes 253 foreach ($hostPrototype['groupPrototypes'] as $groupPrototype) { 254 $groupPrototype['hostid'] = $hostPrototype['hostid']; 255 $groupPrototypes[] = $groupPrototype; 256 } 257 258 // discovery rules 259 $hostPrototypeDiscoveryRules[] = [ 260 'hostid' => $hostPrototype['hostid'], 261 'parent_itemid' => $hostPrototype['ruleid'] 262 ]; 263 264 // inventory 265 if (isset($hostPrototype['inventory']['inventory_mode']) 266 && ($hostPrototype['inventory']['inventory_mode'] == HOST_INVENTORY_MANUAL 267 || $hostPrototype['inventory']['inventory_mode'] == HOST_INVENTORY_AUTOMATIC)) { 268 $hostPrototypeInventory[] = [ 269 'hostid' => $hostPrototype['hostid'], 270 'inventory_mode' => $hostPrototype['inventory']['inventory_mode'] 271 ]; 272 } 273 } 274 275 // save group prototypes 276 $groupPrototypes = DB::save('group_prototype', $groupPrototypes); 277 $i = 0; 278 foreach ($hostPrototypes as &$hostPrototype) { 279 foreach ($hostPrototype['groupPrototypes'] as &$groupPrototype) { 280 $groupPrototype['group_prototypeid'] = $groupPrototypes[$i]['group_prototypeid']; 281 $i++; 282 } 283 unset($groupPrototype); 284 } 285 unset($hostPrototype); 286 287 // link host prototypes to discovery rules 288 DB::insert('host_discovery', $hostPrototypeDiscoveryRules, false); 289 290 // save inventory 291 DB::insert('host_inventory', $hostPrototypeInventory, false); 292 293 // link templates 294 foreach ($hostPrototypes as $hostPrototype) { 295 if (isset($hostPrototype['templates']) && $hostPrototype['templates']) { 296 $this->link(zbx_objectValues($hostPrototype['templates'], 'templateid'), [$hostPrototype['hostid']]); 297 } 298 } 299 300 return $hostPrototypes; 301 } 302 303 /** 304 * Validates the input parameters for the update() method. 305 * 306 * @throws APIException if the input is invalid. 307 * 308 * @param array $host_prototypes 309 * @param array $db_host_prototypes 310 */ 311 protected function validateUpdate(array &$host_prototypes, array &$db_host_prototypes = null) { 312 $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['hostid']], 'fields' => [ 313 'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED], 314 'host' => ['type' => API_H_NAME, 'flags' => API_REQUIRED_LLD_MACRO, 'length' => DB::getFieldLength('hosts', 'host')], 315 'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('hosts', 'name')], 316 'status' => ['type' => API_INT32, 'in' => implode(',', [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])], 317 'groupLinks' => ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY, 'uniq' => [['group_prototypeid'], ['groupid']], 'fields' => [ 318 'group_prototypeid' => ['type' => API_ID], 319 'groupid' => ['type' => API_ID] 320 ]], 321 'groupPrototypes' => ['type' => API_OBJECTS, 'uniq' => [['group_prototypeid'], ['name']], 'fields' => [ 322 'group_prototypeid' => ['type' => API_ID], 323 'name' => ['type' => API_HG_NAME, 'flags' => API_REQUIRED_LLD_MACRO, 'length' => DB::getFieldLength('hstgrp', 'name')] 324 ]], 325 'inventory' => ['type' => API_OBJECT, 'fields' => [ 326 'inventory_mode' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [HOST_INVENTORY_DISABLED, HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC])] 327 ]], 328 'templates' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [ 329 'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED] 330 ]] 331 ]]; 332 if (!CApiInputValidator::validate($api_input_rules, $host_prototypes, '/', $error)) { 333 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 334 } 335 336 $db_host_prototypes = $this->get([ 337 'output' => ['hostid', 'host', 'name', 'status'], 338 'selectDiscoveryRule' => ['itemid'], 339 'selectGroupLinks' => ['group_prototypeid', 'groupid'], 340 'selectGroupPrototypes' => ['group_prototypeid', 'name'], 341 'hostids' => zbx_objectValues($host_prototypes, 'hostid'), 342 'editable' => true, 343 'preservekeys' => true 344 ]); 345 346 $hosts_by_ruleid = []; 347 $names_by_ruleid = []; 348 349 foreach ($host_prototypes as &$host_prototype) { 350 // Check if this host prototype exists. 351 if (!array_key_exists($host_prototype['hostid'], $db_host_prototypes)) { 352 self::exception(ZBX_API_ERROR_PERMISSIONS, 353 _('No permissions to referred object or it does not exist!') 354 ); 355 } 356 357 $db_host_prototype = $db_host_prototypes[$host_prototype['hostid']]; 358 $host_prototype['ruleid'] = $db_host_prototype['discoveryRule']['itemid']; 359 360 if (array_key_exists('host', $host_prototype) && $host_prototype['host'] !== $db_host_prototype['host']) { 361 $hosts_by_ruleid[$host_prototype['ruleid']][] = $host_prototype['host']; 362 } 363 364 if (array_key_exists('name', $host_prototype) && $host_prototype['name'] !== $db_host_prototype['name']) { 365 $names_by_ruleid[$host_prototype['ruleid']][] = $host_prototype['name']; 366 } 367 } 368 unset($host_prototype); 369 370 $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['ruleid', 'host'], ['ruleid', 'name']]]; 371 if (!CApiInputValidator::validateUniqueness($api_input_rules, $host_prototypes, '/', $error)) { 372 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 373 } 374 375 $groupids = []; 376 $db_groupids = []; 377 378 foreach ($host_prototypes as $host_prototype) { 379 $db_host_prototype = $db_host_prototypes[$host_prototype['hostid']]; 380 381 foreach ($db_host_prototype['groupLinks'] as $db_group_link) { 382 $db_groupids[$db_group_link['groupid']] = true; 383 } 384 385 $db_group_links = zbx_toHash($db_host_prototype['groupLinks'], 'group_prototypeid'); 386 $db_group_prototypes = zbx_toHash($db_host_prototype['groupPrototypes'], 'group_prototypeid'); 387 388 // Validate 'group_prototypeid' in 'groupLinks' property. 389 if (array_key_exists('groupLinks', $host_prototype)) { 390 foreach ($host_prototype['groupLinks'] as $group_link) { 391 if (!$group_link) { 392 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); 393 } 394 395 // Don't allow invalid 'group_prototypeid' parameters which do not belong to this 'hostid'. 396 if (array_key_exists('group_prototypeid', $group_link) 397 && !array_key_exists($group_link['group_prototypeid'], $db_group_links)) { 398 self::exception(ZBX_API_ERROR_PERMISSIONS, 399 _('No permissions to referred object or it does not exist!') 400 ); 401 } 402 403 if (array_key_exists('groupid', $group_link)) { 404 $groupids[$group_link['groupid']] = true; 405 } 406 } 407 } 408 409 // Validate 'group_prototypeid' in 'groupPrototypes' property. 410 if (array_key_exists('groupPrototypes', $host_prototype)) { 411 foreach ($host_prototype['groupPrototypes'] as $group_prototype) { 412 if (!$group_prototype) { 413 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); 414 } 415 416 // Don't allow invalid 'group_prototypeid' parameters which do not belong to this 'hostid'. 417 if (array_key_exists('group_prototypeid', $group_prototype) 418 && !array_key_exists($group_prototype['group_prototypeid'], $db_group_prototypes)) { 419 self::exception(ZBX_API_ERROR_PERMISSIONS, 420 _('No permissions to referred object or it does not exist!') 421 ); 422 } 423 } 424 } 425 } 426 427 // Collect only new given groupids for validation. 428 $groupids = array_diff_key($groupids, $db_groupids); 429 430 if ($groupids) { 431 $this->checkHostGroupsPermissions(array_keys($groupids)); 432 } 433 434 $host_prototypes = $this->extendObjectsByKey($host_prototypes, $db_host_prototypes, 'hostid', 435 ['host', 'name', 'groupLinks', 'groupPrototypes'] 436 ); 437 438 if ($hosts_by_ruleid) { 439 $this->checkDuplicates('host', $hosts_by_ruleid); 440 } 441 if ($names_by_ruleid) { 442 $this->checkDuplicates('name', $names_by_ruleid); 443 } 444 } 445 446 /** 447 * Updates the given host prototypes. 448 * 449 * @param array $host_prototypes 450 * 451 * @return array 452 */ 453 public function update(array $host_prototypes) { 454 $this->validateUpdate($host_prototypes, $db_host_prototypes); 455 456 // merge group links into group prototypes 457 foreach ($host_prototypes as &$host_prototype) { 458 $host_prototype['groupPrototypes'] = 459 array_merge($host_prototype['groupPrototypes'], $host_prototype['groupLinks']); 460 unset($host_prototype['groupLinks']); 461 } 462 unset($host_prototype); 463 464 $host_prototypes = $this->updateReal($host_prototypes); 465 $this->inherit($host_prototypes); 466 467 foreach ($db_host_prototypes as &$db_host_prototype) { 468 unset($db_host_prototype['discoveryRule'], $db_host_prototype['groupLinks'], 469 $db_host_prototype['groupPrototypes'] 470 ); 471 } 472 unset($db_host_prototype); 473 474 $this->addAuditBulk(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_HOST_PROTOTYPE, $host_prototypes, $db_host_prototypes); 475 476 return ['hostids' => zbx_objectValues($host_prototypes, 'hostid')]; 477 } 478 479 /** 480 * Updates the host prototypes and propagates the changes to linked hosts and templates. 481 * 482 * @param array $hostPrototypes 483 * 484 * @return array 485 */ 486 protected function updateReal(array $hostPrototypes) { 487 // save the host prototypes 488 foreach ($hostPrototypes as $hostPrototype) { 489 DB::updateByPk($this->tableName(), $hostPrototype['hostid'], $hostPrototype); 490 } 491 492 $exHostPrototypes = $this->get([ 493 'output' => ['hostid'], 494 'selectGroupLinks' => API_OUTPUT_EXTEND, 495 'selectGroupPrototypes' => API_OUTPUT_EXTEND, 496 'selectTemplates' => ['templateid'], 497 'selectInventory' => API_OUTPUT_EXTEND, 498 'hostids' => zbx_objectValues($hostPrototypes, 'hostid'), 499 'preservekeys' => true 500 ]); 501 502 // update related objects 503 $inventoryCreate = []; 504 $inventoryDeleteIds = []; 505 foreach ($hostPrototypes as $key => $hostPrototype) { 506 $exHostPrototype = $exHostPrototypes[$hostPrototype['hostid']]; 507 508 // group prototypes 509 if (isset($hostPrototype['groupPrototypes'])) { 510 foreach ($hostPrototype['groupPrototypes'] as &$groupPrototype) { 511 $groupPrototype['hostid'] = $hostPrototype['hostid']; 512 } 513 unset($groupPrototype); 514 515 // save group prototypes 516 $exGroupPrototypes = zbx_toHash( 517 array_merge($exHostPrototype['groupLinks'], $exHostPrototype['groupPrototypes']), 518 'group_prototypeid' 519 ); 520 $modifiedGroupPrototypes = []; 521 foreach ($hostPrototype['groupPrototypes'] as $groupPrototype) { 522 if (isset($groupPrototype['group_prototypeid'])) { 523 unset($exGroupPrototypes[$groupPrototype['group_prototypeid']]); 524 } 525 526 $modifiedGroupPrototypes[] = $groupPrototype; 527 } 528 if ($exGroupPrototypes) { 529 $this->deleteGroupPrototypes(array_keys($exGroupPrototypes)); 530 } 531 $hostPrototypes[$key]['groupPrototypes'] = DB::save('group_prototype', $modifiedGroupPrototypes); 532 } 533 534 // templates 535 if (isset($hostPrototype['templates'])) { 536 $existingTemplateIds = zbx_objectValues($exHostPrototype['templates'], 'templateid'); 537 $newTemplateIds = zbx_objectValues($hostPrototype['templates'], 'templateid'); 538 $this->unlink(array_diff($existingTemplateIds, $newTemplateIds), [$hostPrototype['hostid']]); 539 $this->link(array_diff($newTemplateIds, $existingTemplateIds), [$hostPrototype['hostid']]); 540 } 541 542 // inventory 543 if (isset($hostPrototype['inventory']) ) { 544 $inventory = zbx_array_mintersect(['inventory_mode'], $hostPrototype['inventory']); 545 546 if (array_key_exists('inventory_mode', $inventory) 547 && ($inventory['inventory_mode'] == HOST_INVENTORY_MANUAL 548 || $inventory['inventory_mode'] == HOST_INVENTORY_AUTOMATIC)) { 549 550 if ($exHostPrototype['inventory']['inventory_mode'] != HOST_INVENTORY_DISABLED) { 551 DB::update('host_inventory', [ 552 'values' => $inventory, 553 'where' => ['hostid' => $hostPrototype['hostid']] 554 ]); 555 } 556 else { 557 $inventoryCreate[] = $inventory + ['hostid' => $hostPrototype['hostid']]; 558 } 559 } 560 else { 561 $inventoryDeleteIds[] = $hostPrototype['hostid']; 562 } 563 } 564 } 565 566 // save inventory 567 DB::insert('host_inventory', $inventoryCreate, false); 568 DB::delete('host_inventory', ['hostid' => $inventoryDeleteIds]); 569 570 return $hostPrototypes; 571 } 572 573 /** 574 * Updates the children of the host prototypes on the given hosts and propagates the inheritance to the child hosts. 575 * 576 * @param array $hostPrototypes array of host prototypes to inherit 577 * @param array $hostids array of hosts to inherit to; if set to null, the children will be updated on all 578 * child hosts 579 * 580 * @return bool 581 */ 582 protected function inherit(array $hostPrototypes, array $hostids = null) { 583 if (empty($hostPrototypes)) { 584 return true; 585 } 586 587 // prepare the child host prototypes 588 $newHostPrototypes = $this->prepareInheritedObjects($hostPrototypes, $hostids); 589 if (!$newHostPrototypes) { 590 return true; 591 } 592 593 $insertHostPrototypes = []; 594 $updateHostPrototypes = []; 595 foreach ($newHostPrototypes as $newHostPrototype) { 596 if (isset($newHostPrototype['hostid'])) { 597 $updateHostPrototypes[] = $newHostPrototype; 598 } 599 else { 600 $insertHostPrototypes[] = $newHostPrototype; 601 } 602 } 603 604 // save the new host prototypes 605 if (!zbx_empty($insertHostPrototypes)) { 606 $insertHostPrototypes = $this->createReal($insertHostPrototypes); 607 } 608 609 if (!zbx_empty($updateHostPrototypes)) { 610 $updateHostPrototypes = $this->updateReal($updateHostPrototypes); 611 } 612 613 $host_prototypes = array_merge($updateHostPrototypes, $insertHostPrototypes); 614 615 if ($host_prototypes) { 616 $sql = 'SELECT hd.hostid'. 617 ' FROM host_discovery hd,items i,hosts h'. 618 ' WHERE hd.parent_itemid=i.itemid'. 619 ' AND i.hostid=h.hostid'. 620 ' AND h.status='.HOST_STATUS_TEMPLATE. 621 ' AND '.dbConditionInt('hd.hostid', zbx_objectValues($host_prototypes, 'hostid')); 622 $valid_prototypes = DBfetchArrayAssoc(DBselect($sql), 'hostid'); 623 624 foreach ($host_prototypes as $key => $host_prototype) { 625 if (!array_key_exists($host_prototype['hostid'], $valid_prototypes)) { 626 unset($host_prototypes[$key]); 627 } 628 } 629 } 630 631 // propagate the inheritance to the children 632 return $this->inherit($host_prototypes); 633 } 634 635 636 /** 637 * Prepares and returns an array of child host prototypes, inherited from host prototypes $hostPrototypes 638 * on the given hosts. 639 * 640 * Each host prototype must have the "ruleid" parameter set. 641 * 642 * @param array $hostPrototypes 643 * @param array $hostIds 644 * 645 * @return array an array of unsaved child host prototypes 646 */ 647 protected function prepareInheritedObjects(array $hostPrototypes, array $hostIds = null) { 648 // fetch the related discovery rules with their hosts 649 $discoveryRules = API::DiscoveryRule()->get([ 650 'output' => ['itemid', 'hostid'], 651 'selectHosts' => ['hostid'], 652 'itemids' => zbx_objectValues($hostPrototypes, 'ruleid'), 653 'templated' => true, 654 'nopermissions' => true, 655 'preservekeys' => true 656 ]); 657 658 // fetch all child hosts to inherit to 659 // do not inherit host prototypes on discovered hosts 660 $chdHosts = API::Host()->get([ 661 'output' => ['hostid', 'host', 'status'], 662 'selectParentTemplates' => ['templateid'], 663 'templateids' => zbx_objectValues($discoveryRules, 'hostid'), 664 'hostids' => $hostIds, 665 'nopermissions' => true, 666 'templated_hosts' => true, 667 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL] 668 ]); 669 if (empty($chdHosts)) { 670 return []; 671 } 672 673 // fetch the child discovery rules 674 $childDiscoveryRules = API::DiscoveryRule()->get([ 675 'output' => ['itemid', 'templateid', 'hostid'], 676 'preservekeys' => true, 677 'filter' => [ 678 'templateid' => array_keys($discoveryRules) 679 ] 680 ]); 681 682 // fetch child host prototypes and group them by discovery rule 683 $childHostPrototypes = API::HostPrototype()->get([ 684 'output' => ['hostid', 'host', 'templateid'], 685 'selectGroupLinks' => API_OUTPUT_EXTEND, 686 'selectGroupPrototypes' => API_OUTPUT_EXTEND, 687 'selectDiscoveryRule' => ['itemid'], 688 'discoveryids' => zbx_objectValues($childDiscoveryRules, 'itemid') 689 ]); 690 foreach ($childDiscoveryRules as &$childDiscoveryRule) { 691 $childDiscoveryRule['hostPrototypes'] = []; 692 } 693 unset($childDiscoveryRule); 694 foreach ($childHostPrototypes as $childHostPrototype) { 695 $discoveryRuleId = $childHostPrototype['discoveryRule']['itemid']; 696 unset($childHostPrototype['discoveryRule']); 697 698 $childDiscoveryRules[$discoveryRuleId]['hostPrototypes'][] = $childHostPrototype; 699 } 700 701 // match each discovery that the parent host prototypes belong to to the child discovery rule for each host 702 $discoveryRuleChildren = []; 703 foreach ($childDiscoveryRules as $childRule) { 704 $discoveryRuleChildren[$childRule['templateid']][$childRule['hostid']] = $childRule['itemid']; 705 } 706 707 $newHostPrototypes = []; 708 foreach ($chdHosts as $host) { 709 $hostId = $host['hostid']; 710 711 // skip items not from parent templates of current host 712 $templateIds = zbx_toHash($host['parentTemplates'], 'templateid'); 713 $parentHostPrototypes = []; 714 foreach ($hostPrototypes as $inum => $parentHostPrototype) { 715 $parentTemplateId = $discoveryRules[$parentHostPrototype['ruleid']]['hostid']; 716 717 if (isset($templateIds[$parentTemplateId])) { 718 $parentHostPrototypes[$inum] = $parentHostPrototype; 719 } 720 } 721 722 foreach ($parentHostPrototypes as $parentHostPrototype) { 723 $childDiscoveryRuleId = $discoveryRuleChildren[$parentHostPrototype['ruleid']][$hostId]; 724 $exHostPrototype = null; 725 726 // check if the child discovery rule already has host prototypes 727 $exHostPrototypes = $childDiscoveryRules[$childDiscoveryRuleId]['hostPrototypes']; 728 if ($exHostPrototypes) { 729 $exHostPrototypesHosts = zbx_toHash($exHostPrototypes, 'host'); 730 $exHostPrototypesTemplateIds = zbx_toHash($exHostPrototypes, 'templateid'); 731 732 // look for an already created inherited host prototype 733 // if one exists - update it 734 if (isset($exHostPrototypesTemplateIds[$parentHostPrototype['hostid']])) { 735 $exHostPrototype = $exHostPrototypesTemplateIds[$parentHostPrototype['hostid']]; 736 737 // check if there's a host prototype on the target host with the same host name but from a different template 738 // or no template 739 if (isset($exHostPrototypesHosts[$parentHostPrototype['host']]) 740 && !idcmp($exHostPrototypesHosts[$parentHostPrototype['host']]['templateid'], $parentHostPrototype['hostid'])) { 741 742 $discoveryRule = DBfetch(DBselect('SELECT i.name FROM items i WHERE i.itemid='.zbx_dbstr($exHostPrototype['discoveryRule']['itemid']))); 743 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Host prototype "%1$s" already exists on "%2$s".', $parentHostPrototype['host'], $discoveryRule['name'])); 744 } 745 } 746 747 // look for a host prototype with the same host name 748 // if one exists - convert it to an inherited host prototype 749 if (isset($exHostPrototypesHosts[$parentHostPrototype['host']])) { 750 $exHostPrototype = $exHostPrototypesHosts[$parentHostPrototype['host']]; 751 752 // check that this host prototype is not inherited from a different template 753 if ($exHostPrototype['templateid'] > 0 && !idcmp($exHostPrototype['templateid'], $parentHostPrototype['hostid'])) { 754 $discoveryRule = DBfetch(DBselect('SELECT i.name FROM items i WHERE i.itemid='.zbx_dbstr($exHostPrototype['discoveryRule']['itemid']))); 755 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Host prototype "%1$s" already exists on "%2$s", inherited from another template.', $parentHostPrototype['host'], $discoveryRule['name'])); 756 } 757 } 758 } 759 760 // copy host prototype 761 $newHostPrototype = $parentHostPrototype; 762 $newHostPrototype['ruleid'] = $discoveryRuleChildren[$parentHostPrototype['ruleid']][$hostId]; 763 $newHostPrototype['templateid'] = $parentHostPrototype['hostid']; 764 765 // update an existing inherited host prototype 766 if ($exHostPrototype) { 767 // look for existing group prototypes to update 768 $exGroupPrototypesByTemplateId = zbx_toHash($exHostPrototype['groupPrototypes'], 'templateid'); 769 $exGroupPrototypesByName = zbx_toHash($exHostPrototype['groupPrototypes'], 'name'); 770 $exGroupPrototypesByGroupId = zbx_toHash($exHostPrototype['groupLinks'], 'groupid'); 771 772 // look for a group prototype that can be updated 773 foreach ($newHostPrototype['groupPrototypes'] as &$groupPrototype) { 774 // updated an inherited item prototype by templateid 775 if (isset($exGroupPrototypesByTemplateId[$groupPrototype['group_prototypeid']])) { 776 $groupPrototype['group_prototypeid'] = $exGroupPrototypesByTemplateId[$groupPrototype['group_prototypeid']]['group_prototypeid']; 777 } 778 // updated an inherited item prototype by name 779 elseif (isset($groupPrototype['name']) && !zbx_empty($groupPrototype['name']) 780 && isset($exGroupPrototypesByName[$groupPrototype['name']])) { 781 782 $groupPrototype['templateid'] = $groupPrototype['group_prototypeid']; 783 $groupPrototype['group_prototypeid'] = $exGroupPrototypesByName[$groupPrototype['name']]['group_prototypeid']; 784 } 785 // updated an inherited item prototype by group ID 786 elseif (isset($groupPrototype['groupid']) && $groupPrototype['groupid'] 787 && isset($exGroupPrototypesByGroupId[$groupPrototype['groupid']])) { 788 789 $groupPrototype['templateid'] = $groupPrototype['group_prototypeid']; 790 $groupPrototype['group_prototypeid'] = $exGroupPrototypesByGroupId[$groupPrototype['groupid']]['group_prototypeid']; 791 } 792 // create a new child group prototype 793 else { 794 $groupPrototype['templateid'] = $groupPrototype['group_prototypeid']; 795 unset($groupPrototype['group_prototypeid']); 796 } 797 798 unset($groupPrototype['hostid']); 799 } 800 unset($groupPrototype); 801 802 $newHostPrototype['hostid'] = $exHostPrototype['hostid']; 803 } 804 // create a new inherited host prototype 805 else { 806 foreach ($newHostPrototype['groupPrototypes'] as &$groupPrototype) { 807 $groupPrototype['templateid'] = $groupPrototype['group_prototypeid']; 808 unset($groupPrototype['group_prototypeid'], $groupPrototype['hostid']); 809 } 810 unset($groupPrototype); 811 812 unset($newHostPrototype['hostid']); 813 } 814 $newHostPrototypes[] = $newHostPrototype; 815 } 816 } 817 818 return $newHostPrototypes; 819 } 820 821 /** 822 * Inherits all host prototypes from the templates given in "templateids" to hosts or templates given in "hostids". 823 * 824 * @param array $data 825 * 826 * @return bool 827 */ 828 public function syncTemplates(array $data) { 829 $data['templateids'] = zbx_toArray($data['templateids']); 830 $data['hostids'] = zbx_toArray($data['hostids']); 831 832 $discoveryRules = API::DiscoveryRule()->get([ 833 'output' => ['itemid'], 834 'hostids' => $data['templateids'] 835 ]); 836 $hostPrototypes = $this->get([ 837 'discoveryids' => zbx_objectValues($discoveryRules, 'itemid'), 838 'preservekeys' => true, 839 'output' => API_OUTPUT_EXTEND, 840 'selectGroupLinks' => API_OUTPUT_EXTEND, 841 'selectGroupPrototypes' => API_OUTPUT_EXTEND, 842 'selectTemplates' => ['templateid'], 843 'selectDiscoveryRule' => ['itemid'], 844 'selectInventory' => ['inventory_mode'] 845 ]); 846 847 foreach ($hostPrototypes as &$hostPrototype) { 848 // merge group links into group prototypes 849 foreach ($hostPrototype['groupLinks'] as $group) { 850 $hostPrototype['groupPrototypes'][] = $group; 851 } 852 unset($hostPrototype['groupLinks']); 853 854 // the ID of the discovery rule must be passed in the "ruleid" parameter 855 $hostPrototype['ruleid'] = $hostPrototype['discoveryRule']['itemid']; 856 unset($hostPrototype['discoveryRule']); 857 } 858 unset($hostPrototype); 859 860 $this->inherit($hostPrototypes, $data['hostids']); 861 862 return true; 863 } 864 865 /** 866 * Validates the input parameters for the delete() method. 867 * 868 * @throws APIException if the input is invalid 869 * 870 * @param array $hostPrototypeIds 871 * @param bool $nopermissions 872 */ 873 protected function validateDelete($hostPrototypeIds, $nopermissions) { 874 if (!$hostPrototypeIds) { 875 self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); 876 } 877 878 if (!$nopermissions) { 879 $this->checkHostPrototypePermissions($hostPrototypeIds); 880 $this->checkNotInherited($hostPrototypeIds); 881 } 882 } 883 884 /** 885 * Delete host prototypes. 886 * 887 * @param array $hostPrototypeIds 888 * @param bool $nopermissions if set to true, permission and template checks will be skipped 889 * 890 * @return array 891 */ 892 public function delete(array $hostPrototypeIds, $nopermissions = false) { 893 $this->validateDelete($hostPrototypeIds, $nopermissions); 894 895 // include child IDs 896 $parentHostPrototypeIds = $hostPrototypeIds; 897 $childHostPrototypeIds = []; 898 do { 899 $query = DBselect('SELECT h.hostid FROM hosts h WHERE '.dbConditionInt('h.templateid', $parentHostPrototypeIds)); 900 $parentHostPrototypeIds = []; 901 while ($hostPrototype = DBfetch($query)) { 902 $parentHostPrototypeIds[] = $hostPrototype['hostid']; 903 $childHostPrototypeIds[] = $hostPrototype['hostid']; 904 } 905 } while (!empty($parentHostPrototypeIds)); 906 907 $hostPrototypeIds = array_merge($hostPrototypeIds, $childHostPrototypeIds); 908 909 // Lock host prototypes before delete to prevent server from adding new LLD hosts. 910 DBselect( 911 'SELECT NULL'. 912 ' FROM hosts h'. 913 ' WHERE '.dbConditionInt('h.hostid', $hostPrototypeIds). 914 ' FOR UPDATE' 915 ); 916 917 $deleteHostPrototypes = $this->get([ 918 'hostids' => $hostPrototypeIds, 919 'output' => ['host'], 920 'selectGroupPrototypes' => ['group_prototypeid'], 921 'selectParentHost' => ['host'], 922 'nopermissions' => true 923 ]); 924 925 // delete discovered hosts 926 $discoveredHosts = DBfetchArray(DBselect( 927 'SELECT hostid FROM host_discovery WHERE '.dbConditionInt('parent_hostid', $hostPrototypeIds) 928 )); 929 if ($discoveredHosts) { 930 API::Host()->delete(zbx_objectValues($discoveredHosts, 'hostid'), true); 931 } 932 933 // delete group prototypes and discovered groups 934 $groupPrototypeIds = []; 935 foreach ($deleteHostPrototypes as $groupPrototype) { 936 foreach ($groupPrototype['groupPrototypes'] as $groupPrototype) { 937 $groupPrototypeIds[] = $groupPrototype['group_prototypeid']; 938 } 939 } 940 $this->deleteGroupPrototypes($groupPrototypeIds); 941 942 // delete host prototypes 943 DB::delete($this->tableName(), ['hostid' => $hostPrototypeIds]); 944 945 // TODO: REMOVE info 946 foreach ($deleteHostPrototypes as $hostProtototype) { 947 info(_s('Deleted: Host prototype "%1$s" on "%2$s".', $hostProtototype['host'], $hostProtototype['parentHost']['host'])); 948 } 949 950 return ['hostids' => $hostPrototypeIds]; 951 } 952 953 protected function link(array $templateids, array $targetids) { 954 $this->checkHostPrototypePermissions($targetids); 955 956 $links = parent::link($templateids, $targetids); 957 958 foreach ($targetids as $targetid) { 959 $linked_templates = API::Template()->get([ 960 'output' => [], 961 'hostids' => [$targetid], 962 'nopermissions' => true 963 ]); 964 965 $result = DBselect( 966 'SELECT i.key_,count(*)'. 967 ' FROM items i'. 968 ' WHERE '.dbConditionInt('i.hostid', array_merge($templateids, array_keys($linked_templates))). 969 ' GROUP BY i.key_'. 970 ' HAVING count(*)>1', 971 1 972 ); 973 if ($row = DBfetch($result)) { 974 $target_templates = API::HostPrototype()->get([ 975 'output' => ['name'], 976 'hostids' => [$targetid], 977 'nopermissions' => true 978 ]); 979 980 self::exception(ZBX_API_ERROR_PARAMETERS, 981 _s('Item "%1$s" already exists on "%2$s", inherited from another template.', $row['key_'], 982 $target_templates[0]['name'] 983 ) 984 ); 985 } 986 } 987 988 return $links; 989 } 990 991 /** 992 * Checks if the current user has access to the given LLD rules. 993 * 994 * @throws APIException if the user doesn't have write permissions for the given LLD rules 995 * 996 * @param array $ruleids 997 */ 998 protected function checkDiscoveryRulePermissions(array $ruleids) { 999 $count = API::DiscoveryRule()->get([ 1000 'countOutput' => true, 1001 'itemids' => $ruleids, 1002 'editable' => true 1003 ]); 1004 1005 if ($count != count($ruleids)) { 1006 self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!')); 1007 } 1008 } 1009 1010 /** 1011 * Checks if the current user has access to the given host groups. 1012 * 1013 * @throws APIException if the user doesn't have write permissions for the given host groups 1014 * 1015 * @param array $groupids 1016 */ 1017 protected function checkHostGroupsPermissions(array $groupids) { 1018 $db_groups = API::HostGroup()->get([ 1019 'output' => ['name', 'flags'], 1020 'groupids' => $groupids, 1021 'editable' => true, 1022 'preservekeys' => true 1023 ]); 1024 1025 foreach ($groupids as $groupid) { 1026 if (!array_key_exists($groupid, $db_groups)) { 1027 self::exception(ZBX_API_ERROR_PERMISSIONS, 1028 _('No permissions to referred object or it does not exist!') 1029 ); 1030 } 1031 1032 $db_group = $db_groups[$groupid]; 1033 1034 // Check if group prototypes use discovered host groups. 1035 if ($db_group['flags'] == ZBX_FLAG_DISCOVERY_CREATED) { 1036 self::exception(ZBX_API_ERROR_PARAMETERS, 1037 _s('Group prototype cannot be based on a discovered host group "%1$s".', $db_group['name']) 1038 ); 1039 } 1040 } 1041 } 1042 1043 /** 1044 * Checks if the current user has access to the given host prototypes. 1045 * 1046 * @throws APIException if the user doesn't have write permissions for the host prototypes. 1047 * 1048 * @param array $hostPrototypeIds 1049 */ 1050 protected function checkHostPrototypePermissions(array $hostPrototypeIds) { 1051 if ($hostPrototypeIds) { 1052 $hostPrototypeIds = array_unique($hostPrototypeIds); 1053 1054 $count = $this->get([ 1055 'countOutput' => true, 1056 'hostids' => $hostPrototypeIds, 1057 'editable' => true 1058 ]); 1059 1060 if ($count != count($hostPrototypeIds)) { 1061 self::exception(ZBX_API_ERROR_PERMISSIONS, 1062 _('No permissions to referred object or it does not exist!') 1063 ); 1064 } 1065 } 1066 } 1067 1068 /** 1069 * Checks if the given host prototypes are not inherited from a template. 1070 * 1071 * @throws APIException if at least one host prototype is inherited 1072 * 1073 * @param array $hostPrototypeIds 1074 */ 1075 protected function checkNotInherited(array $hostPrototypeIds) { 1076 $query = DBSelect('SELECT hostid FROM hosts h WHERE h.templateid>0 AND '.dbConditionInt('h.hostid', $hostPrototypeIds), 1); 1077 1078 if ($hostPrototype = DBfetch($query)) { 1079 self::exception(ZBX_API_ERROR_PERMISSIONS, _('Cannot delete templated host prototype.')); 1080 } 1081 } 1082 1083 protected function applyQueryFilterOptions($tableName, $tableAlias, array $options, array $sqlParts) { 1084 $sqlParts = parent::applyQueryFilterOptions($tableName, $tableAlias, $options, $sqlParts); 1085 1086 // do not return host prototypes from discovered hosts 1087 $sqlParts['from'][] = 'host_discovery hd'; 1088 $sqlParts['from'][] = 'items i'; 1089 $sqlParts['from'][] = 'hosts ph'; 1090 $sqlParts['where'][] = $this->fieldId('hostid').'=hd.hostid'; 1091 $sqlParts['where'][] = 'hd.parent_itemid=i.itemid'; 1092 $sqlParts['where'][] = 'i.hostid=ph.hostid'; 1093 $sqlParts['where'][] = 'ph.flags='.ZBX_FLAG_DISCOVERY_NORMAL; 1094 1095 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) { 1096 $permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ; 1097 1098 $sqlParts['where'][] = 'EXISTS ('. 1099 'SELECT NULL'. 1100 ' FROM '. 1101 'host_discovery hd,items i,hosts_groups hgg'. 1102 ' JOIN rights r'. 1103 ' ON r.id=hgg.groupid'. 1104 ' AND '.dbConditionInt('r.groupid', getUserGroupsByUserId(self::$userData['userid'])). 1105 ' WHERE h.hostid=hd.hostid'. 1106 ' AND hd.parent_itemid=i.itemid'. 1107 ' AND i.hostid=hgg.hostid'. 1108 ' GROUP BY hgg.hostid'. 1109 ' HAVING MIN(r.permission)>'.PERM_DENY. 1110 ' AND MAX(r.permission)>='.zbx_dbstr($permission). 1111 ')'; 1112 } 1113 1114 // discoveryids 1115 if ($options['discoveryids'] !== null) { 1116 $sqlParts['where'][] = dbConditionInt('hd.parent_itemid', (array) $options['discoveryids']); 1117 1118 if ($options['groupCount']) { 1119 $sqlParts['group']['hd'] = 'hd.parent_itemid'; 1120 } 1121 } 1122 1123 // inherited 1124 if ($options['inherited'] !== null) { 1125 $sqlParts['where'][] = ($options['inherited']) ? 'h.templateid IS NOT NULL' : 'h.templateid IS NULL'; 1126 } 1127 1128 return $sqlParts; 1129 } 1130 1131 /** 1132 * Retrieves and adds additional requested data to the result set. 1133 * 1134 * @param array $options 1135 * @param array $result 1136 * 1137 * @return array 1138 */ 1139 protected function addRelatedObjects(array $options, array $result) { 1140 $result = parent::addRelatedObjects($options, $result); 1141 1142 $hostPrototypeIds = array_keys($result); 1143 1144 // adding discovery rule 1145 if ($options['selectDiscoveryRule'] !== null && $options['selectDiscoveryRule'] != API_OUTPUT_COUNT) { 1146 $relationMap = $this->createRelationMap($result, 'hostid', 'parent_itemid', 'host_discovery'); 1147 $discoveryRules = API::DiscoveryRule()->get([ 1148 'output' => $options['selectDiscoveryRule'], 1149 'itemids' => $relationMap->getRelatedIds(), 1150 'nopermissions' => true, 1151 'preservekeys' => true 1152 ]); 1153 $result = $relationMap->mapOne($result, $discoveryRules, 'discoveryRule'); 1154 } 1155 1156 // adding group links 1157 if ($options['selectGroupLinks'] !== null && $options['selectGroupLinks'] != API_OUTPUT_COUNT) { 1158 $groupPrototypes = DBFetchArray(DBselect( 1159 'SELECT hg.group_prototypeid,hg.hostid'. 1160 ' FROM group_prototype hg'. 1161 ' WHERE '.dbConditionInt('hg.hostid', $hostPrototypeIds). 1162 ' AND hg.groupid IS NOT NULL' 1163 )); 1164 $relationMap = $this->createRelationMap($groupPrototypes, 'hostid', 'group_prototypeid'); 1165 $groupPrototypes = API::getApiService()->select('group_prototype', [ 1166 'output' => $options['selectGroupLinks'], 1167 'group_prototypeids' => $relationMap->getRelatedIds(), 1168 'preservekeys' => true 1169 ]); 1170 foreach ($groupPrototypes as &$groupPrototype) { 1171 unset($groupPrototype['name']); 1172 } 1173 unset($groupPrototype); 1174 $result = $relationMap->mapMany($result, $groupPrototypes, 'groupLinks'); 1175 } 1176 1177 // adding group prototypes 1178 if ($options['selectGroupPrototypes'] !== null && $options['selectGroupPrototypes'] != API_OUTPUT_COUNT) { 1179 $groupPrototypes = DBFetchArray(DBselect( 1180 'SELECT hg.group_prototypeid,hg.hostid'. 1181 ' FROM group_prototype hg'. 1182 ' WHERE '.dbConditionInt('hg.hostid', $hostPrototypeIds). 1183 ' AND hg.groupid IS NULL' 1184 )); 1185 $relationMap = $this->createRelationMap($groupPrototypes, 'hostid', 'group_prototypeid'); 1186 $groupPrototypes = API::getApiService()->select('group_prototype', [ 1187 'output' => $options['selectGroupPrototypes'], 1188 'group_prototypeids' => $relationMap->getRelatedIds(), 1189 'preservekeys' => true 1190 ]); 1191 foreach ($groupPrototypes as &$groupPrototype) { 1192 unset($groupPrototype['groupid']); 1193 } 1194 unset($groupPrototype); 1195 $result = $relationMap->mapMany($result, $groupPrototypes, 'groupPrototypes'); 1196 } 1197 1198 // adding host 1199 if ($options['selectParentHost'] !== null && $options['selectParentHost'] != API_OUTPUT_COUNT) { 1200 $relationMap = new CRelationMap(); 1201 $dbRules = DBselect( 1202 'SELECT hd.hostid,i.hostid AS parent_hostid'. 1203 ' FROM host_discovery hd,items i'. 1204 ' WHERE '.dbConditionInt('hd.hostid', $hostPrototypeIds). 1205 ' AND hd.parent_itemid=i.itemid' 1206 ); 1207 while ($relation = DBfetch($dbRules)) { 1208 $relationMap->addRelation($relation['hostid'], $relation['parent_hostid']); 1209 } 1210 1211 $hosts = API::Host()->get([ 1212 'output' => $options['selectParentHost'], 1213 'hostids' => $relationMap->getRelatedIds(), 1214 'templated_hosts' => true, 1215 'nopermissions' => true, 1216 'preservekeys' => true 1217 ]); 1218 $result = $relationMap->mapOne($result, $hosts, 'parentHost'); 1219 } 1220 1221 // adding templates 1222 if ($options['selectTemplates'] !== null) { 1223 if ($options['selectTemplates'] != API_OUTPUT_COUNT) { 1224 $relationMap = $this->createRelationMap($result, 'hostid', 'templateid', 'hosts_templates'); 1225 $templates = API::Template()->get([ 1226 'output' => $options['selectTemplates'], 1227 'templateids' => $relationMap->getRelatedIds(), 1228 'preservekeys' => true 1229 ]); 1230 $result = $relationMap->mapMany($result, $templates, 'templates'); 1231 } 1232 else { 1233 $templates = API::Template()->get([ 1234 'hostids' => $hostPrototypeIds, 1235 'countOutput' => true, 1236 'groupCount' => true 1237 ]); 1238 $templates = zbx_toHash($templates, 'hostid'); 1239 foreach ($result as $hostid => $host) { 1240 $result[$hostid]['templates'] = array_key_exists($hostid, $templates) 1241 ? $templates[$hostid]['rowscount'] 1242 : '0'; 1243 } 1244 } 1245 } 1246 1247 // adding inventory 1248 if ($options['selectInventory'] !== null) { 1249 $inventory = API::getApiService()->select('host_inventory', [ 1250 'output' => ['hostid', 'inventory_mode'], 1251 'filter' => ['hostid' => $hostPrototypeIds], 1252 'preservekeys' => true 1253 ]); 1254 1255 foreach ($hostPrototypeIds as $host_prototypeid) { 1256 // There is no DB record if inventory mode is HOST_INVENTORY_DISABLED. 1257 if (!array_key_exists($host_prototypeid, $inventory)) { 1258 $inventory[$host_prototypeid] = [ 1259 'hostid' => (string) $host_prototypeid, 1260 'inventory_mode' => (string) HOST_INVENTORY_DISABLED 1261 ]; 1262 } 1263 } 1264 1265 $relation_map = $this->createRelationMap($result, 'hostid', 'hostid'); 1266 $inventory = $this->unsetExtraFields($inventory, ['hostid', 'inventory_mode'], $options['selectInventory']); 1267 $result = $relation_map->mapOne($result, $inventory, 'inventory'); 1268 } 1269 1270 return $result; 1271 } 1272 1273 /** 1274 * Deletes the given group prototype and all discovered groups. 1275 * Deletes also group prototype children. 1276 * 1277 * @param array $groupPrototypeIds 1278 */ 1279 protected function deleteGroupPrototypes(array $groupPrototypeIds) { 1280 // Lock group prototypes before delete to prevent server from adding new LLD elements. 1281 DBselect( 1282 'SELECT NULL'. 1283 ' FROM group_prototype gp'. 1284 ' WHERE '.dbConditionInt('gp.group_prototypeid', $groupPrototypeIds). 1285 ' FOR UPDATE' 1286 ); 1287 1288 // delete child group prototypes 1289 $groupPrototypeChildren = DBfetchArray(DBselect( 1290 'SELECT gp.group_prototypeid FROM group_prototype gp WHERE '.dbConditionInt('templateid', $groupPrototypeIds) 1291 )); 1292 if ($groupPrototypeChildren) { 1293 $this->deleteGroupPrototypes(zbx_objectValues($groupPrototypeChildren, 'group_prototypeid')); 1294 } 1295 1296 // delete discovered groups 1297 $hostGroups = DBfetchArray(DBselect( 1298 'SELECT groupid FROM group_discovery WHERE '.dbConditionInt('parent_group_prototypeid', $groupPrototypeIds) 1299 )); 1300 if ($hostGroups) { 1301 API::HostGroup()->delete(zbx_objectValues($hostGroups, 'groupid'), true); 1302 } 1303 1304 // delete group prototypes 1305 DB::delete('group_prototype', ['group_prototypeid' => $groupPrototypeIds]); 1306 } 1307} 1308