]> arthur.barton.de Git - netdata.git/blob - src/rrdvar.c
dns_query_time plugin: replace "." with "_" in dimensions
[netdata.git] / src / rrdvar.c
1 #define NETDATA_HEALTH_INTERNALS
2 #include "common.h"
3
4 // ----------------------------------------------------------------------------
5 // RRDVAR management
6
7 inline int rrdvar_fix_name(char *variable) {
8     int fixed = 0;
9     while(*variable) {
10         if (!isalnum(*variable) && *variable != '.' && *variable != '_') {
11             *variable++ = '_';
12             fixed++;
13         }
14         else
15             variable++;
16     }
17
18     return fixed;
19 }
20
21 int rrdvar_compare(void* a, void* b) {
22     if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1;
23     else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1;
24     else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name);
25 }
26
27 static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
28     RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
29     if(ret != rv)
30         debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
31
32     return ret;
33 }
34
35 static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
36     RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
37     if(!ret)
38         error("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
39
40     return ret;
41 }
42
43 static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
44     RRDVAR tmp;
45     tmp.name = (char *)name;
46     tmp.hash = (hash)?hash:simple_hash(tmp.name);
47
48     return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
49 }
50
51 inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) {
52     (void)host;
53
54     if(!rv) return;
55
56     if(tree) {
57         debug(D_VARIABLES, "Deleting variable '%s'", rv->name);
58         if(unlikely(!rrdvar_index_del(tree, rv)))
59             error("Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname);
60     }
61
62     freez(rv->name);
63     freez(rv);
64 }
65
66 inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value) {
67     char *variable = strdupz(name);
68     rrdvar_fix_name(variable);
69     uint32_t hash = simple_hash(variable);
70
71     RRDVAR *rv = rrdvar_index_find(tree, variable, hash);
72     if(unlikely(!rv)) {
73         debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", variable, scope);
74
75         rv = callocz(1, sizeof(RRDVAR));
76         rv->name = variable;
77         rv->hash = hash;
78         rv->type = type;
79         rv->value = value;
80
81         RRDVAR *ret = rrdvar_index_add(tree, rv);
82         if(unlikely(ret != rv)) {
83             debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", variable, scope);
84             rrdvar_free(NULL, NULL, rv);
85             rv = NULL;
86         }
87         else
88             debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope);
89     }
90     else {
91         debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope);
92
93         // already exists
94         freez(variable);
95
96         // this is important
97         // it must return NULL - not the existing variable - or double-free will happen
98         rv = NULL;
99     }
100
101     return rv;
102 }
103
104 // ----------------------------------------------------------------------------
105 // CUSTOM VARIABLES
106
107 RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) {
108     calculated_number *v = callocz(1, sizeof(calculated_number));
109     *v = NAN;
110     RRDVAR *rv = rrdvar_create_and_index("host", &host->variables_root_index, name, RRDVAR_TYPE_CALCULATED_ALLOCATED, v);
111     if(unlikely(!rv)) {
112         free(v);
113         error("Requested variable '%s' already exists - possibly 2 plugins will be updating it at the same time", name);
114
115         char *variable = strdupz(name);
116         rrdvar_fix_name(variable);
117         uint32_t hash = simple_hash(variable);
118
119         rv = rrdvar_index_find(&host->variables_root_index, variable, hash);
120     }
121
122     return rv;
123 }
124
125 void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name) {
126     char *variable = strdupz(name);
127     rrdvar_fix_name(variable);
128     uint32_t hash = simple_hash(variable);
129
130     RRDVAR *rv = rrdvar_index_find(&host->variables_root_index, variable, hash);
131     freez(variable);
132
133     if(!rv) {
134         error("Attempted to remove variable '%s' from host '%s', but it does not exist.", name, host->hostname);
135         return;
136     }
137
138     if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) {
139         error("Attempted to remove variable '%s' from host '%s', but it does not a custom allocated variable.", name, host->hostname);
140         return;
141     }
142
143     if(!rrdvar_index_del(&host->variables_root_index, rv)) {
144         error("Attempted to remove variable '%s' from host '%s', but it cannot be found.", name, host->hostname);
145         return;
146     }
147
148     freez(rv->name);
149     freez(rv->value);
150     freez(rv);
151 }
152
153 void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) {
154     if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED)
155         error("requested to set variable '%s' to value " CALCULATED_NUMBER_FORMAT " but the variable is not a custom one.", rv->name, value);
156     else {
157         calculated_number *v = rv->value;
158         *v = value;
159     }
160 }
161
162 // ----------------------------------------------------------------------------
163 // RRDVAR lookup
164
165 static calculated_number rrdvar2number(RRDVAR *rv) {
166     switch(rv->type) {
167         case RRDVAR_TYPE_CALCULATED_ALLOCATED:
168         case RRDVAR_TYPE_CALCULATED: {
169             calculated_number *n = (calculated_number *)rv->value;
170             return *n;
171         }
172
173         case RRDVAR_TYPE_TIME_T: {
174             time_t *n = (time_t *)rv->value;
175             return *n;
176         }
177
178         case RRDVAR_TYPE_COLLECTED: {
179             collected_number *n = (collected_number *)rv->value;
180             return *n;
181         }
182
183         case RRDVAR_TYPE_TOTAL: {
184             total_number *n = (total_number *)rv->value;
185             return *n;
186         }
187
188         case RRDVAR_TYPE_INT: {
189             int *n = (int *)rv->value;
190             return *n;
191         }
192
193         default:
194             error("I don't know how to convert RRDVAR type %d to calculated_number", rv->type);
195             return NAN;
196     }
197 }
198
199 int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) {
200     RRDSET *st = rc->rrdset;
201     RRDVAR *rv;
202
203     if(!st) return 0;
204
205     rv = rrdvar_index_find(&st->variables_root_index, variable, hash);
206     if(rv) {
207         *result = rrdvar2number(rv);
208         return 1;
209     }
210
211     rv = rrdvar_index_find(&st->rrdfamily->variables_root_index, variable, hash);
212     if(rv) {
213         *result = rrdvar2number(rv);
214         return 1;
215     }
216
217     rv = rrdvar_index_find(&st->rrdhost->variables_root_index, variable, hash);
218     if(rv) {
219         *result = rrdvar2number(rv);
220         return 1;
221     }
222
223     return 0;
224 }
225
226 // ----------------------------------------------------------------------------
227 // RRDVAR to JSON
228
229 struct variable2json_helper {
230     BUFFER *buf;
231     size_t counter;
232 };
233
234 static int single_variable2json(void *entry, void *data) {
235     struct variable2json_helper *helper = (struct variable2json_helper *)data;
236     RRDVAR *rv = (RRDVAR *)entry;
237     calculated_number value = rrdvar2number(rv);
238
239     if(unlikely(isnan(value) || isinf(value)))
240         buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name);
241     else
242         buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value);
243
244     helper->counter++;
245
246     return 0;
247 }
248
249 void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) {
250     struct variable2json_helper helper = {
251             .buf = buf,
252             .counter = 0
253     };
254
255     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);
256     avl_traverse_lock(&st->variables_root_index, single_variable2json, (void *)&helper);
257     buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family);
258     helper.counter = 0;
259     avl_traverse_lock(&st->rrdfamily->variables_root_index, single_variable2json, (void *)&helper);
260     buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", st->rrdhost->hostname);
261     helper.counter = 0;
262     avl_traverse_lock(&st->rrdhost->variables_root_index, single_variable2json, (void *)&helper);
263     buffer_strcat(buf, "\n\t}\n}\n");
264 }
265