versioning for functions (#18902)
Costa Tsaousis committed
Oct 30, 2024 at 23:55 UTC
37dd2542975b5f968f17ed31e7c4fa9b460973f8
18 files changed
+91
-34
src/collectors/cgroups.plugin/sys_fs_cgroup.c
+4
-2
@@ -1405,12 +1405,14 @@ void *cgroups_main(void *ptr) {
1405
cgroup_netdev_link_init();
1406
1407
rrd_function_add_inline(localhost, NULL, "containers-vms", 10,
1408
- RRDFUNCTIONS_PRIORITY_DEFAULT / 2, RRDFUNCTIONS_CGTOP_HELP,
1408
+ RRDFUNCTIONS_PRIORITY_DEFAULT / 2, RRDFUNCTIONS_VERSION_DEFAULT,
1409
+ RRDFUNCTIONS_CGTOP_HELP,
1410
"top", HTTP_ACCESS_ANONYMOUS_DATA,
1411
cgroup_function_cgroup_top);
1412
1413
rrd_function_add_inline(localhost, NULL, "systemd-services", 10,
1413
- RRDFUNCTIONS_PRIORITY_DEFAULT / 3, RRDFUNCTIONS_SYSTEMD_SERVICES_HELP,
1414
+ RRDFUNCTIONS_PRIORITY_DEFAULT / 3, RRDFUNCTIONS_VERSION_DEFAULT,
1415
+ RRDFUNCTIONS_SYSTEMD_SERVICES_HELP,
1416
"top", HTTP_ACCESS_ANONYMOUS_DATA,
1417
cgroup_function_systemd_top);
1418
src/collectors/diskspace.plugin/plugin_diskspace.c
+2
-1
@@ -849,7 +849,8 @@ void *diskspace_main(void *ptr) {
849
worker_register_job_name(WORKER_JOB_CLEANUP, "cleanup");
850
851
rrd_function_add_inline(localhost, NULL, "mount-points", 10,
852
- RRDFUNCTIONS_PRIORITY_DEFAULT, RRDFUNCTIONS_DISKSPACE_HELP,
852
+ RRDFUNCTIONS_PRIORITY_DEFAULT, RRDFUNCTIONS_VERSION_DEFAULT,
853
+ RRDFUNCTIONS_DISKSPACE_HELP,
854
"top", HTTP_ACCESS_ANONYMOUS_DATA,
855
diskspace_function_mount_points);
856
src/collectors/proc.plugin/proc_diskstats.c
+2
-1
@@ -1403,7 +1403,8 @@ int do_proc_diskstats(int update_every, usec_t dt) {
1403
SIMPLE_PATTERN_EXACT, true);
1404
1405
rrd_function_add_inline(localhost, NULL, "block-devices", 10,
1406
- RRDFUNCTIONS_PRIORITY_DEFAULT, RRDFUNCTIONS_DISKSTATS_HELP,
1406
+ RRDFUNCTIONS_PRIORITY_DEFAULT, RRDFUNCTIONS_VERSION_DEFAULT,
1407
+ RRDFUNCTIONS_DISKSTATS_HELP,
1408
"top", HTTP_ACCESS_ANONYMOUS_DATA,
1409
diskstats_function_block_devices);
1410
}
src/collectors/proc.plugin/proc_net_dev.c
+2
-1
@@ -1701,7 +1701,8 @@ void *netdev_main(void *ptr_is_null __maybe_unused)
1701
virtual_device_collect_delay_secs = 300;
1702
1703
rrd_function_add_inline(localhost, NULL, "network-interfaces", 10,
1704
- RRDFUNCTIONS_PRIORITY_DEFAULT, RRDFUNCTIONS_NETDEV_HELP,
1704
+ RRDFUNCTIONS_PRIORITY_DEFAULT, RRDFUNCTIONS_VERSION_DEFAULT,
1705
+ RRDFUNCTIONS_NETDEV_HELP,
1706
"top", HTTP_ACCESS_ANONYMOUS_DATA,
1707
netdev_function_net_interfaces);
1708
src/daemon/config/dyncfg-tree.c
+2
-2
@@ -284,7 +284,7 @@ void dyncfg_host_init(RRDHOST *host) {
284
// This function needs to be async, although it is internal.
285
// The reason is that it can call by itself another function that may or may not be internal (sync).
286
287
- rrd_function_add(host, NULL, PLUGINSD_FUNCTION_CONFIG, 120,
288
- 1000, "Dynamic configuration", "config", HTTP_ACCESS_ANONYMOUS_DATA,
287
+ rrd_function_add(host, NULL, PLUGINSD_FUNCTION_CONFIG, 120, 1000, DYNCFG_FUNCTIONS_VERSION,
288
+ "Dynamic configuration", "config", HTTP_ACCESS_ANONYMOUS_DATA,
289
false, dyncfg_config_execute_cb, host);
290
}
src/daemon/config/dyncfg.c
+1
@@ -358,6 +358,7 @@ bool dyncfg_add_low_level(RRDHOST *host, const char *id, const char *path,
358
string2str(df->function),
359
120,
360
1000,
361
+ DYNCFG_FUNCTIONS_VERSION,
362
"Dynamic configuration",
363
"config",
364
(view_access & edit_access),
src/daemon/config/dyncfg.h
+2
@@ -7,6 +7,8 @@
7
#include "database/rrd.h"
8
#include "database/rrdfunctions.h"
9
10
+#define DYNCFG_FUNCTIONS_VERSION 0
11
+
12
void dyncfg_add_streaming(BUFFER *wb);
13
bool dyncfg_available_for_rrdhost(RRDHOST *host);
14
void dyncfg_host_init(RRDHOST *host);
src/database/contexts/api_v2_contexts.c
+18
-9
@@ -52,6 +52,7 @@ struct function_v2_entry {
52
STRING *tags;
53
HTTP_ACCESS access;
54
int priority;
55
+ uint32_t version;
56
};
57
58
struct context_v2_entry {
@@ -565,15 +566,16 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu
566
567
if(ctl->mode & CONTEXTS_V2_FUNCTIONS) {
568
struct function_v2_entry t = {
568
- .used = 1,
569
- .size = 1,
570
- .node_ids = &ctl->nodes.ni,
571
- .help = NULL,
572
- .tags = NULL,
573
- .access = HTTP_ACCESS_ALL,
574
- .priority = RRDFUNCTIONS_PRIORITY_DEFAULT,
569
+ .used = 1,
570
+ .size = 1,
571
+ .node_ids = &ctl->nodes.ni,
572
+ .help = NULL,
573
+ .tags = NULL,
574
+ .access = HTTP_ACCESS_ALL,
575
+ .priority = RRDFUNCTIONS_PRIORITY_DEFAULT,
576
+ .version = RRDFUNCTIONS_VERSION_DEFAULT,
577
};
576
- host_functions_to_dict(host, ctl->functions.dict, &t, sizeof(t), &t.help, &t.tags, &t.access, &t.priority);
578
+ host_functions_to_dict(host, ctl->functions.dict, &t, sizeof(t), &t.help, &t.tags, &t.access, &t.priority, &t.version);
579
}
580
581
if(ctl->mode & CONTEXTS_V2_NODES) {
@@ -941,7 +943,13 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
943
dfe_start_read(ctl.functions.dict, t) {
944
buffer_json_add_array_item_object(wb);
945
{
944
- buffer_json_member_add_string(wb, "name", t_dfe.name);
946
+ const char *name = t_dfe.name ? strstr(t_dfe.name, RRDFUNCTIONS_VERSION_SEPARATOR) : NULL;
947
+ if(name)
948
+ name += sizeof(RRDFUNCTIONS_VERSION_SEPARATOR) - 1;
949
+ else
950
+ name = t_dfe.name;
951
+
952
+ buffer_json_member_add_string(wb, "name", name);
953
buffer_json_member_add_string(wb, "help", string2str(t->help));
954
buffer_json_member_add_array(wb, "ni");
955
{
@@ -952,6 +960,7 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
960
buffer_json_member_add_string(wb, "tags", string2str(t->tags));
961
http_access2buffer_json_array(wb, "access", t->access);
962
buffer_json_member_add_uint64(wb, "priority", t->priority);
963
+ buffer_json_member_add_uint64(wb, "version", t->version);
964
}
965
buffer_json_object_close(wb);
966
}
src/database/rrdfunctions-exporters.c
+15
-4
@@ -14,13 +14,14 @@ void rrd_chart_functions_expose_rrdpush(RRDSET *st, BUFFER *wb) {
14
if(t->options & RRD_FUNCTION_DYNCFG) continue;
15
16
buffer_sprintf(wb
17
- , PLUGINSD_KEYWORD_FUNCTION " \"%s\" %d \"%s\" \"%s\" "HTTP_ACCESS_FORMAT" %d\n"
17
+ , PLUGINSD_KEYWORD_FUNCTION " \"%s\" %d \"%s\" \"%s\" "HTTP_ACCESS_FORMAT" %d %"PRIu32"\n"
18
, t_dfe.name
19
, t->timeout
20
, string2str(t->help)
21
, string2str(t->tags)
22
, (HTTP_ACCESS_FORMAT_CAST)t->access
23
, t->priority
24
+ , t->version
25
);
26
}
27
dfe_done(t);
@@ -41,13 +42,14 @@ void rrd_global_functions_expose_rrdpush(RRDHOST *host, BUFFER *wb, bool dyncfg)
42
}
43
44
buffer_sprintf(wb
44
- , PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"%s\" "HTTP_ACCESS_FORMAT" %d\n"
45
+ , PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"%s\" "HTTP_ACCESS_FORMAT" %d %"PRIu32"\n"
46
, tmp_dfe.name
47
, tmp->timeout
48
, string2str(tmp->help)
49
, string2str(tmp->tags)
50
, (HTTP_ACCESS_FORMAT_CAST)tmp->access
51
, tmp->priority
52
+ , tmp->version
53
);
54
}
55
dfe_done(tmp);
@@ -66,6 +68,7 @@ static void functions2json(DICTIONARY *functions, BUFFER *wb) {
68
{
69
buffer_json_member_add_string_or_empty(wb, "help", string2str(t->help));
70
buffer_json_member_add_int64(wb, "timeout", (int64_t) t->timeout);
71
+ buffer_json_member_add_uint64(wb, "version", (uint64_t) t->version);
72
73
char options[65];
74
snprintfz(
@@ -105,6 +108,7 @@ void host_functions2json(RRDHOST *host, BUFFER *wb) {
108
{
109
buffer_json_member_add_string(wb, "help", string2str(t->help));
110
buffer_json_member_add_int64(wb, "timeout", t->timeout);
111
+ buffer_json_member_add_uint64(wb, "version", (uint64_t) t->version);
112
buffer_json_member_add_array(wb, "options");
113
{
114
if (t->options & RRD_FUNCTION_GLOBAL)
@@ -138,7 +142,7 @@ void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst,
142
}
143
144
void host_functions_to_dict(RRDHOST *host, DICTIONARY *dst, void *value, size_t value_size,
141
- STRING **help, STRING **tags, HTTP_ACCESS *access, int *priority) {
145
+ STRING **help, STRING **tags, HTTP_ACCESS *access, int *priority, uint32_t *version) {
146
if(!host || !host->functions || !dictionary_entries(host->functions) || !dst) return;
147
148
struct rrd_host_function *t;
@@ -158,7 +162,14 @@ void host_functions_to_dict(RRDHOST *host, DICTIONARY *dst, void *value, size_t
162
if(priority)
163
*priority = t->priority;
164
161
- dictionary_set(dst, t_dfe.name, value, value_size);
165
+ if(version)
166
+ *version = t->version;
167
+
168
+ char key[UINT64_MAX_LENGTH + sizeof(RRDFUNCTIONS_VERSION_SEPARATOR) + strlen(t_dfe.name)];
169
+ snprintfz(key, sizeof(key), "%"PRIu32 RRDFUNCTIONS_VERSION_SEPARATOR "%s",
170
+ t->version, t_dfe.name);
171
+
172
+ dictionary_set(dst, key, value, value_size);
173
}
174
dfe_done(t);
175
}
src/database/rrdfunctions-exporters.h
+3
-1
@@ -5,13 +5,15 @@
5
6
#include "rrd.h"
7
8
+#define RRDFUNCTIONS_VERSION_SEPARATOR "|"
9
+
10
void rrd_chart_functions_expose_rrdpush(RRDSET *st, BUFFER *wb);
11
void rrd_global_functions_expose_rrdpush(RRDHOST *host, BUFFER *wb, bool dyncfg);
12
13
void chart_functions2json(RRDSET *st, BUFFER *wb);
14
void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst, void *value, size_t value_size);
15
void host_functions_to_dict(RRDHOST *host, DICTIONARY *dst, void *value, size_t value_size, STRING **help, STRING **tags,
14
- HTTP_ACCESS *access, int *priority);
16
+ HTTP_ACCESS *access, int *priority, uint32_t *version);
17
void host_functions2json(RRDHOST *host, BUFFER *wb);
18
19
#endif //NETDATA_RRDFUNCTIONS_EXPORTERS_H
src/database/rrdfunctions-inline.c
+3
-2
@@ -30,7 +30,7 @@ static int rrd_function_run_inline(struct rrd_function_execute *rfe, void *data)
30
return code;
31
}
32
33
-void rrd_function_add_inline(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority,
33
+void rrd_function_add_inline(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority, uint32_t version,
34
const char *help, const char *tags,
35
HTTP_ACCESS access, rrd_function_execute_inline_cb_t execute_cb) {
36
@@ -39,6 +39,7 @@ void rrd_function_add_inline(RRDHOST *host, RRDSET *st, const char *name, int ti
39
struct rrd_function_inline *fi = callocz(1, sizeof(struct rrd_function_inline));
40
fi->cb = execute_cb;
41
42
- rrd_function_add(host, st, name, timeout, priority, help, tags, access, true,
42
+ rrd_function_add(host, st, name, timeout, priority, version,
43
+ help, tags, access, true,
44
rrd_function_run_inline, fi);
45
}
src/database/rrdfunctions-inline.h
+1
-1
@@ -7,7 +7,7 @@
7
8
typedef int (*rrd_function_execute_inline_cb_t)(BUFFER *wb, const char *function, BUFFER *payload, const char *source);
9
10
-void rrd_function_add_inline(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority,
10
+void rrd_function_add_inline(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority, uint32_t version,
11
const char *help, const char *tags,
12
HTTP_ACCESS access, rrd_function_execute_inline_cb_t execute_cb);
13
src/database/rrdfunctions-internals.h
+1
@@ -24,6 +24,7 @@ struct rrd_host_function {
24
STRING *tags;
25
int timeout; // the default timeout of the function
26
int priority;
27
+ uint32_t version;
28
29
rrd_function_execute_cb_t execute_cb;
30
void *execute_cb_data;
src/database/rrdfunctions.c
+16
-4
@@ -94,13 +94,24 @@ static bool rrd_functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_
94
95
if(rdcf->timeout != new_rdcf->timeout) {
96
nd_log(NDLS_DAEMON, NDLP_DEBUG,
97
- "FUNCTIONS: function '%s' of host '%s' changed timeout",
98
- dictionary_acquired_item_name(item), rrdhost_hostname(host));
97
+ "FUNCTIONS: function '%s' of host '%s' changed timeout (from %d to %d)",
98
+ dictionary_acquired_item_name(item), rrdhost_hostname(host),
99
+ rdcf->timeout, new_rdcf->timeout);
100
101
rdcf->timeout = new_rdcf->timeout;
102
changed = true;
103
}
104
105
+ if(rdcf->version != new_rdcf->version) {
106
+ nd_log(NDLS_DAEMON, NDLP_DEBUG,
107
+ "FUNCTIONS: function '%s' of host '%s' changed version (from %"PRIu32", to %"PRIu32")",
108
+ dictionary_acquired_item_name(item), rrdhost_hostname(host),
109
+ rdcf->version, new_rdcf->version);
110
+
111
+ rdcf->version = new_rdcf->version;
112
+ changed = true;
113
+ }
114
+
115
if(rdcf->priority != new_rdcf->priority) {
116
nd_log(NDLS_DAEMON, NDLP_DEBUG,
117
"FUNCTIONS: function '%s' of host '%s' changed priority",
@@ -188,7 +199,7 @@ static inline RRD_FUNCTION_OPTIONS get_function_options(RRDSET *st, const char *
199
return options | (is_function_restricted(name, tags) ? RRD_FUNCTION_RESTRICTED : 0);
200
}
201
191
-void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority,
202
+void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority, uint32_t version,
203
const char *help, const char *tags,
204
HTTP_ACCESS access, bool sync,
205
rrd_function_execute_cb_t execute_cb, void *execute_cb_data) {
@@ -212,13 +223,14 @@ void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout,
223
struct rrd_host_function tmp = {
224
.sync = sync,
225
.timeout = timeout,
226
+ .version = version,
227
+ .priority = priority,
228
.options = get_function_options(st, name, tags),
229
.access = access,
230
.execute_cb = execute_cb,
231
.execute_cb_data = execute_cb_data,
232
.help = string_strdupz(help),
233
.tags = string_strdupz(tags),
221
- .priority = priority,
234
};
235
const DICTIONARY_ITEM *item = dictionary_set_and_acquire_item(host->functions, key, &tmp, sizeof(tmp));
236
src/database/rrdfunctions.h
+2
-1
@@ -7,6 +7,7 @@
7
#include "libnetdata/libnetdata.h"
8
9
#define RRDFUNCTIONS_PRIORITY_DEFAULT 100
10
+#define RRDFUNCTIONS_VERSION_DEFAULT 0
11
#define RRDFUNCTIONS_TAG_HIDDEN "hidden"
12
13
#define RRDFUNCTIONS_TIMEOUT_EXTENSION_UT (1 * USEC_PER_SEC)
@@ -67,7 +68,7 @@ void rrd_functions_host_init(RRDHOST *host);
68
void rrd_functions_host_destroy(RRDHOST *host);
69
70
// add a function, to be run from the collector
70
-void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority, const char *help, const char *tags,
71
+void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout, int priority, uint32_t version, const char *help, const char *tags,
72
HTTP_ACCESS access, bool sync, rrd_function_execute_cb_t execute_cb,
73
void *execute_cb_data);
74
src/plugins.d/README.md
+4
-2
@@ -461,13 +461,15 @@ The `source` is an integer field that can have the following values:
461
462
The plugin can register functions to Netdata, like this:
463
464
-> FUNCTION [GLOBAL] "name and parameters of the function" timeout "help string for users" "tags" "access"
464
+> FUNCTION [GLOBAL] "name and parameters of the function" timeout "help string for users" "tags" "access" priority version
465
466
- Tags currently recognized are either `top` or `logs` (or both, space separated).
467
- Access is one of `any`, `member`, or `admin`:
468
- `any` to offer the function to all users of Netdata, even if they are not authenticated.
469
- `member` to offer the function to all authenticated members of Netdata.
470
- - `admin` to offer the function only to authenticated administrators.
470
+ - `admin` to offer the function only to authenticated administrators.
471
+- Priority defines the position of the function relative to the other functions (default is 100).
472
+- Version defines the version of the function (default is 0).
473
474
Users can use a function to ask for more information from the collector. Netdata maintains a registry of functions in 2 levels:
475
src/plugins.d/pluginsd_functions.c
+10
-3
@@ -285,6 +285,7 @@ PARSER_RC pluginsd_function(char **words, size_t num_words, PARSER *parser) {
285
char *tags = get_word(words, num_words, i++);
286
char *access_str = get_word(words, num_words, i++);
287
char *priority_str = get_word(words, num_words, i++);
288
+ char *version_str = get_word(words, num_words, i++);
289
290
RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_FUNCTION);
291
if(!host) return PARSER_RC_ERROR;
@@ -293,12 +294,14 @@ PARSER_RC pluginsd_function(char **words, size_t num_words, PARSER *parser) {
294
if(!st) global = true;
295
296
if (unlikely(!timeout_str || !name || !help || (!global && !st))) {
296
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s' got a FUNCTION, without providing the required data (global = '%s', name = '%s', timeout = '%s', help = '%s'). Ignoring it.",
297
+ netdata_log_error("PLUGINSD: 'host:%s/chart:%s' got a FUNCTION, without providing the required data (global = '%s', name = '%s', timeout = '%s', priority = '%s', version = '%s', help = '%s'). Ignoring it.",
298
rrdhost_hostname(host),
299
st?rrdset_id(st):"(unset)",
300
global?"yes":"no",
301
name?name:"(unset)",
301
- timeout_str ? timeout_str : "(unset)",
302
+ timeout_str ? timeout_str : "(unset)",
303
+ priority_str ? priority_str : "(unset)",
304
+ version_str ? version_str : "(unset)",
305
help?help:"(unset)"
306
);
307
return PARSER_RC_ERROR;
@@ -318,7 +321,11 @@ PARSER_RC pluginsd_function(char **words, size_t num_words, PARSER *parser) {
321
priority = RRDFUNCTIONS_PRIORITY_DEFAULT;
322
}
323
321
- rrd_function_add(host, st, name, timeout_s, priority, help, tags,
324
+ uint32_t version = RRDFUNCTIONS_VERSION_DEFAULT;
325
+ if(version_str && *version_str)
326
+ version = str2u(version_str);
327
+
328
+ rrd_function_add(host, st, name, timeout_s, priority, version, help, tags,
329
http_access_from_hex_mapping_old_roles(access_str), false,
330
pluginsd_function_execute_cb, parser);
331
src/web/api/functions/functions.c
+3
@@ -11,6 +11,7 @@ void global_functions_add(void) {
11
"streaming",
12
10,
13
RRDFUNCTIONS_PRIORITY_DEFAULT + 1,
14
+ RRDFUNCTIONS_VERSION_DEFAULT,
15
RRDFUNCTIONS_STREAMING_HELP,
16
"top",
17
HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA,
@@ -22,6 +23,7 @@ void global_functions_add(void) {
23
"netdata-api-calls",
24
10,
25
RRDFUNCTIONS_PRIORITY_DEFAULT + 2,
26
+ RRDFUNCTIONS_VERSION_DEFAULT,
27
RRDFUNCTIONS_PROGRESS_HELP,
28
"top",
29
HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA,
@@ -33,6 +35,7 @@ void global_functions_add(void) {
35
RRDFUNCTIONS_BEARER_GET_TOKEN,
36
10,
37
RRDFUNCTIONS_PRIORITY_DEFAULT + 3,
38
+ RRDFUNCTIONS_VERSION_DEFAULT,
39
RRDFUNCTIONS_BEARER_GET_TOKEN_HELP,
40
RRDFUNCTIONS_TAG_HIDDEN,
41
HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA,