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