From 913ef53f6287c25d5ec482b09b81dc0872358a16 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Wed, 7 Apr 2021 13:29:40 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed function period validation --- ui/include/classes/validators/CFunctionValidator.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php index 57ff22046ee..9241e6c2fd9 100644 --- a/ui/include/classes/validators/CFunctionValidator.php +++ b/ui/include/classes/validators/CFunctionValidator.php @@ -530,6 +530,7 @@ class CFunctionValidator extends CValidator { * Valid period shift can contain time range value with precision and multiple of an hour. * * @param mixed $param + * @param int $mandat * * @return bool */ @@ -546,7 +547,7 @@ class CFunctionValidator extends CValidator { $period_shift = $param->time_shift; } - if (($mandat & 0x01) && !$param->sec_num_contains_macros) { + if ((($mandat & 0x01) || $mandat !== '') && !$param->sec_num_contains_macros) { $simple_interval_parser = new CSimpleIntervalParser(['with_year' => true]); if ($simple_interval_parser->parse($period) != CParser::PARSE_SUCCESS) { return false; @@ -558,11 +559,11 @@ class CFunctionValidator extends CValidator { } } - if (!($mandat & 0x02) && $period_shift === null) { + if ((!($mandat & 0x02) && $period_shift === null) || $param->time_shift_contains_macros) { return true; } - elseif (($mandat & 0x02) && !$this->validateTrendPeriods($period, $period_shift)) { - return $this->isMacro($period_shift); + elseif (!$this->validateTrendPeriods($period, $period_shift)) { + return false; } return true; -- cgit v1.2.3 From 616282ba1ac28fcc6afdbf622e37ec63b67401b5 Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Wed, 7 Apr 2021 15:46:03 +0300 Subject: A......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed minor formatting issues discovered during code review --- .../classes/api/services/CTriggerGeneral.php | 33 +++++++++------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index 58c7e5107e0..117cebcc782 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -155,10 +155,8 @@ abstract class CTriggerGeneral extends CApiService { foreach ($hosts_by_tpl_hostid[$tpl_hostid] as $host) { // Replace template name in /host/key reference to target host name. $new_trigger['expression'] = $tpl_trigger['expression']; - $queries = $expression_data->result->getTokensOfTypes([ - CTriggerExprParserResult::TOKEN_TYPE_QUERY - ]); - for ($i = count($queries)-1; $i >= 0; $i--) { + $queries = $expression_data->result->getTokensOfTypes([CTriggerExprParserResult::TOKEN_TYPE_QUERY]); + for ($i = count($queries) - 1; $i >= 0; $i--) { $new_trigger['expression'] = substr_replace($new_trigger['expression'], '/'.$host['host'].'/'.$queries[$i]->item, $queries[$i]->pos, $queries[$i]->length ); @@ -166,14 +164,12 @@ abstract class CTriggerGeneral extends CApiService { if ($tpl_trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) { $new_trigger['recovery_expression'] = $tpl_trigger['recovery_expression']; - $queries = $recovery_expression_data->result->getTokensOfTypes([ CTriggerExprParserResult::TOKEN_TYPE_QUERY ]); - for ($i = count($queries)-1; $i >= 0; $i--) { - $new_trigger['recovery_expression'] = substr_replace( - $new_trigger['recovery_expression'], '/'.$host['host'].'/'.$queries[$i]->item, - $queries[$i]->pos, $queries[$i]->length + for ($i = count($queries) - 1; $i >= 0; $i--) { + $new_trigger['recovery_expression'] = substr_replace($new_trigger['recovery_expression'], + '/'.$host['host'].'/'.$queries[$i]->item, $queries[$i]->pos, $queries[$i]->length ); } } @@ -427,10 +423,8 @@ abstract class CTriggerGeneral extends CApiService { // Replace template name in /host/key reference to target host name. $expression = $tpl_trigger['expression']; - $queries = $expression_data->result->getTokensOfTypes([ - CTriggerExprParserResult::TOKEN_TYPE_QUERY - ]); - for ($i = count($queries)-1; $i >= 0; $i--) { + $queries = $expression_data->result->getTokensOfTypes([CTriggerExprParserResult::TOKEN_TYPE_QUERY]); + for ($i = count($queries) - 1; $i >= 0; $i--) { $expression = substr_replace($expression, '/'.$chd_trigger['host'].'/'.$queries[$i]->item, $queries[$i]->pos, $queries[$i]->length ); @@ -445,7 +439,7 @@ abstract class CTriggerGeneral extends CApiService { $queries = $recovery_expression_data->result->getTokensOfTypes([ CTriggerExprParserResult::TOKEN_TYPE_QUERY ]); - for ($i = count($queries)-1; $i >= 0; $i--) { + for ($i = count($queries) - 1; $i >= 0; $i--) { $recovery_expression = substr_replace($recovery_expression, '/'.$chd_trigger['host'].'/'.$queries[$i]->item, $queries[$i]->pos, $queries[$i]->length ); @@ -1406,14 +1400,13 @@ abstract class CTriggerGeneral extends CApiService { foreach ($triggers as $tnum => $trigger) { $expressions_changed = ($db_triggers === null || ($trigger['expression'] !== $db_triggers[$tnum]['expression'] - || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression'])); + || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression'])); if (!$expressions_changed) { continue; } $expressionData->parse($trigger['expression']); - foreach ($expressionData->result->getItemsGroupedByHosts() as $key => $host) { if (array_key_exists($key, $hosts_keys)) { $hosts_keys[$key]['keys'] += $host['keys']; @@ -1425,7 +1418,6 @@ abstract class CTriggerGeneral extends CApiService { if ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) { $expressionData->parse($trigger['recovery_expression']); - foreach ($expressionData->result->getItemsGroupedByHosts() as $key => $host) { if (array_key_exists($key, $hosts_keys)) { $hosts_keys[$key]['keys'] += $host['keys']; @@ -1566,6 +1558,7 @@ abstract class CTriggerGeneral extends CApiService { // Validate functions of trigger expression. Colect trigger functions in $triggers_functions. foreach ($expressionData->result->getFunctions() as $fn) { $query = $fn->getFunctionTriggerQuery(); + $fn_data = [ 'fn' => $fn, 'value_type' => ($query !== null) @@ -1706,7 +1699,7 @@ abstract class CTriggerGeneral extends CApiService { $this->validateMovedTriggers($moved_triggers); } - $functions_num = array_sum(array_map(function ($funcs) {return count($funcs);}, $triggers_functions)); + $functions_num = array_sum(array_map(function ($funcs) { return count($funcs); }, $triggers_functions)); $functionid = DB::reserveIds('functions', $functions_num); $max_length = [ @@ -1716,9 +1709,9 @@ abstract class CTriggerGeneral extends CApiService { // Replace func(/host/item) macros with {}. foreach ($triggers as $tnum => &$trigger) { - $expressions_changed = $db_triggers === null + $expressions_changed = ($db_triggers === null || ($trigger['expression'] !== $db_triggers[$tnum]['expression'] - || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression']); + || $trigger['recovery_expression'] !== $db_triggers[$tnum]['recovery_expression'])); if (!$expressions_changed) { continue; -- cgit v1.2.3 From 3d9017af00d191754cbfa8cb486daa581570598e Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Wed, 7 Apr 2021 15:47:28 +0300 Subject: A......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed minor coding style issue --- ui/include/classes/api/services/CTriggerGeneral.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index 117cebcc782..53580ccfacf 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -1729,12 +1729,12 @@ abstract class CTriggerGeneral extends CApiService { foreach ($expression_fields as $expr_field) { if (array_key_exists($expr_field, $triggers_function_occurances[$tnum])) { + // Sort trigger function occurrences in reverse order by position. usort($triggers_function_occurances[$tnum][$expr_field], function ($a, $b) { - return $a['pos'] <=> $b['pos']; + return $b['pos'] <=> $a['pos']; }); - for ($i = count($triggers_function_occurances[$tnum][$expr_field])-1; $i>=0; $i--) { - $occurance = $triggers_function_occurances[$tnum][$expr_field][$i]; + foreach ($triggers_function_occurances[$tnum][$expr_field] as $occurance) { $trigger[$expr_field] = substr_replace($trigger[$expr_field], '{'.$triggers_functions[$tnum][$occurance['match']]['functionid'].'}', $occurance['pos'], $occurance['length'] -- cgit v1.2.3 From 121bfaa13a25cc751e504cff72a1ae1aec7196dc Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Wed, 7 Apr 2021 15:51:00 +0300 Subject: A......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed typo "occurance" => "occurrence" --- ui/include/classes/api/services/CTriggerGeneral.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index 53580ccfacf..a182a4afed4 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -1523,7 +1523,7 @@ abstract class CTriggerGeneral extends CApiService { $moved_triggers = []; } - $triggers_function_occurances = []; + $triggers_function_occurrences = []; foreach ($triggers as $tnum => &$trigger) { $expressions_changed = ($db_triggers === null || ($trigger['expression'] !== $db_triggers[$tnum]['expression'] @@ -1534,7 +1534,7 @@ abstract class CTriggerGeneral extends CApiService { } $triggers_functions[$tnum] = []; - $triggers_function_occurances[$tnum] = []; + $triggers_function_occurrences[$tnum] = []; if ($class === 'CTriggerPrototype') { $lld_ruleids = []; } @@ -1613,7 +1613,7 @@ abstract class CTriggerGeneral extends CApiService { ]; } - $triggers_function_occurances[$tnum][$expr_field][] = [ + $triggers_function_occurrences[$tnum][$expr_field][] = [ 'match' => $fn->match, 'length' => $fn->length, 'pos' => $fn->pos @@ -1728,16 +1728,16 @@ abstract class CTriggerGeneral extends CApiService { : ['expression']; foreach ($expression_fields as $expr_field) { - if (array_key_exists($expr_field, $triggers_function_occurances[$tnum])) { + if (array_key_exists($expr_field, $triggers_function_occurrences[$tnum])) { // Sort trigger function occurrences in reverse order by position. - usort($triggers_function_occurances[$tnum][$expr_field], function ($a, $b) { + usort($triggers_function_occurrences[$tnum][$expr_field], function ($a, $b) { return $b['pos'] <=> $a['pos']; }); - foreach ($triggers_function_occurances[$tnum][$expr_field] as $occurance) { + foreach ($triggers_function_occurrences[$tnum][$expr_field] as $occurrence) { $trigger[$expr_field] = substr_replace($trigger[$expr_field], - '{'.$triggers_functions[$tnum][$occurance['match']]['functionid'].'}', $occurance['pos'], - $occurance['length'] + '{'.$triggers_functions[$tnum][$occurrence['match']]['functionid'].'}', $occurrence['pos'], + $occurrence['length'] ); } } -- cgit v1.2.3 From 3c8578597fdc5e1130986648cba62bc55df38141 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Wed, 7 Apr 2021 17:30:37 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed period parser --- ui/include/classes/parsers/CPeriodParser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/include/classes/parsers/CPeriodParser.php b/ui/include/classes/parsers/CPeriodParser.php index 46f71a360ee..1bf56eef04a 100644 --- a/ui/include/classes/parsers/CPeriodParser.php +++ b/ui/include/classes/parsers/CPeriodParser.php @@ -104,7 +104,7 @@ class CPeriodParser extends CParser { // Check format. Otherwaise, almost anything can be period. $is_valid_num = (substr($parts[0], 0, 1) === '#' && ctype_digit(substr($parts[0], 1))); $is_valid_sec = preg_match('/^'.ZBX_PREG_INT.'(?['.ZBX_TIME_SUFFIXES_WITH_YEAR.'])$/', $parts[0]); - if (!$is_valid_num && !$is_valid_sec) { + if (!$is_valid_num && !$is_valid_sec && !$contains_macros[0]) { return CParser::PARSE_FAIL; } -- cgit v1.2.3 From 317cf40a502483f46cbfd4decd071d970ad6a415 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Wed, 7 Apr 2021 17:37:44 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed unused validator --- .../classes/validators/C10FunctionValidator.php | 693 ---------------- .../validators/C10FunctionValidatorTest.php | 876 --------------------- 2 files changed, 1569 deletions(-) delete mode 100644 ui/include/classes/validators/C10FunctionValidator.php delete mode 100644 ui/tests/unit/include/classes/validators/C10FunctionValidatorTest.php diff --git a/ui/include/classes/validators/C10FunctionValidator.php b/ui/include/classes/validators/C10FunctionValidator.php deleted file mode 100644 index 8d73a60b5af..00000000000 --- a/ui/include/classes/validators/C10FunctionValidator.php +++ /dev/null @@ -1,693 +0,0 @@ -' => array( - * 'args' => array( - * array('type' => ''[, 'mandat' => bool]), - * ... - * ), - * 'value_types' => array(, , ...) - * ) - * ) - * - * can be 'fit', 'mode', 'num_suffix', 'num_unsigned', 'operation', 'percent', 'sec_neg', - * 'sec_num', 'sec_num_zero', 'sec_zero' - * can be one of ITEM_VALUE_TYPE_* - * - * @var array - */ - private $allowed; - - /** - * If set to true, LLD macros can be uses inside functions and are properly validated using LLD macro parser. - * - * @var bool - */ - private $lldmacros = true; - - public function __construct(array $options = []) { - /* - * CValidator is an abstract class, so no specific functionality should be bound to it. Thus putting - * an option "lldmacros" (or class variable $lldmacros) in it, is not preferred. Without it, class - * initialization would fail due to __set(). So instead we create a local variable in this extended class - * and remove the option "lldmacros" before calling the parent constructor. - */ - if (array_key_exists('lldmacros', $options)) { - $this->lldmacros = $options['lldmacros']; - unset($options['lldmacros']); - } - parent::__construct($options); - - $valueTypesAll = [ - ITEM_VALUE_TYPE_FLOAT => true, - ITEM_VALUE_TYPE_UINT64 => true, - ITEM_VALUE_TYPE_STR => true, - ITEM_VALUE_TYPE_TEXT => true, - ITEM_VALUE_TYPE_LOG => true - ]; - $valueTypesNum = [ - ITEM_VALUE_TYPE_FLOAT => true, - ITEM_VALUE_TYPE_UINT64 => true - ]; - $valueTypesChar = [ - ITEM_VALUE_TYPE_STR => true, - ITEM_VALUE_TYPE_TEXT => true, - ITEM_VALUE_TYPE_LOG => true - ]; - $valueTypesLog = [ - ITEM_VALUE_TYPE_LOG => true - ]; - $valueTypesInt = [ - ITEM_VALUE_TYPE_UINT64 => true - ]; - - $argsIgnored = [['type' => 'str']]; - - $this->allowed = [ - 'abschange' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'avg' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesNum - ], - 'band' => [ - 'args' => [ - ['type' => 'sec_num_zero', 'mandat' => true, 'can_be_empty' => true], - ['type' => 'num_unsigned', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesInt - ], - 'change' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'count' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'str'], - ['type' => 'operation'], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesAll - ], - 'date' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'dayofmonth' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'dayofweek' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'delta' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesNum - ], - 'diff' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'forecast' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true], - ['type' => 'sec_neg', 'mandat' => true], - ['type' => 'fit', 'can_be_empty' => true], - ['type' => 'mode', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesNum - ], - 'fuzzytime' => [ - 'args' => [ - ['type' => 'sec_zero', 'mandat' => true] - ], - 'value_types' => $valueTypesNum - ], - 'iregexp' => [ - 'args' => [ - ['type' => 'str', 'mandat' => true], - ['type' => 'sec_num', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesChar - ], - 'last' => [ - 'args' => [ - ['type' => 'sec_num_zero', 'mandat' => true, 'can_be_empty' => true], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesAll - ], - 'logeventid' => [ - 'args' => [ - ['type' => 'str', 'mandat' => true] - ], - 'value_types' => $valueTypesLog - ], - 'logseverity' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesLog - ], - 'logsource' => [ - 'args' => [ - ['type' => 'str', 'mandat' => true] - ], - 'value_types' => $valueTypesLog - ], - 'max' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesNum - ], - 'min' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesNum - ], - 'nodata'=> [ - 'args' => [ - ['type' => 'sec', 'mandat' => true], - ['type' => 'nodata_mode', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesAll - ], - 'now' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'percentile' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true], - ['type' => 'percent', 'mandat' => true] - ], - 'value_types' => $valueTypesNum - ], - 'prev' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'regexp' => [ - 'args' => [ - ['type' => 'str', 'mandat' => true], - ['type' => 'sec_num', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesChar - ], - 'str' => [ - 'args' => [ - ['type' => 'str', 'mandat' => true], - ['type' => 'sec_num', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesChar - ], - 'strlen' => [ - 'args' => [ - ['type' => 'sec_num_zero', 'mandat' => true, 'can_be_empty' => true], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesChar - ], - 'sum' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesNum - ], - 'time' => [ - 'args' => $argsIgnored, - 'value_types' => $valueTypesAll - ], - 'timeleft' => [ - 'args' => [ - ['type' => 'sec_num', 'mandat' => true], - ['type' => 'sec_zero', 'can_be_empty' => true], - ['type' => 'num_suffix', 'mandat' => true], - ['type' => 'fit', 'can_be_empty' => true] - ], - 'value_types' => $valueTypesNum - ], - 'trendavg' => [ - 'args' => [ - ['type' => 'period', 'mandat' => true], - ['type' => 'period_shift', 'mandat' => true] - ], - 'value_types' => $valueTypesNum - ], - 'trendcount' => [ - 'args' => [ - ['type' => 'period', 'mandat' => true], - ['type' => 'period_shift', 'mandat' => true] - ], - 'value_types' => $valueTypesAll - ], - 'trenddelta' => [ - 'args' => [ - ['type' => 'period', 'mandat' => true], - ['type' => 'period_shift', 'mandat' => true] - ], - 'value_types' => $valueTypesNum - ], - 'trendmax' => [ - 'args' => [ - ['type' => 'period', 'mandat' => true], - ['type' => 'period_shift', 'mandat' => true] - ], - 'value_types' => $valueTypesNum - ], - 'trendmin' => [ - 'args' => [ - ['type' => 'period', 'mandat' => true], - ['type' => 'period_shift', 'mandat' => true] - ], - 'value_types' => $valueTypesNum - ], - 'trendsum' => [ - 'args' => [ - ['type' => 'period', 'mandat' => true], - ['type' => 'period_shift', 'mandat' => true] - ], - 'value_types' => $valueTypesNum - ] - ]; - } - - /** - * Validate trigger function like last(0), time(), etc. - * Examples: - * array( - * 'function' => last("#15"), - * 'functionName' => 'last', - * 'functionParamList' => array(0 => '#15'), - * 'valueType' => 3 - * ) - * - * @param string $value['function'] - * @param string $value['functionName'] - * @param array $value['functionParamList'] - * @param int $value['valueType'] - * - * @return bool - */ - public function validate($value) { - $this->setError(''); - - if (!isset($this->allowed[$value['functionName']])) { - $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']).' '. - _('Unknown function.')); - return false; - } - - if (!isset($this->allowed[$value['functionName']]['value_types'][$value['valueType']])) { - $this->setError(_s('Incorrect item value type "%1$s" provided for trigger function "%2$s".', - itemValueTypeString($value['valueType']), $value['function'])); - return false; - } - - if (count($this->allowed[$value['functionName']]['args']) < count($value['functionParamList'])) { - $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']).' '. - _('Invalid number of parameters.')); - return false; - } - - $paramLabels = [ - _('Invalid first parameter.'), - _('Invalid second parameter.'), - _('Invalid third parameter.'), - _('Invalid fourth parameter.'), - _('Invalid fifth parameter.') - ]; - - $user_macro_parser = new CUserMacroParser(); - if ($this->lldmacros) { - $lld_macro_parser = new CLLDMacroParser(); - $lld_macro_function_parser = new CLLDMacroFunctionParser(); - } - - $func_args = []; - - foreach ($this->allowed[$value['functionName']]['args'] as $aNum => $arg) { - // mandatory check - if (isset($arg['mandat']) && $arg['mandat'] && !isset($value['functionParamList'][$aNum])) { - $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $value['function']).' '. - _('Mandatory parameter is missing.')); - return false; - } - - if (!isset($value['functionParamList'][$aNum])) { - continue; - } - - if (isset($arg['can_be_empty']) && $value['functionParamList'][$aNum] == '') { - continue; - } - - // user macro - if ($user_macro_parser->parse($value['functionParamList'][$aNum]) == CParser::PARSE_SUCCESS) { - continue; - } - - if ($this->lldmacros - && ($lld_macro_function_parser->parse($value['functionParamList'][$aNum]) == CParser::PARSE_SUCCESS - || $lld_macro_parser->parse($value['functionParamList'][$aNum]) == CParser::PARSE_SUCCESS)) { - continue; - } - - if (!$this->validateParameter($value['functionParamList'][$aNum], $arg['type'])) { - $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', - $value['function']).' '.$paramLabels[$aNum]); - return false; - } - - $func_args[$arg['type']] = $value['functionParamList'][$aNum]; - } - - if (array_key_exists('period', $func_args) && array_key_exists('period_shift', $func_args) - && !$this->validateTrendPeriods($func_args['period'], $func_args['period_shift'])) { - return false; - } - - return true; - } - - /** - * Validate trend* function used period and period_shift arguments. - * - * @param string $period_value Value of period, first argument for trend* function. - * @param string $period_shift_value Value of period shift, second argument for trend* function. - * - * @return bool - */ - private function validateTrendPeriods($period_value, $period_shift_value): bool { - $precisions = 'hdwMy'; - $period = strpos($precisions, substr($period_value, -1)); - - if ($period !== false) { - $relative_time_parser = new CRelativeTimeParser(); - $relative_time_parser->parse($period_shift_value); - - foreach ($relative_time_parser->getTokens() as $token) { - if ($token['type'] !== CRelativeTimeParser::ZBX_TOKEN_PRECISION) { - continue; - } - - if (strpos($precisions, $token['suffix']) < $period) { - $this->setError(_('Time units in period shift must be greater or equal to period time unit.')); - return false; - } - } - } - - return true; - } - - /** - * Validate trigger function parameter. - * - * @param string $param - * @param string $type type of $param ('fit', 'mode', 'num_suffix', 'num_unsigned', 'operation', 'percent', - * 'period', 'period_shift', 'sec_neg', 'sec_num', 'sec_num_zero', 'sec_zero') - * - * @return bool - */ - private function validateParameter($param, $type) { - switch ($type) { - case 'sec': - return $this->validateSec($param); - - case 'sec_zero': - return $this->validateSecZero($param); - - case 'sec_neg': - return $this->validateSecNeg($param); - - case 'sec_num': - return $this->validateSecNum($param); - - case 'sec_num_zero': - return $this->validateSecNumZero($param); - - case 'num_unsigned': - return CNewValidator::is_uint64($param); - - case 'num_suffix': - return $this->validateNumSuffix($param); - - case 'nodata_mode': - return ($param === 'strict'); - - case 'fit': - return $this->validateFit($param); - - case 'mode': - return $this->validateMode($param); - - case 'percent': - return $this->validatePercent($param); - - case 'operation': - return $this->validateOperation($param); - - case 'period': - return $this->validatePeriod($param); - - case 'period_shift': - return $this->validatePeriodShift($param); - } - - return true; - } - - /** - * Validate trigger function parameter seconds value. - * - * @param string $param - * - * @return bool - */ - private function validateSecValue($param) { - return preg_match('/^\d+['.ZBX_TIME_SUFFIXES.']{0,1}$/', $param); - } - - /** - * Validate trigger function parameter which can contain only seconds. - * Examples: 1, 5w - * - * @param string $param - * - * @return bool - */ - private function validateSec($param) { - return ($this->validateSecValue($param) && $param > 0); - } - - /** - * Validate trigger function parameter which can contain only seconds or zero. - * Examples: 0, 1, 5w - * - * @param string $param - * - * @return bool - */ - private function validateSecZero($param) { - return $this->validateSecValue($param); - } - - /** - * Validate trigger function parameter which can contain negative seconds. - * Examples: 0, 1, 5w, -3h - * - * @param string $param - * - * @return bool - */ - private function validateSecNeg($param) { - return preg_match('/^[-]?\d+['.ZBX_TIME_SUFFIXES.']{0,1}$/', $param); - } - - /** - * Validate trigger function parameter which can contain seconds greater zero or count. - * Examples: 1, 5w, #1 - * - * @param string $param - * - * @return bool - */ - private function validateSecNum($param) { - if (preg_match('/^#\d+$/', $param)) { - return (substr($param, 1) > 0); - } - - return ($this->validateSecValue($param) && $param > 0); - } - - /** - * Validate trigger function parameter which can contain seconds or count. - * Examples: 0, 1, 5w, #1 - * - * @param string $param - * - * @return bool - */ - private function validateSecNumZero($param) { - if (preg_match('/^#\d+$/', $param)) { - return (substr($param, 1) > 0); - } - - return $this->validateSecValue($param); - } - - /** - * Validate trigger function parameter which can contain suffixed decimal number. - * Examples: 0, 1, 5w, -3h, 10.2G - * - * @param string $param - * - * @return bool - */ - private function validateNumSuffix($param) { - return ((new CNumberParser(['with_suffix' => true]))->parse($param) == CParser::PARSE_SUCCESS); - } - - /** - * Validate trigger function parameter which can contain fit function (linear, polynomialN with 1 <= N <= 6, - * exponential, logarithmic, power) or an empty value. - * - * @param string $param - * - * @return bool - */ - private function validateFit($param) { - return preg_match('/^(linear|polynomial[1-6]|exponential|logarithmic|power|)$/', $param); - } - - /** - * Validate trigger function parameter which can contain forecast mode (value, max, min, delta, avg) or - * an empty value. - * - * @param string $param - * - * @return bool - */ - private function validateMode($param) { - return preg_match('/^(value|max|min|delta|avg|)$/', $param); - } - - /** - * Validate trigger function parameter which can contain a percentage. - * Examples: 0, 1, 1.2, 1.2345, 1., .1, 100 - * - * @param string $param - * - * @return bool - */ - private function validatePercent($param) { - return (preg_match('/^\d*(\.\d{0,4})?$/', $param) && $param !== '.' && $param <= 100); - } - - /** - * Validate trigger function parameter which can contain operation (band, eq, ge, gt, le, like, lt, ne, - * regexp, iregexp) or an empty value. - * - * @param string $param - * - * @return bool - */ - private function validateOperation($param) { - return preg_match('/^(eq|ne|gt|ge|lt|le|like|band|regexp|iregexp|)$/', $param); - } - - /** - * Validate trigger function parameter which can contain time unit not less than 1 hour and multiple of an hour. - * Examples: 3600, 7200s, 2h, 1d - * - * @param string $param - * - * @return bool - */ - private function validatePeriod(string $param): bool { - $simple_interval_parser = new CSimpleIntervalParser(['with_year' => true]); - $value = (string) $param; - - if ($simple_interval_parser->parse($value) == CParser::PARSE_SUCCESS) { - $value = timeUnitToSeconds($value, true); - - if ($value >= SEC_PER_HOUR && $value % SEC_PER_HOUR === 0) { - return true; - } - } - - return false; - } - - /** - * Validate trigger function parameter which can contain time range value with precision and multiple of an hour. - * Examples: now/h, now/w, now/M, now/y - * - * @param string $param - * - * @return bool - */ - private function validatePeriodShift(string $param): bool { - $relative_time_parser = new CRelativeTimeParser(); - - if ($relative_time_parser->parse((string) $param) === CParser::PARSE_SUCCESS) { - $tokens = $relative_time_parser->getTokens(); - - foreach ($tokens as $token) { - if ($token['type'] === CRelativeTimeParser::ZBX_TOKEN_PRECISION && $token['suffix'] === 'm') { - return false; - } - } - - foreach ($tokens as $token) { - if ($token['type'] === CRelativeTimeParser::ZBX_TOKEN_PRECISION - && $relative_time_parser->getDateTime(true)->getTimestamp() % SEC_PER_HOUR === 0) { - return true; - } - } - } - - return false; - } -} diff --git a/ui/tests/unit/include/classes/validators/C10FunctionValidatorTest.php b/ui/tests/unit/include/classes/validators/C10FunctionValidatorTest.php deleted file mode 100644 index 0858ebb1f98..00000000000 --- a/ui/tests/unit/include/classes/validators/C10FunctionValidatorTest.php +++ /dev/null @@ -1,876 +0,0 @@ - true], ['lldmacros' => false]] as $options) { - foreach ($valueTypesAny as $valueType) { - $params[$no] = '0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '12345'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1s'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1m'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1h'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1d'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1w'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1K'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1M'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1G'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1T'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#12345'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{$M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M: /}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1{$M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{#M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '1{#M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, false]; - } - } - - return $tests; - } - - private static function parameterSecNumOffset_TestCases($func, array $valueTypes, array $params = [], $no = 0) { - $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, - ITEM_VALUE_TYPE_TEXT]; - - $tests = []; - - foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) { - foreach ($valueTypesAny as $valueType) { - $params[$no] = '0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '12345'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1s'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1m'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1h'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1d'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1w'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1K'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1M'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1G'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1T'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#12345'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{$M: /}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1{$M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{#M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '1{#M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, false]; - } - } - - return $tests; - } - - private static function parameterSec_TestCases($func, array $valueTypes, array $params = [], $no = 0) { - $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, - ITEM_VALUE_TYPE_TEXT]; - - $tests = []; - - foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) { - foreach ($valueTypesAny as $valueType) { - $params[$no] = '1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '12345'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1s'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1m'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1h'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1d'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1w'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1K'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1M'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1G'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1T'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#12345'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#01'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{$M: /}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1{$M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{#M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '1{#M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - } - } - - return $tests; - } - - private static function parameterTimeShift_TestCases($func, array $valueTypes, array $params = [], $no = 0) { - $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, - ITEM_VALUE_TYPE_TEXT]; - - $tests = []; - - foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) { - foreach ($valueTypesAny as $valueType) { - $params[$no] = '0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '12345'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1s'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1m'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1h'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1d'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1w'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1K'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1M'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1G'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1T'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#12345'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#01'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{$M: /}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1{$M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{#M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '1{#M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - } - } - - return $tests; - } - - private static function parameterPercent_TestCases($func, array $valueTypes, array $params = [], $no = 0) { - $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, - ITEM_VALUE_TYPE_TEXT]; - - $tests = []; - - foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) { - foreach ($valueTypesAny as $valueType) { - $params[$no] = '0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1s'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1m'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1h'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1d'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1w'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1K'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1M'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1G'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1T'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '-15.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '0.0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1.0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1.0123'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1.01234'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1.00000'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '1.'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '.1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '.'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '100.0000'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '100.0001'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{$M: /}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1{$M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{#M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '1{#M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - } - } - - return $tests; - } - - private static function parameterString_TestCases($func, array $valueTypes, array $params = [], $no = 0) { - $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, - ITEM_VALUE_TYPE_TEXT]; - - $tests = []; - - foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) { - foreach ($valueTypesAny as $valueType) { - $params[$no] = '0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '12345'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '-15'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1.0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#1'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#12345'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#01'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#-15'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '#1.0'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M: /}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1{$M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{#M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '1{#M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - } - } - - return $tests; - } - - private static function parameterOperator_TestCases($func, array $valueTypes, array $params = [], $no = 0) { - $valueTypesAny = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, - ITEM_VALUE_TYPE_TEXT]; - - $tests = []; - - foreach ([[], ['lldmacros' => true], ['lldmacros' => false]] as $options) { - foreach ($valueTypesAny as $valueType) { - $params[$no] = 'eq'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'ne'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'gt'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'ge'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'lt'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'le'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'like'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'band'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'regexp'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = 'iregexp'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M: /}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{$M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '{#M}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}'; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes) - && (!array_key_exists('lldmacros', $options) || $options['lldmacros'] === true) - ]; - - $params[$no] = ''; - $tests[] = [$func, $params, $valueType, $options, array_key_exists($valueType, $valueTypes)]; - - $params[$no] = '0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#12345'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#01'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#-15'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '#1.0'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = 'gt{$M}'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{$M}gt'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{#M}gt'; - $tests[] = [$func, $params, $valueType, $options, false]; - - $params[$no] = '{{#M}.regsub("^([0-9]+)", "{#M}: \1")}gt'; - $tests[] = [$func, $params, $valueType, $options, false]; - } - } - - return $tests; - } - - /** - * Tests for trend functions: 'trendavg', 'trendcount', 'trenddelta', 'trendmax', 'trendmin', 'trendsum'. - */ - private static function trendFunctionsTestData() { - $types = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, - ITEM_VALUE_TYPE_TEXT - ]; - $functions = ['trendavg', 'trendcount', 'trenddelta', 'trendmax', 'trendmin', 'trendsum']; - $supported_types = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64]; - $test_data = []; - - foreach ($functions as $function) { - foreach ($types as $value_type) { - $supported_type = ($function === 'trendcount' || in_array($value_type, $supported_types)); - - $test_data = array_merge($test_data, [ - [$function, ['1M', 'now/M'], $value_type, [], true && $supported_type], - [$function, ['1M', 'now/M-1w+1d/M'], $value_type, [], true && $supported_type], - [$function, ['1y', '{$MACRO}'], $value_type, [], true && $supported_type], - [$function, ['{$MACRO}', 'now/M'], $value_type, [], true && $supported_type], - [$function, ['{$MACRO}', '{$MACRO}'], $value_type, [], true && $supported_type], - [$function, ['1y', '{#MACRO}'], $value_type, ['lldmacros' => true], true && $supported_type], - [$function, ['{#MACRO}', 'now/M'], $value_type, ['lldmacros' => true], true && $supported_type], - [$function, ['{#MACRO}', '{#MACRO}'], $value_type, ['lldmacros' => true], true && $supported_type], - [$function, [], $value_type, [], false], - [$function, ['', ''], $value_type, [], false], - [$function, ['1y', 'now/M'], $value_type, [], false], - [$function, ['1M', 'now/w-1w+1d/M'], $value_type, [], false], - [$function, ['1M', 'now/M-1w+1d/w'], $value_type, [], false], - [$function, ['${MACRO}1y', '{$MACRO}now/y'], $value_type, [], false] - ]); - } - } - - return $test_data; - } - - public static function provider() { - $valueTypesAny = [ - ITEM_VALUE_TYPE_FLOAT => true, - ITEM_VALUE_TYPE_STR => true, - ITEM_VALUE_TYPE_LOG => true, - ITEM_VALUE_TYPE_UINT64 => true, - ITEM_VALUE_TYPE_TEXT => true - ]; - $valueTypesLog = [ - ITEM_VALUE_TYPE_LOG => true - ]; - $valueTypesNum = [ - ITEM_VALUE_TYPE_FLOAT => true, - ITEM_VALUE_TYPE_UINT64 => true - ]; - $valueTypesInt = [ - ITEM_VALUE_TYPE_UINT64 => true - ]; - $valueTypesStr = [ - ITEM_VALUE_TYPE_STR => true, - ITEM_VALUE_TYPE_LOG => true, - ITEM_VALUE_TYPE_TEXT => true - ]; - - return array_merge( - // abschange() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('abschange', $valueTypesAny), - - // change() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('change', $valueTypesAny), - - // date() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('date', $valueTypesAny), - - // dayofmonth() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('dayofmonth', $valueTypesAny), - - // dayofweek() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('dayofweek', $valueTypesAny), - - // diff() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('diff', $valueTypesAny), - - // logseverity() - (ignored) [log] - self::parameterString_TestCases('logseverity', $valueTypesLog), - - // now() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('now', $valueTypesAny), - - // prev() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('prev', $valueTypesAny), - - // time() - (ignored) [float, int, str, text, log] - self::parameterString_TestCases('time', $valueTypesAny), - - // avg() - (sec or #num, time_shift) [float, int] - self::parameterSecNum_TestCases('avg', $valueTypesNum), - self::parameterTimeShift_TestCases('avg', $valueTypesNum, ['#1', ''], 1), - - // band() - (sec or #num, mask, time_shift) [int] - self::parameterSecNumOffset_TestCases('band', $valueTypesInt, ['', '0']), -// TODO Mask - self::parameterTimeShift_TestCases('band', $valueTypesInt, ['#1', '0', ''], 2), - - // count() - (sec or #num, pattern, operator, time_shift) [float, int, str, text, log] - self::parameterSecNum_TestCases('count', $valueTypesAny), -// TODO Pattern - self::parameterOperator_TestCases('count', $valueTypesAny, ['#1', '', ''], 2), - self::parameterTimeShift_TestCases('count', $valueTypesAny, ['#1', '', '', ''], 3), - - // delta() - (sec or #num, time_shift) [float, int] - self::parameterSecNum_TestCases('delta', $valueTypesNum), - self::parameterTimeShift_TestCases('delta', $valueTypesNum, ['#1', ''], 1), - - // last() - (sec or #num, time_shift) [float, int, str, text, log] - self::parameterSecNumOffset_TestCases('last', $valueTypesAny), - self::parameterTimeShift_TestCases('last', $valueTypesAny, ['#1', ''], 1), - - // max() - (sec or #num, time_shift) [float, int] - self::parameterSecNum_TestCases('max', $valueTypesNum), - self::parameterTimeShift_TestCases('max', $valueTypesNum, ['#1', ''], 1), - - // min() - (sec or #num, time_shift) [float, int] - self::parameterSecNum_TestCases('min', $valueTypesNum), - self::parameterTimeShift_TestCases('min', $valueTypesNum, ['#1', ''], 1), - - // percentile() - (sec or #num, time_shift, float) [float, int] - self::parameterSecNum_TestCases('percentile', $valueTypesNum, ['#1', '', '50']), - self::parameterTimeShift_TestCases('percentile', $valueTypesNum, ['#1', '', '50'], 1), - self::parameterPercent_TestCases('percentile', $valueTypesNum, ['#1', '', '50'], 2), - - // strlen() - (sec or #num, time_shift) [str, text, log] - self::parameterSecNumOffset_TestCases('strlen', $valueTypesStr), - self::parameterTimeShift_TestCases('strlen', $valueTypesStr, ['#1', ''], 1), - - // sum() - (sec or #num, time_shift) [float, int] - self::parameterSecNum_TestCases('sum', $valueTypesNum), - self::parameterTimeShift_TestCases('sum', $valueTypesNum, ['#1', ''], 1), - - // fuzzytime() - (sec) [float, int] - self::parameterTimeShift_TestCases('fuzzytime', $valueTypesNum), - - // nodata() - (sec) [float, int, str, text, log] - self::parameterSec_TestCases('nodata', $valueTypesAny), - - // iregexp() - (string, sec or #num) [str, text, log] - self::parameterString_TestCases('iregexp', $valueTypesStr), - self::parameterSecNum_TestCases('iregexp', $valueTypesStr, ['', ''], 1), - - // logeventid() - (string) [log] - self::parameterString_TestCases('logeventid', $valueTypesLog), - - // logsource() - (string) [log] - self::parameterString_TestCases('logsource', $valueTypesLog), - - // regexp() - (string, sec or #num) [str, text, log] - self::parameterString_TestCases('regexp', $valueTypesStr), - self::parameterSecNum_TestCases('regexp', $valueTypesStr, ['', ''], 1), - - // str() - (string, sec or #num) [str, text, log] - self::parameterString_TestCases('str', $valueTypesStr), - self::parameterSecNum_TestCases('str', $valueTypesStr, ['', ''], 1), - - // 'trendavg', 'trendcount', 'trenddelta', 'trendmax', 'trendmin', 'trendsum' - self::trendFunctionsTestData() - ); - } - - /** - * @dataProvider provider - */ - public function test_parse($functionName, $functionParamList, $valueType, $options, $expectedResult) { - $triggerFunctionValidator = new C10FunctionValidator($options); - - $result = $triggerFunctionValidator->validate([ - 'function' => '', - 'functionName' => $functionName, - 'functionParamList' => $functionParamList, - 'valueType' => $valueType - ]); - - $this->assertSame($result, $expectedResult); - } -} -- cgit v1.2.3 From eea508e7ddc2a4a2bafd333452623e55525af7f8 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Wed, 7 Apr 2021 18:00:36 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed macros resolver --- ui/include/classes/macros/CMacrosResolver.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ui/include/classes/macros/CMacrosResolver.php b/ui/include/classes/macros/CMacrosResolver.php index 3bffcbbaea8..93e57253009 100644 --- a/ui/include/classes/macros/CMacrosResolver.php +++ b/ui/include/classes/macros/CMacrosResolver.php @@ -1067,12 +1067,14 @@ class CMacrosResolver extends CMacrosResolverGeneral { } $value = [bold($function['function'].'(')]; - if ($function['parameter'] === TRIGGER_QUERY_PLACEHOLDER) { - $value[] = $link; - } - elseif (substr($function['parameter'], 0, 1) === TRIGGER_QUERY_PLACEHOLDER) { + if (($pos = strpos($function['parameter'], TRIGGER_QUERY_PLACEHOLDER)) !== false) { + if ($pos != 0) { + $value[] = substr($function['parameter'], 0, $pos); + } $value[] = $link; - $value[] = substr($function['parameter'], 1); + if (strlen($function['parameter']) > $pos + 1) { + $value[] = substr($function['parameter'], $pos + 1); + } } else { $value[] = $function['parameter']; -- cgit v1.2.3 From 52b01b354c8aa8aaf0e38825be8fe109b85d5d80 Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Wed, 7 Apr 2021 20:07:36 +0300 Subject: ........S. [ZBXNEXT-6451] renamed OP_BAND define to OP_BITAND to match the renamed operator --- src/libs/zbxserver/evalfunc.c | 14 +++++++------- src/libs/zbxserver/evalfunc2.c | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libs/zbxserver/evalfunc.c b/src/libs/zbxserver/evalfunc.c index 9ab3b2b38c8..a03303c8a9d 100644 --- a/src/libs/zbxserver/evalfunc.c +++ b/src/libs/zbxserver/evalfunc.c @@ -424,7 +424,7 @@ out: #define OP_LIKE 6 #define OP_REGEXP 7 #define OP_IREGEXP 8 -#define OP_BAND 9 +#define OP_BITAND 9 #define OP_MAX 10 static void count_one_ui64(int *count, int op, zbx_uint64_t value, zbx_uint64_t pattern, zbx_uint64_t mask) @@ -455,7 +455,7 @@ static void count_one_ui64(int *count, int op, zbx_uint64_t value, zbx_uint64_t if (value <= pattern) (*count)++; break; - case OP_BAND: + case OP_BITAND: if ((value & mask) == pattern) (*count)++; } @@ -627,7 +627,7 @@ static int evaluate_COUNT(char **value, DC_ITEM *item, const char *parameters, c else if (0 == strcmp(arg3, "iregexp")) op = OP_IREGEXP; else if (0 == strcmp(arg3, "band")) - op = OP_BAND; + op = OP_BITAND; if (OP_UNKNOWN == op) { @@ -652,14 +652,14 @@ static int evaluate_COUNT(char **value, DC_ITEM *item, const char *parameters, c goto out; } - if (OP_BAND == op && ITEM_VALUE_TYPE_FLOAT == item->value_type) + if (OP_BITAND == op && ITEM_VALUE_TYPE_FLOAT == item->value_type) { *error = zbx_dsprintf(*error, "operator \"%s\" is not supported for counting float values", arg3); goto out; } - if (OP_BAND == op && NULL != (arg2_2 = strchr(arg2, '/'))) + if (OP_BITAND == op && NULL != (arg2_2 = strchr(arg2, '/'))) { *arg2_2 = '\0'; /* end of the 1st part of the 2nd parameter (number to compare with) */ arg2_2++; /* start of the 2nd part of the 2nd parameter (mask) */ @@ -669,7 +669,7 @@ static int evaluate_COUNT(char **value, DC_ITEM *item, const char *parameters, c { if (ITEM_VALUE_TYPE_UINT64 == item->value_type) { - if (OP_BAND != op) + if (OP_BITAND != op) { if (SUCCEED != str2uint64(arg2, ZBX_UNIT_SYMBOLS, &arg2_ui64)) { @@ -834,7 +834,7 @@ out: #undef OP_LIKE #undef OP_REGEXP #undef OP_IREGEXP -#undef OP_BAND +#undef OP_BITAND #undef OP_MAX /****************************************************************************** diff --git a/src/libs/zbxserver/evalfunc2.c b/src/libs/zbxserver/evalfunc2.c index 97e4fc8304a..4897f358d89 100644 --- a/src/libs/zbxserver/evalfunc2.c +++ b/src/libs/zbxserver/evalfunc2.c @@ -528,7 +528,7 @@ out: #define OP_LIKE 6 #define OP_REGEXP 7 #define OP_IREGEXP 8 -#define OP_BAND 9 +#define OP_BITAND 9 static void count_one_ui64(int *count, int op, zbx_uint64_t value, zbx_uint64_t pattern, zbx_uint64_t mask) { @@ -558,7 +558,7 @@ static void count_one_ui64(int *count, int op, zbx_uint64_t value, zbx_uint64_t if (value <= pattern) (*count)++; break; - case OP_BAND: + case OP_BITAND: if ((value & mask) == pattern) (*count)++; } @@ -724,7 +724,7 @@ static int evaluate_COUNT(zbx_variant_t *value, DC_ITEM *item, const char *param else if (0 == strcmp(operator, "iregexp")) op = OP_IREGEXP; else if (0 == strcmp(operator, "band")) - op = OP_BAND; + op = OP_BITAND; if (OP_UNKNOWN == op) { @@ -749,14 +749,14 @@ static int evaluate_COUNT(zbx_variant_t *value, DC_ITEM *item, const char *param goto out; } - if (OP_BAND == op && ITEM_VALUE_TYPE_FLOAT == item->value_type) + if (OP_BITAND == op && ITEM_VALUE_TYPE_FLOAT == item->value_type) { *error = zbx_dsprintf(*error, "operator \"%s\" is not supported for counting float values", operator); goto out; } - if (OP_BAND == op && NULL != (pattern2 = strchr(pattern, '/'))) + if (OP_BITAND == op && NULL != (pattern2 = strchr(pattern, '/'))) { *pattern2 = '\0'; /* end of the 1st part of the 2nd parameter (number to compare with) */ pattern2++; /* start of the 2nd part of the 2nd parameter (mask) */ @@ -766,7 +766,7 @@ static int evaluate_COUNT(zbx_variant_t *value, DC_ITEM *item, const char *param { if (ITEM_VALUE_TYPE_UINT64 == item->value_type) { - if (OP_BAND != op) + if (OP_BITAND != op) { if (SUCCEED != str2uint64(pattern, ZBX_UNIT_SYMBOLS, &pattern_ui64)) { @@ -938,7 +938,7 @@ out: #undef OP_LIKE #undef OP_REGEXP #undef OP_IREGEXP -#undef OP_BAND +#undef OP_BITAND /****************************************************************************** * * -- cgit v1.2.3 From 689143cfe23224db30d80af4b02a1753b49ccd6b Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Thu, 8 Apr 2021 10:21:00 +0300 Subject: A......... [ZBXNEXT-6451] fixed usage of trigger validator in trigger api --- ui/include/classes/api/services/CTriggerGeneral.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index a182a4afed4..389e500e792 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -1559,22 +1559,28 @@ abstract class CTriggerGeneral extends CApiService { foreach ($expressionData->result->getFunctions() as $fn) { $query = $fn->getFunctionTriggerQuery(); + // Validate trigger function. $fn_data = [ 'fn' => $fn, 'value_type' => ($query !== null) ? $hosts_keys[$query->host]['keys'][$query->item]['value_type'] : null ]; + $error_msg = ''; - foreach ([$math_function_validator, $trigger_function_validator] as $validator) { - if ($validator->validate($fn_data)) { - $error_msg = ''; - break; + if (!$math_function_validator->validate($fn_data)) { + $error_msg = $math_function_validator->getError(); + + if ($fn_data['value_type'] !== null + && (!$trigger_function_validator->validate($fn_data) + || !$trigger_function_validator->validateValueType($fn_data))) { + $error_msg = $trigger_function_validator->getError(); } else { - $error_msg = $validator->getError(); + $error_msg = ''; } } + if ($error_msg !== '') { self::exception(ZBX_API_ERROR_PARAMETERS, $error_msg); } -- cgit v1.2.3 From c64a431ca5f46e7957c941ac22d5e123c761a3ed Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Thu, 8 Apr 2021 12:25:58 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger macros resolved and function parser --- ui/include/classes/macros/CMacrosResolver.php | 28 ++++++++++---------------- ui/include/classes/parsers/CFunctionParser.php | 9 +++++---- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/ui/include/classes/macros/CMacrosResolver.php b/ui/include/classes/macros/CMacrosResolver.php index 93e57253009..e9ce6979243 100644 --- a/ui/include/classes/macros/CMacrosResolver.php +++ b/ui/include/classes/macros/CMacrosResolver.php @@ -1169,14 +1169,8 @@ class CMacrosResolver extends CMacrosResolverGeneral { $pos_left = 0; foreach ($expression_data->getTokens() as $token) { - if ($token instanceof CParserResult) { - $token_pos = $token->pos; - $token_length = $token->length; - } - else { - $token_pos = $token->pos; - $token_length = $token->length; - } + $token_pos = $token->pos; + $token_length = $token->length; if ($pos_left != $token_pos) { $expression[] = substr($source, $pos_left, $token_pos - $pos_left); @@ -1213,19 +1207,18 @@ class CMacrosResolver extends CMacrosResolverGeneral { private static function makeTriggerFunctionExpression(CFunctionParserResult $fn, array &$macro_values, array &$usermacro_values, array $options) { + $parameters_str = $fn->parameters; $left = $fn->params_raw['pos'] + $fn->pos + 1; $expression = []; - $parameters_str = $fn->parameters; for ($i = count($fn->params_raw['parameters']) - 1; $i >= 0; $i--) { $param = $fn->params_raw['parameters'][$i]; - if ($param instanceof CParserResult) { - $string_after = substr($parameters_str, $param->pos - $left + $param->length); - $parameters_str = substr($parameters_str, 0, $param->pos - $left); - array_unshift($expression, $string_after); - } + // Add substring located between parsed parameters (like comma and space). + array_unshift($expression, substr($parameters_str, $param->pos - $left + $param->length)); + $parameters_str = substr($parameters_str, 0, $param->pos - $left); + // Add parameter. if ($param instanceof CFunctionIdParserResult) { array_unshift($expression, $macro_values[$param->match]); } @@ -1234,11 +1227,12 @@ class CMacrosResolver extends CMacrosResolverGeneral { $options )); } + else { + array_unshift($expression, $param->match); + } } - if ($parameters_str) { - array_unshift($expression, $parameters_str); - } + array_unshift($expression, $parameters_str); $expression = array_filter($expression); array_unshift($expression, $options['html'] ? bold($fn->function.'(') : $fn->function.'('); diff --git a/ui/include/classes/parsers/CFunctionParser.php b/ui/include/classes/parsers/CFunctionParser.php index b654b4a651f..88bbfd4f0e7 100644 --- a/ui/include/classes/parsers/CFunctionParser.php +++ b/ui/include/classes/parsers/CFunctionParser.php @@ -173,7 +173,7 @@ class CFunctionParser extends CParser { $_parameters[$num++] = new CFunctionParameterResult([ 'type' => self::PARAM_UNQUOTED, 'match' => '', - 'pos' => $p - $pos + 'pos' => $p ]); break; @@ -181,7 +181,7 @@ class CFunctionParser extends CParser { $_parameters[$num] = new CFunctionParameterResult([ 'type' => self::PARAM_UNQUOTED, 'match' => '', - 'pos' => $p - $pos + 'pos' => $p ]); $state = self::STATE_END_OF_PARAMS; break; @@ -190,7 +190,8 @@ class CFunctionParser extends CParser { $_parameters[$num] = new CFunctionParameterResult([ 'type' => self::PARAM_QUOTED, 'match' => $source[$p], - 'pos' => $p - $pos + 'pos' => $p, + 'length' => 1 ]); $state = self::STATE_QUOTED; break; @@ -228,7 +229,7 @@ class CFunctionParser extends CParser { $_parameters[$num] = new CFunctionParameterResult([ 'type' => self::PARAM_UNQUOTED, 'match' => $source[$p], - 'pos' => $p - $pos, + 'pos' => $p, 'length' => 1 ]); } -- cgit v1.2.3 From b24ee6feaac67840623ce08702ec73fb0b2ee023 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Thu, 8 Apr 2021 13:33:50 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed period validation for trend functions --- ui/include/classes/validators/CFunctionValidator.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php index 9241e6c2fd9..8bf89527dcb 100644 --- a/ui/include/classes/validators/CFunctionValidator.php +++ b/ui/include/classes/validators/CFunctionValidator.php @@ -726,11 +726,6 @@ class CFunctionValidator extends CValidator { $period = strpos($precisions, substr($period_value, -1)); if ($period !== false) { - if (substr($period_shift_value, 0, 4) !== 'now/') { - return false; - } - $period_shift_value = substr($period_shift_value, 4); - $relative_time_parser = new CRelativeTimeParser(); if ($relative_time_parser->parse($period_shift_value) !== CParser::PARSE_SUCCESS) { return false; -- cgit v1.2.3 From 3a16f84d80682a267bc6bbfb909cac9553f2fba2 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Thu, 8 Apr 2021 14:29:53 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger expression converter for function 'change' --- .../classes/import/converters/C52TriggerExpressionConverter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index a6d2d6b7a40..0d8e2074b48 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -183,6 +183,10 @@ class C52TriggerExpressionConverter extends CConverter { $new_expression = sprintf('bitand(last(%1$s%2$s)%3$s)', $query, $timeshift, $mask); break; + case 'change': + $new_expression = sprintf('change(%1$s)', $query); + break; + case 'delta': $params = self::convertParameters($fn['functionParams'], $fn['functionName']); $params = self::paramsToString($params); -- cgit v1.2.3 From 36747078b142a84c4934b48ecd216a88741c20c8 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Thu, 8 Apr 2021 15:06:25 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed period parser --- ui/include/classes/parsers/CPeriodParser.php | 3 ++- ui/include/classes/validators/CFunctionValidator.php | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/include/classes/parsers/CPeriodParser.php b/ui/include/classes/parsers/CPeriodParser.php index 1bf56eef04a..3c7bb69304e 100644 --- a/ui/include/classes/parsers/CPeriodParser.php +++ b/ui/include/classes/parsers/CPeriodParser.php @@ -97,7 +97,8 @@ class CPeriodParser extends CParser { } } - if (count($parts) > 2) { + // Valid period consists of 1 or 2 non-empty parts. + if (count($parts) > 2 || $parts[0] === '' || (array_key_exists(1, $parts) && $parts[1] === '')) { return CParser::PARSE_FAIL; } diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php index 8bf89527dcb..a204d5f4124 100644 --- a/ui/include/classes/validators/CFunctionValidator.php +++ b/ui/include/classes/validators/CFunctionValidator.php @@ -347,8 +347,6 @@ class CFunctionValidator extends CValidator { return false; } - $parameter_value = $fn->params_raw['parameters'][$num]->getValue(); - if ($arg['mandat'] != 0x00 && !$this->validateParameter($fn->params_raw['parameters'][$num], $arg)) { $this->setError( _s('Incorrect trigger function "%1$s" provided in expression.', $fn->match).' '.$param_labels[$num] -- cgit v1.2.3 From cb81889d8cd3a6c3341f0f8d5b786873d6c63a7a Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Thu, 8 Apr 2021 15:07:38 +0300 Subject: ..F....... [ZBXNEXT-6451,ZBXNEXT-6455] added default value for the "context" option --- ui/include/classes/macros/CMacrosResolver.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/ui/include/classes/macros/CMacrosResolver.php b/ui/include/classes/macros/CMacrosResolver.php index e9ce6979243..49e3284a32a 100644 --- a/ui/include/classes/macros/CMacrosResolver.php +++ b/ui/include/classes/macros/CMacrosResolver.php @@ -863,6 +863,7 @@ class CMacrosResolver extends CMacrosResolverGeneral { * @param bool $options['resolve_functionids'] Resolve finctionid macros. Default: true. * @param array $options['sources'] An array of the field names. Default: ['expression']. * @param string $options['context'] Additional parameter in URL to identify main section. + * Default: 'host'. * * @return string|array */ @@ -872,7 +873,8 @@ class CMacrosResolver extends CMacrosResolverGeneral { 'resolve_usermacros' => false, 'resolve_macros' => false, 'resolve_functionids' => true, - 'sources' => ['expression'] + 'sources' => ['expression'], + 'context' => 'host' ]; $functionids = []; @@ -1038,10 +1040,7 @@ class CMacrosResolver extends CMacrosResolverGeneral { ->setArgument('form', 'update') ->setArgument('itemid', $function['itemid']) ->setArgument('parent_discoveryid', $function['parent_itemid']) - ->setArgument('context', array_key_exists('context', $options) - ? $options['context'] - : 'host' - ) + ->setArgument('context', $options['context']) )) ->addClass(ZBX_STYLE_LINK_ALT) ->addClass($style) @@ -1054,10 +1053,7 @@ class CMacrosResolver extends CMacrosResolverGeneral { (new CUrl('items.php')) ->setArgument('form', 'update') ->setArgument('itemid', $function['itemid']) - ->setArgument('context', array_key_exists('context', $options) - ? $options['context'] - : 'host' - ) + ->setArgument('context', $options['context']) )) ->addClass(ZBX_STYLE_LINK_ALT) ->setAttribute('data-itemid', $function['itemid']) -- cgit v1.2.3 From bed386ea84a4db578e042487490a3d3b876d9d08 Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Thu, 8 Apr 2021 15:11:41 +0300 Subject: ..F....... [ZBXNEXT-6451,ZBXNEXT-6455] fixed formatting issue discovered during code review --- ui/include/classes/macros/CMacrosResolver.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ui/include/classes/macros/CMacrosResolver.php b/ui/include/classes/macros/CMacrosResolver.php index 49e3284a32a..5b385260f35 100644 --- a/ui/include/classes/macros/CMacrosResolver.php +++ b/ui/include/classes/macros/CMacrosResolver.php @@ -1049,17 +1049,17 @@ class CMacrosResolver extends CMacrosResolverGeneral { } else { $link = CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS) - ? (new CLink('/'.$function['host'].'/'.$function['key_'], - (new CUrl('items.php')) - ->setArgument('form', 'update') - ->setArgument('itemid', $function['itemid']) - ->setArgument('context', $options['context']) - )) - ->addClass(ZBX_STYLE_LINK_ALT) - ->setAttribute('data-itemid', $function['itemid']) - ->addClass($style) - : (new CSpan('/'.$function['host'].'/'.$function['key_'])) - ->addClass($style); + ? (new CLink('/'.$function['host'].'/'.$function['key_'], + (new CUrl('items.php')) + ->setArgument('form', 'update') + ->setArgument('itemid', $function['itemid']) + ->setArgument('context', $options['context']) + )) + ->addClass(ZBX_STYLE_LINK_ALT) + ->setAttribute('data-itemid', $function['itemid']) + ->addClass($style) + : (new CSpan('/'.$function['host'].'/'.$function['key_'])) + ->addClass($style); } $value = [bold($function['function'].'(')]; -- cgit v1.2.3 From 23f04ca7567296dda0b30a7a5caa39bdf8ea622b Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Thu, 8 Apr 2021 17:23:26 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed attempt to get query from nested function --- ui/app/controllers/CControllerPopupTriggerExpr.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/controllers/CControllerPopupTriggerExpr.php b/ui/app/controllers/CControllerPopupTriggerExpr.php index bbd06ccdadd..eec2ef2382f 100644 --- a/ui/app/controllers/CControllerPopupTriggerExpr.php +++ b/ui/app/controllers/CControllerPopupTriggerExpr.php @@ -551,7 +551,7 @@ class CControllerPopupTriggerExpr extends CController { $param_values = []; foreach ($params as $i => $param) { if ($param instanceof CFunctionParserResult) { - $param_values[] = $param->getFunctionTriggerQuery()->getValue(); + continue; } elseif ($i == 0 && ($param instanceof CPeriodParserResult)) { $param_values[] = $is_num ? substr($param->sec_num, 1) : $param->sec_num; -- cgit v1.2.3 From 157709dfb69b13bacaf4d71b4ebc2a46a02e7ee3 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Thu, 8 Apr 2021 18:40:48 +0300 Subject: ..F....... [ZBXNEXT-6451] implemented dependent trigger conversion to new syntax --- ui/include/classes/import/converters/C52ImportConverter.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui/include/classes/import/converters/C52ImportConverter.php b/ui/include/classes/import/converters/C52ImportConverter.php index 97329051718..5d0edbcd543 100644 --- a/ui/include/classes/import/converters/C52ImportConverter.php +++ b/ui/include/classes/import/converters/C52ImportConverter.php @@ -336,6 +336,13 @@ class C52ImportConverter extends CConverter { } } + if (array_key_exists('dependencies', $trigger)) { + foreach ($trigger['dependencies'] as &$dep_trigger) { + $dep_trigger = $this->convertTrigger($dep_trigger); + } + unset($dep_trigger); + } + return $trigger; } } -- cgit v1.2.3 From e182f8adbd2278d79a45b167d8da171a768aac53 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 10:12:51 +0300 Subject: A.F....... [ZBXNEXT-6451] removed item value type being used to validate function parameters --- ui/app/controllers/CControllerPopupTriggerExpr.php | 17 ++++------- .../classes/api/services/CTriggerGeneral.php | 21 +++++-------- .../classes/validators/CFunctionValidator.php | 34 ++++++---------------- .../classes/validators/CMathFunctionValidator.php | 7 ++--- 4 files changed, 23 insertions(+), 56 deletions(-) diff --git a/ui/app/controllers/CControllerPopupTriggerExpr.php b/ui/app/controllers/CControllerPopupTriggerExpr.php index eec2ef2382f..5d51be2690f 100644 --- a/ui/app/controllers/CControllerPopupTriggerExpr.php +++ b/ui/app/controllers/CControllerPopupTriggerExpr.php @@ -727,11 +727,7 @@ class CControllerPopupTriggerExpr extends CController { if (($result = $trigger_expression->parse($data['expression'])) !== false) { // Validate trigger function. $trigger_function_validator = new CFunctionValidator(); - - $fn_data = [ - 'fn' => $result->getTokens()[0] - ]; - if (!$trigger_function_validator->validate($fn_data)) { + if (!$trigger_function_validator->validate($result->getTokens()[0])) { error($trigger_function_validator->getError()); } } @@ -835,17 +831,14 @@ class CControllerPopupTriggerExpr extends CController { // Validate trigger function. $math_function_validator = new CMathFunctionValidator(); $trigger_function_validator = new CFunctionValidator(); - $fn_data = [ - 'fn' => $result->getTokens()[0], - 'value_type' => $data['itemValueType'] - ]; + $fn = $result->getTokens()[0]; $error_msg = ''; - if (!$math_function_validator->validate($fn_data)) { + if (!$math_function_validator->validate($fn)) { $error_msg = $math_function_validator->getError(); - if (!$trigger_function_validator->validate($fn_data) - || !$trigger_function_validator->validateValueType($fn_data)) { + if (!$trigger_function_validator->validate($fn) + || !$trigger_function_validator->validateValueType($data['itemValueType'], $fn)) { $error_msg = $trigger_function_validator->getError(); } else { diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index 389e500e792..d81658a7bbf 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -1560,20 +1560,17 @@ abstract class CTriggerGeneral extends CApiService { $query = $fn->getFunctionTriggerQuery(); // Validate trigger function. - $fn_data = [ - 'fn' => $fn, - 'value_type' => ($query !== null) - ? $hosts_keys[$query->host]['keys'][$query->item]['value_type'] - : null - ]; + $value_type = ($query !== null) + ? $hosts_keys[$query->host]['keys'][$query->item]['value_type'] + : null; $error_msg = ''; - if (!$math_function_validator->validate($fn_data)) { + if (!$math_function_validator->validate($fn)) { $error_msg = $math_function_validator->getError(); - if ($fn_data['value_type'] !== null - && (!$trigger_function_validator->validate($fn_data) - || !$trigger_function_validator->validateValueType($fn_data))) { + if ($value_type !== null + && (!$trigger_function_validator->validate($fn) + || !$trigger_function_validator->validateValueType($value_type, $fn))) { $error_msg = $trigger_function_validator->getError(); } else { @@ -1600,10 +1597,6 @@ abstract class CTriggerGeneral extends CApiService { )); } - if (!$trigger_function_validator->validateValueType($fn_data)) { - self::exception(ZBX_API_ERROR_PARAMETERS, $trigger_function_validator->getError()); - } - if (!array_key_exists($fn->match, $triggers_functions[$tnum])) { // -1 for opening bracket. Should be 0 as long as query is first function's parameter. $pos_in_parameters_substr = $query->pos - $fn->params_raw['pos'] - $fn->pos - 1; diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php index a204d5f4124..52953d1f4b8 100644 --- a/ui/include/classes/validators/CFunctionValidator.php +++ b/ui/include/classes/validators/CFunctionValidator.php @@ -297,18 +297,13 @@ class CFunctionValidator extends CValidator { /** * Validate trigger function like last(0), time(), etc. * - * @param array $value - * @param CFunctionParserResult $value['fn'] - * @param int $value['value_type'] Used only to enable some parameters unquoted. + * @param CFunctionParserResult $fn * * @return bool */ - public function validate($value) { + public function validate($fn) { $this->setError(''); - $value_type = array_key_exists('value_type', $value) ? $value['value_type'] : ITEM_VALUE_TYPE_STR; - $fn = $value['fn']; - if (!array_key_exists($fn->function, $this->allowed)) { $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $fn->match).' '. _('Unknown function.')); @@ -340,7 +335,7 @@ class CFunctionValidator extends CValidator { continue; } - if (!$this->checkQuotes($arg['type'], $value_type, $fn->params_raw['parameters'][$num])) { + if (!$this->checkQuotes($arg['type'], $fn->params_raw['parameters'][$num])) { $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $fn->match).' '. $param_labels[$num] ); @@ -361,16 +356,15 @@ class CFunctionValidator extends CValidator { /** * Validate value type. * - * @param array $value - * @param CFunctionParserResult $value['fn'] - * @param int $value['value_type'] To check if function support particular type of values. + * @param int $value_type To check if function support particular type of values. + * @param CFunctionParserResult $fn * * @return bool */ - public function validateValueType(array $value): bool { - if (!array_key_exists($value['value_type'], $this->allowed[$value['fn']->function]['value_types'])) { + public function validateValueType(int $value_type, CFunctionParserResult $fn): bool { + if (!array_key_exists($value_type, $this->allowed[$fn->function]['value_types'])) { $this->setError(_s('Incorrect item value type "%1$s" provided for trigger function "%2$s".', - itemValueTypeString($value['value_type']), $value['fn']->match)); + itemValueTypeString($value_type), $fn->match)); return false; } @@ -381,12 +375,11 @@ class CFunctionValidator extends CValidator { * Check if parameter is properly quoted. * * @param string $type - * @param int $value_type * @param CParserResult $param * * @return bool */ - private function checkQuotes(string $type, int $value_type, CParserResult $parameter): bool { + private function checkQuotes(string $type, CParserResult $parameter): bool { if ($parameter->getValue() === '') { return true; } @@ -400,15 +393,6 @@ class CFunctionValidator extends CValidator { case 'sec_zero': return !self::isQuoted($parameter->getValue(true)); - // Mandatory quoted based on value type. - case 'pattern': - $support_unquoted = (ctype_digit((string) $parameter->getValue()) - && in_array($value_type, [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])); - if (!$support_unquoted && !self::isQuoted($parameter->getValue(true))) { - return false; - } - break; - // Optionally quoted. case 'percent': case 'num_suffix': diff --git a/ui/include/classes/validators/CMathFunctionValidator.php b/ui/include/classes/validators/CMathFunctionValidator.php index 4bae19d6af6..a3e10f4e622 100644 --- a/ui/include/classes/validators/CMathFunctionValidator.php +++ b/ui/include/classes/validators/CMathFunctionValidator.php @@ -104,16 +104,13 @@ class CMathFunctionValidator extends CValidator { /** * Validate trigger math function. * - * @param array $value - * @param CFunctionParserResult $value['fn'] + * @param CFunctionParserResult $fn * * @return bool */ - public function validate($value) { + public function validate($fn) { $this->setError(''); - $fn = $value['fn']; - if (!in_array($fn->function, $this->allowed)) { $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $fn->match).' '. _('Unknown function.')); -- cgit v1.2.3 From 5cb1ec476e6058570276fecb91dfccf9b1155200 Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Fri, 9 Apr 2021 11:08:11 +0300 Subject: .......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed name of the data provider --- ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php b/ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php index 7321390747d..15100200adf 100644 --- a/ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php +++ b/ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php @@ -26,7 +26,7 @@ class C10FunctionParserTest extends TestCase { /** * An array of trigger functions and parsed results. */ - public static function testProvider() { + public static function dataProviderParse() { return [ // valid keys [ @@ -518,7 +518,7 @@ class C10FunctionParserTest extends TestCase { } /** - * @dataProvider testProvider + * @dataProvider dataProviderParse * * @param string $source * @param int $pos -- cgit v1.2.3 From 455f58f1570fd805f916520df3ab4ba174b35b59 Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Fri, 9 Apr 2021 11:14:24 +0300 Subject: .......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed expected value in the C52TriggerExpressionConverterTest test case --- .../unit/include/triggers/C52TriggerExpressionConverterTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php index 824cabb6db8..9e4d9ab9ad0 100644 --- a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php +++ b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php @@ -193,9 +193,9 @@ class C52TriggerExpressionConverterTest extends TestCase { ' and {Trapper:trap[2].forecast(3600,7200,600,linear,avg)} > 0'. ' and {Trapper:trap[2].forecast(30m,1d,600,,avg)} > 0', - 'forecast(/Trapper/trap[2],#10,"100s") > 0'. - ' and forecast(/Trapper/trap[2],3600s:now-7200s,"600s","linear","avg") > 0'. - ' and forecast(/Trapper/trap[2],30m:now-1d,"600s",,"avg") > 0' + 'forecast(/Trapper/trap[2],#10,100s) > 0'. + ' and forecast(/Trapper/trap[2],3600s:now-7200s,600s,"linear","avg") > 0'. + ' and forecast(/Trapper/trap[2],30m:now-1d,600s,,"avg") > 0' ], [ -- cgit v1.2.3 From 8a2d1d3df7f1751dd11af140845668c771f9571d Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 11:40:28 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger expression converter --- .../classes/import/converters/C52TriggerExpressionConverter.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index 0d8e2074b48..180259988f4 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -393,9 +393,10 @@ class C52TriggerExpressionConverter extends CConverter { } private static function convertTimeshift(string $param): string { - return (preg_match('/^(?\d+)(?['.ZBX_TIME_SUFFIXES.']{0,1})$/', $param, $m) && $m['num'] > 0) - ? 'now-'.$m['num'].($m['suffix'] !== '' ? $m['suffix'] : 's') + $param = (preg_match('/^(?\d+)(?['.ZBX_TIME_SUFFIXES.']{0,1})$/', $param, $m) && $m['num'] > 0) + ? $m['num'].($m['suffix'] !== '' ? $m['suffix'] : 's') : $param; + return 'now-'.$param; } private static function paramsToString(array $parameters): string { -- cgit v1.2.3 From a85f623e2e49e66c1bdce6002ddd6a0080d25124 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 11:43:45 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger expression converter --- ui/include/classes/import/converters/C52TriggerExpressionConverter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index 180259988f4..fdb829281cf 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -396,7 +396,7 @@ class C52TriggerExpressionConverter extends CConverter { $param = (preg_match('/^(?\d+)(?['.ZBX_TIME_SUFFIXES.']{0,1})$/', $param, $m) && $m['num'] > 0) ? $m['num'].($m['suffix'] !== '' ? $m['suffix'] : 's') : $param; - return 'now-'.$param; + return ($param !== '') ? 'now-'.$param : ''; } private static function paramsToString(array $parameters): string { -- cgit v1.2.3 From 013ee0a357210a8483b1751c9e9ffd0facd2e2cf Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 13:00:41 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger wizard expression word wrapping --- ui/app/views/popup.triggerwizard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/views/popup.triggerwizard.php b/ui/app/views/popup.triggerwizard.php index eb340fbcc14..28286a698a9 100644 --- a/ui/app/views/popup.triggerwizard.php +++ b/ui/app/views/popup.triggerwizard.php @@ -43,7 +43,7 @@ if (array_key_exists('triggerid', $options)) { $expression_table = (new CTable()) ->addClass('ui-sortable') ->setId('expressions_list') - ->setAttribute('style', 'width: 100%;') + ->addStyle('width: 100%; white-space: normal;') ->setHeader(['', _('Expression'), _('Type'), _('Action')]); $expressions = []; -- cgit v1.2.3 From a370dc5e69a20fc50cbf5cbe239cfe75134b11ce Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 13:18:42 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger wizard expression word wrapping --- ui/app/views/popup.triggerwizard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/views/popup.triggerwizard.php b/ui/app/views/popup.triggerwizard.php index 28286a698a9..937291618d3 100644 --- a/ui/app/views/popup.triggerwizard.php +++ b/ui/app/views/popup.triggerwizard.php @@ -43,7 +43,7 @@ if (array_key_exists('triggerid', $options)) { $expression_table = (new CTable()) ->addClass('ui-sortable') ->setId('expressions_list') - ->addStyle('width: 100%; white-space: normal;') + ->addStyle('width: 100%; white-space: normal; overflow-wrap: break-word;') ->setHeader(['', _('Expression'), _('Type'), _('Action')]); $expressions = []; -- cgit v1.2.3 From 17a244ea0bed0d7bdbec9ed201b245a4e6461a87 Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Fri, 9 Apr 2021 14:50:54 +0300 Subject: .......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed triggerExpressionReplaceHostTest test cases --- ui/include/triggers.inc.php | 2 +- .../include/triggerExpressionReplaceHostTest.php | 45 +++++++++++----------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/ui/include/triggers.inc.php b/ui/include/triggers.inc.php index 426b2894d71..eced214eff2 100644 --- a/ui/include/triggers.inc.php +++ b/ui/include/triggers.inc.php @@ -592,7 +592,7 @@ function triggerExpressionReplaceHost(string $expression, string $src_host, stri $trigger_expression_parser = new CTriggerExpression(); if (($result = $trigger_expression_parser->parse($expression)) !== false) { $queries = $result->getTokensOfTypes([CTriggerExprParserResult::TOKEN_TYPE_QUERY]); - for ($i = count($queries)-1; $i >= 0; $i--) { + for ($i = count($queries) - 1; $i >= 0; $i--) { if ($queries[$i]->host === $src_host) { $expression = substr_replace($expression, '/'.$dst_host.'/'.$queries[$i]->item, $queries[$i]->pos, $queries[$i]->length diff --git a/ui/tests/unit/include/triggerExpressionReplaceHostTest.php b/ui/tests/unit/include/triggerExpressionReplaceHostTest.php index fae85f61ed0..237b9777bf0 100644 --- a/ui/tests/unit/include/triggerExpressionReplaceHostTest.php +++ b/ui/tests/unit/include/triggerExpressionReplaceHostTest.php @@ -29,39 +29,40 @@ class triggerExpressionReplaceHostTest extends TestCase { public static function dataProvider() { return [ [ - '{host:item.func()}', + 'func(/host/item)', 'host', 'Zabbix server', - '{Zabbix server:item.func()}' + 'func(/Zabbix server/item)' ], [ - '5 + {host:item.func()} <> 0 or {$MACRO: "context"} or {#MACRO} or {TRIGGER.VALUE} or'. - ' {host:item.func()} or {host2:item2.func()}', - 'host', - 'Zabbix server', - '5 + {Zabbix server:item.func()} <> 0 or {$MACRO: "context"} or {#MACRO} or {TRIGGER.VALUE} or'. - ' {Zabbix server:item.func()} or {host2:item2.func()}' + '5 + func(/host/item) <> 0 or {$MACRO: "context"} or {#MACRO} or {TRIGGER.VALUE} or'. + ' func(/host/item) or func(/host2/item2)', + 'host', 'Zabbix server', + '5 + func(/Zabbix server/item) <> 0 or {$MACRO: "context"} or {#MACRO} or {TRIGGER.VALUE} or'. + ' func(/Zabbix server/item) or func(/host2/item2)' ], [ - '5 + {host:item.func()} <> 0 or {$MACRO: "context"} or'. + '5 + func(/host/item) <> 0 or {$MACRO: "context"} or'. ' {{#MACRO}.regsub("^([0-9]+)", "{#MACRO}: \1")} or {TRIGGER.VALUE} or'. - ' {host:item.func()} or {host2:item2.func()}', - 'host', - 'Zabbix server', - '5 + {Zabbix server:item.func()} <> 0 or {$MACRO: "context"} or'. + ' func(/host/item) or func(/host2/item2)', + 'host', 'Zabbix server', + '5 + func(/Zabbix server/item) <> 0 or {$MACRO: "context"} or'. ' {{#MACRO}.regsub("^([0-9]+)", "{#MACRO}: \1")} or {TRIGGER.VALUE} or'. - ' {Zabbix server:item.func()} or {host2:item2.func()}' + ' func(/Zabbix server/item) or func(/host2/item2)' + ], + [ + 'func(/host/item) or {{#M}.regsub("func(/host/item)", "\1")}', + 'host', 'Zabbix server', + 'func(/Zabbix server/item) or {{#M}.regsub("func(/host/item)", "\1")}' ], [ - '{host:item.func()} or {{#M}.regsub("{host:item.func()}", "\1")}', - 'host', - 'Zabbix server', - '{Zabbix server:item.func()} or {{#M}.regsub("{host:item.func()}", "\1")}' + '5 + func(/Zabbix server/item) <> 0 or func(/Zabbix server/item) or func(/host2/item2)', + 'Zabbix server', 'host', + '5 + func(/host/item) <> 0 or func(/host/item) or func(/host2/item2)' ], [ - '5 + {Zabbix server:item.func()} <> 0 or {Zabbix server:item.func()} or {host2:item2.func()}', - 'Zabbix server', - 'host', - '5 + {host:item.func()} <> 0 or {host:item.func()} or {host2:item2.func()}' + 'min(func(/host/item), func(/host/item), "func(/host/item)") = "func(/host/item)"', + 'host', 'Zabbix server', + 'min(func(/Zabbix server/item), func(/Zabbix server/item), "func(/host/item)") = "func(/host/item)"' ] ]; } -- cgit v1.2.3 From 68eace74bb91d04c994c590d94231ef425c2bcc2 Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Fri, 9 Apr 2021 14:56:46 +0300 Subject: .......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed CApiInputValidatorTest test cases --- .../classes/validators/CApiInputValidatorTest.php | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php index 2252016e499..a5e6c8954cb 100644 --- a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php +++ b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php @@ -3523,15 +3523,15 @@ class CApiInputValidatorTest extends TestCase { ], [ ['type' => API_TRIGGER_EXPRESSION, 'length' => 10], - '{host:item.last()} = 0', + 'last(/host/item) = 0', '/1/expression', 'Invalid parameter "/1/expression": value is too long.' ], [ ['type' => API_TRIGGER_EXPRESSION], - '{host:item.last() = 0', + 'last(/host/item = 0', '/1/expression', - 'Invalid parameter "/1/expression": incorrect trigger expression starting from "{host:item.last() = 0".' + 'Invalid parameter "/1/expression": incorrect trigger expression starting from "last(/host/item = 0".' ], [ ['type' => API_TRIGGER_EXPRESSION], @@ -3541,27 +3541,27 @@ class CApiInputValidatorTest extends TestCase { ], [ ['type' => API_TRIGGER_EXPRESSION], - '{host:item.last()} = {#LLD_MACRO}', + 'last(/host/item) = {#LLD_MACRO}', '/1/expression', 'Invalid parameter "/1/expression": incorrect trigger expression starting from " {#LLD_MACRO}".' ], [ ['type' => API_TRIGGER_EXPRESSION, 'flags' => API_ALLOW_LLD_MACRO], - '{host:item.last()} = {#LLD_MACRO}', + 'last(/host/item) = {#LLD_MACRO}', '/1/expression', - '{host:item.last()} = {#LLD_MACRO}' + 'last(/host/item) = {#LLD_MACRO}' ], [ ['type' => API_TRIGGER_EXPRESSION], - '{host:item.last()} = 0', + 'last(/host/item) = 0', '/1/expression', - '{host:item.last()} = 0' + 'last(/host/item) = 0' ], [ ['type' => API_TRIGGER_EXPRESSION], - '{host:item.last()} = {$USER_MACRO}', + 'last(/host/item) = {$USER_MACRO}', '/1/expression', - '{host:item.last()} = {$USER_MACRO}' + 'last(/host/item) = {$USER_MACRO}' ], [ ['type' => API_TRIGGER_EXPRESSION], @@ -3607,15 +3607,15 @@ class CApiInputValidatorTest extends TestCase { ], [ ['type' => API_EVENT_NAME], - 'event name {?{host:item.last()} = 0}', + 'event name {?last(/host/item) = 0}', '/1/event_name', - 'event name {?{host:item.last()} = 0}' + 'event name {?last(/host/item) = 0}' ], [ ['type' => API_EVENT_NAME], - 'event name {?{host:item.last()} = {$USER_MACRO}}', + 'event name {?last(/host/item) = {$USER_MACRO}}', '/1/event_name', - 'event name {?{host:item.last()} = {$USER_MACRO}}' + 'event name {?last(/host/item) = {$USER_MACRO}}' ], [ ['type' => API_EVENT_NAME], -- cgit v1.2.3 From 94cab9316def369e62eaa04e423141082a2d27fd Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Fri, 9 Apr 2021 15:32:15 +0300 Subject: .......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed CEventNameValidatorTest test cases --- ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php b/ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php index 9c96af5419e..ec025e0f777 100644 --- a/ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php +++ b/ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php @@ -42,7 +42,7 @@ class CEventNameValidatorTest extends TestCase { ['Incorrect macro except expression macro are ignored {ANY_MACRO_HERE_ {}', true, null], ['Simple expression macro {?100+1} test', true, null], ['Expression macro with modificator {{?100+1-(2)}.anyfunc(2)}', true, null], - ['Macro as host name {?{{HOST.HOST}:item.func(1)}}', true, null], + ['Macro as host name {?func(/{HOST.HOST}/item)}', true, null], ['Expression macro with incorrect syntax {?123++321}', false, 'incorrect syntax near "+321}"'], ['{?Expression macro without closing bracket', false, 'incorrect syntax near "Expression macro without closing bracket"'], ['Expression macro without closing bracket at the end of event name {?', false, 'unexpected end of string'], -- cgit v1.2.3 From 58974267f9ae056a92ae4c03e3e3f8f2d16eb68b Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 15:58:14 +0300 Subject: A.F....... [ZBXNEXT-6451] fixed expression converter; fixed math function validation; fixed error reporting in API when math function validator returns false --- .../classes/api/services/CTriggerGeneral.php | 10 ++++---- .../converters/C52TriggerExpressionConverter.php | 6 ++++- .../classes/validators/CMathFunctionValidator.php | 28 +++++++++++++++------- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index d81658a7bbf..4698543b3db 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -1569,13 +1569,13 @@ abstract class CTriggerGeneral extends CApiService { $error_msg = $math_function_validator->getError(); if ($value_type !== null - && (!$trigger_function_validator->validate($fn) - || !$trigger_function_validator->validateValueType($value_type, $fn))) { - $error_msg = $trigger_function_validator->getError(); - } - else { + && $trigger_function_validator->validate($fn) + && $trigger_function_validator->validateValueType($value_type, $fn)) { $error_msg = ''; } + elseif ($value_type !== null) { + $error_msg = $trigger_function_validator->getError(); + } } if ($error_msg !== '') { diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index fdb829281cf..f18332c0d80 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -360,10 +360,14 @@ class C52TriggerExpressionConverter extends CConverter { ]; $unquotable_parameters = in_array($fn_name, $functions_with_period_parameter) ? [0] : []; - // Time parameter don't need to be quoted for forecast() function. if ($fn_name === 'forecast') { + // Time parameter don't need to be quoted for forecast() function. $unquotable_parameters[] = 2; } + elseif ($fn_name === 'band') { + // Mask parameter don't need to be quoted for bitand() function. + $unquotable_parameters[] = 1; + } array_walk($parameters, function (&$param, $i) use ($unquotable_parameters) { if (in_array($i, $unquotable_parameters)) { diff --git a/ui/include/classes/validators/CMathFunctionValidator.php b/ui/include/classes/validators/CMathFunctionValidator.php index a3e10f4e622..7cc54ded8b3 100644 --- a/ui/include/classes/validators/CMathFunctionValidator.php +++ b/ui/include/classes/validators/CMathFunctionValidator.php @@ -127,17 +127,16 @@ class CMathFunctionValidator extends CValidator { foreach ($fn->params_raw['parameters'] as $param) { if ($param instanceof CQueryParserResult) { - $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $param->match)); + $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $fn->match)); + return false; } elseif ($param instanceof CFunctionParserResult) { continue; } - elseif (!$this->user_macro_parser->parse($param->match) - && $this->number_parser->parse($param->match) - && $this->checkString($param->match) - && (!$this->lldmacros || $this->lld_macro_parser->parse($param->match))) { - $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $param->match)); + elseif (!$this->checkValidConstant($param->getValue(true))) { + $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $fn->match)); + return false; } } @@ -145,7 +144,20 @@ class CMathFunctionValidator extends CValidator { return true; } - private function checkString(string $param): bool { - return preg_match('/^"([^"\\\\]|\\\\["\\\\])*"/', $param); + /** + * Check if parameter is valid constant. + * + * @param string $param + * + * @return bool + */ + private function checkValidConstant(string $param): bool { + if ($this->user_macro_parser->parse($param) == CParser::PARSE_SUCCESS + || $this->number_parser->parse($param) == CParser::PARSE_SUCCESS + || ($this->lldmacros && $this->lld_macro_parser->parse($param) != CParser::PARSE_SUCCESS)) { + return true; + } + + return false; } } -- cgit v1.2.3 From 13e504858abb7658059b437094fb69dcfe1eba7d Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 16:36:50 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed representation of selected /host/key reference in trigger wizard overlay popup --- ui/app/controllers/CControllerPopupTriggerWizard.php | 7 +++++-- ui/app/views/popup.triggerwizard.php | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ui/app/controllers/CControllerPopupTriggerWizard.php b/ui/app/controllers/CControllerPopupTriggerWizard.php index de43dfcaca6..32aabe6c3a9 100644 --- a/ui/app/controllers/CControllerPopupTriggerWizard.php +++ b/ui/app/controllers/CControllerPopupTriggerWizard.php @@ -294,13 +294,16 @@ class CControllerPopupTriggerWizard extends CController { if ($page_options['itemid']) { $items = API::Item()->get([ 'output' => ['itemid', 'hostid', 'key_', 'name'], - 'selectHosts' => ['name'], + 'selectHosts' => ['name', 'host'], 'itemids' => $page_options['itemid'] ]); if ($items) { $items = CMacrosResolverHelper::resolveItemNames($items); - $page_options['item_name'] = $items[0]['hosts'][0]['name'].NAME_DELIMITER.$items[0]['name_expanded']; + $page_options = [ + 'query' => '/'.$items[0]['hosts'][0]['host'].'/'.$items[0]['key_'], + 'item_name' => $items[0]['hosts'][0]['name'].NAME_DELIMITER.$items[0]['name_expanded'] + ] + $page_options; } } diff --git a/ui/app/views/popup.triggerwizard.php b/ui/app/views/popup.triggerwizard.php index 937291618d3..4bc5d139e74 100644 --- a/ui/app/views/popup.triggerwizard.php +++ b/ui/app/views/popup.triggerwizard.php @@ -64,7 +64,11 @@ $ms_itemid = (new CMultiSelect([ 'name' => 'itemid', 'object_name' => 'items', 'multiple' => false, - 'data' => [['id' => $options['itemid'], 'name' => $options['item_name']]], + 'data' => [[ + 'id' => $options['itemid'], + 'name' => $options['item_name'], + 'query' => $options['query'] + ]], 'popup' => [ 'parameters' => [ 'srctbl' => 'items', -- cgit v1.2.3 From 84800f1cff84f4df210f34136122ebaf54c7819e Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 16:49:54 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger conversion --- .../classes/import/converters/C52TriggerExpressionConverter.php | 4 ++-- .../unit/include/triggers/C52TriggerExpressionConverterTest.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index f18332c0d80..e3848d43d6c 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -194,11 +194,11 @@ class C52TriggerExpressionConverter extends CConverter { break; case 'diff': - $new_expression = sprintf('(last(%1$s,1)<>last(%1$s,2))', $query); + $new_expression = sprintf('(last(%1$s,#1)<>last(%1$s,#2))', $query); break; case 'prev': - $new_expression = sprintf('last(%1$s,2)', $query); + $new_expression = sprintf('last(%1$s,#2)', $query); break; case 'trenddelta': diff --git a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php index 9e4d9ab9ad0..9d4ffa34106 100644 --- a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php +++ b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php @@ -88,7 +88,7 @@ class C52TriggerExpressionConverterTest extends TestCase { ], [ '{Trapper:trap[1].diff()} = 0', - '(last(/Trapper/trap[1],1)<>last(/Trapper/trap[1],2)) = 0' + '(last(/Trapper/trap[1],#1)<>last(/Trapper/trap[1],#2)) = 0' ], [ '{Trapper:trap[1].fuzzytime(60)} > 0', @@ -186,7 +186,7 @@ class C52TriggerExpressionConverterTest extends TestCase { ], [ '{Trapper:trap[2].band(#1, 32)} > 0 and {Trapper:trap[2].band(#2, 64, 1h)} > 0', - 'bitand(last(/Trapper/trap[2],#1),"32") > 0 and bitand(last(/Trapper/trap[2],#2:now-1h),"64") > 0' + 'bitand(last(/Trapper/trap[2],#1),32) > 0 and bitand(last(/Trapper/trap[2],#2:now-1h),64) > 0' ], [ '{Trapper:trap[2].forecast(#10,,100)} > 0'. @@ -246,7 +246,7 @@ class C52TriggerExpressionConverterTest extends TestCase { ], [ '{Trapper:trap[3].prev()} > 0', - 'last(/Trapper/trap[3],2) > 0' + 'last(/Trapper/trap[3],#2) > 0' ], [ '{Trapper:trap[3].regexp("^error", #10)} > 0'. -- cgit v1.2.3 From 94a5bcef3f73dce836f05c1bb88a479c7fadddf8 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Fri, 9 Apr 2021 17:23:46 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed quoting of percent and num_suffixed parameters --- .../import/converters/C52TriggerExpressionConverter.php | 2 +- ui/include/classes/validators/CFunctionValidator.php | 7 ++----- .../triggers/C52TriggerExpressionConverterTest.php | 16 ++++++++-------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index e3848d43d6c..ad3adc33a27 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -360,7 +360,7 @@ class C52TriggerExpressionConverter extends CConverter { ]; $unquotable_parameters = in_array($fn_name, $functions_with_period_parameter) ? [0] : []; - if ($fn_name === 'forecast') { + if (in_array($fn_name, ['forecast', 'timeleft', 'percentile'])) { // Time parameter don't need to be quoted for forecast() function. $unquotable_parameters[] = 2; } diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php index 52953d1f4b8..ffbc301a0f9 100644 --- a/ui/include/classes/validators/CFunctionValidator.php +++ b/ui/include/classes/validators/CFunctionValidator.php @@ -391,12 +391,9 @@ class CFunctionValidator extends CValidator { case 'period': case 'sec_neg': case 'sec_zero': - return !self::isQuoted($parameter->getValue(true)); - - // Optionally quoted. - case 'percent': case 'num_suffix': - return true; + case 'percent': + return !self::isQuoted($parameter->getValue(true)); // Mandatory quoted. default: diff --git a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php index 9d4ffa34106..82e532b0270 100644 --- a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php +++ b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php @@ -137,11 +137,11 @@ class C52TriggerExpressionConverterTest extends TestCase { ' and {Trapper:trap[1].percentile(60,3600,80)} > 4'. ' and {Trapper:trap[1].percentile(1m,1h,90)} > 5', - 'percentile(/Trapper/trap[1],30m,"50") > 0'. - ' and percentile(/Trapper/trap[1],60s,"60") > 1'. - ' and percentile(/Trapper/trap[1],#10,"70") > 3'. - ' and percentile(/Trapper/trap[1],60s:now-3600s,"80") > 4'. - ' and percentile(/Trapper/trap[1],1m:now-1h,"90") > 5' + 'percentile(/Trapper/trap[1],30m,50) > 0'. + ' and percentile(/Trapper/trap[1],60s,60) > 1'. + ' and percentile(/Trapper/trap[1],#10,70) > 3'. + ' and percentile(/Trapper/trap[1],60s:now-3600s,80) > 4'. + ' and percentile(/Trapper/trap[1],1m:now-1h,90) > 5' ], [ '{Trapper:trap[1].sum(30m)} > 0'. @@ -203,9 +203,9 @@ class C52TriggerExpressionConverterTest extends TestCase { ' and {Trapper:trap[2].timeleft(3600,7200,600,linear)} > 0'. ' and {Trapper:trap[2].timeleft(30m,1d,600)} > 0', - 'timeleft(/Trapper/trap[2],#10,"100") > 0'. - ' and timeleft(/Trapper/trap[2],3600s:now-7200s,"600","linear") > 0'. - ' and timeleft(/Trapper/trap[2],30m:now-1d,"600") > 0' + 'timeleft(/Trapper/trap[2],#10,100) > 0'. + ' and timeleft(/Trapper/trap[2],3600s:now-7200s,600,"linear") > 0'. + ' and timeleft(/Trapper/trap[2],30m:now-1d,600) > 0' ], [ '{Trapper:trap[3].count(#1, 0, eq)} > 0'. -- cgit v1.2.3 From 7b50a0acd3b19854fc37670afdfab07b316894cf Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Fri, 9 Apr 2021 21:32:36 +0300 Subject: ....I..... [ZBXNEXT-6451] added database patch to convert simple macros in expression macros to function calls --- include/common.h | 1 + src/libs/zbxcommon/str.c | 24 +++ src/libs/zbxdbupgrade/dbupgrade_5030.c | 295 ++++++++++++++++++++++++++++++++- 3 files changed, 317 insertions(+), 3 deletions(-) diff --git a/include/common.h b/include/common.h index aab2d3cf4cc..b7f3bde2b46 100644 --- a/include/common.h +++ b/include/common.h @@ -1706,5 +1706,6 @@ int zbx_check_xml_memory(char *mem, int maxerrlen, char **errmsg); #endif char *zbx_substr_unquote(const char *src, size_t left, size_t right); +char *zbx_substr(const char *src, size_t left, size_t right); #endif diff --git a/src/libs/zbxcommon/str.c b/src/libs/zbxcommon/str.c index 39283ca54e5..87bb5d57834 100644 --- a/src/libs/zbxcommon/str.c +++ b/src/libs/zbxcommon/str.c @@ -6024,3 +6024,27 @@ char *zbx_substr_unquote(const char *src, size_t left, size_t right) return str; } + +/****************************************************************************** + * * + * Function: zbx_substr * + * * + * Purpose: extracts substring at the specified location * + * * + * Parameters: src - [IN] the source string * + * left - [IN] the left substring position 9start) * + * right - [IN] the right substirng position (end) * + * * + * Return value: The unquoted and copied substring. * + * * + ******************************************************************************/ +char *zbx_substr(const char *src, size_t left, size_t right) +{ + char *str; + + str = zbx_malloc(NULL, right - left + 2); + memcpy(str, src + left, right - left + 1); + str[right - left + 1] = '\0'; + + return str; +} diff --git a/src/libs/zbxdbupgrade/dbupgrade_5030.c b/src/libs/zbxdbupgrade/dbupgrade_5030.c index 6b4a9a59985..8ce1e279c70 100644 --- a/src/libs/zbxdbupgrade/dbupgrade_5030.c +++ b/src/libs/zbxdbupgrade/dbupgrade_5030.c @@ -2335,6 +2335,21 @@ static void dbpatch_convert_function(zbx_dbpatch_function_t *function, char **re zbx_vector_loc_destroy(¶ms); } +static int dbpatch_is_time_function(const char *name, size_t len) +{ + const char *functions[] = {"date", "dayofmonth", "dayofweek", "now", "time", NULL}, **func; + size_t func_len; + + for (func = functions; NULL != *func; func++) + { + func_len = strlen(*func); + if (func_len == len && 0 == memcmp(*func, name, len)) + return SUCCEED; + } + + return FAIL; +} + /****************************************************************************** * * * Function: dbpatch_convert_trigger * @@ -2381,9 +2396,7 @@ static int dbpatch_convert_trigger(zbx_dbpatch_trigger_t *trigger, zbx_vector_pt ZBX_STR2UINT64(itemid, row[1]); ZBX_STR2UINT64(hostid, row[4]); - if (0 == strcmp(row[2], "date") || 0 == strcmp(row[2], "dayofmonth") || - 0 == strcmp(row[2], "dayofweek") || 0 == strcmp(row[2], "now") || - 0 == strcmp(row[2], "time")) + if (SUCCEED == dbpatch_is_time_function(row[2], strlen(row[2]))) { char func_name[FUNCTION_NAME_LEN * 4 + 1]; @@ -2636,6 +2649,281 @@ static int DBpatch_5030082(void) return SUCCEED; } +/****************************************************************************** + * * + * Function: dbpatch_replace_functionids * + * * + * Purpose: replace functionids {} in expression * + * with their string format * + * * + * Parameters: expression - [IN/OUT] the expression * + * functions - [IN] the functions * + * * + ******************************************************************************/ +static void dbpatch_replace_functionids(char **expression, const zbx_vector_ptr_t *functions) +{ + zbx_uint64_t index; + int pos = 0, last_pos = 0; + zbx_token_t token; + char *out = NULL; + size_t out_alloc = 0, out_offset = 0; + + for (; SUCCEED == zbx_token_find(*expression, pos, &token, ZBX_TOKEN_SEARCH_FUNCTIONID); pos++) + { + switch (token.type) + { + case ZBX_TOKEN_OBJECTID: + if (SUCCEED == is_uint64_n(*expression + token.loc.l + 1, + token.loc.r - token.loc.l - 1, &index) && + (int)index < functions->values_num) + { + zbx_dbpatch_function_t *func = functions->values[index]; + + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, + *expression + last_pos, token.loc.l - last_pos); + + zbx_snprintf_alloc(&out, &out_alloc, &out_offset, "%s(%s", + func->name, func->arg0); + if ('\0' != *func->parameter) + { + zbx_chrcpy_alloc(&out, &out_alloc, &out_offset, ','); + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, func->parameter); + } + zbx_chrcpy_alloc(&out, &out_alloc, &out_offset, ')'); + last_pos = token.loc.r + 1; + } + pos = token.loc.r; + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + break; + } + } + + if (0 != out_alloc) + { + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, *expression + last_pos); + zbx_free(*expression); + *expression = out; + } +} + +/****************************************************************************** + * * + * Function: dbpatch_convert_simple_macro * + * * + * Purpose: convert simple macro {host.key:func(params)} to the new syntax * + * func(/host/key,params) * + * * + * Parameters: expression - [IN] the expression with simple macro * + * data - [IN] the simple macro token data * + * function - [OUT] the simple macro replacement function * + * * + ******************************************************************************/ +static void dbpatch_convert_simple_macro(const char *expression, const zbx_token_simple_macro_t *data, + char **function) +{ + zbx_dbpatch_function_t *func; + zbx_vector_ptr_t functions; + char *name, *host, *key; + + name = zbx_substr(expression, data->func.l, data->func_param.l - 1); + + if (SUCCEED == dbpatch_is_time_function(name, strlen(name))) + { + *function = zbx_dsprintf(NULL, "%s()", name); + zbx_free(name); + return; + } + + zbx_vector_ptr_create(&functions); + + func = (zbx_dbpatch_function_t *)zbx_malloc(NULL, sizeof(zbx_dbpatch_function_t)); + func->functionid = 0; + func->itemid = 0; + func->flags = 0; + func->name = name; + func->parameter = zbx_substr(expression, data->func_param.l + 1, data->func_param.r -1); + + host = zbx_substr(expression, data->host.l, data->host.r); + key = zbx_substr(expression, data->key.l, data->key.r); + + if (0 == strcmp(host, "{HOST.HOST}")) + func->arg0 = zbx_dsprintf(NULL, "//%s", key); + else + func->arg0 = zbx_dsprintf(NULL, "/%s/%s", host, key); + + zbx_vector_ptr_append(&functions, func); + + dbpatch_convert_function(func, function, &functions); + if (NULL == *function) + *function = zbx_strdup(NULL, "{0}"); + dbpatch_replace_functionids(function, &functions); + + zbx_free(key); + zbx_free(host); + zbx_vector_ptr_clear_ext(&functions, (zbx_clean_func_t)dbpatch_function_free); + zbx_vector_ptr_destroy(&functions); +} + +/****************************************************************************** + * * + * Function: dbpatch_convert_expression_macro * + * * + * Purpose: convert simple macros in expression macro {? } to function calls * + * using new expression syntax * + * * + * Parameters: expression - [IN] the original expression * + * loc - [IN] the macro location within expression * + * replace - [OUT] the expression macro replacement expression * + * * + * Return value: SUCCEED - expression macro was converted * + * FAIL - expression macro does not contain simple macros * + * * + ******************************************************************************/ +static int dbpatch_convert_expression_macro(const char *expression, const zbx_strloc_t *loc, char **replace) +{ + int pos = loc->l + 2; + zbx_token_t token; + char *out = NULL; + size_t out_alloc = 0, out_offset = 0, last_pos = loc->l; + + for (; SUCCEED == zbx_token_find(expression, pos, &token, ZBX_TOKEN_SEARCH_BASIC) && token.loc.r < loc->r; pos++) + { + char *macro = NULL; + + switch (token.type) + { + case ZBX_TOKEN_SIMPLE_MACRO: + dbpatch_convert_simple_macro(expression, &token.data.simple_macro, ¯o); + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, expression + last_pos, + token.loc.l - last_pos); + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, macro); + zbx_free(macro); + last_pos = token.loc.r + 1; + pos = token.loc.r; + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_FUNC_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + break; + } + } + + if (0 == out_offset) + return FAIL; + + if (last_pos <= loc->r) + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, expression + last_pos, loc->r - last_pos + 1); + *replace = out; + + return SUCCEED; +} + +static int DBpatch_5030083(void) +{ + DB_ROW row; + DB_RESULT result; + char *sql; + size_t sql_alloc = 4096, sql_offset = 0; + int ret = SUCCEED; + + if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER)) + return SUCCEED; + + sql = zbx_malloc(NULL, sql_alloc); + + DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset); + + result = DBselect("select triggerid,event_name from triggers order by triggerid"); + + while (NULL != (row = DBfetch(result))) + { + int pos = 0, last_pos = 0; + zbx_token_t token; + char *out = NULL; + size_t out_alloc = 0, out_offset = 0; + + for (; SUCCEED == zbx_token_find(row[1], pos, &token, ZBX_TOKEN_SEARCH_EXPRESSION_MACRO); pos++) + { + char *replace = NULL; + zbx_strloc_t *loc = NULL; + + switch (token.type) + { + case ZBX_TOKEN_EXPRESSION_MACRO: + loc = &token.loc; + break; + case ZBX_TOKEN_FUNC_MACRO: + loc = &token.data.func_macro.macro; + if ('?' != row[1][loc->l + 1]) + { + pos = token.loc.r; + continue; + } + break; + case ZBX_TOKEN_MACRO: + case ZBX_TOKEN_USER_MACRO: + case ZBX_TOKEN_LLD_MACRO: + pos = token.loc.r; + continue; + default: + continue; + } + + if (SUCCEED == dbpatch_convert_expression_macro(row[1], loc, &replace)) + { + zbx_strncpy_alloc(&out, &out_alloc, &out_offset, row[1] + last_pos, loc->l - last_pos); + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, replace); + zbx_free(replace); + last_pos = loc->r + 1; + } + pos = token.loc.r; + } + + if (0 == out_alloc) + continue; + + zbx_strcpy_alloc(&out, &out_alloc, &out_offset, row[1] + last_pos); + + if (TRIGGER_EVENT_NAME_LEN < zbx_strlen_utf8(out)) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot convert trigger \"%s\" event name: too long expression", + row[0]); + } + else + { + char *esc; + + esc = DBdyn_escape_field("triggers", "event_name", out); + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update triggers set event_name='%s'" + " where triggerid=%s;\n", esc, row[0]); + zbx_free(esc); + + ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset); + } + + zbx_free(out); + } + DBfree_result(result); + + DBend_multiple_update(&sql, &sql_alloc, &sql_offset); + + if (SUCCEED == ret && 16 < sql_offset) + { + if (ZBX_DB_OK > DBexecute("%s", sql)) + ret = FAIL; + } + + zbx_free(sql); + + return FAIL; +} + #endif DBPATCH_START(5030) @@ -2725,5 +3013,6 @@ DBPATCH_ADD(5030079, 0, 1) DBPATCH_ADD(5030080, 0, 1) DBPATCH_ADD(5030081, 0, 1) DBPATCH_ADD(5030082, 0, 1) +DBPATCH_ADD(5030083, 0, 1) DBPATCH_END() -- cgit v1.2.3 From e3bd443a4145fb072b78bb13200aaa3470581a9e Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Sat, 10 Apr 2021 10:08:17 +0300 Subject: ....I..... [ZBXNEXT-6451] fixed database patch warnings, return value --- create/src/schema.tmpl | 2 +- src/libs/zbxdbupgrade/dbupgrade_5030.c | 14 ++++++-------- ui/include/defines.inc.php | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/create/src/schema.tmpl b/create/src/schema.tmpl index 5c73b037bee..d124806dbad 100644 --- a/create/src/schema.tmpl +++ b/create/src/schema.tmpl @@ -1895,4 +1895,4 @@ INDEX |4 |creator_userid TABLE|dbversion|| FIELD |mandatory |t_integer |'0' |NOT NULL | FIELD |optional |t_integer |'0' |NOT NULL | -ROW |5030082 |5030082 +ROW |5030083 |5030083 diff --git a/src/libs/zbxdbupgrade/dbupgrade_5030.c b/src/libs/zbxdbupgrade/dbupgrade_5030.c index 8ce1e279c70..5d08bc83d05 100644 --- a/src/libs/zbxdbupgrade/dbupgrade_5030.c +++ b/src/libs/zbxdbupgrade/dbupgrade_5030.c @@ -2785,12 +2785,11 @@ static void dbpatch_convert_simple_macro(const char *expression, const zbx_token ******************************************************************************/ static int dbpatch_convert_expression_macro(const char *expression, const zbx_strloc_t *loc, char **replace) { - int pos = loc->l + 2; zbx_token_t token; char *out = NULL; - size_t out_alloc = 0, out_offset = 0, last_pos = loc->l; - - for (; SUCCEED == zbx_token_find(expression, pos, &token, ZBX_TOKEN_SEARCH_BASIC) && token.loc.r < loc->r; pos++) + size_t out_alloc = 0, out_offset = 0, pos = loc->l + 2, last_pos = loc->l; + for (; SUCCEED == zbx_token_find(expression, (int)pos, &token, ZBX_TOKEN_SEARCH_BASIC) && token.loc.r < loc->r; + pos++) { char *macro = NULL; @@ -2843,12 +2842,11 @@ static int DBpatch_5030083(void) while (NULL != (row = DBfetch(result))) { - int pos = 0, last_pos = 0; zbx_token_t token; char *out = NULL; - size_t out_alloc = 0, out_offset = 0; + size_t out_alloc = 0, out_offset = 0, pos = 0, last_pos = 0; - for (; SUCCEED == zbx_token_find(row[1], pos, &token, ZBX_TOKEN_SEARCH_EXPRESSION_MACRO); pos++) + for (; SUCCEED == zbx_token_find(row[1], (int)pos, &token, ZBX_TOKEN_SEARCH_EXPRESSION_MACRO); pos++) { char *replace = NULL; zbx_strloc_t *loc = NULL; @@ -2921,7 +2919,7 @@ static int DBpatch_5030083(void) zbx_free(sql); - return FAIL; + return ret; } #endif diff --git a/ui/include/defines.inc.php b/ui/include/defines.inc.php index 637ba6161f1..f2c32dae7bc 100644 --- a/ui/include/defines.inc.php +++ b/ui/include/defines.inc.php @@ -21,7 +21,7 @@ define('ZABBIX_VERSION', '5.4.0rc1'); define('ZABBIX_API_VERSION', '5.4.0'); define('ZABBIX_EXPORT_VERSION', '5.4'); -define('ZABBIX_DB_VERSION', 5030082); +define('ZABBIX_DB_VERSION', 5030083); define('ZABBIX_COPYRIGHT_FROM', '2001'); define('ZABBIX_COPYRIGHT_TO', '2021'); -- cgit v1.2.3 From db4df416a22550ebbef724044cf9e23e65d1f18e Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Sun, 11 Apr 2021 18:55:02 +0300 Subject: .......... [ZBXNEXT-6451,ZBXNEXT-6455] fixed CTextTriggerConstructorTest test cases --- .../triggers/CTextTriggerConstructorTest.php | 262 +++++++++------------ 1 file changed, 107 insertions(+), 155 deletions(-) diff --git a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php index cd0f4db5e0c..d51253974a0 100644 --- a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php +++ b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php @@ -35,264 +35,222 @@ class CTextTriggerConstructorTest extends TestCase { public function dataProviderGetExpressionFromPartsValid() { return [ [ - 'host', - 'item', [ [ - 'value' => 'regexp(test)', + 'value' => 'find(/host/item,,"regexp","test")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '(({host:item.regexp(test)})<>0)' + '((find(/host/item,,"regexp","test"))<>0)' ], [ - 'host', - 'item', [ [ - 'value' => 'regexp(test)', + 'value' => 'find(/host/item,,"regexp","test")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '(({host:item.regexp(test)})=0)' + '((find(/host/item,,"regexp","test"))=0)' ], [ - 'host', - 'item', [ [ - 'value' => 'regexp(a) and regexp(b)', + 'value' => 'find(/host/item,,"regexp","a") and find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '(({host:item.regexp(a)})<>0 and ({host:item.regexp(b)})<>0)' + '((find(/host/item,,"regexp","a"))<>0 and (find(/host/item,,"regexp","b"))<>0)' ], [ - 'host', - 'item', [ [ - 'value' => 'regexp(a) or regexp(b)', + 'value' => 'find(/host/item,,"regexp","a") or find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '(({host:item.regexp(a)})<>0 or ({host:item.regexp(b)})<>0)' + '((find(/host/item,,"regexp","a"))<>0 or (find(/host/item,,"regexp","b"))<>0)' ], [ - 'host', - 'item', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/host/item,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ], [ - 'value' => 'regexp(b)', + 'value' => 'find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '((({host:item.regexp(a)})<>0) or (({host:item.regexp(b)})<>0))' + '(((find(/host/item,,"regexp","a"))<>0) or ((find(/host/item,,"regexp","b"))<>0))' ], [ - 'host', - 'item', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/host/item,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(b)', + 'value' => 'find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '(({host:item.regexp(a)})=0) and (({host:item.regexp(b)})=0)' + '((find(/host/item,,"regexp","a"))=0) and ((find(/host/item,,"regexp","b"))=0)' ], [ - 'host', - 'item', [ [ - 'value' => 'regexp(a) and regexp(b)', + 'value' => 'find(/host/item,,"regexp","a") and find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ], [ - 'value' => 'regexp(с) or regexp(d)', + 'value' => 'find(/host/item,,"regexp","c") or find(/host/item,,"regexp","d")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '((({host:item.regexp(a)})<>0 and ({host:item.regexp(b)})<>0) or (({host:item.regexp(с)})<>0 or ({host:item.regexp(d)})<>0))' + '(((find(/host/item,,"regexp","a"))<>0 and (find(/host/item,,"regexp","b"))<>0) or ((find(/host/item,,"regexp","c"))<>0 or (find(/host/item,,"regexp","d"))<>0))' ], [ - 'host', - 'item', [ [ - 'value' => 'regexp(a) and regexp(b)', + 'value' => 'find(/host/item,,"regexp","a") and find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(c) or regexp(d)', + 'value' => 'find(/host/item,,"regexp","c") or find(/host/item,,"regexp","d")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '(({host:item.regexp(a)})=0 and ({host:item.regexp(b)})=0) and (({host:item.regexp(c)})=0 or ({host:item.regexp(d)})=0)' + '((find(/host/item,,"regexp","a"))=0 and (find(/host/item,,"regexp","b"))=0) and ((find(/host/item,,"regexp","c"))=0 or (find(/host/item,,"regexp","d"))=0)' ], [ - 'host', - 'item', [ [ - 'value' => 'iregexp(test)', + 'value' => 'find(/host/item,,"iregexp","test")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '(({host:item.iregexp(test)})<>0)' + '((find(/host/item,,"iregexp","test"))<>0)' ], [ - 'host', - 'item', [ [ - 'value' => 'iregexp(test)', + 'value' => 'find(/host/item,,"iregexp","test")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '(({host:item.iregexp(test)})=0)' + '((find(/host/item,,"iregexp","test"))=0)' ], [ - 'host', - 'item', [ [ - 'value' => '(regexp(a))>0', + 'value' => '(find(/host/item,,"regexp","a"))>0', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '(({host:item.regexp(a)})=0)' + '((find(/host/item,,"regexp","a"))=0)' ], // "not" cases [ - 'host', - 'item', [ [ - 'value' => 'not regexp(test)', + 'value' => 'not find(/host/item,,"regexp","test")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '((not {host:item.regexp(test)})=0)' + '((not find(/host/item,,"regexp","test"))=0)' ], [ - 'host', - 'item', [ [ - 'value' => 'not (regexp(test))', + 'value' => 'not (find(/host/item,,"regexp","test"))', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '((not {host:item.regexp(test)})=0)' + '((not find(/host/item,,"regexp","test"))=0)' ], [ - 'host', - 'item', [ [ - 'value' => 'not regexp(a) and not regexp(b)', + 'value' => 'not find(/host/item,,"regexp","a") and not find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '((not {host:item.regexp(a)})<>0 and (not {host:item.regexp(b)})<>0)' + '((not find(/host/item,,"regexp","a"))<>0 and (not find(/host/item,,"regexp","b"))<>0)' ], [ - 'host', - 'item', [ [ - 'value' => 'not regexp(a) or not regexp(b)', + 'value' => 'not find(/host/item,,"regexp","a") or not find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '((not {host:item.regexp(a)})<>0 or (not {host:item.regexp(b)})<>0)' + '((not find(/host/item,,"regexp","a"))<>0 or (not find(/host/item,,"regexp","b"))<>0)' ], [ - 'host', - 'item', [ [ - 'value' => 'not regexp(a)', + 'value' => 'not find(/host/item,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ], [ - 'value' => 'not regexp(b)', + 'value' => 'not find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '(((not {host:item.regexp(a)})<>0) or ((not {host:item.regexp(b)})<>0))' + '(((not find(/host/item,,"regexp","a"))<>0) or ((not find(/host/item,,"regexp","b"))<>0))' ], // "-" cases [ - 'host', - 'item', [ [ - 'value' => '- regexp(test)', + 'value' => '- find(/host/item,,"regexp","test")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '((-{host:item.regexp(test)})=0)' + '((-find(/host/item,,"regexp","test"))=0)' ], [ - 'host', - 'item', [ [ - 'value' => '- (regexp(test))', + 'value' => '- (find(/host/item,,"regexp","test"))', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ], - '((-{host:item.regexp(test)})=0)' + '((-find(/host/item,,"regexp","test"))=0)' ], [ - 'host', - 'item', [ [ - 'value' => '- regexp(a) and - regexp(b)', + 'value' => '- find(/host/item,,"regexp","a") and - find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '((-{host:item.regexp(a)})<>0 and (-{host:item.regexp(b)})<>0)' + '((-find(/host/item,,"regexp","a"))<>0 and (-find(/host/item,,"regexp","b"))<>0)' ], [ - 'host', - 'item', [ [ - 'value' => '- regexp(a) or - regexp(b)', + 'value' => '- find(/host/item,,"regexp","a") or - find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '((-{host:item.regexp(a)})<>0 or (-{host:item.regexp(b)})<>0)' + '((-find(/host/item,,"regexp","a"))<>0 or (-find(/host/item,,"regexp","b"))<>0)' ], [ - 'host', - 'item', [ [ - 'value' => '- regexp(a)', + 'value' => '- find(/host/item,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ], [ - 'value' => '- regexp(b)', + 'value' => '- find(/host/item,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ], - '(((-{host:item.regexp(a)})<>0) or ((-{host:item.regexp(b)})<>0))' + '(((-find(/host/item,,"regexp","a"))<>0) or ((-find(/host/item,,"regexp","b"))<>0))' ] ]; } @@ -302,148 +260,139 @@ class CTextTriggerConstructorTest extends TestCase { * * @dataProvider dataProviderGetExpressionFromPartsValid * - * @param $host - * @param $item - * @param array $expressions - * @param $expectedExpressions + * @param array $expressions + * @param string $expected_expressions */ - public function testGetExpressionFromPartsValid($host, $item, array $expressions, $expectedExpressions) { - $expression = $this->constructor->getExpressionFromParts($host, $item, $expressions); + public function testGetExpressionFromPartsValid(array $expressions, string $expected_expressions) { + $expression = $this->constructor->getExpressionFromParts('', '', $expressions); - $this->assertEquals($expectedExpressions, $expression); - } - - /** - * Test calling getExpressionFromParts() with invalid parameters. - */ - public function testGetExpressionFromPartsInvalid() { - $this->markTestIncomplete(); + $this->assertSame($expected_expressions, $expression); } public function dataProviderGetPartsFromExpression() { return [ [ - '({Zabbix server:system.hostname.regexp(a)})=0', + '(find(/Zabbix server/system.hostname,,"regexp","a"))=0', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '({Zabbix server:system.hostname.regexp(a)})<>0', + '(find(/Zabbix server/system.hostname,,"regexp","a"))<>0', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_MATCH ] ] ], [ - '(({Zabbix server:system.hostname.regexp(a)})=0)', + '((find(/Zabbix server/system.hostname,,"regexp","a"))=0)', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '({Zabbix server:system.hostname.regexp(a)})=0 and ({Zabbix server:system.hostname.regexp(b)})=0', + '(find(/Zabbix server/system.hostname,,"regexp","a"))=0 and (find(/Zabbix server/system.hostname,,"regexp","b"))=0', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(b)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '({Zabbix server:system.hostname.regexp(a)})=0 or ({Zabbix server:system.hostname.regexp(b)})=0', + '(find(/Zabbix server/system.hostname,,"regexp","a"))=0 or (find(/Zabbix server/system.hostname,,"regexp","b"))=0', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(b)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '(({Zabbix server:system.hostname.regexp(a)})=0 or ({Zabbix server:system.hostname.regexp(b)})=0) and ({Zabbix server:system.hostname.regexp(c)})=0', + '((find(/Zabbix server/system.hostname,,"regexp","a"))=0 or (find(/Zabbix server/system.hostname,,"regexp","b"))=0) and (find(/Zabbix server/system.hostname,,"regexp","c"))=0', [ [ - 'value' => 'regexp(a) or regexp(b)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a") or find(/Zabbix server/system.hostname,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(c)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","c")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '({Zabbix server:system.hostname.regexp(a)})=0 or (({Zabbix server:system.hostname.regexp(b)})=0 and ({Zabbix server:system.hostname.regexp(c)})=0)', + '(find(/Zabbix server/system.hostname,,"regexp","a"))=0 or ((find(/Zabbix server/system.hostname,,"regexp","b"))=0 and (find(/Zabbix server/system.hostname,,"regexp","c"))=0)', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(b) and regexp(c)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","b") and find(/Zabbix server/system.hostname,,"regexp","c")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '({Zabbix server:system.hostname.regexp(a)})=0 or ({Zabbix server:system.hostname.regexp(b)})=0 and ({Zabbix server:system.hostname.regexp(c)})=0', + '(find(/Zabbix server/system.hostname,,"regexp","a"))=0 or (find(/Zabbix server/system.hostname,,"regexp","b"))=0 and (find(/Zabbix server/system.hostname,,"regexp","c"))=0', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(b)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(c)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","c")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '(({Zabbix server:system.hostname.regexp(a)})=0 and ({Zabbix server:system.hostname.regexp(b)})=0) or (({Zabbix server:system.hostname.regexp(c)})=0 or ({Zabbix server:system.hostname.regexp(d)})=0)', + '((find(/Zabbix server/system.hostname,,"regexp","a"))=0 and (find(/Zabbix server/system.hostname,,"regexp","b"))=0) or ((find(/Zabbix server/system.hostname,,"regexp","c"))=0 or (find(/Zabbix server/system.hostname,,"regexp","d"))=0)', [ [ - 'value' => 'regexp(a) and regexp(b)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a") and find(/Zabbix server/system.hostname,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(c) or regexp(d)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","c") or find(/Zabbix server/system.hostname,,"regexp","d")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '((({Zabbix server:system.hostname.regexp(a)})=0) or (({Zabbix server:system.hostname.regexp(b)})=0)) and (({Zabbix server:system.hostname.regexp(c)})=0)', + '(((find(/Zabbix server/system.hostname,,"regexp","a"))=0) or ((find(/Zabbix server/system.hostname,,"regexp","b"))=0)) and ((find(/Zabbix server/system.hostname,,"regexp","c"))=0)', [ [ - 'value' => 'regexp(a)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(b)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","b")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ], [ - 'value' => 'regexp(c)', + 'value' => 'find(/Zabbix server/system.hostname,,"regexp","c")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] @@ -451,28 +400,28 @@ class CTextTriggerConstructorTest extends TestCase { // "not" cases [ - '(not {Zabbix server:system.hostname.regexp(a)})=0', + '(not find(/Zabbix server/system.hostname,,"regexp","a"))=0', [ [ - 'value' => 'not regexp(a)', + 'value' => 'not find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '(not ({Zabbix server:system.hostname.regexp(a)})=0)', + '(not (find(/Zabbix server/system.hostname,,"regexp","a"))=0)', [ [ - 'value' => 'not regexp(a)', + 'value' => 'not find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - 'not (({Zabbix server:system.hostname.regexp(a)})=0)', + 'not ((find(/Zabbix server/system.hostname,,"regexp","a"))=0)', [ [ - 'value' => 'not (regexp(a))', + 'value' => 'not (find(/Zabbix server/system.hostname,,"regexp","a"))', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] @@ -480,55 +429,55 @@ class CTextTriggerConstructorTest extends TestCase { // "-" cases [ - '(-{Zabbix server:system.hostname.regexp(a)})=0', + '(-find(/Zabbix server/system.hostname,,"regexp","a"))=0', [ [ - 'value' => '-regexp(a)', + 'value' => '-find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '(-({Zabbix server:system.hostname.regexp(a)})=0)', + '(-(find(/Zabbix server/system.hostname,,"regexp","a"))=0)', [ [ - 'value' => '-regexp(a)', + 'value' => '-find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '-(({Zabbix server:system.hostname.regexp(a)})=0)', + '-((find(/Zabbix server/system.hostname,,"regexp","a"))=0)', [ [ - 'value' => '-(regexp(a))', + 'value' => '-(find(/Zabbix server/system.hostname,,"regexp","a"))', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '(- {Zabbix server:system.hostname.regexp(a)})=0', + '(- find(/Zabbix server/system.hostname,,"regexp","a"))=0', [ [ - 'value' => '-regexp(a)', + 'value' => '-find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '(- ({Zabbix server:system.hostname.regexp(a)})=0)', + '(- (find(/Zabbix server/system.hostname,,"regexp","a"))=0)', [ [ - 'value' => '-regexp(a)', + 'value' => '-find(/Zabbix server/system.hostname,,"regexp","a")', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] ], [ - '- (({Zabbix server:system.hostname.regexp(a)})=0)', + '- ((find(/Zabbix server/system.hostname,,"regexp","a"))=0)', [ [ - 'value' => '-(regexp(a))', + 'value' => '-(find(/Zabbix server/system.hostname,,"regexp","a"))', 'type' => CTextTriggerConstructor::EXPRESSION_TYPE_NO_MATCH ] ] @@ -539,12 +488,15 @@ class CTextTriggerConstructorTest extends TestCase { /** * @dataProvider dataProviderGetPartsFromExpression * - * @param $expression - * @param array $expectedParts + * @param string $expression + * @param array $expected_parts */ - public function testGetPartsFromExpression($expression, array $expectedParts) { + public function testGetPartsFromExpression(string $expression, array $expected_parts) { $parts = $this->constructor->getPartsFromExpression($expression); - $this->assertEquals($expectedParts, $parts); + $this->assertIsArray($parts); + unset($parts[0]['details']); + + $this->assertSame($expected_parts, $parts); } } -- cgit v1.2.3 From 611ab7fbaac7b8d6cb32c0c6164cf700d592bc00 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 09:37:05 +0300 Subject: A.F....... [ZBXNEXT-6451] fixed ugly design --- .../classes/api/services/CTriggerGeneral.php | 48 +++++++++++++------- .../parsers/results/CTriggerExprParserResult.php | 52 ---------------------- 2 files changed, 31 insertions(+), 69 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index 4698543b3db..1315248142e 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -1406,24 +1406,38 @@ abstract class CTriggerGeneral extends CApiService { continue; } - $expressionData->parse($trigger['expression']); - foreach ($expressionData->result->getItemsGroupedByHosts() as $key => $host) { - if (array_key_exists($key, $hosts_keys)) { - $hosts_keys[$key]['keys'] += $host['keys']; - } - else { - $hosts_keys[$key] = $host; - } - } + $expression_fields = ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) + ? ['expression', 'recovery_expression'] + : ['expression']; - if ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) { - $expressionData->parse($trigger['recovery_expression']); - foreach ($expressionData->result->getItemsGroupedByHosts() as $key => $host) { - if (array_key_exists($key, $hosts_keys)) { - $hosts_keys[$key]['keys'] += $host['keys']; - } - else { - $hosts_keys[$key] = $host; + foreach ($expression_fields as $expression_field) { + $expressionData->parse($trigger[$expression_field]); + $params_stack = $expressionData->result->getTokens(); + + while ($params_stack) { + $param = array_shift($params_stack); + if ($param instanceof CFunctionParserResult) { + $params_stack = array_merge($params_stack, $param->params_raw['parameters']); + + foreach ($param->getItemsGroupedByHosts() as $host => $items) { + if (!array_key_exists($host, $hosts_keys)) { + $hosts_keys[$host] = [ + 'hostid' => null, + 'host' => $host, + 'status' => null, + 'keys' => [] + ]; + } + + foreach ($items as $item) { + $hosts_keys[$host]['keys'][$item] = [ + 'itemid' => null, + 'key' => $item, + 'value_type' => null, + 'flags' => null + ]; + } + } } } } diff --git a/ui/include/classes/parsers/results/CTriggerExprParserResult.php b/ui/include/classes/parsers/results/CTriggerExprParserResult.php index 7727a17a678..e07b9dd6045 100644 --- a/ui/include/classes/parsers/results/CTriggerExprParserResult.php +++ b/ui/include/classes/parsers/results/CTriggerExprParserResult.php @@ -202,56 +202,4 @@ class CTriggerExprParserResult extends CParserResult { return array_keys(array_flip($hosts)); } - - /** - * Return array containing items found in parsed trigger expression grouped by host. - * - * Example: - * [ - * 'host1' => [ - * 'item1' => 'item1', - * 'item2' => 'item2' - * ] - * ], - * [ - * 'host2' => [ - * 'item3' => 'item3', - * ] - * ] - * - * @return array - */ - public function getItemsGroupedByHosts():array { - $params_stack = $this->tokens; - $hosts = []; - - while ($params_stack) { - $param = array_shift($params_stack); - if ($param instanceof CFunctionParserResult) { - $params_stack = array_merge($params_stack, $param->params_raw['parameters']); - - foreach ($param->getItemsGroupedByHosts() as $host => $items) { - if (!array_key_exists($host, $hosts)) { - $hosts[$host] = [ - 'hostid' => null, - 'host' => $host, - 'status' => null, - 'keys' => [] - ]; - } - - foreach ($items as $item) { - $hosts[$host]['keys'][$item] = [ - 'itemid' => null, - 'key' => $item, - 'value_type' => null, - 'flags' => null - ]; - } - } - } - } - - return $hosts; - } } -- cgit v1.2.3 From ce3c0546642f447634d91bb4e4e05df5a2718cbb Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 10:06:35 +0300 Subject: A......... [ZBXNEXT-6451] fixed function validator error reporting --- ui/include/classes/api/services/CTriggerGeneral.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index 1315248142e..caffdf7105f 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -1582,14 +1582,16 @@ abstract class CTriggerGeneral extends CApiService { if (!$math_function_validator->validate($fn)) { $error_msg = $math_function_validator->getError(); - if ($value_type !== null - && $trigger_function_validator->validate($fn) - && $trigger_function_validator->validateValueType($value_type, $fn)) { - $error_msg = ''; + if (!$trigger_function_validator->validate($fn)) { + $error_msg = $trigger_function_validator->getError(); } - elseif ($value_type !== null) { + elseif ($value_type !== null + && !$trigger_function_validator->validateValueType($value_type, $fn)) { $error_msg = $trigger_function_validator->getError(); } + else { + $error_msg = ''; + } } if ($error_msg !== '') { -- cgit v1.2.3 From 24343481f3007a97efd02357fdcd7941fe4ff579 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 11:50:41 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed undefined property error in trigger wizard form --- ui/include/classes/triggers/CTextTriggerConstructor.php | 2 +- ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/include/classes/triggers/CTextTriggerConstructor.php b/ui/include/classes/triggers/CTextTriggerConstructor.php index 43cd111f48e..fa7002508d9 100644 --- a/ui/include/classes/triggers/CTextTriggerConstructor.php +++ b/ui/include/classes/triggers/CTextTriggerConstructor.php @@ -253,7 +253,7 @@ class CTextTriggerConstructor { case CTriggerExprParserResult::TOKEN_TYPE_OPERATOR: // look for an "or" or "and" operator on the top parentheses level // if such an expression is found, save all of the tokens before it as a separate expression - if ($level == 0 && ($token->value === 'or' || $token->value === 'and')) { + if ($level == 0 && ($token->match === 'or' || $token->match === 'and')) { $expressions[] = $currentExpression; $currentExpression = []; diff --git a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php index d51253974a0..e3a0e3c1824 100644 --- a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php +++ b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php @@ -495,7 +495,7 @@ class CTextTriggerConstructorTest extends TestCase { $parts = $this->constructor->getPartsFromExpression($expression); $this->assertIsArray($parts); - unset($parts[0]['details']); + unset($parts[0]['details'], $parts[1]['details'], $parts[2]['details']); $this->assertSame($expected_parts, $parts); } -- cgit v1.2.3 From bf51bf9f15154e1eb23fd6f0116d0322e17972c2 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 12:18:14 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger converter by adding quotes for also string empty parameters --- .../classes/import/converters/C52TriggerExpressionConverter.php | 2 +- ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index ad3adc33a27..3093fda0cd5 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -374,7 +374,7 @@ class C52TriggerExpressionConverter extends CConverter { return; } - if ($param === '' || ($param[0] === '"' && substr($param, -1) === '"')) { + if ($param !== '' && $param[0] === '"' && substr($param, -1) === '"') { return; } diff --git a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php index 82e532b0270..848308c5c66 100644 --- a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php +++ b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php @@ -195,7 +195,7 @@ class C52TriggerExpressionConverterTest extends TestCase { 'forecast(/Trapper/trap[2],#10,100s) > 0'. ' and forecast(/Trapper/trap[2],3600s:now-7200s,600s,"linear","avg") > 0'. - ' and forecast(/Trapper/trap[2],30m:now-1d,600s,,"avg") > 0' + ' and forecast(/Trapper/trap[2],30m:now-1d,600s,"","avg") > 0' ], [ @@ -222,7 +222,7 @@ class C52TriggerExpressionConverterTest extends TestCase { ' and count(/Trapper/trap[1],5m:now-2d,"gt","100") > 0'. ' and count(/Trapper/trap[1],1m,"bitand","32") > 0'. ' and count(/Trapper/trap[1],1m,"bitand","32/8") > 0'. - ' and count(/Trapper/trap[1],1m) > 0' + ' and count(/Trapper/trap[1],1m,"","") > 0' ], [ '{Trapper:trap[3].iregexp("^error", #10)} > 0'. -- cgit v1.2.3 From 98da0f8311716833d776d8f5c6e9a30d188fd925 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 12:59:27 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger function validation --- ui/include/classes/validators/CFunctionValidator.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php index ffbc301a0f9..e5925160b64 100644 --- a/ui/include/classes/validators/CFunctionValidator.php +++ b/ui/include/classes/validators/CFunctionValidator.php @@ -342,7 +342,14 @@ class CFunctionValidator extends CValidator { return false; } - if ($arg['mandat'] != 0x00 && !$this->validateParameter($fn->params_raw['parameters'][$num], $arg)) { + if ($arg['mandat'] != 0x00 && $fn->params_raw['parameters'][$num]->getValue() === '') { + $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $fn->match).' '. + $param_labels[$num] + ); + return false; + } + + if (!$this->validateParameter($fn->params_raw['parameters'][$num], $arg)) { $this->setError( _s('Incorrect trigger function "%1$s" provided in expression.', $fn->match).' '.$param_labels[$num] ); -- cgit v1.2.3 From 22098121358fcfae539ed4fb78734372d9529df7 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 13:11:54 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed trigger function validation --- ui/include/classes/validators/CFunctionValidator.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php index e5925160b64..bf907b7151f 100644 --- a/ui/include/classes/validators/CFunctionValidator.php +++ b/ui/include/classes/validators/CFunctionValidator.php @@ -342,6 +342,10 @@ class CFunctionValidator extends CValidator { return false; } + if ($arg['mandat'] == 0x00 && $fn->params_raw['parameters'][$num]->getValue() === '') { + continue; + } + if ($arg['mandat'] != 0x00 && $fn->params_raw['parameters'][$num]->getValue() === '') { $this->setError(_s('Incorrect trigger function "%1$s" provided in expression.', $fn->match).' '. $param_labels[$num] -- cgit v1.2.3 From 54398cf5ee892f5dfce1b01f85963c6daac2cfa9 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 15:06:34 +0300 Subject: ..F....... [ZBXNEXT-6451] removed unused function parameters --- ui/app/controllers/CControllerPopupTriggerWizard.php | 12 ++++-------- ui/include/classes/triggers/CTextTriggerConstructor.php | 4 +--- .../include/classes/triggers/CTextTriggerConstructorTest.php | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/ui/app/controllers/CControllerPopupTriggerWizard.php b/ui/app/controllers/CControllerPopupTriggerWizard.php index 32aabe6c3a9..199b54f49d5 100644 --- a/ui/app/controllers/CControllerPopupTriggerWizard.php +++ b/ui/app/controllers/CControllerPopupTriggerWizard.php @@ -121,31 +121,27 @@ class CControllerPopupTriggerWizard extends CController { if ($this->hasInput('save')) { $trigger_valid = true; - $item = API::Item()->get([ + $items = API::Item()->get([ 'output' => ['key_'], 'selectHosts' => ['host'], 'itemids' => $page_options['itemid'], 'limit' => 1 ]); - $item = reset($item); - $host = reset($item['hosts']); - // Trigger validation. if ($page_options['description'] === '') { error(_s('Incorrect value for field "%1$s": %2$s.', _('Name'), _('cannot be empty'))); $trigger_valid = false; } - elseif (!$item) { + elseif (!$items) { error('No permissions to referred object or it does not exist!'); $trigger_valid = false; } - elseif ($exprs - && ($expression = $constructor->getExpressionFromParts($host['host'], $item['key_'], $exprs))) { + elseif ($exprs && ($expression = $constructor->getExpressionFromParts($exprs))) { [$editable, $queries] = check_right_on_trigger_by_expression(PERM_READ_WRITE, $expression); // Check of no other /host/key references. - if (count($queries) != 1 || $queries[0] !== '/'.$host['host'].'/'.$item['key_']) { + if (count($queries) != 1 || $queries[0] !== '/'.$items[0]['hosts'][0]['host'].'/'.$items[0]['key_']) { $update = array_key_exists('triggerid', $page_options); $trigger_valid = false; diff --git a/ui/include/classes/triggers/CTextTriggerConstructor.php b/ui/include/classes/triggers/CTextTriggerConstructor.php index fa7002508d9..e5b42726d26 100644 --- a/ui/include/classes/triggers/CTextTriggerConstructor.php +++ b/ui/include/classes/triggers/CTextTriggerConstructor.php @@ -47,8 +47,6 @@ class CTextTriggerConstructor { * Most of this function was left unchanged to preserve the current behavior of the constructor. * Feel free to rewrite and correct it if necessary. * - * @param string $host host name - * @param string $item_key item key * @param array $expressions array of expression parts * @param string $expressions[]['value'] expression string * @param int $expressions[]['type'] whether the string should match the expression; supported values: @@ -56,7 +54,7 @@ class CTextTriggerConstructor { * * @return bool|string */ - public function getExpressionFromParts(string $host, string $item_key, array $expressions) { + public function getExpressionFromParts(array $expressions) { $result = ''; if (empty($expressions)) { diff --git a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php index e3a0e3c1824..9718f93d5b9 100644 --- a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php +++ b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php @@ -264,7 +264,7 @@ class CTextTriggerConstructorTest extends TestCase { * @param string $expected_expressions */ public function testGetExpressionFromPartsValid(array $expressions, string $expected_expressions) { - $expression = $this->constructor->getExpressionFromParts('', '', $expressions); + $expression = $this->constructor->getExpressionFromParts($expressions); $this->assertSame($expected_expressions, $expression); } -- cgit v1.2.3 From b3d03b8df55767fc41c66a07b6db0a7054497bdd Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Mon, 12 Apr 2021 15:36:45 +0300 Subject: ........S. [ZBXNEXT-6451] copied libzxbeval from the calculated item branch, added macro expression evaluation --- include/common.h | 1 + include/dbcache.h | 3 + include/zbxeval.h | 75 +++-- include/zbxserver.h | 8 +- src/libs/zbxcommon/str.c | 53 ++++ src/libs/zbxdbcache/dbconfig.c | 36 +++ src/libs/zbxdbupgrade/dbupgrade_5030.c | 4 +- src/libs/zbxeval/Makefile.am | 4 +- src/libs/zbxeval/execute.c | 118 ++++--- src/libs/zbxeval/misc.c | 82 ++--- src/libs/zbxeval/parse.c | 72 ++--- src/libs/zbxserver/evalfunc2.c | 33 ++ src/libs/zbxserver/expression.c | 359 ++++++++++++++++++---- tests/libs/zbxcommon/zbx_token_find.yaml | 2 +- tests/libs/zbxeval/zbx_eval_execute_ext.c | 8 +- tests/libs/zbxeval/zbx_eval_execute_ext.yaml | 8 +- tests/libs/zbxeval/zbx_eval_parse_expression.c | 2 - tests/libs/zbxeval/zbx_eval_parse_expression.yaml | 66 ++++ 18 files changed, 684 insertions(+), 250 deletions(-) diff --git a/include/common.h b/include/common.h index b7f3bde2b46..fc2a669d77d 100644 --- a/include/common.h +++ b/include/common.h @@ -1183,6 +1183,7 @@ void zbx_strncpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char void zbx_strcpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src); void zbx_chrcpy_alloc(char **str, size_t *alloc_len, size_t *offset, char c); void zbx_str_memcpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src, size_t n); +void zbx_strquote_alloc(char **str, size_t *str_alloc, size_t *str_offset, const char *value_str); void zbx_strsplit(const char *src, char delimiter, char **left, char **right); diff --git a/include/dbcache.h b/include/dbcache.h index 41bba8facc9..3aacdd44a13 100644 --- a/include/dbcache.h +++ b/include/dbcache.h @@ -975,6 +975,9 @@ const char *zbx_dc_get_instanceid(void); char *zbx_dc_expand_user_macros(const char *text, zbx_uint64_t hostid); char *zbx_dc_expand_user_macros_in_func_params(const char *params, zbx_uint64_t hostid); +int zbx_dc_expand_user_macros_len(const char *text, size_t text_len, zbx_uint64_t *hostids, int hostids_num, + char **value, char **error); + /* diagnostic data */ void zbx_hc_get_diag_stats(zbx_uint64_t *items_num, zbx_uint64_t *values_num); diff --git a/include/zbxeval.h b/include/zbxeval.h index 85d7f6fee2d..73bf9b87530 100644 --- a/include/zbxeval.h +++ b/include/zbxeval.h @@ -58,21 +58,20 @@ #define ZBX_EVAL_TOKEN_OP_NOT (14 | ZBX_EVAL_CLASS_OPERATOR1 | ZBX_EVAL_OP_SET_PRECEDENCE(2)) #define ZBX_EVAL_TOKEN_VAR_NUM (15 | ZBX_EVAL_CLASS_OPERAND) #define ZBX_EVAL_TOKEN_VAR_STR (16 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_VAR_TIME (17 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_VAR_MACRO (18 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_VAR_USERMACRO (19 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_VAR_LLDMACRO (20 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_FUNCTIONID (21 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_FUNCTION (22 | ZBX_EVAL_CLASS_FUNCTION) -#define ZBX_EVAL_TOKEN_HIST_FUNCTION (23 | ZBX_EVAL_CLASS_FUNCTION) -#define ZBX_EVAL_TOKEN_GROUP_OPEN (24 | ZBX_EVAL_CLASS_SEPARATOR) -#define ZBX_EVAL_TOKEN_GROUP_CLOSE (25 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_COMMA (26 | ZBX_EVAL_CLASS_SEPARATOR) -#define ZBX_EVAL_TOKEN_ARG_QUERY (27 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_ARG_PERIOD (28 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_ARG_NULL (29 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_ARG_RAW (30 | ZBX_EVAL_CLASS_OPERAND) -#define ZBX_EVAL_TOKEN_EXCEPTION (31 | ZBX_EVAL_CLASS_FUNCTION) +#define ZBX_EVAL_TOKEN_VAR_MACRO (17 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_VAR_USERMACRO (18 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_VAR_LLDMACRO (19 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_FUNCTIONID (20 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_FUNCTION (21 | ZBX_EVAL_CLASS_FUNCTION) +#define ZBX_EVAL_TOKEN_HIST_FUNCTION (22 | ZBX_EVAL_CLASS_FUNCTION) +#define ZBX_EVAL_TOKEN_GROUP_OPEN (23 | ZBX_EVAL_CLASS_SEPARATOR) +#define ZBX_EVAL_TOKEN_GROUP_CLOSE (24 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_COMMA (25 | ZBX_EVAL_CLASS_SEPARATOR) +#define ZBX_EVAL_TOKEN_ARG_QUERY (26 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_ARG_PERIOD (27 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_ARG_NULL (28 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_ARG_RAW (29 | ZBX_EVAL_CLASS_OPERAND) +#define ZBX_EVAL_TOKEN_EXCEPTION (30 | ZBX_EVAL_CLASS_FUNCTION) /* expression parsing rules */ @@ -89,7 +88,11 @@ ZBX_EVAL_PARSE_FUNCTIONID | ZBX_EVAL_PARSE_FUNCTION) #define ZBX_EVAL_PARSE_CALC_EXPRESSSION (ZBX_EVAL_PARSE_MACRO | ZBX_EVAL_PARSE_USERMACRO | \ - ZBX_EVAL_PARSE_ITEM_QUERY | ZBX_EVAL_PARSE_FUNCTION) + ZBX_EVAL_PARSE_ITEM_QUERY | ZBX_EVAL_PARSE_FUNCTION | \ + ZBX_EVAL_PARSE_COMPOUND_CONST) + +#define ZBX_EVAL_PARSE_EXPRESSION_MACRO (ZBX_EVAL_PARSE_USERMACRO | ZBX_EVAL_PARSE_ITEM_QUERY | \ + ZBX_EVAL_PARSE_FUNCTION | ZBX_EVAL_PARSE_COMPOUND_CONST) /* expression composition rules */ @@ -111,6 +114,11 @@ ZBX_EVAL_COMPOSE_LLD | \ ZBX_EVAL_COMPOSE_FUNCTIONID) +#define ZBX_EVAL_CALC_EXPRESSION_LLD (ZBX_EVAL_PARSE_CALC_EXPRESSSION | \ + ZBX_EVAL_PARSE_LLDMACRO | \ + ZBX_EVAL_COMPOSE_LLD) + + typedef zbx_uint32_t zbx_token_type_t; /****************************************************************************** @@ -123,6 +131,8 @@ typedef zbx_uint32_t zbx_token_type_t; * len - [IN] the function name length * * args_num - [IN] the number of function arguments * * args - [IN] an array of the function arguments. * + * data - [IN] the caller data used for function evaluation * + * ts - [IN] the function execution time * * value - [OUT] the function return value * * error - [OUT] the error message if function failed * * * @@ -131,7 +141,7 @@ typedef zbx_uint32_t zbx_token_type_t; * * ******************************************************************************/ typedef int (*zbx_eval_function_cb_t)(const char *name, size_t len, int args_num, const zbx_variant_t *args, - zbx_variant_t *value, char **error); + void *data, const zbx_timespec_t *ts, zbx_variant_t *value, char **error); typedef struct { @@ -154,7 +164,9 @@ typedef struct zbx_timespec_t ts; zbx_vector_eval_token_t stack; zbx_vector_eval_token_t ops; - zbx_eval_function_cb_t function_cb; + zbx_eval_function_cb_t common_func_cb; + zbx_eval_function_cb_t history_func_cb; + void *data_cb; } zbx_eval_context_t; @@ -162,7 +174,6 @@ typedef int (*zbx_macro_resolve_func_t)(const char *str, size_t length, zbx_uint int hostids_num, char **value, char **error); int zbx_eval_parse_expression(zbx_eval_context_t *ctx, const char *expression, zbx_uint64_t rules, char **error); -zbx_eval_context_t *zbx_eval_parse_expression_dyn(const char *expression, zbx_uint64_t rules, char **error); void zbx_eval_init(zbx_eval_context_t *ctx); void zbx_eval_clear(zbx_eval_context_t *ctx); int zbx_eval_status(const zbx_eval_context_t *ctx); @@ -171,8 +182,8 @@ void zbx_eval_deserialize(zbx_eval_context_t *ctx, const char *expression, zbx_u const unsigned char *data); void zbx_eval_compose_expression(const zbx_eval_context_t *ctx, char **expression); int zbx_eval_execute(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_variant_t *value, char **error); -int zbx_eval_execute_ext(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_eval_function_cb_t function_cb, - zbx_variant_t *value, char **error); +int zbx_eval_execute_ext(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_eval_function_cb_t common_func_cb, + zbx_eval_function_cb_t history_func_cb, void *data, zbx_variant_t *value, char **error); void zbx_eval_get_functionids(zbx_eval_context_t *ctx, zbx_vector_uint64_t *functionids); void zbx_eval_get_functionids_ordered(zbx_eval_context_t *ctx, zbx_vector_uint64_t *functionids); int zbx_eval_expand_user_macros(const zbx_eval_context_t *ctx, zbx_uint64_t *hostids, int hostids_num, @@ -198,4 +209,26 @@ void zbx_eval_copy(zbx_eval_context_t *dst, const zbx_eval_context_t *src, const char *zbx_eval_format_function_error(const char *function, const char *host, const char *key, const char *parameter, const char *error); +void zbx_eval_extract_item_refs(zbx_eval_context_t *ctx, zbx_vector_str_t *refs); + +typedef enum +{ + ZBX_ITEM_QUERY_UNKNOWN, + ZBX_ITEM_QUERY_SINGLE, + ZBX_ITEM_QUERY_MULTI +} +zbx_item_query_type_t; + +typedef struct +{ + char *host; + char *key; + zbx_item_query_type_t type; + int index; +} +zbx_item_query_t; + +void zbx_eval_parse_query(const char *str, size_t len, zbx_item_query_t *query); +void zbx_eval_clear_query(zbx_item_query_t *query); + #endif diff --git a/include/zbxserver.h b/include/zbxserver.h index 4f540cea9dc..1c6509b02cd 100644 --- a/include/zbxserver.h +++ b/include/zbxserver.h @@ -50,10 +50,9 @@ #define MACRO_TYPE_ALLOWED_HOSTS 0x00800000 #define MACRO_TYPE_ITEM_TAG 0x01000000 #define MACRO_TYPE_EVENT_NAME 0x02000000 /* event name in trigger configuration */ -#define MACRO_TYPE_EXPRESSION 0x04000000 /* macros in expression macro */ -#define MACRO_TYPE_SCRIPT_PARAMS_FIELD 0x08000000 -#define MACRO_TYPE_SCRIPT_NORMAL 0x10000000 -#define MACRO_TYPE_SCRIPT_RECOVERY 0x20000000 +#define MACRO_TYPE_SCRIPT_PARAMS_FIELD 0x04000000 +#define MACRO_TYPE_SCRIPT_NORMAL 0x08000000 +#define MACRO_TYPE_SCRIPT_RECOVERY 0x10000000 #define MACRO_EXPAND_NO 0 #define MACRO_EXPAND_YES 1 @@ -65,6 +64,7 @@ int evaluate_function(char **value, DC_ITEM *item, const char *function, const c int evaluate_function2(zbx_variant_t *value, DC_ITEM *item, const char *function, const char *parameter, const zbx_timespec_t *ts, char **error); +int zbx_is_trigger_function(const char *name, size_t len); int substitute_simple_macros(const zbx_uint64_t *actionid, const DB_EVENT *event, const DB_EVENT *r_event, const zbx_uint64_t *userid, const zbx_uint64_t *hostid, const DC_HOST *dc_host, const DC_ITEM *dc_item, diff --git a/src/libs/zbxcommon/str.c b/src/libs/zbxcommon/str.c index 87bb5d57834..eae0c246893 100644 --- a/src/libs/zbxcommon/str.c +++ b/src/libs/zbxcommon/str.c @@ -358,6 +358,58 @@ void zbx_chrcpy_alloc(char **str, size_t *alloc_len, size_t *offset, char c) zbx_strncpy_alloc(str, alloc_len, offset, &c, 1); } +void zbx_strquote_alloc(char **str, size_t *str_alloc, size_t *str_offset, const char *value_str) +{ + size_t size; + const char *src; + char *dst; + + for (size = 2, src = value_str; '\0' != *src; src++) + { + switch (*src) + { + case '\\': + case '"': + size++; + } + size++; + } + + if (*str_alloc <= *str_offset + size) + { + if (0 == *str_alloc) + *str_alloc = size; + + do + { + *str_alloc *= 2; + } + while (*str_alloc - *str_offset <= size); + + *str = zbx_realloc(*str, *str_alloc); + } + + dst = *str + *str_offset; + *dst++ = '"'; + + for (src = value_str; '\0' != *src; src++, dst++) + { + switch (*src) + { + case '\\': + case '"': + *dst++ = '\\'; + break; + } + + *dst = *src; + } + + *dst++ = '"'; + *dst = '\0'; + *str_offset += size; +} + /* Has to be rewritten to avoid malloc */ char *string_replace(const char *str, const char *sub_str1, const char *sub_str2) { @@ -3687,6 +3739,7 @@ static int token_parse_expression_macro(const char *expression, const char *macr { switch (tmp.type) { + case ZBX_TOKEN_MACRO: case ZBX_TOKEN_LLD_MACRO: case ZBX_TOKEN_LLD_FUNC_MACRO: case ZBX_TOKEN_USER_MACRO: diff --git a/src/libs/zbxdbcache/dbconfig.c b/src/libs/zbxdbcache/dbconfig.c index 15499201d0b..8e36a10f769 100644 --- a/src/libs/zbxdbcache/dbconfig.c +++ b/src/libs/zbxdbcache/dbconfig.c @@ -10900,6 +10900,42 @@ int dc_expand_user_macros_len(const char *text, size_t text_len, zbx_uint64_t *h return SUCCEED; } +/****************************************************************************** + * * + * Function: zbx_dc_expand_user_macros_len * + * * + * Purpose: expand user macros in the specified text * + * * + * Parameters: text - [IN] the text value to expand * + * len - [IN] the text length * + * hostids - [IN] an array of related hostids * + * hostids_num - [IN] the number of hostids * + * value - [IN] the expanded macro with expanded user * + * macros. Unknown or invalid macros will be * + * left unresolved. * + * error - [IN] the error message, optional. If specified * + * the function will return failure on first * + * unknown user macro * + * * + * Return value: SUCCEED - the macros were expanded successfully * + * FAIL - error parameter was given and at least one of * + * macros was not expanded * + * * + * Comments: The returned value must be freed by the caller. * + * * + ******************************************************************************/ +int zbx_dc_expand_user_macros_len(const char *text, size_t text_len, zbx_uint64_t *hostids, int hostids_num, + char **value, char **error) +{ + int ret; + + RDLOCK_CACHE; + ret = dc_expand_user_macros_len(text, text_len, hostids, hostids_num, value, error); + UNLOCK_CACHE; + + return ret; +} + /****************************************************************************** * * * Function: dc_expand_user_macros * diff --git a/src/libs/zbxdbupgrade/dbupgrade_5030.c b/src/libs/zbxdbupgrade/dbupgrade_5030.c index 5d08bc83d05..0377916cf15 100644 --- a/src/libs/zbxdbupgrade/dbupgrade_5030.c +++ b/src/libs/zbxdbupgrade/dbupgrade_5030.c @@ -2750,10 +2750,12 @@ static void dbpatch_convert_simple_macro(const char *expression, const zbx_token host = zbx_substr(expression, data->host.l, data->host.r); key = zbx_substr(expression, data->key.l, data->key.r); + /* TODO: clarify if {HOST.HOST} should be replaced with // if (0 == strcmp(host, "{HOST.HOST}")) func->arg0 = zbx_dsprintf(NULL, "//%s", key); else - func->arg0 = zbx_dsprintf(NULL, "/%s/%s", host, key); + */ + func->arg0 = zbx_dsprintf(NULL, "/%s/%s", host, key); zbx_vector_ptr_append(&functions, func); diff --git a/src/libs/zbxeval/Makefile.am b/src/libs/zbxeval/Makefile.am index a788c852c12..c6d4dc4c93a 100644 --- a/src/libs/zbxeval/Makefile.am +++ b/src/libs/zbxeval/Makefile.am @@ -5,5 +5,5 @@ noinst_LIBRARIES = libzbxeval.a libzbxeval_a_SOURCES = \ parse.c \ execute.c \ - misc.c - + misc.c \ + query.c diff --git a/src/libs/zbxeval/execute.c b/src/libs/zbxeval/execute.c index 2a9007007b4..761d0866a6e 100644 --- a/src/libs/zbxeval/execute.c +++ b/src/libs/zbxeval/execute.c @@ -428,11 +428,6 @@ static int eval_execute_push_value(const zbx_eval_context_t *ctx, const zbx_eval suffix2factor(ctx->expression[token->loc.r])); } } - else if (ZBX_EVAL_TOKEN_VAR_TIME == token->type) - { - zbx_variant_set_dbl(&value, atof(ctx->expression + token->loc.l) * - suffix2factor(ctx->expression[token->loc.r])); - } else { dst = zbx_malloc(NULL, token->loc.r - token->loc.l + 2); @@ -1248,50 +1243,47 @@ static int eval_execute_function_bitand(const zbx_eval_context_t *ctx, const zbx * * * Purpose: evaluate function by calling custom callback (if configured) * * * - * Parameters: ctx - [IN] the evaluation context * - * token - [IN] the function token * - * output - [IN/OUT] the output value stack * - * error - [OUT] the error message in the case of failure * + * Parameters: ctx - [IN] the evaluation context * + * token - [IN] the function token * + * functio_cb - [IN] the callback function * + * output - [IN/OUT] the output value stack * + * error - [OUT] the error message in the case of failure * * * * Return value: SUCCEED - the function was executed successfully * * FAIL - otherwise * * * ******************************************************************************/ static int eval_execute_cb_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, - zbx_vector_var_t *output, char **error) + zbx_eval_function_cb_t function_cb, zbx_vector_var_t *output, char **error) { zbx_variant_t value, *args; - - if (NULL == ctx->function_cb) - { - *error = zbx_dsprintf(*error, "unknown function at \"%s\"", ctx->expression + token->loc.l); - return FAIL; - } + char *errmsg = NULL; args = (0 == token->opt ? NULL : &output->values[output->values_num - token->opt]); - if (SUCCEED != ctx->function_cb(ctx->expression + token->loc.l, token->loc.r - token->loc.l + 1, - token->opt, args, &value, error)) + if (SUCCEED != function_cb(ctx->expression + token->loc.l, token->loc.r - token->loc.l + 1, + token->opt, args, ctx->data_cb, &ctx->ts, &value, &errmsg)) { - return FAIL; - } + *error = zbx_dsprintf(*error, "%s at \"%s\".", errmsg, ctx->expression + token->loc.l); + zbx_free(errmsg); - if (ZBX_VARIANT_ERR == value.type && 0 == (ctx->rules & ZBX_EVAL_PROCESS_ERROR)) - { - *error = zbx_dsprintf(*error, "%s at \"%s\"", value.data.err, ctx->expression + token->loc.l); - zbx_variant_clear(&value); - return FAIL; + if (0 == (ctx->rules & ZBX_EVAL_PROCESS_ERROR)) + return FAIL; + + zbx_variant_set_error(&value, *error); + *error = NULL; } eval_function_return(token->opt, &value, output); + return SUCCEED; } /****************************************************************************** * * - * Function: eval_execute_function * + * Function: eval_execute_common_function * * * - * Purpose: evaluate normal (non history) function * + * Purpose: evaluate common function * * * * Parameters: ctx - [IN] the evaluation context * * token - [IN] the function token * @@ -1302,11 +1294,9 @@ static int eval_execute_cb_function(const zbx_eval_context_t *ctx, const zbx_eva * FAIL - otherwise * * * ******************************************************************************/ -static int eval_execute_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, +static int eval_execute_common_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { - char *errmsg = NULL; - if ((zbx_uint32_t)output->values_num < token->opt) { *error = zbx_dsprintf(*error, "not enough arguments for function at \"%s\"", @@ -1339,19 +1329,16 @@ static int eval_execute_function(const zbx_eval_context_t *ctx, const zbx_eval_t if (SUCCEED == eval_compare_token(ctx, &token->loc, "bitand", ZBX_CONST_STRLEN("bitand"))) return eval_execute_function_bitand(ctx, token, output, error); - if (FAIL == eval_execute_cb_function(ctx, token, output, &errmsg)) - { - *error = zbx_dsprintf(*error, "%s at \"%s\"", errmsg, ctx->expression + token->loc.l); - zbx_free(errmsg); - return FAIL; - } + if (NULL != ctx->common_func_cb) + return eval_execute_cb_function(ctx, token, ctx->common_func_cb, output, error); - return SUCCEED; + *error = zbx_dsprintf(*error, "Unknown function at \"%s\".", ctx->expression + token->loc.l); + return FAIL; } /****************************************************************************** * * - * Function: eval_execute_hist_function * + * Function: eval_execute_history_function * * * * Purpose: evaluate history function * * * @@ -1364,19 +1351,21 @@ static int eval_execute_function(const zbx_eval_context_t *ctx, const zbx_eval_t * FAIL - otherwise * * * ******************************************************************************/ -static int eval_execute_hist_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, +static int eval_execute_history_function(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, zbx_vector_var_t *output, char **error) { - char *errmsg = NULL; - - if (FAIL == eval_execute_cb_function(ctx, token, output, &errmsg)) + if ((zbx_uint32_t)output->values_num < token->opt) { - *error = zbx_dsprintf(*error, "%s at \"%s\"", errmsg, ctx->expression + token->loc.l); - zbx_free(errmsg); + *error = zbx_dsprintf(*error, "not enough arguments for function at \"%s\"", + ctx->expression + token->loc.l); return FAIL; } - return SUCCEED; + if (NULL != ctx->history_func_cb) + return eval_execute_cb_function(ctx, token, ctx->history_func_cb, output, error); + + *error = zbx_dsprintf(*error, "Unknown function at \"%s\".", ctx->expression + token->loc.l); + return FAIL; } /****************************************************************************** @@ -1445,7 +1434,6 @@ static int eval_execute(const zbx_eval_context_t *ctx, zbx_variant_t *value, cha switch (token->type) { case ZBX_EVAL_TOKEN_VAR_NUM: - case ZBX_EVAL_TOKEN_VAR_TIME: case ZBX_EVAL_TOKEN_VAR_STR: case ZBX_EVAL_TOKEN_VAR_MACRO: case ZBX_EVAL_TOKEN_VAR_USERMACRO: @@ -1461,11 +1449,11 @@ static int eval_execute(const zbx_eval_context_t *ctx, zbx_variant_t *value, cha eval_execute_push_null(&output); break; case ZBX_EVAL_TOKEN_FUNCTION: - if (SUCCEED != eval_execute_function(ctx, token, &output, error)) + if (SUCCEED != eval_execute_common_function(ctx, token, &output, error)) goto out; break; case ZBX_EVAL_TOKEN_HIST_FUNCTION: - if (SUCCEED != eval_execute_hist_function(ctx, token, &output, error)) + if (SUCCEED != eval_execute_history_function(ctx, token, &output, error)) goto out; break; case ZBX_EVAL_TOKEN_FUNCTIONID: @@ -1520,15 +1508,20 @@ out: * * * Purpose: initialize execution context * * * - * Parameters: ctx - [IN] the evaluation context * - * ts - [IN] the timestamp of the execution time * - * function_cb - [IN] the callback for function processing * + * Parameters: ctx - [IN] the evaluation context * + * ts - [IN] the timestamp of the execution time * + * common_func_cb - [IN] the common function callback (optional) * + * history_func_cb - [IN] the history function callback (optional)* + * data_cb - [IN] the caller data to be passed to callback* + * functions * * * ******************************************************************************/ static void eval_init_execute_context(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, - zbx_eval_function_cb_t function_cb) + zbx_eval_function_cb_t common_func_cb, zbx_eval_function_cb_t history_func_cb, void *data_cb) { - ctx->function_cb = function_cb; + ctx->common_func_cb = common_func_cb; + ctx->history_func_cb = history_func_cb; + ctx->data_cb = data_cb; if (NULL == ts) ctx->ts.sec = ctx->ts.ns = 0; @@ -1553,7 +1546,7 @@ static void eval_init_execute_context(zbx_eval_context_t *ctx, const zbx_timespe ******************************************************************************/ int zbx_eval_execute(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_variant_t *value, char **error) { - eval_init_execute_context(ctx, ts, NULL); + eval_init_execute_context(ctx, ts, NULL, NULL, NULL); return eval_execute(ctx, value, error); } @@ -1565,11 +1558,12 @@ int zbx_eval_execute(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_vari * Purpose: evaluate parsed expression with callback for custom function * * processing * * * - * Parameters: ctx - [IN] the evaluation context * - * ts - [IN] the timestamp of the execution time * - * function_cb - [IN] the callback for function processing * - * value - [OUT] the resulting value * - * error - [OUT] the error message in the case of failure * + * Parameters: ctx - [IN] the evaluation context * + * ts - [IN] the timestamp of the execution time * + * common_func_cb - [IN] the common function callback (optional) * + * history_func_cb - [IN] the history function callback (optional)* + * value - [OUT] the resulting value * + * error - [OUT] the error message * * * * Return value: SUCCEED - the expression was evaluated successfully * * FAIL - otherwise * @@ -1578,10 +1572,10 @@ int zbx_eval_execute(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_vari * functions. * * * ******************************************************************************/ -int zbx_eval_execute_ext(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_eval_function_cb_t function_cb, - zbx_variant_t *value, char **error) +int zbx_eval_execute_ext(zbx_eval_context_t *ctx, const zbx_timespec_t *ts, zbx_eval_function_cb_t common_func_cb, + zbx_eval_function_cb_t history_func_cb, void *data, zbx_variant_t *value, char **error) { - eval_init_execute_context(ctx, ts, function_cb); + eval_init_execute_context(ctx, ts, common_func_cb, history_func_cb, data); return eval_execute(ctx, value, error); } diff --git a/src/libs/zbxeval/misc.c b/src/libs/zbxeval/misc.c index 7d2275792fb..ec877071b5f 100644 --- a/src/libs/zbxeval/misc.c +++ b/src/libs/zbxeval/misc.c @@ -277,9 +277,7 @@ static void eval_token_print_alloc(const zbx_eval_context_t *ctx, char **str, si const zbx_eval_token_t *token) { int quoted = 0, check_value = 0; - const char *src, *value_str; - char *dst; - size_t size; + const char *value_str; if (ZBX_VARIANT_NONE == token->value.type) return; @@ -338,55 +336,9 @@ static void eval_token_print_alloc(const zbx_eval_context_t *ctx, char **str, si value_str = zbx_variant_value_desc(&token->value); if (0 == quoted) - { zbx_strcpy_alloc(str, str_alloc, str_offset, value_str); - return; - } - - for (size = 2, src = value_str; '\0' != *src; src++) - { - switch (*src) - { - case '\\': - case '"': - size++; - } - size++; - } - - if (*str_alloc <= *str_offset + size) - { - if (0 == *str_alloc) - *str_alloc = size; - - do - { - *str_alloc *= 2; - } - while (*str_alloc - *str_offset <= size); - - *str = zbx_realloc(*str, *str_alloc); - } - - dst = *str + *str_offset; - *dst++ = '"'; - - for (src = value_str; '\0' != *src; src++, dst++) - { - switch (*src) - { - case '\\': - case '"': - *dst++ = '\\'; - break; - } - - *dst = *src; - } - - *dst++ = '"'; - *dst = '\0'; - *str_offset += size; + else + zbx_strquote_alloc(str, str_alloc, str_offset, value_str); } /****************************************************************************** @@ -473,6 +425,8 @@ int zbx_eval_expand_user_macros(const zbx_eval_context_t *ctx, zbx_uint64_t *hos hostids, hostids_num, &value, error); break; case ZBX_EVAL_TOKEN_VAR_STR: + case ZBX_EVAL_TOKEN_VAR_NUM: + case ZBX_EVAL_TOKEN_ARG_PERIOD: if (NULL == (ptr = strstr(ctx->expression + token->loc.l, "{$")) || ptr >= ctx->expression + token->loc.r) { @@ -970,3 +924,29 @@ char *zbx_eval_format_function_error(const char *function, const char *host, con return msg; } + +/****************************************************************************** + * * + * Function: zbx_eval_extract_history_queries * + * * + * Purpose: copy history query into vector and replace it with vector index * + * * + * Parameters: ctx - [IN] the evaluation context * + * refs - [OUT] the item references * + * * + ******************************************************************************/ +void zbx_eval_extract_item_refs(zbx_eval_context_t *ctx, zbx_vector_str_t *refs) +{ + int i; + + for (i = 0; i < ctx->stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx->stack.values[i]; + + if (ZBX_EVAL_TOKEN_ARG_QUERY != token->type) + continue; + + zbx_variant_set_ui64(&token->value, refs->values_num); + zbx_vector_str_append(refs, zbx_substr(ctx->expression, token->loc.l, token->loc.r)); + } +} diff --git a/src/libs/zbxeval/parse.c b/src/libs/zbxeval/parse.c index 2602a03f6e0..f76f95c887e 100644 --- a/src/libs/zbxeval/parse.c +++ b/src/libs/zbxeval/parse.c @@ -468,19 +468,7 @@ static int eval_parse_number_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval len += offset; - switch (ctx->expression[pos + len - 1]) - { - case 's': - case 'm': - case 'h': - case 'd': - case 'w': - token->type = ZBX_EVAL_TOKEN_VAR_TIME; - break; - default: - token->type = ZBX_EVAL_TOKEN_VAR_NUM; - break; - } + token->type = ZBX_EVAL_TOKEN_VAR_NUM; tmp = strtod(ctx->expression + pos, &end) * suffix2factor(ctx->expression[pos + len - 1]); if (HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno) @@ -594,7 +582,7 @@ static int eval_parse_function_token(zbx_eval_context_t *ctx, size_t pos, zbx_ev ******************************************************************************/ static int eval_parse_query_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_token_t *token, char **error) { -#define MVAR_HOST_HOST "{HOST.HOST}" +#define MVAR_HOST_HOST "{HOST.HOST" const char *ptr = ctx->expression + pos + 1, *key; @@ -605,8 +593,25 @@ static int eval_parse_query_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_ break; case '{': if (0 == strncmp(ptr, MVAR_HOST_HOST, ZBX_CONST_STRLEN(MVAR_HOST_HOST))) - ptr += ZBX_CONST_STRLEN(MVAR_HOST_HOST); - break; + { + int offset = 0; + + if ('}' == ptr[ZBX_CONST_STRLEN(MVAR_HOST_HOST)]) + { + offset = 1; + } + else if (0 != isdigit((unsigned char)ptr[ZBX_CONST_STRLEN(MVAR_HOST_HOST)]) && + '}' == ptr[ZBX_CONST_STRLEN(MVAR_HOST_HOST) + 1]) + { + offset = 2; + } + + if (0 != offset) + { + ptr += ZBX_CONST_STRLEN(MVAR_HOST_HOST) + offset; + break; + } + } default: while (SUCCEED == is_hostname_char(*ptr)) ptr++; @@ -614,7 +619,13 @@ static int eval_parse_query_token(zbx_eval_context_t *ctx, size_t pos, zbx_eval_ if ('/' != *ptr) { - *error = zbx_dsprintf(*error, "invalid host name in query starting at \"%s\"", ctx->expression + pos); + if (ptr == ctx->expression + pos + 1) + { + *error = zbx_dsprintf(*error, "invalid host name in query starting at \"%s\"", ctx->expression + pos); + return FAIL; + } + + *error = zbx_dsprintf(*error, "missing item key in query starting at \"%s\"", ctx->expression + pos); return FAIL; } @@ -953,7 +964,8 @@ static int eval_parse_expression(zbx_eval_context_t *ctx, const char *expression goto out; } - if (ZBX_EVAL_TOKEN_ARG_QUERY == ctx->last_token_type && ZBX_EVAL_TOKEN_COMMA != token.type) + if (ZBX_EVAL_TOKEN_ARG_QUERY == ctx->last_token_type && ZBX_EVAL_TOKEN_COMMA != token.type && + ZBX_EVAL_TOKEN_GROUP_CLOSE != token.type) { *error = zbx_dsprintf(*error, "invalid expression following query token at \"%s\"", ctx->expression + pos); @@ -1171,30 +1183,6 @@ int zbx_eval_parse_expression(zbx_eval_context_t *ctx, const char *expression, z return eval_parse_expression(ctx, expression, rules, error); } -/****************************************************************************** - * * - * Function: zbx_eval_parse_expression * - * * - * Purpose: parse expression into tokens in postfix notation order * - * * - * Parameters: expression - [IN] the expression to parse * - * rules - [IN] the parsing rules * - * error - [OUT] the error message in the case of failure * - * * - * Return value: The evaluation context or NULL in the case of error. * - * * - ******************************************************************************/ -zbx_eval_context_t *zbx_eval_parse_expression_dyn(const char *expression, zbx_uint64_t rules, char **error) -{ - zbx_eval_context_t *ctx; - - ctx = (zbx_eval_context_t *)zbx_malloc(NULL, sizeof(zbx_eval_context_t)); - if (SUCCEED != zbx_eval_parse_expression(ctx, expression, rules, error)) - zbx_free(ctx); - - return ctx; -} - /****************************************************************************** * * * Function: zbx_eval_init * diff --git a/src/libs/zbxserver/evalfunc2.c b/src/libs/zbxserver/evalfunc2.c index 4897f358d89..afc577adc47 100644 --- a/src/libs/zbxserver/evalfunc2.c +++ b/src/libs/zbxserver/evalfunc2.c @@ -2340,3 +2340,36 @@ int evaluate_function2(zbx_variant_t *value, DC_ITEM *item, const char *function return ret; } + +/****************************************************************************** + * * + * Function: zbx_is_trigger_function * + * * + * Purpose: check if the specified function is a trigger function * + * * + * Parameters: name - [IN] the function name to check * + * len - [IN] the length of function name * + * * + * Return value: SUCCEED - the function is a trigger function * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_is_trigger_function(const char *name, size_t len) +{ + char *functions[] = {"last", "min", "max", "avg", "sum", "percentile", "count", "nodata", "change", "find", + "fuzzytime", "logeventid", "logseverity", "logsource", "band", "forecast", "timeleft", + "trendavg", "trendcount", "trendmax", "trendmin", "trendsum", + NULL}; + char **ptr; + + for (ptr = functions; NULL != *ptr; ptr++) + { + size_t compare_len; + + compare_len = strlen(*ptr); + if (compare_len == len && 0 == memcmp(*ptr, name, len)) + return SUCCEED; + } + + return FAIL; +} diff --git a/src/libs/zbxserver/expression.c b/src/libs/zbxserver/expression.c index 0706454d648..582409470a3 100644 --- a/src/libs/zbxserver/expression.c +++ b/src/libs/zbxserver/expression.c @@ -22,6 +22,7 @@ #include "log.h" #include "zbxregexp.h" #include "zbxvariant.h" +#include "zbxeval.h" #include "valuecache.h" #include "macrofunc.h" @@ -2366,40 +2367,6 @@ static void get_event_value(const char *macro, const DB_EVENT *event, char **rep } } -/****************************************************************************** - * * - * Function: get_expression_macro_result * - * * - * Purpose: calculate result of expression macro * - * * - * Return value: upon successful completion return SUCCEED * - * otherwise FAIL * - * * - ******************************************************************************/ -static int get_expression_macro_result(const DB_EVENT *event, const DB_EVENT *r_event, char **expression, - char **replace_to, char *error, int maxerrlen) -{ - int ret; - double expression_result; - - zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, *expression); - - substitute_simple_macros_impl(NULL, event, r_event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, expression, - MACRO_TYPE_EXPRESSION, error, maxerrlen); - - if (SUCCEED == (ret = evaluate(&expression_result, *expression, error, maxerrlen, NULL))) - { - char buffer[ZBX_MAX_DOUBLE_LEN + 1]; - - zbx_print_double(buffer, sizeof(buffer), expression_result); - *replace_to = zbx_strdup(*replace_to, buffer); - } - - zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); - - return ret; -} - /****************************************************************************** * * * Function: get_history_log_value * @@ -2643,6 +2610,294 @@ out: return ret; } + +typedef struct +{ + const DB_EVENT *event; +} +zbx_expression_macro_data_t; + +/****************************************************************************** + * * + * Function: expression_macro_common_func_cb * + * * + * Purpose: evaluate common function * + * * + * Parameters: name - [IN] the function name (not zero terminated) * + * len - [IN] the function name length * + * args_num - [IN] the number of function arguments * + * args - [IN] an array of the function arguments. * + * data - [IN] the caller data used for function evaluation * + * ts - [IN] the function execution time * + * value - [OUT] the function return value * + * error - [OUT] the error message if function failed * + * * + * Return value: SUCCEED - the function was executed successfully * + * FAIL - otherwise * + * * + * Comments: There are no custom common functions for expression macros, but * + * it's used to check for /host/key query quoting errors instead. * + * * + ******************************************************************************/ +static int expression_macro_common_func_cb(const char *name, size_t len, int args_num, const zbx_variant_t *args, + void *data, const zbx_timespec_t *ts, zbx_variant_t *value, char **error) +{ + ZBX_UNUSED(data); + ZBX_UNUSED(ts); + ZBX_UNUSED(value); + + if (SUCCEED != zbx_is_trigger_function(name, len)) + { + *error = zbx_strdup(NULL, "Cannot evaluate formula: unsupported function"); + return FAIL; + } + + if (0 == args_num) + { + *error = zbx_strdup(NULL, "Cannot evaluate function: invalid number of arguments"); + return FAIL; + } + + if (ZBX_VARIANT_STR == args[0].type) + { + zbx_item_query_t query; + + zbx_eval_parse_query(args[0].data.str, strlen(args[0].data.str), &query); + + if (ZBX_ITEM_QUERY_UNKNOWN != query.type) + { + zbx_eval_clear_query(&query); + *error = zbx_strdup(NULL, "Cannot evaluate function: quoted item query argument"); + return FAIL; + } + } + + *error = zbx_strdup(NULL, "Cannot evaluate function: invalid first argument"); + return FAIL; +} + +static int expression_macro_history_func_cb(const char *name, size_t len, int args_num, const zbx_variant_t *args, + void *data, const zbx_timespec_t *ts, zbx_variant_t *value, char **error) +{ + zbx_expression_macro_data_t *md = (zbx_expression_macro_data_t *)data; + zbx_item_query_t query; + zbx_host_key_t hkey; + DC_ITEM item; + int i, errcode, ret = FAIL; + char func_name[MAX_STRING_LEN], *params = NULL, *errmsg = NULL, *host = NULL; + size_t params_alloc = 0, params_offset = 0; + int N_functionid = 0; + + if (SUCCEED != zbx_is_trigger_function(name, len)) + { + *error = zbx_strdup(NULL, "Cannot evaluate formula: unsupported function"); + return FAIL; + } + + if (0 == args_num) + { + *error = zbx_strdup(NULL, "Cannot evaluate function: invalid number of arguments"); + return FAIL; + } + + if (ZBX_VARIANT_STR != args[0].type) + { + *error = zbx_strdup(NULL, "Cannot evaluate function: invalid first argument"); + return FAIL; + } + + zbx_eval_parse_query(args[0].data.str, strlen(args[0].data.str), &query); + + if (ZBX_ITEM_QUERY_SINGLE != query.type) + { + zbx_eval_clear_query(&query); + *error = zbx_strdup(NULL, "Cannot evaluate function: invalid item query"); + return FAIL; + } + + if (query.host != NULL) + { + zbx_strloc_t loc; + + loc.l = 0; + loc.r = strlen(query.host) - 1; + macro_in_list(query.host, loc, simple_host_macros, &N_functionid); + } + else + N_functionid = 1; + + if (0 != N_functionid) + { + if (SUCCEED != DBget_trigger_value(&md->event->trigger, &host, N_functionid, + ZBX_REQUEST_HOST_HOST)) + { + *error = zbx_dsprintf(NULL, "cannot get host from trigger expression by index"); + goto out; + } + hkey.host = host; + } + else + hkey.host = query.host; + + hkey.key = query.key; + + DCconfig_get_items_by_keys(&item, &hkey, &errcode, 1); + + if (SUCCEED != errcode) + { + *error = zbx_dsprintf(NULL, "Cannot evaluate function because item \"/%s/%s\" does not exist", + hkey.host, hkey.key); + goto out; + } + + /* do not evaluate if the item is disabled or belongs to a disabled host */ + + if (ITEM_STATUS_ACTIVE != item.status) + { + *error = zbx_dsprintf(NULL, "Cannot evaluate function with disabled item \"/%s/%s\"", + hkey.host, hkey.key); + goto out; + } + + if (HOST_STATUS_MONITORED != item.host.status) + { + *error = zbx_dsprintf(NULL, "Cannot evaluate function with item \"/%s/%s\" belonging to a disabled host", + hkey.host, hkey.key); + goto out; + } + + memcpy(func_name, name, len); + func_name[len] = '\0'; + + /* If the item is NOTSUPPORTED then evaluation is allowed for: */ + /* - functions white-listed in evaluatable_for_notsupported(). */ + /* Their values can be evaluated to regular numbers even for */ + /* NOTSUPPORTED items. */ + /* - other functions. Result of evaluation is ZBX_UNKNOWN. */ + + if (ITEM_STATE_NOTSUPPORTED == item.state && FAIL == zbx_evaluatable_for_notsupported(func_name)) + { + *error = zbx_dsprintf(NULL,"Cannot evaluate function with not supported item \"/%s/%s\"", + hkey.host, hkey.key); + goto out; + } + + if (1 == args_num) + { + if (SUCCEED != (ret = evaluate_function2(value, &item, func_name, "", ts, &errmsg))) + { + *error = zbx_dsprintf(NULL, "Cannot evaluate calculated item formula: %s", errmsg); + zbx_free(errmsg); + } + goto out; + } + + for (i = 1; i < args_num; i++) + { + if (0 != i) + zbx_chrcpy_alloc(¶ms, ¶ms_alloc, ¶ms_offset, ','); + + switch (args[i].type) + { + case ZBX_VARIANT_DBL: + zbx_snprintf_alloc(¶ms, ¶ms_alloc, ¶ms_offset, ZBX_FS_DBL64, + args[i].data.dbl); + break; + case ZBX_VARIANT_STR: + zbx_strquote_alloc(¶ms, ¶ms_alloc, ¶ms_offset, args[i].data.str); + break; + case ZBX_VARIANT_UI64: + zbx_snprintf_alloc(¶ms, ¶ms_alloc, ¶ms_offset, ZBX_FS_UI64, + args[i].data.ui64); + break; + case ZBX_VARIANT_NONE: + break; + default: + *error = zbx_dsprintf(NULL,"Cannot evaluate function \"%s\":" + " unsupported argument #%d type: %s", + func_name, i + 1, zbx_variant_type_desc(&args[i])); + goto out; + } + } + + if (SUCCEED != (ret = evaluate_function2(value, &item, func_name, params, ts, &errmsg))) + { + *error = zbx_dsprintf(NULL, "Cannot evaluate calculated item formula: %s", errmsg); + zbx_free(errmsg); + } + +out: + zbx_free(params); + zbx_free(host); + zbx_eval_clear_query(&query); + DCconfig_clean_items(&item, &errcode, 1); + + return ret; +} + + +/****************************************************************************** + * * + * Function: get_expression_macro_result * + * * + * Purpose: calculate result of expression macro * + * * + * Return value: upon successful completion return SUCCEED * + * otherwise FAIL * + * * + ******************************************************************************/ +static int get_expression_macro_result(const DB_EVENT *event, const DB_EVENT *r_event, const char *expression, + char **replace_to, char **error) +{ + int ret = FAIL; + zbx_eval_context_t ctx; + const zbx_vector_uint64_t *hostids; + zbx_timespec_t ts; + zbx_variant_t value; + zbx_expression_macro_data_t data; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __func__, expression); + + ZBX_UNUSED(r_event); + + if (SUCCEED != zbx_eval_parse_expression(&ctx, expression, ZBX_EVAL_PARSE_EXPRESSION_MACRO, error)) + goto out; + + if (SUCCEED != zbx_db_trigger_get_all_hostids(&event->trigger, &hostids)) + { + *error = zbx_strdup(NULL, "cannot obtain host identifiers for the expression macro"); + goto out; + } + + if (SUCCEED != zbx_eval_expand_user_macros(&ctx, hostids->values, hostids->values_num, + zbx_dc_expand_user_macros_len, error)) + { + goto out; + } + + ts.sec = event->clock; + ts.ns = event->ns; + + data.event = event; + + if (SUCCEED != zbx_eval_execute_ext(&ctx, &ts, expression_macro_common_func_cb, + expression_macro_history_func_cb, (void *)&data, &value, error)) + { + goto out; + } + + *replace_to = zbx_strdup(NULL, zbx_variant_value_desc(&value)); + zbx_variant_clear(&value); + + ret = SUCCEED; +out: + zbx_eval_clear(&ctx); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret)); + + return ret; +} + /****************************************************************************** * * * Function: cache_item_hostid * @@ -2961,8 +3216,8 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ break; case ZBX_TOKEN_SIMPLE_MACRO: if (0 == (macro_type & (MACRO_TYPE_MESSAGE_NORMAL | MACRO_TYPE_MESSAGE_RECOVERY | - MACRO_TYPE_MESSAGE_ACK | MACRO_TYPE_EXPRESSION | - MACRO_TYPE_SCRIPT_NORMAL | MACRO_TYPE_SCRIPT_RECOVERY)) || + MACRO_TYPE_MESSAGE_ACK | MACRO_TYPE_SCRIPT_NORMAL | + MACRO_TYPE_SCRIPT_RECOVERY)) || EVENT_SOURCE_TRIGGERS != ((NULL != r_event) ? r_event : event)->source) { pos++; @@ -4108,16 +4363,24 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ { if (0 != (macro_type & MACRO_TYPE_EVENT_NAME)) { - char *exp = NULL; + char *exp = NULL, *errmsg = NULL; size_t exp_alloc = 0, exp_offset = 0; zbx_strloc_t *loc = &inner_token.data.expression_macro.expression; zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, *data + loc->l, loc->r - loc->l + 1); - ret = get_expression_macro_result(event, r_event, &exp, &replace_to, - error, maxerrlen); + ret = get_expression_macro_result(event, r_event, exp, &replace_to, + &errmsg); zbx_free(exp); + + if (SUCCEED != ret) + { + zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot evaluate" + " expression macro: %s", __func__, errmsg); + zbx_strlcpy(error, errmsg, maxerrlen); + zbx_free(errmsg); + } } } else if (0 == strcmp(m, MVAR_HOST_HOST) || 0 == strcmp(m, MVAR_HOSTNAME)) @@ -4666,24 +4929,6 @@ static int substitute_simple_macros_impl(const zbx_uint64_t *actionid, const DB_ } } } - else if (0 == indexed_macro && 0 != (macro_type & MACRO_TYPE_EXPRESSION)) - { - const DB_EVENT *c_event; - - c_event = ((NULL != r_event) ? r_event : event); - - if (ZBX_TOKEN_USER_MACRO == token.type) - { - if (SUCCEED == zbx_db_trigger_get_all_hostids(&c_event->trigger, &phostids)) - DCget_user_macro(phostids->values, phostids->values_num, m, &replace_to); - pos = token.loc.r; - } - else if (ZBX_TOKEN_SIMPLE_MACRO == token.type) - { - ret = get_trigger_function_value(&c_event->trigger, &replace_to, - *data, &token.data.simple_macro, ZBX_FORMAT_RAW); - } - } if (0 != (macro_type & MACRO_TYPE_HTTP_JSON) && NULL != replace_to) zbx_json_escape(&replace_to); diff --git a/tests/libs/zbxcommon/zbx_token_find.yaml b/tests/libs/zbxcommon/zbx_token_find.yaml index 798680d3e45..c09ddf73479 100644 --- a/tests/libs/zbxcommon/zbx_token_find.yaml +++ b/tests/libs/zbxcommon/zbx_token_find.yaml @@ -543,7 +543,7 @@ in: out: token: '{?{HOST.HOST}' token_type: ZBX_TOKEN_EXPRESSION_MACRO - expression: '{HOST.HOST' + expression: '{HOST.HOST}' return: SUCCEED --- test case: 'Success: {?{$MACRO}}' diff --git a/tests/libs/zbxeval/zbx_eval_execute_ext.c b/tests/libs/zbxeval/zbx_eval_execute_ext.c index 185c3ad5f84..0a03776c7bd 100644 --- a/tests/libs/zbxeval/zbx_eval_execute_ext.c +++ b/tests/libs/zbxeval/zbx_eval_execute_ext.c @@ -112,12 +112,14 @@ static void mock_read_callbacks(const char *path) } } -static int callback_cb(const char *name, size_t len, int args_num, const zbx_variant_t *args, - zbx_variant_t *value, char **error) +static int callback_cb(const char *name, size_t len, int args_num, const zbx_variant_t *args, void *data, + const zbx_timespec_t *ts, zbx_variant_t *value, char **error) { int i; ZBX_UNUSED(args); + ZBX_UNUSED(data); + ZBX_UNUSED(ts); for (i = 0; i < callbacks.values_num; i++) { @@ -167,7 +169,7 @@ void zbx_mock_test_entry(void **state) mock_eval_read_values(&ctx, "in.replace"); mock_read_callbacks("in.callbacks"); - returned_ret = zbx_eval_execute_ext(&ctx, NULL, callback_cb, &value, &error); + returned_ret = zbx_eval_execute_ext(&ctx, NULL, callback_cb, callback_cb, NULL, &value, &error); if (SUCCEED != returned_ret) printf("ERROR: %s\n", error); diff --git a/tests/libs/zbxeval/zbx_eval_execute_ext.yaml b/tests/libs/zbxeval/zbx_eval_execute_ext.yaml index c01faf467ff..4c9aea8aaca 100644 --- a/tests/libs/zbxeval/zbx_eval_execute_ext.yaml +++ b/tests/libs/zbxeval/zbx_eval_execute_ext.yaml @@ -52,7 +52,7 @@ in: callbacks: - name: err args_num: 0 - reterr: 'forced return error' + error: 'forced return error' out: result: SUCCEED value: 1 @@ -64,7 +64,7 @@ in: callbacks: - name: err args_num: 0 - reterr: 'forced return error' + error: 'forced return error' out: result: FAIL --- @@ -75,7 +75,7 @@ in: callbacks: - name: err args_num: 0 - reterr: 'forced return error' + error: 'forced return error' out: result: SUCCEED value: 0 @@ -87,7 +87,7 @@ in: callbacks: - name: err args_num: 0 - reterr: 'forced return error' + error: 'forced return error' out: result: FAIL --- diff --git a/tests/libs/zbxeval/zbx_eval_parse_expression.c b/tests/libs/zbxeval/zbx_eval_parse_expression.c index d1fcb36549a..4669d5e81d3 100644 --- a/tests/libs/zbxeval/zbx_eval_parse_expression.c +++ b/tests/libs/zbxeval/zbx_eval_parse_expression.c @@ -48,7 +48,6 @@ static const char *mock_token_type2str(zbx_uint32_t type) ZBX_MOCK_TOKEN_CASE(OP_NOT) ZBX_MOCK_TOKEN_CASE(VAR_NUM) ZBX_MOCK_TOKEN_CASE(VAR_STR) - ZBX_MOCK_TOKEN_CASE(VAR_TIME) ZBX_MOCK_TOKEN_CASE(VAR_MACRO) ZBX_MOCK_TOKEN_CASE(VAR_USERMACRO) ZBX_MOCK_TOKEN_CASE(VAR_LLDMACRO) @@ -90,7 +89,6 @@ static zbx_uint32_t mock_token_str2type(const char *str) ZBX_MOCK_TOKEN_IF(OP_NOT) ZBX_MOCK_TOKEN_IF(VAR_NUM) ZBX_MOCK_TOKEN_IF(VAR_STR) - ZBX_MOCK_TOKEN_IF(VAR_TIME) ZBX_MOCK_TOKEN_IF(VAR_MACRO) ZBX_MOCK_TOKEN_IF(VAR_USERMACRO) ZBX_MOCK_TOKEN_IF(VAR_LLDMACRO) diff --git a/tests/libs/zbxeval/zbx_eval_parse_expression.yaml b/tests/libs/zbxeval/zbx_eval_parse_expression.yaml index 4d5e1251d80..0be187fded3 100644 --- a/tests/libs/zbxeval/zbx_eval_parse_expression.yaml +++ b/tests/libs/zbxeval/zbx_eval_parse_expression.yaml @@ -1508,6 +1508,59 @@ in: out: result: FAIL --- +test case: Succeed 'last(/host/key)' +in: + rules: [ZBX_EVAL_PARSE_ITEM_QUERY,ZBX_EVAL_PARSE_FUNCTION] + expression: 'last(/host/key)' +out: + stack: + - {type: ZBX_EVAL_TOKEN_ARG_QUERY, token: '/host/key', opt: 0} + - {type: ZBX_EVAL_TOKEN_HIST_FUNCTION, token: 'last', opt: 1} + result: SUCCEED +--- +test case: Succeed 'last(/host/key,#1)' +in: + rules: [ZBX_EVAL_PARSE_ITEM_QUERY,ZBX_EVAL_PARSE_FUNCTION] + expression: 'last(/host/key,#1)' +out: + stack: + - {type: ZBX_EVAL_TOKEN_ARG_QUERY, token: '/host/key', opt: 0} + - {type: ZBX_EVAL_TOKEN_ARG_PERIOD, token: '#1', opt: 0} + - {type: ZBX_EVAL_TOKEN_HIST_FUNCTION, token: 'last', opt: 2} + result: SUCCEED +--- +test case: Succeed 'count(//trap[1],#5,,0)' +in: + rules: [ZBX_EVAL_PARSE_ITEM_QUERY,ZBX_EVAL_PARSE_FUNCTION] + expression: 'count(//trap[1],#5,,0)' +out: + stack: + - {type: ZBX_EVAL_TOKEN_ARG_QUERY, token: '//trap[1]', opt: 0} + - {type: ZBX_EVAL_TOKEN_ARG_PERIOD, token: '#5', opt: 0} + - {type: ZBX_EVAL_TOKEN_ARG_NULL, token: '', opt: 0} + - {type: ZBX_EVAL_TOKEN_VAR_NUM, token: '0', opt: 0} + - {type: ZBX_EVAL_TOKEN_HIST_FUNCTION, token: 'count', opt: 4} + result: SUCCEED +--- +test case: Succeed 'count(//trap[1],#5,,0) + count(/Trapper/trap[1] ,5m ,"1h ","1 ")' +in: + rules: [ZBX_EVAL_PARSE_ITEM_QUERY,ZBX_EVAL_PARSE_FUNCTION] + expression: 'count(//trap[1],#5,,0) + count(/Trapper/trap[1] ,5m ,"1h","1")' +out: + stack: + - {type: ZBX_EVAL_TOKEN_ARG_QUERY, token: '//trap[1]', opt: 0} + - {type: ZBX_EVAL_TOKEN_ARG_PERIOD, token: '#5', opt: 0} + - {type: ZBX_EVAL_TOKEN_ARG_NULL, token: '', opt: 0} + - {type: ZBX_EVAL_TOKEN_VAR_NUM, token: '0', opt: 0} + - {type: ZBX_EVAL_TOKEN_HIST_FUNCTION, token: 'count', opt: 4} + - {type: ZBX_EVAL_TOKEN_ARG_QUERY, token: '/Trapper/trap[1]', opt: 0} + - {type: ZBX_EVAL_TOKEN_ARG_PERIOD, token: '5m', opt: 0} + - {type: ZBX_EVAL_TOKEN_VAR_STR, token: '"1h"', opt: 0} + - {type: ZBX_EVAL_TOKEN_VAR_STR, token: '"1"', opt: 0} + - {type: ZBX_EVAL_TOKEN_HIST_FUNCTION, token: 'count', opt: 4} + - {type: ZBX_EVAL_TOKEN_OP_ADD, token: '+', opt: 0} + result: SUCCEED +--- test case: Succeed 'min(1 + 2, 0.5 + 1.5)' in: rules: [ZBX_EVAL_PARSE_FUNCTION] @@ -1557,4 +1610,17 @@ out: - {type: ZBX_EVAL_TOKEN_VAR_NUM, token: '-1', opt: 0} - {type: ZBX_EVAL_TOKEN_OP_EQ, token: '=', opt: 0} result: SUCCEED +--- +test case: Succeed 'last(/host/key,#1:now-1h)' +in: + rules: [ZBX_EVAL_PARSE_ITEM_QUERY,ZBX_EVAL_PARSE_FUNCTION] + expression: 'last(/host/key,#1:now-1h)' +out: + stack: + - {type: ZBX_EVAL_TOKEN_ARG_QUERY, token: '/host/key', opt: 0} + - {type: ZBX_EVAL_TOKEN_ARG_PERIOD, token: '#1:now-1h', opt: 0} + - {type: ZBX_EVAL_TOKEN_HIST_FUNCTION, token: 'last', opt: 2} + result: SUCCEED ... + + -- cgit v1.2.3 From 548f63fa49044106d6f484a9805b5eca0fec14a4 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 15:46:14 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed expression condition popup --- ui/app/controllers/CControllerPopupTriggerExpr.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/app/controllers/CControllerPopupTriggerExpr.php b/ui/app/controllers/CControllerPopupTriggerExpr.php index 5d51be2690f..d6b6770a43b 100644 --- a/ui/app/controllers/CControllerPopupTriggerExpr.php +++ b/ui/app/controllers/CControllerPopupTriggerExpr.php @@ -598,9 +598,6 @@ class CControllerPopupTriggerExpr extends CController { ? $value_token->data['string'] : $value_token->match; } - else { - break; - } if (!in_array($fn_name, getStandaloneFunctions()) && ($query = $function_token->getFunctionTriggerQuery()) !== null) { -- cgit v1.2.3 From 56ea1ce3cdf3d456caac4af2627e87220914d62e Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 15:54:00 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed expression condition popup --- ui/app/controllers/CControllerPopupTriggerExpr.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/app/controllers/CControllerPopupTriggerExpr.php b/ui/app/controllers/CControllerPopupTriggerExpr.php index d6b6770a43b..d525212e34b 100644 --- a/ui/app/controllers/CControllerPopupTriggerExpr.php +++ b/ui/app/controllers/CControllerPopupTriggerExpr.php @@ -593,11 +593,11 @@ class CControllerPopupTriggerExpr extends CController { if (array_key_exists($fn_name, $this->functions) && in_array($operator_token->match, $this->functions[$fn_name]['operators'])) { $operator = $operator_token->match; - $value = (($value_token instanceof CTriggerExprParserResult) - && array_key_exists('string', $value_token->data)) - ? $value_token->data['string'] - : $value_token->match; } + $value = (($value_token instanceof CTriggerExprParserResult) + && array_key_exists('string', $value_token->data)) + ? $value_token->data['string'] + : $value_token->match; if (!in_array($fn_name, getStandaloneFunctions()) && ($query = $function_token->getFunctionTriggerQuery()) !== null) { -- cgit v1.2.3 From 09c47e00ac3717f5cd7df4e0f436e24e00662fc9 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Mon, 12 Apr 2021 16:43:52 +0300 Subject: ..F....... [ZBXNEXT-6451] changed the order of input fields for functions count/find/bitand in condition popup --- ui/app/controllers/CControllerPopupTriggerExpr.php | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ui/app/controllers/CControllerPopupTriggerExpr.php b/ui/app/controllers/CControllerPopupTriggerExpr.php index d525212e34b..f675c58a928 100644 --- a/ui/app/controllers/CControllerPopupTriggerExpr.php +++ b/ui/app/controllers/CControllerPopupTriggerExpr.php @@ -132,6 +132,11 @@ class CControllerPopupTriggerExpr extends CController { 'M' => $this->metrics, 'A' => true ], + 'shift' => [ + 'C' => _('Time shift'), + 'T' => T_ZBX_INT, + 'A' => false + ], 'o' => [ 'C' => 'O', 'T' => T_ZBX_STR, @@ -141,11 +146,6 @@ class CControllerPopupTriggerExpr extends CController { 'C' => 'V', 'T' => T_ZBX_STR, 'A' => false - ], - 'shift' => [ - 'C' => _('Time shift'), - 'T' => T_ZBX_INT, - 'A' => false ] ]; @@ -156,6 +156,11 @@ class CControllerPopupTriggerExpr extends CController { 'M' => $this->metrics, 'A' => false ], + 'shift' => [ + 'C' => _('Time shift'), + 'T' => T_ZBX_INT, + 'A' => false + ], 'o' => [ 'C' => 'O', 'T' => T_ZBX_STR, @@ -165,11 +170,6 @@ class CControllerPopupTriggerExpr extends CController { 'C' => 'V', 'T' => T_ZBX_STR, 'A' => false - ], - 'shift' => [ - 'C' => _('Time shift'), - 'T' => T_ZBX_INT, - 'A' => false ] ]; @@ -199,15 +199,15 @@ class CControllerPopupTriggerExpr extends CController { 'M' => $this->metrics, 'A' => true ], - 'mask' => [ - 'C' => _('Mask'), - 'T' => T_ZBX_STR, - 'A' => true - ], 'shift' => [ 'C' => _('Time shift'), 'T' => T_ZBX_INT, 'A' => false + ], + 'mask' => [ + 'C' => _('Mask'), + 'T' => T_ZBX_STR, + 'A' => true ] ]; -- cgit v1.2.3 From 2f059ef26ce280e55f20626829a86cffc5001158 Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Mon, 12 Apr 2021 18:38:58 +0300 Subject: ........S. [ZBXNEXT-6451] updated LLD support of expression macros --- include/zbxeval.h | 4 + src/libs/zbxserver/expression.c | 214 +++++++++++++++++++++++++----------- src/zabbix_server/lld/lld_trigger.c | 3 +- 3 files changed, 158 insertions(+), 63 deletions(-) diff --git a/include/zbxeval.h b/include/zbxeval.h index 73bf9b87530..f025fd1787f 100644 --- a/include/zbxeval.h +++ b/include/zbxeval.h @@ -118,6 +118,10 @@ ZBX_EVAL_PARSE_LLDMACRO | \ ZBX_EVAL_COMPOSE_LLD) +#define ZBX_EVAL_EXPRESSION_MACRO_LLD (ZBX_EVAL_PARSE_EXPRESSION_MACRO | \ + ZBX_EVAL_PARSE_LLDMACRO | \ + ZBX_EVAL_COMPOSE_LLD) + typedef zbx_uint32_t zbx_token_type_t; diff --git a/src/libs/zbxserver/expression.c b/src/libs/zbxserver/expression.c index 582409470a3..0b63787df27 100644 --- a/src/libs/zbxserver/expression.c +++ b/src/libs/zbxserver/expression.c @@ -6013,76 +6013,150 @@ static void process_user_macro_token(char **data, zbx_token_t *token, const stru zbx_free(context); } +static int substitute_item_filter_macros(const zbx_eval_context_t *ctx, const zbx_eval_token_t *token, + const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, char **filter, + char **error) +{ + zbx_item_query_t query; + char err[128]; + int ret; + + zbx_eval_parse_query(ctx->expression + token->loc.l, token->loc.r - token->loc.l + 1, &query); + + if (ZBX_ITEM_QUERY_UNKNOWN == query.type) + { + *error = zbx_strdup(NULL, "invalid item reference"); + return FAIL; + } + + if (SUCCEED == (ret = substitute_key_macros(&query.key, NULL, NULL, jp_row, lld_macro_paths, MACRO_TYPE_ITEM_KEY, + err, sizeof(err)))) + { + *filter = zbx_dsprintf(NULL, "/%s/%s", ZBX_NULL2EMPTY_STR(query.host), query.key); + } + else + *error = zbx_strdup(NULL, err); + + zbx_eval_clear_query(&query); + + return ret; +} + /****************************************************************************** * * - * Function: process_expression_macro_token * + * Function: substitute_expression_macros * * * - * Purpose: expand discovery macro in expression macro * + * Purpose: substitutes lld macros in an expression * * * - * Parameters: data - [IN/OUT] the expression containing lld macro * - * token - [IN/OUT] the token with user macro location data * - * jp_row - [IN] discovery data * - * lld_macro_paths - [IN] discovery data * - * error - [OUT] error message * - * max_error_len - [IN] the size of error buffer * + * Parameters: data - [IN/OUT] the expression * + * jp_row - [IN] the lld data row * + * lld_macro_paths - [IN] use json path to extract from jp_row * + * error - [IN] pointer to string for reporting errors * + * max_error_len - [IN] size of 'error' string * * * ******************************************************************************/ -static int process_expression_macro_token(char **data, zbx_token_t *token, - const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, char *error, - size_t error_len) +static int substitute_expression_macros(char **data, zbx_uint64_t rules, const struct zbx_json_parse *jp_row, + const zbx_vector_ptr_t *lld_macro_paths, char **error) { - zbx_token_t cur_token, tmp_token; - int pos, quoted = 0, last_pos; - size_t i; + char *exp = NULL; + int i, ret = FAIL; + zbx_eval_context_t ctx; - last_pos = pos = token->data.expression_macro.expression.l; + zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:%s", __func__, *data); - while (SUCCEED == zbx_token_find(*data, pos, &cur_token, ZBX_TOKEN_SEARCH_BASIC) && - cur_token.loc.l < token->loc.r) - { - for (i = last_pos + 1; i < cur_token.loc.l; i++) - { - switch ((*data)[i]) - { - case '\\': - if (1 == quoted) - i++; - break; - case '"': - quoted = !quoted; - break; - } - } + if (SUCCEED != zbx_eval_parse_expression(&ctx, *data, rules, error)) + goto out; - tmp_token = cur_token; + for (i = 0; i < ctx.stack.values_num; i++) + { + zbx_eval_token_t *token = &ctx.stack.values[i]; + char *value, err[128]; - switch (cur_token.type) + switch(token->type) { - case ZBX_TOKEN_LLD_MACRO: - case ZBX_TOKEN_LLD_FUNC_MACRO: - if (FAIL == process_lld_macro_token(data, &cur_token, ZBX_TOKEN_STRING, jp_row, - lld_macro_paths, error, error_len, quoted)) + case ZBX_EVAL_TOKEN_ARG_QUERY: + if (FAIL == substitute_item_filter_macros(&ctx, token, jp_row, lld_macro_paths, &value, + error)) { - return FAIL; + goto clean; } - token->loc.r += cur_token.loc.r - tmp_token.loc.r; - pos = cur_token.loc.r; - break; - case ZBX_TOKEN_USER_MACRO: - process_user_macro_token(data, &cur_token, jp_row, lld_macro_paths); - token->loc.r += cur_token.loc.r - tmp_token.loc.r; - pos = cur_token.loc.r; break; - case ZBX_TOKEN_SIMPLE_MACRO: - process_simple_macro_token(data, &cur_token, jp_row, lld_macro_paths, error, error_len); - token->loc.r += cur_token.loc.r - tmp_token.loc.r; - pos = cur_token.loc.r; + case ZBX_EVAL_TOKEN_VAR_LLDMACRO: + case ZBX_EVAL_TOKEN_VAR_USERMACRO: + case ZBX_EVAL_TOKEN_VAR_STR: + case ZBX_EVAL_TOKEN_VAR_NUM: + case ZBX_EVAL_TOKEN_ARG_PERIOD: + value = zbx_substr_unquote(ctx.expression, token->loc.l, token->loc.r); + + if (FAIL == substitute_lld_macros(&value, jp_row, lld_macro_paths, ZBX_MACRO_ANY, err, + sizeof(err))) + { + *error = zbx_strdup(NULL, err); + zbx_free(value); + goto clean; + } break; + default: + continue; } - last_pos = ++pos; + zbx_variant_clear(&token->value); + zbx_variant_set_str(&token->value, value); } + zbx_eval_compose_expression(&ctx, &exp); + + zbx_free(*data); + *data = exp; + exp = NULL; + + ret = SUCCEED; +clean: + zbx_free(exp); + zbx_eval_clear(&ctx); +out: + zabbix_log(LOG_LEVEL_DEBUG, "End of %s() expression:%s", __func__, *data); + + return ret; +} + +/****************************************************************************** + * * + * Function: process_expression_macro_token * + * * + * Purpose: expand discovery macro in expression macro * + * * + * Parameters: data - [IN/OUT] the expression containing macro * + * token - [IN/OUT] the macro token * + * jp_row - [IN] discovery data * + * lld_macro_paths - [IN] discovery data * + * error - [OUT] error message * + * max_error_len - [IN] the size of error buffer * + * * + ******************************************************************************/ +static int process_expression_macro_token(char **data, zbx_token_t *token, const struct zbx_json_parse *jp_row, + const zbx_vector_ptr_t *lld_macro_paths, char *error, size_t error_len) +{ + char *errmsg = NULL, *expression; + size_t right = token->data.expression_macro.expression.r; + + expression = zbx_substr(*data, token->data.expression_macro.expression.l, + token->data.expression_macro.expression.r); + + if (FAIL == substitute_expression_macros(&expression, ZBX_EVAL_EXPRESSION_MACRO_LLD, jp_row, + lld_macro_paths, &errmsg)) + { + zbx_free(expression); + zbx_strlcpy(error, errmsg, error_len); + zbx_free(errmsg); + + return FAIL; + } + + zbx_replace_string(data, token->data.expression_macro.expression.l, &right, expression); + token->loc.r += right - token->data.expression_macro.expression.r; + zbx_free(expression); + return SUCCEED; } @@ -6105,19 +6179,33 @@ static int process_expression_macro_token(char **data, zbx_token_t *token, static int substitute_func_macro(char **data, zbx_token_t *token, const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, char *error, size_t max_error_len) { - int ret; - char *exp = NULL; - size_t exp_alloc = 0, exp_offset = 0; - size_t par_l = token->data.func_macro.func_param.l, par_r = token->data.func_macro.func_param.r; + int ret, offset = 0; + char *exp = NULL; + size_t exp_alloc = 0, exp_offset = 0, right; + size_t par_l = token->data.func_macro.func_param.l, par_r = token->data.func_macro.func_param.r; + zbx_token_t tok; - ret = substitute_function_lld_param(*data + par_l + 1, par_r - (par_l + 1), 0, &exp, &exp_alloc, &exp_offset, - jp_row, lld_macro_paths, error, max_error_len); + if (SUCCEED == zbx_token_find(*data, (int)token->data.func_macro.macro.l, &tok, + ZBX_TOKEN_SEARCH_EXPRESSION_MACRO) && tok.loc.r <= token->data.func_macro.macro.r) + { + offset = (int)tok.loc.r; + + if (SUCCEED == process_expression_macro_token(data, &tok, jp_row, lld_macro_paths, error, + max_error_len)) + { + offset = tok.loc.r - offset; + zabbix_log(LOG_LEVEL_DEBUG, "OFFSET: %d", offset); + } + } + + ret = substitute_function_lld_param(*data + par_l + offset + 1, par_r - (par_l + 1), 0, &exp, &exp_alloc, + &exp_offset, jp_row, lld_macro_paths, error, max_error_len); if (SUCCEED == ret) { - /* copy what is left including closing parenthesis and replace function parameters */ - zbx_strncpy_alloc(&exp, &exp_alloc, &exp_offset, *data + par_r, token->loc.r - (par_r - 1)); - zbx_replace_string(data, par_l + 1, &token->loc.r, exp); + right = par_r + offset - 1; + zbx_replace_string(data, par_l + offset + 1, &right, exp); + token->loc.r = right + 1; } zbx_free(exp); @@ -6208,9 +6296,11 @@ int substitute_lld_macros(char **data, const struct zbx_json_parse *jp_row, cons } break; case ZBX_TOKEN_EXPRESSION_MACRO: - process_expression_macro_token(data, &token, jp_row, lld_macro_paths, error, - max_error_len); - pos = token.loc.r; + if (SUCCEED == process_expression_macro_token(data, &token, jp_row, + lld_macro_paths, error, max_error_len)) + { + pos = token.loc.r; + } break; } } diff --git a/src/zabbix_server/lld/lld_trigger.c b/src/zabbix_server/lld/lld_trigger.c index 56b4f3f15e1..cceacc50de3 100644 --- a/src/zabbix_server/lld/lld_trigger.c +++ b/src/zabbix_server/lld/lld_trigger.c @@ -1504,7 +1504,8 @@ static void lld_trigger_make(const zbx_lld_trigger_prototype_t *trigger_prototy } buffer = zbx_strdup(buffer, trigger_prototype->event_name); - substitute_lld_macros(&buffer, jp_row, lld_macros, ZBX_MACRO_ANY | ZBX_TOKEN_EXPRESSION_MACRO, NULL, 0); + substitute_lld_macros(&buffer, jp_row, lld_macros, + ZBX_MACRO_ANY | ZBX_TOKEN_EXPRESSION_MACRO | ZBX_MACRO_FUNC, NULL, 0); zbx_lrtrim(buffer, ZBX_WHITESPACE); if (0 != strcmp(trigger->event_name, buffer)) { -- cgit v1.2.3 From 6098c96646a9a4cb27b988293f3c1433cc4a5dcb Mon Sep 17 00:00:00 2001 From: Alexander Vladishev Date: Mon, 12 Apr 2021 21:57:39 +0300 Subject: .......... [ZBXNEXT-6451,ZBXNEXT-6455] replaced assertIsArray() by assertTrue(is_array()) for compatibility with phpunit 5, 6 and 7 --- ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php index 9718f93d5b9..53b77e680fe 100644 --- a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php +++ b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php @@ -494,7 +494,7 @@ class CTextTriggerConstructorTest extends TestCase { public function testGetPartsFromExpression(string $expression, array $expected_parts) { $parts = $this->constructor->getPartsFromExpression($expression); - $this->assertIsArray($parts); + $this->assertTrue(is_array($parts)); unset($parts[0]['details'], $parts[1]['details'], $parts[2]['details']); $this->assertSame($expected_parts, $parts); -- cgit v1.2.3 From 3ee3b5d3197f1f693aa41529ab00607685d705d6 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Tue, 13 Apr 2021 09:20:47 +0300 Subject: A.F....... [ZBXNEXT-6451] fixed trigger expression validator --- .../classes/api/services/CTriggerGeneral.php | 22 ++-------------------- .../classes/validators/CApiInputValidator.php | 2 +- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index caffdf7105f..45975c17206 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -506,18 +506,8 @@ abstract class CTriggerGeneral extends CApiService { foreach ($descriptions as $description => $triggers) { foreach ($triggers as $index => $trigger) { - if ($expression_data->parse($trigger['expression'])) { - $expression_hosts = $expression_data->result->getHosts(); - if ($expression_hosts) { - $hosts[$expression_hosts[0]][$description][] = $index; - } - else { - $path = '/'.($index+1).'/expression'; - self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', $path, - _('trigger expression must contain at least one /host/key reference') - )); - } - } + $expression_data->parse($trigger['expression']); + $hosts[$expression_data->result->getHosts()[0]][$description][] = $index; } } @@ -1081,14 +1071,6 @@ abstract class CTriggerGeneral extends CApiService { $triggerid = DB::reserveIds('triggers', count($new_triggers)); foreach ($new_triggers as $tnum => &$new_trigger) { - - if (!array_key_exists($tnum, $triggers_functions)) { - $path = '/'.($tnum+1).'/expression'; - self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', $path, - _('trigger expression must contain at least one /host/key reference') - )); - } - $new_trigger['triggerid'] = $triggerid; $triggers[$tnum]['triggerid'] = $triggerid; diff --git a/ui/include/classes/validators/CApiInputValidator.php b/ui/include/classes/validators/CApiInputValidator.php index d971c85288b..c70dc3bc2e9 100644 --- a/ui/include/classes/validators/CApiInputValidator.php +++ b/ui/include/classes/validators/CApiInputValidator.php @@ -2031,7 +2031,7 @@ class CApiInputValidator { return false; } - if (!$expression_data->expressions) { + if (!$expression_data->result->hasTokenOfType(CTriggerExprParserResult::TOKEN_TYPE_QUERY)) { $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('trigger expression must contain at least one /host/key reference') ); -- cgit v1.2.3 From 6687d515464cfc25a60d24daf220cedd822d65d0 Mon Sep 17 00:00:00 2001 From: Andris Zeila Date: Tue, 13 Apr 2021 09:43:41 +0300 Subject: ........S. [ZBXNEXT-6451] added missing file --- src/libs/zbxeval/query.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/libs/zbxeval/query.c diff --git a/src/libs/zbxeval/query.c b/src/libs/zbxeval/query.c new file mode 100644 index 00000000000..61b21be73c7 --- /dev/null +++ b/src/libs/zbxeval/query.c @@ -0,0 +1,69 @@ +/* +** Zabbix +** Copyright (C) 2001-2020 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. +**/ + +#include "common.h" +#include "log.h" +#include "zbxalgo.h" +#include "zbxserver.h" +#include "zbxeval.h" +#include "eval.h" + +/****************************************************************************** + * * + * Function: zbx_eval_parse_filter * + * * + * Purpose: parse item query /host/key?[filter] into host, key and filter * + * components * + * * + * Parameters: str - [IN] the item query * + * len - [IN] the query length * + * query - [IN] the parsed item query * + * * + ******************************************************************************/ +void zbx_eval_parse_query(const char *str, size_t len, zbx_item_query_t *query) +{ + const char *ptr = str, *key; + + if ('/' != *ptr || NULL == (key = strchr(++ptr, '/'))) + { + query->type = ZBX_ITEM_QUERY_UNKNOWN; + return; + } + + if (ptr != key) + query->host = zbx_substr(ptr, 0, key - ptr - 1); + else + query->host = NULL; + + query->key = zbx_substr(key, 1, len - (key - str) - 1); + query->type = ZBX_ITEM_QUERY_SINGLE; +} + +/****************************************************************************** + * * + * Function: zbx_eval_clear_filter * + * * + * Purpose: frees resources allocated by item reference * + * * + ******************************************************************************/ +void zbx_eval_clear_query(zbx_item_query_t *query) +{ + zbx_free(query->host); + zbx_free(query->key); +} -- cgit v1.2.3 From 6b0b265f8baa0e822d66b3506936e496c384b685 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Tue, 13 Apr 2021 10:17:44 +0300 Subject: A.F....... [ZBXNEXT-6451] fixed trigger expression syntax converter --- .../import/converters/C52ImportConverter.php | 17 +++--- .../converters/C52TriggerExpressionConverter.php | 22 ++------ .../triggers/C52TriggerExpressionConverterTest.php | 64 ++-------------------- 3 files changed, 18 insertions(+), 85 deletions(-) diff --git a/ui/include/classes/import/converters/C52ImportConverter.php b/ui/include/classes/import/converters/C52ImportConverter.php index 5d0edbcd543..0bca10ae587 100644 --- a/ui/include/classes/import/converters/C52ImportConverter.php +++ b/ui/include/classes/import/converters/C52ImportConverter.php @@ -321,19 +321,18 @@ class C52ImportConverter extends CConverter { * @return array */ private function convertTrigger(array $trigger, ?string $host = null, ?string $item = null): array { - $converted_expressions = $this->trigger_expression_converter->convert(array_filter([ + $trigger['expression'] = $this->trigger_expression_converter->convert([ 'expression' => $trigger['expression'], - 'recovery_expression' => array_key_exists('recovery_expression', $trigger) - ? $trigger['recovery_expression'] - : null, 'host' => $host, 'item' => $item - ])); + ]); - foreach (['expression', 'recovery_expression'] as $source) { - if (array_key_exists($source, $converted_expressions)) { - $trigger[$source] = $converted_expressions[$source]; - } + if (array_key_exists('recovery_expression', $trigger) && $trigger['recovery_expression'] !== '') { + $trigger['recovery_expression'] = $this->trigger_expression_converter->convert([ + 'expression' => $trigger['recovery_expression'], + 'host' => $host, + 'item' => $item + ]); } if (array_key_exists('dependencies', $trigger)) { diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index 3093fda0cd5..afffb513038 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -75,8 +75,7 @@ class C52TriggerExpressionConverter extends CConverter { * Converts trigger expression to new syntax. * * @param array $trigger_data - * @param string $trigger_data['expression'] (optional) - * @param string $trigger_data['recovery_expression'] (optional) + * @param string $trigger_data['expression'] * @param string $trigger_data['host'] (optional) * @param string $trigger_data['item'] (optional) * @@ -86,23 +85,12 @@ class C52TriggerExpressionConverter extends CConverter { $this->item = array_key_exists('item', $trigger_data) ? $trigger_data['item'] : null; $this->host = (array_key_exists('host', $trigger_data) && $this->item) ? $trigger_data['host'] : null; - $extra_expressions = []; - - if (array_key_exists('recovery_expression', $trigger_data) && $trigger_data['recovery_expression'] !== '' - && ($this->parser->parse($trigger_data['recovery_expression'])) !== false) { - $functions = $this->parser->result->getTokensByType(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO); - $this->hanged_refs = $this->checkHangedFunctionsPerHost($functions); - $parts = $this->getExpressionParts(0, $this->parser->result->length-1); - $this->wrap_subexpressions = ($parts['type'] === 'operator'); - $this->convertExpressionParts($trigger_data['recovery_expression'], [$parts], $extra_expressions); - } - - if (array_key_exists('expression', $trigger_data) && $trigger_data['expression'] !== '' - && ($this->parser->parse($trigger_data['expression'])) !== false) { + if (($this->parser->parse($trigger_data['expression'])) !== false) { $functions = $this->parser->result->getTokensByType(C10TriggerExprParserResult::TOKEN_TYPE_FUNCTION_MACRO); $this->hanged_refs = $this->checkHangedFunctionsPerHost($functions); - $parts = $this->getExpressionParts(0, $this->parser->result->length-1); + $parts = $this->getExpressionParts(0, $this->parser->result->length - 1); $this->wrap_subexpressions = ($parts['type'] === 'operator'); + $extra_expressions = []; $this->convertExpressionParts($trigger_data['expression'], [$parts], $extra_expressions); $extra_expressions = array_filter($extra_expressions); @@ -116,7 +104,7 @@ class C52TriggerExpressionConverter extends CConverter { } } - return array_intersect_key($trigger_data, array_flip(['recovery_expression', 'expression'])); + return $trigger_data['expression']; } private function convertExpressionParts(string &$expression, array $expression_elements, array &$extra_expr) { diff --git a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php index 848308c5c66..a9b245a802d 100644 --- a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php +++ b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php @@ -328,36 +328,6 @@ class C52TriggerExpressionConverterTest extends TestCase { ]; } - public function twoFieldExpressionProvideData() { - return [ - 'no repeating missing references' => [ - [ - 'expression' => '{Trapper:trap[1].dayofweek()} > 0'. - ' and {Host:trap[1].last()} > 0', - 'recovery_expression' => '{Trapper:trap[1].dayofweek()} > 0'. - ' and {Host:trap[1].last()} > 0' - ], - [ - 'expression' => '(dayofweek() > 0 and last(/Host/trap[1]) > 0)'. - ' or (last(/Trapper/trap[1])<>last(/Trapper/trap[1]))', - 'recovery_expression' => 'dayofweek() > 0 and last(/Host/trap[1]) > 0' - ] - ], - 'are references gathered in expression field' => [ - [ - 'expression' => '{Trapper:trap[1].dayofweek()} > 0', - 'recovery_expression' => '{Trapper2:trap[1].dayofweek()} > 0' - ], - [ - 'expression' => '(dayofweek() > 0)'. - ' or (last(/Trapper/trap[1])<>last(/Trapper/trap[1]))'. - ' or (last(/Trapper2/trap[1])<>last(/Trapper2/trap[1]))', - 'recovery_expression' => 'dayofweek() > 0' - ] - ] - ]; - } - public function shortExpressionProvideData() { return [ 'enrich simple trigger expression' => [ @@ -366,21 +336,7 @@ class C52TriggerExpressionConverterTest extends TestCase { 'host' => 'Zabbix server', 'item' => 'trap' ], - [ - 'expression' => '(dayofweek()=0) or (last(/Zabbix server/trap)<>last(/Zabbix server/trap))' - ] - ], - 'two short expressions' => [ - [ - 'expression' => '{dayofweek()}=0', - 'recovery_expression' => '{dayofweek()}=0', - 'host' => 'Zabbix server', - 'item' => 'trap' - ], - [ - 'expression' => '(dayofweek()=0) or (last(/Zabbix server/trap)<>last(/Zabbix server/trap))', - 'recovery_expression' => 'dayofweek()=0' - ] + 'expression' => '(dayofweek()=0) or (last(/Zabbix server/trap)<>last(/Zabbix server/trap))' ] ]; } @@ -392,26 +348,16 @@ class C52TriggerExpressionConverterTest extends TestCase { * @param string $new_expression */ public function testSimpleConversion(string $old_expression, string $new_expression) { - $this->assertSame($new_expression, $this->converter->convert(['expression' => $old_expression])['expression']); - } - - /** - * @dataProvider twoFieldExpressionProvideData - * - * @param array $old_expressions - * @param array $new_expressions - */ - public function testTwoExpressionConversion(array $old_expressions, array $new_expressions) { - $this->assertSame($new_expressions, $this->converter->convert($old_expressions)); + $this->assertSame($new_expression, $this->converter->convert(['expression' => $old_expression])); } /** * @dataProvider shortExpressionProvideData * - * @param array $old_expression - * @param array $new_expression + * @param array $old_expression + * @param string $new_expression */ - public function testShortExpressionConversion(array $old_expression, array $new_expression) { + public function testShortExpressionConversion(array $old_expression, string $new_expression) { $this->assertSame($new_expression, $this->converter->convert($old_expression)); } } -- cgit v1.2.3 From 466ee56427e7d7dcf96e48799777b68e20fe2100 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Tue, 13 Apr 2021 10:50:37 +0300 Subject: ..F....... [ZBXNEXT-6451] fixed coding style --- ui/include/classes/import/converters/C52TriggerExpressionConverter.php | 2 +- ui/include/classes/macros/CMacrosResolver.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index afffb513038..c952354a959 100644 --- a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php +++ b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php @@ -302,7 +302,7 @@ class C52TriggerExpressionConverter extends CConverter { $parameters[2] = 'bitand'; } unset($parameters[3]); - array_push($parameters, $parameters[1]); + $parameters[] = $parameters[1]; unset($parameters[1]); break; diff --git a/ui/include/classes/macros/CMacrosResolver.php b/ui/include/classes/macros/CMacrosResolver.php index 5b385260f35..3183ac4f662 100644 --- a/ui/include/classes/macros/CMacrosResolver.php +++ b/ui/include/classes/macros/CMacrosResolver.php @@ -1232,7 +1232,7 @@ class CMacrosResolver extends CMacrosResolverGeneral { $expression = array_filter($expression); array_unshift($expression, $options['html'] ? bold($fn->function.'(') : $fn->function.'('); - array_push($expression, $options['html'] ? bold(')') : ')'); + $expression[] = $options['html'] ? bold(')') : ')'; return $options['html'] ? $expression : implode('', $expression); } -- cgit v1.2.3 From 8ab856e081ff677445032bf8ea297af5fda366e9 Mon Sep 17 00:00:00 2001 From: Miks Kronkalns Date: Tue, 13 Apr 2021 11:22:50 +0300 Subject: A......... [ZBXNEXT-6451] fixed expression error reporting --- ui/include/classes/api/services/CTriggerGeneral.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index 45975c17206..7cd2e00171e 100644 --- a/ui/include/classes/api/services/CTriggerGeneral.php +++ b/ui/include/classes/api/services/CTriggerGeneral.php @@ -1561,18 +1561,15 @@ abstract class CTriggerGeneral extends CApiService { : null; $error_msg = ''; - if (!$math_function_validator->validate($fn)) { - $error_msg = $math_function_validator->getError(); - - if (!$trigger_function_validator->validate($fn)) { - $error_msg = $trigger_function_validator->getError(); - } - elseif ($value_type !== null - && !$trigger_function_validator->validateValueType($value_type, $fn)) { + if (!$math_function_validator->validate($fn) + && (!$trigger_function_validator->validate($fn) + || ($value_type !== null + && !$trigger_function_validator->validateValueType($value_type, $fn)))) { + if ($trigger_function_validator->getError() !== '') { $error_msg = $trigger_function_validator->getError(); } - else { - $error_msg = ''; + elseif ($math_function_validator->getError() !== '') { + $error_msg = $math_function_validator->getError(); } } -- cgit v1.2.3