1
// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
#define NETDATA_RRD_INTERNALS
3
-#define NETDATA_RRDCOLLECTOR_INTERNALS
4
5
#include "rrd.h"
6
+#include "rrdfunctions-internals.h"
7
8
#define MAX_FUNCTION_LENGTH (PLUGINSD_LINE_MAX - 512) // we need some space for the rest of the line
9
18
[30] = '_', [31] = '_',
19
20
// symbols
20
- [' '] = ' ', ['!'] = '!', ['"'] = '"', ['#'] = '#', ['$'] = '$', ['%'] = '%', ['&'] = '&', ['\''] = '\'',
21
+ [' '] = ' ', ['!'] = '!', ['"'] = '\'', ['#'] = '#', ['$'] = '$', ['%'] = '%', ['&'] = '&', ['\''] = '\'',
22
['('] = '(', [')'] = ')', ['*'] = '*', ['+'] = '+', [','] = ',', ['-'] = '-', ['.'] = '.', ['/'] = '/',
23
24
// numbers
66
[255] = '_'
67
};
68
68
-static inline size_t sanitize_function_text(char *dst, const char *src, size_t dst_len) {
69
+size_t rrd_functions_sanitize(char *dst, const char *src, size_t dst_len) {
70
return text_sanitize((unsigned char *)dst, (const unsigned char *)src, dst_len,
71
functions_allowed_chars, true, "", NULL);
72
}
73
73
-// we keep a dictionary per RRDSET with these functions
74
-// the dictionary is created on demand (only when a function is added to an RRDSET)
75
-
76
-typedef enum __attribute__((packed)) {
77
- RRD_FUNCTION_LOCAL = (1 << 0),
78
- RRD_FUNCTION_GLOBAL = (1 << 1),
79
-
80
- // this is 8-bit
81
-} RRD_FUNCTION_OPTIONS;
82
-
74
// ----------------------------------------------------------------------------
75
85
-struct rrd_host_function {
86
- bool sync; // when true, the function is called synchronously
87
- RRD_FUNCTION_OPTIONS options; // RRD_FUNCTION_OPTIONS
88
- HTTP_ACCESS access;
89
- STRING *help;
90
- STRING *tags;
91
- int timeout; // the default timeout of the function
92
- int priority;
93
-
94
- rrd_function_execute_cb_t execute_cb;
95
- void *execute_cb_data;
96
-
97
- struct rrd_collector *collector;
98
-};
99
-
100
-struct rrd_function_inflight {
101
- bool used;
102
-
103
- RRDHOST *host;
104
- uuid_t transaction_uuid;
105
- const char *transaction;
106
- const char *cmd;
107
- const char *sanitized_cmd;
108
- size_t sanitized_cmd_length;
109
- int timeout;
110
- bool cancelled;
111
- usec_t stop_monotonic_ut;
112
-
113
- const DICTIONARY_ITEM *host_function_acquired;
114
-
115
- // the collector
116
- // we acquire this structure at the beginning,
117
- // and we release it at the end
118
- struct rrd_host_function *rdcf;
119
-
120
- struct {
121
- BUFFER *wb;
122
-
123
- // in async mode,
124
- // the function to call to send the result back
125
- rrd_function_result_callback_t cb;
126
- void *data;
127
- } result;
128
-
129
- struct {
130
- // to be called in sync mode
131
- // while the function is running
132
- // to check if the function has been canceled
133
- rrd_function_is_cancelled_cb_t cb;
134
- void *data;
135
- } is_cancelled;
136
-
137
- struct {
138
- // to be registered by the function itself
139
- // used to signal the function to cancel
140
- rrd_function_cancel_cb_t cb;
141
- void *data;
142
- } canceller;
143
-
144
- struct {
145
- // callback to receive progress reports from function
146
- rrd_function_progress_cb_t cb;
147
- void *data;
148
- } progress;
149
-
150
- struct {
151
- // to be registered by the function itself
152
- // used to send progress requests to function
153
- rrd_function_progresser_cb_t cb;
154
- void *data;
155
- } progresser;
156
-};
157
-
158
-static DICTIONARY *rrd_functions_inflight_requests = NULL;
159
-
160
-static void rrd_function_cancel_inflight(struct rrd_function_inflight *r);
76
+// we keep a dictionary per RRDSET with these functions
77
+// the dictionary is created on demand (only when a function is added to an RRDSET)
78
79
// ----------------------------------------------------------------------------
80
208
return changed;
209
}
210
294
-void rrdfunctions_host_init(RRDHOST *host) {
211
+void rrd_functions_host_init(RRDHOST *host) {
212
if(host->functions) return;
213
214
host->functions = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
219
dictionary_register_conflict_callback(host->functions, rrd_functions_conflict_callback, host);
220
}
221
305
-void rrdfunctions_host_destroy(RRDHOST *host) {
222
+void rrd_functions_host_destroy(RRDHOST *host) {
223
dictionary_destroy(host->functions);
224
}
225
226
// ----------------------------------------------------------------------------
227
311
-void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority, const char *help, const char *tags,
312
- HTTP_ACCESS access, bool sync, rrd_function_execute_cb_t execute_cb,
313
- void *execute_cb_data) {
228
+static inline bool is_function_dyncfg(const char *name) {
229
+ if(!name || !*name)
230
+ return false;
231
+
232
+ if(strncmp(name, PLUGINSD_FUNCTION_CONFIG, sizeof(PLUGINSD_FUNCTION_CONFIG) - 1) != 0)
233
+ return false;
234
+
235
+ char c = name[sizeof(PLUGINSD_FUNCTION_CONFIG) - 1];
236
+ if(c == 0 || isspace(c))
237
+ return true;
238
+
239
+ return false;
240
+}
241
+
242
+void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority,
243
+ const char *help, const char *tags, HTTP_ACCESS access, bool sync,
244
+ rrd_function_execute_cb_t execute_cb, void *execute_cb_data) {
245
246
// RRDSET *st may be NULL in this function
247
// to create a GLOBAL function
256
if(st && !st->functions_view)
257
st->functions_view = dictionary_create_view(host->functions);
258
328
- char key[PLUGINSD_LINE_MAX + 1];
329
- sanitize_function_text(key, name, PLUGINSD_LINE_MAX);
259
+ char key[strlen(name) + 1];
260
+ rrd_functions_sanitize(key, name, sizeof(key));
261
262
struct rrd_host_function tmp = {
263
.sync = sync,
264
.timeout = timeout,
334
- .options = (st)?RRD_FUNCTION_LOCAL:RRD_FUNCTION_GLOBAL,
265
+ .options = st ? RRD_FUNCTION_LOCAL: (is_function_dyncfg(name) ? RRD_FUNCTION_DYNCFG : RRD_FUNCTION_GLOBAL),
266
.access = access,
267
.execute_cb = execute_cb,
268
.execute_cb_data = execute_cb_data,
280
dictionary_acquired_item_release(host->functions, item);
281
}
282
352
-void rrd_functions_expose_rrdpush(RRDSET *st, BUFFER *wb) {
353
- if(!st->functions_view)
354
- return;
355
-
356
- struct rrd_host_function *tmp;
357
- dfe_start_read(st->functions_view, tmp) {
358
- buffer_sprintf(wb
359
- , PLUGINSD_KEYWORD_FUNCTION " \"%s\" %d \"%s\" \"%s\" \"%s\" %d\n"
360
- , tmp_dfe.name
361
- , tmp->timeout
362
- , string2str(tmp->help)
363
- , string2str(tmp->tags)
364
- , http_id2access(tmp->access)
365
- , tmp->priority
366
- );
367
- }
368
- dfe_done(tmp);
369
-}
370
-
371
-void rrd_functions_expose_global_rrdpush(RRDHOST *host, BUFFER *wb) {
372
- rrdhost_flag_clear(host, RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED);
373
-
374
- struct rrd_host_function *tmp;
375
- dfe_start_read(host->functions, tmp) {
376
- if(!(tmp->options & RRD_FUNCTION_GLOBAL))
377
- continue;
378
-
379
- buffer_sprintf(wb
380
- , PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"%s\" \"%s\" %d\n"
381
- , tmp_dfe.name
382
- , tmp->timeout
383
- , string2str(tmp->help)
384
- , string2str(tmp->tags)
385
- , http_id2access(tmp->access)
386
- , tmp->priority
387
- );
388
- }
389
- dfe_done(tmp);
390
-}
391
-
392
-struct {
393
- const char *format;
394
- HTTP_CONTENT_TYPE content_type;
395
-} function_formats[] = {
396
- { .format = "application/json", CT_APPLICATION_JSON },
397
- { .format = "text/plain", CT_TEXT_PLAIN },
398
- { .format = "application/xml", CT_APPLICATION_XML },
399
- { .format = "prometheus", CT_PROMETHEUS },
400
- { .format = "text", CT_TEXT_PLAIN },
401
- { .format = "txt", CT_TEXT_PLAIN },
402
- { .format = "json", CT_APPLICATION_JSON },
403
- { .format = "html", CT_TEXT_HTML },
404
- { .format = "text/html", CT_TEXT_HTML },
405
- { .format = "xml", CT_APPLICATION_XML },
406
-
407
- // terminator
408
- { .format = NULL, CT_TEXT_PLAIN },
409
-};
410
-
411
-uint8_t functions_format_to_content_type(const char *format) {
412
- if(format && *format) {
413
- for (int i = 0; function_formats[i].format; i++)
414
- if (strcmp(function_formats[i].format, format) == 0)
415
- return function_formats[i].content_type;
416
- }
283
+void rrd_function_del(RRDHOST *host, RRDSET *st, const char *name) {
284
+ char key[strlen(name) + 1];
285
+ rrd_functions_sanitize(key, name, sizeof(key));
286
+ dictionary_del(host->functions, key);
287
418
- return CT_TEXT_PLAIN;
419
-}
420
-
421
-const char *functions_content_type_to_format(HTTP_CONTENT_TYPE content_type) {
422
- for (int i = 0; function_formats[i].format; i++)
423
- if (function_formats[i].content_type == content_type)
424
- return function_formats[i].format;
288
+ if(st)
289
+ dictionary_del(st->functions_view, key);
290
+ else
291
+ rrdhost_flag_set(host, RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED);
292
426
- return "text/plain";
293
+ dictionary_garbage_collect(host->functions);
294
}
295
296
int rrd_call_function_error(BUFFER *wb, const char *msg, int code) {
304
return code;
305
}
306
440
-static int rrd_call_function_find(RRDHOST *host, BUFFER *wb, const char *name, size_t key_length, const DICTIONARY_ITEM **item) {
307
+int rrd_functions_find_by_name(RRDHOST *host, BUFFER *wb, const char *name, size_t key_length, const DICTIONARY_ITEM **item) {
308
char buffer[MAX_FUNCTION_LENGTH + 1];
442
-
443
- strncpyz(buffer, name, MAX_FUNCTION_LENGTH);
309
+ strncpyz(buffer, name, sizeof(buffer) - 1);
310
char *s = NULL;
311
312
bool found = false;
355
return HTTP_RESP_OK;
356
}
357
492
-// ----------------------------------------------------------------------------
493
-
494
-static void rrd_functions_inflight_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
495
- struct rrd_function_inflight *r = value;
496
-
497
- // internal_error(true, "FUNCTIONS: transaction '%s' finished", r->transaction);
498
-
499
- freez((void *)r->transaction);
500
- freez((void *)r->cmd);
501
- freez((void *)r->sanitized_cmd);
502
- dictionary_acquired_item_release(r->host->functions, r->host_function_acquired);
503
-}
504
-
505
-void rrd_functions_inflight_init(void) {
506
- if(rrd_functions_inflight_requests)
507
- return;
508
-
509
- rrd_functions_inflight_requests = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct rrd_function_inflight));
510
-
511
- dictionary_register_delete_callback(rrd_functions_inflight_requests, rrd_functions_inflight_delete_cb, NULL);
512
-}
513
-
514
-void rrd_functions_inflight_destroy(void) {
515
- if(!rrd_functions_inflight_requests)
516
- return;
517
-
518
- dictionary_destroy(rrd_functions_inflight_requests);
519
- rrd_functions_inflight_requests = NULL;
520
-}
521
-
522
-static void rrd_inflight_async_function_register_canceller_cb(void *register_canceller_cb_data, rrd_function_cancel_cb_t canceller_cb, void *canceller_cb_data) {
523
- struct rrd_function_inflight *r = register_canceller_cb_data;
524
- r->canceller.cb = canceller_cb;
525
- r->canceller.data = canceller_cb_data;
526
-}
527
-
528
-static void rrd_inflight_async_function_register_progresser_cb(void *register_progresser_cb_data, rrd_function_progresser_cb_t progresser_cb, void *progresser_cb_data) {
529
- struct rrd_function_inflight *r = register_progresser_cb_data;
530
- r->progresser.cb = progresser_cb;
531
- r->progresser.data = progresser_cb_data;
532
-}
533
-
534
-// ----------------------------------------------------------------------------
535
-// waiting for async function completion
536
-
537
-struct rrd_function_call_wait {
538
- RRDHOST *host;
539
- const DICTIONARY_ITEM *host_function_acquired;
540
- char *transaction;
541
-
542
- bool free_with_signal;
543
- bool data_are_ready;
544
- netdata_mutex_t mutex;
545
- pthread_cond_t cond;
546
- int code;
547
-};
548
-
549
-static void rrd_inflight_function_cleanup(RRDHOST *host __maybe_unused,
550
- const char *transaction) {
551
- dictionary_del(rrd_functions_inflight_requests, transaction);
552
- dictionary_garbage_collect(rrd_functions_inflight_requests);
553
-}
554
-
555
-static void rrd_function_call_wait_free(struct rrd_function_call_wait *tmp) {
556
- rrd_inflight_function_cleanup(tmp->host, tmp->transaction);
557
- freez(tmp->transaction);
558
-
559
- pthread_cond_destroy(&tmp->cond);
560
- netdata_mutex_destroy(&tmp->mutex);
561
- freez(tmp);
562
-}
563
-
564
-static void rrd_async_function_signal_when_ready(BUFFER *temp_wb __maybe_unused, int code, void *callback_data) {
565
- struct rrd_function_call_wait *tmp = callback_data;
566
- bool we_should_free = false;
567
-
568
- netdata_mutex_lock(&tmp->mutex);
569
-
570
- // since we got the mutex,
571
- // the waiting thread is either in pthread_cond_timedwait()
572
- // or gave up and left.
573
-
574
- tmp->code = code;
575
- tmp->data_are_ready = true;
576
-
577
- if(tmp->free_with_signal)
578
- we_should_free = true;
579
-
580
- pthread_cond_signal(&tmp->cond);
581
-
582
- netdata_mutex_unlock(&tmp->mutex);
583
-
584
- if(we_should_free) {
585
- buffer_free(temp_wb);
586
- rrd_function_call_wait_free(tmp);
587
- }
588
-}
589
-
590
-static void rrd_inflight_async_function_nowait_finished(BUFFER *wb, int code, void *data) {
591
- struct rrd_function_inflight *r = data;
592
-
593
- if(r->result.cb)
594
- r->result.cb(wb, code, r->result.data);
595
-
596
- rrd_inflight_function_cleanup(r->host, r->transaction);
597
-}
598
-
599
-static bool rrd_inflight_async_function_is_cancelled(void *data) {
600
- struct rrd_function_inflight *r = data;
601
- return __atomic_load_n(&r->cancelled, __ATOMIC_RELAXED);
602
-}
603
-
604
-static inline int rrd_call_function_async_and_dont_wait(struct rrd_function_inflight *r) {
605
- int code = r->rdcf->execute_cb(&r->transaction_uuid, r->result.wb,
606
- &r->stop_monotonic_ut, r->sanitized_cmd, r->rdcf->execute_cb_data,
607
- rrd_inflight_async_function_nowait_finished, r,
608
- r->progress.cb, r->progress.data,
609
- rrd_inflight_async_function_is_cancelled, r,
610
- rrd_inflight_async_function_register_canceller_cb, r,
611
- rrd_inflight_async_function_register_progresser_cb, r);
612
-
613
- if(code != HTTP_RESP_OK) {
614
- if (!buffer_strlen(r->result.wb))
615
- rrd_call_function_error(r->result.wb, "Failed to send request to the collector.", code);
616
-
617
- rrd_inflight_function_cleanup(r->host, r->transaction);
618
- }
619
-
620
- return code;
621
-}
622
-
623
-static int rrd_call_function_async_and_wait(struct rrd_function_inflight *r) {
624
- struct rrd_function_call_wait *tmp = mallocz(sizeof(struct rrd_function_call_wait));
625
- tmp->free_with_signal = false;
626
- tmp->data_are_ready = false;
627
- tmp->host = r->host;
628
- tmp->host_function_acquired = r->host_function_acquired;
629
- tmp->transaction = strdupz(r->transaction);
630
- netdata_mutex_init(&tmp->mutex);
631
- pthread_cond_init(&tmp->cond, NULL);
632
-
633
- // we need a temporary BUFFER, because we may time out and the caller supplied one may vanish,
634
- // so we create a new one we guarantee will survive until the collector finishes...
635
-
636
- bool we_should_free = true;
637
- BUFFER *temp_wb = buffer_create(PLUGINSD_LINE_MAX + 1, &netdata_buffers_statistics.buffers_functions); // we need it because we may give up on it
638
- temp_wb->content_type = r->result.wb->content_type;
639
-
640
- int code = r->rdcf->execute_cb(&r->transaction_uuid, temp_wb, &r->stop_monotonic_ut,
641
- r->sanitized_cmd, r->rdcf->execute_cb_data,
642
- // we overwrite the result callbacks,
643
- // so that we can clean up the allocations made
644
- rrd_async_function_signal_when_ready, tmp,
645
- r->progress.cb, r->progress.data,
646
- rrd_inflight_async_function_is_cancelled, r,
647
- rrd_inflight_async_function_register_canceller_cb, r,
648
- rrd_inflight_async_function_register_progresser_cb, r);
649
-
650
- if (code == HTTP_RESP_OK) {
651
- netdata_mutex_lock(&tmp->mutex);
652
-
653
- bool cancelled = false;
654
- int rc = 0;
655
- while (rc == 0 && !cancelled && !tmp->data_are_ready) {
656
- usec_t now_mono_ut = now_monotonic_usec();
657
- usec_t stop_mono_ut = __atomic_load_n(&r->stop_monotonic_ut, __ATOMIC_RELAXED) + RRDFUNCTIONS_TIMEOUT_EXTENSION_UT;
658
- if(now_mono_ut > stop_mono_ut) {
659
- rc = ETIMEDOUT;
660
- break;
661
- }
662
-
663
- // wait for 10ms, and loop again...
664
- struct timespec tp;
665
- clock_gettime(CLOCK_REALTIME, &tp);
666
- tp.tv_nsec += 10 * NSEC_PER_MSEC;
667
- if(tp.tv_nsec > (long)(1 * NSEC_PER_SEC)) {
668
- tp.tv_sec++;
669
- tp.tv_nsec -= 1 * NSEC_PER_SEC;
670
- }
671
-
672
- // the mutex is unlocked within pthread_cond_timedwait()
673
- rc = pthread_cond_timedwait(&tmp->cond, &tmp->mutex, &tp);
674
- // the mutex is again ours
675
-
676
- if(rc == ETIMEDOUT) {
677
- // 10ms have passed
678
-
679
- rc = 0;
680
- if (!tmp->data_are_ready && r->is_cancelled.cb &&
681
- r->is_cancelled.cb(r->is_cancelled.data)) {
682
-// internal_error(true, "FUNCTIONS: transaction '%s' is cancelled while waiting for response",
683
-// r->transaction);
684
- cancelled = true;
685
- rrd_function_cancel_inflight(r);
686
- break;
687
- }
688
- }
689
- }
690
-
691
- if (tmp->data_are_ready) {
692
- // we have a response
693
- buffer_fast_strcat(r->result.wb, buffer_tostring(temp_wb), buffer_strlen(temp_wb));
694
- r->result.wb->content_type = temp_wb->content_type;
695
- r->result.wb->expires = temp_wb->expires;
696
-
697
- if(r->result.wb->expires)
698
- buffer_cacheable(r->result.wb);
699
- else
700
- buffer_no_cacheable(r->result.wb);
701
-
702
- code = tmp->code;
703
- }
704
- else if (rc == ETIMEDOUT || cancelled) {
705
- // timeout
706
- // we will go away and let the callback free the structure
707
- tmp->free_with_signal = true;
708
- we_should_free = false;
709
-
710
- if(cancelled)
711
- code = rrd_call_function_error(r->result.wb,
712
- "Request cancelled",
713
- HTTP_RESP_CLIENT_CLOSED_REQUEST);
714
- else
715
- code = rrd_call_function_error(r->result.wb,
716
- "Timeout while waiting for a response from the collector.",
717
- HTTP_RESP_GATEWAY_TIMEOUT);
718
- }
719
- else
720
- code = rrd_call_function_error(r->result.wb,
721
- "Internal error while communicating with the collector",
722
- HTTP_RESP_INTERNAL_SERVER_ERROR);
723
-
724
- netdata_mutex_unlock(&tmp->mutex);
725
- }
726
- else {
727
- if(!buffer_strlen(r->result.wb))
728
- rrd_call_function_error(r->result.wb, "The collector returned an error.", code);
729
- }
730
-
731
- if (we_should_free) {
732
- rrd_function_call_wait_free(tmp);
733
- buffer_free(temp_wb);
734
- }
735
-
736
- return code;
737
-}
738
-
739
-static inline int rrd_call_function_async(struct rrd_function_inflight *r, bool wait) {
740
- if(wait)
741
- return rrd_call_function_async_and_wait(r);
742
- else
743
- return rrd_call_function_async_and_dont_wait(r);
744
-}
745
-
746
-
747
-void call_virtual_function_async(BUFFER *wb, RRDHOST *host, const char *name, const char *payload, rrd_function_result_callback_t callback, void *callback_data);
748
-// ----------------------------------------------------------------------------
749
-
750
-int rrd_function_run(RRDHOST *host, BUFFER *result_wb, int timeout_s, HTTP_ACCESS access, const char *cmd,
751
- bool wait, const char *transaction,
752
- rrd_function_result_callback_t result_cb, void *result_cb_data,
753
- rrd_function_progress_cb_t progress_cb, void *progress_cb_data,
754
- rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data, const char *payload) {
755
-
756
- int code;
757
- char sanitized_cmd[PLUGINSD_LINE_MAX + 1];
758
- const DICTIONARY_ITEM *host_function_acquired = NULL;
759
-
760
- // ------------------------------------------------------------------------
761
- // find the function
762
-
763
- size_t sanitized_cmd_length = sanitize_function_text(sanitized_cmd, cmd, PLUGINSD_LINE_MAX);
764
-
765
- if (is_dyncfg_function(sanitized_cmd, DYNCFG_FUNCTION_TYPE_ALL)) {
766
- call_virtual_function_async(result_wb, host, sanitized_cmd, payload, result_cb, result_cb_data);
767
- return HTTP_RESP_OK;
768
- }
769
-
770
- code = rrd_call_function_find(host, result_wb, sanitized_cmd, sanitized_cmd_length, &host_function_acquired);
771
- if(code != HTTP_RESP_OK)
772
- return code;
773
-
774
- struct rrd_host_function *rdcf = dictionary_acquired_item_value(host_function_acquired);
775
-
776
- if(access != HTTP_ACCESS_ADMINS && rdcf->access != HTTP_ACCESS_ANY && access > rdcf->access) {
777
-
778
- if(!aclk_connected)
779
- rrd_call_function_error(result_wb, "This Netdata must be connected to Netdata Cloud to access this function.", HTTP_RESP_PRECOND_FAIL);
780
- else if(access >= HTTP_ACCESS_ANY)
781
- rrd_call_function_error(result_wb, "You need to login to the Netdata Cloud space this agent is claimed to, to access this function.", HTTP_RESP_PRECOND_FAIL);
782
- else /* if(access < HTTP_ACCESS_ANY && rdcf->access < access) */
783
- rrd_call_function_error(result_wb, "To access this function you need to be an admin in this Netdata Cloud space.", HTTP_RESP_PRECOND_FAIL);
784
-
785
- dictionary_acquired_item_release(host->functions, host_function_acquired);
786
- return HTTP_RESP_PRECOND_FAIL;
787
- }
788
-
789
- if(timeout_s <= 0)
790
- timeout_s = rdcf->timeout;
791
-
792
- // ------------------------------------------------------------------------
793
- // validate and parse the transaction, or generate a new transaction id
794
-
795
- char uuid_str[UUID_COMPACT_STR_LEN];
796
- uuid_t uuid;
797
-
798
- if(!transaction || !*transaction || uuid_parse_flexi(transaction, uuid) != 0)
799
- uuid_generate_random(uuid);
800
-
801
- uuid_unparse_lower_compact(uuid, uuid_str);
802
- transaction = uuid_str;
803
-
804
- // ------------------------------------------------------------------------
805
- // the function can only be executed in async mode
806
- // put the function into the inflight requests
807
-
808
- struct rrd_function_inflight t = {
809
- .used = false,
810
- .host = host,
811
- .cmd = strdupz(cmd),
812
- .sanitized_cmd = strdupz(sanitized_cmd),
813
- .sanitized_cmd_length = sanitized_cmd_length,
814
- .transaction = strdupz(transaction),
815
- .timeout = timeout_s,
816
- .cancelled = false,
817
- .stop_monotonic_ut = now_monotonic_usec() + timeout_s * USEC_PER_SEC,
818
- .host_function_acquired = host_function_acquired,
819
- .rdcf = rdcf,
820
- .result = {
821
- .wb = result_wb,
822
- .cb = result_cb,
823
- .data = result_cb_data,
824
- },
825
- .is_cancelled = {
826
- .cb = is_cancelled_cb,
827
- .data = is_cancelled_cb_data,
828
- },
829
- .progress = {
830
- .cb = progress_cb,
831
- .data = progress_cb_data,
832
- },
833
- };
834
- uuid_copy(t.transaction_uuid, uuid);
835
-
836
- struct rrd_function_inflight *r = dictionary_set(rrd_functions_inflight_requests, transaction, &t, sizeof(t));
837
- if(r->used) {
838
- nd_log(NDLS_DAEMON, NDLP_NOTICE,
839
- "FUNCTIONS: duplicate transaction '%s', function: '%s'",
840
- t.transaction, t.cmd);
841
-
842
- code = rrd_call_function_error(result_wb, "duplicate transaction", HTTP_RESP_BAD_REQUEST);
843
- freez((void *)t.transaction);
844
- freez((void *)t.cmd);
845
- freez((void *)t.sanitized_cmd);
846
- dictionary_acquired_item_release(r->host->functions, t.host_function_acquired);
847
- return code;
848
- }
849
- r->used = true;
850
- // internal_error(true, "FUNCTIONS: transaction '%s' started", r->transaction);
851
-
852
- if(r->rdcf->sync) {
853
- // the caller has to wait
854
- code = r->rdcf->execute_cb(&r->transaction_uuid, r->result.wb,
855
- &r->stop_monotonic_ut, r->sanitized_cmd, r->rdcf->execute_cb_data,
856
- r->result.cb, r->result.data,
857
- r->progress.cb, r->progress.data,
858
- r->is_cancelled.cb, r->is_cancelled.data, // it is ok to pass these, we block the caller
859
- NULL, NULL, // no need to register canceller, we will wait
860
- NULL, NULL // ?? do we need a progresser in this case?
861
- );
862
-
863
- if(code != HTTP_RESP_OK && !buffer_strlen(result_wb))
864
- rrd_call_function_error(result_wb, "Collector reported error.", code);
865
-
866
- rrd_inflight_function_cleanup(host, r->transaction);
867
- return code;
868
- }
869
-
870
- return rrd_call_function_async(r, wait);
871
-}
872
-
873
-static void rrd_function_cancel_inflight(struct rrd_function_inflight *r) {
874
- if(!r)
875
- return;
876
-
877
- bool cancelled = __atomic_load_n(&r->cancelled, __ATOMIC_RELAXED);
878
- if(cancelled) {
879
- nd_log(NDLS_DAEMON, NDLP_DEBUG,
880
- "FUNCTIONS: received a CANCEL request for transaction '%s', but it is already cancelled.",
881
- r->transaction);
882
- return;
883
- }
884
-
885
- __atomic_store_n(&r->cancelled, true, __ATOMIC_RELAXED);
886
-
887
- if(!rrd_collector_dispatcher_acquire(r->rdcf->collector)) {
888
- nd_log(NDLS_DAEMON, NDLP_DEBUG,
889
- "FUNCTIONS: received a CANCEL request for transaction '%s', but the collector is not running.",
890
- r->transaction);
891
- return;
892
- }
893
-
894
- if(r->canceller.cb)
895
- r->canceller.cb(r->canceller.data);
896
-
897
- rrd_collector_dispatcher_release(r->rdcf->collector);
898
-}
899
-
900
-void rrd_function_cancel(const char *transaction) {
901
- // internal_error(true, "FUNCTIONS: request to cancel transaction '%s'", transaction);
902
-
903
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(rrd_functions_inflight_requests, transaction);
904
- if(!item) {
905
- nd_log(NDLS_DAEMON, NDLP_DEBUG,
906
- "FUNCTIONS: received a CANCEL request for transaction '%s', but the transaction is not running.",
907
- transaction);
908
- return;
909
- }
910
-
911
- struct rrd_function_inflight *r = dictionary_acquired_item_value(item);
912
- rrd_function_cancel_inflight(r);
913
- dictionary_acquired_item_release(rrd_functions_inflight_requests, item);
914
-}
915
-
916
-void rrd_function_progress(const char *transaction) {
917
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(rrd_functions_inflight_requests, transaction);
918
- if(!item) {
919
- nd_log(NDLS_DAEMON, NDLP_DEBUG,
920
- "FUNCTIONS: received a PROGRESS request for transaction '%s', but the transaction is not running.",
921
- transaction);
922
- return;
923
- }
924
-
925
- struct rrd_function_inflight *r = dictionary_acquired_item_value(item);
926
-
927
- if(!rrd_collector_dispatcher_acquire(r->rdcf->collector)) {
928
- nd_log(NDLS_DAEMON, NDLP_DEBUG,
929
- "FUNCTIONS: received a PROGRESS request for transaction '%s', but the collector is not running.",
930
- transaction);
931
- goto cleanup;
932
- }
933
-
934
- functions_stop_monotonic_update_on_progress(&r->stop_monotonic_ut);
935
-
936
- if(r->progresser.cb)
937
- r->progresser.cb(r->progresser.data);
938
-
939
- rrd_collector_dispatcher_release(r->rdcf->collector);
940
-
941
-cleanup:
942
- dictionary_acquired_item_release(rrd_functions_inflight_requests, item);
943
-}
944
-
945
-void rrd_function_call_progresser(uuid_t *transaction) {
946
- char str[UUID_COMPACT_STR_LEN];
947
- uuid_unparse_lower_compact(*transaction, str);
948
- rrd_function_progress(str);
949
-}
950
-
951
-// ----------------------------------------------------------------------------
952
-
953
-static void functions2json(DICTIONARY *functions, BUFFER *wb)
954
-{
955
- struct rrd_host_function *t;
956
- dfe_start_read(functions, t)
957
- {
958
- if (!rrd_collector_running(t->collector))
959
- continue;
960
-
961
- buffer_json_member_add_object(wb, t_dfe.name);
962
- {
963
- buffer_json_member_add_string_or_empty(wb, "help", string2str(t->help));
964
- buffer_json_member_add_int64(wb, "timeout", (int64_t) t->timeout);
965
-
966
- char options[65];
967
- snprintfz(
968
- options, 64
969
- , "%s%s"
970
- , (t->options & RRD_FUNCTION_LOCAL) ? "LOCAL " : ""
971
- , (t->options & RRD_FUNCTION_GLOBAL) ? "GLOBAL" : ""
972
- );
973
-
974
- buffer_json_member_add_string_or_empty(wb, "options", options);
975
- buffer_json_member_add_string_or_empty(wb, "tags", string2str(t->tags));
976
- buffer_json_member_add_string(wb, "access", http_id2access(t->access));
977
- buffer_json_member_add_uint64(wb, "priority", t->priority);
978
- }
979
- buffer_json_object_close(wb);
980
- }
981
- dfe_done(t);
982
-}
983
-
984
-void chart_functions2json(RRDSET *st, BUFFER *wb) {
985
- if(!st || !st->functions_view) return;
986
-
987
- functions2json(st->functions_view, wb);
988
-}
989
-
990
-void host_functions2json(RRDHOST *host, BUFFER *wb) {
991
- if(!host || !host->functions) return;
992
-
993
- buffer_json_member_add_object(wb, "functions");
994
-
995
- struct rrd_host_function *t;
996
- dfe_start_read(host->functions, t) {
997
- if(!rrd_collector_running(t->collector)) continue;
998
-
999
- buffer_json_member_add_object(wb, t_dfe.name);
1000
- {
1001
- buffer_json_member_add_string(wb, "help", string2str(t->help));
1002
- buffer_json_member_add_int64(wb, "timeout", t->timeout);
1003
- buffer_json_member_add_array(wb, "options");
1004
- {
1005
- if (t->options & RRD_FUNCTION_GLOBAL)
1006
- buffer_json_add_array_item_string(wb, "GLOBAL");
1007
- if (t->options & RRD_FUNCTION_LOCAL)
1008
- buffer_json_add_array_item_string(wb, "LOCAL");
1009
- }
1010
- buffer_json_array_close(wb);
1011
- buffer_json_member_add_string(wb, "tags", string2str(t->tags));
1012
- buffer_json_member_add_string(wb, "access", http_id2access(t->access));
1013
- buffer_json_member_add_uint64(wb, "priority", t->priority);
1014
- }
1015
- buffer_json_object_close(wb);
1016
- }
1017
- dfe_done(t);
1018
-
1019
- buffer_json_object_close(wb);
1020
-}
1021
-
1022
-void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst, void *value, size_t value_size) {
1023
- if(!rrdset_functions_view || !dst) return;
358
+bool rrd_function_available(RRDHOST *host, const char *function) {
359
+ if(!host || !host->functions)
360
+ return false;
361
1025
- struct rrd_host_function *t;
1026
- dfe_start_read(rrdset_functions_view, t) {
1027
- if(!rrd_collector_running(t->collector)) continue;
362
+ bool ret = false;
363
+ const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(host->functions, function);
364
+ if(item) {
365
+ struct rrd_host_function *rdcf = dictionary_acquired_item_value(item);
366
+ if(rrd_collector_running(rdcf->collector))
367
+ ret = true;
368
1029
- dictionary_set(dst, t_dfe.name, value, value_size);
369
+ dictionary_acquired_item_release(host->functions, item);
370
}
1031
- dfe_done(t);
1032
-}
1033
-
1034
-void host_functions_to_dict(RRDHOST *host, DICTIONARY *dst, void *value, size_t value_size, STRING **help, STRING **tags, HTTP_ACCESS *access, int *priority) {
1035
- if(!host || !host->functions || !dictionary_entries(host->functions) || !dst) return;
1036
-
1037
- struct rrd_host_function *t;
1038
- dfe_start_read(host->functions, t) {
1039
- if(!rrd_collector_running(t->collector)) continue;
1040
-
1041
- if(help)
1042
- *help = t->help;
1043
-
1044
- if(tags)
1045
- *tags = t->tags;
1046
-
1047
- if(access)
1048
- *access = t->access;
1049
-
1050
- if(priority)
1051
- *priority = t->priority;
1052
-
1053
- dictionary_set(dst, t_dfe.name, value, value_size);
1054
- }
1055
- dfe_done(t);
1056
-}
1057
-
1058
-// ----------------------------------------------------------------------------
1059
-
1060
-int rrdhost_function_progress(uuid_t *transaction __maybe_unused, BUFFER *wb,
1061
- usec_t *stop_monotonic_ut __maybe_unused, const char *function __maybe_unused,
1062
- void *collector_data __maybe_unused,
1063
- rrd_function_result_callback_t result_cb, void *result_cb_data,
1064
- rrd_function_progress_cb_t progress_cb __maybe_unused, void *progress_cb_data __maybe_unused,
1065
- rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data,
1066
- rrd_function_register_canceller_cb_t register_canceller_cb __maybe_unused,
1067
- void *register_canceller_cb_data __maybe_unused,
1068
- rrd_function_register_progresser_cb_t register_progresser_cb __maybe_unused,
1069
- void *register_progresser_cb_data __maybe_unused) {
1070
-
1071
- int response = progress_function_result(wb, rrdhost_hostname(localhost));
1072
-
1073
- if(is_cancelled_cb && is_cancelled_cb(is_cancelled_cb_data)) {
1074
- buffer_flush(wb);
1075
- response = HTTP_RESP_CLIENT_CLOSED_REQUEST;
1076
- }
1077
-
1078
- if(result_cb)
1079
- result_cb(wb, response, result_cb_data);
1080
-
1081
- return response;
1082
-}
1083
-
1084
-int rrdhost_function_streaming(uuid_t *transaction __maybe_unused, BUFFER *wb,
1085
- usec_t *stop_monotonic_ut __maybe_unused, const char *function __maybe_unused,
1086
- void *collector_data __maybe_unused,
1087
- rrd_function_result_callback_t result_cb, void *result_cb_data,
1088
- rrd_function_progress_cb_t progress_cb __maybe_unused, void *progress_cb_data __maybe_unused,
1089
- rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data,
1090
- rrd_function_register_canceller_cb_t register_canceller_cb __maybe_unused,
1091
- void *register_canceller_cb_data __maybe_unused,
1092
- rrd_function_register_progresser_cb_t register_progresser_cb __maybe_unused,
1093
- void *register_progresser_cb_data __maybe_unused) {
1094
-
1095
- time_t now = now_realtime_sec();
1096
-
1097
- buffer_flush(wb);
1098
- wb->content_type = CT_APPLICATION_JSON;
1099
- buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
1100
-
1101
- buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(localhost));
1102
- buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
1103
- buffer_json_member_add_string(wb, "type", "table");
1104
- buffer_json_member_add_time_t(wb, "update_every", 1);
1105
- buffer_json_member_add_string(wb, "help", RRDFUNCTIONS_STREAMING_HELP);
1106
- buffer_json_member_add_array(wb, "data");
1107
-
1108
- size_t max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_MAX] = { 0 };
1109
- size_t max_db_metrics = 0, max_db_instances = 0, max_db_contexts = 0;
1110
- size_t max_collection_replication_instances = 0, max_streaming_replication_instances = 0;
1111
- size_t max_ml_anomalous = 0, max_ml_normal = 0, max_ml_trained = 0, max_ml_pending = 0, max_ml_silenced = 0;
1112
- {
1113
- RRDHOST *host;
1114
- dfe_start_read(rrdhost_root_index, host) {
1115
- RRDHOST_STATUS s;
1116
- rrdhost_status(host, now, &s);
1117
- buffer_json_add_array_item_array(wb);
1118
-
1119
- if(s.db.metrics > max_db_metrics)
1120
- max_db_metrics = s.db.metrics;
1121
-
1122
- if(s.db.instances > max_db_instances)
1123
- max_db_instances = s.db.instances;
1124
-
1125
- if(s.db.contexts > max_db_contexts)
1126
- max_db_contexts = s.db.contexts;
1127
-
1128
- if(s.ingest.replication.instances > max_collection_replication_instances)
1129
- max_collection_replication_instances = s.ingest.replication.instances;
1130
-
1131
- if(s.stream.replication.instances > max_streaming_replication_instances)
1132
- max_streaming_replication_instances = s.stream.replication.instances;
1133
-
1134
- for(int i = 0; i < STREAM_TRAFFIC_TYPE_MAX ;i++) {
1135
- if (s.stream.sent_bytes_on_this_connection_per_type[i] >
1136
- max_sent_bytes_on_this_connection_per_type[i])
1137
- max_sent_bytes_on_this_connection_per_type[i] =
1138
- s.stream.sent_bytes_on_this_connection_per_type[i];
1139
- }
1140
-
1141
- // retention
1142
- buffer_json_add_array_item_string(wb, rrdhost_hostname(s.host)); // Node
1143
- buffer_json_add_array_item_uint64(wb, s.db.first_time_s * MSEC_PER_SEC); // dbFrom
1144
- buffer_json_add_array_item_uint64(wb, s.db.last_time_s * MSEC_PER_SEC); // dbTo
1145
-
1146
- if(s.db.first_time_s && s.db.last_time_s && s.db.last_time_s > s.db.first_time_s)
1147
- buffer_json_add_array_item_uint64(wb, s.db.last_time_s - s.db.first_time_s); // dbDuration
1148
- else
1149
- buffer_json_add_array_item_string(wb, NULL); // dbDuration
1150
-
1151
- buffer_json_add_array_item_uint64(wb, s.db.metrics); // dbMetrics
1152
- buffer_json_add_array_item_uint64(wb, s.db.instances); // dbInstances
1153
- buffer_json_add_array_item_uint64(wb, s.db.contexts); // dbContexts
1154
-
1155
- // statuses
1156
- buffer_json_add_array_item_string(wb, rrdhost_ingest_status_to_string(s.ingest.status)); // InStatus
1157
- buffer_json_add_array_item_string(wb, rrdhost_streaming_status_to_string(s.stream.status)); // OutStatus
1158
- buffer_json_add_array_item_string(wb, rrdhost_ml_status_to_string(s.ml.status)); // MLStatus
1159
-
1160
- // collection
1161
- if(s.ingest.since) {
1162
- buffer_json_add_array_item_uint64(wb, s.ingest.since * MSEC_PER_SEC); // InSince
1163
- buffer_json_add_array_item_time_t(wb, s.now - s.ingest.since); // InAge
1164
- }
1165
- else {
1166
- buffer_json_add_array_item_string(wb, NULL); // InSince
1167
- buffer_json_add_array_item_string(wb, NULL); // InAge
1168
- }
1169
- buffer_json_add_array_item_string(wb, stream_handshake_error_to_string(s.ingest.reason)); // InReason
1170
- buffer_json_add_array_item_uint64(wb, s.ingest.hops); // InHops
1171
- buffer_json_add_array_item_double(wb, s.ingest.replication.completion); // InReplCompletion
1172
- buffer_json_add_array_item_uint64(wb, s.ingest.replication.instances); // InReplInstances
1173
- buffer_json_add_array_item_string(wb, s.ingest.peers.local.ip); // InLocalIP
1174
- buffer_json_add_array_item_uint64(wb, s.ingest.peers.local.port); // InLocalPort
1175
- buffer_json_add_array_item_string(wb, s.ingest.peers.peer.ip); // InRemoteIP
1176
- buffer_json_add_array_item_uint64(wb, s.ingest.peers.peer.port); // InRemotePort
1177
- buffer_json_add_array_item_string(wb, s.ingest.ssl ? "SSL" : "PLAIN"); // InSSL
1178
- stream_capabilities_to_json_array(wb, s.ingest.capabilities, NULL); // InCapabilities
1179
-
1180
- // streaming
1181
- if(s.stream.since) {
1182
- buffer_json_add_array_item_uint64(wb, s.stream.since * MSEC_PER_SEC); // OutSince
1183
- buffer_json_add_array_item_time_t(wb, s.now - s.stream.since); // OutAge
1184
- }
1185
- else {
1186
- buffer_json_add_array_item_string(wb, NULL); // OutSince
1187
- buffer_json_add_array_item_string(wb, NULL); // OutAge
1188
- }
1189
- buffer_json_add_array_item_string(wb, stream_handshake_error_to_string(s.stream.reason)); // OutReason
1190
- buffer_json_add_array_item_uint64(wb, s.stream.hops); // OutHops
1191
- buffer_json_add_array_item_double(wb, s.stream.replication.completion); // OutReplCompletion
1192
- buffer_json_add_array_item_uint64(wb, s.stream.replication.instances); // OutReplInstances
1193
- buffer_json_add_array_item_string(wb, s.stream.peers.local.ip); // OutLocalIP
1194
- buffer_json_add_array_item_uint64(wb, s.stream.peers.local.port); // OutLocalPort
1195
- buffer_json_add_array_item_string(wb, s.stream.peers.peer.ip); // OutRemoteIP
1196
- buffer_json_add_array_item_uint64(wb, s.stream.peers.peer.port); // OutRemotePort
1197
- buffer_json_add_array_item_string(wb, s.stream.ssl ? "SSL" : "PLAIN"); // OutSSL
1198
- buffer_json_add_array_item_string(wb, s.stream.compression ? "COMPRESSED" : "UNCOMPRESSED"); // OutCompression
1199
- stream_capabilities_to_json_array(wb, s.stream.capabilities, NULL); // OutCapabilities
1200
- buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA]);
1201
- buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA]);
1202
- buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION]);
1203
- buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS]);
1204
-
1205
- buffer_json_add_array_item_array(wb); // OutAttemptHandshake
1206
- time_t last_attempt = 0;
1207
- for(struct rrdpush_destinations *d = host->destinations; d ; d = d->next) {
1208
- if(d->since > last_attempt)
1209
- last_attempt = d->since;
1210
-
1211
- buffer_json_add_array_item_string(wb, stream_handshake_error_to_string(d->reason));
1212
- }
1213
- buffer_json_array_close(wb); // // OutAttemptHandshake
1214
-
1215
- if(!last_attempt) {
1216
- buffer_json_add_array_item_string(wb, NULL); // OutAttemptSince
1217
- buffer_json_add_array_item_string(wb, NULL); // OutAttemptAge
1218
- }
1219
- else {
1220
- buffer_json_add_array_item_uint64(wb, last_attempt * 1000); // OutAttemptSince
1221
- buffer_json_add_array_item_time_t(wb, s.now - last_attempt); // OutAttemptAge
1222
- }
1223
-
1224
- // ML
1225
- if(s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
1226
- buffer_json_add_array_item_uint64(wb, s.ml.metrics.anomalous); // MlAnomalous
1227
- buffer_json_add_array_item_uint64(wb, s.ml.metrics.normal); // MlNormal
1228
- buffer_json_add_array_item_uint64(wb, s.ml.metrics.trained); // MlTrained
1229
- buffer_json_add_array_item_uint64(wb, s.ml.metrics.pending); // MlPending
1230
- buffer_json_add_array_item_uint64(wb, s.ml.metrics.silenced); // MlSilenced
1231
-
1232
- if(s.ml.metrics.anomalous > max_ml_anomalous)
1233
- max_ml_anomalous = s.ml.metrics.anomalous;
1234
-
1235
- if(s.ml.metrics.normal > max_ml_normal)
1236
- max_ml_normal = s.ml.metrics.normal;
1237
-
1238
- if(s.ml.metrics.trained > max_ml_trained)
1239
- max_ml_trained = s.ml.metrics.trained;
1240
-
1241
- if(s.ml.metrics.pending > max_ml_pending)
1242
- max_ml_pending = s.ml.metrics.pending;
1243
-
1244
- if(s.ml.metrics.silenced > max_ml_silenced)
1245
- max_ml_silenced = s.ml.metrics.silenced;
1246
-
1247
- }
1248
- else {
1249
- buffer_json_add_array_item_string(wb, NULL); // MlAnomalous
1250
- buffer_json_add_array_item_string(wb, NULL); // MlNormal
1251
- buffer_json_add_array_item_string(wb, NULL); // MlTrained
1252
- buffer_json_add_array_item_string(wb, NULL); // MlPending
1253
- buffer_json_add_array_item_string(wb, NULL); // MlSilenced
1254
- }
1255
-
1256
- // close
1257
- buffer_json_array_close(wb);
1258
- }
1259
- dfe_done(host);
1260
- }
1261
- buffer_json_array_close(wb); // data
1262
- buffer_json_member_add_object(wb, "columns");
1263
- {
1264
- size_t field_id = 0;
1265
-
1266
- // Node
1267
- buffer_rrdf_table_add_field(wb, field_id++, "Node", "Node's Hostname",
1268
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1269
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1270
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1271
- RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
1272
- NULL);
1273
-
1274
- buffer_rrdf_table_add_field(wb, field_id++, "dbFrom", "DB Data Retention From",
1275
- RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS,
1276
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1277
- RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1278
- RRDF_FIELD_OPTS_NONE, NULL);
1279
-
1280
- buffer_rrdf_table_add_field(wb, field_id++, "dbTo", "DB Data Retention To",
1281
- RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS,
1282
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1283
- RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
1284
- RRDF_FIELD_OPTS_NONE, NULL);
1285
-
1286
- buffer_rrdf_table_add_field(wb, field_id++, "dbDuration", "DB Data Retention Duration",
1287
- RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S,
1288
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1289
- RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
1290
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1291
-
1292
- buffer_rrdf_table_add_field(wb, field_id++, "dbMetrics", "Time-series Metrics in the DB",
1293
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1294
- 0, NULL, (double)max_db_metrics, RRDF_FIELD_SORT_DESCENDING, NULL,
1295
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1296
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1297
-
1298
- buffer_rrdf_table_add_field(wb, field_id++, "dbInstances", "Instances in the DB",
1299
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1300
- 0, NULL, (double)max_db_instances, RRDF_FIELD_SORT_DESCENDING, NULL,
1301
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1302
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1303
-
1304
- buffer_rrdf_table_add_field(wb, field_id++, "dbContexts", "Contexts in the DB",
1305
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1306
- 0, NULL, (double)max_db_contexts, RRDF_FIELD_SORT_DESCENDING, NULL,
1307
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1308
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1309
-
1310
- // --- statuses ---
1311
-
1312
- buffer_rrdf_table_add_field(wb, field_id++, "InStatus", "Data Collection Online Status",
1313
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1314
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1315
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1316
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1317
-
1318
-
1319
- buffer_rrdf_table_add_field(wb, field_id++, "OutStatus", "Streaming Online Status",
1320
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1321
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1322
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1323
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1324
-
1325
- buffer_rrdf_table_add_field(wb, field_id++, "MlStatus", "ML Status",
1326
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1327
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1328
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1329
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1330
-
1331
- // --- collection ---
1332
-
1333
- buffer_rrdf_table_add_field(wb, field_id++, "InSince", "Last Data Collection Status Change",
1334
- RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS,
1335
- 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
1336
- RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1337
- RRDF_FIELD_OPTS_NONE, NULL);
1338
-
1339
- buffer_rrdf_table_add_field(wb, field_id++, "InAge", "Last Data Collection Online Status Change Age",
1340
- RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S,
1341
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1342
- RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
1343
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1344
-
1345
- buffer_rrdf_table_add_field(wb, field_id++, "InReason", "Data Collection Online Status Reason",
1346
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1347
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1348
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1349
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1350
-
1351
- buffer_rrdf_table_add_field(wb, field_id++, "InHops", "Data Collection Distance Hops from Origin Node",
1352
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1353
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1354
- RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1355
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1356
-
1357
- buffer_rrdf_table_add_field(wb, field_id++, "InReplCompletion", "Inbound Replication Completion",
1358
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1359
- 1, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL,
1360
- RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1361
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1362
-
1363
- buffer_rrdf_table_add_field(wb, field_id++, "InReplInstances", "Inbound Replicating Instances",
1364
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1365
- 0, "instances", (double)max_collection_replication_instances, RRDF_FIELD_SORT_DESCENDING,
1366
- NULL,
1367
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1368
- RRDF_FIELD_OPTS_NONE, NULL);
1369
-
1370
- buffer_rrdf_table_add_field(wb, field_id++, "InLocalIP", "Inbound Local IP",
1371
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1372
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1373
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1374
- RRDF_FIELD_OPTS_NONE, NULL);
1375
-
1376
- buffer_rrdf_table_add_field(wb, field_id++, "InLocalPort", "Inbound Local Port",
1377
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1378
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1379
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE,
1380
- RRDF_FIELD_OPTS_NONE, NULL);
1381
-
1382
- buffer_rrdf_table_add_field(wb, field_id++, "InRemoteIP", "Inbound Remote IP",
1383
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1384
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1385
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1386
- RRDF_FIELD_OPTS_NONE, NULL);
1387
-
1388
- buffer_rrdf_table_add_field(wb, field_id++, "InRemotePort", "Inbound Remote Port",
1389
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1390
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1391
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE,
1392
- RRDF_FIELD_OPTS_NONE, NULL);
1393
-
1394
- buffer_rrdf_table_add_field(wb, field_id++, "InSSL", "Inbound SSL Connection",
1395
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1396
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1397
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1398
- RRDF_FIELD_OPTS_NONE, NULL);
1399
-
1400
- buffer_rrdf_table_add_field(wb, field_id++, "InCapabilities", "Inbound Connection Capabilities",
1401
- RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE,
1402
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1403
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1404
- RRDF_FIELD_OPTS_NONE, NULL);
1405
-
1406
- // --- streaming ---
1407
-
1408
- buffer_rrdf_table_add_field(wb, field_id++, "OutSince", "Last Streaming Status Change",
1409
- RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS,
1410
- 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
1411
- RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
1412
- RRDF_FIELD_OPTS_NONE, NULL);
1413
-
1414
- buffer_rrdf_table_add_field(wb, field_id++, "OutAge", "Last Streaming Status Change Age",
1415
- RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S,
1416
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1417
- RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1418
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1419
-
1420
- buffer_rrdf_table_add_field(wb, field_id++, "OutReason", "Streaming Status Reason",
1421
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1422
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1423
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1424
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1425
-
1426
- buffer_rrdf_table_add_field(wb, field_id++, "OutHops", "Streaming Distance Hops from Origin Node",
1427
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1428
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1429
- RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1430
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1431
-
1432
- buffer_rrdf_table_add_field(wb, field_id++, "OutReplCompletion", "Outbound Replication Completion",
1433
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1434
- 1, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL,
1435
- RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1436
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1437
-
1438
- buffer_rrdf_table_add_field(wb, field_id++, "OutReplInstances", "Outbound Replicating Instances",
1439
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1440
- 0, "instances", (double)max_streaming_replication_instances, RRDF_FIELD_SORT_DESCENDING,
1441
- NULL,
1442
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1443
- RRDF_FIELD_OPTS_NONE, NULL);
1444
-
1445
- buffer_rrdf_table_add_field(wb, field_id++, "OutLocalIP", "Outbound Local IP",
1446
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1447
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1448
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1449
- RRDF_FIELD_OPTS_NONE, NULL);
1450
-
1451
- buffer_rrdf_table_add_field(wb, field_id++, "OutLocalPort", "Outbound Local Port",
1452
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1453
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1454
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE,
1455
- RRDF_FIELD_OPTS_NONE, NULL);
1456
-
1457
- buffer_rrdf_table_add_field(wb, field_id++, "OutRemoteIP", "Outbound Remote IP",
1458
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1459
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1460
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1461
- RRDF_FIELD_OPTS_NONE, NULL);
1462
-
1463
- buffer_rrdf_table_add_field(wb, field_id++, "OutRemotePort", "Outbound Remote Port",
1464
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1465
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1466
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE,
1467
- RRDF_FIELD_OPTS_NONE, NULL);
1468
-
1469
- buffer_rrdf_table_add_field(wb, field_id++, "OutSSL", "Outbound SSL Connection",
1470
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1471
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1472
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1473
- RRDF_FIELD_OPTS_NONE, NULL);
1474
-
1475
- buffer_rrdf_table_add_field(wb, field_id++, "OutCompression", "Outbound Compressed Connection",
1476
- RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1477
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1478
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1479
- RRDF_FIELD_OPTS_NONE, NULL);
1480
-
1481
- buffer_rrdf_table_add_field(wb, field_id++, "OutCapabilities", "Outbound Connection Capabilities",
1482
- RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE,
1483
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1484
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1485
- RRDF_FIELD_OPTS_NONE, NULL);
1486
-
1487
- buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficData", "Outbound Metric Data Traffic",
1488
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1489
- 0, "bytes", (double)max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA],
1490
- RRDF_FIELD_SORT_DESCENDING, NULL,
1491
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1492
- RRDF_FIELD_OPTS_NONE, NULL);
1493
-
1494
- buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficMetadata", "Outbound Metric Metadata Traffic",
1495
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1496
- 0, "bytes",
1497
- (double)max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA],
1498
- RRDF_FIELD_SORT_DESCENDING, NULL,
1499
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1500
- RRDF_FIELD_OPTS_NONE, NULL);
1501
-
1502
- buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficReplication", "Outbound Metric Replication Traffic",
1503
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1504
- 0, "bytes",
1505
- (double)max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION],
1506
- RRDF_FIELD_SORT_DESCENDING, NULL,
1507
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1508
- RRDF_FIELD_OPTS_NONE, NULL);
1509
-
1510
- buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficFunctions", "Outbound Metric Functions Traffic",
1511
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1512
- 0, "bytes",
1513
- (double)max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS],
1514
- RRDF_FIELD_SORT_DESCENDING, NULL,
1515
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1516
- RRDF_FIELD_OPTS_NONE, NULL);
1517
-
1518
- buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptHandshake",
1519
- "Outbound Connection Attempt Handshake Status",
1520
- RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE,
1521
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1522
- RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1523
- RRDF_FIELD_OPTS_NONE, NULL);
1524
-
1525
- buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptSince",
1526
- "Last Outbound Connection Attempt Status Change Time",
1527
- RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS,
1528
- 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
1529
- RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
1530
- RRDF_FIELD_OPTS_NONE, NULL);
1531
-
1532
- buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptAge",
1533
- "Last Outbound Connection Attempt Status Change Age",
1534
- RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S,
1535
- 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1536
- RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE,
1537
- RRDF_FIELD_OPTS_VISIBLE, NULL);
1538
-
1539
- // --- ML ---
1540
-
1541
- buffer_rrdf_table_add_field(wb, field_id++, "MlAnomalous", "Number of Anomalous Metrics",
1542
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1543
- 0, "metrics",
1544
- (double)max_ml_anomalous,
1545
- RRDF_FIELD_SORT_DESCENDING, NULL,
1546
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1547
- RRDF_FIELD_OPTS_NONE, NULL);
1548
-
1549
- buffer_rrdf_table_add_field(wb, field_id++, "MlNormal", "Number of Not Anomalous Metrics",
1550
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1551
- 0, "metrics",
1552
- (double)max_ml_normal,
1553
- RRDF_FIELD_SORT_DESCENDING, NULL,
1554
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1555
- RRDF_FIELD_OPTS_NONE, NULL);
1556
-
1557
- buffer_rrdf_table_add_field(wb, field_id++, "MlTrained", "Number of Trained Metrics",
1558
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1559
- 0, "metrics",
1560
- (double)max_ml_trained,
1561
- RRDF_FIELD_SORT_DESCENDING, NULL,
1562
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1563
- RRDF_FIELD_OPTS_NONE, NULL);
1564
-
1565
- buffer_rrdf_table_add_field(wb, field_id++, "MlPending", "Number of Pending Metrics",
1566
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1567
- 0, "metrics",
1568
- (double)max_ml_pending,
1569
- RRDF_FIELD_SORT_DESCENDING, NULL,
1570
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1571
- RRDF_FIELD_OPTS_NONE, NULL);
1572
-
1573
- buffer_rrdf_table_add_field(wb, field_id++, "MlSilenced", "Number of Silenced Metrics",
1574
- RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1575
- 0, "metrics",
1576
- (double)max_ml_silenced,
1577
- RRDF_FIELD_SORT_DESCENDING, NULL,
1578
- RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
1579
- RRDF_FIELD_OPTS_NONE, NULL);
1580
- }
1581
- buffer_json_object_close(wb); // columns
1582
- buffer_json_member_add_string(wb, "default_sort_column", "Node");
1583
- buffer_json_member_add_object(wb, "charts");
1584
- {
1585
- // Data Collection Age chart
1586
- buffer_json_member_add_object(wb, "InAge");
1587
- {
1588
- buffer_json_member_add_string(wb, "name", "Data Collection Age");
1589
- buffer_json_member_add_string(wb, "type", "stacked-bar");
1590
- buffer_json_member_add_array(wb, "columns");
1591
- {
1592
- buffer_json_add_array_item_string(wb, "InAge");
1593
- }
1594
- buffer_json_array_close(wb);
1595
- }
1596
- buffer_json_object_close(wb);
1597
-
1598
- // Streaming Age chart
1599
- buffer_json_member_add_object(wb, "OutAge");
1600
- {
1601
- buffer_json_member_add_string(wb, "name", "Streaming Age");
1602
- buffer_json_member_add_string(wb, "type", "stacked-bar");
1603
- buffer_json_member_add_array(wb, "columns");
1604
- {
1605
- buffer_json_add_array_item_string(wb, "OutAge");
1606
- }
1607
- buffer_json_array_close(wb);
1608
- }
1609
- buffer_json_object_close(wb);
1610
-
1611
- // DB Duration
1612
- buffer_json_member_add_object(wb, "dbDuration");
1613
- {
1614
- buffer_json_member_add_string(wb, "name", "Retention Duration");
1615
- buffer_json_member_add_string(wb, "type", "stacked-bar");
1616
- buffer_json_member_add_array(wb, "columns");
1617
- {
1618
- buffer_json_add_array_item_string(wb, "dbDuration");
1619
- }
1620
- buffer_json_array_close(wb);
1621
- }
1622
- buffer_json_object_close(wb);
1623
- }
1624
- buffer_json_object_close(wb); // charts
1625
-
1626
- buffer_json_member_add_array(wb, "default_charts");
1627
- {
1628
- buffer_json_add_array_item_array(wb);
1629
- buffer_json_add_array_item_string(wb, "InAge");
1630
- buffer_json_add_array_item_string(wb, "Node");
1631
- buffer_json_array_close(wb);
1632
-
1633
- buffer_json_add_array_item_array(wb);
1634
- buffer_json_add_array_item_string(wb, "OutAge");
1635
- buffer_json_add_array_item_string(wb, "Node");
1636
- buffer_json_array_close(wb);
1637
- }
1638
- buffer_json_array_close(wb);
1639
-
1640
- buffer_json_member_add_object(wb, "group_by");
1641
- {
1642
- buffer_json_member_add_object(wb, "Node");
1643
- {
1644
- buffer_json_member_add_string(wb, "name", "Node");
1645
- buffer_json_member_add_array(wb, "columns");
1646
- {
1647
- buffer_json_add_array_item_string(wb, "Node");
1648
- }
1649
- buffer_json_array_close(wb);
1650
- }
1651
- buffer_json_object_close(wb);
1652
-
1653
- buffer_json_member_add_object(wb, "InStatus");
1654
- {
1655
- buffer_json_member_add_string(wb, "name", "Nodes by Collection Status");
1656
- buffer_json_member_add_array(wb, "columns");
1657
- {
1658
- buffer_json_add_array_item_string(wb, "InStatus");
1659
- }
1660
- buffer_json_array_close(wb);
1661
- }
1662
- buffer_json_object_close(wb);
1663
-
1664
- buffer_json_member_add_object(wb, "OutStatus");
1665
- {
1666
- buffer_json_member_add_string(wb, "name", "Nodes by Streaming Status");
1667
- buffer_json_member_add_array(wb, "columns");
1668
- {
1669
- buffer_json_add_array_item_string(wb, "OutStatus");
1670
- }
1671
- buffer_json_array_close(wb);
1672
- }
1673
- buffer_json_object_close(wb);
1674
-
1675
- buffer_json_member_add_object(wb, "MlStatus");
1676
- {
1677
- buffer_json_member_add_string(wb, "name", "Nodes by ML Status");
1678
- buffer_json_member_add_array(wb, "columns");
1679
- {
1680
- buffer_json_add_array_item_string(wb, "MlStatus");
1681
- }
1682
- buffer_json_array_close(wb);
1683
- }
1684
- buffer_json_object_close(wb);
1685
-
1686
- buffer_json_member_add_object(wb, "InRemoteIP");
1687
- {
1688
- buffer_json_member_add_string(wb, "name", "Nodes by Inbound IP");
1689
- buffer_json_member_add_array(wb, "columns");
1690
- {
1691
- buffer_json_add_array_item_string(wb, "InRemoteIP");
1692
- }
1693
- buffer_json_array_close(wb);
1694
- }
1695
- buffer_json_object_close(wb);
1696
-
1697
- buffer_json_member_add_object(wb, "OutRemoteIP");
1698
- {
1699
- buffer_json_member_add_string(wb, "name", "Nodes by Outbound IP");
1700
- buffer_json_member_add_array(wb, "columns");
1701
- {
1702
- buffer_json_add_array_item_string(wb, "OutRemoteIP");
1703
- }
1704
- buffer_json_array_close(wb);
1705
- }
1706
- buffer_json_object_close(wb);
1707
- }
1708
- buffer_json_object_close(wb); // group_by
1709
-
1710
- buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1);
1711
- buffer_json_finalize(wb);
1712
-
1713
- int response = HTTP_RESP_OK;
1714
- if(is_cancelled_cb && is_cancelled_cb(is_cancelled_cb_data)) {
1715
- buffer_flush(wb);
1716
- response = HTTP_RESP_CLIENT_CLOSED_REQUEST;
1717
- }
1718
-
1719
- if(result_cb)
1720
- result_cb(wb, response, result_cb_data);
371
1722
- return response;
372
+ return ret;
373
}