master
c 825 lines 36.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "api_v2_contexts.h"
4 #include "libnetdata/json/json-keys.h"
5
6 struct alert_counts {
7 size_t critical;
8 size_t warning;
9 size_t clear;
10 size_t error;
11 };
12
13 struct alert_v2_entry {
14 RRDCALC *tmp;
15
16 STRING *name;
17 STRING *summary;
18 RRDLABELS *recipient;
19 RRDLABELS *classification;
20 RRDLABELS *context;
21 RRDLABELS *component;
22 RRDLABELS *type;
23
24 size_t ati;
25
26 struct alert_counts counts;
27
28 size_t instances;
29 DICTIONARY *nodes;
30 DICTIONARY *configs;
31 };
32
33 struct alert_by_x_entry {
34 struct {
35 struct alert_counts counts;
36 size_t silent;
37 size_t total;
38 } running;
39
40 struct {
41 size_t available;
42 } prototypes;
43 };
44
45 bool rrdcontext_matches_alert(struct rrdcontext_to_json_v2_data *ctl, RRDCONTEXT *rc) {
46 const bool summary_requested = (ctl->request->options & CONTEXTS_OPTION_SUMMARY);
47 size_t matches = 0;
48 RRDINSTANCE *ri;
49 dfe_start_read(rc->rrdinstances, ri) {
50 if(ri->rrdset) {
51 RRDSET *st = ri->rrdset;
52 rw_spinlock_read_lock(&st->alerts.spinlock);
53 for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) {
54 if(ctl->alerts.alert_name_pattern && !simple_pattern_matches_string(ctl->alerts.alert_name_pattern, rcl->config.name))
55 continue;
56
57 if(ctl->alerts.alarm_id_filter && ctl->alerts.alarm_id_filter != (time_t)rcl->id)
58 continue;
59
60 size_t m = ctl->request->alerts.status & CONTEXTS_ALERT_STATUSES ? 0 : 1;
61
62 if (!m) {
63 if ((ctl->request->alerts.status & CONTEXT_ALERT_UNINITIALIZED) &&
64 rcl->status == RRDCALC_STATUS_UNINITIALIZED)
65 m++;
66
67 if ((ctl->request->alerts.status & CONTEXT_ALERT_UNDEFINED) &&
68 rcl->status == RRDCALC_STATUS_UNDEFINED)
69 m++;
70
71 if ((ctl->request->alerts.status & CONTEXT_ALERT_CLEAR) &&
72 rcl->status == RRDCALC_STATUS_CLEAR)
73 m++;
74
75 if ((ctl->request->alerts.status & CONTEXT_ALERT_RAISED) &&
76 rcl->status >= RRDCALC_STATUS_RAISED)
77 m++;
78
79 if ((ctl->request->alerts.status & CONTEXT_ALERT_WARNING) &&
80 rcl->status == RRDCALC_STATUS_WARNING)
81 m++;
82
83 if ((ctl->request->alerts.status & CONTEXT_ALERT_CRITICAL) &&
84 rcl->status == RRDCALC_STATUS_CRITICAL)
85 m++;
86
87 if(!m)
88 continue;
89 }
90
91 size_t ati = 0;
92 if(summary_requested) {
93 struct alert_v2_entry t = {
94 .tmp = rcl,
95 };
96 struct alert_v2_entry *a2e =
97 dictionary_set(ctl->alerts.summary, string2str(rcl->config.name),
98 &t, sizeof(struct alert_v2_entry));
99 ati = a2e->ati;
100
101 dictionary_set_advanced(ctl->alerts.by_type,
102 string2str(rcl->config.type),
103 (ssize_t)string_strlen(rcl->config.type),
104 NULL,
105 sizeof(struct alert_by_x_entry),
106 rcl);
107
108 dictionary_set_advanced(ctl->alerts.by_component,
109 string2str(rcl->config.component),
110 (ssize_t)string_strlen(rcl->config.component),
111 NULL,
112 sizeof(struct alert_by_x_entry),
113 rcl);
114
115 dictionary_set_advanced(ctl->alerts.by_classification,
116 string2str(rcl->config.classification),
117 (ssize_t)string_strlen(rcl->config.classification),
118 NULL,
119 sizeof(struct alert_by_x_entry),
120 rcl);
121
122 dictionary_set_advanced(ctl->alerts.by_recipient,
123 string2str(rcl->config.recipient),
124 (ssize_t)string_strlen(rcl->config.recipient),
125 NULL,
126 sizeof(struct alert_by_x_entry),
127 rcl);
128
129 char module[128];
130 rrdlabels_get_value_strcpyz(st->rrdlabels, module, sizeof(module), "_collect_module");
131 if(!*module)
132 strncpyz(module, "[unset]", sizeof(module) - 1);
133
134 dictionary_set_advanced(ctl->alerts.by_module,
135 module,
136 -1,
137 NULL,
138 sizeof(struct alert_by_x_entry),
139 rcl);
140 }
141
142 matches++;
143
144 if (ctl->options & (CONTEXTS_OPTION_INSTANCES | CONTEXTS_OPTION_VALUES)) {
145 char key[20 + 1];
146 snprintfz(key, sizeof(key) - 1, "%p", rcl);
147
148 struct sql_alert_instance_v2_entry z = {
149 .ati = ati,
150 .tmp = rcl,
151 };
152 dictionary_set(ctl->alerts.alert_instances, key, &z, sizeof(z));
153 }
154 }
155 rw_spinlock_read_unlock(&st->alerts.spinlock);
156 }
157 }
158 dfe_done(ri);
159
160 return matches != 0;
161 }
162
163 static void alert_counts_add(struct alert_counts *t, RRDCALC *rc) {
164 switch(rc->status) {
165 case RRDCALC_STATUS_CRITICAL:
166 t->critical++;
167 break;
168
169 case RRDCALC_STATUS_WARNING:
170 t->warning++;
171 break;
172
173 case RRDCALC_STATUS_CLEAR:
174 t->clear++;
175 break;
176
177 case RRDCALC_STATUS_REMOVED:
178 case RRDCALC_STATUS_UNINITIALIZED:
179 break;
180
181 case RRDCALC_STATUS_UNDEFINED:
182 default:
183 if(!netdata_double_isnumber(rc->value))
184 t->error++;
185
186 break;
187 }
188 }
189
190 static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) {
191 t->instances++;
192
193 alert_counts_add(&t->counts, rc);
194
195 dictionary_set(t->nodes, rc->rrdset->rrdhost->machine_guid, NULL, 0);
196
197 char key[UUID_STR_LEN + 1];
198 uuid_unparse_lower(rc->config.hash_id, key);
199 dictionary_set(t->configs, key, NULL, 0);
200 }
201
202 static STRING *silent_string = NULL;
203 void alerts_by_x_cleanup(void) {
204 string_freez(silent_string);
205 silent_string = NULL;
206 }
207
208 static void alerts_by_x_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
209 if(unlikely(!silent_string))
210 silent_string = string_strdupz("silent");
211
212 struct alert_by_x_entry *b = value;
213 RRDCALC *rc = data;
214 if(!rc) {
215 // prototype
216 b->prototypes.available++;
217 }
218 else {
219 alert_counts_add(&b->running.counts, rc);
220
221 b->running.total++;
222
223 if (rc->config.recipient == silent_string)
224 b->running.silent++;
225 }
226 }
227
228 static bool alerts_by_x_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value __maybe_unused, void *data __maybe_unused) {
229 alerts_by_x_insert_callback(item, old_value, data);
230 return false;
231 }
232
233 static void alerts_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
234 struct rrdcontext_to_json_v2_data *ctl = data;
235 struct alert_v2_entry *t = value;
236 RRDCALC *rc = t->tmp;
237 t->name = rc->config.name;
238 t->summary = rc->config.summary; // the original summary
239 t->context = rrdlabels_create();
240 t->recipient = rrdlabels_create();
241 t->classification = rrdlabels_create();
242 t->component = rrdlabels_create();
243 t->type = rrdlabels_create();
244 if (string_strlen(rc->rrdset->context))
245 rrdlabels_add(t->context, string2str(rc->rrdset->context), "yes", RRDLABEL_SRC_AUTO);
246 if (string_strlen(rc->config.recipient))
247 rrdlabels_add(t->recipient, string2str(rc->config.recipient), "yes", RRDLABEL_SRC_AUTO);
248 if (string_strlen(rc->config.classification))
249 rrdlabels_add(t->classification, string2str(rc->config.classification), "yes", RRDLABEL_SRC_AUTO);
250 if (string_strlen(rc->config.component))
251 rrdlabels_add(t->component, string2str(rc->config.component), "yes", RRDLABEL_SRC_AUTO);
252 if (string_strlen(rc->config.type))
253 rrdlabels_add(t->type, string2str(rc->config.type), "yes", RRDLABEL_SRC_AUTO);
254 t->ati = ctl->alerts.ati++;
255
256 t->nodes = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE);
257 t->configs = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE);
258
259 alerts_v2_add(t, rc);
260 }
261
262 static bool alerts_v2_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
263 struct alert_v2_entry *t = old_value, *n = new_value;
264 RRDCALC *rc = n->tmp;
265 if (string_strlen(rc->rrdset->context))
266 rrdlabels_add(t->context, string2str(rc->rrdset->context), "yes", RRDLABEL_SRC_AUTO);
267 if (string_strlen(rc->config.recipient))
268 rrdlabels_add(t->recipient, string2str(rc->config.recipient), "yes", RRDLABEL_SRC_AUTO);
269 if (string_strlen(rc->config.classification))
270 rrdlabels_add(t->classification, string2str(rc->config.classification), "yes", RRDLABEL_SRC_AUTO);
271 if (string_strlen(rc->config.component))
272 rrdlabels_add(t->component, string2str(rc->config.component), "yes", RRDLABEL_SRC_AUTO);
273 if (string_strlen(rc->config.type))
274 rrdlabels_add(t->type, string2str(rc->config.type), "yes", RRDLABEL_SRC_AUTO);
275 alerts_v2_add(t, rc);
276 return true;
277 }
278
279 static void alerts_v2_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
280 struct alert_v2_entry *t = value;
281
282 rrdlabels_destroy(t->context);
283 rrdlabels_destroy(t->recipient);
284 rrdlabels_destroy(t->classification);
285 rrdlabels_destroy(t->component);
286 rrdlabels_destroy(t->type);
287
288 dictionary_destroy(t->nodes);
289 dictionary_destroy(t->configs);
290 }
291
292 struct alert_instances_callback_data {
293 BUFFER *wb;
294 struct rrdcontext_to_json_v2_data *ctl;
295 bool debug;
296 };
297
298 static int contexts_v2_alert_instance_to_json_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
299 struct sql_alert_instance_v2_entry *t = value;
300 struct alert_instances_callback_data *d = data;
301 struct rrdcontext_to_json_v2_data *ctl = d->ctl; (void)ctl;
302 bool debug = d->debug; (void)debug;
303 BUFFER *wb = d->wb;
304
305 buffer_json_add_array_item_object(wb);
306 {
307 if(ctl->request->options & CONTEXTS_OPTION_SUMMARY)
308 buffer_json_member_add_uint64(wb, JSKEY(alerts_index_id), t->ati);
309
310 if(!(ctl->request->options & CONTEXTS_OPTION_MCP))
311 buffer_json_member_add_uint64(wb, JSKEY(node_index), t->ni);
312
313 if(ctl->request->options & CONTEXTS_OPTION_INSTANCES)
314 buffer_json_member_add_uint64(wb, JSKEY(alert_global_id), t->global_id);
315
316 buffer_json_member_add_string(wb, JSKEY(alert_name), string2str(t->name));
317
318 if(ctl->request->options & CONTEXTS_OPTION_MCP)
319 buffer_json_member_add_string(wb, JSKEY(hostname), string2str(t->host->hostname));
320
321 if(ctl->request->options & CONTEXTS_OPTION_INSTANCES)
322 buffer_json_member_add_string(wb, JSKEY(context), string2str(t->context));
323
324 if(!(ctl->request->options & CONTEXTS_OPTION_MCP))
325 buffer_json_member_add_string(wb, JSKEY(instance_id), string2str(t->chart_id));
326
327 buffer_json_member_add_string(wb, JSKEY(instance_name), string2str(t->chart_name));
328
329 if(ctl->request->options & CONTEXTS_OPTION_INSTANCES)
330 buffer_json_member_add_string(wb, JSKEY(status), rrdcalc_status2string(t->status));
331
332 if(ctl->request->options & CONTEXTS_OPTION_INSTANCES) {
333 buffer_json_member_add_string(wb, JSKEY(family), string2str(t->family));
334 buffer_json_member_add_string(wb, "info", string2str(t->info));
335 buffer_json_member_add_string(wb, JSKEY(summary), string2str(t->summary));
336 buffer_json_member_add_string(wb, "units", string2str(t->units));
337 buffer_json_member_add_uuid(wb, JSKEY(last_transition_id), t->last_transition_id);
338 buffer_json_member_add_double(wb, JSKEY(last_transition_value), t->last_status_change_value);
339 buffer_json_member_add_time_t_formatted(wb, JSKEY(last_transition_timestamp), t->last_status_change, ctl->options & CONTEXTS_OPTION_RFC3339);
340 buffer_json_member_add_uuid(wb, JSKEY(config_hash_id), t->config_hash_id);
341 buffer_json_member_add_string(wb, JSKEY(source), string2str(t->source));
342
343 buffer_json_member_add_string(wb, JSKEY(recipients), string2str(t->recipient));
344 buffer_json_member_add_string(wb, JSKEY(type), string2str(t->type));
345 buffer_json_member_add_string(wb, JSKEY(component), string2str(t->component));
346 buffer_json_member_add_string(wb, JSKEY(classification), string2str(t->classification));
347
348 // rrdcalc_flags_to_json_array (wb, "flags", t->flags);
349 }
350
351 if(ctl->request->options & CONTEXTS_OPTION_VALUES) {
352 // Netdata Cloud fetched these by querying the agents
353 buffer_json_member_add_double(wb, JSKEY(last_updated_value), t->value);
354 buffer_json_member_add_time_t_formatted(wb, JSKEY(last_updated_timestamp), t->last_updated, ctl->options & CONTEXTS_OPTION_RFC3339);
355 }
356 }
357 buffer_json_object_close(wb); // alert instance
358
359 return 1;
360 }
361
362 static void contexts_v2_alerts_by_x_update_prototypes(void *data, STRING *type, STRING *component, STRING *classification, STRING *recipient) {
363 struct rrdcontext_to_json_v2_data *ctl = data;
364
365 dictionary_set_advanced(ctl->alerts.by_type, string2str(type), (ssize_t)string_strlen(type), NULL, sizeof(struct alert_by_x_entry), NULL);
366 dictionary_set_advanced(ctl->alerts.by_component, string2str(component), (ssize_t)string_strlen(component), NULL, sizeof(struct alert_by_x_entry), NULL);
367 dictionary_set_advanced(ctl->alerts.by_classification, string2str(classification), (ssize_t)string_strlen(classification), NULL, sizeof(struct alert_by_x_entry), NULL);
368 dictionary_set_advanced(ctl->alerts.by_recipient, string2str(recipient), (ssize_t)string_strlen(recipient), NULL, sizeof(struct alert_by_x_entry), NULL);
369 }
370
371 static void contexts_v2_alerts_by_x_to_json(BUFFER *wb, DICTIONARY *dict, const char *key) {
372 buffer_json_member_add_array(wb, key);
373 {
374 struct alert_by_x_entry *b;
375 dfe_start_read(dict, b) {
376 buffer_json_add_array_item_object(wb);
377 {
378 buffer_json_member_add_string(wb, "name", b_dfe.name);
379 buffer_json_member_add_uint64(wb, JSKEY(critical), b->running.counts.critical);
380 buffer_json_member_add_uint64(wb, JSKEY(warning), b->running.counts.warning);
381 buffer_json_member_add_uint64(wb, JSKEY(clear), b->running.counts.clear);
382 buffer_json_member_add_uint64(wb, JSKEY(error), b->running.counts.error);
383 buffer_json_member_add_uint64(wb, "running", b->running.total);
384
385 buffer_json_member_add_uint64(wb, "running_silent", b->running.silent);
386
387 if(b->prototypes.available)
388 buffer_json_member_add_uint64(wb, "available", b->prototypes.available);
389 }
390 buffer_json_object_close(wb);
391 }
392 dfe_done(b);
393 }
394 buffer_json_array_close(wb);
395 }
396
397 static void contexts_v2_alert_instances_to_json(BUFFER *wb, const char *key, struct rrdcontext_to_json_v2_data *ctl, bool debug) {
398 buffer_json_member_add_array(wb, key);
399 {
400 struct alert_instances_callback_data data = {
401 .wb = wb,
402 .ctl = ctl,
403 .debug = debug,
404 };
405 dictionary_walkthrough_rw(ctl->alerts.alert_instances, DICTIONARY_LOCK_READ,
406 contexts_v2_alert_instance_to_json_callback, &data);
407 }
408 buffer_json_array_close(wb); // alerts_instances
409 }
410
411 void contexts_v2_alerts_to_json_mcp(BUFFER *wb, struct rrdcontext_to_json_v2_data *ctl, bool debug __maybe_unused) {
412 size_t cardinality_limit = ctl->request->cardinality_limit;
413
414 if(ctl->request->options & CONTEXTS_OPTION_SUMMARY) {
415 size_t alerts_count = 0;
416 size_t total_alerts = dictionary_entries(ctl->alerts.summary);
417
418 // Add header array
419 buffer_json_member_add_array(wb, "all_alerts_header");
420 {
421 buffer_json_add_array_item_string(wb, "Alert Name");
422 buffer_json_add_array_item_string(wb, "Alert Summary");
423
424 buffer_json_add_array_item_string(wb, "Metrics Contexts");
425 buffer_json_add_array_item_string(wb, "Alert Classifications");
426 buffer_json_add_array_item_string(wb, "Alert Components");
427 buffer_json_add_array_item_string(wb, "Alert Types");
428 buffer_json_add_array_item_string(wb, "Notification Recipients");
429
430 buffer_json_add_array_item_string(wb, "# of Critical Instances");
431 buffer_json_add_array_item_string(wb, "# of Warning Instances");
432 buffer_json_add_array_item_string(wb, "# of Clear Instances");
433 buffer_json_add_array_item_string(wb, "# of Error Instances");
434 buffer_json_add_array_item_string(wb, "# of Instances Watched");
435 buffer_json_add_array_item_string(wb, "# of Nodes Watched");
436 buffer_json_add_array_item_string(wb, "# of Alert Configurations");
437 }
438 buffer_json_array_close(wb); // header
439
440 // Add data array
441 buffer_json_member_add_array(wb, "all_alerts");
442 struct alert_v2_entry *t;
443 dfe_start_read(ctl->alerts.summary, t)
444 {
445 // Apply cardinality limit for summary
446 if(cardinality_limit && alerts_count >= cardinality_limit)
447 break;
448
449 // Start row array
450 buffer_json_add_array_item_array(wb);
451 {
452 // Add values in same order as header
453 buffer_json_add_array_item_string(wb, string2str(t->name));
454 buffer_json_add_array_item_string(wb, string2str(t->summary));
455
456 rrdlabels_key_to_buffer_array_or_string_or_null(t->context, wb);
457 rrdlabels_key_to_buffer_array_or_string_or_null(t->classification, wb);
458 rrdlabels_key_to_buffer_array_or_string_or_null(t->component, wb);
459 rrdlabels_key_to_buffer_array_or_string_or_null(t->type, wb);
460 rrdlabels_key_to_buffer_array_or_string_or_null(t->recipient, wb);
461
462 buffer_json_add_array_item_uint64(wb, t->counts.critical);
463 buffer_json_add_array_item_uint64(wb, t->counts.warning);
464 buffer_json_add_array_item_uint64(wb, t->counts.clear);
465 buffer_json_add_array_item_uint64(wb, t->counts.error);
466 buffer_json_add_array_item_uint64(wb, t->instances);
467 buffer_json_add_array_item_uint64(wb, dictionary_entries(t->nodes));
468 buffer_json_add_array_item_uint64(wb, dictionary_entries(t->configs));
469 }
470 buffer_json_array_close(wb);
471 alerts_count++;
472 }
473 dfe_done(t);
474 buffer_json_array_close(wb); // all_alerts
475
476 // Add info about truncation if needed
477 if(cardinality_limit && total_alerts > cardinality_limit) {
478 buffer_json_member_add_object(wb, "__all_alerts_info__");
479 buffer_json_member_add_string(wb, "status", "truncated");
480 buffer_json_member_add_uint64(wb, "total_alerts", total_alerts);
481 buffer_json_member_add_uint64(wb, "shown_alerts", alerts_count);
482 buffer_json_member_add_uint64(wb, "cardinality_limit", cardinality_limit);
483 buffer_json_object_close(wb);
484 }
485 }
486
487 if(ctl->request->options & (CONTEXTS_OPTION_INSTANCES | CONTEXTS_OPTION_VALUES)) {
488 size_t instances_count = 0;
489 size_t total_instances = dictionary_entries(ctl->alerts.alert_instances);
490
491 // Add header array for alert instances
492 buffer_json_member_add_array(wb, "alert_instances_header");
493 {
494 buffer_json_add_array_item_string(wb, "Alert Name");
495 buffer_json_add_array_item_string(wb, "Hostname");
496
497 if(ctl->request->options & CONTEXTS_OPTION_INSTANCES)
498 buffer_json_add_array_item_string(wb, "Context");
499
500 buffer_json_add_array_item_string(wb, "Instance Name");
501
502 if(ctl->request->options & CONTEXTS_OPTION_INSTANCES) {
503 buffer_json_add_array_item_string(wb, "Status");
504 buffer_json_add_array_item_string(wb, "Family");
505 buffer_json_add_array_item_string(wb, "Info");
506 buffer_json_add_array_item_string(wb, "Summary");
507 buffer_json_add_array_item_string(wb, "Units");
508 buffer_json_add_array_item_string(wb, "Last Transition ID");
509 buffer_json_add_array_item_string(wb, "Last Transition Value");
510 buffer_json_add_array_item_string(wb, "Last Transition Timestamp");
511 buffer_json_add_array_item_string(wb, "Configuration Hash");
512 buffer_json_add_array_item_string(wb, "Source");
513 buffer_json_add_array_item_string(wb, "Recipients");
514 buffer_json_add_array_item_string(wb, "Type");
515 buffer_json_add_array_item_string(wb, "Component");
516 buffer_json_add_array_item_string(wb, "Classification");
517 }
518
519 if(ctl->request->options & CONTEXTS_OPTION_VALUES) {
520 buffer_json_add_array_item_string(wb, "Last Updated Value");
521 buffer_json_add_array_item_string(wb, "Last Updated Timestamp");
522 }
523 }
524 buffer_json_array_close(wb); // alert_instances_header
525
526 // Add data array for alert instances
527 buffer_json_member_add_array(wb, "alert_instances");
528 {
529 struct sql_alert_instance_v2_entry *t;
530 dfe_start_read(ctl->alerts.alert_instances, t)
531 {
532 // Apply cardinality limit for instances
533 if(cardinality_limit && instances_count >= cardinality_limit)
534 break;
535
536 // Start row array
537 buffer_json_add_array_item_array(wb);
538 {
539 buffer_json_add_array_item_string(wb, string2str(t->name));
540 buffer_json_add_array_item_string(wb, string2str(t->host->hostname));
541
542 if(ctl->request->options & CONTEXTS_OPTION_INSTANCES)
543 buffer_json_add_array_item_string(wb, string2str(t->context));
544
545 buffer_json_add_array_item_string(wb, string2str(t->chart_name));
546
547 if(ctl->request->options & CONTEXTS_OPTION_INSTANCES) {
548 buffer_json_add_array_item_string(wb, rrdcalc_status2string(t->status));
549 buffer_json_add_array_item_string(wb, string2str(t->family));
550 buffer_json_add_array_item_string(wb, string2str(t->info));
551 buffer_json_add_array_item_string(wb, string2str(t->summary));
552 buffer_json_add_array_item_string(wb, string2str(t->units));
553
554 // Add UUID as string
555 char uuid_str[UUID_STR_LEN];
556 uuid_unparse_lower(t->last_transition_id, uuid_str);
557 buffer_json_add_array_item_string(wb, uuid_str);
558
559 buffer_json_add_array_item_double(wb, t->last_status_change_value);
560 buffer_json_add_array_item_time_t_formatted(wb, t->last_status_change, ctl->options & CONTEXTS_OPTION_RFC3339);
561
562 // Add config hash UUID as string
563 uuid_unparse_lower(t->config_hash_id, uuid_str);
564 buffer_json_add_array_item_string(wb, uuid_str);
565
566 buffer_json_add_array_item_string(wb, string2str(t->source));
567 buffer_json_add_array_item_string(wb, string2str(t->recipient));
568 buffer_json_add_array_item_string(wb, string2str(t->type));
569 buffer_json_add_array_item_string(wb, string2str(t->component));
570 buffer_json_add_array_item_string(wb, string2str(t->classification));
571 }
572
573 if(ctl->request->options & CONTEXTS_OPTION_VALUES) {
574 buffer_json_add_array_item_double(wb, t->value);
575 buffer_json_add_array_item_time_t_formatted(wb, t->last_updated, ctl->options & CONTEXTS_OPTION_RFC3339);
576 }
577 }
578 buffer_json_array_close(wb); // row
579 instances_count++;
580 }
581 dfe_done(t);
582 }
583 buffer_json_array_close(wb); // alert_instances
584
585 // Add info about truncation if needed
586 if(cardinality_limit && total_instances > cardinality_limit) {
587 buffer_json_member_add_object(wb, "__alert_instances_info__");
588 buffer_json_member_add_string(wb, "status", "truncated");
589 buffer_json_member_add_uint64(wb, "total_instances", total_instances);
590 buffer_json_member_add_uint64(wb, "shown_instances", instances_count);
591 buffer_json_member_add_uint64(wb, "cardinality_limit", cardinality_limit);
592 buffer_json_object_close(wb);
593 }
594 }
595
596 }
597
598 void contexts_v2_alerts_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_data *ctl, bool debug) {
599 if(ctl->request->options & CONTEXTS_OPTION_MCP) {
600 contexts_v2_alerts_to_json_mcp(wb, ctl, debug);
601 return;
602 }
603
604 if(ctl->request->options & CONTEXTS_OPTION_SUMMARY) {
605 buffer_json_member_add_array(wb, "alerts");
606 {
607 struct alert_v2_entry *t;
608 dfe_start_read(ctl->alerts.summary, t)
609 {
610 buffer_json_add_array_item_object(wb);
611 {
612 buffer_json_member_add_uint64(wb, JSKEY(alerts_index_id), t->ati);
613
614 buffer_json_member_add_array(wb, JSKEY(node_index));
615 void *host_guid;
616 dfe_start_read(t->nodes, host_guid) {
617 struct contexts_v2_node *cn = dictionary_get(ctl->nodes.dict, host_guid_dfe.name);
618 buffer_json_add_array_item_int64(wb, (int64_t) cn->ni);
619 }
620 dfe_done(host_guid);
621 buffer_json_array_close(wb);
622
623 buffer_json_member_add_string(wb, JSKEY(alert_name), string2str(t->name));
624 buffer_json_member_add_string(wb, JSKEY(summary), string2str(t->summary));
625
626 buffer_json_member_add_uint64(wb, JSKEY(critical), t->counts.critical);
627 buffer_json_member_add_uint64(wb, JSKEY(warning), t->counts.warning);
628 buffer_json_member_add_uint64(wb, JSKEY(clear), t->counts.clear);
629 buffer_json_member_add_uint64(wb, JSKEY(error), t->counts.error);
630
631 buffer_json_member_add_uint64(wb, JSKEY(instances_count), t->instances);
632 buffer_json_member_add_uint64(wb, JSKEY(nodes_count), dictionary_entries(t->nodes));
633 buffer_json_member_add_uint64(wb, JSKEY(configurations_count), dictionary_entries(t->configs));
634
635 buffer_json_member_add_array(wb, JSKEY(contexts));
636 rrdlabels_key_to_buffer_array_item(t->context, wb);
637 buffer_json_array_close(wb); // contexts
638
639 buffer_json_member_add_array(wb, JSKEY(classifications));
640 rrdlabels_key_to_buffer_array_item(t->classification, wb);
641 buffer_json_array_close(wb); // classifications
642
643
644 buffer_json_member_add_array(wb, JSKEY(components));
645 rrdlabels_key_to_buffer_array_item(t->component, wb);
646 buffer_json_array_close(wb); // components
647
648 buffer_json_member_add_array(wb, JSKEY(types));
649 rrdlabels_key_to_buffer_array_item(t->type, wb);
650 buffer_json_array_close(wb); // types
651
652 buffer_json_member_add_array(wb, JSKEY(recipients));
653 rrdlabels_key_to_buffer_array_item(t->recipient, wb);
654 buffer_json_array_close(wb); // recipients
655 }
656 buffer_json_object_close(wb); // alert name
657 }
658 dfe_done(t);
659 }
660 buffer_json_array_close(wb); // alerts
661
662 health_prototype_metadata_foreach(ctl, contexts_v2_alerts_by_x_update_prototypes);
663 contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_type, "alerts_by_type");
664 contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_component, "alerts_by_component");
665 contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_classification, "alerts_by_classification");
666 contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_recipient, "alerts_by_recipient");
667 contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_module, "alerts_by_module");
668 }
669
670 if(ctl->request->options & (CONTEXTS_OPTION_INSTANCES | CONTEXTS_OPTION_VALUES)) {
671 contexts_v2_alert_instances_to_json(wb, "alert_instances", ctl, debug);
672 }
673 }
674
675 static void alert_instances_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
676 struct rrdcontext_to_json_v2_data *ctl = data;
677 struct sql_alert_instance_v2_entry *t = value;
678 RRDCALC *rc = t->tmp;
679
680 t->context = rc->rrdset->context;
681 t->chart_id = rc->rrdset->id;
682 t->chart_name = rc->rrdset->name;
683 t->family = rc->rrdset->family;
684 t->units = rc->config.units;
685 t->classification = rc->config.classification;
686 t->type = rc->config.type;
687 t->recipient = rc->config.recipient;
688 t->component = rc->config.component;
689 t->name = rc->config.name;
690 t->source = rc->config.source;
691 t->status = rc->status;
692 t->flags = rc->run_flags;
693 t->info = rc->config.info;
694 t->summary = rc->summary;
695 t->value = rc->value;
696 t->last_updated = rc->last_updated;
697 t->last_status_change = rc->last_status_change;
698 t->last_status_change_value = rc->last_status_change_value;
699 t->host = rc->rrdset->rrdhost;
700 t->alarm_id = rc->id;
701 t->ni = ctl->nodes.ni;
702
703 uuid_copy(t->config_hash_id, rc->config.hash_id);
704 health_alarm_log_get_global_id_and_transition_id_for_rrdcalc(rc, &t->global_id, &t->last_transition_id);
705 }
706
707 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) {
708 internal_fatal(true, "This should never happen!");
709 return true;
710 }
711
712 static void alert_instances_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) {
713 ;
714 }
715
716 static void rrdcontext_v2_set_transition_filter(const char *machine_guid, const char *context, time_t alarm_id, void *data) {
717 struct rrdcontext_to_json_v2_data *ctl = data;
718
719 if(machine_guid && *machine_guid) {
720 if(ctl->nodes.scope_pattern)
721 simple_pattern_free(ctl->nodes.scope_pattern);
722
723 if(ctl->nodes.pattern)
724 simple_pattern_free(ctl->nodes.pattern);
725
726 ctl->nodes.scope_pattern = string_to_simple_pattern(machine_guid);
727 ctl->nodes.pattern = NULL;
728 }
729
730 if(context && *context) {
731 if(ctl->contexts.scope_pattern)
732 simple_pattern_free(ctl->contexts.scope_pattern);
733
734 if(ctl->contexts.pattern)
735 simple_pattern_free(ctl->contexts.pattern);
736
737 ctl->contexts.scope_pattern = string_to_simple_pattern(context);
738 ctl->contexts.pattern = NULL;
739 }
740
741 ctl->alerts.alarm_id_filter = alarm_id;
742 }
743
744 bool rrdcontexts_v2_init_alert_dictionaries(struct rrdcontext_to_json_v2_data *ctl, struct api_v2_contexts_request *req) {
745 const bool summary_requested = (req->options & CONTEXTS_OPTION_SUMMARY);
746
747 if(req->alerts.transition) {
748 ctl->options |= CONTEXTS_OPTION_INSTANCES | CONTEXTS_OPTION_VALUES;
749 if(!sql_find_alert_transition(req->alerts.transition, rrdcontext_v2_set_transition_filter, ctl))
750 return false;
751 }
752
753 if(summary_requested) {
754 ctl->alerts.summary = dictionary_create_advanced(
755 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
756 NULL,
757 sizeof(struct alert_v2_entry));
758
759 dictionary_register_insert_callback(ctl->alerts.summary, alerts_v2_insert_callback, ctl);
760 dictionary_register_conflict_callback(ctl->alerts.summary, alerts_v2_conflict_callback, ctl);
761 dictionary_register_delete_callback(ctl->alerts.summary, alerts_v2_delete_callback, ctl);
762
763 ctl->alerts.by_type = dictionary_create_advanced(
764 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
765 NULL,
766 sizeof(struct alert_by_x_entry));
767
768 dictionary_register_insert_callback(ctl->alerts.by_type, alerts_by_x_insert_callback, NULL);
769 dictionary_register_conflict_callback(ctl->alerts.by_type, alerts_by_x_conflict_callback, NULL);
770
771 ctl->alerts.by_component = dictionary_create_advanced(
772 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
773 NULL,
774 sizeof(struct alert_by_x_entry));
775
776 dictionary_register_insert_callback(ctl->alerts.by_component, alerts_by_x_insert_callback, NULL);
777 dictionary_register_conflict_callback(ctl->alerts.by_component, alerts_by_x_conflict_callback, NULL);
778
779 ctl->alerts.by_classification = dictionary_create_advanced(
780 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
781 NULL,
782 sizeof(struct alert_by_x_entry));
783
784 dictionary_register_insert_callback(ctl->alerts.by_classification, alerts_by_x_insert_callback, NULL);
785 dictionary_register_conflict_callback(ctl->alerts.by_classification, alerts_by_x_conflict_callback, NULL);
786
787 ctl->alerts.by_recipient = dictionary_create_advanced(
788 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
789 NULL,
790 sizeof(struct alert_by_x_entry));
791
792 dictionary_register_insert_callback(ctl->alerts.by_recipient, alerts_by_x_insert_callback, NULL);
793 dictionary_register_conflict_callback(ctl->alerts.by_recipient, alerts_by_x_conflict_callback, NULL);
794
795 ctl->alerts.by_module = dictionary_create_advanced(
796 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
797 NULL,
798 sizeof(struct alert_by_x_entry));
799
800 dictionary_register_insert_callback(ctl->alerts.by_module, alerts_by_x_insert_callback, NULL);
801 dictionary_register_conflict_callback(ctl->alerts.by_module, alerts_by_x_conflict_callback, NULL);
802 }
803
804 if(ctl->options & (CONTEXTS_OPTION_INSTANCES | CONTEXTS_OPTION_VALUES)) {
805 ctl->alerts.alert_instances = dictionary_create_advanced(
806 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
807 NULL, sizeof(struct sql_alert_instance_v2_entry));
808
809 dictionary_register_insert_callback(ctl->alerts.alert_instances, alert_instances_v2_insert_callback, ctl);
810 dictionary_register_conflict_callback(ctl->alerts.alert_instances, alert_instances_v2_conflict_callback, ctl);
811 dictionary_register_delete_callback(ctl->alerts.alert_instances, alert_instances_delete_callback, ctl);
812 }
813
814 return true;
815 }
816
817 void rrdcontexts_v2_alerts_cleanup(struct rrdcontext_to_json_v2_data *ctl) {
818 dictionary_destroy(ctl->alerts.summary);
819 dictionary_destroy(ctl->alerts.alert_instances);
820 dictionary_destroy(ctl->alerts.by_type);
821 dictionary_destroy(ctl->alerts.by_component);
822 dictionary_destroy(ctl->alerts.by_classification);
823 dictionary_destroy(ctl->alerts.by_recipient);
824 dictionary_destroy(ctl->alerts.by_module);
825 }