]> arthur.barton.de Git - netdata.git/commitdiff
RRDHOSTs free all their memory (all substructures) when they are deallocated
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 19 Feb 2017 15:39:09 +0000 (17:39 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Tue, 21 Feb 2017 23:00:25 +0000 (01:00 +0200)
15 files changed:
CMakeLists.txt
src/Makefile.am
src/health.c
src/health.h
src/health_log.c
src/plugin_idlejitter.c
src/rrd.h
src/rrdcalc.c [new file with mode: 0644]
src/rrdcalctemplate.c [new file with mode: 0644]
src/rrddimvar.c [new file with mode: 0644]
src/rrdhost.c
src/rrdset.c
src/rrdsetvar.c [new file with mode: 0644]
src/rrdvar.c [new file with mode: 0644]
src/simple_pattern.c

index ae492c05d5e1609c619f4da62a5aa35fb9cb9e0d..02e545d367216cebc45d7bacaa238028de6ffbd7 100755 (executable)
@@ -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
index b5694fcaf4eb75892a6f24488d8c8da1117bc39d..9b006f4105cefe63433fbca4058a6ad30697c058 100644 (file)
@@ -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 \
index bd62fbcf8b7fbb8d3ead594422b15df7a61909d4..e3ded3195425e394d934579d319c599590c724ab 100644 (file)
 #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--;
index 68637f5e5386cc87374e18da4b945987e1a87efe..aeb7ef30e83040dcec383fa3bff8ba632538f6f9 100644 (file)
@@ -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
index 9c6975b78e8158a9746d361a2d1ddadd8977adb2..eb13169b3b59a594677570a4c4288a7992126057 100644 (file)
@@ -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);
+}
index 66b3b60415ca5beccba40f2c515a0d51d77c8976..2ed78160c93bed78b8d8aea3b63fe08b6734c686 100644 (file)
@@ -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);
index ad75c3d6a11fc34f4320832c8a91adedc1846d4b..2247e9a4e3d3bc7365736ca1c94012fe4edc6230 100644 (file)
--- 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 (file)
index 0000000..bdd66c2
--- /dev/null
@@ -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 (file)
index 0000000..1deec43
--- /dev/null
@@ -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 (file)
index 0000000..f6eb6d8
--- /dev/null
@@ -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);
+}
+
index 7c6d6418d8cec76a12b673a7f2238070ac9760ef..e44a12c38490b2d1feaf64b7ca219c5945896fac 100644 (file)
@@ -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();
 }
index aa002dd26d98293b29682a155f201db994e81e66..ef1713c258a35829a61c1e7481f7a5d0eae40663 100644 (file)
@@ -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 (file)
index 0000000..03d7aec
--- /dev/null
@@ -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 (file)
index 0000000..2223d7c
--- /dev/null
@@ -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");
+}
+
index 7e4424297f86c2c311f58a57dedaa6221dddf144..85215f152747df6e190a4c8df3fba8676dffb9ea 100644 (file)
@@ -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));
 }