/api/v2 improvements (#15227)
* readers should be able to recursively acquire the lock, even when there is a writer waiting * added health section into nodes * uniformity of nodes * nodes instances should not return node info; http_api_v2 capability should be version 4 everywhere * added /api/v2/versions * added /api/v2/functions * /api/v2/version should be neat
Costa Tsaousis committed
Jun 21, 2023 at 22:31 UTC
c980f48ddad30002170c06d1ae106bf73b40e9bd
7 files changed
+238
-117
aclk/aclk_capas.c
+7
-5
@@ -4,17 +4,19 @@
4
5
#include "ml/ml.h"
6
7
+#define HTTP_API_V2_VERSION 4
8
+
9
const struct capability *aclk_get_agent_capas()
10
{
11
static struct capability agent_capabilities[] = {
12
{ .name = "json", .version = 2, .enabled = 0 },
13
{ .name = "proto", .version = 1, .enabled = 1 },
12
- { .name = "ml", .version = 0, .enabled = 0 },
13
- { .name = "mc", .version = 0, .enabled = 0 },
14
+ { .name = "ml", .version = 0, .enabled = 0 }, // index 2, below
15
+ { .name = "mc", .version = 0, .enabled = 0 }, // index 3, below
16
{ .name = "ctx", .version = 1, .enabled = 1 },
17
{ .name = "funcs", .version = 1, .enabled = 1 },
16
- { .name = "http_api_v2", .version = 4, .enabled = 1 },
17
- { .name = "health", .version = 1, .enabled = 0 },
18
+ { .name = "http_api_v2", .version = HTTP_API_V2_VERSION, .enabled = 1 },
19
+ { .name = "health", .version = 1, .enabled = 0 }, // index 7, below
20
{ .name = "req_cancel", .version = 1, .enabled = 1 },
21
{ .name = NULL, .version = 0, .enabled = 0 }
22
};
@@ -41,7 +43,7 @@ struct capability *aclk_get_node_instance_capas(RRDHOST *host)
43
.enabled = enable_metric_correlations },
44
{ .name = "ctx", .version = 1, .enabled = 1 },
45
{ .name = "funcs", .version = functions ? 1 : 0, .enabled = functions ? 1 : 0 },
44
- { .name = "http_api_v2", .version = 3, .enabled = 1 },
46
+ { .name = "http_api_v2", .version = HTTP_API_V2_VERSION, .enabled = 1 },
47
{ .name = "health", .version = 1, .enabled = host->health.health_enabled },
48
{ .name = "req_cancel", .version = 1, .enabled = 1 },
49
{ .name = NULL, .version = 0, .enabled = 0 }
database/contexts/api_v2.c
+197
-105
@@ -58,6 +58,13 @@ static const char *fts_match_to_string(FTS_MATCH match) {
58
}
59
}
60
61
+struct rrdfunction_to_json_v2 {
62
+ size_t size;
63
+ size_t used;
64
+ size_t *node_ids;
65
+ STRING *help;
66
+};
67
+
68
struct rrdcontext_to_json_v2_entry {
69
size_t count;
70
STRING *id;
@@ -115,6 +122,10 @@ struct rrdcontext_to_json_v2_data {
122
FTS_INDEX fts;
123
} q;
124
125
+ struct {
126
+ DICTIONARY *dict;
127
+ } functions;
128
+
129
struct {
130
bool enabled;
131
bool relative;
@@ -389,6 +400,25 @@ static void agent_capabilities_to_json(BUFFER *wb, RRDHOST *host, const char *ke
400
freez(capas);
401
}
402
403
+static inline void rrdhost_health_to_json_v2(BUFFER *wb, const char *key, RRDHOST_STATUS *s) {
404
+ buffer_json_member_add_object(wb, key);
405
+ {
406
+ buffer_json_member_add_string(wb, "status", rrdhost_health_status_to_string(s->health.status));
407
+ if (s->health.status == RRDHOST_HEALTH_STATUS_RUNNING) {
408
+ buffer_json_member_add_object(wb, "alerts");
409
+ {
410
+ buffer_json_member_add_uint64(wb, "critical", s->health.alerts.critical);
411
+ buffer_json_member_add_uint64(wb, "warning", s->health.alerts.warning);
412
+ buffer_json_member_add_uint64(wb, "clear", s->health.alerts.clear);
413
+ buffer_json_member_add_uint64(wb, "undefined", s->health.alerts.undefined);
414
+ buffer_json_member_add_uint64(wb, "uninitialized", s->health.alerts.uninitialized);
415
+ }
416
+ buffer_json_object_close(wb); // alerts
417
+ }
418
+ }
419
+ buffer_json_object_close(wb); // health
420
+}
421
+
422
static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool queryable_host) {
423
if(!queryable_host || !host->rrdctx.contexts)
424
// the host matches the 'scope_host' but does not match the 'host' patterns
@@ -451,115 +481,113 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu
481
if(!host_matched)
482
return 0;
483
484
+ if(ctl->options & CONTEXTS_V2_FUNCTIONS) {
485
+ struct rrdfunction_to_json_v2 t = {
486
+ .used = 1,
487
+ .size = 1,
488
+ .node_ids = &ctl->nodes.ni,
489
+ .help = NULL,
490
+ };
491
+ host_functions_to_dict(host, ctl->functions.dict, &t, sizeof(t), &t.help);
492
+ }
493
+
494
if(ctl->options & CONTEXTS_V2_NODES) {
495
buffer_json_add_array_item_object(wb); // this node
496
buffer_json_node_add_v2(wb, host, ctl->nodes.ni++, 0,
497
(ctl->options & CONTEXTS_V2_AGENTS) && !(ctl->options & CONTEXTS_V2_NODES_INSTANCES));
498
459
- if(ctl->options & (CONTEXTS_V2_NODES_INFO)) {
460
- buffer_json_member_add_string(wb, "v", rrdhost_program_version(host));
499
+ if(ctl->options & (CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES)) {
500
+ RRDHOST_STATUS s;
501
+ rrdhost_status(host, ctl->now, &s);
502
462
- host_labels2json(host, wb, "labels");
503
+ if (ctl->options & (CONTEXTS_V2_NODES_INFO)) {
504
+ buffer_json_member_add_string(wb, "v", rrdhost_program_version(host));
505
464
- if (host->system_info) {
465
- buffer_json_member_add_object(wb, "hw");
466
- {
467
- buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
468
- buffer_json_member_add_string_or_empty(wb, "cpuFrequency", host->system_info->host_cpu_freq);
469
- buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
470
- buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
471
- buffer_json_member_add_string_or_empty(wb, "diskSpace", host->system_info->host_disk_space);
472
- buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
473
- buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
474
- }
475
- buffer_json_object_close(wb);
506
+ host_labels2json(host, wb, "labels");
507
477
- buffer_json_member_add_object(wb, "os");
478
- {
479
- buffer_json_member_add_string_or_empty(wb, "id", host->system_info->host_os_id);
480
- buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->host_os_name);
481
- buffer_json_member_add_string_or_empty(wb, "v", host->system_info->host_os_version);
482
- buffer_json_member_add_object(wb, "kernel");
483
- buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->kernel_name);
484
- buffer_json_member_add_string_or_empty(wb, "v", host->system_info->kernel_version);
508
+ if (host->system_info) {
509
+ buffer_json_member_add_object(wb, "hw");
510
+ {
511
+ buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture);
512
+ buffer_json_member_add_string_or_empty(wb, "cpu_frequency", host->system_info->host_cpu_freq);
513
+ buffer_json_member_add_string_or_empty(wb, "cpus", host->system_info->host_cores);
514
+ buffer_json_member_add_string_or_empty(wb, "memory", host->system_info->host_ram_total);
515
+ buffer_json_member_add_string_or_empty(wb, "disk_space", host->system_info->host_disk_space);
516
+ buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization);
517
+ buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container);
518
+ }
519
buffer_json_object_close(wb);
486
- }
487
- buffer_json_object_close(wb);
488
- }
520
490
- // created - the node is created but never connected to cloud
491
- // unreachable - not currently connected
492
- // stale - connected but not having live data
493
- // reachable - connected with live data
494
- // pruned - not connected for some time and has been removed
495
- buffer_json_member_add_string(wb, "state", rrdhost_state_cloud_emulation(host)?"reachable":"stale");
521
+ buffer_json_member_add_object(wb, "os");
522
+ {
523
+ buffer_json_member_add_string_or_empty(wb, "id", host->system_info->host_os_id);
524
+ buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->host_os_name);
525
+ buffer_json_member_add_string_or_empty(wb, "v", host->system_info->host_os_version);
526
+ buffer_json_member_add_object(wb, "kernel");
527
+ buffer_json_member_add_string_or_empty(wb, "nm", host->system_info->kernel_name);
528
+ buffer_json_member_add_string_or_empty(wb, "v", host->system_info->kernel_version);
529
+ buffer_json_object_close(wb);
530
+ }
531
+ buffer_json_object_close(wb);
532
+ }
533
497
- agent_capabilities_to_json(wb, host, "capabilities");
498
- }
534
+ // created - the node is created but never connected to cloud
535
+ // unreachable - not currently connected
536
+ // stale - connected but not having live data
537
+ // reachable - connected with live data
538
+ // pruned - not connected for some time and has been removed
539
+ buffer_json_member_add_string(wb, "state", rrdhost_state_cloud_emulation(host) ? "reachable" : "stale");
540
500
- if(ctl->options & (CONTEXTS_V2_NODES_INSTANCES)) {
501
- buffer_json_member_add_array(wb, "instances");
502
- buffer_json_add_array_item_object(wb); // this instance
503
- {
504
- buffer_json_agent_status_id(wb, 0, 0);
541
+ rrdhost_health_to_json_v2(wb, "health", &s);
542
+ agent_capabilities_to_json(wb, host, "capabilities");
543
+ }
544
506
- time_t now = now_realtime_sec();
507
- RRDHOST_STATUS s;
508
- rrdhost_status(host, now, &s);
509
- buffer_json_member_add_object(wb, "db");
545
+ if (ctl->options & (CONTEXTS_V2_NODES_INSTANCES)) {
546
+ buffer_json_member_add_array(wb, "instances");
547
+ buffer_json_add_array_item_object(wb); // this instance
548
{
511
- buffer_json_member_add_string(wb, "status", rrdhost_db_status_to_string(s.db.status));
512
- buffer_json_member_add_string(wb, "liveness", rrdhost_db_liveness_to_string(s.db.liveness));
513
- buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
514
- buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
515
- buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
516
- buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
517
- buffer_json_member_add_uint64(wb, "instances", s.db.instances);
518
- buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
519
- }
520
- buffer_json_object_close(wb);
549
+ buffer_json_agent_status_id(wb, 0, 0);
550
522
- rrdhost_receiver_to_json(wb, &s, "ingest");
523
- rrdhost_sender_to_json(wb, &s, "stream");
524
-
525
- buffer_json_member_add_object(wb, "ml");
526
- buffer_json_member_add_string(wb, "status", rrdhost_ml_status_to_string(s.ml.status));
527
- buffer_json_member_add_string(wb, "type", rrdhost_ml_type_to_string(s.ml.type));
528
- if (s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
529
- buffer_json_member_add_object(wb, "metrics");
551
+ buffer_json_member_add_object(wb, "db");
552
{
531
- buffer_json_member_add_uint64(wb, "anomalous", s.ml.metrics.anomalous);
532
- buffer_json_member_add_uint64(wb, "normal", s.ml.metrics.normal);
533
- buffer_json_member_add_uint64(wb, "trained", s.ml.metrics.trained);
534
- buffer_json_member_add_uint64(wb, "pending", s.ml.metrics.pending);
535
- buffer_json_member_add_uint64(wb, "silenced", s.ml.metrics.silenced);
553
+ buffer_json_member_add_string(wb, "status", rrdhost_db_status_to_string(s.db.status));
554
+ buffer_json_member_add_string(wb, "liveness", rrdhost_db_liveness_to_string(s.db.liveness));
555
+ buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
556
+ buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
557
+ buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
558
+ buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
559
+ buffer_json_member_add_uint64(wb, "instances", s.db.instances);
560
+ buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
561
}
537
- buffer_json_object_close(wb); // metrics
538
- }
539
- buffer_json_object_close(wb); // ml
562
+ buffer_json_object_close(wb);
563
541
- buffer_json_member_add_object(wb, "health");
542
- {
543
- buffer_json_member_add_string(wb, "status", rrdhost_health_status_to_string(s.health.status));
544
- if (s.health.status == RRDHOST_HEALTH_STATUS_RUNNING) {
545
- buffer_json_member_add_object(wb, "alerts");
564
+ rrdhost_receiver_to_json(wb, &s, "ingest");
565
+ rrdhost_sender_to_json(wb, &s, "stream");
566
+
567
+ buffer_json_member_add_object(wb, "ml");
568
+ buffer_json_member_add_string(wb, "status", rrdhost_ml_status_to_string(s.ml.status));
569
+ buffer_json_member_add_string(wb, "type", rrdhost_ml_type_to_string(s.ml.type));
570
+ if (s.ml.status == RRDHOST_ML_STATUS_RUNNING) {
571
+ buffer_json_member_add_object(wb, "metrics");
572
{
547
- buffer_json_member_add_uint64(wb, "critical", s.health.alerts.critical);
548
- buffer_json_member_add_uint64(wb, "warning", s.health.alerts.warning);
549
- buffer_json_member_add_uint64(wb, "clear", s.health.alerts.clear);
550
- buffer_json_member_add_uint64(wb, "undefined", s.health.alerts.undefined);
551
- buffer_json_member_add_uint64(wb, "uninitialized", s.health.alerts.uninitialized);
573
+ buffer_json_member_add_uint64(wb, "anomalous", s.ml.metrics.anomalous);
574
+ buffer_json_member_add_uint64(wb, "normal", s.ml.metrics.normal);
575
+ buffer_json_member_add_uint64(wb, "trained", s.ml.metrics.trained);
576
+ buffer_json_member_add_uint64(wb, "pending", s.ml.metrics.pending);
577
+ buffer_json_member_add_uint64(wb, "silenced", s.ml.metrics.silenced);
578
}
553
- buffer_json_object_close(wb); // alerts
579
+ buffer_json_object_close(wb); // metrics
580
}
555
- }
556
- buffer_json_object_close(wb); // health
581
+ buffer_json_object_close(wb); // ml
582
558
- host_functions2json(host, wb); // functions
559
- agent_capabilities_to_json(wb, host, "capabilities");
583
+ rrdhost_health_to_json_v2(wb, "health", &s);
584
+
585
+ host_functions2json(host, wb); // functions
586
+ agent_capabilities_to_json(wb, host, "capabilities");
587
+ }
588
+ buffer_json_object_close(wb); // this instance
589
+ buffer_json_array_close(wb); // instances
590
}
561
- buffer_json_object_close(wb); // this instance
562
- buffer_json_array_close(wb); // instances
591
}
592
buffer_json_object_close(wb); // this node
593
}
@@ -694,7 +722,37 @@ void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings
722
buffer_json_object_close(wb);
723
}
724
697
-bool contexts_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
725
+static void functions_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
726
+ struct rrdfunction_to_json_v2 *t = value;
727
+
728
+ // it is initialized with a static reference - we need to mallocz() the array
729
+ size_t *v = t->node_ids;
730
+ t->node_ids = mallocz(sizeof(size_t));
731
+ *t->node_ids = *v;
732
+ t->size = 1;
733
+ t->used = 1;
734
+}
735
+
736
+static bool functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
737
+ struct rrdfunction_to_json_v2 *t = old_value, *n = new_value;
738
+ size_t *v = n->node_ids;
739
+
740
+ if(t->used >= t->size) {
741
+ t->node_ids = reallocz(t->node_ids, t->size * 2 * sizeof(size_t));
742
+ t->size *= 2;
743
+ }
744
+
745
+ t->node_ids[t->used++] = *v;
746
+
747
+ return true;
748
+}
749
+
750
+static void functions_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
751
+ struct rrdfunction_to_json_v2 *t = value;
752
+ freez(t->node_ids);
753
+}
754
+
755
+static bool contexts_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
756
struct rrdcontext_to_json_v2_entry *o = old_value;
757
struct rrdcontext_to_json_v2_entry *n = new_value;
758
@@ -736,7 +794,7 @@ bool contexts_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void
794
return true;
795
}
796
739
-void contexts_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
797
+static void contexts_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
798
struct rrdcontext_to_json_v2_entry *z = value;
799
string_freez(z->family);
800
}
@@ -784,6 +842,16 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
842
dictionary_register_delete_callback(ctl.ctx, contexts_delete_callback, NULL);
843
}
844
845
+ if(options & CONTEXTS_V2_FUNCTIONS) {
846
+ ctl.functions.dict = dictionary_create_advanced(
847
+ DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL,
848
+ sizeof(struct rrdfunction_to_json_v2));
849
+
850
+ dictionary_register_insert_callback(ctl.functions.dict, functions_insert_callback, NULL);
851
+ dictionary_register_conflict_callback(ctl.functions.dict, functions_conflict_callback, NULL);
852
+ dictionary_register_delete_callback(ctl.functions.dict, functions_delete_callback, NULL);
853
+ }
854
+
855
if(req->after || req->before) {
856
ctl.window.relative = rrdr_relative_window_to_absolute(&ctl.window.after, &ctl.window.before, &ctl.now);
857
ctl.window.enabled = true;
@@ -848,33 +916,56 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
916
917
ctl.timings.executed_ut = now_monotonic_usec();
918
919
+ if(options & CONTEXTS_V2_FUNCTIONS) {
920
+ buffer_json_member_add_array(wb, "functions");
921
+ {
922
+ struct rrdfunction_to_json_v2 *t;
923
+ dfe_start_read(ctl.functions.dict, t) {
924
+ buffer_json_add_array_item_object(wb);
925
+ buffer_json_member_add_string(wb, "name", t_dfe.name);
926
+ buffer_json_member_add_string(wb, "help", string2str(t->help));
927
+ buffer_json_member_add_array(wb, "ni");
928
+ for(size_t i = 0; i < t->used ;i++)
929
+ buffer_json_add_array_item_uint64(wb, t->node_ids[i]);
930
+ buffer_json_array_close(wb);
931
+ buffer_json_object_close(wb);
932
+ }
933
+ dfe_done(t);
934
+ }
935
+ buffer_json_array_close(wb);
936
+ }
937
+
938
if(options & CONTEXTS_V2_CONTEXTS) {
939
buffer_json_member_add_object(wb, "contexts");
853
- struct rrdcontext_to_json_v2_entry *z;
854
- dfe_start_read(ctl.ctx, z){
855
- bool collected = z->flags & RRD_FLAG_COLLECTED;
940
+ {
941
+ struct rrdcontext_to_json_v2_entry *z;
942
+ dfe_start_read(ctl.ctx, z) {
943
+ bool collected = z->flags & RRD_FLAG_COLLECTED;
944
857
- buffer_json_member_add_object(wb, string2str(z->id));
858
- {
859
- buffer_json_member_add_string(wb, "family", string2str(z->family));
860
- buffer_json_member_add_uint64(wb, "priority", z->priority);
861
- buffer_json_member_add_time_t(wb, "first_entry", z->first_time_s);
862
- buffer_json_member_add_time_t(wb, "last_entry", collected ? ctl.now : z->last_time_s);
863
- buffer_json_member_add_boolean(wb, "live", collected);
864
- if (options & CONTEXTS_V2_SEARCH)
865
- buffer_json_member_add_string(wb, "match", fts_match_to_string(z->match));
945
+ buffer_json_member_add_object(wb, string2str(z->id));
946
+ {
947
+ buffer_json_member_add_string(wb, "family", string2str(z->family));
948
+ buffer_json_member_add_uint64(wb, "priority", z->priority);
949
+ buffer_json_member_add_time_t(wb, "first_entry", z->first_time_s);
950
+ buffer_json_member_add_time_t(wb, "last_entry", collected ? ctl.now : z->last_time_s);
951
+ buffer_json_member_add_boolean(wb, "live", collected);
952
+ if (options & CONTEXTS_V2_SEARCH)
953
+ buffer_json_member_add_string(wb, "match", fts_match_to_string(z->match));
954
+ }
955
+ buffer_json_object_close(wb);
956
}
867
- buffer_json_object_close(wb);
957
+ dfe_done(z);
958
}
869
- dfe_done(z);
959
buffer_json_object_close(wb); // contexts
960
}
961
962
if(options & CONTEXTS_V2_SEARCH) {
963
buffer_json_member_add_object(wb, "searches");
875
- buffer_json_member_add_uint64(wb, "strings", ctl.q.fts.string_searches);
876
- buffer_json_member_add_uint64(wb, "char", ctl.q.fts.char_searches);
877
- buffer_json_member_add_uint64(wb, "total", ctl.q.fts.searches);
964
+ {
965
+ buffer_json_member_add_uint64(wb, "strings", ctl.q.fts.string_searches);
966
+ buffer_json_member_add_uint64(wb, "char", ctl.q.fts.char_searches);
967
+ buffer_json_member_add_uint64(wb, "total", ctl.q.fts.searches);
968
+ }
969
buffer_json_object_close(wb);
970
}
971
@@ -889,6 +980,7 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
980
buffer_json_finalize(wb);
981
982
cleanup:
983
+ dictionary_destroy(ctl.functions.dict);
984
dictionary_destroy(ctl.ctx);
985
simple_pattern_free(ctl.nodes.scope_pattern);
986
simple_pattern_free(ctl.nodes.pattern);
database/contexts/rrdcontext.h
+1
@@ -486,6 +486,7 @@ typedef enum __attribute__ ((__packed__)) {
486
CONTEXTS_V2_AGENTS = (1 << 7),
487
CONTEXTS_V2_AGENTS_INFO = (1 << 8),
488
CONTEXTS_V2_VERSIONS = (1 << 9),
489
+ CONTEXTS_V2_FUNCTIONS = (1 << 10),
490
} CONTEXTS_V2_OPTIONS;
491
492
int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_OPTIONS options);
database/rrdfunctions.c
+18
-2
@@ -779,18 +779,34 @@ void host_functions2json(RRDHOST *host, BUFFER *wb) {
779
buffer_json_object_close(wb);
780
}
781
782
-void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst) {
782
+void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst, void *value, size_t value_size) {
783
if(!rrdset_functions_view || !dst) return;
784
785
struct rrd_collector_function *t;
786
dfe_start_read(rrdset_functions_view, t) {
787
if(!t->collector->running) continue;
788
789
- dictionary_set(dst, t_dfe.name, NULL, 0);
789
+ dictionary_set(dst, t_dfe.name, value, value_size);
790
}
791
dfe_done(t);
792
}
793
794
+void host_functions_to_dict(RRDHOST *host, DICTIONARY *dst, void *value, size_t value_size, STRING **help) {
795
+ if(!host || !host->functions || !dictionary_entries(host->functions) || !dst) return;
796
+
797
+ struct rrd_collector_function *t;
798
+ dfe_start_read(host->functions, t) {
799
+ if(!t->collector->running) continue;
800
+
801
+ if(help)
802
+ *help = t->help;
803
+
804
+ dictionary_set(dst, t_dfe.name, value, value_size);
805
+ }
806
+ dfe_done(t);
807
+}
808
+
809
+
810
int rrdhost_function_streaming(BUFFER *wb, int timeout __maybe_unused, const char *function __maybe_unused,
811
void *collector_data __maybe_unused,
812
function_data_ready_callback callback __maybe_unused, void *callback_data __maybe_unused) {
database/rrdfunctions.h
+2
-1
@@ -26,7 +26,8 @@ void rrd_functions_expose_rrdpush(RRDSET *st, BUFFER *wb);
26
void rrd_functions_expose_global_rrdpush(RRDHOST *host, BUFFER *wb);
27
28
void chart_functions2json(RRDSET *st, BUFFER *wb, int tabs, const char *kq, const char *sq);
29
-void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst);
29
+void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst, void *value, size_t value_size);
30
+void host_functions_to_dict(RRDHOST *host, DICTIONARY *dst, void *value, size_t value_size, STRING **help);
31
void host_functions2json(RRDHOST *host, BUFFER *wb);
32
33
uint8_t functions_format_to_content_type(const char *format);
web/api/formatters/json_wrapper.c
+1
-1
@@ -665,7 +665,7 @@ static inline void query_target_functions(BUFFER *wb, const char *key, RRDR *r)
665
continue;
666
667
ria = qi->ria;
668
- chart_functions_to_dict(rrdinstance_acquired_functions(ria), funcs);
668
+ chart_functions_to_dict(rrdinstance_acquired_functions(ria), funcs, NULL, 0);
669
}
670
671
buffer_json_member_add_array(wb, key);
web/api/web_api_v2.c
+12
-3
@@ -38,6 +38,14 @@ static int web_client_api_request_v2_contexts_internal(RRDHOST *host __maybe_unu
38
return rrdcontext_to_json_v2(w->response.data, &req, options);
39
}
40
41
+static int web_client_api_request_v2_functions(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
42
+ return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_FUNCTIONS | CONTEXTS_V2_NODES | CONTEXTS_V2_AGENTS | CONTEXTS_V2_VERSIONS);
43
+}
44
+
45
+static int web_client_api_request_v2_versions(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
46
+ return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_VERSIONS);
47
+}
48
+
49
static int web_client_api_request_v2_q(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
50
return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_SEARCH | CONTEXTS_V2_CONTEXTS | CONTEXTS_V2_NODES | CONTEXTS_V2_AGENTS | CONTEXTS_V2_VERSIONS);
51
}
@@ -51,12 +59,11 @@ static int web_client_api_request_v2_nodes(RRDHOST *host __maybe_unused, struct
59
}
60
61
static int web_client_api_request_v2_nodes_instances(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
54
- return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_INFO | CONTEXTS_V2_NODES_INSTANCES | CONTEXTS_V2_AGENTS | CONTEXTS_V2_AGENTS_INFO | CONTEXTS_V2_VERSIONS);
62
+ return web_client_api_request_v2_contexts_internal(host, w, url, CONTEXTS_V2_NODES | CONTEXTS_V2_NODES_INSTANCES | CONTEXTS_V2_AGENTS | CONTEXTS_V2_AGENTS_INFO | CONTEXTS_V2_VERSIONS);
63
}
64
65
static int web_client_api_request_v2_weights(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
58
- return web_client_api_request_weights(host, w, url, WEIGHTS_METHOD_VALUE,
59
- WEIGHTS_FORMAT_MULTINODE, 2);
66
+ return web_client_api_request_weights(host, w, url, WEIGHTS_METHOD_VALUE, WEIGHTS_FORMAT_MULTINODE, 2);
67
}
68
69
#define GROUP_BY_KEY_MAX_LENGTH 30
@@ -361,6 +368,8 @@ static struct web_api_command api_commands_v2[] = {
368
{"nodes_instances", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_nodes_instances},
369
{"contexts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_contexts},
370
{"weights", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_weights},
371
+ {"versions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_versions},
372
+ {"functions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_functions},
373
{"q", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_q},
374
375
{"rtc_offer", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v2_webrtc},