added RRDCALC management; preparation for expression evaluation
Costa Tsaousis committed
Aug 10, 2016 at 02:33 UTC
cd39ffd3753f705d3edc290df0f2856df9f581f7
9 files changed
+586
-36
CMakeLists.txt
+1
-1
@@ -82,7 +82,7 @@ set(NETDATA_SOURCE_FILES
82
src/web_client.h
83
src/web_server.c
84
src/web_server.h
85
- config.h src/health.h src/health.c)
85
+ config.h src/health.h src/health.c src/eval.h src/eval.c)
86
87
set(APPS_PLUGIN_SOURCE_FILES
88
src/appconfig.c
src/Makefile.am
+1
@@ -32,6 +32,7 @@ netdata_SOURCES = \
32
common.c common.h \
33
daemon.c daemon.h \
34
dictionary.c dictionary.h \
35
+ eval.c eval.h \
36
global_statistics.c global_statistics.h \
37
health.c health.h \
38
log.c log.h \
src/common.h
+1
@@ -80,6 +80,7 @@
80
#include "plugin_tc.h"
81
#include "plugins_d.h"
82
83
+#include "eval.h"
84
#include "health.h"
85
86
#include "rrd.h"
src/eval.c
new
+289
@@ -0,0 +1,289 @@
1
+#include "common.h"
2
+
3
+// ----------------------------------------------------------------------------
4
+// operators that work on 2 operands
5
+
6
+static inline int isoperatorterm_word(const char s) {
7
+ if(isspace(s) || s == '(' || s == '$' || s == '!' || s == '-' || s == '+' || isdigit(s)) return 1;
8
+ return 0;
9
+}
10
+
11
+static inline int isoperatorterm_symbol(const char s) {
12
+ if(isoperatorterm_word(s) || isalpha(s)) return 1;
13
+ return 0;
14
+}
15
+
16
+static inline int parse_and(const char **string) {
17
+ const char *s = *string;
18
+
19
+ // AND
20
+ if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'D' || s[2] == 'd') && isoperatorterm_word(s[3])) {
21
+ *string = &s[4];
22
+ return 1;
23
+ }
24
+
25
+ // &&
26
+ if(s[0] == '&' && s[1] == '&' && isoperatorterm_symbol(s[2])) {
27
+ *string = &s[2];
28
+ return 1;
29
+ }
30
+
31
+ return 0;
32
+}
33
+
34
+static inline int parse_or(const char **string) {
35
+ const char *s = *string;
36
+
37
+ // OR
38
+ if((s[0] == 'O' || s[0] == '0') && (s[1] == 'R' || s[1] == 'r') && isoperatorterm_word(s[2])) {
39
+ *string = &s[3];
40
+ return 1;
41
+ }
42
+
43
+ // ||
44
+ if(s[0] == '|' && s[1] == '|' && isoperatorterm_symbol(s[2])) {
45
+ *string = &s[2];
46
+ return 1;
47
+ }
48
+
49
+ return 0;
50
+}
51
+
52
+static inline int parse_greater_than_or_equal(const char **string) {
53
+ const char *s = *string;
54
+
55
+ // >=
56
+ if(s[0] == '>' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
57
+ *string = &s[2];
58
+ return 1;
59
+ }
60
+
61
+ return 0;
62
+}
63
+
64
+static inline int parse_less_than_or_equal(const char **string) {
65
+ const char *s = *string;
66
+
67
+ // <=
68
+ if (s[0] == '<' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
69
+ *string = &s[2];
70
+ return 1;
71
+ }
72
+
73
+ return 0;
74
+}
75
+
76
+static inline int parse_greater(const char **string) {
77
+ const char *s = *string;
78
+
79
+ // >
80
+ if(s[0] == '>' && isoperatorterm_symbol(s[1])) {
81
+ *string = &s[1];
82
+ return 1;
83
+ }
84
+
85
+ return 0;
86
+}
87
+
88
+static inline int parse_less(const char **string) {
89
+ const char *s = *string;
90
+
91
+ // <
92
+ if(s[0] == '<' && isoperatorterm_symbol(s[1])) {
93
+ *string = &s[1];
94
+ return 1;
95
+ }
96
+
97
+ return 0;
98
+}
99
+
100
+static inline int parse_equal(const char **string) {
101
+ const char *s = *string;
102
+
103
+ // ==
104
+ if(s[0] == '=' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
105
+ *string = &s[2];
106
+ return 1;
107
+ }
108
+
109
+ // =
110
+ if(s[0] == '=' && isoperatorterm_symbol(s[1])) {
111
+ *string = &s[1];
112
+ return 1;
113
+ }
114
+
115
+ return 0;
116
+}
117
+
118
+static inline int parse_not_equal(const char **string) {
119
+ const char *s = *string;
120
+
121
+ // !=
122
+ if(s[0] == '!' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
123
+ *string = &s[2];
124
+ return 1;
125
+ }
126
+
127
+ // <>
128
+ if(s[0] == '<' && s[1] == '>' && isoperatorterm_symbol(s[2])) {
129
+ *string = &s[2];
130
+ }
131
+
132
+ return 0;
133
+}
134
+
135
+static inline int parse_multiply(const char **string) {
136
+ const char *s = *string;
137
+
138
+ // *
139
+ if(s[0] == '*' && isoperatorterm_symbol(s[1])) {
140
+ *string = &s[1];
141
+ return 1;
142
+ }
143
+
144
+ return 0;
145
+}
146
+
147
+static inline int parse_divide(const char **string) {
148
+ const char *s = *string;
149
+
150
+ // /
151
+ if(s[0] == '/' && isoperatorterm_symbol(s[1])) {
152
+ *string = &s[1];
153
+ return 1;
154
+ }
155
+
156
+ return 0;
157
+}
158
+
159
+static inline int parse_minus(const char **string) {
160
+ const char *s = *string;
161
+
162
+ // -
163
+ if(s[0] == '-' && isoperatorterm_symbol(s[1])) {
164
+ *string = &s[1];
165
+ return 1;
166
+ }
167
+
168
+ return 0;
169
+}
170
+
171
+static inline int parse_plus(const char **string) {
172
+ const char *s = *string;
173
+
174
+ // +
175
+ if(s[0] == '+' && isoperatorterm_symbol(s[1])) {
176
+ *string = &s[1];
177
+ return 1;
178
+ }
179
+
180
+ return 0;
181
+}
182
+
183
+// ----------------------------------------------------------------------------
184
+// operators that affect a single operand
185
+
186
+static inline int parse_not(const char **string) {
187
+ const char *s = *string;
188
+
189
+ // NOT
190
+ if((s[0] == 'N' || s[0] == 'n') && (s[1] == 'O' || s[1] == 'o') && (s[2] == 'T' || s[2] == 't') && isoperatorterm_word(s[3])) {
191
+ *string = &s[4];
192
+ return 1;
193
+ }
194
+
195
+ if(s[0] == EVAL_OPERATOR_NOT) {
196
+ *string = &s[1];
197
+ return 1;
198
+ }
199
+
200
+ return 0;
201
+}
202
+
203
+static struct operator {
204
+ const char *printas;
205
+ int precedence;
206
+ char id;
207
+ int (*parse)(const char **);
208
+} operators[] = {
209
+ { "&&", 2, '&', parse_and },
210
+ { "||", 2, '|', parse_or },
211
+ { ">=", 3, '}', parse_greater_than_or_equal },
212
+ { "<=", 3, '{', parse_less_than_or_equal },
213
+ { "<>", 3, '~', parse_not_equal },
214
+ { "==", 3, '=', parse_equal },
215
+ { "<", 3, '<', parse_less },
216
+ { ">", 3, '>', parse_greater },
217
+ { "+", 4, EVAL_OPERATOR_PLUS, parse_plus },
218
+ { "-", 4, EVAL_OPERATOR_MINUS, parse_minus },
219
+ { "*", 5, '*', parse_multiply },
220
+ { "/", 5, '/', parse_divide },
221
+
222
+ // we should not put NOT in this list
223
+
224
+ { NULL, 0, EVAL_OPERATOR_NOP, NULL }
225
+};
226
+
227
+static inline char parse_operator(const char **s, int *precedence) {
228
+ int i;
229
+
230
+ for(i = 0 ; operators[i].parse != NULL ; i++)
231
+ if(operators[i].parse(s)) {
232
+ if(precedence) *precedence = operators[i].precedence;
233
+ return operators[i].id;
234
+ }
235
+
236
+ return EVAL_OPERATOR_NOP;
237
+}
238
+
239
+static inline EVAL_OPERAND *operand_alloc(int count) {
240
+ EVAL_OPERAND *op = calloc(1, sizeof(EVAL_OPERAND) + (sizeof(EVAL_VALUE) * count));
241
+ if(!op) fatal("Cannot allocate memory for OPERAND");
242
+
243
+ op->count = count;
244
+ return op;
245
+}
246
+
247
+static inline void operand_set_value_operand(EVAL_OPERAND *op, int pos, EVAL_OPERAND *value) {
248
+ if(pos >= op->count)
249
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
250
+
251
+ op->ops[pos].type = EVAL_OPERAND_EXPRESSION;
252
+ op->ops[pos].expression = value;
253
+}
254
+
255
+// forward definitions
256
+static inline EVAL_OPERAND *parse_operand(const char **string);
257
+
258
+static inline EVAL_OPERAND *operand_alloc_single(const char **string, char type) {
259
+ EVAL_OPERAND *sub = parse_operand(string);
260
+ if(!sub) return NULL;
261
+
262
+ EVAL_OPERAND *op = operand_alloc(1);
263
+ if(!op) fatal("Cannot allocate memory for OPERAND");
264
+
265
+ op->operator = type;
266
+ operand_set_value_operand(op, 0, sub);
267
+ return op;
268
+}
269
+
270
+static inline EVAL_OPERAND *parse_operand(const char **string) {
271
+ const char *s = *string;
272
+ while(isspace(*s)) s++;
273
+
274
+ if(!*s) return NULL;
275
+ *string = s;
276
+
277
+ if(parse_not(string))
278
+ return operand_alloc_single(string, EVAL_OPERATOR_NOT);
279
+
280
+ if(parse_plus(string))
281
+ return operand_alloc_single(string, EVAL_OPERATOR_PLUS);
282
+
283
+ if(parse_minus(string))
284
+ return operand_alloc_single(string, EVAL_OPERATOR_MINUS);
285
+
286
+
287
+
288
+ return NULL;
289
+}
src/eval.h
new
+38
@@ -0,0 +1,38 @@
1
+#ifndef NETDATA_EVAL_H
2
+#define NETDATA_EVAL_H
3
+
4
+typedef struct variable {
5
+ char *name;
6
+ struct rrdvar *rrdvar;
7
+ struct variable *next;
8
+} VARIABLE;
9
+
10
+#define EVAL_OPERAND_INVALID 0
11
+#define EVAL_OPERAND_NUMBER 1
12
+#define EVAL_OPERAND_VARIABLE 2
13
+#define EVAL_OPERAND_EXPRESSION 3
14
+
15
+// these are used for EVAL_OPERAND.operator
16
+#define EVAL_OPERATOR_NOP '\0'
17
+#define EVAL_OPERATOR_NOT '!'
18
+#define EVAL_OPERATOR_PLUS '+'
19
+#define EVAL_OPERATOR_MINUS '-'
20
+
21
+typedef struct eval_value {
22
+ int type;
23
+
24
+ union {
25
+ calculated_number number;
26
+ VARIABLE *variable;
27
+ struct eval_operand *expression;
28
+ };
29
+} EVAL_VALUE;
30
+
31
+typedef struct eval_operand {
32
+ char operator;
33
+
34
+ int count;
35
+ EVAL_VALUE ops[];
36
+} EVAL_OPERAND;
37
+
38
+#endif //NETDATA_EVAL_H
src/health.c
+174
-27
@@ -45,8 +45,15 @@ static inline RRDVAR *rrdvar_create(const char *name, uint32_t hash, int type, c
45
return rv;
46
}
47
48
-static inline void rrdvar_free(RRDVAR *rv) {
49
- // FIXME: find all references of this and NULL them.
48
+static inline void rrdvar_free(RRDHOST *host, RRDVAR *rv) {
49
+ if(host) {
50
+ // FIXME: we may need some kind of locking here
51
+ // to have mutually exclusive access with eval()
52
+ VARIABLE *rf;
53
+ for (rf = host->references; rf; rf = rf->next)
54
+ if (rf->rrdvar == rv) rf->rrdvar = NULL;
55
+ }
56
+
57
free(rv);
58
}
59
@@ -59,7 +66,7 @@ static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *
66
RRDVAR *ret = rrdvar_index_add(tree, rv);
67
if(unlikely(ret != rv)) {
68
debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", name, scope);
62
- rrdvar_free(rv);
69
+ rrdvar_free(NULL, rv);
70
rv = NULL;
71
}
72
else
@@ -147,12 +154,12 @@ void rrdsetvar_rename_all(RRDSET *st) {
154
// name changed
155
if (rs->context_name) {
156
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
150
- rrdvar_free(rs->context_name);
157
+ rrdvar_free(st->rrdhost, rs->context_name);
158
}
159
160
if (rs->host_name) {
161
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
155
- rrdvar_free(rs->host_name);
162
+ rrdvar_free(st->rrdhost, rs->host_name);
163
}
164
165
free(rs->fullname);
@@ -163,6 +170,8 @@ void rrdsetvar_rename_all(RRDSET *st) {
170
rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
171
}
172
}
173
+
174
+ rrdsetcalc_link_matching(st);
175
}
176
177
void rrdsetvar_free(RRDSETVAR *rs) {
@@ -171,27 +180,27 @@ void rrdsetvar_free(RRDSETVAR *rs) {
180
181
if(rs->local) {
182
rrdvar_index_del(&st->variables_root_index, rs->local);
174
- rrdvar_free(rs->local);
183
+ rrdvar_free(st->rrdhost, rs->local);
184
}
185
186
if(rs->context) {
187
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context);
179
- rrdvar_free(rs->context);
188
+ rrdvar_free(st->rrdhost, rs->context);
189
}
190
191
if(rs->host) {
192
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host);
184
- rrdvar_free(rs->host);
193
+ rrdvar_free(st->rrdhost, rs->host);
194
}
195
196
if(rs->context_name) {
197
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
189
- rrdvar_free(rs->context_name);
198
+ rrdvar_free(st->rrdhost, rs->context_name);
199
}
200
201
if(rs->host_name) {
202
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
194
- rrdvar_free(rs->host_name);
203
+ rrdvar_free(st->rrdhost, rs->host_name);
204
}
205
206
if(st->variables == rs) {
@@ -302,7 +311,7 @@ void rrddimvar_rename_all(RRDDIM *rd) {
311
// name
312
if (rs->local_name) {
313
rrdvar_index_del(&st->variables_root_index, rs->local_name);
305
- rrdvar_free(rs->local_name);
314
+ rrdvar_free(st->rrdhost, rs->local_name);
315
}
316
free(rs->name);
317
snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
@@ -314,11 +323,11 @@ void rrddimvar_rename_all(RRDDIM *rd) {
323
// fullidname
324
if (rs->context_fullidname) {
325
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
317
- rrdvar_free(rs->context_fullidname);
326
+ rrdvar_free(st->rrdhost, rs->context_fullidname);
327
}
328
if (rs->host_fullidname) {
329
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullidname);
321
- rrdvar_free(rs->host_fullidname);
330
+ rrdvar_free(st->rrdhost, rs->host_fullidname);
331
}
332
free(rs->fullidname);
333
snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->name);
@@ -333,11 +342,11 @@ void rrddimvar_rename_all(RRDDIM *rd) {
342
// fullnameid
343
if (rs->context_fullnameid) {
344
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
336
- rrdvar_free(rs->context_fullnameid);
345
+ rrdvar_free(st->rrdhost, rs->context_fullnameid);
346
}
347
if (rs->host_fullnameid) {
348
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnameid);
340
- rrdvar_free(rs->host_fullnameid);
349
+ rrdvar_free(st->rrdhost, rs->host_fullnameid);
350
}
351
free(rs->fullnameid);
352
snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->id);
@@ -352,11 +361,11 @@ void rrddimvar_rename_all(RRDDIM *rd) {
361
// fullnamename
362
if (rs->context_fullnamename) {
363
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
355
- rrdvar_free(rs->context_fullnamename);
364
+ rrdvar_free(st->rrdhost, rs->context_fullnamename);
365
}
366
if (rs->host_fullnamename) {
367
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnamename);
359
- rrdvar_free(rs->host_fullnamename);
368
+ rrdvar_free(st->rrdhost, rs->host_fullnamename);
369
}
370
free(rs->fullnamename);
371
snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->name);
@@ -378,45 +387,45 @@ void rrddimvar_free(RRDDIMVAR *rs) {
387
388
if(rs->local_id) {
389
rrdvar_index_del(&st->variables_root_index, rs->local_id);
381
- rrdvar_free(rs->local_id);
390
+ rrdvar_free(st->rrdhost, rs->local_id);
391
}
392
if(rs->local_name) {
393
rrdvar_index_del(&st->variables_root_index, rs->local_name);
385
- rrdvar_free(rs->local_name);
394
+ rrdvar_free(st->rrdhost, rs->local_name);
395
}
396
397
if(rs->context_fullidid) {
398
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidid);
390
- rrdvar_free(rs->context_fullidid);
399
+ rrdvar_free(st->rrdhost, rs->context_fullidid);
400
}
401
if(rs->context_fullidname) {
402
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
394
- rrdvar_free(rs->context_fullidname);
403
+ rrdvar_free(st->rrdhost, rs->context_fullidname);
404
}
405
if(rs->context_fullnameid) {
406
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
398
- rrdvar_free(rs->context_fullnameid);
407
+ rrdvar_free(st->rrdhost, rs->context_fullnameid);
408
}
409
if(rs->context_fullnamename) {
410
rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
402
- rrdvar_free(rs->context_fullnamename);
411
+ rrdvar_free(st->rrdhost, rs->context_fullnamename);
412
}
413
414
if(rs->host_fullidid) {
415
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidid);
407
- rrdvar_free(rs->host_fullidid);
416
+ rrdvar_free(st->rrdhost, rs->host_fullidid);
417
}
418
if(rs->host_fullidname) {
419
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidname);
411
- rrdvar_free(rs->host_fullidname);
420
+ rrdvar_free(st->rrdhost, rs->host_fullidname);
421
}
422
if(rs->host_fullnameid) {
423
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnameid);
415
- rrdvar_free(rs->host_fullnameid);
424
+ rrdvar_free(st->rrdhost, rs->host_fullnameid);
425
}
426
if(rs->host_fullnamename) {
427
rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnamename);
419
- rrdvar_free(rs->host_fullnamename);
428
+ rrdvar_free(st->rrdhost, rs->host_fullnamename);
429
}
430
431
if(rd->variables == rs) {
@@ -441,3 +450,141 @@ void rrddimvar_free(RRDDIMVAR *rs) {
450
free(rs->fullnamename);
451
free(rs);
452
}
453
+
454
+// ----------------------------------------------------------------------------
455
+// RRDCALC management
456
+
457
+// this has to be called while the caller has locked
458
+// the RRDHOST
459
+static inline void rrdhostcalc_linked(RRDHOST *host, RRDCALC *rc) {
460
+ // move it to be last
461
+
462
+ if(!rc->next)
463
+ // we are last already
464
+ return;
465
+
466
+ RRDCALC *t, *last = NULL, *prev = NULL;
467
+ for (t = host->calculations; t ; t = t->next) {
468
+ if(t->next == rc)
469
+ prev = t;
470
+
471
+ if(!t->next)
472
+ last = t;
473
+ }
474
+
475
+ if(!last) {
476
+ error("RRDCALC '%s' cannot be linked to the end of host '%s' list", rc->name, host->hostname);
477
+ return;
478
+ }
479
+
480
+ if(prev)
481
+ prev->next = rc->next;
482
+ else {
483
+ if(host->calculations == rc)
484
+ host->calculations = rc->next;
485
+ else {
486
+ error("RRDCALC '%s' is not found in host '%s' list", rc->name, host->hostname);
487
+ return;
488
+ }
489
+ }
490
+
491
+ last->next = rc;
492
+ rc->next = NULL;
493
+}
494
+
495
+// this has to be called while the caller has locked
496
+// the RRDHOST
497
+static inline void rrdhostcalc_unlinked(RRDHOST *host, RRDCALC *rc) {
498
+ // move it to be first
499
+
500
+ if(host->calculations == rc) {
501
+ // ok, we are the first
502
+ return;
503
+ }
504
+ else {
505
+ // find the previous one
506
+ RRDCALC *t;
507
+ for (t = host->calculations; t && t->next != rc; rc = rc->next) ;
508
+ if(unlikely(!t)) {
509
+ error("RRDCALC '%s' is not linked to host '%s'.", rc->name, host->hostname);
510
+ return;
511
+ }
512
+ t->next = rc->next;
513
+ rc->next = host->calculations;
514
+ host->calculations = rc;
515
+ }
516
+}
517
+
518
+static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
519
+ rc->rrdset = st;
520
+
521
+ rc->local = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
522
+ rc->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
523
+ rc->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
524
+
525
+ rrdhostcalc_linked(st->rrdhost, rc);
526
+}
527
+
528
+// this has to be called while the RRDHOST is locked
529
+void rrdsetcalc_link_matching(RRDSET *st) {
530
+ RRDCALC *rc;
531
+
532
+ for(rc = st->rrdhost->calculations; rc ; rc = rc->next) {
533
+ // since unlinked ones are in front and linked at the end
534
+ // we stop on the first linked RRDCALC
535
+ if(rc->rrdset != NULL) break;
536
+
537
+ if((rc->hash_chart == st->hash && !strcmp(rc->name, st->id)) ||
538
+ (rc->hash_chart == st->hash_name && !strcmp(rc->name, st->name))) {
539
+ rrdsetcalc_link(st, rc);
540
+ }
541
+ }
542
+}
543
+
544
+// this has to be called while the RRDHOST is locked
545
+void rrdsetcalc_unlink(RRDCALC *rc) {
546
+ RRDSET *st = rc->rrdset;
547
+
548
+ if(!st) {
549
+ error("Requested to unlink RRDCALC '%s' which is not linked to any RRDSET", rc->name);
550
+ return;
551
+ }
552
+
553
+ RRDHOST *host = st->rrdhost;
554
+
555
+ // unlink it
556
+ if(rc->rrdset_prev)
557
+ rc->rrdset_prev->rrdset_next = rc->rrdset_next;
558
+
559
+ if(rc->rrdset_next)
560
+ rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
561
+
562
+ if(st->calculations == rc)
563
+ st->calculations = rc->rrdset_next;
564
+
565
+ rc->rrdset_prev = rc->rrdset_next = NULL;
566
+
567
+ if(rc->local) {
568
+ rrdvar_index_del(&st->variables_root_index, rc->local);
569
+ rrdvar_free(st->rrdhost, rc->local);
570
+ rc->local = NULL;
571
+ }
572
+
573
+ if(rc->context) {
574
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rc->context);
575
+ rc->context = NULL;
576
+ }
577
+
578
+ if(rc->host) {
579
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rc->host);
580
+ rc->host = NULL;
581
+ }
582
+
583
+ rc->rrdset = NULL;
584
+
585
+ // RRDCALC will remain in RRDHOST
586
+ // so that if the matching chart is found in the future
587
+ // it will be applied automatically
588
+
589
+ rrdhostcalc_unlinked(host, rc);
590
+}
src/health.h
+60
-3
@@ -44,6 +44,10 @@ typedef struct rrdvar {
44
} RRDVAR;
45
46
// variables linked to charts
47
+// We link variables to point the values that are already
48
+// calculated / processed by the normal data collection process
49
+// This means, there will be no speed penalty for using
50
+// these variables
51
typedef struct rrdsetvar {
52
char *fullid; // chart type.chart id.variable
53
uint32_t hash_fullid;
@@ -72,6 +76,10 @@ typedef struct rrdsetvar {
76
77
78
// variables linked to dimensions
79
+// We link variables to point the values that are already
80
+// calculated / processed by the normal data collection process
81
+// This means, there will be no speed penalty for using
82
+// these variables
83
typedef struct rrddimvar {
84
char *prefix;
85
char *suffix;
@@ -117,25 +125,71 @@ typedef struct rrddimvar {
125
struct rrddimvar *next;
126
} RRDDIMVAR;
127
128
+// additional calculated variables
129
+// These aggregate time-series data at fixed intervals
130
+// (defined in their update_every member below)
131
+// These increase the overhead of netdata.
132
+//
133
+// These calculations are allocated and linked (->next)
134
+// to RRDHOST.
135
+// Then are also linked to RRDSET (of course only when the
136
+// chart is found, via ->rrdset_next and ->rrdset_prev).
137
+// This double-linked list is maintained sorted at all times
138
+// having as RRDSET->calculations the RRDCALC to be processed
139
+// next.
140
typedef struct rrdcalc {
121
- avl avl;
141
+ char *name;
142
+ uint32_t hash;
143
+
144
+ char *chart; // the chart name
145
+ uint32_t hash_chart;
146
+
147
+ char *dimensions; // the chart dimensions
148
149
int group; // grouping method: average, max, etc.
150
int before; // ending point in time-series
151
int after; // starting point in time-series
152
+ uint32_t options; // calculation options
153
int update_every; // update frequency for the calculation
154
128
- const char *name;
155
+ time_t last_updated;
156
+ time_t next_update;
157
+
158
calculated_number value;
159
160
RRDVAR *local;
161
RRDVAR *context;
162
RRDVAR *host;
163
164
+ struct rrdset *rrdset;
165
+ struct rrdcalc *rrdset_next;
166
+ struct rrdcalc *rrdset_prev;
167
+
168
struct rrdcalc *next;
136
- struct rrdcalc *prev;
169
} RRDCALC;
170
171
+
172
+// RRDCALCTEMPLATE
173
+// these are to be applied to charts found dynamically
174
+// based on their context.
175
+typedef struct rrdcalctemplate {
176
+ char *name;
177
+ uint32_t hash_name;
178
+
179
+ char *context;
180
+ uint32_t hash_context;
181
+
182
+ char *dimensions;
183
+
184
+ int group; // grouping method: average, max, etc.
185
+ int before; // ending point in time-series
186
+ int after; // starting point in time-series
187
+ uint32_t options; // calculation options
188
+ int update_every; // update frequency for the calculation
189
+
190
+ struct rrdcalctemplate *next;
191
+} RRDCALCTEMPLATE;
192
+
193
#include "rrd.h"
194
195
extern void rrdsetvar_rename_all(RRDSET *st);
@@ -146,4 +200,7 @@ extern void rrddimvar_rename_all(RRDDIM *rd);
200
extern RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options);
201
extern void rrddimvar_free(RRDDIMVAR *rs);
202
203
+extern void rrdsetcalc_link_matching(RRDSET *st);
204
+extern void rrdsetcalc_unlink(RRDCALC *rc);
205
+
206
#endif //NETDATA_HEALTH_H
src/rrd.c
+5
@@ -531,6 +531,8 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
531
532
rrdset_index_add(&localhost, st);
533
534
+ rrdsetcalc_link_matching(st);
535
+
536
pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
537
538
return(st);
@@ -740,6 +742,9 @@ void rrdset_free_all(void)
742
while(st->variables)
743
rrdsetvar_free(st->variables);
744
745
+ while(st->calculations)
746
+ rrdsetcalc_unlink(st->calculations);
747
+
748
while(st->dimensions)
749
rrddim_free(st, st->dimensions);
750
src/rrd.h
+17
-5
@@ -253,6 +253,13 @@ struct rrdset {
253
254
struct rrdset *next; // linking of rrdsets
255
256
+ // ------------------------------------------------------------------------
257
+ // local variables
258
+
259
+ avl_tree_lock variables_root_index;
260
+ RRDSETVAR *variables;
261
+ RRDCALC *calculations;
262
+
263
// ------------------------------------------------------------------------
264
// members for checking the data when loading from disk
265
@@ -266,11 +273,6 @@ struct rrdset {
273
avl_tree_lock dimensions_index; // the root of the dimensions index
274
RRDDIM *dimensions; // the actual data for every dimension
275
269
- // ------------------------------------------------------------------------
270
- // local variables
271
-
272
- avl_tree_lock variables_root_index;
273
- RRDSETVAR *variables;
276
};
277
typedef struct rrdset RRDSET;
278
@@ -290,6 +292,16 @@ struct rrdhost {
292
293
avl_tree_lock rrdcontext_root_index;
294
avl_tree_lock variables_root_index;
295
+
296
+ // all RRDCALCs are primarily allocated and linked here
297
+ // RRDCALCs may be linked to charts at any point
298
+ // (charts may or may not exist when these are loaded)
299
+ RRDCALC *calculations;
300
+
301
+ // all variable references are linked here
302
+ // RRDVARs may be free'd, so every time this happens
303
+ // we need to find all their references and invalidate them
304
+ VARIABLE *references;
305
};
306
typedef struct rrdhost RRDHOST;
307