You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1031 lines
34 KiB
1031 lines
34 KiB
<?php
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Class containing methods for operations with correlations.
|
|
*/
|
|
class CCorrelation extends CApiService {
|
|
|
|
public const ACCESS_RULES = [
|
|
'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER],
|
|
'create' => ['min_user_type' => USER_TYPE_SUPER_ADMIN],
|
|
'update' => ['min_user_type' => USER_TYPE_SUPER_ADMIN],
|
|
'delete' => ['min_user_type' => USER_TYPE_SUPER_ADMIN]
|
|
];
|
|
|
|
protected $tableName = 'correlation';
|
|
protected $tableAlias = 'c';
|
|
protected $sortColumns = ['correlationid', 'name', 'status'];
|
|
|
|
/**
|
|
* Set correlation default options in addition to global options.
|
|
*/
|
|
public function __construct() {
|
|
parent::__construct();
|
|
|
|
$this->getOptions = array_merge($this->getOptions, [
|
|
'selectFilter' => null,
|
|
'selectOperations' => null,
|
|
'correlationids' => null,
|
|
'editable' => false,
|
|
'sortfield' => '',
|
|
'sortorder' => ''
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get correlation data.
|
|
*
|
|
* @param array $options
|
|
*
|
|
* @return array|string
|
|
*/
|
|
public function get($options = []) {
|
|
$options = zbx_array_merge($this->getOptions, $options);
|
|
|
|
if ($options['editable'] && self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
return ($options['countOutput'] && !$options['groupCount']) ? '0' : [];
|
|
}
|
|
|
|
$res = DBselect($this->createSelectQuery($this->tableName(), $options), $options['limit']);
|
|
|
|
$result = [];
|
|
while ($row = DBfetch($res)) {
|
|
if ($options['countOutput']) {
|
|
if ($options['groupCount']) {
|
|
$result[] = $row;
|
|
}
|
|
else {
|
|
$result = $row['rowscount'];
|
|
}
|
|
}
|
|
else {
|
|
$result[$row[$this->pk()]] = $row;
|
|
}
|
|
}
|
|
|
|
if ($options['countOutput']) {
|
|
return $result;
|
|
}
|
|
|
|
if ($result) {
|
|
$result = $this->addRelatedObjects($options, $result);
|
|
|
|
foreach ($result as &$correlation) {
|
|
// Unset the fields that are returned in the filter.
|
|
unset($correlation['formula'], $correlation['evaltype']);
|
|
|
|
if ($options['selectFilter'] !== null) {
|
|
$filter = $this->unsetExtraFields(
|
|
[$correlation['filter']],
|
|
['conditions', 'formula', 'evaltype'],
|
|
$options['selectFilter']
|
|
);
|
|
$filter = reset($filter);
|
|
|
|
if (array_key_exists('conditions', $filter)) {
|
|
foreach ($filter['conditions'] as &$condition) {
|
|
unset($condition['correlationid'], $condition['corr_conditionid']);
|
|
}
|
|
unset($condition);
|
|
}
|
|
|
|
$correlation['filter'] = $filter;
|
|
}
|
|
}
|
|
unset($correlation);
|
|
}
|
|
|
|
// removing keys (hash -> array)
|
|
if (!$options['preservekeys']) {
|
|
$result = zbx_cleanHashes($result);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param array $correlations
|
|
*
|
|
* @return array
|
|
*/
|
|
public function create($correlations) {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'correlation', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
self::validateCreate($correlations);
|
|
|
|
$ins_correlations = [];
|
|
|
|
foreach ($correlations as $correlation) {
|
|
$ins_correlations[] = $correlation + ['evaltype' => $correlation['filter']['evaltype']];
|
|
}
|
|
|
|
// Insert correlations into DB, get back array with new correlation IDs.
|
|
$correlationids = DB::insert('correlation', $ins_correlations);
|
|
|
|
foreach ($correlations as $index => &$correlation) {
|
|
$correlation['correlationid'] = $correlationids[$index];
|
|
}
|
|
unset($correlation);
|
|
|
|
self::updateConditions($correlations, __FUNCTION__);
|
|
self::updateOperations($correlations, __FUNCTION__);
|
|
|
|
self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_CORRELATION, $correlations);
|
|
|
|
return ['correlationids' => $correlationids];
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param array $db_conditions
|
|
* @param array $condition
|
|
*
|
|
* @return array|null
|
|
*/
|
|
private static function getDbCondition(array $db_conditions, array $condition): ?array {
|
|
foreach ($db_conditions as $db_condition) {
|
|
if ($condition['type'] != $db_condition['type']) {
|
|
continue;
|
|
}
|
|
|
|
switch ($condition['type']) {
|
|
case ZBX_CORR_CONDITION_OLD_EVENT_TAG:
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_TAG:
|
|
if ($condition['tag'] === $db_condition['tag']) {
|
|
return $db_condition;
|
|
}
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP:
|
|
if ($condition['operator'] == $db_condition['operator']
|
|
&& bccomp($condition['groupid'], $db_condition['groupid']) == 0) {
|
|
return $db_condition;
|
|
}
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_EVENT_TAG_PAIR:
|
|
if ($condition['oldtag'] === $db_condition['oldtag']
|
|
&& $condition['newtag'] === $db_condition['newtag']) {
|
|
return $db_condition;
|
|
}
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE:
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE:
|
|
if ($condition['operator'] == $db_condition['operator']
|
|
&& $condition['tag'] === $db_condition['tag']
|
|
&& $condition['value'] === $db_condition['value']) {
|
|
return $db_condition;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param array $correlations
|
|
* @param string $method
|
|
* @param array|null $db_correlations
|
|
*/
|
|
private static function updateConditions(array &$correlations, string $method,
|
|
array $db_correlations = null): void {
|
|
$ins_conditions = [];
|
|
$del_corr_conditionids = [];
|
|
|
|
foreach ($correlations as &$correlation) {
|
|
if (!array_key_exists('filter', $correlation)) {
|
|
continue;
|
|
}
|
|
|
|
$db_conditions = ($method === 'update')
|
|
? $db_correlations[$correlation['correlationid']]['filter']['conditions']
|
|
: [];
|
|
|
|
foreach ($correlation['filter']['conditions'] as &$condition) {
|
|
$db_condition = self::getDbCondition($db_conditions, $condition);
|
|
|
|
if ($db_condition !== null) {
|
|
$condition['corr_conditionid'] = $db_condition['corr_conditionid'];
|
|
unset($db_conditions[$db_condition['corr_conditionid']]);
|
|
}
|
|
else {
|
|
$ins_conditions[] = ['correlationid' => $correlation['correlationid']] + $condition;
|
|
}
|
|
}
|
|
unset($condition);
|
|
|
|
$del_corr_conditionids = array_merge($del_corr_conditionids, array_keys($db_conditions));
|
|
}
|
|
unset($correlation);
|
|
|
|
if ($ins_conditions) {
|
|
$conditionids = DB::insert('corr_condition', $ins_conditions);
|
|
}
|
|
|
|
if ($del_corr_conditionids) {
|
|
DB::delete('corr_condition_tag', ['corr_conditionid' => $del_corr_conditionids]);
|
|
DB::delete('corr_condition_group', ['corr_conditionid' => $del_corr_conditionids]);
|
|
DB::delete('corr_condition_tagpair', ['corr_conditionid' => $del_corr_conditionids]);
|
|
DB::delete('corr_condition_tagvalue', ['corr_conditionid' => $del_corr_conditionids]);
|
|
DB::delete('corr_condition', ['corr_conditionid' => $del_corr_conditionids]);
|
|
}
|
|
|
|
$ins_condition_tags = [];
|
|
$ins_condition_groups = [];
|
|
$ins_condition_tagpairs = [];
|
|
$ins_condition_tagvalues = [];
|
|
$upd_correlations = [];
|
|
|
|
foreach ($correlations as &$correlation) {
|
|
if (!array_key_exists('filter', $correlation)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($correlation['filter']['conditions'] as &$condition) {
|
|
if (!array_key_exists('corr_conditionid', $condition)) {
|
|
$condition['corr_conditionid'] = array_shift($conditionids);
|
|
|
|
switch ($condition['type']) {
|
|
case ZBX_CORR_CONDITION_OLD_EVENT_TAG:
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_TAG:
|
|
$ins_condition_tags[] = $condition;
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP:
|
|
$ins_condition_groups[] = $condition;
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_EVENT_TAG_PAIR:
|
|
$ins_condition_tagpairs[] = $condition;
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE:
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE:
|
|
$ins_condition_tagvalues[] = $condition;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
unset($condition);
|
|
|
|
if ($method === 'create' || array_key_exists('filter', $correlation)) {
|
|
if ($correlation['filter']['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) {
|
|
$correlation['filter']['formula'] = CConditionHelper::replaceLetterIds(
|
|
$correlation['filter']['formula'],
|
|
array_column($correlation['filter']['conditions'], 'corr_conditionid', 'formulaid')
|
|
);
|
|
}
|
|
else {
|
|
$correlation['filter']['formula'] = '';
|
|
}
|
|
|
|
$db_formula = ($method === 'update')
|
|
? $db_correlations[$correlation['correlationid']]['filter']['formula']
|
|
: '';
|
|
|
|
if ($correlation['filter']['formula'] !== $db_formula) {
|
|
$upd_correlations[] = [
|
|
'values' => ['formula' => $correlation['filter']['formula']],
|
|
'where' => ['correlationid' => $correlation['correlationid']]
|
|
];
|
|
}
|
|
}
|
|
}
|
|
unset($correlation);
|
|
|
|
if ($ins_condition_tags) {
|
|
DB::insert('corr_condition_tag', $ins_condition_tags, false);
|
|
}
|
|
|
|
if ($ins_condition_groups) {
|
|
DB::insert('corr_condition_group', $ins_condition_groups, false);
|
|
}
|
|
|
|
if ($ins_condition_tagpairs) {
|
|
DB::insert('corr_condition_tagpair', $ins_condition_tagpairs, false);
|
|
}
|
|
|
|
if ($ins_condition_tagvalues) {
|
|
DB::insert('corr_condition_tagvalue', $ins_condition_tagvalues, false);
|
|
}
|
|
|
|
if ($upd_correlations) {
|
|
DB::update('correlation', $upd_correlations);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param array $correlations
|
|
* @param string $method
|
|
* @param array|null $db_correlations
|
|
*/
|
|
private static function updateOperations(array &$correlations, string $method,
|
|
array $db_correlations = null): void {
|
|
$ins_operations = [];
|
|
$del_corr_operationids = [];
|
|
|
|
foreach ($correlations as &$correlation) {
|
|
if (!array_key_exists('operations', $correlation)) {
|
|
continue;
|
|
}
|
|
|
|
$db_operations = ($method === 'update')
|
|
? array_column($db_correlations[$correlation['correlationid']]['operations'], null, 'type')
|
|
: [];
|
|
|
|
foreach ($correlation['operations'] as &$operation) {
|
|
if (array_key_exists($operation['type'], $db_operations)) {
|
|
$operation['corr_operationid'] = $db_operations[$operation['type']]['corr_operationid'];
|
|
unset($db_operations[$operation['type']]);
|
|
}
|
|
else {
|
|
$ins_operations[] = ['correlationid' => $correlation['correlationid']] + $operation;
|
|
}
|
|
}
|
|
unset($operation);
|
|
|
|
$del_corr_operationids = array_merge($del_corr_operationids,
|
|
array_column($db_operations, 'corr_operationid')
|
|
);
|
|
}
|
|
unset($correlation);
|
|
|
|
if ($ins_operations) {
|
|
$corr_operationids = DB::insert('corr_operation', $ins_operations);
|
|
}
|
|
|
|
if ($del_corr_operationids) {
|
|
DB::delete('corr_operation', ['corr_operationid' => $del_corr_operationids]);
|
|
}
|
|
|
|
foreach ($correlations as &$correlation) {
|
|
if (!array_key_exists('operations', $correlation)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($correlation['operations'] as &$operation) {
|
|
if (!array_key_exists('corr_operationid', $operation)) {
|
|
$operation['corr_operationid'] = array_shift($corr_operationids);
|
|
}
|
|
}
|
|
unset($operation);
|
|
}
|
|
unset($correlation);
|
|
}
|
|
|
|
/**
|
|
* @param array $correlations
|
|
*
|
|
* @return array
|
|
*/
|
|
public function update(array $correlations): array {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'correlation', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
self::validateUpdate($correlations, $db_correlations);
|
|
|
|
$upd_correlations = [];
|
|
|
|
foreach ($correlations as $correlation) {
|
|
$db_correlation = $db_correlations[$correlation['correlationid']];
|
|
|
|
if (array_key_exists('filter', $correlation)) {
|
|
$correlation['evaltype'] = $correlation['filter']['evaltype'];
|
|
$db_correlation['evaltype'] = $db_correlation['filter']['evaltype'];
|
|
}
|
|
|
|
$upd_correlation = DB::getUpdatedValues('correlation', $correlation, $db_correlation);
|
|
|
|
if ($upd_correlation) {
|
|
$upd_correlations[] = [
|
|
'values' => $upd_correlation,
|
|
'where' => ['correlationid' => $correlation['correlationid']]
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($upd_correlations) {
|
|
DB::update('correlation', $upd_correlations);
|
|
}
|
|
|
|
self::updateConditions($correlations, __FUNCTION__, $db_correlations);
|
|
self::updateOperations($correlations, __FUNCTION__, $db_correlations);
|
|
|
|
self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_CORRELATION, $correlations, $db_correlations);
|
|
|
|
return ['correlationids' => array_column($correlations, 'correlationid')];
|
|
}
|
|
|
|
/**
|
|
* @param array $correlationids
|
|
*
|
|
* @return array
|
|
*/
|
|
public function delete(array $correlationids): array {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'correlation', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
self::validateDelete($correlationids, $db_correlations);
|
|
|
|
DB::delete('correlation', ['correlationid' => $correlationids]);
|
|
|
|
self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_CORRELATION, $db_correlations);
|
|
|
|
return ['correlationids' => $correlationids];
|
|
}
|
|
|
|
/**
|
|
* Validates the input parameters for the delete() method.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $correlationids
|
|
* @param array|null $db_correlations
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
private static function validateDelete(array &$correlationids, array &$db_correlations = null): void {
|
|
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $correlationids, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_correlations = DB::select('correlation', [
|
|
'output' => ['correlationid', 'name'],
|
|
'correlationids' => $correlationids,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_correlations) != count($correlationids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for unique event correlation names.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $correlations
|
|
* @param array|null $db_correlations
|
|
*
|
|
* @throws APIException if event correlation names are not unique.
|
|
*/
|
|
protected static function checkDuplicates(array $correlations, array $db_correlations = null): void {
|
|
$names = [];
|
|
|
|
foreach ($correlations as $correlation) {
|
|
if (!array_key_exists('name', $correlation)) {
|
|
continue;
|
|
}
|
|
|
|
if ($db_correlations === null
|
|
|| $correlation['name'] !== $db_correlations[$correlation['correlationid']]['name']) {
|
|
$names[] = $correlation['name'];
|
|
}
|
|
}
|
|
|
|
if (!$names) {
|
|
return;
|
|
}
|
|
|
|
$duplicates = DB::select('correlation', [
|
|
'output' => ['name'],
|
|
'filter' => ['name' => $names],
|
|
'limit' => 1
|
|
]);
|
|
|
|
if ($duplicates) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Event correlation "%1$s" already exists.',
|
|
$duplicates[0]['name'])
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns validation rules for the filter object.
|
|
*
|
|
* @static
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function getFilterValidationRules(): array {
|
|
$condition_fields = [
|
|
'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_CORR_CONDITION_OLD_EVENT_TAG, ZBX_CORR_CONDITION_NEW_EVENT_TAG, ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP, ZBX_CORR_CONDITION_EVENT_TAG_PAIR, ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE, ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE])],
|
|
'operator' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP], 'type' => API_INT32, 'in' => implode(',', [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL])],
|
|
['if' => ['field' => 'type', 'in' => implode(',', [ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE, ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE])], 'type' => API_INT32, 'in' => implode(',', [CONDITION_OPERATOR_EQUAL, CONDITION_OPERATOR_NOT_EQUAL, CONDITION_OPERATOR_LIKE, CONDITION_OPERATOR_NOT_LIKE])],
|
|
['else' => true, 'type' => API_INT32, 'in' => CONDITION_OPERATOR_EQUAL]
|
|
]],
|
|
'tag' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => implode(',', [ZBX_CORR_CONDITION_OLD_EVENT_TAG, ZBX_CORR_CONDITION_NEW_EVENT_TAG, ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE, ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE])], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('corr_condition_tag', 'tag')],
|
|
['else' => true, 'type' => API_UNEXPECTED]
|
|
]],
|
|
'groupid' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP], 'type' => API_ID, 'flags' => API_REQUIRED],
|
|
['else' => true, 'type' => API_UNEXPECTED]
|
|
]],
|
|
'oldtag' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => ZBX_CORR_CONDITION_EVENT_TAG_PAIR], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('corr_condition_tagpair', 'oldtag')],
|
|
['else' => true, 'type' => API_UNEXPECTED]
|
|
]],
|
|
'newtag' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => ZBX_CORR_CONDITION_EVENT_TAG_PAIR], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('corr_condition_tagpair', 'newtag')],
|
|
['else' => true, 'type' => API_UNEXPECTED]
|
|
]],
|
|
'value' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => implode(',', [ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE, ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE])], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('corr_condition_tagvalue', 'value')],
|
|
['else' => true, 'type' => API_UNEXPECTED]
|
|
]]
|
|
];
|
|
|
|
return ['type' => API_OBJECT, 'fields' => [
|
|
'evaltype' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [CONDITION_EVAL_TYPE_AND_OR, CONDITION_EVAL_TYPE_AND, CONDITION_EVAL_TYPE_OR, CONDITION_EVAL_TYPE_EXPRESSION])],
|
|
'formula' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'evaltype', 'in' => CONDITION_EVAL_TYPE_EXPRESSION], 'type' => API_COND_FORMULA, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('correlation', 'formula')],
|
|
['else' => true, 'type' => API_UNEXPECTED]
|
|
]],
|
|
'conditions' => ['type' => API_MULTIPLE, 'flags' => API_REQUIRED, 'rules' => [
|
|
['if' => ['field' => 'evaltype', 'in' => CONDITION_EVAL_TYPE_EXPRESSION], 'type' => API_OBJECTS, 'flags' => API_NOT_EMPTY, 'uniq' => [['formulaid']], 'fields' => [
|
|
'formulaid' => ['type' => API_COND_FORMULAID, 'flags' => API_REQUIRED]
|
|
] + $condition_fields],
|
|
['else' => true, 'type' => API_OBJECTS, 'flags' => API_NOT_EMPTY, 'fields' => $condition_fields]
|
|
]]
|
|
]];
|
|
}
|
|
|
|
/**
|
|
* Validates the input parameters for the create() method.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $correlations
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
private static function validateCreate(array &$correlations): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('correlation', 'name')],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('correlation', 'description')],
|
|
'status' => ['type' => API_INT32, 'in' => implode(',', [ZBX_CORRELATION_ENABLED, ZBX_CORRELATION_DISABLED])],
|
|
'filter' => self::getFilterValidationRules() + ['flags' => API_REQUIRED],
|
|
'operations' => ['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'uniq' => [['type']], 'fields' => [
|
|
'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_CORR_OPERATION_CLOSE_OLD, ZBX_CORR_OPERATION_CLOSE_NEW])]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $correlations, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
self::checkDuplicates($correlations);
|
|
self::validateFormula($correlations);
|
|
self::checkHostGroups($correlations);
|
|
}
|
|
|
|
/**
|
|
* Check host group permissions.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $correlations
|
|
*/
|
|
private static function checkHostGroups(array $correlations): void {
|
|
$groupids = [];
|
|
|
|
foreach ($correlations as $correlation) {
|
|
if (!array_key_exists('filter', $correlation) || !array_key_exists('conditions', $correlation['filter'])) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($correlation['filter']['conditions'] as $condition) {
|
|
if ($condition['type'] == ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP) {
|
|
$groupids[$condition['groupid']] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$groupids) {
|
|
return;
|
|
}
|
|
|
|
$groups_count = API::HostGroup()->get([
|
|
'countOutput' => true,
|
|
'groupids' => array_keys($groupids)
|
|
]);
|
|
|
|
if ($groups_count != count($groupids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param array $correlations
|
|
* @param array|null $db_correlations
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
private static function validateUpdate(array &$correlations, array &$db_correlations = null): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['correlationid'], ['name']], 'fields' => [
|
|
'correlationid' => ['type' => API_ID, 'flags' => API_REQUIRED],
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('correlation', 'name')],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('correlation', 'description')],
|
|
'status' => ['type' => API_INT32, 'in' => implode(',', [ZBX_CORRELATION_ENABLED, ZBX_CORRELATION_DISABLED])],
|
|
'filter' => self::getFilterValidationRules(),
|
|
'operations' => ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY, 'uniq' => [['type']], 'fields' => [
|
|
'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_CORR_OPERATION_CLOSE_OLD, ZBX_CORR_OPERATION_CLOSE_NEW])]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $correlations, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_correlations = DB::select('correlation', [
|
|
'output' => ['correlationid', 'name', 'description', 'status'],
|
|
'correlationids' => array_column($correlations, 'correlationid'),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($correlations) != count($db_correlations)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
self::checkDuplicates($correlations, $db_correlations);
|
|
|
|
self::addAffectedObjects($correlations, $db_correlations);
|
|
self::validateFormula($correlations);
|
|
self::checkHostGroups($correlations);
|
|
}
|
|
|
|
/**
|
|
* Validate correlation condition formula IDs. Check the "formulaid" field and that formula matches the conditions.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $correlations
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
protected static function validateFormula(array $correlations): void {
|
|
$condition_formula_parser = new CConditionFormula();
|
|
|
|
foreach ($correlations as $i => $correlation) {
|
|
if (!array_key_exists('filter', $correlation)) {
|
|
continue;
|
|
}
|
|
|
|
if ($correlation['filter']['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) {
|
|
$condition_formula_parser->parse($correlation['filter']['formula']);
|
|
|
|
$constants = array_column($condition_formula_parser->constants, 'value', 'value');
|
|
$path = '/'.($i + 1).'/filter';
|
|
|
|
if (count($correlation['filter']['conditions']) != count($constants)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('Invalid parameter "%1$s": %2$s.', $path.'/conditions', _('incorrect number of conditions'))
|
|
);
|
|
}
|
|
|
|
foreach ($correlation['filter']['conditions'] as $j => $condition) {
|
|
if (!array_key_exists($condition['formulaid'], $constants)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
$path.'/conditions/'.($j + 1).'/formulaid', _('an identifier is not defined in the formula')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply query output options.
|
|
*
|
|
* @param string $table_name
|
|
* @param string $table_alias
|
|
* @param array $options
|
|
* @param array $sql_parts
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function applyQueryOutputOptions($table_name, $table_alias, array $options, array $sql_parts) {
|
|
$sql_parts = parent::applyQueryOutputOptions($table_name, $table_alias, $options, $sql_parts);
|
|
|
|
if (!$options['countOutput']) {
|
|
// Add filter fields.
|
|
if ($this->outputIsRequested('formula', $options['selectFilter'])
|
|
|| $this->outputIsRequested('eval_formula', $options['selectFilter'])
|
|
|| $this->outputIsRequested('conditions', $options['selectFilter'])) {
|
|
|
|
$sql_parts = $this->addQuerySelect('c.formula', $sql_parts);
|
|
$sql_parts = $this->addQuerySelect('c.evaltype', $sql_parts);
|
|
}
|
|
|
|
if ($this->outputIsRequested('evaltype', $options['selectFilter'])) {
|
|
$sql_parts = $this->addQuerySelect('c.evaltype', $sql_parts);
|
|
}
|
|
}
|
|
|
|
return $sql_parts;
|
|
}
|
|
|
|
/**
|
|
* Extend result with requested objects.
|
|
*
|
|
* @param array $options
|
|
* @param array $result
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function addRelatedObjects(array $options, array $result): array {
|
|
$result = parent::addRelatedObjects($options, $result);
|
|
|
|
$correlationids = array_keys($result);
|
|
|
|
// Adding formulas and conditions.
|
|
if ($options['selectFilter'] !== null) {
|
|
$formula_requested = $this->outputIsRequested('formula', $options['selectFilter']);
|
|
$eval_formula_requested = $this->outputIsRequested('eval_formula', $options['selectFilter']);
|
|
$conditions_requested = $this->outputIsRequested('conditions', $options['selectFilter']);
|
|
|
|
$filters = [];
|
|
|
|
if ($options['selectFilter']) {
|
|
foreach ($result as $correlation) {
|
|
$filters[$correlation['correlationid']] = [
|
|
'evaltype' => $correlation['evaltype'],
|
|
'formula' => array_key_exists('formula', $correlation) ? $correlation['formula'] : '',
|
|
'conditions' => []
|
|
];
|
|
}
|
|
|
|
if ($formula_requested || $eval_formula_requested || $conditions_requested) {
|
|
$sql = 'SELECT c.correlationid,c.corr_conditionid,c.type,ct.tag AS ct_tag,'.
|
|
'cg.operator AS cg_operator,cg.groupid,ctp.oldtag,ctp.newtag,ctv.tag AS ctv_tag,'.
|
|
'ctv.operator AS ctv_operator,ctv.value'.
|
|
' FROM corr_condition c'.
|
|
' LEFT JOIN corr_condition_tag ct ON ct.corr_conditionid = c.corr_conditionid'.
|
|
' LEFT JOIN corr_condition_group cg ON cg.corr_conditionid = c.corr_conditionid'.
|
|
' LEFT JOIN corr_condition_tagpair ctp ON ctp.corr_conditionid = c.corr_conditionid'.
|
|
' LEFT JOIN corr_condition_tagvalue ctv ON ctv.corr_conditionid = c.corr_conditionid'.
|
|
' WHERE '.dbConditionInt('c.correlationid', $correlationids);
|
|
|
|
$db_corr_conditions = DBselect($sql);
|
|
|
|
while ($row = DBfetch($db_corr_conditions)) {
|
|
$fields = [
|
|
'corr_conditionid' => $row['corr_conditionid'],
|
|
'correlationid' => $row['correlationid'],
|
|
'type' => $row['type']
|
|
];
|
|
|
|
switch ($row['type']) {
|
|
case ZBX_CORR_CONDITION_OLD_EVENT_TAG:
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_TAG:
|
|
$fields['tag'] = $row['ct_tag'];
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP:
|
|
$fields['operator'] = $row['cg_operator'];
|
|
$fields['groupid'] = $row['groupid'];
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_EVENT_TAG_PAIR:
|
|
$fields['oldtag'] = $row['oldtag'];
|
|
$fields['newtag'] = $row['newtag'];
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE:
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE:
|
|
$fields['tag'] = $row['ctv_tag'];
|
|
$fields['operator'] = $row['ctv_operator'];
|
|
$fields['value'] = $row['value'];
|
|
break;
|
|
}
|
|
|
|
$filters[$row['correlationid']]['conditions'][] = $fields;
|
|
}
|
|
|
|
foreach ($filters as &$filter) {
|
|
// In case of a custom expression, use the given formula.
|
|
if ($filter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) {
|
|
$formula = $filter['formula'];
|
|
}
|
|
// In other cases generate the formula automatically.
|
|
else {
|
|
$conditions = $filter['conditions'];
|
|
CArrayHelper::sort($conditions, ['type']);
|
|
$conditions_for_formula = [];
|
|
|
|
foreach ($conditions as $condition) {
|
|
$conditions_for_formula[$condition['corr_conditionid']] = $condition['type'];
|
|
}
|
|
|
|
$formula = CConditionHelper::getFormula($conditions_for_formula, $filter['evaltype']);
|
|
}
|
|
|
|
// Generate formulaids from the effective formula.
|
|
$formulaids = CConditionHelper::getFormulaIds($formula);
|
|
|
|
foreach ($filter['conditions'] as &$condition) {
|
|
$condition['formulaid'] = $formulaids[$condition['corr_conditionid']];
|
|
}
|
|
unset($condition);
|
|
|
|
// Generated a letter based formula only for actions with custom expressions.
|
|
if ($formula_requested && $filter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) {
|
|
$filter['formula'] = CConditionHelper::replaceNumericIds($formula, $formulaids);
|
|
}
|
|
|
|
if ($eval_formula_requested) {
|
|
$filter['eval_formula'] = CConditionHelper::replaceNumericIds($formula, $formulaids);
|
|
}
|
|
}
|
|
unset($filter);
|
|
}
|
|
}
|
|
else {
|
|
// In case no fields are actually selected in "filter", return empty array.
|
|
foreach ($result as $correlation) {
|
|
$filters[$correlation['correlationid']] = [];
|
|
}
|
|
}
|
|
|
|
// Add filters to the result.
|
|
foreach ($result as &$correlation) {
|
|
$correlation['filter'] = $filters[$correlation['correlationid']];
|
|
}
|
|
unset($correlation);
|
|
}
|
|
|
|
// Adding operations.
|
|
if ($options['selectOperations'] !== null && $options['selectOperations'] != API_OUTPUT_COUNT) {
|
|
$operations = API::getApiService()->select('corr_operation', [
|
|
'output' => $this->outputExtend($options['selectOperations'], [
|
|
'correlationid', 'corr_operationid', 'type'
|
|
]),
|
|
'filter' => ['correlationid' => $correlationids],
|
|
'preservekeys' => true
|
|
]);
|
|
$relation_map = $this->createRelationMap($operations, 'correlationid', 'corr_operationid');
|
|
|
|
foreach ($operations as &$operation) {
|
|
unset($operation['correlationid'], $operation['corr_operationid']);
|
|
}
|
|
unset($operation);
|
|
|
|
$result = $relation_map->mapMany($result, $operations, 'operations');
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param array $correlations
|
|
* @param array $db_correlations
|
|
*/
|
|
private static function addAffectedObjects(array $correlations, array &$db_correlations): void {
|
|
$correlationids = ['filter' => [], 'operations' => []];
|
|
|
|
foreach ($correlations as $correlation) {
|
|
if (array_key_exists('filter', $correlation)) {
|
|
$correlationids['filter'][] = $correlation['correlationid'];
|
|
$db_correlations[$correlation['correlationid']]['filter']['conditions'] = [];
|
|
}
|
|
|
|
if (array_key_exists('operations', $correlation)) {
|
|
$correlationids['operations'][] = $correlation['correlationid'];
|
|
$db_correlations[$correlation['correlationid']]['operations'] = [];
|
|
}
|
|
}
|
|
|
|
if ($correlationids['filter']) {
|
|
$options = [
|
|
'output' => ['correlationid', 'evaltype', 'formula'],
|
|
'filter' => ['correlationid' => $correlationids['filter']]
|
|
];
|
|
$db_filters = DBselect(DB::makeSql('correlation', $options));
|
|
|
|
while ($db_filter = DBfetch($db_filters)) {
|
|
$db_correlations[$db_filter['correlationid']]['filter'] =
|
|
array_diff_key($db_filter, array_flip(['correlationid']));
|
|
}
|
|
|
|
$sql = 'SELECT c.correlationid,c.corr_conditionid,c.type,ct.tag AS ct_tag,'.
|
|
'cg.operator AS cg_operator,cg.groupid,ctp.oldtag,ctp.newtag,ctv.tag AS ctv_tag,'.
|
|
'ctv.operator AS ctv_operator,ctv.value'.
|
|
' FROM corr_condition c'.
|
|
' LEFT JOIN corr_condition_tag ct ON ct.corr_conditionid=c.corr_conditionid'.
|
|
' LEFT JOIN corr_condition_group cg ON cg.corr_conditionid=c.corr_conditionid'.
|
|
' LEFT JOIN corr_condition_tagpair ctp ON ctp.corr_conditionid=c.corr_conditionid'.
|
|
' LEFT JOIN corr_condition_tagvalue ctv ON ctv.corr_conditionid=c.corr_conditionid'.
|
|
' WHERE '.dbConditionId('c.correlationid', $correlationids['filter']);
|
|
$db_conditions = DBselect($sql);
|
|
|
|
while ($db_condition = DBfetch($db_conditions)) {
|
|
$condition = [
|
|
'corr_conditionid' => $db_condition['corr_conditionid'],
|
|
'type' => $db_condition['type']
|
|
];
|
|
|
|
switch ($db_condition['type']) {
|
|
case ZBX_CORR_CONDITION_OLD_EVENT_TAG:
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_TAG:
|
|
$condition['tag'] = $db_condition['ct_tag'];
|
|
$condition['operator'] = CONDITION_OPERATOR_EQUAL;
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP:
|
|
$condition['groupid'] = $db_condition['groupid'];
|
|
$condition['operator'] = $db_condition['cg_operator'];
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_EVENT_TAG_PAIR:
|
|
$condition['oldtag'] = $db_condition['oldtag'];
|
|
$condition['newtag'] = $db_condition['newtag'];
|
|
$condition['operator'] = CONDITION_OPERATOR_EQUAL;
|
|
break;
|
|
|
|
case ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE:
|
|
case ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE:
|
|
$condition['tag'] = $db_condition['ctv_tag'];
|
|
$condition['value'] = $db_condition['value'];
|
|
$condition['operator'] = $db_condition['ctv_operator'];
|
|
break;
|
|
}
|
|
|
|
$db_correlations[$db_condition['correlationid']]['filter']['conditions']
|
|
[$db_condition['corr_conditionid']] = $condition;
|
|
}
|
|
|
|
foreach ($db_correlations as $correlationid => &$db_correlation) {
|
|
if ($db_correlation['filter']['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) {
|
|
$formula = $db_correlation['filter']['formula'];
|
|
|
|
$formulaids = CConditionHelper::getFormulaIds($formula);
|
|
|
|
foreach ($db_correlation['filter']['conditions'] as &$db_condition) {
|
|
$db_condition['formulaid'] = $formulaids[$db_condition['corr_conditionid']];
|
|
}
|
|
unset($db_condition);
|
|
}
|
|
}
|
|
unset($db_correlation);
|
|
}
|
|
|
|
if ($correlationids['operations']) {
|
|
$options = [
|
|
'output' => ['corr_operationid', 'correlationid', 'type'],
|
|
'filter' => ['correlationid' => $correlationids['operations']]
|
|
];
|
|
$db_operations = DBselect(DB::makeSql('corr_operation', $options));
|
|
|
|
while ($db_operation = DBfetch($db_operations)) {
|
|
$db_correlations[$db_operation['correlationid']]['operations'][$db_operation['corr_operationid']] =
|
|
array_diff_key($db_operation, array_flip(['correlationid']));
|
|
}
|
|
}
|
|
}
|
|
}
|