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 */ 25class CAction extends CApiService { 26 27 public const ACCESS_RULES = [ 28 'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER], 29 'create' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN], 30 'update' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN], 31 'delete' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN], 32 'validateoperationsintegrity' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN] 33 ]; 34 35 protected $tableName = 'actions'; 36 protected $tableAlias = 'a'; 37 protected $sortColumns = ['actionid', 'name', 'status']; 38 39 /** 40 * Valid condition types for each event source. 41 * 42 * @var array 43 */ 44 protected $valid_condition_types = [ 45 EVENT_SOURCE_TRIGGERS => [ 46 CONDITION_TYPE_HOST_GROUP, CONDITION_TYPE_HOST, CONDITION_TYPE_TRIGGER, CONDITION_TYPE_TRIGGER_NAME, 47 CONDITION_TYPE_TRIGGER_SEVERITY, CONDITION_TYPE_TIME_PERIOD, CONDITION_TYPE_TEMPLATE, 48 CONDITION_TYPE_SUPPRESSED, CONDITION_TYPE_EVENT_TAG, CONDITION_TYPE_EVENT_TAG_VALUE 49 ], 50 EVENT_SOURCE_DISCOVERY => [ 51 CONDITION_TYPE_DHOST_IP, CONDITION_TYPE_DSERVICE_TYPE, CONDITION_TYPE_DSERVICE_PORT, CONDITION_TYPE_DSTATUS, 52 CONDITION_TYPE_DUPTIME, CONDITION_TYPE_DVALUE, CONDITION_TYPE_DRULE, CONDITION_TYPE_DCHECK, 53 CONDITION_TYPE_PROXY, CONDITION_TYPE_DOBJECT 54 ], 55 EVENT_SOURCE_AUTOREGISTRATION => [ 56 CONDITION_TYPE_PROXY, CONDITION_TYPE_HOST_NAME, CONDITION_TYPE_HOST_METADATA 57 ], 58 EVENT_SOURCE_INTERNAL => [ 59 CONDITION_TYPE_HOST_GROUP, CONDITION_TYPE_HOST, CONDITION_TYPE_TEMPLATE, CONDITION_TYPE_EVENT_TYPE, 60 CONDITION_TYPE_EVENT_TAG, CONDITION_TYPE_EVENT_TAG_VALUE 61 ] 62 ]; 63 64 /** 65 * Valid operators for each condition type. 66 * 67 * @var array 68 */ 69 protected $valid_condition_type_operators = [ 70 CONDITION_TYPE_HOST_GROUP => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 71 CONDITION_TYPE_HOST => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 72 CONDITION_TYPE_TRIGGER => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 73 CONDITION_TYPE_TRIGGER_NAME => [CONDITION_OPERATOR_LIKE, CONDITION_OPERATOR_NOT_LIKE], 74 CONDITION_TYPE_TRIGGER_SEVERITY => [ 75 CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL, CONDITION_OPERATOR_MORE_EQUAL, 76 CONDITION_OPERATOR_LESS_EQUAL 77 ], 78 CONDITION_TYPE_TIME_PERIOD => [CONDITION_OPERATOR_IN, CONDITION_OPERATOR_NOT_IN], 79 CONDITION_TYPE_DHOST_IP => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 80 CONDITION_TYPE_DSERVICE_TYPE => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 81 CONDITION_TYPE_DSERVICE_PORT => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 82 CONDITION_TYPE_DSTATUS => [CONDITION_OPERATOR_EQUAL], 83 CONDITION_TYPE_DUPTIME => [CONDITION_OPERATOR_MORE_EQUAL, CONDITION_OPERATOR_LESS_EQUAL], 84 CONDITION_TYPE_DVALUE => [ 85 CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL, CONDITION_OPERATOR_LIKE, 86 CONDITION_OPERATOR_NOT_LIKE, CONDITION_OPERATOR_MORE_EQUAL, CONDITION_OPERATOR_LESS_EQUAL 87 ], 88 CONDITION_TYPE_TEMPLATE => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 89 CONDITION_TYPE_SUPPRESSED => [CONDITION_OPERATOR_YES, CONDITION_OPERATOR_NO], 90 CONDITION_TYPE_DRULE => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 91 CONDITION_TYPE_DCHECK => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 92 CONDITION_TYPE_PROXY => [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL], 93 CONDITION_TYPE_DOBJECT => [CONDITION_OPERATOR_EQUAL], 94 CONDITION_TYPE_HOST_NAME => [CONDITION_OPERATOR_LIKE, CONDITION_OPERATOR_NOT_LIKE, CONDITION_OPERATOR_REGEXP, 95 CONDITION_OPERATOR_NOT_REGEXP 96 ], 97 CONDITION_TYPE_EVENT_TYPE => [CONDITION_OPERATOR_EQUAL], 98 CONDITION_TYPE_HOST_METADATA => [CONDITION_OPERATOR_LIKE, CONDITION_OPERATOR_NOT_LIKE, 99 CONDITION_OPERATOR_REGEXP, CONDITION_OPERATOR_NOT_REGEXP 100 ], 101 CONDITION_TYPE_EVENT_TAG => [ 102 CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL, CONDITION_OPERATOR_LIKE, CONDITION_OPERATOR_NOT_LIKE 103 ], 104 CONDITION_TYPE_EVENT_TAG_VALUE => [ 105 CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL, CONDITION_OPERATOR_LIKE, CONDITION_OPERATOR_NOT_LIKE 106 ] 107 ]; 108 109 /** 110 * Get actions data 111 * 112 * @param array $options 113 * @param array $options['itemids'] 114 * @param array $options['hostids'] 115 * @param array $options['groupids'] 116 * @param array $options['actionids'] 117 * @param array $options['status'] 118 * @param bool $options['editable'] 119 * @param array $options['extendoutput'] 120 * @param array $options['count'] 121 * @param array $options['pattern'] 122 * @param array $options['limit'] 123 * @param array $options['order'] 124 * 125 * @return array|int item data as array or false if error 126 */ 127 public function get($options = []) { 128 $result = []; 129 130 $sqlParts = [ 131 'select' => ['actions' => 'a.actionid'], 132 'from' => ['actions' => 'actions a'], 133 'where' => [], 134 'order' => [], 135 'limit' => null 136 ]; 137 138 $defOptions = [ 139 'groupids' => null, 140 'hostids' => null, 141 'actionids' => null, 142 'triggerids' => null, 143 'mediatypeids' => null, 144 'usrgrpids' => null, 145 'userids' => null, 146 'scriptids' => null, 147 'nopermissions' => null, 148 'editable' => false, 149 // filter 150 'filter' => null, 151 'search' => null, 152 'searchByAny' => null, 153 'startSearch' => false, 154 'excludeSearch' => false, 155 'searchWildcardsEnabled' => null, 156 // output 157 'output' => API_OUTPUT_EXTEND, 158 'selectFilter' => null, 159 'selectOperations' => null, 160 'selectRecoveryOperations' => null, 161 'selectAcknowledgeOperations' => null, 162 'countOutput' => false, 163 'preservekeys' => false, 164 'sortfield' => '', 165 'sortorder' => '', 166 'limit' => null 167 ]; 168 $options = zbx_array_merge($defOptions, $options); 169 170 // editable + PERMISSION CHECK 171 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) { 172 // conditions are checked here by sql, operations after, by api queries 173 $permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ; 174 $userGroups = getUserGroupsByUserId(self::$userData['userid']); 175 176 // condition hostgroup 177 $sqlParts['where'][] = 'NOT EXISTS ('. 178 'SELECT NULL'. 179 ' FROM conditions cc'. 180 ' LEFT JOIN rights r'. 181 ' ON r.id='.zbx_dbcast_2bigint('cc.value'). 182 ' AND '.dbConditionInt('r.groupid', $userGroups). 183 ' WHERE a.actionid=cc.actionid'. 184 ' AND cc.conditiontype='.CONDITION_TYPE_HOST_GROUP. 185 ' GROUP BY cc.value'. 186 ' HAVING MIN(r.permission) IS NULL'. 187 ' OR MIN(r.permission)='.PERM_DENY. 188 ' OR MAX(r.permission)<'.zbx_dbstr($permission). 189 ')'; 190 191 // condition host or template 192 $sqlParts['where'][] = 'NOT EXISTS ('. 193 'SELECT NULL'. 194 ' FROM conditions cc,hosts_groups hgg'. 195 ' LEFT JOIN rights r'. 196 ' ON r.id=hgg.groupid'. 197 ' AND '.dbConditionInt('r.groupid', $userGroups). 198 ' WHERE a.actionid=cc.actionid'. 199 ' AND '.zbx_dbcast_2bigint('cc.value').'=hgg.hostid'. 200 ' AND cc.conditiontype IN ('.CONDITION_TYPE_HOST.','.CONDITION_TYPE_TEMPLATE.')'. 201 ' GROUP BY cc.value'. 202 ' HAVING MIN(r.permission) IS NULL'. 203 ' OR MIN(r.permission)='.PERM_DENY. 204 ' OR MAX(r.permission)<'.zbx_dbstr($permission). 205 ')'; 206 207 // condition trigger 208 $sqlParts['where'][] = 'NOT EXISTS ('. 209 'SELECT NULL'. 210 ' FROM conditions cc,functions f,items i,hosts_groups hgg'. 211 ' LEFT JOIN rights r'. 212 ' ON r.id=hgg.groupid'. 213 ' AND '.dbConditionInt('r.groupid', $userGroups). 214 ' WHERE a.actionid=cc.actionid'. 215 ' AND '.zbx_dbcast_2bigint('cc.value').'=f.triggerid'. 216 ' AND f.itemid=i.itemid'. 217 ' AND i.hostid=hgg.hostid'. 218 ' AND cc.conditiontype='.CONDITION_TYPE_TRIGGER. 219 ' GROUP BY cc.value'. 220 ' HAVING MIN(r.permission) IS NULL'. 221 ' OR MIN(r.permission)='.PERM_DENY. 222 ' OR MAX(r.permission)<'.zbx_dbstr($permission). 223 ')'; 224 } 225 226 // actionids 227 if (!is_null($options['actionids'])) { 228 zbx_value2array($options['actionids']); 229 230 $sqlParts['where'][] = dbConditionInt('a.actionid', $options['actionids']); 231 } 232 233 // groupids 234 if (!is_null($options['groupids'])) { 235 zbx_value2array($options['groupids']); 236 237 $sqlParts['from']['conditions_groups'] = 'conditions cg'; 238 $sqlParts['where'][] = dbConditionString('cg.value', $options['groupids']); 239 $sqlParts['where']['ctg'] = 'cg.conditiontype='.CONDITION_TYPE_HOST_GROUP; 240 $sqlParts['where']['acg'] = 'a.actionid=cg.actionid'; 241 } 242 243 // hostids 244 if (!is_null($options['hostids'])) { 245 zbx_value2array($options['hostids']); 246 247 $sqlParts['from']['conditions_hosts'] = 'conditions ch'; 248 $sqlParts['where'][] = dbConditionString('ch.value', $options['hostids']); 249 $sqlParts['where']['cth'] = 'ch.conditiontype='.CONDITION_TYPE_HOST; 250 $sqlParts['where']['ach'] = 'a.actionid=ch.actionid'; 251 } 252 253 // triggerids 254 if (!is_null($options['triggerids'])) { 255 zbx_value2array($options['triggerids']); 256 257 $sqlParts['from']['conditions_triggers'] = 'conditions ct'; 258 $sqlParts['where'][] = dbConditionString('ct.value', $options['triggerids']); 259 $sqlParts['where']['ctt'] = 'ct.conditiontype='.CONDITION_TYPE_TRIGGER; 260 $sqlParts['where']['act'] = 'a.actionid=ct.actionid'; 261 } 262 263 // mediatypeids 264 if (!is_null($options['mediatypeids'])) { 265 zbx_value2array($options['mediatypeids']); 266 267 $sqlParts['from']['opmessage'] = 'opmessage om'; 268 $sqlParts['from']['operations_media'] = 'operations omed'; 269 $sqlParts['where'][] = dbConditionId('om.mediatypeid', $options['mediatypeids']); 270 $sqlParts['where']['aomed'] = 'a.actionid=omed.actionid'; 271 $sqlParts['where']['oom'] = 'omed.operationid=om.operationid'; 272 } 273 274 // operation messages 275 // usrgrpids 276 if (!is_null($options['usrgrpids'])) { 277 zbx_value2array($options['usrgrpids']); 278 279 $sqlParts['from']['opmessage_grp'] = 'opmessage_grp omg'; 280 $sqlParts['from']['operations_usergroups'] = 'operations oug'; 281 $sqlParts['where'][] = dbConditionInt('omg.usrgrpid', $options['usrgrpids']); 282 $sqlParts['where']['aoug'] = 'a.actionid=oug.actionid'; 283 $sqlParts['where']['oomg'] = 'oug.operationid=omg.operationid'; 284 } 285 286 // userids 287 if (!is_null($options['userids'])) { 288 zbx_value2array($options['userids']); 289 290 $sqlParts['from']['opmessage_usr'] = 'opmessage_usr omu'; 291 $sqlParts['from']['operations_users'] = 'operations ou'; 292 $sqlParts['where'][] = dbConditionInt('omu.userid', $options['userids']); 293 $sqlParts['where']['aou'] = 'a.actionid=ou.actionid'; 294 $sqlParts['where']['oomu'] = 'ou.operationid=omu.operationid'; 295 } 296 297 // operation commands 298 // scriptids 299 if (!is_null($options['scriptids'])) { 300 zbx_value2array($options['scriptids']); 301 302 $sqlParts['from']['opcommand'] = 'opcommand oc'; 303 $sqlParts['from']['operations_scripts'] = 'operations os'; 304 $sqlParts['where'][] = dbConditionInt('oc.scriptid', $options['scriptids']); 305 $sqlParts['where']['aos'] = 'a.actionid=os.actionid'; 306 $sqlParts['where']['ooc'] = 'os.operationid=oc.operationid'; 307 } 308 309 // filter 310 if (is_array($options['filter'])) { 311 if (array_key_exists('esc_period', $options['filter']) && $options['filter']['esc_period'] !== null) { 312 $options['filter']['esc_period'] = getTimeUnitFilters($options['filter']['esc_period']); 313 } 314 315 $this->dbFilter('actions a', $options, $sqlParts); 316 } 317 318 // search 319 if (is_array($options['search'])) { 320 zbx_db_search('actions a', $options, $sqlParts); 321 } 322 323 // limit 324 if (zbx_ctype_digit($options['limit']) && $options['limit']) { 325 $sqlParts['limit'] = $options['limit']; 326 } 327 328 $actionIds = []; 329 330 $sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 331 $sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 332 $dbRes = DBselect(self::createSelectQueryFromParts($sqlParts), $sqlParts['limit']); 333 while ($action = DBfetch($dbRes)) { 334 if ($options['countOutput']) { 335 $result = $action['rowscount']; 336 } 337 else { 338 $actionIds[$action['actionid']] = $action['actionid']; 339 340 $result[$action['actionid']] = $action; 341 } 342 } 343 344 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) { 345 // check hosts, templates 346 $hosts = []; 347 $hostIds = []; 348 $sql = 'SELECT o.actionid,och.hostid'. 349 ' FROM operations o,opcommand_hst och'. 350 ' WHERE o.operationid=och.operationid'. 351 ' AND och.hostid<>0'. 352 ' AND '.dbConditionInt('o.actionid', $actionIds); 353 $dbHosts = DBselect($sql); 354 while ($host = DBfetch($dbHosts)) { 355 if (!isset($hosts[$host['hostid']])) { 356 $hosts[$host['hostid']] = []; 357 } 358 $hosts[$host['hostid']][$host['actionid']] = $host['actionid']; 359 $hostIds[$host['hostid']] = $host['hostid']; 360 } 361 362 $dbTemplates = DBselect( 363 'SELECT o.actionid,ot.templateid'. 364 ' FROM operations o,optemplate ot'. 365 ' WHERE o.operationid=ot.operationid'. 366 ' AND '.dbConditionInt('o.actionid', $actionIds) 367 ); 368 while ($template = DBfetch($dbTemplates)) { 369 if (!isset($hosts[$template['templateid']])) { 370 $hosts[$template['templateid']] = []; 371 } 372 $hosts[$template['templateid']][$template['actionid']] = $template['actionid']; 373 $hostIds[$template['templateid']] = $template['templateid']; 374 } 375 376 $allowedHosts = API::Host()->get([ 377 'hostids' => $hostIds, 378 'output' => ['hostid'], 379 'editable' => $options['editable'], 380 'templated_hosts' => true, 381 'preservekeys' => true 382 ]); 383 foreach ($hostIds as $hostId) { 384 if (isset($allowedHosts[$hostId])) { 385 continue; 386 } 387 foreach ($hosts[$hostId] as $actionId) { 388 unset($result[$actionId], $actionIds[$actionId]); 389 } 390 } 391 unset($allowedHosts); 392 393 // check hostgroups 394 $groups = []; 395 $groupIds = []; 396 $dbGroups = DBselect( 397 'SELECT o.actionid,ocg.groupid'. 398 ' FROM operations o,opcommand_grp ocg'. 399 ' WHERE o.operationid=ocg.operationid'. 400 ' AND '.dbConditionInt('o.actionid', $actionIds) 401 ); 402 while ($group = DBfetch($dbGroups)) { 403 if (!isset($groups[$group['groupid']])) { 404 $groups[$group['groupid']] = []; 405 } 406 $groups[$group['groupid']][$group['actionid']] = $group['actionid']; 407 $groupIds[$group['groupid']] = $group['groupid']; 408 } 409 410 $dbGroups = DBselect( 411 'SELECT o.actionid,og.groupid'. 412 ' FROM operations o,opgroup og'. 413 ' WHERE o.operationid=og.operationid'. 414 ' AND '.dbConditionInt('o.actionid', $actionIds) 415 ); 416 while ($group = DBfetch($dbGroups)) { 417 if (!isset($groups[$group['groupid']])) { 418 $groups[$group['groupid']] = []; 419 } 420 $groups[$group['groupid']][$group['actionid']] = $group['actionid']; 421 $groupIds[$group['groupid']] = $group['groupid']; 422 } 423 424 $allowedGroups = API::HostGroup()->get([ 425 'groupids' => $groupIds, 426 'output' => ['groupid'], 427 'editable' => $options['editable'], 428 'preservekeys' => true 429 ]); 430 foreach ($groupIds as $groupId) { 431 if (isset($allowedGroups[$groupId])) { 432 continue; 433 } 434 foreach ($groups[$groupId] as $actionId) { 435 unset($result[$actionId], $actionIds[$actionId]); 436 } 437 } 438 unset($allowedGroups); 439 440 // check scripts 441 $scripts = []; 442 $scriptIds = []; 443 $dbScripts = DBselect( 444 'SELECT o.actionid,oc.scriptid'. 445 ' FROM operations o,opcommand oc'. 446 ' WHERE o.operationid=oc.operationid'. 447 ' AND '.dbConditionInt('o.actionid', $actionIds) 448 ); 449 while ($script = DBfetch($dbScripts)) { 450 if (!isset($scripts[$script['scriptid']])) { 451 $scripts[$script['scriptid']] = []; 452 } 453 $scripts[$script['scriptid']][$script['actionid']] = $script['actionid']; 454 $scriptIds[$script['scriptid']] = $script['scriptid']; 455 } 456 457 $allowedScripts = API::Script()->get([ 458 'output' => ['scriptid'], 459 'scriptids' => $scriptIds, 460 'filter' => ['scope' => ZBX_SCRIPT_SCOPE_ACTION], 461 'preservekeys' => true 462 ]); 463 foreach ($scriptIds as $scriptId) { 464 if (isset($allowedScripts[$scriptId])) { 465 continue; 466 } 467 foreach ($scripts[$scriptId] as $actionId) { 468 unset($result[$actionId], $actionIds[$actionId]); 469 } 470 } 471 unset($allowedScripts); 472 473 // check users 474 $users = []; 475 $userIds = []; 476 $dbUsers = DBselect( 477 'SELECT o.actionid,omu.userid'. 478 ' FROM operations o,opmessage_usr omu'. 479 ' WHERE o.operationid=omu.operationid'. 480 ' AND '.dbConditionInt('o.actionid', $actionIds) 481 ); 482 while ($user = DBfetch($dbUsers)) { 483 if (!isset($users[$user['userid']])) { 484 $users[$user['userid']] = []; 485 } 486 $users[$user['userid']][$user['actionid']] = $user['actionid']; 487 $userIds[$user['userid']] = $user['userid']; 488 } 489 490 $allowedUsers = API::User()->get([ 491 'userids' => $userIds, 492 'output' => ['userid'], 493 'preservekeys' => true 494 ]); 495 foreach ($userIds as $userId) { 496 if (isset($allowedUsers[$userId])) { 497 continue; 498 } 499 foreach ($users[$userId] as $actionId) { 500 unset($result[$actionId], $actionIds[$actionId]); 501 } 502 } 503 504 // check usergroups 505 $userGroups = []; 506 $userGroupIds = []; 507 $dbUserGroups = DBselect( 508 'SELECT o.actionid,omg.usrgrpid'. 509 ' FROM operations o,opmessage_grp omg'. 510 ' WHERE o.operationid=omg.operationid'. 511 ' AND '.dbConditionInt('o.actionid', $actionIds) 512 ); 513 while ($userGroup = DBfetch($dbUserGroups)) { 514 if (!isset($userGroups[$userGroup['usrgrpid']])) { 515 $userGroups[$userGroup['usrgrpid']] = []; 516 } 517 $userGroups[$userGroup['usrgrpid']][$userGroup['actionid']] = $userGroup['actionid']; 518 $userGroupIds[$userGroup['usrgrpid']] = $userGroup['usrgrpid']; 519 } 520 521 $allowedUserGroups = API::UserGroup()->get([ 522 'usrgrpids' => $userGroupIds, 523 'output' => ['usrgrpid'], 524 'preservekeys' => true 525 ]); 526 527 foreach ($userGroupIds as $userGroupId) { 528 if (isset($allowedUserGroups[$userGroupId])) { 529 continue; 530 } 531 foreach ($userGroups[$userGroupId] as $actionId) { 532 unset($result[$actionId], $actionIds[$actionId]); 533 } 534 } 535 } 536 537 if ($options['countOutput']) { 538 return $result; 539 } 540 541 if ($result) { 542 $result = $this->addRelatedObjects($options, $result); 543 544 foreach ($result as &$action) { 545 // unset the fields that are returned in the filter 546 unset($action['formula'], $action['evaltype']); 547 548 if ($options['selectFilter'] !== null) { 549 $filter = $this->unsetExtraFields( 550 [$action['filter']], 551 ['conditions', 'formula', 'evaltype'], 552 $options['selectFilter'] 553 ); 554 $filter = reset($filter); 555 556 if (isset($filter['conditions'])) { 557 foreach ($filter['conditions'] as &$condition) { 558 unset($condition['actionid'], $condition['conditionid']); 559 } 560 unset($condition); 561 } 562 563 $action['filter'] = $filter; 564 } 565 } 566 unset($action); 567 } 568 569 // removing keys (hash -> array) 570 if (!$options['preservekeys']) { 571 $result = zbx_cleanHashes($result); 572 } 573 574 return $result; 575 } 576 577 /** 578 * Add actions. 579 * 580 * @param array $actions multidimensional array with actions data 581 * @param array $actions[0,...]['expression'] 582 * @param array $actions[0,...]['description'] 583 * @param array $actions[0,...]['type'] OPTIONAL 584 * @param array $actions[0,...]['priority'] OPTIONAL 585 * @param array $actions[0,...]['status'] OPTIONAL 586 * @param array $actions[0,...]['comments'] OPTIONAL 587 * @param array $actions[0,...]['url'] OPTIONAL 588 * @param array $actions[0,...]['filter'] OPTIONAL 589 * @param array $actions[0,...]['pause_suppressed'] OPTIONAL 590 * 591 * @return array 592 */ 593 public function create($actions) { 594 $actions = zbx_toArray($actions); 595 596 $this->validateCreate($actions); 597 598 // Set "evaltype" if specified in "filter" section of action. 599 foreach ($actions as &$action) { 600 if (isset($action['filter'])) { 601 $action['evaltype'] = $action['filter']['evaltype']; 602 } 603 604 // Set default values for recovery operations and their messages. 605 if (array_key_exists('recovery_operations', $action)) { 606 foreach ($action['recovery_operations'] as &$operation) { 607 if ($operation['operationtype'] == OPERATION_TYPE_MESSAGE 608 || $operation['operationtype'] == OPERATION_TYPE_RECOVERY_MESSAGE) { 609 $message = (array_key_exists('opmessage', $operation) && is_array($operation['opmessage'])) 610 ? $operation['opmessage'] 611 : []; 612 613 $operation['opmessage'] = $message + [ 614 'default_msg' => 1, 615 'mediatypeid' => 0, 616 'subject' => '', 617 'message' => '' 618 ]; 619 } 620 } 621 unset($operation); 622 } 623 624 // Set default values for acknowledge operations and their messages. 625 if (array_key_exists('acknowledge_operations', $action)) { 626 foreach ($action['acknowledge_operations'] as &$operation) { 627 if ($operation['operationtype'] == OPERATION_TYPE_MESSAGE 628 || $operation['operationtype'] == OPERATION_TYPE_ACK_MESSAGE) { 629 $message = (array_key_exists('opmessage', $operation) && is_array($operation['opmessage'])) 630 ? $operation['opmessage'] 631 : []; 632 633 $operation['opmessage'] = $message + [ 634 'default_msg' => 1, 635 'mediatypeid' => 0, 636 'subject' => '', 637 'message' => '' 638 ]; 639 } 640 } 641 unset($operation); 642 } 643 } 644 unset($action); 645 646 // Insert actions into db, get back array with new actionids. 647 $actions = DB::save('actions', $actions); 648 $actions = zbx_toHash($actions, 'actionid'); 649 $audit = []; 650 651 $conditions_to_create = []; 652 $operations_to_create = []; 653 654 // Collect conditions and operations to be created and set appropriate action ID. 655 foreach ($actions as $actionid => $action) { 656 $audit[] = ['actionid' => $actionid, 'name' => $action['name']]; 657 658 if (isset($action['filter'])) { 659 foreach ($action['filter']['conditions'] as $condition) { 660 $condition['actionid'] = $actionid; 661 $conditions_to_create[] = $condition; 662 } 663 } 664 665 if (array_key_exists('operations', $action) && $action['operations']) { 666 foreach ($action['operations'] as $operation) { 667 $operation['actionid'] = $actionid; 668 $operation['recovery'] = ACTION_OPERATION; 669 $operations_to_create[] = $operation; 670 } 671 } 672 673 if (array_key_exists('recovery_operations', $action) && $action['recovery_operations']) { 674 foreach ($action['recovery_operations'] as $recovery_operation) { 675 $recovery_operation['actionid'] = $actionid; 676 $recovery_operation['recovery'] = ACTION_RECOVERY_OPERATION; 677 unset($recovery_operation['esc_period'], $recovery_operation['esc_step_from'], 678 $recovery_operation['esc_step_to'] 679 ); 680 681 if ($recovery_operation['operationtype'] == OPERATION_TYPE_RECOVERY_MESSAGE) { 682 unset($recovery_operation['opmessage']['mediatypeid']); 683 } 684 685 $operations_to_create[] = $recovery_operation; 686 } 687 } 688 689 if (array_key_exists('acknowledge_operations', $action) && $action['acknowledge_operations']) { 690 foreach ($action['acknowledge_operations'] as $ack_operation) { 691 $ack_operation['actionid'] = $actionid; 692 $ack_operation['recovery'] = ACTION_ACKNOWLEDGE_OPERATION; 693 unset($ack_operation['esc_period'], $ack_operation['esc_step_from'], $ack_operation['esc_step_to']); 694 $operations_to_create[] = $ack_operation; 695 } 696 } 697 } 698 699 $createdConditions = $this->addConditions($conditions_to_create); 700 701 // Group back created action conditions by action ID to be used for updating action formula. 702 $conditionsForActions = []; 703 foreach ($createdConditions as $condition) { 704 $conditionsForActions[$condition['actionid']][$condition['conditionid']] = $condition; 705 } 706 707 // Update "formula" field if evaltype is custom expression. 708 foreach ($actions as $actionid => $action) { 709 if (isset($action['filter'])) { 710 $actionFilter = $action['filter']; 711 if ($actionFilter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 712 $this->updateFormula($actionid, $actionFilter['formula'], $conditionsForActions[$actionid]); 713 } 714 } 715 } 716 717 // Add operations. 718 $this->addOperations($operations_to_create); 719 720 $this->addAuditBulk(AUDIT_ACTION_ADD, AUDIT_RESOURCE_ACTION, $audit); 721 722 return ['actionids' => array_keys($actions)]; 723 } 724 725 /** 726 * Update actions. 727 * 728 * @param array $actions multidimensional array with actions data 729 * @param array $actions[0,...]['actionid'] 730 * @param array $actions[0,...]['expression'] 731 * @param array $actions[0,...]['description'] 732 * @param array $actions[0,...]['type'] OPTIONAL 733 * @param array $actions[0,...]['priority'] OPTIONAL 734 * @param array $actions[0,...]['status'] OPTIONAL 735 * @param array $actions[0,...]['comments'] OPTIONAL 736 * @param array $actions[0,...]['url'] OPTIONAL 737 * @param array $actions[0,...]['filter'] OPTIONAL 738 * @param array $actions[0,...]['pause_suppressed'] OPTIONAL 739 * 740 * @return array 741 */ 742 public function update($actions) { 743 $actions = zbx_toArray($actions); 744 $actions = zbx_toHash($actions, 'actionid'); 745 $actionIds = array_keys($actions); 746 747 $db_actions = $this->get([ 748 'output' => API_OUTPUT_EXTEND, 749 'selectFilter' => ['formula', 'conditions'], 750 'selectOperations' => API_OUTPUT_EXTEND, 751 'selectRecoveryOperations' => API_OUTPUT_EXTEND, 752 'selectAcknowledgeOperations' => ['operationid', 'actionid', 'operationtype', 'opmessage', 'opmessage_grp', 753 'opmessage_usr', 'opcommand', 'opcommand_hst', 'opcommand_grp' 754 ], 755 'actionids' => $actionIds, 756 'editable' => true, 757 'preservekeys' => true 758 ]); 759 760 $this->validateUpdate($actions, $db_actions); 761 762 $operations_to_create = []; 763 $operations_to_update = []; 764 $operationids_to_delete = []; 765 766 $actions_update_data = []; 767 768 $newActionConditions = null; 769 foreach ($actions as $actionId => $action) { 770 $db_action = $db_actions[$actionId]; 771 772 $actionUpdateValues = $action; 773 unset( 774 $actionUpdateValues['actionid'], 775 $actionUpdateValues['filter'], 776 $actionUpdateValues['operations'], 777 $actionUpdateValues['recovery_operations'], 778 $actionUpdateValues['acknowledge_operations'], 779 $actionUpdateValues['conditions'], 780 $actionUpdateValues['formula'], 781 $actionUpdateValues['evaltype'] 782 ); 783 784 if (isset($action['filter'])) { 785 $actionFilter = $action['filter']; 786 787 // set formula to empty string of not custom expression 788 if ($actionFilter['evaltype'] != CONDITION_EVAL_TYPE_EXPRESSION) { 789 $actionUpdateValues['formula'] = ''; 790 } 791 792 $actionUpdateValues['evaltype'] = $actionFilter['evaltype']; 793 } 794 795 if (array_key_exists('operations', $action)) { 796 $db_operations = zbx_toHash($db_action['operations'], 'operationid'); 797 798 foreach ($action['operations'] as $operation) { 799 if (!array_key_exists('operationid', $operation)) { 800 $operation['actionid'] = $action['actionid']; 801 $operation['recovery'] = ACTION_OPERATION; 802 $operations_to_create[] = $operation; 803 } 804 else { 805 $operationid = $operation['operationid']; 806 807 if (array_key_exists($operationid, $db_operations)) { 808 $operation['recovery'] = ACTION_OPERATION; 809 $operations_to_update[] = $operation; 810 unset($db_operations[$operationid]); 811 } 812 else { 813 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', 814 $operationid, 'operationid' 815 )); 816 } 817 } 818 } 819 $operationids_to_delete = array_merge($operationids_to_delete, array_keys($db_operations)); 820 } 821 822 if (array_key_exists('recovery_operations', $action)) { 823 $db_recovery_operations = zbx_toHash($db_action['recoveryOperations'], 'operationid'); 824 825 foreach ($action['recovery_operations'] as $recovery_operation) { 826 unset($recovery_operation['esc_period'], $recovery_operation['esc_step_from'], 827 $recovery_operation['esc_step_to'] 828 ); 829 $recovery_operation['actionid'] = $action['actionid']; 830 831 if (!array_key_exists('operationid', $recovery_operation)) { 832 if ($recovery_operation['operationtype'] == OPERATION_TYPE_RECOVERY_MESSAGE) { 833 unset($recovery_operation['opmessage']['mediatypeid']); 834 } 835 836 $recovery_operation['recovery'] = ACTION_RECOVERY_OPERATION; 837 $operations_to_create[] = $recovery_operation; 838 } 839 else { 840 $recovery_operationid = $recovery_operation['operationid']; 841 842 if (array_key_exists($recovery_operationid, $db_recovery_operations)) { 843 $db_operation_type = $db_recovery_operations[$recovery_operationid]['operationtype']; 844 if ((array_key_exists('operationtype', $recovery_operation) 845 && $recovery_operation['operationtype'] == OPERATION_TYPE_RECOVERY_MESSAGE) 846 || (!array_key_exists('operationtype', $recovery_operation) 847 && $db_operation_type == OPERATION_TYPE_RECOVERY_MESSAGE)) { 848 unset($recovery_operation['opmessage']['mediatypeid']); 849 } 850 851 $recovery_operation['recovery'] = ACTION_RECOVERY_OPERATION; 852 $operations_to_update[] = $recovery_operation; 853 unset($db_recovery_operations[$recovery_operationid]); 854 } 855 else { 856 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', 857 $recovery_operationid, 'operationid' 858 )); 859 } 860 } 861 } 862 $operationids_to_delete = array_merge($operationids_to_delete, array_keys($db_recovery_operations)); 863 } 864 865 if (array_key_exists('acknowledge_operations', $action)) { 866 $db_ack_operations = zbx_toHash($db_action['acknowledgeOperations'], 'operationid'); 867 868 foreach ($action['acknowledge_operations'] as $ack_operation) { 869 $ack_operation['recovery'] = ACTION_ACKNOWLEDGE_OPERATION; 870 $opmessage = (array_key_exists('opmessage', $ack_operation) && is_array($ack_operation['opmessage'])) 871 ? $ack_operation['opmessage'] 872 : []; 873 unset($ack_operation['esc_period'], $ack_operation['esc_step_from'], $ack_operation['esc_step_to']); 874 $ack_operation['actionid'] = $action['actionid']; 875 876 if (!array_key_exists('operationid', $ack_operation)) { 877 if ($ack_operation['operationtype'] == OPERATION_TYPE_MESSAGE 878 || $ack_operation['operationtype'] == OPERATION_TYPE_ACK_MESSAGE) { 879 $ack_operation['opmessage'] += [ 880 'default_msg' => 1, 881 'mediatypeid' => 0, 882 'subject' => '', 883 'message' => '' 884 ]; 885 } 886 887 $operations_to_create[] = $ack_operation; 888 } 889 elseif (array_key_exists($ack_operation['operationid'], $db_ack_operations)) { 890 if ($ack_operation['operationtype'] == OPERATION_TYPE_MESSAGE 891 || $ack_operation['operationtype'] == OPERATION_TYPE_ACK_MESSAGE) { 892 $db_opmessage = array_key_exists('opmessage', $db_ack_operations[$ack_operation['operationid']]) 893 ? $db_ack_operations[$ack_operation['operationid']]['opmessage'] 894 : [ 895 'default_msg' => 1, 896 'mediatypeid' => 0, 897 'subject' => '', 898 'message' => '' 899 ]; 900 $default_msg = array_key_exists('default_msg', $opmessage) 901 ? $opmessage['default_msg'] 902 : $db_opmessage['default_msg']; 903 904 if ($default_msg == 1) { 905 $opmessage['subject'] = ''; 906 $opmessage['message'] = ''; 907 $ack_operation['opmessage'] = $opmessage; 908 } 909 } 910 911 $operations_to_update[] = $ack_operation; 912 unset($db_ack_operations[$ack_operation['operationid']]); 913 } 914 else { 915 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', 916 $ack_operation['operationid'], 'operationid' 917 )); 918 } 919 } 920 $operationids_to_delete = array_merge($operationids_to_delete, array_keys($db_ack_operations)); 921 } 922 923 if ($actionUpdateValues) { 924 $actions_update_data[] = ['values' => $actionUpdateValues, 'where' => ['actionid' => $actionId]]; 925 } 926 } 927 928 if ($actions_update_data) { 929 DB::update('actions', $actions_update_data); 930 $this->addAuditBulk(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_ACTION, $actions, $db_actions); 931 } 932 933 // add, update and delete operations 934 $this->addOperations($operations_to_create); 935 $this->updateOperations($operations_to_update, $db_actions); 936 if (!empty($operationids_to_delete)) { 937 $this->deleteOperations($operationids_to_delete); 938 } 939 940 // set actionid for all conditions and group by actionid into $newActionConditions 941 $newActionConditions = null; 942 foreach ($actions as $actionId => $action) { 943 if (isset($action['filter'])) { 944 if ($newActionConditions === null) { 945 $newActionConditions = []; 946 } 947 948 $newActionConditions[$actionId] = []; 949 foreach ($action['filter']['conditions'] as $condition) { 950 $condition['actionid'] = $actionId; 951 $newActionConditions[$actionId][] = $condition; 952 } 953 } 954 } 955 956 // if we have any conditions, fetch current conditions from db and do replace by position and group result 957 // by actionid into $actionConditions 958 $actionConditions = []; 959 if ($newActionConditions !== null) { 960 $existingConditions = DBfetchArray(DBselect( 961 'SELECT conditionid,actionid,conditiontype,operator,value,value2'. 962 ' FROM conditions'. 963 ' WHERE '.dbConditionInt('actionid', $actionIds). 964 ' ORDER BY conditionid' 965 )); 966 $existingActionConditions = []; 967 foreach ($existingConditions as $condition) { 968 $existingActionConditions[$condition['actionid']][] = $condition; 969 } 970 971 $conditions = DB::replaceByPosition('conditions', $existingActionConditions, $newActionConditions); 972 foreach ($conditions as $condition) { 973 $actionConditions[$condition['actionid']][] = $condition; 974 } 975 } 976 977 // update formulas for user expressions using new conditions 978 foreach ($actions as $actionId => $action) { 979 if (isset($action['filter']) && $action['filter']['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 980 $this->updateFormula($actionId, $action['filter']['formula'], $actionConditions[$actionId]); 981 } 982 } 983 984 return ['actionids' => $actionIds]; 985 } 986 987 /** 988 * @param array $conditions 989 * 990 * @return mixed 991 */ 992 protected function addConditions($conditions) { 993 foreach ($conditions as $condition) { 994 $connectionDbFields = [ 995 'actionid' => null, 996 'conditiontype' => null 997 ]; 998 if (!check_db_fields($connectionDbFields, $condition)) { 999 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect parameters for condition.')); 1000 } 1001 } 1002 1003 return DB::save('conditions', $conditions); 1004 } 1005 1006 protected function updateConditions($conditions) { 1007 $update = []; 1008 foreach ($conditions as $condition) { 1009 $conditionId = $condition['conditionid']; 1010 unset($condition['conditionid']); 1011 $update = [ 1012 'values' => $condition, 1013 'where' => ['conditionid' => $conditionId] 1014 ]; 1015 } 1016 DB::update('conditions', $update); 1017 1018 return $conditions; 1019 } 1020 1021 protected function deleteConditions($conditionids) { 1022 DB::delete('conditions', ['conditionid' => $conditionids]); 1023 } 1024 1025 /** 1026 * @param array $operations 1027 * 1028 * @return bool 1029 */ 1030 protected function addOperations($operations) { 1031 foreach ($operations as $operation) { 1032 $operationDbFields = [ 1033 'actionid' => null, 1034 'operationtype' => null 1035 ]; 1036 if (!check_db_fields($operationDbFields, $operation)) { 1037 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect parameter for operations.')); 1038 } 1039 } 1040 1041 $operations = DB::save('operations', $operations); 1042 $operations = zbx_toHash($operations, 'operationid'); 1043 1044 $opMessagesToInsert = []; 1045 $opCommandsToInsert = []; 1046 $msggroups_to_insert = []; 1047 $msgusers_to_insert = []; 1048 $opCommandHstsToInsert = []; 1049 $opCommandGroupInserts = []; 1050 $opGroupsToInsert = []; 1051 $opTemplatesToInsert = []; 1052 $opConditionsToInsert = []; 1053 $opInventoryToInsert = []; 1054 1055 foreach ($operations as $operationId => $operation) { 1056 switch ($operation['operationtype']) { 1057 case OPERATION_TYPE_MESSAGE: 1058 if (isset($operation['opmessage']) && !empty($operation['opmessage'])) { 1059 $operation['opmessage']['operationid'] = $operationId; 1060 $opMessagesToInsert[] = $operation['opmessage']; 1061 } 1062 if (isset($operation['opmessage_usr'])) { 1063 foreach ($operation['opmessage_usr'] as $user) { 1064 $msgusers_to_insert[] = [ 1065 'operationid' => $operationId, 1066 'userid' => $user['userid'] 1067 ]; 1068 } 1069 } 1070 if (isset($operation['opmessage_grp'])) { 1071 foreach ($operation['opmessage_grp'] as $userGroup) { 1072 $msggroups_to_insert[] = [ 1073 'operationid' => $operationId, 1074 'usrgrpid' => $userGroup['usrgrpid'] 1075 ]; 1076 } 1077 } 1078 break; 1079 1080 case OPERATION_TYPE_COMMAND: 1081 if (isset($operation['opcommand']) && !empty($operation['opcommand'])) { 1082 $operation['opcommand']['operationid'] = $operationId; 1083 $opCommandsToInsert[] = $operation['opcommand']; 1084 } 1085 if (isset($operation['opcommand_hst'])) { 1086 foreach ($operation['opcommand_hst'] as $host) { 1087 $opCommandHstsToInsert[] = [ 1088 'operationid' => $operationId, 1089 'hostid' => $host['hostid'] 1090 ]; 1091 } 1092 } 1093 if (isset($operation['opcommand_grp'])) { 1094 foreach ($operation['opcommand_grp'] as $hostGroup) { 1095 $opCommandGroupInserts[] = [ 1096 'operationid' => $operationId, 1097 'groupid' => $hostGroup['groupid'] 1098 ]; 1099 } 1100 } 1101 break; 1102 1103 case OPERATION_TYPE_GROUP_ADD: 1104 case OPERATION_TYPE_GROUP_REMOVE: 1105 foreach ($operation['opgroup'] as $hostGroup) { 1106 $opGroupsToInsert[] = [ 1107 'operationid' => $operationId, 1108 'groupid' => $hostGroup['groupid'] 1109 ]; 1110 } 1111 break; 1112 1113 case OPERATION_TYPE_TEMPLATE_ADD: 1114 case OPERATION_TYPE_TEMPLATE_REMOVE: 1115 foreach ($operation['optemplate'] as $template) { 1116 $opTemplatesToInsert[] = [ 1117 'operationid' => $operationId, 1118 'templateid' => $template['templateid'] 1119 ]; 1120 } 1121 break; 1122 1123 case OPERATION_TYPE_HOST_INVENTORY: 1124 $opInventoryToInsert[] = [ 1125 'operationid' => $operationId, 1126 'inventory_mode' => $operation['opinventory']['inventory_mode'] 1127 ]; 1128 break; 1129 1130 case OPERATION_TYPE_ACK_MESSAGE: 1131 // falls through 1132 case OPERATION_TYPE_RECOVERY_MESSAGE: 1133 if (array_key_exists('opmessage', $operation) && $operation['opmessage']) { 1134 $operation['opmessage']['operationid'] = $operationId; 1135 $opMessagesToInsert[] = $operation['opmessage']; 1136 } 1137 break; 1138 } 1139 if (isset($operation['opconditions'])) { 1140 foreach ($operation['opconditions'] as $opCondition) { 1141 $opCondition['operationid'] = $operationId; 1142 $opConditionsToInsert[] = $opCondition; 1143 } 1144 } 1145 } 1146 1147 DB::insert('opmessage_grp', $msggroups_to_insert); 1148 DB::insert('opmessage_usr', $msgusers_to_insert); 1149 DB::insert('opconditions', $opConditionsToInsert); 1150 DB::insert('opmessage', $opMessagesToInsert, false); 1151 DB::insert('opcommand', $opCommandsToInsert, false); 1152 DB::insert('opcommand_hst', $opCommandHstsToInsert); 1153 DB::insert('opcommand_grp', $opCommandGroupInserts); 1154 DB::insert('opgroup', $opGroupsToInsert); 1155 DB::insert('optemplate', $opTemplatesToInsert); 1156 DB::insert('opinventory', $opInventoryToInsert, false); 1157 1158 return true; 1159 } 1160 1161 /** 1162 * @param array $operations 1163 * @param array $db_actions 1164 */ 1165 protected function updateOperations($operations, $db_actions) { 1166 $operationsUpdate = []; 1167 1168 // messages 1169 $opMessagesToInsert = []; 1170 $opMessagesToUpdate = []; 1171 $opMessagesToDeleteByOpId = []; 1172 1173 $opMessageGrpsToInsert = []; 1174 $opMessageUsrsToInsert = []; 1175 $opMessageGrpsToDeleteByOpId = []; 1176 $opMessageUsrsToDeleteByOpId = []; 1177 1178 // commands 1179 $opCommandsToInsert = []; 1180 $opCommandsToUpdate = []; 1181 $opCommandsToDeleteByOpId = []; 1182 1183 $opCommandGrpsToInsert = []; 1184 $opCommandHstsToInsert = []; 1185 $opCommandGrpsToDeleteByOpId = []; 1186 $opCommandHstsToDeleteByOpId = []; 1187 1188 // groups 1189 $opGroupsToInsert = []; 1190 $opGroupsToDeleteByOpId = []; 1191 1192 // templates 1193 $opTemplateToInsert = []; 1194 $opTemplatesToDeleteByOpId = []; 1195 1196 // operation conditions 1197 $opConditionsToInsert = []; 1198 1199 // inventory 1200 $opInventoryToInsert = []; 1201 $opInventoryToUpdate = []; 1202 $opInventoryToDeleteByOpId = []; 1203 1204 $operation_actions_hashkey = [ 1205 ACTION_OPERATION => 'operations', 1206 ACTION_RECOVERY_OPERATION => 'recoveryOperations', 1207 ACTION_ACKNOWLEDGE_OPERATION => 'acknowledgeOperations' 1208 ]; 1209 1210 foreach ($operations as $operation) { 1211 $actions_key = $operation_actions_hashkey[$operation['recovery']]; 1212 1213 $operationsDb = zbx_toHash($db_actions[$operation['actionid']][$actions_key], 'operationid'); 1214 1215 $operationDb = $operationsDb[$operation['operationid']]; 1216 1217 $type_changed = false; 1218 1219 if (isset($operation['operationtype']) && ($operation['operationtype'] != $operationDb['operationtype'])) { 1220 $type_changed = true; 1221 1222 switch ($operationDb['operationtype']) { 1223 case OPERATION_TYPE_MESSAGE: 1224 $opMessagesToDeleteByOpId[] = $operationDb['operationid']; 1225 $opMessageGrpsToDeleteByOpId[] = $operationDb['operationid']; 1226 $opMessageUsrsToDeleteByOpId[] = $operationDb['operationid']; 1227 break; 1228 1229 case OPERATION_TYPE_COMMAND: 1230 $opCommandsToDeleteByOpId[] = $operationDb['operationid']; 1231 $opCommandHstsToDeleteByOpId[] = $operationDb['operationid']; 1232 $opCommandGrpsToDeleteByOpId[] = $operationDb['operationid']; 1233 break; 1234 1235 case OPERATION_TYPE_GROUP_ADD: 1236 if ($operation['operationtype'] == OPERATION_TYPE_GROUP_REMOVE) { 1237 break; 1238 } 1239 case OPERATION_TYPE_GROUP_REMOVE: 1240 if ($operation['operationtype'] == OPERATION_TYPE_GROUP_ADD) { 1241 break; 1242 } 1243 $opGroupsToDeleteByOpId[] = $operationDb['operationid']; 1244 break; 1245 1246 case OPERATION_TYPE_TEMPLATE_ADD: 1247 if ($operation['operationtype'] == OPERATION_TYPE_TEMPLATE_REMOVE) { 1248 break; 1249 } 1250 case OPERATION_TYPE_TEMPLATE_REMOVE: 1251 if ($operation['operationtype'] == OPERATION_TYPE_TEMPLATE_ADD) { 1252 break; 1253 } 1254 $opTemplatesToDeleteByOpId[] = $operationDb['operationid']; 1255 break; 1256 1257 case OPERATION_TYPE_HOST_INVENTORY: 1258 $opInventoryToDeleteByOpId[] = $operationDb['operationid']; 1259 break; 1260 1261 case OPERATION_TYPE_ACK_MESSAGE: 1262 // falls through 1263 case OPERATION_TYPE_RECOVERY_MESSAGE: 1264 $opMessagesToDeleteByOpId[] = $operationDb['operationid']; 1265 break; 1266 } 1267 } 1268 1269 if (!isset($operation['operationtype'])) { 1270 $operation['operationtype'] = $operationDb['operationtype']; 1271 } 1272 1273 switch ($operation['operationtype']) { 1274 case OPERATION_TYPE_MESSAGE: 1275 if (!isset($operation['opmessage_grp'])) { 1276 $operation['opmessage_grp'] = []; 1277 } 1278 else { 1279 zbx_array_push($operation['opmessage_grp'], ['operationid' => $operation['operationid']]); 1280 } 1281 1282 if (!isset($operation['opmessage_usr'])) { 1283 $operation['opmessage_usr'] = []; 1284 } 1285 else { 1286 zbx_array_push($operation['opmessage_usr'], ['operationid' => $operation['operationid']]); 1287 } 1288 1289 if (!isset($operationDb['opmessage_usr'])) { 1290 $operationDb['opmessage_usr'] = []; 1291 } 1292 if (!isset($operationDb['opmessage_grp'])) { 1293 $operationDb['opmessage_grp'] = []; 1294 } 1295 1296 if ($type_changed) { 1297 $operation['opmessage']['operationid'] = $operation['operationid']; 1298 $opMessagesToInsert[] = $operation['opmessage']; 1299 1300 $opMessageGrpsToInsert = array_merge($opMessageGrpsToInsert, $operation['opmessage_grp']); 1301 $opMessageUsrsToInsert = array_merge($opMessageUsrsToInsert, $operation['opmessage_usr']); 1302 } 1303 else { 1304 if (array_key_exists('opmessage', $operation)) { 1305 $opMessagesToUpdate[] = [ 1306 'values' => $operation['opmessage'], 1307 'where' => ['operationid' => $operation['operationid']] 1308 ]; 1309 } 1310 1311 $diff = zbx_array_diff($operation['opmessage_grp'], $operationDb['opmessage_grp'], 'usrgrpid'); 1312 $opMessageGrpsToInsert = array_merge($opMessageGrpsToInsert, $diff['first']); 1313 1314 foreach ($diff['second'] as $opMessageGrp) { 1315 DB::delete('opmessage_grp', [ 1316 'usrgrpid' => $opMessageGrp['usrgrpid'], 1317 'operationid' => $operation['operationid'] 1318 ]); 1319 } 1320 1321 $diff = zbx_array_diff($operation['opmessage_usr'], $operationDb['opmessage_usr'], 'userid'); 1322 $opMessageUsrsToInsert = array_merge($opMessageUsrsToInsert, $diff['first']); 1323 foreach ($diff['second'] as $opMessageUsr) { 1324 DB::delete('opmessage_usr', [ 1325 'userid' => $opMessageUsr['userid'], 1326 'operationid' => $operation['operationid'] 1327 ]); 1328 } 1329 } 1330 break; 1331 1332 case OPERATION_TYPE_COMMAND: 1333 if (!isset($operation['opcommand_grp'])) { 1334 $operation['opcommand_grp'] = []; 1335 } 1336 else { 1337 zbx_array_push($operation['opcommand_grp'], ['operationid' => $operation['operationid']]); 1338 } 1339 1340 if (!isset($operation['opcommand_hst'])) { 1341 $operation['opcommand_hst'] = []; 1342 } 1343 else { 1344 zbx_array_push($operation['opcommand_hst'], ['operationid' => $operation['operationid']]); 1345 } 1346 1347 if (!isset($operationDb['opcommand_grp'])) { 1348 $operationDb['opcommand_grp'] = []; 1349 } 1350 if (!isset($operationDb['opcommand_hst'])) { 1351 $operationDb['opcommand_hst'] = []; 1352 } 1353 1354 if ($type_changed) { 1355 $operation['opcommand']['operationid'] = $operation['operationid']; 1356 $opCommandsToInsert[] = $operation['opcommand']; 1357 1358 $opCommandGrpsToInsert = array_merge($opCommandGrpsToInsert, $operation['opcommand_grp']); 1359 $opCommandHstsToInsert = array_merge($opCommandHstsToInsert, $operation['opcommand_hst']); 1360 } 1361 else { 1362 // Check if "scriptid" needs to be updated in "opcommand" table. 1363 if (bccomp($operation['opcommand']['scriptid'], $operationDb['opcommand']['scriptid']) != 0) { 1364 $opCommandsToUpdate[] = [ 1365 'values' => $operation['opcommand'], 1366 'where' => ['operationid' => $operation['operationid']] 1367 ]; 1368 } 1369 1370 $diff = zbx_array_diff($operation['opcommand_grp'], $operationDb['opcommand_grp'], 'groupid'); 1371 $opCommandGrpsToInsert = array_merge($opCommandGrpsToInsert, $diff['first']); 1372 1373 foreach ($diff['second'] as $opMessageGrp) { 1374 DB::delete('opcommand_grp', [ 1375 'groupid' => $opMessageGrp['groupid'], 1376 'operationid' => $operation['operationid'] 1377 ]); 1378 } 1379 1380 $diff = zbx_array_diff($operation['opcommand_hst'], $operationDb['opcommand_hst'], 'hostid'); 1381 $opCommandHstsToInsert = array_merge($opCommandHstsToInsert, $diff['first']); 1382 $opCommandHostIds = zbx_objectValues($diff['second'], 'opcommand_hstid'); 1383 if ($opCommandHostIds) { 1384 DB::delete('opcommand_hst', [ 1385 'opcommand_hstid' => $opCommandHostIds 1386 ]); 1387 } 1388 } 1389 break; 1390 1391 case OPERATION_TYPE_GROUP_ADD: 1392 case OPERATION_TYPE_GROUP_REMOVE: 1393 if (!isset($operation['opgroup'])) { 1394 $operation['opgroup'] = []; 1395 } 1396 else { 1397 zbx_array_push($operation['opgroup'], ['operationid' => $operation['operationid']]); 1398 } 1399 1400 if (!isset($operationDb['opgroup'])) { 1401 $operationDb['opgroup'] = []; 1402 } 1403 1404 $diff = zbx_array_diff($operation['opgroup'], $operationDb['opgroup'], 'groupid'); 1405 $opGroupsToInsert = array_merge($opGroupsToInsert, $diff['first']); 1406 foreach ($diff['second'] as $opGroup) { 1407 DB::delete('opgroup', [ 1408 'groupid' => $opGroup['groupid'], 1409 'operationid' => $operation['operationid'] 1410 ]); 1411 } 1412 break; 1413 1414 case OPERATION_TYPE_TEMPLATE_ADD: 1415 case OPERATION_TYPE_TEMPLATE_REMOVE: 1416 if (!isset($operation['optemplate'])) { 1417 $operation['optemplate'] = []; 1418 } 1419 else { 1420 zbx_array_push($operation['optemplate'], ['operationid' => $operation['operationid']]); 1421 } 1422 1423 if (!isset($operationDb['optemplate'])) { 1424 $operationDb['optemplate'] = []; 1425 } 1426 1427 $diff = zbx_array_diff($operation['optemplate'], $operationDb['optemplate'], 'templateid'); 1428 $opTemplateToInsert = array_merge($opTemplateToInsert, $diff['first']); 1429 1430 foreach ($diff['second'] as $opTemplate) { 1431 DB::delete('optemplate', [ 1432 'templateid' => $opTemplate['templateid'], 1433 'operationid' => $operation['operationid'] 1434 ]); 1435 } 1436 break; 1437 1438 case OPERATION_TYPE_HOST_INVENTORY: 1439 if ($type_changed) { 1440 $operation['opinventory']['operationid'] = $operation['operationid']; 1441 $opInventoryToInsert[] = $operation['opinventory']; 1442 } 1443 else { 1444 $opInventoryToUpdate[] = [ 1445 'values' => $operation['opinventory'], 1446 'where' => ['operationid' => $operation['operationid']] 1447 ]; 1448 } 1449 break; 1450 1451 case OPERATION_TYPE_ACK_MESSAGE: 1452 // falls through 1453 case OPERATION_TYPE_RECOVERY_MESSAGE: 1454 if ($type_changed) { 1455 $operation['opmessage']['operationid'] = $operation['operationid']; 1456 $opMessagesToInsert[] = $operation['opmessage']; 1457 } 1458 elseif (array_key_exists('opmessage', $operation)) { 1459 $opMessagesToUpdate[] = [ 1460 'values' => $operation['opmessage'], 1461 'where' => ['operationid' => $operation['operationid']] 1462 ]; 1463 } 1464 break; 1465 } 1466 1467 if (!isset($operation['opconditions'])) { 1468 $operation['opconditions'] = []; 1469 } 1470 else { 1471 zbx_array_push($operation['opconditions'], ['operationid' => $operation['operationid']]); 1472 } 1473 1474 self::validateOperationConditions($operation['opconditions']); 1475 1476 $db_opconditions = array_key_exists('opconditions', $operationDb) ? $operationDb['opconditions'] : []; 1477 $diff = zbx_array_diff($operation['opconditions'], $db_opconditions, 'opconditionid'); 1478 $opConditionsToInsert = array_merge($opConditionsToInsert, $diff['first']); 1479 1480 $opConditionsIdsToDelete = zbx_objectValues($diff['second'], 'opconditionid'); 1481 if (!empty($opConditionsIdsToDelete)) { 1482 DB::delete('opconditions', ['opconditionid' => $opConditionsIdsToDelete]); 1483 } 1484 1485 $operationId = $operation['operationid']; 1486 unset($operation['operationid']); 1487 if (!empty($operation)) { 1488 $operationsUpdate[] = [ 1489 'values' => $operation, 1490 'where' => ['operationid' => $operationId] 1491 ]; 1492 } 1493 } 1494 1495 DB::update('operations', $operationsUpdate); 1496 1497 if (!empty($opMessagesToDeleteByOpId)) { 1498 DB::delete('opmessage', ['operationid' => $opMessagesToDeleteByOpId]); 1499 } 1500 if (!empty($opCommandsToDeleteByOpId)) { 1501 DB::delete('opcommand', ['operationid' => $opCommandsToDeleteByOpId]); 1502 } 1503 if (!empty($opMessageGrpsToDeleteByOpId)) { 1504 DB::delete('opmessage_grp', ['operationid' => $opMessageGrpsToDeleteByOpId]); 1505 } 1506 if (!empty($opMessageUsrsToDeleteByOpId)) { 1507 DB::delete('opmessage_usr', ['operationid' => $opMessageUsrsToDeleteByOpId]); 1508 } 1509 if (!empty($opCommandHstsToDeleteByOpId)) { 1510 DB::delete('opcommand_hst', ['operationid' => $opCommandHstsToDeleteByOpId]); 1511 } 1512 if (!empty($opCommandGrpsToDeleteByOpId)) { 1513 DB::delete('opcommand_grp', ['operationid' => $opCommandGrpsToDeleteByOpId]); 1514 } 1515 if (!empty($opCommandGrpsToDeleteByOpId)) { 1516 DB::delete('opcommand_grp', ['opcommand_grpid' => $opCommandGrpsToDeleteByOpId]); 1517 } 1518 if (!empty($opCommandHstsToDeleteByOpId)) { 1519 DB::delete('opcommand_hst', ['opcommand_hstid' => $opCommandHstsToDeleteByOpId]); 1520 } 1521 if (!empty($opGroupsToDeleteByOpId)) { 1522 DB::delete('opgroup', ['operationid' => $opGroupsToDeleteByOpId]); 1523 } 1524 if (!empty($opTemplatesToDeleteByOpId)) { 1525 DB::delete('optemplate', ['operationid' => $opTemplatesToDeleteByOpId]); 1526 } 1527 if (!empty($opInventoryToDeleteByOpId)) { 1528 DB::delete('opinventory', ['operationid' => $opInventoryToDeleteByOpId]); 1529 } 1530 1531 DB::insert('opmessage', $opMessagesToInsert, false); 1532 DB::insert('opcommand', $opCommandsToInsert, false); 1533 DB::insert('opmessage_grp', $opMessageGrpsToInsert); 1534 DB::insert('opmessage_usr', $opMessageUsrsToInsert); 1535 DB::insert('opcommand_grp', $opCommandGrpsToInsert); 1536 DB::insert('opcommand_hst', $opCommandHstsToInsert); 1537 DB::insert('opgroup', $opGroupsToInsert); 1538 DB::insert('optemplate', $opTemplateToInsert); 1539 DB::update('opmessage', $opMessagesToUpdate); 1540 DB::update('opcommand', $opCommandsToUpdate); 1541 DB::insert('opconditions', $opConditionsToInsert); 1542 DB::insert('opinventory', $opInventoryToInsert, false); 1543 DB::update('opinventory', $opInventoryToUpdate); 1544 } 1545 1546 protected function deleteOperations($operationIds) { 1547 DB::delete('operations', ['operationid' => $operationIds]); 1548 } 1549 1550 /** 1551 * @param array $actionids 1552 * 1553 * @return array 1554 */ 1555 public function delete(array $actionids) { 1556 $api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true]; 1557 if (!CApiInputValidator::validate($api_input_rules, $actionids, '/', $error)) { 1558 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 1559 } 1560 1561 $db_actions = $this->get([ 1562 'output' => ['actionid', 'name'], 1563 'actionids' => $actionids, 1564 'editable' => true, 1565 'preservekeys' => true 1566 ]); 1567 1568 foreach ($actionids as $actionid) { 1569 if (!array_key_exists($actionid, $db_actions)) { 1570 self::exception(ZBX_API_ERROR_PERMISSIONS, 1571 _('No permissions to referred object or it does not exist!') 1572 ); 1573 } 1574 } 1575 1576 DB::delete('actions', ['actionid' => $actionids]); 1577 1578 $this->addAuditBulk(AUDIT_ACTION_DELETE, AUDIT_RESOURCE_ACTION, $db_actions); 1579 1580 return ['actionids' => $actionids]; 1581 } 1582 1583 /** 1584 * Validate condition type and operator based on action event source. 1585 * 1586 * @param string $name Action name. 1587 * @param int $eventsource Action event source. 1588 * @param array $conditions Conditions data array. 1589 * @param string $conditions[]['conditiontype'] Action condition type. 1590 * @param int $conditions[]['operator'] Action condition operator. 1591 * 1592 * @return bool 1593 */ 1594 public function validateFilterConditionsIntegrity($name, $eventsource, array $conditions) { 1595 foreach ($conditions as $condition) { 1596 if (!in_array($condition['conditiontype'], $this->valid_condition_types[$eventsource])) { 1597 self::exception(ZBX_API_ERROR_PARAMETERS, 1598 _s('Incorrect filter condition type for action "%1$s".', $name) 1599 ); 1600 } 1601 1602 if (!in_array($condition['operator'], 1603 $this->valid_condition_type_operators[$condition['conditiontype']])) { 1604 self::exception(ZBX_API_ERROR_PARAMETERS, 1605 _s('Incorrect filter condition operator for action "%1$s".', $name) 1606 ); 1607 } 1608 } 1609 } 1610 1611 /** 1612 * Validate operation, recovery operation, acknowledge operations. 1613 * 1614 * @param array $operations Operation data array. 1615 * 1616 * @return bool 1617 */ 1618 public function validateOperationsIntegrity(array $operations) { 1619 $operations = zbx_toArray($operations); 1620 1621 $all_groupids = []; 1622 $all_hostids = []; 1623 $all_templateids = []; 1624 $all_userids = []; 1625 $all_usrgrpids = []; 1626 $all_mediatypeids = []; 1627 1628 $valid_operationtypes = [ 1629 ACTION_OPERATION => [ 1630 EVENT_SOURCE_TRIGGERS => [OPERATION_TYPE_MESSAGE, OPERATION_TYPE_COMMAND], 1631 EVENT_SOURCE_DISCOVERY => [ 1632 OPERATION_TYPE_MESSAGE, OPERATION_TYPE_COMMAND, OPERATION_TYPE_GROUP_ADD, 1633 OPERATION_TYPE_GROUP_REMOVE, OPERATION_TYPE_TEMPLATE_ADD, OPERATION_TYPE_TEMPLATE_REMOVE, 1634 OPERATION_TYPE_HOST_ADD, OPERATION_TYPE_HOST_REMOVE, OPERATION_TYPE_HOST_ENABLE, 1635 OPERATION_TYPE_HOST_DISABLE, OPERATION_TYPE_HOST_INVENTORY 1636 ], 1637 EVENT_SOURCE_AUTOREGISTRATION => [ 1638 OPERATION_TYPE_MESSAGE, OPERATION_TYPE_COMMAND, OPERATION_TYPE_GROUP_ADD, 1639 OPERATION_TYPE_GROUP_REMOVE, OPERATION_TYPE_TEMPLATE_ADD, OPERATION_TYPE_TEMPLATE_REMOVE, 1640 OPERATION_TYPE_HOST_ADD, OPERATION_TYPE_HOST_REMOVE, OPERATION_TYPE_HOST_ENABLE, 1641 OPERATION_TYPE_HOST_DISABLE, OPERATION_TYPE_HOST_INVENTORY 1642 ], 1643 EVENT_SOURCE_INTERNAL => [OPERATION_TYPE_MESSAGE] 1644 ], 1645 ACTION_RECOVERY_OPERATION => [ 1646 EVENT_SOURCE_TRIGGERS => [OPERATION_TYPE_MESSAGE, OPERATION_TYPE_COMMAND, 1647 OPERATION_TYPE_RECOVERY_MESSAGE 1648 ], 1649 EVENT_SOURCE_INTERNAL => [OPERATION_TYPE_MESSAGE, OPERATION_TYPE_RECOVERY_MESSAGE] 1650 ], 1651 ACTION_ACKNOWLEDGE_OPERATION => [ 1652 EVENT_SOURCE_TRIGGERS => [OPERATION_TYPE_MESSAGE, OPERATION_TYPE_COMMAND, OPERATION_TYPE_ACK_MESSAGE] 1653 ] 1654 ]; 1655 1656 $required_fields = ['eventsource', 'recovery', 'operationtype']; 1657 1658 $default_msg_validator = new CLimitedSetValidator([ 1659 'values' => [0, 1] 1660 ]); 1661 1662 foreach ($operations as $operation) { 1663 foreach ($required_fields as $field) { 1664 if (!array_key_exists($field, $operation)) { 1665 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Field "%1$s" is mandatory.', $field)); 1666 } 1667 } 1668 $eventsource = $operation['eventsource']; 1669 $recovery = $operation['recovery']; 1670 $operationtype = $operation['operationtype']; 1671 1672 if ($recovery == ACTION_OPERATION) { 1673 if ((array_key_exists('esc_step_from', $operation) || array_key_exists('esc_step_to', $operation)) 1674 && (!array_key_exists('esc_step_from', $operation) 1675 || !array_key_exists('esc_step_to', $operation))) { 1676 self::exception(ZBX_API_ERROR_PARAMETERS, _('esc_step_from and esc_step_to must be set together.')); 1677 } 1678 1679 if (array_key_exists('esc_step_from', $operation) && array_key_exists('esc_step_to', $operation)) { 1680 if ($operation['esc_step_from'] < 1 || $operation['esc_step_to'] < 0) { 1681 self::exception(ZBX_API_ERROR_PARAMETERS, 1682 _('Incorrect action operation escalation step values.') 1683 ); 1684 } 1685 1686 if ($operation['esc_step_from'] > $operation['esc_step_to'] && $operation['esc_step_to'] != 0) { 1687 self::exception(ZBX_API_ERROR_PARAMETERS, 1688 _('Incorrect action operation escalation step values.') 1689 ); 1690 } 1691 } 1692 1693 if (array_key_exists('esc_period', $operation) 1694 && !validateTimeUnit($operation['esc_period'], SEC_PER_MIN, SEC_PER_WEEK, true, $error, 1695 ['usermacros' => true])) { 1696 self::exception(ZBX_API_ERROR_PARAMETERS, 1697 _s('Incorrect value for field "%1$s": %2$s.', 'esc_period', $error) 1698 ); 1699 } 1700 } 1701 1702 if (!array_key_exists($eventsource, $valid_operationtypes[$recovery]) 1703 || !in_array($operationtype, $valid_operationtypes[$recovery][$eventsource])) { 1704 self::exception(ZBX_API_ERROR_PARAMETERS, 1705 _s('Incorrect action operation type "%1$s" for event source "%2$s".', $operationtype, $eventsource) 1706 ); 1707 } 1708 1709 switch ($operationtype) { 1710 case OPERATION_TYPE_MESSAGE: 1711 $userids = array_key_exists('opmessage_usr', $operation) 1712 ? zbx_objectValues($operation['opmessage_usr'], 'userid') 1713 : []; 1714 1715 $usrgrpids = array_key_exists('opmessage_grp', $operation) 1716 ? zbx_objectValues($operation['opmessage_grp'], 'usrgrpid') 1717 : []; 1718 1719 if (!$userids && !$usrgrpids) { 1720 self::exception(ZBX_API_ERROR_PARAMETERS, _('No recipients for action operation message.')); 1721 } 1722 1723 $all_userids = array_merge($all_userids, $userids); 1724 $all_usrgrpids = array_merge($all_usrgrpids, $usrgrpids); 1725 // falls through 1726 case OPERATION_TYPE_ACK_MESSAGE: 1727 $message = array_key_exists('opmessage', $operation) ? $operation['opmessage'] : []; 1728 1729 if (array_key_exists('mediatypeid', $message) && $message['mediatypeid']) { 1730 $all_mediatypeids[$message['mediatypeid']] = true; 1731 } 1732 1733 if (array_key_exists('default_msg', $message) 1734 && (!$default_msg_validator->validate($message['default_msg']))) { 1735 self::exception(ZBX_API_ERROR_PARAMETERS, 1736 _s('Incorrect value "%1$s" for "%2$s" field: must be between %3$s and %4$s.', 1737 $message['default_msg'], 'default_msg', 0, 1 1738 )); 1739 } 1740 break; 1741 case OPERATION_TYPE_COMMAND: 1742 if (!array_key_exists('scriptid', $operation['opcommand']) 1743 || $operation['opcommand']['scriptid'] === null) { 1744 self::exception(ZBX_API_ERROR_PARAMETERS, 1745 _('No script specified for action operation command.') 1746 ); 1747 } 1748 1749 $scripts = API::Script()->get([ 1750 'output' => ['scriptid', 'name'], 1751 'scriptids' => $operation['opcommand']['scriptid'], 1752 'filter' => ['scope' => ZBX_SCRIPT_SCOPE_ACTION], 1753 'preservekeys' => true 1754 ]); 1755 1756 if (!array_key_exists($operation['opcommand']['scriptid'], $scripts)) { 1757 self::exception(ZBX_API_ERROR_PARAMETERS, _( 1758 'Specified script does not exist or you do not have rights on it for action operation command.' 1759 )); 1760 } 1761 1762 $groupids = []; 1763 if (array_key_exists('opcommand_grp', $operation)) { 1764 $groupids = zbx_objectValues($operation['opcommand_grp'], 'groupid'); 1765 } 1766 1767 $hostids = []; 1768 $withoutCurrent = true; 1769 if (array_key_exists('opcommand_hst', $operation)) { 1770 foreach ($operation['opcommand_hst'] as $hstCommand) { 1771 if (!is_array($hstCommand) || !array_key_exists('hostid', $hstCommand)) { 1772 self::exception(ZBX_API_ERROR_PARAMETERS, 1773 _s('Incorrect value for field "%1$s": %2$s.', 'hostid', _('cannot be empty')) 1774 ); 1775 } 1776 1777 if ($hstCommand['hostid'] == 0) { 1778 $withoutCurrent = false; 1779 } 1780 else { 1781 $hostids[$hstCommand['hostid']] = $hstCommand['hostid']; 1782 } 1783 } 1784 } 1785 1786 if (!$groupids && !$hostids && $withoutCurrent) { 1787 self::exception(ZBX_API_ERROR_PARAMETERS, 1788 _s('You did not specify targets for action operation global script "%1$s".', 1789 $scripts[$operation['opcommand']['scriptid']]['name'] 1790 ) 1791 ); 1792 } 1793 1794 $all_hostids = array_merge($all_hostids, $hostids); 1795 $all_groupids = array_merge($all_groupids, $groupids); 1796 break; 1797 case OPERATION_TYPE_GROUP_ADD: 1798 case OPERATION_TYPE_GROUP_REMOVE: 1799 $groupids = array_key_exists('opgroup', $operation) 1800 ? zbx_objectValues($operation['opgroup'], 'groupid') 1801 : []; 1802 1803 if (!$groupids) { 1804 self::exception(ZBX_API_ERROR_PARAMETERS, _('Operation has no group to operate.')); 1805 } 1806 1807 $all_groupids = array_merge($all_groupids, $groupids); 1808 break; 1809 case OPERATION_TYPE_TEMPLATE_ADD: 1810 case OPERATION_TYPE_TEMPLATE_REMOVE: 1811 $templateids = isset($operation['optemplate']) 1812 ? zbx_objectValues($operation['optemplate'], 'templateid') 1813 : []; 1814 1815 if (!$templateids) { 1816 self::exception(ZBX_API_ERROR_PARAMETERS, _('Operation has no template to operate.')); 1817 } 1818 1819 $all_templateids = array_merge($all_templateids, $templateids); 1820 break; 1821 case OPERATION_TYPE_HOST_ADD: 1822 case OPERATION_TYPE_HOST_REMOVE: 1823 case OPERATION_TYPE_HOST_ENABLE: 1824 case OPERATION_TYPE_HOST_DISABLE: 1825 break; 1826 1827 case OPERATION_TYPE_HOST_INVENTORY: 1828 if (!array_key_exists('opinventory', $operation) 1829 || !array_key_exists('inventory_mode', $operation['opinventory'])) { 1830 self::exception(ZBX_API_ERROR_PARAMETERS, 1831 _('No inventory mode specified for action operation.') 1832 ); 1833 } 1834 if ($operation['opinventory']['inventory_mode'] != HOST_INVENTORY_MANUAL 1835 && $operation['opinventory']['inventory_mode'] != HOST_INVENTORY_AUTOMATIC) { 1836 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect inventory mode in action operation.')); 1837 } 1838 break; 1839 } 1840 } 1841 1842 $this->checkMediatypesExists($all_mediatypeids, _s('Incorrect value for field "%1$s": %2$s.', 'mediatypeid', 1843 _('No permissions to referred object or it does not exist!') 1844 )); 1845 $this->checkHostGroupsPermissions($all_groupids, _( 1846 'Incorrect action operation host group. Host group does not exist or you have no access to this host group.' 1847 )); 1848 $this->checkHostsPermissions($all_hostids, 1849 _('Incorrect action operation host. Host does not exist or you have no access to this host.') 1850 ); 1851 $this->checkTemplatesPermissions($all_templateids, 1852 _('Incorrect action operation template. Template does not exist or you have no access to this template.') 1853 ); 1854 $this->checkUsersPermissions($all_userids, 1855 _('Incorrect action operation user. User does not exist or you have no access to this user.') 1856 ); 1857 $this->checkUserGroupsPermissions($all_usrgrpids, _( 1858 'Incorrect action operation user group. User group does not exist or you have no access to this user group.' 1859 )); 1860 1861 return true; 1862 } 1863 1864 /** 1865 * Validate operation conditions. 1866 * 1867 * @static 1868 * @param $conditions 1869 * @return bool 1870 */ 1871 public static function validateOperationConditions($conditions) { 1872 $conditions = zbx_toArray($conditions); 1873 $ackStatuses = [ 1874 EVENT_ACKNOWLEDGED => 1, 1875 EVENT_NOT_ACKNOWLEDGED => 1 1876 ]; 1877 1878 foreach ($conditions as $condition) { 1879 switch ($condition['conditiontype']) { 1880 case CONDITION_TYPE_EVENT_ACKNOWLEDGED: 1881 if (!isset($ackStatuses[$condition['value']])) { 1882 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation condition acknowledge type.')); 1883 } 1884 break; 1885 1886 default: 1887 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operation condition type.')); 1888 break; 1889 } 1890 } 1891 1892 return true; 1893 } 1894 1895 protected function addRelatedObjects(array $options, array $result) { 1896 $result = parent::addRelatedObjects($options, $result); 1897 1898 $actionIds = array_keys($result); 1899 1900 // adding formulas 1901 if ($options['selectFilter'] !== null) { 1902 $formulaRequested = $this->outputIsRequested('formula', $options['selectFilter']); 1903 $evalFormulaRequested = $this->outputIsRequested('eval_formula', $options['selectFilter']); 1904 $conditionsRequested = $this->outputIsRequested('conditions', $options['selectFilter']); 1905 1906 $filters = []; 1907 foreach ($result as $action) { 1908 $filters[$action['actionid']] = [ 1909 'evaltype' => $action['evaltype'], 1910 'formula' => isset($action['formula']) ? $action['formula'] : '' 1911 ]; 1912 } 1913 1914 if ($formulaRequested || $evalFormulaRequested || $conditionsRequested) { 1915 $conditions = API::getApiService()->select('conditions', [ 1916 'output' => ['actionid', 'conditionid', 'conditiontype', 'operator', 'value', 'value2'], 1917 'filter' => ['actionid' => $actionIds], 1918 'preservekeys' => true 1919 ]); 1920 1921 $relationMap = $this->createRelationMap($conditions, 'actionid', 'conditionid'); 1922 $filters = $relationMap->mapMany($filters, $conditions, 'conditions'); 1923 1924 foreach ($filters as &$filter) { 1925 // in case of a custom expression - use the given formula 1926 if ($filter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 1927 $formula = $filter['formula']; 1928 } 1929 // in other cases - generate the formula automatically 1930 else { 1931 $conditions = $filter['conditions']; 1932 1933 // sort conditions 1934 $sortFields = [ 1935 ['field' => 'conditiontype', 'order' => ZBX_SORT_DOWN], 1936 ['field' => 'operator', 'order' => ZBX_SORT_DOWN], 1937 ['field' => 'value2', 'order' => ZBX_SORT_DOWN], 1938 ['field' => 'value', 'order' => ZBX_SORT_DOWN] 1939 ]; 1940 CArrayHelper::sort($conditions, $sortFields); 1941 1942 $conditionsForFormula = []; 1943 foreach ($conditions as $condition) { 1944 $conditionsForFormula[$condition['conditionid']] = $condition['conditiontype']; 1945 } 1946 $formula = CConditionHelper::getFormula($conditionsForFormula, $filter['evaltype']); 1947 } 1948 1949 // generate formulaids from the effective formula 1950 $formulaIds = CConditionHelper::getFormulaIds($formula); 1951 foreach ($filter['conditions'] as &$condition) { 1952 $condition['formulaid'] = $formulaIds[$condition['conditionid']]; 1953 } 1954 unset($condition); 1955 1956 // generated a letter based formula only for actions with custom expressions 1957 if ($formulaRequested && $filter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 1958 $filter['formula'] = CConditionHelper::replaceNumericIds($formula, $formulaIds); 1959 } 1960 1961 if ($evalFormulaRequested) { 1962 $filter['eval_formula'] = CConditionHelper::replaceNumericIds($formula, $formulaIds); 1963 } 1964 } 1965 unset($filter); 1966 } 1967 1968 // add filters to the result 1969 foreach ($result as &$action) { 1970 $action['filter'] = $filters[$action['actionid']]; 1971 } 1972 unset($action); 1973 } 1974 1975 // Acknowledge operations data. 1976 if ($options['selectAcknowledgeOperations'] !== null 1977 && $options['selectAcknowledgeOperations'] != API_OUTPUT_COUNT) { 1978 $ack_operations = API::getApiService()->select('operations', [ 1979 'output' => $this->outputExtend($options['selectAcknowledgeOperations'], 1980 ['operationid', 'actionid', 'operationtype'] 1981 ), 1982 'filter' => ['actionid' => $actionIds, 'recovery' => ACTION_ACKNOWLEDGE_OPERATION], 1983 'preservekeys' => true 1984 ]); 1985 1986 foreach ($result as &$action) { 1987 $action['acknowledgeOperations'] = []; 1988 } 1989 unset($action); 1990 1991 $ack_operations = $this->getAcknowledgeOperations($ack_operations, $options['selectAcknowledgeOperations']); 1992 1993 foreach ($ack_operations as $ack_operation) { 1994 $actionid = $ack_operation['actionid']; 1995 unset($ack_operation['actionid'], $ack_operation['recovery']); 1996 $result[$actionid]['acknowledgeOperations'][] = $ack_operation; 1997 } 1998 } 1999 2000 // adding operations 2001 if ($options['selectOperations'] !== null && $options['selectOperations'] != API_OUTPUT_COUNT) { 2002 $operations = API::getApiService()->select('operations', [ 2003 'output' => $this->outputExtend($options['selectOperations'], 2004 ['operationid', 'actionid', 'operationtype'] 2005 ), 2006 'filter' => ['actionid' => $actionIds, 'recovery' => ACTION_OPERATION], 2007 'preservekeys' => true 2008 ]); 2009 $relationMap = $this->createRelationMap($operations, 'actionid', 'operationid'); 2010 $operationIds = $relationMap->getRelatedIds(); 2011 2012 if ($this->outputIsRequested('opconditions', $options['selectOperations'])) { 2013 foreach ($operations as &$operation) { 2014 $operation['opconditions'] = []; 2015 } 2016 unset($operation); 2017 2018 $res = DBselect('SELECT op.* FROM opconditions op WHERE '.dbConditionInt('op.operationid', $operationIds)); 2019 while ($opcondition = DBfetch($res)) { 2020 $operations[$opcondition['operationid']]['opconditions'][] = $opcondition; 2021 } 2022 } 2023 2024 $opmessage = []; 2025 $opcommand = []; 2026 $opgroup = []; 2027 $optemplate = []; 2028 $opinventory = []; 2029 2030 foreach ($operations as $operationid => $operation) { 2031 unset($operations[$operationid]['recovery']); 2032 2033 switch ($operation['operationtype']) { 2034 case OPERATION_TYPE_MESSAGE: 2035 $opmessage[] = $operationid; 2036 break; 2037 case OPERATION_TYPE_COMMAND: 2038 $opcommand[] = $operationid; 2039 break; 2040 case OPERATION_TYPE_GROUP_ADD: 2041 case OPERATION_TYPE_GROUP_REMOVE: 2042 $opgroup[] = $operationid; 2043 break; 2044 case OPERATION_TYPE_TEMPLATE_ADD: 2045 case OPERATION_TYPE_TEMPLATE_REMOVE: 2046 $optemplate[] = $operationid; 2047 break; 2048 case OPERATION_TYPE_HOST_ADD: 2049 case OPERATION_TYPE_HOST_REMOVE: 2050 case OPERATION_TYPE_HOST_ENABLE: 2051 case OPERATION_TYPE_HOST_DISABLE: 2052 break; 2053 case OPERATION_TYPE_HOST_INVENTORY: 2054 $opinventory[] = $operationid; 2055 break; 2056 } 2057 } 2058 2059 // get OPERATION_TYPE_MESSAGE data 2060 if ($opmessage) { 2061 if ($this->outputIsRequested('opmessage', $options['selectOperations'])) { 2062 foreach ($opmessage as $operationId) { 2063 $operations[$operationId]['opmessage'] = []; 2064 } 2065 2066 $db_opmessages = DBselect( 2067 'SELECT o.operationid,o.default_msg,o.subject,o.message,o.mediatypeid'. 2068 ' FROM opmessage o'. 2069 ' WHERE '.dbConditionInt('o.operationid', $opmessage) 2070 ); 2071 while ($db_opmessage = DBfetch($db_opmessages)) { 2072 $operationid = $db_opmessage['operationid']; 2073 unset($db_opmessage['operationid']); 2074 $operations[$operationid]['opmessage'] = $db_opmessage; 2075 } 2076 } 2077 2078 if ($this->outputIsRequested('opmessage_grp', $options['selectOperations'])) { 2079 foreach ($opmessage as $operationId) { 2080 $operations[$operationId]['opmessage_grp'] = []; 2081 } 2082 2083 $db_opmessages_grp = DBselect( 2084 'SELECT og.operationid,og.usrgrpid'. 2085 ' FROM opmessage_grp og'. 2086 ' WHERE '.dbConditionInt('og.operationid', $opmessage) 2087 ); 2088 while ($db_opmessage_grp = DBfetch($db_opmessages_grp)) { 2089 $operationid = $db_opmessage_grp['operationid']; 2090 unset($db_opmessage_grp['operationid']); 2091 $operations[$operationid]['opmessage_grp'][] = $db_opmessage_grp; 2092 } 2093 } 2094 2095 if ($this->outputIsRequested('opmessage_usr', $options['selectOperations'])) { 2096 foreach ($opmessage as $operationId) { 2097 $operations[$operationId]['opmessage_usr'] = []; 2098 } 2099 2100 $db_opmessages_usr = DBselect( 2101 'SELECT ou.operationid,ou.userid'. 2102 ' FROM opmessage_usr ou'. 2103 ' WHERE '.dbConditionInt('ou.operationid', $opmessage) 2104 ); 2105 while ($db_opmessage_usr = DBfetch($db_opmessages_usr)) { 2106 $operationid = $db_opmessage_usr['operationid']; 2107 unset($db_opmessage_usr['operationid']); 2108 $operations[$operationid]['opmessage_usr'][] = $db_opmessage_usr; 2109 } 2110 } 2111 } 2112 2113 // get OPERATION_TYPE_COMMAND data 2114 if ($opcommand) { 2115 if ($this->outputIsRequested('opcommand', $options['selectOperations'])) { 2116 foreach ($opcommand as $operationId) { 2117 $operations[$operationId]['opcommand'] = []; 2118 } 2119 2120 $db_opcommands = DBselect( 2121 'SELECT o.operationid,o.scriptid'. 2122 ' FROM opcommand o'. 2123 ' WHERE '.dbConditionInt('o.operationid', $opcommand) 2124 ); 2125 while ($db_opcommand = DBfetch($db_opcommands)) { 2126 $operationid = $db_opcommand['operationid']; 2127 unset($db_opcommand['operationid']); 2128 $operations[$operationid]['opcommand'] = $db_opcommand; 2129 } 2130 } 2131 2132 if ($this->outputIsRequested('opcommand_hst', $options['selectOperations'])) { 2133 foreach ($opcommand as $operationId) { 2134 $operations[$operationId]['opcommand_hst'] = []; 2135 } 2136 2137 $db_opcommands_hst = DBselect( 2138 'SELECT oh.opcommand_hstid,oh.operationid,oh.hostid'. 2139 ' FROM opcommand_hst oh'. 2140 ' WHERE '.dbConditionInt('oh.operationid', $opcommand) 2141 ); 2142 while ($db_opcommand_hst = DBfetch($db_opcommands_hst)) { 2143 $operationid = $db_opcommand_hst['operationid']; 2144 unset($db_opcommand_hst['operationid']); 2145 $operations[$operationid]['opcommand_hst'][] = $db_opcommand_hst; 2146 } 2147 } 2148 2149 if ($this->outputIsRequested('opcommand_grp', $options['selectOperations'])) { 2150 foreach ($opcommand as $operationId) { 2151 $operations[$operationId]['opcommand_grp'] = []; 2152 } 2153 2154 $db_opcommands_grp = DBselect( 2155 'SELECT og.opcommand_grpid,og.operationid,og.groupid'. 2156 ' FROM opcommand_grp og'. 2157 ' WHERE '.dbConditionInt('og.operationid', $opcommand) 2158 ); 2159 while ($db_opcommand_grp = DBfetch($db_opcommands_grp)) { 2160 $operationid = $db_opcommand_grp['operationid']; 2161 unset($db_opcommand_grp['operationid']); 2162 $operations[$operationid]['opcommand_grp'][] = $db_opcommand_grp; 2163 } 2164 } 2165 } 2166 2167 // get OPERATION_TYPE_GROUP_ADD, OPERATION_TYPE_GROUP_REMOVE data 2168 if ($opgroup) { 2169 if ($this->outputIsRequested('opgroup', $options['selectOperations'])) { 2170 foreach ($opgroup as $operationId) { 2171 $operations[$operationId]['opgroup'] = []; 2172 } 2173 2174 $db_opgroups = DBselect( 2175 'SELECT o.operationid,o.groupid'. 2176 ' FROM opgroup o'. 2177 ' WHERE '.dbConditionInt('o.operationid', $opgroup) 2178 ); 2179 while ($db_opgroup = DBfetch($db_opgroups)) { 2180 $operationid = $db_opgroup['operationid']; 2181 unset($db_opgroup['operationid']); 2182 $operations[$operationid]['opgroup'][] = $db_opgroup; 2183 } 2184 } 2185 } 2186 2187 // get OPERATION_TYPE_TEMPLATE_ADD, OPERATION_TYPE_TEMPLATE_REMOVE data 2188 if ($optemplate) { 2189 if ($this->outputIsRequested('optemplate', $options['selectOperations'])) { 2190 foreach ($optemplate as $operationId) { 2191 $operations[$operationId]['optemplate'] = []; 2192 } 2193 2194 $db_optemplates = DBselect( 2195 'SELECT o.operationid,o.templateid'. 2196 ' FROM optemplate o'. 2197 ' WHERE '.dbConditionInt('o.operationid', $optemplate) 2198 ); 2199 while ($db_optemplate = DBfetch($db_optemplates)) { 2200 $operationid = $db_optemplate['operationid']; 2201 unset($db_optemplate['operationid']); 2202 $operations[$operationid]['optemplate'][] = $db_optemplate; 2203 } 2204 } 2205 } 2206 2207 // get OPERATION_TYPE_HOST_INVENTORY data 2208 if ($opinventory) { 2209 if ($this->outputIsRequested('opinventory', $options['selectOperations'])) { 2210 foreach ($opinventory as $operationId) { 2211 $operations[$operationId]['opinventory'] = []; 2212 } 2213 2214 $db_opinventories = DBselect( 2215 'SELECT o.operationid,o.inventory_mode'. 2216 ' FROM opinventory o'. 2217 ' WHERE '.dbConditionInt('o.operationid', $opinventory) 2218 ); 2219 while ($db_opinventory = DBfetch($db_opinventories)) { 2220 $operationid = $db_opinventory['operationid']; 2221 unset($db_opinventory['operationid']); 2222 $operations[$operationid]['opinventory'] = $db_opinventory; 2223 } 2224 } 2225 } 2226 2227 $operations = $this->unsetExtraFields($operations, ['operationid', 'actionid', 'operationtype'], 2228 $options['selectOperations'] 2229 ); 2230 $result = $relationMap->mapMany($result, $operations, 'operations'); 2231 } 2232 2233 // Adding recovery operations. 2234 if ($options['selectRecoveryOperations'] !== null 2235 && $options['selectRecoveryOperations'] != API_OUTPUT_COUNT) { 2236 $recovery_operations = API::getApiService()->select('operations', [ 2237 'output' => $this->outputExtend($options['selectRecoveryOperations'], 2238 ['operationid', 'actionid', 'operationtype'] 2239 ), 2240 'filter' => ['actionid' => $actionIds, 'recovery' => ACTION_RECOVERY_OPERATION], 2241 'preservekeys' => true 2242 ]); 2243 2244 $relationMap = $this->createRelationMap($recovery_operations, 'actionid', 'operationid'); 2245 $recovery_operationids = $relationMap->getRelatedIds(); 2246 2247 if ($this->outputIsRequested('opconditions', $options['selectRecoveryOperations'])) { 2248 foreach ($recovery_operations as &$recovery_operation) { 2249 unset($recovery_operation['esc_period'], $recovery_operation['esc_step_from'], 2250 $recovery_operation['esc_step_to'] 2251 ); 2252 2253 $recovery_operation['opconditions'] = []; 2254 } 2255 unset($recovery_operation); 2256 2257 $res = DBselect('SELECT op.* FROM opconditions op WHERE '. 2258 dbConditionInt('op.operationid', $recovery_operationids) 2259 ); 2260 while ($opcondition = DBfetch($res)) { 2261 $recovery_operations[$opcondition['operationid']]['opconditions'][] = $opcondition; 2262 } 2263 } 2264 2265 $opmessage = []; 2266 $opcommand = []; 2267 $op_recovery_message = []; 2268 2269 foreach ($recovery_operations as $recovery_operationid => $recovery_operation) { 2270 unset($recovery_operations[$recovery_operationid]['recovery']); 2271 2272 switch ($recovery_operation['operationtype']) { 2273 case OPERATION_TYPE_MESSAGE: 2274 $opmessage[] = $recovery_operationid; 2275 break; 2276 case OPERATION_TYPE_COMMAND: 2277 $opcommand[] = $recovery_operationid; 2278 break; 2279 case OPERATION_TYPE_RECOVERY_MESSAGE: 2280 $op_recovery_message[] = $recovery_operationid; 2281 break; 2282 } 2283 } 2284 2285 // Get OPERATION_TYPE_MESSAGE data. 2286 if ($opmessage) { 2287 if ($this->outputIsRequested('opmessage', $options['selectRecoveryOperations'])) { 2288 foreach ($opmessage as $recovery_operationid) { 2289 $recovery_operations[$recovery_operationid]['opmessage'] = []; 2290 } 2291 2292 $db_opmessages = DBselect( 2293 'SELECT o.operationid,o.default_msg,o.subject,o.message,o.mediatypeid'. 2294 ' FROM opmessage o'. 2295 ' WHERE '.dbConditionInt('o.operationid', $opmessage) 2296 ); 2297 while ($db_opmessage = DBfetch($db_opmessages)) { 2298 $operationid = $db_opmessage['operationid']; 2299 unset($db_opmessage['operationid']); 2300 $recovery_operations[$operationid]['opmessage'] = $db_opmessage; 2301 } 2302 } 2303 2304 if ($this->outputIsRequested('opmessage_grp', $options['selectRecoveryOperations'])) { 2305 foreach ($opmessage as $recovery_operationid) { 2306 $recovery_operations[$recovery_operationid]['opmessage_grp'] = []; 2307 } 2308 2309 $db_opmessages_grp = DBselect( 2310 'SELECT og.operationid,og.usrgrpid'. 2311 ' FROM opmessage_grp og'. 2312 ' WHERE '.dbConditionInt('og.operationid', $opmessage) 2313 ); 2314 while ($db_opmessage_grp = DBfetch($db_opmessages_grp)) { 2315 $operationid = $db_opmessage_grp['operationid']; 2316 unset($db_opmessage_grp['operationid']); 2317 $recovery_operations[$operationid]['opmessage_grp'][] = $db_opmessage_grp; 2318 } 2319 } 2320 2321 if ($this->outputIsRequested('opmessage_usr', $options['selectRecoveryOperations'])) { 2322 foreach ($opmessage as $recovery_operationid) { 2323 $recovery_operations[$recovery_operationid]['opmessage_usr'] = []; 2324 } 2325 2326 $db_opmessages_usr = DBselect( 2327 'SELECT ou.operationid,ou.userid'. 2328 ' FROM opmessage_usr ou'. 2329 ' WHERE '.dbConditionInt('ou.operationid', $opmessage) 2330 ); 2331 while ($db_opmessage_usr = DBfetch($db_opmessages_usr)) { 2332 $operationid = $db_opmessage_usr['operationid']; 2333 unset($db_opmessage_usr['operationid']); 2334 $recovery_operations[$operationid]['opmessage_usr'][] = $db_opmessage_usr; 2335 } 2336 } 2337 } 2338 2339 // Get OPERATION_TYPE_COMMAND data. 2340 if ($opcommand) { 2341 if ($this->outputIsRequested('opcommand', $options['selectRecoveryOperations'])) { 2342 foreach ($opcommand as $recovery_operationid) { 2343 $recovery_operations[$recovery_operationid]['opcommand'] = []; 2344 } 2345 2346 $db_opcommands = DBselect( 2347 'SELECT o.operationid,o.scriptid'. 2348 ' FROM opcommand o'. 2349 ' WHERE '.dbConditionInt('o.operationid', $opcommand) 2350 ); 2351 while ($db_opcommand = DBfetch($db_opcommands)) { 2352 $operationid = $db_opcommand['operationid']; 2353 unset($db_opcommand['operationid']); 2354 $recovery_operations[$operationid]['opcommand'] = $db_opcommand; 2355 } 2356 } 2357 2358 if ($this->outputIsRequested('opcommand_hst', $options['selectRecoveryOperations'])) { 2359 foreach ($opcommand as $recovery_operationid) { 2360 $recovery_operations[$recovery_operationid]['opcommand_hst'] = []; 2361 } 2362 2363 $db_opcommands_hst = DBselect( 2364 'SELECT oh.opcommand_hstid,oh.operationid,oh.hostid'. 2365 ' FROM opcommand_hst oh'. 2366 ' WHERE '.dbConditionInt('oh.operationid', $opcommand) 2367 ); 2368 while ($db_opcommand_hst = DBfetch($db_opcommands_hst)) { 2369 $operationid = $db_opcommand_hst['operationid']; 2370 unset($db_opcommand_hst['operationid']); 2371 $recovery_operations[$operationid]['opcommand_hst'][] = $db_opcommand_hst; 2372 } 2373 } 2374 2375 if ($this->outputIsRequested('opcommand_grp', $options['selectRecoveryOperations'])) { 2376 foreach ($opcommand as $recovery_operationid) { 2377 $recovery_operations[$recovery_operationid]['opcommand_grp'] = []; 2378 } 2379 2380 $db_opcommands_grp = DBselect( 2381 'SELECT og.opcommand_grpid,og.operationid,og.groupid'. 2382 ' FROM opcommand_grp og'. 2383 ' WHERE '.dbConditionInt('og.operationid', $opcommand) 2384 ); 2385 while ($db_opcommand_grp = DBfetch($db_opcommands_grp)) { 2386 $operationid = $db_opcommand_grp['operationid']; 2387 unset($db_opcommand_grp['operationid']); 2388 $recovery_operations[$operationid]['opcommand_grp'][] = $db_opcommand_grp; 2389 } 2390 } 2391 } 2392 2393 // get OPERATION_TYPE_RECOVERY_MESSAGE data 2394 if ($op_recovery_message) { 2395 if ($this->outputIsRequested('opmessage', $options['selectRecoveryOperations'])) { 2396 foreach ($op_recovery_message as $operationid) { 2397 $recovery_operations[$operationid]['opmessage'] = []; 2398 } 2399 2400 $db_opmessages = DBselect( 2401 'SELECT o.operationid,o.default_msg,o.subject,o.message,o.mediatypeid'. 2402 ' FROM opmessage o'. 2403 ' WHERE '.dbConditionInt('o.operationid', $op_recovery_message) 2404 ); 2405 while ($db_opmessage = DBfetch($db_opmessages)) { 2406 $operationid = $db_opmessage['operationid']; 2407 unset($db_opmessage['operationid']); 2408 $recovery_operations[$operationid]['opmessage'] = $db_opmessage; 2409 } 2410 } 2411 } 2412 2413 $recovery_operations = $this->unsetExtraFields($recovery_operations, 2414 ['operationid', 'actionid', 'operationtype'], $options['selectRecoveryOperations'] 2415 ); 2416 $result = $relationMap->mapMany($result, $recovery_operations, 'recoveryOperations'); 2417 } 2418 2419 return $result; 2420 } 2421 2422 /** 2423 * Returns array of acknowledge operations according to requested options. 2424 * 2425 * @param array $ack_operations Array of acknowledge operation with key set to operationid. 2426 * @param array $ack_options Array of acknowledge operation options from request. 2427 * 2428 * @return array 2429 */ 2430 protected function getAcknowledgeOperations($ack_operations, $ack_options) { 2431 $opmessages = []; 2432 $nonack_messages = []; 2433 $opcommands = []; 2434 2435 foreach ($ack_operations as $ack_operationid => &$ack_operation) { 2436 unset($ack_operation['esc_period'], $ack_operation['esc_step_from'], $ack_operation['esc_step_to']); 2437 2438 switch ($ack_operation['operationtype']) { 2439 case OPERATION_TYPE_ACK_MESSAGE: 2440 $opmessages[] = $ack_operationid; 2441 break; 2442 case OPERATION_TYPE_MESSAGE: 2443 $opmessages[] = $ack_operationid; 2444 $nonack_messages[] = $ack_operationid; 2445 break; 2446 case OPERATION_TYPE_COMMAND: 2447 $opcommands[] = $ack_operationid; 2448 break; 2449 } 2450 } 2451 unset($ack_operation); 2452 2453 if ($opmessages) { 2454 if ($this->outputIsRequested('opmessage', $ack_options)) { 2455 foreach ($opmessages as $operationid) { 2456 $ack_operations[$operationid]['opmessage'] = []; 2457 } 2458 2459 $db_opmessages = DBselect( 2460 'SELECT o.operationid,o.default_msg,o.subject,o.message,o.mediatypeid'. 2461 ' FROM opmessage o'. 2462 ' WHERE '.dbConditionInt('o.operationid', $opmessages) 2463 ); 2464 while ($db_opmessage = DBfetch($db_opmessages)) { 2465 $operationid = $db_opmessage['operationid']; 2466 unset($db_opmessage['operationid']); 2467 $ack_operations[$operationid]['opmessage'] = $db_opmessage; 2468 } 2469 } 2470 2471 if ($this->outputIsRequested('opmessage_grp', $ack_options) && $nonack_messages) { 2472 foreach ($nonack_messages as $operationid) { 2473 $ack_operations[$operationid]['opmessage_grp'] = []; 2474 } 2475 2476 $db_opmessage_grp = DBselect( 2477 'SELECT og.operationid,og.usrgrpid'. 2478 ' FROM opmessage_grp og'. 2479 ' WHERE '.dbConditionInt('og.operationid', $nonack_messages) 2480 ); 2481 while ($opmessage_grp = DBfetch($db_opmessage_grp)) { 2482 $operationid = $opmessage_grp['operationid']; 2483 unset($opmessage_grp['operationid']); 2484 $ack_operations[$operationid]['opmessage_grp'][] = $opmessage_grp; 2485 } 2486 } 2487 2488 if ($this->outputIsRequested('opmessage_usr', $ack_options) && $nonack_messages) { 2489 foreach ($nonack_messages as $operationid) { 2490 $ack_operations[$operationid]['opmessage_usr'] = []; 2491 } 2492 2493 $db_opmessage_usr = DBselect( 2494 'SELECT ou.operationid,ou.userid'. 2495 ' FROM opmessage_usr ou'. 2496 ' WHERE '.dbConditionInt('ou.operationid', $nonack_messages) 2497 ); 2498 while ($opmessage_usr = DBfetch($db_opmessage_usr)) { 2499 $operationid = $opmessage_usr['operationid']; 2500 unset($opmessage_usr['operationid']); 2501 $ack_operations[$operationid]['opmessage_usr'][] = $opmessage_usr; 2502 } 2503 } 2504 } 2505 2506 if ($opcommands) { 2507 if ($this->outputIsRequested('opcommand', $ack_options)) { 2508 foreach ($opcommands as $operationid) { 2509 $ack_operations[$operationid]['opcommand'] = []; 2510 } 2511 2512 $db_opcommands = DBselect( 2513 'SELECT o.operationid,o.scriptid'. 2514 ' FROM opcommand o'. 2515 ' WHERE '.dbConditionInt('o.operationid', $opcommands) 2516 ); 2517 while ($db_opcommand = DBfetch($db_opcommands)) { 2518 $operationid = $db_opcommand['operationid']; 2519 unset($db_opcommand['operationid']); 2520 $ack_operations[$operationid]['opcommand'] = $db_opcommand; 2521 } 2522 } 2523 2524 if ($this->outputIsRequested('opcommand_hst', $ack_options)) { 2525 foreach ($opcommands as $operationid) { 2526 $ack_operations[$operationid]['opcommand_hst'] = []; 2527 } 2528 2529 $db_opcommand_hst = DBselect( 2530 'SELECT oh.opcommand_hstid,oh.operationid,oh.hostid'. 2531 ' FROM opcommand_hst oh'. 2532 ' WHERE '.dbConditionInt('oh.operationid', $opcommands) 2533 ); 2534 while ($opcommand_hst = DBfetch($db_opcommand_hst)) { 2535 $operationid = $opcommand_hst['operationid']; 2536 unset($opcommand_hst['operationid']); 2537 $ack_operations[$operationid]['opcommand_hst'][] = $opcommand_hst; 2538 } 2539 } 2540 2541 if ($this->outputIsRequested('opcommand_grp', $ack_options)) { 2542 foreach ($opcommands as $operationid) { 2543 $ack_operations[$operationid]['opcommand_grp'] = []; 2544 } 2545 2546 $db_opcommand_grp = DBselect( 2547 'SELECT og.opcommand_grpid,og.operationid,og.groupid'. 2548 ' FROM opcommand_grp og'. 2549 ' WHERE '.dbConditionInt('og.operationid', $opcommands) 2550 ); 2551 while ($opcommand_grp = DBfetch($db_opcommand_grp)) { 2552 $operationid = $opcommand_grp['operationid']; 2553 unset($opcommand_grp['operationid']); 2554 $ack_operations[$operationid]['opcommand_grp'][] = $opcommand_grp; 2555 } 2556 } 2557 } 2558 2559 $ack_operations = $this->unsetExtraFields($ack_operations, ['operationid', 'operationtype'], 2560 $ack_options 2561 ); 2562 2563 return $ack_operations; 2564 } 2565 2566 /** 2567 * Returns the parameters for creating a discovery rule filter validator. 2568 * 2569 * @return array 2570 */ 2571 protected function getFilterSchema() { 2572 return [ 2573 'validators' => [ 2574 'evaltype' => new CLimitedSetValidator([ 2575 'values' => [ 2576 CONDITION_EVAL_TYPE_OR, 2577 CONDITION_EVAL_TYPE_AND, 2578 CONDITION_EVAL_TYPE_AND_OR, 2579 CONDITION_EVAL_TYPE_EXPRESSION 2580 ], 2581 'messageInvalid' => _('Incorrect type of calculation for action "%1$s".') 2582 ]), 2583 'formula' => new CStringValidator([ 2584 'empty' => true 2585 ]), 2586 'conditions' => new CCollectionValidator([ 2587 'empty' => true, 2588 'messageInvalid' => _('Incorrect conditions for action "%1$s".') 2589 ]) 2590 ], 2591 'postValidators' => [ 2592 new CConditionValidator([ 2593 'messageMissingFormula' => _('Formula missing for action "%1$s".'), 2594 'messageInvalidFormula' => _('Incorrect custom expression "%2$s" for action "%1$s": %3$s.'), 2595 'messageMissingCondition' => _('Condition "%2$s" used in formula "%3$s" for action "%1$s" is not defined.'), 2596 'messageUnusedCondition' => _('Condition "%2$s" is not used in formula "%3$s" for action "%1$s".'), 2597 'messageAndWithSeveralTriggers' => _('Comparing several triggers with "and" is not allowed.') 2598 ]) 2599 ], 2600 'required' => ['evaltype', 'conditions'], 2601 'messageRequired' => _('No "%2$s" given for the filter of action "%1$s".'), 2602 'messageUnsupported' => _('Unsupported parameter "%2$s" for the filter of action "%1$s".') 2603 ]; 2604 } 2605 2606 /** 2607 * Returns the parameters for creating a action filter condition validator. 2608 * 2609 * @return array 2610 */ 2611 protected function getFilterConditionSchema() { 2612 return [ 2613 'validators' => [ 2614 'conditiontype' => new CStringValidator([ 2615 'regex' => '/\d+/', 2616 'messageEmpty' => _('Empty filter condition type for action "%1$s".'), 2617 'messageRegex' => _('Incorrect filter condition type for action "%1$s".') 2618 ]), 2619 'value' => new CStringValidator([ 2620 'empty' => true 2621 ]), 2622 'value2' => new CStringValidator([ 2623 'empty' => true 2624 ]), 2625 'formulaid' => new CStringValidator([ 2626 'regex' => '/[A-Z]+/', 2627 'messageEmpty' => _('Empty filter condition formula ID for action "%1$s".'), 2628 'messageRegex' => _('Incorrect filter condition formula ID for action "%1$s".') 2629 ]), 2630 'operator' => new CStringValidator([ 2631 'regex' => '/\d+/', 2632 'messageEmpty' => _('Empty filter condition operator for action "%1$s".'), 2633 'messageRegex' => _('Incorrect filter condition operator for action "%1$s".') 2634 ]) 2635 ], 2636 'required' => ['conditiontype', 'value'], 2637 'postValidators' => [ 2638 new CActionCondValidator() 2639 ], 2640 'messageRequired' => _('No "%2$s" given for a filter condition of action "%1$s".'), 2641 'messageUnsupported' => _('Unsupported parameter "%2$s" for a filter condition of action "%1$s".') 2642 ]; 2643 } 2644 2645 protected function applyQueryOutputOptions($tableName, $tableAlias, array $options, array $sqlParts) { 2646 $sqlParts = parent::applyQueryOutputOptions($tableName, $tableAlias, $options, $sqlParts); 2647 2648 if (!$options['countOutput']) { 2649 // add filter fields 2650 if ($this->outputIsRequested('formula', $options['selectFilter']) 2651 || $this->outputIsRequested('eval_formula', $options['selectFilter']) 2652 || $this->outputIsRequested('conditions', $options['selectFilter'])) { 2653 2654 $sqlParts = $this->addQuerySelect('a.formula', $sqlParts); 2655 $sqlParts = $this->addQuerySelect('a.evaltype', $sqlParts); 2656 } 2657 if ($this->outputIsRequested('evaltype', $options['selectFilter'])) { 2658 $sqlParts = $this->addQuerySelect('a.evaltype', $sqlParts); 2659 } 2660 } 2661 2662 return $sqlParts; 2663 } 2664 2665 /** 2666 * Converts a formula with letters to a formula with IDs and updates it. 2667 * 2668 * @param string $actionId 2669 * @param string $formulaWithLetters formula with letters 2670 * @param array $conditions 2671 */ 2672 protected function updateFormula($actionId, $formulaWithLetters, array $conditions) { 2673 $formulaIdToConditionId = []; 2674 2675 foreach ($conditions as $condition) { 2676 $formulaIdToConditionId[$condition['formulaid']] = $condition['conditionid']; 2677 } 2678 $formula = CConditionHelper::replaceLetterIds($formulaWithLetters, $formulaIdToConditionId); 2679 2680 DB::updateByPk('actions', $actionId, ['formula' => $formula]); 2681 } 2682 2683 /** 2684 * Validate input given to action.create API call. 2685 * 2686 * @param $actions 2687 */ 2688 protected function validateCreate($actions) { 2689 if (!$actions) { 2690 self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); 2691 } 2692 2693 $actionDbFields = [ 2694 'name' => null, 2695 'eventsource' => null 2696 ]; 2697 2698 $duplicates = []; 2699 2700 foreach ($actions as $action) { 2701 if (!check_db_fields($actionDbFields, $action)) { 2702 self::exception( 2703 ZBX_API_ERROR_PARAMETERS, 2704 _s('Incorrect parameter for action "%1$s".', $action['name']) 2705 ); 2706 } 2707 2708 if (isset($duplicates[$action['name']])) { 2709 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" already exists.', $action['name'])); 2710 } 2711 else { 2712 $duplicates[$action['name']] = $action['name']; 2713 } 2714 2715 if (array_key_exists('esc_period', $action) && $action['eventsource'] == EVENT_SOURCE_TRIGGERS) { 2716 self::validateStepDuration($action['esc_period']); 2717 } 2718 } 2719 2720 $dbActionsWithSameName = $this->get([ 2721 'output' => ['name'], 2722 'filter' => ['name' => $duplicates], 2723 'nopermissions' => true 2724 ]); 2725 if ($dbActionsWithSameName) { 2726 $dbActionWithSameName = reset($dbActionsWithSameName); 2727 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" already exists.', $dbActionWithSameName['name'])); 2728 } 2729 2730 $filterValidator = new CSchemaValidator($this->getFilterSchema()); 2731 $filterConditionValidator = new CSchemaValidator($this->getFilterConditionSchema()); 2732 $pause_suppressed_validator = new CLimitedSetValidator([ 2733 'values' => [ACTION_PAUSE_SUPPRESSED_FALSE, ACTION_PAUSE_SUPPRESSED_TRUE] 2734 ]); 2735 2736 $conditionsToValidate = []; 2737 $operations_to_validate = []; 2738 2739 // Validate "filter" sections and "conditions" in them, ensure that "operations" section 2740 // is present and is not empty. Also collect conditions and operations for more validation. 2741 foreach ($actions as $action) { 2742 if ($action['eventsource'] != EVENT_SOURCE_TRIGGERS) { 2743 $this->checkNoParameters($action, ['pause_suppressed'], _('Cannot set "%1$s" for action "%2$s".'), 2744 $action['name'] 2745 ); 2746 } 2747 elseif (array_key_exists('pause_suppressed', $action) 2748 && !$pause_suppressed_validator->validate($action['pause_suppressed'])) { 2749 self::exception(ZBX_API_ERROR_PARAMETERS, 2750 _s('Incorrect value "%1$s" for "%2$s" field.', $action['pause_suppressed'], 'pause_suppressed') 2751 ); 2752 } 2753 2754 if (isset($action['filter'])) { 2755 $filterValidator->setObjectName($action['name']); 2756 $this->checkValidator($action['filter'], $filterValidator); 2757 $filterConditionValidator->setObjectName($action['name']); 2758 2759 foreach ($action['filter']['conditions'] as $condition) { 2760 if ($condition['conditiontype'] == CONDITION_TYPE_EVENT_TAG_VALUE && 2761 !array_key_exists('value2', $condition)) { 2762 self::exception( 2763 ZBX_API_ERROR_PARAMETERS, 2764 _s('No "%2$s" given for a filter condition of action "%1$s".', $action['name'], 'value2') 2765 ); 2766 } 2767 2768 $this->checkValidator($condition, $filterConditionValidator); 2769 $conditionsToValidate[] = $condition; 2770 } 2771 2772 $this->validateFilterConditionsIntegrity($action['name'], $action['eventsource'], 2773 $action['filter']['conditions'] 2774 ); 2775 } 2776 2777 if ((!array_key_exists('operations', $action) || !$action['operations']) 2778 && (!array_key_exists('recovery_operations', $action) || !$action['recovery_operations']) 2779 && (!array_key_exists('acknowledge_operations', $action) || !$action['acknowledge_operations'])) { 2780 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" no operations defined.', $action['name'])); 2781 } 2782 2783 if (array_key_exists('operations', $action)) { 2784 foreach ($action['operations'] as $operation) { 2785 if (array_key_exists('operationid', $operation)) { 2786 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect input parameters.')); 2787 } 2788 2789 $operation['recovery'] = ACTION_OPERATION; 2790 $operation['eventsource'] = $action['eventsource']; 2791 $operations_to_validate[] = $operation; 2792 } 2793 } 2794 2795 if (array_key_exists('recovery_operations', $action)) { 2796 foreach ($action['recovery_operations'] as $operation) { 2797 if (array_key_exists('operationid', $operation)) { 2798 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect input parameters.')); 2799 } 2800 2801 $operation['recovery'] = ACTION_RECOVERY_OPERATION; 2802 $operation['eventsource'] = $action['eventsource']; 2803 $operations_to_validate[] = $operation; 2804 } 2805 } 2806 2807 if (array_key_exists('acknowledge_operations', $action)) { 2808 foreach ($action['acknowledge_operations'] as $operation) { 2809 if (array_key_exists('operationid', $operation)) { 2810 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect input parameters.')); 2811 } 2812 2813 $operation['recovery'] = ACTION_ACKNOWLEDGE_OPERATION; 2814 $operation['eventsource'] = $action['eventsource']; 2815 $operations_to_validate[] = $operation; 2816 } 2817 } 2818 } 2819 2820 // Validate conditions and operations in regard to what's in database now. 2821 if ($conditionsToValidate) { 2822 $this->validateConditionsPermissions($conditionsToValidate); 2823 } 2824 if ($operations_to_validate) { 2825 $this->validateOperationsIntegrity($operations_to_validate); 2826 } 2827 } 2828 2829 /** 2830 * Validate default step duration and operation step duration values. 2831 * 2832 * @param string $esc_period Step duration. 2833 * 2834 * @throws APIException if the input is invalid. 2835 */ 2836 private static function validateStepDuration($esc_period) { 2837 if (!validateTimeUnit($esc_period, SEC_PER_MIN, SEC_PER_WEEK, false, $error, ['usermacros' => true])) { 2838 self::exception(ZBX_API_ERROR_PARAMETERS, 2839 _s('Incorrect value for field "%1$s": %2$s.', 'esc_period', $error) 2840 ); 2841 } 2842 } 2843 2844 /** 2845 * Validate input given to action.update API call. 2846 * 2847 * @param array $actions 2848 * @param array $db_actions 2849 */ 2850 protected function validateUpdate($actions, $db_actions) { 2851 foreach ($actions as $action) { 2852 if (isset($action['actionid']) && !isset($db_actions[$action['actionid']])) { 2853 self::exception( 2854 ZBX_API_ERROR_PERMISSIONS, 2855 _('No permissions to referred object or it does not exist!') 2856 ); 2857 } 2858 } 2859 $actions = zbx_toHash($actions, 'actionid'); 2860 2861 $pause_suppressed_validator = new CLimitedSetValidator([ 2862 'values' => [ACTION_PAUSE_SUPPRESSED_FALSE, ACTION_PAUSE_SUPPRESSED_TRUE] 2863 ]); 2864 2865 // check fields 2866 $duplicates = []; 2867 2868 foreach ($actions as $action) { 2869 $action_name = isset($action['name']) ? $action['name'] : $db_actions[$action['actionid']]['name']; 2870 2871 if (!check_db_fields(['actionid' => null], $action)) { 2872 self::exception(ZBX_API_ERROR_PARAMETERS, _s( 2873 'Incorrect parameters for action update method "%1$s".', $action_name 2874 )); 2875 } 2876 2877 // check if user changed esc_period for trigger eventsource 2878 if (array_key_exists('esc_period', $action) 2879 && $db_actions[$action['actionid']]['eventsource'] == EVENT_SOURCE_TRIGGERS) { 2880 self::validateStepDuration($action['esc_period']); 2881 } 2882 2883 $this->checkNoParameters($action, ['eventsource'], _('Cannot update "%1$s" for action "%2$s".'), 2884 $action_name 2885 ); 2886 2887 if ($db_actions[$action['actionid']]['eventsource'] != EVENT_SOURCE_TRIGGERS) { 2888 $this->checkNoParameters($action, ['pause_suppressed'], _('Cannot update "%1$s" for action "%2$s".'), 2889 $action_name 2890 ); 2891 } 2892 elseif (array_key_exists('pause_suppressed', $action) 2893 && !$pause_suppressed_validator->validate($action['pause_suppressed'])) { 2894 self::exception(ZBX_API_ERROR_PARAMETERS, 2895 _s('Incorrect value "%1$s" for "%2$s" field.', $action['pause_suppressed'], 'pause_suppressed') 2896 ); 2897 } 2898 2899 if (!isset($action['name'])) { 2900 continue; 2901 } 2902 2903 if (isset($duplicates[$action['name']])) { 2904 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" already exists.', $action['name'])); 2905 } 2906 else { 2907 $duplicates[$action['name']] = $action['name']; 2908 } 2909 } 2910 2911 // Unset accidentally passed in "evaltype" and "formula" fields. 2912 foreach ($actions as &$action) { 2913 unset($action['evaltype'], $action['formula']); 2914 } 2915 unset($action); 2916 2917 $filterValidator = new CSchemaValidator($this->getFilterSchema()); 2918 2919 $filterConditionValidator = new CSchemaValidator($this->getFilterConditionSchema()); 2920 2921 $operations_to_validate = []; 2922 $conditionsToValidate = []; 2923 2924 foreach ($actions as $actionId => $action) { 2925 $db_action = $db_actions[$actionId]; 2926 2927 if (isset($action['name'])) { 2928 $action_name = $action['name']; 2929 2930 $actionExists = $this->get([ 2931 'output' => ['actionid'], 2932 'filter' => ['name' => $action_name], 2933 'nopermissions' => true 2934 ]); 2935 if (($actionExists = reset($actionExists)) 2936 && (bccomp($actionExists['actionid'], $actionId) != 0)) { 2937 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" already exists.', $action_name)); 2938 } 2939 } 2940 else { 2941 $action_name = $db_action['name']; 2942 } 2943 2944 if (isset($action['filter'])) { 2945 $action_filter = $action['filter']; 2946 2947 $filterValidator->setObjectName($action_name); 2948 $filterConditionValidator->setObjectName($action_name); 2949 2950 $this->checkValidator($action_filter, $filterValidator); 2951 2952 foreach ($action_filter['conditions'] as $condition) { 2953 if ($condition['conditiontype'] == CONDITION_TYPE_EVENT_TAG_VALUE 2954 && !array_key_exists('value2', $condition)) { 2955 self::exception( 2956 ZBX_API_ERROR_PARAMETERS, 2957 _s('No "%2$s" given for a filter condition of action "%1$s".', $action_name, 'value2') 2958 ); 2959 } 2960 2961 $this->checkValidator($condition, $filterConditionValidator); 2962 $conditionsToValidate[] = $condition; 2963 } 2964 2965 $this->validateFilterConditionsIntegrity($action_name, $db_action['eventsource'], 2966 $action_filter['conditions'] 2967 ); 2968 } 2969 2970 $operations_defined = array_key_exists('operations', $action) 2971 ? (bool) $action['operations'] 2972 : (bool) $db_action['operations']; 2973 $rcv_operations_defined = array_key_exists('recovery_operations', $action) 2974 ? (bool) $action['recovery_operations'] 2975 : (bool) $db_action['recoveryOperations']; 2976 $ack_operations_defined = array_key_exists('acknowledge_operations', $action) 2977 ? (bool) $action['acknowledge_operations'] 2978 : (bool) $db_action['acknowledgeOperations']; 2979 2980 if (!$operations_defined && !$rcv_operations_defined && !$ack_operations_defined) { 2981 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Action "%1$s" no operations defined.', $action_name)); 2982 } 2983 2984 if (array_key_exists('operations', $action) && $action['operations']) { 2985 $db_operations = zbx_toHash($db_actions[$action['actionid']]['operations'], 'operationid'); 2986 foreach ($action['operations'] as $operation) { 2987 if (!array_key_exists('operationid', $operation) 2988 || array_key_exists($operation['operationid'], $db_operations)) { 2989 $operation['recovery'] = ACTION_OPERATION; 2990 $operation['eventsource'] = $db_action['eventsource']; 2991 $operations_to_validate[] = $operation; 2992 } 2993 else { 2994 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operationid.')); 2995 } 2996 } 2997 } 2998 2999 // Recovery operations. 3000 if (array_key_exists('recovery_operations', $action) && $action['recovery_operations']) { 3001 $db_recovery_operations = zbx_toHash($db_actions[$action['actionid']]['recoveryOperations'], 3002 'operationid' 3003 ); 3004 foreach ($action['recovery_operations'] as $recovery_operation) { 3005 if (!array_key_exists('operationid', $recovery_operation) 3006 || array_key_exists($recovery_operation['operationid'], $db_recovery_operations)) { 3007 $recovery_operation['recovery'] = ACTION_RECOVERY_OPERATION; 3008 $recovery_operation['eventsource'] = $db_action['eventsource']; 3009 $operations_to_validate[] = $recovery_operation; 3010 } 3011 else { 3012 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect action operationid.')); 3013 } 3014 } 3015 } 3016 3017 if (array_key_exists('acknowledge_operations', $action) && $action['acknowledge_operations']) { 3018 $db_ack_operations = zbx_toHash($db_actions[$action['actionid']]['acknowledgeOperations'], 3019 'operationid' 3020 ); 3021 foreach ($action['acknowledge_operations'] as $ack_operation) { 3022 if (!array_key_exists('operationid', $ack_operation) 3023 || array_key_exists($ack_operation['operationid'], $db_ack_operations)) { 3024 $ack_operation['recovery'] = ACTION_ACKNOWLEDGE_OPERATION; 3025 $ack_operation['eventsource'] = $db_action['eventsource']; 3026 3027 if (array_key_exists('operationid', $ack_operation) 3028 && array_key_exists($ack_operation['operationid'], $db_ack_operations)) { 3029 $db_ack_operation = $db_ack_operations[$ack_operation['operationid']]; 3030 $operation_type = array_key_exists('operationtype', $ack_operation) 3031 ? $ack_operation['operationtype'] 3032 : $db_ack_operation['operationtype']; 3033 3034 // Field 'operationtype' is required. 3035 unset($db_ack_operation['operationtype']); 3036 3037 if ($operation_type == OPERATION_TYPE_MESSAGE) { 3038 unset($db_ack_operation['opmessage_grp'], $db_ack_operation['opmessage_usr']); 3039 } 3040 elseif ($operation_type == OPERATION_TYPE_COMMAND) { 3041 unset($db_ack_operation['opcommand_grp'], $db_ack_operation['opcommand_hst']); 3042 } 3043 3044 $ack_operation += $db_ack_operation; 3045 } 3046 3047 $operations_to_validate[] = $ack_operation; 3048 } 3049 else { 3050 self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect acknowledgement action operationid.')); 3051 } 3052 } 3053 } 3054 } 3055 3056 if ($conditionsToValidate) { 3057 $this->validateConditionsPermissions($conditionsToValidate); 3058 } 3059 $this->validateOperationsIntegrity($operations_to_validate); 3060 } 3061 3062 /** 3063 * Check permissions to DB entities referenced by action conditions. 3064 * 3065 * @param array $conditions conditions for which permissions to referenced DB entities will be checked 3066 */ 3067 protected function validateConditionsPermissions(array $conditions) { 3068 $hostGroupIdsAll = []; 3069 $templateIdsAll = []; 3070 $triggerIdsAll = []; 3071 $hostIdsAll = []; 3072 $discoveryRuleIdsAll = []; 3073 $discoveryCheckIdsAll = []; 3074 $proxyIdsAll = []; 3075 3076 foreach ($conditions as $condition) { 3077 $conditionValue = $condition['value']; 3078 // validate condition values depending on condition type 3079 switch ($condition['conditiontype']) { 3080 case CONDITION_TYPE_HOST_GROUP: 3081 $hostGroupIdsAll[$conditionValue] = $conditionValue; 3082 break; 3083 3084 case CONDITION_TYPE_TEMPLATE: 3085 $templateIdsAll[$conditionValue] = $conditionValue; 3086 break; 3087 3088 case CONDITION_TYPE_TRIGGER: 3089 $triggerIdsAll[$conditionValue] = $conditionValue; 3090 break; 3091 3092 case CONDITION_TYPE_HOST: 3093 $hostIdsAll[$conditionValue] = $conditionValue; 3094 break; 3095 3096 case CONDITION_TYPE_DRULE: 3097 $discoveryRuleIdsAll[$conditionValue] = $conditionValue; 3098 break; 3099 3100 case CONDITION_TYPE_DCHECK: 3101 $discoveryCheckIdsAll[$conditionValue] = $conditionValue; 3102 break; 3103 3104 case CONDITION_TYPE_PROXY: 3105 $proxyIdsAll[$conditionValue] = $conditionValue; 3106 break; 3107 } 3108 } 3109 3110 $this->checkHostGroupsPermissions($hostGroupIdsAll, 3111 _('Incorrect action condition host group. Host group does not exist or you have no access to it.') 3112 ); 3113 $this->checkHostsPermissions($hostIdsAll, 3114 _('Incorrect action condition host. Host does not exist or you have no access to it.') 3115 ); 3116 $this->checkTemplatesPermissions($templateIdsAll, 3117 _('Incorrect action condition template. Template does not exist or you have no access to it.') 3118 ); 3119 $this->checkTriggersPermissions($triggerIdsAll, 3120 _('Incorrect action condition trigger. Trigger does not exist or you have no access to it.') 3121 ); 3122 $this->checkDRulesPermissions($discoveryRuleIdsAll, 3123 _('Incorrect action condition discovery rule. Discovery rule does not exist or you have no access to it.') 3124 ); 3125 $this->checkDChecksPermissions($discoveryCheckIdsAll, 3126 _('Incorrect action condition discovery check. Discovery check does not exist or you have no access to it.') 3127 ); 3128 $this->checkProxiesPermissions($proxyIdsAll, 3129 _('Incorrect action condition proxy. Proxy does not exist or you have no access to it.') 3130 ); 3131 } 3132 3133 /** 3134 * Checks if all given media types are valid. 3135 * 3136 * @param array $mediatypeids Array of media type ids where key is checked media type id. 3137 * @param string $error Error message to throw if invalid media type id was supplied. 3138 * 3139 * @throws APIException if invalid media types given. 3140 */ 3141 private function checkMediatypesExists(array $mediatypeids, $error) { 3142 if ($mediatypeids) { 3143 $count = API::MediaType()->get([ 3144 'countOutput' => true, 3145 'mediatypeids' => array_keys($mediatypeids) 3146 ]); 3147 3148 if ($count != count($mediatypeids)) { 3149 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3150 } 3151 } 3152 } 3153 3154 /** 3155 * Checks if the current user has access to the given host groups. 3156 * 3157 * @throws APIException if the user doesn't have write permissions for the given host groups 3158 * 3159 * @param array $groupids 3160 * @param string $error 3161 */ 3162 private function checkHostGroupsPermissions(array $groupids, $error) { 3163 if ($groupids) { 3164 $groupids = array_unique($groupids); 3165 3166 $count = API::HostGroup()->get([ 3167 'countOutput' => true, 3168 'groupids' => $groupids, 3169 'editable' => true 3170 ]); 3171 3172 if ($count != count($groupids)) { 3173 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3174 } 3175 } 3176 } 3177 3178 /** 3179 * Checks if the current user has access to the given hosts. 3180 * 3181 * @throws APIException if the user doesn't have write permissions for the given hosts 3182 * 3183 * @param array $hostids 3184 * @param string $error 3185 */ 3186 private function checkHostsPermissions(array $hostids, $error) { 3187 if ($hostids) { 3188 $hostids = array_unique($hostids); 3189 3190 $count = API::Host()->get([ 3191 'countOutput' => true, 3192 'hostids' => $hostids, 3193 'editable' => true 3194 ]); 3195 3196 if ($count != count($hostids)) { 3197 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3198 } 3199 } 3200 } 3201 3202 /** 3203 * Checks if the current user has access to the given users. 3204 * 3205 * @throws APIException if the user doesn't have write permissions for the given users 3206 * 3207 * @param array $userids 3208 * @param string $error 3209 */ 3210 protected function checkUsersPermissions(array $userids, $error) { 3211 if ($userids) { 3212 $userids = array_unique($userids); 3213 3214 $count = API::User()->get([ 3215 'countOutput' => true, 3216 'userids' => $userids 3217 ]); 3218 3219 if ($count != count($userids)) { 3220 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3221 } 3222 } 3223 } 3224 3225 /** 3226 * Checks if the current user has access to the given user groups. 3227 * 3228 * @throws APIException if the user doesn't have write permissions for the given user groups 3229 * 3230 * @param array $usrgrpids 3231 * @param string $error 3232 */ 3233 protected function checkUserGroupsPermissions(array $usrgrpids, $error) { 3234 if ($usrgrpids) { 3235 $usrgrpids = array_unique($usrgrpids); 3236 3237 $count = API::UserGroup()->get([ 3238 'countOutput' => true, 3239 'usrgrpids' => $usrgrpids 3240 ]); 3241 3242 if ($count != count($usrgrpids)) { 3243 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3244 } 3245 } 3246 } 3247 3248 /** 3249 * Checks if the current user has access to the given templates. 3250 * 3251 * @throws APIException if the user doesn't have write permissions for the given templates 3252 * 3253 * @param array $templateids 3254 * @param string $error 3255 */ 3256 protected function checkTemplatesPermissions(array $templateids, $error) { 3257 if ($templateids) { 3258 $templateids = array_unique($templateids); 3259 3260 $count = API::Template()->get([ 3261 'countOutput' => true, 3262 'templateids' => $templateids, 3263 'editable' => true 3264 ]); 3265 3266 if ($count != count($templateids)) { 3267 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3268 } 3269 } 3270 } 3271 3272 /** 3273 * Checks if the current user has access to the given triggers. 3274 * 3275 * @throws APIException if the user doesn't have write permissions for the given triggers 3276 * 3277 * @param array $triggerids 3278 * @param string $error 3279 */ 3280 protected function checkTriggersPermissions(array $triggerids, $error) { 3281 if ($triggerids) { 3282 $triggerids = array_unique($triggerids); 3283 3284 $count = API::Trigger()->get([ 3285 'countOutput' => true, 3286 'triggerids' => $triggerids, 3287 'editable' => true 3288 ]); 3289 3290 if ($count != count($triggerids)) { 3291 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3292 } 3293 } 3294 } 3295 3296 /** 3297 * Checks if the current user has access to the given discovery rules. 3298 * 3299 * @throws APIException if the user doesn't have write permissions for the given discovery rules 3300 * 3301 * @param array $druleids 3302 * @param string $error 3303 */ 3304 protected function checkDRulesPermissions(array $druleids, $error) { 3305 if ($druleids) { 3306 $druleids = array_unique($druleids); 3307 3308 $count = API::DRule()->get([ 3309 'countOutput' => true, 3310 'druleids' => $druleids, 3311 'editable' => true 3312 ]); 3313 3314 if ($count != count($druleids)) { 3315 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3316 } 3317 } 3318 } 3319 3320 /** 3321 * Checks if the current user has access to the given discovery checks. 3322 * 3323 * @throws APIException if the user doesn't have write permissions for the given discovery checks 3324 * 3325 * @param array $dcheckids 3326 * @param string $error 3327 */ 3328 protected function checkDChecksPermissions(array $dcheckids, $error) { 3329 if ($dcheckids) { 3330 $dcheckids = array_unique($dcheckids); 3331 3332 $count = API::DCheck()->get([ 3333 'countOutput' => true, 3334 'dcheckids' => $dcheckids, 3335 'editable' => true 3336 ]); 3337 3338 if ($count != count($dcheckids)) { 3339 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3340 } 3341 } 3342 } 3343 3344 /** 3345 * Checks if the current user has access to the given proxies. 3346 * 3347 * @throws APIException if the user doesn't have write permissions for the given proxies 3348 * 3349 * @param array $proxyids 3350 * @param string $error 3351 */ 3352 protected function checkProxiesPermissions(array $proxyids, $error) { 3353 if ($proxyids) { 3354 $proxyids = array_unique($proxyids); 3355 3356 $count = API::Proxy()->get([ 3357 'countOutput' => true, 3358 'proxyids' => $proxyids, 3359 'editable' => true 3360 ]); 3361 3362 if ($count != count($proxyids)) { 3363 self::exception(ZBX_API_ERROR_PERMISSIONS, $error); 3364 } 3365 } 3366 } 3367} 3368