/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "service_manager.h" #include "../server.h" #include "log.h" #include "zbxself.h" #include "zbxnix.h" #include "zbxservice.h" #include "service_actions.h" #include "zbxserialize.h" #include "zbxnum.h" #include "zbxtime.h" #include "zbxexpr.h" #include "zbxcacheconfig.h" #include "zbx_trigger_constants.h" extern int CONFIG_SERVICEMAN_SYNC_FREQUENCY; /* keep deleted problem eventids up to 2 hours in case problem deletion arrived before problem or before recovery */ #define ZBX_PROBLEM_CLEANUP_AGE (SEC_PER_HOUR * 2) #define ZBX_PROBLEM_CLEANUP_FREQUENCY SEC_PER_HOUR typedef struct { zbx_uint64_t eventid; zbx_vector_ptr_t services; } zbx_service_problem_index_t; typedef struct { zbx_uint64_t service_problem_tagid; zbx_uint64_t current_eventid; zbx_service_t *service; char *tag; char *value; int op; int revision; } zbx_service_problem_tag_t; typedef struct { char *tag; zbx_hashset_t values; zbx_vector_ptr_t service_problem_tags_like; } zbx_tag_services_t; typedef struct { char *value; zbx_vector_ptr_t service_problem_tags; } zbx_values_eq_t; typedef struct { zbx_uint64_t linkid; int revision; } zbx_services_link_t; typedef struct { zbx_uint64_t serviceid; zbx_vector_ptr_t service_problems; zbx_vector_ptr_t service_problems_recovered; #define ZBX_FLAG_SERVICE_UPDATE __UINT64_C(0x00) #define ZBX_FLAG_SERVICE_RECALCULATE __UINT64_C(0x01) int flags; } zbx_services_diff_t; /* preprocessing manager data */ typedef struct { zbx_hashset_t services; zbx_hashset_t service_rules; zbx_hashset_t service_tags; zbx_hashset_t service_diffs; zbx_hashset_t service_problem_tags; zbx_hashset_t service_problem_tags_index; zbx_hashset_t services_links; zbx_hashset_t service_problems_index; zbx_hashset_t problem_events; zbx_hashset_t recovery_events; zbx_hashset_t deleted_eventids; zbx_hashset_t actions; zbx_hashset_t action_conditions; char *severities[TRIGGER_SEVERITY_COUNT]; } zbx_service_manager_t; /*#define ZBX_AVAILABILITY_MANAGER_DELAY 1*/ static void event_free(zbx_event_t *event) { zbx_vector_ptr_clear_ext(&event->tags, (zbx_clean_func_t)zbx_free_tag); zbx_vector_ptr_destroy(&event->tags); zbx_free(event); } static void event_ptr_free(zbx_event_t **event) { event_free(*event); } static zbx_hash_t default_uint64_ptr_hash_func(const void *d) { return ZBX_DEFAULT_UINT64_HASH_FUNC(*(const zbx_uint64_t * const *)d); } static void match_event_to_service_problem_tags(zbx_event_t *event, zbx_hashset_t *service_problem_tags_index, zbx_hashset_t *services_diffs, int flags) { int i, j; zbx_vector_ptr_t candidates; zbx_vector_ptr_create(&candidates); for (i = 0; i < event->tags.values_num; i++) { zbx_tag_services_t tag_services_local, *tag_services; const zbx_tag_t *tag = (const zbx_tag_t *)event->tags.values[i]; tag_services_local.tag = tag->tag; if (NULL != (tag_services = (zbx_tag_services_t *)zbx_hashset_search(service_problem_tags_index, &tag_services_local))) { zbx_values_eq_t values_eq_local = {.value = tag->value}, *values_eq; if (NULL != (values_eq = (zbx_values_eq_t *)zbx_hashset_search(&tag_services->values, &values_eq_local))) { for (j = 0; j < values_eq->service_problem_tags.values_num; j++) { zbx_service_problem_tag_t *service_problem_tag; service_problem_tag = (zbx_service_problem_tag_t *)values_eq->service_problem_tags.values[j]; service_problem_tag->current_eventid = event->eventid; zbx_vector_ptr_append(&candidates, service_problem_tag->service); } } for (j = 0; j < tag_services->service_problem_tags_like.values_num; j++) { zbx_service_problem_tag_t *service_problem_tag; service_problem_tag = (zbx_service_problem_tag_t *)tag_services->service_problem_tags_like.values[j]; if (NULL == strstr(tag->value, service_problem_tag->value)) continue; service_problem_tag->current_eventid = event->eventid; zbx_vector_ptr_append(&candidates, service_problem_tag->service); } } } zbx_vector_ptr_sort(&candidates, ZBX_DEFAULT_PTR_COMPARE_FUNC); zbx_vector_ptr_uniq(&candidates, ZBX_DEFAULT_PTR_COMPARE_FUNC); for (i = 0; i < candidates.values_num; i++) { zbx_service_t *service = (zbx_service_t *)candidates.values[i]; for (j = 0; j < service->service_problem_tags.values_num; j++) { zbx_service_problem_tag_t *service_problem_tag; service_problem_tag = (zbx_service_problem_tag_t *)service->service_problem_tags.values[j]; if (event->eventid != service_problem_tag->current_eventid) break; } if (j == service->service_problem_tags.values_num) { zbx_services_diff_t services_diff_local = {.serviceid = service->serviceid}, *services_diff; zbx_service_problem_t *service_problem; if (NULL == (services_diff = zbx_hashset_search(services_diffs, &services_diff_local))) { zbx_vector_ptr_create(&services_diff_local.service_problems); zbx_vector_ptr_create(&services_diff_local.service_problems_recovered); services_diff_local.flags = flags; services_diff = zbx_hashset_insert(services_diffs, &services_diff_local, sizeof(services_diff_local)); } service_problem = zbx_malloc(NULL, sizeof(zbx_service_problem_t)); service_problem->eventid = event->eventid; service_problem->service_problemid = 0; service_problem->serviceid = services_diff->serviceid; service_problem->severity = event->severity; service_problem->ts.sec = event->clock; service_problem->ts.ns = event->ns; zbx_vector_ptr_append(&services_diff->service_problems, service_problem); } } zbx_vector_ptr_destroy(&candidates); } static void db_get_events(zbx_hashset_t *problem_events) { DB_RESULT result; zbx_event_t *event = NULL; DB_ROW row; zbx_uint64_t eventid; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); result = zbx_db_select("select p.eventid,p.clock,p.severity,t.tag,t.value,p.ns" " from problem p" " left join problem_tag t" " on p.eventid=t.eventid" " where p.source=%d" " and p.object=%d" " and r_eventid is null" " order by p.eventid", EVENT_SOURCE_TRIGGERS, EVENT_OBJECT_TRIGGER); while (NULL != (row = zbx_db_fetch(result))) { ZBX_STR2UINT64(eventid, row[0]); if (NULL == event || eventid != event->eventid) { event = (zbx_event_t *)zbx_malloc(NULL, sizeof(zbx_event_t)); event->eventid = eventid; event->clock = atoi(row[1]); event->ns = atoi(row[5]); event->value = TRIGGER_VALUE_PROBLEM; event->severity = atoi(row[2]); zbx_vector_ptr_create(&event->tags); zbx_hashset_insert(problem_events, &event, sizeof(zbx_event_t *)); } if (FAIL == zbx_db_is_null(row[3])) { zbx_tag_t *tag; tag = (zbx_tag_t *)zbx_malloc(NULL, sizeof(zbx_tag_t)); tag->tag = zbx_strdup(NULL, row[3]); tag->value = zbx_strdup(NULL, row[4]); zbx_vector_ptr_append(&event->tags, tag); } } zbx_db_free_result(result); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void add_service_problem(zbx_service_t *service, zbx_hashset_t *service_problems_index, zbx_service_problem_t *service_problem) { zbx_service_problem_index_t *service_problem_index, service_problem_index_local; service_problem_index_local.eventid = service_problem->eventid; if (NULL == (service_problem_index = zbx_hashset_search(service_problems_index, &service_problem_index_local))) { zbx_vector_ptr_create(&service_problem_index_local.services); service_problem_index = zbx_hashset_insert(service_problems_index, &service_problem_index_local, sizeof(service_problem_index_local)); } zbx_vector_ptr_append(&service_problem_index->services, service); zbx_vector_ptr_append(&service->service_problems, service_problem); } static void remove_service_problem(zbx_service_t *service, int index, zbx_hashset_t *service_problems_index) { zbx_service_problem_index_t *service_problem_index, service_problem_index_local; int i; zbx_service_problem_t *service_problem; service_problem = (zbx_service_problem_t *)service->service_problems.values[index]; service_problem_index_local.eventid = service_problem->eventid; if (NULL == (service_problem_index = zbx_hashset_search(service_problems_index, &service_problem_index_local))) { THIS_SHOULD_NEVER_HAPPEN; } else { if (FAIL == (i = zbx_vector_ptr_search(&service_problem_index->services, service, ZBX_DEFAULT_PTR_COMPARE_FUNC))) { THIS_SHOULD_NEVER_HAPPEN; } else { zbx_vector_ptr_remove_noorder(&service_problem_index->services, i); if (0 == service_problem_index->services.values_num) zbx_hashset_remove_direct(service_problems_index, service_problem_index); } } zbx_vector_ptr_remove_noorder(&service->service_problems, index); zbx_free(service_problem); } static zbx_hash_t values_eq_hash(const void *data) { const zbx_values_eq_t *d = (const zbx_values_eq_t *)data; return ZBX_DEFAULT_STRING_HASH_ALGO(d->value, strlen(d->value), ZBX_DEFAULT_HASH_SEED); } static int values_eq_compare(const void *d1, const void *d2) { return strcmp(((const zbx_values_eq_t *)d1)->value, ((const zbx_values_eq_t *)d2)->value); } static void values_eq_clean(void *data) { zbx_values_eq_t *d = (zbx_values_eq_t *)data; zbx_vector_ptr_destroy(&d->service_problem_tags); zbx_free(d->value); } static void add_service_problem_tag_index(zbx_hashset_t *service_problem_tags_index, zbx_service_problem_tag_t *service_problem_tag) { /* service problem tag operators */ #define ZBX_SERVICE_TAG_OPERATOR_LIKE 2 zbx_tag_services_t tag_services_local, *tag_services; zbx_values_eq_t value_eq_local, *value_eq; tag_services_local.tag = service_problem_tag->tag; if (NULL == (tag_services = zbx_hashset_search(service_problem_tags_index, &tag_services_local))) { tag_services_local.tag = zbx_strdup(NULL, service_problem_tag->tag); zbx_hashset_create_ext(&tag_services_local.values, 1, values_eq_hash, values_eq_compare, values_eq_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_vector_ptr_create(&tag_services_local.service_problem_tags_like); tag_services = zbx_hashset_insert(service_problem_tags_index, &tag_services_local, sizeof(tag_services_local)); } if (ZBX_SERVICE_TAG_OPERATOR_LIKE == service_problem_tag->op) { zbx_vector_ptr_append(&tag_services->service_problem_tags_like, service_problem_tag); } else { /* add value to index */ value_eq_local.value = service_problem_tag->value; if (NULL == (value_eq = zbx_hashset_search(&tag_services->values, &value_eq_local))) { value_eq_local.value = zbx_strdup(NULL, service_problem_tag->value); zbx_vector_ptr_create(&value_eq_local.service_problem_tags); value_eq = zbx_hashset_insert(&tag_services->values, &value_eq_local, sizeof(value_eq_local)); } zbx_vector_ptr_append(&value_eq->service_problem_tags, service_problem_tag); } } static void remove_service_problem_tag_index(zbx_hashset_t *service_problem_tags_index, zbx_service_problem_tag_t *service_problem_tag) { zbx_tag_services_t tag_services_local, *tag_services; zbx_values_eq_t value_eq_local, *value_eq; int i; tag_services_local.tag = service_problem_tag->tag; if (NULL == (tag_services = zbx_hashset_search(service_problem_tags_index, &tag_services_local))) { THIS_SHOULD_NEVER_HAPPEN; } else { if (ZBX_SERVICE_TAG_OPERATOR_LIKE == service_problem_tag->op) { i = zbx_vector_ptr_search(&tag_services->service_problem_tags_like, service_problem_tag, ZBX_DEFAULT_PTR_COMPARE_FUNC); if (FAIL == i) { THIS_SHOULD_NEVER_HAPPEN; } else zbx_vector_ptr_remove_noorder(&tag_services->service_problem_tags_like, i); } else { value_eq_local.value = service_problem_tag->value; if (NULL != (value_eq = zbx_hashset_search(&tag_services->values, &value_eq_local))) { i = zbx_vector_ptr_search(&value_eq->service_problem_tags, service_problem_tag, ZBX_DEFAULT_PTR_COMPARE_FUNC); if (FAIL == i) { THIS_SHOULD_NEVER_HAPPEN; } else { zbx_vector_ptr_remove_noorder(&value_eq->service_problem_tags, i); if (0 == value_eq->service_problem_tags.values_num) zbx_hashset_remove_direct(&tag_services->values, value_eq); } } } if (0 == tag_services->values.num_data && 0 == tag_services->service_problem_tags_like.values_num) zbx_hashset_remove_direct(service_problem_tags_index, tag_services); } #undef ZBX_SERVICE_TAG_OPERATOR_LIKE } static void sync_service_problem_tags(zbx_service_manager_t *service_manager, int *updated, int revision) { DB_RESULT result; DB_ROW row; zbx_service_problem_tag_t service_problem_tag_local, *service_problem_tag; zbx_hashset_iter_t iter; zbx_service_t *service = NULL; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); result = zbx_db_select("select service_problem_tagid,serviceid,tag,operator,value" " from service_problem_tag" " order by serviceid"); while (NULL != (row = zbx_db_fetch(result))) { zbx_uint64_t serviceid; unsigned char op; ZBX_STR2UINT64(service_problem_tag_local.service_problem_tagid, row[0]); ZBX_STR2UINT64(serviceid, row[1]); if (NULL == (service_problem_tag = zbx_hashset_search(&service_manager->service_problem_tags, &service_problem_tag_local))) { if (NULL == service || serviceid != service->serviceid) { if (NULL == (service = zbx_hashset_search(&service_manager->services, &serviceid))) { THIS_SHOULD_NEVER_HAPPEN; continue; } } service_problem_tag_local.revision = revision; service_problem_tag_local.current_eventid = 0; service_problem_tag_local.tag = zbx_strdup(NULL, row[2]); service_problem_tag_local.op = atoi(row[3]); service_problem_tag_local.value = zbx_strdup(NULL, row[4]); service_problem_tag_local.service = service; service_problem_tag = zbx_hashset_insert(&service_manager->service_problem_tags, &service_problem_tag_local, sizeof(service_problem_tag_local)); zbx_vector_ptr_append(&service_problem_tag_local.service->service_problem_tags, service_problem_tag); add_service_problem_tag_index(&service_manager->service_problem_tags_index, service_problem_tag); (*updated)++; continue; } service_problem_tag->revision = revision; service_problem_tag->current_eventid = 0; op = (unsigned char)atoi(row[3]); if (0 != strcmp(service_problem_tag->tag, row[2]) || service_problem_tag->op != op || 0 != strcmp(service_problem_tag->value, row[4])) { remove_service_problem_tag_index(&service_manager->service_problem_tags_index, service_problem_tag); (*updated)++; service_problem_tag->tag = zbx_strdup(service_problem_tag->tag, row[2]); service_problem_tag->op = op; service_problem_tag->value = zbx_strdup(service_problem_tag->value, row[4]); add_service_problem_tag_index(&service_manager->service_problem_tags_index, service_problem_tag); } } zbx_db_free_result(result); zbx_hashset_iter_reset(&service_manager->service_problem_tags, &iter); while (NULL != (service_problem_tag = (zbx_service_problem_tag_t *)zbx_hashset_iter_next(&iter))) { int i; if (revision == service_problem_tag->revision) continue; remove_service_problem_tag_index(&service_manager->service_problem_tags_index, service_problem_tag); i = zbx_vector_ptr_search(&service_problem_tag->service->service_problem_tags, service_problem_tag, ZBX_DEFAULT_PTR_COMPARE_FUNC); if (FAIL == i) THIS_SHOULD_NEVER_HAPPEN; else zbx_vector_ptr_remove_noorder(&service_problem_tag->service->service_problem_tags, i); (*updated)++; zbx_hashset_iter_remove(&iter); } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void sync_services(zbx_service_manager_t *service_manager, int *updated, int revision) { DB_RESULT result; DB_ROW row; zbx_service_t service_local, *service; zbx_hashset_iter_t iter; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); result = zbx_db_select("select serviceid,status,algorithm,name,weight,propagation_rule,propagation_value" " from services"); while (NULL != (row = zbx_db_fetch(result))) { int update = 0; ZBX_STR2UINT64(service_local.serviceid, row[0]); service_local.status = atoi(row[1]); service_local.algorithm = atoi(row[2]); service_local.weight = atoi(row[4]); service_local.propagation_rule = atoi(row[5]); service_local.propagation_value = atoi(row[6]); if (NULL == (service = zbx_hashset_search(&service_manager->services, &service_local))) { zbx_vector_ptr_create(&service_local.tags); zbx_vector_ptr_create(&service_local.children); zbx_vector_ptr_create(&service_local.parents); zbx_vector_ptr_create(&service_local.service_problem_tags); zbx_vector_ptr_create(&service_local.service_problems); zbx_vector_ptr_create(&service_local.status_rules); service_local.name = zbx_strdup(NULL, row[3]); service = zbx_hashset_insert(&service_manager->services, &service_local, sizeof(service_local)); update = 1; } else { zbx_vector_ptr_clear(&service->children); zbx_vector_ptr_clear(&service->parents); zbx_vector_ptr_clear(&service->status_rules); if (service->status != service_local.status) { service->status = service_local.status; update = 1; } if (service->algorithm != service_local.algorithm) { service->algorithm = service_local.algorithm; update = 1; } if (service->propagation_rule != service_local.propagation_rule) { service->propagation_rule = service_local.propagation_rule; update = 1; } if (service->propagation_value != service_local.propagation_value) { service->propagation_value = service_local.propagation_value; update = 1; } if (service->weight != service_local.weight) { service->weight = service_local.weight; update = 1; } } service->revision = revision; if (0 != update) (*updated)++; } zbx_db_free_result(result); zbx_hashset_iter_reset(&service_manager->services, &iter); while (NULL != (service = (zbx_service_t *)zbx_hashset_iter_next(&iter))) { int i; if (revision == service->revision) continue; for (i = 0; i < service->service_problem_tags.values_num; i++) { zbx_service_problem_tag_t *service_problem_tag; service_problem_tag = (zbx_service_problem_tag_t *)service->service_problem_tags.values[i]; remove_service_problem_tag_index(&service_manager->service_problem_tags_index, service_problem_tag); zbx_hashset_remove_direct(&service_manager->service_problem_tags, service_problem_tag); } for (i = 0; i < service->service_problems.values_num; i++) { remove_service_problem(service, i, &service_manager->service_problems_index); i--; } zbx_hashset_iter_remove(&iter); (*updated)++; } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void sync_service_rules(zbx_service_manager_t *service_manager, int *updated, int revision) { DB_RESULT result; DB_ROW row; zbx_service_t service_local, *service = NULL; zbx_service_rule_t rule_local, *rule; zbx_hashset_iter_t iter; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); result = zbx_db_select("select service_status_ruleid,serviceid,type,limit_value,limit_status,new_status" " from service_status_rule" " order by serviceid"); while (NULL != (row = zbx_db_fetch(result))) { int update = 0; ZBX_STR2UINT64(service_local.serviceid, row[1]); if (NULL == service || service->serviceid != service_local.serviceid) { if (NULL == (service = zbx_hashset_search(&service_manager->services, &service_local))) continue; } ZBX_STR2UINT64(rule_local.service_ruleid, row[0]); rule_local.type = atoi(row[2]); rule_local.limit_value = atoi(row[3]); rule_local.limit_status = atoi(row[4]); rule_local.new_status = atoi(row[5]); if (NULL == (rule = zbx_hashset_search(&service_manager->service_rules, &rule_local))) { rule = zbx_hashset_insert(&service_manager->service_rules, &rule_local, sizeof(rule_local)); update = 1; } else { if (rule->type != rule_local.type) { rule->type = rule_local.type; update = 1; } if (rule->limit_value != rule_local.limit_value) { rule->limit_value = rule_local.limit_value; update = 1; } if (rule->limit_status != rule_local.limit_status) { rule->limit_status = rule_local.limit_status; update = 1; } if (rule->new_status != rule_local.new_status) { rule->new_status = rule_local.new_status; update = 1; } } rule->revision = revision; zbx_vector_ptr_append(&service->status_rules, rule); if (0 != update) (*updated)++; } zbx_db_free_result(result); zbx_hashset_iter_reset(&service_manager->service_rules, &iter); while (NULL != (rule = (zbx_service_rule_t *)zbx_hashset_iter_next(&iter))) { if (revision == rule->revision) continue; zbx_hashset_iter_remove(&iter); (*updated)++; } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void sync_service_tags(zbx_service_manager_t *service_manager, int revision) { DB_RESULT result; DB_ROW row; zbx_service_t service_local, *service; zbx_service_tag_t service_tag_local, *service_tag; zbx_hashset_iter_t iter; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); result = zbx_db_select("select servicetagid,serviceid,tag,value from service_tag"); while (NULL != (row = zbx_db_fetch(result))) { ZBX_STR2UINT64(service_local.serviceid, row[1]); if (NULL == (service = zbx_hashset_search(&service_manager->services, &service_local))) continue; ZBX_STR2UINT64(service_tag_local.servicetagid, row[0]); if (NULL == (service_tag = zbx_hashset_search(&service_manager->service_tags, &service_tag_local))) { service_tag_local.name = zbx_strdup(NULL, row[2]); service_tag_local.value = zbx_strdup(NULL, row[3]); service_tag_local.serviceid = service_local.serviceid; service_tag = zbx_hashset_insert(&service_manager->service_tags, &service_tag_local, sizeof(service_tag_local)); zbx_vector_ptr_append(&service->tags, service_tag); } service_tag->revision = revision; } zbx_db_free_result(result); zbx_hashset_iter_reset(&service_manager->service_tags, &iter); while (NULL != (service_tag = (zbx_service_tag_t *)zbx_hashset_iter_next(&iter))) { int index; if (revision == service_tag->revision) continue; if (NULL != (service = zbx_hashset_search(&service_manager->services, &service_tag->serviceid)) && FAIL != (index = zbx_vector_ptr_search(&service->tags, service_tag, ZBX_DEFAULT_PTR_COMPARE_FUNC))) { zbx_vector_ptr_remove_noorder(&service->tags, index); } zbx_hashset_iter_remove(&iter); } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void sync_services_links(zbx_service_manager_t *service_manager, int *updated, int revision) { DB_RESULT result; DB_ROW row; zbx_hashset_iter_t iter; zbx_services_link_t services_link_local, *services_link; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); result = zbx_db_select("select linkid,serviceupid,servicedownid from services_links"); while (NULL != (row = zbx_db_fetch(result))) { zbx_service_t *parent, *child; zbx_uint64_t serviceupid; zbx_uint64_t servicedownid; ZBX_STR2UINT64(services_link_local.linkid, row[0]); ZBX_STR2UINT64(serviceupid, row[1]); ZBX_STR2UINT64(servicedownid, row[2]); if (NULL == (services_link = zbx_hashset_search(&service_manager->services_links, &services_link_local))) { (*updated)++; services_link = zbx_hashset_insert(&service_manager->services_links, &services_link_local, sizeof(services_link_local)); } services_link->revision = revision; parent = zbx_hashset_search(&service_manager->services, &serviceupid); child = zbx_hashset_search(&service_manager->services, &servicedownid); if (NULL == parent || NULL == child) { /* it is not possible for link to exist without corresponding service */ THIS_SHOULD_NEVER_HAPPEN; continue; } zbx_vector_ptr_append(&parent->children, child); zbx_vector_ptr_append(&child->parents, parent); } zbx_db_free_result(result); zbx_hashset_iter_reset(&service_manager->services_links, &iter); while (NULL != (services_link = (zbx_services_link_t *)zbx_hashset_iter_next(&iter))) { if (revision == services_link->revision) continue; zbx_hashset_iter_remove(&iter); (*updated)++; } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void sync_service_problems(zbx_hashset_t *services, zbx_hashset_t *service_problems_index) { DB_RESULT result; DB_ROW row; zbx_service_t service_local, *service; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); result = zbx_db_select("select service_problemid,eventid,serviceid,severity from service_problem"); while (NULL != (row = zbx_db_fetch(result))) { zbx_service_problem_t *service_problem; ZBX_STR2UINT64(service_local.serviceid, row[2]); if (NULL == (service = zbx_hashset_search(services, &service_local))) { THIS_SHOULD_NEVER_HAPPEN; continue; } service_problem = zbx_malloc(NULL, sizeof(zbx_service_problem_t)); ZBX_STR2UINT64(service_problem->service_problemid, row[0]); ZBX_STR2UINT64(service_problem->eventid, row[1]); service_problem->serviceid = service_local.serviceid; service_problem->severity = atoi(row[3]); service_problem->ts.sec = 0; service_problem->ts.ns = 0; add_service_problem(service, service_problems_index, service_problem); } zbx_db_free_result(result); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void sync_actions(zbx_service_manager_t *service_manager, int revision) { DB_RESULT result; DB_ROW row; zbx_service_action_t action_local, *action; zbx_hashset_iter_t iter; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); result = zbx_db_select("select actionid,evaltype,formula from actions " "where eventsource=%d" " and status=%d", EVENT_SOURCE_SERVICE, ACTION_STATUS_ACTIVE); while (NULL != (row = zbx_db_fetch(result))) { ZBX_STR2UINT64(action_local.actionid, row[0]); if (NULL == (action = zbx_hashset_search(&service_manager->actions, &action_local))) { action_local.formula = zbx_strdup(NULL, row[2]); zbx_vector_ptr_create(&action_local.conditions); action = zbx_hashset_insert(&service_manager->actions, &action_local, sizeof(action_local)); } else { if (0 != strcmp(action->formula, row[2])) action->formula = zbx_strdup(action->formula, row[2]); } ZBX_STR2UCHAR(action->evaltype, row[1]); action->revision = revision; } zbx_db_free_result(result); zbx_hashset_iter_reset(&service_manager->actions, &iter); while (NULL != (action = (zbx_service_action_t *)zbx_hashset_iter_next(&iter))) { if (revision == action->revision) continue; zbx_hashset_iter_remove(&iter); } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static int condition_type_compare(const void *d1, const void *d2) { const zbx_service_action_condition_t *c1 = *(const zbx_service_action_condition_t * const *)d1; const zbx_service_action_condition_t *c2 = *(const zbx_service_action_condition_t * const *)d2; ZBX_RETURN_IF_NOT_EQUAL(c1->conditiontype, c2->conditiontype); return 0; } static void update_action_formula(zbx_service_action_t *action) { #define ZBX_CONDITION_TYPE_NONE 255 char *formula = NULL; size_t formula_alloc = 0, formula_offset = 0; int i; zbx_service_action_condition_t *condition; unsigned char last_type = ZBX_CONDITION_TYPE_NONE; char *ops[] = {NULL, "and", "or"}; zabbix_log(LOG_LEVEL_DEBUG, "In %s() actionid:" ZBX_FS_UI64, __func__, action->actionid); if (0 == action->conditions.values_num || ZBX_CONDITION_EVAL_TYPE_EXPRESSION == action->evaltype) goto out; for (i = 0; i < action->conditions.values_num; i++) { condition = (zbx_service_action_condition_t *)action->conditions.values[i]; if (ZBX_CONDITION_EVAL_TYPE_AND_OR == action->evaltype) { if (last_type != condition->conditiontype) { if (ZBX_CONDITION_TYPE_NONE != last_type) zbx_strcpy_alloc(&formula, &formula_alloc, &formula_offset, ") and "); zbx_chrcpy_alloc(&formula, &formula_alloc, &formula_offset, '('); } else zbx_strcpy_alloc(&formula, &formula_alloc, &formula_offset, " or "); } else { if (ZBX_CONDITION_TYPE_NONE != last_type) { zbx_chrcpy_alloc(&formula, &formula_alloc, &formula_offset, ' '); zbx_strcpy_alloc(&formula, &formula_alloc, &formula_offset, ops[action->evaltype]); zbx_chrcpy_alloc(&formula, &formula_alloc, &formula_offset, ' '); } } zbx_snprintf_alloc(&formula, &formula_alloc, &formula_offset, "{" ZBX_FS_UI64 "}", condition->conditionid); last_type = condition->conditiontype; } if (ZBX_CONDITION_EVAL_TYPE_AND_OR == action->evaltype) zbx_chrcpy_alloc(&formula, &formula_alloc, &formula_offset, ')'); zbx_free(action->formula); action->formula = formula; out: zabbix_log(LOG_LEVEL_DEBUG, "End of %s() formula:%s", __func__, action->formula); #undef ZBX_CONDITION_TYPE_NONE } static void sync_action_conditions(zbx_service_manager_t *service_manager, int revision) { DB_RESULT result; DB_ROW row; zbx_service_action_t action_local, *action; zbx_service_action_condition_t action_condition_local, *action_condition; zbx_hashset_iter_t iter; zbx_vector_ptr_t actions; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_vector_ptr_create(&actions); result = zbx_db_select("select c.conditionid,c.actionid,c.conditiontype,c.operator,c.value,c.value2" " from conditions c,actions a" " where c.actionid=a.actionid" " and a.eventsource=%d" " and a.status=%d", EVENT_SOURCE_SERVICE, ACTION_STATUS_ACTIVE); while (NULL != (row = zbx_db_fetch(result))) { ZBX_STR2UINT64(action_local.actionid, row[1]); if (NULL == (action = zbx_hashset_search(&service_manager->actions, &action_local))) continue; ZBX_STR2UINT64(action_condition_local.conditionid, row[0]); if (NULL == (action_condition = zbx_hashset_search(&service_manager->action_conditions, &action_condition_local))) { action_condition_local.actionid = action_local.actionid; ZBX_STR2UCHAR(action_condition_local.conditiontype, row[2]); ZBX_STR2UCHAR(action_condition_local.op, row[3]); action_condition_local.value = zbx_strdup(NULL, row[4]); action_condition_local.value2 = zbx_strdup(NULL, row[5]); action_condition = zbx_hashset_insert(&service_manager->action_conditions, &action_condition_local, sizeof(action_condition_local)); zbx_vector_ptr_append(&action->conditions, action_condition); } action_condition->revision = revision; zbx_vector_ptr_append(&actions, action); } zbx_db_free_result(result); zbx_hashset_iter_reset(&service_manager->action_conditions, &iter); while (NULL != (action_condition = (zbx_service_action_condition_t *)zbx_hashset_iter_next(&iter))) { int index; if (revision == action_condition->revision) continue; if (NULL != (action = zbx_hashset_search(&service_manager->actions, &action_condition->actionid))) { if (FAIL != (index = zbx_vector_ptr_search(&action->conditions, action_condition, ZBX_DEFAULT_PTR_COMPARE_FUNC))) { zbx_vector_ptr_remove_noorder(&action->conditions, index); } zbx_vector_ptr_append(&actions, action); } zbx_hashset_iter_remove(&iter); } if (0 != actions.values_num) { int i; zbx_vector_ptr_sort(&actions, ZBX_DEFAULT_PTR_COMPARE_FUNC); zbx_vector_ptr_uniq(&actions, ZBX_DEFAULT_PTR_COMPARE_FUNC); for (i = 0; i < actions.values_num; i++) { action = (zbx_service_action_t *)actions.values[i]; zbx_vector_ptr_sort(&action->conditions, condition_type_compare); update_action_formula(action); } } zbx_vector_ptr_destroy(&actions); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void sync_config(zbx_service_manager_t *service_manager) { DB_ROW row; DB_RESULT result; int i; result = zbx_db_select("select severity_name_0,severity_name_1,severity_name_2,severity_name_3,severity_name_4," "severity_name_5 from config"); if (NULL != (row = zbx_db_fetch(result))) { for (i = 0; i < TRIGGER_SEVERITY_COUNT; i++) service_manager->severities[i] = zbx_strdup(service_manager->severities[i], row[i]); } else { const ZBX_TABLE *table; char field[16]; table = zbx_db_get_table("config"); for (i = 0; i < TRIGGER_SEVERITY_COUNT; i++) { zbx_snprintf(field, sizeof(field), "severity_name_%d", i); service_manager->severities[i] = zbx_strdup(service_manager->severities[i], zbx_db_get_field(table, field)->default_value); } } zbx_db_free_result(result); } static void service_clean(zbx_service_t *service) { zbx_free(service->name); zbx_vector_ptr_destroy(&service->status_rules); zbx_vector_ptr_destroy(&service->tags); zbx_vector_ptr_destroy(&service->children); zbx_vector_ptr_destroy(&service->parents); zbx_vector_ptr_destroy(&service->service_problem_tags); zbx_vector_ptr_clear_ext(&service->service_problems, zbx_ptr_free); zbx_vector_ptr_destroy(&service->service_problems); } static void service_tag_clean(zbx_service_tag_t *tag) { zbx_free(tag->name); zbx_free(tag->value); } static void service_problem_tag_clean(zbx_service_problem_tag_t *service_problem_tag) { zbx_free(service_problem_tag->tag); zbx_free(service_problem_tag->value); } static void service_diff_clean(void *data) { zbx_services_diff_t *d = (zbx_services_diff_t *)data; zbx_vector_ptr_clear_ext(&d->service_problems_recovered, zbx_ptr_free); zbx_vector_ptr_clear_ext(&d->service_problems, zbx_ptr_free); zbx_vector_ptr_destroy(&d->service_problems); zbx_vector_ptr_destroy(&d->service_problems_recovered); } static void service_action_clean(zbx_service_action_t *action) { zbx_free(action->formula); zbx_vector_ptr_destroy(&action->conditions); } static void service_action_condition_clean(zbx_service_action_condition_t *condition) { zbx_free(condition->value); zbx_free(condition->value2); } static zbx_hash_t tag_services_hash(const void *data) { const zbx_tag_services_t *d = (const zbx_tag_services_t *)data; return ZBX_DEFAULT_STRING_HASH_ALGO(d->tag, strlen(d->tag), ZBX_DEFAULT_HASH_SEED); } static int tag_services_compare(const void *d1, const void *d2) { return strcmp(((const zbx_tag_services_t *)d1)->tag, ((const zbx_tag_services_t *)d2)->tag); } static void tag_services_clean(void *data) { zbx_tag_services_t *d = (zbx_tag_services_t *)data; zbx_vector_ptr_destroy(&d->service_problem_tags_like); zbx_hashset_destroy(&d->values); zbx_free(d->tag); } static void service_problems_index_clean(void *data) { zbx_service_problem_index_t *d = (zbx_service_problem_index_t *)data; zbx_vector_ptr_destroy(&d->services); } /****************************************************************************** * * * Purpose: get service status when calculating parent service status * * * * Parameters: service - [IN] the service * * status - [OUT] the service status * * * * Return value: SUCCEED - the status is returned * * FAIL - the service must be ignored * * * ******************************************************************************/ int service_get_status(const zbx_service_t *service, int *status) { if (ZBX_SERVICE_STATUS_PROPAGATION_IGNORE == service->propagation_rule) return FAIL; if (ZBX_SERVICE_STATUS_OK == service->status) { *status = ZBX_SERVICE_STATUS_OK; return SUCCEED; } switch (service->propagation_rule) { case ZBX_SERVICE_STATUS_PROPAGATION_AS_IS: *status = service->status; break; case ZBX_SERVICE_STATUS_PROPAGATION_INCREASE: *status = service->status + service->propagation_value; if (TRIGGER_SEVERITY_COUNT <= *status) *status = TRIGGER_SEVERITY_COUNT - 1; break; case ZBX_SERVICE_STATUS_PROPAGATION_DECREASE: *status = service->status - service->propagation_value; if (ZBX_SERVICE_STATUS_OK >= *status) *status = ZBX_SERVICE_STATUS_OK + 1; break; case ZBX_SERVICE_STATUS_PROPAGATION_FIXED: *status = service->propagation_value; break; default: THIS_SHOULD_NEVER_HAPPEN; *status = ZBX_SERVICE_STATUS_OK; break; } return SUCCEED; } /****************************************************************************** * * * Purpose: adds an update to the queue * * * * Parameters: updates - [OUT] the update queue * * sourceid - [IN] the update source id * * status - [IN] the update status * * clock - [IN] the update timestamp * * * * Return value: The created status update. * * * ******************************************************************************/ static zbx_status_update_t *its_updates_append(zbx_vector_ptr_t *updates, zbx_uint64_t sourceid, int status, int clock) { zbx_status_update_t *update; update = (zbx_status_update_t *)zbx_malloc(NULL, sizeof(zbx_status_update_t)); update->sourceid = sourceid; update->status = status; update->clock = clock; zbx_vector_ptr_append(updates, update); return update; } static zbx_service_update_t *update_service(zbx_hashset_t *service_updates, zbx_service_t *service, int status, const zbx_timespec_t *ts) { zbx_service_update_t update_local = {.service = service}, *update; if (NULL == (update = (zbx_service_update_t *)zbx_hashset_search(service_updates, &update_local))) { update_local.old_status = service->status; update = (zbx_service_update_t *)zbx_hashset_insert(service_updates, &update_local, sizeof(update_local)); } update->ts = *ts; service->status = status; return update; } /****************************************************************************** * * * Purpose: used to sort service updates by source id * * * ******************************************************************************/ static int its_updates_compare(const zbx_status_update_t **update1, const zbx_status_update_t **update2) { ZBX_RETURN_IF_NOT_EQUAL((*update1)->sourceid, (*update2)->sourceid); return 0; } /****************************************************************************** * * * Purpose: writes service status changes, generated service alarms, service * * problem changes into database * * * * Parameters: alarms - [IN] the service alarms update queue * * service_updates - [IN] the service status updates * * service_problems_new - [IN] the service problems * * service_problemids - [IN] the service problems to delete * * * * Return value: SUCCEED - the data was written successfully * * FAIL - otherwise * * * ******************************************************************************/ static int its_write_status_and_alarms(zbx_vector_ptr_t *alarms, zbx_hashset_t *service_updates, zbx_vector_ptr_t *service_problems_new, zbx_vector_uint64_t *service_problemids) { int i, ret = FAIL; zbx_vector_ptr_t updates; char *sql = NULL; size_t sql_alloc = 0, sql_offset = 0; zbx_uint64_t alarmid; zbx_hashset_iter_t iter; zbx_service_update_t *update; zbx_vector_uint64_t serviceids; /* get a list of service status updates that must be written to database */ zbx_vector_ptr_create(&updates); zbx_vector_uint64_create(&serviceids); zbx_hashset_iter_reset(service_updates, &iter); while (NULL != (update = (zbx_service_update_t *)zbx_hashset_iter_next(&iter))) { if (update->old_status != update->service->status) its_updates_append(&updates, update->service->serviceid, update->service->status, 0); } /* write service status changes into database */ zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset); if (0 != updates.values_num) { zbx_vector_ptr_sort(&updates, (zbx_compare_func_t)its_updates_compare); for (i = 0; i < updates.values_num; i++) { zbx_status_update_t *status_update = (zbx_status_update_t *)updates.values[i]; zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update services" " set status=%d" " where serviceid=" ZBX_FS_UI64 ";\n", status_update->status, status_update->sourceid); if (SUCCEED != zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset)) goto out; } } zbx_vector_uint64_sort(service_problemids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); if (0 != service_problemids->values_num) { zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "delete from service_problem where"); zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "service_problemid", service_problemids->values, service_problemids->values_num); zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n"); } zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset); if (16 < sql_offset) { if (ZBX_DB_OK > zbx_db_execute("%s", sql)) goto out; } ret = SUCCEED; for (i = 0; i < service_problems_new->values_num; i++) { zbx_service_problem_t *service_problem; service_problem = (zbx_service_problem_t *)service_problems_new->values[i]; zbx_vector_uint64_append(&serviceids, service_problem->serviceid); } for (i = 0; i < alarms->values_num; i++) { zbx_status_update_t *status_update = (zbx_status_update_t *)alarms->values[i]; zbx_vector_uint64_append(&serviceids, status_update->sourceid); } zbx_vector_uint64_sort(&serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_vector_uint64_uniq(&serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_db_lock_ids("services", "serviceid", &serviceids); /* write generated service alarms into database */ if (0 != alarms->values_num) { zbx_db_insert_t db_insert; alarmid = zbx_db_get_maxid_num("service_alarms", alarms->values_num); zbx_db_insert_prepare(&db_insert, "service_alarms", "servicealarmid", "serviceid", "value", "clock", NULL); for (i = 0; i < alarms->values_num; i++) { zbx_status_update_t *status_update = (zbx_status_update_t *)alarms->values[i]; if (FAIL == zbx_vector_uint64_bsearch(&serviceids, status_update->sourceid, ZBX_DEFAULT_UINT64_COMPARE_FUNC)) { continue; } status_update->servicealarmid = alarmid++; zbx_db_insert_add_values(&db_insert, status_update->servicealarmid, status_update->sourceid, status_update->status, status_update->clock); } ret = zbx_db_insert_execute(&db_insert); zbx_db_insert_clean(&db_insert); } if (0 != service_problems_new->values_num) { zbx_db_insert_t db_insert; zbx_uint64_t service_problemid; zbx_vector_uint64_t eventids; zbx_vector_uint64_create(&eventids); zbx_vector_ptr_sort(service_problems_new, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC); for (i = 0; i < service_problems_new->values_num; i++) { zbx_service_problem_t *service_problem; service_problem = (zbx_service_problem_t *)service_problems_new->values[i]; zbx_vector_uint64_append(&eventids, service_problem->eventid); } zbx_vector_uint64_uniq(&eventids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_db_lock_ids("problem", "eventid", &eventids); service_problemid = zbx_db_get_maxid_num("service_problem", service_problems_new->values_num); zbx_db_insert_prepare(&db_insert, "service_problem", "service_problemid", "eventid", "serviceid", "severity", NULL); for (i = 0; i < service_problems_new->values_num; i++) { zbx_service_problem_t *service_problem; service_problem = (zbx_service_problem_t *)service_problems_new->values[i]; if (FAIL == zbx_vector_uint64_bsearch(&eventids, service_problem->eventid, ZBX_DEFAULT_UINT64_COMPARE_FUNC)) { continue; } if (FAIL == zbx_vector_uint64_bsearch(&serviceids, service_problem->serviceid, ZBX_DEFAULT_UINT64_COMPARE_FUNC)) { continue; } service_problem->service_problemid = service_problemid++; zbx_db_insert_add_values(&db_insert, service_problem->service_problemid, service_problem->eventid, service_problem->serviceid, service_problem->severity); } ret = zbx_db_insert_execute(&db_insert); zbx_db_insert_clean(&db_insert); zbx_vector_uint64_destroy(&eventids); } out: zbx_free(sql); zbx_vector_uint64_destroy(&serviceids); zbx_vector_ptr_clear_ext(&updates, zbx_ptr_free); zbx_vector_ptr_destroy(&updates); return ret; } /****************************************************************************** * * * Purpose: get service status by applying the main service status algorithm * * * * Parameters: service - [IN] the service * * * * Return value: The service status. * * * ******************************************************************************/ int service_get_main_status(const zbx_service_t *service) { int status = ZBX_SERVICE_STATUS_OK, child_status, i; switch (service->algorithm) { case ZBX_SERVICE_STATUS_CALC_MOST_CRITICAL_ALL: for (i = 0; i < service->children.values_num; i++) { zbx_service_t *child = (zbx_service_t *)service->children.values[i]; if (SUCCEED != service_get_status(child, &child_status)) continue; if (ZBX_SERVICE_STATUS_OK == child_status) { status = child_status; break; } if (status < child_status) status = child_status; } break; case ZBX_SERVICE_STATUS_CALC_MOST_CRITICAL_ONE: for (i = 0; i < service->children.values_num; i++) { zbx_service_t *child = (zbx_service_t *)service->children.values[i]; if (SUCCEED != service_get_status(child, &child_status)) continue; if (status < child_status) status = child_status; } break; case ZBX_SERVICE_STATUS_CALC_SET_OK: break; default: zabbix_log(LOG_LEVEL_ERR, "unknown calculation algorithm of service status [%d]", service->algorithm); break; } return status; } /****************************************************************************** * * * Purpose: get children with status greater or equal to the specified * * * * Parameters: service - [IN] the service * * status - [IN] the target status * * children - [OUT] the children having the required status * * total_weight - [OUT] the weight of all not ignored children * * total_num - [OUT] the number of all not ignored children * * * ******************************************************************************/ static void service_get_children_by_status(const zbx_service_t *service, int status, zbx_vector_ptr_t *children, int *total_weight, int *total_num) { int i, child_status; *total_num = 0; *total_weight = 0; for (i = 0; i < service->children.values_num; i++) { zbx_service_t *child = (zbx_service_t *)service->children.values[i]; if (SUCCEED != service_get_status(child, &child_status)) continue; (*total_weight) += child->weight; (*total_num)++; if (child_status >= status) zbx_vector_ptr_append(children, child); } } /****************************************************************************** * * * Purpose: get total weight of all specified services * * * ******************************************************************************/ static int services_get_weight(const zbx_vector_ptr_t *services) { int i, weight = 0; for (i = 0; i < services->values_num; i++) { zbx_service_t *service = (zbx_service_t *)services->values[i]; weight += service->weight; } return weight; } /****************************************************************************** * * * Purpose: get service status according to the specified rule * * * * Parameters: service - [IN] the service * * rule - [IN] the service status rule * * * * Return value: The service status. * * * ******************************************************************************/ int service_get_rule_status(const zbx_service_t *service, const zbx_service_rule_t *rule) { zbx_vector_ptr_t children; int status = ZBX_SERVICE_STATUS_OK, status_limit, total_num, total_weight, weight; zabbix_log(LOG_LEVEL_DEBUG, "In %s() service:" ZBX_FS_UI64 ", rule:" ZBX_FS_UI64, __func__, service->serviceid, rule->service_ruleid); zbx_vector_ptr_create(&children); switch (rule->type) { case ZBX_SERVICE_STATUS_RULE_TYPE_N_GE: case ZBX_SERVICE_STATUS_RULE_TYPE_NP_GE: case ZBX_SERVICE_STATUS_RULE_TYPE_W_GE: case ZBX_SERVICE_STATUS_RULE_TYPE_WP_GE: status_limit = rule->limit_status; break; case ZBX_SERVICE_STATUS_RULE_TYPE_N_L: case ZBX_SERVICE_STATUS_RULE_TYPE_NP_L: case ZBX_SERVICE_STATUS_RULE_TYPE_W_L: case ZBX_SERVICE_STATUS_RULE_TYPE_WP_L: status_limit = rule->limit_status + 1; break; default: THIS_SHOULD_NEVER_HAPPEN; goto out; } service_get_children_by_status(service, status_limit, &children, &total_weight, &total_num); switch (rule->type) { case ZBX_SERVICE_STATUS_RULE_TYPE_N_GE: if (children.values_num < rule->limit_value) goto out; break; case ZBX_SERVICE_STATUS_RULE_TYPE_NP_GE: if (0 == total_num || children.values_num * 100 / total_num < rule->limit_value) goto out; break; case ZBX_SERVICE_STATUS_RULE_TYPE_N_L: if (total_num - children.values_num >= rule->limit_value) goto out; break; case ZBX_SERVICE_STATUS_RULE_TYPE_NP_L: if (0 == total_num || (total_num - children.values_num) * 100 / total_num >= rule->limit_value) goto out; break; case ZBX_SERVICE_STATUS_RULE_TYPE_W_GE: weight = services_get_weight(&children); if (weight < rule->limit_value) goto out; break; case ZBX_SERVICE_STATUS_RULE_TYPE_WP_GE: weight = services_get_weight(&children); if (0 == total_weight || weight * 100 / total_weight < rule->limit_value) goto out; break; case ZBX_SERVICE_STATUS_RULE_TYPE_W_L: weight = services_get_weight(&children); if (total_weight - weight >= rule->limit_value) goto out; break; case ZBX_SERVICE_STATUS_RULE_TYPE_WP_L: weight = services_get_weight(&children); if (0 == total_weight || (total_weight - weight) * 100 / total_weight >= rule->limit_value) goto out; break; default: THIS_SHOULD_NEVER_HAPPEN; goto out; } status = rule->new_status; out: zbx_vector_ptr_destroy(&children); zabbix_log(LOG_LEVEL_DEBUG, "End of %s() status:%d", __func__, status); return status; } typedef struct { zbx_service_t *service; int severity; } zbx_service_severity_t; static void service_add_cause(zbx_vector_ptr_t *causes, zbx_service_t *service, int severity) { int i; zbx_service_severity_t *cause; for (i = 0; i < causes->values_num; i++) { cause = (zbx_service_severity_t *)causes->values[i]; if (cause->service == service) { if (cause->severity > severity) cause->severity = severity; return; } } cause = (zbx_service_severity_t *)zbx_malloc(NULL, sizeof(zbx_service_severity_t)); cause->service = service; cause->severity = severity; zbx_vector_ptr_append(causes, cause); } /****************************************************************************** * * * Purpose: get services that caused the target service to be in the * * specified severity state * * * * Parameters: service - [IN] the service * * severity - [IN] the required severity (-1 if there is no * * minimum severity required) * * eventids - [OUT] the root cause events * * * * Comments: The returned list includes children, grandchildren etc * * * ******************************************************************************/ static void service_get_causes(const zbx_service_t *service, int severity, zbx_vector_uint64_t *eventids) { int i, min_severity; zbx_vector_ptr_t causes; zbx_service_rule_t *n_rule = NULL, *w_rule = NULL; /* calculate the minimum severity by reversing propagation rule */ if (ZBX_SERVICE_STATUS_OK != severity) { switch (service->propagation_rule) { case ZBX_SERVICE_STATUS_PROPAGATION_INCREASE: min_severity = severity - service->propagation_value; if (ZBX_SERVICE_STATUS_OK >= min_severity) min_severity = ZBX_SERVICE_STATUS_OK + 1; severity = min_severity; break; case ZBX_SERVICE_STATUS_PROPAGATION_DECREASE: min_severity = severity + service->propagation_value; if (TRIGGER_SEVERITY_COUNT <= min_severity) min_severity = TRIGGER_SEVERITY_COUNT - 1; severity = min_severity; break; case ZBX_SERVICE_STATUS_PROPAGATION_FIXED: min_severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; severity = ZBX_SERVICE_STATUS_OK; break; default: min_severity = severity; } } else min_severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; if (0 == service->children.values_num) { for (i = 0; i < service->service_problems.values_num; i++) { zbx_service_problem_t *service_problem; service_problem = (zbx_service_problem_t *)service->service_problems.values[i]; if (service_problem->severity >= min_severity) zbx_vector_uint64_append(eventids, service_problem->eventid); } return; } zbx_vector_ptr_create(&causes); if (service_get_main_status(service) >= min_severity) { for (i = 0; i < service->children.values_num; i++) { zbx_service_t *child = (zbx_service_t *)service->children.values[i]; int child_status; if (SUCCEED != service_get_status(child, &child_status) || ZBX_SERVICE_STATUS_OK == child_status) { continue; } if (ZBX_SERVICE_STATUS_CALC_MOST_CRITICAL_ALL == service->algorithm) service_add_cause(&causes, child, ZBX_SERVICE_STATUS_OK); if (ZBX_SERVICE_STATUS_CALC_MOST_CRITICAL_ONE == service->algorithm && child_status >= min_severity) { service_add_cause(&causes, child, severity); } } } for (i = 0; i < service->status_rules.values_num; i++) { zbx_service_rule_t *rule = (zbx_service_rule_t *)service->status_rules.values[i]; /* check if the rule can return status of acceptable severity */ if (rule->new_status < min_severity) continue; if (ZBX_SERVICE_STATUS_OK == service_get_rule_status(service, rule)) continue; switch (rule->type) { case ZBX_SERVICE_STATUS_RULE_TYPE_N_GE: case ZBX_SERVICE_STATUS_RULE_TYPE_NP_GE: case ZBX_SERVICE_STATUS_RULE_TYPE_N_L: case ZBX_SERVICE_STATUS_RULE_TYPE_NP_L: if (NULL == n_rule || rule->limit_status < n_rule->limit_status) n_rule = rule; break; default: if (NULL == w_rule || rule->limit_status < w_rule->limit_status) w_rule = rule; break; } } if (NULL != n_rule) { int total_weight, total_num; zbx_vector_ptr_t children; zbx_vector_ptr_create(&children); service_get_children_by_status(service, n_rule->limit_status, &children, &total_weight, &total_num); for (i = 0; i < children.values_num; i++) service_add_cause(&causes, (zbx_service_t *)children.values[i], n_rule->limit_status); zbx_vector_ptr_destroy(&children); } /* cause is only added once, even if weight based rule is covered by the count based rule */ if (NULL != w_rule) { int total_weight, total_num; zbx_vector_ptr_t children; zbx_vector_ptr_create(&children); service_get_children_by_status(service, w_rule->limit_status, &children, &total_weight, &total_num); for (i = 0; i < children.values_num; i++) { zbx_service_t *child = (zbx_service_t *)children.values[i]; if (0 == child->weight) continue; service_add_cause(&causes, child, w_rule->limit_status); } zbx_vector_ptr_destroy(&children); } for (i = 0; i < causes.values_num; i++) { zbx_service_severity_t *cause = (zbx_service_severity_t *)causes.values[i]; service_get_causes(cause->service, cause->severity, eventids); } zbx_vector_ptr_clear_ext(&causes, zbx_ptr_free); zbx_vector_ptr_destroy(&causes); } /****************************************************************************** * * * Purpose: get root cause eventids for the service * * * * Parameters: parent - [IN] the service * * eventids - [OUT] the event identifierse * * * ******************************************************************************/ void service_get_rootcause_eventids(const zbx_service_t *parent, zbx_vector_uint64_t *eventids) { service_get_causes(parent, ZBX_SERVICE_STATUS_OK, eventids); zbx_vector_uint64_sort(eventids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_vector_uint64_uniq(eventids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); } /****************************************************************************** * * * Purpose: updates service and its parents statuses * * * * Parameters: service - [IN] the service to update * * ts - [IN] the update timestamp * * alarms - [OUT] the alarms update queue * * * * Comments: This function recalculates service status according to the * * algorithm and status of the children services. If the status * * has been changed, an alarm is generated and parent services * * (up until the root service) are updated too. * * * ******************************************************************************/ static void its_itservice_update_status(zbx_service_t *itservice, const zbx_timespec_t *ts, zbx_vector_ptr_t *alarms, zbx_hashset_t *service_updates, int flags) { int status, rule_status, i; status = service_get_main_status(itservice); for (i = 0; i < itservice->status_rules.values_num; i++) { zbx_service_rule_t *rule = (zbx_service_rule_t *)itservice->status_rules.values[i]; if (status < (rule_status = service_get_rule_status(itservice, rule))) status = rule_status; } if (itservice->status != status) { zbx_service_update_t *update; update = update_service(service_updates, itservice, status, ts); update->alarm = its_updates_append(alarms, itservice->serviceid, status, ts->sec); /* update parent services */ for (i = 0; i < itservice->parents.values_num; i++) { its_itservice_update_status((zbx_service_t *)itservice->parents.values[i], ts, alarms, service_updates, flags); } } else if (0 != (ZBX_FLAG_SERVICE_RECALCULATE & flags)) { /* update parent services */ for (i = 0; i < itservice->parents.values_num; i++) { its_itservice_update_status((zbx_service_t *)itservice->parents.values[i], ts, alarms, service_updates, flags); } } } static char *service_get_event_name(zbx_service_manager_t *manager, const char *name, int status) { const char *severity; switch (status) { case ZBX_SERVICE_STATUS_OK: severity = "OK"; break; case TRIGGER_SEVERITY_NOT_CLASSIFIED: case TRIGGER_SEVERITY_INFORMATION: case TRIGGER_SEVERITY_WARNING: case TRIGGER_SEVERITY_AVERAGE: case TRIGGER_SEVERITY_HIGH: case TRIGGER_SEVERITY_DISASTER: severity = manager->severities[status]; break; default: severity = "unknown"; } if (NULL != name) return zbx_dsprintf(NULL, "Status of service \"%s\" changed to %s", name, severity); else return zbx_dsprintf(NULL, "Status of unknown service changed to %s", severity); } /* business service values */ #define SERVICE_VALUE_OK 0 #define SERVICE_VALUE_PROBLEM 1 /****************************************************************************** * * * Purpose: create service events based on service updates * * * * Parameters: manager - [IN] the service manager * * updates - [IN] the service updates * * * ******************************************************************************/ static void db_create_service_events(zbx_service_manager_t *manager, const zbx_vector_ptr_t *updates) { const zbx_service_update_t *update; int i, j, events_num = 0; zbx_db_insert_t db_insert_events, db_insert_problem, db_insert_event_tag, db_insert_problem_tag, db_insert_escalations; zbx_uint64_t eventid; char *name; zbx_vector_uint64_t *actionids; zabbix_log(LOG_LEVEL_DEBUG, "In %s() updates:%d", __func__, updates->values_num); actionids = zbx_malloc(NULL, sizeof(zbx_vector_uint64_t) * (size_t)updates->values_num); for (i = 0; i < updates->values_num; i++) { zbx_vector_uint64_create(&actionids[i]); update = (const zbx_service_update_t *)updates->values[i]; service_update_process_actions(update, &manager->actions, &actionids[i]); if (0 != actionids[i].values_num) events_num++; } if (0 == events_num) goto out; zbx_db_insert_prepare(&db_insert_events, "events", "eventid", "source", "object", "objectid", "clock", "value", "ns", "name", "severity", NULL); zbx_db_insert_prepare(&db_insert_problem, "problem", "eventid", "source", "object", "objectid", "clock", "ns", "name", "severity", NULL); zbx_db_insert_prepare(&db_insert_event_tag, "event_tag", "eventtagid", "eventid", "tag", "value", NULL); zbx_db_insert_prepare(&db_insert_problem_tag, "problem_tag", "problemtagid", "eventid", "tag", "value", NULL); zbx_db_insert_prepare(&db_insert_escalations, "escalations", "escalationid", "actionid", "eventid", "serviceid", NULL); eventid = zbx_db_get_maxid_num("events", events_num); for (i = 0; i < updates->values_num; i++) { if (0 == actionids[i].values_num) continue; update = (const zbx_service_update_t *)updates->values[i]; name = service_get_event_name(manager, update->service->name, update->service->status); zbx_db_insert_add_values(&db_insert_events, eventid, EVENT_SOURCE_SERVICE, EVENT_OBJECT_SERVICE, update->service->serviceid, update->ts.sec, SERVICE_VALUE_PROBLEM, update->ts.ns, name, update->service->status); zbx_db_insert_add_values(&db_insert_problem, eventid, EVENT_SOURCE_SERVICE, EVENT_OBJECT_SERVICE, update->service->serviceid, update->ts.sec, update->ts.ns, name, update->service->status); for (j = 0; j < update->service->tags.values_num; j++) { zbx_service_tag_t *tag = (zbx_service_tag_t *)update->service->tags.values[j]; zbx_db_insert_add_values(&db_insert_event_tag, __UINT64_C(0), eventid, tag->name, tag->value); zbx_db_insert_add_values(&db_insert_problem_tag, __UINT64_C(0), eventid, tag->name, tag->value); } for (j = 0; j < actionids[i].values_num; j++) { zbx_db_insert_add_values(&db_insert_escalations, __UINT64_C(0), actionids[i].values[j], eventid, update->service->serviceid); } eventid++; zbx_free(name); } zbx_db_insert_execute(&db_insert_events); zbx_db_insert_clean(&db_insert_events); zbx_db_insert_execute(&db_insert_problem); zbx_db_insert_clean(&db_insert_problem); zbx_db_insert_autoincrement(&db_insert_event_tag, "eventtagid"); zbx_db_insert_execute(&db_insert_event_tag); zbx_db_insert_clean(&db_insert_event_tag); zbx_db_insert_autoincrement(&db_insert_problem_tag, "problemtagid"); zbx_db_insert_execute(&db_insert_problem_tag); zbx_db_insert_clean(&db_insert_problem_tag); zbx_db_insert_autoincrement(&db_insert_escalations, "escalationid"); zbx_db_insert_execute(&db_insert_escalations); zbx_db_insert_clean(&db_insert_escalations); out: for (i = 0; i < updates->values_num; i++) zbx_vector_uint64_destroy(&actionids[i]); zbx_free(actionids); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static const zbx_service_update_t *get_update_by_serviceid(const zbx_vector_ptr_t *updates, zbx_uint64_t serviceid) { int i; const zbx_service_update_t *update; for (i = 0; i < updates->values_num; i++) { update = (zbx_service_update_t *)updates->values[i]; if (update->service->serviceid == serviceid) return update; } return NULL; } /****************************************************************************** * * * Purpose: get open problems for the specified services * * * * Parameters: manager - [IN] the service manager * * problem_service - [IN] a vector of eventid, serviceid pairs * * * ******************************************************************************/ static void db_get_service_problems(zbx_vector_uint64_t *serviceids, zbx_vector_uint64_pair_t *problem_service) { DB_ROW row; DB_RESULT result; char *sql = NULL; size_t sql_alloc = 0, sql_offset = 0; zbx_vector_uint64_sort(serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "select eventid,objectid from problem" " where source=%d" " and object=%d" " and", EVENT_SOURCE_SERVICE, EVENT_OBJECT_SERVICE); zbx_db_add_condition_alloc(&sql, &sql_alloc, &sql_offset, "objectid", serviceids->values, serviceids->values_num); zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, " and r_eventid is null"); result = zbx_db_select("%s", sql); while (NULL != (row = zbx_db_fetch(result))) { zbx_uint64_pair_t pair; ZBX_DBROW2UINT64(pair.first, row[0]); ZBX_DBROW2UINT64(pair.second, row[1]); zbx_vector_uint64_pair_append(problem_service, pair); } zbx_db_free_result(result); zbx_free(sql); } typedef struct { zbx_uint64_t eventid; zbx_uint64_t r_eventid; zbx_timespec_t ts; } zbx_service_recovery_t; /****************************************************************************** * * * Purpose: resolve service events based on service updates * * * * Parameters: manager - [IN] the service manager * * updates - [IN] the service updates * * * ******************************************************************************/ static void db_resolve_service_events(zbx_service_manager_t *manager, const zbx_vector_ptr_t *updates) { int i, j; const zbx_service_update_t *update; zbx_vector_uint64_t serviceids; zbx_vector_uint64_pair_t problem_service; zbx_vector_ptr_t recoveries; char *sql = NULL, *name; size_t sql_alloc = 0, sql_offset = 0; zbx_uint64_t eventid; zbx_db_insert_t db_insert_events, db_insert_event_tag, db_insert_recovery; zbx_service_recovery_t *recovery; zabbix_log(LOG_LEVEL_DEBUG, "In %s() updates:%d", __func__, updates->values_num); zbx_vector_uint64_create(&serviceids); zbx_vector_uint64_pair_create(&problem_service); zbx_vector_ptr_create(&recoveries); for (i = 0; i < updates->values_num; i++) { update = (const zbx_service_update_t *)updates->values[i]; zbx_vector_uint64_append(&serviceids, update->service->serviceid); } db_get_service_problems(&serviceids, &problem_service); if (0 == problem_service.values_num) goto out; /* insert recovery events */ zbx_db_insert_prepare(&db_insert_events, "events", "eventid", "source", "object", "objectid", "clock", "value", "ns", "name", "severity", NULL); zbx_db_insert_prepare(&db_insert_event_tag, "event_tag", "eventtagid", "eventid", "tag", "value", NULL); eventid = zbx_db_get_maxid_num("events", problem_service.values_num); for (i = 0; i < problem_service.values_num; i++) { zbx_timespec_t ts; if (NULL != (update = get_update_by_serviceid(updates, problem_service.values[i].second))) { name = service_get_event_name(manager, update->service->name, ZBX_SERVICE_STATUS_OK); ts = update->ts; } else { zbx_timespec(&ts); name = service_get_event_name(manager, NULL, ZBX_SERVICE_STATUS_OK); } zbx_db_insert_add_values(&db_insert_events, eventid, EVENT_SOURCE_SERVICE, EVENT_OBJECT_SERVICE, problem_service.values[i].second, ts.sec, SERVICE_VALUE_OK, ts.ns, name, ZBX_SERVICE_STATUS_OK); if (NULL != update) { for (j = 0; j < update->service->tags.values_num; j++) { zbx_service_tag_t *tag = (zbx_service_tag_t *)update->service->tags.values[j]; zbx_db_insert_add_values(&db_insert_event_tag, __UINT64_C(0), eventid, tag->name, tag->value); } } zbx_free(name); recovery = (zbx_service_recovery_t *)zbx_malloc(NULL, sizeof(zbx_service_recovery_t)); recovery->eventid = problem_service.values[i].first; recovery->r_eventid = eventid++; recovery->ts = ts; zbx_vector_ptr_append(&recoveries, recovery); } zbx_db_insert_execute(&db_insert_events); zbx_db_insert_clean(&db_insert_events); zbx_db_insert_autoincrement(&db_insert_event_tag, "eventtagid"); zbx_db_insert_execute(&db_insert_event_tag); zbx_db_insert_clean(&db_insert_event_tag); /* update problems, escalations and link problems with recovery events */ sql_offset = 0; zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset); zbx_db_insert_prepare(&db_insert_recovery, "event_recovery", "eventid", "r_eventid", NULL); for (i = 0; i < recoveries.values_num; i++) { recovery = (zbx_service_recovery_t *)recoveries.values[i]; zbx_db_insert_add_values(&db_insert_recovery, recovery->eventid, recovery->r_eventid); zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update problem" " set r_eventid=" ZBX_FS_UI64 ",r_clock=%d" ",r_ns=%d" " where eventid=" ZBX_FS_UI64 ";\n", recovery->r_eventid, recovery->ts.sec, recovery->ts.ns, recovery->eventid); zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset); zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update escalations set r_eventid=" ZBX_FS_UI64 "," "nextcheck=0 where eventid=" ZBX_FS_UI64 " and servicealarmid is null;\n", recovery->r_eventid, recovery->eventid); zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset); } zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset); if (16 < sql_offset) /* in ORACLE always present begin..end; */ zbx_db_execute("%s", sql); zbx_db_insert_execute(&db_insert_recovery); zbx_db_insert_clean(&db_insert_recovery); out: zbx_free(sql); zbx_vector_ptr_clear_ext(&recoveries, zbx_ptr_free); zbx_vector_ptr_destroy(&recoveries); zbx_vector_uint64_pair_destroy(&problem_service); zbx_vector_uint64_destroy(&serviceids); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } #undef SERVICE_VALUE_OK #undef SERVICE_VALUE_PROBLEM static int compare_uint64_pair_second(const void *d1, const void *d2) { const zbx_uint64_pair_t *p1 = (const zbx_uint64_pair_t *)d1; const zbx_uint64_pair_t *p2 = (const zbx_uint64_pair_t *)d2; ZBX_RETURN_IF_NOT_EQUAL(p1->second, p2->second); return SUCCEED; } /****************************************************************************** * * * Purpose: create update escalations based on service updates * * * * Parameters: manager - [IN] the service manager * * updates - [IN] the service updates * * * ******************************************************************************/ static void db_update_service_events(zbx_service_manager_t *manager, const zbx_vector_ptr_t *updates) { const zbx_service_update_t *update; int i, j, escalations_num = 0; zbx_db_insert_t db_insert_escalations; zbx_vector_uint64_t *actionids, serviceids; zbx_vector_uint64_pair_t problem_service; zabbix_log(LOG_LEVEL_DEBUG, "In %s() updates:%d", __func__, updates->values_num); zbx_vector_uint64_create(&serviceids); zbx_vector_uint64_pair_create(&problem_service); actionids = zbx_malloc(NULL, sizeof(zbx_vector_uint64_t) * (size_t)updates->values_num); /* Update actions should be processed on the original update that created event. */ /* However service properties checked by action conditions (id, name, tags) either */ /* cannot be changed or the change cannot be tracked. So until new conditions are */ /* added the current update can be used instead. */ for (i = 0; i < updates->values_num; i++) { zbx_vector_uint64_create(&actionids[i]); update = (const zbx_service_update_t *)updates->values[i]; service_update_process_actions(update, &manager->actions, &actionids[i]); if (0 != actionids[i].values_num) { escalations_num += actionids[i].values_num; zbx_vector_uint64_append(&serviceids, update->service->serviceid); } } if (0 == escalations_num) goto out; db_get_service_problems(&serviceids, &problem_service); if (0 == problem_service.values_num) goto out; zbx_db_insert_prepare(&db_insert_escalations, "escalations", "escalationid", "actionid", "eventid", "serviceid", "servicealarmid", NULL); for (i = 0; i < updates->values_num; i++) { int index; zbx_uint64_pair_t pair; if (0 == actionids[i].values_num) continue; update = (const zbx_service_update_t *)updates->values[i]; pair.second = update->service->serviceid; if (FAIL == (index = zbx_vector_uint64_pair_search(&problem_service, pair, compare_uint64_pair_second))) continue; for (j = 0; j < actionids[i].values_num; j++) { zbx_db_insert_add_values(&db_insert_escalations, __UINT64_C(0), actionids[i].values[j], problem_service.values[index].first, update->service->serviceid, update->alarm->servicealarmid); } } zbx_db_insert_autoincrement(&db_insert_escalations, "escalationid"); zbx_db_insert_execute(&db_insert_escalations); zbx_db_insert_clean(&db_insert_escalations); out: for (i = 0; i < updates->values_num; i++) zbx_vector_uint64_destroy(&actionids[i]); zbx_free(actionids); zbx_vector_uint64_pair_destroy(&problem_service); zbx_vector_uint64_destroy(&serviceids); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } /****************************************************************************** * * * Purpose: generate and process service events in response to service * * updates * * * ******************************************************************************/ static void db_manage_service_events(zbx_service_manager_t *manager, zbx_hashset_t *service_updates) { zbx_hashset_iter_t iter; zbx_service_update_t *update; zbx_vector_ptr_t events_create, events_resolve, events_update; zbx_vector_ptr_create(&events_create); zbx_vector_ptr_create(&events_resolve); zbx_vector_ptr_create(&events_update); zbx_hashset_iter_reset(service_updates, &iter); while (NULL != (update = (zbx_service_update_t *)zbx_hashset_iter_next(&iter))) { if (update->old_status == update->service->status) continue; if (ZBX_SERVICE_STATUS_OK != update->service->status) { if (ZBX_SERVICE_STATUS_OK == update->old_status) zbx_vector_ptr_append(&events_create, update); else zbx_vector_ptr_append(&events_update, update); } else zbx_vector_ptr_append(&events_resolve, update); } if (0 != events_create.values_num) db_create_service_events(manager, &events_create); if (0 != events_resolve.values_num) db_resolve_service_events(manager, &events_resolve); if (0 != events_update.values_num) db_update_service_events(manager, &events_update); zbx_vector_ptr_destroy(&events_update); zbx_vector_ptr_destroy(&events_resolve); zbx_vector_ptr_destroy(&events_create); } static zbx_hash_t service_update_hash_func(const void *d) { const zbx_service_update_t *update = (const zbx_service_update_t *)d; return ZBX_DEFAULT_UINT64_HASH_FUNC(&update->service->serviceid); } static int service_update_compare_func(const void *d1, const void *d2) { const zbx_service_update_t *update1 = (const zbx_service_update_t *)d1; const zbx_service_update_t *update2 = (const zbx_service_update_t *)d2; ZBX_RETURN_IF_NOT_EQUAL(update1->service->serviceid, update2->service->serviceid); return 0; } static void db_update_services(zbx_service_manager_t *manager) { zbx_hashset_iter_t iter; zbx_services_diff_t *service_diff; zbx_vector_ptr_t alarms, service_problems_new; zbx_vector_uint64_t service_problemids; zbx_hashset_t service_updates; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_vector_ptr_create(&alarms); zbx_vector_ptr_create(&service_problems_new); zbx_vector_uint64_create(&service_problemids); zbx_hashset_create(&service_updates, 100, service_update_hash_func, service_update_compare_func); zbx_hashset_iter_reset(&manager->service_diffs, &iter); while (NULL != (service_diff = (zbx_services_diff_t *)zbx_hashset_iter_next(&iter))) { zbx_service_t service_local = {.serviceid = service_diff->serviceid}, *service; int status = ZBX_SERVICE_STATUS_OK, i; zbx_timespec_t ts = {0, 0}; service = zbx_hashset_search(&manager->services, &service_local); if (NULL == service) { THIS_SHOULD_NEVER_HAPPEN; continue; } if (service_diff->flags & ZBX_FLAG_SERVICE_RECALCULATE) { for (i = 0; i < service->service_problems.values_num; i++) { zbx_service_problem_t *service_problem; int index; service_problem = (zbx_service_problem_t *)service->service_problems.values[i]; if (FAIL == (index = zbx_vector_ptr_search(&service_diff->service_problems, &service_problem->eventid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC))) { zbx_vector_uint64_append(&service_problemids, service_problem->service_problemid); remove_service_problem(service, i, &manager->service_problems_index); i--; continue; } zbx_free(service_diff->service_problems.values[index]); zbx_vector_ptr_remove_noorder(&service_diff->service_problems, index); } } for (i = 0; i < service_diff->service_problems.values_num; i++) { zbx_service_problem_t *service_problem; service_problem = (zbx_service_problem_t *)service_diff->service_problems.values[i]; add_service_problem(service, &manager->service_problems_index, service_problem); zbx_vector_ptr_append(&service_problems_new, service_problem); } service_diff->service_problems.values_num = 0; for (i = 0; i < service_diff->service_problems_recovered.values_num; i++) { zbx_service_problem_t *service_problem; int index; service_problem = (zbx_service_problem_t *)service_diff->service_problems_recovered.values[i]; if (FAIL == (index = zbx_vector_ptr_search(&service->service_problems, &service_problem->eventid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC))) { THIS_SHOULD_NEVER_HAPPEN; continue; } else { if (0 > zbx_timespec_compare(&ts, &service_problem->ts)) ts = service_problem->ts; service_problem = (zbx_service_problem_t *)service->service_problems.values[index]; zbx_vector_uint64_append(&service_problemids, service_problem->service_problemid); remove_service_problem(service, index, &manager->service_problems_index); } } for (i = 0; i < service->service_problems.values_num; i++) { zbx_service_problem_t *service_problem; service_problem = (zbx_service_problem_t *)service->service_problems.values[i]; if (service_problem->severity > status) { status = service_problem->severity; ts = service_problem->ts; } } if (0 == ts.sec) zbx_timespec(&ts); if (service->status != status) { zbx_service_update_t *update; update = update_service(&service_updates, service, status, &ts); update->alarm = its_updates_append(&alarms, service->serviceid, service->status, ts.sec); /* update parent services */ for (i = 0; i < service->parents.values_num; i++) { its_itservice_update_status((zbx_service_t *)service->parents.values[i], &ts, &alarms, &service_updates, service_diff->flags); } } else if (0 != (ZBX_FLAG_SERVICE_RECALCULATE & service_diff->flags)) { /* update parent services */ for (i = 0; i < service->parents.values_num; i++) { its_itservice_update_status((zbx_service_t *)service->parents.values[i], &ts, &alarms, &service_updates, service_diff->flags); } } } do { zbx_db_begin(); its_write_status_and_alarms(&alarms, &service_updates, &service_problems_new, &service_problemids); if (0 != manager->actions.num_data) db_manage_service_events(manager, &service_updates); } while (ZBX_DB_DOWN == zbx_db_commit()); zbx_vector_uint64_destroy(&service_problemids); zbx_vector_ptr_destroy(&service_problems_new); zbx_hashset_destroy(&service_updates); zbx_vector_ptr_clear_ext(&alarms, zbx_ptr_free); zbx_vector_ptr_destroy(&alarms); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void recover_services_problem(zbx_service_manager_t *service_manager, const zbx_event_t *event) { zbx_service_problem_index_t *service_problem_index, service_problem_index_local; service_problem_index_local.eventid = event->eventid; if (NULL != (service_problem_index = zbx_hashset_search(&service_manager->service_problems_index, &service_problem_index_local))) { int i; for (i = 0; i < service_problem_index->services.values_num; i++) { zbx_service_t *service; zbx_services_diff_t service_diff_local, *service_diff; zbx_service_problem_t *service_problem; service = (zbx_service_t *)service_problem_index->services.values[i]; service_diff_local.serviceid = service->serviceid; if (NULL == (service_diff = zbx_hashset_search(&service_manager->service_diffs, &service_diff_local))) { zbx_vector_ptr_create(&service_diff_local.service_problems); zbx_vector_ptr_create(&service_diff_local.service_problems_recovered); service_diff_local.flags = ZBX_FLAG_SERVICE_UPDATE; service_diff = zbx_hashset_insert(&service_manager->service_diffs, &service_diff_local, sizeof(service_diff_local)); } service_problem = zbx_malloc(NULL, sizeof(zbx_service_problem_t)); service_problem->eventid = event->eventid; service_problem->serviceid = service_diff->serviceid; service_problem->severity = event->severity; service_problem->ts.sec = event->clock; service_problem->ts.ns = event->ns; zbx_vector_ptr_append(&service_diff->service_problems_recovered, service_problem); } } } static void process_deleted_problems(zbx_vector_uint64_t *eventids, zbx_service_manager_t *service_manager) { int i; zbx_timespec_t ts; zabbix_log(LOG_LEVEL_DEBUG, "In %s() events_num:%d", __func__, eventids->values_num); zbx_timespec(&ts); for (i = 0; i < eventids->values_num; i++) { zbx_event_t *event, event_local = {.eventid = eventids->values[i]}, **ptr; zbx_uint64_pair_t pair; pair.first = eventids->values[i]; pair.second = (zbx_uint64_t)ts.sec; zbx_hashset_insert(&service_manager->deleted_eventids, &pair, sizeof(pair)); event = &event_local; if (NULL == (ptr = zbx_hashset_search(&service_manager->problem_events, &event))) continue; event = *ptr; event->clock = ts.sec; event->ns = ts.ns; recover_services_problem(service_manager, event); zbx_hashset_remove_direct(&service_manager->problem_events, ptr); } db_update_services(service_manager); zbx_hashset_clear(&service_manager->service_diffs); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void process_problem_tags(zbx_vector_ptr_t *events, zbx_service_manager_t *service_manager) { int i, j; zabbix_log(LOG_LEVEL_DEBUG, "In %s() events_num:%d", __func__, events->values_num); for (i = 0; i < events->values_num; i++) { zbx_event_t *event, **ptr; event = (zbx_event_t *)events->values[i]; if (NULL == (ptr = zbx_hashset_search(&service_manager->problem_events, &event))) { event_free(event); continue; } for (j = 0; j < event->tags.values_num; j++) zbx_vector_ptr_append(&(*ptr)->tags, event->tags.values[j]); event->tags.values_num = 0; event_free(event); match_event_to_service_problem_tags(*ptr, &service_manager->service_problem_tags_index, &service_manager->service_diffs, ZBX_FLAG_SERVICE_RECALCULATE); } db_update_services(service_manager); zbx_hashset_clear(&service_manager->service_diffs); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void process_events(zbx_vector_ptr_t *events, zbx_service_manager_t *service_manager) { int i; zabbix_log(LOG_LEVEL_DEBUG, "In %s() events_num:%d", __func__, events->values_num); for (i = 0; i < events->values_num; i++) { zbx_event_t *event, **ptr; event = (zbx_event_t *)events->values[i]; /* skip problem or recovery if trigger and it's associated problems are already deleted */ if (NULL != (zbx_hashset_search(&service_manager->deleted_eventids, &event->eventid))) { event_free(event); continue; } switch (event->value) { case TRIGGER_VALUE_OK: if (NULL == (ptr = zbx_hashset_search(&service_manager->problem_events, &event))) { /* handle possible race condition when recovery is received before problem */ zbx_hashset_insert(&service_manager->recovery_events, &event, sizeof(zbx_event_t *)); continue; } recover_services_problem(service_manager, event); event_free(event); zbx_hashset_remove_direct(&service_manager->problem_events, ptr); break; case TRIGGER_VALUE_PROBLEM: if (NULL != zbx_hashset_search(&service_manager->problem_events, &event)) { zabbix_log(LOG_LEVEL_ERR, "cannot process event \"" ZBX_FS_UI64 "\": event" " already processed", event->eventid); THIS_SHOULD_NEVER_HAPPEN; event_free(event); continue; } if (NULL != (ptr = zbx_hashset_search(&service_manager->recovery_events, &event))) { /* handle possible race condition when recovery is received before problem */ zbx_hashset_remove_direct(&service_manager->recovery_events, ptr); event_free(event); continue; } zbx_hashset_insert(&service_manager->problem_events, &event, sizeof(zbx_event_t *)); match_event_to_service_problem_tags(event, &service_manager->service_problem_tags_index, &service_manager->service_diffs, ZBX_FLAG_SERVICE_UPDATE); break; default: zabbix_log(LOG_LEVEL_ERR, "cannot process event \"" ZBX_FS_UI64 "\" unexpected value:%d", event->eventid, event->value); THIS_SHOULD_NEVER_HAPPEN; event_free(event); } } db_update_services(service_manager); zbx_hashset_clear(&service_manager->service_diffs); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void process_rootcause(const zbx_ipc_message_t *message, zbx_service_manager_t *service_manager, zbx_ipc_client_t *client) { zbx_vector_uint64_t serviceids, eventids; int i; unsigned char *data = NULL; size_t data_alloc = 0, data_offset = 0; zbx_vector_uint64_create(&serviceids); zbx_vector_uint64_create(&eventids); zbx_service_deserialize_ids(message->data, message->size, &serviceids); for (i = 0; i < serviceids.values_num; i++) { zbx_service_t *service, service_local = {.serviceid = serviceids.values[i]}; if (NULL == (service = zbx_hashset_search(&service_manager->services, &service_local))) continue; service_get_rootcause_eventids(service, &eventids); if (0 == eventids.values_num) continue; zbx_service_serialize_rootcause(&data, &data_alloc, &data_offset, serviceids.values[i], &eventids); zbx_vector_uint64_clear(&eventids); } zbx_ipc_client_send(client, ZBX_IPC_SERVICE_SERVICE_ROOTCAUSE, data, (zbx_uint32_t)data_offset); zbx_free(data); zbx_vector_uint64_destroy(&eventids); zbx_vector_uint64_destroy(&serviceids); } static void get_parent_serviceids(zbx_service_t *service, zbx_vector_uint64_t *parentids) { int i; for (i = 0; i < service->parents.values_num; i++) { zbx_service_t *parent; parent = (zbx_service_t*)(service->parents.values[i]); zbx_vector_uint64_append(parentids, parent->serviceid); get_parent_serviceids(parent, parentids); } } static void process_parentlist(const zbx_ipc_message_t *message, zbx_service_manager_t *service_manager, zbx_ipc_client_t *client) { unsigned char *data = NULL; zbx_uint32_t data_len = 0; zbx_uint64_t child_serviceid = 0; zbx_service_t *service, service_local; zbx_vector_uint64_t parentids; (void)zbx_deserialize_uint64(message->data, &child_serviceid); service_local.serviceid = child_serviceid; zbx_vector_uint64_create(&parentids); if (NULL != (service = zbx_hashset_search(&service_manager->services, &service_local))) { get_parent_serviceids(service, &parentids); zbx_vector_uint64_sort(&parentids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_vector_uint64_uniq(&parentids, ZBX_DEFAULT_UINT64_COMPARE_FUNC); data_len = zbx_service_serialize_parentids(&data, &parentids); } zbx_ipc_client_send(client, ZBX_IPC_SERVICE_SERVICE_PARENT_LIST, data, data_len); zbx_vector_uint64_destroy(&parentids); zbx_free(data); } /****************************************************************************** * * * Purpose: update cached service problem and queue service for update * * * ******************************************************************************/ static void service_update_event_severity(zbx_service_manager_t *service_manager, zbx_service_t *service, zbx_uint64_t eventid, int severity) { int index; zbx_service_problem_t *service_problem; zbx_services_diff_t services_diff_local; if (FAIL == (index = zbx_vector_ptr_search(&service->service_problems, &eventid, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC))) { return; } service_problem = (zbx_service_problem_t *)service->service_problems.values[index]; service_problem->severity = severity; services_diff_local.serviceid = service->serviceid; if (NULL == zbx_hashset_search(&service_manager->service_diffs, &services_diff_local)) { zbx_vector_ptr_create(&services_diff_local.service_problems); zbx_vector_ptr_create(&services_diff_local.service_problems_recovered); services_diff_local.flags = ZBX_FLAG_SERVICE_UPDATE; zbx_hashset_insert(&service_manager->service_diffs, &services_diff_local, sizeof(services_diff_local)); } } /****************************************************************************** * * * Purpose: update service_problem table with the changed event severities * * * ******************************************************************************/ static int db_update_service_problems(const zbx_vector_ptr_t *event_severities) { int i, txn_rc; char *sql = NULL; size_t sql_alloc = 0; do { size_t sql_offset = 0; zbx_db_begin(); zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset); for (i = 0; i < event_severities->values_num; i++) { zbx_event_severity_t *es = (zbx_event_severity_t *)event_severities->values[i]; zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update service_problem set severity=%d where eventid=" ZBX_FS_UI64 ";\n", es->severity, es->eventid); zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset); } zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset); if (16 < sql_offset) zbx_db_execute("%s", sql); } while (ZBX_DB_DOWN == (txn_rc = zbx_db_commit())); zbx_free(sql); return (ZBX_DB_FAIL != txn_rc ? SUCCEED : FAIL); } /****************************************************************************** * * * Purpose: update event severities, service statuses in cache and database * * according to the event severity changes during acknowledgment * * * ******************************************************************************/ static void process_event_severities(const zbx_ipc_message_t *message, zbx_service_manager_t *service_manager) { zbx_vector_ptr_t event_severities; int i, j, severities_num; zabbix_log(LOG_LEVEL_DEBUG, "In %s() size:%u" , __func__, message->size); zbx_vector_ptr_create(&event_severities); zbx_service_deserialize_event_severities(message->data, &event_severities); severities_num = event_severities.values_num; if (SUCCEED != db_update_service_problems(&event_severities)) { zabbix_log(LOG_LEVEL_WARNING, "cannot update service problem severities in database"); goto out; } for (i = 0; i < event_severities.values_num; i++) { zbx_event_severity_t *es = (zbx_event_severity_t *)event_severities.values[i]; zbx_event_t event_local = {.eventid = es->eventid}, *event = &event_local, **pevent; zbx_service_problem_index_t *pi, pi_local; /* update event severity in problem cache */ if (NULL == (pevent = (zbx_event_t **)zbx_hashset_search(&service_manager->problem_events, &event))) continue; (*pevent)->severity = es->severity; /* update event severities in service problems lists */ pi_local.eventid = es->eventid; if (NULL == (pi = zbx_hashset_search(&service_manager->service_problems_index, &pi_local))) continue; for (j = 0; j < pi->services.values_num; j++) { zbx_service_t *service = (zbx_service_t *)pi->services.values[j]; service_update_event_severity(service_manager, service, es->eventid, es->severity); } } db_update_services(service_manager); out: zbx_vector_ptr_clear_ext(&event_severities, zbx_ptr_free); zbx_vector_ptr_destroy(&event_severities); zabbix_log(LOG_LEVEL_DEBUG, "End of %s() severities_num:%d", __func__, severities_num); } static void service_manager_init(zbx_service_manager_t *service_manager) { zbx_hashset_create_ext(&service_manager->problem_events, 1000, default_uint64_ptr_hash_func, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC, (zbx_clean_func_t)event_ptr_free, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create_ext(&service_manager->recovery_events, 1, default_uint64_ptr_hash_func, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC, (zbx_clean_func_t)event_ptr_free, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create_ext(&service_manager->services, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC, (zbx_clean_func_t)service_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create(&service_manager->service_rules, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_hashset_create_ext(&service_manager->service_tags, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC, (zbx_clean_func_t)service_tag_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create_ext(&service_manager->service_problem_tags, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC, (zbx_clean_func_t)service_problem_tag_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create_ext(&service_manager->service_problem_tags_index, 1000, tag_services_hash, tag_services_compare, tag_services_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create_ext(&service_manager->service_problems_index, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC, service_problems_index_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create(&service_manager->services_links, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_hashset_create_ext(&service_manager->service_diffs, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC, service_diff_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create(&service_manager->deleted_eventids, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zbx_hashset_create_ext(&service_manager->actions, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC, (zbx_clean_func_t)service_action_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); zbx_hashset_create_ext(&service_manager->action_conditions, 1000, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC, (zbx_clean_func_t)service_action_condition_clean, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); memset(&service_manager->severities, 0, sizeof(service_manager->severities)); } static void service_manager_free(zbx_service_manager_t *service_manager) { int i; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); for (i = 0; i < TRIGGER_SEVERITY_COUNT; i++) zbx_free(service_manager->severities[i]); zbx_hashset_destroy(&service_manager->service_rules); zbx_hashset_destroy(&service_manager->service_problems_index); zbx_hashset_destroy(&service_manager->services_links); zbx_hashset_destroy(&service_manager->service_problem_tags); zbx_hashset_destroy(&service_manager->services); zbx_hashset_destroy(&service_manager->service_tags); zbx_hashset_destroy(&service_manager->problem_events); zbx_hashset_destroy(&service_manager->recovery_events); zbx_hashset_destroy(&service_manager->deleted_eventids); zbx_hashset_destroy(&service_manager->actions); zbx_hashset_destroy(&service_manager->action_conditions); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void dump_events(zbx_hashset_t *events) { zbx_hashset_iter_t iter; zbx_event_t **ptr, *event; int i; zbx_hashset_iter_reset(events, &iter); while (NULL != (ptr = (zbx_event_t **)zbx_hashset_iter_next(&iter))) { event = *ptr; zabbix_log(LOG_LEVEL_TRACE, "eventid:" ZBX_FS_UI64 " value:%d severity:%d clock:%d", event->eventid, event->value, event->severity, event->clock); for (i = 0; i < event->tags.values_num; i++) { const zbx_tag_t *tag = (const zbx_tag_t *)event->tags.values[i]; zabbix_log(LOG_LEVEL_TRACE, " tag:'%s' value:'%s'", tag->tag, tag->value); } } } static void dump_actions(zbx_hashset_t *actions) { zbx_hashset_iter_t iter; zbx_service_action_t *action; zbx_service_action_condition_t *condition; zbx_hashset_iter_reset(actions, &iter); while (NULL != (action = (zbx_service_action_t *)zbx_hashset_iter_next(&iter))) { int i; zabbix_log(LOG_LEVEL_TRACE, " actionid:" ZBX_FS_UI64 " evaltype:%d formula:%s", action->actionid, action->evaltype, action->formula); for (i = 0; i < action->conditions.values_num; i++) { condition = (zbx_service_action_condition_t *)action->conditions.values[i]; zabbix_log(LOG_LEVEL_TRACE, " conditionid:" ZBX_FS_UI64 " type:%d op:%d value:%s value2:%s", condition->conditionid, condition->conditiontype, condition->op, condition->value, condition->value2); } } } static void service_manager_trace(zbx_service_manager_t *service_manager) { if (SUCCEED != ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_TRACE)) return; zabbix_log(LOG_LEVEL_TRACE, "services : %d (%d slots)", service_manager->services.num_data, service_manager->services.num_slots); zabbix_log(LOG_LEVEL_TRACE, "service tags : %d (%d slots)", service_manager->service_tags.num_data, service_manager->service_tags.num_slots); zabbix_log(LOG_LEVEL_TRACE, "services links : %d (%d slots)", service_manager->services_links.num_data, service_manager->services_links.num_slots); zabbix_log(LOG_LEVEL_TRACE, "service problem tags : %d (%d slots)", service_manager->service_problem_tags.num_data, service_manager->service_problem_tags.num_slots); zabbix_log(LOG_LEVEL_TRACE, "service problem tag index : %d (%d slots)", service_manager->service_problem_tags_index.num_data, service_manager->service_problem_tags_index.num_slots); zabbix_log(LOG_LEVEL_TRACE, "service problems index : %d (%d slots)", service_manager->service_problems_index.num_data, service_manager->service_problems_index.num_slots); zabbix_log(LOG_LEVEL_TRACE, "service matches : %d (%d slots)", service_manager->service_diffs.num_data, service_manager->service_diffs.num_slots); zabbix_log(LOG_LEVEL_TRACE, "problem events : %d (%d slots)", service_manager->problem_events.num_data, service_manager->problem_events.num_slots); zabbix_log(LOG_LEVEL_TRACE, "recovery events : %d (%d slots)", service_manager->recovery_events.num_data, service_manager->recovery_events.num_slots); zabbix_log(LOG_LEVEL_TRACE, "deleted events : %d (%d slots)", service_manager->deleted_eventids.num_data, service_manager->deleted_eventids.num_slots); zabbix_log(LOG_LEVEL_TRACE, "recovery events : %d (%d slots)", service_manager->recovery_events.num_data, service_manager->recovery_events.num_slots); zabbix_log(LOG_LEVEL_TRACE, "events:"); dump_events(&service_manager->problem_events); dump_events(&service_manager->recovery_events); zabbix_log(LOG_LEVEL_TRACE, "actions : %d (%d slots)", service_manager->actions.num_data, service_manager->actions.num_slots); zabbix_log(LOG_LEVEL_TRACE, "action conditions: %d (%d slots)", service_manager->action_conditions.num_data, service_manager->action_conditions.num_slots); dump_actions(&service_manager->actions); } static void recalculate_services(zbx_service_manager_t *service_manager) { zbx_hashset_iter_t iter; zbx_event_t **event; zbx_service_t *service; zbx_services_diff_t services_diff_local, *services_diff; int flags; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); flags = ZBX_FLAG_SERVICE_RECALCULATE; zbx_hashset_iter_reset(&service_manager->problem_events, &iter); while (NULL != (event = (zbx_event_t **)zbx_hashset_iter_next(&iter))) { match_event_to_service_problem_tags(*event, &service_manager->service_problem_tags_index, &service_manager->service_diffs, flags); } zbx_hashset_iter_reset(&service_manager->services, &iter); while (NULL != (service = (zbx_service_t *)zbx_hashset_iter_next(&iter))) { if (0 != service->children.values_num) continue; services_diff_local.serviceid = service->serviceid; services_diff = zbx_hashset_search(&service_manager->service_diffs, &services_diff_local); if (NULL == services_diff) { zbx_vector_ptr_create(&services_diff_local.service_problems); zbx_vector_ptr_create(&services_diff_local.service_problems_recovered); services_diff_local.flags = flags; zbx_hashset_insert(&service_manager->service_diffs, &services_diff_local, sizeof(services_diff_local)); } } db_update_services(service_manager); zbx_hashset_clear(&service_manager->service_diffs); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } static void cleanup_deleted_problems(zbx_service_manager_t *service_manager, int now) { zbx_hashset_iter_t iter; zbx_uint64_pair_t *pair; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); zbx_hashset_iter_reset(&service_manager->deleted_eventids, &iter); while (NULL != (pair = (zbx_uint64_pair_t *)zbx_hashset_iter_next(&iter))) { if (ZBX_PROBLEM_CLEANUP_AGE < now - (int)pair->second) zbx_hashset_iter_remove(&iter); } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } ZBX_THREAD_ENTRY(service_manager_thread, args) { zbx_ipc_service_t service; char *error = NULL; zbx_ipc_client_t *client; zbx_ipc_message_t *message; int ret, events_num = 0, tags_update_num = 0, problems_delete_num = 0, service_update_num = 0; double time_stat, time_idle = 0, time_now, time_flush = 0, time_cleanup = 0, sec; zbx_service_manager_t service_manager; zbx_timespec_t timeout = {1, 0}; int service_cache_reload_requested = 0; const zbx_thread_info_t *info = &((zbx_thread_args_t *)args)->info; int server_num = ((zbx_thread_args_t *)args)->info.server_num; int process_num = ((zbx_thread_args_t *)args)->info.process_num; unsigned char process_type = ((zbx_thread_args_t *)args)->info.process_type; #define STAT_INTERVAL 5 /* if a process is busy and does not sleep then update status not faster than */ /* once in STAT_INTERVAL seconds */ zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(info->program_type), server_num, get_process_type_string(process_type), process_num); zbx_update_selfmon_counter(info, ZBX_PROCESS_STATE_BUSY); zbx_setproctitle("%s #%d [connecting to the database]", get_process_type_string(process_type), process_num); zbx_db_connect(ZBX_DB_CONNECT_NORMAL); if (FAIL == zbx_ipc_service_start(&service, ZBX_IPC_SERVICE_SERVICE, &error)) { zabbix_log(LOG_LEVEL_CRIT, "cannot start service manager service: %s", error); zbx_free(error); exit(EXIT_FAILURE); } /* initialize statistics */ time_stat = zbx_time(); service_manager_init(&service_manager); db_get_events(&service_manager.problem_events); zbx_setproctitle("%s #%d started", get_process_type_string(process_type), process_num); for (;;) { time_now = zbx_time(); if (STAT_INTERVAL < time_now - time_stat) { zbx_setproctitle("%s #%d [processed %d events, updated %d event tags, deleted %d problems," " synced %d service updates, idle " ZBX_FS_DBL " sec during " ZBX_FS_DBL " sec]", get_process_type_string(process_type), process_num, events_num, tags_update_num, problems_delete_num, service_update_num, time_idle, time_now - time_stat); time_stat = time_now; time_idle = 0; events_num = 0; tags_update_num = 0; problems_delete_num = 0; service_update_num = 0; service_manager_trace(&service_manager); } if (CONFIG_SERVICEMAN_SYNC_FREQUENCY < time_now - time_flush || 1 == service_cache_reload_requested) { int updated = 0, revision; if (1 == service_cache_reload_requested) zabbix_log(LOG_LEVEL_WARNING, "forced reloading of the service manager cache"); do { revision = (int)time(NULL); zbx_db_begin(); sync_services(&service_manager, &updated, revision); sync_service_rules(&service_manager, &updated, revision); sync_service_tags(&service_manager, revision); sync_service_problem_tags(&service_manager, &updated, revision); sync_services_links(&service_manager, &updated, revision); sync_actions(&service_manager, revision); sync_action_conditions(&service_manager, revision); sync_config(&service_manager); /* load service problems once during startup */ if (0 == (int)time_flush) { sync_service_problems(&service_manager.services, &service_manager.service_problems_index); } } while (ZBX_DB_DOWN == zbx_db_commit()); if (0 != updated) recalculate_services(&service_manager); if (1 == service_cache_reload_requested) { zabbix_log(LOG_LEVEL_WARNING, "finished forced reloading of the service manager cache"); service_cache_reload_requested = 0; } service_update_num += updated; time_flush = time_now; time_now = zbx_time(); } if (ZBX_PROBLEM_CLEANUP_FREQUENCY < time_now - time_cleanup) { cleanup_deleted_problems(&service_manager, (int)time_now); time_cleanup = time_now; time_now = zbx_time(); } zbx_update_selfmon_counter(info, ZBX_PROCESS_STATE_IDLE); ret = zbx_ipc_service_recv(&service, &timeout, &client, &message); zbx_update_selfmon_counter(info, ZBX_PROCESS_STATE_BUSY); sec = zbx_time(); zbx_update_env(get_process_type_string(process_type), sec); if (ZBX_IPC_RECV_IMMEDIATE != ret) time_idle += sec - time_now; if (NULL != message) { zbx_vector_ptr_t events; zbx_vector_uint64_t eventids; zbx_vector_ptr_create(&events); zbx_vector_uint64_create(&eventids); switch (message->code) { case ZBX_IPC_SERVICE_SERVICE_PROBLEMS: zbx_service_deserialize(message->data, message->size, &events); process_events(&events, &service_manager); events_num += events.values_num; break; case ZBX_IPC_SERVICE_SERVICE_PROBLEMS_TAGS: zbx_service_deserialize_problem_tags(message->data, message->size, &events); process_problem_tags(&events, &service_manager); tags_update_num += events.values_num; break; case ZBX_IPC_SERVICE_SERVICE_PROBLEMS_DELETE: zbx_service_deserialize_ids(message->data, message->size, &eventids); process_deleted_problems(&eventids, &service_manager); problems_delete_num += events.values_num; break; case ZBX_IPC_SERVICE_SERVICE_ROOTCAUSE: process_rootcause(message, &service_manager, client); break; case ZBX_IPC_SERVICE_SERVICE_PARENT_LIST: process_parentlist(message, &service_manager, client); break; case ZBX_IPC_SERVICE_EVENT_SEVERITIES: process_event_severities(message, &service_manager); break; case ZBX_IPC_SERVICE_RELOAD_CACHE: if (0 != service_cache_reload_requested) { zabbix_log(LOG_LEVEL_WARNING, "service manager cache reloading is" " already in progress"); } else service_cache_reload_requested = 1; break; default: THIS_SHOULD_NEVER_HAPPEN; } zbx_ipc_message_free(message); zbx_vector_uint64_destroy(&eventids); zbx_vector_ptr_destroy(&events); } if (NULL != client) zbx_ipc_client_release(client); if (NULL != message) continue; if (0 == timeout.sec) break; if (!ZBX_IS_RUNNING()) timeout.sec = 0; } service_manager_free(&service_manager); zbx_db_close(); exit(EXIT_SUCCESS); #undef STAT_INTERVAL }