Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndris Zeila <andris.zeila@zabbix.com>2021-04-13 11:41:50 +0300
committerAndris Zeila <andris.zeila@zabbix.com>2021-04-13 11:41:50 +0300
commit0ba133694812ce2e585405fbd30748503f0bde2a (patch)
treeb59c19b919391c3c71ffb0f7fe54588f5ff8b2a5
parent3f5a5ff05b51000d7f66894a2d5759d81cf95563 (diff)
parent8ab856e081ff677445032bf8ea297af5fda366e9 (diff)
.......... [ZBXNEXT-6457] merged branch 'feature/ZBXNEXT-6451-5.3' into feature/ZBXNEXT-6457-5.3
-rw-r--r--include/common.h1
-rw-r--r--include/dbcache.h3
-rw-r--r--include/zbxeval.h5
-rw-r--r--include/zbxserver.h7
-rw-r--r--src/libs/zbxcommon/str.c1
-rw-r--r--src/libs/zbxdbcache/dbconfig.c36
-rw-r--r--src/libs/zbxdbupgrade/dbupgrade_5030.c295
-rw-r--r--src/libs/zbxeval/parse.c23
-rw-r--r--src/libs/zbxserver/evalfunc.c14
-rw-r--r--src/libs/zbxserver/evalfunc2.c14
-rw-r--r--src/libs/zbxserver/expression.c576
-rw-r--r--src/zabbix_server/lld/lld_trigger.c3
-rw-r--r--tests/libs/zbxcommon/zbx_token_find.yaml2
-rw-r--r--ui/app/controllers/CControllerPopupTriggerExpr.php60
-rw-r--r--ui/app/controllers/CControllerPopupTriggerWizard.php19
-rw-r--r--ui/app/views/popup.triggerwizard.php8
-rw-r--r--ui/include/classes/api/services/CTriggerGeneral.php155
-rw-r--r--ui/include/classes/import/converters/C52ImportConverter.php22
-rw-r--r--ui/include/classes/import/converters/C52TriggerExpressionConverter.php47
-rw-r--r--ui/include/classes/macros/CMacrosResolver.php76
-rw-r--r--ui/include/classes/parsers/CFunctionParser.php9
-rw-r--r--ui/include/classes/parsers/CPeriodParser.php5
-rw-r--r--ui/include/classes/parsers/results/CTriggerExprParserResult.php52
-rw-r--r--ui/include/classes/triggers/CTextTriggerConstructor.php6
-rw-r--r--ui/include/classes/validators/C10FunctionValidator.php693
-rw-r--r--ui/include/classes/validators/CApiInputValidator.php2
-rw-r--r--ui/include/classes/validators/CFunctionValidator.php68
-rw-r--r--ui/include/classes/validators/CMathFunctionValidator.php35
-rw-r--r--ui/include/triggers.inc.php2
-rw-r--r--ui/tests/unit/include/classes/parsers/C10FunctionParserTest.php4
-rw-r--r--ui/tests/unit/include/classes/triggers/CTextTriggerConstructorTest.php262
-rw-r--r--ui/tests/unit/include/classes/validators/C10FunctionValidatorTest.php876
-rw-r--r--ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php28
-rw-r--r--ui/tests/unit/include/classes/validators/CEventNameValidatorTest.php2
-rw-r--r--ui/tests/unit/include/triggerExpressionReplaceHostTest.php45
-rw-r--r--ui/tests/unit/include/triggers/C52TriggerExpressionConverterTest.php94
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 = &params->values[index];
if ('"' == parameter[loc->l])
{
+ loc = &params->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, &macro);
+ 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(&params, &params_alloc, &params_offset, ',');
+
+ switch (args[i].type)
+ {
+ case ZBX_VARIANT_DBL:
+ zbx_snprintf_alloc(&params, &params_alloc, &params_offset, ZBX_FS_DBL64,
+ args[i].data.dbl);
+ break;
+ case ZBX_VARIANT_STR:
+ zbx_strquote_alloc(&params, &params_alloc, &params_offset, args[i].data.str);
+ break;
+ case ZBX_VARIANT_UI64:
+ zbx_snprintf_alloc(&params, &params_alloc, &params_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));
}
}