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 interfaces. 24 * 25 * @package API 26 */ 27class CHostInterface extends CApiService { 28 29 protected $tableName = 'interface'; 30 protected $tableAlias = 'hi'; 31 protected $sortColumns = ['interfaceid', 'dns', 'ip']; 32 33 /** 34 * Get interface data. 35 * 36 * @param array $options 37 * @param array $options['hostids'] Interface IDs 38 * @param bool $options['editable'] only with read-write permission. Ignored for SuperAdmins 39 * @param bool $options['selectHosts'] select Interface hosts 40 * @param bool $options['selectItems'] select Items 41 * @param int $options['count'] count Interfaces, returned column name is rowscount 42 * @param string $options['pattern'] search hosts by pattern in Interface name 43 * @param int $options['limit'] limit selection 44 * @param string $options['sortfield'] field to sort by 45 * @param string $options['sortorder'] sort order 46 * 47 * @return array|boolean Interface data as array or false if error 48 */ 49 public function get(array $options = []) { 50 $result = []; 51 52 $sqlParts = [ 53 'select' => ['interface' => 'hi.interfaceid'], 54 'from' => ['interface' => 'interface hi'], 55 'where' => [], 56 'group' => [], 57 'order' => [], 58 'limit' => null 59 ]; 60 61 $defOptions = [ 62 'groupids' => null, 63 'hostids' => null, 64 'interfaceids' => null, 65 'itemids' => null, 66 'triggerids' => null, 67 'editable' => false, 68 'nopermissions' => null, 69 // filter 70 'filter' => null, 71 'search' => null, 72 'searchByAny' => null, 73 'startSearch' => null, 74 'excludeSearch' => null, 75 'searchWildcardsEnabled' => null, 76 // output 77 'output' => API_OUTPUT_EXTEND, 78 'selectHosts' => null, 79 'selectItems' => null, 80 'countOutput' => null, 81 'groupCount' => null, 82 'preservekeys' => null, 83 'sortfield' => '', 84 'sortorder' => '', 85 'limit' => null, 86 'limitSelects' => null 87 ]; 88 $options = zbx_array_merge($defOptions, $options); 89 90 // editable + PERMISSION CHECK 91 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) { 92 $permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ; 93 $userGroups = getUserGroupsByUserId(self::$userData['userid']); 94 95 $sqlParts['where'][] = 'EXISTS ('. 96 'SELECT NULL'. 97 ' FROM hosts_groups hgg'. 98 ' JOIN rights r'. 99 ' ON r.id=hgg.groupid'. 100 ' AND '.dbConditionInt('r.groupid', $userGroups). 101 ' WHERE hi.hostid=hgg.hostid'. 102 ' GROUP BY hgg.hostid'. 103 ' HAVING MIN(r.permission)>'.PERM_DENY. 104 ' AND MAX(r.permission)>='.zbx_dbstr($permission). 105 ')'; 106 } 107 108 // interfaceids 109 if (!is_null($options['interfaceids'])) { 110 zbx_value2array($options['interfaceids']); 111 $sqlParts['where']['interfaceid'] = dbConditionInt('hi.interfaceid', $options['interfaceids']); 112 } 113 114 // hostids 115 if (!is_null($options['hostids'])) { 116 zbx_value2array($options['hostids']); 117 $sqlParts['where']['hostid'] = dbConditionInt('hi.hostid', $options['hostids']); 118 } 119 120 // itemids 121 if (!is_null($options['itemids'])) { 122 zbx_value2array($options['itemids']); 123 124 $sqlParts['from']['items'] = 'items i'; 125 $sqlParts['where'][] = dbConditionInt('i.itemid', $options['itemids']); 126 $sqlParts['where']['hi'] = 'hi.interfaceid=i.interfaceid'; 127 } 128 129 // triggerids 130 if (!is_null($options['triggerids'])) { 131 zbx_value2array($options['triggerids']); 132 133 $sqlParts['from']['functions'] = 'functions f'; 134 $sqlParts['from']['items'] = 'items i'; 135 $sqlParts['where'][] = dbConditionInt('f.triggerid', $options['triggerids']); 136 $sqlParts['where']['hi'] = 'hi.hostid=i.hostid'; 137 $sqlParts['where']['fi'] = 'f.itemid=i.itemid'; 138 } 139 140 // search 141 if (is_array($options['search'])) { 142 zbx_db_search('interface hi', $options, $sqlParts); 143 } 144 145 // filter 146 if (is_array($options['filter'])) { 147 $this->dbFilter('interface hi', $options, $sqlParts); 148 } 149 150 // limit 151 if (zbx_ctype_digit($options['limit']) && $options['limit']) { 152 $sqlParts['limit'] = $options['limit']; 153 } 154 155 $sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 156 $sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 157 $res = DBselect($this->createSelectQueryFromParts($sqlParts), $sqlParts['limit']); 158 while ($interface = DBfetch($res)) { 159 if (!is_null($options['countOutput'])) { 160 if (!is_null($options['groupCount'])) { 161 $result[] = $interface; 162 } 163 else { 164 $result = $interface['rowscount']; 165 } 166 } 167 else { 168 $result[$interface['interfaceid']] = $interface; 169 } 170 } 171 172 if (!is_null($options['countOutput'])) { 173 return $result; 174 } 175 176 if ($result) { 177 $result = $this->addRelatedObjects($options, $result); 178 $result = $this->unsetExtraFields($result, ['hostid'], $options['output']); 179 } 180 181 // removing keys (hash -> array) 182 if (is_null($options['preservekeys'])) { 183 $result = zbx_cleanHashes($result); 184 } 185 186 return $result; 187 } 188 189 /** 190 * Check interfaces input. 191 * 192 * @param array $interfaces 193 * @param string $method 194 */ 195 public function checkInput(array &$interfaces, $method) { 196 $update = ($method == 'update'); 197 198 // permissions 199 if ($update) { 200 $interfaceDBfields = ['interfaceid' => null]; 201 $dbInterfaces = $this->get([ 202 'output' => API_OUTPUT_EXTEND, 203 'interfaceids' => zbx_objectValues($interfaces, 'interfaceid'), 204 'editable' => true, 205 'preservekeys' => true 206 ]); 207 } 208 else { 209 $interfaceDBfields = [ 210 'hostid' => null, 211 'ip' => null, 212 'dns' => null, 213 'useip' => null, 214 'port' => null, 215 'main' => null 216 ]; 217 } 218 219 $dbHosts = API::Host()->get([ 220 'output' => ['host'], 221 'hostids' => zbx_objectValues($interfaces, 'hostid'), 222 'editable' => true, 223 'preservekeys' => true 224 ]); 225 226 $dbProxies = API::Proxy()->get([ 227 'output' => ['host'], 228 'proxyids' => zbx_objectValues($interfaces, 'hostid'), 229 'editable' => true, 230 'preservekeys' => true 231 ]); 232 233 $check_have_items = []; 234 foreach ($interfaces as &$interface) { 235 if (!check_db_fields($interfaceDBfields, $interface)) { 236 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); 237 } 238 239 if ($update) { 240 if (!isset($dbInterfaces[$interface['interfaceid']])) { 241 self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!')); 242 } 243 244 $dbInterface = $dbInterfaces[$interface['interfaceid']]; 245 if (isset($interface['hostid']) && bccomp($dbInterface['hostid'], $interface['hostid']) != 0) { 246 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Cannot switch host for interface.')); 247 } 248 249 if (array_key_exists('type', $interface) && $interface['type'] != $dbInterface['type']) { 250 $check_have_items[] = $interface['interfaceid']; 251 } 252 253 $interface['hostid'] = $dbInterface['hostid']; 254 255 // we check all fields on "updated" interface 256 $updInterface = $interface; 257 $interface = zbx_array_merge($dbInterface, $interface); 258 } 259 else { 260 if (!isset($dbHosts[$interface['hostid']]) && !isset($dbProxies[$interface['hostid']])) { 261 self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!')); 262 } 263 264 if (isset($dbProxies[$interface['hostid']])) { 265 $interface['type'] = INTERFACE_TYPE_UNKNOWN; 266 } 267 elseif (!isset($interface['type'])) { 268 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to method.')); 269 } 270 } 271 272 if (zbx_empty($interface['ip']) && zbx_empty($interface['dns'])) { 273 self::exception(ZBX_API_ERROR_PARAMETERS, _('IP and DNS cannot be empty for host interface.')); 274 } 275 276 if ($interface['useip'] == INTERFACE_USE_IP && zbx_empty($interface['ip'])) { 277 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Interface with DNS "%1$s" cannot have empty IP address.', $interface['dns'])); 278 } 279 280 if ($interface['useip'] == INTERFACE_USE_DNS && zbx_empty($interface['dns'])) { 281 if ($dbHosts && !empty($dbHosts[$interface['hostid']]['host'])) { 282 self::exception(ZBX_API_ERROR_PARAMETERS, 283 _s('Interface with IP "%1$s" cannot have empty DNS name while having "Use DNS" property on "%2$s".', 284 $interface['ip'], 285 $dbHosts[$interface['hostid']]['host'] 286 )); 287 } 288 elseif ($dbProxies && !empty($dbProxies[$interface['hostid']]['host'])) { 289 self::exception(ZBX_API_ERROR_PARAMETERS, 290 _s('Interface with IP "%1$s" cannot have empty DNS name while having "Use DNS" property on "%2$s".', 291 $interface['ip'], 292 $dbProxies[$interface['hostid']]['host'] 293 )); 294 } 295 else { 296 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Interface with IP "%1$s" cannot have empty DNS name.', $interface['ip'])); 297 } 298 } 299 300 if (isset($interface['dns'])) { 301 $this->checkDns($interface); 302 } 303 if (isset($interface['ip'])) { 304 $this->checkIp($interface); 305 } 306 if (isset($interface['port']) || $method == 'create') { 307 $this->checkPort($interface); 308 } 309 310 $this->checkBulk($interface); 311 312 if ($update) { 313 $interface = $updInterface; 314 } 315 } 316 unset($interface); 317 318 // check if any of the affected hosts are discovered 319 if ($update) { 320 $interfaces = $this->extendObjects('interface', $interfaces, ['hostid']); 321 322 if ($check_have_items) { 323 $this->checkIfInterfaceHasItems($check_have_items); 324 } 325 } 326 $this->checkValidator(zbx_objectValues($interfaces, 'hostid'), new CHostNormalValidator([ 327 'message' => _('Cannot update interface for discovered host "%1$s".') 328 ])); 329 } 330 331 /** 332 * Add interfaces. 333 * 334 * @param array $interfaces multidimensional array with Interfaces data 335 * 336 * @return array 337 */ 338 public function create(array $interfaces) { 339 $interfaces = zbx_toArray($interfaces); 340 341 $this->checkInput($interfaces, __FUNCTION__); 342 $this->checkMainInterfacesOnCreate($interfaces); 343 344 $interfaceIds = DB::insert('interface', $interfaces); 345 346 return ['interfaceids' => $interfaceIds]; 347 } 348 349 /** 350 * Update interfaces. 351 * 352 * @param array $interfaces multidimensional array with Interfaces data 353 * 354 * @return array 355 */ 356 public function update(array $interfaces) { 357 $interfaces = zbx_toArray($interfaces); 358 359 $this->checkInput($interfaces, __FUNCTION__); 360 $this->checkMainInterfacesOnUpdate($interfaces); 361 362 $data = []; 363 foreach ($interfaces as $interface) { 364 $data[] = [ 365 'values' => $interface, 366 'where' => ['interfaceid' => $interface['interfaceid']] 367 ]; 368 } 369 DB::update('interface', $data); 370 371 return ['interfaceids' => zbx_objectValues($interfaces, 'interfaceid')]; 372 } 373 374 protected function clearValues(array $interface) { 375 if (isset($interface['port']) && $interface['port'] != '') { 376 $interface['port'] = ltrim($interface['port'], '0'); 377 378 if ($interface['port'] == '') { 379 $interface['port'] = 0; 380 } 381 } 382 383 return $interface; 384 } 385 386 /** 387 * Delete interfaces. 388 * Interface cannot be deleted if it's main interface and exists other interface of same type on same host. 389 * Interface cannot be deleted if it is used in items. 390 * 391 * @param array $interfaceids 392 * 393 * @return array 394 */ 395 public function delete(array $interfaceids) { 396 if (empty($interfaceids)) { 397 self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); 398 } 399 400 $dbInterfaces = $this->get([ 401 'output' => API_OUTPUT_EXTEND, 402 'interfaceids' => $interfaceids, 403 'editable' => true, 404 'preservekeys' => true 405 ]); 406 foreach ($interfaceids as $interfaceId) { 407 if (!isset($dbInterfaces[$interfaceId])) { 408 self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!')); 409 } 410 } 411 412 $this->checkMainInterfacesOnDelete($interfaceids); 413 414 DB::delete('interface', ['interfaceid' => $interfaceids]); 415 416 return ['interfaceids' => $interfaceids]; 417 } 418 419 public function massAdd(array $data) { 420 $interfaces = zbx_toArray($data['interfaces']); 421 $hosts = zbx_toArray($data['hosts']); 422 423 $insertData = []; 424 foreach ($interfaces as $interface) { 425 foreach ($hosts as $host) { 426 $newInterface = $interface; 427 $newInterface['hostid'] = $host['hostid']; 428 429 $insertData[] = $newInterface; 430 } 431 } 432 433 $interfaceIds = $this->create($insertData); 434 435 return ['interfaceids' => $interfaceIds]; 436 } 437 438 protected function validateMassRemove(array $data) { 439 // check permissions 440 $this->checkHostPermissions($data['hostids']); 441 442 // check interfaces 443 foreach ($data['interfaces'] as $interface) { 444 if (!isset($interface['dns']) || !isset($interface['ip']) || !isset($interface['port'])) { 445 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); 446 } 447 448 $this->checkDns($interface); 449 $this->checkIp($interface); 450 $this->checkPort($interface); 451 $this->checkBulk($interface); 452 453 // check main interfaces 454 $interfacesToRemove = API::getApiService()->select($this->tableName(), [ 455 'output' => ['interfaceid'], 456 'filter' => [ 457 'hostid' => $data['hostids'], 458 'ip' => $interface['ip'], 459 'dns' => $interface['dns'], 460 'port' => $interface['port'], 461 'bulk' => $interface['bulk'] 462 ] 463 ]); 464 if ($interfacesToRemove) { 465 $this->checkMainInterfacesOnDelete(zbx_objectValues($interfacesToRemove, 'interfaceid')); 466 } 467 } 468 } 469 470 /** 471 * Remove hosts from interfaces. 472 * 473 * @param array $data 474 * @param array $data['interfaceids'] 475 * @param array $data['hostids'] 476 * @param array $data['templateids'] 477 * 478 * @return array 479 */ 480 public function massRemove(array $data) { 481 $data['interfaces'] = zbx_toArray($data['interfaces']); 482 $data['hostids'] = zbx_toArray($data['hostids']); 483 484 $this->validateMassRemove($data); 485 486 $interfaceIds = []; 487 foreach ($data['interfaces'] as $interface) { 488 $interfaces = $this->get([ 489 'output' => ['interfaceid'], 490 'filter' => [ 491 'hostid' => $data['hostids'], 492 'ip' => $interface['ip'], 493 'dns' => $interface['dns'], 494 'port' => $interface['port'], 495 'bulk' => $interface['bulk'] 496 ], 497 'editable' => true, 498 'preservekeys' => true 499 ]); 500 501 if ($interfaces) { 502 $interfaceIds = array_merge($interfaceIds, array_keys($interfaces)); 503 } 504 } 505 506 if ($interfaceIds) { 507 $interfaceIds = array_keys(array_flip($interfaceIds)); 508 DB::delete('interface', ['interfaceid' => $interfaceIds]); 509 } 510 511 return ['interfaceids' => $interfaceIds]; 512 } 513 514 /** 515 * Replace existing interfaces with input interfaces. 516 * 517 * @param array $host 518 */ 519 public function replaceHostInterfaces(array $host) { 520 if (isset($host['interfaces']) && !is_null($host['interfaces'])) { 521 $host['interfaces'] = zbx_toArray($host['interfaces']); 522 523 $this->checkHostInterfaces($host['interfaces'], $host['hostid']); 524 525 $interfacesToDelete = API::HostInterface()->get([ 526 'output' => [], 527 'hostids' => $host['hostid'], 528 'preservekeys' => true, 529 'nopermissions' => true 530 ]); 531 532 $interfacesToAdd = []; 533 $interfacesToUpdate = []; 534 535 foreach ($host['interfaces'] as $interface) { 536 $interface['hostid'] = $host['hostid']; 537 538 if (!isset($interface['interfaceid'])) { 539 $interfacesToAdd[] = $interface; 540 } 541 elseif (isset($interfacesToDelete[$interface['interfaceid']])) { 542 $interfacesToUpdate[] = $interface; 543 unset($interfacesToDelete[$interface['interfaceid']]); 544 } 545 } 546 547 if ($interfacesToUpdate) { 548 API::HostInterface()->checkInput($interfacesToUpdate, 'update'); 549 550 $data = []; 551 foreach ($interfacesToUpdate as $interface) { 552 $data[] = [ 553 'values' => $interface, 554 'where' => ['interfaceid' => $interface['interfaceid']] 555 ]; 556 } 557 DB::update('interface', $data); 558 } 559 560 if ($interfacesToAdd) { 561 $this->checkInput($interfacesToAdd, 'create'); 562 $interfaceids = DB::insert('interface', $interfacesToAdd); 563 564 foreach ($host['interfaces'] as &$interface) { 565 if (!array_key_exists('interfaceid', $interface)) { 566 $interface['interfaceid'] = array_shift($interfaceids); 567 } 568 } 569 unset($interface); 570 } 571 572 if ($interfacesToDelete) { 573 $this->delete(zbx_objectValues($interfacesToDelete, 'interfaceid')); 574 } 575 576 return ['interfaceids' => zbx_objectValues($host['interfaces'], 'interfaceid')]; 577 } 578 579 return ['interfaceids' => []]; 580 } 581 582 /** 583 * Validates the "dns" field. 584 * 585 * @throws APIException if the field is invalid. 586 * 587 * @param array $interface 588 * @param string $interface['dns'] 589 */ 590 protected function checkDns(array $interface) { 591 if ($interface['dns'] === '') { 592 return; 593 } 594 595 $user_macro_parser = new CUserMacroParser(); 596 597 if (!preg_match('/^'.ZBX_PREG_DNS_FORMAT.'$/', $interface['dns']) 598 && $user_macro_parser->parse($interface['dns']) != CParser::PARSE_SUCCESS) { 599 self::exception(ZBX_API_ERROR_PARAMETERS, 600 _s('Incorrect interface DNS parameter "%s" provided.', $interface['dns']) 601 ); 602 } 603 } 604 605 /** 606 * Validates the "ip" field. 607 * 608 * @throws APIException if the field is invalid. 609 * 610 * @param array $interface 611 * @param string $interface['ip'] 612 */ 613 protected function checkIp(array $interface) { 614 if ($interface['ip'] === '') { 615 return; 616 } 617 618 $user_macro_parser = new CUserMacroParser(); 619 620 if (preg_match('/^'.ZBX_PREG_MACRO_NAME_FORMAT.'$/', $interface['ip']) 621 || $user_macro_parser->parse($interface['ip']) == CParser::PARSE_SUCCESS) { 622 return; 623 } 624 625 $ipValidator = new CIPValidator(); 626 if (!$ipValidator->validate($interface['ip'])) { 627 self::exception(ZBX_API_ERROR_PARAMETERS, $ipValidator->getError()); 628 } 629 } 630 631 /** 632 * Validates the "port" field. 633 * 634 * @throws APIException if the field is empty or invalid. 635 * 636 * @param array $interface 637 */ 638 protected function checkPort(array $interface) { 639 if (!isset($interface['port']) || zbx_empty($interface['port'])) { 640 self::exception(ZBX_API_ERROR_PARAMETERS, _('Port cannot be empty for host interface.')); 641 } 642 elseif (!validatePortNumberOrMacro($interface['port'])) { 643 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect interface port "%s" provided.', $interface['port'])); 644 } 645 } 646 647 /** 648 * Checks if the current user has access to the given hosts. Assumes the "hostid" field is valid. 649 * 650 * @throws APIException if the user doesn't have write permissions for the given hosts 651 * 652 * @param array $hostIds an array of host IDs 653 */ 654 protected function checkHostPermissions(array $hostIds) { 655 if (!API::Host()->isWritable($hostIds)) { 656 self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!')); 657 } 658 } 659 660 /** 661 * Validates interface "bulk" field. 662 * For SNMP interfaces bulk value should be either 0 (disabled) or 1 (enabled). 663 * For other non-SNMP interfaces bulk value should be 1 (default). 664 * 665 * @throws APIException if bulk field is incorrect. 666 * 667 * @param array $interface 668 */ 669 protected function checkBulk(array $interface) { 670 if ($interface['type'] !== null && (($interface['type'] != INTERFACE_TYPE_SNMP && isset($interface['bulk']) 671 && $interface['bulk'] != SNMP_BULK_ENABLED) 672 || ($interface['type'] == INTERFACE_TYPE_SNMP && isset($interface['bulk']) 673 && (zbx_empty($interface['bulk']) 674 || ($interface['bulk'] != SNMP_BULK_DISABLED && $interface['bulk'] != SNMP_BULK_ENABLED))))) { 675 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect bulk value for interface.')); 676 } 677 } 678 679 private function checkHostInterfaces(array $interfaces, $hostid) { 680 $interfacesWithMissingData = []; 681 682 foreach ($interfaces as $interface) { 683 if (!isset($interface['type'], $interface['main'])) { 684 $interfacesWithMissingData[] = $interface['interfaceid']; 685 } 686 } 687 688 if ($interfacesWithMissingData) { 689 $dbInterfaces = API::HostInterface()->get([ 690 'interfaceids' => $interfacesWithMissingData, 691 'output' => ['main', 'type'], 692 'preservekeys' => true, 693 'nopermissions' => true 694 ]); 695 } 696 697 foreach ($interfaces as $id => $interface) { 698 if (isset($interface['interfaceid']) && isset($dbInterfaces[$interface['interfaceid']])) { 699 $interfaces[$id] = array_merge($interface, $dbInterfaces[$interface['interfaceid']]); 700 } 701 $interfaces[$id]['hostid'] = $hostid; 702 } 703 704 $this->checkMainInterfaces($interfaces); 705 } 706 707 private function checkMainInterfacesOnCreate(array $interfaces) { 708 $hostIds = []; 709 foreach ($interfaces as $interface) { 710 $hostIds[$interface['hostid']] = $interface['hostid']; 711 } 712 713 $dbInterfaces = API::HostInterface()->get([ 714 'hostids' => $hostIds, 715 'output' => ['hostid', 'main', 'type'], 716 'preservekeys' => true, 717 'nopermissions' => true 718 ]); 719 $interfaces = array_merge($dbInterfaces, $interfaces); 720 721 $this->checkMainInterfaces($interfaces); 722 } 723 724 private function checkMainInterfacesOnUpdate(array $interfaces) { 725 $interfaceidsWithoutHostIds = []; 726 727 // gather all hostids where interfaces should be checked 728 foreach ($interfaces as $interface) { 729 if (isset($interface ['type']) || isset($interface['main'])) { 730 if (isset($interface['hostid'])) { 731 $hostids[$interface['hostid']] = $interface['hostid']; 732 } 733 else { 734 $interfaceidsWithoutHostIds[] = $interface['interfaceid']; 735 } 736 } 737 } 738 739 // gather missing host ids 740 $hostIds = []; 741 if ($interfaceidsWithoutHostIds) { 742 $dbResult = DBselect('SELECT DISTINCT i.hostid FROM interface i WHERE '.dbConditionInt('i.interfaceid', $interfaceidsWithoutHostIds)); 743 while ($hostData = DBfetch($dbResult)) { 744 $hostIds[$hostData['hostid']] = $hostData['hostid']; 745 } 746 } 747 748 $dbInterfaces = API::HostInterface()->get([ 749 'hostids' => $hostIds, 750 'output' => ['hostid', 'main', 'type'], 751 'preservekeys' => true, 752 'nopermissions' => true 753 ]); 754 755 // update interfaces from DB with data that will be updated. 756 foreach ($interfaces as $interface) { 757 if (isset($dbInterfaces[$interface['interfaceid']])) { 758 $dbInterfaces[$interface['interfaceid']] = array_merge( 759 $dbInterfaces[$interface['interfaceid']], 760 $interfaces[$interface['interfaceid']] 761 ); 762 } 763 } 764 765 $this->checkMainInterfaces($dbInterfaces); 766 } 767 768 private function checkMainInterfacesOnDelete(array $interfaceIds) { 769 $this->checkIfInterfaceHasItems($interfaceIds); 770 771 $hostids = []; 772 $dbResult = DBselect('SELECT DISTINCT i.hostid FROM interface i WHERE '.dbConditionInt('i.interfaceid', $interfaceIds)); 773 while ($hostData = DBfetch($dbResult)) { 774 $hostids[$hostData['hostid']] = $hostData['hostid']; 775 } 776 777 $dbInterfaces = API::HostInterface()->get([ 778 'hostids' => $hostids, 779 'output' => ['hostid', 'main', 'type'], 780 'preservekeys' => true, 781 'nopermissions' => true 782 ]); 783 784 foreach ($interfaceIds as $interfaceId) { 785 unset($dbInterfaces[$interfaceId]); 786 } 787 788 $this->checkMainInterfaces($dbInterfaces); 789 } 790 791 /** 792 * Check if main interfaces are correctly set for every interface type. 793 * Each host must either have only one main interface for each interface type, or have no interface of that type at all. 794 * 795 * @param array $interfaces 796 */ 797 private function checkMainInterfaces(array $interfaces) { 798 $interfaceTypes = []; 799 foreach ($interfaces as $interface) { 800 if (!isset($interfaceTypes[$interface['hostid']])) { 801 $interfaceTypes[$interface['hostid']] = []; 802 } 803 804 if (!isset($interfaceTypes[$interface['hostid']][$interface['type']])) { 805 $interfaceTypes[$interface['hostid']][$interface['type']] = ['main' => 0, 'all' => 0]; 806 } 807 808 if ($interface['main'] == INTERFACE_PRIMARY) { 809 $interfaceTypes[$interface['hostid']][$interface['type']]['main']++; 810 } 811 else { 812 $interfaceTypes[$interface['hostid']][$interface['type']]['all']++; 813 } 814 } 815 816 foreach ($interfaceTypes as $interfaceHostId => $interfaceType) { 817 foreach ($interfaceType as $type => $counters) { 818 if ($counters['all'] && !$counters['main']) { 819 $host = API::Host()->get([ 820 'hostids' => $interfaceHostId, 821 'output' => ['name'], 822 'preservekeys' => true, 823 'nopermissions' => true 824 ]); 825 $host = reset($host); 826 827 self::exception(ZBX_API_ERROR_PARAMETERS, 828 _s('No default interface for "%1$s" type on "%2$s".', hostInterfaceTypeNumToName($type), $host['name'])); 829 } 830 831 if ($counters['main'] > 1) { 832 self::exception(ZBX_API_ERROR_PARAMETERS, _('Host cannot have more than one default interface of the same type.')); 833 } 834 } 835 } 836 } 837 838 private function checkIfInterfaceHasItems(array $interfaceIds) { 839 $items = API::Item()->get([ 840 'output' => ['name'], 841 'selectHosts' => ['name'], 842 'interfaceids' => $interfaceIds, 843 'preservekeys' => true, 844 'nopermissions' => true, 845 'limit' => 1 846 ]); 847 848 foreach ($items as $item) { 849 $host = reset($item['hosts']); 850 851 self::exception(ZBX_API_ERROR_PARAMETERS, 852 _s('Interface is linked to item "%1$s" on "%2$s".', $item['name'], $host['name'])); 853 } 854 } 855 856 protected function applyQueryOutputOptions($tableName, $tableAlias, array $options, array $sqlParts) { 857 $sqlParts = parent::applyQueryOutputOptions($tableName, $tableAlias, $options, $sqlParts); 858 859 if ($options['countOutput'] === null && $options['selectHosts'] !== null) { 860 $sqlParts = $this->addQuerySelect('hi.hostid', $sqlParts); 861 } 862 863 return $sqlParts; 864 } 865 866 protected function addRelatedObjects(array $options, array $result) { 867 $result = parent::addRelatedObjects($options, $result); 868 869 $interfaceIds = array_keys($result); 870 871 // adding hosts 872 if ($options['selectHosts'] !== null && $options['selectHosts'] != API_OUTPUT_COUNT) { 873 $relationMap = $this->createRelationMap($result, 'interfaceid', 'hostid'); 874 $hosts = API::Host()->get([ 875 'output' => $options['selectHosts'], 876 'hosts' => $relationMap->getRelatedIds(), 877 'preservekeys' => true 878 ]); 879 $result = $relationMap->mapMany($result, $hosts, 'hosts'); 880 } 881 882 // adding items 883 if ($options['selectItems'] !== null) { 884 if ($options['selectItems'] != API_OUTPUT_COUNT) { 885 $items = API::Item()->get([ 886 'output' => $this->outputExtend($options['selectItems'], ['itemid', 'interfaceid']), 887 'interfaceids' => $interfaceIds, 888 'nopermissions' => true, 889 'preservekeys' => true, 890 'filter' => ['flags' => null] 891 ]); 892 $relationMap = $this->createRelationMap($items, 'interfaceid', 'itemid'); 893 894 $items = $this->unsetExtraFields($items, ['interfaceid', 'itemid'], $options['selectItems']); 895 $result = $relationMap->mapMany($result, $items, 'items', $options['limitSelects']); 896 } 897 else { 898 $items = API::Item()->get([ 899 'interfaceids' => $interfaceIds, 900 'nopermissions' => true, 901 'filter' => ['flags' => null], 902 'countOutput' => true, 903 'groupCount' => true 904 ]); 905 $items = zbx_toHash($items, 'interfaceid'); 906 foreach ($result as $interfaceId => $interface) { 907 $result[$interfaceId]['items'] = isset($items[$interfaceId]) ? $items[$interfaceId]['rowscount'] : 0; 908 } 909 } 910 } 911 912 return $result; 913 } 914} 915