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 #include "dbcache.h"
24 
25 #include "checks_aggregate.h"
26 
27 #define ZBX_VALUE_FUNC_MIN	0
28 #define ZBX_VALUE_FUNC_AVG	1
29 #define ZBX_VALUE_FUNC_MAX	2
30 #define ZBX_VALUE_FUNC_SUM	3
31 #define ZBX_VALUE_FUNC_COUNT	4
32 #define ZBX_VALUE_FUNC_LAST	5
33 
34 /******************************************************************************
35  *                                                                            *
36  * Function: evaluate_history_func_min                                        *
37  *                                                                            *
38  * Purpose: calculate minimum value from the history value vector             *
39  *                                                                            *
40  * Parameters: values      - [IN] a vector containing history values          *
41  *             value_type  - [IN] the type of values. Only float/uint64       *
42  *                           values are supported.                            *
43  *             result      - [OUT] the resulting value                        *
44  *                                                                            *
45  ******************************************************************************/
evaluate_history_func_min(zbx_vector_history_record_t * values,int value_type,history_value_t * result)46 static void	evaluate_history_func_min(zbx_vector_history_record_t *values, int value_type, history_value_t *result)
47 {
48 	int	i;
49 
50 	*result = values->values[0].value;
51 
52 	if (ITEM_VALUE_TYPE_UINT64 == value_type)
53 	{
54 		for (i = 1; i < values->values_num; i++)
55 			if (values->values[i].value.ui64 < result->ui64)
56 				result->ui64 = values->values[i].value.ui64;
57 	}
58 	else
59 	{
60 		for (i = 1; i < values->values_num; i++)
61 			if (values->values[i].value.dbl < result->dbl)
62 				result->dbl = values->values[i].value.dbl;
63 	}
64 }
65 
66 /******************************************************************************
67  *                                                                            *
68  * Function: evaluate_history_func_max                                        *
69  *                                                                            *
70  * Purpose: calculate maximum value from the history value vector             *
71  *                                                                            *
72  * Parameters: values      - [IN] a vector containing history values          *
73  *             value_type  - [IN] the type of values. Only float/uint64       *
74  *                           values are supported.                            *
75  *             result      - [OUT] the resulting value                        *
76  *                                                                            *
77  ******************************************************************************/
evaluate_history_func_max(zbx_vector_history_record_t * values,int value_type,history_value_t * result)78 static void	evaluate_history_func_max(zbx_vector_history_record_t *values, int value_type, history_value_t *result)
79 {
80 	int	i;
81 
82 	*result = values->values[0].value;
83 
84 	if (ITEM_VALUE_TYPE_UINT64 == value_type)
85 	{
86 		for (i = 1; i < values->values_num; i++)
87 			if (values->values[i].value.ui64 > result->ui64)
88 				result->ui64 = values->values[i].value.ui64;
89 	}
90 	else
91 	{
92 		for (i = 1; i < values->values_num; i++)
93 			if (values->values[i].value.dbl > result->dbl)
94 				result->dbl = values->values[i].value.dbl;
95 	}
96 }
97 
98 /******************************************************************************
99  *                                                                            *
100  * Function: evaluate_history_func_sum                                        *
101  *                                                                            *
102  * Purpose: calculate sum of values from the history value vector             *
103  *                                                                            *
104  * Parameters: values      - [IN] a vector containing history values          *
105  *             value_type  - [IN] the type of values. Only float/uint64       *
106  *                           values are supported.                            *
107  *             result      - [OUT] the resulting value                        *
108  *                                                                            *
109  ******************************************************************************/
evaluate_history_func_sum(zbx_vector_history_record_t * values,int value_type,history_value_t * result)110 static void	evaluate_history_func_sum(zbx_vector_history_record_t *values, int value_type, history_value_t *result)
111 {
112 	int	i;
113 
114 	if (ITEM_VALUE_TYPE_UINT64 == value_type)
115 	{
116 		result->ui64 = 0;
117 		for (i = 0; i < values->values_num; i++)
118 			result->ui64 += values->values[i].value.ui64;
119 	}
120 	else
121 	{
122 		result->dbl = 0;
123 		for (i = 0; i < values->values_num; i++)
124 			result->dbl += values->values[i].value.dbl;
125 	}
126 }
127 
128 /******************************************************************************
129  *                                                                            *
130  * Function: evaluate_history_func_avg                                        *
131  *                                                                            *
132  * Purpose: calculate average value of values from the history value vector   *
133  *                                                                            *
134  * Parameters: values      - [IN] a vector containing history values          *
135  *             value_type  - [IN] the type of values. Only float/uint64       *
136  *                           values are supported.                            *
137  *             result      - [OUT] the resulting value                        *
138  *                                                                            *
139  ******************************************************************************/
evaluate_history_func_avg(zbx_vector_history_record_t * values,int value_type,history_value_t * result)140 static void	evaluate_history_func_avg(zbx_vector_history_record_t *values, int value_type, history_value_t *result)
141 {
142 	evaluate_history_func_sum(values, value_type, result);
143 
144 	if (ITEM_VALUE_TYPE_UINT64 == value_type)
145 		result->ui64 /= values->values_num;
146 	else
147 		result->dbl /= values->values_num;
148 }
149 
150 /******************************************************************************
151  *                                                                            *
152  * Function: evaluate_history_func_count                                      *
153  *                                                                            *
154  * Purpose: calculate number of values in value vector                        *
155  *                                                                            *
156  * Parameters: values      - [IN] a vector containing history values          *
157  *             value_type  - [IN] the type of values. Only float/uint64       *
158  *                           values are supported.                            *
159  *             result      - [OUT] the resulting value                        *
160  *                                                                            *
161  ******************************************************************************/
evaluate_history_func_count(zbx_vector_history_record_t * values,int value_type,history_value_t * result)162 static void	evaluate_history_func_count(zbx_vector_history_record_t *values, int value_type,
163 		history_value_t *result)
164 {
165 	if (ITEM_VALUE_TYPE_UINT64 == value_type)
166 		result->ui64 = values->values_num;
167 	else
168 		result->dbl = values->values_num;
169 }
170 
171 /******************************************************************************
172  *                                                                            *
173  * Function: evaluate_history_func_last                                       *
174  *                                                                            *
175  * Purpose: calculate the last (newest) value in value vector                 *
176  *                                                                            *
177  * Parameters: values      - [IN] a vector containing history values          *
178  *             result      - [OUT] the resulting value                        *
179  *                                                                            *
180  ******************************************************************************/
evaluate_history_func_last(zbx_vector_history_record_t * values,history_value_t * result)181 static void	evaluate_history_func_last(zbx_vector_history_record_t *values, history_value_t *result)
182 {
183 	*result = values->values[0].value;
184 }
185 
186 /******************************************************************************
187  *                                                                            *
188  * Function: evaluate_history_func                                            *
189  *                                                                            *
190  * Purpose: calculate function with values from value vector                  *
191  *                                                                            *
192  * Parameters: values      - [IN] a vector containing history values          *
193  *             value_type  - [IN] the type of values. Only float/uint64       *
194  *                           values are supported.                            *
195  *             func        - [IN] the function to calculate. Only             *
196  *                           ZBX_VALUE_FUNC_MIN, ZBX_VALUE_FUNC_AVG,          *
197  *                           ZBX_VALUE_FUNC_MAX, ZBX_VALUE_FUNC_SUM,          *
198  *                           ZBX_VALUE_FUNC_COUNT, ZBX_VALUE_FUNC_LAST        *
199  *                           functions are supported.                         *
200  *             result      - [OUT] the resulting value                        *
201  *                                                                            *
202  ******************************************************************************/
evaluate_history_func(zbx_vector_history_record_t * values,int value_type,int func,history_value_t * result)203 static void	evaluate_history_func(zbx_vector_history_record_t *values, int value_type, int func,
204 		history_value_t *result)
205 {
206 	switch (func)
207 	{
208 		case ZBX_VALUE_FUNC_MIN:
209 			evaluate_history_func_min(values, value_type, result);
210 			break;
211 		case ZBX_VALUE_FUNC_AVG:
212 			evaluate_history_func_avg(values, value_type, result);
213 			break;
214 		case ZBX_VALUE_FUNC_MAX:
215 			evaluate_history_func_max(values, value_type, result);
216 			break;
217 		case ZBX_VALUE_FUNC_SUM:
218 			evaluate_history_func_sum(values, value_type, result);
219 			break;
220 		case ZBX_VALUE_FUNC_COUNT:
221 			evaluate_history_func_count(values, value_type, result);
222 			break;
223 		case ZBX_VALUE_FUNC_LAST:
224 			evaluate_history_func_last(values, result);
225 			break;
226 	}
227 }
228 
229 /******************************************************************************
230  *                                                                            *
231  * Function: quote_string                                                     *
232  *                                                                            *
233  * Purpose: quotes string by enclosing it in double quotes and escaping       *
234  *          double quotes inside string with '\'.                             *
235  *                                                                            *
236  * Parameters: str    - [IN/OUT] the string to quote                          *
237  *             sz_str - [IN] the string length                                *
238  *                                                                            *
239  * Comments: The '\' character itself is not quoted. As the result if string  *
240  *           ends with '\' it can be quoted (for example for error messages), *
241  *           but it's impossible to unquote it.                               *
242  *                                                                            *
243  ******************************************************************************/
quote_string(char ** str,size_t sz_src)244 static void	quote_string(char **str, size_t sz_src)
245 {
246 	size_t	sz_dst;
247 
248 	sz_dst = zbx_get_escape_string_len(*str, "\"") + 3;
249 
250 	*str = (char *)zbx_realloc(*str, sz_dst);
251 
252 	(*str)[--sz_dst] = '\0';
253 	(*str)[--sz_dst] = '"';
254 
255 	while (0 < sz_src)
256 	{
257 		(*str)[--sz_dst] = (*str)[--sz_src];
258 
259 		if ('"' == (*str)[sz_src])
260 			(*str)[--sz_dst] = '\\';
261 	}
262 	(*str)[--sz_dst] = '"';
263 }
264 
265 /******************************************************************************
266  *                                                                            *
267  * Function: aggregate_quote_groups                                           *
268  *                                                                            *
269  * Purpose: quotes the individual groups in the list if necessary             *
270  *                                                                            *
271  ******************************************************************************/
aggregate_quote_groups(char ** str,size_t * str_alloc,size_t * str_offset,zbx_vector_str_t * groups)272 static void	aggregate_quote_groups(char **str, size_t *str_alloc, size_t *str_offset, zbx_vector_str_t *groups)
273 {
274 	int	i;
275 	char	*group, *separator = "";
276 
277 	for (i = 1; i <= groups->values_num; i++)
278 	{
279 		group = zbx_strdup(NULL, groups->values[i - 1]);
280 		zbx_strcpy_alloc(str, str_alloc, str_offset, separator);
281 		separator = (char *)", ";
282 
283 		quote_string(&group, strlen(group));
284 		zbx_strcpy_alloc(str, str_alloc, str_offset, group);
285 		zbx_free(group);
286 	}
287 }
288 
289 /******************************************************************************
290  *                                                                            *
291  * Function: aggregate_get_items                                              *
292  *                                                                            *
293  * Purpose: get array of items specified by key for selected groups           *
294  *          (including nested groups)                                         *
295  *                                                                            *
296  * Parameters: itemids - [OUT] list of item ids                               *
297  *             groups  - [IN] list of host groups                             *
298  *             itemkey - [IN] item key to aggregate                           *
299  *             error   - [OUT] the error message                              *
300  *                                                                            *
301  * Return value: SUCCEED - item identifier(s) were retrieved successfully     *
302  *               FAIL    - no items matching the specified groups or keys     *
303  *                                                                            *
304  ******************************************************************************/
aggregate_get_items(zbx_vector_uint64_t * itemids,zbx_vector_str_t * groups,const char * itemkey,char ** error)305 static int	aggregate_get_items(zbx_vector_uint64_t *itemids, zbx_vector_str_t *groups, const char *itemkey,
306 		char **error)
307 {
308 	const char	*__function_name = "aggregate_get_items";
309 
310 	char			*esc;
311 	DB_RESULT		result;
312 	DB_ROW			row;
313 	zbx_uint64_t		itemid;
314 	char			*sql = NULL;
315 	size_t			sql_alloc = ZBX_KIBIBYTE, sql_offset = 0, error_alloc = 0, error_offset = 0;
316 	int			ret = FAIL;
317 	zbx_vector_uint64_t	groupids;
318 
319 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() itemkey:'%s'", __function_name, itemkey);
320 
321 	zbx_vector_uint64_create(&groupids);
322 	zbx_dc_get_nested_hostgroupids_by_names(groups, &groupids);
323 
324 	if (0 == groupids.values_num)
325 	{
326 		zbx_strcpy_alloc(error, &error_alloc, &error_offset, "None of the groups in list ");
327 		aggregate_quote_groups(error, &error_alloc, &error_offset, groups);
328 		zbx_strcpy_alloc(error, &error_alloc, &error_offset, " is correct.");
329 		goto out;
330 	}
331 
332 	sql = (char *)zbx_malloc(sql, sql_alloc);
333 	esc = DBdyn_escape_string(itemkey);
334 
335 	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
336 			"select distinct i.itemid"
337 			" from items i,hosts h,hosts_groups hg"
338 			" where i.hostid=h.hostid"
339 				" and h.hostid=hg.hostid"
340 				" and i.key_='%s'"
341 				" and i.status=%d"
342 				" and i.state=%d"
343 				" and h.status=%d"
344 				" and",
345 			esc, ITEM_STATUS_ACTIVE, ITEM_STATE_NORMAL, HOST_STATUS_MONITORED);
346 
347 	zbx_free(esc);
348 
349 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "hg.groupid", groupids.values, groupids.values_num);
350 	result = DBselect("%s", sql);
351 	zbx_free(sql);
352 
353 	while (NULL != (row = DBfetch(result)))
354 	{
355 		ZBX_STR2UINT64(itemid, row[0]);
356 		zbx_vector_uint64_append(itemids, itemid);
357 	}
358 	DBfree_result(result);
359 
360 	if (0 == itemids->values_num)
361 	{
362 		zbx_snprintf_alloc(error, &error_alloc, &error_offset, "No items for key \"%s\" in group(s) ", itemkey);
363 		aggregate_quote_groups(error, &error_alloc, &error_offset, groups);
364 		zbx_chrcpy_alloc(error, &error_alloc, &error_offset, '.');
365 		goto out;
366 	}
367 
368 	zbx_vector_uint64_sort(itemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
369 
370 	ret = SUCCEED;
371 
372 out:
373 	zbx_vector_uint64_destroy(&groupids);
374 
375 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
376 
377 	return ret;
378 }
379 
380 /******************************************************************************
381  *                                                                            *
382  * Function: evaluate_aggregate                                               *
383  *                                                                            *
384  * Parameters: item      - [IN] aggregated item                               *
385  *             grp_func  - [IN] one of ZBX_GRP_FUNC_*                         *
386  *             groups    - [IN] list of host groups                           *
387  *             itemkey   - [IN] item key to aggregate                         *
388  *             item_func - [IN] one of ZBX_VALUE_FUNC_*                       *
389  *             param     - [IN] item_func parameter (optional)                *
390  *                                                                            *
391  * Return value: SUCCEED - aggregate item evaluated successfully              *
392  *               FAIL - otherwise                                             *
393  *                                                                            *
394  ******************************************************************************/
evaluate_aggregate(DC_ITEM * item,AGENT_RESULT * res,int grp_func,zbx_vector_str_t * groups,const char * itemkey,int item_func,const char * param)395 static int	evaluate_aggregate(DC_ITEM *item, AGENT_RESULT *res, int grp_func, zbx_vector_str_t *groups,
396 		const char *itemkey, int item_func, const char *param)
397 {
398 	const char			*__function_name = "evaluate_aggregate";
399 	zbx_vector_uint64_t		itemids;
400 	history_value_t			value, item_result;
401 	zbx_history_record_t		group_value;
402 	int				ret = FAIL, *errcodes = NULL, i, count, seconds;
403 	DC_ITEM				*items = NULL;
404 	zbx_vector_history_record_t	values, group_values;
405 	char				*error = NULL;
406 	zbx_timespec_t			ts;
407 
408 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() grp_func:%d itemkey:'%s' item_func:%d param:'%s'",
409 			__function_name, grp_func, itemkey, item_func, ZBX_NULL2STR(param));
410 
411 	zbx_timespec(&ts);
412 	zbx_vector_uint64_create(&itemids);
413 
414 	if (FAIL == aggregate_get_items(&itemids, groups, itemkey, &error))
415 	{
416 		SET_MSG_RESULT(res, error);
417 		goto clean1;
418 	}
419 
420 	memset(&value, 0, sizeof(value));
421 	zbx_history_record_vector_create(&group_values);
422 
423 	items = (DC_ITEM *)zbx_malloc(items, sizeof(DC_ITEM) * itemids.values_num);
424 	errcodes = (int *)zbx_malloc(errcodes, sizeof(int) * itemids.values_num);
425 
426 	DCconfig_get_items_by_itemids(items, itemids.values, errcodes, itemids.values_num);
427 
428 	if (ZBX_VALUE_FUNC_LAST == item_func)
429 	{
430 		count = 1;
431 		seconds = 0;
432 	}
433 	else
434 	{
435 		if (FAIL == is_time_suffix(param, &seconds, ZBX_LENGTH_UNLIMITED))
436 		{
437 			SET_MSG_RESULT(res, zbx_strdup(NULL, "Invalid fourth parameter."));
438 			goto clean2;
439 		}
440 		count = 0;
441 	}
442 
443 	for (i = 0; i < itemids.values_num; i++)
444 	{
445 		if (SUCCEED != errcodes[i])
446 			continue;
447 
448 		if (ITEM_STATUS_ACTIVE != items[i].status)
449 			continue;
450 
451 		if (HOST_STATUS_MONITORED != items[i].host.status)
452 			continue;
453 
454 		if (ITEM_VALUE_TYPE_FLOAT != items[i].value_type && ITEM_VALUE_TYPE_UINT64 != items[i].value_type)
455 			continue;
456 
457 		zbx_history_record_vector_create(&values);
458 
459 		if (SUCCEED == zbx_vc_get_values(items[i].itemid, items[i].value_type, &values, seconds, count, &ts) &&
460 				0 < values.values_num)
461 		{
462 			evaluate_history_func(&values, items[i].value_type, item_func, &item_result);
463 
464 			if (item->value_type == items[i].value_type)
465 				group_value.value = item_result;
466 			else
467 			{
468 				if (ITEM_VALUE_TYPE_UINT64 == item->value_type)
469 					group_value.value.ui64 = (zbx_uint64_t)item_result.dbl;
470 				else
471 					group_value.value.dbl = (double)item_result.ui64;
472 			}
473 
474 			zbx_vector_history_record_append_ptr(&group_values, &group_value);
475 		}
476 
477 		zbx_history_record_vector_destroy(&values, items[i].value_type);
478 	}
479 
480 	if (0 == group_values.values_num)
481 	{
482 		char	*tmp = NULL;
483 		size_t	tmp_alloc = 0, tmp_offset = 0;
484 
485 		aggregate_quote_groups(&tmp, &tmp_alloc, &tmp_offset, groups);
486 		SET_MSG_RESULT(res, zbx_dsprintf(NULL, "No values for key \"%s\" in group(s) %s.", itemkey, tmp));
487 		zbx_free(tmp);
488 
489 		goto clean2;
490 	}
491 
492 	evaluate_history_func(&group_values, item->value_type, grp_func, &value);
493 
494 	if (ITEM_VALUE_TYPE_FLOAT == item->value_type)
495 		SET_DBL_RESULT(res, value.dbl);
496 	else
497 		SET_UI64_RESULT(res, value.ui64);
498 
499 	ret = SUCCEED;
500 clean2:
501 	DCconfig_clean_items(items, errcodes, itemids.values_num);
502 
503 	zbx_free(errcodes);
504 	zbx_free(items);
505 	zbx_history_record_vector_destroy(&group_values, item->value_type);
506 clean1:
507 	zbx_vector_uint64_destroy(&itemids);
508 
509 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
510 
511 	return ret;
512 }
513 
514 /******************************************************************************
515  *                                                                            *
516  * Function: get_value_aggregate                                              *
517  *                                                                            *
518  * Purpose: retrieve data from Zabbix server (aggregate items)                *
519  *                                                                            *
520  * Parameters: item - item we are interested in                               *
521  *                                                                            *
522  * Return value: SUCCEED - data successfully retrieved and stored in result   *
523  *                         and result_str (as string)                         *
524  *               NOTSUPPORTED - requested item is not supported               *
525  *                                                                            *
526  * Author: Alexei Vladishev                                                   *
527  *                                                                            *
528  ******************************************************************************/
get_value_aggregate(DC_ITEM * item,AGENT_RESULT * result)529 int	get_value_aggregate(DC_ITEM *item, AGENT_RESULT *result)
530 {
531 	const char	*__function_name = "get_value_aggregate";
532 
533 	AGENT_REQUEST		request;
534 	int			ret = NOTSUPPORTED;
535 	const char		*tmp, *groups, *itemkey, *funcp = NULL;
536 	int			grp_func, item_func, params_num;
537 	zbx_vector_str_t	group_names;
538 
539 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s'", __function_name, item->key_orig);
540 
541 	init_request(&request);
542 	zbx_vector_str_create(&group_names);
543 
544 	if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type)
545 	{
546 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Value type must be Numeric for aggregate items"));
547 		goto out;
548 	}
549 
550 	if (SUCCEED != parse_item_key(item->key, &request))
551 	{
552 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid item key format."));
553 		goto out;
554 	}
555 
556 	if (0 == strcmp(get_rkey(&request), "grpmin"))
557 	{
558 		grp_func = ZBX_VALUE_FUNC_MIN;
559 	}
560 	else if (0 == strcmp(get_rkey(&request), "grpavg"))
561 	{
562 		grp_func = ZBX_VALUE_FUNC_AVG;
563 	}
564 	else if (0 == strcmp(get_rkey(&request), "grpmax"))
565 	{
566 		grp_func = ZBX_VALUE_FUNC_MAX;
567 	}
568 	else if (0 == strcmp(get_rkey(&request), "grpsum"))
569 	{
570 		grp_func = ZBX_VALUE_FUNC_SUM;
571 	}
572 	else
573 	{
574 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid item key."));
575 		goto out;
576 	}
577 
578 	params_num = get_rparams_num(&request);
579 
580 	if (3 > params_num || params_num > 4)
581 	{
582 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid number of parameters."));
583 		goto out;
584 	}
585 
586 	groups = get_rparam(&request, 0);
587 	itemkey = get_rparam(&request, 1);
588 	tmp = get_rparam(&request, 2);
589 
590 	if (REQUEST_PARAMETER_TYPE_ARRAY == get_rparam_type(&request, 0))
591 	{
592 		int				i, groups_num;
593 		char				*group;
594 		zbx_request_parameter_type_t	type;
595 
596 		groups_num = num_param(groups);
597 
598 		for (i = 1; i <= groups_num; i++)
599 		{
600 			if (NULL == (group = get_param_dyn(groups, i, &type)))
601 				continue;
602 
603 			zbx_vector_str_append(&group_names, group);
604 
605 			if (REQUEST_PARAMETER_TYPE_STRING != type)
606 			{
607 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Invalid host group list: %s", groups));
608 				goto out;
609 			}
610 		}
611 	}
612 	else
613 		zbx_vector_str_append(&group_names, zbx_strdup(NULL, groups));
614 
615 	zabbix_log(LOG_LEVEL_DEBUG, "Host groups: '%s', Item key: '%s', Item function: '%s'", groups, itemkey, tmp);
616 
617 	if (0 == strcmp(tmp, "min"))
618 		item_func = ZBX_VALUE_FUNC_MIN;
619 	else if (0 == strcmp(tmp, "avg"))
620 		item_func = ZBX_VALUE_FUNC_AVG;
621 	else if (0 == strcmp(tmp, "max"))
622 		item_func = ZBX_VALUE_FUNC_MAX;
623 	else if (0 == strcmp(tmp, "sum"))
624 		item_func = ZBX_VALUE_FUNC_SUM;
625 	else if (0 == strcmp(tmp, "count"))
626 		item_func = ZBX_VALUE_FUNC_COUNT;
627 	else if (0 == strcmp(tmp, "last"))
628 		item_func = ZBX_VALUE_FUNC_LAST;
629 	else
630 	{
631 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
632 		goto out;
633 	}
634 
635 	if (4 == params_num)
636 	{
637 		funcp = get_rparam(&request, 3);
638 	}
639 	else if (3 == params_num && ZBX_VALUE_FUNC_LAST != item_func)
640 	{
641 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid number of parameters."));
642 		goto out;
643 	}
644 
645 	if (SUCCEED != evaluate_aggregate(item, result, grp_func, &group_names, itemkey, item_func, funcp))
646 		goto out;
647 
648 	ret = SUCCEED;
649 out:
650 	zbx_vector_str_clear_ext(&group_names, zbx_str_free);
651 	zbx_vector_str_destroy(&group_names);
652 	free_request(&request);
653 
654 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
655 
656 	return ret;
657 }
658