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 actions. 24 * 25 * @package API 26 */ 27class CAction extends CApiService { 28 29 protected $tableName = 'actions'; 30 protected $tableAlias = 'a'; 31 protected $sortColumns = ['actionid', 'name', 'status']; 32 33 /** 34 * Get actions data 35 * 36 * @param array $options 37 * @param array $options['itemids'] 38 * @param array $options['hostids'] 39 * @param array $options['groupids'] 40 * @param array $options['actionids'] 41 * @param array $options['applicationids'] 42 * @param array $options['status'] 43 * @param bool $options['editable'] 44 * @param array $options['extendoutput'] 45 * @param array $options['count'] 46 * @param array $options['pattern'] 47 * @param array $options['limit'] 48 * @param array $options['order'] 49 * 50 * @return array|int item data as array or false if error 51 */ 52 public function get($options = []) { 53 $result = []; 54 55 $sqlParts = [ 56 'select' => ['actions' => 'a.actionid'], 57 'from' => ['actions' => 'actions a'], 58 'where' => [], 59 'order' => [], 60 'limit' => null 61 ]; 62 63 $defOptions = [ 64 'groupids' => null, 65 'hostids' => null, 66 'actionids' => null, 67 'triggerids' => null, 68 'mediatypeids' => null, 69 'usrgrpids' => null, 70 'userids' => null, 71 'scriptids' => null, 72 'nopermissions' => null, 73 'editable' => false, 74 // filter 75 'filter' => null, 76 'search' => null, 77 'searchByAny' => null, 78 'startSearch' => null, 79 'excludeSearch' => null, 80 'searchWildcardsEnabled' => null, 81 // output 82 'output' => API_OUTPUT_EXTEND, 83 'selectFilter' => null, 84 'selectOperations' => null, 85 'countOutput' => null, 86 'preservekeys' => null, 87 'sortfield' => '', 88 'sortorder' => '', 89 'limit' => null 90 ]; 91 $options = zbx_array_merge($defOptions, $options); 92 93 // editable + PERMISSION CHECK 94 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) { 95 // conditions are checked here by sql, operations after, by api queries 96 $permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ; 97 $userGroups = getUserGroupsByUserId(self::$userData['userid']); 98 99 // condition hostgroup 100 $sqlParts['where'][] = 'NOT EXISTS ('. 101 'SELECT NULL'. 102 ' FROM conditions cc'. 103 ' LEFT JOIN rights r'. 104 ' ON r.id='.zbx_dbcast_2bigint('cc.value'). 105 ' AND '.dbConditionInt('r.groupid', $userGroups). 106 ' WHERE a.actionid=cc.actionid'. 107 ' AND cc.conditiontype='.CONDITION_TYPE_HOST_GROUP. 108 ' GROUP BY cc.value'. 109 ' HAVING MIN(r.permission) IS NULL'. 110 ' OR MIN(r.permission)='.PERM_DENY. 111 ' OR MAX(r.permission)<'.zbx_dbstr($permission). 112 ')'; 113 114 // condition host or template 115 $sqlParts['where'][] = 'NOT EXISTS ('. 116 'SELECT NULL'. 117 ' FROM conditions cc,hosts_groups hgg'. 118 ' LEFT JOIN rights r'. 119 ' ON r.id=hgg.groupid'. 120 ' AND '.dbConditionInt('r.groupid', $userGroups). 121 ' WHERE a.actionid=cc.actionid'. 122 ' AND '.zbx_dbcast_2bigint('cc.value').'=hgg.hostid'. 123 ' AND cc.conditiontype IN ('.CONDITION_TYPE_HOST.','.CONDITION_TYPE_TEMPLATE.')'. 124 ' GROUP BY cc.value'. 125 ' HAVING MIN(r.permission) IS NULL'. 126 ' OR MIN(r.permission)='.PERM_DENY. 127 ' OR MAX(r.permission)<'.zbx_dbstr($permission). 128 ')'; 129 130 // condition trigger 131 $sqlParts['where'][] = 'NOT EXISTS ('. 132 'SELECT NULL'. 133 ' FROM conditions cc,functions f,items i,hosts_groups hgg'. 134 ' LEFT JOIN rights r'. 135 ' ON r.id=hgg.groupid'. 136 ' AND '.dbConditionInt('r.groupid', $userGroups). 137 ' WHERE a.actionid=cc.actionid'. 138 ' AND '.zbx_dbcast_2bigint('cc.value').'=f.triggerid'. 139 ' AND f.itemid=i.itemid'. 140 ' AND i.hostid=hgg.hostid'. 141 ' AND cc.conditiontype='.CONDITION_TYPE_TRIGGER. 142 ' GROUP BY cc.value'. 143 ' HAVING MIN(r.permission) IS NULL'. 144 ' OR MIN(r.permission)='.PERM_DENY. 145 ' OR MAX(r.permission)<'.zbx_dbstr($permission). 146 ')'; 147 } 148 149 // actionids 150 if (!is_null($options['actionids'])) { 151 zbx_value2array($options['actionids']); 152 153 $sqlParts['where'][] = dbConditionInt('a.actionid', $options['actionids']); 154 } 155 156 // groupids 157 if (!is_null($options['groupids'])) { 158 zbx_value2array($options['groupids']); 159 160 $sqlParts['from']['conditions_groups'] = 'conditions cg'; 161 $sqlParts['where'][] = dbConditionString('cg.value', $options['groupids']); 162 $sqlParts['where']['ctg'] = 'cg.conditiontype='.CONDITION_TYPE_HOST_GROUP; 163 $sqlParts['where']['acg'] = 'a.actionid=cg.actionid'; 164 } 165 166 // hostids 167 if (!is_null($options['hostids'])) { 168 zbx_value2array($options['hostids']); 169 170 $sqlParts['from']['conditions_hosts'] = 'conditions ch'; 171 $sqlParts['where'][] = dbConditionString('ch.value', $options['hostids']); 172 $sqlParts['where']['cth'] = 'ch.conditiontype='.CONDITION_TYPE_HOST; 173 $sqlParts['where']['ach'] = 'a.actionid=ch.actionid'; 174 } 175 176 // triggerids 177 if (!is_null($options['triggerids'])) { 178 zbx_value2array($options['triggerids']); 179 180 $sqlParts['from']['conditions_triggers'] = 'conditions ct'; 181 $sqlParts['where'][] = dbConditionString('ct.value', $options['triggerids']); 182 $sqlParts['where']['ctt'] = 'ct.conditiontype='.CONDITION_TYPE_TRIGGER; 183 $sqlParts['where']['act'] = 'a.actionid=ct.actionid'; 184 } 185 186 // mediatypeids 187 if (!is_null($options['mediatypeids'])) { 188 zbx_value2array($options['mediatypeids']); 189 190 $sqlParts['from']['opmessage'] = 'opmessage om'; 191 $sqlParts['from']['operations_media'] = 'operations omed'; 192 $sqlParts['where'][] = dbConditionInt('om.mediatypeid', $options['mediatypeids']); 193 $sqlParts['where']['aomed'] = 'a.actionid=omed.actionid'; 194 $sqlParts['where']['oom'] = 'omed.operationid=om.operationid'; 195 } 196 197 // operation messages 198 // usrgrpids 199 if (!is_null($options['usrgrpids'])) { 200 zbx_value2array($options['usrgrpids']); 201 202 $sqlParts['from']['opmessage_grp'] = 'opmessage_grp omg'; 203 $sqlParts['from']['operations_usergroups'] = 'operations oug'; 204 $sqlParts['where'][] = dbConditionInt('omg.usrgrpid', $options['usrgrpids']); 205 $sqlParts['where']['aoug'] = 'a.actionid=oug.actionid'; 206 $sqlParts['where']['oomg'] = 'oug.operationid=omg.operationid'; 207 } 208 209 // userids 210 if (!is_null($options['userids'])) { 211 zbx_value2array($options['userids']); 212 213 $sqlParts['from']['opmessage_usr'] = 'opmessage_usr omu'; 214 $sqlParts['from']['operations_users'] = 'operations ou'; 215 $sqlParts['where'][] = dbConditionInt('omu.userid', $options['userids']); 216 $sqlParts['where']['aou'] = 'a.actionid=ou.actionid'; 217 $sqlParts['where']['oomu'] = 'ou.operationid=omu.operationid'; 218 } 219 220 // operation commands 221 // scriptids 222 if (!is_null($options['scriptids'])) { 223 zbx_value2array($options['scriptids']); 224 225 $sqlParts['from']['opcommand'] = 'opcommand oc'; 226 $sqlParts['from']['operations_scripts'] = 'operations os'; 227 $sqlParts['where'][] = '('.dbConditionInt('oc.scriptid', $options['scriptids']). 228 ' AND oc.type='.ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT.')'; 229 $sqlParts['where']['aos'] = 'a.actionid=os.actionid'; 230 $sqlParts['where']['ooc'] = 'os.operationid=oc.operationid'; 231 } 232 233 // filter 234 if (is_array($options['filter'])) { 235 $this->dbFilter('actions a', $options, $sqlParts); 236 } 237 238 // search 239 if (is_array($options['search'])) { 240 zbx_db_search('actions a', $options, $sqlParts); 241 } 242 243 // limit 244 if (zbx_ctype_digit($options['limit']) && $options['limit']) { 245 $sqlParts['limit'] = $options['limit']; 246 } 247 248 $actionIds = []; 249 250 $sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 251 $sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 252 $dbRes = DBselect($this->createSelectQueryFromParts($sqlParts), $sqlParts['limit']); 253 while ($action = DBfetch($dbRes)) { 254 if ($options['countOutput']) { 255 $result = $action['rowscount']; 256 } 257 else { 258 $actionIds[$action['actionid']] = $action['actionid']; 259 260 $result[$action['actionid']] = $action; 261 } 262 } 263 264 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) { 265 // check hosts, templates 266 $hosts = []; 267 $hostIds = []; 268 $sql = 'SELECT o.actionid,och.hostid'. 269 ' FROM operations o,opcommand_hst och'. 270 ' WHERE o.operationid=och.operationid'. 271 ' AND och.hostid<>0'. 272 ' AND '.dbConditionInt('o.actionid', $actionIds); 273 $dbHosts = DBselect($sql); 274 while ($host = DBfetch($dbHosts)) { 275 if (!isset($hosts[$host['hostid']])) { 276 $hosts[$host['hostid']] = []; 277 } 278 $hosts[$host['hostid']][$host['actionid']] = $host['actionid']; 279 $hostIds[$host['hostid']] = $host['hostid']; 280 } 281 282 $dbTemplates = DBselect( 283 'SELECT o.actionid,ot.templateid'. 284 ' FROM operations o,optemplate ot'. 285 ' WHERE o.operationid=ot.operationid'. 286 ' AND '.dbConditionInt('o.actionid', $actionIds) 287 ); 288 while ($template = DBfetch($dbTemplates)) { 289 if (!isset($hosts[$template['templateid']])) { 290 $hosts[$template['templateid']] = []; 291 } 292 $hosts[$template['templateid']][$template['actionid']] = $template['actionid']; 293 $hostIds[$template['templateid']] = $template['templateid']; 294 } 295 296 $allowedHosts = API::Host()->get([ 297 'hostids' => $hostIds, 298 'output' => ['hostid'], 299 'editable' => $options['editable'], 300 'templated_hosts' => true, 301 'preservekeys' => true 302 ]); 303 foreach ($hostIds as $hostId) { 304 if (isset($allowedHosts[$hostId])) { 305 continue; 306 } 307 foreach ($hosts[$hostId] as $actionId) { 308 unset($result[$actionId], $actionIds[$actionId]); 309 } 310 } 311 unset($allowedHosts); 312 313 // check hostgroups 314 $groups = []; 315 $groupIds = []; 316 $dbGroups = DBselect( 317 'SELECT o.actionid,ocg.groupid'. 318 ' FROM operations o,opcommand_grp ocg'. 319 ' WHERE o.operationid=ocg.operationid'. 320 ' AND '.dbConditionInt('o.actionid', $actionIds) 321 ); 322 while ($group = DBfetch($dbGroups)) { 323 if (!isset($groups[$group['groupid']])) { 324 $groups[$group['groupid']] = []; 325 } 326 $groups[$group['groupid']][$group['actionid']] = $group['actionid']; 327 $groupIds[$group['groupid']] = $group['groupid']; 328 } 329 330 $dbGroups = DBselect( 331 'SELECT o.actionid,og.groupid'. 332 ' FROM operations o,opgroup og'. 333 ' WHERE o.operationid=og.operationid'. 334 ' AND '.dbConditionInt('o.actionid', $actionIds) 335 ); 336 while ($group = DBfetch($dbGroups)) { 337 if (!isset($groups[$group['groupid']])) { 338 $groups[$group['groupid']] = []; 339 } 340 $groups[$group['groupid']][$group['actionid']] = $group['actionid']; 341 $groupIds[$group['groupid']] = $group['groupid']; 342 } 343 344 $allowedGroups = API::HostGroup()->get([ 345 'groupids' => $groupIds, 346 'output' => ['groupid'], 347 'editable' => $options['editable'], 348 'preservekeys' => true 349 ]); 350 foreach ($groupIds as $groupId) { 351 if (isset($allowedGroups[$groupId])) { 352 continue; 353 } 354 foreach ($groups[$groupId] as $actionId) { 355 unset($result[$actionId], $actionIds[$actionId]); 356 } 357 } 358 unset($allowedGroups); 359 360 // check scripts 361 $scripts = []; 362 $scriptIds = []; 363 $dbScripts = DBselect( 364 'SELECT o.actionid,oc.scriptid'. 365 ' FROM operations o,opcommand oc'. 366 ' WHERE o.operationid=oc.operationid'. 367 ' AND '.dbConditionInt('o.actionid', $actionIds). 368 ' AND oc.type='.ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT 369 ); 370 while ($script = DBfetch($dbScripts)) { 371 if (!isset($scripts[$script['scriptid']])) { 372 $scripts[$script['scriptid']] = []; 373 } 374 $scripts[$script['scriptid']][$script['actionid']] = $script['actionid']; 375 $scriptIds[$script['scriptid']] = $script['scriptid']; 376 } 377 378 $allowedScripts = API::Script()->get([ 379 'scriptids' => $scriptIds, 380 'output' => ['scriptid'], 381 'preservekeys' => true 382 ]); 383 foreach ($scriptIds as $scriptId) { 384 if (isset($allowedScripts[$scriptId])) { 385 continue; 386 } 387 foreach ($scripts[$scriptId] as $actionId) { 388 unset($result[$actionId], $actionIds[$actionId]); 389 } 390 } 391 unset($allowedScripts); 392 393 // check users 394 $users = []; 395 $userIds = []; 396 $dbUsers = DBselect( 397 'SELECT o.actionid,omu.userid'. 398 ' FROM operations o,opmessage_usr omu'. 399 ' WHERE o.operationid=omu.operationid'. 400 ' AND '.dbConditionInt('o.actionid', $actionIds) 401 ); 402 while ($user = DBfetch($dbUsers)) { 403 if (!isset($users[$user['userid']])) { 404 $users[$user['userid']] = []; 405 } 406 $users[$user['userid']][$user['actionid']] = $user['actionid']; 407 $userIds[$user['userid']] = $user['userid']; 408 } 409 410 $allowedUsers = API::User()->get([ 411 'userids' => $userIds, 412 'output' => ['userid'], 413 'preservekeys' => true 414 ]); 415 foreach ($userIds as $userId) { 416 if (isset($allowedUsers[$userId])) { 417 continue; 418 } 419 foreach ($users[$userId] as $actionId) { 420 unset($result[$actionId], $actionIds[$actionId]); 421 } 422 } 423 424 // check usergroups 425 $userGroups = []; 426 $userGroupIds = []; 427 $dbUserGroups = DBselect( 428 'SELECT o.actionid,omg.usrgrpid'. 429 ' FROM operations o,opmessage_grp omg'. 430 ' WHERE o.operationid=omg.operationid'. 431 ' AND '.dbConditionInt('o.actionid', $actionIds) 432 ); 433 while ($userGroup = DBfetch($dbUserGroups)) { 434 if (!isset($userGroups[$userGroup['usrgrpid']])) { 435 $userGroups[$userGroup['usrgrpid']] = []; 436 } 437 $userGroups[$userGroup['usrgrpid']][$userGroup['actionid']] = $userGroup['actionid']; 438 $userGroupIds[$userGroup['usrgrpid']] = $userGroup['usrgrpid']; 439 } 440 441 $allowedUserGroups = API::UserGroup()->get([ 442 'usrgrpids' => $userGroupIds, 443 'output' => ['usrgrpid'], 444 'preservekeys' => true 445 ]); 446 447 foreach ($userGroupIds as $userGroupId) { 448 if (isset($allowedUserGroups[$userGroupId])) { 449 continue; 450 } 451 foreach ($userGroups[$userGroupId] as $actionId) { 452 unset($result[$actionId], $actionIds[$actionId]); 453 } 454 } 455 } 456 457 if (!is_null($options['countOutput'])) { 458 return $result; 459 } 460 461 if ($result) { 462 $result = $this->addRelatedObjects($options, $result); 463 464 foreach ($result as &$action) { 465 // unset the fields that are returned in the filter 466 unset($action['formula'], $action['evaltype']); 467 468 if ($options['selectFilter'] !== null) { 469 $filter = $this->unsetExtraFields( 470 [$action['filter']], 471 ['conditions', 'formula', 'evaltype'], 472 $options['selectFilter'] 473 ); 474 $filter = reset($filter); 475 476 if (isset($filter['conditions'])) { 477 foreach ($filter['conditions'] as &$condition) { 478 unset($condition['actionid'], $condition['conditionid']); 479 } 480 unset($condition); 481 } 482 483 $action['filter'] = $filter; 484 } 485 } 486 unset($action); 487 } 488 489 // removing keys (hash -> array) 490 if (is_null($options['preservekeys'])) { 491 $result = zbx_cleanHashes($result); 492 } 493 494 return $result; 495 } 496 497 /** 498 * Add actions 499 * 500 * @param array $actions multidimensional array with actions data 501 * @param array $actions[0,...]['expression'] 502 * @param array $actions[0,...]['description'] 503 * @param array $actions[0,...]['type'] OPTIONAL 504 * @param array $actions[0,...]['priority'] OPTIONAL 505 * @param array $actions[0,...]['status'] OPTIONAL 506 * @param array $actions[0,...]['comments'] OPTIONAL 507 * @param array $actions[0,...]['url'] OPTIONAL 508 * @param array $actions[0,...]['filter'] OPTIONAL 509 * @return boolean 510 */ 511 public function create($actions) { 512 $actions = zbx_toArray($actions); 513 514 $this->validateCreate($actions); 515 516 // Set "evaltype" if specified in "filter" section of action. 517 foreach ($actions as &$action) { 518 if (isset($action['filter'])) { 519 $action['evaltype'] = $action['filter']['evaltype']; 520 } 521 } 522 unset($action); 523 524 // Insert actions into db, get back array with new actionids. 525 $actions = DB::save('actions', $actions); 526 $actions = zbx_toHash($actions, 'actionid'); 527 528 $conditionsToCreate = []; 529 $operationsToCreate = []; 530 // Collect conditions and operations to be created and set appropriate action ID. 531 foreach ($actions as $actionId => &$action) { 532 if (isset($action['filter'])) { 533 foreach ($action['filter']['conditions'] as $condition) { 534 $condition['actionid'] = $actionId; 535 $conditionsToCreate[] = $condition; 536 } 537 } 538 539 foreach ($action['operations'] as $operation) { 540 $operation['actionid'] = $actionId; 541 $operationsToCreate[] = $operation; 542 } 543 } 544 unset($action); 545 546 $createdConditions = $this->addConditions($conditionsToCreate); 547 548 // Group back created action conditions by action ID to be used for updating action formula. 549 $conditionsForActions = []; 550 foreach ($createdConditions as $condition) { 551 $conditionsForActions[$condition['actionid']][$condition['conditionid']] = $condition; 552 } 553 554 // Update "formula" field if evaltype is custom expression. 555 foreach ($actions as $actionId => $action) { 556 if (isset($action['filter'])) { 557 $actionFilter = $action['filter']; 558 if ($actionFilter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 559 $this->updateFormula($actionId, $actionFilter['formula'], $conditionsForActions[$actionId]); 560 } 561 } 562 } 563 564 // Add operations. 565 $this->addOperations($operationsToCreate); 566 567 return ['actionids' => array_keys($actions)]; 568 } 569 570 /** 571 * Update actions 572 * 573 * @param array $actions multidimensional array with actions data 574 * @param array $actions[0,...]['actionid'] 575 * @param array $actions[0,...]['expression'] 576 * @param array $actions[0,...]['description'] 577 * @param array $actions[0,...]['type'] OPTIONAL 578 * @param array $actions[0,...]['priority'] OPTIONAL 579 * @param array $actions[0,...]['status'] OPTIONAL 580 * @param array $actions[0,...]['comments'] OPTIONAL 581 * @param array $actions[0,...]['url'] OPTIONAL 582 * @param array $actions[0,...]['filter'] OPTIONAL 583 * @return boolean 584 */ 585 public function update($actions) { 586 $actions = zbx_toArray($actions); 587 $actions = zbx_toHash($actions, 'actionid'); 588 $actionIds = array_keys($actions); 589 590 $actionsDb = $this->get([ 591 'actionids' => $actionIds, 592 'editable' => true, 593 'output' => API_OUTPUT_EXTEND, 594 'preservekeys' => true, 595 'selectOperations' => API_OUTPUT_EXTEND, 596 'selectFilter' => ['formula', 'conditions'] 597 ]); 598 599 $this->validateUpdate($actions, $actionsDb); 600 601 $operationsToCreate = []; 602 $operationsToUpdate = []; 603 $operationIdsForDelete = []; 604 605 $actionsUpdateData = []; 606 607 $newActionConditions = null; 608 foreach ($actions as $actionId => $action) { 609 $actionDb = $actionsDb[$actionId]; 610 611 $actionUpdateValues = $action; 612 unset( 613 $actionUpdateValues['actionid'], 614 $actionUpdateValues['filter'], 615 $actionUpdateValues['operations'], 616 $actionUpdateValues['conditions'], 617 $actionUpdateValues['formula'], 618 $actionUpdateValues['evaltype'] 619 ); 620 621 if (isset($action['filter'])) { 622 $actionFilter = $action['filter']; 623 624 // set formula to empty string of not custom expression 625 if ($actionFilter['evaltype'] != CONDITION_EVAL_TYPE_EXPRESSION) { 626 $actionUpdateValues['formula'] = ''; 627 } 628 629 $actionUpdateValues['evaltype'] = $actionFilter['evaltype']; 630 } 631 632 if (isset($action['operations'])) { 633 $operationsDb = $actionDb['operations']; 634 $operationsDb = zbx_toHash($operationsDb, 'operationid'); 635 636 foreach ($action['operations'] as $operation) { 637 if (!isset($operation['operationid'])) { 638 $operation['actionid'] = $action['actionid']; 639 $operationsToCreate[] = $operation; 640 } 641 else { 642 $operationId = $operation['operationid']; 643 644 if (isset($operationsDb[$operationId])) { 645 $operationsToUpdate[] = $operation; 646 unset($operationsDb[$operationId]); 647 } 648 } 649 } 650 $operationIdsForDelete = array_merge($operationIdsForDelete, array_keys($operationsDb)); 651 } 652 653 $actionsUpdateData[] = ['values' => $actionUpdateValues, 'where' => ['actionid' => $actionId]]; 654 } 655 656 if ($actionsUpdateData) { 657 DB::update('actions', $actionsUpdateData); 658 } 659 660 // add, update and delete operations 661 $this->addOperations($operationsToCreate); 662 $this->updateOperations($operationsToUpdate, $actionsDb); 663 if (!empty($operationIdsForDelete)) { 664 $this->deleteOperations($operationIdsForDelete); 665 } 666 667 // set actionid for all conditions and group by actionid into $newActionConditions 668 $newActionConditions = null; 669 foreach ($actions as $actionId => $action) { 670 if (isset($action['filter'])) { 671 if ($newActionConditions === null) { 672 $newActionConditions = []; 673 } 674 675 $newActionConditions[$actionId] = []; 676 foreach ($action['filter']['conditions'] as $condition) { 677 $condition['actionid'] = $actionId; 678 $newActionConditions[$actionId][] = $condition; 679 } 680 } 681 } 682 683 // if we have any conditions, fetch current conditions from db and do replace by position and group result 684 // by actionid into $actionConditions 685 $actionConditions = []; 686 if ($newActionConditions !== null) { 687 $existingConditions = DBfetchArray(DBselect( 688 'SELECT conditionid,actionid,conditiontype,operator,value'. 689 ' FROM conditions'. 690 ' WHERE '.dbConditionInt('actionid', $actionIds). 691 ' ORDER BY conditionid' 692 )); 693 $existingActionConditions = []; 694 foreach ($existingConditions as $condition) { 695 $existingActionConditions[$condition['actionid']][] = $condition; 696 } 697 698 $conditions = DB::replaceByPosition('conditions', $existingActionConditions, $newActionConditions); 699 foreach ($conditions as $condition) { 700 $actionConditions[$condition['actionid']][] = $condition; 701 } 702 } 703 704 // update formulas for user expressions using new conditions 705 foreach ($actions as $actionId => $action) { 706 if (isset($action['filter']) && $action['filter']['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 707 $this->updateFormula($actionId, $action['filter']['formula'], $actionConditions[$actionId]); 708 } 709 } 710 711 return ['actionids' => $actionIds]; 712 } 713 714 /** 715 * @param array $conditions 716 * 717 * @return mixed 718 */ 719 protected function addConditions($conditions) { 720 foreach ($conditions as $condition) { 721 $connectionDbFields = [ 722 'actionid' => null, 723 'conditiontype' => null 724 ]; 725 if (!check_db_fields($connectionDbFields, $condition)) { 726 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect parameters for condition.')); 727 } 728 } 729 730 return DB::save('conditions', $conditions); 731 } 732 733 protected function updateConditions($conditions) { 734 $update = []; 735 foreach ($conditions as $condition) { 736 $conditionId = $condition['conditionid']; 737 unset($condition['conditionid']); 738 $update = [ 739 'values' => $condition, 740 'where' => ['conditionid' => $conditionId] 741 ]; 742 } 743 DB::update('conditions', $update); 744 745 return $conditions; 746 } 747 748 protected function deleteConditions($conditionids) { 749 DB::delete('conditions', ['conditionid' => $conditionids]); 750 } 751 752 /** 753 * @param array $operations 754 * 755 * @return bool 756 */ 757 protected function addOperations($operations) { 758 foreach ($operations as $operation) { 759 $operationDbFields = [ 760 'actionid' => null, 761 'operationtype' => null 762 ]; 763 if (!check_db_fields($operationDbFields, $operation)) { 764 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect parameter for operations.')); 765 } 766 } 767 768 $operations = DB::save('operations', $operations); 769 $operations = zbx_toHash($operations, 'operationid'); 770 771 $opMessagesToInsert = []; 772 $opCommandsToInsert = []; 773 $opMessageGrpsToInsert = []; 774 $opMessageUsrsToInsert = []; 775 $opCommandHstsToInsert = []; 776 $opCommandGroupInserts = []; 777 $opGroupsToInsert = []; 778 $opTemplatesToInsert = []; 779 $opConditionsToInsert = []; 780 $opInventoryToInsert = []; 781 782 foreach ($operations as $operationId => $operation) { 783 switch ($operation['operationtype']) { 784 case OPERATION_TYPE_MESSAGE: 785 if (isset($operation['opmessage']) && !empty($operation['opmessage'])) { 786 $operation['opmessage']['operationid'] = $operationId; 787 $opMessagesToInsert[] = $operation['opmessage']; 788 } 789 if (isset($operation['opmessage_usr'])) { 790 foreach ($operation['opmessage_usr'] as $user) { 791 $opMessageUsrsToInsert[] = [ 792 'operationid' => $operationId, 793 'userid' => $user['userid'] 794 ]; 795 } 796 } 797 if (isset($operation['opmessage_grp'])) { 798 foreach ($operation['opmessage_grp'] as $userGroup) { 799 $opMessageGrpsToInsert[] = [ 800 'operationid' => $operationId, 801 'usrgrpid' => $userGroup['usrgrpid'] 802 ]; 803 } 804 } 805 break; 806 case OPERATION_TYPE_COMMAND: 807 if (isset($operation['opcommand']) && !empty($operation['opcommand'])) { 808 $operation['opcommand']['operationid'] = $operationId; 809 $opCommandsToInsert[] = $operation['opcommand']; 810 } 811 if (isset($operation['opcommand_hst'])) { 812 foreach ($operation['opcommand_hst'] as $host) { 813 $opCommandHstsToInsert[] = [ 814 'operationid' => $operationId, 815 'hostid' => $host['hostid'] 816 ]; 817 } 818 } 819 if (isset($operation['opcommand_grp'])) { 820 foreach ($operation['opcommand_grp'] as $hostGroup) { 821 $opCommandGroupInserts[] = [ 822 'operationid' => $operationId, 823 'groupid' => $hostGroup['groupid'] 824 ]; 825 } 826 } 827 break; 828 case OPERATION_TYPE_GROUP_ADD: 829 case OPERATION_TYPE_GROUP_REMOVE: 830 foreach ($operation['opgroup'] as $hostGroup) { 831 $opGroupsToInsert[] = [ 832 'operationid' => $operationId, 833 'groupid' => $hostGroup['groupid'] 834 ]; 835 } 836 break; 837 case OPERATION_TYPE_TEMPLATE_ADD: 838 case OPERATION_TYPE_TEMPLATE_REMOVE: 839 foreach ($operation['optemplate'] as $template) { 840 $opTemplatesToInsert[] = [ 841 'operationid' => $operationId, 842 'templateid' => $template['templateid'] 843 ]; 844 } 845 break; 846 case OPERATION_TYPE_HOST_ADD: 847 case OPERATION_TYPE_HOST_REMOVE: 848 case OPERATION_TYPE_HOST_ENABLE: 849 case OPERATION_TYPE_HOST_DISABLE: 850 break; 851 case OPERATION_TYPE_HOST_INVENTORY: 852 $opInventoryToInsert[] = [ 853 'operationid' => $operationId, 854 'inventory_mode' => $operation['opinventory']['inventory_mode'] 855 ]; 856 break; 857 } 858 if (isset($operation['opconditions'])) { 859 foreach ($operation['opconditions'] as $opCondition) { 860 $opCondition['operationid'] = $operationId; 861 $opConditionsToInsert[] = $opCondition; 862 } 863 } 864 } 865 DB::insert('opconditions', $opConditionsToInsert); 866 DB::insert('opmessage', $opMessagesToInsert, false); 867 DB::insert('opcommand', $opCommandsToInsert, false); 868 DB::insert('opmessage_grp', $opMessageGrpsToInsert); 869 DB::insert('opmessage_usr', $opMessageUsrsToInsert); 870 DB::insert('opcommand_hst', $opCommandHstsToInsert); 871 DB::insert('opcommand_grp', $opCommandGroupInserts); 872 DB::insert('opgroup', $opGroupsToInsert); 873 DB::insert('optemplate', $opTemplatesToInsert); 874 DB::insert('opinventory', $opInventoryToInsert, false); 875 876 return true; 877 } 878 879 /** 880 * @param array $operations 881 * @param array $actionsDb 882 */ 883 protected function updateOperations($operations, $actionsDb) { 884 $operationsUpdate = []; 885 886 // messages 887 $opMessagesToInsert = []; 888 $opMessagesToUpdate = []; 889 $opMessagesToDeleteByOpId = []; 890 891 $opMessageGrpsToInsert = []; 892 $opMessageUsrsToInsert = []; 893 $opMessageGrpsToDeleteByOpId = []; 894 $opMessageUsrsToDeleteByOpId = []; 895 896 // commands 897 $opCommandsToInsert = []; 898 $opCommandsToUpdate = []; 899 $opCommandsToDeleteByOpId = []; 900 901 $opCommandGrpsToInsert = []; 902 $opCommandHstsToInsert = []; 903 $opCommandGrpsToDeleteByOpId = []; 904 $opCommandHstsToDeleteByOpId = []; 905 906 // groups 907 $opGroupsToInsert = []; 908 $opGroupsToDeleteByOpId = []; 909 910 // templates 911 $opTemplateToInsert = []; 912 $opTemplatesToDeleteByOpId = []; 913 914 // operation conditions 915 $opConditionsToInsert = []; 916 917 // inventory 918 $opInventoryToInsert = []; 919 $opInventoryToUpdate = []; 920 $opInventoryToDeleteByOpId = []; 921 922 foreach ($operations as $operation) { 923 $operationsDb = zbx_toHash($actionsDb[$operation['actionid']]['operations'], 'operationid'); 924 $operationDb = $operationsDb[$operation['operationid']]; 925 926 $typeChanged = false; 927 if (isset($operation['operationtype']) && ($operation['operationtype'] != $operationDb['operationtype'])) { 928 $typeChanged = true; 929 930 switch ($operationDb['operationtype']) { 931 case OPERATION_TYPE_MESSAGE: 932 $opMessagesToDeleteByOpId[] = $operationDb['operationid']; 933 $opMessageGrpsToDeleteByOpId[] = $operationDb['operationid']; 934 $opMessageUsrsToDeleteByOpId[] = $operationDb['operationid']; 935 break; 936 case OPERATION_TYPE_COMMAND: 937 $opCommandsToDeleteByOpId[] = $operationDb['operationid']; 938 $opCommandHstsToDeleteByOpId[] = $operationDb['operationid']; 939 $opCommandGrpsToDeleteByOpId[] = $operationDb['operationid']; 940 break; 941 case OPERATION_TYPE_GROUP_ADD: 942 if ($operation['operationtype'] == OPERATION_TYPE_GROUP_REMOVE) { 943 break; 944 } 945 case OPERATION_TYPE_GROUP_REMOVE: 946 if ($operation['operationtype'] == OPERATION_TYPE_GROUP_ADD) { 947 break; 948 } 949 $opGroupsToDeleteByOpId[] = $operationDb['operationid']; 950 break; 951 case OPERATION_TYPE_TEMPLATE_ADD: 952 if ($operation['operationtype'] == OPERATION_TYPE_TEMPLATE_REMOVE) { 953 break; 954 } 955 case OPERATION_TYPE_TEMPLATE_REMOVE: 956 if ($operation['operationtype'] == OPERATION_TYPE_TEMPLATE_ADD) { 957 break; 958 } 959 $opTemplatesToDeleteByOpId[] = $operationDb['operationid']; 960 break; 961 case OPERATION_TYPE_HOST_INVENTORY: 962 $opInventoryToDeleteByOpId[] = $operationDb['operationid']; 963 break; 964 } 965 } 966 967 if (!isset($operation['operationtype'])) { 968 $operation['operationtype'] = $operationDb['operationtype']; 969 } 970 971 switch ($operation['operationtype']) { 972 case OPERATION_TYPE_MESSAGE: 973 if (!isset($operation['opmessage_grp'])) { 974 $operation['opmessage_grp'] = []; 975 } 976 else { 977 zbx_array_push($operation['opmessage_grp'], ['operationid' => $operation['operationid']]); 978 } 979 980 if (!isset($operation['opmessage_usr'])) { 981 $operation['opmessage_usr'] = []; 982 } 983 else { 984 zbx_array_push($operation['opmessage_usr'], ['operationid' => $operation['operationid']]); 985 } 986 987 if (!isset($operationDb['opmessage_usr'])) { 988 $operationDb['opmessage_usr'] = []; 989 } 990 if (!isset($operationDb['opmessage_grp'])) { 991 $operationDb['opmessage_grp'] = []; 992 } 993 994 if ($typeChanged) { 995 $operation['opmessage']['operationid'] = $operation['operationid']; 996 $opMessagesToInsert[] = $operation['opmessage']; 997 998 $opMessageGrpsToInsert = array_merge($opMessageGrpsToInsert, $operation['opmessage_grp']); 999 $opMessageUsrsToInsert = array_merge($opMessageUsrsToInsert, $operation['opmessage_usr']); 1000 } 1001 else { 1002 $opMessagesToUpdate[] = [ 1003 'values' => $operation['opmessage'], 1004 'where' => ['operationid'=>$operation['operationid']] 1005 ]; 1006 1007 $diff = zbx_array_diff($operation['opmessage_grp'], $operationDb['opmessage_grp'], 'usrgrpid'); 1008 $opMessageGrpsToInsert = array_merge($opMessageGrpsToInsert, $diff['first']); 1009 1010 foreach ($diff['second'] as $opMessageGrp) { 1011 DB::delete('opmessage_grp', [ 1012 'usrgrpid' => $opMessageGrp['usrgrpid'], 1013 'operationid' => $operation['operationid'] 1014 ]); 1015 } 1016 1017 $diff = zbx_array_diff($operation['opmessage_usr'], $operationDb['opmessage_usr'], 'userid'); 1018 $opMessageUsrsToInsert = array_merge($opMessageUsrsToInsert, $diff['first']); 1019 foreach ($diff['second'] as $opMessageUsr) { 1020 DB::delete('opmessage_usr', [ 1021 'userid' => $opMessageUsr['userid'], 1022 'operationid' => $operation['operationid'] 1023 ]); 1024 } 1025 } 1026 break; 1027 case OPERATION_TYPE_COMMAND: 1028 if (!isset($operation['opcommand_grp'])) { 1029 $operation['opcommand_grp'] = []; 1030 } 1031 else { 1032 zbx_array_push($operation['opcommand_grp'], ['operationid' => $operation['operationid']]); 1033 } 1034 1035 if (!isset($operation['opcommand_hst'])) { 1036 $operation['opcommand_hst'] = []; 1037 } 1038 else { 1039 zbx_array_push($operation['opcommand_hst'], ['operationid' => $operation['operationid']]); 1040 } 1041 1042 if (!isset($operationDb['opcommand_grp'])) { 1043 $operationDb['opcommand_grp'] = []; 1044 } 1045 if (!isset($operationDb['opcommand_hst'])) { 1046 $operationDb['opcommand_hst'] = []; 1047 } 1048 1049 if ($typeChanged) { 1050 $operation['opcommand']['operationid'] = $operation['operationid']; 1051 $opCommandsToInsert[] = $operation['opcommand']; 1052 1053 $opCommandGrpsToInsert = array_merge($opCommandGrpsToInsert, $operation['opcommand_grp']); 1054 $opCommandHstsToInsert = array_merge($opCommandHstsToInsert, $operation['opcommand_hst']); 1055 } 1056 else { 1057 // clear and reset fields to default values on type change 1058 if ($operation['opcommand']['type'] == ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT) { 1059 $operation['opcommand']['command'] = ''; 1060 } 1061 else { 1062 $operation['opcommand']['scriptid'] = null; 1063 } 1064 if ($operation['opcommand']['type'] != ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT) { 1065 $operation['opcommand']['execute_on'] = ZBX_SCRIPT_EXECUTE_ON_AGENT; 1066 } 1067 if ($operation['opcommand']['type'] != ZBX_SCRIPT_TYPE_SSH 1068 && $operation['opcommand']['type'] != ZBX_SCRIPT_TYPE_TELNET) { 1069 $operation['opcommand']['port'] = ''; 1070 $operation['opcommand']['username'] = ''; 1071 $operation['opcommand']['password'] = ''; 1072 } 1073 if (!isset($operation['opcommand']['authtype'])) { 1074 $operation['opcommand']['authtype'] = ITEM_AUTHTYPE_PASSWORD; 1075 } 1076 if ($operation['opcommand']['authtype'] == ITEM_AUTHTYPE_PASSWORD) { 1077 $operation['opcommand']['publickey'] = ''; 1078 $operation['opcommand']['privatekey'] = ''; 1079 } 1080 1081 $opCommandsToUpdate[] = [ 1082 'values' => $operation['opcommand'], 1083 'where' => ['operationid' => $operation['operationid']] 1084 ]; 1085 1086 $diff = zbx_array_diff($operation['opcommand_grp'], $operationDb['opcommand_grp'], 'groupid'); 1087 $opCommandGrpsToInsert = array_merge($opCommandGrpsToInsert, $diff['first']); 1088 1089 foreach ($diff['second'] as $opMessageGrp) { 1090 DB::delete('opcommand_grp', [ 1091 'groupid' => $opMessageGrp['groupid'], 1092 'operationid' => $operation['operationid'] 1093 ]); 1094 } 1095 1096 $diff = zbx_array_diff($operation['opcommand_hst'], $operationDb['opcommand_hst'], 'hostid'); 1097 $opCommandHstsToInsert = array_merge($opCommandHstsToInsert, $diff['first']); 1098 $opCommandHostIds = zbx_objectValues($diff['second'], 'opcommand_hstid'); 1099 if ($opCommandHostIds) { 1100 DB::delete('opcommand_hst', [ 1101 'opcommand_hstid' => $opCommandHostIds 1102 ]); 1103 } 1104 } 1105 break; 1106 case OPERATION_TYPE_GROUP_ADD: 1107 case OPERATION_TYPE_GROUP_REMOVE: 1108 if (!isset($operation['opgroup'])) { 1109 $operation['opgroup'] = []; 1110 } 1111 else { 1112 zbx_array_push($operation['opgroup'], ['operationid' => $operation['operationid']]); 1113 } 1114 1115 if (!isset($operationDb['opgroup'])) { 1116 $operationDb['opgroup'] = []; 1117 } 1118 1119 $diff = zbx_array_diff($operation['opgroup'], $operationDb['opgroup'], 'groupid'); 1120 $opGroupsToInsert = array_merge($opGroupsToInsert, $diff['first']); 1121 foreach ($diff['second'] as $opGroup) { 1122 DB::delete('opgroup', [ 1123 'groupid' => $opGroup['groupid'], 1124 'operationid' => $operation['operationid'] 1125 ]); 1126 } 1127 break; 1128 case OPERATION_TYPE_TEMPLATE_ADD: 1129 case OPERATION_TYPE_TEMPLATE_REMOVE: 1130 if (!isset($operation['optemplate'])) { 1131 $operation['optemplate'] = []; 1132 } 1133 else { 1134 zbx_array_push($operation['optemplate'], ['operationid' => $operation['operationid']]); 1135 } 1136 1137 if (!isset($operationDb['optemplate'])) { 1138 $operationDb['optemplate'] = []; 1139 } 1140 1141 $diff = zbx_array_diff($operation['optemplate'], $operationDb['optemplate'], 'templateid'); 1142 $opTemplateToInsert = array_merge($opTemplateToInsert, $diff['first']); 1143 1144 foreach ($diff['second'] as $opTemplate) { 1145 DB::delete('optemplate', [ 1146 'templateid' => $opTemplate['templateid'], 1147 'operationid' => $operation['operationid'] 1148 ]); 1149 } 1150 break; 1151 case OPERATION_TYPE_HOST_INVENTORY: 1152 if ($typeChanged) { 1153 $operation['opinventory']['operationid'] = $operation['operationid']; 1154 $opInventoryToInsert[] = $operation['opinventory']; 1155 } 1156 else { 1157 $opInventoryToUpdate[] = [ 1158 'values' => $operation['opinventory'], 1159 'where' => ['operationid' => $operation['operationid']] 1160 ]; 1161 } 1162 break; 1163 } 1164 1165 if (!isset($operation['opconditions'])) { 1166 $operation['opconditions'] = []; 1167 } 1168 else { 1169 zbx_array_push($operation['opconditions'], ['operationid' => $operation['operationid']]); 1170 } 1171 1172 self::validateOperationConditions($operation['opconditions']); 1173 1174 $diff = zbx_array_diff($operation['opconditions'], $operationDb['opconditions'], 'opconditionid'); 1175 $opConditionsToInsert = array_merge($opConditionsToInsert, $diff['first']); 1176 1177 $opConditionsIdsToDelete = zbx_objectValues($diff['second'], 'opconditionid'); 1178 if (!empty($opConditionsIdsToDelete)) { 1179 DB::delete('opconditions', ['opconditionid' => $opConditionsIdsToDelete]); 1180 } 1181 1182 $operationId = $operation['operationid']; 1183 unset($operation['operationid']); 1184 if (!empty($operation)) { 1185 $operationsUpdate[] = [ 1186 'values' => $operation, 1187 'where' => ['operationid' => $operationId] 1188 ]; 1189 } 1190 } 1191 1192 DB::update('operations', $operationsUpdate); 1193 1194 if (!empty($opMessagesToDeleteByOpId)) { 1195 DB::delete('opmessage', ['operationid' => $opMessagesToDeleteByOpId]); 1196 } 1197 if (!empty($opCommandsToDeleteByOpId)) { 1198 DB::delete('opcommand', ['operationid' => $opCommandsToDeleteByOpId]); 1199 } 1200 if (!empty($opMessageGrpsToDeleteByOpId)) { 1201 DB::delete('opmessage_grp', ['operationid' => $opMessageGrpsToDeleteByOpId]); 1202 } 1203 if (!empty($opMessageUsrsToDeleteByOpId)) { 1204 DB::delete('opmessage_usr', ['operationid' => $opMessageUsrsToDeleteByOpId]); 1205 } 1206 if (!empty($opCommandHstsToDeleteByOpId)) { 1207 DB::delete('opcommand_hst', ['operationid' => $opCommandHstsToDeleteByOpId]); 1208 } 1209 if (!empty($opCommandGrpsToDeleteByOpId)) { 1210 DB::delete('opcommand_grp', ['operationid' => $opCommandGrpsToDeleteByOpId]); 1211 } 1212 if (!empty($opCommandGrpsToDeleteByOpId)) { 1213 DB::delete('opcommand_grp', ['opcommand_grpid' => $opCommandGrpsToDeleteByOpId]); 1214 } 1215 if (!empty($opCommandHstsToDeleteByOpId)) { 1216 DB::delete('opcommand_hst', ['opcommand_hstid' => $opCommandHstsToDeleteByOpId]); 1217 } 1218 if (!empty($opGroupsToDeleteByOpId)) { 1219 DB::delete('opgroup', ['operationid' => $opGroupsToDeleteByOpId]); 1220 } 1221 if (!empty($opTemplatesToDeleteByOpId)) { 1222 DB::delete('optemplate', ['operationid' => $opTemplatesToDeleteByOpId]); 1223 } 1224 if (!empty($opInventoryToDeleteByOpId)) { 1225 DB::delete('opinventory', ['operationid' => $opInventoryToDeleteByOpId]); 1226 } 1227 1228 DB::insert('opmessage', $opMessagesToInsert, false); 1229 DB::insert('opcommand', $opCommandsToInsert, false); 1230 DB::insert('opmessage_grp', $opMessageGrpsToInsert); 1231 DB::insert('opmessage_usr', $opMessageUsrsToInsert); 1232 DB::insert('opcommand_grp', $opCommandGrpsToInsert); 1233 DB::insert('opcommand_hst', $opCommandHstsToInsert); 1234 DB::insert('opgroup', $opGroupsToInsert); 1235 DB::insert('optemplate', $opTemplateToInsert); 1236 DB::update('opmessage', $opMessagesToUpdate); 1237 DB::update('opcommand', $opCommandsToUpdate); 1238 DB::insert('opconditions', $opConditionsToInsert); 1239 DB::insert('opinventory', $opInventoryToInsert, false); 1240 DB::update('opinventory', $opInventoryToUpdate); 1241 } 1242 1243 protected function deleteOperations($operationIds) { 1244 DB::delete('operations', ['operationid' => $operationIds]); 1245 } 1246 1247 /** 1248 * Delete actions. 1249 * 1250 * @param array $actionids 1251 * 1252 * @return array 1253 */ 1254 public function delete(array $actionids) { 1255 if (empty($actionids)) { 1256 self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); 1257 } 1258 1259 $delActions = $this->get([ 1260 'actionids' => $actionids, 1261 'editable' => true, 1262 'output' => ['actionid'], 1263 'preservekeys' => true 1264 ]); 1265 foreach ($actionids as $actionid) { 1266 if (isset($delActions[$actionid])) { 1267 continue; 1268 } 1269 self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!')); 1270 } 1271 1272 DB::delete('actions', ['actionid' => $actionids]); 1273 DB::delete('alerts', ['actionid' => $actionids]); 1274 1275 return ['actionids' => $actionids]; 1276 } 1277 1278 /** 1279 * @param array $operations 1280 * 1281 * @return bool 1282 */ 1283 public function validateOperationsIntegrity($operations) { 1284 $operations = zbx_toArray($operations); 1285 1286 foreach ($operations as $operation) { 1287 if ((isset($operation['esc_step_from']) || isset($operation['esc_step_to'])) && !isset($operation['esc_step_from'], $operation['esc_step_to'])) { 1288 self::exception(ZBX_API_ERROR_PARAMETERS, _('esc_step_from and esc_step_to must be set together.')); 1289 } 1290 1291 if (isset($operation['esc_step_from'], $operation['esc_step_to'])) { 1292 if ($operation['esc_step_from'] < 1 || $operation['esc_step_to'] < 0) { 1293 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation escalation step values.')); 1294 } 1295 1296 if ($operation['esc_step_from'] > $operation['esc_step_to'] && $operation['esc_step_to'] != 0) { 1297 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation escalation step values.')); 1298 } 1299 } 1300 1301 if (isset($operation['esc_period'])) { 1302 if (isset($operation['esc_period']) && $operation['esc_period'] != 0 && $operation['esc_period'] < SEC_PER_MIN) { 1303 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation step duration.')); 1304 } 1305 } 1306 1307 $hostIdsAll = []; 1308 $hostGroupIdsAll = []; 1309 $userIdsAll = []; 1310 $userGroupIdsAll = []; 1311 switch ($operation['operationtype']) { 1312 case OPERATION_TYPE_MESSAGE: 1313 $userIds = isset($operation['opmessage_usr']) ? zbx_objectValues($operation['opmessage_usr'], 'userid') : []; 1314 $userGroupIds = isset($operation['opmessage_grp']) ? zbx_objectValues($operation['opmessage_grp'], 'usrgrpid') : []; 1315 1316 if (empty($userIds) && empty($userGroupIds)) { 1317 self::exception(ZBX_API_ERROR_PARAMETERS, _('No recipients for action operation message.')); 1318 } 1319 1320 $userIdsAll = array_merge($userIdsAll, $userIds); 1321 $userGroupIdsAll = array_merge($userGroupIdsAll, $userGroupIds); 1322 break; 1323 case OPERATION_TYPE_COMMAND: 1324 if (!isset($operation['opcommand']['type'])) { 1325 self::exception(ZBX_API_ERROR_PARAMETERS, _('No command type specified for action operation.')); 1326 } 1327 1328 if ((!isset($operation['opcommand']['command']) || zbx_empty(trim($operation['opcommand']['command']))) 1329 && $operation['opcommand']['type'] != ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT) { 1330 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No command specified for action operation.')); 1331 } 1332 1333 switch ($operation['opcommand']['type']) { 1334 case ZBX_SCRIPT_TYPE_IPMI: 1335 break; 1336 case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT: 1337 if (!isset($operation['opcommand']['execute_on'])) { 1338 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No execution target specified for action operation command "%s".', $operation['opcommand']['command'])); 1339 } 1340 break; 1341 case ZBX_SCRIPT_TYPE_SSH: 1342 if (!isset($operation['opcommand']['authtype']) || zbx_empty($operation['opcommand']['authtype'])) { 1343 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No authentication type specified for action operation command "%s".', $operation['opcommand']['command'])); 1344 } 1345 1346 if (!isset($operation['opcommand']['username']) || zbx_empty($operation['opcommand']['username'])) { 1347 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No authentication user name specified for action operation command "%s".', $operation['opcommand']['command'])); 1348 } 1349 1350 if ($operation['opcommand']['authtype'] == ITEM_AUTHTYPE_PUBLICKEY) { 1351 if (!isset($operation['opcommand']['publickey']) || zbx_empty($operation['opcommand']['publickey'])) { 1352 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No public key file specified for action operation command "%s".', $operation['opcommand']['command'])); 1353 } 1354 if (!isset($operation['opcommand']['privatekey']) || zbx_empty($operation['opcommand']['privatekey'])) { 1355 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No private key file specified for action operation command "%s".', $operation['opcommand']['command'])); 1356 } 1357 } 1358 break; 1359 case ZBX_SCRIPT_TYPE_TELNET: 1360 if (!isset($operation['opcommand']['username']) || zbx_empty($operation['opcommand']['username'])) { 1361 self::exception(ZBX_API_ERROR_PARAMETERS, _s('No authentication user name specified for action operation command "%s".', $operation['opcommand']['command'])); 1362 } 1363 break; 1364 case ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT: 1365 if (!isset($operation['opcommand']['scriptid']) || zbx_empty($operation['opcommand']['scriptid'])) { 1366 self::exception(ZBX_API_ERROR_PARAMETERS, _('No script specified for action operation command.')); 1367 } 1368 $scripts = API::Script()->get([ 1369 'output' => ['scriptid','name'], 1370 'scriptids' => $operation['opcommand']['scriptid'], 1371 'preservekeys' => true 1372 ]); 1373 if (!isset($scripts[$operation['opcommand']['scriptid']])) { 1374 self::exception(ZBX_API_ERROR_PARAMETERS, _('Specified script does not exist or you do not have rights on it for action operation command.')); 1375 } 1376 break; 1377 default: 1378 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation command type.')); 1379 } 1380 1381 if (isset($operation['opcommand']['port']) && !zbx_empty($operation['opcommand']['port'])) { 1382 if (zbx_ctype_digit($operation['opcommand']['port'])) { 1383 if ($operation['opcommand']['port'] > 65535 || $operation['opcommand']['port'] < 1) { 1384 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect action operation port "%s".', $operation['opcommand']['port'])); 1385 } 1386 } 1387 else { 1388 $user_macro_parser = new CUserMacroParser(); 1389 1390 if ($user_macro_parser->parse($operation['opcommand']['port']) != CParser::PARSE_SUCCESS) { 1391 self::exception(ZBX_API_ERROR_PARAMETERS, 1392 _s('Incorrect action operation port "%s".', $operation['opcommand']['port']) 1393 ); 1394 } 1395 } 1396 } 1397 1398 $groupIds = []; 1399 if (isset($operation['opcommand_grp'])) { 1400 $groupIds = zbx_objectValues($operation['opcommand_grp'], 'groupid'); 1401 } 1402 1403 $hostIds = []; 1404 $withoutCurrent = true; 1405 if (isset($operation['opcommand_hst'])) { 1406 foreach ($operation['opcommand_hst'] as $hstCommand) { 1407 if ($hstCommand['hostid'] == 0) { 1408 $withoutCurrent = false; 1409 } 1410 else { 1411 $hostIds[$hstCommand['hostid']] = $hstCommand['hostid']; 1412 } 1413 } 1414 } 1415 1416 if (empty($groupIds) && empty($hostIds) && $withoutCurrent) { 1417 if ($operation['opcommand']['type'] == ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT) { 1418 self::exception(ZBX_API_ERROR_PARAMETERS, _s('You did not specify targets for action operation global script "%s".', $scripts[$operation['opcommand']['scriptid']]['name'])); 1419 } 1420 else { 1421 self::exception(ZBX_API_ERROR_PARAMETERS, _s('You did not specify targets for action operation command "%s".', $operation['opcommand']['command'])); 1422 } 1423 } 1424 1425 $hostIdsAll = array_merge($hostIdsAll, $hostIds); 1426 $hostGroupIdsAll = array_merge($hostGroupIdsAll, $groupIds); 1427 break; 1428 case OPERATION_TYPE_GROUP_ADD: 1429 case OPERATION_TYPE_GROUP_REMOVE: 1430 $groupIds = isset($operation['opgroup']) ? zbx_objectValues($operation['opgroup'], 'groupid') : []; 1431 if (empty($groupIds)) { 1432 self::exception(ZBX_API_ERROR_PARAMETERS, _('Operation has no group to operate.')); 1433 } 1434 $hostGroupIdsAll = array_merge($hostGroupIdsAll, $groupIds); 1435 break; 1436 case OPERATION_TYPE_TEMPLATE_ADD: 1437 case OPERATION_TYPE_TEMPLATE_REMOVE: 1438 $templateIds = isset($operation['optemplate']) ? zbx_objectValues($operation['optemplate'], 'templateid') : []; 1439 if (empty($templateIds)) { 1440 self::exception(ZBX_API_ERROR_PARAMETERS, _('Operation has no template to operate.')); 1441 } 1442 $hostIdsAll = array_merge($hostIdsAll, $templateIds); 1443 break; 1444 case OPERATION_TYPE_HOST_ADD: 1445 case OPERATION_TYPE_HOST_REMOVE: 1446 case OPERATION_TYPE_HOST_ENABLE: 1447 case OPERATION_TYPE_HOST_DISABLE: 1448 break; 1449 1450 case OPERATION_TYPE_HOST_INVENTORY: 1451 if (!array_key_exists('opinventory', $operation) 1452 || !array_key_exists('inventory_mode', $operation['opinventory'])) { 1453 self::exception(ZBX_API_ERROR_PARAMETERS, 1454 _('No inventory mode specified for action operation.') 1455 ); 1456 } 1457 if ($operation['opinventory']['inventory_mode'] != HOST_INVENTORY_MANUAL 1458 && $operation['opinventory']['inventory_mode'] != HOST_INVENTORY_AUTOMATIC) { 1459 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect inventory mode in action operation.')); 1460 } 1461 break; 1462 1463 default: 1464 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation type.')); 1465 } 1466 } 1467 1468 if (!API::HostGroup()->isWritable($hostGroupIdsAll)) { 1469 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation host group. Host group does not exist or you have no access to this host group.')); 1470 } 1471 if (!API::Host()->isWritable($hostIdsAll)) { 1472 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation host. Host does not exist or you have no access to this host.')); 1473 } 1474 if (!API::User()->isReadable($userIdsAll)) { 1475 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation user. User does not exist or you have no access to this user.')); 1476 } 1477 if (!API::UserGroup()->isReadable($userGroupIdsAll)) { 1478 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation user group. User group does not exist or you have no access to this user group.')); 1479 } 1480 1481 return true; 1482 } 1483 1484 /** 1485 * Validate operation conditions. 1486 * 1487 * @static 1488 * @param $conditions 1489 * @return bool 1490 */ 1491 public static function validateOperationConditions($conditions) { 1492 $conditions = zbx_toArray($conditions); 1493 $ackStatuses = [ 1494 EVENT_ACKNOWLEDGED => 1, 1495 EVENT_NOT_ACKNOWLEDGED => 1 1496 ]; 1497 1498 foreach ($conditions as $condition) { 1499 switch ($condition['conditiontype']) { 1500 case CONDITION_TYPE_EVENT_ACKNOWLEDGED: 1501 if (!isset($ackStatuses[$condition['value']])) { 1502 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation condition acknowledge type.')); 1503 } 1504 break; 1505 1506 default: 1507 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation condition type.')); 1508 break; 1509 } 1510 } 1511 1512 return true; 1513 } 1514 1515 protected function addRelatedObjects(array $options, array $result) { 1516 $result = parent::addRelatedObjects($options, $result); 1517 1518 $actionIds = array_keys($result); 1519 1520 // adding formulas 1521 if ($options['selectFilter'] !== null) { 1522 $formulaRequested = $this->outputIsRequested('formula', $options['selectFilter']); 1523 $evalFormulaRequested = $this->outputIsRequested('eval_formula', $options['selectFilter']); 1524 $conditionsRequested = $this->outputIsRequested('conditions', $options['selectFilter']); 1525 1526 $filters = []; 1527 foreach ($result as $action) { 1528 $filters[$action['actionid']] = [ 1529 'evaltype' => $action['evaltype'], 1530 'formula' => isset($action['formula']) ? $action['formula'] : '' 1531 ]; 1532 } 1533 1534 if ($formulaRequested || $evalFormulaRequested || $conditionsRequested) { 1535 $conditions = API::getApiService()->select('conditions', [ 1536 'output' => ['actionid', 'conditionid', 'conditiontype', 'operator', 'value'], 1537 'filter' => ['actionid' => $actionIds], 1538 'preservekeys' => true 1539 ]); 1540 1541 $relationMap = $this->createRelationMap($conditions, 'actionid', 'conditionid'); 1542 $filters = $relationMap->mapMany($filters, $conditions, 'conditions'); 1543 1544 foreach ($filters as &$filter) { 1545 // in case of a custom expression - use the given formula 1546 if ($filter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 1547 $formula = $filter['formula']; 1548 } 1549 // in other cases - generate the formula automatically 1550 else { 1551 $conditions = $filter['conditions']; 1552 1553 // sort conditions 1554 $sortFields = [ 1555 ['field' => 'conditiontype', 'order' => ZBX_SORT_DOWN], 1556 ['field' => 'operator', 'order' => ZBX_SORT_DOWN], 1557 ['field' => 'value', 'order' => ZBX_SORT_DOWN] 1558 ]; 1559 CArrayHelper::sort($conditions, $sortFields); 1560 1561 $conditionsForFormula = []; 1562 foreach ($conditions as $condition) { 1563 $conditionsForFormula[$condition['conditionid']] = $condition['conditiontype']; 1564 } 1565 $formula = CConditionHelper::getFormula($conditionsForFormula, $filter['evaltype']); 1566 } 1567 1568 // generate formulaids from the effective formula 1569 $formulaIds = CConditionHelper::getFormulaIds($formula); 1570 foreach ($filter['conditions'] as &$condition) { 1571 $condition['formulaid'] = $formulaIds[$condition['conditionid']]; 1572 } 1573 unset($condition); 1574 1575 // generated a letter based formula only for actions with custom expressions 1576 if ($formulaRequested && $filter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 1577 $filter['formula'] = CConditionHelper::replaceNumericIds($formula, $formulaIds); 1578 } 1579 1580 if ($evalFormulaRequested) { 1581 $filter['eval_formula'] = CConditionHelper::replaceNumericIds($formula, $formulaIds); 1582 } 1583 } 1584 unset($filter); 1585 } 1586 1587 // add filters to the result 1588 foreach ($result as &$action) { 1589 $action['filter'] = $filters[$action['actionid']]; 1590 } 1591 unset($action); 1592 } 1593 1594 // adding operations 1595 if ($options['selectOperations'] !== null && $options['selectOperations'] != API_OUTPUT_COUNT) { 1596 $operations = API::getApiService()->select('operations', [ 1597 'output' => $this->outputExtend($options['selectOperations'], 1598 ['operationid', 'actionid', 'operationtype'] 1599 ), 1600 'filter' => ['actionid' => $actionIds], 1601 'preservekeys' => true 1602 ]); 1603 $relationMap = $this->createRelationMap($operations, 'actionid', 'operationid'); 1604 $operationIds = $relationMap->getRelatedIds(); 1605 1606 if ($this->outputIsRequested('opconditions', $options['selectOperations'])) { 1607 foreach ($operations as &$operation) { 1608 $operation['opconditions'] = []; 1609 } 1610 unset($operation); 1611 1612 $res = DBselect('SELECT op.* FROM opconditions op WHERE '.dbConditionInt('op.operationid', $operationIds)); 1613 while ($opcondition = DBfetch($res)) { 1614 $operations[$opcondition['operationid']]['opconditions'][] = $opcondition; 1615 } 1616 } 1617 1618 $opmessage = []; 1619 $opcommand = []; 1620 $opgroup = []; 1621 $optemplate = []; 1622 $opinventory = []; 1623 1624 foreach ($operations as $operationid => $operation) { 1625 switch ($operation['operationtype']) { 1626 case OPERATION_TYPE_MESSAGE: 1627 $opmessage[] = $operationid; 1628 break; 1629 case OPERATION_TYPE_COMMAND: 1630 $opcommand[] = $operationid; 1631 break; 1632 case OPERATION_TYPE_GROUP_ADD: 1633 case OPERATION_TYPE_GROUP_REMOVE: 1634 $opgroup[] = $operationid; 1635 break; 1636 case OPERATION_TYPE_TEMPLATE_ADD: 1637 case OPERATION_TYPE_TEMPLATE_REMOVE: 1638 $optemplate[] = $operationid; 1639 break; 1640 case OPERATION_TYPE_HOST_ADD: 1641 case OPERATION_TYPE_HOST_REMOVE: 1642 case OPERATION_TYPE_HOST_ENABLE: 1643 case OPERATION_TYPE_HOST_DISABLE: 1644 break; 1645 case OPERATION_TYPE_HOST_INVENTORY: 1646 $opinventory[] = $operationid; 1647 break; 1648 } 1649 } 1650 1651 // get OPERATION_TYPE_MESSAGE data 1652 if ($opmessage) { 1653 if ($this->outputIsRequested('opmessage', $options['selectOperations'])) { 1654 foreach ($opmessage as $operationId) { 1655 $operations[$operationId]['opmessage'] = []; 1656 } 1657 1658 $dbOpmessages = DBselect( 1659 'SELECT o.operationid,o.default_msg,o.subject,o.message,o.mediatypeid'. 1660 ' FROM opmessage o'. 1661 ' WHERE '.dbConditionInt('operationid', $opmessage) 1662 ); 1663 while ($dbOpmessage = DBfetch($dbOpmessages)) { 1664 $operations[$dbOpmessage['operationid']]['opmessage'] = $dbOpmessage; 1665 } 1666 } 1667 1668 if ($this->outputIsRequested('opmessage_grp', $options['selectOperations'])) { 1669 foreach ($opmessage as $operationId) { 1670 $operations[$operationId]['opmessage_grp'] = []; 1671 } 1672 1673 $dbOpmessageGrp = DBselect( 1674 'SELECT og.operationid,og.usrgrpid'. 1675 ' FROM opmessage_grp og'. 1676 ' WHERE '.dbConditionInt('operationid', $opmessage) 1677 ); 1678 while ($opmessageGrp = DBfetch($dbOpmessageGrp)) { 1679 $operations[$opmessageGrp['operationid']]['opmessage_grp'][] = $opmessageGrp; 1680 } 1681 } 1682 1683 if ($this->outputIsRequested('opmessage_usr', $options['selectOperations'])) { 1684 foreach ($opmessage as $operationId) { 1685 $operations[$operationId]['opmessage_usr'] = []; 1686 } 1687 1688 $dbOpmessageUsr = DBselect( 1689 'SELECT ou.operationid,ou.userid'. 1690 ' FROM opmessage_usr ou'. 1691 ' WHERE '.dbConditionInt('operationid', $opmessage) 1692 ); 1693 while ($opmessageUsr = DBfetch($dbOpmessageUsr)) { 1694 $operations[$opmessageUsr['operationid']]['opmessage_usr'][] = $opmessageUsr; 1695 } 1696 } 1697 } 1698 1699 // get OPERATION_TYPE_COMMAND data 1700 if ($opcommand) { 1701 if ($this->outputIsRequested('opcommand', $options['selectOperations'])) { 1702 foreach ($opcommand as $operationId) { 1703 $operations[$operationId]['opcommand'] = []; 1704 } 1705 1706 $dbOpcommands = DBselect( 1707 'SELECT o.*'. 1708 ' FROM opcommand o'. 1709 ' WHERE '.dbConditionInt('operationid', $opcommand) 1710 ); 1711 while ($dbOpcommand = DBfetch($dbOpcommands)) { 1712 $operations[$dbOpcommand['operationid']]['opcommand'] = $dbOpcommand; 1713 } 1714 } 1715 1716 if ($this->outputIsRequested('opcommand_hst', $options['selectOperations'])) { 1717 foreach ($opcommand as $operationId) { 1718 $operations[$operationId]['opcommand_hst'] = []; 1719 } 1720 1721 $dbOpcommandHst = DBselect( 1722 'SELECT oh.opcommand_hstid,oh.operationid,oh.hostid'. 1723 ' FROM opcommand_hst oh'. 1724 ' WHERE '.dbConditionInt('operationid', $opcommand) 1725 ); 1726 while ($opcommandHst = DBfetch($dbOpcommandHst)) { 1727 $operations[$opcommandHst['operationid']]['opcommand_hst'][] = $opcommandHst; 1728 } 1729 } 1730 1731 if ($this->outputIsRequested('opcommand_grp', $options['selectOperations'])) { 1732 foreach ($opcommand as $operationId) { 1733 $operations[$operationId]['opcommand_grp'] = []; 1734 } 1735 1736 $dbOpcommandGrp = DBselect( 1737 'SELECT og.opcommand_grpid,og.operationid,og.groupid'. 1738 ' FROM opcommand_grp og'. 1739 ' WHERE '.dbConditionInt('operationid', $opcommand) 1740 ); 1741 while ($opcommandGrp = DBfetch($dbOpcommandGrp)) { 1742 $operations[$opcommandGrp['operationid']]['opcommand_grp'][] = $opcommandGrp; 1743 } 1744 } 1745 } 1746 1747 // get OPERATION_TYPE_GROUP_ADD, OPERATION_TYPE_GROUP_REMOVE data 1748 if ($opgroup) { 1749 if ($this->outputIsRequested('opgroup', $options['selectOperations'])) { 1750 foreach ($opgroup as $operationId) { 1751 $operations[$operationId]['opgroup'] = []; 1752 } 1753 1754 $dbOpgroup = DBselect( 1755 'SELECT o.operationid,o.groupid'. 1756 ' FROM opgroup o'. 1757 ' WHERE '.dbConditionInt('operationid', $opgroup) 1758 ); 1759 while ($opgroup = DBfetch($dbOpgroup)) { 1760 $operations[$opgroup['operationid']]['opgroup'][] = $opgroup; 1761 } 1762 } 1763 } 1764 1765 // get OPERATION_TYPE_TEMPLATE_ADD, OPERATION_TYPE_TEMPLATE_REMOVE data 1766 if ($optemplate) { 1767 if ($this->outputIsRequested('optemplate', $options['selectOperations'])) { 1768 foreach ($optemplate as $operationId) { 1769 $operations[$operationId]['optemplate'] = []; 1770 } 1771 1772 $dbOptemplate = DBselect( 1773 'SELECT o.operationid,o.templateid'. 1774 ' FROM optemplate o'. 1775 ' WHERE '.dbConditionInt('operationid', $optemplate) 1776 ); 1777 while ($optemplate = DBfetch($dbOptemplate)) { 1778 $operations[$optemplate['operationid']]['optemplate'][] = $optemplate; 1779 } 1780 } 1781 } 1782 1783 // get OPERATION_TYPE_HOST_INVENTORY data 1784 if ($opinventory) { 1785 if ($this->outputIsRequested('opinventory', $options['selectOperations'])) { 1786 foreach ($opinventory as $operationId) { 1787 $operations[$operationId]['opinventory'] = []; 1788 } 1789 1790 $dbOpinventory = DBselect( 1791 'SELECT o.operationid,o.inventory_mode'. 1792 ' FROM opinventory o'. 1793 ' WHERE '.dbConditionInt('operationid', $opinventory) 1794 ); 1795 while ($opinventory = DBfetch($dbOpinventory)) { 1796 $operations[$opinventory['operationid']]['opinventory'] = $opinventory; 1797 } 1798 } 1799 } 1800 1801 $operations = $this->unsetExtraFields($operations, ['operationid', 'actionid' ,'operationtype'], 1802 $options['selectOperations'] 1803 ); 1804 $result = $relationMap->mapMany($result, $operations, 'operations'); 1805 } 1806 1807 return $result; 1808 } 1809 1810 /** 1811 * Returns the parameters for creating a discovery rule filter validator. 1812 * 1813 * @return array 1814 */ 1815 protected function getFilterSchema() { 1816 return [ 1817 'validators' => [ 1818 'evaltype' => new CLimitedSetValidator([ 1819 'values' => [ 1820 CONDITION_EVAL_TYPE_OR, 1821 CONDITION_EVAL_TYPE_AND, 1822 CONDITION_EVAL_TYPE_AND_OR, 1823 CONDITION_EVAL_TYPE_EXPRESSION 1824 ], 1825 'messageInvalid' => _('Incorrect type of calculation for action "%1$s".') 1826 ]), 1827 'formula' => new CStringValidator([ 1828 'empty' => true 1829 ]), 1830 'conditions' => new CCollectionValidator([ 1831 'empty' => true, 1832 'messageInvalid' => _('Incorrect conditions for action "%1$s".') 1833 ]) 1834 ], 1835 'postValidators' => [ 1836 new CConditionValidator([ 1837 'messageInvalidFormula' => _('Incorrect custom expression "%2$s" for action "%1$s": %3$s.'), 1838 'messageMissingCondition' => _('Condition "%2$s" used in formula "%3$s" for action "%1$s" is not defined.'), 1839 'messageUnusedCondition' => _('Condition "%2$s" is not used in formula "%3$s" for action "%1$s".'), 1840 'messageAndWithSeveralTriggers' => _('Comparing several triggers with "and" is not allowed.') 1841 ]) 1842 ], 1843 'required' => ['evaltype', 'conditions'], 1844 'messageRequired' => _('No "%2$s" given for the filter of action "%1$s".'), 1845 'messageUnsupported' => _('Unsupported parameter "%2$s" for the filter of action "%1$s".') 1846 ]; 1847 } 1848 1849 /** 1850 * Returns the parameters for creating a action filter condition validator. 1851 * 1852 * @return array 1853 */ 1854 protected function getFilterConditionSchema() { 1855 $conditionTypes = [ 1856 CONDITION_TYPE_HOST_GROUP, CONDITION_TYPE_HOST, CONDITION_TYPE_TRIGGER, CONDITION_TYPE_TRIGGER_NAME, 1857 CONDITION_TYPE_TRIGGER_SEVERITY, CONDITION_TYPE_TRIGGER_VALUE, CONDITION_TYPE_TIME_PERIOD, 1858 CONDITION_TYPE_DHOST_IP, CONDITION_TYPE_DSERVICE_TYPE, CONDITION_TYPE_DSERVICE_PORT, 1859 CONDITION_TYPE_DSTATUS, CONDITION_TYPE_DUPTIME, CONDITION_TYPE_DVALUE, CONDITION_TYPE_TEMPLATE, 1860 CONDITION_TYPE_EVENT_ACKNOWLEDGED, CONDITION_TYPE_APPLICATION, CONDITION_TYPE_MAINTENANCE, 1861 CONDITION_TYPE_DRULE, CONDITION_TYPE_DCHECK, CONDITION_TYPE_PROXY, CONDITION_TYPE_DOBJECT, 1862 CONDITION_TYPE_HOST_NAME, CONDITION_TYPE_EVENT_TYPE, CONDITION_TYPE_HOST_METADATA 1863 ]; 1864 1865 $operators = [ 1866 CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL, CONDITION_OPERATOR_LIKE, 1867 CONDITION_OPERATOR_NOT_LIKE, CONDITION_OPERATOR_IN, CONDITION_OPERATOR_MORE_EQUAL, 1868 CONDITION_OPERATOR_LESS_EQUAL, CONDITION_OPERATOR_NOT_IN 1869 ]; 1870 1871 return [ 1872 'validators' => [ 1873 'conditiontype' => new CLimitedSetValidator([ 1874 'values' => $conditionTypes, 1875 'messageInvalid' => _('Incorrect filter condition type for action "%1$s".') 1876 ]) , 1877 'value' => new CStringValidator([ 1878 'empty' => true 1879 ]), 1880 'formulaid' => new CStringValidator([ 1881 'regex' => '/[A-Z]+/', 1882 'messageEmpty' => _('Empty filter condition formula ID for action "%1$s".'), 1883 'messageRegex' => _('Incorrect filter condition formula ID for action "%1$s".') 1884 ]), 1885 'operator' => new CLimitedSetValidator([ 1886 'values' => $operators, 1887 'messageInvalid' => _('Incorrect filter condition operator for action "%1$s".') 1888 ]) 1889 ], 1890 'required' => ['conditiontype', 'value'], 1891 'postValidators' => [ 1892 new CActionCondValidator() 1893 ], 1894 'messageRequired' => _('No "%2$s" given for a filter condition of action "%1$s".'), 1895 'messageUnsupported' => _('Unsupported parameter "%2$s" for a filter condition of action "%1$s".') 1896 ]; 1897 } 1898 1899 protected function applyQueryOutputOptions($tableName, $tableAlias, array $options, array $sqlParts) { 1900 $sqlParts = parent::applyQueryOutputOptions($tableName, $tableAlias, $options, $sqlParts); 1901 1902 if ($options['countOutput'] === null) { 1903 // add filter fields 1904 if ($this->outputIsRequested('formula', $options['selectFilter']) 1905 || $this->outputIsRequested('eval_formula', $options['selectFilter']) 1906 || $this->outputIsRequested('conditions', $options['selectFilter'])) { 1907 1908 $sqlParts = $this->addQuerySelect('a.formula', $sqlParts); 1909 $sqlParts = $this->addQuerySelect('a.evaltype', $sqlParts); 1910 } 1911 if ($this->outputIsRequested('evaltype', $options['selectFilter'])) { 1912 $sqlParts = $this->addQuerySelect('a.evaltype', $sqlParts); 1913 } 1914 } 1915 1916 return $sqlParts; 1917 } 1918 1919 /** 1920 * Converts a formula with letters to a formula with IDs and updates it. 1921 * 1922 * @param string $actionId 1923 * @param string $formulaWithLetters formula with letters 1924 * @param array $conditions 1925 */ 1926 protected function updateFormula($actionId, $formulaWithLetters, array $conditions) { 1927 $formulaIdToConditionId = []; 1928 1929 foreach ($conditions as $condition) { 1930 $formulaIdToConditionId[$condition['formulaid']] = $condition['conditionid']; 1931 } 1932 $formula = CConditionHelper::replaceLetterIds($formulaWithLetters, $formulaIdToConditionId); 1933 1934 DB::updateByPk('actions', $actionId, ['formula' => $formula]); 1935 } 1936 1937 /** 1938 * Validate input given to action.create API call. 1939 * 1940 * @param $actions 1941 */ 1942 protected function validateCreate($actions) { 1943 $actionDbFields = [ 1944 'name' => null, 1945 'eventsource' => null 1946 ]; 1947 1948 $duplicates = []; 1949 foreach ($actions as $action) { 1950 if (!check_db_fields($actionDbFields, $action)) { 1951 self::exception( 1952 ZBX_API_ERROR_PARAMETERS, 1953 _s('Incorrect parameter for action "%1$s".', $action['name']) 1954 ); 1955 } 1956 if (isset($action['esc_period']) && $action['esc_period'] < SEC_PER_MIN 1957 && $action['eventsource'] == EVENT_SOURCE_TRIGGERS) { 1958 1959 self::exception(ZBX_API_ERROR_PARAMETERS, _s( 1960 'Action "%1$s" has incorrect value for "esc_period" (minimum %2$s seconds).', 1961 $action['name'], SEC_PER_MIN 1962 )); 1963 } 1964 if (isset($duplicates[$action['name']])) { 1965 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" already exists.', $action['name'])); 1966 } 1967 else { 1968 $duplicates[$action['name']] = $action['name']; 1969 } 1970 } 1971 1972 $dbActionsWithSameName = $this->get([ 1973 'filter' => ['name' => $duplicates], 1974 'output' => API_OUTPUT_EXTEND, 1975 'editable' => true, 1976 'nopermissions' => true 1977 ]); 1978 if ($dbActionsWithSameName) { 1979 $dbActionWithSameName = reset($dbActionsWithSameName); 1980 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" already exists.', $dbActionWithSameName['name'])); 1981 } 1982 1983 $filterValidator = new CSchemaValidator($this->getFilterSchema()); 1984 $filterConditionValidator = new CSchemaValidator($this->getFilterConditionSchema()); 1985 1986 $conditionsToValidate = []; 1987 $operationsToValidate = []; 1988 1989 // Validate "filter" sections and "conditions" in them, ensure that "operations" section 1990 // is present and is not empty. Also collect conditions and operations for more validation. 1991 foreach ($actions as $action) { 1992 if (isset($action['filter'])) { 1993 $filterValidator->setObjectName($action['name']); 1994 $this->checkValidator($action['filter'], $filterValidator); 1995 1996 foreach ($action['filter']['conditions'] as $condition) { 1997 $filterConditionValidator->setObjectName($action['name']); 1998 $this->checkValidator($condition, $filterConditionValidator); 1999 $conditionsToValidate[] = $condition; 2000 } 2001 } 2002 2003 if (!isset($action['operations']) || empty($action['operations'])) { 2004 self::exception( 2005 ZBX_API_ERROR_PARAMETERS, 2006 _s('Incorrect parameter for action "%1$s".', $action['name']) 2007 ); 2008 } 2009 else { 2010 foreach ($action['operations'] as $operation) { 2011 if (array_key_exists('operationid', $operation)) { 2012 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect input parameters.')); 2013 } 2014 2015 $operationsToValidate[] = $operation; 2016 } 2017 } 2018 } 2019 2020 // Validate conditions and operations in regard to whats in database now. 2021 if ($conditionsToValidate) { 2022 $this->validateConditionsPermissions($conditionsToValidate); 2023 } 2024 $this->validateOperationsIntegrity($operationsToValidate); 2025 } 2026 2027 /** 2028 * Validate input given to action.update API call. 2029 * 2030 * @param array $actions 2031 * @param array $actionsDb 2032 * 2033 * @internal param array $actionDb 2034 */ 2035 protected function validateUpdate($actions, $actionsDb) { 2036 foreach ($actions as $action) { 2037 if (isset($action['actionid']) && !isset($actionsDb[$action['actionid']])) { 2038 self::exception( 2039 ZBX_API_ERROR_PERMISSIONS, 2040 _('No permissions to referred object or it does not exist!') 2041 ); 2042 } 2043 } 2044 $actions = zbx_toHash($actions, 'actionid'); 2045 2046 // check fields 2047 $duplicates = []; 2048 foreach ($actions as $action) { 2049 $actionName = isset($action['name']) ? $action['name'] : $actionsDb[$action['actionid']]['name']; 2050 2051 if (!check_db_fields(['actionid' => null], $action)) { 2052 self::exception(ZBX_API_ERROR_PARAMETERS, _s( 2053 'Incorrect parameters for action update method "%1$s".', $actionName 2054 )); 2055 } 2056 2057 // check if user changed esc_period for trigger eventsource 2058 if (isset($action['esc_period']) 2059 && $action['esc_period'] < SEC_PER_MIN 2060 && $actionsDb[$action['actionid']]['eventsource'] == EVENT_SOURCE_TRIGGERS) { 2061 2062 self::exception(ZBX_API_ERROR_PARAMETERS, _s( 2063 'Action "%1$s" has incorrect value for "esc_period" (minimum %2$s seconds).', 2064 $actionName, SEC_PER_MIN 2065 )); 2066 } 2067 2068 $this->checkNoParameters( 2069 $action, 2070 ['eventsource'], 2071 _('Cannot update "%1$s" for action "%2$s".'), 2072 $actionName 2073 ); 2074 2075 if (!isset($action['name'])) { 2076 continue; 2077 } 2078 2079 if (isset($duplicates[$action['name']])) { 2080 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" already exists.', $action['name'])); 2081 } 2082 else { 2083 $duplicates[$action['name']] = $action['name']; 2084 } 2085 } 2086 2087 // Unset accidentally passed in "evaltype" and "formula" fields. 2088 foreach ($actions as &$action) { 2089 unset($action['evaltype'], $action['formula']); 2090 } 2091 unset($action); 2092 2093 $filterValidator = new CSchemaValidator($this->getFilterSchema()); 2094 2095 $filterConditionValidator = new CSchemaValidator($this->getFilterConditionSchema()); 2096 2097 $operationsToValidate = []; 2098 $conditionsToValidate = []; 2099 2100 foreach ($actions as $actionId => $action) { 2101 $actionDb = $actionsDb[$actionId]; 2102 2103 if (isset($action['name'])) { 2104 $actionName = $action['name']; 2105 2106 $actionExists = $this->get([ 2107 'filter' => ['name' => $actionName], 2108 'output' => ['actionid'], 2109 'editable' => true, 2110 'nopermissions' => true, 2111 'preservekeys' => true 2112 ]); 2113 if (($actionExists = reset($actionExists)) 2114 && (bccomp($actionExists['actionid'], $actionId) != 0) 2115 ) { 2116 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" already exists.', $actionName)); 2117 } 2118 } 2119 else { 2120 $actionName = $actionDb['name']; 2121 } 2122 2123 if (isset($action['filter'])) { 2124 $actionFilter = $action['filter']; 2125 2126 $filterValidator->setObjectName($actionName); 2127 $filterConditionValidator->setObjectName($actionName); 2128 2129 $this->checkValidator($actionFilter, $filterValidator); 2130 2131 foreach ($actionFilter['conditions'] as $condition) { 2132 $this->checkValidator($condition, $filterConditionValidator); 2133 $conditionsToValidate[] = $condition; 2134 } 2135 } 2136 2137 if (isset($action['operations']) && empty($action['operations'])) { 2138 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" no operations defined.', $actionName)); 2139 } 2140 elseif (isset($action['operations'])) { 2141 $operationsDb = $actionsDb[$action['actionid']]['operations']; 2142 $operationsDb = zbx_toHash($operationsDb, 'operationid'); 2143 foreach ($action['operations'] as $operation) { 2144 if (!isset($operation['operationid'])) { 2145 $operationsToValidate[] = $operation; 2146 } 2147 elseif (isset($operationsDb[$operation['operationid']])) { 2148 $operationsToValidate[] = $operation; 2149 } 2150 else { 2151 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operationid.')); 2152 } 2153 } 2154 } 2155 } 2156 2157 if ($conditionsToValidate) { 2158 $this->validateConditionsPermissions($conditionsToValidate); 2159 } 2160 if ($operationsToValidate) { 2161 $this->validateOperationsIntegrity($operationsToValidate); 2162 } 2163 } 2164 2165 /** 2166 * Check permissions to DB entities referenced by action conditions. 2167 * 2168 * @param array $conditions conditions for which permissions to referenced DB entities will be checked 2169 */ 2170 protected function validateConditionsPermissions(array $conditions) { 2171 $hostGroupIdsAll = []; 2172 $templateIdsAll = []; 2173 $triggerIdsAll = []; 2174 $hostIdsAll = []; 2175 $discoveryRuleIdsAll = []; 2176 $discoveryCheckIdsAll = []; 2177 $proxyIdsAll = []; 2178 2179 foreach ($conditions as $condition) { 2180 $conditionValue = $condition['value']; 2181 // validate condition values depending on condition type 2182 switch ($condition['conditiontype']) { 2183 case CONDITION_TYPE_HOST_GROUP: 2184 $hostGroupIdsAll[$conditionValue] = $conditionValue; 2185 break; 2186 2187 case CONDITION_TYPE_TEMPLATE: 2188 $templateIdsAll[$conditionValue] = $conditionValue; 2189 break; 2190 2191 case CONDITION_TYPE_TRIGGER: 2192 $triggerIdsAll[$conditionValue] = $conditionValue; 2193 break; 2194 2195 case CONDITION_TYPE_HOST: 2196 $hostIdsAll[$conditionValue] = $conditionValue; 2197 break; 2198 2199 case CONDITION_TYPE_DRULE: 2200 $discoveryRuleIdsAll[$conditionValue] = $conditionValue; 2201 break; 2202 2203 case CONDITION_TYPE_DCHECK: 2204 $discoveryCheckIdsAll[$conditionValue] = $conditionValue; 2205 break; 2206 2207 case CONDITION_TYPE_PROXY: 2208 $proxyIdsAll[$conditionValue] = $conditionValue; 2209 break; 2210 } 2211 } 2212 2213 if (!API::HostGroup()->isWritable($hostGroupIdsAll)) { 2214 self::exception( 2215 ZBX_API_ERROR_PARAMETERS, 2216 _('Incorrect action condition host group. Host group does not exist or you have no access to it.') 2217 ); 2218 } 2219 if (!API::Host()->isWritable($hostIdsAll)) { 2220 self::exception( 2221 ZBX_API_ERROR_PARAMETERS, 2222 _('Incorrect action condition host. Host does not exist or you have no access to it.') 2223 ); 2224 } 2225 if (!API::Template()->isWritable($templateIdsAll)) { 2226 self::exception( 2227 ZBX_API_ERROR_PARAMETERS, 2228 _('Incorrect action condition template. Template does not exist or you have no access to it.') 2229 ); 2230 } 2231 if (!API::Trigger()->isWritable($triggerIdsAll)) { 2232 self::exception( 2233 ZBX_API_ERROR_PARAMETERS, 2234 _('Incorrect action condition trigger. Trigger does not exist or you have no access to it.') 2235 ); 2236 } 2237 if (!API::DRule()->isWritable($discoveryRuleIdsAll)) { 2238 self::exception( 2239 ZBX_API_ERROR_PARAMETERS, 2240 _('Incorrect action condition discovery rule. Discovery rule does not exist or you have no access to it.') 2241 ); 2242 } 2243 if (!API::DCheck()->isWritable($discoveryCheckIdsAll)) { 2244 self::exception( 2245 ZBX_API_ERROR_PARAMETERS, 2246 _('Incorrect action condition discovery check. Discovery check does not exist or you have no access to it.') 2247 ); 2248 } 2249 if (!API::Proxy()->isWritable($proxyIdsAll)) { 2250 self::exception( 2251 ZBX_API_ERROR_PARAMETERS, 2252 _('Incorrect action condition proxy. Proxy does not exist or you have no access to it.') 2253 ); 2254 } 2255 } 2256} 2257