diff options
author | Costa Tsaousis <costa@netdata.cloud> | 2023-06-28 23:14:10 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-28 23:14:10 +0300 |
commit | 5be9be74854d00879e6d52d6432ae12b5e8558cd (patch) | |
tree | 7d2c3e28db5aa1306cc480a45f1bfdd17b3c8375 /database | |
parent | 26ec3b119efb9271f622047259866a6083ed6269 (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.c | 1307 | ||||
-rw-r--r-- | database/contexts/rrdcontext.h | 84 | ||||
-rw-r--r-- | database/rrdcalc.c | 30 | ||||
-rw-r--r-- | database/rrdcalc.h | 3 | ||||
-rw-r--r-- | database/sqlite/sqlite_health.c | 281 | ||||
-rw-r--r-- | database/sqlite/sqlite_health.h | 4 |
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"); |