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