1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "log.h"
22 #include "valuecache.h"
23
24 #include "checks_aggregate.h"
25
26 #define ZBX_VALUE_FUNC_MIN 0
27 #define ZBX_VALUE_FUNC_AVG 1
28 #define ZBX_VALUE_FUNC_MAX 2
29 #define ZBX_VALUE_FUNC_SUM 3
30 #define ZBX_VALUE_FUNC_COUNT 4
31 #define ZBX_VALUE_FUNC_LAST 5
32
33 /******************************************************************************
34 * *
35 * Function: evaluate_history_func_min *
36 * *
37 * Purpose: calculate minimum value from the history value vector *
38 * *
39 * Parameters: values - [IN] a vector containing history values *
40 * value_type - [IN] the type of values. Only float/uint64 *
41 * values are supported. *
42 * result - [OUT] the resulting value *
43 * *
44 ******************************************************************************/
evaluate_history_func_min(zbx_vector_history_record_t * values,int value_type,history_value_t * result)45 static void evaluate_history_func_min(zbx_vector_history_record_t *values, int value_type, history_value_t *result)
46 {
47 int i;
48
49 *result = values->values[0].value;
50
51 if (ITEM_VALUE_TYPE_UINT64 == value_type)
52 {
53 for (i = 1; i < values->values_num; i++)
54 if (values->values[i].value.ui64 < result->ui64)
55 result->ui64 = values->values[i].value.ui64;
56 }
57 else
58 {
59 for (i = 1; i < values->values_num; i++)
60 if (values->values[i].value.dbl < result->dbl)
61 result->dbl = values->values[i].value.dbl;
62 }
63 }
64
65 /******************************************************************************
66 * *
67 * Function: evaluate_history_func_max *
68 * *
69 * Purpose: calculate maximum value from the history value vector *
70 * *
71 * Parameters: values - [IN] a vector containing history values *
72 * value_type - [IN] the type of values. Only float/uint64 *
73 * values are supported. *
74 * result - [OUT] the resulting value *
75 * *
76 ******************************************************************************/
evaluate_history_func_max(zbx_vector_history_record_t * values,int value_type,history_value_t * result)77 static void evaluate_history_func_max(zbx_vector_history_record_t *values, int value_type, history_value_t *result)
78 {
79 int i;
80
81 *result = values->values[0].value;
82
83 if (ITEM_VALUE_TYPE_UINT64 == value_type)
84 {
85 for (i = 1; i < values->values_num; i++)
86 if (values->values[i].value.ui64 > result->ui64)
87 result->ui64 = values->values[i].value.ui64;
88 }
89 else
90 {
91 for (i = 1; i < values->values_num; i++)
92 if (values->values[i].value.dbl > result->dbl)
93 result->dbl = values->values[i].value.dbl;
94 }
95 }
96
97 /******************************************************************************
98 * *
99 * Function: evaluate_history_func_sum *
100 * *
101 * Purpose: calculate sum of values from the history value vector *
102 * *
103 * Parameters: values - [IN] a vector containing history values *
104 * value_type - [IN] the type of values. Only float/uint64 *
105 * values are supported. *
106 * result - [OUT] the resulting value *
107 * *
108 ******************************************************************************/
evaluate_history_func_sum(zbx_vector_history_record_t * values,int value_type,history_value_t * result)109 static void evaluate_history_func_sum(zbx_vector_history_record_t *values, int value_type, history_value_t *result)
110 {
111 int i;
112
113 if (ITEM_VALUE_TYPE_UINT64 == value_type)
114 {
115 result->ui64 = 0;
116 for (i = 0; i < values->values_num; i++)
117 result->ui64 += values->values[i].value.ui64;
118 }
119 else
120 {
121 result->dbl = 0;
122 for (i = 0; i < values->values_num; i++)
123 result->dbl += values->values[i].value.dbl;
124 }
125 }
126
127 /******************************************************************************
128 * *
129 * Function: evaluate_history_func_avg *
130 * *
131 * Purpose: calculate average value of values from the history value vector *
132 * *
133 * Parameters: values - [IN] a vector containing history values *
134 * value_type - [IN] the type of values. Only float/uint64 *
135 * values are supported. *
136 * result - [OUT] the resulting value *
137 * *
138 ******************************************************************************/
evaluate_history_func_avg(zbx_vector_history_record_t * values,int value_type,history_value_t * result)139 static void evaluate_history_func_avg(zbx_vector_history_record_t *values, int value_type, history_value_t *result)
140 {
141 evaluate_history_func_sum(values, value_type, result);
142
143 if (ITEM_VALUE_TYPE_UINT64 == value_type)
144 result->ui64 /= values->values_num;
145 else
146 result->dbl /= values->values_num;
147 }
148
149 /******************************************************************************
150 * *
151 * Function: evaluate_history_func_count *
152 * *
153 * Purpose: calculate number of values in value vector *
154 * *
155 * Parameters: values - [IN] a vector containing history values *
156 * value_type - [IN] the type of values. Only float/uint64 *
157 * values are supported. *
158 * result - [OUT] the resulting value *
159 * *
160 ******************************************************************************/
evaluate_history_func_count(zbx_vector_history_record_t * values,int value_type,history_value_t * result)161 static void evaluate_history_func_count(zbx_vector_history_record_t *values, int value_type,
162 history_value_t *result)
163 {
164 if (ITEM_VALUE_TYPE_UINT64 == value_type)
165 result->ui64 = values->values_num;
166 else
167 result->dbl = values->values_num;
168 }
169
170 /******************************************************************************
171 * *
172 * Function: evaluate_history_func_last *
173 * *
174 * Purpose: calculate the last (newest) value in value vector *
175 * *
176 * Parameters: values - [IN] a vector containing history values *
177 * value_type - [IN] the type of values. Only float/uint64 *
178 * values are supported. *
179 * result - [OUT] the resulting value *
180 * *
181 ******************************************************************************/
evaluate_history_func_last(zbx_vector_history_record_t * values,int value_type,history_value_t * result)182 static void evaluate_history_func_last(zbx_vector_history_record_t *values, int value_type,
183 history_value_t *result)
184 {
185 *result = values->values[0].value;
186 }
187
188 /******************************************************************************
189 * *
190 * Function: evaluate_history_func *
191 * *
192 * Purpose: calculate function with values from value vector *
193 * *
194 * Parameters: values - [IN] a vector containing history values *
195 * value_type - [IN] the type of values. Only float/uint64 *
196 * values are supported. *
197 * func - [IN] the function to calculate. Only *
198 * ZBX_VALUE_FUNC_MIN, ZBX_VALUE_FUNC_AVG, *
199 * ZBX_VALUE_FUNC_MAX, ZBX_VALUE_FUNC_SUM, *
200 * ZBX_VALUE_FUNC_COUNT, ZBX_VALUE_FUNC_LAST *
201 * functions are supported. *
202 * result - [OUT] the resulting value *
203 * *
204 ******************************************************************************/
evaluate_history_func(zbx_vector_history_record_t * values,int value_type,int func,history_value_t * result)205 static void evaluate_history_func(zbx_vector_history_record_t *values, int value_type, int func,
206 history_value_t *result)
207 {
208 switch (func)
209 {
210 case ZBX_VALUE_FUNC_MIN:
211 evaluate_history_func_min(values, value_type, result);
212 break;
213 case ZBX_VALUE_FUNC_AVG:
214 evaluate_history_func_avg(values, value_type, result);
215 break;
216 case ZBX_VALUE_FUNC_MAX:
217 evaluate_history_func_max(values, value_type, result);
218 break;
219 case ZBX_VALUE_FUNC_SUM:
220 evaluate_history_func_sum(values, value_type, result);
221 break;
222 case ZBX_VALUE_FUNC_COUNT:
223 evaluate_history_func_count(values, value_type, result);
224 break;
225 case ZBX_VALUE_FUNC_LAST:
226 evaluate_history_func_last(values, value_type, result);
227 break;
228 }
229 }
230
231 /******************************************************************************
232 * *
233 * Function: aggregate_get_items *
234 * *
235 * Purpose: get array of items specified by key for selected groups *
236 * *
237 * Parameters: itemids - [OUT] list of item ids *
238 * groups - [IN] list of comma-separated host groups *
239 * itemkey - [IN] item key to aggregate *
240 * *
241 ******************************************************************************/
aggregate_get_items(zbx_vector_uint64_t * itemids,const char * groups,const char * itemkey)242 static void aggregate_get_items(zbx_vector_uint64_t *itemids, const char *groups, const char *itemkey)
243 {
244 const char *__function_name = "aggregate_get_items";
245
246 char *group, *esc;
247 DB_RESULT result;
248 DB_ROW row;
249 zbx_uint64_t itemid;
250 char *sql = NULL;
251 size_t sql_alloc = ZBX_KIBIBYTE, sql_offset = 0;
252 int num, n;
253
254 zabbix_log(LOG_LEVEL_DEBUG, "In %s() groups:'%s' itemkey:'%s'", __function_name, groups, itemkey);
255
256 sql = zbx_malloc(sql, sql_alloc);
257
258 esc = DBdyn_escape_string(itemkey);
259
260 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
261 "select distinct i.itemid"
262 " from items i,hosts h,hosts_groups hg,groups g"
263 " where i.hostid=h.hostid"
264 " and h.hostid=hg.hostid"
265 " and hg.groupid=g.groupid"
266 " and i.key_='%s'"
267 " and i.status=%d"
268 " and i.state=%d"
269 " and h.status=%d",
270 esc, ITEM_STATUS_ACTIVE, ITEM_STATE_NORMAL, HOST_STATUS_MONITORED);
271
272 zbx_free(esc);
273
274 num = num_param(groups);
275
276 zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " and g.name in (");
277
278 for (n = 1; n <= num; n++)
279 {
280 if (NULL == (group = get_param_dyn(groups, n)))
281 continue;
282
283 esc = DBdyn_escape_string(group);
284
285 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "'%s'", esc);
286
287 if (n != num)
288 zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, ',');
289
290 zbx_free(esc);
291 zbx_free(group);
292 }
293
294 zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, ')');
295
296 result = DBselect("%s", sql);
297
298 zbx_free(sql);
299
300 while (NULL != (row = DBfetch(result)))
301 {
302 ZBX_STR2UINT64(itemid, row[0]);
303 zbx_vector_uint64_append(itemids, itemid);
304 }
305 DBfree_result(result);
306
307 zbx_vector_uint64_sort(itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
308
309 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
310 }
311
312 /******************************************************************************
313 * *
314 * Function: evaluate_aggregate *
315 * *
316 * Parameters: item - [IN] aggregated item *
317 * grp_func - [IN] one of ZBX_GRP_FUNC_* *
318 * groups - [IN] list of comma-separated host groups *
319 * itemkey - [IN] item key to aggregate *
320 * item_func - [IN] one of ZBX_VALUE_FUNC_* *
321 * param - [IN] item_func parameter (optional) *
322 * *
323 * Return value: SUCCEED - aggregate item evaluated successfully *
324 * FAIL - otherwise *
325 * *
326 ******************************************************************************/
evaluate_aggregate(DC_ITEM * item,AGENT_RESULT * res,int grp_func,const char * groups,const char * itemkey,int item_func,const char * param)327 static int evaluate_aggregate(DC_ITEM *item, AGENT_RESULT *res, int grp_func, const char *groups,
328 const char *itemkey, int item_func, const char *param)
329 {
330 const char *__function_name = "evaluate_aggregate";
331 zbx_vector_uint64_t itemids;
332 history_value_t value, item_result;
333 zbx_history_record_t group_value;
334 int ret = FAIL, now, *errcodes = NULL, i, count, seconds;
335 DC_ITEM *items = NULL;
336 zbx_vector_history_record_t values, group_values;
337
338 zabbix_log(LOG_LEVEL_DEBUG, "In %s() grp_func:%d groups:'%s' itemkey:'%s' item_func:%d param:'%s'",
339 __function_name, grp_func, groups, itemkey, item_func, ZBX_NULL2STR(param));
340
341 now = time(NULL);
342
343 zbx_vector_uint64_create(&itemids);
344 aggregate_get_items(&itemids, groups, itemkey);
345
346 if (0 == itemids.values_num)
347 {
348 SET_MSG_RESULT(res, zbx_dsprintf(NULL, "No items for key \"%s\" in group(s) \"%s\".", itemkey, groups));
349 goto clean1;
350 }
351
352 memset(&value, 0, sizeof(value));
353 zbx_history_record_vector_create(&group_values);
354
355 items = zbx_malloc(items, sizeof(DC_ITEM) * itemids.values_num);
356 errcodes = zbx_malloc(errcodes, sizeof(int) * itemids.values_num);
357
358 DCconfig_get_items_by_itemids(items, itemids.values, errcodes, itemids.values_num);
359
360 if (ZBX_VALUE_FUNC_LAST == item_func)
361 {
362 count = 1;
363 seconds = 0;
364 }
365 else
366 {
367 if (FAIL == is_time_suffix(param, &seconds))
368 {
369 SET_MSG_RESULT(res, zbx_strdup(NULL, "Invalid fourth parameter."));
370 goto clean2;
371 }
372 count = 0;
373 }
374
375 for (i = 0; i < itemids.values_num; i++)
376 {
377 if (SUCCEED != errcodes[i])
378 continue;
379
380 if (ITEM_STATUS_ACTIVE != items[i].status)
381 continue;
382
383 if (HOST_STATUS_MONITORED != items[i].host.status)
384 continue;
385
386 if (ITEM_VALUE_TYPE_FLOAT != items[i].value_type && ITEM_VALUE_TYPE_UINT64 != items[i].value_type)
387 continue;
388
389 zbx_history_record_vector_create(&values);
390
391 if (SUCCEED == zbx_vc_get_value_range(items[i].itemid, items[i].value_type, &values, seconds,
392 count, now) && 0 < values.values_num)
393 {
394 evaluate_history_func(&values, items[i].value_type, item_func, &item_result);
395
396 if (item->value_type == items[i].value_type)
397 group_value.value = item_result;
398 else
399 {
400 if (ITEM_VALUE_TYPE_UINT64 == item->value_type)
401 group_value.value.ui64 = (zbx_uint64_t)item_result.dbl;
402 else
403 group_value.value.dbl = (double)item_result.ui64;
404 }
405
406 zbx_vector_history_record_append_ptr(&group_values, &group_value);
407 }
408
409 zbx_history_record_vector_destroy(&values, items[i].value_type);
410 }
411
412 if (0 == group_values.values_num)
413 {
414 SET_MSG_RESULT(res, zbx_dsprintf(NULL, "No values for key \"%s\" in group(s) \"%s\"", itemkey, groups));
415 goto clean2;
416 }
417
418 evaluate_history_func(&group_values, item->value_type, grp_func, &value);
419
420 if (ITEM_VALUE_TYPE_FLOAT == item->value_type)
421 SET_DBL_RESULT(res, value.dbl);
422 else
423 SET_UI64_RESULT(res, value.ui64);
424
425 ret = SUCCEED;
426 clean2:
427 DCconfig_clean_items(items, errcodes, itemids.values_num);
428
429 zbx_free(errcodes);
430 zbx_free(items);
431 zbx_history_record_vector_destroy(&group_values, item->value_type);
432 clean1:
433 zbx_vector_uint64_destroy(&itemids);
434
435 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
436
437 return ret;
438 }
439
440 /******************************************************************************
441 * *
442 * Function: get_value_aggregate *
443 * *
444 * Purpose: retrieve data from Zabbix server (aggregate items) *
445 * *
446 * Parameters: item - item we are interested in *
447 * *
448 * Return value: SUCCEED - data successfully retrieved and stored in result *
449 * and result_str (as string) *
450 * NOTSUPPORTED - requested item is not supported *
451 * *
452 * Author: Alexei Vladishev *
453 * *
454 ******************************************************************************/
get_value_aggregate(DC_ITEM * item,AGENT_RESULT * result)455 int get_value_aggregate(DC_ITEM *item, AGENT_RESULT *result)
456 {
457 const char *__function_name = "get_value_aggregate";
458
459 AGENT_REQUEST request;
460 int ret = NOTSUPPORTED;
461 const char *tmp, *groups, *itemkey, *funcp = NULL;
462 int grp_func, item_func, params_num;
463
464 zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s'", __function_name, item->key_orig);
465
466 init_request(&request);
467
468 if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type)
469 {
470 SET_MSG_RESULT(result, zbx_strdup(NULL, "Value type must be Numeric for aggregate items"));
471 goto out;
472 }
473
474 if (SUCCEED != parse_item_key(item->key, &request))
475 {
476 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid item key format."));
477 goto out;
478 }
479
480 if (0 == strcmp(get_rkey(&request), "grpmin"))
481 {
482 grp_func = ZBX_VALUE_FUNC_MIN;
483 }
484 else if (0 == strcmp(get_rkey(&request), "grpavg"))
485 {
486 grp_func = ZBX_VALUE_FUNC_AVG;
487 }
488 else if (0 == strcmp(get_rkey(&request), "grpmax"))
489 {
490 grp_func = ZBX_VALUE_FUNC_MAX;
491 }
492 else if (0 == strcmp(get_rkey(&request), "grpsum"))
493 {
494 grp_func = ZBX_VALUE_FUNC_SUM;
495 }
496 else
497 {
498 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid item key."));
499 goto out;
500 }
501
502 params_num = get_rparams_num(&request);
503
504 if (3 > params_num || params_num > 4)
505 {
506 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid number of parameters."));
507 goto out;
508 }
509
510 groups = get_rparam(&request, 0);
511 itemkey = get_rparam(&request, 1);
512 tmp = get_rparam(&request, 2);
513
514 if (0 == strcmp(tmp, "min"))
515 item_func = ZBX_VALUE_FUNC_MIN;
516 else if (0 == strcmp(tmp, "avg"))
517 item_func = ZBX_VALUE_FUNC_AVG;
518 else if (0 == strcmp(tmp, "max"))
519 item_func = ZBX_VALUE_FUNC_MAX;
520 else if (0 == strcmp(tmp, "sum"))
521 item_func = ZBX_VALUE_FUNC_SUM;
522 else if (0 == strcmp(tmp, "count"))
523 item_func = ZBX_VALUE_FUNC_COUNT;
524 else if (0 == strcmp(tmp, "last"))
525 item_func = ZBX_VALUE_FUNC_LAST;
526 else
527 {
528 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
529 goto out;
530 }
531
532 if (4 == params_num)
533 {
534 funcp = get_rparam(&request, 3);
535 }
536 else if (3 == params_num && ZBX_VALUE_FUNC_LAST != item_func)
537 {
538 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid number of parameters."));
539 goto out;
540 }
541
542 if (SUCCEED != evaluate_aggregate(item, result, grp_func, groups, itemkey, item_func, funcp))
543 goto out;
544
545 ret = SUCCEED;
546 out:
547 free_request(&request);
548
549 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
550
551 return ret;
552 }
553