@cryptotaxi247 / netdata-1 / commits / f0575ccc9

operational health monitoring - we got alarms! - no notifications yet though

Costa Tsaousis committed Aug 15, 2016 at 02:39 UTC f0575ccc983e41843bd19616b7ef0a729297ceba
15 files changed +408 -77
src/appconfig.c
+6 -2
@@ -478,8 +478,12 @@ void generate_config(BUFFER *wb, int only_changed)
478
479 config_global_write_lock();
480 for(co = config_root; co ; co = co->next) {
481 - if(strcmp(co->name, "global") == 0 || strcmp(co->name, "plugins") == 0 || strcmp(co->name, "registry") == 0) pri = 0;
482 - else if(strncmp(co->name, "plugin:", 7) == 0) pri = 1;
481 + if(!strcmp(co->name, "global") ||
482 + !strcmp(co->name, "plugins") ||
483 + !strcmp(co->name, "registry") ||
484 + !strcmp(co->name, "health"))
485 + pri = 0;
486 + else if(!strncmp(co->name, "plugin:", 7)) pri = 1;
487 else pri = 2;
488
489 if(i == pri) {
src/avl.c
+30 -2
@@ -278,7 +278,27 @@ avl *avl_remove(avl_tree *tree, avl *item) {
278 }
279
280 /* ------------------------------------------------------------------------- */
281 -// these functions are (C) Costa Tsaousis
281 +// below are functions by (C) Costa Tsaousis
282 +
283 +// ---------------------------
284 +// traversing
285 +
286 +void avl_walker(avl *node, void (*callback)(void *)) {
287 + if(node->avl_link[0])
288 + avl_walker(node->avl_link[0], callback);
289 +
290 + callback(node);
291 +
292 + if(node->avl_link[1])
293 + avl_walker(node->avl_link[1], callback);
294 +}
295 +
296 +void avl_traverse(avl_tree *t, void (*callback)(void *)) {
297 + avl_walker(t->root, callback);
298 +}
299 +
300 +// ---------------------------
301 +// locks
302
303 void avl_read_lock(avl_tree_lock *t) {
304 #ifndef AVL_WITHOUT_PTHREADS
@@ -310,7 +330,8 @@ void avl_unlock(avl_tree_lock *t) {
330 #endif /* AVL_WITHOUT_PTHREADS */
331 }
332
313 -/* ------------------------------------------------------------------------- */
333 +// ---------------------------
334 +// operations with locking
335
336 void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b)) {
337 avl_init(&t->avl_tree, compar);
@@ -351,8 +372,15 @@ avl *avl_insert_lock(avl_tree_lock *t, avl *a) {
372 return ret;
373 }
374
375 +void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *)) {
376 + avl_read_lock(t);
377 + avl_traverse(&t->avl_tree, callback);
378 + avl_unlock(t);
379 +}
380 +
381 void avl_init(avl_tree *t, int (*compar)(void *a, void *b)) {
382 t->root = NULL;
383 t->compar = compar;
384 }
385
386 +// ------------------
\ No newline at end of file
src/avl.h
+4
@@ -79,4 +79,8 @@ avl *avl_search(avl_tree *t, avl *a);
79 void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b));
80 void avl_init(avl_tree *t, int (*compar)(void *a, void *b));
81
82 +
83 +void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *));
84 +void avl_traverse(avl_tree *t, void (*callback)(void *));
85 +
86 #endif /* avl.h */
src/eval.c
+32 -5
@@ -52,6 +52,7 @@ static inline EVAL_NODE *parse_full_expression(const char **string, int *error);
52 static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error);
53 static inline calculated_number eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
54 static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error);
55 +static inline void print_parsed_as_constant(BUFFER *out, calculated_number n);
56
57 // ----------------------------------------------------------------------------
58 // evaluation of expressions
@@ -71,13 +72,28 @@ static inline calculated_number eval_check_number(calculated_number n, int *erro
72 }
73
74 static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
74 - // FIXME: do the variable look up here
75 + static uint32_t this_hash = 0;
76 +
77 + if(unlikely(this_hash == 0))
78 + this_hash = simple_hash("this");
79 +
80 + if(exp->this && v->hash == this_hash && !strcmp(v->name, "this")) {
81 + buffer_strcat(exp->error_msg, "[ $this = ");
82 + print_parsed_as_constant(exp->error_msg, *exp->this);
83 + buffer_strcat(exp->error_msg, " ] ");
84 + return *exp->this;
85 + }
86 +
87 + calculated_number n;
88 + if(exp->rrdcalc && health_variable_lookup(v->name, v->hash, exp->rrdcalc, &n)) {
89 + buffer_sprintf(exp->error_msg, "[ $%s = ", v->name);
90 + print_parsed_as_constant(exp->error_msg, n);
91 + buffer_strcat(exp->error_msg, " ] ");
92 + return n;
93 + }
94
76 -// if(!exp->data) {
95 *error = EVAL_ERROR_UNKNOWN_VARIABLE;
96 buffer_sprintf(exp->error_msg, "unknown variable '%s'", v->name);
79 -// }
80 -
97 return 0;
98 }
99
@@ -208,6 +224,16 @@ static inline void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *
224 }
225
226 static inline void print_parsed_as_constant(BUFFER *out, calculated_number n) {
227 + if(unlikely(isnan(n))) {
228 + buffer_strcat(out, "NaN");
229 + return;
230 + }
231 +
232 + if(unlikely(isinf(n))) {
233 + buffer_strcat(out, "INFINITE");
234 + return;
235 + }
236 +
237 char b[100+1], *s;
238 snprintfz(b, 100, CALCULATED_NUMBER_FORMAT, n);
239
@@ -641,6 +667,7 @@ static inline void eval_node_set_value_to_variable(EVAL_NODE *op, int pos, const
667 op->ops[pos].type = EVAL_VALUE_VARIABLE;
668 op->ops[pos].variable = callocz(1, sizeof(EVAL_VARIABLE));
669 op->ops[pos].variable->name = strdupz(variable);
670 + op->ops[pos].variable->hash = simple_hash(op->ops[pos].variable->name);
671 }
672
673 static inline void eval_variable_free(EVAL_VARIABLE *v) {
@@ -910,7 +937,7 @@ const char *expression_strerror(int error) {
937 return "wrong number of operands for operation - internal error";
938
939 case EVAL_ERROR_VALUE_IS_NAN:
913 - return "value or variable is missing or is not a number";
940 + return "value is unset";
941
942 case EVAL_ERROR_VALUE_IS_INFINITE:
943 return "computed value is infinite";
src/eval.h
+3 -1
@@ -5,6 +5,7 @@
5
6 typedef struct eval_variable {
7 char *name;
8 + uint32_t hash;
9 struct rrdvar *rrdvar;
10 struct eval_variable *next;
11 } EVAL_VARIABLE;
@@ -13,6 +14,7 @@ typedef struct eval_expression {
14 const char *source;
15 const char *parsed_as;
16
17 + calculated_number *this;
18 calculated_number result;
19
20 int error;
@@ -22,7 +24,7 @@ typedef struct eval_expression {
24 void *nodes;
25
26 // custom data to be used for looking up variables
25 - void *data;
27 + struct rrdcalc *rrdcalc;
28 } EVAL_EXPRESSION;
29
30 #define EVAL_VALUE_INVALID 0
src/health.c
+274 -23
@@ -80,21 +80,83 @@ static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *
80 rv = NULL;
81 }
82
83 - /*
84 - * check
83 + return rv;
84 +}
85 +
86 +// ----------------------------------------------------------------------------
87 +// RRDVAR lookup
88 +
89 +calculated_number rrdvar2number(RRDVAR *rv) {
90 + switch(rv->type) {
91 + case RRDVAR_TYPE_CALCULATED: {
92 + calculated_number *n = (calculated_number *)rv->value;
93 + return *n;
94 + }
95 + break;
96 +
97 + case RRDVAR_TYPE_TIME_T: {
98 + time_t *n = (time_t *)rv->value;
99 + return *n;
100 + }
101 + break;
102 +
103 + case RRDVAR_TYPE_COLLECTED: {
104 + collected_number *n = (collected_number *)rv->value;
105 + return *n;
106 + }
107 + break;
108 +
109 + case RRDVAR_TYPE_TOTAL: {
110 + total_number *n = (total_number *)rv->value;
111 + return *n;
112 + }
113 +
114 + default:
115 + error("I don't know how to convert RRDVAR type %d to calculated_number", rv->type);
116 + return NAN;
117 + break;
118 + }
119 +}
120 +
121 +void dump_variable(void *data) {
122 + RRDVAR *rv = (RRDVAR *)data;
123 + debug(D_HEALTH, "%30s : " CALCULATED_NUMBER_FORMAT, rv->name, rrdvar2number(rv));
124 +}
125 +
126 +int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) {
127 + RRDSET *st = rc->rrdset;
128 + RRDVAR *rv;
129 +
130 + if(!st) return 0;
131 +
132 + rv = rrdvar_index_find(&st->variables_root_index, variable, hash);
133 if(rv) {
86 - RRDVAR *ret = rrdvar_index_find(tree, name, hash);
87 - if(ret != rv) fatal("oops! 1");
134 + *result = rrdvar2number(rv);
135 + return 1;
136 + }
137
89 - ret = rrdvar_index_del(tree, rv);
90 - if(ret != rv) fatal("oops! 2");
138 + rv = rrdvar_index_find(&st->rrdcontext->variables_root_index, variable, hash);
139 + if(rv) {
140 + *result = rrdvar2number(rv);
141 + return 1;
142 + }
143
92 - ret = rrdvar_index_add(tree, rv);
93 - if(ret != rv) fatal("oops! 3");
144 + rv = rrdvar_index_find(&st->rrdhost->variables_root_index, variable, hash);
145 + if(rv) {
146 + *result = rrdvar2number(rv);
147 + return 1;
148 }
95 - */
149
97 - return rv;
150 + debug(D_HEALTH, "Available local chart '%s' variables:", st->id);
151 + avl_traverse_lock(&st->variables_root_index, dump_variable);
152 +
153 + debug(D_HEALTH, "Available context '%s' variables:", st->rrdcontext->id);
154 + avl_traverse_lock(&st->rrdcontext->variables_root_index, dump_variable);
155 +
156 + debug(D_HEALTH, "Available host '%s' variables:", st->rrdhost->hostname);
157 + avl_traverse_lock(&st->rrdhost->variables_root_index, dump_variable);
158 +
159 + return 0;
160 }
161
162 // ----------------------------------------------------------------------------
@@ -446,7 +508,7 @@ void rrddimvar_free(RRDDIMVAR *rs) {
508
509 // ----------------------------------------------------------------------------
510 // RRDCALC management
449 -
511 +/*
512 // this has to be called while the caller has locked
513 // the RRDHOST
514 static inline void rrdset_linked_optimize_rrdhost(RRDHOST *host, RRDCALC *rc) {
@@ -511,6 +573,7 @@ static inline void rrdcalc_unlinked_optimize_rrdhost(RRDHOST *host, RRDCALC *rc)
573 host->calculations = rc;
574 }
575 }
576 +*/
577
578 static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
579 debug(D_HEALTH, "Health linking alarm '%s' from chart '%s' of host '%s'", rc->name, st->id, st->rrdhost->hostname);
@@ -527,12 +590,12 @@ static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
590 rc->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
591 rc->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
592
530 - rrdset_linked_optimize_rrdhost(st->rrdhost, rc);
593 + // rrdset_linked_optimize_rrdhost(st->rrdhost, rc);
594 }
595
596 static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) {
534 - if((rc->hash_chart == st->hash && !strcmp(rc->name, st->id)) ||
535 - (rc->hash_chart == st->hash_name && !strcmp(rc->name, st->name)))
597 + if( (rc->hash_chart == st->hash && !strcmp(rc->chart, st->id)) ||
598 + (rc->hash_chart == st->hash_name && !strcmp(rc->chart, st->name)))
599 return 1;
600
601 return 0;
@@ -540,12 +603,11 @@ static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) {
603
604 // this has to be called while the RRDHOST is locked
605 void rrdsetcalc_link_matching(RRDSET *st) {
543 - RRDCALC *rc;
606 + // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id);
607
608 + RRDCALC *rc;
609 for(rc = st->rrdhost->calculations; rc ; rc = rc->next) {
546 - // since unlinked ones are in front and linked at the end
547 - // we stop on the first linked RRDCALC
548 - if(rc->rrdset != NULL) break;
610 + if(rc->rrdset) continue;
611
612 if(rrdcalc_is_matching_this_rrdset(rc, st))
613 rrdsetcalc_link(st, rc);
@@ -599,7 +661,7 @@ void rrdsetcalc_unlink(RRDCALC *rc) {
661 // so that if the matching chart is found in the future
662 // it will be applied automatically
663
602 - rrdcalc_unlinked_optimize_rrdhost(host, rc);
664 + // rrdcalc_unlinked_optimize_rrdhost(host, rc);
665 }
666
667 static inline int rrdcalc_exists(RRDHOST *host, const char *name, uint32_t hash) {
@@ -617,6 +679,23 @@ static inline int rrdcalc_exists(RRDHOST *host, const char *name, uint32_t hash)
679 }
680
681 void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
682 + rrdhost_check_rdlock(host);
683 +
684 + if(rc->calculation) {
685 + rc->calculation->this = &rc->value;
686 + rc->calculation->rrdcalc = rc;
687 + }
688 +
689 + if(rc->warning) {
690 + rc->warning->this = &rc->value;
691 + rc->warning->rrdcalc = rc;
692 + }
693 +
694 + if(rc->critical) {
695 + rc->critical->this = &rc->value;
696 + rc->critical->rrdcalc = rc;
697 + }
698 +
699 // link it to the host
700 rc->next = host->calculations;
701 host->calculations = rc;
@@ -634,7 +713,8 @@ void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
713 RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, const char *dimensions, int group_method,
714 int after, int before, int update_every, uint32_t options,
715 calculated_number green, calculated_number red,
637 - const char *exec, const char *calc, const char *warn, const char *crit) {
716 + const char *exec, const char *source,
717 + const char *calc, const char *warn, const char *crit) {
718 uint32_t hash = simple_hash(name);
719
720 if(rrdcalc_exists(host, name, hash))
@@ -658,7 +738,10 @@ RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, cons
738
739 rc->green = green;
740 rc->red = red;
741 +
742 if(exec) rc->exec = strdupz(exec);
743 + if(source) rc->source = strdupz(source);
744 +
745 if(calc) {
746 rc->calculation = expression_parse(calc, NULL, NULL);
747 if(!rc->calculation)
@@ -758,12 +841,16 @@ void rrdcalctemplate_link_matching(RRDSET *st) {
841 char buffer[RRDSETVAR_ID_MAX + 1];
842 snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->family, rt->name);
843 variable_fix_name(buffer);
761 - rrdcalc_create(st->rrdhost, buffer, st->id,
844 + RRDCALC *rc = rrdcalc_create(st->rrdhost, buffer, st->id,
845 rt->dimensions, rt->group, rt->after, rt->before, rt->update_every, rt->options,
763 - rt->green, rt->red, rt->exec,
846 + rt->green, rt->red, rt->exec, rt->source,
847 (rt->calculation)?rt->calculation->source:NULL,
848 (rt->warning)?rt->warning->source:NULL,
849 (rt->critical)?rt->critical->source:NULL);
850 +
851 + // FIXME
852 + if(rc->rrdset != st)
853 + fatal("RRDCAL '%s' is not linked to chart '%s'", rc->name, st->id);
854 }
855 }
856 }
@@ -1360,6 +1447,8 @@ void health_readdir(const char *path) {
1447 health_readfile(path, de->d_name);
1448 }
1449 }
1450 +
1451 + closedir(dir);
1452 }
1453
1454 void health_init(void) {
@@ -1375,11 +1464,173 @@ void health_init(void) {
1464 {
1465 char buffer[FILENAME_MAX + 1];
1466 snprintfz(buffer, FILENAME_MAX, "%s/health.d", config_get("global", "config directory", CONFIG_DIR));
1378 - path = config_get("health", "configuration files in directory", buffer);
1467 + path = config_get("health", "health configuration directory", buffer);
1468
1469 snprintfz(buffer, FILENAME_MAX, "%s/alarm.sh", config_get("global", "plugins directory", PLUGINS_DIR));
1470 health_default_exec = config_get("health", "script to execute on alarm", buffer);
1471 }
1472
1473 + rrdhost_rwlock(&localhost);
1474 health_readdir(path);
1475 + rrdhost_unlock(&localhost);
1476 +}
1477 +
1478 +static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) {
1479 + if (unlikely(!rc->rrdset)) {
1480 + debug(D_HEALTH, "Health not running alarm '%s'. It is not linked to a chart.", rc->name);
1481 + return 0;
1482 + }
1483 +
1484 + if (unlikely(!rc->update_every)) {
1485 + debug(D_HEALTH, "Health not running alarm '%s'. It does not have an update frequency", rc->name);
1486 + return 0;
1487 + }
1488 +
1489 + if (unlikely(rc->next_update > now)) {
1490 + if (*next_run > rc->next_update)
1491 + *next_run = rc->next_update;
1492 +
1493 + debug(D_HEALTH, "Health not examining alarm '%s' yet (will do in %d secs).", rc->name,
1494 + (int) (rc->next_update - now));
1495 + return 0;
1496 + }
1497 +
1498 + return 1;
1499 +}
1500 +
1501 +void *health_main(void *ptr) {
1502 + (void)ptr;
1503 +
1504 + info("HEALTH thread created with task id %d", gettid());
1505 +
1506 + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
1507 + error("Cannot set pthread cancel type to DEFERRED.");
1508 +
1509 + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
1510 + error("Cannot set pthread cancel state to ENABLE.");
1511 +
1512 + int min_run_every = (int)config_get_number("health", "run at least every seconds", 10);
1513 + if(min_run_every < 1) min_run_every = 1;
1514 +
1515 + BUFFER *wb = buffer_create(100);
1516 +
1517 + unsigned int loop = 0;
1518 + while(health_enabled) {
1519 + loop++;
1520 + debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
1521 +
1522 + int oldstate, runnable = 0;
1523 + time_t now = time(NULL);
1524 + time_t next_run = now + min_run_every;
1525 + RRDCALC *rc;
1526 +
1527 + if (unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
1528 + error("Cannot set pthread cancel state to DISABLE.");
1529 +
1530 + rrdhost_rdlock(&localhost);
1531 + for (rc = localhost.calculations; rc; rc = rc->next) {
1532 + if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run)))
1533 + continue;
1534 +
1535 + runnable++;
1536 + debug(D_HEALTH, "Health running alarm '%s'", rc->name);
1537 +
1538 + // 1. if there is database lookup, do it
1539 + // 1b. if the lookup has a calculation expression run it
1540 + // 2. if there is warning expression, do it
1541 + // 3. if there is critical expression, do it
1542 +
1543 + if (rc->after) {
1544 + time_t latest_timestamp;
1545 + int value_is_null;
1546 + int ret = rrd2value(rc->rrdset, wb, &rc->value, rc->dimensions, 1, rc->after, rc->before, rc->group,
1547 + rc->options, &latest_timestamp, &value_is_null);
1548 + if (ret != 200) {
1549 + error("Health for alarm '%s', database lookup returned error %d", rc->name, ret);
1550 + }
1551 + else {
1552 + if(value_is_null) {
1553 + rc->value = NAN;
1554 + error("Health for alarm '%s', database lookup returned empty value (possibly value is not collected yet)", rc->name);
1555 + }
1556 + else {
1557 + debug(D_HEALTH, "Health for alarm '%s', database lookup gave value "
1558 + CALCULATED_NUMBER_FORMAT, rc->name, rc->value);
1559 +
1560 + if (rc->calculation) {
1561 + if (!expression_evaluate(rc->calculation)) {
1562 + error("Health for alarm '%s', failed to evaluate calculation with error: %s", rc->name,
1563 + buffer_tostring(rc->calculation->error_msg));
1564 + } else {
1565 + debug(D_HEALTH, "Health for alarm '%s', calculation gave value "
1566 + CALCULATED_NUMBER_FORMAT, rc->name, rc->calculation->result);
1567 + rc->value = rc->calculation->result;
1568 + }
1569 + }
1570 + }
1571 + }
1572 + }
1573 + }
1574 + rrdhost_unlock(&localhost);
1575 +
1576 + if (runnable) {
1577 + rrdhost_rdlock(&localhost);
1578 +
1579 + for (rc = localhost.calculations; rc; rc = rc->next) {
1580 + if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run)))
1581 + continue;
1582 +
1583 + if(rc->warning) {
1584 + if (!expression_evaluate(rc->warning)) {
1585 + error("Health for alarm '%s', failed to evaluate warning expression with error: %s", rc->name,
1586 + buffer_tostring(rc->warning->error_msg));
1587 + }
1588 + else {
1589 + debug(D_HEALTH, "Health for alarm '%s', warning expression gave value "
1590 + CALCULATED_NUMBER_FORMAT ": %s",
1591 + rc->name, rc->warning->result,
1592 + buffer_tostring(rc->warning->error_msg)
1593 + );
1594 + }
1595 + }
1596 +
1597 + if(rc->critical) {
1598 + if (!expression_evaluate(rc->critical)) {
1599 + error("Health for alarm '%s', failed to evaluate critical expression with error: %s", rc->name,
1600 + buffer_tostring(rc->critical->error_msg));
1601 + }
1602 + else {
1603 + debug(D_HEALTH, "Health for alarm '%s', critical expression gave value "
1604 + CALCULATED_NUMBER_FORMAT ": %s",
1605 + rc->name, rc->critical->result,
1606 + buffer_tostring(rc->critical->error_msg)
1607 + );
1608 + }
1609 + }
1610 +
1611 + rc->last_updated = now;
1612 + rc->next_update = now + rc->update_every;
1613 +
1614 + if (next_run > rc->next_update)
1615 + next_run = rc->next_update;
1616 + }
1617 +
1618 + rrdhost_unlock(&localhost);
1619 + }
1620 +
1621 +
1622 + if (unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
1623 + error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
1624 +
1625 + debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs",
1626 + loop, (int) (next_run - now));
1627 +
1628 + sleep_usec(1000000 * (unsigned long long) (next_run - now));
1629 + }
1630 +
1631 + buffer_free(wb);
1632 +
1633 + info("HEALTH thread exiting");
1634 + pthread_exit(NULL);
1635 + return NULL;
1636 }
src/health.h
+3
@@ -218,5 +218,8 @@ extern void rrdsetcalc_unlink(RRDCALC *rc);
218 extern void rrdcalctemplate_link_matching(RRDSET *st);
219
220 extern void health_init(void);
221 +extern void *health_main(void *ptr);
222 +
223 +extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result);
224
225 #endif //NETDATA_HEALTH_H
src/log.c
+4 -15
@@ -75,24 +75,13 @@ int open_log_file(int fd, FILE **fp, const char *filename, int *enabled_syslog)
75 }
76 else fd = f;
77
78 - if(fp && *fp == NULL) {
79 - // info("fdopen(%d) on filename '%s'", fd, filename);
80 -
81 - FILE *n = fdopen(fd, "a");
82 - if (!n)
78 + if(fp && !*fp) {
79 + *fp = fdopen(fd, "a");
80 + if (!*fp)
81 error("Cannot fdopen() fd %d ('%s')", fd, filename);
84 -
82 else {
86 - if (setvbuf(n, NULL, _IOLBF, 0) != 0)
83 + if (setvbuf(*fp, NULL, _IOLBF, 0) != 0)
84 error("Cannot set line buffering on fd %d ('%s')", fd, filename);
88 -
89 - if(!*fp)
90 - *fp = n;
91 - else {
92 - FILE *o = *fp;
93 - *fp = n;
94 - fclose(o);
95 - }
85 }
86 }
87
src/main.c
+2 -1
@@ -47,8 +47,9 @@ struct netdata_static_thread {
47 {"idlejitter", "plugins", "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
48 {"proc", "plugins", "proc", 1, NULL, NULL, proc_main},
49 {"cgroups", "plugins", "cgroups", 1, NULL, NULL, cgroups_main},
50 + {"check", "plugins", "checks", 0, NULL, NULL, checks_main},
51 + {"health", NULL, NULL, 1, NULL, NULL, health_main},
52 {"plugins.d", NULL, NULL, 1, NULL, NULL, pluginsd_main},
51 - {"check", "plugins", "checks", 0, NULL, NULL, checks_main},
53 {"web", NULL, NULL, 1, NULL, NULL, socket_listen_main_multi_threaded},
54 {"web-single-threaded", NULL, NULL, 0, NULL, NULL, socket_listen_main_single_threaded},
55 {NULL, NULL, NULL, 0, NULL, NULL, NULL}
src/plugins_d.c
+2 -3
@@ -439,9 +439,8 @@ void *pluginsd_worker_thread(void *arg)
439 return NULL;
440 }
441
442 -void *pluginsd_main(void *ptr)
443 -{
444 - if(ptr) { ; }
442 +void *pluginsd_main(void *ptr) {
443 + (void)ptr;
444
445 info("PLUGINS.D thread created with task id %d", gettid());
446
src/rrd.c
+27 -14
@@ -55,10 +55,17 @@ void rrdhost_unlock(RRDHOST *host) {
55 pthread_rwlock_unlock(&host->rrdset_root_rwlock);
56 }
57
58 +void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
59 + int ret = pthread_rwlock_trywrlock(&host->rrdset_root_rwlock);
60 +
61 + if(ret == 0)
62 + fatal("RRDHOST '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
63 +}
64 +
65 void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
66 int ret = pthread_rwlock_tryrdlock(&host->rrdset_root_rwlock);
67
61 - if(ret != 0)
68 + if(ret == 0)
69 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);
70 }
71
@@ -526,7 +533,7 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
533 avl_init_lock(&st->variables_root_index, rrdvar_compare);
534
535 pthread_rwlock_init(&st->rwlock, NULL);
529 - pthread_rwlock_wrlock(&localhost.rrdset_root_rwlock);
536 + rrdhost_rwlock(&localhost);
537
538 if(name && *name) rrdset_set_name(st, name);
539 else rrdset_set_name(st, id);
@@ -543,17 +550,19 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
550 st->next = localhost.rrdset_root;
551 localhost.rrdset_root = st;
552
546 - rrdsetvar_create(st, "last_collected", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, 0);
547 - rrdsetvar_create(st, "raw_total", RRDVAR_TYPE_TOTAL, &st->collected_total, 0);
548 - rrdsetvar_create(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, 0);
549 - rrdsetvar_create(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, 0);
553 + if(health_enabled) {
554 + rrdsetvar_create(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, 0);
555 + rrdsetvar_create(st, "collected_total_raw", RRDVAR_TYPE_TOTAL, &st->last_collected_total, 0);
556 + rrdsetvar_create(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, 0);
557 + rrdsetvar_create(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, 0);
558 + }
559
560 rrdset_index_add(&localhost, st);
561
562 rrdsetcalc_link_matching(st);
563 rrdcalctemplate_link_matching(st);
564
556 - pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
565 + rrdhost_unlock(&localhost);
566
567 return(st);
568 }
@@ -681,9 +690,11 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier
690 td->next = rd;
691 }
692
684 - rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->calculated_value, 0);
685 - rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->collected_value, 0);
686 - rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected", &rd->last_collected_time.tv_sec, 0);
693 + if(health_enabled) {
694 + rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->last_stored_value, 0);
695 + rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->last_collected_value, 0);
696 + rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->last_collected_time.tv_sec, 0);
697 + }
698
699 pthread_rwlock_unlock(&st->rwlock);
700
@@ -746,7 +757,7 @@ void rrdset_free_all(void)
757 {
758 info("Freeing all memory...");
759
749 - pthread_rwlock_wrlock(&localhost.rrdset_root_rwlock);
760 + rrdhost_rwlock(&localhost);
761
762 RRDSET *st;
763 for(st = localhost.rrdset_root; st ;) {
@@ -789,7 +800,7 @@ void rrdset_free_all(void)
800 }
801 localhost.rrdset_root = NULL;
802
792 - pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
803 + rrdhost_unlock(&localhost);
804
805 info("Memory cleanup completed...");
806 }
@@ -800,7 +811,7 @@ void rrdset_save_all(void) {
811 RRDSET *st;
812 RRDDIM *rd;
813
803 - pthread_rwlock_wrlock(&localhost.rrdset_root_rwlock);
814 + rrdhost_rwlock(&localhost);
815 for(st = localhost.rrdset_root; st ; st = st->next) {
816 pthread_rwlock_wrlock(&st->rwlock);
817
@@ -818,7 +829,7 @@ void rrdset_save_all(void) {
829
830 pthread_rwlock_unlock(&st->rwlock);
831 }
821 - pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
832 + rrdhost_unlock(&localhost);
833 }
834
835
@@ -1326,6 +1337,7 @@ unsigned long long rrdset_done(RRDSET *st)
1337
1338 if(likely(rd->updated && rd->counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
1339 rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
1340 + rd->last_stored_value = new_value;
1341
1342 if(unlikely(st->debug))
1343 debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
@@ -1341,6 +1353,7 @@ unsigned long long rrdset_done(RRDSET *st)
1353 , st->current_entry
1354 );
1355 rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
1356 + rd->last_stored_value = 0;
1357 }
1358
1359 stored_entries++;
src/rrd.h
+14 -4
@@ -138,11 +138,13 @@ struct rrddim {
138 // this is actual date time we updated the last_collected_value
139 // THIS IS DIFFERENT FROM THE SAME MEMBER OF RRDSET
140
141 - calculated_number calculated_value; // the current calculated value, after applying the algorithm
142 - calculated_number last_calculated_value; // the last calculated value
141 + calculated_number calculated_value; // the current calculated value, after applying the algorithm - resets to zero after being used
142 + calculated_number last_calculated_value; // the last calculated value processed
143
144 - collected_number collected_value; // the current value, as collected
145 - collected_number last_collected_value; // the last value that was collected
144 + calculated_number last_stored_value; // the last value as stored in the database (after interpolation)
145 +
146 + collected_number collected_value; // the current value, as collected - resets to 0 after being used
147 + collected_number last_collected_value; // the last value that was collected, after being processed
148
149 // the *_volume members are used to calculate the accuracy of the rounding done by the
150 // storage number - they are printed to debug.log when debug is enabled for a set.
@@ -315,10 +317,18 @@ extern RRDHOST localhost;
317
318 #ifdef NETDATA_INTERNAL_CHECKS
319 #define rrdhost_check_wrlock(host) rrdhost_check_wrlock_int(host, __FILE__, __FUNCTION__, __LINE__)
320 +#define rrdhost_check_rdlock(host) rrdhost_check_rdlock_int(host, __FILE__, __FUNCTION__, __LINE__)
321 #else
322 +#define rrdhost_check_rdlock(host) (void)0
323 #define rrdhost_check_wrlock(host) (void)0
324 #endif
325 +
326 extern void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
327 +extern void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
328 +
329 +extern void rrdhost_rwlock(RRDHOST *host);
330 +extern void rrdhost_rdlock(RRDHOST *host);
331 +extern void rrdhost_unlock(RRDHOST *host);
332
333 // ----------------------------------------------------------------------------
334 // RRD SET functions
src/rrd2json.c
+5 -5
@@ -1039,12 +1039,12 @@ inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, in
1039 }
1040
1041 if(unlikely(all_null)) {
1042 - if(likely(*all_values_are_null))
1042 + if(likely(all_values_are_null))
1043 *all_values_are_null = 1;
1044 return 0;
1045 }
1046 else {
1047 - if(likely(*all_values_are_null))
1047 + if(likely(all_values_are_null))
1048 *all_values_are_null = 0;
1049 }
1050
@@ -1524,7 +1524,7 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
1524 return r;
1525 }
1526
1527 -int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, BUFFER *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp, int *value_is_null)
1527 +int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp, int *value_is_null)
1528 {
1529 RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
1530 if(!r) {
@@ -1543,10 +1543,10 @@ int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, BUFFER *dimensions,
1543 else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
1544 wb->options |= WB_CONTENT_CACHEABLE;
1545
1546 - options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
1546 + options = rrdr_check_options(r, options, dimensions);
1547
1548 if(dimensions)
1549 - rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
1549 + rrdr_disable_not_selected_dimensions(r, dimensions);
1550
1551 if(latest_timestamp)
1552 *latest_timestamp = r->before;
src/rrd2json.h
+1 -1
@@ -61,6 +61,6 @@ extern void rrd_stats_all_json(BUFFER *wb);
61 extern time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long entries_to_show, long group, int group_method, time_t after, time_t before, int only_non_zero);
62
63 extern int rrd2format(RRDSET *st, BUFFER *out, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp);
64 -extern int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, BUFFER *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp, int *value_is_null);
64 +extern int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp, int *value_is_null);
65
66 #endif /* NETDATA_RRD2JSON_H */
src/web_client.c
+1 -1
@@ -759,7 +759,7 @@ int web_client_api_v1_badge(struct web_client *w, char *url) {
759
760 // if the collected value is too old, don't calculate its value
761 if(rrdset_last_entry_t(st) >= (time(NULL) - (st->update_every * st->gap_when_lost_iterations_above)))
762 - ret = rrd2value(st, w->response.data, &n, dimensions, points, after, before, group, options, &latest_timestamp, &value_is_null);
762 + ret = rrd2value(st, w->response.data, &n, (dimensions)?buffer_tostring(dimensions):NULL, points, after, before, group, options, &latest_timestamp, &value_is_null);
763
764 // if the value cannot be calculated, show empty badge
765 if(ret != 200) {