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 
22 #include "db.h"
23 #include "log.h"
24 #include "mutexs.h"
25 
26 #define LOCK_ITSERVICES		zbx_mutex_lock(&itservices_lock)
27 #define UNLOCK_ITSERVICES	zbx_mutex_unlock(&itservices_lock)
28 
29 static ZBX_MUTEX	itservices_lock = ZBX_MUTEX_NULL;
30 
31 /* status update queue items */
32 typedef struct
33 {
34 	/* the update source id */
35 	zbx_uint64_t	sourceid;
36 	/* the new status */
37 	int		status;
38 	/* timestamp */
39 	int		clock;
40 }
41 zbx_status_update_t;
42 
43 /* IT service node */
44 typedef struct
45 {
46 	/* service id */
47 	zbx_uint64_t		serviceid;
48 	/* trigger id of leaf nodes */
49 	zbx_uint64_t		triggerid;
50 	/* the initial service status */
51 	int			old_status;
52 	/* the calculated service status */
53 	int			status;
54 	/* the service status calculation algorithm, see SERVICE_ALGORITHM_* defines */
55 	int			algorithm;
56 	/* the parent nodes */
57 	zbx_vector_ptr_t	parents;
58 	/* the child nodes */
59 	zbx_vector_ptr_t	children;
60 }
61 zbx_itservice_t;
62 
63 /* index of services by triggerid */
64 typedef struct
65 {
66 	zbx_uint64_t		triggerid;
67 	zbx_vector_ptr_t	itservices;
68 }
69 zbx_itservice_index_t;
70 
71 /* a set of IT services used during update session                          */
72 /*                                                                          */
73 /* All services are stored into hashset accessed by serviceid. The services */
74 /* also are indexed by triggerid.                                           */
75 /* The following types of services are loaded during update session:        */
76 /*  1) services directly linked to the triggers with values changed         */
77 /*     during update session.                                               */
78 /*  2) direct or indirect parent services of (1)                            */
79 /*  3) services required to calculate status of (2) and not already loaded  */
80 /*     as (1) or (2).                                                       */
81 /*                                                                          */
82 /* In this schema:                                                          */
83 /*   (1) can't have children services                                       */
84 /*   (2) will have children services                                        */
85 /*   (1) and (2) will have parent services unless it's the root service     */
86 /*   (3) will have neither children or parent services                      */
87 /*                                                                          */
88 typedef struct
89 {
90 	/* loaded IT services */
91 	zbx_hashset_t	itservices;
92 	/* service index by triggerid */
93 	zbx_hashset_t	index;
94 }
95 zbx_itservices_t;
96 
97 /******************************************************************************
98  *                                                                            *
99  * Function: its_itservices_init                                              *
100  *                                                                            *
101  * Purpose: initializes IT services data set to store services during update  *
102  *          session                                                           *
103  *                                                                            *
104  * Parameters: set   - [IN] the data set to initialize                        *
105  *                                                                            *
106  ******************************************************************************/
its_itservices_init(zbx_itservices_t * itservices)107 static void	its_itservices_init(zbx_itservices_t *itservices)
108 {
109 	zbx_hashset_create(&itservices->itservices, 512, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
110 	zbx_hashset_create(&itservices->index, 128, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
111 }
112 
113 /******************************************************************************
114  *                                                                            *
115  * Function: its_itservices_clean                                             *
116  *                                                                            *
117  * Purpose: cleans IT services data set by releasing allocated memory         *
118  *                                                                            *
119  * Parameters: set   - [IN] the data set to clean                             *
120  *                                                                            *
121  ******************************************************************************/
its_itservices_clean(zbx_itservices_t * itservices)122 static void	its_itservices_clean(zbx_itservices_t *itservices)
123 {
124 	zbx_hashset_iter_t	iter;
125 	zbx_itservice_t		*itservice;
126 	zbx_itservice_index_t	*index;
127 
128 	zbx_hashset_iter_reset(&itservices->index, &iter);
129 
130 	while (NULL != (index = zbx_hashset_iter_next(&iter)))
131 		zbx_vector_ptr_destroy(&index->itservices);
132 
133 	zbx_hashset_destroy(&itservices->index);
134 
135 	zbx_hashset_iter_reset(&itservices->itservices, &iter);
136 
137 	while (NULL != (itservice = zbx_hashset_iter_next(&iter)))
138 	{
139 		zbx_vector_ptr_destroy(&itservice->children);
140 		zbx_vector_ptr_destroy(&itservice->parents);
141 	}
142 
143 	zbx_hashset_destroy(&itservices->itservices);
144 }
145 
146 /******************************************************************************
147  *                                                                            *
148  * Function: its_itservice_create                                             *
149  *                                                                            *
150  * Purpose: creates a new IT service node                                     *
151  *                                                                            *
152  * Parameters: itservices  - [IN] the IT services data                        *
153  *             serviceid   - [IN] the service id                              *
154  *             algorithm   - [IN] the service status calculation mode         *
155  *             triggerid   - [IN] the source trigger id for leaf nodes        *
156  *             status      - [IN] the initial service status                  *
157  *                                                                            *
158  * Return value: the created IT service node                                  *
159  *                                                                            *
160  ******************************************************************************/
its_itservice_create(zbx_itservices_t * itservices,zbx_uint64_t serviceid,zbx_uint64_t triggerid,int status,int algorithm)161 static zbx_itservice_t	*its_itservice_create(zbx_itservices_t *itservices, zbx_uint64_t serviceid,
162 		zbx_uint64_t triggerid, int status, int algorithm)
163 {
164 	zbx_itservice_t		itservice = {serviceid, triggerid, status, status, algorithm}, *pitservice;
165 	zbx_itservice_index_t	*pindex;
166 
167 	zbx_vector_ptr_create(&itservice.children);
168 	zbx_vector_ptr_create(&itservice.parents);
169 
170 	pitservice = zbx_hashset_insert(&itservices->itservices, &itservice, sizeof(itservice));
171 
172 	if (0 != triggerid)
173 	{
174 		if (NULL == (pindex = zbx_hashset_search(&itservices->index, &triggerid)))
175 		{
176 			zbx_itservice_index_t	index = {triggerid};
177 
178 			zbx_vector_ptr_create(&index.itservices);
179 
180 			pindex = zbx_hashset_insert(&itservices->index, &index, sizeof(index));
181 		}
182 
183 		zbx_vector_ptr_append(&pindex->itservices, pitservice);
184 	}
185 
186 	return pitservice;
187 }
188 
189 /******************************************************************************
190  *                                                                            *
191  * Function: its_updates_append                                               *
192  *                                                                            *
193  * Purpose: adds an update to the queue                                       *
194  *                                                                            *
195  * Parameters: updates   - [OUT] the update queue                             *
196  *             sourceid  - [IN] the update source id                          *
197  *             status    - [IN] the update status                             *
198  *             clock     - [IN] the update timestamp                          *
199  *                                                                            *
200  ******************************************************************************/
its_updates_append(zbx_vector_ptr_t * updates,zbx_uint64_t sourceid,int status,int clock)201 static void	its_updates_append(zbx_vector_ptr_t *updates, zbx_uint64_t sourceid, int status, int clock)
202 {
203 	zbx_status_update_t	*update;
204 
205 	update = zbx_malloc(NULL, sizeof(zbx_status_update_t));
206 
207 	update->sourceid = sourceid;
208 	update->status = status;
209 	update->clock = clock;
210 
211 	zbx_vector_ptr_append(updates, update);
212 }
213 
zbx_status_update_free(zbx_status_update_t * update)214 static void	zbx_status_update_free(zbx_status_update_t *update)
215 {
216 	zbx_free(update);
217 }
218 
219 /******************************************************************************
220  *                                                                            *
221  * Function: its_itservices_load_children                                     *
222  *                                                                            *
223  * Purpose: loads all missing children of the specified services              *
224  *                                                                            *
225  * Parameters: itservices   - [IN] the IT services data                       *
226  *                                                                            *
227  ******************************************************************************/
its_itservices_load_children(zbx_itservices_t * itservices)228 static void	its_itservices_load_children(zbx_itservices_t *itservices)
229 {
230 	const char		*__function_name = "its_itservices_load_children";
231 
232 	char			*sql = NULL;
233 	size_t			sql_alloc = 0, sql_offset = 0;
234 	DB_RESULT		result;
235 	DB_ROW			row;
236 	zbx_itservice_t		*itservice, *parent;
237 	zbx_uint64_t		serviceid, parentid;
238 	zbx_vector_uint64_t	serviceids;
239 	zbx_hashset_iter_t	iter;
240 
241 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
242 
243 	zbx_vector_uint64_create(&serviceids);
244 
245 	zbx_hashset_iter_reset(&itservices->itservices, &iter);
246 
247 	while (NULL != (itservice = zbx_hashset_iter_next(&iter)))
248 	{
249 		if (0 == itservice->triggerid)
250 			zbx_vector_uint64_append(&serviceids, itservice->serviceid);
251 	}
252 
253 	/* check for extreme case when there are only leaf nodes */
254 	if (0 == serviceids.values_num)
255 		goto out;
256 
257 	zbx_vector_uint64_sort(&serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
258 
259 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
260 			"select s.serviceid,s.status,s.algorithm,sl.serviceupid"
261 			" from services s,services_links sl"
262 			" where s.serviceid=sl.servicedownid"
263 				" and");
264 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "sl.serviceupid", serviceids.values,
265 			serviceids.values_num);
266 
267 	result = DBselect("%s", sql);
268 
269 	while (NULL != (row = DBfetch(result)))
270 	{
271 		ZBX_STR2UINT64(serviceid, row[0]);
272 		ZBX_STR2UINT64(parentid, row[3]);
273 
274 		if (NULL == (parent = zbx_hashset_search(&itservices->itservices, &parentid)))
275 		{
276 			THIS_SHOULD_NEVER_HAPPEN;
277 			continue;
278 		}
279 
280 		if (NULL == (itservice = zbx_hashset_search(&itservices->itservices, &serviceid)))
281 			itservice = its_itservice_create(itservices, serviceid, 0, atoi(row[1]), atoi(row[2]));
282 
283 		if (FAIL == zbx_vector_ptr_search(&parent->children, itservice, ZBX_DEFAULT_PTR_COMPARE_FUNC))
284 			zbx_vector_ptr_append(&parent->children, itservice);
285 	}
286 	DBfree_result(result);
287 
288 	zbx_free(sql);
289 
290 	zbx_vector_uint64_destroy(&serviceids);
291 out:
292 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
293 }
294 
295 /******************************************************************************
296  *                                                                            *
297  * Function: its_itservices_load_parents                                      *
298  *                                                                            *
299  * Purpose: recursively loads parent nodes of the specified service until the *
300  *          root node                                                         *
301  *                                                                            *
302  * Parameters: itservices   - [IN] the IT services data                       *
303  *             serviceids   - [IN] a vector containing ids of services to     *
304  *                                 load parents                               *
305  *                                                                            *
306  ******************************************************************************/
its_itservices_load_parents(zbx_itservices_t * itservices,zbx_vector_uint64_t * serviceids)307 static void	its_itservices_load_parents(zbx_itservices_t *itservices, zbx_vector_uint64_t *serviceids)
308 {
309 	const char	*__function_name = "its_itservices_load_parents";
310 
311 	DB_RESULT	result;
312 	DB_ROW		row;
313 	char		*sql = NULL;
314 	size_t		sql_alloc = 0, sql_offset = 0;
315 	zbx_itservice_t	*parent, *itservice;
316 	zbx_uint64_t	parentid, serviceid;
317 
318 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
319 
320 	zbx_vector_uint64_sort(serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
321 	zbx_vector_uint64_uniq(serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
322 
323 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
324 			"select s.serviceid,s.status,s.algorithm,sl.servicedownid"
325 			" from services s,services_links sl"
326 			" where s.serviceid=sl.serviceupid"
327 				" and");
328 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "sl.servicedownid", serviceids->values,
329 			serviceids->values_num);
330 
331 	zbx_vector_uint64_clear(serviceids);
332 
333 	result = DBselect("%s", sql);
334 
335 	while (NULL != (row = DBfetch(result)))
336 	{
337 		ZBX_STR2UINT64(parentid, row[0]);
338 		ZBX_STR2UINT64(serviceid, row[3]);
339 
340 		/* find the service */
341 		if (NULL == (itservice = zbx_hashset_search(&itservices->itservices, &serviceid)))
342 		{
343 			THIS_SHOULD_NEVER_HAPPEN;
344 			continue;
345 		}
346 
347 		/* find/load the parent service */
348 		if (NULL == (parent = zbx_hashset_search(&itservices->itservices, &parentid)))
349 		{
350 			parent = its_itservice_create(itservices, parentid, 0, atoi(row[1]), atoi(row[2]));
351 			zbx_vector_uint64_append(serviceids, parent->serviceid);
352 		}
353 
354 		/* link the service as a parent's child */
355 		if (FAIL == zbx_vector_ptr_search(&itservice->parents, parent, ZBX_DEFAULT_PTR_COMPARE_FUNC))
356 			zbx_vector_ptr_append(&itservice->parents, parent);
357 	}
358 	DBfree_result(result);
359 
360 	zbx_free(sql);
361 
362 	if (0 != serviceids->values_num)
363 		its_itservices_load_parents(itservices, serviceids);
364 
365 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
366 }
367 
368 /******************************************************************************
369  *                                                                            *
370  * Function: its_load_services_by_triggerids                                  *
371  *                                                                            *
372  * Purpose: loads services that might be affected by the specified triggerid  *
373  *          or are required to calculate status of loaded services            *
374  *                                                                            *
375  * Parameters: itservices - [IN] the IT services data                         *
376  *             triggerids - [IN] the sorted list of trigger ids               *
377  *                                                                            *
378  ******************************************************************************/
its_load_services_by_triggerids(zbx_itservices_t * itservices,const zbx_vector_uint64_t * triggerids)379 static void	its_load_services_by_triggerids(zbx_itservices_t *itservices, const zbx_vector_uint64_t *triggerids)
380 {
381 	const char		*__function_name = "its_load_services_by_triggerids";
382 
383 	DB_RESULT		result;
384 	DB_ROW			row;
385 	zbx_uint64_t		serviceid, triggerid;
386 	zbx_itservice_t		*itservice;
387 	char			*sql = NULL;
388 	size_t			sql_alloc = 0, sql_offset = 0;
389 	zbx_vector_uint64_t	serviceids;
390 
391 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
392 
393 	zbx_vector_uint64_create(&serviceids);
394 
395 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
396 			"select serviceid,triggerid,status,algorithm"
397 			" from services"
398 			" where");
399 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "triggerid", triggerids->values, triggerids->values_num);
400 
401 	result = DBselect("%s", sql);
402 
403 	zbx_free(sql);
404 
405 	while (NULL != (row = DBfetch(result)))
406 	{
407 		ZBX_STR2UINT64(serviceid, row[0]);
408 		ZBX_STR2UINT64(triggerid, row[1]);
409 
410 		itservice = its_itservice_create(itservices, serviceid, triggerid, atoi(row[2]), atoi(row[3]));
411 
412 		zbx_vector_uint64_append(&serviceids, itservice->serviceid);
413 	}
414 	DBfree_result(result);
415 
416 	if (0 != serviceids.values_num)
417 	{
418 		its_itservices_load_parents(itservices, &serviceids);
419 		its_itservices_load_children(itservices);
420 	}
421 
422 	zbx_vector_uint64_destroy(&serviceids);
423 
424 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
425 }
426 
427 /******************************************************************************
428  *                                                                            *
429  * Function: its_itservice_update_status                                      *
430  *                                                                            *
431  * Purpose: updates service and its parents statuses                          *
432  *                                                                            *
433  * Parameters: service    - [IN] the service to update                        *
434  *             clock      - [IN] the update timestamp                         *
435  *             alarms     - [OUT] the alarms update queue                     *
436  *                                                                            *
437  * Comments: This function recalculates service status according to the       *
438  *           algorithm and status of the children services. If the status     *
439  *           has been changed, an alarm is generated and parent services      *
440  *           (up until the root service) are updated too.                     *
441  *                                                                            *
442  ******************************************************************************/
its_itservice_update_status(zbx_itservice_t * itservice,int clock,zbx_vector_ptr_t * alarms)443 static void	its_itservice_update_status(zbx_itservice_t *itservice, int clock, zbx_vector_ptr_t *alarms)
444 {
445 	int	status, i;
446 
447 	switch (itservice->algorithm)
448 	{
449 		case SERVICE_ALGORITHM_MIN:
450 			status = TRIGGER_SEVERITY_COUNT;
451 			for (i = 0; i < itservice->children.values_num; i++)
452 			{
453 				zbx_itservice_t	*child = (zbx_itservice_t *)itservice->children.values[i];
454 
455 				if (child->status < status)
456 					status = child->status;
457 			}
458 			break;
459 		case SERVICE_ALGORITHM_MAX:
460 			status = 0;
461 			for (i = 0; i < itservice->children.values_num; i++)
462 			{
463 				zbx_itservice_t	*child = (zbx_itservice_t *)itservice->children.values[i];
464 
465 				if (child->status > status)
466 					status = child->status;
467 			}
468 			break;
469 		case SERVICE_ALGORITHM_NONE:
470 			goto out;
471 		default:
472 			zabbix_log(LOG_LEVEL_ERR, "unknown calculation algorithm of service status [%d]",
473 					itservice->algorithm);
474 			goto out;
475 	}
476 
477 	if (itservice->status != status)
478 	{
479 		itservice->status = status;
480 
481 		its_updates_append(alarms, itservice->serviceid, status, clock);
482 
483 		/* update parent services */
484 		for (i = 0; i < itservice->parents.values_num; i++)
485 			its_itservice_update_status(itservice->parents.values[i], clock, alarms);
486 	}
487 out:
488 	;
489 }
490 
491 /******************************************************************************
492  *                                                                            *
493  * Function: its_updates_compare                                              *
494  *                                                                            *
495  * Purpose: used to sort service updates by source id                         *
496  *                                                                            *
497  ******************************************************************************/
its_updates_compare(const zbx_status_update_t ** update1,const zbx_status_update_t ** update2)498 static int	its_updates_compare(const zbx_status_update_t **update1, const zbx_status_update_t **update2)
499 {
500 	ZBX_RETURN_IF_NOT_EQUAL((*update1)->sourceid, (*update2)->sourceid);
501 
502 	return 0;
503 }
504 
505 /******************************************************************************
506  *                                                                            *
507  * Function: its_write_status_and_alarms                                      *
508  *                                                                            *
509  * Purpose: writes service status changes and generated service alarms into   *
510  *          database                                                          *
511  *                                                                            *
512  * Parameters: itservices - [IN] the IT services data                         *
513  *             alarms     - [IN] the service alarms update queue              *
514  *                                                                            *
515  * Return value: SUCCEED - the data was written successfully                  *
516  *               FAIL    - otherwise                                          *
517  *                                                                            *
518  ******************************************************************************/
its_write_status_and_alarms(zbx_itservices_t * itservices,zbx_vector_ptr_t * alarms)519 static int	its_write_status_and_alarms(zbx_itservices_t *itservices, zbx_vector_ptr_t *alarms)
520 {
521 	int			i, ret = FAIL;
522 	zbx_vector_ptr_t	updates;
523 	char			*sql = NULL;
524 	size_t			sql_alloc = 0, sql_offset = 0;
525 	zbx_uint64_t		alarmid;
526 	zbx_hashset_iter_t	iter;
527 	zbx_itservice_t		*itservice;
528 
529 	/* get a list of service status updates that must be written to database */
530 	zbx_vector_ptr_create(&updates);
531 	zbx_hashset_iter_reset(&itservices->itservices, &iter);
532 
533 	while (NULL != (itservice = zbx_hashset_iter_next(&iter)))
534 	{
535 		if (itservice->old_status != itservice->status)
536 			its_updates_append(&updates, itservice->serviceid, itservice->status, 0);
537 	}
538 
539 	/* write service status changes into database */
540 	DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
541 
542 	if (0 != updates.values_num)
543 	{
544 		zbx_vector_ptr_sort(&updates, (zbx_compare_func_t)its_updates_compare);
545 		zbx_vector_ptr_uniq(&updates, (zbx_compare_func_t)its_updates_compare);
546 
547 		for (i = 0; i < updates.values_num; i++)
548 		{
549 			zbx_status_update_t	*update = updates.values[i];
550 
551 			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
552 					"update services"
553 					" set status=%d"
554 					" where serviceid=" ZBX_FS_UI64 ";\n",
555 					update->status, update->sourceid);
556 
557 			if (SUCCEED != DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset))
558 				goto out;
559 		}
560 	}
561 
562 	DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
563 
564 	if (16 < sql_offset)
565 	{
566 		if (ZBX_DB_OK > DBexecute("%s", sql))
567 			goto out;
568 	}
569 
570 	ret = SUCCEED;
571 
572 	/* write generated service alarms into database */
573 	if (0 != alarms->values_num)
574 	{
575 		zbx_db_insert_t	db_insert;
576 
577 		alarmid = DBget_maxid_num("service_alarms", alarms->values_num);
578 
579 		zbx_db_insert_prepare(&db_insert, "service_alarms", "servicealarmid", "serviceid", "value", "clock",
580 				NULL);
581 
582 		for (i = 0; i < alarms->values_num; i++)
583 		{
584 			zbx_status_update_t	*update = alarms->values[i];
585 
586 			zbx_db_insert_add_values(&db_insert, alarmid++, update->sourceid, update->status,
587 					update->clock);
588 		}
589 
590 		ret = zbx_db_insert_execute(&db_insert);
591 
592 		zbx_db_insert_clean(&db_insert);
593 	}
594 out:
595 	zbx_free(sql);
596 
597 	zbx_vector_ptr_clear_ext(&updates, (zbx_clean_func_t)zbx_status_update_free);
598 	zbx_vector_ptr_destroy(&updates);
599 
600 	return ret;
601 }
602 
603 /******************************************************************************
604  *                                                                            *
605  * Function: its_flush_updates                                                *
606  *                                                                            *
607  * Purpose: processes the service update queue                                *
608  *                                                                            *
609  * Return value: SUCCEED - the data was written successfully                  *
610  *               FAIL    - otherwise                                          *
611  *                                                                            *
612  * Comments: The following steps are taken to process the queue:              *
613  *           1) Load all services either directly referenced (with triggerid) *
614  *              by update queue or dependent on those services (directly or   *
615  *              indirectly) or required to calculate status of any loaded     *
616  *              services.                                                     *
617  *           2) Apply updates to the loaded service tree. Queue new service   *
618  *              alarms whenever service status changes.                       *
619  *           3) Write the final service status changes and the generated      *
620  *              service alarm queue into database.                            *
621  *                                                                            *
622  ******************************************************************************/
its_flush_updates(const zbx_vector_ptr_t * updates)623 static int	its_flush_updates(const zbx_vector_ptr_t *updates)
624 {
625 	const char			*__function_name = "its_flush_updates";
626 
627 	int				i, j, k, ret = FAIL;
628 	const zbx_status_update_t	*update;
629 	zbx_itservices_t		itservices;
630 	zbx_vector_ptr_t		alarms;
631 	zbx_itservice_index_t		*index;
632 	zbx_vector_uint64_t		triggerids;
633 
634 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
635 
636 	its_itservices_init(&itservices);
637 
638 	zbx_vector_uint64_create(&triggerids);
639 
640 	for (i = 0; i < updates->values_num; i++)
641 	{
642 		update = (zbx_status_update_t *)updates->values[i];
643 
644 		zbx_vector_uint64_append(&triggerids, update->sourceid);
645 	}
646 
647 	zbx_vector_uint64_sort(&triggerids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
648 
649 	/* load all services affected by the trigger status change and      */
650 	/* the services that are required for resulting status calculations */
651 	its_load_services_by_triggerids(&itservices, &triggerids);
652 
653 	zbx_vector_uint64_destroy(&triggerids);
654 
655 	if (0 == itservices.itservices.num_data)
656 	{
657 		ret = SUCCEED;
658 		goto out;
659 	}
660 
661 	zbx_vector_ptr_create(&alarms);
662 
663 	/* apply status updates */
664 	for (i = 0; i < updates->values_num; i++)
665 	{
666 		update = updates->values[i];
667 
668 		if (NULL == (index = zbx_hashset_search(&itservices.index, update)))
669 			continue;
670 
671 		/* change the status of services based on the update */
672 		for (j = 0; j < index->itservices.values_num; j++)
673 		{
674 			zbx_itservice_t	*itservice = (zbx_itservice_t *)index->itservices.values[j];
675 
676 			if (SERVICE_ALGORITHM_NONE == itservice->algorithm || itservice->status == update->status)
677 				continue;
678 
679 			its_updates_append(&alarms, itservice->serviceid, update->status, update->clock);
680 			itservice->status = update->status;
681 		}
682 
683 		/* recalculate status of the parent services */
684 		for (j = 0; j < index->itservices.values_num; j++)
685 		{
686 			zbx_itservice_t	*itservice = (zbx_itservice_t *)index->itservices.values[j];
687 
688 			/* update parent services */
689 			for (k = 0; k < itservice->parents.values_num; k++)
690 				its_itservice_update_status(itservice->parents.values[k], update->clock, &alarms);
691 		}
692 	}
693 
694 	ret = its_write_status_and_alarms(&itservices, &alarms);
695 
696 	zbx_vector_ptr_clear_ext(&alarms, (zbx_clean_func_t)zbx_status_update_free);
697 	zbx_vector_ptr_destroy(&alarms);
698 out:
699 	its_itservices_clean(&itservices);
700 
701 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
702 
703 	return ret;
704 }
705 
706 /*
707  * Public API
708  */
709 
710 /******************************************************************************
711  *                                                                            *
712  * Function: zbx_get_itservices_updates                                       *
713  *                                                                            *
714  * Purpose: use events to generate list of updates for IT services            *
715  *                                                                            *
716  ******************************************************************************/
zbx_get_itservices_updates(const DB_EVENT * events,size_t events_num,zbx_vector_ptr_t * itservice_updates)717 void	zbx_get_itservices_updates(const DB_EVENT *events, size_t events_num, zbx_vector_ptr_t *itservice_updates)
718 {
719 	size_t	i;
720 
721 	for (i = 0; i < events_num; i++)
722 	{
723 		if (EVENT_SOURCE_TRIGGERS != events[i].source)
724 			continue;
725 
726 		its_updates_append(itservice_updates, events[i].objectid, TRIGGER_VALUE_PROBLEM == events[i].value ?
727 				events[i].trigger.priority : 0, events[i].clock);
728 	}
729 }
730 
731 /******************************************************************************
732  *                                                                            *
733  * Function: DBupdate_itservices                                              *
734  *                                                                            *
735  * Purpose: apply updates for IT service tree                                 *
736  *                                                                            *
737  * Return value: SUCCEED - the IT services were updated successfully          *
738  *               FAIL    - otherwise                                          *
739  *                                                                            *
740  ******************************************************************************/
DBupdate_itservices(const zbx_vector_ptr_t * itservice_updates)741 int	DBupdate_itservices(const zbx_vector_ptr_t *itservice_updates)
742 {
743 	const char	*__function_name = "DBupdate_itservices";
744 
745 	int		ret = SUCCEED;
746 
747 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
748 
749 	if (0 != itservice_updates->values_num)
750 	{
751 		LOCK_ITSERVICES;
752 		DBbegin();
753 
754 		ret = its_flush_updates(itservice_updates);
755 
756 		DBcommit();
757 		UNLOCK_ITSERVICES;
758 	}
759 
760 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
761 
762 	return ret;
763 }
764 
765 /******************************************************************************
766  *                                                                            *
767  * Function: DBremove_itservice_triggers                                      *
768  *                                                                            *
769  * Purpose: removes specified trigger ids from dependent services and reset   *
770  *          the status of those services to the default value (0)             *
771  *                                                                            *
772  * Parameters: triggerids     - [IN] an array of trigger ids to remove        *
773  *             triggerids_num - [IN] the number of items in triggerids array  *
774  *                                                                            *
775  * Return value: SUCCEED - the data was written successfully                  *
776  *               FAIL    - otherwise                                          *
777  *                                                                            *
778  ******************************************************************************/
DBremove_triggers_from_itservices(zbx_uint64_t * triggerids,int triggerids_num)779 int	DBremove_triggers_from_itservices(zbx_uint64_t *triggerids, int triggerids_num)
780 {
781 	char			*sql = NULL;
782 	size_t			sql_alloc = 0, sql_offset = 0;
783 	zbx_vector_ptr_t	updates;
784 	int			i, ret = FAIL, now;
785 
786 	if (0 == triggerids_num)
787 		return SUCCEED;
788 
789 	now = time(NULL);
790 
791 	zbx_vector_ptr_create(&updates);
792 
793 	for (i = 0; i < triggerids_num; i++)
794 		its_updates_append(&updates, triggerids[i], 0, now);
795 
796 	LOCK_ITSERVICES;
797 
798 	if (FAIL == its_flush_updates(&updates))
799 		goto out;
800 
801 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update services set triggerid=null,showsla=0 where");
802 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "triggerid", triggerids, triggerids_num);
803 
804 	if (ZBX_DB_OK <= DBexecute("%s", sql))
805 		ret = SUCCEED;
806 
807 	zbx_free(sql);
808 out:
809 	UNLOCK_ITSERVICES;
810 
811 	zbx_vector_ptr_clear_ext(&updates, (zbx_clean_func_t)zbx_status_update_free);
812 	zbx_vector_ptr_destroy(&updates);
813 
814 	return ret;
815 }
816 
zbx_create_itservices_lock(void)817 void	zbx_create_itservices_lock(void)
818 {
819 	if (FAIL == zbx_mutex_create_force(&itservices_lock, ZBX_MUTEX_ITSERVICES))
820 	{
821 		zbx_error("cannot create mutex for IT services");
822 		exit(EXIT_FAILURE);
823 	}
824 }
825 
zbx_destroy_itservices_lock(void)826 void	zbx_destroy_itservices_lock(void)
827 {
828 	zbx_mutex_destroy(&itservices_lock);
829 }
830