summaryrefslogtreecommitdiffstats
path: root/database
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2023-06-28 23:14:10 +0300
committerGitHub <noreply@github.com>2023-06-28 23:14:10 +0300
commit5be9be74854d00879e6d52d6432ae12b5e8558cd (patch)
tree7d2c3e28db5aa1306cc480a45f1bfdd17b3c8375 /database
parent26ec3b119efb9271f622047259866a6083ed6269 (diff)
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>
Diffstat (limited to 'database')
-rw-r--r--database/contexts/api_v2.c1307
-rw-r--r--database/contexts/rrdcontext.h84
-rw-r--r--database/rrdcalc.c30
-rw-r--r--database/rrdcalc.h3
-rw-r--r--database/sqlite/sqlite_health.c281
-rw-r--r--database/sqlite/sqlite_health.h4
6 files changed, 1023 insertions, 686 deletions
diff --git a/database/contexts/api_v2.c b/database/contexts/api_v2.c
index 053c5e7a9b..d3494bb1c4 100644
--- a/database/contexts/api_v2.c
+++ b/database/contexts/api_v2.c
@@ -58,14 +58,14 @@ static const char *fts_match_to_string(FTS_MATCH match) {
}
}
-struct rrdfunction_to_json_v2 {
+struct function_v2_entry {
size_t size;
size_t used;
size_t *node_ids;
STRING *help;
};
-struct rrdcontext_to_json_v2_entry {
+struct context_v2_entry {
size_t count;
STRING *id;
STRING *family;
@@ -76,6 +76,31 @@ struct rrdcontext_to_json_v2_entry {
FTS_MATCH match;
};
+struct alert_v2_entry {
+ RRDCALC *tmp;
+
+ STRING *name;
+
+ size_t ati;
+
+ size_t critical;
+ size_t warning;
+ size_t clear;
+ size_t error;
+
+ size_t instances;
+ DICTIONARY *nodes;
+ DICTIONARY *configs;
+};
+
+struct alert_config_v2_entry {
+ RRDCALC *tmp;
+
+ size_t ati;
+ size_t aci;
+ size_t alerts_using_this;
+};
+
typedef struct full_text_search_index {
size_t searches;
size_t string_searches;
@@ -94,17 +119,18 @@ static inline bool full_text_search_char(FTS_INDEX *fts, SIMPLE_PATTERN *q, char
return simple_pattern_matches(q, ptr);
}
+struct contexts_v2_node {
+ size_t ni;
+ RRDHOST *host;
+};
+
struct rrdcontext_to_json_v2_data {
time_t now;
BUFFER *wb;
- union {
- struct api_v2_contexts_request *request;
- struct api_v2_alerts_request *alerts_request;
- };
-
- DICTIONARY *ctx;
+ struct api_v2_contexts_request *request;
+ CONTEXTS_V2_MODE mode;
CONTEXTS_V2_OPTIONS options;
struct query_versions versions;
@@ -112,22 +138,28 @@ struct rrdcontext_to_json_v2_data {
SIMPLE_PATTERN *scope_pattern;
SIMPLE_PATTERN *pattern;
size_t ni;
+ DICTIONARY *dict; // the result set
} nodes;
struct {
- Pvoid_t JudyHS;
-// ALERT_OPTIONS alert_options;
SIMPLE_PATTERN *scope_pattern;
SIMPLE_PATTERN *pattern;
-// time_t after;
-// time_t before;
- size_t li;
- } alerts;
+ size_t ci;
+ DICTIONARY *dict; // the result set
+ } contexts;
struct {
- SIMPLE_PATTERN *scope_pattern;
- SIMPLE_PATTERN *pattern;
- } contexts;
+ SIMPLE_PATTERN *alert_name_pattern;
+ time_t alarm_id_filter;
+
+ size_t ati;
+ size_t aii;
+ size_t aci;
+
+ DICTIONARY *alerts;
+ DICTIONARY *alert_instances;
+ DICTIONARY *alert_configs;
+ } alerts;
struct {
FTS_MATCH host_match;
@@ -137,7 +169,7 @@ struct rrdcontext_to_json_v2_data {
} q;
struct {
- DICTIONARY *dict;
+ DICTIONARY *dict; // the result set
} functions;
struct {
@@ -150,20 +182,119 @@ struct rrdcontext_to_json_v2_data {
struct query_timings timings;
};
-static void add_alert_index(Pvoid_t *JudyHS, uuid_t *uuid, ssize_t idx)
-{
- Pvoid_t *PValue = JudyHSIns(JudyHS, uuid, sizeof(*uuid), PJE0);
- if (!PValue)
- return;
- *((Word_t *) PValue) = (Word_t) idx;
+static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) {
+ t->instances++;
+
+ switch(rc->status) {
+ case RRDCALC_STATUS_CRITICAL:
+ t->critical++;
+ break;
+
+ case RRDCALC_STATUS_WARNING:
+ t->warning++;
+ break;
+
+ case RRDCALC_STATUS_CLEAR:
+ t->clear++;
+ break;
+
+ case RRDCALC_STATUS_REMOVED:
+ case RRDCALC_STATUS_UNINITIALIZED:
+ break;
+
+ case RRDCALC_STATUS_UNDEFINED:
+ default:
+ if(!netdata_double_isnumber(rc->value))
+ t->error++;
+
+ break;
+ }
+
+ dictionary_set(t->nodes, rc->rrdset->rrdhost->machine_guid, NULL, 0);
+
+ char key[UUID_STR_LEN + 1];
+ uuid_unparse_lower(rc->config_hash_id, key);
+ dictionary_set(t->configs, key, NULL, 0);
+}
+
+static void alerts_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
+ struct rrdcontext_to_json_v2_data *ctl = data;
+ struct alert_v2_entry *t = value;
+ RRDCALC *rc = t->tmp;
+ t->name = rc->name;
+ t->ati = ctl->alerts.ati++;
+
+ t->nodes = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE);
+ t->configs = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE);
+
+ alerts_v2_add(t, rc);
}
-ssize_t get_alert_index(Pvoid_t JudyHS, uuid_t *uuid)
-{
- Pvoid_t *PValue = JudyHSGet(JudyHS, uuid, sizeof(*uuid));
- if (!PValue)
- return -1;
- return (ssize_t) *((Word_t *) PValue);
+static bool alerts_v2_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
+ struct alert_v2_entry *t = old_value, *n = new_value;
+ RRDCALC *rc = n->tmp;
+ alerts_v2_add(t, rc);
+ return true;
+}
+
+static void alerts_v2_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
+ struct alert_v2_entry *t = value;
+ dictionary_destroy(t->nodes);
+ dictionary_destroy(t->configs);
+}
+
+static void alert_configs_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
+ struct rrdcontext_to_json_v2_data *ctl = data;
+ struct alert_config_v2_entry *t = value;
+ t->aci = ctl->alerts.aci++;
+ t->alerts_using_this = 1;
+}
+
+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) {
+ struct alert_config_v2_entry *t = old_value;
+ t->alerts_using_this++;
+ return false;
+}
+
+static void alert_configs_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) {
+ ;
+}
+
+static void alert_instances_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
+ struct rrdcontext_to_json_v2_data *ctl = data;
+ struct alert_instance_v2_entry *t = value;
+ RRDCALC *rc = t->tmp;
+
+ t->chart_id = rc->rrdset->id;
+ t->chart_name = rc->rrdset->name;
+ t->family = rc->rrdset->family;
+ t->name = rc->name;
+ t->status = rc->status;
+ t->flags = rc->run_flags;
+ t->info = rc->info;
+ t->value = rc->value;
+ t->last_updated = rc->last_updated;
+ t->last_status_change = rc->last_status_change;
+ t->last_status_change_value = rc->last_status_change_value;
+ t->host = rc->rrdset->rrdhost;
+ t->alarm_id = rc->id;
+ t->ni = ctl->nodes.ni;
+ t->global_id = rc->ae ? rc->ae->global_id : 0;
+ t->name = rc->name;
+ t->aii = ctl->alerts.aii++;
+
+ uuid_copy(t->config_hash_id, rc->config_hash_id);
+ if(rc->ae)
+ uuid_copy(t->last_transition_id, rc->ae->transition_id);
+}
+
+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) {
+ internal_fatal(true, "This should never happen!");
+ return true;
+}
+
+static void alert_instances_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) {
+ ;
}
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
return matched;
}
+static bool rrdcontext_matches_alert(struct rrdcontext_to_json_v2_data *ctl, RRDCONTEXT *rc) {
+ size_t matches = 0;
+ RRDINSTANCE *ri;
+ dfe_start_read(rc->rrdinstances, ri) {
+ if(ri->rrdset) {
+ RRDSET *st = ri->rrdset;
+ netdata_rwlock_rdlock(&st->alerts.rwlock);
+ for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) {
+ if(ctl->alerts.alert_name_pattern && !simple_pattern_matches_string(ctl->alerts.alert_name_pattern, rcl->name))
+ continue;
+
+ if(ctl->alerts.alarm_id_filter && ctl->alerts.alarm_id_filter != rcl->id)
+ continue;
+
+ size_t m = ctl->request->alerts.status & CONTEXTS_V2_ALERT_STATUSES ? 0 : 1;
+
+ if (!m) {
+ if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_UNINITIALIZED) &&
+ rcl->status == RRDCALC_STATUS_UNINITIALIZED)
+ m++;
+
+ if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_UNDEFINED) &&
+ rcl->status == RRDCALC_STATUS_UNDEFINED)
+ m++;
+
+ if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_CLEAR) &&
+ rcl->status == RRDCALC_STATUS_CLEAR)
+ m++;
+
+ if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_RAISED) &&
+ rcl->status >= RRDCALC_STATUS_RAISED)
+ m++;
+
+ if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_WARNING) &&
+ rcl->status == RRDCALC_STATUS_WARNING)
+ m++;
+
+ if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_CRITICAL) &&
+ rcl->status == RRDCALC_STATUS_CRITICAL)
+ m++;
+
+ if(!m)
+ continue;
+ }
+
+ struct alert_v2_entry t = {
+ .tmp = rcl,
+ };
+ struct alert_v2_entry *a2e = dictionary_set(ctl->alerts.alerts, string2str(rcl->name), &t,
+ sizeof(struct alert_v2_entry));
+ size_t ati = a2e->ati;
+ size_t aci = 0;
+ matches++;
+
+ if (ctl->options & CONTEXT_V2_OPTION_ALERT_CONFIGURATIONS) {
+ char key[UUID_STR_LEN + 1];
+ uuid_unparse_lower(rcl->config_hash_id, key);
+ struct alert_config_v2_entry t2 = {
+ .tmp = rcl,
+ .ati = a2e->ati,
+ };
+ struct alert_config_v2_entry *a2c = dictionary_set(ctl->alerts.alert_configs, key, &t2,
+ sizeof(struct alert_config_v2_entry));
+ aci = a2c->aci;
+ }
+
+ if (ctl->options & CONTEXT_V2_OPTION_ALERT_INSTANCES) {
+ char key[20 + 1];
+ snprintfz(key, 20, "%p", rcl);
+
+ struct alert_instance_v2_entry z = {
+ .ati = ati,
+ .aci = aci,
+ .tmp = rcl,
+ };
+ dictionary_set(ctl->alerts.alert_instances, key, &z, sizeof(z));
+ }
+ }
+ netdata_rwlock_unlock(&st->alerts.rwlock);
+ }
+ }
+ dfe_done(ri);
+
+ return matches != 0;
+}
+
+
static ssize_t rrdcontext_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED *rca, bool queryable_context __maybe_unused) {
struct rrdcontext_to_json_v2_data *ctl = data;
@@ -245,25 +463,32 @@ static ssize_t rrdcontext_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED
return 0; // continue to next context
FTS_MATCH match = ctl->q.host_match;
- if((ctl->options & CONTEXTS_V2_SEARCH) && ctl->q.pattern) {
+ if((ctl->mode & CONTEXTS_V2_SEARCH) && ctl->q.pattern) {
match = rrdcontext_to_json_v2_full_text_search(ctl, rc, ctl->q.pattern);
if(match == FTS_MATCHED_NONE)
return 0; // continue to next context
}
- struct rrdcontext_to_json_v2_entry t = {
- .count = 1,
- .id = rc->id,
- .family = string_dup(rc->family),
- .priority = rc->priority,
- .first_time_s = rc->first_time_s,
- .last_time_s = rc->last_time_s,
- .flags = rc->flags,
- .match = match,
- };
+ if(ctl->mode & CONTEXTS_V2_ALERTS) {
+ if(!rrdcontext_matches_alert(ctl, rc))
+ return 0; // continue to next context
+ }
- dictionary_set(ctl->ctx, string2str(rc->id), &t, sizeof(t));
+ if(ctl->contexts.dict) {
+ struct context_v2_entry t = {
+ .count = 1,
+ .id = rc->id,
+ .family = string_dup(rc->family),
+ .priority = rc->priority,
+ .first_time_s = rc->first_time_s,
+ .last_time_s = rc->last_time_s,
+ .flags = rc->flags,
+ .match = match,
+ };
+
+ dictionary_set(ctl->contexts.dict, string2str(rc->id), &t, sizeof(struct context_v2_entry));
+ }
return 1;
}
@@ -280,11 +505,6 @@ void buffer_json_agent_status_id(BUFFER *wb, size_t ai, usec_t duration_ut) {
buffer_json_object_close(wb);
}
-static ssize_t alert_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED *rca, bool queryable_context __maybe_unused)
-{
- return rrdcontext_to_json_v2_add_context(data, rca, queryable_context);
-}
-
void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status) {
buffer_json_member_add_string(wb, "mg", host->machine_guid);
@@ -453,6 +673,107 @@ static inline void rrdhost_health_to_json_v2(BUFFER *wb, const char *key, RRDHOS
buffer_json_object_close(wb); // health
}
+static void rrdcontext_to_json_v2_rrdhost(BUFFER *wb, RRDHOST *host, struct rrdcontext_to_json_v2_data *ctl, size_t node_id) {
+ buffer_json_add_array_item_object(wb); // this node
+ buffer_json_node_add_v2(wb, host, node_id, 0,
+ (ctl->mode & CONTEXTS_V2_AGENTS) && !(ctl->mode & CONTEXTS_V2_NODES_INSTANCES));
+
+ if(ctl->mode & (CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES)) {
+ RRDHOST_STATUS s;
+ rrdhost_status(host, ctl->now, &s);
+
+ if (ctl->mode & (CONTEXTS_V2_NODES_INFO)) {
+ buffer_json_member_add_string(wb, "v", rrdhost_program_version(host));
+
+ host_labels2json(host, wb, "labels");
+
+ if (host->system_info) {
+ buffer_json_member_add_object(wb, "hw");
+ {
+ buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
+ buffer_json_member_add_string_or_empty(wb, "cpu_frequency", host->system_info->host_cpu_freq);
+ buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
+ buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
+ buffer_json_member_add_string_or_empty(wb, "disk_space", host->system_info->host_disk_space);
+ buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
+ buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
+ }
+ buffer_json_object_close(wb);
+
+ buffer_json_member_add_object(wb, "os");
+ {
+ buffer_json_member_add_string_or_empty(wb, "id", host->system_info->host_os_id);
+ buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->host_os_name);
+ buffer_json_member_add_string_or_empty(wb, "v", host->system_info->host_os_version);
+ buffer_json_member_add_object(wb, "kernel");
+ buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->kernel_name);
+ buffer_json_member_add_string_or_empty(wb, "v", host->system_info->kernel_version);
+ buffer_json_object_close(wb);
+ }
+ buffer_json_object_close(wb);
+ }
+
+ // created - the node is created but never connected to cloud
+ // unreachable - not currently connected
+ // stale - connected but not having live data
+ // reachable - connected with live data
+ // pruned - not connected for some time and has been removed
+ buffer_json_member_add_string(wb, "state", rrdhost_state_cloud_emulation(host) ? "reachable" : "stale");
+
+ rrdhost_health_to_json_v2(wb, "health", &s);
+ agent_capabilities_to_json(wb, host, "capabilities");
+ }
+
+ if (ctl->mode & (CONTEXTS_V2_NODES_INSTANCES)) {
+ buffer_json_member_add_array(wb, "instances");
+ buffer_json_add_array_item_object(wb); // this instance
+ {
+ buffer_json_agent_status_id(wb, 0, 0);
+
+ buffer_json_member_add_object(wb, "db");
+ {
+ buffer_json_member_add_string(wb, "status", rrdhost_db_status_to_string(s.db.status));
+ buffer_json_member_add_string(wb, "liveness", rrdhost_db_liveness_to_string(s.db.liveness));
+ buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
+ buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
+ buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
+ buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
+ buffer_json_member_add_uint64(wb, "instances", s.db.instances);
+ buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
+ }
+ buffer_json_object_close(wb);
+
+ rrdhost_receiver_to_json(wb, &s, "ingest");
+ rrdhost_sender_to_json(wb, &s, "stream");
+
+ buffer_json_member_add_object(wb, "ml");
+ buffer_json_member_add_string(wb, "status", rrdhost_ml_status_to_string(s.ml.status));
+ buffer_json_member_add_string(wb, "type", rrdhost_ml_type_to_string(s.ml.type));
+ if (s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
+ buffer_json_member_add_object(wb, "metrics");
+ {
+ buffer_json_member_add_uint64(wb, "anomalous", s.ml.metrics.anomalous);
+ buffer_json_member_add_uint64(wb, "normal", s.ml.metrics.normal);
+ buffer_json_member_add_uint64(wb, "trained", s.ml.metrics.trained);
+ buffer_json_member_add_uint64(wb, "pending", s.ml.metrics.pending);
+ buffer_json_member_add_uint64(wb, "silenced", s.ml.metrics.silenced);
+ }
+ buffer_json_object_close(wb); // metrics
+ }
+ buffer_json_object_close(wb); // ml
+
+ rrdhost_health_to_json_v2(wb, "health", &s);
+
+ host_functions2json(host, wb); // functions
+ agent_capabilities_to_json(wb, host, "capabilities");
+ }
+ buffer_json_object_close(wb); // this instance
+ buffer_json_array_close(wb); // instances
+ }
+ }
+ buffer_json_object_close(wb); // this node
+}
+
static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool queryable_host) {
if(!queryable_host || !host->rrdctx.contexts)
// 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
return 0; // continue to next host
struct rrdcontext_to_json_v2_data *ctl = data;
- BUFFER *wb = ctl->wb;
if(ctl->window.enabled && !rrdhost_matches_window(host, ctl->window.after, ctl->window.before, ctl->now))
// 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
// interrupted
return -1; // stop the query
- bool host_matched = (ctl->options & CONTEXTS_V2_NODES);
- bool do_contexts = (ctl->options & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH));
+ bool host_matched = (ctl->mode & CONTEXTS_V2_NODES);
+ bool do_contexts = (ctl->mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_ALERTS));
ctl->q.host_match = FTS_MATCHED_NONE;
- if((ctl->options & CONTEXTS_V2_SEARCH)) {
+ if((ctl->mode & CONTEXTS_V2_SEARCH)) {
// check if we match the host itself
if(ctl->q.pattern && (
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
if(!host_matched)
return 0;
- if(ctl->options & CONTEXTS_V2_FUNCTIONS) {
- struct rrdfunction_to_json_v2 t = {
+ if(ctl->mode & CONTEXTS_V2_FUNCTIONS) {
+ struct function_v2_entry t = {
.used = 1,
.size = 1,
.node_ids = &ctl->nodes.ni,
@@ -525,363 +845,49 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu
host_functions_to_dict(host, ctl->functions.dict, &t, sizeof(t), &t.help);
}
- if(ctl->options & CONTEXTS_V2_NODES) {
- buffer_json_add_array_item_object(wb); // this node
- buffer_json_node_add_v2(wb, host, ctl->nodes.ni++, 0,
- (ctl->options & CONTEXTS_V2_AGENTS) && !(ctl->options & CONTEXTS_V2_NODES_INSTANCES));
-
- if(ctl->options & (CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES)) {
- RRDHOST_STATUS s;
- rrdhost_status(host, ctl->now, &s);
-
- if (ctl->options & (CONTEXTS_V2_NODES_INFO)) {
- buffer_json_member_add_string(wb, "v", rrdhost_program_version(host));
-
- host_labels2json(host, wb, "labels");
-
- if (host->system_info) {
- buffer_json_member_add_object(wb, "hw");
- {
- buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
- buffer_json_member_add_string_or_empty(wb, "cpu_frequency", host->system_info->host_cpu_freq);
- buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
- buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
- buffer_json_member_add_string_or_empty(wb, "disk_space", host->system_info->host_disk_space);
- buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
- buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
- }
- buffer_json_object_close(wb);
-
- buffer_json_member_add_object(wb, "os");
- {
- buffer_json_member_add_string_or_empty(wb, "id", host->system_info->host_os_id);
- buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->host_os_name);
- buffer_json_member_add_string_or_empty(wb, "v", host->system_info->host_os_version);
- buffer_json_member_add_object(wb, "kernel");
- buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->kernel_name);
- buffer_json_member_add_string_or_empty(wb, "v", host->system_info->kernel_version);
- buffer_json_object_close(wb);
- }
- buffer_json_object_close(wb);
- }
-
- // created - the node is created but never connected to cloud
- // unreachable - not currently connected
- // stale - connected but not having live data
- // reachable - connected with live data
- // pruned - not connected for some time and has been removed
- buffer_json_member_add_string(wb, "state", rrdhost_state_cloud_emulation(host) ? "reachable" : "stale");
-
- rrdhost_health_to_json_v2(wb, "health", &s);
- agent_capabilities_to_json(wb, host, "capabilities");
- }
-
- if (ctl->options & (CONTEXTS_V2_NODES_INSTANCES)) {
- buffer_json_member_add_array(wb, "instances");
- buffer_json_add_array_item_object(wb); // this instance
- {
- buffer_json_agent_status_id(wb, 0, 0);
-
- buffer_json_member_add_object(wb, "db");
- {
- buffer_json_member_add_string(wb, "status", rrdhost_db_status_to_string(s.db.status));
- buffer_json_member_add_string(wb, "liveness", rrdhost_db_liveness_to_string(s.db.liveness));
- buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
- buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
- buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
- buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
- buffer_json_member_add_uint64(wb, "instances", s.db.instances);
- buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
- }
- buffer_json_object_close(wb);
-
- rrdhost_receiver_to_json(wb, &s, "ingest");
- rrdhost_sender_to_json(wb, &s, "stream");
-
- buffer_json_member_add_object(wb, "ml");
- buffer_json_member_add_string(wb, "status", rrdhost_ml_status_to_string(s.ml.status));
- buffer_json_member_add_string(wb, "type", rrdhost_ml_type_to_string(s.ml.type));
- if (s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
- buffer_json_member_add_object(wb, "metrics");
- {
- buffer_json_member_add_uint64(wb, "anomalous", s.ml.metrics.anomalous);
- buffer_json_member_add_uint64(wb, "normal", s.ml.metrics.normal);
- buffer_json_member_add_uint64(wb, "trained", s.ml.metrics.trained);
- buffer_json_member_add_uint64(wb, "pending", s.ml.metrics.pending);
- buffer_json_member_add_uint64(wb, "silenced", s.ml.metrics.silenced);
- }
- buffer_json_object_close(wb); // metrics
- }
- buffer_json_object_close(wb); // ml
-
- rrdhost_health_to_json_v2(wb, "health", &s);
+ if(ctl->mode & CONTEXTS_V2_NODES) {
+ struct contexts_v2_node t = {
+ .ni = ctl->nodes.ni++,
+ .host = host,
+ };
- host_functions2json(host, wb); // functions
- agent_capabilities_to_json(wb, host, "capabilities");
- }
- buffer_json_object_close(wb); // this instance
- buffer_json_array_close(wb); // instances
- }
- }
- buffer_json_object_close(wb); // this node
+ dictionary_set(ctl->nodes.dict, host->machine_guid, &t, sizeof(struct contexts_v2_node));
}
return 1;
}
-static ssize_t alert_to_json_v2_add_host(void *data, RRDHOST *host, bool queryable_host) {
- if(!queryable_host || !host->rrdctx.contexts)
- // the host matches the 'scope_host' but does not match the 'host' patterns
- // or the host does not have any contexts
- return 0;
-
- struct rrdcontext_to_json_v2_data *ctl = data;
- BUFFER *wb = ctl->wb;
-
- bool host_matched = (ctl->options & CONTEXTS_V2_NODES);
- bool do_contexts = (ctl->options & (CONTEXTS_V2_CONTEXTS));
-
- ctl->q.host_match = FTS_MATCHED_NONE;
- if((ctl->options & CONTEXTS_V2_SEARCH)) {
- // check if we match the host itself
- if(ctl->q.pattern && (
- full_text_search_string(&ctl->q.fts, ctl->q.pattern, host->hostname) ||
- full_text_search_char(&ctl->q.fts, ctl->q.pattern, host->machine_guid) ||
- (ctl->q.pattern && full_text_search_char(&ctl->q.fts, ctl->q.pattern, ctl->q.host_node_id_str)))) {
- ctl->q.host_match = FTS_MATCHED_HOST;
- do_contexts = true;
- }
- }
-
- if(do_contexts) {
- // save it
- SIMPLE_PATTERN *old_q = ctl->q.pattern;
-
- if(ctl->q.host_match == FTS_MATCHED_HOST)
- // do not do pattern matching on contexts - we matched the host itself
- ctl->q.pattern = NULL;
-
- ssize_t added = query_scope_foreach_context(
- host, ctl->alerts_request->scope_contexts,
- ctl->contexts.scope_pattern, ctl->contexts.pattern,
- alert_to_json_v2_add_context, queryable_host, ctl);
-
- // restore it
- ctl->q.pattern = old_q;
-
- if(added == -1)
- return -1;
-
- if(added)
- host_matched = true;
- }
-
- if(host_matched && (ctl->options & (CONTEXTS_V2_NODES))) {
- buffer_json_add_array_item_object(wb);
- buffer_json_node_add_v2(wb, host, ctl->nodes.ni++, 0, false);
-
- if (ctl->alerts_request->options & ALERT_OPTION_TRANSITIONS) {
- if (rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)) {
- buffer_json_member_add_array(wb, "instances");
- health_alert2json(host, wb, ctl->alerts_request->options, ctl->alerts.JudyHS, ctl->alerts_request->after, ctl->alerts_request->before, ctl->alerts_request->last);
- buffer_json_array_close(wb);
- }
- }
-
- buffer_json_object_close(wb);
- }
-
- return host_matched ? 1 : 0;
-}
-
-static inline bool alert_is_matched( struct api_v2_alerts_request *alerts_request, RRDCALC *rc)
-{
- char hash_id[UUID_STR_LEN];
- uuid_unparse_lower(rc->config_hash_id, hash_id);
-
- if (alerts_request->alert_id)
- return (rc->id == alerts_request->alert_id);
-
- SIMPLE_PATTERN_RESULT match = SP_MATCHED_POSITIVE;
- SIMPLE_PATTERN *match_pattern = alerts_request->config_hash_pattern;
- if(match_pattern) {
- match = simple_pattern_matches_extract(match_pattern, hash_id, NULL, 0);
- if(match == SP_NOT_MATCHED)
- return false;;
- }
-
- match = SP_MATCHED_POSITIVE;
- match_pattern = alerts_request->alert_name_pattern;
- if(match_pattern) {
- match = simple_pattern_matches_string_extract(match_pattern, rc->name, NULL, 0);
- if(match == SP_NOT_MATCHED)
- return false;
- }
-
- return true;
-}
-
-static ssize_t alert_to_json_v2_add_alert(void *data, RRDHOST *host, bool queryable_host) {
- if(!queryable_host || !host->rrdctx.contexts)
- // the host matches the 'scope_host' but does not match the 'host' patterns
- // or the host does not have any contexts
- return 0;
-
- struct rrdcontext_to_json_v2_data *ctl = data;
- BUFFER *wb = ctl->wb;
-
- bool host_matched = (ctl->options & CONTEXTS_V2_NODES);
- bool do_contexts = (ctl->options & (CONTEXTS_V2_CONTEXTS));
-
- if(do_contexts) {
- ssize_t added = query_scope_foreach_context(
- host, ctl->request->scope_contexts,
- ctl->contexts.scope_pattern, ctl->contexts.pattern,
- alert_to_json_v2_add_context, queryable_host, ctl);
-
- if(added == -1)
- return -1;
-
- if(added)
- host_matched = true;
- }
-
- if(host_matched && (ctl->options & (CONTEXTS_V2_NODES))) {
- if (rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)) {
-
- RRDCALC *rc;
- foreach_rrdcalc_in_rrdhost_read(host, rc) {
- if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
- continue;
-
- if (unlikely(!rrdset_is_available_for_exporting_and_alarms(rc->rrdset)))
- continue;
-
- if ((ctl->alerts_request->options & ALERT_OPTION_ACTIVE) &&
- !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL))
- continue;
-
- char hash_id[GUID_LEN + 1];
- uuid_unparse_lower(rc->config_hash_id, hash_id);
-
- if (!alert_is_matched(ctl->alerts_request, rc))
- continue;
-
- ssize_t idx = get_alert_index(ctl->alerts.JudyHS, &rc->config_hash_id);
- if (idx >= 0)
- continue;
-
- buffer_json_add_array_item_object(wb);
- add_alert_index(&ctl->alerts.JudyHS, &rc->config_hash_id, (ssize_t)ctl->alerts.li++);
-
- buffer_json_member_add_string(wb, "config_hash_id", hash_id);
- buffer_json_member_add_string(wb, "name", rrdcalc_name(rc));
- buffer_json_member_add_string(wb, "chart", rrdcalc_chart_name(rc));
- buffer_json_member_add_string(wb, "family", (rc->rrdset) ? rrdset_family(rc->rrdset) : "");
- buffer_json_member_add_string(wb, "class", rc->classification ? rrdcalc_classification(rc) : "Unknown");