['min_user_type' => USER_TYPE_ZABBIX_USER], 'create' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN], 'update' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN], 'delete' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN] ]; protected $tableName = 'drules'; protected $tableAlias = 'dr'; protected $sortColumns = ['druleid', 'name']; /** * Get drule data. * * @param array $options * * @return array */ public function get(array $options = []) { $result = []; $sqlParts = [ 'select' => ['drules' => 'dr.druleid'], 'from' => ['drules' => 'drules dr'], 'where' => [], 'group' => [], 'order' => [], 'limit' => null ]; $defOptions = [ 'druleids' => null, 'dhostids' => null, 'dserviceids' => null, 'editable' => false, 'selectDHosts' => null, 'selectDChecks' => null, // filter 'filter' => null, 'search' => null, 'searchByAny' => null, 'startSearch' => false, 'excludeSearch' => false, 'searchWildcardsEnabled' => null, // output 'output' => API_OUTPUT_EXTEND, 'countOutput' => false, 'groupCount' => false, 'preservekeys' => false, 'sortfield' => '', 'sortorder' => '', 'limit' => null, 'limitSelects' => null ]; $options = zbx_array_merge($defOptions, $options); if (self::$userData['type'] < USER_TYPE_ZABBIX_ADMIN) { return []; } // druleids if (!is_null($options['druleids'])) { zbx_value2array($options['druleids']); $sqlParts['where']['druleid'] = dbConditionInt('dr.druleid', $options['druleids']); } // dhostids if (!is_null($options['dhostids'])) { zbx_value2array($options['dhostids']); $sqlParts['from']['dhosts'] = 'dhosts dh'; $sqlParts['where']['dhostid'] = dbConditionInt('dh.dhostid', $options['dhostids']); $sqlParts['where']['dhdr'] = 'dh.druleid=dr.druleid'; if ($options['groupCount']) { $sqlParts['group']['dhostid'] = 'dh.dhostid'; } } // dserviceids if (!is_null($options['dserviceids'])) { zbx_value2array($options['dserviceids']); $sqlParts['from']['dhosts'] = 'dhosts dh'; $sqlParts['from']['dservices'] = 'dservices ds'; $sqlParts['where']['dserviceid'] = dbConditionInt('ds.dserviceid', $options['dserviceids']); $sqlParts['where']['dhdr'] = 'dh.druleid=dr.druleid'; $sqlParts['where']['dhds'] = 'dh.dhostid=ds.dhostid'; if ($options['groupCount']) { $sqlParts['group']['dserviceid'] = 'ds.dserviceid'; } } // search if (!is_null($options['search'])) { zbx_db_search('drules dr', $options, $sqlParts); } // filter if (is_array($options['filter'])) { if (array_key_exists('delay', $options['filter']) && $options['filter']['delay'] !== null) { $options['filter']['delay'] = getTimeUnitFilters($options['filter']['delay']); } $this->dbFilter('drules dr', $options, $sqlParts); } // search if (is_array($options['search'])) { zbx_db_search('drules dr', $options, $sqlParts); } // limit if (zbx_ctype_digit($options['limit']) && $options['limit']) { $sqlParts['limit'] = $options['limit']; } //------------ $sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); $sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); $dbRes = DBselect(self::createSelectQueryFromParts($sqlParts), $sqlParts['limit']); while ($drule = DBfetch($dbRes)) { if ($options['countOutput']) { if ($options['groupCount']) { $result[] = $drule; } else { $result = $drule['rowscount']; } } else { $result[$drule['druleid']] = $drule; } } if ($options['countOutput']) { return $result; } if ($result) { $result = $this->addRelatedObjects($options, $result); } // removing keys (hash -> array) if (!$options['preservekeys']) { $result = zbx_cleanHashes($result); } return $result; } /** * Validate the input parameters for create() method. * * @param array $drules Discovery rules data. * * @throws APIException if the input is invalid. */ protected function validateCreate(array $drules) { // Check permissions. if (self::$userData['type'] == USER_TYPE_ZABBIX_USER) { self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!')); } if (!$drules) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); } $proxyids = []; $ip_range_parser = new CIPRangeParser(['v6' => ZBX_HAVE_IPV6, 'dns' => false, 'max_ipv4_cidr' => 30]); foreach ($drules as $drule) { if (!array_key_exists('name', $drule)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Field "%1$s" is mandatory.', 'name')); } elseif (is_array($drule['name'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } elseif ($drule['name'] === '' || $drule['name'] === null || $drule['name'] === false) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'name', _('cannot be empty')) ); } if (!array_key_exists('iprange', $drule) || $drule['iprange'] === '') { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'iprange', _('cannot be empty')) ); } elseif (!$ip_range_parser->parse($drule['iprange'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'iprange', $ip_range_parser->getError()) ); } elseif (bccomp($ip_range_parser->getMaxIPCount(), ZBX_DISCOVERER_IPRANGE_LIMIT) > 0) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'iprange', _s('IP range "%1$s" exceeds "%2$s" address limit', $ip_range_parser->getMaxIPRange(), ZBX_DISCOVERER_IPRANGE_LIMIT ) ) ); } if (array_key_exists('delay', $drule) && !validateTimeUnit($drule['delay'], 1, SEC_PER_WEEK, false, $error, ['usermacros' => true])) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'delay', $error) ); } if (array_key_exists('status', $drule) && $drule['status'] != DRULE_STATUS_DISABLED && $drule['status'] != DRULE_STATUS_ACTIVE) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $drule['status'], 'status') ); } if (array_key_exists('concurrency_max', $drule) && ($drule['concurrency_max'] < ZBX_DISCOVERY_CHECKS_UNLIMITED || $drule['concurrency_max'] > ZBX_DISCOVERY_CHECKS_MAX)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $drule['concurrency_max'], 'concurrency_max') ); } if (array_key_exists('proxyid', $drule)) { if (!zbx_is_int($drule['proxyid'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $drule['proxyid'], 'proxyid') ); } if ($drule['proxyid'] > 0) { $proxyids[] = $drule['proxyid']; } } if (array_key_exists('dchecks', $drule) && $drule['dchecks']) { $this->validateDChecks($drule['dchecks']); } else { self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot save discovery rule without checks.')); } } // Check drule name duplicates in input data. $duplicate = CArrayHelper::findDuplicate($drules, 'name'); if ($duplicate) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Discovery rule "%1$s" already exists.', $duplicate['name']) ); } // Check drule name duplicates in DB. $db_duplicate = $this->get([ 'output' => ['name'], 'filter' => ['name' => zbx_objectValues($drules, 'name')], 'limit' => 1 ]); if ($db_duplicate) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Discovery rule "%1$s" already exists.', $db_duplicate[0]['name']) ); } // Check proxy IDs. if ($proxyids) { $db_proxies = API::Proxy()->get([ 'output' => ['proxyid'], 'proxyids' => $proxyids, 'preservekeys' => true ]); foreach ($proxyids as $proxyid) { if (!array_key_exists($proxyid, $db_proxies)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $proxyid, 'proxyid') ); } } } } /** * Validate the input parameters for update() method. * * @param array $drules Discovery rules data. * * @throws APIException if the input is invalid. */ protected function validateUpdate(array $drules) { // Check permissions. if (self::$userData['type'] == USER_TYPE_ZABBIX_USER) { self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!')); } if (!$drules) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty input parameter.')); } // Validate given IDs. $this->checkObjectIds($drules, 'druleid', _('Field "%1$s" is mandatory.'), _s('Incorrect value for field "%1$s": %2$s.', 'druleid', _('cannot be empty')), _s('Incorrect value for field "%1$s": %2$s.', 'druleid', _('a numeric value is expected')) ); $db_drules = $this->get([ 'output' => ['druleid', 'name'], 'druleids' => zbx_objectValues($drules, 'druleid'), 'preservekeys' => true ]); $drule_names_changed = []; $proxyids = []; $ip_range_parser = new CIPRangeParser(['v6' => ZBX_HAVE_IPV6, 'dns' => false, 'max_ipv4_cidr' => 30]); foreach ($drules as $drule) { if (!array_key_exists($drule['druleid'], $db_drules)) { self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!')); } if (array_key_exists('name', $drule)) { if (is_array($drule['name'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } elseif ($drule['name'] === '' || $drule['name'] === null || $drule['name'] === false) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'name', _('cannot be empty')) ); } if ($db_drules[$drule['druleid']]['name'] !== $drule['name']) { $drule_names_changed[] = $drule; } } if (array_key_exists('iprange', $drule)) { if ($drule['iprange'] === '') { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'iprange', _('cannot be empty')) ); } elseif (!$ip_range_parser->parse($drule['iprange'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'iprange', $ip_range_parser->getError()) ); } elseif (bccomp($ip_range_parser->getMaxIPCount(), ZBX_DISCOVERER_IPRANGE_LIMIT) > 0) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'iprange', _s('IP range "%1$s" exceeds "%2$s" address limit', $ip_range_parser->getMaxIPRange(), ZBX_DISCOVERER_IPRANGE_LIMIT ) ) ); } } if (array_key_exists('delay', $drule) && !validateTimeUnit($drule['delay'], 1, SEC_PER_WEEK, false, $error, ['usermacros' => true])) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'delay', $error) ); } if (array_key_exists('status', $drule) && $drule['status'] != DRULE_STATUS_DISABLED && $drule['status'] != DRULE_STATUS_ACTIVE) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $drule['status'], 'status') ); } if (array_key_exists('concurrency_max', $drule) && ($drule['concurrency_max'] < ZBX_DISCOVERY_CHECKS_UNLIMITED || $drule['concurrency_max'] > ZBX_DISCOVERY_CHECKS_MAX)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $drule['concurrency_max'], 'concurrency_max') ); } if (array_key_exists('proxyid', $drule)) { if (!zbx_is_int($drule['proxyid'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $drule['proxyid'], 'proxyid') ); } if ($drule['proxyid'] > 0) { $proxyids[] = $drule['proxyid']; } } if (array_key_exists('dchecks', $drule)) { if ($drule['dchecks']) { $this->validateDChecks($drule['dchecks']); } else { self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot save discovery rule without checks.')); } } } if ($drule_names_changed) { // Check drule name duplicates in input data. $duplicate = CArrayHelper::findDuplicate($drule_names_changed, 'name'); if ($duplicate) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Discovery rule "%1$s" already exists.', $duplicate['name']) ); } // Check drule name duplicates in DB. $db_duplicate = $this->get([ 'output' => ['name'], 'filter' => ['name' => zbx_objectValues($drule_names_changed, 'name')], 'limit' => 1 ]); if ($db_duplicate) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Discovery rule "%1$s" already exists.', $db_duplicate[0]['name']) ); } } // Check proxy IDs. if ($proxyids) { $db_proxies = API::Proxy()->get([ 'output' => ['proxyid'], 'proxyids' => $proxyids, 'preservekeys' => true ]); foreach ($proxyids as $proxyid) { if (!array_key_exists($proxyid, $db_proxies)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $proxyid, 'proxyid') ); } } } } /** * Validate discovery checks. * * @param array $dchecks */ protected function validateDChecks(array $dchecks) { $uniq = 0; $item_key_parser = new CItemKey(); $source_values = [ 'name_source' => [ZBX_DISCOVERY_UNSPEC, ZBX_DISCOVERY_DNS, ZBX_DISCOVERY_IP, ZBX_DISCOVERY_VALUE], 'host_source' => [ZBX_DISCOVERY_DNS, ZBX_DISCOVERY_IP, ZBX_DISCOVERY_VALUE] ]; if (!is_array($dchecks)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'dchecks', _('an array is expected')) ); } foreach ($dchecks as $dcnum => $dcheck) { if (!is_array($dcheck)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'dchecks', _('an array is expected')) ); } if (array_key_exists('uniq', $dcheck) && ($dcheck['uniq'] == 1)) { if (!in_array($dcheck['type'], [SVC_AGENT, SVC_SNMPv1, SVC_SNMPv2c, SVC_SNMPv3])) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Only Zabbix agent, SNMPv1, SNMPv2 and SNMPv3 checks can be made unique.') ); } $uniq++; } if (array_key_exists('ports', $dcheck) && !validate_port_list($dcheck['ports'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect port range.')); } foreach ($source_values as $field => $values) { if (!array_key_exists($field, $dcheck)) { continue; } if (!in_array($dcheck['type'], [SVC_AGENT, SVC_SNMPv1, SVC_SNMPv2c, SVC_SNMPv3]) && $dcheck[$field] == ZBX_DISCOVERY_VALUE) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', $field, $dcheck[$field]) ); } if (!in_array($dcheck[$field], $values)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', $field, $dcheck[$field]) ); } // Only one check can be equal ZBX_DISCOVERY_VALUE for 'host_source' and 'name_source' fields. if ($dcheck[$field] == ZBX_DISCOVERY_VALUE) { array_pop($source_values[$field]); } } $dcheck_types = [SVC_SSH, SVC_LDAP, SVC_SMTP, SVC_FTP, SVC_HTTP, SVC_POP, SVC_NNTP, SVC_IMAP, SVC_TCP, SVC_AGENT, SVC_SNMPv1, SVC_SNMPv2c, SVC_ICMPPING, SVC_SNMPv3, SVC_HTTPS, SVC_TELNET ]; if (!array_key_exists('type', $dcheck)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Field "%1$s" is mandatory.', 'type')); } elseif (!is_numeric($dcheck['type']) || !in_array($dcheck['type'], $dcheck_types)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $dcheck['type'], 'type') ); } switch ($dcheck['type']) { case SVC_AGENT: if (!array_key_exists('key_', $dcheck)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Field "%1$s" is mandatory.', 'key_')); } if (is_array($dcheck['key_'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } if ($dcheck['key_'] === '' || $dcheck['key_'] === null || $dcheck['key_'] === false) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'key_', _('cannot be empty')) ); } $length = mb_strlen($dcheck['key_']); $max_length = DB::getFieldLength('dchecks', 'key_'); if ($length > $max_length) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'key_', _s('%1$d characters exceeds maximum length of %2$d characters', $length, $max_length) ) ); } if ($item_key_parser->parse($dcheck['key_']) != CParser::PARSE_SUCCESS) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid key "%1$s": %2$s.', $dcheck['key_'], $item_key_parser->getError()) ); } break; case SVC_SNMPv1: // break; is not missing here case SVC_SNMPv2c: if (!array_key_exists('snmp_community', $dcheck) || $dcheck['snmp_community'] === null || $dcheck['snmp_community'] === false || $dcheck['snmp_community'] === '') { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect SNMP community.')); } // break; is not missing here case SVC_SNMPv3: if (!array_key_exists('key_', $dcheck) || $dcheck['key_'] === null || $dcheck['key_'] === false || $dcheck['key_'] === '') { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect SNMP OID.')); } break; case SVC_ICMPPING: if (array_key_exists('allow_redirect', $dcheck) && $dcheck['allow_redirect'] != 1 && $dcheck['allow_redirect'] != 0) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $dcheck['allow_redirect'], 'allow_redirect') ); } break; } // validate snmpv3 fields if (array_key_exists('snmpv3_securitylevel', $dcheck) && $dcheck['snmpv3_securitylevel'] != ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV) { // snmpv3 authprotocol if ($dcheck['snmpv3_securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV || $dcheck['snmpv3_securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV) { if (!array_key_exists('snmpv3_authprotocol', $dcheck) || !array_key_exists($dcheck['snmpv3_authprotocol'], getSnmpV3AuthProtocols())) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $dcheck['snmpv3_authprotocol'], 'snmpv3_authprotocol' ) ); } } // snmpv3 privprotocol if ($dcheck['snmpv3_securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV) { if (!array_key_exists('snmpv3_privprotocol', $dcheck) || !array_key_exists($dcheck['snmpv3_privprotocol'], getSnmpV3PrivProtocols())) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.', $dcheck['snmpv3_privprotocol'], 'snmpv3_privprotocol' ) ); } } } $this->validateDuplicateChecks($dchecks); } if ($uniq > 1) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one check can be unique.')); } } protected function validateDuplicateChecks(array $dchecks) { $default_values = DB::getDefaults('dchecks'); foreach ($dchecks as &$dcheck) { // set default values for snmpv3 fields if (!array_key_exists('snmpv3_securitylevel', $dcheck) || $dcheck['snmpv3_securitylevel'] === null) { $dcheck['snmpv3_securitylevel'] = ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV; } switch ($dcheck['snmpv3_securitylevel']) { case ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV: $dcheck['snmpv3_authprotocol'] = ITEM_SNMPV3_AUTHPROTOCOL_MD5; $dcheck['snmpv3_privprotocol'] = ITEM_SNMPV3_PRIVPROTOCOL_DES; $dcheck['snmpv3_authpassphrase'] = ''; $dcheck['snmpv3_privpassphrase'] = ''; break; case ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV: $dcheck['snmpv3_privprotocol'] = ITEM_SNMPV3_PRIVPROTOCOL_DES; $dcheck['snmpv3_privpassphrase'] = ''; break; } $dcheck += $default_values; unset($dcheck['dcheckid'], $dcheck['uniq']); } unset($dcheck); while ($current = array_pop($dchecks)) { foreach ($dchecks as $dcheck) { $equal = true; foreach ($dcheck as $field => $value) { if (array_key_exists($field, $current) && (strcmp($value, $current[$field]) !== 0)) { $equal = false; break; } } if ($equal) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Checks should be unique.')); } } } } /** * Create new discovery rules. * * @param array( * name => string, * proxyid => int, * iprange => string, * delay => string, * status => int, * concurrency_max => int, * dchecks => array( * array( * type => int, * ports => string, * key_ => string, * snmp_community => string, * snmpv3_securityname => string, * snmpv3_securitylevel => int, * snmpv3_authpassphrase => string, * snmpv3_privpassphrase => string, * uniq => int, * allow_redirect => int, * ), ... * ) * ) $drules * * @return array */ public function create(array $drules) { $drules = zbx_toArray($drules); $this->validateCreate($drules); $druleids = DB::insert('drules', $drules); array_walk($drules, function (&$drule, $index) use ($druleids) { $drule['druleid'] = $druleids[$index]; }); $this->addAuditBulk(CAudit::ACTION_ADD, CAudit::RESOURCE_DISCOVERY_RULE, $drules); $create_dchecks = []; foreach ($drules as $dnum => $drule) { foreach ($drule['dchecks'] as $dcheck) { $dcheck['druleid'] = $druleids[$dnum]; $create_dchecks[] = $dcheck; } } DB::insert('dchecks', $create_dchecks); return ['druleids' => $druleids]; } /** * Update existing drules. * * @param array( * druleid => int, * name => string, * proxyid => int, * iprange => string, * delay => string, * status => int, * concurrency_max => int, * dchecks => array( * array( * dcheckid => int, * type => int, * ports => string, * key_ => string, * snmp_community => string, * snmpv3_securityname => string, * snmpv3_securitylevel => int, * snmpv3_authpassphrase => string, * snmpv3_privpassphrase => string, * uniq => int, * allow_redirect => int, * ), ... * ) * ) $drules * * @return array */ public function update(array $drules) { $drules = zbx_toArray($drules); $druleids = zbx_objectValues($drules, 'druleid'); $this->validateUpdate($drules); $db_drules = API::DRule()->get([ 'output' => ['druleid', 'proxyid', 'name', 'iprange', 'delay', 'status', 'concurrency_max'], 'selectDChecks' => ['dcheckid', 'druleid', 'type', 'key_', 'snmp_community', 'ports', 'snmpv3_securityname', 'snmpv3_securitylevel', 'snmpv3_authpassphrase', 'snmpv3_privpassphrase', 'uniq', 'snmpv3_authprotocol', 'snmpv3_privprotocol', 'snmpv3_contextname', 'host_source', 'name_source', 'allow_redirect' ], 'druleids' => $druleids, 'editable' => true, 'preservekeys' => true ]); $default_values = DB::getDefaults('dchecks'); foreach ($drules as $drule) { $db_drule = $db_drules[$drule['druleid']]; // Update drule if it's modified. if (DB::recordModified('drules', $db_drule, $drule)) { DB::updateByPk('drules', $drule['druleid'], $drule); } if (array_key_exists('dchecks', $drule)) { // Update dchecks. $db_dchecks = $db_drule['dchecks']; $new_dchecks = []; $old_dchecks = []; foreach ($drule['dchecks'] as $check) { $check['druleid'] = $drule['druleid']; if (!isset($check['dcheckid'])) { $new_dchecks[] = array_merge($default_values, $check); } else { $old_dchecks[] = $check; } } $del_dcheckids = array_diff( zbx_objectValues($db_dchecks, 'dcheckid'), zbx_objectValues($old_dchecks, 'dcheckid') ); if ($del_dcheckids) { $this->deleteActionConditions($del_dcheckids); } DB::replace('dchecks', $db_dchecks, array_merge($old_dchecks, $new_dchecks)); } } $this->addAuditBulk(CAudit::ACTION_UPDATE, CAudit::RESOURCE_DISCOVERY_RULE, $drules, $db_drules); return ['druleids' => $druleids]; } /** * @param array $druleids * * @return array */ public function delete(array $druleids) { $api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true]; if (!CApiInputValidator::validate($api_input_rules, $druleids, '/', $error)) { self::exception(ZBX_API_ERROR_PARAMETERS, $error); } $db_drules = $this->get([ 'output' => ['druleid', 'name'], 'druleids' => $druleids, 'editable' => true, 'preservekeys' => true ]); foreach ($druleids as $druleid) { if (!array_key_exists($druleid, $db_drules)) { self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!') ); } } // Check if discovery rules are used in actions. $db_actions = DBselect( 'SELECT a.name,c.value'. ' FROM actions a,conditions c'. ' WHERE a.actionid=c.actionid'. ' AND c.conditiontype='.CONDITION_TYPE_DRULE. ' AND '.dbConditionString('c.value', $druleids), 1 ); if ($db_action = DBfetch($db_actions)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Discovery rule "%1$s" is used in "%2$s" action.', $db_drules[$db_action['value']]['name'], $db_action['name'] )); } // Check if discovery checks are used in actions. $db_actions = DBselect( 'SELECT a.name,dc.druleid'. ' FROM actions a,conditions c,dchecks dc'. ' WHERE a.actionid=c.actionid'. ' AND '.zbx_dbcast_2bigint('c.value').'=dc.dcheckid'. ' AND c.conditiontype='.CONDITION_TYPE_DCHECK. ' AND '.dbConditionString('dc.druleid', $druleids), 1 ); if ($db_action = DBfetch($db_actions)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Discovery rule "%1$s" is used in "%2$s" action.', $db_drules[$db_action['druleid']]['name'], $db_action['name'] )); } DB::delete('dchecks', ['druleid' => $druleids]); DB::delete('drules', ['druleid' => $druleids]); $this->addAuditBulk(CAudit::ACTION_DELETE, CAudit::RESOURCE_DISCOVERY_RULE, $db_drules); return ['druleids' => $druleids]; } /** * Delete related action conditions. * * @param array $dCheckIds */ protected function deleteActionConditions(array $dCheckIds) { $actionIds = []; // conditions $dbActions = DBselect( 'SELECT DISTINCT c.actionid'. ' FROM conditions c'. ' WHERE c.conditiontype='.CONDITION_TYPE_DCHECK. ' AND '.dbConditionString('c.value', $dCheckIds). ' ORDER BY c.actionid' ); while ($dbAction = DBfetch($dbActions)) { $actionIds[] = $dbAction['actionid']; } // disabling actions with deleted conditions if ($actionIds) { DB::update('actions', [ 'values' => ['status' => ACTION_STATUS_DISABLED], 'where' => ['actionid' => $actionIds] ]); DB::delete('conditions', [ 'conditiontype' => CONDITION_TYPE_DCHECK, 'value' => $dCheckIds ]); } } protected function addRelatedObjects(array $options, array $result) { $result = parent::addRelatedObjects($options, $result); $druleids = array_keys($result); // Adding Discovery Checks if (!is_null($options['selectDChecks'])) { if ($options['selectDChecks'] != API_OUTPUT_COUNT) { $dchecks = []; $relationMap = $this->createRelationMap($result, 'druleid', 'dcheckid', 'dchecks'); $related_ids = $relationMap->getRelatedIds(); if ($related_ids) { $dchecks = API::DCheck()->get([ 'output' => $options['selectDChecks'], 'dcheckids' => $related_ids, 'nopermissions' => true, 'preservekeys' => true ]); if (!is_null($options['limitSelects'])) { order_result($dchecks, 'dcheckid'); } } $result = $relationMap->mapMany($result, $dchecks, 'dchecks', $options['limitSelects']); } else { $dchecks = API::DCheck()->get([ 'druleids' => $druleids, 'nopermissions' => true, 'countOutput' => true, 'groupCount' => true ]); $dchecks = zbx_toHash($dchecks, 'druleid'); foreach ($result as $druleid => $drule) { $result[$druleid]['dchecks'] = array_key_exists($druleid, $dchecks) ? $dchecks[$druleid]['rowscount'] : '0'; } } } // Adding Discovery Hosts if (!is_null($options['selectDHosts'])) { if ($options['selectDHosts'] != API_OUTPUT_COUNT) { $dhosts = []; $relationMap = $this->createRelationMap($result, 'druleid', 'dhostid', 'dhosts'); $related_ids = $relationMap->getRelatedIds(); if ($related_ids) { $dhosts = API::DHost()->get([ 'output' => $options['selectDHosts'], 'dhostids' => $related_ids, 'preservekeys' => true ]); if (!is_null($options['limitSelects'])) { order_result($dhosts, 'dhostid'); } } $result = $relationMap->mapMany($result, $dhosts, 'dhosts', $options['limitSelects']); } else { $dhosts = API::DHost()->get([ 'druleids' => $druleids, 'countOutput' => true, 'groupCount' => true ]); $dhosts = zbx_toHash($dhosts, 'druleid'); foreach ($result as $druleid => $drule) { $result[$druleid]['dhosts'] = array_key_exists($druleid, $dhosts) ? $dhosts[$druleid]['rowscount'] : '0'; } } } return $result; } }