From: Costa Tsaousis (ktsaou) Date: Sun, 19 Feb 2017 15:39:09 +0000 (+0200) Subject: RRDHOSTs free all their memory (all substructures) when they are deallocated X-Git-Tag: ab-debian_0.20170227.01-0ab1~1^2~14^2~58 X-Git-Url: https://arthur.barton.de/gitweb/?p=netdata.git;a=commitdiff_plain;h=e19a6cb6bd31e175842dc4c3b61fd3fe78eeab2c RRDHOSTs free all their memory (all substructures) when they are deallocated --- diff --git a/CMakeLists.txt b/CMakeLists.txt index ae492c05..02e545d3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,7 +124,7 @@ set(NETDATA_SOURCE_FILES src/web_client.h src/web_server.c src/web_server.h - src/rrdhost.c src/rrdfamily.c src/rrdset.c src/rrddim.c src/health_log.c src/health_config.c src/health_json.c) + src/rrdhost.c src/rrdfamily.c src/rrdset.c src/rrddim.c src/health_log.c src/health_config.c src/health_json.c src/rrdcalc.c src/rrdcalctemplate.c src/rrdvar.c src/rrddimvar.c src/rrdsetvar.c) set(APPS_PLUGIN_SOURCE_FILES src/appconfig.c diff --git a/src/Makefile.am b/src/Makefile.am index b5694fca..9b006f41 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -69,6 +69,11 @@ netdata_SOURCES = \ rrdfamily.c \ rrdhost.c \ rrdset.c \ + rrdcalc.c \ + rrdcalctemplate.c \ + rrdvar.c \ + rrddimvar.c \ + rrdsetvar.c \ rrd2json.c rrd2json.h \ storage_number.c storage_number.h \ unit_test.c unit_test.h \ diff --git a/src/health.c b/src/health.c index bd62fbcf..e3ded319 100644 --- a/src/health.c +++ b/src/health.c @@ -1,1068 +1,8 @@ #define NETDATA_HEALTH_INTERNALS #include "common.h" -#define RRDVAR_MAX_LENGTH 1024 - int default_localhost_health_enabled = 1; -// ---------------------------------------------------------------------------- -// RRDVAR management - -inline int rrdvar_fix_name(char *variable) { - int fixed = 0; - while(*variable) { - if (!isalnum(*variable) && *variable != '.' && *variable != '_') { - *variable++ = '_'; - fixed++; - } - else - variable++; - } - - return fixed; -} - -int rrdvar_compare(void* a, void* b) { - if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1; - else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1; - else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name); -} - -static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) { - RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv)); - if(ret != rv) - debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name); - - return ret; -} - -static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) { - RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv)); - if(!ret) - error("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name); - - return ret; -} - -static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) { - RRDVAR tmp; - tmp.name = (char *)name; - tmp.hash = (hash)?hash:simple_hash(tmp.name); - - return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp); -} - -static inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) { - (void)host; - - if(!rv) return; - - if(tree) { - debug(D_VARIABLES, "Deleting variable '%s'", rv->name); - if(unlikely(!rrdvar_index_del(tree, rv))) - error("Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname); - } - - freez(rv->name); - freez(rv); -} - -static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value) { - char *variable = strdupz(name); - rrdvar_fix_name(variable); - uint32_t hash = simple_hash(variable); - - RRDVAR *rv = rrdvar_index_find(tree, variable, hash); - if(unlikely(!rv)) { - debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", variable, scope); - - rv = callocz(1, sizeof(RRDVAR)); - rv->name = variable; - rv->hash = hash; - rv->type = type; - rv->value = value; - - RRDVAR *ret = rrdvar_index_add(tree, rv); - if(unlikely(ret != rv)) { - debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", variable, scope); - rrdvar_free(NULL, NULL, rv); - rv = NULL; - } - else - debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope); - } - else { - debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope); - - // already exists - freez(variable); - - // this is important - // it must return NULL - not the existing variable - or double-free will happen - rv = NULL; - } - - return rv; -} - -// ---------------------------------------------------------------------------- -// CUSTOM VARIABLES - -RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) { - calculated_number *v = callocz(1, sizeof(calculated_number)); - *v = NAN; - RRDVAR *rv = rrdvar_create_and_index("host", &host->variables_root_index, name, RRDVAR_TYPE_CALCULATED_ALLOCATED, v); - if(unlikely(!rv)) { - free(v); - error("Requested variable '%s' already exists - possibly 2 plugins will be updating it at the same time", name); - - char *variable = strdupz(name); - rrdvar_fix_name(variable); - uint32_t hash = simple_hash(variable); - - rv = rrdvar_index_find(&host->variables_root_index, variable, hash); - } - - return rv; -} - -void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name) { - char *variable = strdupz(name); - rrdvar_fix_name(variable); - uint32_t hash = simple_hash(variable); - - RRDVAR *rv = rrdvar_index_find(&host->variables_root_index, variable, hash); - freez(variable); - - if(!rv) { - error("Attempted to remove variable '%s' from host '%s', but it does not exist.", name, host->hostname); - return; - } - - if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) { - error("Attempted to remove variable '%s' from host '%s', but it does not a custom allocated variable.", name, host->hostname); - return; - } - - if(!rrdvar_index_del(&host->variables_root_index, rv)) { - error("Attempted to remove variable '%s' from host '%s', but it cannot be found.", name, host->hostname); - return; - } - - freez(rv->name); - freez(rv->value); - freez(rv); -} - -void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) { - if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) - error("requested to set variable '%s' to value " CALCULATED_NUMBER_FORMAT " but the variable is not a custom one.", rv->name, value); - else { - calculated_number *v = rv->value; - *v = value; - } -} - -// ---------------------------------------------------------------------------- -// RRDVAR lookup - -static calculated_number rrdvar2number(RRDVAR *rv) { - switch(rv->type) { - case RRDVAR_TYPE_CALCULATED_ALLOCATED: - case RRDVAR_TYPE_CALCULATED: { - calculated_number *n = (calculated_number *)rv->value; - return *n; - } - - case RRDVAR_TYPE_TIME_T: { - time_t *n = (time_t *)rv->value; - return *n; - } - - case RRDVAR_TYPE_COLLECTED: { - collected_number *n = (collected_number *)rv->value; - return *n; - } - - case RRDVAR_TYPE_TOTAL: { - total_number *n = (total_number *)rv->value; - return *n; - } - - case RRDVAR_TYPE_INT: { - int *n = (int *)rv->value; - return *n; - } - - default: - error("I don't know how to convert RRDVAR type %d to calculated_number", rv->type); - return NAN; - } -} - -int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) { - RRDSET *st = rc->rrdset; - RRDVAR *rv; - - if(!st) return 0; - - rv = rrdvar_index_find(&st->variables_root_index, variable, hash); - if(rv) { - *result = rrdvar2number(rv); - return 1; - } - - rv = rrdvar_index_find(&st->rrdfamily->variables_root_index, variable, hash); - if(rv) { - *result = rrdvar2number(rv); - return 1; - } - - rv = rrdvar_index_find(&st->rrdhost->variables_root_index, variable, hash); - if(rv) { - *result = rrdvar2number(rv); - return 1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RRDVAR to JSON - -struct variable2json_helper { - BUFFER *buf; - size_t counter; -}; - -static int single_variable2json(void *entry, void *data) { - struct variable2json_helper *helper = (struct variable2json_helper *)data; - RRDVAR *rv = (RRDVAR *)entry; - calculated_number value = rrdvar2number(rv); - - if(unlikely(isnan(value) || isinf(value))) - buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name); - else - buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value); - - helper->counter++; - - return 0; -} - -void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) { - struct variable2json_helper helper = { - .buf = buf, - .counter = 0 - }; - - buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context); - avl_traverse_lock(&st->variables_root_index, single_variable2json, (void *)&helper); - buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family); - helper.counter = 0; - avl_traverse_lock(&st->rrdfamily->variables_root_index, single_variable2json, (void *)&helper); - buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", st->rrdhost->hostname); - helper.counter = 0; - avl_traverse_lock(&st->rrdhost->variables_root_index, single_variable2json, (void *)&helper); - buffer_strcat(buf, "\n\t}\n}\n"); -} - - -// ---------------------------------------------------------------------------- -// RRDDIMVAR management -// DIMENSION VARIABLES - -#define RRDDIMVAR_ID_MAX 1024 - -static inline void rrddimvar_free_variables(RRDDIMVAR *rs) { - RRDDIM *rd = rs->rrddim; - RRDSET *st = rd->rrdset; - - // CHART VARIABLES FOR THIS DIMENSION - - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_id); - rs->var_local_id = NULL; - - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_name); - rs->var_local_name = NULL; - - // FAMILY VARIABLES FOR THIS DIMENSION - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_id); - rs->var_family_id = NULL; - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); - rs->var_family_name = NULL; - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextid); - rs->var_family_contextid = NULL; - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextname); - rs->var_family_contextname = NULL; - - // HOST VARIABLES FOR THIS DIMENSION - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidid); - rs->var_host_chartidid = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidname); - rs->var_host_chartidname = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnameid); - rs->var_host_chartnameid = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnamename); - rs->var_host_chartnamename = NULL; - - // KEYS - - freez(rs->key_id); - rs->key_id = NULL; - - freez(rs->key_name); - rs->key_name = NULL; - - freez(rs->key_fullidid); - rs->key_fullidid = NULL; - - freez(rs->key_fullidname); - rs->key_fullidname = NULL; - - freez(rs->key_contextid); - rs->key_contextid = NULL; - - freez(rs->key_contextname); - rs->key_contextname = NULL; - - freez(rs->key_fullnameid); - rs->key_fullnameid = NULL; - - freez(rs->key_fullnamename); - rs->key_fullnamename = NULL; -} - -static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { - rrddimvar_free_variables(rs); - - RRDDIM *rd = rs->rrddim; - RRDSET *st = rd->rrdset; - - char buffer[RRDDIMVAR_ID_MAX + 1]; - - // KEYS - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix); - rs->key_id = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix); - rs->key_name = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_id); - rs->key_fullidid = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_name); - rs->key_fullidname = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_id); - rs->key_contextid = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_name); - rs->key_contextname = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_id); - rs->key_fullnameid = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_name); - rs->key_fullnamename = strdupz(buffer); - - // CHART VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $id - // - $name - - rs->var_local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_id, rs->type, rs->value); - rs->var_local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_name, rs->type, rs->value); - - // FAMILY VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $id (only the first, when multiple overlap) - // - $name (only the first, when multiple overlap) - // - $chart-context.id - // - $chart-context.name - - rs->var_family_id = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_id, rs->type, rs->value); - rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_name, rs->type, rs->value); - rs->var_family_contextid = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextid, rs->type, rs->value); - rs->var_family_contextname = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextname, rs->type, rs->value); - - // HOST VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $chart-id.id - // - $chart-id.name - // - $chart-name.id - // - $chart-name.name - - rs->var_host_chartidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidid, rs->type, rs->value); - rs->var_host_chartidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidname, rs->type, rs->value); - rs->var_host_chartnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnameid, rs->type, rs->value); - rs->var_host_chartnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnamename, rs->type, rs->value); -} - -RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) { - RRDSET *st = rd->rrdset; - - debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:""); - - if(!prefix) prefix = ""; - if(!suffix) suffix = ""; - - RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR)); - - rs->prefix = strdupz(prefix); - rs->suffix = strdupz(suffix); - - rs->type = type; - rs->value = value; - rs->options = options; - rs->rrddim = rd; - - rs->next = rd->variables; - rd->variables = rs; - - rrddimvar_create_variables(rs); - - return rs; -} - -void rrddimvar_rename_all(RRDDIM *rd) { - RRDSET *st = rd->rrdset; - debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); - - RRDDIMVAR *rs, *next = rd->variables; - while((rs = next)) { - next = rs->next; - rrddimvar_create_variables(rs); - } -} - -void rrddimvar_free(RRDDIMVAR *rs) { - RRDDIM *rd = rs->rrddim; - RRDSET *st = rd->rrdset; - debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix); - - rrddimvar_free_variables(rs); - - if(rd->variables == rs) { - debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); - rd->variables = rs->next; - } - else { - debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); - RRDDIMVAR *t; - for (t = rd->variables; t && t->next != rs; t = t->next) ; - if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->key_name, st->id, rd->id); - else t->next = rs->next; - } - - freez(rs->prefix); - freez(rs->suffix); - freez(rs); -} - -// ---------------------------------------------------------------------------- -// RRDSETVAR management -// CHART VARIABLES - -static inline void rrdsetvar_free_variables(RRDSETVAR *rs) { - RRDSET *st = rs->rrdset; - - // CHART - - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local); - rs->var_local = NULL; - - // FAMILY - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family); - rs->var_family = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host); - rs->var_host = NULL; - - // HOST - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); - rs->var_family_name = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_name); - rs->var_host_name = NULL; - - // KEYS - - freez(rs->key_fullid); - rs->key_fullid = NULL; - - freez(rs->key_fullname); - rs->key_fullname = NULL; -} - -static inline void rrdsetvar_create_variables(RRDSETVAR *rs) { - rrdsetvar_free_variables(rs); - - RRDSET *st = rs->rrdset; - - // KEYS - - char buffer[RRDVAR_MAX_LENGTH + 1]; - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rs->variable); - rs->key_fullid = strdupz(buffer); - - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable); - rs->key_fullname = strdupz(buffer); - - // CHART - - rs->var_local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->type, rs->value); - - // FAMILY - - rs->var_family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullid, rs->type, rs->value); - rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullname, rs->type, rs->value); - - // HOST - - rs->var_host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullid, rs->type, rs->value); - rs->var_host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullname, rs->type, rs->value); - -} - -RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) { - debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable); - RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR)); - - rs->variable = strdupz(variable); - rs->type = type; - rs->value = value; - rs->options = options; - rs->rrdset = st; - - rs->next = st->variables; - st->variables = rs; - - rrdsetvar_create_variables(rs); - - return rs; -} - -void rrdsetvar_rename_all(RRDSET *st) { - debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name); - - RRDSETVAR *rs, *next = st->variables; - while((rs = next)) { - next = rs->next; - rrdsetvar_create_variables(rs); - } - - rrdsetcalc_link_matching(st); -} - -void rrdsetvar_free(RRDSETVAR *rs) { - RRDSET *st = rs->rrdset; - debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable); - - if(st->variables == rs) { - st->variables = rs->next; - } - else { - RRDSETVAR *t; - for (t = st->variables; t && t->next != rs; t = t->next); - if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->key_fullname, st->id); - else t->next = rs->next; - } - - rrdsetvar_free_variables(rs); - - freez(rs->variable); - freez(rs); -} - -// ---------------------------------------------------------------------------- -// RRDCALC management - -inline const char *rrdcalc_status2string(int status) { - switch(status) { - case RRDCALC_STATUS_REMOVED: - return "REMOVED"; - - case RRDCALC_STATUS_UNDEFINED: - return "UNDEFINED"; - - case RRDCALC_STATUS_UNINITIALIZED: - return "UNINITIALIZED"; - - case RRDCALC_STATUS_CLEAR: - return "CLEAR"; - - case RRDCALC_STATUS_RAISED: - return "RAISED"; - - case RRDCALC_STATUS_WARNING: - return "WARNING"; - - case RRDCALC_STATUS_CRITICAL: - return "CRITICAL"; - - default: - error("Unknown alarm status %d", status); - return "UNKNOWN"; - } -} - -static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) { - debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, st->rrdhost->hostname); - - rc->last_status_change = now_realtime_sec(); - rc->rrdset = st; - - rc->rrdset_next = st->alarms; - rc->rrdset_prev = NULL; - - if(rc->rrdset_next) - rc->rrdset_next->rrdset_prev = rc; - - st->alarms = rc; - - if(rc->update_every < rc->rrdset->update_every) { - error("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rc->rrdset->id, rc->name, rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every); - rc->update_every = rc->rrdset->update_every; - } - - if(!isnan(rc->green) && isnan(st->green)) { - debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->green, rc->green); - st->green = rc->green; - } - - if(!isnan(rc->red) && isnan(st->red)) { - debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->red, rc->red); - st->red = rc->red; - } - - rc->local = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value); - rc->family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value); - - char fullname[RRDVAR_MAX_LENGTH + 1]; - snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rc->name); - rc->hostid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value); - - snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rc->name); - rc->hostname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value); - - if(!rc->units) rc->units = strdupz(st->units); - - { - time_t now = now_realtime_sec(); - health_alarm_log( - st->rrdhost, - rc->id, - rc->next_event_id++, - now, - rc->name, - rc->rrdset->id, - rc->rrdset->family, - rc->exec, - rc->recipient, - now - rc->last_status_change, - rc->old_value, - rc->value, - rc->status, - RRDCALC_STATUS_UNINITIALIZED, - rc->source, - rc->units, - rc->info, - 0, - 0 - ); - } -} - -static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) { - if( (rc->hash_chart == st->hash && !strcmp(rc->chart, st->id)) || - (rc->hash_chart == st->hash_name && !strcmp(rc->chart, st->name))) - return 1; - - return 0; -} - -// this has to be called while the RRDHOST is locked -inline void rrdsetcalc_link_matching(RRDSET *st) { - // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id); - - RRDCALC *rc; - for(rc = st->rrdhost->alarms; rc ; rc = rc->next) { - if(unlikely(rc->rrdset)) - continue; - - if(unlikely(rrdcalc_is_matching_this_rrdset(rc, st))) - rrdsetcalc_link(st, rc); - } -} - -// this has to be called while the RRDHOST is locked -inline void rrdsetcalc_unlink(RRDCALC *rc) { - RRDSET *st = rc->rrdset; - - if(!st) { - debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); - error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); - return; - } - - { - time_t now = now_realtime_sec(); - health_alarm_log( - st->rrdhost, - rc->id, - rc->next_event_id++, - now, - rc->name, - rc->rrdset->id, - rc->rrdset->family, - rc->exec, - rc->recipient, - now - rc->last_status_change, - rc->old_value, - rc->value, - rc->status, - RRDCALC_STATUS_REMOVED, - rc->source, - rc->units, - rc->info, - 0, - 0 - ); - } - - RRDHOST *host = st->rrdhost; - - debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname); - - // unlink it - if(rc->rrdset_prev) - rc->rrdset_prev->rrdset_next = rc->rrdset_next; - - if(rc->rrdset_next) - rc->rrdset_next->rrdset_prev = rc->rrdset_prev; - - if(st->alarms == rc) - st->alarms = rc->rrdset_next; - - rc->rrdset_prev = rc->rrdset_next = NULL; - - rrdvar_free(st->rrdhost, &st->variables_root_index, rc->local); - rc->local = NULL; - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rc->family); - rc->family = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostid); - rc->hostid = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostname); - rc->hostname = NULL; - - rc->rrdset = NULL; - - // RRDCALC will remain in RRDHOST - // so that if the matching chart is found in the future - // it will be applied automatically -} - -RRDCALC *rrdcalc_find(RRDSET *st, const char *name) { - RRDCALC *rc; - uint32_t hash = simple_hash(name); - - for( rc = st->alarms; rc ; rc = rc->rrdset_next ) { - if(unlikely(rc->hash == hash && !strcmp(rc->name, name))) - return rc; - } - - return NULL; -} - -inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) { - RRDCALC *rc; - - if(unlikely(!chart)) { - error("attempt to find RRDCALC '%s' without giving a chart name", name); - return 1; - } - - if(unlikely(!hash_chart)) hash_chart = simple_hash(chart); - if(unlikely(!hash_name)) hash_name = simple_hash(name); - - // make sure it does not already exist - for(rc = host->alarms; rc ; rc = rc->next) { - if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) { - debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); - error("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); - return 1; - } - } - - return 0; -} - -inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) { - if(chart && name) { - uint32_t hash_chart = simple_hash(chart); - uint32_t hash_name = simple_hash(name); - - // re-use old IDs, by looking them up in the alarm log - ALARM_ENTRY *ae; - for(ae = host->health_log.alarms; ae ;ae = ae->next) { - if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) { - if(next_event_id) *next_event_id = ae->alarm_event_id + 1; - return ae->alarm_id; - } - } - } - - return host->health_log.next_alarm_id++; -} - -inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) { - rrdhost_check_rdlock(host); - - if(rc->calculation) { - rc->calculation->status = &rc->status; - rc->calculation->this = &rc->value; - rc->calculation->after = &rc->db_after; - rc->calculation->before = &rc->db_before; - rc->calculation->rrdcalc = rc; - } - - if(rc->warning) { - rc->warning->status = &rc->status; - rc->warning->this = &rc->value; - rc->warning->after = &rc->db_after; - rc->warning->before = &rc->db_before; - rc->warning->rrdcalc = rc; - } - - if(rc->critical) { - rc->critical->status = &rc->status; - rc->critical->this = &rc->value; - rc->critical->after = &rc->db_after; - rc->critical->before = &rc->db_before; - rc->critical->rrdcalc = rc; - } - - // link it to the host - if(likely(host->alarms)) { - // append it - RRDCALC *t; - for(t = host->alarms; t && t->next ; t = t->next) ; - t->next = rc; - } - else { - host->alarms = rc; - } - - // link it to its chart - RRDSET *st; - for(st = host->rrdset_root; st ; st = st->next) { - if(rrdcalc_is_matching_this_rrdset(rc, st)) { - rrdsetcalc_link(st, rc); - break; - } - } -} - -static inline RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) { - - debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name); - - if(rrdcalc_exists(host, chart, rt->name, 0, 0)) - return NULL; - - RRDCALC *rc = callocz(1, sizeof(RRDCALC)); - rc->next_event_id = 1; - rc->id = rrdcalc_get_unique_id(host, chart, rt->name, &rc->next_event_id); - rc->name = strdupz(rt->name); - rc->hash = simple_hash(rc->name); - rc->chart = strdupz(chart); - rc->hash_chart = simple_hash(rc->chart); - - if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions); - - rc->green = rt->green; - rc->red = rt->red; - rc->value = NAN; - rc->old_value = NAN; - - rc->delay_up_duration = rt->delay_up_duration; - rc->delay_down_duration = rt->delay_down_duration; - rc->delay_max_duration = rt->delay_max_duration; - rc->delay_multiplier = rt->delay_multiplier; - - rc->group = rt->group; - rc->after = rt->after; - rc->before = rt->before; - rc->update_every = rt->update_every; - rc->options = rt->options; - - if(rt->exec) rc->exec = strdupz(rt->exec); - if(rt->recipient) rc->recipient = strdupz(rt->recipient); - if(rt->source) rc->source = strdupz(rt->source); - if(rt->units) rc->units = strdupz(rt->units); - if(rt->info) rc->info = strdupz(rt->info); - - if(rt->calculation) { - rc->calculation = expression_parse(rt->calculation->source, NULL, NULL); - if(!rc->calculation) - error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source); - } - if(rt->warning) { - rc->warning = expression_parse(rt->warning->source, NULL, NULL); - if(!rc->warning) - error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source); - } - if(rt->critical) { - rc->critical = expression_parse(rt->critical->source, NULL, NULL); - if(!rc->critical) - error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source); - } - - debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f", - (rc->chart)?rc->chart:"NOCHART", - rc->name, - (rc->exec)?rc->exec:"DEFAULT", - (rc->recipient)?rc->recipient:"DEFAULT", - rc->green, - rc->red, - rc->group, - rc->after, - rc->before, - rc->options, - (rc->dimensions)?rc->dimensions:"NONE", - rc->update_every, - (rc->calculation)?rc->calculation->parsed_as:"NONE", - (rc->warning)?rc->warning->parsed_as:"NONE", - (rc->critical)?rc->critical->parsed_as:"NONE", - rc->source, - rc->delay_up_duration, - rc->delay_down_duration, - rc->delay_max_duration, - rc->delay_multiplier - ); - - rrdcalc_create_part2(host, rc); - return rc; -} - -void rrdcalc_free(RRDHOST *host, RRDCALC *rc) { - if(!rc) return; - - debug(D_HEALTH, "Health removing alarm '%s.%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname); - - // unlink it from RRDSET - if(rc->rrdset) rrdsetcalc_unlink(rc); - - // unlink it from RRDHOST - if(unlikely(rc == host->alarms)) - host->alarms = rc->next; - - else if(likely(host->alarms)) { - RRDCALC *t, *last = host->alarms; - for(t = last->next; t && t != rc; last = t, t = t->next) ; - if(last->next == rc) - last->next = rc->next; - else - error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname); - } - else - error("Cannot unlink unlink '%s.%s' from host '%s': This host does not have any calculations", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname); - - expression_free(rc->calculation); - expression_free(rc->warning); - expression_free(rc->critical); - - freez(rc->name); - freez(rc->chart); - freez(rc->family); - freez(rc->dimensions); - freez(rc->exec); - freez(rc->recipient); - freez(rc->source); - freez(rc->units); - freez(rc->info); - freez(rc); -} - -// ---------------------------------------------------------------------------- -// RRDCALCTEMPLATE management - -void rrdcalctemplate_link_matching(RRDSET *st) { - RRDCALCTEMPLATE *rt; - - for(rt = st->rrdhost->templates; rt ; rt = rt->next) { - if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context) - && (!rt->family_pattern || simple_pattern_matches(rt->family_pattern, st->family))) { - RRDCALC *rc = rrdcalc_create(st->rrdhost, rt, st->id); - if(unlikely(!rc)) - error("Health tried to create alarm from template '%s', but it failed", rt->name); - -#ifdef NETDATA_INTERNAL_CHECKS - else if(rc->rrdset != st) - error("Health alarm '%s.%s' should be linked to chart '%s', but it is not", rc->chart?rc->chart:"NOCHART", rc->name, st->id); -#endif - } - } -} - -inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) { - debug(D_HEALTH, "Health removing template '%s' of host '%s'", rt->name, host->hostname); - - if(host->templates) { - if(host->templates == rt) { - host->templates = rt->next; - } - else { - RRDCALCTEMPLATE *t, *last = host->templates; - for (t = last->next; t && t != rt; last = t, t = t->next ) ; - if(last && last->next == rt) { - last->next = rt->next; - rt->next = NULL; - } - else - error("Cannot find RRDCALCTEMPLATE '%s' linked in host '%s'", rt->name, host->hostname); - } - } - - expression_free(rt->calculation); - expression_free(rt->warning); - expression_free(rt->critical); - - freez(rt->family_match); - simple_pattern_free(rt->family_pattern); - - freez(rt->name); - freez(rt->exec); - freez(rt->recipient); - freez(rt->context); - freez(rt->source); - freez(rt->units); - freez(rt->info); - freez(rt->dimensions); - freez(rt); -} - // ---------------------------------------------------------------------------- // health initialization @@ -1089,20 +29,18 @@ void health_init(void) { // ---------------------------------------------------------------------------- // re-load health configuration -inline void health_free_host_nolock(RRDHOST *host) { +void health_reload_host(RRDHOST *host) { + char *path = health_config_dir(); + + // free all running alarms + rrdhost_wrlock(host); + while(host->templates) rrdcalctemplate_free(host, host->templates); while(host->alarms) rrdcalc_free(host, host->alarms); -} -void health_reload_host(RRDHOST *host) { - char *path = health_config_dir(); - - // free all running alarms - rrdhost_wrlock(host); - health_free_host_nolock(host); rrdhost_unlock(host); // invalidate all previous entries in the alarm log @@ -1311,17 +249,7 @@ static inline void health_alarm_log_process(RRDHOST *host) { ALARM_ENTRY *t = ae->next; - freez(ae->name); - freez(ae->chart); - freez(ae->family); - freez(ae->exec); - freez(ae->recipient); - freez(ae->source); - freez(ae->units); - freez(ae->info); - freez(ae->old_value_string); - freez(ae->new_value_string); - freez(ae); + health_alarm_log_free_one_nochecks_nounlink(ae); ae = t; host->health_log.count--; diff --git a/src/health.h b/src/health.h index 68637f5e..aeb7ef30 100644 --- a/src/health.h +++ b/src/health.h @@ -400,16 +400,27 @@ extern void health_alarm_log( extern void health_readdir(RRDHOST *host, const char *path); extern char *health_config_dir(void); -extern void health_free_host_nolock(RRDHOST *host); extern void health_reload_host(RRDHOST *host); +extern void health_alarm_log_free(RRDHOST *host); + +extern void rrdcalc_free(RRDHOST *host, RRDCALC *rc); +extern void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt); #ifdef NETDATA_HEALTH_INTERNALS +#define RRDVAR_MAX_LENGTH 1024 + extern int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name); extern uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id); -extern void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc); -extern void rrdcalc_free(RRDHOST *host, RRDCALC *rc); -extern void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt); extern int rrdvar_fix_name(char *variable); -#endif + +extern RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart); +extern void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc); + +extern RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value); +extern void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv); + +extern void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae); + +#endif // NETDATA_HEALTH_INTERNALS #endif //NETDATA_HEALTH_H diff --git a/src/health_log.c b/src/health_log.c index 9c6975b7..eb13169b 100644 --- a/src/health_log.c +++ b/src/health_log.c @@ -436,3 +436,30 @@ inline void health_alarm_log( health_alarm_log_save(host, ae); } +inline void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae) { + freez(ae->name); + freez(ae->chart); + freez(ae->family); + freez(ae->exec); + freez(ae->recipient); + freez(ae->source); + freez(ae->units); + freez(ae->info); + freez(ae->old_value_string); + freez(ae->new_value_string); + freez(ae); +} + +inline void health_alarm_log_free(RRDHOST *host) { + rrdhost_check_wrlock(host); + + pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock); + + ALARM_ENTRY *ae; + while((ae = host->health_log.alarms)) { + host->health_log.alarms = ae->next; + health_alarm_log_free_one_nochecks_nounlink(ae); + } + + pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock); +} diff --git a/src/plugin_idlejitter.c b/src/plugin_idlejitter.c index 66b3b604..2ed78160 100644 --- a/src/plugin_idlejitter.c +++ b/src/plugin_idlejitter.c @@ -31,6 +31,8 @@ void *cpuidlejitter_main(void *ptr) { for(counter = 0; 1 ;counter++) { usec_t usec = 0, susec = 0; + if(netdata_exit) break; + while(susec < (localhost->rrd_update_every * USEC_PER_SEC)) { now_monotonic_timeval(&before); diff --git a/src/rrd.h b/src/rrd.h index ad75c3d6..2247e9a4 100644 --- a/src/rrd.h +++ b/src/rrd.h @@ -371,14 +371,27 @@ extern RRDHOST *rrdhost_find_or_create(const char *hostname, const char *guid); #ifdef NETDATA_INTERNAL_CHECKS #define rrdhost_check_rdlock(host) rrdhost_check_rdlock_int(host, __FILE__, __FUNCTION__, __LINE__) #define rrdhost_check_wrlock(host) rrdhost_check_wrlock_int(host, __FILE__, __FUNCTION__, __LINE__) +#define rrd_check_rdlock() rrd_check_rdlock_int(__FILE__, __FUNCTION__, __LINE__) +#define rrd_check_wrlock() rrd_check_wrlock_int(__FILE__, __FUNCTION__, __LINE__) #else #define rrdhost_check_rdlock(host) (void)0 #define rrdhost_check_wrlock(host) (void)0 +#define rrd_check_rdlock() (void)0 +#define rrd_check_wrlock() (void)0 #endif extern void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line); extern void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line); +// ---------------------------------------------------------------------------- +// global lock for all RRDHOSTs + +extern pthread_rwlock_t rrd_rwlock; +#define rrd_rdlock() pthread_rwlock_rdlock(&rrd_rwlock) +#define rrd_wrlock() pthread_rwlock_wrlock(&rrd_rwlock) +#define rrd_unlock() pthread_rwlock_unlock(&rrd_rwlock) + + // ---------------------------------------------------------------------------- // RRDSET functions diff --git a/src/rrdcalc.c b/src/rrdcalc.c new file mode 100644 index 00000000..bdd66c2b --- /dev/null +++ b/src/rrdcalc.c @@ -0,0 +1,415 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDCALC management + +inline const char *rrdcalc_status2string(int status) { + switch(status) { + case RRDCALC_STATUS_REMOVED: + return "REMOVED"; + + case RRDCALC_STATUS_UNDEFINED: + return "UNDEFINED"; + + case RRDCALC_STATUS_UNINITIALIZED: + return "UNINITIALIZED"; + + case RRDCALC_STATUS_CLEAR: + return "CLEAR"; + + case RRDCALC_STATUS_RAISED: + return "RAISED"; + + case RRDCALC_STATUS_WARNING: + return "WARNING"; + + case RRDCALC_STATUS_CRITICAL: + return "CRITICAL"; + + default: + error("Unknown alarm status %d", status); + return "UNKNOWN"; + } +} + +static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) { + debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, st->rrdhost->hostname); + + rc->last_status_change = now_realtime_sec(); + rc->rrdset = st; + + rc->rrdset_next = st->alarms; + rc->rrdset_prev = NULL; + + if(rc->rrdset_next) + rc->rrdset_next->rrdset_prev = rc; + + st->alarms = rc; + + if(rc->update_every < rc->rrdset->update_every) { + error("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rc->rrdset->id, rc->name, rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every); + rc->update_every = rc->rrdset->update_every; + } + + if(!isnan(rc->green) && isnan(st->green)) { + debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->green, rc->green); + st->green = rc->green; + } + + if(!isnan(rc->red) && isnan(st->red)) { + debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->red, rc->red); + st->red = rc->red; + } + + rc->local = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value); + rc->family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value); + + char fullname[RRDVAR_MAX_LENGTH + 1]; + snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rc->name); + rc->hostid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value); + + snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rc->name); + rc->hostname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value); + + if(!rc->units) rc->units = strdupz(st->units); + + { + time_t now = now_realtime_sec(); + health_alarm_log( + st->rrdhost, + rc->id, + rc->next_event_id++, + now, + rc->name, + rc->rrdset->id, + rc->rrdset->family, + rc->exec, + rc->recipient, + now - rc->last_status_change, + rc->old_value, + rc->value, + rc->status, + RRDCALC_STATUS_UNINITIALIZED, + rc->source, + rc->units, + rc->info, + 0, + 0 + ); + } +} + +static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) { + if( (rc->hash_chart == st->hash && !strcmp(rc->chart, st->id)) || + (rc->hash_chart == st->hash_name && !strcmp(rc->chart, st->name))) + return 1; + + return 0; +} + +// this has to be called while the RRDHOST is locked +inline void rrdsetcalc_link_matching(RRDSET *st) { + // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id); + + RRDCALC *rc; + for(rc = st->rrdhost->alarms; rc ; rc = rc->next) { + if(unlikely(rc->rrdset)) + continue; + + if(unlikely(rrdcalc_is_matching_this_rrdset(rc, st))) + rrdsetcalc_link(st, rc); + } +} + +// this has to be called while the RRDHOST is locked +inline void rrdsetcalc_unlink(RRDCALC *rc) { + RRDSET *st = rc->rrdset; + + if(!st) { + debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); + error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); + return; + } + + { + time_t now = now_realtime_sec(); + health_alarm_log( + st->rrdhost, + rc->id, + rc->next_event_id++, + now, + rc->name, + rc->rrdset->id, + rc->rrdset->family, + rc->exec, + rc->recipient, + now - rc->last_status_change, + rc->old_value, + rc->value, + rc->status, + RRDCALC_STATUS_REMOVED, + rc->source, + rc->units, + rc->info, + 0, + 0 + ); + } + + RRDHOST *host = st->rrdhost; + + debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname); + + // unlink it + if(rc->rrdset_prev) + rc->rrdset_prev->rrdset_next = rc->rrdset_next; + + if(rc->rrdset_next) + rc->rrdset_next->rrdset_prev = rc->rrdset_prev; + + if(st->alarms == rc) + st->alarms = rc->rrdset_next; + + rc->rrdset_prev = rc->rrdset_next = NULL; + + rrdvar_free(st->rrdhost, &st->variables_root_index, rc->local); + rc->local = NULL; + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rc->family); + rc->family = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostid); + rc->hostid = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostname); + rc->hostname = NULL; + + rc->rrdset = NULL; + + // RRDCALC will remain in RRDHOST + // so that if the matching chart is found in the future + // it will be applied automatically +} + +RRDCALC *rrdcalc_find(RRDSET *st, const char *name) { + RRDCALC *rc; + uint32_t hash = simple_hash(name); + + for( rc = st->alarms; rc ; rc = rc->rrdset_next ) { + if(unlikely(rc->hash == hash && !strcmp(rc->name, name))) + return rc; + } + + return NULL; +} + +inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) { + RRDCALC *rc; + + if(unlikely(!chart)) { + error("attempt to find RRDCALC '%s' without giving a chart name", name); + return 1; + } + + if(unlikely(!hash_chart)) hash_chart = simple_hash(chart); + if(unlikely(!hash_name)) hash_name = simple_hash(name); + + // make sure it does not already exist + for(rc = host->alarms; rc ; rc = rc->next) { + if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) { + debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); + error("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); + return 1; + } + } + + return 0; +} + +inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) { + if(chart && name) { + uint32_t hash_chart = simple_hash(chart); + uint32_t hash_name = simple_hash(name); + + // re-use old IDs, by looking them up in the alarm log + ALARM_ENTRY *ae; + for(ae = host->health_log.alarms; ae ;ae = ae->next) { + if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) { + if(next_event_id) *next_event_id = ae->alarm_event_id + 1; + return ae->alarm_id; + } + } + } + + return host->health_log.next_alarm_id++; +} + +inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) { + rrdhost_check_rdlock(host); + + if(rc->calculation) { + rc->calculation->status = &rc->status; + rc->calculation->this = &rc->value; + rc->calculation->after = &rc->db_after; + rc->calculation->before = &rc->db_before; + rc->calculation->rrdcalc = rc; + } + + if(rc->warning) { + rc->warning->status = &rc->status; + rc->warning->this = &rc->value; + rc->warning->after = &rc->db_after; + rc->warning->before = &rc->db_before; + rc->warning->rrdcalc = rc; + } + + if(rc->critical) { + rc->critical->status = &rc->status; + rc->critical->this = &rc->value; + rc->critical->after = &rc->db_after; + rc->critical->before = &rc->db_before; + rc->critical->rrdcalc = rc; + } + + // link it to the host + if(likely(host->alarms)) { + // append it + RRDCALC *t; + for(t = host->alarms; t && t->next ; t = t->next) ; + t->next = rc; + } + else { + host->alarms = rc; + } + + // link it to its chart + RRDSET *st; + for(st = host->rrdset_root; st ; st = st->next) { + if(rrdcalc_is_matching_this_rrdset(rc, st)) { + rrdsetcalc_link(st, rc); + break; + } + } +} + +inline RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) { + + debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name); + + if(rrdcalc_exists(host, chart, rt->name, 0, 0)) + return NULL; + + RRDCALC *rc = callocz(1, sizeof(RRDCALC)); + rc->next_event_id = 1; + rc->id = rrdcalc_get_unique_id(host, chart, rt->name, &rc->next_event_id); + rc->name = strdupz(rt->name); + rc->hash = simple_hash(rc->name); + rc->chart = strdupz(chart); + rc->hash_chart = simple_hash(rc->chart); + + if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions); + + rc->green = rt->green; + rc->red = rt->red; + rc->value = NAN; + rc->old_value = NAN; + + rc->delay_up_duration = rt->delay_up_duration; + rc->delay_down_duration = rt->delay_down_duration; + rc->delay_max_duration = rt->delay_max_duration; + rc->delay_multiplier = rt->delay_multiplier; + + rc->group = rt->group; + rc->after = rt->after; + rc->before = rt->before; + rc->update_every = rt->update_every; + rc->options = rt->options; + + if(rt->exec) rc->exec = strdupz(rt->exec); + if(rt->recipient) rc->recipient = strdupz(rt->recipient); + if(rt->source) rc->source = strdupz(rt->source); + if(rt->units) rc->units = strdupz(rt->units); + if(rt->info) rc->info = strdupz(rt->info); + + if(rt->calculation) { + rc->calculation = expression_parse(rt->calculation->source, NULL, NULL); + if(!rc->calculation) + error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source); + } + if(rt->warning) { + rc->warning = expression_parse(rt->warning->source, NULL, NULL); + if(!rc->warning) + error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source); + } + if(rt->critical) { + rc->critical = expression_parse(rt->critical->source, NULL, NULL); + if(!rc->critical) + error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source); + } + + debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f", + (rc->chart)?rc->chart:"NOCHART", + rc->name, + (rc->exec)?rc->exec:"DEFAULT", + (rc->recipient)?rc->recipient:"DEFAULT", + rc->green, + rc->red, + rc->group, + rc->after, + rc->before, + rc->options, + (rc->dimensions)?rc->dimensions:"NONE", + rc->update_every, + (rc->calculation)?rc->calculation->parsed_as:"NONE", + (rc->warning)?rc->warning->parsed_as:"NONE", + (rc->critical)?rc->critical->parsed_as:"NONE", + rc->source, + rc->delay_up_duration, + rc->delay_down_duration, + rc->delay_max_duration, + rc->delay_multiplier + ); + + rrdcalc_create_part2(host, rc); + return rc; +} + +void rrdcalc_free(RRDHOST *host, RRDCALC *rc) { + if(!rc) return; + + debug(D_HEALTH, "Health removing alarm '%s.%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname); + + // unlink it from RRDSET + if(rc->rrdset) rrdsetcalc_unlink(rc); + + // unlink it from RRDHOST + if(unlikely(rc == host->alarms)) + host->alarms = rc->next; + + else { + RRDCALC *t; + for(t = host->alarms; t && t->next != rc; t = t->next) ; + if(t) { + t->next = rc->next; + rc->next = NULL; + } + else + error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname); + } + + expression_free(rc->calculation); + expression_free(rc->warning); + expression_free(rc->critical); + + freez(rc->name); + freez(rc->chart); + freez(rc->family); + freez(rc->dimensions); + freez(rc->exec); + freez(rc->recipient); + freez(rc->source); + freez(rc->units); + freez(rc->info); + freez(rc); +} diff --git a/src/rrdcalctemplate.c b/src/rrdcalctemplate.c new file mode 100644 index 00000000..1deec43a --- /dev/null +++ b/src/rrdcalctemplate.c @@ -0,0 +1,60 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDCALCTEMPLATE management + +void rrdcalctemplate_link_matching(RRDSET *st) { + RRDCALCTEMPLATE *rt; + + for(rt = st->rrdhost->templates; rt ; rt = rt->next) { + if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context) + && (!rt->family_pattern || simple_pattern_matches(rt->family_pattern, st->family))) { + RRDCALC *rc = rrdcalc_create(st->rrdhost, rt, st->id); + if(unlikely(!rc)) + error("Health tried to create alarm from template '%s', but it failed", rt->name); + +#ifdef NETDATA_INTERNAL_CHECKS + else if(rc->rrdset != st) + error("Health alarm '%s.%s' should be linked to chart '%s', but it is not", rc->chart?rc->chart:"NOCHART", rc->name, st->id); +#endif + } + } +} + +inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) { + debug(D_HEALTH, "Health removing template '%s' of host '%s'", rt->name, host->hostname); + + if(host->templates == rt) { + host->templates = rt->next; + } + else { + RRDCALCTEMPLATE *t; + for (t = host->templates; t && t->next != rt; t = t->next ) ; + if(t) { + t->next = rt->next; + rt->next = NULL; + } + else + error("Cannot find RRDCALCTEMPLATE '%s' linked in host '%s'", rt->name, host->hostname); + } + + expression_free(rt->calculation); + expression_free(rt->warning); + expression_free(rt->critical); + + freez(rt->family_match); + simple_pattern_free(rt->family_pattern); + + freez(rt->name); + freez(rt->exec); + freez(rt->recipient); + freez(rt->context); + freez(rt->source); + freez(rt->units); + freez(rt->info); + freez(rt->dimensions); + freez(rt); +} + + diff --git a/src/rrddimvar.c b/src/rrddimvar.c new file mode 100644 index 00000000..f6eb6d8e --- /dev/null +++ b/src/rrddimvar.c @@ -0,0 +1,210 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDDIMVAR management +// DIMENSION VARIABLES + +#define RRDDIMVAR_ID_MAX 1024 + +static inline void rrddimvar_free_variables(RRDDIMVAR *rs) { + RRDDIM *rd = rs->rrddim; + RRDSET *st = rd->rrdset; + + // CHART VARIABLES FOR THIS DIMENSION + + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_id); + rs->var_local_id = NULL; + + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_name); + rs->var_local_name = NULL; + + // FAMILY VARIABLES FOR THIS DIMENSION + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_id); + rs->var_family_id = NULL; + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); + rs->var_family_name = NULL; + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextid); + rs->var_family_contextid = NULL; + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextname); + rs->var_family_contextname = NULL; + + // HOST VARIABLES FOR THIS DIMENSION + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidid); + rs->var_host_chartidid = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidname); + rs->var_host_chartidname = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnameid); + rs->var_host_chartnameid = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnamename); + rs->var_host_chartnamename = NULL; + + // KEYS + + freez(rs->key_id); + rs->key_id = NULL; + + freez(rs->key_name); + rs->key_name = NULL; + + freez(rs->key_fullidid); + rs->key_fullidid = NULL; + + freez(rs->key_fullidname); + rs->key_fullidname = NULL; + + freez(rs->key_contextid); + rs->key_contextid = NULL; + + freez(rs->key_contextname); + rs->key_contextname = NULL; + + freez(rs->key_fullnameid); + rs->key_fullnameid = NULL; + + freez(rs->key_fullnamename); + rs->key_fullnamename = NULL; +} + +static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { + rrddimvar_free_variables(rs); + + RRDDIM *rd = rs->rrddim; + RRDSET *st = rd->rrdset; + + char buffer[RRDDIMVAR_ID_MAX + 1]; + + // KEYS + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix); + rs->key_id = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix); + rs->key_name = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_id); + rs->key_fullidid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_name); + rs->key_fullidname = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_id); + rs->key_contextid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_name); + rs->key_contextname = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_id); + rs->key_fullnameid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_name); + rs->key_fullnamename = strdupz(buffer); + + // CHART VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $id + // - $name + + rs->var_local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_id, rs->type, rs->value); + rs->var_local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_name, rs->type, rs->value); + + // FAMILY VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $id (only the first, when multiple overlap) + // - $name (only the first, when multiple overlap) + // - $chart-context.id + // - $chart-context.name + + rs->var_family_id = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_id, rs->type, rs->value); + rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_name, rs->type, rs->value); + rs->var_family_contextid = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextid, rs->type, rs->value); + rs->var_family_contextname = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextname, rs->type, rs->value); + + // HOST VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $chart-id.id + // - $chart-id.name + // - $chart-name.id + // - $chart-name.name + + rs->var_host_chartidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidid, rs->type, rs->value); + rs->var_host_chartidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidname, rs->type, rs->value); + rs->var_host_chartnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnameid, rs->type, rs->value); + rs->var_host_chartnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnamename, rs->type, rs->value); +} + +RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) { + RRDSET *st = rd->rrdset; + + debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:""); + + if(!prefix) prefix = ""; + if(!suffix) suffix = ""; + + RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR)); + + rs->prefix = strdupz(prefix); + rs->suffix = strdupz(suffix); + + rs->type = type; + rs->value = value; + rs->options = options; + rs->rrddim = rd; + + rs->next = rd->variables; + rd->variables = rs; + + rrddimvar_create_variables(rs); + + return rs; +} + +void rrddimvar_rename_all(RRDDIM *rd) { + RRDSET *st = rd->rrdset; + debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); + + RRDDIMVAR *rs, *next = rd->variables; + while((rs = next)) { + next = rs->next; + rrddimvar_create_variables(rs); + } +} + +void rrddimvar_free(RRDDIMVAR *rs) { + RRDDIM *rd = rs->rrddim; + RRDSET *st = rd->rrdset; + debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix); + + rrddimvar_free_variables(rs); + + if(rd->variables == rs) { + debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); + rd->variables = rs->next; + } + else { + debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); + RRDDIMVAR *t; + for (t = rd->variables; t && t->next != rs; t = t->next) ; + if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->key_name, st->id, rd->id); + else t->next = rs->next; + } + + freez(rs->prefix); + freez(rs->suffix); + freez(rs); +} + diff --git a/src/rrdhost.c b/src/rrdhost.c index 7c6d6418..e44a12c3 100644 --- a/src/rrdhost.c +++ b/src/rrdhost.c @@ -3,6 +3,9 @@ RRDHOST *localhost = NULL; +pthread_rwlock_t rrd_rwlock = PTHREAD_RWLOCK_INITIALIZER; + + // ---------------------------------------------------------------------------- // RRDHOST index @@ -46,6 +49,7 @@ static inline void rrdhost_init_machine_guid(RRDHOST *host, const char *machine_ host->hash_machine_guid = simple_hash(host->machine_guid); } + // ---------------------------------------------------------------------------- // RRDHOST - add a host @@ -75,7 +79,6 @@ RRDHOST *rrdhost_create(const char *hostname, avl_init_lock(&(host->rrdfamily_root_index), rrdfamily_compare); avl_init_lock(&(host->variables_root_index), rrdvar_compare); - // ------------------------------------------------------------------------ // initialize health variables @@ -150,11 +153,20 @@ RRDHOST *rrdhost_create(const char *hostname, // ------------------------------------------------------------------------ - // add it to the index + // link it and add it to the index + + rrd_wrlock(); + + if(localhost) { + host->next = localhost->next; + localhost->next = host; + } if(rrdhost_index_add(host) != host) fatal("Cannot add host '%s' to index. It already exists.", hostname); + rrd_unlock(); + debug(D_RRDHOST, "Added host '%s' with guid '%s'", host->hostname, host->machine_guid); return host; } @@ -210,6 +222,22 @@ void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *funct fatal("RRDHOST '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file); } +void rrd_check_rdlock_int(const char *file, const char *function, const unsigned long line) { + debug(D_RRDHOST, "Checking read lock on all RRDs"); + + int ret = pthread_rwlock_trywrlock(&rrd_rwlock); + if(ret == 0) + fatal("RRDs should be read-locked, but it are not, at function %s() at line %lu of file '%s'", function, line, file); +} + +void rrd_check_wrlock_int(const char *file, const char *function, const unsigned long line) { + debug(D_RRDHOST, "Checking write lock on all RRDs"); + + int ret = pthread_rwlock_tryrdlock(&rrd_rwlock); + if(ret == 0) + fatal("RRDs should be write-locked, but it are not, at function %s() at line %lu of file '%s'", function, line, file); +} + // ---------------------------------------------------------------------------- // RRDHOST - free @@ -218,21 +246,45 @@ void rrdhost_free(RRDHOST *host) { info("Freeing all memory for host '%s'...", host->hostname); - rrdhost_wrlock(host); + rrd_check_wrlock(); // make sure the RRDs are write locked + rrdhost_wrlock(host); // lock this RRDHOST - RRDSET *st; - for(st = host->rrdset_root; st ;) { - RRDSET *next = st->next; + // ------------------------------------------------------------------------ + // release its children resources - rrdset_free(st); + while(host->rrdset_root) rrdset_free(host->rrdset_root); - st = next; - } - host->rrdset_root = NULL; + while(host->alarms) rrdcalc_free(host, host->alarms); + while(host->templates) rrdcalctemplate_free(host, host->templates); + health_alarm_log_free(host); + + + // ------------------------------------------------------------------------ + // remove it from the indexes if(rrdhost_index_del(host) != host) error("RRDHOST '%s' removed from index, deleted the wrong entry.", host->hostname); + + // ------------------------------------------------------------------------ + // unlink it from the host + + if(host == localhost) { + localhost = host->next; + } + else { + // find the previous one + RRDHOST *h; + for(h = localhost; h && h->next != host ; h = h->next) ; + + // bypass it + if(h) h->next = host->next; + else error("Request to free RRDHOST '%s': cannot find it", host->hostname); + } + + // ------------------------------------------------------------------------ + // free it + freez(host->cache_dir); freez(host->varlib_dir); freez(host->health_default_exec); @@ -246,19 +298,9 @@ void rrdhost_free(RRDHOST *host) { } void rrdhost_free_all(void) { - RRDHOST *host = localhost; - - // FIXME: lock all hosts - - while(host) { - RRDHOST *next = host = host->next; - rrdhost_free(host); - host = next; - } - - localhost = NULL; - - // FIXME: unlock all hosts + rrd_wrlock(); + while(localhost) rrdhost_free(localhost); + rrd_unlock(); } // ---------------------------------------------------------------------------- @@ -300,7 +342,11 @@ void rrdhost_save(RRDHOST *host) { void rrdhost_save_all(void) { info("Saving database..."); + rrd_rdlock(); + RRDHOST *host; for(host = localhost; host ; host = host->next) rrdhost_save(host); + + rrd_unlock(); } diff --git a/src/rrdset.c b/src/rrdset.c index aa002dd2..ef1713c2 100644 --- a/src/rrdset.c +++ b/src/rrdset.c @@ -209,24 +209,50 @@ static inline void timeval_align(struct timeval *tv, int update_every) { // RRDSET - free a chart void rrdset_free(RRDSET *st) { - rrdset_wrlock(st); + if(unlikely(!st)) return; + + rrdhost_check_wrlock(st->rrdhost); // make sure we have a write lock on the host + rrdset_wrlock(st); // lock this RRDSET + + // ------------------------------------------------------------------------ + // free its children structures while(st->variables) rrdsetvar_free(st->variables); while(st->alarms) rrdsetcalc_unlink(st->alarms); while(st->dimensions) rrddim_free(st, st->dimensions); + rrdfamily_free(st->rrdhost, st->rrdfamily); + + // ------------------------------------------------------------------------ + // remove it from the indexes + if(unlikely(rrdset_index_del(st->rrdhost, st) != st)) error("RRDSET: INTERNAL ERROR: attempt to remove from index chart '%s', removed a different chart.", st->id); rrdset_index_del_name(st->rrdhost, st); - st->rrdfamily->use_count--; - if(!st->rrdfamily->use_count) - rrdfamily_free(st->rrdhost, st->rrdfamily); + // ------------------------------------------------------------------------ + // unlink it from the host + + if(st == st->rrdhost->rrdset_root) { + st->rrdhost->rrdset_root = st->next; + } + else { + // find the previous one + RRDSET *s; + for(s = st->rrdhost->rrdset_root; s && s->next != st ; s = s->next) ; + + // bypass it + if(s) s->next = st->next; + else error("Request to free RRDSET '%s': cannot find it under host '%s'", st->id, st->rrdhost->hostname); + } rrdset_unlock(st); - // free directly allocated memory + // ------------------------------------------------------------------------ + // free it + + // free directly allocated members freez(st->config_section); if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { diff --git a/src/rrdsetvar.c b/src/rrdsetvar.c new file mode 100644 index 00000000..03d7aece --- /dev/null +++ b/src/rrdsetvar.c @@ -0,0 +1,120 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDSETVAR management +// CHART VARIABLES + +static inline void rrdsetvar_free_variables(RRDSETVAR *rs) { + RRDSET *st = rs->rrdset; + + // CHART + + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local); + rs->var_local = NULL; + + // FAMILY + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family); + rs->var_family = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host); + rs->var_host = NULL; + + // HOST + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); + rs->var_family_name = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_name); + rs->var_host_name = NULL; + + // KEYS + + freez(rs->key_fullid); + rs->key_fullid = NULL; + + freez(rs->key_fullname); + rs->key_fullname = NULL; +} + +static inline void rrdsetvar_create_variables(RRDSETVAR *rs) { + rrdsetvar_free_variables(rs); + + RRDSET *st = rs->rrdset; + + // KEYS + + char buffer[RRDVAR_MAX_LENGTH + 1]; + snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rs->variable); + rs->key_fullid = strdupz(buffer); + + snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable); + rs->key_fullname = strdupz(buffer); + + // CHART + + rs->var_local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->type, rs->value); + + // FAMILY + + rs->var_family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullid, rs->type, rs->value); + rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullname, rs->type, rs->value); + + // HOST + + rs->var_host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullid, rs->type, rs->value); + rs->var_host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullname, rs->type, rs->value); + +} + +RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) { + debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable); + RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR)); + + rs->variable = strdupz(variable); + rs->type = type; + rs->value = value; + rs->options = options; + rs->rrdset = st; + + rs->next = st->variables; + st->variables = rs; + + rrdsetvar_create_variables(rs); + + return rs; +} + +void rrdsetvar_rename_all(RRDSET *st) { + debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name); + + RRDSETVAR *rs, *next = st->variables; + while((rs = next)) { + next = rs->next; + rrdsetvar_create_variables(rs); + } + + rrdsetcalc_link_matching(st); +} + +void rrdsetvar_free(RRDSETVAR *rs) { + RRDSET *st = rs->rrdset; + debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable); + + if(st->variables == rs) { + st->variables = rs->next; + } + else { + RRDSETVAR *t; + for (t = st->variables; t && t->next != rs; t = t->next); + if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->key_fullname, st->id); + else t->next = rs->next; + } + + rrdsetvar_free_variables(rs); + + freez(rs->variable); + freez(rs); +} + diff --git a/src/rrdvar.c b/src/rrdvar.c new file mode 100644 index 00000000..2223d7c9 --- /dev/null +++ b/src/rrdvar.c @@ -0,0 +1,265 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDVAR management + +inline int rrdvar_fix_name(char *variable) { + int fixed = 0; + while(*variable) { + if (!isalnum(*variable) && *variable != '.' && *variable != '_') { + *variable++ = '_'; + fixed++; + } + else + variable++; + } + + return fixed; +} + +int rrdvar_compare(void* a, void* b) { + if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1; + else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1; + else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name); +} + +static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) { + RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv)); + if(ret != rv) + debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name); + + return ret; +} + +static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) { + RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv)); + if(!ret) + error("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name); + + return ret; +} + +static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) { + RRDVAR tmp; + tmp.name = (char *)name; + tmp.hash = (hash)?hash:simple_hash(tmp.name); + + return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp); +} + +inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) { + (void)host; + + if(!rv) return; + + if(tree) { + debug(D_VARIABLES, "Deleting variable '%s'", rv->name); + if(unlikely(!rrdvar_index_del(tree, rv))) + error("Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname); + } + + freez(rv->name); + freez(rv); +} + +inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value) { + char *variable = strdupz(name); + rrdvar_fix_name(variable); + uint32_t hash = simple_hash(variable); + + RRDVAR *rv = rrdvar_index_find(tree, variable, hash); + if(unlikely(!rv)) { + debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", variable, scope); + + rv = callocz(1, sizeof(RRDVAR)); + rv->name = variable; + rv->hash = hash; + rv->type = type; + rv->value = value; + + RRDVAR *ret = rrdvar_index_add(tree, rv); + if(unlikely(ret != rv)) { + debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", variable, scope); + rrdvar_free(NULL, NULL, rv); + rv = NULL; + } + else + debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope); + } + else { + debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope); + + // already exists + freez(variable); + + // this is important + // it must return NULL - not the existing variable - or double-free will happen + rv = NULL; + } + + return rv; +} + +// ---------------------------------------------------------------------------- +// CUSTOM VARIABLES + +RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) { + calculated_number *v = callocz(1, sizeof(calculated_number)); + *v = NAN; + RRDVAR *rv = rrdvar_create_and_index("host", &host->variables_root_index, name, RRDVAR_TYPE_CALCULATED_ALLOCATED, v); + if(unlikely(!rv)) { + free(v); + error("Requested variable '%s' already exists - possibly 2 plugins will be updating it at the same time", name); + + char *variable = strdupz(name); + rrdvar_fix_name(variable); + uint32_t hash = simple_hash(variable); + + rv = rrdvar_index_find(&host->variables_root_index, variable, hash); + } + + return rv; +} + +void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name) { + char *variable = strdupz(name); + rrdvar_fix_name(variable); + uint32_t hash = simple_hash(variable); + + RRDVAR *rv = rrdvar_index_find(&host->variables_root_index, variable, hash); + freez(variable); + + if(!rv) { + error("Attempted to remove variable '%s' from host '%s', but it does not exist.", name, host->hostname); + return; + } + + if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) { + error("Attempted to remove variable '%s' from host '%s', but it does not a custom allocated variable.", name, host->hostname); + return; + } + + if(!rrdvar_index_del(&host->variables_root_index, rv)) { + error("Attempted to remove variable '%s' from host '%s', but it cannot be found.", name, host->hostname); + return; + } + + freez(rv->name); + freez(rv->value); + freez(rv); +} + +void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) { + if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) + error("requested to set variable '%s' to value " CALCULATED_NUMBER_FORMAT " but the variable is not a custom one.", rv->name, value); + else { + calculated_number *v = rv->value; + *v = value; + } +} + +// ---------------------------------------------------------------------------- +// RRDVAR lookup + +static calculated_number rrdvar2number(RRDVAR *rv) { + switch(rv->type) { + case RRDVAR_TYPE_CALCULATED_ALLOCATED: + case RRDVAR_TYPE_CALCULATED: { + calculated_number *n = (calculated_number *)rv->value; + return *n; + } + + case RRDVAR_TYPE_TIME_T: { + time_t *n = (time_t *)rv->value; + return *n; + } + + case RRDVAR_TYPE_COLLECTED: { + collected_number *n = (collected_number *)rv->value; + return *n; + } + + case RRDVAR_TYPE_TOTAL: { + total_number *n = (total_number *)rv->value; + return *n; + } + + case RRDVAR_TYPE_INT: { + int *n = (int *)rv->value; + return *n; + } + + default: + error("I don't know how to convert RRDVAR type %d to calculated_number", rv->type); + return NAN; + } +} + +int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) { + RRDSET *st = rc->rrdset; + RRDVAR *rv; + + if(!st) return 0; + + rv = rrdvar_index_find(&st->variables_root_index, variable, hash); + if(rv) { + *result = rrdvar2number(rv); + return 1; + } + + rv = rrdvar_index_find(&st->rrdfamily->variables_root_index, variable, hash); + if(rv) { + *result = rrdvar2number(rv); + return 1; + } + + rv = rrdvar_index_find(&st->rrdhost->variables_root_index, variable, hash); + if(rv) { + *result = rrdvar2number(rv); + return 1; + } + + return 0; +} + +// ---------------------------------------------------------------------------- +// RRDVAR to JSON + +struct variable2json_helper { + BUFFER *buf; + size_t counter; +}; + +static int single_variable2json(void *entry, void *data) { + struct variable2json_helper *helper = (struct variable2json_helper *)data; + RRDVAR *rv = (RRDVAR *)entry; + calculated_number value = rrdvar2number(rv); + + if(unlikely(isnan(value) || isinf(value))) + buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name); + else + buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value); + + helper->counter++; + + return 0; +} + +void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) { + struct variable2json_helper helper = { + .buf = buf, + .counter = 0 + }; + + buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context); + avl_traverse_lock(&st->variables_root_index, single_variable2json, (void *)&helper); + buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family); + helper.counter = 0; + avl_traverse_lock(&st->rrdfamily->variables_root_index, single_variable2json, (void *)&helper); + buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", st->rrdhost->hostname); + helper.counter = 0; + avl_traverse_lock(&st->rrdhost->variables_root_index, single_variable2json, (void *)&helper); + buffer_strcat(buf, "\n\t}\n}\n"); +} + diff --git a/src/simple_pattern.c b/src/simple_pattern.c index 7e442429..85215f15 100644 --- a/src/simple_pattern.c +++ b/src/simple_pattern.c @@ -184,8 +184,8 @@ int simple_pattern_matches(SIMPLE_PATTERN *list, const char *str) { static inline void free_pattern(struct simple_pattern *m) { if(!m) return; - if(m->next) free_pattern(m->next); - if(m->child) free_pattern(m->child); + free_pattern(m->child); + free_pattern(m->next); freez((void *)m->match); freez(m); } @@ -193,5 +193,5 @@ static inline void free_pattern(struct simple_pattern *m) { void simple_pattern_free(SIMPLE_PATTERN *list) { if(!list) return; - free_pattern(((struct simple_pattern *)list)->next); + free_pattern(((struct simple_pattern *)list)); }