@cryptotaxi247 / netdata-1 / commits / 5be9be748

rewrite /api/v2/alerts (#15257)

* rewrite /api/v2/alerts * implement searching for transition * Find transition id and issue callback * Fix parameters * call and transition filter * Search with transition as well * renames and cleanup * render flags * what if scenario for moving transitions at the top level * If transition is given, limit the query appropriately * Add alert transitions * Optimize find transition to use prepared query Drop temp table properly * enabled alert instances again * Order by when key * Order by global_id * Return last X transitions * updated field names * add ati to configurations and show all keys in debug mode * Code cleanup and optimizations * Drop temp table in case of error * Finalize temp table population statement to prevent memory leak * final changes --------- Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>

Costa Tsaousis committed Jun 28, 2023 at 23:14 UTC 5be9be74854d00879e6d52d6432ae12b5e8558cd
18 files changed +1257 -940
database/contexts/api_v2.c
+744 -563
@@ -58,14 +58,14 @@ static const char *fts_match_to_string(FTS_MATCH match) {
58 }
59 }
60
61 -struct rrdfunction_to_json_v2 {
61 +struct function_v2_entry {
62 size_t size;
63 size_t used;
64 size_t *node_ids;
65 STRING *help;
66 };
67
68 -struct rrdcontext_to_json_v2_entry {
68 +struct context_v2_entry {
69 size_t count;
70 STRING *id;
71 STRING *family;
@@ -76,6 +76,31 @@ struct rrdcontext_to_json_v2_entry {
76 FTS_MATCH match;
77 };
78
79 +struct alert_v2_entry {
80 + RRDCALC *tmp;
81 +
82 + STRING *name;
83 +
84 + size_t ati;
85 +
86 + size_t critical;
87 + size_t warning;
88 + size_t clear;
89 + size_t error;
90 +
91 + size_t instances;
92 + DICTIONARY *nodes;
93 + DICTIONARY *configs;
94 +};
95 +
96 +struct alert_config_v2_entry {
97 + RRDCALC *tmp;
98 +
99 + size_t ati;
100 + size_t aci;
101 + size_t alerts_using_this;
102 +};
103 +
104 typedef struct full_text_search_index {
105 size_t searches;
106 size_t string_searches;
@@ -94,17 +119,18 @@ static inline bool full_text_search_char(FTS_INDEX *fts, SIMPLE_PATTERN *q, char
119 return simple_pattern_matches(q, ptr);
120 }
121
122 +struct contexts_v2_node {
123 + size_t ni;
124 + RRDHOST *host;
125 +};
126 +
127 struct rrdcontext_to_json_v2_data {
128 time_t now;
129
130 BUFFER *wb;
101 - union {
102 - struct api_v2_contexts_request *request;
103 - struct api_v2_alerts_request *alerts_request;
104 - };
105 -
106 - DICTIONARY *ctx;
131 + struct api_v2_contexts_request *request;
132
133 + CONTEXTS_V2_MODE mode;
134 CONTEXTS_V2_OPTIONS options;
135 struct query_versions versions;
136
@@ -112,22 +138,28 @@ struct rrdcontext_to_json_v2_data {
138 SIMPLE_PATTERN *scope_pattern;
139 SIMPLE_PATTERN *pattern;
140 size_t ni;
141 + DICTIONARY *dict; // the result set
142 } nodes;
143
144 struct {
118 - Pvoid_t JudyHS;
119 -// ALERT_OPTIONS alert_options;
145 SIMPLE_PATTERN *scope_pattern;
146 SIMPLE_PATTERN *pattern;
122 -// time_t after;
123 -// time_t before;
124 - size_t li;
125 - } alerts;
147 + size_t ci;
148 + DICTIONARY *dict; // the result set
149 + } contexts;
150
151 struct {
128 - SIMPLE_PATTERN *scope_pattern;
129 - SIMPLE_PATTERN *pattern;
130 - } contexts;
152 + SIMPLE_PATTERN *alert_name_pattern;
153 + time_t alarm_id_filter;
154 +
155 + size_t ati;
156 + size_t aii;
157 + size_t aci;
158 +
159 + DICTIONARY *alerts;
160 + DICTIONARY *alert_instances;
161 + DICTIONARY *alert_configs;
162 + } alerts;
163
164 struct {
165 FTS_MATCH host_match;
@@ -137,7 +169,7 @@ struct rrdcontext_to_json_v2_data {
169 } q;
170
171 struct {
140 - DICTIONARY *dict;
172 + DICTIONARY *dict; // the result set
173 } functions;
174
175 struct {
@@ -150,20 +182,119 @@ struct rrdcontext_to_json_v2_data {
182 struct query_timings timings;
183 };
184
153 -static void add_alert_index(Pvoid_t *JudyHS, uuid_t *uuid, ssize_t idx)
154 -{
155 - Pvoid_t *PValue = JudyHSIns(JudyHS, uuid, sizeof(*uuid), PJE0);
156 - if (!PValue)
157 - return;
158 - *((Word_t *) PValue) = (Word_t) idx;
185 +static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) {
186 + t->instances++;
187 +
188 + switch(rc->status) {
189 + case RRDCALC_STATUS_CRITICAL:
190 + t->critical++;
191 + break;
192 +
193 + case RRDCALC_STATUS_WARNING:
194 + t->warning++;
195 + break;
196 +
197 + case RRDCALC_STATUS_CLEAR:
198 + t->clear++;
199 + break;
200 +
201 + case RRDCALC_STATUS_REMOVED:
202 + case RRDCALC_STATUS_UNINITIALIZED:
203 + break;
204 +
205 + case RRDCALC_STATUS_UNDEFINED:
206 + default:
207 + if(!netdata_double_isnumber(rc->value))
208 + t->error++;
209 +
210 + break;
211 + }
212 +
213 + dictionary_set(t->nodes, rc->rrdset->rrdhost->machine_guid, NULL, 0);
214 +
215 + char key[UUID_STR_LEN + 1];
216 + uuid_unparse_lower(rc->config_hash_id, key);
217 + dictionary_set(t->configs, key, NULL, 0);
218 +}
219 +
220 +static void alerts_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
221 + struct rrdcontext_to_json_v2_data *ctl = data;
222 + struct alert_v2_entry *t = value;
223 + RRDCALC *rc = t->tmp;
224 + t->name = rc->name;
225 + t->ati = ctl->alerts.ati++;
226 +
227 + t->nodes = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE);
228 + t->configs = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE);
229 +
230 + alerts_v2_add(t, rc);
231 }
232
161 -ssize_t get_alert_index(Pvoid_t JudyHS, uuid_t *uuid)
162 -{
163 - Pvoid_t *PValue = JudyHSGet(JudyHS, uuid, sizeof(*uuid));
164 - if (!PValue)
165 - return -1;
166 - return (ssize_t) *((Word_t *) PValue);
233 +static bool alerts_v2_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
234 + struct alert_v2_entry *t = old_value, *n = new_value;
235 + RRDCALC *rc = n->tmp;
236 + alerts_v2_add(t, rc);
237 + return true;
238 +}
239 +
240 +static void alerts_v2_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
241 + struct alert_v2_entry *t = value;
242 + dictionary_destroy(t->nodes);
243 + dictionary_destroy(t->configs);
244 +}
245 +
246 +static void alert_configs_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
247 + struct rrdcontext_to_json_v2_data *ctl = data;
248 + struct alert_config_v2_entry *t = value;
249 + t->aci = ctl->alerts.aci++;
250 + t->alerts_using_this = 1;
251 +}
252 +
253 +static bool alert_configs_v2_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value __maybe_unused, void *new_value __maybe_unused, void *data __maybe_unused) {
254 + struct alert_config_v2_entry *t = old_value;
255 + t->alerts_using_this++;
256 + return false;
257 +}
258 +
259 +static void alert_configs_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) {
260 + ;
261 +}
262 +
263 +static void alert_instances_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
264 + struct rrdcontext_to_json_v2_data *ctl = data;
265 + struct alert_instance_v2_entry *t = value;
266 + RRDCALC *rc = t->tmp;
267 +
268 + t->chart_id = rc->rrdset->id;
269 + t->chart_name = rc->rrdset->name;
270 + t->family = rc->rrdset->family;
271 + t->name = rc->name;
272 + t->status = rc->status;
273 + t->flags = rc->run_flags;
274 + t->info = rc->info;
275 + t->value = rc->value;
276 + t->last_updated = rc->last_updated;
277 + t->last_status_change = rc->last_status_change;
278 + t->last_status_change_value = rc->last_status_change_value;
279 + t->host = rc->rrdset->rrdhost;
280 + t->alarm_id = rc->id;
281 + t->ni = ctl->nodes.ni;
282 + t->global_id = rc->ae ? rc->ae->global_id : 0;
283 + t->name = rc->name;
284 + t->aii = ctl->alerts.aii++;
285 +
286 + uuid_copy(t->config_hash_id, rc->config_hash_id);
287 + if(rc->ae)
288 + uuid_copy(t->last_transition_id, rc->ae->transition_id);
289 +}
290 +
291 +static bool alert_instances_v2_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value __maybe_unused, void *new_value __maybe_unused, void *data __maybe_unused) {
292 + internal_fatal(true, "This should never happen!");
293 + return true;
294 +}
295 +
296 +static void alert_instances_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) {
297 + ;
298 }
299
300 static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_json_v2_data *ctl, RRDCONTEXT *rc, SIMPLE_PATTERN *q) {
@@ -236,6 +367,93 @@ static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_jso
367 return matched;
368 }
369
370 +static bool rrdcontext_matches_alert(struct rrdcontext_to_json_v2_data *ctl, RRDCONTEXT *rc) {
371 + size_t matches = 0;
372 + RRDINSTANCE *ri;
373 + dfe_start_read(rc->rrdinstances, ri) {
374 + if(ri->rrdset) {
375 + RRDSET *st = ri->rrdset;
376 + netdata_rwlock_rdlock(&st->alerts.rwlock);
377 + for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) {
378 + if(ctl->alerts.alert_name_pattern && !simple_pattern_matches_string(ctl->alerts.alert_name_pattern, rcl->name))
379 + continue;
380 +
381 + if(ctl->alerts.alarm_id_filter && ctl->alerts.alarm_id_filter != rcl->id)
382 + continue;
383 +
384 + size_t m = ctl->request->alerts.status & CONTEXTS_V2_ALERT_STATUSES ? 0 : 1;
385 +
386 + if (!m) {
387 + if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_UNINITIALIZED) &&
388 + rcl->status == RRDCALC_STATUS_UNINITIALIZED)
389 + m++;
390 +
391 + if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_UNDEFINED) &&
392 + rcl->status == RRDCALC_STATUS_UNDEFINED)
393 + m++;
394 +
395 + if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_CLEAR) &&
396 + rcl->status == RRDCALC_STATUS_CLEAR)
397 + m++;
398 +
399 + if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_RAISED) &&
400 + rcl->status >= RRDCALC_STATUS_RAISED)
401 + m++;
402 +
403 + if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_WARNING) &&
404 + rcl->status == RRDCALC_STATUS_WARNING)
405 + m++;
406 +
407 + if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_CRITICAL) &&
408 + rcl->status == RRDCALC_STATUS_CRITICAL)
409 + m++;
410 +
411 + if(!m)
412 + continue;
413 + }
414 +
415 + struct alert_v2_entry t = {
416 + .tmp = rcl,
417 + };
418 + struct alert_v2_entry *a2e = dictionary_set(ctl->alerts.alerts, string2str(rcl->name), &t,
419 + sizeof(struct alert_v2_entry));
420 + size_t ati = a2e->ati;
421 + size_t aci = 0;
422 + matches++;
423 +
424 + if (ctl->options & CONTEXT_V2_OPTION_ALERT_CONFIGURATIONS) {
425 + char key[UUID_STR_LEN + 1];
426 + uuid_unparse_lower(rcl->config_hash_id, key);
427 + struct alert_config_v2_entry t2 = {
428 + .tmp = rcl,
429 + .ati = a2e->ati,
430 + };
431 + struct alert_config_v2_entry *a2c = dictionary_set(ctl->alerts.alert_configs, key, &t2,
432 + sizeof(struct alert_config_v2_entry));
433 + aci = a2c->aci;
434 + }
435 +
436 + if (ctl->options & CONTEXT_V2_OPTION_ALERT_INSTANCES) {
437 + char key[20 + 1];
438 + snprintfz(key, 20, "%p", rcl);
439 +
440 + struct alert_instance_v2_entry z = {
441 + .ati = ati,
442 + .aci = aci,
443 + .tmp = rcl,
444 + };
445 + dictionary_set(ctl->alerts.alert_instances, key, &z, sizeof(z));
446 + }
447 + }
448 + netdata_rwlock_unlock(&st->alerts.rwlock);
449 + }
450 + }
451 + dfe_done(ri);
452 +
453 + return matches != 0;
454 +}
455 +
456 +
457 static ssize_t rrdcontext_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED *rca, bool queryable_context __maybe_unused) {
458 struct rrdcontext_to_json_v2_data *ctl = data;
459
@@ -245,25 +463,32 @@ static ssize_t rrdcontext_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED
463 return 0; // continue to next context
464
465 FTS_MATCH match = ctl->q.host_match;
248 - if((ctl->options & CONTEXTS_V2_SEARCH) && ctl->q.pattern) {
466 + if((ctl->mode & CONTEXTS_V2_SEARCH) && ctl->q.pattern) {
467 match = rrdcontext_to_json_v2_full_text_search(ctl, rc, ctl->q.pattern);
468
469 if(match == FTS_MATCHED_NONE)
470 return 0; // continue to next context
471 }
472
255 - struct rrdcontext_to_json_v2_entry t = {
256 - .count = 1,
257 - .id = rc->id,
258 - .family = string_dup(rc->family),
259 - .priority = rc->priority,
260 - .first_time_s = rc->first_time_s,
261 - .last_time_s = rc->last_time_s,
262 - .flags = rc->flags,
263 - .match = match,
264 - };
473 + if(ctl->mode & CONTEXTS_V2_ALERTS) {
474 + if(!rrdcontext_matches_alert(ctl, rc))
475 + return 0; // continue to next context
476 + }
477
266 - dictionary_set(ctl->ctx, string2str(rc->id), &t, sizeof(t));
478 + if(ctl->contexts.dict) {
479 + struct context_v2_entry t = {
480 + .count = 1,
481 + .id = rc->id,
482 + .family = string_dup(rc->family),
483 + .priority = rc->priority,
484 + .first_time_s = rc->first_time_s,
485 + .last_time_s = rc->last_time_s,
486 + .flags = rc->flags,
487 + .match = match,
488 + };
489 +
490 + dictionary_set(ctl->contexts.dict, string2str(rc->id), &t, sizeof(struct context_v2_entry));
491 + }
492
493 return 1;
494 }
@@ -280,11 +505,6 @@ void buffer_json_agent_status_id(BUFFER *wb, size_t ai, usec_t duration_ut) {
505 buffer_json_object_close(wb);
506 }
507
283 -static ssize_t alert_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED *rca, bool queryable_context __maybe_unused)
284 -{
285 - return rrdcontext_to_json_v2_add_context(data, rca, queryable_context);
286 -}
287 -
508 void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status) {
509 buffer_json_member_add_string(wb, "mg", host->machine_guid);
510
@@ -453,6 +673,107 @@ static inline void rrdhost_health_to_json_v2(BUFFER *wb, const char *key, RRDHOS
673 buffer_json_object_close(wb); // health
674 }
675
676 +static void rrdcontext_to_json_v2_rrdhost(BUFFER *wb, RRDHOST *host, struct rrdcontext_to_json_v2_data *ctl, size_t node_id) {
677 + buffer_json_add_array_item_object(wb); // this node
678 + buffer_json_node_add_v2(wb, host, node_id, 0,
679 + (ctl->mode & CONTEXTS_V2_AGENTS) && !(ctl->mode & CONTEXTS_V2_NODES_INSTANCES));
680 +
681 + if(ctl->mode & (CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES)) {
682 + RRDHOST_STATUS s;
683 + rrdhost_status(host, ctl->now, &s);
684 +
685 + if (ctl->mode & (CONTEXTS_V2_NODES_INFO)) {
686 + buffer_json_member_add_string(wb, "v", rrdhost_program_version(host));
687 +
688 + host_labels2json(host, wb, "labels");
689 +
690 + if (host->system_info) {
691 + buffer_json_member_add_object(wb, "hw");
692 + {
693 + buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
694 + buffer_json_member_add_string_or_empty(wb, "cpu_frequency", host->system_info->host_cpu_freq);
695 + buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
696 + buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
697 + buffer_json_member_add_string_or_empty(wb, "disk_space", host->system_info->host_disk_space);
698 + buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
699 + buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
700 + }
701 + buffer_json_object_close(wb);
702 +
703 + buffer_json_member_add_object(wb, "os");
704 + {
705 + buffer_json_member_add_string_or_empty(wb, "id", host->system_info->host_os_id);
706 + buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->host_os_name);
707 + buffer_json_member_add_string_or_empty(wb, "v", host->system_info->host_os_version);
708 + buffer_json_member_add_object(wb, "kernel");
709 + buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->kernel_name);
710 + buffer_json_member_add_string_or_empty(wb, "v", host->system_info->kernel_version);
711 + buffer_json_object_close(wb);
712 + }
713 + buffer_json_object_close(wb);
714 + }
715 +
716 + // created - the node is created but never connected to cloud
717 + // unreachable - not currently connected
718 + // stale - connected but not having live data
719 + // reachable - connected with live data
720 + // pruned - not connected for some time and has been removed
721 + buffer_json_member_add_string(wb, "state", rrdhost_state_cloud_emulation(host) ? "reachable" : "stale");
722 +
723 + rrdhost_health_to_json_v2(wb, "health", &s);
724 + agent_capabilities_to_json(wb, host, "capabilities");
725 + }
726 +
727 + if (ctl->mode & (CONTEXTS_V2_NODES_INSTANCES)) {
728 + buffer_json_member_add_array(wb, "instances");
729 + buffer_json_add_array_item_object(wb); // this instance
730 + {
731 + buffer_json_agent_status_id(wb, 0, 0);
732 +
733 + buffer_json_member_add_object(wb, "db");
734 + {
735 + buffer_json_member_add_string(wb, "status", rrdhost_db_status_to_string(s.db.status));
736 + buffer_json_member_add_string(wb, "liveness", rrdhost_db_liveness_to_string(s.db.liveness));
737 + buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
738 + buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
739 + buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
740 + buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
741 + buffer_json_member_add_uint64(wb, "instances", s.db.instances);
742 + buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
743 + }
744 + buffer_json_object_close(wb);
745 +
746 + rrdhost_receiver_to_json(wb, &s, "ingest");
747 + rrdhost_sender_to_json(wb, &s, "stream");
748 +
749 + buffer_json_member_add_object(wb, "ml");
750 + buffer_json_member_add_string(wb, "status", rrdhost_ml_status_to_string(s.ml.status));
751 + buffer_json_member_add_string(wb, "type", rrdhost_ml_type_to_string(s.ml.type));
752 + if (s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
753 + buffer_json_member_add_object(wb, "metrics");
754 + {
755 + buffer_json_member_add_uint64(wb, "anomalous", s.ml.metrics.anomalous);
756 + buffer_json_member_add_uint64(wb, "normal", s.ml.metrics.normal);
757 + buffer_json_member_add_uint64(wb, "trained", s.ml.metrics.trained);
758 + buffer_json_member_add_uint64(wb, "pending", s.ml.metrics.pending);
759 + buffer_json_member_add_uint64(wb, "silenced", s.ml.metrics.silenced);
760 + }
761 + buffer_json_object_close(wb); // metrics
762 + }
763 + buffer_json_object_close(wb); // ml
764 +
765 + rrdhost_health_to_json_v2(wb, "health", &s);
766 +
767 + host_functions2json(host, wb); // functions
768 + agent_capabilities_to_json(wb, host, "capabilities");
769 + }
770 + buffer_json_object_close(wb); // this instance
771 + buffer_json_array_close(wb); // instances
772 + }
773 + }
774 + buffer_json_object_close(wb); // this node
775 +}
776 +
777 static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool queryable_host) {
778 if(!queryable_host || !host->rrdctx.contexts)
779 // the host matches the 'scope_host' but does not match the 'host' patterns
@@ -460,7 +781,6 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu
781 return 0; // continue to next host
782
783 struct rrdcontext_to_json_v2_data *ctl = data;
463 - BUFFER *wb = ctl->wb;
784
785 if(ctl->window.enabled && !rrdhost_matches_window(host, ctl->window.after, ctl->window.before, ctl->now))
786 // the host does not have data in the requested window
@@ -474,11 +794,11 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu
794 // interrupted
795 return -1; // stop the query
796
477 - bool host_matched = (ctl->options & CONTEXTS_V2_NODES);
478 - bool do_contexts = (ctl->options & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH));
797 + bool host_matched = (ctl->mode & CONTEXTS_V2_NODES);
798 + bool do_contexts = (ctl->mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_ALERTS));
799
800 ctl->q.host_match = FTS_MATCHED_NONE;
481 - if((ctl->options & CONTEXTS_V2_SEARCH)) {
801 + if((ctl->mode & CONTEXTS_V2_SEARCH)) {
802 // check if we match the host itself
803 if(ctl->q.pattern && (
804 full_text_search_string(&ctl->q.fts, ctl->q.pattern, host->hostname) ||
@@ -515,8 +835,8 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu
835 if(!host_matched)
836 return 0;
837
518 - if(ctl->options & CONTEXTS_V2_FUNCTIONS) {
519 - struct rrdfunction_to_json_v2 t = {
838 + if(ctl->mode & CONTEXTS_V2_FUNCTIONS) {
839 + struct function_v2_entry t = {
840 .used = 1,
841 .size = 1,
842 .node_ids = &ctl->nodes.ni,
@@ -525,363 +845,49 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu
845 host_functions_to_dict(host, ctl->functions.dict, &t, sizeof(t), &t.help);
846 }
847
528 - if(ctl->options & CONTEXTS_V2_NODES) {
529 - buffer_json_add_array_item_object(wb); // this node
530 - buffer_json_node_add_v2(wb, host, ctl->nodes.ni++, 0,
531 - (ctl->options & CONTEXTS_V2_AGENTS) && !(ctl->options & CONTEXTS_V2_NODES_INSTANCES));
532 -
533 - if(ctl->options & (CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES)) {
534 - RRDHOST_STATUS s;
535 - rrdhost_status(host, ctl->now, &s);
536 -
537 - if (ctl->options & (CONTEXTS_V2_NODES_INFO)) {
538 - buffer_json_member_add_string(wb, "v", rrdhost_program_version(host));
539 -
540 - host_labels2json(host, wb, "labels");
541 -
542 - if (host->system_info) {
543 - buffer_json_member_add_object(wb, "hw");
544 - {
545 - buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
546 - buffer_json_member_add_string_or_empty(wb, "cpu_frequency", host->system_info->host_cpu_freq);
547 - buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
548 - buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
549 - buffer_json_member_add_string_or_empty(wb, "disk_space", host->system_info->host_disk_space);
550 - buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
551 - buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
552 - }
553 - buffer_json_object_close(wb);
554 -
555 - buffer_json_member_add_object(wb, "os");
556 - {
557 - buffer_json_member_add_string_or_empty(wb, "id", host->system_info->host_os_id);
558 - buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->host_os_name);
559 - buffer_json_member_add_string_or_empty(wb, "v", host->system_info->host_os_version);
560 - buffer_json_member_add_object(wb, "kernel");
561 - buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->kernel_name);
562 - buffer_json_member_add_string_or_empty(wb, "v", host->system_info->kernel_version);
563 - buffer_json_object_close(wb);
564 - }
565 - buffer_json_object_close(wb);
566 - }
567 -
568 - // created - the node is created but never connected to cloud
569 - // unreachable - not currently connected
570 - // stale - connected but not having live data
571 - // reachable - connected with live data
572 - // pruned - not connected for some time and has been removed
573 - buffer_json_member_add_string(wb, "state", rrdhost_state_cloud_emulation(host) ? "reachable" : "stale");
574 -
575 - rrdhost_health_to_json_v2(wb, "health", &s);
576 - agent_capabilities_to_json(wb, host, "capabilities");
577 - }
578 -
579 - if (ctl->options & (CONTEXTS_V2_NODES_INSTANCES)) {
580 - buffer_json_member_add_array(wb, "instances");
581 - buffer_json_add_array_item_object(wb); // this instance
582 - {
583 - buffer_json_agent_status_id(wb, 0, 0);
584 -
585 - buffer_json_member_add_object(wb, "db");
586 - {
587 - buffer_json_member_add_string(wb, "status", rrdhost_db_status_to_string(s.db.status));
588 - buffer_json_member_add_string(wb, "liveness", rrdhost_db_liveness_to_string(s.db.liveness));
589 - buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
590 - buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
591 - buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
592 - buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
593 - buffer_json_member_add_uint64(wb, "instances", s.db.instances);
594 - buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
595 - }
596 - buffer_json_object_close(wb);
597 -
598 - rrdhost_receiver_to_json(wb, &s, "ingest");
599 - rrdhost_sender_to_json(wb, &s, "stream");
600 -
601 - buffer_json_member_add_object(wb, "ml");
602 - buffer_json_member_add_string(wb, "status", rrdhost_ml_status_to_string(s.ml.status));
603 - buffer_json_member_add_string(wb, "type", rrdhost_ml_type_to_string(s.ml.type));
604 - if (s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
605 - buffer_json_member_add_object(wb, "metrics");
606 - {
607 - buffer_json_member_add_uint64(wb, "anomalous", s.ml.metrics.anomalous);
608 - buffer_json_member_add_uint64(wb, "normal", s.ml.metrics.normal);
609 - buffer_json_member_add_uint64(wb, "trained", s.ml.metrics.trained);
610 - buffer_json_member_add_uint64(wb, "pending", s.ml.metrics.pending);
611 - buffer_json_member_add_uint64(wb, "silenced", s.ml.metrics.silenced);
612 - }
613 - buffer_json_object_close(wb); // metrics
614 - }
615 - buffer_json_object_close(wb); // ml
616 -
617 - rrdhost_health_to_json_v2(wb, "health", &s);
848 + if(ctl->mode & CONTEXTS_V2_NODES) {
849 + struct contexts_v2_node t = {
850 + .ni = ctl->nodes.ni++,
851 + .host = host,
852 + };
853
619 - host_functions2json(host, wb); // functions
620 - agent_capabilities_to_json(wb, host, "capabilities");
621 - }
622 - buffer_json_object_close(wb); // this instance
623 - buffer_json_array_close(wb); // instances
624 - }
625 - }
626 - buffer_json_object_close(wb); // this node
854 + dictionary_set(ctl->nodes.dict, host->machine_guid, &t, sizeof(struct contexts_v2_node));
855 }
856
857 return 1;
858 }
859
632 -static ssize_t alert_to_json_v2_add_host(void *data, RRDHOST *host, bool queryable_host) {
633 - if(!queryable_host || !host->rrdctx.contexts)
634 - // the host matches the 'scope_host' but does not match the 'host' patterns
635 - // or the host does not have any contexts
636 - return 0;
637 -
638 - struct rrdcontext_to_json_v2_data *ctl = data;
639 - BUFFER *wb = ctl->wb;
640 -
641 - bool host_matched = (ctl->options & CONTEXTS_V2_NODES);
642 - bool do_contexts = (ctl->options & (CONTEXTS_V2_CONTEXTS));
643 -
644 - ctl->q.host_match = FTS_MATCHED_NONE;
645 - if((ctl->options & CONTEXTS_V2_SEARCH)) {
646 - // check if we match the host itself
647 - if(ctl->q.pattern && (
648 - full_text_search_string(&ctl->q.fts, ctl->q.pattern, host->hostname) ||
649 - full_text_search_char(&ctl->q.fts, ctl->q.pattern, host->machine_guid) ||
650 - (ctl->q.pattern && full_text_search_char(&ctl->q.fts, ctl->q.pattern, ctl->q.host_node_id_str)))) {
651 - ctl->q.host_match = FTS_MATCHED_HOST;
652 - do_contexts = true;
653 - }
654 - }
655 -
656 - if(do_contexts) {
657 - // save it
658 - SIMPLE_PATTERN *old_q = ctl->q.pattern;
659 -
660 - if(ctl->q.host_match == FTS_MATCHED_HOST)
661 - // do not do pattern matching on contexts - we matched the host itself
662 - ctl->q.pattern = NULL;
663 -
664 - ssize_t added = query_scope_foreach_context(
665 - host, ctl->alerts_request->scope_contexts,
666 - ctl->contexts.scope_pattern, ctl->contexts.pattern,
667 - alert_to_json_v2_add_context, queryable_host, ctl);
668 -
669 - // restore it
670 - ctl->q.pattern = old_q;
671 -
672 - if(added == -1)
673 - return -1;
674 -
675 - if(added)
676 - host_matched = true;
677 - }
678 -
679 - if(host_matched && (ctl->options & (CONTEXTS_V2_NODES))) {
680 - buffer_json_add_array_item_object(wb);
681 - buffer_json_node_add_v2(wb, host, ctl->nodes.ni++, 0, false);
682 -
683 - if (ctl->alerts_request->options & ALERT_OPTION_TRANSITIONS) {
684 - if (rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)) {
685 - buffer_json_member_add_array(wb, "instances");
686 - health_alert2json(host, wb, ctl->alerts_request->options, ctl->alerts.JudyHS, ctl->alerts_request->after, ctl->alerts_request->before, ctl->alerts_request->last);
687 - buffer_json_array_close(wb);
688 - }
689 - }
690 -
691 - buffer_json_object_close(wb);
692 - }
693 -
694 - return host_matched ? 1 : 0;
695 -}
696 -
697 -static inline bool alert_is_matched( struct api_v2_alerts_request *alerts_request, RRDCALC *rc)
698 -{
699 - char hash_id[UUID_STR_LEN];
700 - uuid_unparse_lower(rc->config_hash_id, hash_id);
701 -
702 - if (alerts_request->alert_id)
703 - return (rc->id == alerts_request->alert_id);
704 -
705 - SIMPLE_PATTERN_RESULT match = SP_MATCHED_POSITIVE;
706 - SIMPLE_PATTERN *match_pattern = alerts_request->config_hash_pattern;
707 - if(match_pattern) {
708 - match = simple_pattern_matches_extract(match_pattern, hash_id, NULL, 0);
709 - if(match == SP_NOT_MATCHED)
710 - return false;;
711 - }
712 -
713 - match = SP_MATCHED_POSITIVE;
714 - match_pattern = alerts_request->alert_name_pattern;
715 - if(match_pattern) {
716 - match = simple_pattern_matches_string_extract(match_pattern, rc->name, NULL, 0);
717 - if(match == SP_NOT_MATCHED)
718 - return false;
719 - }
720 -
721 - return true;
722 -}
723 -
724 -static ssize_t alert_to_json_v2_add_alert(void *data, RRDHOST *host, bool queryable_host) {
725 - if(!queryable_host || !host->rrdctx.contexts)
726 - // the host matches the 'scope_host' but does not match the 'host' patterns
727 - // or the host does not have any contexts
728 - return 0;
729 -
730 - struct rrdcontext_to_json_v2_data *ctl = data;
731 - BUFFER *wb = ctl->wb;
732 -
733 - bool host_matched = (ctl->options & CONTEXTS_V2_NODES);
734 - bool do_contexts = (ctl->options & (CONTEXTS_V2_CONTEXTS));
735 -
736 - if(do_contexts) {
737 - ssize_t added = query_scope_foreach_context(
738 - host, ctl->request->scope_contexts,
739 - ctl->contexts.scope_pattern, ctl->contexts.pattern,
740 - alert_to_json_v2_add_context, queryable_host, ctl);
741 -
742 - if(added == -1)
743 - return -1;
744 -
745 - if(added)
746 - host_matched = true;
747 - }
748 -
749 - if(host_matched && (ctl->options & (CONTEXTS_V2_NODES))) {
750 - if (rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)) {
751 -
752 - RRDCALC *rc;
753 - foreach_rrdcalc_in_rrdhost_read(host, rc) {
754 - if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
755 - continue;
756 -
757 - if (unlikely(!rrdset_is_available_for_exporting_and_alarms(rc->rrdset)))
758 - continue;
759 -
760 - if ((ctl->alerts_request->options & ALERT_OPTION_ACTIVE) &&
761 - !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL))
762 - continue;
763 -
764 - char hash_id[GUID_LEN + 1];
765 - uuid_unparse_lower(rc->config_hash_id, hash_id);
766 -
767 - if (!alert_is_matched(ctl->alerts_request, rc))
768 - continue;
769 -
770 - ssize_t idx = get_alert_index(ctl->alerts.JudyHS, &rc->config_hash_id);
771 - if (idx >= 0)
772 - continue;
773 -
774 - buffer_json_add_array_item_object(wb);
775 - add_alert_index(&ctl->alerts.JudyHS, &rc->config_hash_id, (ssize_t)ctl->alerts.li++);
776 -
777 - buffer_json_member_add_string(wb, "config_hash_id", hash_id);
778 - buffer_json_member_add_string(wb, "name", rrdcalc_name(rc));
779 - buffer_json_member_add_string(wb, "chart", rrdcalc_chart_name(rc));
780 - buffer_json_member_add_string(wb, "family", (rc->rrdset) ? rrdset_family(rc->rrdset) : "");
781 - buffer_json_member_add_string(wb, "class", rc->classification ? rrdcalc_classification(rc) : "Unknown");
782 - buffer_json_member_add_string(wb, "component", rc->component ? rrdcalc_component(rc) : "Unknown");
783 - buffer_json_member_add_string(wb, "type", rc->type ? rrdcalc_type(rc) : "Unknown");
784 - buffer_json_member_add_string(wb, "units", rrdcalc_units(rc));
785 - buffer_json_member_add_boolean(wb, "enabled", host->health.health_enabled);
786 -
787 - if (ctl->alerts_request->options & ALERT_OPTION_CONFIG) {
788 - buffer_json_member_add_object(wb, "config");
789 - {
790 - buffer_json_member_add_boolean(wb, "active", (rc->rrdset));
791 - buffer_json_member_add_boolean(wb, "disabled", (rc->run_flags & RRDCALC_FLAG_DISABLED));
792 - buffer_json_member_add_boolean(wb, "silenced", (rc->run_flags & RRDCALC_FLAG_SILENCED));
793 - buffer_json_member_add_string(
794 - wb, "exec", rc->exec ? rrdcalc_exec(rc) : string2str(host->health.health_default_exec));
795 - buffer_json_member_add_string(
796 - wb,
797 - "recipient",
798 - rc->recipient ? rrdcalc_recipient(rc) : string2str(host->health.health_default_recipient));
799 - buffer_json_member_add_string(wb, "info", rrdcalc_info(rc));
800 - buffer_json_member_add_string(wb, "source", rrdcalc_source(rc));
801 - buffer_json_member_add_time_t(wb, "update_every", rc->update_every);
802 - buffer_json_member_add_time_t(wb, "delay_up_duration", rc->delay_up_duration);
803 - buffer_json_member_add_time_t(wb, "delay_down_duration", rc->delay_down_duration);
804 - buffer_json_member_add_time_t(wb, "delay_max_duration", rc->delay_max_duration);
805 - buffer_json_member_add_double(wb, "delay_multiplier", rc->delay_multiplier);
806 - buffer_json_member_add_time_t(wb, "delay", rc->delay_last);
807 - buffer_json_member_add_time_t(wb, "warn_repeat_every", rc->warn_repeat_every);
808 - buffer_json_member_add_time_t(wb, "crit_repeat_every", rc->crit_repeat_every);
809 - if (unlikely(rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)) {
810 - buffer_json_member_add_boolean(wb, "no_clear_notification", true);
811 - }
812 -
813 - if (rc->calculation) {
814 - buffer_json_member_add_string(wb, "calc", rc->calculation->source);
815 - buffer_json_member_add_string(wb, "calc_parsed", rc->calculation->parsed_as);
816 - }
817 -
818 - if (rc->warning) {
819 - buffer_json_member_add_string(wb, "warn", rc->warning->source);
820 - buffer_json_member_add_string(wb, "warn_parsed", rc->warning->parsed_as);
821 - }
822 -
823 - if (rc->critical) {
824 - buffer_json_member_add_string(wb, "crit", rc->critical->source);
825 - buffer_json_member_add_string(wb, "crit_parsed", rc->critical->parsed_as);
826 - }
827 -
828 - if (RRDCALC_HAS_DB_LOOKUP(rc)) {
829 - if (rc->dimensions)
830 - buffer_json_member_add_string(wb, "lookup_dimensions", rrdcalc_dimensions(rc));
831 -
832 - buffer_json_member_add_string(wb, "lookup_method", time_grouping_method2string(rc->group));
833 - buffer_json_member_add_time_t(wb, "lookup_after", rc->after);
834 - buffer_json_member_add_time_t(wb, "lookup_before", rc->before);
835 -
836 - BUFFER *temp_id = buffer_create(1, NULL);
837 - buffer_data_options2string(temp_id, rc->options);
838 -
839 - buffer_json_member_add_string(wb, "lookup_options", buffer_tostring(temp_id));
840 -
841 - buffer_free(temp_id);
842 - }
843 - }
844 - buffer_json_object_close(wb); // config
845 - }
846 - buffer_json_object_close(wb); // Alert
847 - }
848 - foreach_rrdcalc_in_rrdhost_done(rc);
849 - }
850 - }
851 -
852 - return host_matched ? 1 : 0;
853 -}
854 -
855 -static void buffer_json_contexts_v2_options_to_array(BUFFER *wb, CONTEXTS_V2_OPTIONS options) {
856 - if(options & CONTEXTS_V2_DEBUG)
857 - buffer_json_add_array_item_string(wb, "debug");
858 -
859 - if(options & CONTEXTS_V2_MINIFY)
860 - buffer_json_add_array_item_string(wb, "minify");
860 +static void buffer_json_contexts_v2_mode_to_array(BUFFER *wb, const char *key, CONTEXTS_V2_MODE mode) {
861 + buffer_json_member_add_array(wb, key);
862
862 - if(options & CONTEXTS_V2_VERSIONS)
863 + if(mode & CONTEXTS_V2_VERSIONS)
864 buffer_json_add_array_item_string(wb, "versions");
865
865 - if(options & CONTEXTS_V2_AGENTS)
866 + if(mode & CONTEXTS_V2_AGENTS)
867 buffer_json_add_array_item_string(wb, "agents");
868
868 - if(options & CONTEXTS_V2_AGENTS_INFO)
869 + if(mode & CONTEXTS_V2_AGENTS_INFO)
870 buffer_json_add_array_item_string(wb, "agents-info");
871
871 - if(options & CONTEXTS_V2_NODES)
872 + if(mode & CONTEXTS_V2_NODES)
873 buffer_json_add_array_item_string(wb, "nodes");
874
874 - if(options & CONTEXTS_V2_NODES_INFO)
875 + if(mode & CONTEXTS_V2_NODES_INFO)
876 buffer_json_add_array_item_string(wb, "nodes-info");
877
877 - if(options & CONTEXTS_V2_NODES_INSTANCES)
878 + if(mode & CONTEXTS_V2_NODES_INSTANCES)
879 buffer_json_add_array_item_string(wb, "nodes-instances");
880
880 - if(options & CONTEXTS_V2_CONTEXTS)
881 + if(mode & CONTEXTS_V2_CONTEXTS)
882 buffer_json_add_array_item_string(wb, "contexts");
883
883 - if(options & CONTEXTS_V2_SEARCH)
884 + if(mode & CONTEXTS_V2_SEARCH)
885 buffer_json_add_array_item_string(wb, "search");
886 +
887 + if(mode & CONTEXTS_V2_ALERTS)
888 + buffer_json_add_array_item_string(wb, "alerts");
889 +
890 + buffer_json_array_close(wb);
891 }
892
893 void buffer_json_query_timings(BUFFER *wb, const char *key, struct query_timings *timings) {
@@ -980,7 +986,7 @@ void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings
986 }
987
988 static void functions_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
983 - struct rrdfunction_to_json_v2 *t = value;
989 + struct function_v2_entry *t = value;
990
991 // it is initialized with a static reference - we need to mallocz() the array
992 size_t *v = t->node_ids;
@@ -991,7 +997,7 @@ static void functions_insert_callback(const DICTIONARY_ITEM *item __maybe_unused
997 }
998
999 static bool functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
994 - struct rrdfunction_to_json_v2 *t = old_value, *n = new_value;
1000 + struct function_v2_entry *t = old_value, *n = new_value;
1001 size_t *v = n->node_ids;
1002
1003 if(t->used >= t->size) {
@@ -1005,13 +1011,13 @@ static bool functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
1011 }
1012
1013 static void functions_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
1008 - struct rrdfunction_to_json_v2 *t = value;
1014 + struct function_v2_entry *t = value;
1015 freez(t->node_ids);
1016 }
1017
1018 static bool contexts_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
1013 - struct rrdcontext_to_json_v2_entry *o = old_value;
1014 - struct rrdcontext_to_json_v2_entry *n = new_value;
1019 + struct context_v2_entry *o = old_value;
1020 + struct context_v2_entry *n = new_value;
1021
1022 o->count++;
1023
@@ -1052,33 +1058,63 @@ static bool contexts_conflict_callback(const DICTIONARY_ITEM *item __maybe_unuse
1058 }
1059
1060 static void contexts_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
1055 - struct rrdcontext_to_json_v2_entry *z = value;
1061 + struct context_v2_entry *z = value;
1062 string_freez(z->family);
1063 }
1064
1059 -int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_OPTIONS options) {
1065 +static void rrdcontext_v2_set_transition_filter(const char *machine_guid, const char *context, time_t alarm_id, void *data) {
1066 + struct rrdcontext_to_json_v2_data *ctl = data;
1067 +
1068 + if(machine_guid && *machine_guid) {
1069 + if(ctl->nodes.scope_pattern)
1070 + simple_pattern_free(ctl->nodes.scope_pattern);
1071 +
1072 + if(ctl->nodes.pattern)
1073 + simple_pattern_free(ctl->nodes.pattern);
1074 +
1075 + ctl->nodes.scope_pattern = string_to_simple_pattern(machine_guid);
1076 + ctl->nodes.pattern = NULL;
1077 + }
1078 +
1079 + if(context && *context) {
1080 + if(ctl->contexts.scope_pattern)
1081 + simple_pattern_free(ctl->contexts.scope_pattern);
1082 +
1083 + if(ctl->contexts.pattern)
1084 + simple_pattern_free(ctl->contexts.pattern);
1085 +
1086 + ctl->contexts.scope_pattern = string_to_simple_pattern(context);
1087 + ctl->contexts.pattern = NULL;
1088 + }
1089 +
1090 + ctl->alerts.alarm_id_filter = alarm_id;
1091 +}
1092 +
1093 +int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_MODE mode) {
1094 int resp = HTTP_RESP_OK;
1095 + bool run = true;
1096
1062 - if(options & CONTEXTS_V2_SEARCH)
1063 - options |= CONTEXTS_V2_CONTEXTS;
1097 + if(mode & CONTEXTS_V2_SEARCH)
1098 + mode |= CONTEXTS_V2_CONTEXTS;
1099
1065 - if(options & (CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES))
1066 - options |= CONTEXTS_V2_NODES;
1100 + if(mode & (CONTEXTS_V2_AGENTS_INFO))
1101 + mode |= CONTEXTS_V2_AGENTS;
1102
1068 - if(options & (CONTEXTS_V2_AGENTS_INFO))
1069 - options |= CONTEXTS_V2_AGENTS;
1103 + if(mode & (CONTEXTS_V2_ALERTS | CONTEXTS_V2_FUNCTIONS | CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES))
1104 + mode |= CONTEXTS_V2_NODES;
1105
1106 struct rrdcontext_to_json_v2_data ctl = {
1107 .wb = wb,
1108 .request = req,
1074 - .ctx = NULL,
1075 - .options = options,
1109 + .mode = mode,
1110 + .options = req->options,
1111 .versions = { 0 },
1112 .nodes.scope_pattern = string_to_simple_pattern(req->scope_nodes),
1113 .nodes.pattern = string_to_simple_pattern(req->nodes),
1114 .contexts.pattern = string_to_simple_pattern(req->contexts),
1115 .contexts.scope_pattern = string_to_simple_pattern(req->scope_contexts),
1116 .q.pattern = string_to_simple_pattern_nocase(req->q),
1117 + .alerts.alert_name_pattern = string_to_simple_pattern(req->alerts.alert),
1118 .window = {
1119 .enabled = false,
1120 .relative = false,
@@ -1090,23 +1126,66 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
1126 }
1127 };
1128
1093 - if(options & CONTEXTS_V2_CONTEXTS) {
1094 - ctl.ctx = dictionary_create_advanced(
1129 + bool debug = ctl.options & CONTEXT_V2_OPTION_DEBUG;
1130 +
1131 + if(mode & CONTEXTS_V2_NODES) {
1132 + ctl.nodes.dict = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
1133 + NULL, sizeof(struct contexts_v2_node));
1134 + }
1135 +
1136 + if(mode & CONTEXTS_V2_CONTEXTS) {
1137 + ctl.contexts.dict = dictionary_create_advanced(
1138 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL,
1096 - sizeof(struct rrdcontext_to_json_v2_entry));
1139 + sizeof(struct context_v2_entry));
1140
1098 - dictionary_register_conflict_callback(ctl.ctx, contexts_conflict_callback, NULL);
1099 - dictionary_register_delete_callback(ctl.ctx, contexts_delete_callback, NULL);
1141 + dictionary_register_conflict_callback(ctl.contexts.dict, contexts_conflict_callback, &ctl);
1142 + dictionary_register_delete_callback(ctl.contexts.dict, contexts_delete_callback, &ctl);
1143 }
1144
1102 - if(options & CONTEXTS_V2_FUNCTIONS) {
1145 + if(mode & CONTEXTS_V2_FUNCTIONS) {
1146 ctl.functions.dict = dictionary_create_advanced(
1147 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL,
1105 - sizeof(struct rrdfunction_to_json_v2));
1148 + sizeof(struct function_v2_entry));
1149 +
1150 + dictionary_register_insert_callback(ctl.functions.dict, functions_insert_callback, &ctl);
1151 + dictionary_register_conflict_callback(ctl.functions.dict, functions_conflict_callback, &ctl);
1152 + dictionary_register_delete_callback(ctl.functions.dict, functions_delete_callback, &ctl);
1153 + }
1154 +
1155 + if(mode & CONTEXTS_V2_ALERTS) {
1156 + if(req->alerts.transition) {
1157 + ctl.options |= CONTEXT_V2_OPTION_ALERT_INSTANCES | CONTEXT_V2_OPTION_ALERT_CONFIGURATIONS | CONTEXT_V2_OPTION_ALERT_TRANSITIONS;
1158 + run = sql_find_alert_transition(req->alerts.transition, rrdcontext_v2_set_transition_filter, &ctl);
1159 + if(!run) {
1160 + resp = HTTP_RESP_NOT_FOUND;
1161 + goto cleanup;
1162 + }
1163 + }
1164
1107 - dictionary_register_insert_callback(ctl.functions.dict, functions_insert_callback, NULL);
1108 - dictionary_register_conflict_callback(ctl.functions.dict, functions_conflict_callback, NULL);
1109 - dictionary_register_delete_callback(ctl.functions.dict, functions_delete_callback, NULL);
1165 + ctl.alerts.alerts = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
1166 + NULL, sizeof(struct alert_v2_entry));
1167 +
1168 + dictionary_register_insert_callback(ctl.alerts.alerts, alerts_v2_insert_callback, &ctl);
1169 + dictionary_register_conflict_callback(ctl.alerts.alerts, alerts_v2_conflict_callback, &ctl);
1170 + dictionary_register_delete_callback(ctl.alerts.alerts, alerts_v2_delete_callback, &ctl);
1171 +
1172 + if(ctl.options & CONTEXT_V2_OPTION_ALERT_INSTANCES) {
1173 + ctl.alerts.alert_instances = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
1174 + NULL, sizeof(struct alert_instance_v2_entry));
1175 +
1176 + dictionary_register_insert_callback(ctl.alerts.alert_instances, alert_instances_v2_insert_callback, &ctl);
1177 + dictionary_register_conflict_callback(ctl.alerts.alert_instances, alert_instances_v2_conflict_callback, &ctl);
1178 + dictionary_register_delete_callback(ctl.alerts.alert_instances, alert_instances_delete_callback, &ctl);
1179 + }
1180 +
1181 + if(ctl.options & CONTEXT_V2_OPTION_ALERT_CONFIGURATIONS) {
1182 + ctl.alerts.alert_configs = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
1183 + NULL, sizeof(struct alert_config_v2_entry));
1184 +
1185 + dictionary_register_insert_callback(ctl.alerts.alert_configs, alert_configs_v2_insert_callback, &ctl);
1186 + dictionary_register_conflict_callback(ctl.alerts.alert_configs, alert_configs_v2_conflict_callback, &ctl);
1187 + dictionary_register_delete_callback(ctl.alerts.alert_configs, alert_configs_delete_callback, &ctl);
1188 + }
1189 }
1190
1191 if(req->after || req->before) {
@@ -1117,40 +1196,58 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
1196 ctl.now = now_realtime_sec();
1197
1198 buffer_json_initialize(wb, "\"", "\"", 0,
1120 - true, (options & CONTEXTS_V2_MINIFY) && !(options & CONTEXTS_V2_DEBUG));
1199 + true, (req->options & CONTEXT_V2_OPTION_MINIFY) && !(req->options & CONTEXT_V2_OPTION_DEBUG));
1200
1201 buffer_json_member_add_uint64(wb, "api", 2);
1202
1124 - if(options & CONTEXTS_V2_DEBUG) {
1203 + if(req->options & CONTEXT_V2_OPTION_DEBUG) {
1204 buffer_json_member_add_object(wb, "request");
1205 + {
1206 + buffer_json_contexts_v2_mode_to_array(wb, "mode", mode);
1207 + web_client_api_request_v2_contexts_options_to_buffer_json_array(wb, "options", req->options);
1208
1127 - buffer_json_member_add_object(wb, "scope");
1128 - buffer_json_member_add_string(wb, "scope_nodes", req->scope_nodes);
1129 - buffer_json_member_add_string(wb, "scope_contexts", req->scope_contexts);
1130 - buffer_json_object_close(wb);
1131 -
1132 - buffer_json_member_add_object(wb, "selectors");
1133 - buffer_json_member_add_string(wb, "nodes", req->nodes);
1134 - buffer_json_member_add_string(wb, "contexts", req->contexts);
1135 - buffer_json_object_close(wb);
1209 + buffer_json_member_add_object(wb, "scope");
1210 + {
1211 + buffer_json_member_add_string(wb, "scope_nodes", req->scope_nodes);
1212 + if (mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_ALERTS))
1213 + buffer_json_member_add_string(wb, "scope_contexts", req->scope_contexts);
1214 + }
1215 + buffer_json_object_close(wb);
1216
1137 - buffer_json_member_add_object(wb, "filters");
1138 - buffer_json_member_add_string(wb, "q", req->q);
1139 - buffer_json_member_add_time_t(wb, "after", req->after);
1140 - buffer_json_member_add_time_t(wb, "before", req->before);
1141 - buffer_json_object_close(wb);
1217 + buffer_json_member_add_object(wb, "selectors");
1218 + {
1219 + buffer_json_member_add_string(wb, "nodes", req->nodes);
1220 +
1221 + if (mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_ALERTS))
1222 + buffer_json_member_add_string(wb, "contexts", req->contexts);
1223 +
1224 + if(mode & CONTEXTS_V2_ALERTS) {
1225 + buffer_json_member_add_object(wb, "alerts");
1226 + web_client_api_request_v2_contexts_alerts_status_to_buffer_json_array(wb, "status", req->alerts.status);
1227 + buffer_json_member_add_string(wb, "alert", req->alerts.alert);
1228 + buffer_json_member_add_string(wb, "transition", req->alerts.transition);
1229 + buffer_json_member_add_uint64(wb, "last", req->alerts.last);
1230 + buffer_json_object_close(wb); // alerts
1231 + }
1232 + }
1233 + buffer_json_object_close(wb);
1234
1143 - buffer_json_member_add_array(wb, "options");
1144 - buffer_json_contexts_v2_options_to_array(wb, options);
1145 - buffer_json_array_close(wb);
1235 + buffer_json_member_add_object(wb, "filters");
1236 + {
1237 + if (mode & CONTEXTS_V2_SEARCH)
1238 + buffer_json_member_add_string(wb, "q", req->q);
1239
1240 + buffer_json_member_add_time_t(wb, "after", req->after);
1241 + buffer_json_member_add_time_t(wb, "before", req->before);
1242 + }
1243 + buffer_json_object_close(wb);
1244 + }
1245 buffer_json_object_close(wb);
1246 }
1247
1150 - if(options & CONTEXTS_V2_NODES)
1151 - buffer_json_member_add_array(wb, "nodes");
1152 -
1153 - ssize_t ret = query_scope_foreach_host(ctl.nodes.scope_pattern, ctl.nodes.pattern,
1248 + ssize_t ret = 0;
1249 + if(run)
1250 + ret = query_scope_foreach_host(ctl.nodes.scope_pattern, ctl.nodes.pattern,
1251 rrdcontext_to_json_v2_add_host, &ctl,
1252 &ctl.versions, ctl.q.host_node_id_str);
1253
@@ -1168,15 +1265,22 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
1265 goto cleanup;
1266 }
1267
1171 - if(options & CONTEXTS_V2_NODES)
1172 - buffer_json_array_close(wb);
1173 -
1268 ctl.timings.executed_ut = now_monotonic_usec();
1269
1176 - if(options & CONTEXTS_V2_FUNCTIONS) {
1270 + if(mode & CONTEXTS_V2_NODES) {
1271 + buffer_json_member_add_array(wb, "nodes");
1272 + struct contexts_v2_node *t;
1273 + dfe_start_read(ctl.nodes.dict, t) {
1274 + rrdcontext_to_json_v2_rrdhost(wb, t->host, &ctl, t->ni);
1275 + }
1276 + dfe_done(t);
1277 + buffer_json_array_close(wb);
1278 + }
1279 +
1280 + if(mode & CONTEXTS_V2_FUNCTIONS) {
1281 buffer_json_member_add_array(wb, "functions");
1282 {
1179 - struct rrdfunction_to_json_v2 *t;
1283 + struct function_v2_entry *t;
1284 dfe_start_read(ctl.functions.dict, t) {
1285 buffer_json_add_array_item_object(wb);
1286 buffer_json_member_add_string(wb, "name", t_dfe.name);
@@ -1192,11 +1296,11 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
1296 buffer_json_array_close(wb);
1297 }
1298
1195 - if(options & CONTEXTS_V2_CONTEXTS) {
1299 + if(mode & CONTEXTS_V2_CONTEXTS) {
1300 buffer_json_member_add_object(wb, "contexts");
1301 {
1198 - struct rrdcontext_to_json_v2_entry *z;
1199 - dfe_start_read(ctl.ctx, z) {
1302 + struct context_v2_entry *z;
1303 + dfe_start_read(ctl.contexts.dict, z) {
1304 bool collected = z->flags & RRD_FLAG_COLLECTED;
1305
1306 buffer_json_member_add_object(wb, string2str(z->id));
@@ -1206,7 +1310,7 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
1310 buffer_json_member_add_time_t(wb, "first_entry", z->first_time_s);
1311 buffer_json_member_add_time_t(wb, "last_entry", collected ? ctl.now : z->last_time_s);
1312 buffer_json_member_add_boolean(wb, "live", collected);
1209 - if (options & CONTEXTS_V2_SEARCH)
1313 + if (mode & CONTEXTS_V2_SEARCH)
1314 buffer_json_member_add_string(wb, "match", fts_match_to_string(z->match));
1315 }
1316 buffer_json_object_close(wb);
@@ -1216,151 +1320,228 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
1320 buffer_json_object_close(wb); // contexts
1321 }
1322
1219 - if(options & CONTEXTS_V2_SEARCH) {
1220 - buffer_json_member_add_object(wb, "searches");
1323 + if(mode & CONTEXTS_V2_ALERTS) {
1324 + buffer_json_member_add_array(wb, "alerts");
1325 {
1222 - buffer_json_member_add_uint64(wb, "strings", ctl.q.fts.string_searches);
1223 - buffer_json_member_add_uint64(wb, "char", ctl.q.fts.char_searches);
1224 - buffer_json_member_add_uint64(wb, "total", ctl.q.fts.searches);
1225 - }
1226 - buffer_json_object_close(wb);
1227 - }
1228 -
1229 - if(options & (CONTEXTS_V2_VERSIONS))
1230 - version_hashes_api_v2(wb, &ctl.versions);
1231 -
1232 - if(options & CONTEXTS_V2_AGENTS)
1233 - buffer_json_agents_array_v2(wb, &ctl.timings, ctl.now, options & (CONTEXTS_V2_AGENTS_INFO));
1234 -
1235 - buffer_json_cloud_timings(wb, "timings", &ctl.timings);
1236 -
1237 - buffer_json_finalize(wb);
1238 -
1239 -cleanup:
1240 - dictionary_destroy(ctl.functions.dict);
1241 - dictionary_destroy(ctl.ctx);
1242 - simple_pattern_free(ctl.nodes.scope_pattern);
1243 - simple_pattern_free(ctl.nodes.pattern);
1244 - simple_pattern_free(ctl.contexts.pattern);
1245 - simple_pattern_free(ctl.contexts.scope_pattern);
1246 - simple_pattern_free(ctl.q.pattern);
1247 -
1248 - return resp;
1249 -}
1326 + struct alert_v2_entry *t;
1327 + dfe_start_read(ctl.alerts.alerts, t){
1328 + buffer_json_add_array_item_object(wb);
1329 + {
1330 + buffer_json_member_add_uint64(wb, "ati", t->ati);
1331 + buffer_json_member_add_string(wb, "nm", string2str(t->name));
1332
1251 -int alerts_to_json_v2(BUFFER *wb, struct api_v2_alerts_request *req, CONTEXTS_V2_OPTIONS options)
1252 -{
1253 - int resp = HTTP_RESP_OK;
1333 + buffer_json_member_add_uint64(wb, "cr", t->critical);
1334 + buffer_json_member_add_uint64(wb, "wr", t->warning);
1335 + buffer_json_member_add_uint64(wb, "cl", t->clear);
1336 + buffer_json_member_add_uint64(wb, "er", t->error);
1337
1255 -// ALERT_OPTIONS alert_options = req->options;
1256 - struct rrdcontext_to_json_v2_data ctl = {
1257 - .wb = wb,
1258 - .alerts_request = req,
1259 - .ctx = NULL,
1260 - .options = options,
1261 - .versions = { 0 },
1262 - .nodes.scope_pattern = string_to_simple_pattern(req->scope_nodes),
1263 - .nodes.pattern = string_to_simple_pattern(req->nodes),
1264 - .contexts.pattern = string_to_simple_pattern(req->contexts),
1265 - .contexts.scope_pattern = string_to_simple_pattern(req->scope_contexts),
1266 - .timings = {
1267 - .received_ut = now_monotonic_usec(),
1338 + buffer_json_member_add_uint64(wb, "in", t->instances);
1339 + buffer_json_member_add_uint64(wb, "nd", dictionary_entries(t->nodes));
1340 + buffer_json_member_add_uint64(wb, "cfg", dictionary_entries(t->configs));
1341 + }
1342 + buffer_json_object_close(wb); // alert name
1343 + }
1344 + dfe_done(t);
1345 }
1269 - };
1270 -
1271 - if(options & CONTEXTS_V2_CONTEXTS)
1272 - {
1273 - ctl.ctx = dictionary_create_advanced(
1274 - DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL,
1275 - sizeof(struct rrdcontext_to_json_v2_entry));
1346 + buffer_json_array_close(wb); // alerts
1347
1277 - dictionary_register_delete_callback(ctl.ctx, contexts_delete_callback, NULL);
1278 - }
1279 -
1280 - time_t now_s = now_realtime_sec();
1281 - buffer_json_member_add_uint64(wb, "api", 2);
1282 -
1283 - {
1284 - buffer_json_member_add_object(wb, "request");
1285 -
1286 - buffer_json_member_add_object(wb, "scope");
1287 - buffer_json_member_add_string(wb, "scope_nodes", req->scope_nodes);
1288 - buffer_json_member_add_string(wb, "scope_contexts", req->scope_contexts);
1289 - buffer_json_object_close(wb);
1290 -
1291 - buffer_json_member_add_object(wb, "selectors");
1292 - buffer_json_member_add_string(wb, "nodes", req->nodes);
1293 - buffer_json_member_add_string(wb, "contexts", req->contexts);
1294 - buffer_json_object_close(wb);
1348 + if(req->options & CONTEXT_V2_OPTION_ALERT_INSTANCES) {
1349 + buffer_json_member_add_array(wb, "alert_instances");
1350 + {
1351 + struct alert_instance_v2_entry *t;
1352 + dfe_start_read(ctl.alerts.alert_instances, t){
1353 + buffer_json_add_array_item_object(wb);
1354 + {
1355 + buffer_json_member_add_uint64(wb, "ni", t->ni);
1356 + buffer_json_member_add_uint64(wb, "ati", t->ati);
1357 + buffer_json_member_add_uint64(wb, "aii", t->aii);
1358 + if(req->options & CONTEXT_V2_OPTION_ALERT_CONFIGURATIONS) {
1359 + buffer_json_member_add_uint64(wb, "aci", t->aci);
1360 + }
1361 + buffer_json_member_add_uint64(wb, "gi", t->global_id);
1362 +
1363 + if(debug)
1364 + buffer_json_member_add_string(wb, "nm", string2str(t->name));
1365 +
1366 + buffer_json_member_add_string(wb, "fami", string2str(t->family));
1367 + buffer_json_member_add_string(wb, "info", string2str(t->info));
1368 + buffer_json_member_add_string(wb, "ch", string2str(t->chart_name));
1369 + buffer_json_member_add_string(wb, "st", rrdcalc_status2string(t->status));
1370 + buffer_json_member_add_double(wb, "v", t->value);
1371 + buffer_json_member_add_time_t(wb, "t", t->last_updated);
1372 + buffer_json_member_add_uuid (wb, "tr_i", &t->last_transition_id);
1373 + buffer_json_member_add_double(wb, "tr_v", t->last_status_change_value);
1374 + buffer_json_member_add_time_t(wb, "tr_t", t->last_status_change);
1375 + buffer_json_member_add_uuid (wb, "cfg", &t->config_hash_id);
1376 + rrdcalc_flags_to_json_array (wb, "flags", t->flags);
1377 + }
1378 + buffer_json_object_close(wb); // alert instance
1379 + }
1380 + dfe_done(t);
1381 + }
1382 + buffer_json_array_close(wb); // alerts_instances
1383 +
1384 + if(req->options & CONTEXT_V2_OPTION_ALERT_TRANSITIONS) {
1385 + buffer_json_member_add_array(wb, "alert_transitions");
1386 + sql_health_alarm_log2json_v3(
1387 + wb,
1388 + ctl.alerts.alert_instances,
1389 + ctl.request->after,
1390 + ctl.request->before,
1391 + ctl.request->alerts.transition,
1392 + ctl.request->alerts.last ? ctl.request->alerts.last : 1,
1393 + debug);
1394 + buffer_json_array_close(wb); // alerts_transitions
1395 + }
1396 + }
1397
1296 - buffer_json_member_add_array(wb, "options");
1297 - buffer_json_contexts_v2_options_to_array(wb, options);
1298 - buffer_json_array_close(wb);
1398 + if(req->options & CONTEXT_V2_OPTION_ALERT_CONFIGURATIONS) {
1399 + buffer_json_member_add_array(wb, "alert_configurations");
1400 + {
1401 + struct alert_config_v2_entry *t;
1402 + dfe_start_read(ctl.alerts.alert_configs, t){
1403 + RRDCALC *rc = t->tmp;
1404
1300 - buffer_json_object_close(wb);
1301 - }
1405 + buffer_json_add_array_item_object(wb);
1406 + {
1407 + buffer_json_member_add_uint64(wb, "ati", t->ati);
1408 + buffer_json_member_add_uint64(wb, "aci", t->aci);
1409 + buffer_json_member_add_string(wb, "cfg", t_dfe.name);
1410
1303 - // Alert configuration
1304 - buffer_json_member_add_array(wb, "alerts");
1411 + if(debug)
1412 + buffer_json_member_add_string(wb, "nm", string2str(rc->name));
1413
1306 - ssize_t ret = query_scope_foreach_host(ctl.nodes.scope_pattern, ctl.nodes.pattern,
1307 - alert_to_json_v2_add_alert, &ctl, &ctl.versions, NULL);
1414 + buffer_json_member_add_string(wb, "ctx", string2str(rc->rrdset->context));
1415 + buffer_json_member_add_string(wb, "class", string2str(rc->classification));
1416 + buffer_json_member_add_string(wb, "component", string2str(rc->component));
1417 + buffer_json_member_add_string(wb, "type", string2str(rc->type));
1418 + buffer_json_member_add_string(wb, "info", string2str(rc->original_info));
1419
1309 - if(unlikely(ret < 0)) {
1310 - buffer_flush(wb);
1420 + buffer_json_member_add_object(wb, "v"); // value
1421 + {
1422 + buffer_json_member_add_uint64(wb, "update_every", rc->update_every);
1423 + buffer_json_member_add_string(wb, "units", string2str(rc->units));
1424 +
1425 + if (RRDCALC_HAS_DB_LOOKUP(rc) || debug) {
1426 + buffer_json_member_add_object(wb, "db");
1427 + {
1428 + if (rc->dimensions || debug)
1429 + buffer_json_member_add_string(wb, "dimensions", rrdcalc_dimensions(rc));
1430 +
1431 + buffer_json_member_add_string(wb, "method", time_grouping_method2string(rc->group));
1432 + buffer_json_member_add_time_t(wb, "after", rc->after);
1433 + buffer_json_member_add_time_t(wb, "before", rc->before);
1434 +
1435 + web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", (RRDR_OPTIONS) rc->options);
1436 + }
1437 + buffer_json_object_close(wb); // db
1438 + }
1439 +
1440 + if (rc->calculation || debug) {
1441 + buffer_json_member_add_string(wb, "calc", rc->calculation ? rc->calculation->source : NULL);
1442 + // buffer_json_member_add_string(wb, "calc_parsed", rc->calculation->parsed_as);
1443 + }
1444 + }
1445 + buffer_json_object_close(wb); // value
1446 +
1447 + if(rc->warning || rc->critical || debug) {
1448 + buffer_json_member_add_object(wb, "st"); // conditions
1449 + {
1450 + if(!isnan(rc->green) || debug)
1451 + buffer_json_member_add_double(wb, "green", rc->green);
1452 +
1453 + if(!isnan(rc->red) || debug)
1454 + buffer_json_member_add_double(wb, "red", rc->red);
1455 +
1456 + if (rc->warning || debug) {
1457 + buffer_json_member_add_string(wb, "warn", rc->warning ? rc->warning->source : NULL);
1458 + // buffer_json_member_add_string(wb, "warn_parsed", rc->warning ? rc->warning->parsed_as : NULL);
1459 + }
1460 +
1461 + if (rc->critical || debug) {
1462 + buffer_json_member_add_string(wb, "crit", rc->critical ? rc->critical->source : NULL);
1463 + // buffer_json_member_add_string(wb, "crit_parsed", rc->critical ? rc->critical->parsed_as : NULL);
1464 + }
1465 + }
1466 + buffer_json_object_close(wb); // conditions
1467 + }
1468
1312 - if(ret == -2) {
1313 - buffer_strcat(wb, "query timeout");
1314 - resp = HTTP_RESP_GATEWAY_TIMEOUT;
1315 - }
1316 - else {
1317 - buffer_strcat(wb, "query interrupted");
1318 - resp = HTTP_RESP_BACKEND_FETCH_FAILED;
1469 + buffer_json_member_add_object(wb, "nf");
1470 + {
1471 + buffer_json_member_add_string(wb, "type", "agent");
1472 + buffer_json_member_add_string(wb, "method", rc->exec ? rrdcalc_exec(rc) : string2str(localhost->health.health_default_exec));
1473 + buffer_json_member_add_string(wb, "to", rc->recipient ? string2str(rc->recipient) : string2str(rc->rrdset->rrdhost->health.health_default_recipient));
1474 +
1475 + if(rc->delay_up_duration || rc->delay_down_duration || debug) {
1476 + buffer_json_member_add_object(wb, "delay");
1477 + {
1478 + buffer_json_member_add_time_t(wb, "up", rc->delay_up_duration);
1479 + buffer_json_member_add_time_t(wb, "down", rc->delay_down_duration);
1480 + buffer_json_member_add_time_t(wb, "max", rc->delay_max_duration);
1481 + buffer_json_member_add_double(wb, "multiplier", rc->delay_multiplier);
1482 + }
1483 + buffer_json_object_close(wb); //delay
1484 + }
1485 +
1486 + if(rc->warn_repeat_every || rc->crit_repeat_every || debug) {
1487 + buffer_json_member_add_object(wb, "repeat");
1488 + {
1489 + if(rc->warn_repeat_every || debug)
1490 + buffer_json_member_add_time_t(wb, "warn", rc->warn_repeat_every);
1491 +
1492 + if(rc->crit_repeat_every || debug)
1493 + buffer_json_member_add_time_t(wb, "crit", rc->crit_repeat_every);
1494 + }
1495 + buffer_json_object_close(wb); // repeat
1496 + }
1497 +
1498 + if (unlikely((rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION) || debug))
1499 + buffer_json_member_add_boolean(wb, "no_clear_notification", rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION);
1500 + }
1501 + buffer_json_object_close(wb); // notification
1502 + buffer_json_member_add_string(wb, "src", string2str(rc->source));
1503 + }
1504 + buffer_json_object_close(wb); // alert config
1505 + }
1506 + dfe_done(t);
1507 + }
1508 + buffer_json_array_close(wb); // alerts_configs
1509 }
1320 - goto cleanup;
1510 }
1511
1323 - buffer_json_array_close(wb); // alerts
1324 -
1325 - buffer_json_member_add_array(wb, "nodes");
1326 -
1327 - ret = query_scope_foreach_host(ctl.nodes.scope_pattern, ctl.nodes.pattern,
1328 - alert_to_json_v2_add_host, &ctl,
1329 - &ctl.versions, NULL);
1330 -
1331 - if(unlikely(ret < 0)) {
1332 - buffer_flush(wb);
1333 -
1334 - if(ret == -2) {
1335 - buffer_strcat(wb, "query timeout");
1336 - resp = HTTP_RESP_GATEWAY_TIMEOUT;
1337 - }
1338 - else {
1339 - buffer_strcat(wb, "query interrupted");
1340 - resp = HTTP_RESP_BACKEND_FETCH_FAILED;
1512 + if(mode & CONTEXTS_V2_SEARCH) {
1513 + buffer_json_member_add_object(wb, "searches");
1514 + {
1515 + buffer_json_member_add_uint64(wb, "strings", ctl.q.fts.string_searches);
1516 + buffer_json_member_add_uint64(wb, "char", ctl.q.fts.char_searches);
1517 + buffer_json_member_add_uint64(wb, "total", ctl.q.fts.searches);
1518 }
1342 - goto cleanup;
1519 + buffer_json_object_close(wb);
1520 }
1521
1345 - buffer_json_array_close(wb); // Nodes
1522 + if(mode & (CONTEXTS_V2_VERSIONS))
1523 + version_hashes_api_v2(wb, &ctl.versions);
1524
1347 - ctl.timings.executed_ut = now_monotonic_usec();
1348 - version_hashes_api_v2(wb, &ctl.versions);
1525 + if(mode & CONTEXTS_V2_AGENTS)
1526 + buffer_json_agents_array_v2(wb, &ctl.timings, ctl.now, mode & (CONTEXTS_V2_AGENTS_INFO));
1527
1350 - buffer_json_agents_array_v2(wb, &ctl.timings, now_s, false);
1528 buffer_json_cloud_timings(wb, "timings", &ctl.timings);
1352 - buffer_json_finalize(wb);
1529
1354 - JudyHSFreeArray(&ctl.alerts.JudyHS, PJE0);
1530 + buffer_json_finalize(wb);
1531
1532 cleanup:
1357 -// dictionary_destroy(ctl.ctx);
1533 + dictionary_destroy(ctl.nodes.dict);
1534 + dictionary_destroy(ctl.contexts.dict);
1535 + dictionary_destroy(ctl.functions.dict);
1536 + dictionary_destroy(ctl.alerts.alerts);
1537 + dictionary_destroy(ctl.alerts.alert_instances);
1538 + dictionary_destroy(ctl.alerts.alert_configs);
1539 simple_pattern_free(ctl.nodes.scope_pattern);
1540 simple_pattern_free(ctl.nodes.pattern);
1541 simple_pattern_free(ctl.contexts.pattern);
1542 simple_pattern_free(ctl.contexts.scope_pattern);
1362 - simple_pattern_free(req->config_hash_pattern);
1363 - simple_pattern_free(req->alert_name_pattern);
1543 + simple_pattern_free(ctl.q.pattern);
1544 + simple_pattern_free(ctl.alerts.alert_name_pattern);
1545
1546 return resp;
1547 }
database/contexts/rrdcontext.h
+48 -36
@@ -415,6 +415,32 @@ typedef struct query_target {
415 } internal;
416 } QUERY_TARGET;
417
418 +struct alert_instance_v2_entry {
419 + RRDCALC *tmp;
420 +
421 + size_t ati;
422 + size_t aci;
423 + size_t aii;
424 +
425 + STRING *chart_id;
426 + STRING *chart_name;
427 + STRING *name;
428 + STRING *family;
429 + RRDCALC_STATUS status;
430 + RRDCALC_FLAGS flags;
431 + STRING *info;
432 + NETDATA_DOUBLE value;
433 + time_t last_updated;
434 + time_t last_status_change;
435 + NETDATA_DOUBLE last_status_change_value;
436 + uuid_t config_hash_id;
437 + usec_t global_id;
438 + uuid_t last_transition_id;
439 + uint32_t alarm_id;
440 + RRDHOST *host;
441 + size_t ni;
442 +};
443 +
444 static inline NEVERNULL QUERY_NODE *query_node(QUERY_TARGET *qt, size_t id) {
445 internal_fatal(id >= qt->nodes.used, "QUERY: invalid query host id");
446 return &qt->nodes.array[id];
@@ -467,6 +493,15 @@ struct api_v2_contexts_request {
493 char *contexts;
494 char *q;
495
496 + CONTEXTS_V2_OPTIONS options;
497 +
498 + struct {
499 + CONTEXTS_V2_ALERT_STATUS status;
500 + char *alert;
501 + char *transition;
502 + uint32_t last;
503 + } alerts;
504 +
505 time_t after;
506 time_t before;
507 time_t timeout_ms;
@@ -475,43 +510,20 @@ struct api_v2_contexts_request {
510 void *interrupt_callback_data;
511 };
512
478 -struct api_v2_alerts_request {
479 - char *scope_nodes;
480 - char *scope_contexts;
481 - char *nodes;
482 - char *contexts;
483 - char *config_hash;
484 - char *state;
485 - SIMPLE_PATTERN *config_hash_pattern;
486 - char *transition_id;
487 - time_t alert_id;
488 - uint32_t last;
489 - char *alert_name;
490 - SIMPLE_PATTERN *alert_name_pattern;
491 - ALERT_OPTIONS options;
492 - time_t after;
493 - time_t before;
494 - char *q;
495 -};
496 -
497 -ssize_t get_alert_index(Pvoid_t JudyHS, uuid_t *uuid);
498 -
513 typedef enum __attribute__ ((__packed__)) {
500 - CONTEXTS_V2_DEBUG = (1 << 0),
501 - CONTEXTS_V2_MINIFY = (1 << 1),
502 - CONTEXTS_V2_SEARCH = (1 << 2),
503 - CONTEXTS_V2_NODES = (1 << 3),
504 - CONTEXTS_V2_NODES_INFO = (1 << 4),
505 - CONTEXTS_V2_NODES_INSTANCES = (1 << 5),
506 - CONTEXTS_V2_CONTEXTS = (1 << 6),
507 - CONTEXTS_V2_AGENTS = (1 << 7),
508 - CONTEXTS_V2_AGENTS_INFO = (1 << 8),
509 - CONTEXTS_V2_VERSIONS = (1 << 9),
510 - CONTEXTS_V2_FUNCTIONS = (1 << 10),
511 -} CONTEXTS_V2_OPTIONS;
512 -
513 -int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_OPTIONS options);
514 -int alerts_to_json_v2(BUFFER *wb, struct api_v2_alerts_request *req, CONTEXTS_V2_OPTIONS options);
514 + CONTEXTS_V2_SEARCH = (1 << 1),
515 + CONTEXTS_V2_NODES = (1 << 2),
516 + CONTEXTS_V2_NODES_INFO = (1 << 3),
517 + CONTEXTS_V2_NODES_INSTANCES = (1 << 4),
518 + CONTEXTS_V2_CONTEXTS = (1 << 5),
519 + CONTEXTS_V2_AGENTS = (1 << 6),
520 + CONTEXTS_V2_AGENTS_INFO = (1 << 7),
521 + CONTEXTS_V2_VERSIONS = (1 << 8),
522 + CONTEXTS_V2_FUNCTIONS = (1 << 9),
523 + CONTEXTS_V2_ALERTS = (1 << 10),
524 +} CONTEXTS_V2_MODE;
525 +
526 +int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_MODE mode);
527
528 RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o);
529 void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info);
database/rrdcalc.c
+29 -1
@@ -5,6 +5,33 @@
5 // ----------------------------------------------------------------------------
6 // RRDCALC helpers
7
8 +void rrdcalc_flags_to_json_array(BUFFER *wb, const char *key, RRDCALC_FLAGS flags) {
9 + buffer_json_member_add_array(wb, key);
10 +
11 + if(flags & RRDCALC_FLAG_DB_ERROR)
12 + buffer_json_add_array_item_string(wb, "DB_ERROR");
13 + if(flags & RRDCALC_FLAG_DB_NAN)
14 + buffer_json_add_array_item_string(wb, "DB_NAN");
15 + if(flags & RRDCALC_FLAG_CALC_ERROR)
16 + buffer_json_add_array_item_string(wb, "CALC_ERROR");
17 + if(flags & RRDCALC_FLAG_WARN_ERROR)
18 + buffer_json_add_array_item_string(wb, "WARN_ERROR");
19 + if(flags & RRDCALC_FLAG_CRIT_ERROR)
20 + buffer_json_add_array_item_string(wb, "CRIT_ERROR");
21 + if(flags & RRDCALC_FLAG_RUNNABLE)
22 + buffer_json_add_array_item_string(wb, "RUNNABLE");
23 + if(flags & RRDCALC_FLAG_DISABLED)
24 + buffer_json_add_array_item_string(wb, "DISABLED");
25 + if(flags & RRDCALC_FLAG_SILENCED)
26 + buffer_json_add_array_item_string(wb, "SILENCED");
27 + if(flags & RRDCALC_FLAG_RUN_ONCE)
28 + buffer_json_add_array_item_string(wb, "RUN_ONCE");
29 + if(flags & RRDCALC_FLAG_FROM_TEMPLATE)
30 + buffer_json_add_array_item_string(wb, "FROM_TEMPLATE");
31 +
32 + buffer_json_array_close(wb);
33 +}
34 +
35 inline const char *rrdcalc_status2string(RRDCALC_STATUS status) {
36 switch(status) {
37 case RRDCALC_STATUS_REMOVED:
@@ -181,6 +208,7 @@ static void rrdcalc_link_to_rrdset(RRDSET *st, RRDCALC *rc) {
208
209 debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rrdcalc_chart_name(rc), rrdcalc_name(rc), rrdset_id(st), rrdhost_hostname(host));
210
211 + rc->last_status_change_value = rc->value;
212 rc->last_status_change = now_realtime_sec();
213 rc->rrdset = st;
214
@@ -273,8 +301,8 @@ static void rrdcalc_link_to_rrdset(RRDSET *st, RRDCALC *rc) {
301 now - rc->last_status_change,
302 rc->old_value,
303 rc->value,
304 + RRDCALC_STATUS_REMOVED,
305 rc->status,
277 - RRDCALC_STATUS_UNINITIALIZED,
306 rc->source,
307 rc->units,
308 rc->info,
database/rrdcalc.h
+3
@@ -28,6 +28,8 @@ typedef enum {
28 RRDCALC_FLAG_FROM_TEMPLATE = (1 << 10), // the rrdcalc has been created from a template
29 } RRDCALC_FLAGS;
30
31 +void rrdcalc_flags_to_json_array(BUFFER *wb, const char *key, RRDCALC_FLAGS flags);
32 +
33 typedef enum {
34 // This list uses several other options from RRDR_OPTIONS for db lookups.
35 // To add an item here, you need to reserve a bit in RRDR_OPTIONS.
@@ -120,6 +122,7 @@ struct rrdcalc {
122
123 NETDATA_DOUBLE value; // the current value of the alarm
124 NETDATA_DOUBLE old_value; // the previous value of the alarm
125 + NETDATA_DOUBLE last_status_change_value; // the value at the last status change
126
127 RRDCALC_FLAGS run_flags; // check RRDCALC_FLAG_*
128
database/sqlite/sqlite_health.c
+197 -84
@@ -1247,14 +1247,14 @@ int alert_hash_and_store_config(
1247 return 1;
1248 }
1249
1250 -#define SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT "SELECT hld.new_status FROM health_log hl, health_log_detail hld WHERE hl.alarm_id = %u AND hld.unique_id != %u AND hld.flags & %d AND hl.host_id = @host_id and hl.health_log_id = hld.health_log_id ORDER BY hld.unique_id DESC LIMIT 1;"
1250 +#define SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT "SELECT hld.new_status FROM health_log hl, health_log_detail hld WHERE hl.alarm_id = %u AND hld.unique_id != %u AND hld.flags & %u AND hl.host_id = @host_id and hl.health_log_id = hld.health_log_id ORDER BY hld.unique_id DESC LIMIT 1;"
1251 int sql_health_get_last_executed_event(RRDHOST *host, ALARM_ENTRY *ae, RRDCALC_STATUS *last_executed_status)
1252 {
1253 int rc = 0, ret = -1;
1254 char command[MAX_HEALTH_SQL_SIZE + 1];
1255 sqlite3_stmt *res = NULL;
1256
1257 - snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT, ae->alarm_id, ae->unique_id, HEALTH_ENTRY_FLAG_EXEC_RUN);
1257 + snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT, ae->alarm_id, ae->unique_id, (uint32_t) HEALTH_ENTRY_FLAG_EXEC_RUN);
1258
1259 rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0);
1260 if (rc != SQLITE_OK) {
@@ -1690,26 +1690,145 @@ uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *
1690 return alarm_id;
1691 }
1692
1693 -#define SQL_SELECT_HEALTH_LOG_V2 "SELECT unique_id, alarm_event_id, updated_by_id, " \
1694 - "updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, " \
1695 - "exec, recipient, info, exec_code, new_status, old_status, delay, new_value, " \
1696 - "old_value, last_repeat, transition_id, units, d.global_id FROM health_log_detail d, health_log h " \
1697 - "WHERE h.host_id = @host_id AND h.health_log_id = d.health_log_id "
1693 +#define SQL_GET_ALARM_ID_FROM_TRANSITION_ID "SELECT hld.alarm_id, hl.host_id, hl.chart_context FROM " \
1694 + "health_log_detail hld, health_log hl WHERE hld.transition_id = @transition_id " \
1695 + "and hld.health_log_id = hl.health_log_id"
1696
1699 -void sql_health_alarm_log2json_v2(RRDHOST *host, BUFFER *wb, uint32_t alert_id, char *chart, time_t after, time_t before, uint32_t max)
1697 +bool sql_find_alert_transition(const char *transition, void (*cb)(const char *machine_guid, const char *context, time_t alert_id, void *data), void *data)
1698 {
1699 + static __thread sqlite3_stmt *res = NULL;
1700 +
1701 + char machine_guid[UUID_STR_LEN];
1702 +
1703 + int rc;
1704 + uuid_t transition_uuid;
1705 + if (uuid_parse(transition, transition_uuid))
1706 + return false;
1707 +
1708 + if (unlikely(!res)) {
1709 + rc = prepare_statement(db_meta, SQL_GET_ALARM_ID_FROM_TRANSITION_ID, &res);
1710 + if (unlikely(rc != SQLITE_OK)) {
1711 + error_report("Failed to prepare statement when trying to get transition id");
1712 + return false;
1713 + }
1714 + }
1715 +
1716 + bool ok = false;
1717 +
1718 + rc = sqlite3_bind_blob(res, 1, &transition_uuid, sizeof(transition_uuid), SQLITE_STATIC);
1719 + if (unlikely(rc != SQLITE_OK)) {
1720 + error_report("Failed to bind transition");
1721 + goto fail;
1722 + }
1723 +
1724 + while (sqlite3_step_monitored(res) == SQLITE_ROW) {
1725 + ok = true;
1726 + uuid_unparse_lower(*(uuid_t *) sqlite3_column_blob(res, 1), machine_guid);
1727 + cb(machine_guid, (const char *) sqlite3_column_text(res, 2), sqlite3_column_int(res, 0), data);
1728 + }
1729 +
1730 +fail:
1731 + rc = sqlite3_reset(res);
1732 + if (unlikely(rc != SQLITE_OK))
1733 + error_report("Failed to reset the statement when trying to find transition");
1734 +
1735 + return ok;
1736 +}
1737 +
1738 +#define SQL_BUILD_TEMP_ALERT_MATCH "CREATE TEMP TABLE IF NOT EXISTS v_%p (host_id blob, chart text, name text, alarm_id int, aii int, ati int, aci int)"
1739 +
1740 +#define SQL_POPULATE_TEMP_ALERT_MATCH_TABLE "INSERT INTO v_%p (host_id, chart, name, alarm_id, aii, ati, aci) " \
1741 + "VALUES (@host_id, @chart, @alarm_name, @alarm_id, @aii, @ati, @aci)"
1742 +
1743 +#define SQL_SEARCH_ALERT_LOG "SELECT when_key, duration, flags, exec_run_timestamp, delay_up_to_timestamp, " \
1744 + "recipient, exec_code, new_status, old_status, new_value, " \
1745 + "old_value, transition_id, d.global_id, t.aii, t.ati, t.aci, h.exec FROM health_log_detail d, health_log h, v_%p t " \
1746 + "WHERE h.host_id = t.host_id AND h.chart = t.chart AND h.alarm_id = t.alarm_id AND h.name = t.name " \
1747 + "AND h.health_log_id = d.health_log_id "
1748 +
1749 +void sql_health_alarm_log2json_v3(BUFFER *wb, DICTIONARY *alert_instances, time_t after, time_t before, const char *transition, uint32_t max, bool debug __maybe_unused)
1750 +{
1751 + uuid_t transition_uuid;
1752 + char sql[512];
1753 sqlite3_stmt *res = NULL;
1754 int rc;
1755
1704 - BUFFER *command = buffer_create(MAX_HEALTH_SQL_SIZE, NULL);
1756 + if (unlikely(!alert_instances))
1757 + return;
1758 +
1759 + if (transition) {
1760 + if (uuid_parse(transition, transition_uuid)) {
1761 + error_report("Invalid transition given %s", transition);
1762 + return;
1763 + }
1764 + }
1765 +
1766 + snprintfz(sql, 511, SQL_BUILD_TEMP_ALERT_MATCH, alert_instances);
1767 + rc = db_execute(db_meta, sql);
1768 + if (rc)
1769 + return;
1770 +
1771 + snprintfz(sql, 511, SQL_POPULATE_TEMP_ALERT_MATCH_TABLE, alert_instances);
1772 +
1773 + // Prepare statement to add things
1774 + rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0);
1775 + if (unlikely(rc != SQLITE_OK)) {
1776 + error_report("Failed to prepare statement to INSERT into v_%p", alert_instances);
1777 + goto fail_only_drop;
1778 + }
1779 +
1780 + struct alert_instance_v2_entry *t;
1781 + dfe_start_read(alert_instances, t) {
1782 + rc = sqlite3_bind_blob(res, 1, &t->host->host_uuid, sizeof(t->host->host_uuid), SQLITE_STATIC);
1783 + if (unlikely(rc != SQLITE_OK))
1784 + error_report("Failed to bind host_id parameter.");
1785
1706 - buffer_sprintf(command, SQL_SELECT_HEALTH_LOG_V2);
1786 + rc = sqlite3_bind_text(res, 2, string2str(t->chart_name), -1, SQLITE_STATIC);
1787 + if (unlikely(rc != SQLITE_OK))
1788 + error_report("Failed to bind chart_name parameter.");
1789
1708 - if (alert_id)
1709 - buffer_sprintf(command, "AND d.alarm_id = %u ", alert_id);
1790 + rc = sqlite3_bind_text(res, 3, string2str(t->name), -1, SQLITE_STATIC);
1791 + if (unlikely(rc != SQLITE_OK))
1792 + error_report("Failed to bind alert_name parameter.");
1793
1711 - if (chart)
1712 - buffer_sprintf(command, "AND h.chart = '%s' ", chart);
1794 + rc = sqlite3_bind_int(res, 4, (int) t->alarm_id);
1795 + if (unlikely(rc != SQLITE_OK))
1796 + error_report("Failed to bind alarm_id parameter.");
1797 +
1798 + rc = sqlite3_bind_int(res, 5, (int) t->aii);
1799 + if (unlikely(rc != SQLITE_OK))
1800 + error_report("Failed to bind aii parameter.");
1801 +
1802 + rc = sqlite3_bind_int(res, 6, (int) t->ati);
1803 + if (unlikely(rc != SQLITE_OK))
1804 + error_report("Failed to bind ati parameter.");
1805 +
1806 + rc = sqlite3_bind_int(res, 7, (int) t->aci);
1807 + if (unlikely(rc != SQLITE_OK))
1808 + error_report("Failed to bind aci parameter.");
1809 +
1810 + rc = sqlite3_step_monitored(res);
1811 + if (rc != SQLITE_DONE)
1812 + error_report("Error while populating temp table");
1813 +
1814 + rc = sqlite3_reset(res);
1815 + if (rc != SQLITE_OK)
1816 + error_report("Error while resetting parameters");
1817 + }
1818 + dfe_done(t);
1819 +
1820 + rc = sqlite3_finalize(res);
1821 + if (unlikely(rc != SQLITE_OK)) {
1822 + // log error but continue
1823 + error_report("Failed to finalize statement for sql_health_alarm_log2json_v3 temp table population");
1824 + }
1825 +
1826 + BUFFER *command = buffer_create(MAX_HEALTH_SQL_SIZE, NULL);
1827 +
1828 + buffer_sprintf(command, SQL_SEARCH_ALERT_LOG, alert_instances);
1829 +
1830 + if (transition)
1831 + buffer_sprintf(command, " AND d.global_id <= (SELECT global_id FROM health_log_detail WHERE transition_id = @transition) ");
1832
1833 if (after)
1834 buffer_sprintf(command, "AND d.when_key >= %ld ", after);
@@ -1717,91 +1836,85 @@ void sql_health_alarm_log2json_v2(RRDHOST *host, BUFFER *wb, uint32_t alert_id,
1836 if (before)
1837 buffer_sprintf(command, "AND d.when_key < %ld ", before);
1838
1720 - buffer_sprintf(command, " ORDER BY d.alarm_event_id DESC LIMIT %u", max);
1839 + buffer_sprintf(command, " ORDER BY d.global_id DESC LIMIT %u", max);
1840
1841 rc = sqlite3_prepare_v2(db_meta, buffer_tostring(command), -1, &res, 0);
1842 if (unlikely(rc != SQLITE_OK)) {
1724 - error_report("Failed to prepare statement SQL_SELECT_HEALTH_LOG");
1725 - buffer_free(command);
1726 - return;
1843 + error_report("Failed to prepare statement sql_health_alarm_log2json_v3");
1844 + goto fail_only_drop;
1845 }
1846
1729 - rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC);
1730 - if (unlikely(rc != SQLITE_OK))
1731 - error_report("Failed to bind host_id parameter for SQL_GET_ALARM_ID.");
1847 + if (transition) {
1848 + rc = sqlite3_bind_blob(res, 1, &transition_uuid, sizeof(transition_uuid), SQLITE_STATIC);
1849 + if (unlikely(rc != SQLITE_OK)) {
1850 + error_report("Failed to bind transition parameter");
1851 + goto fail;
1852 + }
1853 + }
1854
1855 while (sqlite3_step(res) == SQLITE_ROW) {
1734 -
1735 - char old_value_string[100 + 1];
1736 - char new_value_string[100 + 1];
1737 -
1738 - // One alarm
1856 buffer_json_add_array_item_object(wb);
1740 -
1741 - if (sqlite3_column_type(res, 20) != SQLITE_NULL) {
1742 - char transition_id[UUID_STR_LEN];
1743 - uuid_unparse_lower(*((uuid_t *)sqlite3_column_blob(res, 20)), transition_id);
1744 - buffer_json_member_add_string(wb, "transition_id", transition_id);
1745 - }
1746 - else
1747 - buffer_json_member_add_quoted_string(wb, "transition_id", "null");
1748 -
1749 - buffer_json_member_add_uint64(wb, "gi", (uint64_t) sqlite3_column_int64(res, 22));
1750 -
1751 - uint64_t flags = sqlite3_column_int64(res, 8);
1752 - buffer_json_member_add_uint64 (wb, "utc_offset", host->utc_offset);
1753 - buffer_json_member_add_string (wb, "timezone", rrdhost_abbrev_timezone(host));
1754 - buffer_json_member_add_uint64 (wb, "unique_id", (unsigned int) sqlite3_column_int64(res, 0));
1755 - buffer_json_member_add_uint64 (wb, "alarm_event_id", (unsigned int) sqlite3_column_int64(res, 1));
1756 - buffer_json_member_add_object(wb, "notif_status");
1857 {
1758 - const char *exec = (const char *) sqlite3_column_text(res, 10);
1759 - const char *recipient = (const char *) sqlite3_column_text(res, 11);
1760 -
1761 - buffer_json_member_add_boolean (wb, "processed", flags & HEALTH_ENTRY_FLAG_PROCESSED);
1762 - buffer_json_member_add_boolean(wb, "updated", flags & HEALTH_ENTRY_FLAG_UPDATED);
1763 - buffer_json_member_add_uint64 (wb, "exec_run", (long unsigned int)sqlite3_column_int64(res, 9));
1764 - buffer_json_member_add_boolean (wb, "exec_failed", flags & HEALTH_ENTRY_FLAG_EXEC_FAILED);
1765 - buffer_json_member_add_string (wb, "exec", exec ? (const char *) exec : string2str(host->health.health_default_exec));
1766 - buffer_json_member_add_string (wb, "recipient", recipient ? recipient : string2str(host->health.health_default_recipient));
1767 - buffer_json_member_add_uint64 (wb, "exec_code", sqlite3_column_int(res, 13));
1768 - }
1769 - buffer_json_object_close(wb); // notif_status
1770 - buffer_json_member_add_uint64 (wb, "when", (long unsigned int)sqlite3_column_int64(res, 4));
1771 - buffer_json_member_add_uint64 (wb, "duration", (long unsigned int)sqlite3_column_int64(res, 5));
1772 - buffer_json_member_add_uint64 (wb, "non_clear_duration", (long unsigned int)sqlite3_column_int64(res, 6));
1773 -
1774 - buffer_json_member_add_string (wb, "status", rrdcalc_status2string(sqlite3_column_int(res, 14)));
1775 - buffer_json_member_add_string (wb, "old_status", rrdcalc_status2string(sqlite3_column_int(res, 15)));
1776 - buffer_json_member_add_uint64 (wb, "delay", sqlite3_column_int(res, 16));
1777 - buffer_json_member_add_uint64 (wb, "delay_up_to_timestamp", (long unsigned int)sqlite3_column_int64(res, 10));
1778 - buffer_json_member_add_uint64 (wb, "updated_by_id", (unsigned int)sqlite3_column_int64(res, 3));
1779 - buffer_json_member_add_uint64 (wb, "updates_id", (unsigned int)sqlite3_column_int64(res, 4));
1780 - buffer_json_member_add_uint64(wb, "last_repeat", (long unsigned int)sqlite3_column_int64(res, 19));
1781 - buffer_json_member_add_boolean (wb, "silenced", flags & HEALTH_ENTRY_FLAG_SILENCED);
1782 - buffer_json_member_add_string (wb, "info", (const char *)sqlite3_column_text(res, 12));
1783 -
1784 - if(unlikely(flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION))
1785 - buffer_json_member_add_boolean (wb, "no_clear_notification", true);
1786 -
1787 - buffer_json_member_add_string (wb, "value_string", sqlite3_column_type(res, 17) == SQLITE_NULL ? "-" : format_value_and_unit(new_value_string, 100, sqlite3_column_double(res, 17), (char *) sqlite3_column_text(res, 21), -1));
1788 - if (sqlite3_column_type(res, 17) == SQLITE_NULL) {
1789 - buffer_json_member_add_quoted_string(wb, "value", "null");
1790 - } else {
1791 - buffer_json_member_add_double(wb, "value", sqlite3_column_double(res, 17));
1858 + size_t aii = sqlite3_column_int64(res, 13);
1859 + size_t ati = sqlite3_column_int64(res, 14);
1860 + size_t aci = sqlite3_column_int64(res, 15);
1861 + buffer_json_member_add_uint64(wb, "aii", aii);
1862 + buffer_json_member_add_uint64(wb, "ati", ati);
1863 + buffer_json_member_add_uint64(wb, "aci", aci);
1864 +
1865 + uuid_t *transition_id = (sqlite3_column_type(res, 11) != SQLITE_NULL) ? ((uuid_t *)sqlite3_column_blob(res, 11)) : NULL;
1866 + buffer_json_member_add_uuid(wb, "tr_i", transition_id);
1867 +
1868 + buffer_json_member_add_uint64(wb, "gi", (uint64_t)sqlite3_column_int64(res, 12));
1869 +
1870 + uint64_t flags = sqlite3_column_int64(res, 2);
1871 + buffer_json_member_add_uint64(wb, "t", (long unsigned int)sqlite3_column_int64(res, 0));
1872 +
1873 + buffer_json_member_add_object(wb, "st");
1874 + {
1875 + buffer_json_member_add_string(wb, "new", rrdcalc_status2string(sqlite3_column_int(res, 7)));
1876 + buffer_json_member_add_string(wb, "old", rrdcalc_status2string(sqlite3_column_int(res, 8)));
1877 + buffer_json_member_add_uint64(wb, "dur", (long unsigned int)sqlite3_column_int64(res, 1)); // old duration
1878 + }
1879 + buffer_json_object_close(wb); //st
1880 + buffer_json_member_add_object(wb, "v");
1881 + {
1882 + NETDATA_DOUBLE n = (sqlite3_column_type(res, 9) == SQLITE_NULL) ? NAN : sqlite3_column_double(res, 9);
1883 + buffer_json_member_add_double(wb, "new", n);
1884 +
1885 + NETDATA_DOUBLE o = (sqlite3_column_type(res, 10) == SQLITE_NULL) ? NAN : sqlite3_column_double(res, 10);
1886 + buffer_json_member_add_double(wb, "old", o);
1887 + }
1888 + buffer_json_object_close(wb); // v
1889 +
1890 + buffer_json_member_add_object(wb, "nf");
1891 + {
1892 + const char *exec = (const char *)sqlite3_column_text(res, 16);
1893 + const char *recipient = (const char *)sqlite3_column_text(res, 5);
1894 +
1895 + buffer_json_member_add_string(wb, "type", "agent");
1896 + health_entry_flags_to_json_array(wb, "flags", flags);
1897 + buffer_json_member_add_uint64(wb, "t", (long unsigned int)sqlite3_column_int64(res, 9));
1898 + buffer_json_member_add_string(
1899 + wb, "method", exec ? (const char *)exec : string2str(localhost->health.health_default_exec));
1900 + buffer_json_member_add_string(
1901 + wb,
1902 + "to",
1903 + recipient ? recipient : string2str(localhost->health.health_default_recipient));
1904 + buffer_json_member_add_uint64(wb, "code", sqlite3_column_int(res, 6));
1905 + }
1906 + buffer_json_object_close(wb); // notification
1907 }
1793 -
1794 - buffer_json_member_add_string (wb, "old_value_string", sqlite3_column_type(res, 18) == SQLITE_NULL ? "-" : format_value_and_unit(old_value_string, 100, sqlite3_column_double(res, 18), (char *) sqlite3_column_text(res, 21), -1));
1795 - if (sqlite3_column_type(res, 18) == SQLITE_NULL) {
1796 - buffer_json_member_add_quoted_string(wb, "old_value", "null");
1797 - } else
1798 - buffer_json_member_add_double(wb, "old_value", sqlite3_column_double(res, 18));
1908 buffer_json_object_close(wb);
1909 }
1910
1911 +fail:
1912 rc = sqlite3_finalize(res);
1913 if (unlikely(rc != SQLITE_OK))
1804 - error_report("Failed to finalize statement for SQL_SELECT_HEALTH_LOG");
1914 + error_report("Failed to finalize statement for sql_health_alarm_log2json_v3");
1915
1916 +fail_only_drop:
1917 + (void) snprintfz(sql, 511, "DROP TABLE IF EXISTS v_%p", alert_instances);
1918 + (void) db_execute(db_meta, sql);
1919 buffer_free(command);
1920 }
database/sqlite/sqlite_health.h
+2 -2
@@ -7,7 +7,6 @@
7
8 extern sqlite3 *db_meta;
9 void sql_health_alarm_log_load(RRDHOST *host);
10 -int sql_create_health_log_table(void);
10 void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae);
11 void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae);
12 void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae);
@@ -18,5 +17,6 @@ int sql_health_get_last_executed_event(RRDHOST *host, ALARM_ENTRY *ae, RRDCALC_S
17 void sql_health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after, char *chart);
18 int health_migrate_old_health_log_table(char *table);
19 uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id);
21 -void sql_health_alarm_log2json_v2(RRDHOST *host, BUFFER *wb, uint32_t alert_id, char *chart, time_t after, time_t before, uint32_t top);
20 +void sql_health_alarm_log2json_v3(BUFFER *wb, DICTIONARY *alert_instances, time_t after, time_t before, const char *transition, uint32_t max, bool debug);
21 +bool sql_find_alert_transition(const char *transition, void (*cb)(const char *machine_guid, const char *context, time_t alert_id, void *data), void *data);
22 #endif //NETDATA_SQLITE_HEALTH_H
exporting/prometheus/prometheus.c
+10 -7
@@ -627,6 +627,8 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus(
627 static struct format_prometheus_chart_label_callback plabels = {
628 .labels_buffer = NULL,
629 };
630 +
631 + STRING *prometheus = string_strdupz("prometheus");
632 rrdset_foreach_read(st, host) {
633
634 if (likely(can_send_rrdset(instance, st, filter))) {
@@ -642,16 +644,18 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus(
644 int as_collected = (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AS_COLLECTED);
645 int homogeneous = 1;
646 int prometheus_collector = 0;
647 + RRDSET_FLAGS flags = __atomic_load_n(&st->flags, __ATOMIC_RELAXED);
648 if (as_collected) {
646 - if (rrdset_flag_check(st, RRDSET_FLAG_HOMOGENEOUS_CHECK))
649 + if (flags & RRDSET_FLAG_HOMOGENEOUS_CHECK)
650 rrdset_update_heterogeneous_flag(st);
651
649 - if (rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS))
652 + if (flags & RRDSET_FLAG_HETEROGENEOUS)
653 homogeneous = 0;
654
652 - if (!strcmp(rrdset_module_name(st), "prometheus"))
655 + if (st->module_name == prometheus)
656 prometheus_collector = 1;
654 - } else {
657 + }
658 + else {
659 if (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AVERAGE &&
660 !(output_options & PROMETHEUS_OUTPUT_HIDEUNITS))
661 prometheus_units_copy(
@@ -953,11 +957,10 @@ void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(
957 prometheus_exporter_instance->before,
958 output_options);
959
956 - rrd_rdlock();
957 - rrdhost_foreach_read(host)
960 + dfe_start_reentrant(rrdhost_root_index, host)
961 {
962 rrd_stats_api_v1_charts_allmetrics_prometheus(
963 prometheus_exporter_instance, host, filter_string, wb, prefix, exporting_options, 1, output_options);
964 }
962 - rrd_unlock();
965 + dfe_done(host);
966 }
health/health.c
+50 -17
@@ -22,6 +22,36 @@ char *silencers_filename;
22 SIMPLE_PATTERN *conf_enabled_alarms = NULL;
23 DICTIONARY *health_rrdvars;
24
25 +
26 +void health_entry_flags_to_json_array(BUFFER *wb, const char *key, HEALTH_ENTRY_FLAGS flags) {
27 + buffer_json_member_add_array(wb, key);
28 +
29 + if(flags & HEALTH_ENTRY_FLAG_PROCESSED)
30 + buffer_json_add_array_item_string(wb, "PROCESSED");
31 + if(flags & HEALTH_ENTRY_FLAG_UPDATED)
32 + buffer_json_add_array_item_string(wb, "UPDATED");
33 + if(flags & HEALTH_ENTRY_FLAG_EXEC_RUN)
34 + buffer_json_add_array_item_string(wb, "EXEC_RUN");
35 + if(flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)
36 + buffer_json_add_array_item_string(wb, "FAILED");
37 + if(flags & HEALTH_ENTRY_FLAG_SILENCED)
38 + buffer_json_add_array_item_string(wb, "SILENCED");
39 + if(flags & HEALTH_ENTRY_RUN_ONCE)
40 + buffer_json_add_array_item_string(wb, "ONCE");
41 + if(flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS)
42 + buffer_json_add_array_item_string(wb, "IN_PROGRESS");
43 + if(flags & HEALTH_ENTRY_FLAG_IS_REPEATING)
44 + buffer_json_add_array_item_string(wb, "RECURRING");
45 + if(flags & HEALTH_ENTRY_FLAG_SAVED)
46 + buffer_json_add_array_item_string(wb, "SAVED");
47 + if(flags & HEALTH_ENTRY_FLAG_ACLK_QUEUED)
48 + buffer_json_add_array_item_string(wb, "ACLK_QUEUED");
49 + if(flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION)
50 + buffer_json_add_array_item_string(wb, "NO_CLEAR_NOTIFICATION");
51 +
52 + buffer_json_array_close(wb);
53 +}
54 +
55 static bool prepare_command(BUFFER *wb,
56 const char *exec,
57 const char *recipient,
@@ -1126,6 +1156,7 @@ void *health_main(void *ptr) {
1156 rc->old_status = rc->status;
1157 rc->status = RRDCALC_STATUS_REMOVED;
1158 rc->last_status_change = now;
1159 + rc->last_status_change_value = rc->value;
1160 rc->last_updated = now;
1161 rc->value = NAN;
1162 rc->ae = ae;
@@ -1297,36 +1328,37 @@ void *health_main(void *ptr) {
1328 RRDCALC_STATUS status = RRDCALC_STATUS_UNDEFINED;
1329
1330 switch (warning_status) {
1300 - case RRDCALC_STATUS_CLEAR:
1301 - status = RRDCALC_STATUS_CLEAR;
1302 - break;
1331 + case RRDCALC_STATUS_CLEAR:
1332 + status = RRDCALC_STATUS_CLEAR;
1333 + break;
1334
1304 - case RRDCALC_STATUS_RAISED:
1305 - status = RRDCALC_STATUS_WARNING;
1306 - break;
1335 + case RRDCALC_STATUS_RAISED:
1336 + status = RRDCALC_STATUS_WARNING;
1337 + break;
1338
1308 - default:
1309 - break;
1339 + default:
1340 + break;
1341 }
1342
1343 switch (critical_status) {
1313 - case RRDCALC_STATUS_CLEAR:
1314 - if (status == RRDCALC_STATUS_UNDEFINED)
1315 - status = RRDCALC_STATUS_CLEAR;
1316 - break;
1344 + case RRDCALC_STATUS_CLEAR:
1345 + if (status == RRDCALC_STATUS_UNDEFINED)
1346 + status = RRDCALC_STATUS_CLEAR;
1347 + break;
1348
1318 - case RRDCALC_STATUS_RAISED:
1319 - status = RRDCALC_STATUS_CRITICAL;
1320 - break;
1349 + case RRDCALC_STATUS_RAISED:
1350 + status = RRDCALC_STATUS_CRITICAL;
1351 + break;
1352
1322 - default:
1323 - break;
1353 + default:
1354 + break;
1355 }
1356
1357 // --------------------------------------------------------
1358 // check if the new status and the old differ
1359
1360 if (status != rc->status) {
1361 +
1362 worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY);
1363 int delay = 0;
1364
@@ -1394,6 +1426,7 @@ void *health_main(void *ptr) {
1426
1427 log_health("[%s]: Alert event for [%s.%s], value [%s], status [%s].", rrdhost_hostname(host), ae_chart_name(ae), ae_name(ae), ae_new_value_string(ae), rrdcalc_status2string(ae->new_status));
1428
1429 + rc->last_status_change_value = rc->value;
1430 rc->last_status_change = now;
1431 rc->old_status = rc->status;
1432 rc->status = status;
health/health.h
+17 -15
@@ -7,18 +7,21 @@
7
8 extern unsigned int default_health_enabled;
9
10 -#define HEALTH_ENTRY_FLAG_PROCESSED 0x00000001
11 -#define HEALTH_ENTRY_FLAG_UPDATED 0x00000002
12 -#define HEALTH_ENTRY_FLAG_EXEC_RUN 0x00000004
13 -#define HEALTH_ENTRY_FLAG_EXEC_FAILED 0x00000008
14 -#define HEALTH_ENTRY_FLAG_SILENCED 0x00000010
15 -#define HEALTH_ENTRY_RUN_ONCE 0x00000020
16 -#define HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS 0x00000040
17 -#define HEALTH_ENTRY_FLAG_IS_REPEATING 0x00000080
18 -
19 -#define HEALTH_ENTRY_FLAG_SAVED 0x10000000
20 -#define HEALTH_ENTRY_FLAG_ACLK_QUEUED 0x20000000
21 -#define HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION 0x80000000
10 +typedef enum __attribute__((packed)) {
11 + HEALTH_ENTRY_FLAG_PROCESSED = 0x00000001,
12 + HEALTH_ENTRY_FLAG_UPDATED = 0x00000002,
13 + HEALTH_ENTRY_FLAG_EXEC_RUN = 0x00000004,
14 + HEALTH_ENTRY_FLAG_EXEC_FAILED = 0x00000008,
15 + HEALTH_ENTRY_FLAG_SILENCED = 0x00000010,
16 + HEALTH_ENTRY_RUN_ONCE = 0x00000020,
17 + HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS = 0x00000040,
18 + HEALTH_ENTRY_FLAG_IS_REPEATING = 0x00000080,
19 + HEALTH_ENTRY_FLAG_SAVED = 0x10000000,
20 + HEALTH_ENTRY_FLAG_ACLK_QUEUED = 0x20000000,
21 + HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION = 0x80000000,
22 +} HEALTH_ENTRY_FLAGS;
23 +
24 +void health_entry_flags_to_json_array(BUFFER *wb, const char *key, HEALTH_ENTRY_FLAGS flags);
25
26 #ifndef HEALTH_LISTEN_PORT
27 #define HEALTH_LISTEN_PORT 19998
@@ -40,8 +43,7 @@ void health_reload(void);
43
44 void health_aggregate_alarms(RRDHOST *host, BUFFER *wb, BUFFER* context, RRDCALC_STATUS status);
45 void health_alarms2json(RRDHOST *host, BUFFER *wb, int all);
43 -void health_alert2json(RRDHOST *host, BUFFER *wb, ALERT_OPTIONS all, Pvoid_t JudyHS, time_t after, time_t before, uint32_t top);
44 -void health_alert2json_conf(RRDHOST *host, BUFFER *wb, ALERT_OPTIONS all);
46 +void health_alert2json_conf(RRDHOST *host, BUFFER *wb, CONTEXTS_V2_OPTIONS all);
47 void health_alarms_values2json(RRDHOST *host, BUFFER *wb, int all);
48
49 void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf);
@@ -75,7 +77,7 @@ ALARM_ENTRY* health_create_alarm_entry(
77 STRING *units,
78 STRING *info,
79 int delay,
78 - uint32_t flags);
80 + HEALTH_ENTRY_FLAGS flags);
81
82 void health_alarm_log_add_entry(RRDHOST *host, ALARM_ENTRY *ae);
83
health/health_json.c
-93
@@ -167,68 +167,6 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC
167 buffer_strcat(wb, "\t\t}");
168 }
169
170 -
171 -static inline void health_alerts_rrdcalc2json_nolock(RRDHOST *host __maybe_unused, BUFFER *wb,
172 - RRDCALC *rc, ALERT_OPTIONS options __maybe_unused,
173 - Pvoid_t JudyHS, time_t after, time_t before, uint32_t top)
174 -{
175 - ssize_t idx= get_alert_index(JudyHS, &rc->config_hash_id);
176 - // If not in index then skip it
177 - if (idx < 0)
178 - return;
179 -
180 - char value_string[100 + 1];
181 - format_value_and_unit(value_string, 100, rc->value, rrdcalc_units(rc), -1);
182 -
183 -
184 - char hash_id[UUID_STR_LEN];
185 - uuid_unparse_lower(rc->config_hash_id, hash_id);
186 -
187 - buffer_json_add_array_item_object(wb);
188 - if ((!after || after <= rc->last_updated) && (!before || before >= rc->last_updated)) {
189 -
190 - buffer_json_member_add_uint64(wb, "li", (size_t) idx);
191 -
192 - char trans_uuid_str[UUID_STR_LEN];
193 - if (rc->ae) {
194 - uuid_unparse_lower(rc->ae->transition_id, trans_uuid_str);
195 - buffer_json_member_add_string(wb, "transition_id", trans_uuid_str);
196 - buffer_json_member_add_uint64(wb, "gi", rc->ae->global_id);
197 - }
198 - else {
199 - buffer_json_member_add_quoted_string(wb, "transition_id", "NULL");
200 - buffer_json_member_add_quoted_string(wb, "gi", "NULL");
201 - }
202 -
203 - buffer_json_member_add_string(wb, "status", rrdcalc_status2string(rc->status));
204 - buffer_json_member_add_uint64(wb, "last_status_change", (unsigned long)rc->last_status_change);
205 - buffer_json_member_add_uint64(wb, "last_updated", (unsigned long)rc->last_updated);
206 - buffer_json_member_add_uint64(wb, "next_update", (unsigned long)rc->next_update);
207 - buffer_json_member_add_uint64(wb, "delay_up_to_timestamp", (unsigned long)rc->delay_up_to_timestamp);
208 -
209 - buffer_json_member_add_string(wb, "value_string", value_string);
210 - buffer_json_member_add_uint64(wb, "last_repeat", (unsigned long)rc->last_repeat);
211 - buffer_json_member_add_uint64(wb, "times_repeat", (unsigned long)rc->times_repeat);
212 - buffer_json_member_add_uint64(wb, "db_after", (unsigned long)rc->db_after);
213 - buffer_json_member_add_uint64(wb, "db_before", (unsigned long)rc->db_before);
214 -
215 - buffer_json_member_add_double(wb, "green", rc->green);
216 - buffer_json_member_add_double(wb, "red", rc->red);
217 - buffer_json_member_add_double(wb, "value", rc->value);
218 -
219 - if (options & ALERT_OPTION_INSTANCES) {
220 - buffer_json_member_add_array(wb, "transitions");
221 - sql_health_alarm_log2json_v2(host, wb, rc->id, NULL, after, before, top);
222 - buffer_json_array_close(wb);
223 - }
224 - }
225 - buffer_json_object_close(wb); // array entry
226 -}
227 -
228 -//void health_rrdcalctemplate2json_nolock(BUFFER *wb, RRDCALCTEMPLATE *rt) {
229 -//
230 -//}
231 -
170 void health_aggregate_alarms(RRDHOST *host, BUFFER *wb, BUFFER* contexts, RRDCALC_STATUS status) {
171 RRDCALC *rc;
172 int numberOfAlarms = 0;
@@ -292,37 +230,6 @@ static void health_alarms2json_fill_alarms(RRDHOST *host, BUFFER *wb, int all, v
230 foreach_rrdcalc_in_rrdhost_done(rc);
231 }
232
295 -static void health_alerts2json_fill_alarms(
296 - RRDHOST *host,
297 - BUFFER *wb,
298 - ALERT_OPTIONS all,
299 - Pvoid_t JudyHS,
300 - time_t after,
301 - time_t before,
302 - uint32_t top,
303 - void (*fp)(RRDHOST *, BUFFER *, RRDCALC *, ALERT_OPTIONS, Pvoid_t , time_t, time_t, uint32_t))
304 -{
305 - RRDCALC *rc;
306 - foreach_rrdcalc_in_rrdhost_read(host, rc) {
307 - if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
308 - continue;
309 -
310 - if (unlikely(!rrdset_is_available_for_exporting_and_alarms(rc->rrdset)))
311 - continue;
312 -
313 - if(likely((all & ALERT_OPTION_ACTIVE) && !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL)))
314 - continue;
315 -
316 - fp(host, wb, rc, all, JudyHS, after, before, top);
317 - }
318 - foreach_rrdcalc_in_rrdhost_done(rc);
319 -}
320 -
321 -void health_alert2json(RRDHOST *host, BUFFER *wb, ALERT_OPTIONS options, Pvoid_t JudyHS, time_t after, time_t before, uint32_t top)
322 -{
323 - health_alerts2json_fill_alarms(host, wb, options, JudyHS, after, before, top, health_alerts_rrdcalc2json_nolock);
324 -}
325 -
233 void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) {
234 buffer_sprintf(wb, "{\n\t\"hostname\": \"%s\","
235 "\n\t\"latest_alarm_log_unique_id\": %u,"
health/health_log.c
+1 -1
@@ -35,7 +35,7 @@ inline ALARM_ENTRY* health_create_alarm_entry(
35 STRING *units,
36 STRING *info,
37 int delay,
38 - uint32_t flags
38 + HEALTH_ENTRY_FLAGS flags
39 ) {
40 debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id);
41
libnetdata/buffer/buffer.h
+1 -1
@@ -757,7 +757,7 @@ static inline void buffer_json_member_add_uuid(BUFFER *wb, const char *key, uuid
757 buffer_print_json_key(wb, key);
758 buffer_fast_strcat(wb, ":", 1);
759
760 - if(value) {
760 + if(value && !uuid_is_null(*value)) {
761 char uuid[GUID_LEN + 1];
762 uuid_unparse_lower(*value, uuid);
763 buffer_json_add_string_value(wb, uuid);
libnetdata/eval/eval.c
+7
@@ -213,6 +213,13 @@ static inline NETDATA_DOUBLE eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, int
213 break;
214 }
215
216 + if(likely(!*error)) {
217 + if(unlikely(isnan(n)))
218 + *error = EVAL_ERROR_VALUE_IS_NAN;
219 + else if(unlikely(isinf(n)))
220 + *error = EVAL_ERROR_VALUE_IS_INFINITE;
221 + }
222 +
223 return n;
224 }
225
web/api/queries/rrdr.h
+18 -7
@@ -51,13 +51,24 @@ typedef enum rrdr_options {
51 RRDR_OPTION_INTERNAL_AR = (1 << 31), // internal use only, to let the formatters know we want to render the anomaly rate
52 } RRDR_OPTIONS;
53
54 -typedef enum alert_options {
55 - ALERT_OPTION_MINIFY = (1 << 0), // remove JSON spaces and newlines from JSON output
56 - ALERT_OPTION_ACTIVE = (1 << 1), // Only active alerts
57 - ALERT_OPTION_CONFIG = (1 << 2), // Include config
58 - ALERT_OPTION_TRANSITIONS = (1 << 3), // Include transitions
59 - ALERT_OPTION_INSTANCES = (1 << 4), // Include alert instances
60 -} ALERT_OPTIONS;
54 +typedef enum context_v2_options {
55 + CONTEXT_V2_OPTION_MINIFY = (1 << 0), // remove JSON spaces and newlines from JSON output
56 + CONTEXT_V2_OPTION_DEBUG = (1 << 1), // show the request
57 + CONTEXT_V2_OPTION_ALERT_CONFIGURATIONS = (1 << 2), // include alert configurations
58 + CONTEXT_V2_OPTION_ALERT_TRANSITIONS = (1 << 3), // include alert transitions
59 + CONTEXT_V2_OPTION_ALERT_INSTANCES = (1 << 4), // include alert instances
60 +} CONTEXTS_V2_OPTIONS;
61 +
62 +typedef enum context_v2_alert_status {
63 + CONTEXT_V2_ALERT_UNINITIALIZED = (1 << 5), // include UNINITIALIZED alerts
64 + CONTEXT_V2_ALERT_UNDEFINED = (1 << 6), // include UNDEFINED alerts
65 + CONTEXT_V2_ALERT_CLEAR = (1 << 7), // include CLEAR alerts
66 + CONTEXT_V2_ALERT_RAISED = (1 << 8), // include WARNING & CRITICAL alerts
67 + CONTEXT_V2_ALERT_WARNING = (1 << 9), // include WARNING alerts
68 + CONTEXT_V2_ALERT_CRITICAL = (1 << 10), // include CRITICAL alerts
69 +} CONTEXTS_V2_ALERT_STATUS;
70 +
71 +#define CONTEXTS_V2_ALERT_STATUSES (CONTEXT_V2_ALERT_UNINITIALIZED|CONTEXT_V2_ALERT_UNDEFINED|CONTEXT_V2_ALERT_CLEAR|CONTEXT_V2_ALERT_RAISED|CONTEXT_V2_ALERT_WARNING|CONTEXT_V2_ALERT_CRITICAL)
72
73 typedef enum __attribute__ ((__packed__)) rrdr_value_flag {
74
web/api/web_api_v1.c
+86 -16
@@ -53,14 +53,29 @@ static struct {
53 static struct {
54 const char *name;
55 uint32_t hash;
56 - ALERT_OPTIONS value;
57 -} api_v2_alert_options[] = {
58 - {"minify" , 0 , ALERT_OPTION_MINIFY}
59 - , {"active" , 0 , ALERT_OPTION_ACTIVE}
60 - , {"config" , 0 , ALERT_OPTION_CONFIG}
61 - , {"transitions" , 0 , ALERT_OPTION_TRANSITIONS}
62 - , {"instances" , 0 , ALERT_OPTION_INSTANCES}
63 - , {NULL , 0 , 0}
56 + CONTEXTS_V2_OPTIONS value;
57 +} contexts_v2_options[] = {
58 + {"minify" , 0 , CONTEXT_V2_OPTION_MINIFY}
59 + , {"debug" , 0 , CONTEXT_V2_OPTION_DEBUG}
60 + , {"config" , 0 , CONTEXT_V2_OPTION_ALERT_CONFIGURATIONS}
61 + , {"transitions" , 0 , CONTEXT_V2_OPTION_ALERT_TRANSITIONS}
62 + , {"instances" , 0 , CONTEXT_V2_OPTION_ALERT_INSTANCES}
63 + , {NULL , 0 , 0}
64 +};
65 +
66 +static struct {
67 + const char *name;
68 + uint32_t hash;
69 + CONTEXTS_V2_ALERT_STATUS value;
70 +} contexts_v2_alert_status[] = {
71 + {"uninitialized" , 0 , CONTEXT_V2_ALERT_UNINITIALIZED}
72 + , {"undefined" , 0 , CONTEXT_V2_ALERT_UNDEFINED}
73 + , {"clear" , 0 , CONTEXT_V2_ALERT_CLEAR}
74 + , {"raised" , 0 , CONTEXT_V2_ALERT_RAISED}
75 + , {"active" , 0 , CONTEXT_V2_ALERT_RAISED}
76 + , {"warning" , 0 , CONTEXT_V2_ALERT_WARNING}
77 + , {"critical" , 0 , CONTEXT_V2_ALERT_CRITICAL}
78 + , {NULL , 0 , 0}
79 };
80
81 static struct {
@@ -104,11 +119,14 @@ static struct {
119 void web_client_api_v1_init(void) {
120 int i;
121
122 + for(i = 0; contexts_v2_alert_status[i].name ; i++)
123 + contexts_v2_alert_status[i].hash = simple_hash(contexts_v2_alert_status[i].name);
124 +
125 for(i = 0; api_v1_data_options[i].name ; i++)
126 api_v1_data_options[i].hash = simple_hash(api_v1_data_options[i].name);
127
110 - for(i = 0; api_v2_alert_options[i].name ; i++)
111 - api_v2_alert_options[i].hash = simple_hash(api_v2_alert_options[i].name);
128 + for(i = 0; contexts_v2_options[i].name ; i++)
129 + contexts_v2_options[i].hash = simple_hash(contexts_v2_options[i].name);
130
131 for(i = 0; api_v1_data_formats[i].name ; i++)
132 api_v1_data_formats[i].hash = simple_hash(api_v1_data_formats[i].name);
@@ -191,7 +209,7 @@ void web_client_api_v1_management_init(void) {
209 }
210
211 inline RRDR_OPTIONS web_client_api_request_v1_data_options(char *o) {
194 - RRDR_OPTIONS ret = 0x00000000;
212 + RRDR_OPTIONS ret = 0;
213 char *tok;
214
215 while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) {
@@ -210,8 +228,28 @@ inline RRDR_OPTIONS web_client_api_request_v1_data_options(char *o) {
228 return ret;
229 }
230
213 -inline ALERT_OPTIONS web_client_api_request_v2_alert_options(char *o) {
214 - ALERT_OPTIONS ret = 0x00000000;
231 +inline CONTEXTS_V2_OPTIONS web_client_api_request_v2_context_options(char *o) {
232 + CONTEXTS_V2_OPTIONS ret = 0;
233 + char *tok;
234 +
235 + while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) {
236 + if(!*tok) continue;
237 +
238 + uint32_t hash = simple_hash(tok);
239 + int i;
240 + for(i = 0; contexts_v2_options[i].name ; i++) {
241 + if (unlikely(hash == contexts_v2_options[i].hash && !strcmp(tok, contexts_v2_options[i].name))) {
242 + ret |= contexts_v2_options[i].value;
243 + break;
244 + }
245 + }
246 + }
247 +
248 + return ret;
249 +}
250 +
251 +inline CONTEXTS_V2_ALERT_STATUS web_client_api_request_v2_alert_status(char *o) {
252 + CONTEXTS_V2_ALERT_STATUS ret = 0;
253 char *tok;
254
255 while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) {
@@ -219,9 +257,9 @@ inline ALERT_OPTIONS web_client_api_request_v2_alert_options(char *o) {
257
258 uint32_t hash = simple_hash(tok);
259 int i;
222 - for(i = 0; api_v2_alert_options[i].name ; i++) {
223 - if (unlikely(hash == api_v2_alert_options[i].hash && !strcmp(tok, api_v2_alert_options[i].name))) {
224 - ret |= api_v2_alert_options[i].value;
260 + for(i = 0; contexts_v2_alert_status[i].name ; i++) {
261 + if (unlikely(hash == contexts_v2_alert_status[i].hash && !strcmp(tok, contexts_v2_alert_status[i].name))) {
262 + ret |= contexts_v2_alert_status[i].value;
263 break;
264 }
265 }
@@ -230,6 +268,38 @@ inline ALERT_OPTIONS web_client_api_request_v2_alert_options(char *o) {
268 return ret;
269 }
270
271 +void web_client_api_request_v2_contexts_alerts_status_to_buffer_json_array(BUFFER *wb, const char *key, CONTEXTS_V2_ALERT_STATUS options) {
272 + buffer_json_member_add_array(wb, key);
273 +
274 + RRDR_OPTIONS used = 0; // to prevent adding duplicates
275 + for(int i = 0; contexts_v2_alert_status[i].name ; i++) {
276 + if (unlikely((contexts_v2_alert_status[i].value & options) && !(contexts_v2_alert_status[i].value & used))) {
277 + const char *name = contexts_v2_alert_status[i].name;
278 + used |= contexts_v2_alert_status[i].value;
279 +
280 + buffer_json_add_array_item_string(wb, name);
281 + }
282 + }
283 +
284 + buffer_json_array_close(wb);
285 +}
286 +
287 +void web_client_api_request_v2_contexts_options_to_buffer_json_array(BUFFER *wb, const char *key, CONTEXTS_V2_OPTIONS options) {
288 + buffer_json_member_add_array(wb, key);
289 +
290 + RRDR_OPTIONS used = 0; // to prevent adding duplicates
291 + for(int i = 0; contexts_v2_options[i].name ; i++) {
292 + if (unlikely((contexts_v2_options[i].value & options) && !(contexts_v2_options[i].value & used))) {
293 + const char *name = contexts_v2_options[i].name;
294 + used |= contexts_v2_options[i].value;
295 +
296 + buffer_json_add_array_item_string(wb, name);
297 + }
298 + }
299 +
300 + buffer_json_array_close(wb);
301 +}
302 +
303 void web_client_api_request_v1_data_options_to_buffer_json_array(BUFFER *wb, const char *key, RRDR_OPTIONS options) {
304 buffer_json_member_add_array(wb, key);
305
web/api/web_api_v1.h
+5 -1
@@ -7,8 +7,12 @@
7
8 struct web_client;
9
10 +CONTEXTS_V2_OPTIONS web_client_api_request_v2_context_options(char *o);
11 +CONTEXTS_V2_ALERT_STATUS web_client_api_request_v2_alert_status(char *o);
12 +void web_client_api_request_v2_contexts_options_to_buffer_json_array(BUFFER *wb, const char *key, CONTEXTS_V2_OPTIONS options);
13 +void web_client_api_request_v2_contexts_alerts_status_to_buffer_json_array(BUFFER *wb, const char *key, CONTEXTS_V2_ALERT_STATUS options);
14 +
15 RRDR_OPTIONS web_client_api_request_v1_data_options(char *o);
11 -ALERT_OPTIONS web_client_api_request_v2_alert_options(char *o);
16 void web_client_api_request_v1_data_options_to_buffer_json_array(BUFFER *wb, const char *key, RRDR_OPTIONS options);
17 void web_client_api_request_v1_data_options_to_string(char *buf, size_t size, RRDR_OPTIONS options);
18
web/api/web_api_v2.c
+38 -94
@@ -3,7 +3,7 @@
3 #include "web_api_v2.h"
4 #include "../rtc/webrtc.h"
5
6 -static int web_client_api_request_v2_contexts_internal(RRDHOST *host __maybe_unused, struct web_client *w, char *url, CONTEXTS_V2_OPTIONS options) {
6 +static int web_client_api_request_v2_contexts_internal(RRDHOST *host __maybe_unused, struct web_client *w, char *url, CONTEXTS_V2_MODE mode) {
7 struct api_v2_contexts_request req = { 0 };
8
9 while(url) {
@@ -17,25 +17,47 @@ static int web_client_api_request_v2_contexts_internal(RRDHOST *host __maybe_unu
17 // name and value are now the parameters
18 // they are not null and not empty
19
20 - if(!strcmp(name, "scope_nodes")) req.scope_nodes = value;
21 - else if(!strcmp(name, "nodes")) req.nodes = value;
22 - else if((options & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH)) && !strcmp(name, "scope_contexts")) req.scope_contexts = value;
23 - else if((options & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH)) && !strcmp(name, "contexts")) req.contexts = value;
24 - else if((options & CONTEXTS_V2_SEARCH) && !strcmp(name, "q")) req.q = value;
25 - else if(!strcmp(name, "options")) {
26 - if(strstr(value, "minify"))
27 - options |= CONTEXTS_V2_MINIFY;
28 - else if(strstr(value, "debug"))
29 - options |= CONTEXTS_V2_DEBUG;
20 + if(!strcmp(name, "scope_nodes"))
21 + req.scope_nodes = value;
22 + else if(!strcmp(name, "nodes"))
23 + req.nodes = value;
24 + else if((mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_ALERTS)) && !strcmp(name, "scope_contexts"))
25 + req.scope_contexts = value;
26 + else if((mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_ALERTS)) && !strcmp(name, "contexts"))
27 + req.contexts = value;
28 + else if((mode & CONTEXTS_V2_SEARCH) && !strcmp(name, "q"))
29 + req.q = value;
30 + else if(!strcmp(name, "options"))
31 + req.options = web_client_api_request_v2_context_options(value);
32 + else if(!strcmp(name, "after"))
33 + req.after = str2l(value);
34 + else if(!strcmp(name, "before"))
35 + req.before = str2l(value);
36 + else if(!strcmp(name, "timeout"))
37 + req.timeout_ms = str2l(value);
38 + else if(mode & CONTEXTS_V2_ALERTS) {
39 + if(!strcmp(name, "status"))
40 + req.alerts.status = web_client_api_request_v2_alert_status(value);
41 + else if (!strcmp(name, "last")) {
42 + req.alerts.last = strtoul(value, NULL, 0);
43 + } else if (!strcmp(name, "transition")) {
44 + req.alerts.transition = value;
45 + } else if (!strcmp(name, "alert")) {
46 + req.alerts.alert = value;
47 + }
48 }
31 - else if(!strcmp(name, "after")) req.after = str2l(value);
32 - else if(!strcmp(name, "before")) req.before = str2l(value);
33 - else if(!strcmp(name, "timeout")) req.timeout_ms = str2l(value);
49 }
50
51 + if ((mode & CONTEXTS_V2_ALERTS) && (req.options & CONTEXT_V2_OPTION_ALERT_INSTANCES) && !req.alerts.last)
52 + req.alerts.last = 1;
53 +
54 buffer_flush(w->response.data);
55 buffer_no_cacheable(w->response.data);
38 - return rrdcontext_to_json_v2(w->response.data, &req, options);
56 + return rrdcontext_to_json_v2(w->response.data, &req, mode);
57 +}
58 +
59 +static int web_client_api_request_v2_alerts(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
60 + return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_ALERTS | CONTEXTS_V2_NODES);
61 }
62
63 static int web_client_api_request_v2_functions(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
@@ -358,84 +380,6 @@ cleanup:
380 return ret;
381 }
382
361 -static int web_client_api_request_v2_alerts(RRDHOST *host __maybe_unused, struct web_client *w, char *url)
362 -{
363 - time_t after = 0;
364 - time_t before = 0;
365 - struct api_v2_alerts_request req = { 0 };
366 -
367 - CONTEXTS_V2_OPTIONS options = CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_NODES;
368 - ALERT_OPTIONS alert_options = 0;
369 -
370 - while(url) {
371 - char *value = strsep_skip_consecutive_separators(&url, "&");
372 - if(!value || !*value) continue;
373 -
374 - char *name = strsep_skip_consecutive_separators(&value, "=");
375 - if(!name || !*name) continue;
376 - if(!value || !*value) continue;
377 -
378 - // name and value are now the parameters
379 - // they are not null and not empty
380 -
381 - if (!strcmp(name, "scope_nodes")) {
382 - req.scope_nodes = value;
383 - options |= CONTEXTS_V2_NODES;
384 - }
385 - else if (!strcmp(name, "nodes")) {
386 - req.nodes = value;
387 - options |= CONTEXTS_V2_NODES;
388 - }
389 - else if (!strcmp(name, "scope_contexts")) {
390 - req.scope_contexts = value;
391 - options |= CONTEXTS_V2_CONTEXTS;
392 - }
393 - else if (!strcmp(name, "instance_id"))
394 - req.alert_id = (time_t)strtoul(value, NULL, 0);
395 - else if (!strcmp(name, "last"))
396 - req.last = strtoul(value, NULL, 0);
397 - else if (!strcmp(name, "transition_id")) {
398 - req.transition_id = value;
399 - } else if (!strcmp(name, "alert_name")) {
400 - req.alert_name = value;
401 - req.alert_name_pattern = string_to_simple_pattern(value);
402 - } else if (!strcmp(name, "config_hash")) {
403 - req.config_hash = value;
404 - req.config_hash_pattern = string_to_simple_pattern(value);
405 - } else if (!strcmp(name, "contexts")) {
406 - req.contexts = value;
407 - options |= CONTEXTS_V2_CONTEXTS;
408 - } else if (!strcmp(name, "state")) {
409 - req.state = value; // all | warning | critical, clear, undefined, uninitialiazed
410 - } else if (!strcmp(name, "q"))
411 - req.q = value;
412 - else if (!strcmp(name, "after"))
413 - after = (time_t)strtoul(value, NULL, 0);
414 - else if (!strcmp(name, "before"))
415 - before = (time_t)strtoul(value, NULL, 0);
416 - else if (!strcmp(name, "output")) {
417 - // config, instances, transitions
418 - alert_options |= web_client_api_request_v2_alert_options(value);
419 - }
420 -
421 - // TODO: All states and special "ACTIVE"
422 - }
423 -
424 - buffer_flush(w->response.data);
425 - buffer_no_cacheable(w->response.data);
426 - w->response.data->content_type = CT_APPLICATION_JSON;
427 - buffer_json_initialize( w->response.data, "\"", "\"", 0, true, alert_options & ALERT_OPTION_MINIFY);
428 -
429 - if (alert_options & ALERT_OPTION_INSTANCES && !req.last)
430 - req.last = 1;
431 -
432 - req.options = alert_options;
433 - req.after = after;
434 - req.before = before;
435 -
436 - return alerts_to_json_v2(w->response.data, &req, options);
437 -}
438 -
383 static int web_client_api_request_v2_webrtc(RRDHOST *host __maybe_unused, struct web_client *w, char *url __maybe_unused) {
384 return webrtc_new_connection(w->post_payload, w->response.data);
385 }
@@ -448,10 +392,10 @@ static struct web_api_command api_commands_v2[] = {
392 {"weights", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_weights},
393 {"versions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_versions},
394 {"functions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_functions},
395 + {"alerts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_alerts},
396 {"q", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_q},
397
398 {"rtc_offer", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v2_webrtc},
454 - {"alerts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_alerts},
399
400 // terminator
401 {NULL, 0, WEB_CLIENT_ACL_NONE, NULL},
web/server/web_client.c
+1 -2
@@ -839,7 +839,7 @@ const char *web_response_code_to_string(int code) {
839
840 static inline char *http_header_parse(struct web_client *w, char *s, int parse_useragent) {
841 static uint32_t hash_origin = 0, hash_connection = 0, hash_donottrack = 0, hash_useragent = 0,
842 - hash_authorization = 0, hash_host = 0, hash_forwarded_proto = 0, hash_forwarded_host = 0;
842 + hash_authorization = 0, hash_host = 0, hash_forwarded_host = 0;
843 static uint32_t hash_accept_encoding = 0;
844
845 if(unlikely(!hash_origin)) {
@@ -850,7 +850,6 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
850 hash_useragent = simple_uhash("User-Agent");
851 hash_authorization = simple_uhash("X-Auth-Token");
852 hash_host = simple_uhash("Host");
853 - hash_forwarded_proto = simple_uhash("X-Forwarded-Proto");
853 hash_forwarded_host = simple_uhash("X-Forwarded-Host");
854 }
855