1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-#include "internal.h"
4
-
5
-#include "aclk/aclk_capas.h"
6
-
7
-// ----------------------------------------------------------------------------
8
-// /api/v2/contexts API
9
-
10
-struct alert_transitions_facets alert_transition_facets[] = {
11
- [ATF_STATUS] = {
12
- .id = "f_status",
13
- .name = "Alert Status",
14
- .query_param = "f_status",
15
- .order = 1,
16
- },
17
- [ATF_TYPE] = {
18
- .id = "f_type",
19
- .name = "Alert Type",
20
- .query_param = "f_type",
21
- .order = 2,
22
- },
23
- [ATF_ROLE] = {
24
- .id = "f_role",
25
- .name = "Recipient Role",
26
- .query_param = "f_role",
27
- .order = 3,
28
- },
29
- [ATF_CLASS] = {
30
- .id = "f_class",
31
- .name = "Alert Class",
32
- .query_param = "f_class",
33
- .order = 4,
34
- },
35
- [ATF_COMPONENT] = {
36
- .id = "f_component",
37
- .name = "Alert Component",
38
- .query_param = "f_component",
39
- .order = 5,
40
- },
41
- [ATF_NODE] = {
42
- .id = "f_node",
43
- .name = "Alert Node",
44
- .query_param = "f_node",
45
- .order = 6,
46
- },
47
- [ATF_ALERT_NAME] = {
48
- .id = "f_alert",
49
- .name = "Alert Name",
50
- .query_param = "f_alert",
51
- .order = 7,
52
- },
53
- [ATF_CHART_NAME] = {
54
- .id = "f_instance",
55
- .name = "Instance Name",
56
- .query_param = "f_instance",
57
- .order = 8,
58
- },
59
- [ATF_CONTEXT] = {
60
- .id = "f_context",
61
- .name = "Context",
62
- .query_param = "f_context",
63
- .order = 9,
64
- },
65
-
66
- // terminator
67
- [ATF_TOTAL_ENTRIES] = {
68
- .id = NULL,
69
- .name = NULL,
70
- .query_param = NULL,
71
- .order = 9999,
72
- }
73
-};
74
-
75
-struct facet_entry {
76
- uint32_t count;
77
-};
78
-
79
-struct alert_transitions_callback_data {
80
- struct rrdcontext_to_json_v2_data *ctl;
81
- BUFFER *wb;
82
- bool debug;
83
- bool only_one_config;
84
-
85
- struct {
86
- SIMPLE_PATTERN *pattern;
87
- DICTIONARY *dict;
88
- } facets[ATF_TOTAL_ENTRIES];
89
-
90
- uint32_t max_items_to_return;
91
- uint32_t items_to_return;
92
-
93
- uint32_t items_evaluated;
94
- uint32_t items_matched;
95
-
96
-
97
- struct sql_alert_transition_fixed_size *base; // double linked list - last item is base->prev
98
- struct sql_alert_transition_fixed_size *last_added; // the last item added, not the last of the list
99
-
100
- struct {
101
- size_t first;
102
- size_t skips_before;
103
- size_t skips_after;
104
- size_t backwards;
105
- size_t forwards;
106
- size_t prepend;
107
- size_t append;
108
- size_t shifts;
109
- } operations;
110
-
111
- uint32_t configs_added;
112
-};
113
-
114
-typedef enum __attribute__ ((__packed__)) {
115
- FTS_MATCHED_NONE = 0,
116
- FTS_MATCHED_HOST,
117
- FTS_MATCHED_CONTEXT,
118
- FTS_MATCHED_INSTANCE,
119
- FTS_MATCHED_DIMENSION,
120
- FTS_MATCHED_LABEL,
121
- FTS_MATCHED_ALERT,
122
- FTS_MATCHED_ALERT_INFO,
123
- FTS_MATCHED_FAMILY,
124
- FTS_MATCHED_TITLE,
125
- FTS_MATCHED_UNITS,
126
-} FTS_MATCH;
127
-
128
-static const char *fts_match_to_string(FTS_MATCH match) {
129
- switch(match) {
130
- case FTS_MATCHED_HOST:
131
- return "HOST";
132
-
133
- case FTS_MATCHED_CONTEXT:
134
- return "CONTEXT";
135
-
136
- case FTS_MATCHED_INSTANCE:
137
- return "INSTANCE";
138
-
139
- case FTS_MATCHED_DIMENSION:
140
- return "DIMENSION";
141
-
142
- case FTS_MATCHED_ALERT:
143
- return "ALERT";
144
-
145
- case FTS_MATCHED_ALERT_INFO:
146
- return "ALERT_INFO";
147
-
148
- case FTS_MATCHED_LABEL:
149
- return "LABEL";
150
-
151
- case FTS_MATCHED_FAMILY:
152
- return "FAMILY";
153
-
154
- case FTS_MATCHED_TITLE:
155
- return "TITLE";
156
-
157
- case FTS_MATCHED_UNITS:
158
- return "UNITS";
159
-
160
- default:
161
- return "NONE";
162
- }
163
-}
164
-
165
-struct function_v2_entry {
166
- size_t size;
167
- size_t used;
168
- size_t *node_ids;
169
- STRING *help;
170
- STRING *tags;
171
- HTTP_ACCESS access;
172
- int priority;
173
-};
174
-
175
-struct context_v2_entry {
176
- size_t count;
177
- STRING *id;
178
- STRING *family;
179
- uint32_t priority;
180
- time_t first_time_s;
181
- time_t last_time_s;
182
- RRD_FLAGS flags;
183
- FTS_MATCH match;
184
-};
185
-
186
-struct alert_counts {
187
- size_t critical;
188
- size_t warning;
189
- size_t clear;
190
- size_t error;
191
-};
192
-
193
-struct alert_v2_entry {
194
- RRDCALC *tmp;
195
-
196
- STRING *name;
197
- STRING *summary;
198
- RRDLABELS *recipient;
199
- RRDLABELS *classification;
200
- RRDLABELS *context;
201
- RRDLABELS *component;
202
- RRDLABELS *type;
203
-
204
- size_t ati;
205
-
206
- struct alert_counts counts;
207
-
208
- size_t instances;
209
- DICTIONARY *nodes;
210
- DICTIONARY *configs;
211
-};
212
-
213
-struct alert_by_x_entry {
214
- struct {
215
- struct alert_counts counts;
216
- size_t silent;
217
- size_t total;
218
- } running;
219
-
220
- struct {
221
- size_t available;
222
- } prototypes;
223
-};
224
-
225
-typedef struct full_text_search_index {
226
- size_t searches;
227
- size_t string_searches;
228
- size_t char_searches;
229
-} FTS_INDEX;
230
-
231
-static inline bool full_text_search_string(FTS_INDEX *fts, SIMPLE_PATTERN *q, STRING *ptr) {
232
- fts->searches++;
233
- fts->string_searches++;
234
- return simple_pattern_matches_string(q, ptr);
235
-}
236
-
237
-static inline bool full_text_search_char(FTS_INDEX *fts, SIMPLE_PATTERN *q, char *ptr) {
238
- fts->searches++;
239
- fts->char_searches++;
240
- return simple_pattern_matches(q, ptr);
241
-}
242
-
243
-struct contexts_v2_node {
244
- size_t ni;
245
- RRDHOST *host;
246
-};
247
-
248
-struct rrdcontext_to_json_v2_data {
249
- time_t now;
250
-
251
- BUFFER *wb;
252
- struct api_v2_contexts_request *request;
253
-
254
- CONTEXTS_V2_MODE mode;
255
- CONTEXTS_V2_OPTIONS options;
256
- struct query_versions versions;
257
-
258
- struct {
259
- SIMPLE_PATTERN *scope_pattern;
260
- SIMPLE_PATTERN *pattern;
261
- size_t ni;
262
- DICTIONARY *dict; // the result set
263
- } nodes;
264
-
265
- struct {
266
- SIMPLE_PATTERN *scope_pattern;
267
- SIMPLE_PATTERN *pattern;
268
- size_t ci;
269
- DICTIONARY *dict; // the result set
270
- } contexts;
271
-
272
- struct {
273
- SIMPLE_PATTERN *alert_name_pattern;
274
- time_t alarm_id_filter;
275
-
276
- size_t ati;
277
-
278
- DICTIONARY *summary;
279
- DICTIONARY *alert_instances;
280
-
281
- DICTIONARY *by_type;
282
- DICTIONARY *by_component;
283
- DICTIONARY *by_classification;
284
- DICTIONARY *by_recipient;
285
- DICTIONARY *by_module;
286
- } alerts;
287
-
288
- struct {
289
- FTS_MATCH host_match;
290
- char host_node_id_str[UUID_STR_LEN];
291
- SIMPLE_PATTERN *pattern;
292
- FTS_INDEX fts;
293
- } q;
294
-
295
- struct {
296
- DICTIONARY *dict; // the result set
297
- } functions;
298
-
299
- struct {
300
- bool enabled;
301
- bool relative;
302
- time_t after;
303
- time_t before;
304
- } window;
305
-
306
- struct query_timings timings;
307
-};
308
-
309
-static void alert_counts_add(struct alert_counts *t, RRDCALC *rc) {
310
- switch(rc->status) {
311
- case RRDCALC_STATUS_CRITICAL:
312
- t->critical++;
313
- break;
314
-
315
- case RRDCALC_STATUS_WARNING:
316
- t->warning++;
317
- break;
318
-
319
- case RRDCALC_STATUS_CLEAR:
320
- t->clear++;
321
- break;
322
-
323
- case RRDCALC_STATUS_REMOVED:
324
- case RRDCALC_STATUS_UNINITIALIZED:
325
- break;
326
-
327
- case RRDCALC_STATUS_UNDEFINED:
328
- default:
329
- if(!netdata_double_isnumber(rc->value))
330
- t->error++;
331
-
332
- break;
333
- }
334
-}
335
-
336
-static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) {
337
- t->instances++;
338
-
339
- alert_counts_add(&t->counts, rc);
340
-
341
- dictionary_set(t->nodes, rc->rrdset->rrdhost->machine_guid, NULL, 0);
342
-
343
- char key[UUID_STR_LEN + 1];
344
- uuid_unparse_lower(rc->config.hash_id, key);
345
- dictionary_set(t->configs, key, NULL, 0);
346
-}
347
-
348
-static void alerts_by_x_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
349
- static STRING *silent = NULL;
350
- if(unlikely(!silent)) silent = string_strdupz("silent");
351
-
352
- struct alert_by_x_entry *b = value;
353
- RRDCALC *rc = data;
354
- if(!rc) {
355
- // prototype
356
- b->prototypes.available++;
357
- }
358
- else {
359
- alert_counts_add(&b->running.counts, rc);
360
-
361
- b->running.total++;
362
-
363
- if (rc->config.recipient == silent)
364
- b->running.silent++;
365
- }
366
-}
367
-
368
-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) {
369
- alerts_by_x_insert_callback(item, old_value, data);
370
- return false;
371
-}
372
-
373
-static void alerts_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
374
- struct rrdcontext_to_json_v2_data *ctl = data;
375
- struct alert_v2_entry *t = value;
376
- RRDCALC *rc = t->tmp;
377
- t->name = rc->config.name;
378
- t->summary = rc->config.summary; // the original summary
379
- t->context = rrdlabels_create();
380
- t->recipient = rrdlabels_create();
381
- t->classification = rrdlabels_create();
382
- t->component = rrdlabels_create();
383
- t->type = rrdlabels_create();
384
- if (string_strlen(rc->rrdset->context))
385
- rrdlabels_add(t->context, string2str(rc->rrdset->context), "yes", RRDLABEL_SRC_AUTO);
386
- if (string_strlen(rc->config.recipient))
387
- rrdlabels_add(t->recipient, string2str(rc->config.recipient), "yes", RRDLABEL_SRC_AUTO);
388
- if (string_strlen(rc->config.classification))
389
- rrdlabels_add(t->classification, string2str(rc->config.classification), "yes", RRDLABEL_SRC_AUTO);
390
- if (string_strlen(rc->config.component))
391
- rrdlabels_add(t->component, string2str(rc->config.component), "yes", RRDLABEL_SRC_AUTO);
392
- if (string_strlen(rc->config.type))
393
- rrdlabels_add(t->type, string2str(rc->config.type), "yes", RRDLABEL_SRC_AUTO);
394
- t->ati = ctl->alerts.ati++;
395
-
396
- t->nodes = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE);
397
- t->configs = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE);
398
-
399
- alerts_v2_add(t, rc);
400
-}
401
-
402
-static bool alerts_v2_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
403
- struct alert_v2_entry *t = old_value, *n = new_value;
404
- RRDCALC *rc = n->tmp;
405
- if (string_strlen(rc->rrdset->context))
406
- rrdlabels_add(t->context, string2str(rc->rrdset->context), "yes", RRDLABEL_SRC_AUTO);
407
- if (string_strlen(rc->config.recipient))
408
- rrdlabels_add(t->recipient, string2str(rc->config.recipient), "yes", RRDLABEL_SRC_AUTO);
409
- if (string_strlen(rc->config.classification))
410
- rrdlabels_add(t->classification, string2str(rc->config.classification), "yes", RRDLABEL_SRC_AUTO);
411
- if (string_strlen(rc->config.component))
412
- rrdlabels_add(t->component, string2str(rc->config.component), "yes", RRDLABEL_SRC_AUTO);
413
- if (string_strlen(rc->config.type))
414
- rrdlabels_add(t->type, string2str(rc->config.type), "yes", RRDLABEL_SRC_AUTO);
415
- alerts_v2_add(t, rc);
416
- return true;
417
-}
418
-
419
-static void alerts_v2_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
420
- struct alert_v2_entry *t = value;
421
-
422
- rrdlabels_destroy(t->context);
423
- rrdlabels_destroy(t->recipient);
424
- rrdlabels_destroy(t->classification);
425
- rrdlabels_destroy(t->component);
426
- rrdlabels_destroy(t->type);
427
-
428
- dictionary_destroy(t->nodes);
429
- dictionary_destroy(t->configs);
430
-}
431
-
432
-static void alert_instances_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
433
- struct rrdcontext_to_json_v2_data *ctl = data;
434
- struct sql_alert_instance_v2_entry *t = value;
435
- RRDCALC *rc = t->tmp;
436
-
437
- t->context = rc->rrdset->context;
438
- t->chart_id = rc->rrdset->id;
439
- t->chart_name = rc->rrdset->name;
440
- t->family = rc->rrdset->family;
441
- t->units = rc->config.units;
442
- t->classification = rc->config.classification;
443
- t->type = rc->config.type;
444
- t->recipient = rc->config.recipient;
445
- t->component = rc->config.component;
446
- t->name = rc->config.name;
447
- t->source = rc->config.source;
448
- t->status = rc->status;
449
- t->flags = rc->run_flags;
450
- t->info = rc->config.info;
451
- t->summary = rc->summary;
452
- t->value = rc->value;
453
- t->last_updated = rc->last_updated;
454
- t->last_status_change = rc->last_status_change;
455
- t->last_status_change_value = rc->last_status_change_value;
456
- t->host = rc->rrdset->rrdhost;
457
- t->alarm_id = rc->id;
458
- t->ni = ctl->nodes.ni;
459
-
460
- uuid_copy(t->config_hash_id, rc->config.hash_id);
461
- health_alarm_log_get_global_id_and_transition_id_for_rrdcalc(rc, &t->global_id, &t->last_transition_id);
462
-}
463
-
464
-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) {
465
- internal_fatal(true, "This should never happen!");
466
- return true;
467
-}
468
-
469
-static void alert_instances_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) {
470
- ;
471
-}
472
-
473
-static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_json_v2_data *ctl, RRDCONTEXT *rc, SIMPLE_PATTERN *q) {
474
- if(unlikely(full_text_search_string(&ctl->q.fts, q, rc->id) ||
475
- full_text_search_string(&ctl->q.fts, q, rc->family)))
476
- return FTS_MATCHED_CONTEXT;
477
-
478
- if(unlikely(full_text_search_string(&ctl->q.fts, q, rc->title)))
479
- return FTS_MATCHED_TITLE;
480
-
481
- if(unlikely(full_text_search_string(&ctl->q.fts, q, rc->units)))
482
- return FTS_MATCHED_UNITS;
483
-
484
- FTS_MATCH matched = FTS_MATCHED_NONE;
485
- RRDINSTANCE *ri;
486
- dfe_start_read(rc->rrdinstances, ri) {
487
- if(matched) break;
488
-
489
- if(ctl->window.enabled && !query_matches_retention(ctl->window.after, ctl->window.before, ri->first_time_s, (ri->flags & RRD_FLAG_COLLECTED) ? ctl->now : ri->last_time_s, 0))
490
- continue;
491
-
492
- if(unlikely(full_text_search_string(&ctl->q.fts, q, ri->id)) ||
493
- (ri->name != ri->id && full_text_search_string(&ctl->q.fts, q, ri->name))) {
494
- matched = FTS_MATCHED_INSTANCE;
495
- break;
496
- }
497
-
498
- RRDMETRIC *rm;
499
- dfe_start_read(ri->rrdmetrics, rm) {
500
- if(ctl->window.enabled && !query_matches_retention(ctl->window.after, ctl->window.before, rm->first_time_s, (rm->flags & RRD_FLAG_COLLECTED) ? ctl->now : rm->last_time_s, 0))
501
- continue;
502
-
503
- if(unlikely(full_text_search_string(&ctl->q.fts, q, rm->id)) ||
504
- (rm->name != rm->id && full_text_search_string(&ctl->q.fts, q, rm->name))) {
505
- matched = FTS_MATCHED_DIMENSION;
506
- break;
507
- }
508
- }
509
- dfe_done(rm);
510
-
511
- size_t label_searches = 0;
512
- if(unlikely(ri->rrdlabels && rrdlabels_entries(ri->rrdlabels) &&
513
- rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, q, ':', &label_searches) == SP_MATCHED_POSITIVE)) {
514
- ctl->q.fts.searches += label_searches;
515
- ctl->q.fts.char_searches += label_searches;
516
- matched = FTS_MATCHED_LABEL;
517
- break;
518
- }
519
- ctl->q.fts.searches += label_searches;
520
- ctl->q.fts.char_searches += label_searches;
521
-
522
- if(ri->rrdset) {
523
- RRDSET *st = ri->rrdset;
524
- rw_spinlock_read_lock(&st->alerts.spinlock);
525
- for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) {
526
- if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->config.name))) {
527
- matched = FTS_MATCHED_ALERT;
528
- break;
529
- }
530
-
531
- if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->config.info))) {
532
- matched = FTS_MATCHED_ALERT_INFO;
533
- break;
534
- }
535
- }
536
- rw_spinlock_read_unlock(&st->alerts.spinlock);
537
- }
538
- }
539
- dfe_done(ri);
540
- return matched;
541
-}
542
-
543
-static bool rrdcontext_matches_alert(struct rrdcontext_to_json_v2_data *ctl, RRDCONTEXT *rc) {
544
- size_t matches = 0;
545
- RRDINSTANCE *ri;
546
- dfe_start_read(rc->rrdinstances, ri) {
547
- if(ri->rrdset) {
548
- RRDSET *st = ri->rrdset;
549
- rw_spinlock_read_lock(&st->alerts.spinlock);
550
- for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) {
551
- if(ctl->alerts.alert_name_pattern && !simple_pattern_matches_string(ctl->alerts.alert_name_pattern, rcl->config.name))
552
- continue;
553
-
554
- if(ctl->alerts.alarm_id_filter && ctl->alerts.alarm_id_filter != rcl->id)
555
- continue;
556
-
557
- size_t m = ctl->request->alerts.status & CONTEXTS_V2_ALERT_STATUSES ? 0 : 1;
558
-
559
- if (!m) {
560
- if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_UNINITIALIZED) &&
561
- rcl->status == RRDCALC_STATUS_UNINITIALIZED)
562
- m++;
563
-
564
- if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_UNDEFINED) &&
565
- rcl->status == RRDCALC_STATUS_UNDEFINED)
566
- m++;
567
-
568
- if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_CLEAR) &&
569
- rcl->status == RRDCALC_STATUS_CLEAR)
570
- m++;
571
-
572
- if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_RAISED) &&
573
- rcl->status >= RRDCALC_STATUS_RAISED)
574
- m++;
575
-
576
- if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_WARNING) &&
577
- rcl->status == RRDCALC_STATUS_WARNING)
578
- m++;
579
-
580
- if ((ctl->request->alerts.status & CONTEXT_V2_ALERT_CRITICAL) &&
581
- rcl->status == RRDCALC_STATUS_CRITICAL)
582
- m++;
583
-
584
- if(!m)
585
- continue;
586
- }
587
-
588
- struct alert_v2_entry t = {
589
- .tmp = rcl,
590
- };
591
- struct alert_v2_entry *a2e =
592
- dictionary_set(ctl->alerts.summary, string2str(rcl->config.name),
593
- &t, sizeof(struct alert_v2_entry));
594
- size_t ati = a2e->ati;
595
- matches++;
596
-
597
- dictionary_set_advanced(ctl->alerts.by_type,
598
- string2str(rcl->config.type),
599
- (ssize_t)string_strlen(rcl->config.type),
600
- NULL,
601
- sizeof(struct alert_by_x_entry),
602
- rcl);
603
-
604
- dictionary_set_advanced(ctl->alerts.by_component,
605
- string2str(rcl->config.component),
606
- (ssize_t)string_strlen(rcl->config.component),
607
- NULL,
608
- sizeof(struct alert_by_x_entry),
609
- rcl);
610
-
611
- dictionary_set_advanced(ctl->alerts.by_classification,
612
- string2str(rcl->config.classification),
613
- (ssize_t)string_strlen(rcl->config.classification),
614
- NULL,
615
- sizeof(struct alert_by_x_entry),
616
- rcl);
617
-
618
- dictionary_set_advanced(ctl->alerts.by_recipient,
619
- string2str(rcl->config.recipient),
620
- (ssize_t)string_strlen(rcl->config.recipient),
621
- NULL,
622
- sizeof(struct alert_by_x_entry),
623
- rcl);
624
-
625
- char *module = NULL;
626
- rrdlabels_get_value_strdup_or_null(st->rrdlabels, &module, "_collect_module");
627
- if(!module || !*module) module = "[unset]";
628
-
629
- dictionary_set_advanced(ctl->alerts.by_module,
630
- module,
631
- -1,
632
- NULL,
633
- sizeof(struct alert_by_x_entry),
634
- rcl);
635
-
636
- if (ctl->options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES | CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) {
637
- char key[20 + 1];
638
- snprintfz(key, sizeof(key) - 1, "%p", rcl);
639
-
640
- struct sql_alert_instance_v2_entry z = {
641
- .ati = ati,
642
- .tmp = rcl,
643
- };
644
- dictionary_set(ctl->alerts.alert_instances, key, &z, sizeof(z));
645
- }
646
- }
647
- rw_spinlock_read_unlock(&st->alerts.spinlock);
648
- }
649
- }
650
- dfe_done(ri);
651
-
652
- return matches != 0;
653
-}
654
-
655
-
656
-static ssize_t rrdcontext_to_json_v2_add_context(void *data, RRDCONTEXT_ACQUIRED *rca, bool queryable_context __maybe_unused) {
657
- struct rrdcontext_to_json_v2_data *ctl = data;
658
-
659
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
660
-
661
- if(ctl->window.enabled && !query_matches_retention(ctl->window.after, ctl->window.before, rc->first_time_s, (rc->flags & RRD_FLAG_COLLECTED) ? ctl->now : rc->last_time_s, 0))
662
- return 0; // continue to next context
663
-
664
- FTS_MATCH match = ctl->q.host_match;
665
- if((ctl->mode & CONTEXTS_V2_SEARCH) && ctl->q.pattern) {
666
- match = rrdcontext_to_json_v2_full_text_search(ctl, rc, ctl->q.pattern);
667
-
668
- if(match == FTS_MATCHED_NONE)
669
- return 0; // continue to next context
670
- }
671
-
672
- if(ctl->mode & CONTEXTS_V2_ALERTS) {
673
- if(!rrdcontext_matches_alert(ctl, rc))
674
- return 0; // continue to next context
675
- }
676
-
677
- if(ctl->contexts.dict) {
678
- struct context_v2_entry t = {
679
- .count = 1,
680
- .id = rc->id,
681
- .family = string_dup(rc->family),
682
- .priority = rc->priority,
683
- .first_time_s = rc->first_time_s,
684
- .last_time_s = rc->last_time_s,
685
- .flags = rc->flags,
686
- .match = match,
687
- };
688
-
689
- dictionary_set(ctl->contexts.dict, string2str(rc->id), &t, sizeof(struct context_v2_entry));
690
- }
691
-
692
- return 1;
693
-}
694
-
695
-void buffer_json_agent_status_id(BUFFER *wb, size_t ai, usec_t duration_ut) {
696
- buffer_json_member_add_object(wb, "st");
697
- {
698
- buffer_json_member_add_uint64(wb, "ai", ai);
699
- buffer_json_member_add_uint64(wb, "code", 200);
700
- buffer_json_member_add_string(wb, "msg", "");
701
- if (duration_ut)
702
- buffer_json_member_add_double(wb, "ms", (NETDATA_DOUBLE) duration_ut / 1000.0);
703
- }
704
- buffer_json_object_close(wb);
705
-}
706
-
707
-void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status) {
708
- buffer_json_member_add_string(wb, "mg", host->machine_guid);
709
-
710
- if(host->node_id)
711
- buffer_json_member_add_uuid(wb, "nd", host->node_id);
712
- buffer_json_member_add_string(wb, "nm", rrdhost_hostname(host));
713
- buffer_json_member_add_uint64(wb, "ni", ni);
714
-
715
- if(status)
716
- buffer_json_agent_status_id(wb, 0, duration_ut);
717
-}
718
-
719
-static void rrdhost_receiver_to_json(BUFFER *wb, RRDHOST_STATUS *s, const char *key) {
720
- buffer_json_member_add_object(wb, key);
721
- {
722
- buffer_json_member_add_uint64(wb, "id", s->ingest.id);
723
- buffer_json_member_add_uint64(wb, "hops", s->ingest.hops);
724
- buffer_json_member_add_string(wb, "type", rrdhost_ingest_type_to_string(s->ingest.type));
725
- buffer_json_member_add_string(wb, "status", rrdhost_ingest_status_to_string(s->ingest.status));
726
- buffer_json_member_add_time_t(wb, "since", s->ingest.since);
727
- buffer_json_member_add_time_t(wb, "age", s->now - s->ingest.since);
728
-
729
- if(s->ingest.type == RRDHOST_INGEST_TYPE_CHILD) {
730
- if(s->ingest.status == RRDHOST_INGEST_STATUS_OFFLINE)
731
- buffer_json_member_add_string(wb, "reason", stream_handshake_error_to_string(s->ingest.reason));
732
-
733
- if(s->ingest.status == RRDHOST_INGEST_STATUS_REPLICATING) {
734
- buffer_json_member_add_object(wb, "replication");
735
- {
736
- buffer_json_member_add_boolean(wb, "in_progress", s->ingest.replication.in_progress);
737
- buffer_json_member_add_double(wb, "completion", s->ingest.replication.completion);
738
- buffer_json_member_add_uint64(wb, "instances", s->ingest.replication.instances);
739
- }
740
- buffer_json_object_close(wb); // replication
741
- }
742
-
743
- if(s->ingest.status == RRDHOST_INGEST_STATUS_REPLICATING || s->ingest.status == RRDHOST_INGEST_STATUS_ONLINE) {
744
- buffer_json_member_add_object(wb, "source");
745
- {
746
- char buf[1024 + 1];
747
- snprintfz(buf, sizeof(buf) - 1, "[%s]:%d%s", s->ingest.peers.local.ip, s->ingest.peers.local.port, s->ingest.ssl ? ":SSL" : "");
748
- buffer_json_member_add_string(wb, "local", buf);
749
-
750
- snprintfz(buf, sizeof(buf) - 1, "[%s]:%d%s", s->ingest.peers.peer.ip, s->ingest.peers.peer.port, s->ingest.ssl ? ":SSL" : "");
751
- buffer_json_member_add_string(wb, "remote", buf);
752
-
753
- stream_capabilities_to_json_array(wb, s->ingest.capabilities, "capabilities");
754
- }
755
- buffer_json_object_close(wb); // source
756
- }
757
- }
758
- }
759
- buffer_json_object_close(wb); // collection
760
-}
761
-
762
-static void rrdhost_sender_to_json(BUFFER *wb, RRDHOST_STATUS *s, const char *key) {
763
- if(s->stream.status == RRDHOST_STREAM_STATUS_DISABLED)
764
- return;
765
-
766
- buffer_json_member_add_object(wb, key);
767
- {
768
- buffer_json_member_add_uint64(wb, "id", s->stream.id);
769
- buffer_json_member_add_uint64(wb, "hops", s->stream.hops);
770
- buffer_json_member_add_string(wb, "status", rrdhost_streaming_status_to_string(s->stream.status));
771
- buffer_json_member_add_time_t(wb, "since", s->stream.since);
772
- buffer_json_member_add_time_t(wb, "age", s->now - s->stream.since);
773
-
774
- if (s->stream.status == RRDHOST_STREAM_STATUS_OFFLINE)
775
- buffer_json_member_add_string(wb, "reason", stream_handshake_error_to_string(s->stream.reason));
776
-
777
- if (s->stream.status == RRDHOST_STREAM_STATUS_REPLICATING) {
778
- buffer_json_member_add_object(wb, "replication");
779
- {
780
- buffer_json_member_add_boolean(wb, "in_progress", s->stream.replication.in_progress);
781
- buffer_json_member_add_double(wb, "completion", s->stream.replication.completion);
782
- buffer_json_member_add_uint64(wb, "instances", s->stream.replication.instances);
783
- }
784
- buffer_json_object_close(wb);
785
- }
786
-
787
- buffer_json_member_add_object(wb, "destination");
788
- {
789
- char buf[1024 + 1];
790
- snprintfz(buf, sizeof(buf) - 1, "[%s]:%d%s", s->stream.peers.local.ip, s->stream.peers.local.port, s->stream.ssl ? ":SSL" : "");
791
- buffer_json_member_add_string(wb, "local", buf);
792
-
793
- snprintfz(buf, sizeof(buf) - 1, "[%s]:%d%s", s->stream.peers.peer.ip, s->stream.peers.peer.port, s->stream.ssl ? ":SSL" : "");
794
- buffer_json_member_add_string(wb, "remote", buf);
795
-
796
- stream_capabilities_to_json_array(wb, s->stream.capabilities, "capabilities");
797
-
798
- buffer_json_member_add_object(wb, "traffic");
799
- {
800
- buffer_json_member_add_boolean(wb, "compression", s->stream.compression);
801
- buffer_json_member_add_uint64(wb, "data", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA]);
802
- buffer_json_member_add_uint64(wb, "metadata", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA]);
803
- buffer_json_member_add_uint64(wb, "functions", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS]);
804
- buffer_json_member_add_uint64(wb, "replication", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION]);
805
- buffer_json_member_add_uint64(wb, "dyncfg", s->stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DYNCFG]);
806
- }
807
- buffer_json_object_close(wb); // traffic
808
-
809
- buffer_json_member_add_array(wb, "candidates");
810
- struct rrdpush_destinations *d;
811
- for (d = s->host->destinations; d; d = d->next) {
812
- buffer_json_add_array_item_object(wb);
813
- buffer_json_member_add_uint64(wb, "attempts", d->attempts);
814
- {
815
-
816
- if (d->ssl) {
817
- snprintfz(buf, sizeof(buf) - 1, "%s:SSL", string2str(d->destination));
818
- buffer_json_member_add_string(wb, "destination", buf);
819
- }
820
- else
821
- buffer_json_member_add_string(wb, "destination", string2str(d->destination));
822
-
823
- buffer_json_member_add_time_t(wb, "since", d->since);
824
- buffer_json_member_add_time_t(wb, "age", s->now - d->since);
825
- buffer_json_member_add_string(wb, "last_handshake", stream_handshake_error_to_string(d->reason));
826
- if(d->postpone_reconnection_until > s->now) {
827
- buffer_json_member_add_time_t(wb, "next_check", d->postpone_reconnection_until);
828
- buffer_json_member_add_time_t(wb, "next_in", d->postpone_reconnection_until - s->now);
829
- }
830
- }
831
- buffer_json_object_close(wb); // each candidate
832
- }
833
- buffer_json_array_close(wb); // candidates
834
- }
835
- buffer_json_object_close(wb); // destination
836
- }
837
- buffer_json_object_close(wb); // streaming
838
-}
839
-
840
-static void agent_capabilities_to_json(BUFFER *wb, RRDHOST *host, const char *key) {
841
- buffer_json_member_add_array(wb, key);
842
-
843
- struct capability *capas = aclk_get_node_instance_capas(host);
844
- for(struct capability *capa = capas; capa->name ;capa++) {
845
- buffer_json_add_array_item_object(wb);
846
- {
847
- buffer_json_member_add_string(wb, "name", capa->name);
848
- buffer_json_member_add_uint64(wb, "version", capa->version);
849
- buffer_json_member_add_boolean(wb, "enabled", capa->enabled);
850
- }
851
- buffer_json_object_close(wb);
852
- }
853
- buffer_json_array_close(wb);
854
- freez(capas);
855
-}
856
-
857
-static inline void host_dyncfg_to_json_v2(BUFFER *wb, const char *key, RRDHOST_STATUS *s) {
858
- buffer_json_member_add_object(wb, key);
859
- {
860
- buffer_json_member_add_string(wb, "status", rrdhost_dyncfg_status_to_string(s->dyncfg.status));
861
- }
862
- buffer_json_object_close(wb); // health
863
-
864
-}
865
-
866
-static inline void rrdhost_health_to_json_v2(BUFFER *wb, const char *key, RRDHOST_STATUS *s) {
867
- buffer_json_member_add_object(wb, key);
868
- {
869
- buffer_json_member_add_string(wb, "status", rrdhost_health_status_to_string(s->health.status));
870
- if (s->health.status == RRDHOST_HEALTH_STATUS_RUNNING) {
871
- buffer_json_member_add_object(wb, "alerts");
872
- {
873
- buffer_json_member_add_uint64(wb, "critical", s->health.alerts.critical);
874
- buffer_json_member_add_uint64(wb, "warning", s->health.alerts.warning);
875
- buffer_json_member_add_uint64(wb, "clear", s->health.alerts.clear);
876
- buffer_json_member_add_uint64(wb, "undefined", s->health.alerts.undefined);
877
- buffer_json_member_add_uint64(wb, "uninitialized", s->health.alerts.uninitialized);
878
- }
879
- buffer_json_object_close(wb); // alerts
880
- }
881
- }
882
- buffer_json_object_close(wb); // health
883
-}
884
-
885
-static void rrdcontext_to_json_v2_rrdhost(BUFFER *wb, RRDHOST *host, struct rrdcontext_to_json_v2_data *ctl, size_t node_id) {
886
- buffer_json_add_array_item_object(wb); // this node
887
- buffer_json_node_add_v2(wb, host, node_id, 0,
888
- (ctl->mode & CONTEXTS_V2_AGENTS) && !(ctl->mode & CONTEXTS_V2_NODE_INSTANCES));
889
-
890
- if(ctl->mode & (CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODE_INSTANCES)) {
891
- RRDHOST_STATUS s;
892
- rrdhost_status(host, ctl->now, &s);
893
-
894
- if (ctl->mode & (CONTEXTS_V2_NODES_INFO)) {
895
- buffer_json_member_add_string(wb, "v", rrdhost_program_version(host));
896
-
897
- host_labels2json(host, wb, "labels");
898
-
899
- if (host->system_info) {
900
- buffer_json_member_add_object(wb, "hw");
901
- {
902
- buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
903
- buffer_json_member_add_string_or_empty(wb, "cpu_frequency", host->system_info->host_cpu_freq);
904
- buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
905
- buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
906
- buffer_json_member_add_string_or_empty(wb, "disk_space", host->system_info->host_disk_space);
907
- buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
908
- buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
909
- }
910
- buffer_json_object_close(wb);
911
-
912
- buffer_json_member_add_object(wb, "os");
913
- {
914
- buffer_json_member_add_string_or_empty(wb, "id", host->system_info->host_os_id);
915
- buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->host_os_name);
916
- buffer_json_member_add_string_or_empty(wb, "v", host->system_info->host_os_version);
917
- buffer_json_member_add_object(wb, "kernel");
918
- buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->kernel_name);
919
- buffer_json_member_add_string_or_empty(wb, "v", host->system_info->kernel_version);
920
- buffer_json_object_close(wb);
921
- }
922
- buffer_json_object_close(wb);
923
- }
924
-
925
- // created - the node is created but never connected to cloud
926
- // unreachable - not currently connected
927
- // stale - connected but not having live data
928
- // reachable - connected with live data
929
- // pruned - not connected for some time and has been removed
930
- buffer_json_member_add_string(wb, "state", rrdhost_state_cloud_emulation(host) ? "reachable" : "stale");
931
-
932
- rrdhost_health_to_json_v2(wb, "health", &s);
933
- agent_capabilities_to_json(wb, host, "capabilities");
934
- }
935
-
936
- if (ctl->mode & (CONTEXTS_V2_NODE_INSTANCES)) {
937
- buffer_json_member_add_array(wb, "instances");
938
- buffer_json_add_array_item_object(wb); // this instance
939
- {
940
- buffer_json_agent_status_id(wb, 0, 0);
941
-
942
- buffer_json_member_add_object(wb, "db");
943
- {
944
- buffer_json_member_add_string(wb, "status", rrdhost_db_status_to_string(s.db.status));
945
- buffer_json_member_add_string(wb, "liveness", rrdhost_db_liveness_to_string(s.db.liveness));
946
- buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
947
- buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
948
- buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
949
- buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
950
- buffer_json_member_add_uint64(wb, "instances", s.db.instances);
951
- buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
952
- }
953
- buffer_json_object_close(wb);
954
-
955
- rrdhost_receiver_to_json(wb, &s, "ingest");
956
- rrdhost_sender_to_json(wb, &s, "stream");
957
-
958
- buffer_json_member_add_object(wb, "ml");
959
- buffer_json_member_add_string(wb, "status", rrdhost_ml_status_to_string(s.ml.status));
960
- buffer_json_member_add_string(wb, "type", rrdhost_ml_type_to_string(s.ml.type));
961
- if (s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
962
- buffer_json_member_add_object(wb, "metrics");
963
- {
964
- buffer_json_member_add_uint64(wb, "anomalous", s.ml.metrics.anomalous);
965
- buffer_json_member_add_uint64(wb, "normal", s.ml.metrics.normal);
966
- buffer_json_member_add_uint64(wb, "trained", s.ml.metrics.trained);
967
- buffer_json_member_add_uint64(wb, "pending", s.ml.metrics.pending);
968
- buffer_json_member_add_uint64(wb, "silenced", s.ml.metrics.silenced);
969
- }
970
- buffer_json_object_close(wb); // metrics
971
- }
972
- buffer_json_object_close(wb); // ml
973
-
974
- rrdhost_health_to_json_v2(wb, "health", &s);
975
-
976
- host_functions2json(host, wb); // functions
977
- agent_capabilities_to_json(wb, host, "capabilities");
978
-
979
- host_dyncfg_to_json_v2(wb, "dyncfg", &s);
980
- }
981
- buffer_json_object_close(wb); // this instance
982
- buffer_json_array_close(wb); // instances
983
- }
984
- }
985
- buffer_json_object_close(wb); // this node
986
-}
987
-
988
-static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool queryable_host) {
989
- if(!queryable_host || !host->rrdctx.contexts)
990
- // the host matches the 'scope_host' but does not match the 'host' patterns
991
- // or the host does not have any contexts
992
- return 0; // continue to next host
993
-
994
- struct rrdcontext_to_json_v2_data *ctl = data;
995
-
996
- if(ctl->window.enabled && !rrdhost_matches_window(host, ctl->window.after, ctl->window.before, ctl->now))
997
- // the host does not have data in the requested window
998
- return 0; // continue to next host
999
-
1000
- if(ctl->request->timeout_ms && now_monotonic_usec() > ctl->timings.received_ut + ctl->request->timeout_ms * USEC_PER_MS)
1001
- // timed out
1002
- return -2; // stop the query
1003
-
1004
- if(ctl->request->interrupt_callback && ctl->request->interrupt_callback(ctl->request->interrupt_callback_data))
1005
- // interrupted
1006
- return -1; // stop the query
1007
-
1008
- bool host_matched = (ctl->mode & CONTEXTS_V2_NODES);
1009
- bool do_contexts = (ctl->mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_ALERTS));
1010
-
1011
- ctl->q.host_match = FTS_MATCHED_NONE;
1012
- if((ctl->mode & CONTEXTS_V2_SEARCH)) {
1013
- // check if we match the host itself
1014
- if(ctl->q.pattern && (
1015
- full_text_search_string(&ctl->q.fts, ctl->q.pattern, host->hostname) ||
1016
- full_text_search_char(&ctl->q.fts, ctl->q.pattern, host->machine_guid) ||
1017
- (ctl->q.pattern && full_text_search_char(&ctl->q.fts, ctl->q.pattern, ctl->q.host_node_id_str)))) {
1018
- ctl->q.host_match = FTS_MATCHED_HOST;
1019
- do_contexts = true;
1020
- }
1021
- }
1022
-
1023
- if(do_contexts) {
1024
- // save it
1025
- SIMPLE_PATTERN *old_q = ctl->q.pattern;
1026
-
1027
- if(ctl->q.host_match == FTS_MATCHED_HOST)
1028
- // do not do pattern matching on contexts - we matched the host itself
1029
- ctl->q.pattern = NULL;
1030
-
1031
- ssize_t added = query_scope_foreach_context(
1032
- host, ctl->request->scope_contexts,
1033
- ctl->contexts.scope_pattern, ctl->contexts.pattern,
1034
- rrdcontext_to_json_v2_add_context, queryable_host, ctl);
1035
-
1036
- // restore it
1037
- ctl->q.pattern = old_q;
1038
-
1039
- if(unlikely(added < 0))
1040
- return -1; // stop the query
1041
-
1042
- if(added)
1043
- host_matched = true;
1044
- }
1045
-
1046
- if(!host_matched)
1047
- return 0;
1048
-
1049
- if(ctl->mode & CONTEXTS_V2_FUNCTIONS) {
1050
- struct function_v2_entry t = {
1051
- .used = 1,
1052
- .size = 1,
1053
- .node_ids = &ctl->nodes.ni,
1054
- .help = NULL,
1055
- .tags = NULL,
1056
- .access = HTTP_ACCESS_ALL,
1057
- .priority = RRDFUNCTIONS_PRIORITY_DEFAULT,
1058
- };
1059
- host_functions_to_dict(host, ctl->functions.dict, &t, sizeof(t), &t.help, &t.tags, &t.access, &t.priority);
1060
- }
1061
-
1062
- if(ctl->mode & CONTEXTS_V2_NODES) {
1063
- struct contexts_v2_node t = {
1064
- .ni = ctl->nodes.ni++,
1065
- .host = host,
1066
- };
1067
-
1068
- dictionary_set(ctl->nodes.dict, host->machine_guid, &t, sizeof(struct contexts_v2_node));
1069
- }
1070
-
1071
- return 1;
1072
-}
1073
-
1074
-static void buffer_json_contexts_v2_mode_to_array(BUFFER *wb, const char *key, CONTEXTS_V2_MODE mode) {
1075
- buffer_json_member_add_array(wb, key);
1076
-
1077
- if(mode & CONTEXTS_V2_VERSIONS)
1078
- buffer_json_add_array_item_string(wb, "versions");
1079
-
1080
- if(mode & CONTEXTS_V2_AGENTS)
1081
- buffer_json_add_array_item_string(wb, "agents");
1082
-
1083
- if(mode & CONTEXTS_V2_AGENTS_INFO)
1084
- buffer_json_add_array_item_string(wb, "agents-info");
1085
-
1086
- if(mode & CONTEXTS_V2_NODES)
1087
- buffer_json_add_array_item_string(wb, "nodes");
1088
-
1089
- if(mode & CONTEXTS_V2_NODES_INFO)
1090
- buffer_json_add_array_item_string(wb, "nodes-info");
1091
-
1092
- if(mode & CONTEXTS_V2_NODE_INSTANCES)
1093
- buffer_json_add_array_item_string(wb, "nodes-instances");
1094
-
1095
- if(mode & CONTEXTS_V2_CONTEXTS)
1096
- buffer_json_add_array_item_string(wb, "contexts");
1097
-
1098
- if(mode & CONTEXTS_V2_SEARCH)
1099
- buffer_json_add_array_item_string(wb, "search");
1100
-
1101
- if(mode & CONTEXTS_V2_ALERTS)
1102
- buffer_json_add_array_item_string(wb, "alerts");
1103
-
1104
- if(mode & CONTEXTS_V2_ALERT_TRANSITIONS)
1105
- buffer_json_add_array_item_string(wb, "alert_transitions");
1106
-
1107
- buffer_json_array_close(wb);
1108
-}
1109
-
1110
-void buffer_json_query_timings(BUFFER *wb, const char *key, struct query_timings *timings) {
1111
- timings->finished_ut = now_monotonic_usec();
1112
- if(!timings->executed_ut)
1113
- timings->executed_ut = timings->finished_ut;
1114
- if(!timings->preprocessed_ut)
1115
- timings->preprocessed_ut = timings->received_ut;
1116
- buffer_json_member_add_object(wb, key);
1117
- buffer_json_member_add_double(wb, "prep_ms", (NETDATA_DOUBLE)(timings->preprocessed_ut - timings->received_ut) / USEC_PER_MS);
1118
- buffer_json_member_add_double(wb, "query_ms", (NETDATA_DOUBLE)(timings->executed_ut - timings->preprocessed_ut) / USEC_PER_MS);
1119
- buffer_json_member_add_double(wb, "output_ms", (NETDATA_DOUBLE)(timings->finished_ut - timings->executed_ut) / USEC_PER_MS);
1120
- buffer_json_member_add_double(wb, "total_ms", (NETDATA_DOUBLE)(timings->finished_ut - timings->received_ut) / USEC_PER_MS);
1121
- buffer_json_member_add_double(wb, "cloud_ms", (NETDATA_DOUBLE)(timings->finished_ut - timings->received_ut) / USEC_PER_MS);
1122
- buffer_json_object_close(wb);
1123
-}
1124
-
1125
-void build_info_to_json_object(BUFFER *b);
1126
-
1127
-static void convert_seconds_to_dhms(time_t seconds, char *result, int result_size) {
1128
- int days, hours, minutes;
1129
-
1130
- days = (int) (seconds / (24 * 3600));
1131
- seconds = (int) (seconds % (24 * 3600));
1132
- hours = (int) (seconds / 3600);
1133
- seconds %= 3600;
1134
- minutes = (int) (seconds / 60);
1135
- seconds %= 60;
1136
-
1137
- // Format the result into the provided string buffer
1138
- BUFFER *buf = buffer_create(128, NULL);
1139
- if (days)
1140
- buffer_sprintf(buf,"%d day%s%s", days, days==1 ? "" : "s", hours || minutes ? ", " : "");
1141
- if (hours)
1142
- buffer_sprintf(buf,"%d hour%s%s", hours, hours==1 ? "" : "s", minutes ? ", " : "");
1143
- if (minutes)
1144
- buffer_sprintf(buf,"%d minute%s%s", minutes, minutes==1 ? "" : "s", seconds ? ", " : "");
1145
- if (seconds)
1146
- buffer_sprintf(buf,"%d second%s", (int) seconds, seconds==1 ? "" : "s");
1147
- strncpyz(result, buffer_tostring(buf), result_size);
1148
- buffer_free(buf);
1149
-}
1150
-
1151
-void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info, bool array) {
1152
- if(!now_s)
1153
- now_s = now_realtime_sec();
1154
-
1155
- if(array) {
1156
- buffer_json_member_add_array(wb, "agents");
1157
- buffer_json_add_array_item_object(wb);
1158
- }
1159
- else
1160
- buffer_json_member_add_object(wb, "agent");
1161
-
1162
- buffer_json_member_add_string(wb, "mg", localhost->machine_guid);
1163
- buffer_json_member_add_uuid(wb, "nd", localhost->node_id);
1164
- buffer_json_member_add_string(wb, "nm", rrdhost_hostname(localhost));
1165
- buffer_json_member_add_time_t(wb, "now", now_s);
1166
-
1167
- if(array)
1168
- buffer_json_member_add_uint64(wb, "ai", 0);
1169
-
1170
- if(info) {
1171
- buffer_json_member_add_object(wb, "application");
1172
- build_info_to_json_object(wb);
1173
- buffer_json_object_close(wb); // netdata
1174
-
1175
- buffer_json_cloud_status(wb, now_s);
1176
-
1177
- buffer_json_member_add_array(wb, "db_size");
1178
- size_t group_seconds = localhost->rrd_update_every;
1179
- for (size_t tier = 0; tier < storage_tiers; tier++) {
1180
- STORAGE_ENGINE *eng = localhost->db[tier].eng;
1181
- if (!eng) continue;
1182
-
1183
- group_seconds *= storage_tiers_grouping_iterations[tier];
1184
- uint64_t max = storage_engine_disk_space_max(eng->seb, localhost->db[tier].si);
1185
- uint64_t used = storage_engine_disk_space_used(eng->seb, localhost->db[tier].si);
1186
-#ifdef ENABLE_DBENGINE
1187
- if (!max && eng->seb == STORAGE_ENGINE_BACKEND_DBENGINE) {
1188
- max = get_directory_free_bytes_space(multidb_ctx[tier]);
1189
- max += used;
1190
- }
1191
-#endif
1192
- time_t first_time_s = storage_engine_global_first_time_s(eng->seb, localhost->db[tier].si);
1193
- size_t currently_collected_metrics = storage_engine_collected_metrics(eng->seb, localhost->db[tier].si);
1194
-
1195
- NETDATA_DOUBLE percent;
1196
- if (used && max)
1197
- percent = (NETDATA_DOUBLE) used * 100.0 / (NETDATA_DOUBLE) max;
1198
- else
1199
- percent = 0.0;
1200
-
1201
- buffer_json_add_array_item_object(wb);
1202
- buffer_json_member_add_uint64(wb, "tier", tier);
1203
- char human_retention[128];
1204
- convert_seconds_to_dhms((time_t) group_seconds, human_retention, sizeof(human_retention) - 1);
1205
- buffer_json_member_add_string(wb, "point_every", human_retention);
1206
-
1207
- buffer_json_member_add_uint64(wb, "metrics", storage_engine_metrics(eng->seb, localhost->db[tier].si));
1208
- buffer_json_member_add_uint64(wb, "samples", storage_engine_samples(eng->seb, localhost->db[tier].si));
1209
-
1210
- if(used || max) {
1211
- buffer_json_member_add_uint64(wb, "disk_used", used);
1212
- buffer_json_member_add_uint64(wb, "disk_max", max);
1213
- buffer_json_member_add_double(wb, "disk_percent", percent);
1214
- }
1215
-
1216
- if(first_time_s) {
1217
- time_t retention = now_s - first_time_s;
1218
-
1219
- buffer_json_member_add_time_t(wb, "from", first_time_s);
1220
- buffer_json_member_add_time_t(wb, "to", now_s);
1221
- buffer_json_member_add_time_t(wb, "retention", retention);
1222
-
1223
- convert_seconds_to_dhms(retention, human_retention, sizeof(human_retention) - 1);
1224
- buffer_json_member_add_string(wb, "retention_human", human_retention);
1225
-
1226
- if(used || max) { // we have disk space information
1227
- time_t time_retention = 0;
1228
-#ifdef ENABLE_DBENGINE
1229
- time_retention = multidb_ctx[tier]->config.max_retention_s;
1230
-#endif
1231
- time_t space_retention = (time_t)((NETDATA_DOUBLE)(now_s - first_time_s) * 100.0 / percent);
1232
- time_t actual_retention = MIN(space_retention, time_retention ? time_retention : space_retention);
1233
-
1234
- if (time_retention) {
1235
- convert_seconds_to_dhms(time_retention, human_retention, sizeof(human_retention) - 1);
1236
- buffer_json_member_add_time_t(wb, "requested_retention", time_retention);
1237
- buffer_json_member_add_string(wb, "requested_retention_human", human_retention);
1238
- }
1239
-
1240
- convert_seconds_to_dhms(actual_retention, human_retention, sizeof(human_retention) - 1);
1241
- buffer_json_member_add_time_t(wb, "expected_retention", actual_retention);
1242
- buffer_json_member_add_string(wb, "expected_retention_human", human_retention);
1243
- }
1244
- }
1245
-
1246
- if(currently_collected_metrics)
1247
- buffer_json_member_add_uint64(wb, "currently_collected_metrics", currently_collected_metrics);
1248
-
1249
- buffer_json_object_close(wb);
1250
- }
1251
- buffer_json_array_close(wb); // db_size
1252
- }
1253
-
1254
- if(timings)
1255
- buffer_json_query_timings(wb, "timings", timings);
1256
-
1257
- buffer_json_object_close(wb);
1258
-
1259
- if(array)
1260
- buffer_json_array_close(wb);
1261
-}
1262
-
1263
-void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings *timings) {
1264
- if(!timings->finished_ut)
1265
- timings->finished_ut = now_monotonic_usec();
1266
-
1267
- buffer_json_member_add_object(wb, key);
1268
- buffer_json_member_add_double(wb, "routing_ms", 0.0);
1269
- buffer_json_member_add_double(wb, "node_max_ms", 0.0);
1270
- buffer_json_member_add_double(wb, "total_ms", (NETDATA_DOUBLE)(timings->finished_ut - timings->received_ut) / USEC_PER_MS);
1271
- buffer_json_object_close(wb);
1272
-}
1273
-
1274
-static void functions_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
1275
- struct function_v2_entry *t = value;
1276
-
1277
- // it is initialized with a static reference - we need to mallocz() the array
1278
- size_t *v = t->node_ids;
1279
- t->node_ids = mallocz(sizeof(size_t));
1280
- *t->node_ids = *v;
1281
- t->size = 1;
1282
- t->used = 1;
1283
-}
1284
-
1285
-static bool functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
1286
- struct function_v2_entry *t = old_value, *n = new_value;
1287
- size_t *v = n->node_ids;
1288
-
1289
- if(t->used >= t->size) {
1290
- t->node_ids = reallocz(t->node_ids, t->size * 2 * sizeof(size_t));
1291
- t->size *= 2;
1292
- }
1293
-
1294
- t->node_ids[t->used++] = *v;
1295
-
1296
- return true;
1297
-}
1298
-
1299
-static void functions_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
1300
- struct function_v2_entry *t = value;
1301
- freez(t->node_ids);
1302
-}
1303
-
1304
-static bool contexts_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
1305
- struct context_v2_entry *o = old_value;
1306
- struct context_v2_entry *n = new_value;
1307
-
1308
- o->count++;
1309
-
1310
- if(o->family != n->family) {
1311
- if((o->flags & RRD_FLAG_COLLECTED) && !(n->flags & RRD_FLAG_COLLECTED))
1312
- // keep old
1313
- ;
1314
- else if(!(o->flags & RRD_FLAG_COLLECTED) && (n->flags & RRD_FLAG_COLLECTED)) {
1315
- // keep new
1316
- string_freez(o->family);
1317
- o->family = string_dup(n->family);
1318
- }
1319
- else {
1320
- // merge
1321
- STRING *old_family = o->family;
1322
- o->family = string_2way_merge(o->family, n->family);
1323
- string_freez(old_family);
1324
- }
1325
- }
1326
-
1327
- if(o->priority != n->priority) {
1328
- if((o->flags & RRD_FLAG_COLLECTED) && !(n->flags & RRD_FLAG_COLLECTED))
1329
- // keep o
1330
- ;
1331
- else if(!(o->flags & RRD_FLAG_COLLECTED) && (n->flags & RRD_FLAG_COLLECTED))
1332
- // keep n
1333
- o->priority = n->priority;
1334
- else
1335
- // keep the min
1336
- o->priority = MIN(o->priority, n->priority);
1337
- }
1338
-
1339
- if(o->first_time_s && n->first_time_s)
1340
- o->first_time_s = MIN(o->first_time_s, n->first_time_s);
1341
- else if(!o->first_time_s)
1342
- o->first_time_s = n->first_time_s;
1343
-
1344
- if(o->last_time_s && n->last_time_s)
1345
- o->last_time_s = MAX(o->last_time_s, n->last_time_s);
1346
- else if(!o->last_time_s)
1347
- o->last_time_s = n->last_time_s;
1348
-
1349
- o->flags |= n->flags;
1350
- o->match = MIN(o->match, n->match);
1351
-
1352
- string_freez(n->family);
1353
-
1354
- return true;
1355
-}
1356
-
1357
-static void contexts_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
1358
- struct context_v2_entry *z = value;
1359
- string_freez(z->family);
1360
-}
1361
-
1362
-static void rrdcontext_v2_set_transition_filter(const char *machine_guid, const char *context, time_t alarm_id, void *data) {
1363
- struct rrdcontext_to_json_v2_data *ctl = data;
1364
-
1365
- if(machine_guid && *machine_guid) {
1366
- if(ctl->nodes.scope_pattern)
1367
- simple_pattern_free(ctl->nodes.scope_pattern);
1368
-
1369
- if(ctl->nodes.pattern)
1370
- simple_pattern_free(ctl->nodes.pattern);
1371
-
1372
- ctl->nodes.scope_pattern = string_to_simple_pattern(machine_guid);
1373
- ctl->nodes.pattern = NULL;
1374
- }
1375
-
1376
- if(context && *context) {
1377
- if(ctl->contexts.scope_pattern)
1378
- simple_pattern_free(ctl->contexts.scope_pattern);
1379
-
1380
- if(ctl->contexts.pattern)
1381
- simple_pattern_free(ctl->contexts.pattern);
1382
-
1383
- ctl->contexts.scope_pattern = string_to_simple_pattern(context);
1384
- ctl->contexts.pattern = NULL;
1385
- }
1386
-
1387
- ctl->alerts.alarm_id_filter = alarm_id;
1388
-}
1389
-
1390
-struct alert_instances_callback_data {
1391
- BUFFER *wb;
1392
- struct rrdcontext_to_json_v2_data *ctl;
1393
- bool debug;
1394
-};
1395
-
1396
-static void contexts_v2_alert_config_to_json_from_sql_alert_config_data(struct sql_alert_config_data *t, void *data) {
1397
- struct alert_transitions_callback_data *d = data;
1398
- BUFFER *wb = d->wb;
1399
- bool debug = d->debug;
1400
- d->configs_added++;
1401
-
1402
- if(d->only_one_config)
1403
- buffer_json_add_array_item_object(wb); // alert config
1404
-
1405
- {
1406
- buffer_json_member_add_string(wb, "name", t->name);
1407
- buffer_json_member_add_uuid(wb, "config_hash_id", t->config_hash_id);
1408
-
1409
- buffer_json_member_add_object(wb, "selectors");
1410
- {
1411
- bool is_template = t->selectors.on_template && *t->selectors.on_template ? true : false;
1412
- buffer_json_member_add_string(wb, "type", is_template ? "template" : "alarm");
1413
- buffer_json_member_add_string(wb, "on", is_template ? t->selectors.on_template : t->selectors.on_key);
1414
-
1415
- buffer_json_member_add_string(wb, "families", t->selectors.families);
1416
- buffer_json_member_add_string(wb, "host_labels", t->selectors.host_labels);
1417
- buffer_json_member_add_string(wb, "chart_labels", t->selectors.chart_labels);
1418
- }
1419
- buffer_json_object_close(wb); // selectors
1420
-
1421
- buffer_json_member_add_object(wb, "value"); // value
1422
- {
1423
- // buffer_json_member_add_string(wb, "every", t->value.every); // does not exist in Netdata Cloud
1424
- buffer_json_member_add_string(wb, "units", t->value.units);
1425
- buffer_json_member_add_uint64(wb, "update_every", t->value.update_every);
1426
-
1427
- if (t->value.db.after || debug) {
1428
- buffer_json_member_add_object(wb, "db");
1429
- {
1430
- // buffer_json_member_add_string(wb, "lookup", t->value.db.lookup); // does not exist in Netdata Cloud
1431
-
1432
- buffer_json_member_add_time_t(wb, "after", t->value.db.after);
1433
- buffer_json_member_add_time_t(wb, "before", t->value.db.before);
1434
- buffer_json_member_add_string(wb, "time_group_condition", alerts_group_conditions_id2txt(t->value.db.time_group_condition));
1435
- buffer_json_member_add_double(wb, "time_group_value", t->value.db.time_group_value);
1436
- buffer_json_member_add_string(wb, "dims_group", alerts_dims_grouping_id2group(t->value.db.dims_group));
1437
- buffer_json_member_add_string(wb, "data_source", alerts_data_source_id2source(t->value.db.data_source));
1438
- buffer_json_member_add_string(wb, "method", t->value.db.method);
1439
- buffer_json_member_add_string(wb, "dimensions", t->value.db.dimensions);
1440
- rrdr_options_to_buffer_json_array(wb, "options", (RRDR_OPTIONS)t->value.db.options);
1441
- }
1442
- buffer_json_object_close(wb); // db
1443
- }
1444
-
1445
- if (t->value.calc || debug)
1446
- buffer_json_member_add_string(wb, "calc", t->value.calc);
1447
- }
1448
- buffer_json_object_close(wb); // value
1449
-
1450
- if (t->status.warn || t->status.crit || debug) {
1451
- buffer_json_member_add_object(wb, "status"); // status
1452
- {
1453
- NETDATA_DOUBLE green = t->status.green ? str2ndd(t->status.green, NULL) : NAN;
1454
- NETDATA_DOUBLE red = t->status.red ? str2ndd(t->status.red, NULL) : NAN;
1455
-
1456
- if (!isnan(green) || debug)
1457
- buffer_json_member_add_double(wb, "green", green);
1458
-
1459
- if (!isnan(red) || debug)
1460
- buffer_json_member_add_double(wb, "red", red);
1461
-
1462
- if (t->status.warn || debug)
1463
- buffer_json_member_add_string(wb, "warn", t->status.warn);
1464
-
1465
- if (t->status.crit || debug)
1466
- buffer_json_member_add_string(wb, "crit", t->status.crit);
1467
- }
1468
- buffer_json_object_close(wb); // status
1469
- }
1470
-
1471
- buffer_json_member_add_object(wb, "notification");
1472
- {
1473
- buffer_json_member_add_string(wb, "type", "agent");
1474
- buffer_json_member_add_string(wb, "exec", t->notification.exec ? t->notification.exec : NULL);
1475
- buffer_json_member_add_string(wb, "to", t->notification.to_key ? t->notification.to_key : string2str(localhost->health.health_default_recipient));
1476
- buffer_json_member_add_string(wb, "delay", t->notification.delay);
1477
- buffer_json_member_add_string(wb, "repeat", t->notification.repeat);
1478
- buffer_json_member_add_string(wb, "options", t->notification.options);
1479
- }
1480
- buffer_json_object_close(wb); // notification
1481
-
1482
- buffer_json_member_add_string(wb, "class", t->classification);
1483
- buffer_json_member_add_string(wb, "component", t->component);
1484
- buffer_json_member_add_string(wb, "type", t->type);
1485
- buffer_json_member_add_string(wb, "info", t->info);
1486
- buffer_json_member_add_string(wb, "summary", t->summary);
1487
- // buffer_json_member_add_string(wb, "source", t->source); // moved to alert instance
1488
- }
1489
-
1490
- if(d->only_one_config)
1491
- buffer_json_object_close(wb);
1492
-}
1493
-
1494
-int contexts_v2_alert_config_to_json(struct web_client *w, const char *config_hash_id) {
1495
- struct alert_transitions_callback_data data = {
1496
- .wb = w->response.data,
1497
- .debug = false,
1498
- .only_one_config = false,
1499
- };
1500
- DICTIONARY *configs = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
1501
- dictionary_set(configs, config_hash_id, NULL, 0);
1502
-
1503
- buffer_flush(w->response.data);
1504
-
1505
- buffer_json_initialize(w->response.data, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
1506
-
1507
- int added = sql_get_alert_configuration(configs, contexts_v2_alert_config_to_json_from_sql_alert_config_data, &data, false);
1508
- buffer_json_finalize(w->response.data);
1509
-
1510
- int ret = HTTP_RESP_OK;
1511
-
1512
- if(added <= 0) {
1513
- buffer_flush(w->response.data);
1514
- w->response.data->content_type = CT_TEXT_PLAIN;
1515
- if(added < 0) {
1516
- buffer_strcat(w->response.data, "Failed to execute SQL query.");
1517
- ret = HTTP_RESP_INTERNAL_SERVER_ERROR;
1518
- }
1519
- else {
1520
- buffer_strcat(w->response.data, "Config is not found.");
1521
- ret = HTTP_RESP_NOT_FOUND;
1522
- }
1523
- }
1524
-
1525
- return ret;
1526
-}
1527
-
1528
-static int contexts_v2_alert_instance_to_json_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
1529
- struct sql_alert_instance_v2_entry *t = value;
1530
- struct alert_instances_callback_data *d = data;
1531
- struct rrdcontext_to_json_v2_data *ctl = d->ctl; (void)ctl;
1532
- bool debug = d->debug; (void)debug;
1533
- BUFFER *wb = d->wb;
1534
-
1535
- buffer_json_add_array_item_object(wb);
1536
- {
1537
- buffer_json_member_add_uint64(wb, "ni", t->ni);
1538
-
1539
- buffer_json_member_add_string(wb, "nm", string2str(t->name));
1540
- buffer_json_member_add_string(wb, "ch", string2str(t->chart_id));
1541
- buffer_json_member_add_string(wb, "ch_n", string2str(t->chart_name));
1542
-
1543
- if(ctl->request->options & CONTEXT_V2_OPTION_ALERTS_WITH_SUMMARY)
1544
- buffer_json_member_add_uint64(wb, "ati", t->ati);
1545
-
1546
- if(ctl->request->options & CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES) {
1547
- buffer_json_member_add_string(wb, "units", string2str(t->units));
1548
- buffer_json_member_add_string(wb, "fami", string2str(t->family));
1549
- buffer_json_member_add_string(wb, "info", string2str(t->info));
1550
- buffer_json_member_add_string(wb, "sum", string2str(t->summary));
1551
- buffer_json_member_add_string(wb, "ctx", string2str(t->context));
1552
- buffer_json_member_add_string(wb, "st", rrdcalc_status2string(t->status));
1553
- buffer_json_member_add_uuid(wb, "tr_i", &t->last_transition_id);
1554
- buffer_json_member_add_double(wb, "tr_v", t->last_status_change_value);
1555
- buffer_json_member_add_time_t(wb, "tr_t", t->last_status_change);
1556
- buffer_json_member_add_uuid(wb, "cfg", &t->config_hash_id);
1557
- buffer_json_member_add_string(wb, "src", string2str(t->source));
1558
-
1559
- buffer_json_member_add_string(wb, "to", string2str(t->recipient));
1560
- buffer_json_member_add_string(wb, "tp", string2str(t->type));
1561
- buffer_json_member_add_string(wb, "cm", string2str(t->component));
1562
- buffer_json_member_add_string(wb, "cl", string2str(t->classification));
1563
-
1564
- // Agent specific fields
1565
- buffer_json_member_add_uint64(wb, "gi", t->global_id);
1566
- // rrdcalc_flags_to_json_array (wb, "flags", t->flags);
1567
- }
1568
-
1569
- if(ctl->request->options & CONTEXT_V2_OPTION_ALERTS_WITH_VALUES) {
1570
- // Netdata Cloud fetched these by querying the agents
1571
- buffer_json_member_add_double(wb, "v", t->value);
1572
- buffer_json_member_add_time_t(wb, "t", t->last_updated);
1573
- }
1574
- }
1575
- buffer_json_object_close(wb); // alert instance
1576
-
1577
- return 1;
1578
-}
1579
-
1580
-static void contexts_v2_alerts_by_x_update_prototypes(void *data, STRING *type, STRING *component, STRING *classification, STRING *recipient) {
1581
- struct rrdcontext_to_json_v2_data *ctl = data;
1582
-
1583
- dictionary_set_advanced(ctl->alerts.by_type, string2str(type), (ssize_t)string_strlen(type), NULL, sizeof(struct alert_by_x_entry), NULL);
1584
- dictionary_set_advanced(ctl->alerts.by_component, string2str(component), (ssize_t)string_strlen(component), NULL, sizeof(struct alert_by_x_entry), NULL);
1585
- dictionary_set_advanced(ctl->alerts.by_classification, string2str(classification), (ssize_t)string_strlen(classification), NULL, sizeof(struct alert_by_x_entry), NULL);
1586
- dictionary_set_advanced(ctl->alerts.by_recipient, string2str(recipient), (ssize_t)string_strlen(recipient), NULL, sizeof(struct alert_by_x_entry), NULL);
1587
-}
1588
-
1589
-static void contexts_v2_alerts_by_x_to_json(BUFFER *wb, DICTIONARY *dict, const char *key) {
1590
- buffer_json_member_add_array(wb, key);
1591
- {
1592
- struct alert_by_x_entry *b;
1593
- dfe_start_read(dict, b) {
1594
- buffer_json_add_array_item_object(wb);
1595
- {
1596
- buffer_json_member_add_string(wb, "name", b_dfe.name);
1597
- buffer_json_member_add_uint64(wb, "cr", b->running.counts.critical);
1598
- buffer_json_member_add_uint64(wb, "wr", b->running.counts.warning);
1599
- buffer_json_member_add_uint64(wb, "cl", b->running.counts.clear);
1600
- buffer_json_member_add_uint64(wb, "er", b->running.counts.error);
1601
- buffer_json_member_add_uint64(wb, "running", b->running.total);
1602
-
1603
- buffer_json_member_add_uint64(wb, "running_silent", b->running.silent);
1604
-
1605
- if(b->prototypes.available)
1606
- buffer_json_member_add_uint64(wb, "available", b->prototypes.available);
1607
- }
1608
- buffer_json_object_close(wb);
1609
- }
1610
- dfe_done(b);
1611
- }
1612
- buffer_json_array_close(wb);
1613
-}
1614
-
1615
-static void contexts_v2_alert_instances_to_json(BUFFER *wb, const char *key, struct rrdcontext_to_json_v2_data *ctl, bool debug) {
1616
- buffer_json_member_add_array(wb, key);
1617
- {
1618
- struct alert_instances_callback_data data = {
1619
- .wb = wb,
1620
- .ctl = ctl,
1621
- .debug = debug,
1622
- };
1623
- dictionary_walkthrough_rw(ctl->alerts.alert_instances, DICTIONARY_LOCK_READ,
1624
- contexts_v2_alert_instance_to_json_callback, &data);
1625
- }
1626
- buffer_json_array_close(wb); // alerts_instances
1627
-}
1628
-
1629
-static void contexts_v2_alerts_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_data *ctl, bool debug) {
1630
- if(ctl->request->options & CONTEXT_V2_OPTION_ALERTS_WITH_SUMMARY) {
1631
- buffer_json_member_add_array(wb, "alerts");
1632
- {
1633
- struct alert_v2_entry *t;
1634
- dfe_start_read(ctl->alerts.summary, t)
1635
- {
1636
- buffer_json_add_array_item_object(wb);
1637
- {
1638
- buffer_json_member_add_uint64(wb, "ati", t->ati);
1639
-
1640
- buffer_json_member_add_array(wb, "ni");
1641
- void *host_guid;
1642
- dfe_start_read(t->nodes, host_guid) {
1643
- struct contexts_v2_node *cn = dictionary_get(ctl->nodes.dict,host_guid_dfe.name);
1644
- buffer_json_add_array_item_int64(wb, (int64_t) cn->ni);
1645
- }
1646
- dfe_done(host_guid);
1647
- buffer_json_array_close(wb);
1648
-
1649
- buffer_json_member_add_string(wb, "nm", string2str(t->name));
1650
- buffer_json_member_add_string(wb, "sum", string2str(t->summary));
1651
-
1652
- buffer_json_member_add_uint64(wb, "cr", t->counts.critical);
1653
- buffer_json_member_add_uint64(wb, "wr", t->counts.warning);
1654
- buffer_json_member_add_uint64(wb, "cl", t->counts.clear);
1655
- buffer_json_member_add_uint64(wb, "er", t->counts.error);
1656
-
1657
- buffer_json_member_add_uint64(wb, "in", t->instances);
1658
- buffer_json_member_add_uint64(wb, "nd", dictionary_entries(t->nodes));
1659
- buffer_json_member_add_uint64(wb, "cfg", dictionary_entries(t->configs));
1660
-
1661
- buffer_json_member_add_array(wb, "ctx");
1662
- rrdlabels_key_to_buffer_array_item(t->context, wb);
1663
- buffer_json_array_close(wb); // ctx
1664
-
1665
- buffer_json_member_add_array(wb, "cls");
1666
- rrdlabels_key_to_buffer_array_item(t->classification, wb);
1667
- buffer_json_array_close(wb); // classification
1668
-
1669
-
1670
- buffer_json_member_add_array(wb, "cp");
1671
- rrdlabels_key_to_buffer_array_item(t->component, wb);
1672
- buffer_json_array_close(wb); // component
1673
-
1674
- buffer_json_member_add_array(wb, "ty");
1675
- rrdlabels_key_to_buffer_array_item(t->type, wb);
1676
- buffer_json_array_close(wb); // type
1677
-
1678
- buffer_json_member_add_array(wb, "to");
1679
- rrdlabels_key_to_buffer_array_item(t->recipient, wb);
1680
- buffer_json_array_close(wb); // recipient
1681
- }
1682
- buffer_json_object_close(wb); // alert name
1683
- }
1684
- dfe_done(t);
1685
- }
1686
- buffer_json_array_close(wb); // alerts
1687
-
1688
- health_prototype_metadata_foreach(ctl, contexts_v2_alerts_by_x_update_prototypes);
1689
- contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_type, "alerts_by_type");
1690
- contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_component, "alerts_by_component");
1691
- contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_classification, "alerts_by_classification");
1692
- contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_recipient, "alerts_by_recipient");
1693
- contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_module, "alerts_by_module");
1694
- }
1695
-
1696
- if(ctl->request->options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES|CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) {
1697
- contexts_v2_alert_instances_to_json(wb, "alert_instances", ctl, debug);
1698
- }
1699
-}
1700
-
1701
-#define SQL_TRANSITION_DATA_SMALL_STRING (6 * 8)
1702
-#define SQL_TRANSITION_DATA_MEDIUM_STRING (12 * 8)
1703
-#define SQL_TRANSITION_DATA_BIG_STRING 512
1704
-
1705
-struct sql_alert_transition_fixed_size {
1706
- usec_t global_id;
1707
- nd_uuid_t transition_id;
1708
- nd_uuid_t host_id;
1709
- nd_uuid_t config_hash_id;
1710
- uint32_t alarm_id;
1711
- char alert_name[SQL_TRANSITION_DATA_SMALL_STRING];
1712
- char chart[RRD_ID_LENGTH_MAX];
1713
- char chart_name[RRD_ID_LENGTH_MAX];
1714
- char chart_context[SQL_TRANSITION_DATA_MEDIUM_STRING];
1715
- char family[SQL_TRANSITION_DATA_SMALL_STRING];
1716
- char recipient[SQL_TRANSITION_DATA_MEDIUM_STRING];
1717
- char units[SQL_TRANSITION_DATA_SMALL_STRING];
1718
- char exec[SQL_TRANSITION_DATA_BIG_STRING];
1719
- char info[SQL_TRANSITION_DATA_BIG_STRING];
1720
- char summary[SQL_TRANSITION_DATA_BIG_STRING];
1721
- char classification[SQL_TRANSITION_DATA_SMALL_STRING];
1722
- char type[SQL_TRANSITION_DATA_SMALL_STRING];
1723
- char component[SQL_TRANSITION_DATA_SMALL_STRING];
1724
- time_t when_key;
1725
- time_t duration;
1726
- time_t non_clear_duration;
1727
- uint64_t flags;
1728
- time_t delay_up_to_timestamp;
1729
- time_t exec_run_timestamp;
1730
- int exec_code;
1731
- int new_status;
1732
- int old_status;
1733
- int delay;
1734
- time_t last_repeat;
1735
- NETDATA_DOUBLE new_value;
1736
- NETDATA_DOUBLE old_value;
1737
-
1738
- char machine_guid[UUID_STR_LEN];
1739
- struct sql_alert_transition_fixed_size *next;
1740
- struct sql_alert_transition_fixed_size *prev;
1741
-};
1742
-
1743
-static struct sql_alert_transition_fixed_size *contexts_v2_alert_transition_dup(struct sql_alert_transition_data *t, const char *machine_guid, struct sql_alert_transition_fixed_size *dst) {
1744
- struct sql_alert_transition_fixed_size *n = dst ? dst : mallocz(sizeof(*n));
1745
-
1746
- n->global_id = t->global_id;
1747
- uuid_copy(n->transition_id, *t->transition_id);
1748
- uuid_copy(n->host_id, *t->host_id);
1749
- uuid_copy(n->config_hash_id, *t->config_hash_id);
1750
- n->alarm_id = t->alarm_id;
1751
- strncpyz(n->alert_name, t->alert_name ? t->alert_name : "", sizeof(n->alert_name) - 1);
1752
- strncpyz(n->chart, t->chart ? t->chart : "", sizeof(n->chart) - 1);
1753
- strncpyz(n->chart_name, t->chart_name ? t->chart_name : n->chart, sizeof(n->chart_name) - 1);
1754
- strncpyz(n->chart_context, t->chart_context ? t->chart_context : "", sizeof(n->chart_context) - 1);
1755
- strncpyz(n->family, t->family ? t->family : "", sizeof(n->family) - 1);
1756
- strncpyz(n->recipient, t->recipient ? t->recipient : "", sizeof(n->recipient) - 1);
1757
- strncpyz(n->units, t->units ? t->units : "", sizeof(n->units) - 1);
1758
- strncpyz(n->exec, t->exec ? t->exec : "", sizeof(n->exec) - 1);
1759
- strncpyz(n->info, t->info ? t->info : "", sizeof(n->info) - 1);
1760
- strncpyz(n->summary, t->summary ? t->summary : "", sizeof(n->summary) - 1);
1761
- strncpyz(n->classification, t->classification ? t->classification : "", sizeof(n->classification) - 1);
1762
- strncpyz(n->type, t->type ? t->type : "", sizeof(n->type) - 1);
1763
- strncpyz(n->component, t->component ? t->component : "", sizeof(n->component) - 1);
1764
- n->when_key = t->when_key;
1765
- n->duration = t->duration;
1766
- n->non_clear_duration = t->non_clear_duration;
1767
- n->flags = t->flags;
1768
- n->delay_up_to_timestamp = t->delay_up_to_timestamp;
1769
- n->exec_run_timestamp = t->exec_run_timestamp;
1770
- n->exec_code = t->exec_code;
1771
- n->new_status = t->new_status;
1772
- n->old_status = t->old_status;
1773
- n->delay = t->delay;
1774
- n->last_repeat = t->last_repeat;
1775
- n->new_value = t->new_value;
1776
- n->old_value = t->old_value;
1777
-
1778
- memcpy(n->machine_guid, machine_guid, sizeof(n->machine_guid));
1779
- n->next = n->prev = NULL;
1780
-
1781
- return n;
1782
-}
1783
-
1784
-static void contexts_v2_alert_transition_free(struct sql_alert_transition_fixed_size *t) {
1785
- freez(t);
1786
-}
1787
-
1788
-static inline void contexts_v2_alert_transition_keep(struct alert_transitions_callback_data *d, struct sql_alert_transition_data *t, const char *machine_guid) {
1789
- d->items_matched++;
1790
-
1791
- if(unlikely(t->global_id <= d->ctl->request->alerts.global_id_anchor)) {
1792
- // this is in our past, we are not interested
1793
- d->operations.skips_before++;
1794
- return;
1795
- }
1796
-
1797
- if(unlikely(!d->base)) {
1798
- d->last_added = contexts_v2_alert_transition_dup(t, machine_guid, NULL);
1799
- DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(d->base, d->last_added, prev, next);
1800
- d->items_to_return++;
1801
- d->operations.first++;
1802
- return;
1803
- }
1804
-
1805
- struct sql_alert_transition_fixed_size *last = d->last_added;
1806
- while(last->prev != d->base->prev && t->global_id > last->prev->global_id) {
1807
- last = last->prev;
1808
- d->operations.backwards++;
1809
- }
1810
-
1811
- while(last->next && t->global_id < last->next->global_id) {
1812
- last = last->next;
1813
- d->operations.forwards++;
1814
- }
1815
-
1816
- if(d->items_to_return >= d->max_items_to_return) {
1817
- if(last == d->base->prev && t->global_id < last->global_id) {
1818
- d->operations.skips_after++;
1819
- return;
1820
- }
1821
- }
1822
-
1823
- d->items_to_return++;
1824
-
1825
- if(t->global_id > last->global_id) {
1826
- if(d->items_to_return > d->max_items_to_return) {
1827
- d->items_to_return--;
1828
- d->operations.shifts++;
1829
- d->last_added = d->base->prev;
1830
- DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(d->base, d->last_added, prev, next);
1831
- d->last_added = contexts_v2_alert_transition_dup(t, machine_guid, d->last_added);
1832
- }
1833
- DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(d->base, d->last_added, prev, next);
1834
- d->operations.prepend++;
1835
- }
1836
- else {
1837
- d->last_added = contexts_v2_alert_transition_dup(t, machine_guid, NULL);
1838
- DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(d->base, d->last_added, prev, next);
1839
- d->operations.append++;
1840
- }
1841
-
1842
- while(d->items_to_return > d->max_items_to_return) {
1843
- // we have to remove something
1844
-
1845
- struct sql_alert_transition_fixed_size *tmp = d->base->prev;
1846
- DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(d->base, tmp, prev, next);
1847
- d->items_to_return--;
1848
-
1849
- if(unlikely(d->last_added == tmp))
1850
- d->last_added = d->base;
1851
-
1852
- contexts_v2_alert_transition_free(tmp);
1853
-
1854
- d->operations.shifts++;
1855
- }
1856
-}
1857
-
1858
-static void contexts_v2_alert_transition_callback(struct sql_alert_transition_data *t, void *data) {
1859
- struct alert_transitions_callback_data *d = data;
1860
- d->items_evaluated++;
1861
-
1862
- char machine_guid[UUID_STR_LEN] = "";
1863
- uuid_unparse_lower(*t->host_id, machine_guid);
1864
-
1865
- const char *facets[ATF_TOTAL_ENTRIES] = {
1866
- [ATF_STATUS] = rrdcalc_status2string(t->new_status),
1867
- [ATF_CLASS] = t->classification,
1868
- [ATF_TYPE] = t->type,
1869
- [ATF_COMPONENT] = t->component,
1870
- [ATF_ROLE] = t->recipient && *t->recipient ? t->recipient : string2str(localhost->health.health_default_recipient),
1871
- [ATF_NODE] = machine_guid,
1872
- [ATF_ALERT_NAME] = t->alert_name,
1873
- [ATF_CHART_NAME] = t->chart_name,
1874
- [ATF_CONTEXT] = t->chart_context,
1875
- };
1876
-
1877
- for(size_t i = 0; i < ATF_TOTAL_ENTRIES ;i++) {
1878
- if (!facets[i] || !*facets[i]) facets[i] = "unknown";
1879
-
1880
- struct facet_entry tmp = {
1881
- .count = 0,
1882
- };
1883
- dictionary_set(d->facets[i].dict, facets[i], &tmp, sizeof(tmp));
1884
- }
1885
-
1886
- bool selected[ATF_TOTAL_ENTRIES] = { 0 };
1887
-
1888
- uint32_t selected_by = 0;
1889
- for(size_t i = 0; i < ATF_TOTAL_ENTRIES ;i++) {
1890
- selected[i] = !d->facets[i].pattern || simple_pattern_matches(d->facets[i].pattern, facets[i]);
1891
- if(selected[i])
1892
- selected_by++;
1893
- }
1894
-
1895
- if(selected_by == ATF_TOTAL_ENTRIES) {
1896
- // this item is selected by all facets
1897
- // put it in our result (if it fits)
1898
- contexts_v2_alert_transition_keep(d, t, machine_guid);
1899
- }
1900
-
1901
- if(selected_by >= ATF_TOTAL_ENTRIES - 1) {
1902
- // this item is selected by all, or all except one facet
1903
- // in both cases we need to add it to our counters
1904
-
1905
- for (size_t i = 0; i < ATF_TOTAL_ENTRIES; i++) {
1906
- uint32_t counted_by = selected_by;
1907
-
1908
- if (counted_by != ATF_TOTAL_ENTRIES) {
1909
- counted_by = 0;
1910
- for (size_t j = 0; j < ATF_TOTAL_ENTRIES; j++) {
1911
- if (i == j || selected[j])
1912
- counted_by++;
1913
- }
1914
- }
1915
-
1916
- if (counted_by == ATF_TOTAL_ENTRIES) {
1917
- // we need to count it on this facet
1918
- struct facet_entry *x = dictionary_get(d->facets[i].dict, facets[i]);
1919
- internal_fatal(!x, "facet is not found");
1920
- if(x)
1921
- x->count++;
1922
- }
1923
- }
1924
- }
1925
-}
1926
-
1927
-static void contexts_v2_alert_transitions_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_data *ctl, bool debug) {
1928
- struct alert_transitions_callback_data data = {
1929
- .wb = wb,
1930
- .ctl = ctl,
1931
- .debug = debug,
1932
- .only_one_config = true,
1933
- .max_items_to_return = ctl->request->alerts.last,
1934
- .items_to_return = 0,
1935
- .base = NULL,
1936
- };
1937
-
1938
- for(size_t i = 0; i < ATF_TOTAL_ENTRIES ;i++) {
1939
- data.facets[i].dict = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_FIXED_SIZE | DICT_OPTION_DONT_OVERWRITE_VALUE, NULL, sizeof(struct facet_entry));
1940
- if(ctl->request->alerts.facets[i])
1941
- data.facets[i].pattern = simple_pattern_create(ctl->request->alerts.facets[i], ",|", SIMPLE_PATTERN_EXACT, false);
1942
- }
1943
-
1944
- sql_alert_transitions(
1945
- ctl->nodes.dict,
1946
- ctl->window.after,
1947
- ctl->window.before,
1948
- ctl->request->contexts,
1949
- ctl->request->alerts.alert,
1950
- ctl->request->alerts.transition,
1951
- contexts_v2_alert_transition_callback,
1952
- &data,
1953
- debug);
1954
-
1955
- buffer_json_member_add_array(wb, "facets");
1956
- for (size_t i = 0; i < ATF_TOTAL_ENTRIES; i++) {
1957
- buffer_json_add_array_item_object(wb);
1958
- {
1959
- buffer_json_member_add_string(wb, "id", alert_transition_facets[i].id);
1960
- buffer_json_member_add_string(wb, "name", alert_transition_facets[i].name);
1961
- buffer_json_member_add_uint64(wb, "order", alert_transition_facets[i].order);
1962
- buffer_json_member_add_array(wb, "options");
1963
- {
1964
- struct facet_entry *x;
1965
- dfe_start_read(data.facets[i].dict, x) {
1966
- buffer_json_add_array_item_object(wb);
1967
- {
1968
- buffer_json_member_add_string(wb, "id", x_dfe.name);
1969
- if (i == ATF_NODE) {
1970
- RRDHOST *host = rrdhost_find_by_guid(x_dfe.name);
1971
- if (host)
1972
- buffer_json_member_add_string(wb, "name", rrdhost_hostname(host));
1973
- else
1974
- buffer_json_member_add_string(wb, "name", x_dfe.name);
1975
- } else
1976
- buffer_json_member_add_string(wb, "name", x_dfe.name);
1977
- buffer_json_member_add_uint64(wb, "count", x->count);
1978
- }
1979
- buffer_json_object_close(wb);
1980
- }
1981
- dfe_done(x);
1982
- }
1983
- buffer_json_array_close(wb); // options
1984
- }
1985
- buffer_json_object_close(wb); // facet
1986
- }
1987
- buffer_json_array_close(wb); // facets
1988
-
1989
- buffer_json_member_add_array(wb, "transitions");
1990
- for(struct sql_alert_transition_fixed_size *t = data.base; t ; t = t->next) {
1991
- buffer_json_add_array_item_object(wb);
1992
- {
1993
- RRDHOST *host = rrdhost_find_by_guid(t->machine_guid);
1994
-
1995
- buffer_json_member_add_uint64(wb, "gi", t->global_id);
1996
- buffer_json_member_add_uuid(wb, "transition_id", &t->transition_id);
1997
- buffer_json_member_add_uuid(wb, "config_hash_id", &t->config_hash_id);
1998
- buffer_json_member_add_string(wb, "machine_guid", t->machine_guid);
1999
-
2000
- if(host) {
2001
- buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(host));
2002
-
2003
- if(host->node_id)
2004
- buffer_json_member_add_uuid(wb, "node_id", host->node_id);
2005
- }
2006
-
2007
- buffer_json_member_add_string(wb, "alert", *t->alert_name ? t->alert_name : NULL);
2008
- buffer_json_member_add_string(wb, "instance", *t->chart ? t->chart : NULL);
2009
- buffer_json_member_add_string(wb, "instance_n", *t->chart_name ? t->chart_name : NULL);
2010
- buffer_json_member_add_string(wb, "context", *t->chart_context ? t->chart_context : NULL);
2011
- // buffer_json_member_add_string(wb, "family", *t->family ? t->family : NULL);
2012
- buffer_json_member_add_string(wb, "component", *t->component ? t->component : NULL);
2013
- buffer_json_member_add_string(wb, "classification", *t->classification ? t->classification : NULL);
2014
- buffer_json_member_add_string(wb, "type", *t->type ? t->type : NULL);
2015
-
2016
- buffer_json_member_add_time_t(wb, "when", t->when_key);
2017
- buffer_json_member_add_string(wb, "info", *t->info ? t->info : "");
2018
- buffer_json_member_add_string(wb, "summary", *t->summary ? t->summary : "");
2019
- buffer_json_member_add_string(wb, "units", *t->units ? t->units : NULL);
2020
- buffer_json_member_add_object(wb, "new");
2021
- {
2022
- buffer_json_member_add_string(wb, "status", rrdcalc_status2string(t->new_status));
2023
- buffer_json_member_add_double(wb, "value", t->new_value);
2024
- }
2025
- buffer_json_object_close(wb); // new
2026
- buffer_json_member_add_object(wb, "old");
2027
- {
2028
- buffer_json_member_add_string(wb, "status", rrdcalc_status2string(t->old_status));
2029
- buffer_json_member_add_double(wb, "value", t->old_value);
2030
- buffer_json_member_add_time_t(wb, "duration", t->duration);
2031
- buffer_json_member_add_time_t(wb, "raised_duration", t->non_clear_duration);
2032
- }
2033
- buffer_json_object_close(wb); // old
2034
-
2035
- buffer_json_member_add_object(wb, "notification");
2036
- {
2037
- buffer_json_member_add_time_t(wb, "when", t->exec_run_timestamp);
2038
- buffer_json_member_add_time_t(wb, "delay", t->delay);
2039
- buffer_json_member_add_time_t(wb, "delay_up_to_time", t->delay_up_to_timestamp);
2040
- health_entry_flags_to_json_array(wb, "flags", t->flags);
2041
- buffer_json_member_add_string(wb, "exec", *t->exec ? t->exec : string2str(localhost->health.health_default_exec));
2042
- buffer_json_member_add_uint64(wb, "exec_code", t->exec_code);
2043
- buffer_json_member_add_string(wb, "to", *t->recipient ? t->recipient : string2str(localhost->health.health_default_recipient));
2044
- }
2045
- buffer_json_object_close(wb); // notification
2046
- }
2047
- buffer_json_object_close(wb); // a transition
2048
- }
2049
- buffer_json_array_close(wb); // all transitions
2050
-
2051
- if(ctl->options & CONTEXT_V2_OPTION_ALERTS_WITH_CONFIGURATIONS) {
2052
- DICTIONARY *configs = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
2053
-
2054
- for(struct sql_alert_transition_fixed_size *t = data.base; t ; t = t->next) {
2055
- char guid[UUID_STR_LEN];
2056
- uuid_unparse_lower(t->config_hash_id, guid);
2057
- dictionary_set(configs, guid, NULL, 0);
2058
- }
2059
-
2060
- buffer_json_member_add_array(wb, "configurations");
2061
- sql_get_alert_configuration(configs, contexts_v2_alert_config_to_json_from_sql_alert_config_data, &data, debug);
2062
- buffer_json_array_close(wb);
2063
-
2064
- dictionary_destroy(configs);
2065
- }
2066
-
2067
- while(data.base) {
2068
- struct sql_alert_transition_fixed_size *t = data.base;
2069
- DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(data.base, t, prev, next);
2070
- contexts_v2_alert_transition_free(t);
2071
- }
2072
-
2073
- for(size_t i = 0; i < ATF_TOTAL_ENTRIES ;i++) {
2074
- dictionary_destroy(data.facets[i].dict);
2075
- simple_pattern_free(data.facets[i].pattern);
2076
- }
2077
-
2078
- buffer_json_member_add_object(wb, "items");
2079
- {
2080
- // all the items in the window, under the scope_nodes, ignoring the facets (filters)
2081
- buffer_json_member_add_uint64(wb, "evaluated", data.items_evaluated);
2082
-
2083
- // all the items matching the query (if you didn't put anchor_gi and last, these are all the items you would get back)
2084
- buffer_json_member_add_uint64(wb, "matched", data.items_matched);
2085
-
2086
- // the items included in this response
2087
- buffer_json_member_add_uint64(wb, "returned", data.items_to_return);
2088
-
2089
- // same as last=X parameter
2090
- buffer_json_member_add_uint64(wb, "max_to_return", data.max_items_to_return);
2091
-
2092
- // items before the first returned, this should be 0 if anchor_gi is not set
2093
- buffer_json_member_add_uint64(wb, "before", data.operations.skips_before);
2094
-
2095
- // items after the last returned, when this is zero there aren't any items after the current list
2096
- buffer_json_member_add_uint64(wb, "after", data.operations.skips_after + data.operations.shifts);
2097
- }
2098
- buffer_json_object_close(wb); // items
2099
-
2100
- if(debug) {
2101
- buffer_json_member_add_object(wb, "stats");
2102
- {
2103
- buffer_json_member_add_uint64(wb, "first", data.operations.first);
2104
- buffer_json_member_add_uint64(wb, "prepend", data.operations.prepend);
2105
- buffer_json_member_add_uint64(wb, "append", data.operations.append);
2106
- buffer_json_member_add_uint64(wb, "backwards", data.operations.backwards);
2107
- buffer_json_member_add_uint64(wb, "forwards", data.operations.forwards);
2108
- buffer_json_member_add_uint64(wb, "shifts", data.operations.shifts);
2109
- buffer_json_member_add_uint64(wb, "skips_before", data.operations.skips_before);
2110
- buffer_json_member_add_uint64(wb, "skips_after", data.operations.skips_after);
2111
- }
2112
- buffer_json_object_close(wb);
2113
- }
2114
-}
2115
-
2116
-int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_MODE mode) {
2117
- int resp = HTTP_RESP_OK;
2118
- bool run = true;
2119
-
2120
- if(mode & CONTEXTS_V2_SEARCH)
2121
- mode |= CONTEXTS_V2_CONTEXTS;
2122
-
2123
- if(mode & (CONTEXTS_V2_AGENTS_INFO))
2124
- mode |= CONTEXTS_V2_AGENTS;
2125
-
2126
- if(mode & (CONTEXTS_V2_FUNCTIONS | CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODE_INSTANCES))
2127
- mode |= CONTEXTS_V2_NODES;
2128
-
2129
- if(mode & CONTEXTS_V2_ALERTS) {
2130
- mode |= CONTEXTS_V2_NODES;
2131
- req->options &= ~CONTEXT_V2_OPTION_ALERTS_WITH_CONFIGURATIONS;
2132
-
2133
- if(!(req->options & (CONTEXT_V2_OPTION_ALERTS_WITH_SUMMARY|CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES|CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)))
2134
- req->options |= CONTEXT_V2_OPTION_ALERTS_WITH_SUMMARY;
2135
- }
2136
-
2137
- if(mode & CONTEXTS_V2_ALERT_TRANSITIONS) {
2138
- mode |= CONTEXTS_V2_NODES;
2139
- req->options &= ~CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES;
2140
- }
2141
-
2142
- struct rrdcontext_to_json_v2_data ctl = {
2143
- .wb = wb,
2144
- .request = req,
2145
- .mode = mode,
2146
- .options = req->options,
2147
- .versions = { 0 },
2148
- .nodes.scope_pattern = string_to_simple_pattern(req->scope_nodes),
2149
- .nodes.pattern = string_to_simple_pattern(req->nodes),
2150
- .contexts.pattern = string_to_simple_pattern(req->contexts),
2151
- .contexts.scope_pattern = string_to_simple_pattern(req->scope_contexts),
2152
- .q.pattern = string_to_simple_pattern_nocase(req->q),
2153
- .alerts.alert_name_pattern = string_to_simple_pattern(req->alerts.alert),
2154
- .window = {
2155
- .enabled = false,
2156
- .relative = false,
2157
- .after = req->after,
2158
- .before = req->before,
2159
- },
2160
- .timings = {
2161
- .received_ut = now_monotonic_usec(),
2162
- }
2163
- };
2164
-
2165
- bool debug = ctl.options & CONTEXT_V2_OPTION_DEBUG;
2166
-
2167
- if(mode & CONTEXTS_V2_NODES) {
2168
- ctl.nodes.dict = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2169
- NULL, sizeof(struct contexts_v2_node));
2170
- }
2171
-
2172
- if(mode & CONTEXTS_V2_CONTEXTS) {
2173
- ctl.contexts.dict = dictionary_create_advanced(
2174
- DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL,
2175
- sizeof(struct context_v2_entry));
2176
-
2177
- dictionary_register_conflict_callback(ctl.contexts.dict, contexts_conflict_callback, &ctl);
2178
- dictionary_register_delete_callback(ctl.contexts.dict, contexts_delete_callback, &ctl);
2179
- }
2180
-
2181
- if(mode & CONTEXTS_V2_FUNCTIONS) {
2182
- ctl.functions.dict = dictionary_create_advanced(
2183
- DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL,
2184
- sizeof(struct function_v2_entry));
2185
-
2186
- dictionary_register_insert_callback(ctl.functions.dict, functions_insert_callback, &ctl);
2187
- dictionary_register_conflict_callback(ctl.functions.dict, functions_conflict_callback, &ctl);
2188
- dictionary_register_delete_callback(ctl.functions.dict, functions_delete_callback, &ctl);
2189
- }
2190
-
2191
- if(mode & CONTEXTS_V2_ALERTS) {
2192
- if(req->alerts.transition) {
2193
- ctl.options |= CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES|CONTEXT_V2_OPTION_ALERTS_WITH_VALUES;
2194
- run = sql_find_alert_transition(req->alerts.transition, rrdcontext_v2_set_transition_filter, &ctl);
2195
- if(!run) {
2196
- resp = HTTP_RESP_NOT_FOUND;
2197
- goto cleanup;
2198
- }
2199
- }
2200
-
2201
- ctl.alerts.summary = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2202
- NULL, sizeof(struct alert_v2_entry));
2203
-
2204
- dictionary_register_insert_callback(ctl.alerts.summary, alerts_v2_insert_callback, &ctl);
2205
- dictionary_register_conflict_callback(ctl.alerts.summary, alerts_v2_conflict_callback, &ctl);
2206
- dictionary_register_delete_callback(ctl.alerts.summary, alerts_v2_delete_callback, &ctl);
2207
-
2208
- ctl.alerts.by_type = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2209
- NULL, sizeof(struct alert_by_x_entry));
2210
-
2211
- dictionary_register_insert_callback(ctl.alerts.by_type, alerts_by_x_insert_callback, NULL);
2212
- dictionary_register_conflict_callback(ctl.alerts.by_type, alerts_by_x_conflict_callback, NULL);
2213
-
2214
- ctl.alerts.by_component = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2215
- NULL, sizeof(struct alert_by_x_entry));
2216
-
2217
- dictionary_register_insert_callback(ctl.alerts.by_component, alerts_by_x_insert_callback, NULL);
2218
- dictionary_register_conflict_callback(ctl.alerts.by_component, alerts_by_x_conflict_callback, NULL);
2219
-
2220
- ctl.alerts.by_classification = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2221
- NULL, sizeof(struct alert_by_x_entry));
2222
-
2223
- dictionary_register_insert_callback(ctl.alerts.by_classification, alerts_by_x_insert_callback, NULL);
2224
- dictionary_register_conflict_callback(ctl.alerts.by_classification, alerts_by_x_conflict_callback, NULL);
2225
-
2226
- ctl.alerts.by_recipient = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2227
- NULL, sizeof(struct alert_by_x_entry));
2228
-
2229
- dictionary_register_insert_callback(ctl.alerts.by_recipient, alerts_by_x_insert_callback, NULL);
2230
- dictionary_register_conflict_callback(ctl.alerts.by_recipient, alerts_by_x_conflict_callback, NULL);
2231
-
2232
- ctl.alerts.by_module = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2233
- NULL, sizeof(struct alert_by_x_entry));
2234
-
2235
- dictionary_register_insert_callback(ctl.alerts.by_module, alerts_by_x_insert_callback, NULL);
2236
- dictionary_register_conflict_callback(ctl.alerts.by_module, alerts_by_x_conflict_callback, NULL);
2237
-
2238
- if(ctl.options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES | CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) {
2239
- ctl.alerts.alert_instances = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2240
- NULL, sizeof(struct sql_alert_instance_v2_entry));
2241
-
2242
- dictionary_register_insert_callback(ctl.alerts.alert_instances, alert_instances_v2_insert_callback, &ctl);
2243
- dictionary_register_conflict_callback(ctl.alerts.alert_instances, alert_instances_v2_conflict_callback, &ctl);
2244
- dictionary_register_delete_callback(ctl.alerts.alert_instances, alert_instances_delete_callback, &ctl);
2245
- }
2246
- }
2247
-
2248
- if(req->after || req->before) {
2249
- ctl.window.relative = rrdr_relative_window_to_absolute_query(&ctl.window.after, &ctl.window.before, &ctl.now
2250
- , false
2251
- );
2252
- ctl.window.enabled = !(mode & CONTEXTS_V2_ALERT_TRANSITIONS);
2253
- }
2254
- else
2255
- ctl.now = now_realtime_sec();
2256
-
2257
- buffer_json_initialize(wb, "\"", "\"", 0, true,
2258
- ((req->options & CONTEXT_V2_OPTION_MINIFY) && !(req->options & CONTEXT_V2_OPTION_DEBUG)) ? BUFFER_JSON_OPTIONS_MINIFY : BUFFER_JSON_OPTIONS_DEFAULT);
2259
-
2260
- buffer_json_member_add_uint64(wb, "api", 2);
2261
-
2262
- if(req->options & CONTEXT_V2_OPTION_DEBUG) {
2263
- buffer_json_member_add_object(wb, "request");
2264
- {
2265
- buffer_json_contexts_v2_mode_to_array(wb, "mode", mode);
2266
- web_client_api_request_v2_contexts_options_to_buffer_json_array(wb, "options", req->options);
2267
-
2268
- buffer_json_member_add_object(wb, "scope");
2269
- {
2270
- buffer_json_member_add_string(wb, "scope_nodes", req->scope_nodes);
2271
- if (mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_ALERTS))
2272
- buffer_json_member_add_string(wb, "scope_contexts", req->scope_contexts);
2273
- }
2274
- buffer_json_object_close(wb);
2275
-
2276
- buffer_json_member_add_object(wb, "selectors");
2277
- {
2278
- buffer_json_member_add_string(wb, "nodes", req->nodes);
2279
-
2280
- if (mode & (CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_SEARCH | CONTEXTS_V2_ALERTS))
2281
- buffer_json_member_add_string(wb, "contexts", req->contexts);
2282
-
2283
- if(mode & (CONTEXTS_V2_ALERTS | CONTEXTS_V2_ALERT_TRANSITIONS)) {
2284
- buffer_json_member_add_object(wb, "alerts");
2285
-
2286
- if(mode & CONTEXTS_V2_ALERTS)
2287
- web_client_api_request_v2_contexts_alerts_status_to_buffer_json_array(wb, "status", req->alerts.status);
2288
-
2289
- if(mode & CONTEXTS_V2_ALERT_TRANSITIONS) {
2290
- buffer_json_member_add_string(wb, "context", req->contexts);
2291
- buffer_json_member_add_uint64(wb, "anchor_gi", req->alerts.global_id_anchor);
2292
- buffer_json_member_add_uint64(wb, "last", req->alerts.last);
2293
- }
2294
-
2295
- buffer_json_member_add_string(wb, "alert", req->alerts.alert);
2296
- buffer_json_member_add_string(wb, "transition", req->alerts.transition);
2297
- buffer_json_object_close(wb); // alerts
2298
- }
2299
- }
2300
- buffer_json_object_close(wb); // selectors
2301
-
2302
- buffer_json_member_add_object(wb, "filters");
2303
- {
2304
- if (mode & CONTEXTS_V2_SEARCH)
2305
- buffer_json_member_add_string(wb, "q", req->q);
2306
-
2307
- buffer_json_member_add_time_t(wb, "after", req->after);
2308
- buffer_json_member_add_time_t(wb, "before", req->before);
2309
- }
2310
- buffer_json_object_close(wb); // filters
2311
-
2312
- if(mode & CONTEXTS_V2_ALERT_TRANSITIONS) {
2313
- buffer_json_member_add_object(wb, "facets");
2314
- {
2315
- for (int i = 0; i < ATF_TOTAL_ENTRIES; i++) {
2316
- buffer_json_member_add_string(wb, alert_transition_facets[i].query_param, req->alerts.facets[i]);
2317
- }
2318
- }
2319
- buffer_json_object_close(wb); // facets
2320
- }
2321
- }
2322
- buffer_json_object_close(wb);
2323
- }
2324
-
2325
- ssize_t ret = 0;
2326
- if(run)
2327
- ret = query_scope_foreach_host(ctl.nodes.scope_pattern, ctl.nodes.pattern,
2328
- rrdcontext_to_json_v2_add_host, &ctl,
2329
- &ctl.versions, ctl.q.host_node_id_str);
2330
-
2331
- if(unlikely(ret < 0)) {
2332
- buffer_flush(wb);
2333
-
2334
- if(ret == -2) {
2335
- buffer_strcat(wb, "query timeout");
2336
- resp = HTTP_RESP_GATEWAY_TIMEOUT;
2337
- }
2338
- else {
2339
- buffer_strcat(wb, "query interrupted");
2340
- resp = HTTP_RESP_CLIENT_CLOSED_REQUEST;
2341
- }
2342
- goto cleanup;
2343
- }
2344
-
2345
- ctl.timings.executed_ut = now_monotonic_usec();
2346
-
2347
- if(mode & CONTEXTS_V2_ALERT_TRANSITIONS) {
2348
- contexts_v2_alert_transitions_to_json(wb, &ctl, debug);
2349
- }
2350
- else {
2351
- if (mode & CONTEXTS_V2_NODES) {
2352
- buffer_json_member_add_array(wb, "nodes");
2353
- struct contexts_v2_node *t;
2354
- dfe_start_read(ctl.nodes.dict, t) {
2355
- rrdcontext_to_json_v2_rrdhost(wb, t->host, &ctl, t->ni);
2356
- }
2357
- dfe_done(t);
2358
- buffer_json_array_close(wb);
2359
- }
2360
-
2361
- if (mode & CONTEXTS_V2_FUNCTIONS) {
2362
- buffer_json_member_add_array(wb, "functions");
2363
- {
2364
- struct function_v2_entry *t;
2365
- dfe_start_read(ctl.functions.dict, t) {
2366
- buffer_json_add_array_item_object(wb);
2367
- {
2368
- buffer_json_member_add_string(wb, "name", t_dfe.name);
2369
- buffer_json_member_add_string(wb, "help", string2str(t->help));
2370
- buffer_json_member_add_array(wb, "ni");
2371
- {
2372
- for (size_t i = 0; i < t->used; i++)
2373
- buffer_json_add_array_item_uint64(wb, t->node_ids[i]);
2374
- }
2375
- buffer_json_array_close(wb);
2376
- buffer_json_member_add_string(wb, "tags", string2str(t->tags));
2377
- http_access2buffer_json_array(wb, "access", t->access);
2378
- buffer_json_member_add_uint64(wb, "priority", t->priority);
2379
- }
2380
- buffer_json_object_close(wb);
2381
- }
2382
- dfe_done(t);
2383
- }
2384
- buffer_json_array_close(wb);
2385
- }
2386
-
2387
- if (mode & CONTEXTS_V2_CONTEXTS) {
2388
- buffer_json_member_add_object(wb, "contexts");
2389
- {
2390
- struct context_v2_entry *z;
2391
- dfe_start_read(ctl.contexts.dict, z) {
2392
- bool collected = z->flags & RRD_FLAG_COLLECTED;
2393
-
2394
- buffer_json_member_add_object(wb, string2str(z->id));
2395
- {
2396
- buffer_json_member_add_string(wb, "family", string2str(z->family));
2397
- buffer_json_member_add_uint64(wb, "priority", z->priority);
2398
- buffer_json_member_add_time_t(wb, "first_entry", z->first_time_s);
2399
- buffer_json_member_add_time_t(wb, "last_entry", collected ? ctl.now : z->last_time_s);
2400
- buffer_json_member_add_boolean(wb, "live", collected);
2401
- if (mode & CONTEXTS_V2_SEARCH)
2402
- buffer_json_member_add_string(wb, "match", fts_match_to_string(z->match));
2403
- }
2404
- buffer_json_object_close(wb);
2405
- }
2406
- dfe_done(z);
2407
- }
2408
- buffer_json_object_close(wb); // contexts
2409
- }
2410
-
2411
- if (mode & CONTEXTS_V2_ALERTS)
2412
- contexts_v2_alerts_to_json(wb, &ctl, debug);
2413
-
2414
- if (mode & CONTEXTS_V2_SEARCH) {
2415
- buffer_json_member_add_object(wb, "searches");
2416
- {
2417
- buffer_json_member_add_uint64(wb, "strings", ctl.q.fts.string_searches);
2418
- buffer_json_member_add_uint64(wb, "char", ctl.q.fts.char_searches);
2419
- buffer_json_member_add_uint64(wb, "total", ctl.q.fts.searches);
2420
- }
2421
- buffer_json_object_close(wb);
2422
- }
2423
-
2424
- if (mode & (CONTEXTS_V2_VERSIONS))
2425
- version_hashes_api_v2(wb, &ctl.versions);
2426
-
2427
- if (mode & CONTEXTS_V2_AGENTS)
2428
- buffer_json_agents_v2(wb, &ctl.timings, ctl.now, mode & (CONTEXTS_V2_AGENTS_INFO), true);
2429
- }
2430
-
2431
- buffer_json_cloud_timings(wb, "timings", &ctl.timings);
2432
-
2433
- buffer_json_finalize(wb);
2434
-
2435
-cleanup:
2436
- dictionary_destroy(ctl.nodes.dict);
2437
- dictionary_destroy(ctl.contexts.dict);
2438
- dictionary_destroy(ctl.functions.dict);
2439
- dictionary_destroy(ctl.alerts.summary);
2440
- dictionary_destroy(ctl.alerts.alert_instances);
2441
- dictionary_destroy(ctl.alerts.by_type);
2442
- dictionary_destroy(ctl.alerts.by_component);
2443
- dictionary_destroy(ctl.alerts.by_classification);
2444
- dictionary_destroy(ctl.alerts.by_recipient);
2445
- dictionary_destroy(ctl.alerts.by_module);
2446
- simple_pattern_free(ctl.nodes.scope_pattern);
2447
- simple_pattern_free(ctl.nodes.pattern);
2448
- simple_pattern_free(ctl.contexts.pattern);
2449
- simple_pattern_free(ctl.contexts.scope_pattern);
2450
- simple_pattern_free(ctl.q.pattern);
2451
- simple_pattern_free(ctl.alerts.alert_name_pattern);
2452
-
2453
- return resp;
2454
-}