@cryptotaxi247 / netdata-1 / commits / c980f48dd

/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},