diff --git a/frontends/php/include/classes/validators/CTriggerFunctionValidator.php b/frontends/php/include/classes/validators/CTriggerFunctionValidator.php index deadf96..8665389 100644 --- a/frontends/php/include/classes/validators/CTriggerFunctionValidator.php +++ b/frontends/php/include/classes/validators/CTriggerFunctionValidator.php @@ -175,6 +175,14 @@ class CTriggerFunctionValidator extends CValidator { ), 'value_types' => $valueTypesNum ), + 'percentile' => array( + 'args' => array( + array('type' => 'sec_num', 'mandat' => true), + array('type' => 'num', 'mandat' => true), + array('type' => 'sec_zero', 'can_be_empty' => true) + ), + 'value_types' => $valueTypesNum + ), 'nodata'=> array( 'args' => array( array('type' => 'sec_zero', 'mandat' => true) diff --git a/frontends/php/include/triggers.inc.php b/frontends/php/include/triggers.inc.php index fe672e5..387f410 100644 --- a/frontends/php/include/triggers.inc.php +++ b/frontends/php/include/triggers.inc.php @@ -2267,6 +2267,7 @@ function get_item_function_info($expr) { 'logsource' => array('value_type' => _('0 or 1'), 'type' => T_ZBX_INT, 'validation' => IN('0,1')), 'max' => array('value_type' => $value_type, 'type' => $type_of_value_type, 'validation' => NOT_EMPTY), 'min' => array('value_type' => $value_type, 'type' => $type_of_value_type, 'validation' => NOT_EMPTY), + 'percentile' => array('value_type' => $value_type, 'type' => $type_of_value_type, 'validation' => NOT_EMPTY), 'nodata' => array('value_type' => _('0 or 1'), 'type' => T_ZBX_INT, 'validation' => IN('0,1')), 'now' => array('value_type' => _('Numeric (integer 64bit)'), 'type' => T_ZBX_INT, 'validation' => NOT_EMPTY), 'prev' => array('value_type' => $value_type, 'type' => $type_of_value_type, 'validation' => NOT_EMPTY), diff --git a/frontends/php/popup_trexpr.php b/frontends/php/popup_trexpr.php index 716d065..f74527e 100644 --- a/frontends/php/popup_trexpr.php +++ b/frontends/php/popup_trexpr.php @@ -77,6 +77,21 @@ $param2SecCount = array( 'M' => $metrics // metrcis ) ); +$param2PcntSecCount = array( + array( + 'C' => _('Last of').' (T)', /* caption */ + 'T' => T_ZBX_INT, /* type */ + 'M' => $metrics /* metrcis */ + ), + array( + 'C' => 'Percentile (P)', /* caption */ + 'T' => T_ZBX_STR /* I would use T_ZBX_INT but then we get "seconds" text after the parameter box. */ + ), + array( + 'C' => _('Time shift').' ', // caption + 'T' => T_ZBX_INT // type + ) +); $param3SecVal = array( array( 'C' => _('Last of').' (T)', // caption @@ -295,6 +310,26 @@ $functions = array( 'params' => $param1SecCount, 'allowed_types' => $allowedTypesNumeric ), + 'percentile[<]' => array( + 'description' => _('Pth percentile for period T is < N'), + 'params' => $param2PcntSecCount, + 'allowed_types' => $allowedTypesNumeric + ), + 'percentile[>]' => array( + 'description' => _('Pth percentile for period T is > N'), + 'params' => $param2PcntSecCount, + 'allowed_types' => $allowedTypesNumeric + ), + 'percentile[=]' => array( + 'description' => _('Pth percentile for period T is = N'), + 'params' => $param2PcntSecCount, + 'allowed_types' => $allowedTypesNumeric + ), + 'percentile[#]' => array( + 'description' => _('Pth percentile for period T is NOT N'), + 'params' => $param2PcntSecCount, + 'allowed_types' => $allowedTypesNumeric + ), 'prev[<]' => array( 'description' => _('Previous value is < N'), 'allowed_types' => $allowedTypesAny diff --git a/src/libs/zbxserver/evalfunc.c b/src/libs/zbxserver/evalfunc.c index 33ecaaf..0ef5d2c 100644 --- a/src/libs/zbxserver/evalfunc.c +++ b/src/libs/zbxserver/evalfunc.c @@ -30,6 +30,16 @@ int cmp_double(double a, double b) return fabs(a - b) < TRIGGER_EPSILON ? SUCCEED : FAIL; } +int compare_float(const void *float1, const void *float2) { + if (*(double *)float1 > *(double *)float2) + return 1; + else if (*(double *)float1 < *(double *)float2) + return -1; + else + return 0; + +} + static int __get_function_parameter_uint31(zbx_uint64_t hostid, const char *parameters, int Nparam, int *value, int *flag, int defaults_on_empty, int def_value, int def_flag) { @@ -1002,6 +1012,112 @@ out: /****************************************************************************** * * + * Function: evaluate_PERCENTILE * + * * + * Purpose: evaluate function 'percentile' for the item * + * * + * Parameters: item - item (performance metric) * + * parameters - percent and number of seconds/values * + * * + * Return value: SUCCEED - evaluated successfully, result is stored in 'value'* + * FAIL - failed to evaluate function * + * * + * Author: Matthew Gould * + * Based on patch for Zabbix 1.8 by Sergey Kononenko * + * * + * Comments: * + * * + ******************************************************************************/ +static int evaluate_PERCENTILE(char *value, DC_ITEM *item, const char *function, const char *parameters, time_t now) +{ + const char *__function_name = "evaluate_PERCENTILE"; + int nparams, arg1, flag, ret = FAIL, i, seconds = 0, nvalues = 0, percent; + + zbx_vector_history_record_t values; + + zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name); + + zbx_history_record_vector_create(&values); + + if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type) + goto out; + + nparams = num_param(parameters); + if (!(nparams == 2 || nparams == 3)) + goto out; + + if (SUCCEED != get_function_parameter_uint31(item->host.hostid, parameters, 2, &percent, &flag)) + goto out; + + if (SUCCEED != get_function_parameter_uint31(item->host.hostid, parameters, 1, &arg1, &flag) || 0 == arg1) + goto out; + + if (3 == nparams) + { + int time_shift, time_shift_flag; + + if (SUCCEED != get_function_parameter_uint31_default(item->host.hostid, parameters, 3, &time_shift, + &time_shift_flag, 0, ZBX_FLAG_SEC) || ZBX_FLAG_SEC != time_shift_flag) + goto out; + + now -= time_shift; + } + + if (percent < 0 || percent > 100) + { + zabbix_log(LOG_LEVEL_WARNING, "No result for invalid %d PERCENTILE", percent); + goto out; + } + if (ZBX_FLAG_SEC == flag) + seconds = arg1; + else + nvalues = arg1; + + if (FAIL == zbx_vc_get_value_range(item->itemid, item->value_type, &values, seconds, nvalues, now)) + goto out; + + if (0 < values.values_num) + { + if (100 == percent) + { + percent = values.values_num-1; + } + else + { + percent = (int)((percent/100.0)*values.values_num); + } + if (ITEM_VALUE_TYPE_UINT64 == item->value_type) + { + zbx_uint64_t *auint64 = zbx_malloc(auint64, sizeof(zbx_uint64_t) * values.values_num); + for (i = 0; i < values.values_num; i++) + auint64[i] = values.values[i].value.ui64; + qsort(auint64, values.values_num, sizeof(zbx_uint64_t), zbx_default_uint64_compare_func); + zbx_snprintf(value, MAX_BUFFER_LEN, ZBX_FS_UI64, auint64[percent]); + zbx_free(auint64); + } + else + { + double *afloat = zbx_malloc(afloat, sizeof(double) * values.values_num); + for (i = 0; i < values.values_num; i++) + afloat[i] = values.values[i].value.dbl; + qsort(afloat, values.values_num, sizeof(double), compare_float); + zbx_snprintf(value, MAX_BUFFER_LEN, ZBX_FS_DBL, afloat[percent]); + zbx_free(afloat); + } + ret = SUCCEED; + } + else + zabbix_log(LOG_LEVEL_DEBUG, "result for PERCENTILE is empty"); +out: + zbx_history_record_vector_destroy(&values, item->value_type); + + zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret)); + + return ret; +} + +/****************************************************************************** + * * * Function: evaluate_DELTA * * * * Purpose: evaluate function 'delta' for the item * @@ -1714,6 +1830,10 @@ int evaluate_function(char *value, DC_ITEM *item, const char *function, const ch { ret = evaluate_MAX(value, item, function, parameter, now); } + else if (0 == strcmp(function, "percentile")) + { + ret = evaluate_PERCENTILE(value, item, function, parameter, now); + } else if (0 == strcmp(function, "avg")) { ret = evaluate_AVG(value, item, function, parameter, now); diff --git a/src/libs/zbxserver/evalfunc.h b/src/libs/zbxserver/evalfunc.h index 5e122d5..a78c146 100644 --- a/src/libs/zbxserver/evalfunc.h +++ b/src/libs/zbxserver/evalfunc.h @@ -27,6 +27,7 @@ #define ZBX_FLAG_VALUES 1 int cmp_double(double a, double b); +int compare_float(const void *float1, const void *float2); int evaluate_macro_function(char *value, const char *host, const char *key, const char *function, const char *parameter);