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