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 hosts. 24 * 25 * @package API 26 */ 27class CHost extends CHostGeneral { 28 29 protected $sortColumns = ['hostid', 'host', 'name', 'status']; 30 31 /** 32 * Get host data. 33 * 34 * @param array $options 35 * @param array $options['groupids'] HostGroup IDs 36 * @param array $options['hostids'] Host IDs 37 * @param bool $options['monitored_hosts'] only monitored Hosts 38 * @param bool $options['templated_hosts'] include templates in result 39 * @param bool $options['with_items'] only with items 40 * @param bool $options['with_monitored_items'] only with monitored items 41 * @param bool $options['with_triggers'] only with triggers 42 * @param bool $options['with_monitored_triggers'] only with monitored triggers 43 * @param bool $options['with_httptests'] only with http tests 44 * @param bool $options['with_monitored_httptests'] only with monitored http tests 45 * @param bool $options['with_graphs'] only with graphs 46 * @param bool $options['editable'] only with read-write permission. Ignored for SuperAdmins 47 * @param bool $options['selectGroups'] select HostGroups 48 * @param bool $options['selectItems'] select Items 49 * @param bool $options['selectTriggers'] select Triggers 50 * @param bool $options['selectGraphs'] select Graphs 51 * @param bool $options['selectApplications'] select Applications 52 * @param bool $options['selectMacros'] select Macros 53 * @param bool|array $options['selectInventory'] select Inventory 54 * @param bool $options['withInventory'] select only hosts with inventory 55 * @param int $options['count'] count Hosts, returned column name is rowscount 56 * @param string $options['pattern'] search hosts by pattern in Host name 57 * @param string $options['extendPattern'] search hosts by pattern in Host name, ip and DNS 58 * @param int $options['limit'] limit selection 59 * @param string $options['sortfield'] field to sort by 60 * @param string $options['sortorder'] sort order 61 * 62 * @return array|boolean Host data as array or false if error 63 */ 64 public function get($options = []) { 65 $result = []; 66 67 $sqlParts = [ 68 'select' => ['hosts' => 'h.hostid'], 69 'from' => ['hosts' => 'hosts h'], 70 'where' => ['flags' => 'h.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'], 71 'group' => [], 72 'order' => [], 73 'limit' => null 74 ]; 75 76 $defOptions = [ 77 'groupids' => null, 78 'hostids' => null, 79 'proxyids' => null, 80 'templateids' => null, 81 'interfaceids' => null, 82 'itemids' => null, 83 'triggerids' => null, 84 'maintenanceids' => null, 85 'graphids' => null, 86 'applicationids' => null, 87 'dserviceids' => null, 88 'httptestids' => null, 89 'monitored_hosts' => null, 90 'templated_hosts' => null, 91 'proxy_hosts' => null, 92 'with_items' => null, 93 'with_monitored_items' => null, 94 'with_simple_graph_items' => null, 95 'with_triggers' => null, 96 'with_monitored_triggers' => null, 97 'with_httptests' => null, 98 'with_monitored_httptests' => null, 99 'with_graphs' => null, 100 'with_applications' => null, 101 'withInventory' => null, 102 'editable' => false, 103 'nopermissions' => null, 104 // filter 105 'filter' => null, 106 'search' => null, 107 'searchInventory' => null, 108 'searchByAny' => null, 109 'startSearch' => null, 110 'excludeSearch' => null, 111 'searchWildcardsEnabled' => null, 112 // output 113 'output' => API_OUTPUT_EXTEND, 114 'selectGroups' => null, 115 'selectParentTemplates' => null, 116 'selectItems' => null, 117 'selectDiscoveries' => null, 118 'selectTriggers' => null, 119 'selectGraphs' => null, 120 'selectApplications' => null, 121 'selectMacros' => null, 122 'selectScreens' => null, 123 'selectInterfaces' => null, 124 'selectInventory' => null, 125 'selectHttpTests' => null, 126 'selectDiscoveryRule' => null, 127 'selectHostDiscovery' => null, 128 'countOutput' => null, 129 'groupCount' => null, 130 'preservekeys' => null, 131 'sortfield' => '', 132 'sortorder' => '', 133 'limit' => null, 134 'limitSelects' => null 135 ]; 136 $options = zbx_array_merge($defOptions, $options); 137 138 // editable + PERMISSION CHECK 139 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) { 140 $permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ; 141 $userGroups = getUserGroupsByUserId(self::$userData['userid']); 142 143 $sqlParts['where'][] = 'EXISTS ('. 144 'SELECT NULL'. 145 ' FROM hosts_groups hgg'. 146 ' JOIN rights r'. 147 ' ON r.id=hgg.groupid'. 148 ' AND '.dbConditionInt('r.groupid', $userGroups). 149 ' WHERE h.hostid=hgg.hostid'. 150 ' GROUP BY hgg.hostid'. 151 ' HAVING MIN(r.permission)>'.PERM_DENY. 152 ' AND MAX(r.permission)>='.zbx_dbstr($permission). 153 ')'; 154 } 155 156 // hostids 157 if (!is_null($options['hostids'])) { 158 zbx_value2array($options['hostids']); 159 $sqlParts['where']['hostid'] = dbConditionInt('h.hostid', $options['hostids']); 160 } 161 162 // groupids 163 if (!is_null($options['groupids'])) { 164 zbx_value2array($options['groupids']); 165 166 $sqlParts['from']['hosts_groups'] = 'hosts_groups hg'; 167 $sqlParts['where'][] = dbConditionInt('hg.groupid', $options['groupids']); 168 $sqlParts['where']['hgh'] = 'hg.hostid=h.hostid'; 169 170 if (!is_null($options['groupCount'])) { 171 $sqlParts['group']['groupid'] = 'hg.groupid'; 172 } 173 } 174 175 // proxyids 176 if (!is_null($options['proxyids'])) { 177 zbx_value2array($options['proxyids']); 178 179 $sqlParts['where'][] = dbConditionInt('h.proxy_hostid', $options['proxyids']); 180 } 181 182 // templateids 183 if (!is_null($options['templateids'])) { 184 zbx_value2array($options['templateids']); 185 186 $sqlParts['from']['hosts_templates'] = 'hosts_templates ht'; 187 $sqlParts['where'][] = dbConditionInt('ht.templateid', $options['templateids']); 188 $sqlParts['where']['hht'] = 'h.hostid=ht.hostid'; 189 190 if (!is_null($options['groupCount'])) { 191 $sqlParts['group']['templateid'] = 'ht.templateid'; 192 } 193 } 194 195 // interfaceids 196 if (!is_null($options['interfaceids'])) { 197 zbx_value2array($options['interfaceids']); 198 199 $sqlParts['from']['interface'] = 'interface hi'; 200 $sqlParts['where'][] = dbConditionInt('hi.interfaceid', $options['interfaceids']); 201 $sqlParts['where']['hi'] = 'h.hostid=hi.hostid'; 202 } 203 204 // itemids 205 if (!is_null($options['itemids'])) { 206 zbx_value2array($options['itemids']); 207 208 $sqlParts['from']['items'] = 'items i'; 209 $sqlParts['where'][] = dbConditionInt('i.itemid', $options['itemids']); 210 $sqlParts['where']['hi'] = 'h.hostid=i.hostid'; 211 } 212 213 // triggerids 214 if (!is_null($options['triggerids'])) { 215 zbx_value2array($options['triggerids']); 216 217 $sqlParts['from']['functions'] = 'functions f'; 218 $sqlParts['from']['items'] = 'items i'; 219 $sqlParts['where'][] = dbConditionInt('f.triggerid', $options['triggerids']); 220 $sqlParts['where']['hi'] = 'h.hostid=i.hostid'; 221 $sqlParts['where']['fi'] = 'f.itemid=i.itemid'; 222 } 223 224 // httptestids 225 if (!is_null($options['httptestids'])) { 226 zbx_value2array($options['httptestids']); 227 228 $sqlParts['from']['httptest'] = 'httptest ht'; 229 $sqlParts['where'][] = dbConditionInt('ht.httptestid', $options['httptestids']); 230 $sqlParts['where']['aht'] = 'ht.hostid=h.hostid'; 231 } 232 233 // graphids 234 if (!is_null($options['graphids'])) { 235 zbx_value2array($options['graphids']); 236 237 $sqlParts['from']['graphs_items'] = 'graphs_items gi'; 238 $sqlParts['from']['items'] = 'items i'; 239 $sqlParts['where'][] = dbConditionInt('gi.graphid', $options['graphids']); 240 $sqlParts['where']['igi'] = 'i.itemid=gi.itemid'; 241 $sqlParts['where']['hi'] = 'h.hostid=i.hostid'; 242 } 243 244 // applicationids 245 if (!is_null($options['applicationids'])) { 246 zbx_value2array($options['applicationids']); 247 248 $sqlParts['from']['applications'] = 'applications a'; 249 $sqlParts['where'][] = dbConditionInt('a.applicationid', $options['applicationids']); 250 $sqlParts['where']['ah'] = 'a.hostid=h.hostid'; 251 } 252 253 // dserviceids 254 if (!is_null($options['dserviceids'])) { 255 zbx_value2array($options['dserviceids']); 256 257 $sqlParts['from']['dservices'] = 'dservices ds'; 258 $sqlParts['from']['interface'] = 'interface i'; 259 $sqlParts['where'][] = dbConditionInt('ds.dserviceid', $options['dserviceids']); 260 $sqlParts['where']['dsh'] = 'ds.ip=i.ip'; 261 $sqlParts['where']['hi'] = 'h.hostid=i.hostid'; 262 263 if (!is_null($options['groupCount'])) { 264 $sqlParts['group']['dserviceid'] = 'ds.dserviceid'; 265 } 266 } 267 268 // maintenanceids 269 if (!is_null($options['maintenanceids'])) { 270 zbx_value2array($options['maintenanceids']); 271 272 $sqlParts['from']['maintenances_hosts'] = 'maintenances_hosts mh'; 273 $sqlParts['where'][] = dbConditionInt('mh.maintenanceid', $options['maintenanceids']); 274 $sqlParts['where']['hmh'] = 'h.hostid=mh.hostid'; 275 276 if (!is_null($options['groupCount'])) { 277 $sqlParts['group']['maintenanceid'] = 'mh.maintenanceid'; 278 } 279 } 280 281 // monitored_hosts, templated_hosts 282 if (!is_null($options['monitored_hosts'])) { 283 $sqlParts['where']['status'] = 'h.status='.HOST_STATUS_MONITORED; 284 } 285 elseif (!is_null($options['templated_hosts'])) { 286 $sqlParts['where']['status'] = 'h.status IN ('.HOST_STATUS_MONITORED.','.HOST_STATUS_NOT_MONITORED.','.HOST_STATUS_TEMPLATE.')'; 287 } 288 elseif (!is_null($options['proxy_hosts'])) { 289 $sqlParts['where']['status'] = 'h.status IN ('.HOST_STATUS_PROXY_ACTIVE.','.HOST_STATUS_PROXY_PASSIVE.')'; 290 } 291 else { 292 $sqlParts['where']['status'] = 'h.status IN ('.HOST_STATUS_MONITORED.','.HOST_STATUS_NOT_MONITORED.')'; 293 } 294 295 // with_items, with_monitored_items, with_simple_graph_items 296 if (!is_null($options['with_items'])) { 297 $sqlParts['where'][] = 'EXISTS ('. 298 'SELECT NULL'. 299 ' FROM items i'. 300 ' WHERE h.hostid=i.hostid'. 301 ' AND i.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'. 302 ')'; 303 } 304 elseif (!is_null($options['with_monitored_items'])) { 305 $sqlParts['where'][] = 'EXISTS ('. 306 'SELECT NULL'. 307 ' FROM items i'. 308 ' WHERE h.hostid=i.hostid'. 309 ' AND i.status='.ITEM_STATUS_ACTIVE. 310 ' AND i.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'. 311 ')'; 312 } 313 elseif (!is_null($options['with_simple_graph_items'])) { 314 $sqlParts['where'][] = 'EXISTS ('. 315 'SELECT NULL'. 316 ' FROM items i'. 317 ' WHERE h.hostid=i.hostid'. 318 ' AND i.value_type IN ('.ITEM_VALUE_TYPE_FLOAT.','.ITEM_VALUE_TYPE_UINT64.')'. 319 ' AND i.status='.ITEM_STATUS_ACTIVE. 320 ' AND i.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'. 321 ')'; 322 } 323 324 // with_triggers, with_monitored_triggers 325 if (!is_null($options['with_triggers'])) { 326 $sqlParts['where'][] = 'EXISTS ('. 327 'SELECT NULL'. 328 ' FROM items i,functions f,triggers t'. 329 ' WHERE h.hostid=i.hostid'. 330 ' AND i.itemid=f.itemid'. 331 ' AND f.triggerid=t.triggerid'. 332 ' AND t.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'. 333 ')'; 334 } 335 elseif (!is_null($options['with_monitored_triggers'])) { 336 $sqlParts['where'][] = 'EXISTS ('. 337 'SELECT NULL'. 338 ' FROM items i,functions f,triggers t'. 339 ' WHERE h.hostid=i.hostid'. 340 ' AND i.itemid=f.itemid'. 341 ' AND f.triggerid=t.triggerid'. 342 ' AND i.status='.ITEM_STATUS_ACTIVE. 343 ' AND t.status='.TRIGGER_STATUS_ENABLED. 344 ' AND t.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'. 345 ')'; 346 } 347 348 // with_httptests, with_monitored_httptests 349 if (!empty($options['with_httptests'])) { 350 $sqlParts['where'][] = 'EXISTS (SELECT NULL FROM httptest ht WHERE ht.hostid=h.hostid)'; 351 } 352 elseif (!empty($options['with_monitored_httptests'])) { 353 $sqlParts['where'][] = 'EXISTS ('. 354 'SELECT NULL'. 355 ' FROM httptest ht'. 356 ' WHERE h.hostid=ht.hostid'. 357 ' AND ht.status='.HTTPTEST_STATUS_ACTIVE. 358 ')'; 359 } 360 361 // with_graphs 362 if (!is_null($options['with_graphs'])) { 363 $sqlParts['where'][] = 'EXISTS ('. 364 'SELECT NULL'. 365 ' FROM items i,graphs_items gi,graphs g'. 366 ' WHERE i.hostid=h.hostid'. 367 ' AND i.itemid=gi.itemid '. 368 ' AND gi.graphid=g.graphid'. 369 ' AND g.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'. 370 ')'; 371 } 372 373 // with applications 374 if (!is_null($options['with_applications'])) { 375 $sqlParts['from']['applications'] = 'applications a'; 376 $sqlParts['where'][] = 'a.hostid=h.hostid'; 377 } 378 379 // withInventory 380 if (!is_null($options['withInventory']) && $options['withInventory']) { 381 $sqlParts['where'][] = ' h.hostid IN ('. 382 ' SELECT hin.hostid'. 383 ' FROM host_inventory hin'. 384 ')'; 385 } 386 387 // search 388 if (is_array($options['search'])) { 389 zbx_db_search('hosts h', $options, $sqlParts); 390 391 if (zbx_db_search('interface hi', $options, $sqlParts)) { 392 $sqlParts['from']['interface'] = 'interface hi'; 393 $sqlParts['where']['hi'] = 'h.hostid=hi.hostid'; 394 } 395 } 396 397 // search inventory 398 if ($options['searchInventory'] !== null) { 399 $sqlParts['from']['host_inventory'] = 'host_inventory hii'; 400 $sqlParts['where']['hii'] = 'h.hostid=hii.hostid'; 401 402 zbx_db_search('host_inventory hii', 403 [ 404 'search' => $options['searchInventory'], 405 'startSearch' => $options['startSearch'], 406 'excludeSearch' => $options['excludeSearch'], 407 'searchWildcardsEnabled' => $options['searchWildcardsEnabled'], 408 'searchByAny' => $options['searchByAny'] 409 ], 410 $sqlParts 411 ); 412 } 413 414 // filter 415 if (is_array($options['filter'])) { 416 $this->dbFilter('hosts h', $options, $sqlParts); 417 418 if ($this->dbFilter('interface hi', $options, $sqlParts)) { 419 $sqlParts['from']['interface'] = 'interface hi'; 420 $sqlParts['where']['hi'] = 'h.hostid=hi.hostid'; 421 } 422 } 423 424 // limit 425 if (zbx_ctype_digit($options['limit']) && $options['limit']) { 426 $sqlParts['limit'] = $options['limit']; 427 } 428 429 $sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 430 $sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 431 $res = DBselect($this->createSelectQueryFromParts($sqlParts), $sqlParts['limit']); 432 while ($host = DBfetch($res)) { 433 if (!is_null($options['countOutput'])) { 434 if (!is_null($options['groupCount'])) { 435 $result[] = $host; 436 } 437 else { 438 $result = $host['rowscount']; 439 } 440 } 441 else { 442 $result[$host['hostid']] = $host; 443 } 444 } 445 446 if (!is_null($options['countOutput'])) { 447 return $result; 448 } 449 450 if ($result) { 451 $result = $this->addRelatedObjects($options, $result); 452 } 453 454 // removing keys (hash -> array) 455 if (is_null($options['preservekeys'])) { 456 $result = zbx_cleanHashes($result); 457 } 458 459 return $result; 460 } 461 462 /** 463 * Add host. 464 * 465 * @param array $hosts An array with hosts data. 466 * @param string $hosts[]['host'] Host technical name. 467 * @param string $hosts[]['name'] Host visible name (optional). 468 * @param array $hosts[]['groups'] An array of host group objects with IDs that host will be added to. 469 * @param int $hosts[]['status'] Status of the host (optional). 470 * @param array $hosts[]['interfaces'] An array of host interfaces data. 471 * @param int $hosts[]['interfaces']['type'] Interface type. 472 * @param int $hosts[]['interfaces']['main'] Is this the default interface to use. 473 * @param string $hosts[]['interfaces']['ip'] Interface IP (optional). 474 * @param int $hosts[]['interfaces']['port'] Interface port (optional). 475 * @param int $hosts[]['interfaces']['useip'] Interface shoud use IP (optional). 476 * @param string $hosts[]['interfaces']['dns'] Interface shoud use DNS (optional). 477 * @param int $hosts[]['interfaces']['bulk'] Use bulk requests for interface (optional). 478 * @param int $hosts[]['proxy_hostid'] ID of the proxy that is used to monitor the host (optional). 479 * @param int $hosts[]['ipmi_authtype'] IPMI authentication type (optional). 480 * @param int $hosts[]['ipmi_privilege'] IPMI privilege (optional). 481 * @param string $hosts[]['ipmi_username'] IPMI username (optional). 482 * @param string $hosts[]['ipmi_password'] IPMI password (optional). 483 * @param array $hosts[]['inventory'] An array of host inventory data (optional). 484 * @param array $hosts[]['macros'] An array of host macros (optional). 485 * @param string $hosts[]['macros'][]['macro'] Host macro (required if "macros" is set). 486 * @param array $hosts[]['templates'] An array of template objects with IDs that will be linked to host (optional). 487 * @param string $hosts[]['templates'][]['templateid'] Template ID (required if "templates" is set). 488 * @param string $hosts[]['tls_connect'] Connections to host (optional). 489 * @param string $hosts[]['tls_accept'] Connections from host (optional). 490 * @param string $hosts[]['tls_psk_identity'] PSK identity (required if "PSK" type is set). 491 * @param string $hosts[]['tls_psk'] PSK (required if "PSK" type is set). 492 * @param string $hosts[]['tls_issuer'] Certificate issuer (optional). 493 * @param string $hosts[]['tls_subject'] Certificate subject (optional). 494 * 495 * @return array 496 */ 497 public function create($hosts) { 498 $hosts = zbx_toArray($hosts); 499 500 $this->validateCreate($hosts); 501 502 $hostids = []; 503 504 foreach ($hosts as $host) { 505 // If visible name is not given or empty it should be set to host name. 506 if (!array_key_exists('name', $host) || !trim($host['name'])) { 507 $host['name'] = $host['host']; 508 } 509 510 $hostid = DB::insert('hosts', [$host]); 511 $hostid = reset($hostid); 512 $host['hostid'] = $hostid; 513 $hostids[] = $hostid; 514 515 // Save groups. Groups must be added before calling massAdd() for permission validation to work. 516 $groupsToAdd = []; 517 foreach ($host['groups'] as $group) { 518 $groupsToAdd[] = [ 519 'hostid' => $hostid, 520 'groupid' => $group['groupid'] 521 ]; 522 } 523 DB::insert('hosts_groups', $groupsToAdd); 524 525 $options = [ 526 'hosts' => $host 527 ]; 528 529 if (isset($host['templates']) && !is_null($host['templates'])) { 530 $options['templates'] = $host['templates']; 531 } 532 533 if (isset($host['macros']) && !is_null($host['macros'])) { 534 $options['macros'] = $host['macros']; 535 } 536 537 if (isset($host['interfaces']) && !is_null($host['interfaces'])) { 538 $options['interfaces'] = $host['interfaces']; 539 } 540 541 $result = API::Host()->massAdd($options); 542 if (!$result) { 543 self::exception(); 544 } 545 546 if (array_key_exists('inventory', $host) && $host['inventory']) { 547 $hostInventory = $host['inventory']; 548 $hostInventory['inventory_mode'] = HOST_INVENTORY_MANUAL; 549 } 550 else { 551 $hostInventory = []; 552 } 553 554 if (array_key_exists('inventory_mode', $host) && $host['inventory_mode'] != HOST_INVENTORY_DISABLED) { 555 $hostInventory['inventory_mode'] = $host['inventory_mode']; 556 } 557 558 if (array_key_exists('inventory_mode', $hostInventory) 559 && ($hostInventory['inventory_mode'] == HOST_INVENTORY_MANUAL 560 || $hostInventory['inventory_mode'] == HOST_INVENTORY_AUTOMATIC)) { 561 $hostInventory['hostid'] = $hostid; 562 DB::insert('host_inventory', [$hostInventory], false); 563 } 564 } 565 566 return ['hostids' => $hostids]; 567 } 568 569 /** 570 * Update host. 571 * 572 * @param array $hosts An array with hosts data. 573 * @param string $hosts[]['hostid'] Host ID. 574 * @param string $hosts[]['host'] Host technical name (optional). 575 * @param string $hosts[]['name'] Host visible name (optional). 576 * @param array $hosts[]['groups'] An array of host group objects with IDs that host will be replaced to. 577 * @param int $hosts[]['status'] Status of the host (optional). 578 * @param array $hosts[]['interfaces'] An array of host interfaces data to be replaced. 579 * @param int $hosts[]['interfaces']['type'] Interface type. 580 * @param int $hosts[]['interfaces']['main'] Is this the default interface to use. 581 * @param string $hosts[]['interfaces']['ip'] Interface IP (optional). 582 * @param int $hosts[]['interfaces']['port'] Interface port (optional). 583 * @param int $hosts[]['interfaces']['useip'] Interface shoud use IP (optional). 584 * @param string $hosts[]['interfaces']['dns'] Interface shoud use DNS (optional). 585 * @param int $hosts[]['interfaces']['bulk'] Use bulk requests for interface (optional). 586 * @param int $hosts[]['proxy_hostid'] ID of the proxy that is used to monitor the host (optional). 587 * @param int $hosts[]['ipmi_authtype'] IPMI authentication type (optional). 588 * @param int $hosts[]['ipmi_privilege'] IPMI privilege (optional). 589 * @param string $hosts[]['ipmi_username'] IPMI username (optional). 590 * @param string $hosts[]['ipmi_password'] IPMI password (optional). 591 * @param array $hosts[]['inventory'] An array of host inventory data (optional). 592 * @param array $hosts[]['macros'] An array of host macros (optional). 593 * @param string $hosts[]['macros'][]['macro'] Host macro (required if "macros" is set). 594 * @param array $hosts[]['templates'] An array of template objects with IDs that will be linked to host (optional). 595 * @param string $hosts[]['templates'][]['templateid'] Template ID (required if "templates" is set). 596 * @param array $hosts[]['templates_clear'] Templates to unlink and clear from the host (optional). 597 * @param string $hosts[]['templates_clear'][]['templateid'] Template ID (required if "templates" is set). 598 * @param string $hosts[]['tls_connect'] Connections to host (optional). 599 * @param string $hosts[]['tls_accept'] Connections from host (optional). 600 * @param string $hosts[]['tls_psk_identity'] PSK identity (required if "PSK" type is set). 601 * @param string $hosts[]['tls_psk'] PSK (required if "PSK" type is set). 602 * @param string $hosts[]['tls_issuer'] Certificate issuer (optional). 603 * @param string $hosts[]['tls_subject'] Certificate subject (optional). 604 * 605 * @return array 606 */ 607 public function update($hosts) { 608 $hosts = zbx_toArray($hosts); 609 $hostids = zbx_objectValues($hosts, 'hostid'); 610 611 $db_hosts = $this->get([ 612 'output' => ['hostid', 'host', 'flags', 'tls_connect', 'tls_accept', 'tls_issuer', 'tls_subject', 613 'tls_psk_identity', 'tls_psk' 614 ], 615 'hostids' => $hostids, 616 'editable' => true, 617 'preservekeys' => true 618 ]); 619 620 $hosts = $this->validateUpdate($hosts, $db_hosts); 621 622 $inventories = []; 623 foreach ($hosts as &$host) { 624 // If visible name is not given or empty it should be set to host name. 625 if (array_key_exists('host', $host) && (!array_key_exists('name', $host) || !trim($host['name']))) { 626 $host['name'] = $host['host']; 627 } 628 629 // Fetch fields required to update host inventory. 630 if (array_key_exists('inventory', $host)) { 631 $inventory = $host['inventory']; 632 $inventory['hostid'] = $host['hostid']; 633 634 $inventories[] = $inventory; 635 } 636 } 637 unset($host); 638 639 $inventories = $this->extendObjects('host_inventory', $inventories, ['inventory_mode']); 640 $inventories = zbx_toHash($inventories, 'hostid'); 641 642 $macros = []; 643 foreach ($hosts as &$host) { 644 if (isset($host['macros'])) { 645 $macros[$host['hostid']] = $host['macros']; 646 647 unset($host['macros']); 648 } 649 } 650 unset($host); 651 652 if ($macros) { 653 API::UserMacro()->replaceMacros($macros); 654 } 655 656 $hosts = $this->extendObjectsByKey($hosts, $db_hosts, 'hostid', ['tls_connect', 'tls_accept', 'tls_issuer', 657 'tls_subject', 'tls_psk_identity', 'tls_psk' 658 ]); 659 660 foreach ($hosts as $host) { 661 // Extend host inventory with the required data. 662 if (array_key_exists('inventory', $host) && $host['inventory']) { 663 // If inventory mode is HOST_INVENTORY_DISABLED, database record is not created. 664 if (array_key_exists('inventory_mode', $inventories[$host['hostid']]) 665 && ($inventories[$host['hostid']]['inventory_mode'] == HOST_INVENTORY_MANUAL 666 || $inventories[$host['hostid']]['inventory_mode'] == HOST_INVENTORY_AUTOMATIC)) { 667 $host['inventory'] = $inventories[$host['hostid']]; 668 } 669 } 670 671 $data = $host; 672 $data['hosts'] = ['hostid' => $host['hostid']]; 673 $result = $this->massUpdate($data); 674 675 if (!$result) { 676 self::exception(ZBX_API_ERROR_INTERNAL, _('Host update failed.')); 677 } 678 } 679 680 return ['hostids' => $hostids]; 681 } 682 683 /** 684 * Additionally allows to create new interfaces on hosts. 685 * 686 * Checks write permissions for hosts. 687 * 688 * Additional supported $data parameters are: 689 * - interfaces - an array of interfaces to create on the hosts 690 * - templates - an array of templates to link to the hosts, overrides the CHostGeneral::massAdd() 691 * 'templates' parameter 692 * 693 * @param array $data 694 * 695 * @return array 696 */ 697 public function massAdd(array $data) { 698 $hosts = isset($data['hosts']) ? zbx_toArray($data['hosts']) : []; 699 $hostIds = zbx_objectValues($hosts, 'hostid'); 700 701 // check permissions 702 if (!$this->isWritable($hostIds)) { 703 self::exception(ZBX_API_ERROR_PERMISSIONS, _('You do not have permission to perform this operation.')); 704 } 705 706 // add new interfaces 707 if (!empty($data['interfaces'])) { 708 API::HostInterface()->massAdd([ 709 'hosts' => $data['hosts'], 710 'interfaces' => zbx_toArray($data['interfaces']) 711 ]); 712 } 713 714 // rename the "templates" parameter to the common "templates_link" 715 if (isset($data['templates'])) { 716 $data['templates_link'] = $data['templates']; 717 unset($data['templates']); 718 } 719 720 $data['templates'] = []; 721 722 return parent::massAdd($data); 723 } 724 725 /** 726 * Mass update hosts. 727 * 728 * @param array $hosts multidimensional array with Hosts data 729 * @param array $hosts['hosts'] Array of Host objects to update 730 * @param string $hosts['fields']['host'] Host name. 731 * @param array $hosts['fields']['groupids'] HostGroup IDs add Host to. 732 * @param int $hosts['fields']['port'] Port. OPTIONAL 733 * @param int $hosts['fields']['status'] Host Status. OPTIONAL 734 * @param int $hosts['fields']['useip'] Use IP. OPTIONAL 735 * @param string $hosts['fields']['dns'] DNS. OPTIONAL 736 * @param string $hosts['fields']['ip'] IP. OPTIONAL 737 * @param int $hosts['fields']['bulk'] bulk. OPTIONAL 738 * @param int $hosts['fields']['proxy_hostid'] Proxy Host ID. OPTIONAL 739 * @param int $hosts['fields']['ipmi_authtype'] IPMI authentication type. OPTIONAL 740 * @param int $hosts['fields']['ipmi_privilege'] IPMI privilege. OPTIONAL 741 * @param string $hosts['fields']['ipmi_username'] IPMI username. OPTIONAL 742 * @param string $hosts['fields']['ipmi_password'] IPMI password. OPTIONAL 743 * 744 * @return boolean 745 */ 746 public function massUpdate($data) { 747 if (!array_key_exists('hosts', $data) || !is_array($data['hosts'])) { 748 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Field "%1$s" is mandatory.', 'hosts')); 749 } 750 751 $hosts = zbx_toArray($data['hosts']); 752 $inputHostIds = zbx_objectValues($hosts, 'hostid'); 753 $hostids = array_unique($inputHostIds); 754 755 sort($hostids); 756 757 $db_hosts = $this->get([ 758 'output' => ['hostid', 'host'], 759 'hostids' => $hostids, 760 'editable' => true, 761 'preservekeys' => true 762 ]); 763 764 foreach ($hosts as $host) { 765 if (!array_key_exists($host['hostid'], $db_hosts)) { 766 self::exception(ZBX_API_ERROR_PERMISSIONS, _('You do not have permission to perform this operation.')); 767 } 768 } 769 770 // Check inventory mode value. 771 if (array_key_exists('inventory_mode', $data)) { 772 $valid_inventory_modes = [HOST_INVENTORY_DISABLED, HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC]; 773 $inventory_mode = new CLimitedSetValidator([ 774 'values' => $valid_inventory_modes, 775 'messageInvalid' => _s('Incorrect value for field "%1$s": %2$s.', 'inventory_mode', 776 _s('value must be one of %1$s', implode(', ', $valid_inventory_modes))) 777 ]); 778 $this->checkValidator($data['inventory_mode'], $inventory_mode); 779 } 780 781 // Check connection fields only for massupdate action. 782 if (array_key_exists('tls_connect', $data) || array_key_exists('tls_accept', $data) 783 || array_key_exists('tls_psk_identity', $data) || array_key_exists('tls_psk', $data) 784 || array_key_exists('tls_issuer', $data) || array_key_exists('tls_subject', $data)) { 785 if (!array_key_exists('tls_connect', $data) || !array_key_exists('tls_accept', $data)) { 786 self::exception(ZBX_API_ERROR_PERMISSIONS, _( 787 'Cannot update host encryption settings. Connection settings for both directions should be specified.' 788 )); 789 } 790 791 // Clean PSK fields. 792 if ($data['tls_connect'] != HOST_ENCRYPTION_PSK && !($data['tls_accept'] & HOST_ENCRYPTION_PSK)) { 793 $data['tls_psk_identity'] = ''; 794 $data['tls_psk'] = ''; 795 } 796 797 // Clean certificate fields. 798 if ($data['tls_connect'] != HOST_ENCRYPTION_CERTIFICATE 799 && !($data['tls_accept'] & HOST_ENCRYPTION_CERTIFICATE)) { 800 $data['tls_issuer'] = ''; 801 $data['tls_subject'] = ''; 802 } 803 } 804 805 $this->validateEncryption([$data]); 806 807 if (array_key_exists('groups', $data) && !$data['groups'] && $db_hosts) { 808 $host = reset($db_hosts); 809 810 self::exception(ZBX_API_ERROR_PARAMETERS, 811 _s('Host "%1$s" cannot be without host group.', $host['host']) 812 ); 813 } 814 815 /* 816 * Update hosts properties 817 */ 818 if (isset($data['name'])) { 819 if (count($hosts) > 1) { 820 self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot mass update visible host name.')); 821 } 822 } 823 824 if (isset($data['host'])) { 825 if (!preg_match('/^'.ZBX_PREG_HOST_FORMAT.'$/', $data['host'])) { 826 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect characters used for host name "%s".', $data['host'])); 827 } 828 829 if (count($hosts) > 1) { 830 self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot mass update host name.')); 831 } 832 833 $curHost = reset($hosts); 834 835 $sameHostnameHost = $this->get([ 836 'output' => ['hostid'], 837 'filter' => ['host' => $data['host']], 838 'nopermissions' => true, 839 'limit' => 1 840 ]); 841 $sameHostnameHost = reset($sameHostnameHost); 842 if ($sameHostnameHost && (bccomp($sameHostnameHost['hostid'], $curHost['hostid']) != 0)) { 843 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Host "%1$s" already exists.', $data['host'])); 844 } 845 846 // can't add host with the same name as existing template 847 $sameHostnameTemplate = API::Template()->get([ 848 'output' => ['templateid'], 849 'filter' => ['host' => $data['host']], 850 'nopermissions' => true, 851 'limit' => 1 852 ]); 853 if ($sameHostnameTemplate) { 854 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Template "%1$s" already exists.', $data['host'])); 855 } 856 } 857 858 if (isset($data['groups'])) { 859 $updateGroups = $data['groups']; 860 } 861 862 if (isset($data['interfaces'])) { 863 $updateInterfaces = $data['interfaces']; 864 } 865 866 if (array_key_exists('templates_clear', $data)) { 867 $updateTemplatesClear = zbx_toArray($data['templates_clear']); 868 } 869 870 if (isset($data['templates'])) { 871 $updateTemplates = $data['templates']; 872 } 873 874 if (isset($data['macros'])) { 875 $updateMacros = $data['macros']; 876 } 877 878 // second check is necessary, because import incorrectly inputs unset 'inventory' as empty string rather than null 879 if (isset($data['inventory']) && $data['inventory']) { 880 if (isset($data['inventory_mode']) && $data['inventory_mode'] == HOST_INVENTORY_DISABLED) { 881 self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot set inventory fields for disabled inventory.')); 882 } 883 884 $updateInventory = $data['inventory']; 885 $updateInventory['inventory_mode'] = null; 886 } 887 888 if (isset($data['inventory_mode'])) { 889 if (!isset($updateInventory)) { 890 $updateInventory = []; 891 } 892 $updateInventory['inventory_mode'] = $data['inventory_mode']; 893 } 894 895 if (isset($data['status'])) { 896 $updateStatus = $data['status']; 897 } 898 899 unset($data['hosts'], $data['groups'], $data['interfaces'], $data['templates_clear'], $data['templates'], 900 $data['macros'], $data['inventory'], $data['inventory_mode'], $data['status']); 901 902 if (!zbx_empty($data)) { 903 DB::update('hosts', [ 904 'values' => $data, 905 'where' => ['hostid' => $hostids] 906 ]); 907 } 908 909 if (isset($updateStatus)) { 910 updateHostStatus($hostids, $updateStatus); 911 } 912 913 /* 914 * Update template linkage 915 */ 916 if (isset($updateTemplatesClear)) { 917 $templateIdsClear = zbx_objectValues($updateTemplatesClear, 'templateid'); 918 919 if ($updateTemplatesClear) { 920 $this->massRemove(['hostids' => $hostids, 'templateids_clear' => $templateIdsClear]); 921 } 922 } 923 else { 924 $templateIdsClear = []; 925 } 926 927 // unlink templates 928 if (isset($updateTemplates)) { 929 $hostTemplates = API::Template()->get([ 930 'hostids' => $hostids, 931 'output' => ['templateid'], 932 'preservekeys' => true 933 ]); 934 935 $hostTemplateids = array_keys($hostTemplates); 936 $newTemplateids = zbx_objectValues($updateTemplates, 'templateid'); 937 938 $templatesToDel = array_diff($hostTemplateids, $newTemplateids); 939 $templatesToDel = array_diff($templatesToDel, $templateIdsClear); 940 941 if ($templatesToDel) { 942 $result = $this->massRemove([ 943 'hostids' => $hostids, 944 'templateids' => $templatesToDel 945 ]); 946 if (!$result) { 947 self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot unlink template')); 948 } 949 } 950 } 951 952 /* 953 * update interfaces 954 */ 955 if (isset($updateInterfaces)) { 956 foreach($hostids as $hostid) { 957 API::HostInterface()->replaceHostInterfaces([ 958 'hostid' => $hostid, 959 'interfaces' => $updateInterfaces 960 ]); 961 } 962 } 963 964 // link new templates 965 if (isset($updateTemplates)) { 966 $result = $this->massAdd([ 967 'hosts' => $hosts, 968 'templates' => $updateTemplates 969 ]); 970 971 if (!$result) { 972 self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot link template')); 973 } 974 } 975 976 // macros 977 if (isset($updateMacros)) { 978 DB::delete('hostmacro', ['hostid' => $hostids]); 979 980 $this->massAdd([ 981 'hosts' => $hosts, 982 'macros' => $updateMacros 983 ]); 984 } 985 986 /* 987 * Inventory 988 */ 989 if (isset($updateInventory)) { 990 // disabling inventory 991 if ($updateInventory['inventory_mode'] == HOST_INVENTORY_DISABLED) { 992 $sql = 'DELETE FROM host_inventory WHERE '.dbConditionInt('hostid', $hostids); 993 if (!DBexecute($sql)) { 994 self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot delete inventory.')); 995 } 996 } 997 // changing inventory mode or setting inventory fields 998 else { 999 $existingInventoriesDb = DBfetchArrayAssoc(DBselect( 1000 'SELECT hostid,inventory_mode'. 1001 ' FROM host_inventory'. 1002 ' WHERE '.dbConditionInt('hostid', $hostids) 1003 ), 'hostid'); 1004 1005 // check existing host inventory data 1006 $automaticHostIds = []; 1007 if ($updateInventory['inventory_mode'] === null) { 1008 foreach ($hostids as $hostid) { 1009 // if inventory is disabled for one of the updated hosts, throw an exception 1010 if (!isset($existingInventoriesDb[$hostid])) { 1011 $host = get_host_by_hostid($hostid); 1012 self::exception(ZBX_API_ERROR_PARAMETERS, _s( 1013 'Inventory disabled for host "%1$s".', $host['host'] 1014 )); 1015 } 1016 // if inventory mode is set to automatic, save its ID for later usage 1017 elseif ($existingInventoriesDb[$hostid]['inventory_mode'] == HOST_INVENTORY_AUTOMATIC) { 1018 $automaticHostIds[] = $hostid; 1019 } 1020 } 1021 } 1022 1023 $inventoriesToSave = []; 1024 foreach ($hostids as $hostid) { 1025 $hostInventory = $updateInventory; 1026 $hostInventory['hostid'] = $hostid; 1027 1028 // if no 'inventory_mode' has been passed, set inventory 'inventory_mode' from DB 1029 if ($updateInventory['inventory_mode'] === null) { 1030 $hostInventory['inventory_mode'] = $existingInventoriesDb[$hostid]['inventory_mode']; 1031 } 1032 1033 $inventoriesToSave[$hostid] = $hostInventory; 1034 } 1035 1036 // when updating automatic inventory, ignore fields that have items linked to them 1037 if ($updateInventory['inventory_mode'] == HOST_INVENTORY_AUTOMATIC 1038 || ($updateInventory['inventory_mode'] === null && $automaticHostIds)) { 1039 1040 $itemsToInventories = API::item()->get([ 1041 'output' => ['inventory_link', 'hostid'], 1042 'hostids' => $automaticHostIds ? $automaticHostIds : $hostids, 1043 'nopermissions' => true 1044 ]); 1045 1046 $inventoryFields = getHostInventories(); 1047 foreach ($itemsToInventories as $hinv) { 1048 // 0 means 'no link' 1049 if ($hinv['inventory_link'] != 0) { 1050 $inventoryName = $inventoryFields[$hinv['inventory_link']]['db_field']; 1051 unset($inventoriesToSave[$hinv['hostid']][$inventoryName]); 1052 } 1053 } 1054 } 1055 1056 // save inventory data 1057 foreach ($inventoriesToSave as $inventory) { 1058 $hostid = $inventory['hostid']; 1059 if (isset($existingInventoriesDb[$hostid])) { 1060 DB::update('host_inventory', [ 1061 'values' => $inventory, 1062 'where' => ['hostid' => $hostid] 1063 ]); 1064 } 1065 else { 1066 DB::insert('host_inventory', [$inventory], false); 1067 } 1068 } 1069 } 1070 } 1071 1072 /* 1073 * Update host and host group linkage. This procedure should be done the last because user can unlink 1074 * him self from a group with write permissions leaving only read premissions. Thus other procedures, like 1075 * host-template linkage, inventory update, macros update, must be done before this. 1076 */ 1077 if (isset($updateGroups)) { 1078 $updateGroups = zbx_toArray($updateGroups); 1079 1080 $hostGroups = API::HostGroup()->get([ 1081 'output' => ['groupid'], 1082 'hostids' => $hostids 1083 ]); 1084 $hostGroupIds = zbx_objectValues($hostGroups, 'groupid'); 1085 $newGroupIds = zbx_objectValues($updateGroups, 'groupid'); 1086 1087 $groupsToAdd = array_diff($newGroupIds, $hostGroupIds); 1088 if ($groupsToAdd) { 1089 $this->massAdd([ 1090 'hosts' => $hosts, 1091 'groups' => zbx_toObject($groupsToAdd, 'groupid') 1092 ]); 1093 } 1094 1095 $groupIdsToDelete = array_diff($hostGroupIds, $newGroupIds); 1096 if ($groupIdsToDelete) { 1097 $this->massRemove([ 1098 'hostids' => $hostids, 1099 'groupids' => $groupIdsToDelete 1100 ]); 1101 } 1102 } 1103 1104 return ['hostids' => $inputHostIds]; 1105 } 1106 1107 /** 1108 * Additionally allows to remove interfaces from hosts. 1109 * 1110 * Checks write permissions for hosts. 1111 * 1112 * Additional supported $data parameters are: 1113 * - interfaces - an array of interfaces to delete from the hosts 1114 * 1115 * @param array $data 1116 * 1117 * @return array 1118 */ 1119 public function massRemove(array $data) { 1120 $hostids = zbx_toArray($data['hostids']); 1121 1122 $this->checkPermissions($hostids); 1123 1124 if (isset($data['interfaces'])) { 1125 $options = [ 1126 'hostids' => $hostids, 1127 'interfaces' => zbx_toArray($data['interfaces']) 1128 ]; 1129 API::HostInterface()->massRemove($options); 1130 } 1131 1132 // rename the "templates" parameter to the common "templates_link" 1133 if (isset($data['templateids'])) { 1134 $data['templateids_link'] = $data['templateids']; 1135 unset($data['templateids']); 1136 } 1137 1138 $data['templateids'] = []; 1139 1140 return parent::massRemove($data); 1141 } 1142 1143 /** 1144 * Validates the input parameters for the delete() method. 1145 * 1146 * @throws APIException if the input is invalid 1147 * 1148 * @param array $hostIds 1149 * @param bool $nopermissions 1150 * 1151 * @return void 1152 */ 1153 protected function validateDelete(array $hostIds, $nopermissions = false) { 1154 if (!$hostIds) { 1155 self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); 1156 } 1157 1158 if (!$nopermissions) { 1159 $this->checkPermissions($hostIds); 1160 } 1161 } 1162 1163 /** 1164 * Delete Host. 1165 * 1166 * @param array $hostIds 1167 * @param bool $nopermissions 1168 * 1169 * @return array 1170 */ 1171 public function delete(array $hostIds, $nopermissions = false) { 1172 $this->validateDelete($hostIds, $nopermissions); 1173 1174 // delete the discovery rules first 1175 $delRules = API::DiscoveryRule()->get([ 1176 'output' => ['itemid'], 1177 'hostids' => $hostIds, 1178 'nopermissions' => true, 1179 'preservekeys' => true 1180 ]); 1181 if ($delRules) { 1182 API::DiscoveryRule()->delete(array_keys($delRules), true); 1183 } 1184 1185 // delete the items 1186 $delItems = API::Item()->get([ 1187 'templateids' => $hostIds, 1188 'output' => ['itemid'], 1189 'nopermissions' => true, 1190 'preservekeys' => true 1191 ]); 1192 if ($delItems) { 1193 API::Item()->delete(array_keys($delItems), true); 1194 } 1195 1196 // delete web tests 1197 $delHttptests = []; 1198 $dbHttptests = get_httptests_by_hostid($hostIds); 1199 while ($dbHttptest = DBfetch($dbHttptests)) { 1200 $delHttptests[$dbHttptest['httptestid']] = $dbHttptest['httptestid']; 1201 } 1202 if (!empty($delHttptests)) { 1203 API::HttpTest()->delete($delHttptests, true); 1204 } 1205 1206 1207 // delete screen items 1208 DB::delete('screens_items', [ 1209 'resourceid' => $hostIds, 1210 'resourcetype' => SCREEN_RESOURCE_HOST_TRIGGERS 1211 ]); 1212 1213 // delete host from maps 1214 if (!empty($hostIds)) { 1215 DB::delete('sysmaps_elements', [ 1216 'elementtype' => SYSMAP_ELEMENT_TYPE_HOST, 1217 'elementid' => $hostIds 1218 ]); 1219 } 1220 1221 // disable actions 1222 // actions from conditions 1223 $actionids = []; 1224 $sql = 'SELECT DISTINCT actionid'. 1225 ' FROM conditions'. 1226 ' WHERE conditiontype='.CONDITION_TYPE_HOST. 1227 ' AND '.dbConditionString('value', $hostIds); 1228 $dbActions = DBselect($sql); 1229 while ($dbAction = DBfetch($dbActions)) { 1230 $actionids[$dbAction['actionid']] = $dbAction['actionid']; 1231 } 1232 1233 // actions from operations 1234 $sql = 'SELECT DISTINCT o.actionid'. 1235 ' FROM operations o, opcommand_hst oh'. 1236 ' WHERE o.operationid=oh.operationid'. 1237 ' AND '.dbConditionInt('oh.hostid', $hostIds); 1238 $dbActions = DBselect($sql); 1239 while ($dbAction = DBfetch($dbActions)) { 1240 $actionids[$dbAction['actionid']] = $dbAction['actionid']; 1241 } 1242 1243 if (!empty($actionids)) { 1244 $update = []; 1245 $update[] = [ 1246 'values' => ['status' => ACTION_STATUS_DISABLED], 1247 'where' => ['actionid' => $actionids] 1248 ]; 1249 DB::update('actions', $update); 1250 } 1251 1252 // delete action conditions 1253 DB::delete('conditions', [ 1254 'conditiontype' => CONDITION_TYPE_HOST, 1255 'value' => $hostIds 1256 ]); 1257 1258 // delete action operation commands 1259 $operationids = []; 1260 $sql = 'SELECT DISTINCT oh.operationid'. 1261 ' FROM opcommand_hst oh'. 1262 ' WHERE '.dbConditionInt('oh.hostid', $hostIds); 1263 $dbOperations = DBselect($sql); 1264 while ($dbOperation = DBfetch($dbOperations)) { 1265 $operationids[$dbOperation['operationid']] = $dbOperation['operationid']; 1266 } 1267 1268 DB::delete('opcommand_hst', [ 1269 'hostid' => $hostIds, 1270 ]); 1271 1272 // delete empty operations 1273 $delOperationids = []; 1274 $sql = 'SELECT DISTINCT o.operationid'. 1275 ' FROM operations o'. 1276 ' WHERE '.dbConditionInt('o.operationid', $operationids). 1277 ' AND NOT EXISTS(SELECT oh.opcommand_hstid FROM opcommand_hst oh WHERE oh.operationid=o.operationid)'; 1278 $dbOperations = DBselect($sql); 1279 while ($dbOperation = DBfetch($dbOperations)) { 1280 $delOperationids[$dbOperation['operationid']] = $dbOperation['operationid']; 1281 } 1282 1283 DB::delete('operations', [ 1284 'operationid' => $delOperationids, 1285 ]); 1286 1287 $hosts = API::Host()->get([ 1288 'output' => [ 1289 'hostid', 1290 'name' 1291 ], 1292 'hostids' => $hostIds, 1293 'nopermissions' => true 1294 ]); 1295 1296 // delete host inventory 1297 DB::delete('host_inventory', ['hostid' => $hostIds]); 1298 1299 // delete host applications 1300 DB::delete('applications', ['hostid' => $hostIds]); 1301 1302 // delete host 1303 DB::delete('hosts', ['hostid' => $hostIds]); 1304 1305 // TODO: remove info from API 1306 foreach ($hosts as $host) { 1307 info(_s('Deleted: Host "%1$s".', $host['name'])); 1308 add_audit_ext(AUDIT_ACTION_DELETE, AUDIT_RESOURCE_HOST, $host['hostid'], $host['name'], 'hosts', NULL, NULL); 1309 } 1310 1311 // remove Monitoring > Latest data toggle profile values related to given hosts 1312 DB::delete('profiles', ['idx' => 'web.latest.toggle_other', 'idx2' => $hostIds]); 1313 1314 return ['hostids' => $hostIds]; 1315 } 1316 1317 /** 1318 * Check if user has read permissions for host. 1319 * 1320 * @param array $ids 1321 * 1322 * @return bool 1323 */ 1324 public function isReadable(array $ids) { 1325 if (!is_array($ids)) { 1326 return false; 1327 } 1328 if (empty($ids)) { 1329 return true; 1330 } 1331 1332 $ids = array_unique($ids); 1333 1334 $count = $this->get([ 1335 'hostids' => $ids, 1336 'templated_hosts' => true, 1337 'countOutput' => true 1338 ]); 1339 1340 return (count($ids) == $count); 1341 } 1342 1343 /** 1344 * Check if user has write permissions for host. 1345 * 1346 * @param array $ids 1347 * 1348 * @return bool 1349 */ 1350 public function isWritable(array $ids) { 1351 if (!is_array($ids)) { 1352 return false; 1353 } 1354 if (empty($ids)) { 1355 return true; 1356 } 1357 1358 $ids = array_unique($ids); 1359 1360 $count = $this->get([ 1361 'hostids' => $ids, 1362 'editable' => true, 1363 'templated_hosts' => true, 1364 'countOutput' => true 1365 ]); 1366 1367 return (count($ids) == $count); 1368 } 1369 1370 protected function addRelatedObjects(array $options, array $result) { 1371 $result = parent::addRelatedObjects($options, $result); 1372 1373 $hostids = array_keys($result); 1374 1375 // adding inventories 1376 if ($options['selectInventory'] !== null) { 1377 $relationMap = $this->createRelationMap($result, 'hostid', 'hostid'); 1378 $inventory = API::getApiService()->select('host_inventory', [ 1379 'output' => $options['selectInventory'], 1380 'filter' => ['hostid' => $hostids] 1381 ]); 1382 $result = $relationMap->mapOne($result, zbx_toHash($inventory, 'hostid'), 'inventory'); 1383 } 1384 1385 // adding hostinterfaces 1386 if ($options['selectInterfaces'] !== null) { 1387 if ($options['selectInterfaces'] != API_OUTPUT_COUNT) { 1388 $interfaces = API::HostInterface()->get([ 1389 'output' => $this->outputExtend($options['selectInterfaces'], ['hostid', 'interfaceid']), 1390 'hostids' => $hostids, 1391 'nopermissions' => true, 1392 'preservekeys' => true 1393 ]); 1394 1395 // we need to order interfaces for proper linkage and viewing 1396 order_result($interfaces, 'interfaceid', ZBX_SORT_UP); 1397 1398 $relationMap = $this->createRelationMap($interfaces, 'hostid', 'interfaceid'); 1399 1400 $interfaces = $this->unsetExtraFields($interfaces, ['hostid', 'interfaceid'], $options['selectInterfaces']); 1401 $result = $relationMap->mapMany($result, $interfaces, 'interfaces', $options['limitSelects']); 1402 } 1403 else { 1404 $interfaces = API::HostInterface()->get([ 1405 'hostids' => $hostids, 1406 'nopermissions' => true, 1407 'countOutput' => true, 1408 'groupCount' => true 1409 ]); 1410 1411 $interfaces = zbx_toHash($interfaces, 'hostid'); 1412 foreach ($result as $hostid => $host) { 1413 $result[$hostid]['interfaces'] = isset($interfaces[$hostid]) ? $interfaces[$hostid]['rowscount'] : 0; 1414 } 1415 } 1416 } 1417 1418 // adding screens 1419 if ($options['selectScreens'] !== null) { 1420 if ($options['selectScreens'] != API_OUTPUT_COUNT) { 1421 $screens = API::TemplateScreen()->get([ 1422 'output' => $this->outputExtend($options['selectScreens'], ['hostid']), 1423 'hostids' => $hostids, 1424 'nopermissions' => true 1425 ]); 1426 if (!is_null($options['limitSelects'])) { 1427 order_result($screens, 'name'); 1428 } 1429 1430 // inherited screens do not have a unique screenid, so we're building a map using array keys 1431 $relationMap = new CRelationMap(); 1432 foreach ($screens as $key => $screen) { 1433 $relationMap->addRelation($screen['hostid'], $key); 1434 } 1435 1436 $screens = $this->unsetExtraFields($screens, ['hostid'], $options['selectScreens']); 1437 $result = $relationMap->mapMany($result, $screens, 'screens', $options['limitSelects']); 1438 } 1439 else { 1440 $screens = API::TemplateScreen()->get([ 1441 'hostids' => $hostids, 1442 'nopermissions' => true, 1443 'countOutput' => true, 1444 'groupCount' => true 1445 ]); 1446 $screens = zbx_toHash($screens, 'hostid'); 1447 1448 foreach ($result as $hostid => $host) { 1449 $result[$hostid]['screens'] = isset($screens[$hostid]) ? $screens[$hostid]['rowscount'] : 0; 1450 } 1451 } 1452 } 1453 1454 // adding discovery rule 1455 if ($options['selectDiscoveryRule'] !== null && $options['selectDiscoveryRule'] != API_OUTPUT_COUNT) { 1456 // discovered items 1457 $discoveryRules = DBFetchArray(DBselect( 1458 'SELECT hd.hostid,hd2.parent_itemid'. 1459 ' FROM host_discovery hd,host_discovery hd2'. 1460 ' WHERE '.dbConditionInt('hd.hostid', $hostids). 1461 ' AND hd.parent_hostid=hd2.hostid' 1462 )); 1463 $relationMap = $this->createRelationMap($discoveryRules, 'hostid', 'parent_itemid'); 1464 1465 $discoveryRules = API::DiscoveryRule()->get([ 1466 'output' => $options['selectDiscoveryRule'], 1467 'itemids' => $relationMap->getRelatedIds(), 1468 'preservekeys' => true 1469 ]); 1470 $result = $relationMap->mapOne($result, $discoveryRules, 'discoveryRule'); 1471 } 1472 1473 // adding host discovery 1474 if ($options['selectHostDiscovery'] !== null) { 1475 $hostDiscoveries = API::getApiService()->select('host_discovery', [ 1476 'output' => $this->outputExtend($options['selectHostDiscovery'], ['hostid']), 1477 'filter' => ['hostid' => $hostids], 1478 'preservekeys' => true 1479 ]); 1480 $relationMap = $this->createRelationMap($hostDiscoveries, 'hostid', 'hostid'); 1481 1482 $hostDiscoveries = $this->unsetExtraFields($hostDiscoveries, ['hostid'], 1483 $options['selectHostDiscovery'] 1484 ); 1485 $result = $relationMap->mapOne($result, $hostDiscoveries, 'hostDiscovery'); 1486 } 1487 1488 return $result; 1489 } 1490 1491 /** 1492 * Checks if all of the given hosts are available for writing. 1493 * 1494 * @throws APIException if a host is not writable or does not exist 1495 * 1496 * @param array $hostIds 1497 */ 1498 protected function checkPermissions(array $hostIds) { 1499 if (!$this->isWritable($hostIds)) { 1500 self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!')); 1501 } 1502 } 1503 1504 /** 1505 * Validate connections from/to host and PSK fields. 1506 * 1507 * @param array $hosts 1508 * @param string $hosts[]['hostid'] (optional if $db_hosts is null) 1509 * @param int $hosts[]['tls_connect'] (optionsl) 1510 * @param int $hosts[]['tls_accept'] (optional) 1511 * @param string $hosts[]['tls_psk_identity'] (optional) 1512 * @param string $hosts[]['tls_psk'] (optional) 1513 * @param string $hosts[]['tls_issuer'] (optional) 1514 * @param string $hosts[]['tls_subject'] (optional) 1515 * @param array $db_hosts (optional) 1516 * @param int $hosts[<hostid>]['tls_connect'] 1517 * @param int $hosts[<hostid>]['tls_accept'] 1518 * @param string $hosts[<hostid>]['tls_psk_identity'] 1519 * @param string $hosts[<hostid>]['tls_psk'] 1520 * @param string $hosts[<hostid>]['tls_issuer'] 1521 * @param string $hosts[<hostid>]['tls_subject'] 1522 * 1523 * @throws APIException if incorrect encryption options. 1524 */ 1525 protected function validateEncryption(array $hosts, array $db_hosts = null) { 1526 $available_connect_types = [HOST_ENCRYPTION_NONE, HOST_ENCRYPTION_PSK, HOST_ENCRYPTION_CERTIFICATE]; 1527 $min_accept_type = HOST_ENCRYPTION_NONE; 1528 $max_accept_type = HOST_ENCRYPTION_NONE | HOST_ENCRYPTION_PSK | HOST_ENCRYPTION_CERTIFICATE; 1529 1530 foreach ($hosts as $host) { 1531 foreach (['tls_connect', 'tls_accept'] as $field_name) { 1532 $$field_name = array_key_exists($field_name, $host) 1533 ? $host[$field_name] 1534 : ($db_hosts !== null ? $db_hosts[$host['hostid']][$field_name] : HOST_ENCRYPTION_NONE); 1535 } 1536 1537 if (!in_array($tls_connect, $available_connect_types)) { 1538 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'tls_connect', 1539 _s('unexpected value "%1$s"', $tls_connect) 1540 )); 1541 } 1542 1543 if ($tls_accept < $min_accept_type || $tls_accept > $max_accept_type) { 1544 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'tls_accept', 1545 _s('unexpected value "%1$s"', $tls_accept) 1546 )); 1547 } 1548 1549 foreach (['tls_psk_identity', 'tls_psk', 'tls_issuer', 'tls_subject'] as $field_name) { 1550 $$field_name = array_key_exists($field_name, $host) 1551 ? $host[$field_name] 1552 : ($db_hosts !== null ? $db_hosts[$host['hostid']][$field_name] : ''); 1553 } 1554 1555 // PSK validation. 1556 if ($tls_connect == HOST_ENCRYPTION_PSK || ($tls_accept & HOST_ENCRYPTION_PSK)) { 1557 if ($tls_psk_identity === '') { 1558 self::exception(ZBX_API_ERROR_PARAMETERS, 1559 _s('Incorrect value for field "%1$s": %2$s.', 'tls_psk_identity', _('cannot be empty')) 1560 ); 1561 } 1562 1563 if ($tls_psk === '') { 1564 self::exception(ZBX_API_ERROR_PARAMETERS, 1565 _s('Incorrect value for field "%1$s": %2$s.', 'tls_psk', _('cannot be empty')) 1566 ); 1567 } 1568 1569 if (!preg_match('/^([0-9a-f]{2})+$/i', $tls_psk)) { 1570 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'tls_psk', 1571 _('an even number of hexadecimal characters is expected') 1572 )); 1573 } 1574 1575 if (strlen($tls_psk) < PSK_MIN_LEN) { 1576 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'tls_psk', 1577 _s('minimum length is %1$s characters', PSK_MIN_LEN) 1578 )); 1579 } 1580 } 1581 else { 1582 if ($tls_psk_identity !== '') { 1583 self::exception(ZBX_API_ERROR_PARAMETERS, 1584 _s('Incorrect value for field "%1$s": %2$s.', 'tls_psk_identity', _('should be empty')) 1585 ); 1586 } 1587 1588 if ($tls_psk !== '') { 1589 self::exception(ZBX_API_ERROR_PARAMETERS, 1590 _s('Incorrect value for field "%1$s": %2$s.', 'tls_psk', _('should be empty')) 1591 ); 1592 } 1593 } 1594 1595 // Certificate validation. 1596 if ($tls_connect != HOST_ENCRYPTION_CERTIFICATE && !($tls_accept & HOST_ENCRYPTION_CERTIFICATE)) { 1597 if ($tls_issuer !== '') { 1598 self::exception(ZBX_API_ERROR_PARAMETERS, 1599 _s('Incorrect value for field "%1$s": %2$s.', 'tls_issuer', _('should be empty')) 1600 ); 1601 } 1602 1603 if ($tls_subject !== '') { 1604 self::exception(ZBX_API_ERROR_PARAMETERS, 1605 _s('Incorrect value for field "%1$s": %2$s.', 'tls_subject', _('should be empty')) 1606 ); 1607 } 1608 } 1609 } 1610 } 1611 1612 /** 1613 * Validates the input parameters for the create() method. 1614 * 1615 * @param array $hosts hosts data array 1616 * 1617 * @throws APIException if the input is invalid. 1618 */ 1619 protected function validateCreate(array $hosts) { 1620 $host_db_fields = ['host' => null]; 1621 1622 $groupids = []; 1623 1624 foreach ($hosts as &$host) { 1625 // Validate mandatory fields. 1626 if (!check_db_fields($host_db_fields, $host)) { 1627 self::exception(ZBX_API_ERROR_PARAMETERS, 1628 _s('Wrong fields for host "%1$s".', array_key_exists('host', $host) ? $host['host'] : '') 1629 ); 1630 } 1631 1632 // Validate "host" field. 1633 if (!preg_match('/^'.ZBX_PREG_HOST_FORMAT.'$/', $host['host'])) { 1634 self::exception(ZBX_API_ERROR_PARAMETERS, 1635 _s('Incorrect characters used for host name "%s".', $host['host']) 1636 ); 1637 } 1638 1639 // If visible name is not given or empty it should be set to host name. Required for duplicate checks. 1640 if (!array_key_exists('name', $host) || !trim($host['name'])) { 1641 $host['name'] = $host['host']; 1642 } 1643 1644 // Validate "groups" field. 1645 if (!array_key_exists('groups', $host) || !is_array($host['groups']) || !$host['groups']) { 1646 self::exception(ZBX_API_ERROR_PARAMETERS, 1647 _s('Host "%1$s" cannot be without host group.', $host['host']) 1648 ); 1649 } 1650 1651 $groupids = array_merge($groupids, zbx_objectValues($host['groups'], 'groupid')); 1652 } 1653 unset($host); 1654 1655 // Check for duplicate "host" and "name" fields. 1656 $duplicate = CArrayHelper::findDuplicate($hosts, 'host'); 1657 if ($duplicate) { 1658 self::exception(ZBX_API_ERROR_PARAMETERS, 1659 _s('Duplicate host. Host with the same host name "%s" already exists in data.', $duplicate['host']) 1660 ); 1661 } 1662 1663 $duplicate = CArrayHelper::findDuplicate($hosts, 'name'); 1664 if ($duplicate) { 1665 self::exception(ZBX_API_ERROR_PARAMETERS, 1666 _s('Duplicate host. Host with the same visible name "%s" already exists in data.', $duplicate['name']) 1667 ); 1668 } 1669 1670 // Validate permissions to host groups. 1671 if ($groupids) { 1672 $db_groups = API::HostGroup()->get([ 1673 'output' => ['groupid'], 1674 'groupids' => $groupids, 1675 'editable' => true, 1676 'preservekeys' => true 1677 ]); 1678 } 1679 1680 foreach ($hosts as $host) { 1681 foreach ($host['groups'] as $group) { 1682 if (!array_key_exists($group['groupid'], $db_groups)) { 1683 self::exception(ZBX_API_ERROR_PERMISSIONS, 1684 _('No permissions to referred object or it does not exist!') 1685 ); 1686 } 1687 } 1688 } 1689 1690 $inventory_fields = zbx_objectValues(getHostInventories(), 'db_field'); 1691 1692 $valid_inventory_modes = [HOST_INVENTORY_DISABLED, HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC]; 1693 $inventory_mode = new CLimitedSetValidator([ 1694 'values' => $valid_inventory_modes, 1695 'messageInvalid' => _s('Incorrect value for field "%1$s": %2$s.', 'inventory_mode', 1696 _s('value must be one of %1$s', implode(', ', $valid_inventory_modes))) 1697 ]); 1698 1699 $status_validator = new CLimitedSetValidator([ 1700 'values' => [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED], 1701 'messageInvalid' => _('Incorrect status for host "%1$s".') 1702 ]); 1703 1704 $host_names = []; 1705 1706 foreach ($hosts as $host) { 1707 if (!array_key_exists('interfaces', $host) || !is_array($host['interfaces']) || !$host['interfaces']) { 1708 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No interfaces for host "%s".', $host['host'])); 1709 } 1710 1711 if (array_key_exists('status', $host)) { 1712 $status_validator->setObjectName($host['host']); 1713 $this->checkValidator($host['status'], $status_validator); 1714 } 1715 1716 if (array_key_exists('inventory_mode', $host)) { 1717 $inventory_mode->setObjectName($host['host']); 1718 $this->checkValidator($host['inventory_mode'], $inventory_mode); 1719 } 1720 1721 if (array_key_exists('inventory', $host) && $host['inventory']) { 1722 if (array_key_exists('inventory_mode', $host) && $host['inventory_mode'] == HOST_INVENTORY_DISABLED) { 1723 self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot set inventory fields for disabled inventory.')); 1724 } 1725 1726 $fields = array_keys($host['inventory']); 1727 foreach ($fields as $field) { 1728 if (!in_array($field, $inventory_fields)) { 1729 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect inventory field "%s".', $field)); 1730 } 1731 } 1732 } 1733 1734 // Collect technical and visible names to check if they exist in hosts and templates. 1735 $host_names['host'][$host['host']] = true; 1736 $host_names['name'][$host['name']] = true; 1737 } 1738 1739 $filter = [ 1740 'host' => array_keys($host_names['host']), 1741 'name' => array_keys($host_names['name']) 1742 ]; 1743 1744 $hosts_exists = $this->get([ 1745 'output' => ['host', 'name'], 1746 'filter' => $filter, 1747 'searchByAny' => true, 1748 'nopermissions' => true 1749 ]); 1750 1751 foreach ($hosts_exists as $host_exists) { 1752 if (array_key_exists($host_exists['host'], $host_names['host'])) { 1753 self::exception(ZBX_API_ERROR_PARAMETERS, 1754 _s('Host with the same name "%s" already exists.', $host_exists['host']) 1755 ); 1756 } 1757 1758 if (array_key_exists($host_exists['name'], $host_names['name'])) { 1759 self::exception(ZBX_API_ERROR_PARAMETERS, 1760 _s('Host with the same visible name "%s" already exists.', $host_exists['name']) 1761 ); 1762 } 1763 } 1764 1765 $templates_exists = API::Template()->get([ 1766 'output' => ['host', 'name'], 1767 'filter' => $filter, 1768 'searchByAny' => true, 1769 'nopermissions' => true 1770 ]); 1771 1772 foreach ($templates_exists as $template_exists) { 1773 if (array_key_exists($template_exists['host'], $host_names['host'])) { 1774 self::exception(ZBX_API_ERROR_PARAMETERS, 1775 _s('Template with the same name "%s" already exists.', $template_exists['host']) 1776 ); 1777 } 1778 1779 if (array_key_exists($template_exists['name'], $host_names['name'])) { 1780 self::exception(ZBX_API_ERROR_PARAMETERS, 1781 _s('Template with the same visible name "%s" already exists.', $template_exists['name']) 1782 ); 1783 } 1784 } 1785 1786 $this->validateEncryption($hosts); 1787 } 1788 1789 /** 1790 * Validates the input parameters for the update() method. 1791 * 1792 * @param array $hosts hosts data array 1793 * @param array $db_hosts db hosts data array 1794 * 1795 * @throws APIException if the input is invalid. 1796 */ 1797 protected function validateUpdate(array $hosts, array $db_hosts) { 1798 $host_db_fields = ['hostid' => null]; 1799 1800 foreach ($hosts as $host) { 1801 // Validate mandatory fields. 1802 if (!check_db_fields($host_db_fields, $host)) { 1803 self::exception(ZBX_API_ERROR_PARAMETERS, 1804 _s('Wrong fields for host "%1$s".', array_key_exists('host', $host) ? $host['host'] : '') 1805 ); 1806 } 1807 1808 // Validate host permissions. 1809 if (!array_key_exists($host['hostid'], $db_hosts)) { 1810 self::exception(ZBX_API_ERROR_PARAMETERS, _( 1811 'No permissions to referred object or it does not exist!' 1812 )); 1813 } 1814 1815 // Validate "groups" field. 1816 if (array_key_exists('groups', $host) && (!is_array($host['groups']) || !$host['groups'])) { 1817 self::exception(ZBX_API_ERROR_PARAMETERS, 1818 _s('Host "%1$s" cannot be without host group.', $db_hosts[$host['hostid']]['host']) 1819 ); 1820 } 1821 1822 // Permissions to host groups is validated in massUpdate(). 1823 } 1824 1825 $inventory_fields = zbx_objectValues(getHostInventories(), 'db_field'); 1826 1827 $valid_inventory_modes = [HOST_INVENTORY_DISABLED, HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC]; 1828 $inventory_mode = new CLimitedSetValidator([ 1829 'values' => $valid_inventory_modes, 1830 'messageInvalid' => _s('Incorrect value for field "%1$s": %2$s.', 'inventory_mode', 1831 _s('value must be one of %1$s', implode(', ', $valid_inventory_modes))) 1832 ]); 1833 1834 $status_validator = new CLimitedSetValidator([ 1835 'values' => [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED], 1836 'messageInvalid' => _('Incorrect status for host "%1$s".') 1837 ]); 1838 1839 $update_discovered_validator = new CUpdateDiscoveredValidator([ 1840 'allowed' => ['hostid', 'status', 'inventory', 'description'], 1841 'messageAllowedField' => _('Cannot update "%2$s" for a discovered host "%1$s".') 1842 ]); 1843 1844 $host_names = []; 1845 1846 foreach ($hosts as &$host) { 1847 $db_host = $db_hosts[$host['hostid']]; 1848 $host_name = array_key_exists('host', $host) ? $host['host'] : $db_host['host']; 1849 1850 if (array_key_exists('status', $host)) { 1851 $status_validator->setObjectName($host_name); 1852 $this->checkValidator($host['status'], $status_validator); 1853 } 1854 1855 if (array_key_exists('inventory_mode', $host)) { 1856 $inventory_mode->setObjectName($host_name); 1857 $this->checkValidator($host['inventory_mode'], $inventory_mode); 1858 } 1859 1860 if (array_key_exists('inventory', $host) && $host['inventory']) { 1861 if (array_key_exists('inventory_mode', $host) && $host['inventory_mode'] == HOST_INVENTORY_DISABLED) { 1862 self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot set inventory fields for disabled inventory.')); 1863 } 1864 1865 $fields = array_keys($host['inventory']); 1866 foreach ($fields as $field) { 1867 if (!in_array($field, $inventory_fields)) { 1868 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect inventory field "%s".', $field)); 1869 } 1870 } 1871 } 1872 1873 // cannot update certain fields for discovered hosts 1874 $update_discovered_validator->setObjectName($host_name); 1875 $this->checkPartialValidator($host, $update_discovered_validator, $db_host); 1876 1877 if (array_key_exists('interfaces', $host)) { 1878 if (!is_array($host['interfaces']) || !$host['interfaces']) { 1879 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No interfaces for host "%s".', $host['host'])); 1880 } 1881 } 1882 1883 if (array_key_exists('host', $host)) { 1884 if (!preg_match('/^'.ZBX_PREG_HOST_FORMAT.'$/', $host['host'])) { 1885 self::exception(ZBX_API_ERROR_PARAMETERS, 1886 _s('Incorrect characters used for host name "%s".', $host['host']) 1887 ); 1888 } 1889 1890 if (array_key_exists('host', $host_names) && array_key_exists($host['host'], $host_names['host'])) { 1891 self::exception(ZBX_API_ERROR_PARAMETERS, 1892 _s('Duplicate host. Host with the same host name "%s" already exists in data.', $host['host']) 1893 ); 1894 } 1895 1896 $host_names['host'][$host['host']] = $host['hostid']; 1897 } 1898 1899 if (array_key_exists('name', $host)) { 1900 // if visible name is empty replace it with host name 1901 if (zbx_empty(trim($host['name']))) { 1902 if (!array_key_exists('host', $host)) { 1903 self::exception(ZBX_API_ERROR_PARAMETERS, 1904 _s('Visible name cannot be empty if host name is missing.') 1905 ); 1906 } 1907 $host['name'] = $host['host']; 1908 } 1909 1910 if (array_key_exists('name', $host_names) && array_key_exists($host['name'], $host_names['name'])) { 1911 self::exception(ZBX_API_ERROR_PARAMETERS, _s( 1912 'Duplicate host. Host with the same visible name "%s" already exists in data.', $host['name']) 1913 ); 1914 } 1915 $host_names['name'][$host['name']] = $host['hostid']; 1916 } 1917 1918 if (array_key_exists('tls_connect', $host) || array_key_exists('tls_accept', $host)) { 1919 $tls_connect = array_key_exists('tls_connect', $host) ? $host['tls_connect'] : $db_host['tls_connect']; 1920 $tls_accept = array_key_exists('tls_accept', $host) ? $host['tls_accept'] : $db_host['tls_accept']; 1921 1922 // Clean PSK fields. 1923 if ($tls_connect != HOST_ENCRYPTION_PSK && !($tls_accept & HOST_ENCRYPTION_PSK)) { 1924 if (!array_key_exists('tls_psk_identity', $host)) { 1925 $host['tls_psk_identity'] = ''; 1926 } 1927 if (!array_key_exists('tls_psk', $host)) { 1928 $host['tls_psk'] = ''; 1929 } 1930 } 1931 1932 // Clean certificate fields. 1933 if ($tls_connect != HOST_ENCRYPTION_CERTIFICATE && !($tls_accept & HOST_ENCRYPTION_CERTIFICATE)) { 1934 if (!array_key_exists('tls_issuer', $host)) { 1935 $host['tls_issuer'] = ''; 1936 } 1937 if (!array_key_exists('tls_subject', $host)) { 1938 $host['tls_subject'] = ''; 1939 } 1940 } 1941 } 1942 } 1943 unset($host); 1944 1945 if (array_key_exists('host', $host_names) || array_key_exists('name', $host_names)) { 1946 $filter = []; 1947 1948 if (array_key_exists('host', $host_names)) { 1949 $filter['host'] = array_keys($host_names['host']); 1950 } 1951 1952 if (array_key_exists('name', $host_names)) { 1953 $filter['name'] = array_keys($host_names['name']); 1954 } 1955 1956 $hosts_exists = $this->get([ 1957 'output' => ['hostid', 'host', 'name'], 1958 'filter' => $filter, 1959 'searchByAny' => true, 1960 'nopermissions' => true, 1961 'preservekeys' => true 1962 ]); 1963 1964 foreach ($hosts_exists as $host_exists) { 1965 if (array_key_exists('host', $host_names) && array_key_exists($host_exists['host'], $host_names['host']) 1966 && bccomp($host_exists['hostid'], $host_names['host'][$host_exists['host']]) != 0) { 1967 self::exception(ZBX_API_ERROR_PARAMETERS, 1968 _s('Host with the same name "%s" already exists.', $host_exists['host']) 1969 ); 1970 } 1971 1972 if (array_key_exists('name', $host_names) && array_key_exists($host_exists['name'], $host_names['name']) 1973 && bccomp($host_exists['hostid'], $host_names['name'][$host_exists['name']]) != 0) { 1974 self::exception(ZBX_API_ERROR_PARAMETERS, 1975 _s('Host with the same visible name "%s" already exists.', $host_exists['name']) 1976 ); 1977 } 1978 } 1979 1980 $templates_exists = API::Template()->get([ 1981 'output' => ['hostid', 'host', 'name'], 1982 'filter' => $filter, 1983 'searchByAny' => true, 1984 'nopermissions' => true, 1985 'preservekeys' => true 1986 ]); 1987 1988 foreach ($templates_exists as $template_exists) { 1989 if (array_key_exists('host', $host_names) 1990 && array_key_exists($template_exists['host'], $host_names['host']) 1991 && bccomp($template_exists['templateid'], $host_names['host'][$template_exists['host']]) != 0) { 1992 self::exception(ZBX_API_ERROR_PARAMETERS, 1993 _s('Template with the same name "%s" already exists.', $template_exists['host']) 1994 ); 1995 } 1996 1997 if (array_key_exists('name', $host_names) 1998 && array_key_exists($template_exists['name'], $host_names['name']) 1999 && bccomp($template_exists['templateid'], $host_names['name'][$template_exists['name']]) != 0) { 2000 self::exception(ZBX_API_ERROR_PARAMETERS, 2001 _s('Template with the same visible name "%s" already exists.', $template_exists['name']) 2002 ); 2003 } 2004 } 2005 } 2006 2007 $this->validateEncryption($hosts, $db_hosts); 2008 2009 return $hosts; 2010 } 2011} 2012