diff options
36 files changed, 1238 insertions, 2312 deletions
diff --git a/include/common.h b/include/common.h index 17b44f235bc..ac14312aea4 100644 --- a/include/common.h +++ b/include/common.h @@ -1707,5 +1707,6 @@ int zbx_check_xml_memory(char *mem, int maxerrlen, char **errmsg); char *zbx_substr(const char *src, size_t left, size_t right); 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/include/dbcache.h b/include/dbcache.h index a9ad9eba95f..0caad84c4ce 100644 --- a/include/dbcache.h +++ b/include/dbcache.h @@ -976,6 +976,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 e912b0e521f..8d6f4284550 100644 --- a/include/zbxeval.h +++ b/include/zbxeval.h @@ -91,6 +91,8 @@ 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 */ #define ZBX_EVAL_COMPOSE_LLD __UINT64_C(0x00010000) @@ -115,6 +117,9 @@ 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/include/zbxserver.h b/include/zbxserver.h index 255e2f38676..33838a8ada3 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 diff --git a/src/libs/zbxcommon/str.c b/src/libs/zbxcommon/str.c index 7ce68b6990f..aa818452036 100644 --- a/src/libs/zbxcommon/str.c +++ b/src/libs/zbxcommon/str.c @@ -3739,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 980033a9c0a..265968b2bba 100644 --- a/src/libs/zbxdbcache/dbconfig.c +++ b/src/libs/zbxdbcache/dbconfig.c @@ -10927,6 +10927,42 @@ int dc_expand_user_macros_len(const char *text, size_t text_len, zbx_uint64_t *h /****************************************************************************** * * + * 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 * * * * Purpose: expand user macros in the specified text value * diff --git a/src/libs/zbxdbupgrade/dbupgrade_5030.c b/src/libs/zbxdbupgrade/dbupgrade_5030.c index 96bca46689e..6ada3ff6fbb 100644 --- a/src/libs/zbxdbupgrade/dbupgrade_5030.c +++ b/src/libs/zbxdbupgrade/dbupgrade_5030.c @@ -1850,7 +1850,7 @@ static void dbpatch_update_trigger(zbx_dbpatch_trigger_t *trigger, zbx_uint64_t static void dbpatch_update_func_abschange(zbx_dbpatch_function_t *function, char **replace) { - dbpatch_update_function(function, "change", NULL, ZBX_DBPATCH_FUNCTION_UPDATE_NAME); + dbpatch_update_function(function, "change", "", ZBX_DBPATCH_FUNCTION_UPDATE); *replace = zbx_dsprintf(NULL, "abs({" ZBX_FS_UI64 "})", function->functionid); } @@ -1931,7 +1931,7 @@ static void dbpatch_update_hist2common(zbx_dbpatch_function_t *function, int ext static void dbpatch_parse_function_params(const char *parameter, zbx_vector_loc_t *params) { const char *ptr; - size_t len, pos, sep, eol; + size_t len, pos, sep = 0, eol; zbx_strloc_t loc; eol = strlen(parameter); @@ -1947,7 +1947,7 @@ static void dbpatch_parse_function_params(const char *parameter, zbx_vector_loc_ } else { - loc.l = eol; + loc.l = ptr - parameter + eol - (ptr - parameter); loc.r = loc.l; } @@ -2058,21 +2058,19 @@ static void dbpatch_convert_params(char **out, const char *parameter, const zbx_ loc = ¶ms->values[index]; if ('"' == parameter[loc->l]) { + loc = ¶ms->values[index]; zbx_strncpy_alloc(out, &out_alloc, &out_offset, parameter + loc->l, loc->r - loc->l + 1); } else if ('\0' != parameter[loc->l]) { - char *raw, *quoted; + char raw[FUNCTION_PARAM_LEN * 4 + 1], quoted[sizeof(raw)]; - raw = zbx_substr(parameter, loc->l, loc->r); - quoted = zbx_dyn_escape_string(raw, "\"\\"); + zbx_strlcpy(raw, parameter + loc->l, loc->r - loc->l + 2); + zbx_escape_string(quoted, sizeof(quoted), raw, "\"\\"); zbx_chrcpy_alloc(out, &out_alloc, &out_offset, '"'); zbx_strcpy_alloc(out, &out_alloc, &out_offset, quoted); zbx_chrcpy_alloc(out, &out_alloc, &out_offset, '"'); - - zbx_free(quoted); - zbx_free(raw); } } break; @@ -2651,6 +2649,281 @@ static int DBpatch_5030082(void) return SUCCEED; } +/****************************************************************************** + * * + * Function: dbpatch_replace_functionids * + * * + * Purpose: replace functionids {<index in functions vector>} 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); + + /* 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); + + 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) +{ + zbx_token_t token; + char *out = NULL; + 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; + + 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))) + { + zbx_token_t token; + char *out = NULL; + size_t out_alloc = 0, out_offset = 0, pos = 0, last_pos = 0; + + for (; SUCCEED == zbx_token_find(row[1], (int)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 ret; +} + static char *dbpatch_formula_to_expression(zbx_uint64_t itemid, const char *formula, zbx_vector_ptr_t *functions) { zbx_dbpatch_function_t *func; @@ -2717,7 +2990,7 @@ static char *dbpatch_formula_to_expression(zbx_uint64_t itemid, const char *form return exp; } -static int DBpatch_5030083(void) +static int DBpatch_5030084(void) { DB_ROW row; DB_RESULT result; @@ -2839,6 +3112,7 @@ static int DBpatch_5030083(void) return ret; } + #endif DBPATCH_START(5030) @@ -2929,5 +3203,6 @@ DBPATCH_ADD(5030080, 0, 1) DBPATCH_ADD(5030081, 0, 1) DBPATCH_ADD(5030082, 0, 1) DBPATCH_ADD(5030083, 0, 1) +DBPATCH_ADD(5030084, 0, 1) DBPATCH_END() diff --git a/src/libs/zbxeval/parse.c b/src/libs/zbxeval/parse.c index 1b14fee215d..f76f95c887e 100644 --- a/src/libs/zbxeval/parse.c +++ b/src/libs/zbxeval/parse.c @@ -582,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; @@ -593,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++; 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 0b3eac34c05..54c56169792 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 /****************************************************************************** * * diff --git a/src/libs/zbxserver/expression.c b/src/libs/zbxserver/expression.c index 6bc983aa6dd..8394bac0ef6 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" @@ -2368,40 +2369,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 * * * * Purpose: retrieve a particular attribute of a 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 * @@ -2917,8 +3172,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++; @@ -4064,16 +4319,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)) @@ -4622,24 +4885,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); @@ -5685,72 +5930,151 @@ 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 void 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: - process_lld_macro_token(data, &cur_token, ZBX_TOKEN_STRING, jp_row, lld_macro_paths, - quoted); - 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; + case ZBX_EVAL_TOKEN_ARG_QUERY: + if (FAIL == substitute_item_filter_macros(&ctx, token, jp_row, lld_macro_paths, &value, + error)) + { + goto clean; + } 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; } /****************************************************************************** @@ -5772,19 +6096,33 @@ static void 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; + + 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; - 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 == 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); @@ -5875,9 +6213,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)) { 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/ui/app/controllers/CControllerPopupTriggerExpr.php b/ui/app/controllers/CControllerPopupTriggerExpr.php index bbd06ccdadd..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 ] ]; @@ -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; @@ -593,14 +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; - } - else { - break; } + $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) { @@ -727,11 +724,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 +828,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/app/controllers/CControllerPopupTriggerWizard.php b/ui/app/controllers/CControllerPopupTriggerWizard.php index de43dfcaca6..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; @@ -294,13 +290,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 eb340fbcc14..4bc5d139e74 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; overflow-wrap: break-word;') ->setHeader(['', _('Expression'), _('Type'), _('Action')]); $expressions = []; @@ -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', diff --git a/ui/include/classes/api/services/CTriggerGeneral.php b/ui/include/classes/api/services/CTriggerGeneral.php index 58c7e5107e0..7cd2e00171e 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 ); @@ -512,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; } } @@ -1087,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; @@ -1406,32 +1382,44 @@ 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']; - } - else { - $hosts_keys[$key] = $host; - } - } - - if ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) { - $expressionData->parse($trigger['recovery_expression']); + $expression_fields = ($trigger['recovery_mode'] == ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION) + ? ['expression', 'recovery_expression'] + : ['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 + ]; + } + } } } } @@ -1531,7 +1519,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'] @@ -1542,7 +1530,7 @@ abstract class CTriggerGeneral extends CApiService { } $triggers_functions[$tnum] = []; - $triggers_function_occurances[$tnum] = []; + $triggers_function_occurrences[$tnum] = []; if ($class === 'CTriggerPrototype') { $lld_ruleids = []; } @@ -1566,22 +1554,25 @@ 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) - ? $hosts_keys[$query->host]['keys'][$query->item]['value_type'] - : null - ]; - - foreach ([$math_function_validator, $trigger_function_validator] as $validator) { - if ($validator->validate($fn_data)) { - $error_msg = ''; - break; + + // Validate trigger function. + $value_type = ($query !== null) + ? $hosts_keys[$query->host]['keys'][$query->item]['value_type'] + : null; + $error_msg = ''; + + 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 = $validator->getError(); + elseif ($math_function_validator->getError() !== '') { + $error_msg = $math_function_validator->getError(); } } + if ($error_msg !== '') { self::exception(ZBX_API_ERROR_PARAMETERS, $error_msg); } @@ -1601,10 +1592,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; @@ -1620,7 +1607,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 @@ -1706,7 +1693,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 +1703,9 @@ abstract class CTriggerGeneral extends CApiService { // Replace func(/host/item) macros with {<functionid>}. 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; @@ -1735,16 +1722,16 @@ abstract class CTriggerGeneral extends CApiService { : ['expression']; foreach ($expression_fields as $expr_field) { - if (array_key_exists($expr_field, $triggers_function_occurances[$tnum])) { - usort($triggers_function_occurances[$tnum][$expr_field], function ($a, $b) { - return $a['pos'] <=> $b['pos']; + if (array_key_exists($expr_field, $triggers_function_occurrences[$tnum])) { + // Sort trigger function occurrences in reverse order by position. + usort($triggers_function_occurrences[$tnum][$expr_field], function ($a, $b) { + 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_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'] ); } } diff --git a/ui/include/classes/import/converters/C52ImportConverter.php b/ui/include/classes/import/converters/C52ImportConverter.php index 7c65b98c53d..f7750402f2d 100644 --- a/ui/include/classes/import/converters/C52ImportConverter.php +++ b/ui/include/classes/import/converters/C52ImportConverter.php @@ -332,19 +332,25 @@ 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 - ])); + ]); + + 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 + ]); + } - foreach (['expression', 'recovery_expression'] as $source) { - if (array_key_exists($source, $converted_expressions)) { - $trigger[$source] = $converted_expressions[$source]; + if (array_key_exists('dependencies', $trigger)) { + foreach ($trigger['dependencies'] as &$dep_trigger) { + $dep_trigger = $this->convertTrigger($dep_trigger); } + unset($dep_trigger); } return $trigger; diff --git a/ui/include/classes/import/converters/C52TriggerExpressionConverter.php b/ui/include/classes/import/converters/C52TriggerExpressionConverter.php index 12a13547394..a39615a4287 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'] : ''; $this->host = (array_key_exists('host', $trigger_data) && $this->item) ? $trigger_data['host'] : ''; - $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']; } protected function convertExpressionParts(string &$expression, array $expression_elements, array &$extra_expr) { @@ -183,6 +171,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); @@ -190,11 +182,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': @@ -310,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; @@ -356,17 +348,21 @@ 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') { + if (in_array($fn_name, ['forecast', 'timeleft', 'percentile'])) { + // 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)) { return; } - if ($param === '' || ($param[0] === '"' && substr($param, -1) === '"')) { + if ($param !== '' && $param[0] === '"' && substr($param, -1) === '"') { return; } @@ -389,9 +385,10 @@ class C52TriggerExpressionConverter extends CConverter { } private static function convertTimeshift(string $param): string { - return (preg_match('/^(?<num>\d+)(?<suffix>['.ZBX_TIME_SUFFIXES.']{0,1})$/', $param, $m) && $m['num'] > 0) - ? 'now-'.$m['num'].($m['suffix'] !== '' ? $m['suffix'] : 's') + $param = (preg_match('/^(?<num>\d+)(?<suffix>['.ZBX_TIME_SUFFIXES.']{0,1})$/', $param, $m) && $m['num'] > 0) + ? $m['num'].($m['suffix'] !== '' ? $m['suffix'] : 's') : $param; + return ($param !== '') ? 'now-'.$param : ''; } private static function paramsToString(array $parameters): string { diff --git a/ui/include/classes/macros/CMacrosResolver.php b/ui/include/classes/macros/CMacrosResolver.php index 3bffcbbaea8..3183ac4f662 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) @@ -1050,29 +1049,28 @@ 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', array_key_exists('context', $options) - ? $options['context'] - : 'host' - ) - )) - ->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'].'(')]; - 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']; @@ -1167,14 +1165,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); @@ -1211,19 +1203,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]); } @@ -1232,15 +1223,16 @@ 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.'('); - array_push($expression, $options['html'] ? bold(')') : ')'); + $expression[] = $options['html'] ? bold(')') : ')'; return $options['html'] ? $expression : implode('', $expression); } diff --git a/ui/include/classes/parsers/CFunctionParser.php b/ui/include/classes/parsers/CFunctionParser.php index e9b5beb8abb..c085071a07c 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; @@ -235,7 +236,7 @@ class CFunctionParser extends CParser { $_parameters[$num] = new CFunctionParameterResult([ 'type' => self::PARAM_UNQUOTED, 'match' => $source[$p], - 'pos' => $p - $pos, + 'pos' => $p, 'length' => 1 ]); } diff --git a/ui/include/classes/parsers/CPeriodParser.php b/ui/include/classes/parsers/CPeriodParser.php index 46f71a360ee..3c7bb69304e 100644 --- a/ui/include/classes/parsers/CPeriodParser.php +++ b/ui/include/classes/parsers/CPeriodParser.php @@ -97,14 +97,15 @@ 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; } // 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.'(?<suffix>['.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; } 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; - } } diff --git a/ui/include/classes/triggers/CTextTriggerConstructor.php b/ui/include/classes/triggers/CTextTriggerConstructor.php index 43cd111f48e..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)) { @@ -253,7 +251,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/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 @@ -<?php -/* -** Zabbix -** Copyright (C) 2001-2021 Zabbix SIA -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -**/ - - -class C10FunctionValidator extends CValidator { - - /** - * The array containing valid functions and parameters to them. - * - * Structure: array( - * '<function>' => array( - * 'args' => array( - * array('type' => '<parameter_type>'[, 'mandat' => bool]), - * ... - * ), - * 'value_types' => array(<value_type>, <value_type>, ...) - * ) - * ) - * - * <parameter_type> can be 'fit', 'mode', 'num_suffix', 'num_unsigned', 'operation', 'percent', 'sec_neg', - * 'sec_num', 'sec_num_zero', 'sec_zero' - * <value_type> 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/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') ); diff --git a/ui/include/classes/validators/CFunctionValidator.php b/ui/include/classes/validators/CFunctionValidator.php index b0afcc99f54..5ac5243b7f6 100644 --- a/ui/include/classes/validators/CFunctionValidator.php +++ b/ui/include/classes/validators/CFunctionValidator.php @@ -308,18 +308,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.')); @@ -351,16 +346,25 @@ 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] ); return false; } - $parameter_value = $fn->params_raw['parameters'][$num]->getValue(); + if ($arg['mandat'] == 0x00 && $fn->params_raw['parameters'][$num]->getValue() === '') { + continue; + } - 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] ); @@ -374,16 +378,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; } @@ -394,12 +397,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; } @@ -411,21 +413,9 @@ class CFunctionValidator extends CValidator { case 'period': case 'sec_neg': 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': - return true; + case 'percent': + return !self::isQuoted($parameter->getValue(true)); // Mandatory quoted. default: @@ -541,6 +531,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 */ @@ -557,7 +548,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; @@ -569,11 +560,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; @@ -736,11 +727,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; diff --git a/ui/include/classes/validators/CMathFunctionValidator.php b/ui/include/classes/validators/CMathFunctionValidator.php index 4bae19d6af6..7cc54ded8b3 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.')); @@ -130,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; } } @@ -148,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; } } 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/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 diff --git a/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php b/ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php index cd0f4db5e0c..53b77e680fe 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->assertTrue(is_array($parts)); + unset($parts[0]['details'], $parts[1]['details'], $parts[2]['details']); + + $this->assertSame($expected_parts, $parts); } } 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 @@ -<?php -/* -** Zabbix -** Copyright (C) 2001-2021 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. -**/ - - -use PHPUnit\Framework\TestCase; - -class C10FunctionValidatorTest extends TestCase { - - private static function parameterSecNum_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, 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); - } -} 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], 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'], 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)"' ] ]; } diff --git a/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php b/ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php index 824cabb6db8..a9b245a802d 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', @@ -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'. @@ -186,16 +186,16 @@ 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'. ' 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' ], [ @@ -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'. @@ -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'. @@ -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'. @@ -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)); } } |