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 groups. 24 * 25 * @package API 26 */ 27class CHostGroup extends CApiService { 28 29 protected $tableName = 'groups'; 30 protected $tableAlias = 'g'; 31 protected $sortColumns = ['groupid', 'name']; 32 33 /** 34 * Get host groups. 35 * 36 * @param array $params 37 * 38 * @return array 39 */ 40 public function get($params) { 41 $result = []; 42 43 $sqlParts = [ 44 'select' => ['groups' => 'g.groupid'], 45 'from' => ['groups' => 'groups g'], 46 'where' => [], 47 'order' => [], 48 'limit' => null 49 ]; 50 51 $defOptions = [ 52 'groupids' => null, 53 'hostids' => null, 54 'templateids' => null, 55 'graphids' => null, 56 'triggerids' => null, 57 'maintenanceids' => null, 58 'monitored_hosts' => null, 59 'templated_hosts' => null, 60 'real_hosts' => null, 61 'with_hosts_and_templates' => null, 62 'with_items' => null, 63 'with_simple_graph_items' => null, 64 'with_monitored_items' => null, 65 'with_triggers' => null, 66 'with_monitored_triggers' => null, 67 'with_httptests' => null, 68 'with_monitored_httptests' => null, 69 'with_graphs' => null, 70 'with_applications' => null, 71 'editable' => false, 72 'nopermissions' => null, 73 // filter 74 'filter' => null, 75 'search' => null, 76 'searchByAny' => null, 77 'startSearch' => null, 78 'excludeSearch' => null, 79 'searchWildcardsEnabled' => null, 80 // output 81 'output' => API_OUTPUT_EXTEND, 82 'selectHosts' => null, 83 'selectTemplates' => null, 84 'selectGroupDiscovery' => null, 85 'selectDiscoveryRule' => null, 86 'countOutput' => null, 87 'groupCount' => null, 88 'preservekeys' => null, 89 'sortfield' => '', 90 'sortorder' => '', 91 'limit' => null, 92 'limitSelects' => null 93 ]; 94 $options = zbx_array_merge($defOptions, $params); 95 96 // editable + PERMISSION CHECK 97 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) { 98 $permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ; 99 $userGroups = getUserGroupsByUserId(self::$userData['userid']); 100 101 $sqlParts['where'][] = 'EXISTS ('. 102 'SELECT NULL'. 103 ' FROM rights r'. 104 ' WHERE g.groupid=r.id'. 105 ' AND '.dbConditionInt('r.groupid', $userGroups). 106 ' GROUP BY r.id'. 107 ' HAVING MIN(r.permission)>'.PERM_DENY. 108 ' AND MAX(r.permission)>='.zbx_dbstr($permission). 109 ')'; 110 } 111 112 // groupids 113 if (!is_null($options['groupids'])) { 114 zbx_value2array($options['groupids']); 115 $sqlParts['where']['groupid'] = dbConditionInt('g.groupid', $options['groupids']); 116 } 117 118 // templateids 119 if (!is_null($options['templateids'])) { 120 zbx_value2array($options['templateids']); 121 122 if (!is_null($options['hostids'])) { 123 zbx_value2array($options['hostids']); 124 $options['hostids'] = array_merge($options['hostids'], $options['templateids']); 125 } 126 else { 127 $options['hostids'] = $options['templateids']; 128 } 129 } 130 131 // hostids 132 if (!is_null($options['hostids'])) { 133 zbx_value2array($options['hostids']); 134 135 $sqlParts['from']['hosts_groups'] = 'hosts_groups hg'; 136 $sqlParts['where'][] = dbConditionInt('hg.hostid', $options['hostids']); 137 $sqlParts['where']['hgg'] = 'hg.groupid=g.groupid'; 138 } 139 140 // triggerids 141 if (!is_null($options['triggerids'])) { 142 zbx_value2array($options['triggerids']); 143 144 $sqlParts['from']['hosts_groups'] = 'hosts_groups hg'; 145 $sqlParts['from']['functions'] = 'functions f'; 146 $sqlParts['from']['items'] = 'items i'; 147 $sqlParts['where'][] = dbConditionInt('f.triggerid', $options['triggerids']); 148 $sqlParts['where']['fi'] = 'f.itemid=i.itemid'; 149 $sqlParts['where']['hgi'] = 'hg.hostid=i.hostid'; 150 $sqlParts['where']['hgg'] = 'hg.groupid=g.groupid'; 151 } 152 153 // graphids 154 if (!is_null($options['graphids'])) { 155 zbx_value2array($options['graphids']); 156 157 $sqlParts['from']['gi'] = 'graphs_items gi'; 158 $sqlParts['from']['i'] = 'items i'; 159 $sqlParts['from']['hg'] = 'hosts_groups hg'; 160 $sqlParts['where'][] = dbConditionInt('gi.graphid', $options['graphids']); 161 $sqlParts['where']['hgg'] = 'hg.groupid=g.groupid'; 162 $sqlParts['where']['igi'] = 'i.itemid=gi.itemid'; 163 $sqlParts['where']['hgi'] = 'hg.hostid=i.hostid'; 164 } 165 166 // maintenanceids 167 if (!is_null($options['maintenanceids'])) { 168 zbx_value2array($options['maintenanceids']); 169 170 $sqlParts['from']['maintenances_groups'] = 'maintenances_groups mg'; 171 $sqlParts['where'][] = dbConditionInt('mg.maintenanceid', $options['maintenanceids']); 172 $sqlParts['where']['hmh'] = 'g.groupid=mg.groupid'; 173 } 174 175 $sub_sql_parts = array(); 176 177 // monitored_hosts, real_hosts, templated_hosts, with_hosts_and_templates 178 if ($options['monitored_hosts'] !== null) { 179 $sub_sql_parts['from']['h'] = 'hosts h'; 180 $sub_sql_parts['where']['hg-h'] = 'hg.hostid=h.hostid'; 181 $sub_sql_parts['where'][] = dbConditionInt('h.status', array(HOST_STATUS_MONITORED)); 182 } 183 elseif ($options['real_hosts'] !== null) { 184 $sub_sql_parts['from']['h'] = 'hosts h'; 185 $sub_sql_parts['where']['hg-h'] = 'hg.hostid=h.hostid'; 186 $sub_sql_parts['where'][] = dbConditionInt('h.status', 187 array(HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED) 188 ); 189 } 190 elseif ($options['templated_hosts'] !== null) { 191 $sub_sql_parts['from']['h'] = 'hosts h'; 192 $sub_sql_parts['where']['hg-h'] = 'hg.hostid=h.hostid'; 193 $sub_sql_parts['where'][] = dbConditionInt('h.status', array(HOST_STATUS_TEMPLATE)); 194 } 195 elseif ($options['with_hosts_and_templates'] !== null) { 196 $sub_sql_parts['from']['h'] = 'hosts h'; 197 $sub_sql_parts['where']['hg-h'] = 'hg.hostid=h.hostid'; 198 $sub_sql_parts['where'][] = dbConditionInt('h.status', 199 array(HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED, HOST_STATUS_TEMPLATE) 200 ); 201 } 202 203 // with_items, with_monitored_items, with_simple_graph_items 204 if ($options['with_items'] !== null) { 205 $sub_sql_parts['from']['i'] = 'items i'; 206 $sub_sql_parts['where']['hg-i'] = 'hg.hostid=i.hostid'; 207 $sub_sql_parts['where'][] = dbConditionInt('i.flags', 208 array(ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED) 209 ); 210 } 211 elseif ($options['with_monitored_items'] !== null) { 212 $sub_sql_parts['from']['i'] = 'items i'; 213 $sub_sql_parts['from']['h'] = 'hosts h'; 214 $sub_sql_parts['where']['hg-i'] = 'hg.hostid=i.hostid'; 215 $sub_sql_parts['where']['hg-h'] = 'hg.hostid=h.hostid'; 216 $sub_sql_parts['where'][] = dbConditionInt('h.status', array(HOST_STATUS_MONITORED)); 217 $sub_sql_parts['where'][] = dbConditionInt('i.status', array(ITEM_STATUS_ACTIVE)); 218 $sub_sql_parts['where'][] = dbConditionInt('i.flags', 219 array(ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED) 220 ); 221 } 222 elseif ($options['with_simple_graph_items'] !== null) { 223 $sub_sql_parts['from']['i'] = 'items i'; 224 $sub_sql_parts['where']['hg-i'] = 'hg.hostid=i.hostid'; 225 $sub_sql_parts['where'][] = dbConditionInt('i.value_type', 226 array(ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64) 227 ); 228 $sub_sql_parts['where'][] = dbConditionInt('i.status', array(ITEM_STATUS_ACTIVE)); 229 $sub_sql_parts['where'][] = dbConditionInt('i.flags', 230 array(ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED) 231 ); 232 } 233 234 // with_triggers, with_monitored_triggers 235 if ($options['with_triggers'] !== null) { 236 $sub_sql_parts['from']['i'] = 'items i'; 237 $sub_sql_parts['from']['f'] = 'functions f'; 238 $sub_sql_parts['from']['t'] = 'triggers t'; 239 $sub_sql_parts['where']['hg-i'] = 'hg.hostid=i.hostid'; 240 $sub_sql_parts['where']['i-f'] = 'i.itemid=f.itemid'; 241 $sub_sql_parts['where']['f-t'] = 'f.triggerid=t.triggerid'; 242 $sub_sql_parts['where'][] = dbConditionInt('t.flags', 243 array(ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED) 244 ); 245 } 246 elseif ($options['with_monitored_triggers'] !== null) { 247 $sub_sql_parts['from']['i'] = 'items i'; 248 $sub_sql_parts['from']['h'] = 'hosts h'; 249 $sub_sql_parts['from']['f'] = 'functions f'; 250 $sub_sql_parts['from']['t'] = 'triggers t'; 251 $sub_sql_parts['where']['hg-i'] = 'hg.hostid=i.hostid'; 252 $sub_sql_parts['where']['hg-h'] = 'hg.hostid=h.hostid'; 253 $sub_sql_parts['where']['i-f'] = 'i.itemid=f.itemid'; 254 $sub_sql_parts['where']['f-t'] = 'f.triggerid=t.triggerid'; 255 $sub_sql_parts['where'][] = dbConditionInt('h.status', array(HOST_STATUS_MONITORED)); 256 $sub_sql_parts['where'][] = dbConditionInt('i.status', array(ITEM_STATUS_ACTIVE)); 257 $sub_sql_parts['where'][] = dbConditionInt('t.status', array(TRIGGER_STATUS_ENABLED)); 258 $sub_sql_parts['where'][] = dbConditionInt('t.flags', 259 array(ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED) 260 ); 261 } 262 263 // with_httptests, with_monitored_httptests 264 if ($options['with_httptests'] !== null) { 265 $sub_sql_parts['from']['ht'] = 'httptest ht'; 266 $sub_sql_parts['where']['hg-ht'] = 'hg.hostid=ht.hostid'; 267 } 268 elseif ($options['with_monitored_httptests'] !== null) { 269 $sub_sql_parts['from']['ht'] = 'httptest ht'; 270 $sub_sql_parts['where']['hg-ht'] = 'hg.hostid=ht.hostid'; 271 $sub_sql_parts['where'][] = dbConditionInt('ht.status', array(HTTPTEST_STATUS_ACTIVE)); 272 } 273 274 // with_graphs 275 if ($options['with_graphs'] !== null) { 276 $sub_sql_parts['from']['i'] = 'items i'; 277 $sub_sql_parts['from']['gi'] = 'graphs_items gi'; 278 $sub_sql_parts['from']['gr'] = 'graphs gr'; 279 $sub_sql_parts['where']['hg-i'] = 'hg.hostid=i.hostid'; 280 $sub_sql_parts['where']['i-gi'] = 'i.itemid=gi.itemid'; 281 $sub_sql_parts['where']['gi-gr'] = 'gi.graphid=gr.graphid'; 282 $sub_sql_parts['where'][] = dbConditionInt('gr.flags', 283 array(ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_CREATED) 284 ); 285 } 286 287 // with_applications 288 if ($options['with_applications'] !== null) { 289 $sub_sql_parts['from']['a'] = 'applications a'; 290 $sub_sql_parts['where']['hg-a'] = 'hg.hostid=a.hostid'; 291 } 292 293 if ($sub_sql_parts) { 294 $sub_sql_parts['from']['hg'] = 'hosts_groups hg'; 295 $sub_sql_parts['where']['g-hg'] = 'g.groupid=hg.groupid'; 296 297 $sqlParts['where'][] = 'EXISTS ('. 298 'SELECT NULL'. 299 ' FROM '.implode(',', $sub_sql_parts['from']). 300 ' WHERE '.implode(' AND ', array_unique($sub_sql_parts['where'])). 301 ')'; 302 } 303 304 // filter 305 if (is_array($options['filter'])) { 306 $this->dbFilter('groups g', $options, $sqlParts); 307 } 308 309 // search 310 if (is_array($options['search'])) { 311 zbx_db_search('groups g', $options, $sqlParts); 312 } 313 314 // limit 315 if (zbx_ctype_digit($options['limit']) && $options['limit']) { 316 $sqlParts['limit'] = $options['limit']; 317 } 318 319 $sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 320 $sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 321 $res = DBselect($this->createSelectQueryFromParts($sqlParts), $sqlParts['limit']); 322 while ($group = DBfetch($res)) { 323 if (!is_null($options['countOutput'])) { 324 if (!is_null($options['groupCount'])) { 325 $result[] = $group; 326 } 327 else { 328 $result = $group['rowscount']; 329 } 330 } 331 else { 332 $result[$group['groupid']] = $group; 333 } 334 } 335 336 if (!is_null($options['countOutput'])) { 337 return $result; 338 } 339 340 if ($result) { 341 $result = $this->addRelatedObjects($options, $result); 342 } 343 344 // removing keys (hash -> array) 345 if (is_null($options['preservekeys'])) { 346 $result = zbx_cleanHashes($result); 347 } 348 return $result; 349 } 350 351 /** 352 * Create host groups. 353 * 354 * @param array $groups array with host group names 355 * @param array $groups['name'] 356 * 357 * @return array 358 */ 359 public function create(array $groups) { 360 $groups = zbx_toArray($groups); 361 362 if (USER_TYPE_SUPER_ADMIN != self::$userData['type']) { 363 self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can create host groups.')); 364 } 365 366 foreach ($groups as $group) { 367 if (!isset($group['name']) || zbx_empty($group['name'])) { 368 self::exception(ZBX_API_ERROR_PARAMETERS, _('Host group name cannot be empty.')); 369 } 370 $this->checkNoParameters( 371 $group, 372 ['internal'], 373 _('Cannot set "%1$s" for host group "%2$s".'), 374 $group['name'] 375 ); 376 } 377 378 // check host name duplicates in passed parameters 379 $collectionValidator = new CCollectionValidator([ 380 'uniqueField' => 'name', 381 'messageDuplicate' => _('Host group "%1$s" already exists.') 382 ]); 383 $this->checkValidator($groups, $collectionValidator); 384 385 // check host name duplicates in DB 386 $dbHostGroups = API::getApiService()->select($this->tableName(), [ 387 'output' => ['name'], 388 'filter' => ['name' => zbx_objectValues($groups, 'name')], 389 'limit' => 1 390 ]); 391 392 if ($dbHostGroups) { 393 $dbHostGroup = reset($dbHostGroups); 394 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Host group "%1$s" already exists.', $dbHostGroup['name'])); 395 } 396 397 $groupids = DB::insert('groups', $groups); 398 399 return ['groupids' => $groupids]; 400 } 401 402 /** 403 * Update host groups. 404 * 405 * @param array $groups 406 * @param array $groups[0]['name'], ... 407 * @param array $groups[0]['groupid'], ... 408 * 409 * @return boolean 410 */ 411 public function update(array $groups) { 412 $groups = zbx_toArray($groups); 413 $groupids = zbx_objectValues($groups, 'groupid'); 414 415 if (empty($groups)) { 416 self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); 417 } 418 419 // permissions 420 $updGroups = $this->get([ 421 'output' => ['groupid', 'flags', 'name'], 422 'groupids' => $groupids, 423 'editable' => true, 424 'preservekeys' => true 425 ]); 426 foreach ($groups as $group) { 427 if (!isset($updGroups[$group['groupid']])) { 428 self::exception(ZBX_API_ERROR_PERMISSIONS, 429 _('No permissions to referred object or it does not exist!') 430 ); 431 } 432 $this->checkNoParameters( 433 $group, 434 ['internal'], 435 _('Cannot update "%1$s" for host group "%2$s".'), 436 isset($group['name']) ? $group['name'] : $updGroups[$group['groupid']]['name'] 437 ); 438 } 439 440 // name duplicate check 441 $groupsNames = $this->get([ 442 'filter' => ['name' => zbx_objectValues($groups, 'name')], 443 'output' => ['groupid', 'name'], 444 'editable' => true, 445 'nopermissions' => true 446 ]); 447 $groupsNames = zbx_toHash($groupsNames, 'name'); 448 449 $updateDiscoveredValidator = new CUpdateDiscoveredValidator([ 450 'messageAllowed' => _('Cannot update a discovered host group.') 451 ]); 452 453 $update = []; 454 foreach ($groups as $group) { 455 if (isset($group['name'])) { 456 if (zbx_empty($group['name'])) { 457 self::exception(ZBX_API_ERROR_PARAMETERS, _('Host group name cannot be empty.')); 458 } 459 460 // cannot update discovered host groups 461 $this->checkPartialValidator($group, $updateDiscoveredValidator, $updGroups[$group['groupid']]); 462 463 if (isset($groupsNames[$group['name']]) 464 && !idcmp($groupsNames[$group['name']]['groupid'], $group['groupid'])) { 465 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Host group "%1$s" already exists.', $group['name'])); 466 } 467 468 $update[] = [ 469 'values' => ['name' => $group['name']], 470 'where' => ['groupid' => $group['groupid']] 471 ]; 472 } 473 474 // prevents updating several groups with same name 475 $groupsNames[$group['name']] = ['groupid' => $group['groupid']]; 476 } 477 478 DB::update('groups', $update); 479 480 return ['groupids' => $groupids]; 481 } 482 483 /** 484 * Delete host groups. 485 * 486 * @param array $groupids 487 * @param bool $nopermissions 488 * 489 * @return array 490 */ 491 public function delete(array $groupids, $nopermissions = false) { 492 if (empty($groupids)) { 493 self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); 494 } 495 sort($groupids); 496 497 $delGroups = $this->get([ 498 'groupids' => $groupids, 499 'editable' => true, 500 'output' => ['groupid', 'name', 'internal'], 501 'selectHosts' => ['hostid', 'host'], 502 'selectTemplates' => ['templateid', 'host'], 503 'preservekeys' => true, 504 'nopermissions' => $nopermissions 505 ]); 506 foreach ($groupids as $groupid) { 507 if (!isset($delGroups[$groupid])) { 508 self::exception(ZBX_API_ERROR_PERMISSIONS, 509 _('No permissions to referred object or it does not exist!') 510 ); 511 } 512 if ($delGroups[$groupid]['internal'] == ZBX_INTERNAL_GROUP) { 513 self::exception(ZBX_API_ERROR_PARAMETERS, 514 _s('Host group "%1$s" is internal and can not be deleted.', $delGroups[$groupid]['name'])); 515 } 516 } 517 518 // check if a group is used in a group prototype 519 $groupPrototype = DBFetch(DBselect( 520 'SELECT groupid'. 521 ' FROM group_prototype gp'. 522 ' WHERE '.dbConditionInt('groupid', $groupids), 523 1 524 )); 525 if ($groupPrototype) { 526 self::exception(ZBX_API_ERROR_PARAMETERS, 527 _s('Group "%1$s" cannot be deleted, because it is used by a host prototype.', 528 $delGroups[$groupPrototype['groupid']]['name'] 529 ) 530 ); 531 } 532 533 $hosts_to_unlink = []; 534 $templates_to_unlink = []; 535 536 foreach ($delGroups as $group) { 537 foreach ($group['hosts'] as $host) { 538 $hosts_to_unlink[] = $host; 539 } 540 541 foreach ($group['templates'] as $template) { 542 $templates_to_unlink[] = $template; 543 } 544 } 545 546 $this->verifyHostsAndTemplatesAreUnlinkable($hosts_to_unlink, $templates_to_unlink, $groupids); 547 548 $dbScripts = API::Script()->get([ 549 'groupids' => $groupids, 550 'output' => ['scriptid', 'groupid'], 551 'nopermissions' => true 552 ]); 553 554 if (!empty($dbScripts)) { 555 foreach ($dbScripts as $script) { 556 if ($script['groupid'] == 0) { 557 continue; 558 } 559 self::exception(ZBX_API_ERROR_PARAMETERS, 560 _s('Host group "%1$s" cannot be deleted, because it is used in a global script.', 561 $delGroups[$script['groupid']]['name'] 562 ) 563 ); 564 } 565 } 566 567 // delete screens items 568 $resources = [ 569 SCREEN_RESOURCE_HOSTGROUP_TRIGGERS, 570 SCREEN_RESOURCE_HOSTS_INFO, 571 SCREEN_RESOURCE_TRIGGERS_INFO, 572 SCREEN_RESOURCE_TRIGGERS_OVERVIEW, 573 SCREEN_RESOURCE_DATA_OVERVIEW 574 ]; 575 DB::delete('screens_items', [ 576 'resourceid' => $groupids, 577 'resourcetype' => $resources 578 ]); 579 580 // delete sysmap element 581 if (!empty($groupids)) { 582 DB::delete('sysmaps_elements', ['elementtype' => SYSMAP_ELEMENT_TYPE_HOST_GROUP, 'elementid' => $groupids]); 583 } 584 585 // disable actions 586 // actions from conditions 587 $actionids = []; 588 $dbActions = DBselect( 589 'SELECT DISTINCT c.actionid'. 590 ' FROM conditions c'. 591 ' WHERE c.conditiontype='.CONDITION_TYPE_HOST_GROUP. 592 ' AND '.dbConditionString('c.value', $groupids) 593 ); 594 while ($dbAction = DBfetch($dbActions)) { 595 $actionids[$dbAction['actionid']] = $dbAction['actionid']; 596 } 597 598 // actions from operations 599 $dbActions = DBselect( 600 'SELECT o.actionid'. 601 ' FROM operations o,opgroup og'. 602 ' WHERE o.operationid=og.operationid AND '.dbConditionInt('og.groupid', $groupids). 603 ' UNION'. 604 ' SELECT o.actionid'. 605 ' FROM operations o,opcommand_grp ocg'. 606 ' WHERE o.operationid=ocg.operationid AND '.dbConditionInt('ocg.groupid', $groupids) 607 ); 608 while ($dbAction = DBfetch($dbActions)) { 609 $actionids[$dbAction['actionid']] = $dbAction['actionid']; 610 } 611 612 if (!empty($actionids)) { 613 $update = []; 614 $update[] = [ 615 'values' => ['status' => ACTION_STATUS_DISABLED], 616 'where' => ['actionid' => $actionids] 617 ]; 618 DB::update('actions', $update); 619 } 620 621 // delete action conditions 622 DB::delete('conditions', [ 623 'conditiontype' => CONDITION_TYPE_HOST_GROUP, 624 'value' => $groupids 625 ]); 626 627 // delete action operation groups 628 $operationids = []; 629 $dbOperations = DBselect( 630 'SELECT DISTINCT og.operationid'. 631 ' FROM opgroup og'. 632 ' WHERE '.dbConditionInt('og.groupid', $groupids) 633 ); 634 while ($dbOperation = DBfetch($dbOperations)) { 635 $operationids[$dbOperation['operationid']] = $dbOperation['operationid']; 636 } 637 DB::delete('opgroup', [ 638 'groupid' => $groupids 639 ]); 640 641 // delete action operation commands 642 $dbOperations = DBselect( 643 'SELECT DISTINCT ocg.operationid'. 644 ' FROM opcommand_grp ocg'. 645 ' WHERE '.dbConditionInt('ocg.groupid', $groupids) 646 ); 647 while ($dbOperation = DBfetch($dbOperations)) { 648 $operationids[$dbOperation['operationid']] = $dbOperation['operationid']; 649 } 650 DB::delete('opcommand_grp', [ 651 'groupid' => $groupids 652 ]); 653 654 // delete empty operations 655 $delOperationids = []; 656 $dbOperations = DBselect( 657 'SELECT DISTINCT o.operationid'. 658 ' FROM operations o'. 659 ' WHERE '.dbConditionInt('o.operationid', $operationids). 660 ' AND NOT EXISTS (SELECT NULL FROM opgroup og WHERE o.operationid=og.operationid)'. 661 ' AND NOT EXISTS (SELECT NULL FROM opcommand_grp ocg WHERE o.operationid=ocg.operationid)' 662 ); 663 while ($dbOperation = DBfetch($dbOperations)) { 664 $delOperationids[$dbOperation['operationid']] = $dbOperation['operationid']; 665 } 666 667 DB::delete('operations', ['operationid' => $delOperationids]); 668 669 DB::delete('groups', ['groupid' => $groupids]); 670 671 DB::delete('profiles', [ 672 'idx' => 'web.dashconf.groups.groupids', 673 'value_id' => $groupids 674 ]); 675 676 DB::delete('profiles', [ 677 'idx' => 'web.dashconf.groups.hide.groupids', 678 'value_id' => $groupids 679 ]); 680 681 // TODO: remove audit 682 foreach ($groupids as $groupid) { 683 add_audit_ext(AUDIT_ACTION_DELETE, AUDIT_RESOURCE_HOST_GROUP, $groupid, $delGroups[$groupid]['name'], 'groups', null, null); 684 } 685 686 return ['groupids' => $groupids]; 687 } 688 689 /** 690 * Add hosts and templates to host groups. All given hosts and templates are added to all given host groups. 691 * 692 * @param array $data 693 * @param array $data['groups'] 694 * @param array $data['hosts'] 695 * @param array $data['templates'] 696 * 697 * @return array returns array of group IDs that hosts and templates have been added to 698 */ 699 public function massAdd(array $data) { 700 $data['groups'] = zbx_toArray($data['groups']); 701 $data['hosts'] = isset($data['hosts']) ? zbx_toArray($data['hosts']) : []; 702 $data['templates'] = isset($data['templates']) ? zbx_toArray($data['templates']) : []; 703 704 $this->validateMassAdd($data); 705 706 $groupIds = zbx_objectValues($data['groups'], 'groupid'); 707 $hostIds = zbx_objectValues($data['hosts'], 'hostid'); 708 $templateIds = zbx_objectValues($data['templates'], 'templateid'); 709 710 $objectIds = array_merge($hostIds, $templateIds); 711 $objectIds = array_keys(array_flip($objectIds)); 712 713 $linked = []; 714 $linkedDb = DBselect( 715 'SELECT hg.hostid,hg.groupid'. 716 ' FROM hosts_groups hg'. 717 ' WHERE '.dbConditionInt('hg.hostid', $objectIds). 718 ' AND '.dbConditionInt('hg.groupid', $groupIds) 719 ); 720 while ($pair = DBfetch($linkedDb)) { 721 $linked[$pair['groupid']][$pair['hostid']] = 1; 722 } 723 724 $insert = []; 725 foreach ($groupIds as $groupId) { 726 foreach ($objectIds as $objectId) { 727 if (isset($linked[$groupId][$objectId])) { 728 continue; 729 } 730 $insert[] = ['hostid' => $objectId, 'groupid' => $groupId]; 731 } 732 } 733 734 DB::insert('hosts_groups', $insert); 735 736 return ['groupids' => $groupIds]; 737 } 738 739 /** 740 * Remove hosts and templates from host groups. All given hosts and templates are removed from all given host groups. 741 * 742 * @param array $data 743 * @param array $data['groupids'] 744 * @param array $data['hostids'] 745 * @param array $data['templateids'] 746 * 747 * @return array returns array of group IDs that hosts and templates have been removed from 748 */ 749 public function massRemove(array $data) { 750 $data['groupids'] = zbx_toArray($data['groupids'], 'groupid'); 751 $data['hostids'] = isset($data['hostids']) ? zbx_toArray($data['hostids']) : []; 752 $data['templateids'] = isset($data['templateids']) ? zbx_toArray($data['templateids']) : []; 753 754 $this->validateMassRemove($data); 755 756 $objectIds = array_merge($data['hostids'], $data['templateids']); 757 $objectIds = array_keys(array_flip($objectIds)); 758 759 DB::delete('hosts_groups', [ 760 'hostid' => $objectIds, 761 'groupid' => $data['groupids'] 762 ]); 763 764 return ['groupids' => $data['groupids']]; 765 } 766 767 /** 768 * Update host groups with new hosts and templates. 769 * 770 * @param array $data 771 * @param array $data['groups'] 772 * @param array $data['hosts'] 773 * @param array $data['templates'] 774 * 775 * @return array returns array of group IDs that hosts and templates have been added to and 776 * removed from 777 */ 778 public function massUpdate(array $data) { 779 if (!array_key_exists('groups', $data) || !is_array($data['groups'])) { 780 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Field "%1$s" is mandatory.', 'groups')); 781 } 782 783 $data['groups'] = zbx_toArray($data['groups']); 784 $data['hosts'] = isset($data['hosts']) ? zbx_toArray($data['hosts']) : []; 785 $data['templates'] = isset($data['templates']) ? zbx_toArray($data['templates']) : []; 786 787 $this->validateMassUpdate($data); 788 789 $groupIds = zbx_objectValues($data['groups'], 'groupid'); 790 $hostIds = zbx_objectValues($data['hosts'], 'hostid'); 791 $templateIds = zbx_objectValues($data['templates'], 'templateid'); 792 793 $objectIds = zbx_toHash(array_merge($hostIds, $templateIds)); 794 795 // get old records and skip discovered hosts 796 $oldRecords = DBfetchArray(DBselect( 797 'SELECT hg.hostid,hg.groupid,hg.hostgroupid'. 798 ' FROM hosts_groups hg,hosts h'. 799 ' WHERE '.dbConditionInt('hg.groupid', $groupIds). 800 ' AND hg.hostid=h.hostid'. 801 ' AND h.flags='.ZBX_FLAG_DISCOVERY_NORMAL 802 )); 803 804 // calculate new records 805 $replaceRecords = []; 806 $newRecords = []; 807 808 foreach ($groupIds as $groupId) { 809 $groupRecords = []; 810 foreach ($oldRecords as $oldRecord) { 811 if ($oldRecord['groupid'] == $groupId) { 812 $groupRecords[] = $oldRecord; 813 } 814 } 815 816 // find records for replace 817 foreach ($groupRecords as $groupRecord) { 818 if (isset($objectIds[$groupRecord['hostid']])) { 819 $replaceRecords[] = $groupRecord; 820 } 821 } 822 823 // find records for create 824 $groupHostIds = zbx_toHash(zbx_objectValues($groupRecords, 'hostid')); 825 826 $newHostIds = array_diff($objectIds, $groupHostIds); 827 foreach ($newHostIds as $newHostId) { 828 $newRecords[] = [ 829 'groupid' => $groupId, 830 'hostid' => $newHostId 831 ]; 832 } 833 } 834 835 DB::replace('hosts_groups', $oldRecords, array_merge($replaceRecords, $newRecords)); 836 837 return ['groupids' => $groupIds]; 838 } 839 840 /** 841 * Validate write permissions to host groups that are added to given hosts and templates. 842 * 843 * @param array $data 844 * @param array $data['groups'] 845 * @param array $data['hosts'] 846 * @param array $data['templates'] 847 * 848 * @throws APIException if user has no write permissions to any of the given host groups 849 */ 850 protected function validateMassAdd(array $data) { 851 $groupIds = zbx_objectValues($data['groups'], 'groupid'); 852 $hostIds = zbx_objectValues($data['hosts'], 'hostid'); 853 $templateIds = zbx_objectValues($data['templates'], 'templateid'); 854 855 $groupIdsToAdd = []; 856 857 if ($hostIds) { 858 $dbHosts = API::Host()->get([ 859 'output' => ['hostid'], 860 'selectGroups' => ['groupid'], 861 'hostids' => $hostIds, 862 'editable' => true, 863 'preservekeys' => true 864 ]); 865 866 $this->validateHostsPermissions($hostIds, $dbHosts); 867 868 $this->checkValidator($hostIds, new CHostNormalValidator([ 869 'message' => _('Cannot update groups for discovered host "%1$s".') 870 ])); 871 872 foreach ($dbHosts as $dbHost) { 873 $oldGroupIds = zbx_objectValues($dbHost['groups'], 'groupid'); 874 875 foreach (array_diff($groupIds, $oldGroupIds) as $groupId) { 876 $groupIdsToAdd[$groupId] = $groupId; 877 } 878 } 879 } 880 881 if ($templateIds) { 882 $dbTemplates = API::Template()->get([ 883 'output' => ['templateid'], 884 'selectGroups' => ['groupid'], 885 'templateids' => $templateIds, 886 'editable' => true, 887 'preservekeys' => true 888 ]); 889 890 $this->validateHostsPermissions($templateIds, $dbTemplates); 891 892 foreach ($dbTemplates as $dbTemplate) { 893 $oldGroupIds = zbx_objectValues($dbTemplate['groups'], 'groupid'); 894 895 foreach (array_diff($groupIds, $oldGroupIds) as $groupId) { 896 $groupIdsToAdd[$groupId] = $groupId; 897 } 898 } 899 } 900 901 if ($groupIdsToAdd && !$this->isWritable($groupIdsToAdd)) { 902 self::exception(ZBX_API_ERROR_PERMISSIONS, 903 _('No permissions to referred object or it does not exist!') 904 ); 905 } 906 } 907 908 /** 909 * Validate write permissions to host groups that are added and removed from given hosts and templates. Also check 910 * if host and template has at least one host group left when removing host groups. 911 * 912 * @param array $data 913 * @param array $data['groups'] 914 * @param array $data['hosts'] 915 * @param array $data['templates'] 916 * 917 * @throws APIException if user has no write permissions to any of the given host groups or one of the hosts and 918 * templates is left without a host group 919 */ 920 protected function validateMassUpdate(array $data) { 921 $groupIds = zbx_objectValues($data['groups'], 'groupid'); 922 $hostIds = zbx_objectValues($data['hosts'], 'hostid'); 923 $templateIds = zbx_objectValues($data['templates'], 'templateid'); 924 925 $dbGroups = $this->get([ 926 'output' => ['groupid'], 927 'groupids' => $groupIds, 928 'selectHosts' => ['hostid', 'host'], 929 'selectTemplates' => ['templateid', 'host'] 930 ]); 931 932 if (!$dbGroups) { 933 self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!')); 934 } 935 936 // Collect group IDs that will added to given hosts and templates. 937 $groupIdsToAdd = []; 938 939 // Collect group IDs that will removed from given hosts and templates. 940 $groupIdsToRemove = []; 941 942 /* 943 * When given hosts or templates belong to other groups and those group IDs are not passed in parameters, 944 * those groups will be removed from given hosts and templates. Collect those host and template IDs 945 * from groups that will be removed. 946 */ 947 $objectIds = []; 948 949 /* 950 * New or existing hosts have been passed in parameters. First check write permissions to hosts 951 * and if hosts are not discovered. Then check if groups should be added and/or removed from given hosts. 952 */ 953 if ($hostIds) { 954 $dbHosts = API::Host()->get([ 955 'output' => ['hostid'], 956 'selectGroups' => ['groupid'], 957 'hostids' => $hostIds, 958 'editable' => true, 959 'preservekeys' => true 960 ]); 961 962 $this->validateHostsPermissions($hostIds, $dbHosts); 963 964 $this->checkValidator($hostIds, new CHostNormalValidator([ 965 'message' => _('Cannot update groups for discovered host "%1$s".') 966 ])); 967 968 foreach ($dbHosts as $dbHost) { 969 $oldGroupIds = zbx_objectValues($dbHost['groups'], 'groupid'); 970 971 // Validate groups that are added for current host. 972 foreach (array_diff($groupIds, $oldGroupIds) as $groupId) { 973 $groupIdsToAdd[$groupId] = $groupId; 974 } 975 976 // Validate groups that are removed from current host. 977 foreach (array_diff($oldGroupIds, $groupIds) as $groupId) { 978 $groupIdsToRemove[$groupId] = $groupId; 979 } 980 981 if ($groupIdsToRemove) { 982 $objectIds[] = $dbHost['hostid']; 983 } 984 } 985 } 986 987 /* 988 * New or existing templates have been passed in parameters. First check write permissions to templates. 989 * Then check if groups should be added and/or removed from given templates. 990 */ 991 if ($templateIds) { 992 $dbTemplates = API::Template()->get([ 993 'output' => ['templateid'], 994 'selectGroups' => ['groupid'], 995 'templateids' => $templateIds, 996 'editable' => true, 997 'preservekeys' => true 998 ]); 999 1000 $this->validateHostsPermissions($templateIds, $dbTemplates); 1001 1002 foreach ($dbTemplates as $dbTemplate) { 1003 $oldGroupIds = zbx_objectValues($dbTemplate['groups'], 'groupid'); 1004 1005 // Validate groups that are added for current template. 1006 foreach (array_diff($groupIds, $oldGroupIds) as $groupId) { 1007 $groupIdsToAdd[$groupId] = $groupId; 1008 } 1009 1010 // Validate groups that are removed from current template. 1011 foreach (array_diff($oldGroupIds, $groupIds) as $groupId) { 1012 $groupIdsToRemove[$groupId] = $groupId; 1013 } 1014 1015 if ($groupIdsToRemove) { 1016 $objectIds[] = $dbTemplate['templateid']; 1017 } 1018 } 1019 } 1020 1021 // Continue to check new, existing or removable groups for given hosts and templates. 1022 $groupIdsToUpdate = array_merge($groupIdsToAdd, $groupIdsToRemove); 1023 1024 // Validate write permissions only to changed (added/removed) groups for given hosts and templates. 1025 if ($groupIdsToUpdate && !$this->isWritable($groupIdsToUpdate)) { 1026 self::exception(ZBX_API_ERROR_PERMISSIONS, 1027 _('No permissions to referred object or it does not exist!') 1028 ); 1029 } 1030 1031 // Check if groups can be removed from given hosts and templates. Only check if no groups are added. 1032 if (!$groupIdsToAdd && $groupIdsToRemove) { 1033 $unlinkableObjectIds = getUnlinkableHostIds($groupIdsToRemove, $objectIds); 1034 1035 if (count($objectIds) != count($unlinkableObjectIds)) { 1036 self::exception(ZBX_API_ERROR_PARAMETERS, _('One of the objects is left without a host group.')); 1037 } 1038 } 1039 1040 $hosts_to_unlink = []; 1041 $templates_to_unlink = []; 1042 $hostIds = array_flip($hostIds); 1043 $templateIds = array_flip($templateIds); 1044 1045 foreach ($dbGroups as $group) { 1046 foreach ($group['hosts'] as $host) { 1047 if (!array_key_exists($host['hostid'], $hostIds)) { 1048 $hosts_to_unlink[] = $host; 1049 } 1050 } 1051 1052 foreach ($group['templates'] as $template) { 1053 if (!array_key_exists($template['templateid'], $templateIds)) { 1054 $templates_to_unlink[] = $template; 1055 } 1056 } 1057 } 1058 1059 $this->verifyHostsAndTemplatesAreUnlinkable($hosts_to_unlink, $templates_to_unlink, $groupIds); 1060 } 1061 1062 /** 1063 * Validate write permissions to host groups that are removed from given hosts and templates. Also check 1064 * if host and template has at least one host group left. 1065 * 1066 * @param array $data 1067 * @param array $data['groupids'] 1068 * @param array $data['hostids'] 1069 * @param array $data['templateids'] 1070 * 1071 * @throws APIException if user has no write permissions to any of the given host groups or one of the hosts and 1072 * templates is left without a host group 1073 */ 1074 protected function validateMassRemove(array $data) { 1075 $groupIdsToRemove = []; 1076 $hostIds = isset($data['hostids']) ? $data['hostids'] : []; 1077 $templateIds = isset($data['templateids']) ? $data['templateids'] : []; 1078 $hosts_to_unlink = []; 1079 $templates_to_unlink = []; 1080 1081 if ($hostIds) { 1082 $dbHosts = API::Host()->get([ 1083 'output' => ['hostid', 'host'], 1084 'selectGroups' => ['groupid'], 1085 'hostids' => $hostIds, 1086 'editable' => true, 1087 'preservekeys' => true 1088 ]); 1089 1090 $this->validateHostsPermissions($hostIds, $dbHosts); 1091 1092 $this->checkValidator($hostIds, new CHostNormalValidator([ 1093 'message' => _('Cannot update groups for discovered host "%1$s".') 1094 ])); 1095 1096 foreach ($dbHosts as $dbHost) { 1097 $oldGroupIds = zbx_objectValues($dbHost['groups'], 'groupid'); 1098 1099 // check if host belongs to the removable host group 1100 $hostGroupIdsToRemove = array_intersect($data['groupids'], $oldGroupIds); 1101 1102 if ($hostGroupIdsToRemove) { 1103 $hosts_to_unlink[] = $dbHost; 1104 1105 foreach ($hostGroupIdsToRemove as $groupId) { 1106 $groupIdsToRemove[$groupId] = $groupId; 1107 } 1108 } 1109 } 1110 } 1111 1112 if ($templateIds) { 1113 $dbTemplates = API::Template()->get([ 1114 'output' => ['templateid', 'host'], 1115 'selectGroups' => ['groupid'], 1116 'templateids' => $templateIds, 1117 'editable' => true, 1118 'preservekeys' => true 1119 ]); 1120 1121 $this->validateHostsPermissions($templateIds, $dbTemplates); 1122 1123 foreach ($dbTemplates as $dbTemplate) { 1124 $oldGroupIds = zbx_objectValues($dbTemplate['groups'], 'groupid'); 1125 1126 // check if template belongs to the removable host group 1127 $templateGroupIdsToRemove = array_intersect($data['groupids'], $oldGroupIds); 1128 1129 if ($templateGroupIdsToRemove) { 1130 $templates_to_unlink[] = $dbTemplate; 1131 1132 foreach ($templateGroupIdsToRemove as $groupId) { 1133 $groupIdsToRemove[$groupId] = $groupId; 1134 } 1135 } 1136 } 1137 } 1138 1139 if ($groupIdsToRemove && !$this->isWritable($groupIdsToRemove)) { 1140 self::exception(ZBX_API_ERROR_PERMISSIONS, 1141 _('No permissions to referred object or it does not exist!') 1142 ); 1143 } 1144 1145 $this->verifyHostsAndTemplatesAreUnlinkable($hosts_to_unlink, $templates_to_unlink, $groupIdsToRemove); 1146 } 1147 1148 /** 1149 * Validate write permissions to hosts or templates by given host or template IDs. 1150 * 1151 * @param array $hostIds array of host IDs or template IDs 1152 * @param array $dbHosts array of allowed hosts or templates 1153 * 1154 * @throws APIException if user has no write permissions to one of the hosts or templates 1155 */ 1156 protected function validateHostsPermissions(array $hostIds, array $dbHosts) { 1157 foreach ($hostIds as $hostId) { 1158 if (!isset($dbHosts[$hostId])) { 1159 self::exception(ZBX_API_ERROR_PERMISSIONS, 1160 _('No permissions to referred object or it does not exist!') 1161 ); 1162 } 1163 } 1164 } 1165 1166 /** 1167 * Check if user has read permissions for host groups. 1168 * 1169 * @param array $ids 1170 * 1171 * @return bool 1172 */ 1173 public function isReadable(array $ids) { 1174 if (!is_array($ids)) { 1175 return false; 1176 } 1177 if (empty($ids)) { 1178 return true; 1179 } 1180 1181 $ids = array_unique($ids); 1182 1183 $count = $this->get([ 1184 'groupids' => $ids, 1185 'countOutput' => true 1186 ]); 1187 return count($ids) == $count; 1188 } 1189 1190 /** 1191 * Check if user has write permissions for host groups. 1192 * 1193 * @param array $ids 1194 * 1195 * @return bool 1196 */ 1197 public function isWritable(array $ids) { 1198 if (!is_array($ids)) { 1199 return false; 1200 } 1201 if (empty($ids)) { 1202 return true; 1203 } 1204 1205 $ids = array_unique($ids); 1206 1207 $count = $this->get([ 1208 'groupids' => $ids, 1209 'editable' => true, 1210 'countOutput' => true 1211 ]); 1212 1213 return count($ids) == $count; 1214 } 1215 1216 protected function addRelatedObjects(array $options, array $result) { 1217 $result = parent::addRelatedObjects($options, $result); 1218 1219 $groupIds = array_keys($result); 1220 sort($groupIds); 1221 1222 // adding hosts 1223 if ($options['selectHosts'] !== null) { 1224 if ($options['selectHosts'] !== API_OUTPUT_COUNT) { 1225 $relationMap = $this->createRelationMap($result, 'groupid', 'hostid', 'hosts_groups'); 1226 $hosts = API::Host()->get([ 1227 'output' => $options['selectHosts'], 1228 'hostids' => $relationMap->getRelatedIds(), 1229 'preservekeys' => true 1230 ]); 1231 if (!is_null($options['limitSelects'])) { 1232 order_result($hosts, 'host'); 1233 } 1234 $result = $relationMap->mapMany($result, $hosts, 'hosts', $options['limitSelects']); 1235 } 1236 else { 1237 $hosts = API::Host()->get([ 1238 'groupids' => $groupIds, 1239 'countOutput' => true, 1240 'groupCount' => true 1241 ]); 1242 $hosts = zbx_toHash($hosts, 'groupid'); 1243 foreach ($result as $groupid => $group) { 1244 if (isset($hosts[$groupid])) { 1245 $result[$groupid]['hosts'] = $hosts[$groupid]['rowscount']; 1246 } 1247 else { 1248 $result[$groupid]['hosts'] = 0; 1249 } 1250 } 1251 } 1252 } 1253 1254 // adding templates 1255 if ($options['selectTemplates'] !== null) { 1256 if ($options['selectTemplates'] !== API_OUTPUT_COUNT) { 1257 $relationMap = $this->createRelationMap($result, 'groupid', 'hostid', 'hosts_groups'); 1258 $hosts = API::Template()->get([ 1259 'output' => $options['selectTemplates'], 1260 'templateids' => $relationMap->getRelatedIds(), 1261 'preservekeys' => true 1262 ]); 1263 if (!is_null($options['limitSelects'])) { 1264 order_result($hosts, 'host'); 1265 } 1266 $result = $relationMap->mapMany($result, $hosts, 'templates', $options['limitSelects']); 1267 } 1268 else { 1269 $hosts = API::Template()->get([ 1270 'groupids' => $groupIds, 1271 'countOutput' => true, 1272 'groupCount' => true 1273 ]); 1274 $hosts = zbx_toHash($hosts, 'groupid'); 1275 foreach ($result as $groupid => $group) { 1276 if (isset($hosts[$groupid])) { 1277 $result[$groupid]['templates'] = $hosts[$groupid]['rowscount']; 1278 } 1279 else { 1280 $result[$groupid]['templates'] = 0; 1281 } 1282 } 1283 } 1284 } 1285 1286 // adding discovery rule 1287 if ($options['selectDiscoveryRule'] !== null && $options['selectDiscoveryRule'] != API_OUTPUT_COUNT) { 1288 // discovered items 1289 $discoveryRules = DBFetchArray(DBselect( 1290 'SELECT gd.groupid,hd.parent_itemid'. 1291 ' FROM group_discovery gd,group_prototype gp,host_discovery hd'. 1292 ' WHERE '.dbConditionInt('gd.groupid', $groupIds). 1293 ' AND gd.parent_group_prototypeid=gp.group_prototypeid'. 1294 ' AND gp.hostid=hd.hostid' 1295 )); 1296 $relationMap = $this->createRelationMap($discoveryRules, 'groupid', 'parent_itemid'); 1297 1298 $discoveryRules = API::DiscoveryRule()->get([ 1299 'output' => $options['selectDiscoveryRule'], 1300 'itemids' => $relationMap->getRelatedIds(), 1301 'preservekeys' => true 1302 ]); 1303 $result = $relationMap->mapOne($result, $discoveryRules, 'discoveryRule'); 1304 } 1305 1306 // adding group discovery 1307 if ($options['selectGroupDiscovery'] !== null) { 1308 $groupDiscoveries = API::getApiService()->select('group_discovery', [ 1309 'output' => $this->outputExtend($options['selectGroupDiscovery'], ['groupid']), 1310 'filter' => ['groupid' => $groupIds], 1311 'preservekeys' => true 1312 ]); 1313 $relationMap = $this->createRelationMap($groupDiscoveries, 'groupid', 'groupid'); 1314 1315 $groupDiscoveries = $this->unsetExtraFields($groupDiscoveries, ['groupid'], 1316 $options['selectGroupDiscovery'] 1317 ); 1318 $result = $relationMap->mapOne($result, $groupDiscoveries, 'groupDiscovery'); 1319 } 1320 1321 return $result; 1322 } 1323 1324 /** 1325 * Verify that hosts and templates are unlinkable from groups. 1326 * 1327 * @param array $hosts 1328 * @param integer $hosts[]['hostid'] 1329 * @param string $hosts[]['host'] 1330 * @param array $templates 1331 * @param integer $templates[]['templateid'] 1332 * @param string $templates[]['host'] 1333 * @param array $groupids 1334 */ 1335 protected function verifyHostsAndTemplatesAreUnlinkable(array $hosts, array $templates, array $groupids) { 1336 $objectids = []; 1337 $host_names = []; 1338 $template_names = []; 1339 1340 foreach ($hosts as $host) { 1341 $objectids[] = $host['hostid']; 1342 $host_names[$host['hostid']] = $host['host']; 1343 } 1344 1345 foreach ($templates as $template) { 1346 $objectids[] = $template['templateid']; 1347 $template_names[$template['templateid']] = $template['host']; 1348 } 1349 1350 if ($objectids && $groupids) { 1351 $not_unlinkable_objectids = array_diff($objectids, getUnlinkableHostIds($groupids, $objectids)); 1352 1353 if ($not_unlinkable_objectids) { 1354 $objectid = reset($not_unlinkable_objectids); 1355 1356 if (array_key_exists($objectid, $host_names)) { 1357 self::exception(ZBX_API_ERROR_PARAMETERS, 1358 _s('Host "%1$s" cannot be without host group.', $host_names[$objectid]) 1359 ); 1360 } 1361 1362 self::exception(ZBX_API_ERROR_PARAMETERS, 1363 _s('Template "%1$s" cannot be without host group.', $template_names[$objectid]) 1364 ); 1365 } 1366 } 1367 } 1368} 1369