@cryptotaxi247 / netdata-1 / commits / 7f9bcd418

stream paths propagated to children and parents (#18430)

* stream paths propagates to children and parents * do not send the stream paths back to the parent * cleanup paths when the child disconnects * fix version placement * removed last_time_t * remove deadlock * prevent recurring sender cleanups when the sender cannot connect to parent * split sender.c to multiple files * fix json generation * allow null for uuids * update stream_paths when the retention of the node changes * clear missing claim_id * validations * fix uuid json parsing * fixed left-over uuid * keep the array sorted * fix sending nodes calculation * send stream path updates when node_id is set or changed * stream path capabilities are now the full capabilities as a sender, independently of the current streaming connections

Costa Tsaousis committed Aug 29, 2024 at 17:03 UTC 7f9bcd4189b65a059d21c1e48509386cc49682be
50 files changed +2280 -1659
CMakeLists.txt
+8
@@ -1264,6 +1264,14 @@ set(STREAMING_PLUGIN_FILES
1264 src/streaming/protocol/commands.c
1265 src/streaming/protocol/commands.h
1266 src/streaming/protocol/command-claimed_id.c
1267 + src/streaming/stream_path.c
1268 + src/streaming/stream_path.h
1269 + src/streaming/stream_capabilities.c
1270 + src/streaming/stream_capabilities.h
1271 + src/streaming/sender_connect.c
1272 + src/streaming/sender_internals.h
1273 + src/streaming/sender_execute.c
1274 + src/streaming/sender_commit.c
1275 )
1276
1277 set(WEB_PLUGIN_FILES
src/aclk/aclk.c
+13 -13
@@ -839,16 +839,16 @@ exit:
839
840 void aclk_host_state_update(RRDHOST *host, int cmd, int queryable)
841 {
842 - nd_uuid_t node_id;
842 + ND_UUID node_id;
843
844 if (!aclk_online())
845 return;
846
847 - if (!uuid_is_null(host->node_id)) {
848 - uuid_copy(node_id, host->node_id);
847 + if (!UUIDiszero(host->node_id)) {
848 + node_id = host->node_id;
849 }
850 else {
851 - int ret = get_node_id(&host->host_uuid, &node_id);
851 + int ret = get_node_id(&host->host_id.uuid, &node_id.uuid);
852 if (ret > 0) {
853 // this means we were not able to check if node_id already present
854 netdata_log_error("Unable to check for node_id. Ignoring the host state update.");
@@ -887,7 +887,7 @@ void aclk_host_state_update(RRDHOST *host, int cmd, int queryable)
887 .session_id = aclk_session_newarch
888 };
889 node_state_update.node_id = mallocz(UUID_STR_LEN);
890 - uuid_unparse_lower(node_id, (char*)node_state_update.node_id);
890 + uuid_unparse_lower(node_id.uuid, (char*)node_state_update.node_id);
891
892 node_state_update.capabilities = aclk_get_agent_capas();
893
@@ -1059,12 +1059,12 @@ char *aclk_state(void)
1059 else
1060 buffer_strcat(wb, "null");
1061
1062 - if (uuid_is_null(host->node_id))
1062 + if (UUIDiszero(host->node_id))
1063 buffer_strcat(wb, "\n\tNode ID: null\n");
1064 else {
1065 - char node_id[GUID_LEN + 1];
1066 - uuid_unparse_lower(host->node_id, node_id);
1067 - buffer_sprintf(wb, "\n\tNode ID: %s\n", node_id);
1065 + char node_id_str[UUID_STR_LEN];
1066 + uuid_unparse_lower(host->node_id.uuid, node_id_str);
1067 + buffer_sprintf(wb, "\n\tNode ID: %s\n", node_id_str);
1068 }
1069
1070 buffer_sprintf(wb, "\tStreaming Hops: %d\n\tRelationship: %s", host->system_info->hops, host == localhost ? "self" : "child");
@@ -1192,12 +1192,12 @@ char *aclk_state_json(void)
1192 } else
1193 json_object_object_add(nodeinstance, "claimed_id", NULL);
1194
1195 - if (uuid_is_null(host->node_id)) {
1195 + if (UUIDiszero(host->node_id)) {
1196 json_object_object_add(nodeinstance, "node-id", NULL);
1197 } else {
1198 - char node_id[GUID_LEN + 1];
1199 - uuid_unparse_lower(host->node_id, node_id);
1200 - tmp = json_object_new_string(node_id);
1198 + char node_id_str[UUID_STR_LEN];
1199 + uuid_unparse_lower(host->node_id.uuid, node_id_str);
1200 + tmp = json_object_new_string(node_id_str);
1201 json_object_object_add(nodeinstance, "node-id", tmp);
1202 }
1203
src/claim/claim.c
+3 -3
@@ -166,8 +166,8 @@ bool load_claiming_state(void) {
166 have_claimed_id = true;
167 }
168
169 - invalidate_node_instances(&localhost->host_uuid, have_claimed_id ? &uuid.uuid : NULL);
170 - metaqueue_store_claim_id(&localhost->host_uuid, have_claimed_id ? &uuid.uuid : NULL);
169 + invalidate_node_instances(&localhost->host_id.uuid, have_claimed_id ? &uuid.uuid : NULL);
170 + metaqueue_store_claim_id(&localhost->host_id.uuid, have_claimed_id ? &uuid.uuid : NULL);
171
172 errno_clear();
173
@@ -197,7 +197,7 @@ CLOUD_STATUS claim_reload_and_wait_online(void) {
197 int ms = 0;
198 do {
199 status = cloud_status();
200 - if ((status == CLOUD_STATUS_ONLINE || status == CLOUD_STATUS_INDIRECT) && !uuid_is_null(localhost->host_uuid))
200 + if ((status == CLOUD_STATUS_ONLINE || status == CLOUD_STATUS_INDIRECT) && !UUIDiszero(localhost->host_id))
201 break;
202
203 sleep_usec(50 * USEC_PER_MS);
src/claim/cloud-status.c
+1 -1
@@ -32,7 +32,7 @@ CLOUD_STATUS cloud_status(void) {
32 if(localhost->sender &&
33 rrdhost_flag_check(localhost, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS) &&
34 stream_has_capability(localhost->sender, STREAM_CAP_NODE_ID) &&
35 - !uuid_is_null(localhost->node_id) &&
35 + !UUIDiszero(localhost->node_id) &&
36 !UUIDiszero(localhost->aclk.claim_id_of_parent))
37 return CLOUD_STATUS_INDIRECT;
38
src/collectors/plugins.d/gperf-config.txt
+12 -6
@@ -35,6 +35,8 @@
35 #define PLUGINSD_KEYWORD_ID_RSET 21
36 #define PLUGINSD_KEYWORD_ID_RSSTATE 24
37
38 +#define PLUGINSD_KEYWORD_ID_JSON 80
39 +
40 #define PLUGINSD_KEYWORD_ID_DYNCFG_ENABLE 901
41 #define PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_MODULE 902
42 #define PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_JOB 903
@@ -102,11 +104,15 @@ REND, PLUGINSD_KEYWORD_ID_REND, PARSER_INIT_STRE
104 RSET, PLUGINSD_KEYWORD_ID_RSET, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 30
105 RSSTATE, PLUGINSD_KEYWORD_ID_RSSTATE, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 31
106 #
107 +# JSON
108 +#
109 +JSON, PLUGINSD_KEYWORD_ID_JSON, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 32
110 +#
111 # obsolete - do nothing commands
112 #
107 -DYNCFG_ENABLE, PLUGINSD_KEYWORD_ID_DYNCFG_ENABLE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 32
108 -DYNCFG_REGISTER_MODULE, PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_MODULE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 33
109 -DYNCFG_REGISTER_JOB, PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 34
110 -DYNCFG_RESET, PLUGINSD_KEYWORD_ID_DYNCFG_RESET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 35
111 -REPORT_JOB_STATUS, PLUGINSD_KEYWORD_ID_REPORT_JOB_STATUS, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 36
112 -DELETE_JOB, PLUGINSD_KEYWORD_ID_DELETE_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 37
113 +DYNCFG_ENABLE, PLUGINSD_KEYWORD_ID_DYNCFG_ENABLE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 33
114 +DYNCFG_REGISTER_MODULE, PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_MODULE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 34
115 +DYNCFG_REGISTER_JOB, PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 35
116 +DYNCFG_RESET, PLUGINSD_KEYWORD_ID_DYNCFG_RESET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 36
117 +REPORT_JOB_STATUS, PLUGINSD_KEYWORD_ID_REPORT_JOB_STATUS, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 37
118 +DELETE_JOB, PLUGINSD_KEYWORD_ID_DELETE_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 38
src/collectors/plugins.d/gperf-hashtable.h
+102 -98
@@ -67,6 +67,8 @@
67 #define PLUGINSD_KEYWORD_ID_RSET 21
68 #define PLUGINSD_KEYWORD_ID_RSSTATE 24
69
70 +#define PLUGINSD_KEYWORD_ID_JSON 80
71 +
72 #define PLUGINSD_KEYWORD_ID_DYNCFG_ENABLE 901
73 #define PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_MODULE 902
74 #define PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_JOB 903
@@ -75,12 +77,12 @@
77 #define PLUGINSD_KEYWORD_ID_DELETE_JOB 906
78
79
78 -#define GPERF_PARSER_TOTAL_KEYWORDS 37
80 +#define GPERF_PARSER_TOTAL_KEYWORDS 38
81 #define GPERF_PARSER_MIN_WORD_LENGTH 3
82 #define GPERF_PARSER_MAX_WORD_LENGTH 22
81 -#define GPERF_PARSER_MIN_HASH_VALUE 7
82 -#define GPERF_PARSER_MAX_HASH_VALUE 52
83 -/* maximum key range = 46, duplicates = 0 */
83 +#define GPERF_PARSER_MIN_HASH_VALUE 4
84 +#define GPERF_PARSER_MAX_HASH_VALUE 53
85 +/* maximum key range = 50, duplicates = 0 */
86
87 #ifdef __GNUC__
88 __inline
@@ -94,32 +96,32 @@ gperf_keyword_hash_function (register const char *str, register size_t len)
96 {
97 static const unsigned char asso_values[] =
98 {
97 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
98 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
99 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
100 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
101 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
102 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
103 - 53, 53, 53, 53, 53, 6, 24, 3, 9, 6,
104 - 0, 53, 3, 27, 53, 53, 33, 53, 42, 0,
105 - 53, 53, 0, 30, 53, 12, 3, 53, 9, 0,
106 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
107 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
108 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
109 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
110 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
111 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
112 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
113 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
114 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
115 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
116 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
117 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
118 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
119 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
120 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
121 - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
122 - 53, 53, 53, 53, 53, 53
99 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
100 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
101 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
102 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
103 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
104 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
105 + 54, 54, 54, 54, 54, 31, 28, 2, 4, 0,
106 + 5, 54, 0, 25, 20, 54, 17, 54, 27, 0,
107 + 54, 54, 1, 16, 54, 15, 0, 54, 2, 0,
108 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
109 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
110 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
111 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
112 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
113 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
114 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
115 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
116 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
117 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
118 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
119 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
120 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
121 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
122 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
123 + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
124 + 54, 54, 54, 54, 54, 54
125 };
126 return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]];
127 }
@@ -130,92 +132,94 @@ static const PARSER_KEYWORD gperf_keywords[] =
132 {(char*)0,0,PARSER_INIT_PLUGINSD,0},
133 {(char*)0,0,PARSER_INIT_PLUGINSD,0},
134 {(char*)0,0,PARSER_INIT_PLUGINSD,0},
133 - {(char*)0,0,PARSER_INIT_PLUGINSD,0},
134 - {(char*)0,0,PARSER_INIT_PLUGINSD,0},
135 - {(char*)0,0,PARSER_INIT_PLUGINSD,0},
136 -#line 67 "gperf-config.txt"
135 +#line 69 "gperf-config.txt"
136 {"HOST", PLUGINSD_KEYWORD_ID_HOST, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 4},
138 - {(char*)0,0,PARSER_INIT_PLUGINSD,0},
139 -#line 87 "gperf-config.txt"
140 - {"CONFIG", PLUGINSD_KEYWORD_ID_CONFIG, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 21},
141 -#line 101 "gperf-config.txt"
137 +#line 103 "gperf-config.txt"
138 {"REND", PLUGINSD_KEYWORD_ID_REND, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 29},
143 -#line 75 "gperf-config.txt"
139 +#line 68 "gperf-config.txt"
140 + {"EXIT", PLUGINSD_KEYWORD_ID_EXIT, PARSER_INIT_PLUGINSD, WORKER_PARSER_FIRST_JOB + 3},
141 +#line 77 "gperf-config.txt"
142 {"CHART", PLUGINSD_KEYWORD_ID_CHART, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 9},
145 -#line 84 "gperf-config.txt"
143 +#line 89 "gperf-config.txt"
144 + {"CONFIG", PLUGINSD_KEYWORD_ID_CONFIG, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 21},
145 +#line 86 "gperf-config.txt"
146 {"OVERWRITE", PLUGINSD_KEYWORD_ID_OVERWRITE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 18},
147 -#line 70 "gperf-config.txt"
147 +#line 72 "gperf-config.txt"
148 {"HOST_LABEL", PLUGINSD_KEYWORD_ID_HOST_LABEL, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 7},
149 -#line 68 "gperf-config.txt"
149 +#line 70 "gperf-config.txt"
150 {"HOST_DEFINE", PLUGINSD_KEYWORD_ID_HOST_DEFINE, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 5},
151 - {(char*)0,0,PARSER_INIT_PLUGINSD,0},
152 -#line 100 "gperf-config.txt"
151 +#line 102 "gperf-config.txt"
152 {"RDSTATE", PLUGINSD_KEYWORD_ID_RDSTATE, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 28},
154 -#line 86 "gperf-config.txt"
155 - {"VARIABLE", PLUGINSD_KEYWORD_ID_VARIABLE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 20},
156 -#line 69 "gperf-config.txt"
157 - {"HOST_DEFINE_END", PLUGINSD_KEYWORD_ID_HOST_DEFINE_END, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 6},
158 -#line 66 "gperf-config.txt"
159 - {"EXIT", PLUGINSD_KEYWORD_ID_EXIT, PARSER_INIT_PLUGINSD, WORKER_PARSER_FIRST_JOB + 3},
160 -#line 80 "gperf-config.txt"
161 - {"FUNCTION", PLUGINSD_KEYWORD_ID_FUNCTION, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 14},
162 -#line 110 "gperf-config.txt"
163 - {"DYNCFG_RESET", PLUGINSD_KEYWORD_ID_DYNCFG_RESET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 35},
164 -#line 107 "gperf-config.txt"
165 - {"DYNCFG_ENABLE", PLUGINSD_KEYWORD_ID_DYNCFG_ENABLE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 32},
166 -#line 111 "gperf-config.txt"
167 - {"REPORT_JOB_STATUS", PLUGINSD_KEYWORD_ID_REPORT_JOB_STATUS, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 36},
168 - {(char*)0,0,PARSER_INIT_PLUGINSD,0},
169 -#line 112 "gperf-config.txt"
170 - {"DELETE_JOB", PLUGINSD_KEYWORD_ID_DELETE_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 37},
171 -#line 98 "gperf-config.txt"
172 - {"CHART_DEFINITION_END", PLUGINSD_KEYWORD_ID_CHART_DEFINITION_END, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 26},
153 {(char*)0,0,PARSER_INIT_PLUGINSD,0},
174 -#line 109 "gperf-config.txt"
175 - {"DYNCFG_REGISTER_JOB", PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 34},
176 -#line 82 "gperf-config.txt"
177 - {"FUNCTION_PROGRESS", PLUGINSD_KEYWORD_ID_FUNCTION_PROGRESS, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 16},
178 -#line 99 "gperf-config.txt"
179 - {"RBEGIN", PLUGINSD_KEYWORD_ID_RBEGIN, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 27},
180 -#line 108 "gperf-config.txt"
181 - {"DYNCFG_REGISTER_MODULE", PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_MODULE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 33},
182 - {(char*)0,0,PARSER_INIT_PLUGINSD,0},
183 -#line 81 "gperf-config.txt"
184 - {"FUNCTION_RESULT_BEGIN", PLUGINSD_KEYWORD_ID_FUNCTION_RESULT_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 15},
185 -#line 102 "gperf-config.txt"
154 +#line 118 "gperf-config.txt"
155 + {"DELETE_JOB", PLUGINSD_KEYWORD_ID_DELETE_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 38},
156 +#line 71 "gperf-config.txt"
157 + {"HOST_DEFINE_END", PLUGINSD_KEYWORD_ID_HOST_DEFINE_END, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 6},
158 +#line 116 "gperf-config.txt"
159 + {"DYNCFG_RESET", PLUGINSD_KEYWORD_ID_DYNCFG_RESET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 36},
160 +#line 113 "gperf-config.txt"
161 + {"DYNCFG_ENABLE", PLUGINSD_KEYWORD_ID_DYNCFG_ENABLE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 33},
162 +#line 117 "gperf-config.txt"
163 + {"REPORT_JOB_STATUS", PLUGINSD_KEYWORD_ID_REPORT_JOB_STATUS, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 37},
164 +#line 87 "gperf-config.txt"
165 + {"SET", PLUGINSD_KEYWORD_ID_SET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 19},
166 +#line 95 "gperf-config.txt"
167 + {"SET2", PLUGINSD_KEYWORD_ID_SET2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 24},
168 +#line 104 "gperf-config.txt"
169 {"RSET", PLUGINSD_KEYWORD_ID_RSET, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 30},
187 -#line 74 "gperf-config.txt"
188 - {"BEGIN", PLUGINSD_KEYWORD_ID_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 8},
189 -#line 92 "gperf-config.txt"
190 - {"BEGIN2", PLUGINSD_KEYWORD_ID_BEGIN2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 23},
191 -#line 103 "gperf-config.txt"
170 +#line 100 "gperf-config.txt"
171 + {"CHART_DEFINITION_END", PLUGINSD_KEYWORD_ID_CHART_DEFINITION_END, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 26},
172 +#line 115 "gperf-config.txt"
173 + {"DYNCFG_REGISTER_JOB", PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 35},
174 +#line 105 "gperf-config.txt"
175 {"RSSTATE", PLUGINSD_KEYWORD_ID_RSSTATE, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 31},
193 -#line 64 "gperf-config.txt"
176 +#line 78 "gperf-config.txt"
177 + {"CLABEL", PLUGINSD_KEYWORD_ID_CLABEL, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 10},
178 +#line 114 "gperf-config.txt"
179 + {"DYNCFG_REGISTER_MODULE", PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_MODULE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 34},
180 +#line 66 "gperf-config.txt"
181 {"FLUSH", PLUGINSD_KEYWORD_ID_FLUSH, PARSER_INIT_PLUGINSD, WORKER_PARSER_FIRST_JOB + 1},
195 -#line 85 "gperf-config.txt"
196 - {"SET", PLUGINSD_KEYWORD_ID_SET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 19},
182 +#line 82 "gperf-config.txt"
183 + {"FUNCTION", PLUGINSD_KEYWORD_ID_FUNCTION, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 14},
184 #line 93 "gperf-config.txt"
198 - {"SET2", PLUGINSD_KEYWORD_ID_SET2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 24},
199 - {(char*)0,0,PARSER_INIT_PLUGINSD,0},
185 + {"CLAIMED_ID", PLUGINSD_KEYWORD_ID_CLAIMED_ID, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 22},
186 +#line 81 "gperf-config.txt"
187 + {"END", PLUGINSD_KEYWORD_ID_END, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 13},
188 +#line 96 "gperf-config.txt"
189 + {"END2", PLUGINSD_KEYWORD_ID_END2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 25},
190 +#line 79 "gperf-config.txt"
191 + {"CLABEL_COMMIT", PLUGINSD_KEYWORD_ID_CLABEL_COMMIT, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 11},
192 #line 76 "gperf-config.txt"
201 - {"CLABEL", PLUGINSD_KEYWORD_ID_CLABEL, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 10},
202 -#line 65 "gperf-config.txt"
193 + {"BEGIN", PLUGINSD_KEYWORD_ID_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 8},
194 +#line 94 "gperf-config.txt"
195 + {"BEGIN2", PLUGINSD_KEYWORD_ID_BEGIN2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 23},
196 +#line 101 "gperf-config.txt"
197 + {"RBEGIN", PLUGINSD_KEYWORD_ID_RBEGIN, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 27},
198 +#line 67 "gperf-config.txt"
199 {"DISABLE", PLUGINSD_KEYWORD_ID_DISABLE, PARSER_INIT_PLUGINSD, WORKER_PARSER_FIRST_JOB + 2},
204 -#line 83 "gperf-config.txt"
205 - {"LABEL", PLUGINSD_KEYWORD_ID_LABEL, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 17},
206 -#line 78 "gperf-config.txt"
200 +#line 84 "gperf-config.txt"
201 + {"FUNCTION_PROGRESS", PLUGINSD_KEYWORD_ID_FUNCTION_PROGRESS, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 16},
202 +#line 80 "gperf-config.txt"
203 {"DIMENSION", PLUGINSD_KEYWORD_ID_DIMENSION, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 12},
208 -#line 91 "gperf-config.txt"
209 - {"CLAIMED_ID", PLUGINSD_KEYWORD_ID_CLAIMED_ID, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 22},
204 +#line 88 "gperf-config.txt"
205 + {"VARIABLE", PLUGINSD_KEYWORD_ID_VARIABLE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 20},
206 +#line 109 "gperf-config.txt"
207 + {"JSON", PLUGINSD_KEYWORD_ID_JSON, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 32},
208 +#line 83 "gperf-config.txt"
209 + {"FUNCTION_RESULT_BEGIN", PLUGINSD_KEYWORD_ID_FUNCTION_RESULT_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 15},
210 {(char*)0,0,PARSER_INIT_PLUGINSD,0},
211 {(char*)0,0,PARSER_INIT_PLUGINSD,0},
212 -#line 77 "gperf-config.txt"
213 - {"CLABEL_COMMIT", PLUGINSD_KEYWORD_ID_CLABEL_COMMIT, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 11},
212 {(char*)0,0,PARSER_INIT_PLUGINSD,0},
215 -#line 79 "gperf-config.txt"
216 - {"END", PLUGINSD_KEYWORD_ID_END, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 13},
217 -#line 94 "gperf-config.txt"
218 - {"END2", PLUGINSD_KEYWORD_ID_END2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 25}
213 + {(char*)0,0,PARSER_INIT_PLUGINSD,0},
214 + {(char*)0,0,PARSER_INIT_PLUGINSD,0},
215 + {(char*)0,0,PARSER_INIT_PLUGINSD,0},
216 + {(char*)0,0,PARSER_INIT_PLUGINSD,0},
217 + {(char*)0,0,PARSER_INIT_PLUGINSD,0},
218 + {(char*)0,0,PARSER_INIT_PLUGINSD,0},
219 + {(char*)0,0,PARSER_INIT_PLUGINSD,0},
220 + {(char*)0,0,PARSER_INIT_PLUGINSD,0},
221 +#line 85 "gperf-config.txt"
222 + {"LABEL", PLUGINSD_KEYWORD_ID_LABEL, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 17}
223 };
224
225 const PARSER_KEYWORD *
src/collectors/plugins.d/pluginsd_parser.c
+33
@@ -1081,6 +1081,35 @@ static inline PARSER_RC pluginsd_exit(char **words __maybe_unused, size_t num_wo
1081 return PARSER_RC_STOP;
1082 }
1083
1084 +static void pluginsd_json_stream_paths(PARSER *parser, void *action_data __maybe_unused) {
1085 + stream_path_set_from_json(parser->user.host, buffer_tostring(parser->defer.response), false);
1086 + buffer_free(parser->defer.response);
1087 +}
1088 +
1089 +static void pluginsd_json_dev_null(PARSER *parser, void *action_data __maybe_unused) {
1090 + buffer_free(parser->defer.response);
1091 +}
1092 +
1093 +static PARSER_RC pluginsd_json(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser) {
1094 + RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_JSON);
1095 + if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1096 +
1097 + char *keyword = get_word(words, num_words, 1);
1098 +
1099 + parser->defer.response = buffer_create(0, NULL);
1100 + parser->defer.end_keyword = PLUGINSD_KEYWORD_JSON_END;
1101 + parser->defer.action = pluginsd_json_dev_null;
1102 + parser->defer.action_data = NULL;
1103 + parser->flags |= PARSER_DEFER_UNTIL_KEYWORD;
1104 +
1105 + if(strcmp(keyword, PLUGINSD_KEYWORD_STREAM_PATH) == 0)
1106 + parser->defer.action = pluginsd_json_stream_paths;
1107 + else
1108 + netdata_log_error("PLUGINSD: invalid JSON payload keyword '%s'", keyword);
1109 +
1110 + return PARSER_RC_OK;
1111 +}
1112 +
1113 PARSER_RC rrdpush_receiver_pluginsd_claimed_id(char **words, size_t num_words, PARSER *parser);
1114
1115 // ----------------------------------------------------------------------------
@@ -1215,6 +1244,8 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, int fd_input,
1244 #include "gperf-hashtable.h"
1245
1246 PARSER_RC parser_execute(PARSER *parser, const PARSER_KEYWORD *keyword, char **words, size_t num_words) {
1247 + // put all the keywords ordered by the frequency they are used
1248 +
1249 switch(keyword->id) {
1250 case PLUGINSD_KEYWORD_ID_SET2:
1251 return pluginsd_set_v2(words, num_words, parser);
@@ -1254,6 +1285,8 @@ PARSER_RC parser_execute(PARSER *parser, const PARSER_KEYWORD *keyword, char **w
1285 return pluginsd_function_result_begin(words, num_words, parser);
1286 case PLUGINSD_KEYWORD_ID_FUNCTION_PROGRESS:
1287 return pluginsd_function_progress(words, num_words, parser);
1288 + case PLUGINSD_KEYWORD_ID_JSON:
1289 + return pluginsd_json(words, num_words, parser);
1290 case PLUGINSD_KEYWORD_ID_LABEL:
1291 return pluginsd_label(words, num_words, parser);
1292 case PLUGINSD_KEYWORD_ID_OVERWRITE:
src/collectors/plugins.d/pluginsd_parser.h
+3 -1
@@ -93,6 +93,8 @@ typedef struct parser_user_object {
93 } v2;
94 } PARSER_USER_OBJECT;
95
96 +typedef void (*parser_deferred_action_t)(struct parser *parser, void *action_data);
97 +
98 struct parser {
99 uint8_t version; // Parser version
100 PARSER_REPERTOIRE repertoire;
@@ -115,7 +117,7 @@ struct parser {
117 struct {
118 const char *end_keyword;
119 BUFFER *response;
118 - void (*action)(struct parser *parser, void *action_data);
120 + parser_deferred_action_t action;
121 void *action_data;
122 } defer;
123
src/daemon/commands.c
+3 -3
@@ -372,10 +372,10 @@ static int remove_ephemeral_host(BUFFER *wb, RRDHOST *host, bool report_error)
372
373 if (!rrdhost_option_check(host, RRDHOST_OPTION_EPHEMERAL_HOST)) {
374 rrdhost_option_set(host, RRDHOST_OPTION_EPHEMERAL_HOST);
375 - sql_set_host_label(&host->host_uuid, "_is_ephemeral", "true");
375 + sql_set_host_label(&host->host_id.uuid, "_is_ephemeral", "true");
376 aclk_host_state_update(host, 0, 0);
377 unregister_node(host->machine_guid);
378 - uuid_clear(host->node_id);
378 + host->node_id = UUID_ZERO;
379 buffer_sprintf(wb, "Unregistering node with machine guid %s, hostname = %s", host->machine_guid, rrdhost_hostname(host));
380 rrd_wrlock();
381 rrdhost_free___while_having_rrd_wrlock(host, true);
@@ -517,7 +517,7 @@ static void pipe_write_cb(uv_write_t* req, int status)
517
518 static inline void add_char_to_command_reply(BUFFER *reply_string, unsigned *reply_string_size, char character)
519 {
520 - buffer_fast_charcat(reply_string, character);
520 + buffer_putc(reply_string, character);
521 *reply_string_size +=1;
522 }
523
src/daemon/config/dyncfg-tree.c
+1 -3
@@ -71,12 +71,10 @@ static void dyncfg_tree_for_host(RRDHOST *host, BUFFER *wb, const char *path, co
71 if(id && *id)
72 template = string_strdupz(id);
73
74 - ND_UUID host_uuid = uuid2UUID(host->host_uuid);
75 -
74 size_t path_len = strlen(path);
75 DYNCFG *df;
76 dfe_start_read(dyncfg_globals.nodes, df) {
79 - if(!UUIDeq(df->host_uuid, host_uuid))
77 + if(!UUIDeq(df->host_uuid, host->host_id))
78 continue;
79
80 if(strncmp(string2str(df->path), path, path_len) != 0)
src/daemon/config/dyncfg.c
+1 -1
@@ -192,7 +192,7 @@ const DICTIONARY_ITEM *dyncfg_add_internal(RRDHOST *host, const char *id, const
192 rrd_function_execute_cb_t execute_cb, void *execute_cb_data,
193 bool overwrite_cb) {
194 DYNCFG tmp = {
195 - .host_uuid = uuid2UUID(host->host_uuid),
195 + .host_uuid = host->host_id,
196 .path = string_strdupz(path),
197 .cmds = cmds,
198 .type = type,
src/daemon/main.c
+1 -3
@@ -2006,9 +2006,7 @@ int netdata_main(int argc, char **argv) {
2006 nd_log_initialize();
2007 netdata_log_info("Netdata agent version '%s' is starting", NETDATA_VERSION);
2008
2009 - ieee754_doubles = is_system_ieee754_double();
2010 - if(!ieee754_doubles)
2011 - globally_disabled_capabilities |= STREAM_CAP_IEEE754;
2009 + check_local_streaming_capabilities();
2010
2011 aral_judy_init();
2012
src/database/contexts/api_v1_contexts.c
+2 -2
@@ -399,8 +399,8 @@ int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before,
399
400 char node_uuid[UUID_STR_LEN] = "";
401
402 - if(!uuid_is_null(host->node_id))
403 - uuid_unparse_lower(host->node_id, node_uuid);
402 + if(!UUIDiszero(host->node_id))
403 + uuid_unparse_lower(host->node_id.uuid, node_uuid);
404
405 if(after != 0 && before != 0)
406 rrdr_relative_window_to_absolute_query(&after, &before, NULL, false);
src/database/contexts/api_v2_contexts.c
+3 -2
@@ -201,8 +201,8 @@ void buffer_json_agent_status_id(BUFFER *wb, size_t ai, usec_t duration_ut) {
201 void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status) {
202 buffer_json_member_add_string(wb, "mg", host->machine_guid);
203
204 - if(!uuid_is_null(host->node_id))
205 - buffer_json_member_add_uuid(wb, "nd", host->node_id);
204 + if(!UUIDiszero(host->node_id))
205 + buffer_json_member_add_uuid(wb, "nd", host->node_id.uuid);
206 buffer_json_member_add_string(wb, "nm", rrdhost_hostname(host));
207 buffer_json_member_add_uint64(wb, "ni", ni);
208
@@ -425,6 +425,7 @@ static void rrdcontext_to_json_v2_rrdhost(BUFFER *wb, RRDHOST *host, struct rrdc
425
426 rrdhost_health_to_json_v2(wb, "health", &s);
427 agent_capabilities_to_json(wb, host, "capabilities");
428 + rrdhost_stream_path_to_json(wb, host, STREAM_PATH_JSON_MEMBER, false);
429 }
430
431 if (ctl->mode & (CONTEXTS_V2_NODE_INSTANCES)) {
src/database/contexts/api_v2_contexts_agents.c
+8 -9
@@ -41,7 +41,7 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
41 buffer_json_member_add_object(wb, "agent");
42
43 buffer_json_member_add_string(wb, "mg", localhost->machine_guid);
44 - buffer_json_member_add_uuid(wb, "nd", localhost->node_id);
44 + buffer_json_member_add_uuid(wb, "nd", localhost->node_id.uuid);
45 buffer_json_member_add_string(wb, "nm", rrdhost_hostname(localhost));
46 buffer_json_member_add_time_t(wb, "now", now_s);
47
@@ -62,16 +62,15 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
62 dfe_start_read(rrdhost_root_index, host) {
63 total++;
64
65 - if(host == localhost)
66 - continue;
67 -
68 - if(rrdhost_state_cloud_emulation(host))
69 - receiving++;
70 - else
71 - archived++;
72 -
65 if(rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED))
66 sending++;
67 +
68 + if(host != localhost) {
69 + if (rrdhost_state_cloud_emulation(host))
70 + receiving++;
71 + else
72 + archived++;
73 + }
74 }
75 dfe_done(host);
76
src/database/contexts/api_v2_contexts_alert_transitions.c
+2 -2
@@ -373,8 +373,8 @@ void contexts_v2_alert_transitions_to_json(BUFFER *wb, struct rrdcontext_to_json
373 if(host) {
374 buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(host));
375
376 - if(!uuid_is_null(host->node_id))
377 - buffer_json_member_add_uuid(wb, "node_id", host->node_id);
376 + if(!UUIDiszero(host->node_id))
377 + buffer_json_member_add_uuid(wb, "node_id", host->node_id.uuid);
378 }
379
380 buffer_json_member_add_string(wb, "alert", *t->alert_name ? t->alert_name : NULL);
src/database/contexts/query_scope.c
+2 -2
@@ -18,8 +18,8 @@ ssize_t query_scope_foreach_host(SIMPLE_PATTERN *scope_hosts_sp, SIMPLE_PATTERN
18 uint64_t t_hash = 0;
19
20 dfe_start_read(rrdhost_root_index, host) {
21 - if(!uuid_is_null(host->node_id))
22 - uuid_unparse_lower(host->node_id, host_node_id_str);
21 + if(!UUIDiszero(host->node_id))
22 + uuid_unparse_lower(host->node_id.uuid, host_node_id_str);
23 else
24 host_node_id_str[0] = '\0';
25
src/database/contexts/query_target.c
+4 -4
@@ -897,9 +897,9 @@ static ssize_t query_node_add(void *data, RRDHOST *host, bool queryable_host) {
897 QUERY_TARGET *qt = qtl->qt;
898 QUERY_NODE *qn = query_node_allocate(qt, host);
899
900 - if(!uuid_is_null(host->node_id)) {
900 + if(!UUIDiszero(host->node_id)) {
901 if(!qtl->host_node_id_str[0])
902 - uuid_unparse_lower(host->node_id, qn->node_id);
902 + uuid_unparse_lower(host->node_id.uuid, qn->node_id);
903 else
904 memcpy(qn->node_id, qtl->host_node_id_str, sizeof(qn->node_id));
905 }
@@ -1120,8 +1120,8 @@ QUERY_TARGET *query_target_create(QUERY_TARGET_REQUEST *qtr) {
1120 }
1121
1122 if(host) {
1123 - if(!uuid_is_null(host->node_id))
1124 - uuid_unparse_lower(host->node_id, qtl.host_node_id_str);
1123 + if(!UUIDiszero(host->node_id))
1124 + uuid_unparse_lower(host->node_id.uuid, qtl.host_node_id_str);
1125 else
1126 qtl.host_node_id_str[0] = '\0';
1127
src/database/contexts/rrdcontext.c
+4 -4
@@ -241,9 +241,9 @@ void rrdcontext_hub_checkpoint_command(void *ptr) {
241 cmd->version_hash, rrdhost_hostname(host), our_version_hash);
242
243 // prepare the snapshot
244 - char uuid[UUID_STR_LEN];
245 - uuid_unparse_lower(host->node_id, uuid);
246 - contexts_snapshot_t bundle = contexts_snapshot_new(cmd->claim_id, uuid, our_version_hash);
244 + char uuid_str[UUID_STR_LEN];
245 + uuid_unparse_lower(host->node_id.uuid, uuid_str);
246 + contexts_snapshot_t bundle = contexts_snapshot_new(cmd->claim_id, uuid_str, our_version_hash);
247
248 // do a deep scan on every metric of the host to make sure all our data are updated
249 rrdcontext_recalculate_host_retention(host, RRD_FLAG_NONE, false);
@@ -264,7 +264,7 @@ void rrdcontext_hub_checkpoint_command(void *ptr) {
264
265 rrdhost_flag_set(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS);
266 char node_str[UUID_STR_LEN];
267 - uuid_unparse_lower(host->node_id, node_str);
267 + uuid_unparse_lower(host->node_id.uuid, node_str);
268 nd_log(NDLS_ACCESS, NDLP_DEBUG,
269 "ACLK REQ [%s (%s)]: STREAM CONTEXTS ENABLED",
270 node_str, rrdhost_hostname(host));
src/database/contexts/worker.c
+15 -8
@@ -99,8 +99,8 @@ void rrdhost_load_rrdcontext_data(RRDHOST *host) {
99 if(host->rrdctx.contexts) return;
100
101 rrdhost_create_rrdcontexts(host);
102 - ctx_get_context_list(&host->host_uuid, rrdcontext_load_context_callback, host);
103 - ctx_get_chart_list(&host->host_uuid, rrdinstance_load_chart_callback, host);
102 + ctx_get_context_list(&host->host_id.uuid, rrdcontext_load_context_callback, host);
103 + ctx_get_chart_list(&host->host_id.uuid, rrdinstance_load_chart_callback, host);
104
105 RRDCONTEXT *rc;
106 dfe_start_read(host->rrdctx.contexts, rc) {
@@ -173,6 +173,8 @@ static void rrdhost_update_cached_retention(RRDHOST *host, time_t first_time_s,
173
174 spinlock_lock(&host->retention.spinlock);
175
176 + time_t old_first_time_s = host->retention.first_time_s;
177 +
178 if(global) {
179 host->retention.first_time_s = first_time_s;
180 host->retention.last_time_s = last_time_s;
@@ -185,7 +187,12 @@ static void rrdhost_update_cached_retention(RRDHOST *host, time_t first_time_s,
187 host->retention.last_time_s = last_time_s;
188 }
189
190 + bool stream_path_update_required = old_first_time_s != host->retention.first_time_s;
191 +
192 spinlock_unlock(&host->retention.spinlock);
193 +
194 + if(stream_path_update_required)
195 + stream_path_retention_updated(host);
196 }
197
198 void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason, bool worker_jobs) {
@@ -349,7 +356,7 @@ void rrdcontext_delete_from_sql_unsafe(RRDCONTEXT *rc) {
356 rc->hub.family = string2str(rc->family);
357
358 // delete it from SQL
352 - if(ctx_delete_context(&rc->rrdhost->host_uuid, &rc->hub) != 0)
359 + if(ctx_delete_context(&rc->rrdhost->host_id.uuid, &rc->hub) != 0)
360 netdata_log_error("RRDCONTEXT: failed to delete context '%s' version %"PRIu64" from SQL.",
361 rc->hub.id, rc->hub.version);
362 }
@@ -845,7 +852,7 @@ void rrdcontext_message_send_unsafe(RRDCONTEXT *rc, bool snapshot __maybe_unused
852 if(rrd_flag_is_deleted(rc))
853 rrdcontext_delete_from_sql_unsafe(rc);
854
848 - else if (ctx_store_context(&rc->rrdhost->host_uuid, &rc->hub) != 0)
855 + else if (ctx_store_context(&rc->rrdhost->host_id.uuid, &rc->hub) != 0)
856 netdata_log_error("RRDCONTEXT: failed to save context '%s' version %"PRIu64" to SQL.", rc->hub.id, rc->hub.version);
857 }
858
@@ -954,7 +961,7 @@ static void rrdcontext_dequeue_from_hub_queue(RRDCONTEXT *rc) {
961 static void rrdcontext_dispatch_queued_contexts_to_hub(RRDHOST *host, usec_t now_ut) {
962
963 // check if we have received a streaming command for this host
957 - if(uuid_is_null(host->node_id) || !rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS) || !aclk_online_for_contexts() || !host->rrdctx.hub_queue)
964 + if(UUIDiszero(host->node_id) || !rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS) || !aclk_online_for_contexts() || !host->rrdctx.hub_queue)
965 return;
966
967 // check if there are queued items to send
@@ -985,10 +992,10 @@ static void rrdcontext_dispatch_queued_contexts_to_hub(RRDHOST *host, usec_t now
992
993 if(!bundle) {
994 // prepare the bundle to send the messages
988 - char uuid[UUID_STR_LEN];
989 - uuid_unparse_lower(host->node_id, uuid);
995 + char uuid_str[UUID_STR_LEN];
996 + uuid_unparse_lower(host->node_id.uuid, uuid_str);
997
991 - bundle = contexts_updated_new(claim_id.str, uuid, 0, now_ut);
998 + bundle = contexts_updated_new(claim_id.str, uuid_str, 0, now_ut);
999 }
1000 // update the hub data of the context, give a new version, pack the message
1001 // and save an update to SQL
src/database/rrd.h
+6 -2
@@ -104,6 +104,8 @@ struct ml_metrics_statistics {
104 #include "health/rrdvar.h"
105 #include "health/rrdcalc.h"
106 #include "rrdlabels.h"
107 +#include "streaming/stream_capabilities.h"
108 +#include "streaming/stream_path.h"
109 #include "streaming/rrdpush.h"
110 #include "aclk/aclk_rrdhost_state.h"
111 #include "sqlite/sqlite_health.h"
@@ -1217,6 +1219,8 @@ struct rrdhost {
1219 RRDSET **array;
1220 } pluginsd_chart_slots;
1221 } receive;
1222 +
1223 + RRDHOST_STREAM_PATH path;
1224 } rrdpush;
1225
1226 struct rrdpush_destinations *destinations; // a linked list of possible destinations
@@ -1307,8 +1311,8 @@ struct rrdhost {
1311 time_t last_time_s;
1312 } retention;
1313
1310 - nd_uuid_t host_uuid; // Global GUID for this host
1311 - nd_uuid_t node_id; // Cloud node_id
1314 + ND_UUID host_id; // Global GUID for this host
1315 + ND_UUID node_id; // Cloud node_id
1316
1317 struct {
1318 ND_UUID claim_id_of_origin;
src/database/rrdhost.c
+9 -5
@@ -36,13 +36,13 @@ time_t rrdhost_free_ephemeral_time_s = 86400;
36
37 RRDHOST *find_host_by_node_id(char *node_id) {
38
39 - nd_uuid_t node_uuid;
40 - if (unlikely(!node_id || uuid_parse(node_id, node_uuid)))
39 + ND_UUID node_uuid;
40 + if (unlikely(!node_id || uuid_parse(node_id, node_uuid.uuid)))
41 return NULL;
42
43 RRDHOST *host, *ret = NULL;
44 dfe_start_read(rrdhost_root_index, host) {
45 - if (uuid_eq(host->node_id, node_uuid)) {
45 + if (UUIDeq(host->node_id, node_uuid)) {
46 ret = host;
47 break;
48 }
@@ -423,7 +423,7 @@ static RRDHOST *rrdhost_create(
423 if(!host->rrdvars)
424 host->rrdvars = rrdvariables_create();
425
426 - if (likely(!uuid_parse(host->machine_guid, host->host_uuid)))
426 + if (likely(!uuid_parse(host->machine_guid, host->host_id.uuid)))
427 sql_load_node_id(host);
428 else
429 error_report("Host machine GUID %s is not valid", host->machine_guid);
@@ -1117,7 +1117,7 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unitt
1117 global_functions_add();
1118
1119 if (likely(system_info)) {
1120 - detect_machine_guid_change(&localhost->host_uuid);
1120 + detect_machine_guid_change(&localhost->host_id.uuid);
1121 sql_aclk_sync_init();
1122 api_v1_management_init();
1123 }
@@ -1229,6 +1229,10 @@ void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host, bool force) {
1229 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(localhost, host, prev, next);
1230 }
1231
1232 + // ------------------------------------------------------------------------
1233 +
1234 + rrdhost_stream_path_clear(host, true);
1235 +
1236 // ------------------------------------------------------------------------
1237 // clean up streaming chart slots
1238
src/database/sqlite/sqlite_aclk.c
+5 -5
@@ -167,8 +167,8 @@ static int create_host_callback(void *data, int argc, char **argv, char **column
167
168 #ifdef NETDATA_INTERNAL_CHECKS
169 char node_str[UUID_STR_LEN] = "<none>";
170 - if (likely(!uuid_is_null(host->node_id)))
171 - uuid_unparse_lower(host->node_id, node_str);
170 + if (likely(!UUIDiszero(host->node_id)))
171 + uuid_unparse_lower(host->node_id.uuid, node_str);
172 internal_error(true, "Adding archived host \"%s\" with GUID \"%s\" node id = \"%s\" ephemeral=%d",
173 rrdhost_hostname(host), host->machine_guid, node_str, is_ephemeral);
174 #endif
@@ -417,7 +417,7 @@ static void aclk_synchronization(void *arg)
417 int live = (host == localhost || host->receiver || !(rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN))) ? 1 : 0;
418 struct aclk_sync_cfg_t *ahc = host->aclk_config;
419 if (unlikely(!ahc))
420 - create_aclk_config(host, &host->host_uuid, &host->node_id);
420 + create_aclk_config(host, &host->host_id.uuid, &host->node_id.uuid);
421 aclk_host_state_update(host, live, 1);
422 break;
423 case ACLK_DATABASE_NODE_UNREGISTER:
@@ -493,8 +493,8 @@ void create_aclk_config(RRDHOST *host __maybe_unused, nd_uuid_t *host_uuid __may
493 uuid_unparse_lower(*node_id, wc->node_id);
494
495 host->aclk_config = wc;
496 - if (node_id && uuid_is_null(host->node_id)) {
497 - uuid_copy(host->node_id, *node_id);
496 + if (node_id && UUIDiszero(host->node_id)) {
497 + uuid_copy(host->node_id.uuid, *node_id);
498 }
499
500 wc->host = host;
src/database/sqlite/sqlite_aclk_alert.c
+14 -14
@@ -115,14 +115,14 @@ static int insert_alert_to_submit_queue(RRDHOST *host, int64_t health_log_id, ui
115 return 1;
116 }
117
118 - if (is_event_from_alert_variable_config(unique_id, &host->host_uuid))
118 + if (is_event_from_alert_variable_config(unique_id, &host->host_id.uuid))
119 return 2;
120
121 if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_QUEUE_ALERT_TO_CLOUD, &res))
122 return -1;
123
124 int param = 0;
125 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
125 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
126 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, health_log_id));
127 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (int64_t) unique_id));
128
@@ -151,7 +151,7 @@ static int delete_alert_from_submit_queue(RRDHOST *host, int64_t first_seq_id, i
151 return -1;
152
153 int param = 0;
154 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
154 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
155 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, first_seq_id));
156 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, last_seq_id));
157
@@ -265,7 +265,7 @@ static void commit_alert_events(RRDHOST *host)
265 return;
266
267 int param = 0;
268 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
268 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
269
270 int64_t first_sequence_id = 0;
271 int64_t last_sequence_id = 0;
@@ -425,7 +425,7 @@ static void aclk_push_alert_event(RRDHOST *host __maybe_unused)
425 {
426 CLAIM_ID claim_id = claim_id_get();
427
428 - if (!claim_id_is_set(claim_id) || uuid_is_null(host->node_id))
428 + if (!claim_id_is_set(claim_id) || UUIDiszero(host->node_id))
429 return;
430
431 sqlite3_stmt *res = NULL;
@@ -434,10 +434,10 @@ static void aclk_push_alert_event(RRDHOST *host __maybe_unused)
434 return;
435
436 int param = 0;
437 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
437 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
438
439 char node_id_str[UUID_STR_LEN];
440 - uuid_unparse_lower(host->node_id, node_id_str);
440 + uuid_unparse_lower(host->node_id.uuid, node_id_str);
441
442 struct alarm_log_entry alarm_log;
443 alarm_log.node_id = node_id_str;
@@ -494,7 +494,7 @@ static void delete_alert_from_pending_queue(RRDHOST *host, int64_t row1, int64_t
494 return;
495
496 int param = 0;
497 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
497 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
498 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, row1));
499 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, row2));
500
@@ -525,7 +525,7 @@ void rebuild_host_alert_version_table(RRDHOST *host)
525 return;
526
527 int param = 0;
528 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
528 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
529
530 param = 0;
531 int rc = execute_insert(res);
@@ -538,7 +538,7 @@ void rebuild_host_alert_version_table(RRDHOST *host)
538 if (!PREPARE_STATEMENT(db_meta, SQL_REBUILD_HOST_ALERT_VERSION_TABLE, &res))
539 return;
540
541 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
541 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
542
543 param = 0;
544 rc = execute_insert(res);
@@ -563,7 +563,7 @@ bool process_alert_pending_queue(RRDHOST *host)
563
564 int param = 0;
565 int added =0, count = 0;
566 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
566 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
567
568 param = 0;
569 int64_t start_row = 0;
@@ -781,7 +781,7 @@ static uint64_t calculate_node_alert_version(RRDHOST *host)
781
782 uint64_t version = 0;
783 int param = 0;
784 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
784 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
785
786 param = 0;
787 while (sqlite3_step_monitored(res) == SQLITE_ROW) {
@@ -908,7 +908,7 @@ void send_alert_snapshot_to_cloud(RRDHOST *host __maybe_unused)
908 return;
909
910 // Check the database for this node to see how many alerts we will need to put in the snapshot
911 - int cnt = calculate_alert_snapshot_entries(&host->host_uuid);
911 + int cnt = calculate_alert_snapshot_entries(&host->host_id.uuid);
912 if (!cnt)
913 return;
914
@@ -917,7 +917,7 @@ void send_alert_snapshot_to_cloud(RRDHOST *host __maybe_unused)
917 return;
918
919 int param = 0;
920 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
920 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
921
922 nd_uuid_t local_snapshot_uuid;
923 char snapshot_uuid_str[UUID_STR_LEN];
src/database/sqlite/sqlite_health.c
+11 -11
@@ -161,7 +161,7 @@ static void insert_alert_queue(
161 int submit_delay = calculate_delay(old_status, new_status);
162
163 int param = 0;
164 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
164 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
165 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)health_log_id));
166 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, unique_id));
167 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, alarm_id));
@@ -253,7 +253,7 @@ static void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae)
253 return;
254
255 int param = 0;
256 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
256 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
257 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) ae->alarm_id));
258 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &ae->config_hash_id, sizeof(ae->config_hash_id), SQLITE_STATIC));
259 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->name));
@@ -310,7 +310,7 @@ void sql_health_alarm_log_cleanup(RRDHOST *host)
310 return;
311
312 int param = 0;
313 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
313 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
314 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)host->health_log.health_log_history));
315
316 param = 0;
@@ -339,7 +339,7 @@ bool sql_update_transition_in_health_log(RRDHOST *host, uint32_t alarm_id, nd_uu
339 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, transition_id, sizeof(*transition_id), SQLITE_STATIC));
340 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)alarm_id));
341 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, last_transition, sizeof(*last_transition), SQLITE_STATIC));
342 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
342 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
343
344 param = 0;
345 rc = execute_insert(res);
@@ -454,7 +454,7 @@ uint32_t sql_get_max_unique_id (RRDHOST *host)
454 return 0;
455
456 int param = 0;
457 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
457 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
458
459 param = 0;
460 while (sqlite3_step_monitored(res) == SQLITE_ROW)
@@ -480,7 +480,7 @@ void sql_check_removed_alerts_state(RRDHOST *host)
480 return;
481
482 int param = 0;
483 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
483 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
484
485 param = 0;
486 while (sqlite3_step_monitored(res) == SQLITE_ROW) {
@@ -514,7 +514,7 @@ static void sql_remove_alerts_from_deleted_charts(RRDHOST *host, nd_uuid_t *host
514 sqlite3_stmt *res = NULL;
515 int ret;
516
517 - nd_uuid_t *actual_uuid = host ? &host->host_uuid : host_uuid;
517 + nd_uuid_t *actual_uuid = host ? &host->host_id.uuid : host_uuid;
518 if (!actual_uuid)
519 return;
520
@@ -595,7 +595,7 @@ void sql_health_alarm_log_load(RRDHOST *host)
595 return;
596
597 int param = 0;
598 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
598 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
599
600 DICTIONARY *all_rrdcalcs = dictionary_create(
601 DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE);
@@ -887,7 +887,7 @@ int sql_health_get_last_executed_event(RRDHOST *host, ALARM_ENTRY *ae, RRDCALC_S
887 return ret;
888
889 int param = 0;
890 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
890 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
891 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, (int) ae->alarm_id));
892 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, (int) ae->unique_id));
893 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, (uint32_t) HEALTH_ENTRY_FLAG_EXEC_RUN));
@@ -950,7 +950,7 @@ void sql_health_alarm_log2json(RRDHOST *host, BUFFER *wb, time_t after, const ch
950 stmt_query = *active_stmt;
951
952 int param = 0;
953 - rc = sqlite3_bind_blob(stmt_query, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC);
953 + rc = sqlite3_bind_blob(stmt_query, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC);
954 if (unlikely(rc != SQLITE_OK)) {
955 error_report("Failed to bind host_id for SQL_SELECT_HEALTH_LOG.");
956 goto finish;
@@ -1233,7 +1233,7 @@ uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *
1233 return alarm_id;
1234
1235 int param = 0;
1236 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
1236 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
1237 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, chart));
1238 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, name));
1239
src/database/sqlite/sqlite_metadata.c
+36 -35
@@ -255,20 +255,21 @@ static inline void set_host_node_id(RRDHOST *host, nd_uuid_t *node_id)
255 return;
256
257 if (unlikely(!node_id)) {
258 - uuid_clear(host->node_id);
258 + host->node_id = UUID_ZERO;
259 return;
260 }
261
262 struct aclk_sync_cfg_t *wc = host->aclk_config;
263
264 - uuid_copy(host->node_id, *node_id);
264 + uuid_copy(host->node_id.uuid, *node_id);
265
266 if (unlikely(!wc))
267 - create_aclk_config(host, &host->host_uuid, node_id);
267 + create_aclk_config(host, &host->host_id.uuid, node_id);
268 else
269 uuid_unparse_lower(*node_id, wc->node_id);
270
271 rrdpush_receiver_send_node_and_claim_id_to_child(host);
272 + stream_path_node_id_updated(host);
273 }
274
275 #define SQL_SET_HOST_LABEL \
@@ -451,7 +452,7 @@ struct node_instance_list *get_node_list(void)
452 node_list[row].live =
453 (host == localhost || host->receiver || !(rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN))) ? 1 : 0;
454 node_list[row].hops = host->system_info ? host->system_info->hops :
454 - uuid_eq(*host_id, localhost->host_uuid) ? 0 : 1;
455 + uuid_eq(*host_id, localhost->host_id.uuid) ? 0 : 1;
456 node_list[row].hostname =
457 sqlite3_column_bytes(res, 2) ? strdupz((char *)sqlite3_column_text(res, 2)) : NULL;
458 }
@@ -480,7 +481,7 @@ void sql_load_node_id(RRDHOST *host)
481 return;
482
483 int param = 0;
483 - SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
484 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
485
486 param = 0;
487 int rc = sqlite3_step_monitored(res);
@@ -940,7 +941,7 @@ static int store_host_metadata(RRDHOST *host)
941 return false;
942
943 int param = 0;
943 - SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC));
944 + SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
945 SQLITE_BIND_FAIL(bind_fail, bind_text_null(res, ++param, rrdhost_hostname(host), 0));
946 SQLITE_BIND_FAIL(bind_fail, bind_text_null(res, ++param, rrdhost_registry_hostname(host), 1));
947 SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_int(res, ++param, host->rrd_update_every));
@@ -1010,30 +1011,30 @@ static bool store_host_systeminfo(RRDHOST *host)
1011
1012 int ret = 0;
1013
1013 - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_NAME", system_info->container_os_name, &host->host_uuid);
1014 - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_ID", system_info->container_os_id, &host->host_uuid);
1015 - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_ID_LIKE", system_info->container_os_id_like, &host->host_uuid);
1016 - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_VERSION", system_info->container_os_version, &host->host_uuid);
1017 - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_VERSION_ID", system_info->container_os_version_id, &host->host_uuid);
1018 - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_DETECTION", system_info->host_os_detection, &host->host_uuid);
1019 - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_NAME", system_info->host_os_name, &host->host_uuid);
1020 - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_ID", system_info->host_os_id, &host->host_uuid);
1021 - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_ID_LIKE", system_info->host_os_id_like, &host->host_uuid);
1022 - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_VERSION", system_info->host_os_version, &host->host_uuid);
1023 - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_VERSION_ID", system_info->host_os_version_id, &host->host_uuid);
1024 - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_DETECTION", system_info->host_os_detection, &host->host_uuid);
1025 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_KERNEL_NAME", system_info->kernel_name, &host->host_uuid);
1026 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT", system_info->host_cores, &host->host_uuid);
1027 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CPU_FREQ", system_info->host_cpu_freq, &host->host_uuid);
1028 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_TOTAL_RAM", system_info->host_ram_total, &host->host_uuid);
1029 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_TOTAL_DISK_SIZE", system_info->host_disk_space, &host->host_uuid);
1030 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_KERNEL_VERSION", system_info->kernel_version, &host->host_uuid);
1031 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_ARCHITECTURE", system_info->architecture, &host->host_uuid);
1032 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_VIRTUALIZATION", system_info->virtualization, &host->host_uuid);
1033 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_VIRT_DETECTION", system_info->virt_detection, &host->host_uuid);
1034 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CONTAINER", system_info->container, &host->host_uuid);
1035 - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CONTAINER_DETECTION", system_info->container_detection, &host->host_uuid);
1036 - ret += add_host_sysinfo_key_value("NETDATA_HOST_IS_K8S_NODE", system_info->is_k8s_node, &host->host_uuid);
1014 + ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_NAME", system_info->container_os_name, &host->host_id.uuid);
1015 + ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_ID", system_info->container_os_id, &host->host_id.uuid);
1016 + ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_ID_LIKE", system_info->container_os_id_like, &host->host_id.uuid);
1017 + ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_VERSION", system_info->container_os_version, &host->host_id.uuid);
1018 + ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_VERSION_ID", system_info->container_os_version_id, &host->host_id.uuid);
1019 + ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_DETECTION", system_info->host_os_detection, &host->host_id.uuid);
1020 + ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_NAME", system_info->host_os_name, &host->host_id.uuid);
1021 + ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_ID", system_info->host_os_id, &host->host_id.uuid);
1022 + ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_ID_LIKE", system_info->host_os_id_like, &host->host_id.uuid);
1023 + ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_VERSION", system_info->host_os_version, &host->host_id.uuid);
1024 + ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_VERSION_ID", system_info->host_os_version_id, &host->host_id.uuid);
1025 + ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_DETECTION", system_info->host_os_detection, &host->host_id.uuid);
1026 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_KERNEL_NAME", system_info->kernel_name, &host->host_id.uuid);
1027 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT", system_info->host_cores, &host->host_id.uuid);
1028 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CPU_FREQ", system_info->host_cpu_freq, &host->host_id.uuid);
1029 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_TOTAL_RAM", system_info->host_ram_total, &host->host_id.uuid);
1030 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_TOTAL_DISK_SIZE", system_info->host_disk_space, &host->host_id.uuid);
1031 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_KERNEL_VERSION", system_info->kernel_version, &host->host_id.uuid);
1032 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_ARCHITECTURE", system_info->architecture, &host->host_id.uuid);
1033 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_VIRTUALIZATION", system_info->virtualization, &host->host_id.uuid);
1034 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_VIRT_DETECTION", system_info->virt_detection, &host->host_id.uuid);
1035 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CONTAINER", system_info->container, &host->host_id.uuid);
1036 + ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CONTAINER_DETECTION", system_info->container_detection, &host->host_id.uuid);
1037 + ret += add_host_sysinfo_key_value("NETDATA_HOST_IS_K8S_NODE", system_info->is_k8s_node, &host->host_id.uuid);
1038
1039 return !(24 == ret);
1040 }
@@ -1052,7 +1053,7 @@ static int store_chart_metadata(RRDSET *st)
1053
1054 int param = 0;
1055 SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_blob(res, ++param, &st->chart_uuid, sizeof(st->chart_uuid), SQLITE_STATIC));
1055 - SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_blob(res, ++param, &st->rrdhost->host_uuid, sizeof(st->rrdhost->host_uuid), SQLITE_STATIC));
1056 + SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_blob(res, ++param, &st->rrdhost->host_id.uuid, sizeof(st->rrdhost->host_id.uuid), SQLITE_STATIC));
1057 SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_text(res, ++param, string2str(st->parts.type), -1, SQLITE_STATIC));
1058 SQLITE_BIND_FAIL(bind_fail, sqlite3_bind_text(res, ++param, string2str(st->parts.id), -1, SQLITE_STATIC));
1059
@@ -1915,13 +1916,13 @@ static void start_metadata_hosts(uv_work_t *req __maybe_unused)
1916 if (unlikely(rrdhost_flag_check(host, RRDHOST_FLAG_METADATA_LABELS))) {
1917 rrdhost_flag_clear(host, RRDHOST_FLAG_METADATA_LABELS);
1918
1918 - int rc = exec_statement_with_uuid(SQL_DELETE_HOST_LABELS, &host->host_uuid);
1919 + int rc = exec_statement_with_uuid(SQL_DELETE_HOST_LABELS, &host->host_id.uuid);
1920 if (likely(!rc)) {
1921 query_counter++;
1922
1923 buffer_flush(work_buffer);
1924 struct query_build tmp = {.sql = work_buffer, .count = 0};
1924 - uuid_unparse_lower(host->host_uuid, tmp.uuid_str);
1925 + uuid_unparse_lower(host->host_id.uuid, tmp.uuid_str);
1926 rrdlabels_walkthrough_read(host->rrdlabels, host_label_store_to_sql_callback, &tmp);
1927 buffer_strcat(work_buffer, " ON CONFLICT (host_id, label_key) DO UPDATE SET source_type = excluded.source_type, label_value=excluded.label_value, date_created=UNIXEPOCH()");
1928 rc = db_execute(db_meta, buffer_tostring(work_buffer));
@@ -1943,9 +1944,9 @@ static void start_metadata_hosts(uv_work_t *req __maybe_unused)
1944 int rc;
1945 ND_UUID uuid = claim_id_get_uuid();
1946 if(!UUIDiszero(uuid))
1946 - rc = store_claim_id(&host->host_uuid, &uuid.uuid);
1947 + rc = store_claim_id(&host->host_id.uuid, &uuid.uuid);
1948 else
1948 - rc = store_claim_id(&host->host_uuid, NULL);
1949 + rc = store_claim_id(&host->host_id.uuid, NULL);
1950
1951 if (unlikely(rc))
1952 rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_CLAIMID | RRDHOST_FLAG_METADATA_UPDATE);
src/libnetdata/buffer/buffer.h
+2 -12
@@ -152,13 +152,10 @@ static inline void _buffer_json_depth_pop(BUFFER *wb) {
152 wb->json.depth--;
153 }
154
155 -static inline void buffer_fast_charcat(BUFFER *wb, const char c) {
156 -
155 +static inline void buffer_putc(BUFFER *wb, char c) {
156 buffer_need_bytes(wb, 2);
158 - *(&wb->buffer[wb->len]) = c;
159 - wb->len += 1;
157 + wb->buffer[wb->len++] = c;
158 wb->buffer[wb->len] = '\0';
161 -
159 buffer_overflow_check(wb);
160 }
161
@@ -181,13 +178,6 @@ static inline void buffer_fast_rawcat(BUFFER *wb, const char *txt, size_t len) {
178 buffer_overflow_check(wb);
179 }
180
184 -static inline void buffer_putc(BUFFER *wb, char c) {
185 - buffer_need_bytes(wb, 2);
186 - wb->buffer[wb->len++] = c;
187 - wb->buffer[wb->len] = '\0';
188 - buffer_overflow_check(wb);
189 -}
190 -
181 static inline void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
182 if(unlikely(!txt || !*txt || !len)) return;
183
src/libnetdata/functions_evloop/functions_evloop.h
+4
@@ -75,6 +75,10 @@
75 #define PLUGINSD_KEYWORD_NODE_ID "NODE_ID"
76 #define PLUGINSD_KEYWORD_CLAIMED_ID "CLAIMED_ID"
77
78 +#define PLUGINSD_KEYWORD_JSON "JSON"
79 +#define PLUGINSD_KEYWORD_JSON_END "JSON_PAYLOAD_END"
80 +#define PLUGINSD_KEYWORD_STREAM_PATH "STREAM_PATH"
81 +
82 typedef void (*functions_evloop_worker_execute_t)(const char *transaction, char *function, usec_t *stop_monotonic_ut,
83 bool *cancelled, BUFFER *payload, HTTP_ACCESS access,
84 const char *source, void *data);
src/libnetdata/json/json-c-parser-inline.h
+17 -5
@@ -39,13 +39,26 @@
39
40 #define JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
41 json_object *_j; \
42 - if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) { \
43 - if(uuid_parse(json_object_get_string(_j), dst) != 0 && required) { \
44 - buffer_sprintf(error, "invalid UUID '%s.%s'", path, member); \
42 + if (json_object_object_get_ex(jobj, member, &_j)) { \
43 + if (json_object_is_type(_j, json_type_string)) { \
44 + if (uuid_parse(json_object_get_string(_j), dst) != 0) { \
45 + if(required) { \
46 + buffer_sprintf(error, "invalid UUID '%s.%s'", path, member); \
47 + return false; \
48 + } \
49 + else \
50 + uuid_clear(dst); \
51 + } \
52 + } \
53 + else if (json_object_is_type(_j, json_type_null)) { \
54 + uuid_clear(dst); \
55 + } \
56 + else if (required) { \
57 + buffer_sprintf(error, "expected UUID or null '%s.%s'", path, member); \
58 return false; \
59 } \
60 } \
48 - else if(required) { \
61 + else if (required) { \
62 buffer_sprintf(error, "missing UUID '%s.%s'", path, member); \
63 return false; \
64 } \
@@ -137,7 +150,6 @@
150 } \
151 } while(0)
152
140 -
153 #define JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error, required) do { \
154 json_object *_j; \
155 if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) \
src/libnetdata/template-enum.h
+32
@@ -37,4 +37,36 @@
37 return def_str; \
38 }
39
40 +// --------------------------------------------------------------------------------------------------------------------
41 +
42 +#define BITMAP_STR_DEFINE_FUNCTIONS_EXTERN(type) \
43 + type type ## _2id_one(const char *str); \
44 + const char *type##_2json(BUFFER *wb, const char *key, type id);
45 +
46 +#define BITMAP_STR_DEFINE_FUNCTIONS(type, def, def_str) \
47 + type type##_2id_one(const char *str) \
48 + { \
49 + if (!str || !*str) \
50 + return def; \
51 + \
52 + for (size_t i = 0; type ## _names[i].name; i++) { \
53 + if (strcmp(type ## _names[i].name, str) == 0) \
54 + return type ## _names[i].id; \
55 + } \
56 + \
57 + return def; \
58 + } \
59 + \
60 + const char *type##_2json(BUFFER *wb, const char *key, type id) \
61 + { \
62 + buffer_json_member_add_array(wb, key); \
63 + for (size_t i = 0; type ## _names[i].name; i++) { \
64 + if ((id & type ## _names[i].id) == type ## _names[i].id) \
65 + buffer_json_add_array_item_string(wb, type ## _names[i].name); \
66 + } \
67 + buffer_json_array_close(wb); \
68 + \
69 + return def_str; \
70 + }
71 +
72 #endif //NETDATA_TEMPLATE_ENUM_H
src/libnetdata/uuid/uuid.h
+1 -1
@@ -37,7 +37,7 @@ ND_UUID UUID_generate_from_hash(const void *payload, size_t payload_len);
37
38 #define UUIDeq(a, b) ((a).parts.hig64 == (b).parts.hig64 && (a).parts.low64 == (b).parts.low64)
39
40 -#define UUIDiszero(a) (UUIDeq(a, UUID_ZERO))
40 +#define UUIDiszero(a) ((a).parts.hig64 == 0 && (a).parts.low64 == 0)
41
42 static inline ND_UUID uuid2UUID(const nd_uuid_t uu1) {
43 // uu1 may not be aligned, so copy it to the output
src/registry/registry.c
+6 -6
@@ -164,15 +164,15 @@ void registry_update_cloud_base_url() {
164 int registry_request_hello_json(RRDHOST *host, struct web_client *w, bool do_not_track) {
165 registry_json_header(host, w, "hello", REGISTRY_STATUS_OK);
166
167 - if(!uuid_is_null(host->node_id))
168 - buffer_json_member_add_uuid(w->response.data, "node_id", host->node_id);
167 + if(!UUIDiszero(host->node_id))
168 + buffer_json_member_add_uuid(w->response.data, "node_id", host->node_id.uuid);
169
170 buffer_json_member_add_object(w->response.data, "agent");
171 {
172 buffer_json_member_add_string(w->response.data, "machine_guid", localhost->machine_guid);
173
174 - if(!uuid_is_null(localhost->node_id))
175 - buffer_json_member_add_uuid(w->response.data, "node_id", localhost->node_id);
174 + if(!UUIDiszero(localhost->node_id))
175 + buffer_json_member_add_uuid(w->response.data, "node_id", localhost->node_id.uuid);
176
177 CLAIM_ID claim_id = claim_id_get();
178 if (claim_id_is_set(claim_id))
@@ -196,8 +196,8 @@ int registry_request_hello_json(RRDHOST *host, struct web_client *w, bool do_not
196 buffer_json_add_array_item_object(w->response.data);
197 buffer_json_member_add_string(w->response.data, "machine_guid", h->machine_guid);
198
199 - if(!uuid_is_null(h->node_id))
200 - buffer_json_member_add_uuid(w->response.data, "node_id", h->node_id);
199 + if(!UUIDiszero(h->node_id))
200 + buffer_json_member_add_uuid(w->response.data, "node_id", h->node_id.uuid);
201
202 buffer_json_member_add_string(w->response.data, "hostname", rrdhost_registry_hostname(h));
203 buffer_json_object_close(w->response.data);
src/streaming/protocol/command-nodeid.c
+32 -20
@@ -4,18 +4,18 @@
4 #include "collectors/plugins.d/pluginsd_internals.h"
5
6 // the child disconnected from the parent, and it has to clear the parent's claim id
7 -void rrdpush_sender_clear_child_claim_id(RRDHOST *host) {
7 +void rrdpush_sender_clear_parent_claim_id(RRDHOST *host) {
8 host->aclk.claim_id_of_parent = UUID_ZERO;
9 }
10
11 // the parent sends to the child its claim id, node id and cloud url
12 void rrdpush_receiver_send_node_and_claim_id_to_child(RRDHOST *host) {
13 - if(host == localhost || uuid_is_null(host->node_id)) return;
13 + if(host == localhost || UUIDiszero(host->node_id)) return;
14
15 spinlock_lock(&host->receiver_lock);
16 if(host->receiver && stream_has_capability(host->receiver, STREAM_CAP_NODE_ID)) {
17 char node_id_str[UUID_STR_LEN] = "";
18 - uuid_unparse_lower(host->node_id, node_id_str);
18 + uuid_unparse_lower(host->node_id.uuid, node_id_str);
19
20 CLAIM_ID claim_id = claim_id_get();
21
@@ -39,46 +39,52 @@ void rrdpush_receiver_send_node_and_claim_id_to_child(RRDHOST *host) {
39
40 // the sender of the child receives node id, claim id and cloud url from the receiver of the parent
41 void rrdpush_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
42 - char *claim_id = get_word(s->line.words, s->line.num_words, 1);
43 - char *node_id = get_word(s->line.words, s->line.num_words, 2);
42 + char *claim_id_str = get_word(s->line.words, s->line.num_words, 1);
43 + char *node_id_str = get_word(s->line.words, s->line.num_words, 2);
44 char *url = get_word(s->line.words, s->line.num_words, 3);
45
46 bool claimed = is_agent_claimed();
47 + bool update_node_id = false;
48
48 - ND_UUID claim_uuid;
49 - if (uuid_parse(claim_id ? claim_id : "", claim_uuid.uuid) != 0) {
49 + ND_UUID claim_id;
50 + if (uuid_parse(claim_id_str ? claim_id_str : "", claim_id.uuid) != 0) {
51 nd_log(NDLS_DAEMON, NDLP_ERR,
52 "STREAM %s [send to %s] received invalid claim id '%s'",
53 rrdhost_hostname(s->host), s->connected_to,
53 - claim_id ? claim_id : "(unset)");
54 + claim_id_str ? claim_id_str : "(unset)");
55 return;
56 }
57
57 - ND_UUID node_uuid;
58 - if(uuid_parse(node_id ? node_id : "", node_uuid.uuid) != 0) {
58 + ND_UUID node_id;
59 + if(uuid_parse(node_id_str ? node_id_str : "", node_id.uuid) != 0) {
60 nd_log(NDLS_DAEMON, NDLP_ERR,
61 "STREAM %s [send to %s] received an invalid node id '%s'",
62 rrdhost_hostname(s->host), s->connected_to,
62 - node_id ? node_id : "(unset)");
63 + node_id_str ? node_id_str : "(unset)");
64 return;
65 }
66
66 - if (!UUIDiszero(s->host->aclk.claim_id_of_parent) && !UUIDeq(s->host->aclk.claim_id_of_parent, claim_uuid))
67 + if (!UUIDiszero(s->host->aclk.claim_id_of_parent) && !UUIDeq(s->host->aclk.claim_id_of_parent, claim_id))
68 nd_log(NDLS_DAEMON, NDLP_INFO,
69 "STREAM %s [send to %s] changed parent's claim id to %s",
69 - rrdhost_hostname(s->host), s->connected_to, claim_id ? claim_id : "(unset)");
70 + rrdhost_hostname(s->host), s->connected_to,
71 + claim_id_str ? claim_id_str : "(unset)");
72
71 - if(!uuid_is_null(s->host->node_id) && uuid_compare(s->host->node_id, node_uuid.uuid) != 0) {
73 + if(!UUIDiszero(s->host->node_id) && !UUIDeq(s->host->node_id, node_id)) {
74 if(claimed) {
75 nd_log(NDLS_DAEMON, NDLP_ERR,
76 "STREAM %s [send to %s] parent reports different node id '%s', but we are claimed. Ignoring it.",
75 - rrdhost_hostname(s->host), s->connected_to, node_id ? node_id : "(unset)");
77 + rrdhost_hostname(s->host), s->connected_to,
78 + node_id_str ? node_id_str : "(unset)");
79 return;
80 }
78 - else
81 + else {
82 + update_node_id = true;
83 nd_log(NDLS_DAEMON, NDLP_WARNING,
84 "STREAM %s [send to %s] changed node id to %s",
81 - rrdhost_hostname(s->host), s->connected_to, node_id ? node_id : "(unset)");
85 + rrdhost_hostname(s->host), s->connected_to,
86 + node_id_str ? node_id_str : "(unset)");
87 + }
88 }
89
90 if(!url || !*url) {
@@ -89,7 +95,7 @@ void rrdpush_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
95 return;
96 }
97
92 - s->host->aclk.claim_id_of_parent = claim_uuid;
98 + s->host->aclk.claim_id_of_parent = claim_id;
99
100 // There are some very strange corner cases here:
101 //
@@ -105,12 +111,18 @@ void rrdpush_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
111 // we are directly claimed and connected, ignore node id and cloud url
112 return;
113
108 - if(uuid_is_null(s->host->node_id))
109 - uuid_copy(s->host->node_id, node_uuid.uuid);
114 + bool node_id_updated = false;
115 + if(UUIDiszero(s->host->node_id) || update_node_id) {
116 + s->host->node_id = node_id;
117 + node_id_updated = true;
118 + }
119
120 // we change the URL, to allow the agent dashboard to work with Netdata Cloud on-prem, if any.
121 cloud_config_url_set(url);
122
123 // send it down the line (to children)
124 rrdpush_receiver_send_node_and_claim_id_to_child(s->host);
125 +
126 + if(node_id_updated)
127 + stream_path_node_id_updated(s->host);
128 }
src/streaming/protocol/commands.h
+1 -1
@@ -7,7 +7,7 @@
7
8 void rrdpush_sender_get_node_and_claim_id_from_parent(struct sender_state *s);
9 void rrdpush_receiver_send_node_and_claim_id_to_child(RRDHOST *host);
10 -void rrdpush_sender_clear_child_claim_id(RRDHOST *host);
10 +void rrdpush_sender_clear_parent_claim_id(RRDHOST *host);
11
12 void rrdpush_sender_send_claimed_id(RRDHOST *host);
13
src/streaming/receiver.c
+2
@@ -489,6 +489,8 @@ static void rrdhost_clear_receiver(struct receiver_state *rpt) {
489
490 if(rpt->config.health_enabled)
491 rrdcalc_child_disconnected(host);
492 +
493 + stream_path_child_disconnected(host);
494 }
495
496 spinlock_unlock(&host->receiver_lock);
src/streaming/rrdpush.c
+1 -145
@@ -39,7 +39,6 @@ struct config stream_config = {
39 };
40
41 unsigned int default_rrdpush_enabled = 0;
42 -STREAM_CAPABILITIES globally_disabled_capabilities = STREAM_CAP_NONE;
42
43 unsigned int default_rrdpush_compression_enabled = 1;
44 char *default_rrdpush_destination = NULL;
@@ -801,6 +800,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *decoded_query_stri
800 return rrdpush_receiver_too_busy_now(w);
801
802 struct receiver_state *rpt = callocz(1, sizeof(*rpt));
803 + rpt->connected_since_s = now_realtime_sec();
804 rpt->last_msg_t = now_monotonic_sec();
805 rpt->hops = 1;
806
@@ -1265,147 +1265,3 @@ const char *stream_handshake_error_to_string(STREAM_HANDSHAKE handshake_error) {
1265
1266 return "UNKNOWN";
1267 }
1268 -
1269 -static struct {
1270 - STREAM_CAPABILITIES cap;
1271 - const char *str;
1272 -} capability_names[] = {
1273 - {STREAM_CAP_V1, "V1" },
1274 - {STREAM_CAP_V2, "V2" },
1275 - {STREAM_CAP_VN, "VN" },
1276 - {STREAM_CAP_VCAPS, "VCAPS" },
1277 - {STREAM_CAP_HLABELS, "HLABELS" },
1278 - {STREAM_CAP_CLAIM, "CLAIM" },
1279 - {STREAM_CAP_CLABELS, "CLABELS" },
1280 - {STREAM_CAP_LZ4, "LZ4" },
1281 - {STREAM_CAP_FUNCTIONS, "FUNCTIONS" },
1282 - {STREAM_CAP_REPLICATION, "REPLICATION" },
1283 - {STREAM_CAP_BINARY, "BINARY" },
1284 - {STREAM_CAP_INTERPOLATED, "INTERPOLATED" },
1285 - {STREAM_CAP_IEEE754, "IEEE754" },
1286 - {STREAM_CAP_DATA_WITH_ML, "ML" },
1287 - {STREAM_CAP_DYNCFG, "DYNCFG" },
1288 - {STREAM_CAP_NODE_ID, "NODEID" },
1289 - {STREAM_CAP_SLOTS, "SLOTS" },
1290 - {STREAM_CAP_ZSTD, "ZSTD" },
1291 - {STREAM_CAP_GZIP, "GZIP" },
1292 - {STREAM_CAP_BROTLI, "BROTLI" },
1293 - {STREAM_CAP_PROGRESS, "PROGRESS" },
1294 - {0 , NULL },
1295 -};
1296 -
1297 -void stream_capabilities_to_string(BUFFER *wb, STREAM_CAPABILITIES caps) {
1298 - for(size_t i = 0; capability_names[i].str ; i++) {
1299 - if(caps & capability_names[i].cap) {
1300 - buffer_strcat(wb, capability_names[i].str);
1301 - buffer_strcat(wb, " ");
1302 - }
1303 - }
1304 -}
1305 -
1306 -void stream_capabilities_to_json_array(BUFFER *wb, STREAM_CAPABILITIES caps, const char *key) {
1307 - if(key)
1308 - buffer_json_member_add_array(wb, key);
1309 - else
1310 - buffer_json_add_array_item_array(wb);
1311 -
1312 - for(size_t i = 0; capability_names[i].str ; i++) {
1313 - if(caps & capability_names[i].cap)
1314 - buffer_json_add_array_item_string(wb, capability_names[i].str);
1315 - }
1316 -
1317 - buffer_json_array_close(wb);
1318 -}
1319 -
1320 -void log_receiver_capabilities(struct receiver_state *rpt) {
1321 - BUFFER *wb = buffer_create(100, NULL);
1322 - stream_capabilities_to_string(wb, rpt->capabilities);
1323 -
1324 - nd_log_daemon(NDLP_INFO, "STREAM %s [receive from [%s]:%s]: established link with negotiated capabilities: %s",
1325 - rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, buffer_tostring(wb));
1326 -
1327 - buffer_free(wb);
1328 -}
1329 -
1330 -void log_sender_capabilities(struct sender_state *s) {
1331 - BUFFER *wb = buffer_create(100, NULL);
1332 - stream_capabilities_to_string(wb, s->capabilities);
1333 -
1334 - nd_log_daemon(NDLP_INFO, "STREAM %s [send to %s]: established link with negotiated capabilities: %s",
1335 - rrdhost_hostname(s->host), s->connected_to, buffer_tostring(wb));
1336 -
1337 - buffer_free(wb);
1338 -}
1339 -
1340 -STREAM_CAPABILITIES stream_our_capabilities(RRDHOST *host, bool sender) {
1341 - STREAM_CAPABILITIES disabled_capabilities = globally_disabled_capabilities;
1342 -
1343 - if(host && sender) {
1344 - // we have DATA_WITH_ML capability
1345 - // we should remove the DATA_WITH_ML capability if our database does not have anomaly info
1346 - // this can happen under these conditions: 1. we don't run ML, and 2. we don't receive ML
1347 - spinlock_lock(&host->receiver_lock);
1348 -
1349 - if(!ml_host_running(host) && !stream_has_capability(host->receiver, STREAM_CAP_DATA_WITH_ML))
1350 - disabled_capabilities |= STREAM_CAP_DATA_WITH_ML;
1351 -
1352 - spinlock_unlock(&host->receiver_lock);
1353 -
1354 - if(host->sender)
1355 - disabled_capabilities |= host->sender->disabled_capabilities;
1356 - }
1357 -
1358 - return (STREAM_CAP_V1 |
1359 - STREAM_CAP_V2 |
1360 - STREAM_CAP_VN |
1361 - STREAM_CAP_VCAPS |
1362 - STREAM_CAP_HLABELS |
1363 - STREAM_CAP_CLAIM |
1364 - STREAM_CAP_CLABELS |
1365 - STREAM_CAP_FUNCTIONS |
1366 - STREAM_CAP_REPLICATION |
1367 - STREAM_CAP_BINARY |
1368 - STREAM_CAP_INTERPOLATED |
1369 - STREAM_CAP_SLOTS |
1370 - STREAM_CAP_PROGRESS |
1371 - STREAM_CAP_COMPRESSIONS_AVAILABLE |
1372 - STREAM_CAP_DYNCFG |
1373 - STREAM_CAP_NODE_ID |
1374 - STREAM_CAP_IEEE754 |
1375 - STREAM_CAP_DATA_WITH_ML |
1376 - 0) & ~disabled_capabilities;
1377 -}
1378 -
1379 -STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version, RRDHOST *host, bool sender) {
1380 - STREAM_CAPABILITIES caps = 0;
1381 -
1382 - if(version <= 1) caps = STREAM_CAP_V1;
1383 - else if(version < STREAM_OLD_VERSION_CLAIM) caps = STREAM_CAP_V2 | STREAM_CAP_HLABELS;
1384 - else if(version <= STREAM_OLD_VERSION_CLAIM) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM;
1385 - else if(version <= STREAM_OLD_VERSION_CLABELS) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM | STREAM_CAP_CLABELS;
1386 - else if(version <= STREAM_OLD_VERSION_LZ4) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM | STREAM_CAP_CLABELS | STREAM_CAP_LZ4_AVAILABLE;
1387 - else caps = version;
1388 -
1389 - if(caps & STREAM_CAP_VCAPS)
1390 - caps &= ~(STREAM_CAP_V1|STREAM_CAP_V2|STREAM_CAP_VN);
1391 -
1392 - if(caps & STREAM_CAP_VN)
1393 - caps &= ~(STREAM_CAP_V1|STREAM_CAP_V2);
1394 -
1395 - if(caps & STREAM_CAP_V2)
1396 - caps &= ~(STREAM_CAP_V1);
1397 -
1398 - STREAM_CAPABILITIES common_caps = caps & stream_our_capabilities(host, sender);
1399 -
1400 - if(!(common_caps & STREAM_CAP_INTERPOLATED))
1401 - // DATA WITH ML requires INTERPOLATED
1402 - common_caps &= ~STREAM_CAP_DATA_WITH_ML;
1403 -
1404 - return common_caps;
1405 -}
1406 -
1407 -int32_t stream_capabilities_to_vn(uint32_t caps) {
1408 - if(caps & STREAM_CAP_LZ4) return STREAM_OLD_VERSION_LZ4;
1409 - if(caps & STREAM_CAP_CLABELS) return STREAM_OLD_VERSION_CLABELS;
1410 - return STREAM_OLD_VERSION_CLAIM; // if(caps & STREAM_CAP_CLAIM)
1411 -}
src/streaming/rrdpush.h
+11 -92
@@ -8,90 +8,12 @@
8 #include "web/server/web_client.h"
9 #include "database/rrdfunctions.h"
10 #include "database/rrd.h"
11 +#include "stream_capabilities.h"
12
13 #define CONNECTED_TO_SIZE 100
14 #define CBUFFER_INITIAL_SIZE (16 * 1024)
15 #define THREAD_BUFFER_INITIAL_SIZE (CBUFFER_INITIAL_SIZE / 2)
16
16 -// ----------------------------------------------------------------------------
17 -// obsolete versions - do not use anymore
18 -
19 -#define STREAM_OLD_VERSION_CLAIM 3
20 -#define STREAM_OLD_VERSION_CLABELS 4
21 -#define STREAM_OLD_VERSION_LZ4 5
22 -
23 -// ----------------------------------------------------------------------------
24 -// capabilities negotiation
25 -
26 -typedef enum {
27 - STREAM_CAP_NONE = 0,
28 -
29 - // do not use the first 3 bits
30 - // they used to be versions 1, 2 and 3
31 - // before we introduce capabilities
32 -
33 - STREAM_CAP_V1 = (1 << 3), // v1 = the oldest protocol
34 - STREAM_CAP_V2 = (1 << 4), // v2 = the second version of the protocol (with host labels)
35 - STREAM_CAP_VN = (1 << 5), // version negotiation supported (for versions 3, 4, 5 of the protocol)
36 - // v3 = claiming supported
37 - // v4 = chart labels supported
38 - // v5 = lz4 compression supported
39 - STREAM_CAP_VCAPS = (1 << 6), // capabilities negotiation supported
40 - STREAM_CAP_HLABELS = (1 << 7), // host labels supported
41 - STREAM_CAP_CLAIM = (1 << 8), // claiming supported
42 - STREAM_CAP_CLABELS = (1 << 9), // chart labels supported
43 - STREAM_CAP_LZ4 = (1 << 10), // lz4 compression supported
44 - STREAM_CAP_FUNCTIONS = (1 << 11), // plugin functions supported
45 - STREAM_CAP_REPLICATION = (1 << 12), // replication supported
46 - STREAM_CAP_BINARY = (1 << 13), // streaming supports binary data
47 - STREAM_CAP_INTERPOLATED = (1 << 14), // streaming supports interpolated streaming of values
48 - STREAM_CAP_IEEE754 = (1 << 15), // streaming supports binary/hex transfer of double values
49 - STREAM_CAP_DATA_WITH_ML = (1 << 16), // streaming supports transferring anomaly bit
50 - // STREAM_CAP_DYNCFG = (1 << 17), // leave this unused for as long as possible
51 - STREAM_CAP_SLOTS = (1 << 18), // the sender can appoint a unique slot for each chart
52 - STREAM_CAP_ZSTD = (1 << 19), // ZSTD compression supported
53 - STREAM_CAP_GZIP = (1 << 20), // GZIP compression supported
54 - STREAM_CAP_BROTLI = (1 << 21), // BROTLI compression supported
55 - STREAM_CAP_PROGRESS = (1 << 22), // Functions PROGRESS support
56 - STREAM_CAP_DYNCFG = (1 << 23), // support for DYNCFG
57 - STREAM_CAP_NODE_ID = (1 << 24), // support for sending NODE_ID back to the child
58 -
59 - STREAM_CAP_INVALID = (1 << 30), // used as an invalid value for capabilities when this is set
60 - // this must be signed int, so don't use the last bit
61 - // needed for negotiating errors between parent and child
62 -} STREAM_CAPABILITIES;
63 -
64 -#ifdef ENABLE_LZ4
65 -#define STREAM_CAP_LZ4_AVAILABLE STREAM_CAP_LZ4
66 -#else
67 -#define STREAM_CAP_LZ4_AVAILABLE 0
68 -#endif // ENABLE_LZ4
69 -
70 -#ifdef ENABLE_ZSTD
71 -#define STREAM_CAP_ZSTD_AVAILABLE STREAM_CAP_ZSTD
72 -#else
73 -#define STREAM_CAP_ZSTD_AVAILABLE 0
74 -#endif // ENABLE_ZSTD
75 -
76 -#ifdef ENABLE_BROTLI
77 -#define STREAM_CAP_BROTLI_AVAILABLE STREAM_CAP_BROTLI
78 -#else
79 -#define STREAM_CAP_BROTLI_AVAILABLE 0
80 -#endif // ENABLE_BROTLI
81 -
82 -#define STREAM_CAP_COMPRESSIONS_AVAILABLE (STREAM_CAP_LZ4_AVAILABLE|STREAM_CAP_ZSTD_AVAILABLE|STREAM_CAP_BROTLI_AVAILABLE|STREAM_CAP_GZIP)
83 -
84 -extern STREAM_CAPABILITIES globally_disabled_capabilities;
85 -
86 -STREAM_CAPABILITIES stream_our_capabilities(RRDHOST *host, bool sender);
87 -
88 -#define stream_has_capability(rpt, capability) ((rpt) && ((rpt)->capabilities & (capability)) == (capability))
89 -
90 -static inline bool stream_has_more_than_one_capability_of(STREAM_CAPABILITIES caps, STREAM_CAPABILITIES mask) {
91 - STREAM_CAPABILITIES common = (STREAM_CAPABILITIES)(caps & mask);
92 - return (common & (common - 1)) != 0 && common != 0;
93 -}
94 -
17 // ----------------------------------------------------------------------------
18 // stream handshake
19
@@ -200,6 +122,9 @@ typedef enum __attribute__((packed)) {
122 SENDER_FLAG_OVERFLOW = (1 << 0), // The buffer has been overflown
123 } SENDER_FLAGS;
124
125 +typedef void (*rrdpush_defer_action_t)(struct sender_state *s, void *data);
126 +typedef void (*rrdpush_defer_cleanup_t)(struct sender_state *s, void *data);
127 +
128 struct sender_state {
129 RRDHOST *host;
130 pid_t tid; // the thread id of the sender, from gettid_cached()
@@ -267,14 +192,12 @@ struct sender_state {
192 } atomic;
193
194 struct {
270 - bool intercept_input;
271 - const char *transaction;
272 - const char *timeout_s;
273 - const char *function;
274 - const char *access;
275 - const char *source;
195 + const char *end_keyword;
196 BUFFER *payload;
277 - } functions;
197 + rrdpush_defer_action_t action;
198 + rrdpush_defer_cleanup_t cleanup;
199 + void *action_data;
200 + } defer;
201
202 int parent_using_h2o;
203 };
@@ -355,6 +278,7 @@ struct receiver_state {
278 struct rrdhost_system_info *system_info;
279 STREAM_CAPABILITIES capabilities;
280 time_t last_msg_t;
281 + time_t connected_since_s;
282
283 struct buffered_reader reader;
284
@@ -479,13 +403,7 @@ void rrdpush_signal_sender_to_wake_up(struct sender_state *s);
403
404 void rrdpush_reset_destinations_postpone_time(RRDHOST *host);
405 const char *stream_handshake_error_to_string(STREAM_HANDSHAKE handshake_error);
482 -void stream_capabilities_to_json_array(BUFFER *wb, STREAM_CAPABILITIES caps, const char *key);
406 void rrdpush_receive_log_status(struct receiver_state *rpt, const char *msg, const char *status, ND_LOG_FIELD_PRIORITY priority);
484 -void log_receiver_capabilities(struct receiver_state *rpt);
485 -void log_sender_capabilities(struct sender_state *s);
486 -STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version, RRDHOST *host, bool sender);
487 -int32_t stream_capabilities_to_vn(uint32_t caps);
488 -void stream_capabilities_to_string(BUFFER *wb, STREAM_CAPABILITIES caps);
407
408 void receiver_state_free(struct receiver_state *rpt);
409 bool stop_streaming_receiver(RRDHOST *host, STREAM_HANDSHAKE reason);
@@ -763,5 +681,6 @@ void rrdpush_select_receiver_compression_algorithm(struct receiver_state *rpt);
681 void rrdpush_compression_deactivate(struct sender_state *s);
682
683 #include "protocol/commands.h"
684 +#include "stream_path.h"
685
686 #endif //NETDATA_RRDPUSH_H
src/streaming/sender.c
+24 -1133
@@ -1,209 +1,6 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#include "rrdpush.h"
4 -#include "common.h"
5 -#include "aclk/https_client.h"
6 -
7 -#define WORKER_SENDER_JOB_CONNECT 0
8 -#define WORKER_SENDER_JOB_PIPE_READ 1
9 -#define WORKER_SENDER_JOB_SOCKET_RECEIVE 2
10 -#define WORKER_SENDER_JOB_EXECUTE 3
11 -#define WORKER_SENDER_JOB_SOCKET_SEND 4
12 -#define WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE 5
13 -#define WORKER_SENDER_JOB_DISCONNECT_OVERFLOW 6
14 -#define WORKER_SENDER_JOB_DISCONNECT_TIMEOUT 7
15 -#define WORKER_SENDER_JOB_DISCONNECT_POLL_ERROR 8
16 -#define WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR 9
17 -#define WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR 10
18 -#define WORKER_SENDER_JOB_DISCONNECT_PARENT_CLOSED 11
19 -#define WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR 12
20 -#define WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR 13
21 -#define WORKER_SENDER_JOB_DISCONNECT_NO_COMPRESSION 14
22 -#define WORKER_SENDER_JOB_BUFFER_RATIO 15
23 -#define WORKER_SENDER_JOB_BYTES_RECEIVED 16
24 -#define WORKER_SENDER_JOB_BYTES_SENT 17
25 -#define WORKER_SENDER_JOB_BYTES_COMPRESSED 18
26 -#define WORKER_SENDER_JOB_BYTES_UNCOMPRESSED 19
27 -#define WORKER_SENDER_JOB_BYTES_COMPRESSION_RATIO 20
28 -#define WORKER_SENDER_JOB_REPLAY_REQUEST 21
29 -#define WORKER_SENDER_JOB_FUNCTION_REQUEST 22
30 -#define WORKER_SENDER_JOB_REPLAY_DICT_SIZE 23
31 -#define WORKER_SENDER_JOB_DISCONNECT_CANT_UPGRADE_CONNECTION 24
32 -
33 -#if WORKER_UTILIZATION_MAX_JOB_TYPES < 25
34 -#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 25
35 -#endif
36 -
37 -extern struct config stream_config;
38 -extern char *netdata_ssl_ca_path;
39 -extern char *netdata_ssl_ca_file;
40 -
41 -static __thread BUFFER *sender_thread_buffer = NULL;
42 -static __thread bool sender_thread_buffer_used = false;
43 -static __thread time_t sender_thread_buffer_last_reset_s = 0;
44 -
45 -void sender_thread_buffer_free(void) {
46 - buffer_free(sender_thread_buffer);
47 - sender_thread_buffer = NULL;
48 - sender_thread_buffer_used = false;
49 -}
50 -
51 -// Collector thread starting a transmission
52 -BUFFER *sender_start(struct sender_state *s) {
53 - if(unlikely(sender_thread_buffer_used))
54 - fatal("STREAMING: thread buffer is used multiple times concurrently.");
55 -
56 - if(unlikely(rrdpush_sender_last_buffer_recreate_get(s) > sender_thread_buffer_last_reset_s)) {
57 - if(unlikely(sender_thread_buffer && sender_thread_buffer->size > THREAD_BUFFER_INITIAL_SIZE)) {
58 - buffer_free(sender_thread_buffer);
59 - sender_thread_buffer = NULL;
60 - }
61 - }
62 -
63 - if(unlikely(!sender_thread_buffer)) {
64 - sender_thread_buffer = buffer_create(THREAD_BUFFER_INITIAL_SIZE, &netdata_buffers_statistics.buffers_streaming);
65 - sender_thread_buffer_last_reset_s = rrdpush_sender_last_buffer_recreate_get(s);
66 - }
67 -
68 - sender_thread_buffer_used = true;
69 - buffer_flush(sender_thread_buffer);
70 - return sender_thread_buffer;
71 -}
72 -
73 -static inline void rrdpush_sender_thread_close_socket(RRDHOST *host);
74 -
75 -#define SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE 3
76 -
77 -// Collector thread finishing a transmission
78 -void sender_commit(struct sender_state *s, BUFFER *wb, STREAM_TRAFFIC_TYPE type) {
79 -
80 - if(unlikely(wb != sender_thread_buffer))
81 - fatal("STREAMING: sender is trying to commit a buffer that is not this thread's buffer.");
82 -
83 - if(unlikely(!sender_thread_buffer_used))
84 - fatal("STREAMING: sender is committing a buffer twice.");
85 -
86 - sender_thread_buffer_used = false;
87 -
88 - char *src = (char *)buffer_tostring(wb);
89 - size_t src_len = buffer_strlen(wb);
90 -
91 - if(unlikely(!src || !src_len))
92 - return;
93 -
94 - sender_lock(s);
95 -
96 -#ifdef NETDATA_LOG_STREAM_SENDER
97 - if(type == STREAM_TRAFFIC_TYPE_METADATA) {
98 - if(!s->stream_log_fp) {
99 - char filename[FILENAME_MAX + 1];
100 - snprintfz(filename, FILENAME_MAX, "/tmp/stream-sender-%s.txt", s->host ? rrdhost_hostname(s->host) : "unknown");
101 -
102 - s->stream_log_fp = fopen(filename, "w");
103 - }
104 -
105 - fprintf(s->stream_log_fp, "\n--- SEND MESSAGE START: %s ----\n"
106 - "%s"
107 - "--- SEND MESSAGE END ----------------------------------------\n"
108 - , rrdhost_hostname(s->host), src
109 - );
110 - }
111 -#endif
112 -
113 - if(unlikely(s->buffer->max_size < (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE)) {
114 - netdata_log_info("STREAM %s [send to %s]: max buffer size of %zu is too small for a data message of size %zu. Increasing the max buffer size to %d times the max data message size.",
115 - rrdhost_hostname(s->host), s->connected_to, s->buffer->max_size, buffer_strlen(wb) + 1, SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE);
116 -
117 - s->buffer->max_size = (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE;
118 - }
119 -
120 - if (s->compressor.initialized) {
121 - while(src_len) {
122 - size_t size_to_compress = src_len;
123 -
124 - if(unlikely(size_to_compress > COMPRESSION_MAX_MSG_SIZE)) {
125 - if (stream_has_capability(s, STREAM_CAP_BINARY))
126 - size_to_compress = COMPRESSION_MAX_MSG_SIZE;
127 - else {
128 - if (size_to_compress > COMPRESSION_MAX_MSG_SIZE) {
129 - // we need to find the last newline
130 - // so that the decompressor will have a whole line to work with
131 -
132 - const char *t = &src[COMPRESSION_MAX_MSG_SIZE];
133 - while (--t >= src)
134 - if (unlikely(*t == '\n'))
135 - break;
136 -
137 - if (t <= src) {
138 - size_to_compress = COMPRESSION_MAX_MSG_SIZE;
139 - } else
140 - size_to_compress = t - src + 1;
141 - }
142 - }
143 - }
144 -
145 - const char *dst;
146 - size_t dst_len = rrdpush_compress(&s->compressor, src, size_to_compress, &dst);
147 - if (!dst_len) {
148 - netdata_log_error("STREAM %s [send to %s]: COMPRESSION failed. Resetting compressor and re-trying",
149 - rrdhost_hostname(s->host), s->connected_to);
150 -
151 - rrdpush_compression_initialize(s);
152 - dst_len = rrdpush_compress(&s->compressor, src, size_to_compress, &dst);
153 - if(!dst_len) {
154 - netdata_log_error("STREAM %s [send to %s]: COMPRESSION failed again. Deactivating compression",
155 - rrdhost_hostname(s->host), s->connected_to);
156 -
157 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_NO_COMPRESSION);
158 - rrdpush_compression_deactivate(s);
159 - rrdpush_sender_thread_close_socket(s->host);
160 - sender_unlock(s);
161 - return;
162 - }
163 - }
164 -
165 - rrdpush_signature_t signature = rrdpush_compress_encode_signature(dst_len);
166 -
167 -#ifdef NETDATA_INTERNAL_CHECKS
168 - // check if reversing the signature provides the same length
169 - size_t decoded_dst_len = rrdpush_decompress_decode_signature((const char *)&signature, sizeof(signature));
170 - if(decoded_dst_len != dst_len)
171 - fatal("RRDPUSH COMPRESSION: invalid signature, original payload %zu bytes, "
172 - "compressed payload length %zu bytes, but signature says payload is %zu bytes",
173 - size_to_compress, dst_len, decoded_dst_len);
174 -#endif
175 -
176 - if(cbuffer_add_unsafe(s->buffer, (const char *)&signature, sizeof(signature)))
177 - s->flags |= SENDER_FLAG_OVERFLOW;
178 - else {
179 - if(cbuffer_add_unsafe(s->buffer, dst, dst_len))
180 - s->flags |= SENDER_FLAG_OVERFLOW;
181 - else
182 - s->sent_bytes_on_this_connection_per_type[type] += dst_len + sizeof(signature);
183 - }
184 -
185 - src = src + size_to_compress;
186 - src_len -= size_to_compress;
187 - }
188 - }
189 - else if(cbuffer_add_unsafe(s->buffer, src, src_len))
190 - s->flags |= SENDER_FLAG_OVERFLOW;
191 - else
192 - s->sent_bytes_on_this_connection_per_type[type] += src_len;
193 -
194 - replication_recalculate_buffer_used_ratio_unsafe(s);
195 -
196 - bool signal_sender = false;
197 - if(!rrdpush_sender_pipe_has_pending_data(s)) {
198 - rrdpush_sender_pipe_set_pending_data(s);
199 - signal_sender = true;
200 - }
201 -
202 - sender_unlock(s);
203 -
204 - if(signal_sender && (!stream_has_capability(s, STREAM_CAP_INTERPOLATED) || type != STREAM_TRAFFIC_TYPE_DATA))
205 - rrdpush_signal_sender_to_wake_up(s);
206 -}
3 +#include "sender_internals.h"
4
5 static inline void rrdpush_sender_add_host_variable_to_buffer(BUFFER *wb, const RRDVAR_ACQUIRED *rva) {
6 buffer_sprintf(
@@ -324,692 +121,32 @@ static void rrdpush_sender_charts_and_replication_reset(RRDHOST *host) {
121 rrdpush_sender_replicating_charts_zero(host->sender);
122 }
123
327 -static void rrdpush_sender_on_connect(RRDHOST *host) {
124 +void rrdpush_sender_on_connect(RRDHOST *host) {
125 rrdpush_sender_cbuffer_flush(host);
126 rrdpush_sender_charts_and_replication_reset(host);
127 }
128
332 -static void rrdpush_sender_after_connect(RRDHOST *host) {
129 +void rrdpush_sender_after_connect(RRDHOST *host) {
130 rrdpush_sender_thread_send_custom_host_variables(host);
131 }
132
336 -static inline void rrdpush_sender_thread_close_socket(RRDHOST *host) {
337 - netdata_ssl_close(&host->sender->ssl);
133 +void rrdpush_sender_disconnect_and_cleanup(RRDHOST *host) {
134 + rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED | RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS);
135
339 - if(host->sender->rrdpush_sender_socket != -1) {
340 - close(host->sender->rrdpush_sender_socket);
341 - host->sender->rrdpush_sender_socket = -1;
342 - }
136 + rrdpush_sender_thread_close_socket(host);
137
344 - rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS);
345 - rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED);
138 + // we have been connected to this parent - let's cleanup
139
140 // do not flush the circular buffer here
348 - // this function is called sometimes with the mutex lock, sometimes without the lock
141 + // this function is called sometimes with the sender lock, sometimes without the lock
142 +
143 rrdpush_sender_charts_and_replication_reset(host);
144
145 // clear the parent's claim id
352 - rrdpush_sender_clear_child_claim_id(host);
146 + rrdpush_sender_clear_parent_claim_id(host);
147 rrdpush_receiver_send_node_and_claim_id_to_child(host);
354 -}
355 -
356 -void rrdpush_encode_variable(stream_encoded_t *se, RRDHOST *host) {
357 - se->os_name = (host->system_info->host_os_name)?url_encode(host->system_info->host_os_name):strdupz("");
358 - se->os_id = (host->system_info->host_os_id)?url_encode(host->system_info->host_os_id):strdupz("");
359 - se->os_version = (host->system_info->host_os_version)?url_encode(host->system_info->host_os_version):strdupz("");
360 - se->kernel_name = (host->system_info->kernel_name)?url_encode(host->system_info->kernel_name):strdupz("");
361 - se->kernel_version = (host->system_info->kernel_version)?url_encode(host->system_info->kernel_version):strdupz("");
362 -}
363 -
364 -void rrdpush_clean_encoded(stream_encoded_t *se) {
365 - if (se->os_name) {
366 - freez(se->os_name);
367 - se->os_name = NULL;
368 - }
369 -
370 - if (se->os_id) {
371 - freez(se->os_id);
372 - se->os_id = NULL;
373 - }
374 -
375 - if (se->os_version) {
376 - freez(se->os_version);
377 - se->os_version = NULL;
378 - }
379 -
380 - if (se->kernel_name) {
381 - freez(se->kernel_name);
382 - se->kernel_name = NULL;
383 - }
384 -
385 - if (se->kernel_version) {
386 - freez(se->kernel_version);
387 - se->kernel_version = NULL;
388 - }
389 -}
390 -
391 -struct {
392 - const char *response;
393 - const char *status;
394 - size_t length;
395 - int32_t version;
396 - bool dynamic;
397 - const char *error;
398 - int worker_job_id;
399 - int postpone_reconnect_seconds;
400 - ND_LOG_FIELD_PRIORITY priority;
401 -} stream_responses[] = {
402 - {
403 - .response = START_STREAMING_PROMPT_VN,
404 - .length = sizeof(START_STREAMING_PROMPT_VN) - 1,
405 - .status = RRDPUSH_STATUS_CONNECTED,
406 - .version = STREAM_HANDSHAKE_OK_V3, // and above
407 - .dynamic = true, // dynamic = we will parse the version / capabilities
408 - .error = NULL,
409 - .worker_job_id = 0,
410 - .postpone_reconnect_seconds = 0,
411 - .priority = NDLP_INFO,
412 - },
413 - {
414 - .response = START_STREAMING_PROMPT_V2,
415 - .length = sizeof(START_STREAMING_PROMPT_V2) - 1,
416 - .status = RRDPUSH_STATUS_CONNECTED,
417 - .version = STREAM_HANDSHAKE_OK_V2,
418 - .dynamic = false,
419 - .error = NULL,
420 - .worker_job_id = 0,
421 - .postpone_reconnect_seconds = 0,
422 - .priority = NDLP_INFO,
423 - },
424 - {
425 - .response = START_STREAMING_PROMPT_V1,
426 - .length = sizeof(START_STREAMING_PROMPT_V1) - 1,
427 - .status = RRDPUSH_STATUS_CONNECTED,
428 - .version = STREAM_HANDSHAKE_OK_V1,
429 - .dynamic = false,
430 - .error = NULL,
431 - .worker_job_id = 0,
432 - .postpone_reconnect_seconds = 0,
433 - .priority = NDLP_INFO,
434 - },
435 - {
436 - .response = START_STREAMING_ERROR_SAME_LOCALHOST,
437 - .length = sizeof(START_STREAMING_ERROR_SAME_LOCALHOST) - 1,
438 - .status = RRDPUSH_STATUS_LOCALHOST,
439 - .version = STREAM_HANDSHAKE_ERROR_LOCALHOST,
440 - .dynamic = false,
441 - .error = "remote server rejected this stream, the host we are trying to stream is its localhost",
442 - .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
443 - .postpone_reconnect_seconds = 60 * 60, // the IP may change, try it every hour
444 - .priority = NDLP_DEBUG,
445 - },
446 - {
447 - .response = START_STREAMING_ERROR_ALREADY_STREAMING,
448 - .length = sizeof(START_STREAMING_ERROR_ALREADY_STREAMING) - 1,
449 - .status = RRDPUSH_STATUS_ALREADY_CONNECTED,
450 - .version = STREAM_HANDSHAKE_ERROR_ALREADY_CONNECTED,
451 - .dynamic = false,
452 - .error = "remote server rejected this stream, the host we are trying to stream is already streamed to it",
453 - .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
454 - .postpone_reconnect_seconds = 2 * 60, // 2 minutes
455 - .priority = NDLP_DEBUG,
456 - },
457 - {
458 - .response = START_STREAMING_ERROR_NOT_PERMITTED,
459 - .length = sizeof(START_STREAMING_ERROR_NOT_PERMITTED) - 1,
460 - .status = RRDPUSH_STATUS_PERMISSION_DENIED,
461 - .version = STREAM_HANDSHAKE_ERROR_DENIED,
462 - .dynamic = false,
463 - .error = "remote server denied access, probably we don't have the right API key?",
464 - .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
465 - .postpone_reconnect_seconds = 1 * 60, // 1 minute
466 - .priority = NDLP_ERR,
467 - },
468 - {
469 - .response = START_STREAMING_ERROR_BUSY_TRY_LATER,
470 - .length = sizeof(START_STREAMING_ERROR_BUSY_TRY_LATER) - 1,
471 - .status = RRDPUSH_STATUS_RATE_LIMIT,
472 - .version = STREAM_HANDSHAKE_BUSY_TRY_LATER,
473 - .dynamic = false,
474 - .error = "remote server is currently busy, we should try later",
475 - .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
476 - .postpone_reconnect_seconds = 2 * 60, // 2 minutes
477 - .priority = NDLP_NOTICE,
478 - },
479 - {
480 - .response = START_STREAMING_ERROR_INTERNAL_ERROR,
481 - .length = sizeof(START_STREAMING_ERROR_INTERNAL_ERROR) - 1,
482 - .status = RRDPUSH_STATUS_INTERNAL_SERVER_ERROR,
483 - .version = STREAM_HANDSHAKE_INTERNAL_ERROR,
484 - .dynamic = false,
485 - .error = "remote server is encountered an internal error, we should try later",
486 - .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
487 - .postpone_reconnect_seconds = 5 * 60, // 5 minutes
488 - .priority = NDLP_CRIT,
489 - },
490 - {
491 - .response = START_STREAMING_ERROR_INITIALIZATION,
492 - .length = sizeof(START_STREAMING_ERROR_INITIALIZATION) - 1,
493 - .status = RRDPUSH_STATUS_INITIALIZATION_IN_PROGRESS,
494 - .version = STREAM_HANDSHAKE_INITIALIZATION,
495 - .dynamic = false,
496 - .error = "remote server is initializing, we should try later",
497 - .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
498 - .postpone_reconnect_seconds = 2 * 60, // 2 minute
499 - .priority = NDLP_NOTICE,
500 - },
501 -
502 - // terminator
503 - {
504 - .response = NULL,
505 - .length = 0,
506 - .status = RRDPUSH_STATUS_BAD_HANDSHAKE,
507 - .version = STREAM_HANDSHAKE_ERROR_BAD_HANDSHAKE,
508 - .dynamic = false,
509 - .error = "remote node response is not understood, is it Netdata?",
510 - .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
511 - .postpone_reconnect_seconds = 1 * 60, // 1 minute
512 - .priority = NDLP_ERR,
513 - }
514 -};
515 -
516 -static inline bool rrdpush_sender_validate_response(RRDHOST *host, struct sender_state *s, char *http, size_t http_length) {
517 - int32_t version = STREAM_HANDSHAKE_ERROR_BAD_HANDSHAKE;
518 -
519 - int i;
520 - for(i = 0; stream_responses[i].response ; i++) {
521 - if(stream_responses[i].dynamic &&
522 - http_length > stream_responses[i].length && http_length < (stream_responses[i].length + 30) &&
523 - strncmp(http, stream_responses[i].response, stream_responses[i].length) == 0) {
524 -
525 - version = str2i(&http[stream_responses[i].length]);
526 - break;
527 - }
528 - else if(http_length == stream_responses[i].length && strcmp(http, stream_responses[i].response) == 0) {
529 - version = stream_responses[i].version;
530 -
531 - break;
532 - }
533 - }
534 -
535 - if(version >= STREAM_HANDSHAKE_OK_V1) {
536 - host->destination->reason = version;
537 - host->destination->postpone_reconnection_until = now_realtime_sec() + s->reconnect_delay;
538 - s->capabilities = convert_stream_version_to_capabilities(version, host, true);
539 - return true;
540 - }
541 -
542 - ND_LOG_FIELD_PRIORITY priority = stream_responses[i].priority;
543 - const char *error = stream_responses[i].error;
544 - const char *status = stream_responses[i].status;
545 - int worker_job_id = stream_responses[i].worker_job_id;
546 - int delay = stream_responses[i].postpone_reconnect_seconds;
547 -
548 - worker_is_busy(worker_job_id);
549 - rrdpush_sender_thread_close_socket(host);
550 - host->destination->reason = version;
551 - host->destination->postpone_reconnection_until = now_realtime_sec() + delay;
552 -
553 - ND_LOG_STACK lgs[] = {
554 - ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, status),
555 - ND_LOG_FIELD_END(),
556 - };
557 - ND_LOG_STACK_PUSH(lgs);
558 -
559 - char buf[RFC3339_MAX_LENGTH];
560 - rfc3339_datetime_ut(buf, sizeof(buf), host->destination->postpone_reconnection_until * USEC_PER_SEC, 0, false);
561 -
562 - nd_log(NDLS_DAEMON, priority,
563 - "STREAM %s [send to %s]: %s - will retry in %d secs, at %s",
564 - rrdhost_hostname(host), s->connected_to, error, delay, buf);
565 -
566 - return false;
567 -}
568 -
569 -unsigned char alpn_proto_list[] = {
570 - 18, 'n', 'e', 't', 'd', 'a', 't', 'a', '_', 's', 't', 'r', 'e', 'a', 'm', '/', '2', '.', '0',
571 - 8, 'h', 't', 't', 'p', '/', '1', '.', '1'
572 -};
573 -
574 -#define CONN_UPGRADE_VAL "upgrade"
575 -
576 -static bool rrdpush_sender_connect_ssl(struct sender_state *s __maybe_unused) {
577 - RRDHOST *host = s->host;
578 - bool ssl_required = host->destination && host->destination->ssl;
579 -
580 - netdata_ssl_close(&host->sender->ssl);
581 -
582 - if(!ssl_required)
583 - return true;
584 -
585 - if (netdata_ssl_open_ext(&host->sender->ssl, netdata_ssl_streaming_sender_ctx, s->rrdpush_sender_socket, alpn_proto_list, sizeof(alpn_proto_list))) {
586 - if(!netdata_ssl_connect(&host->sender->ssl)) {
587 - // couldn't connect
588 -
589 - ND_LOG_STACK lgs[] = {
590 - ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_SSL_ERROR),
591 - ND_LOG_FIELD_END(),
592 - };
593 - ND_LOG_STACK_PUSH(lgs);
594 -
595 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR);
596 - rrdpush_sender_thread_close_socket(host);
597 - host->destination->reason = STREAM_HANDSHAKE_ERROR_SSL_ERROR;
598 - host->destination->postpone_reconnection_until = now_realtime_sec() + 5 * 60;
599 - return false;
600 - }
601 -
602 - if (netdata_ssl_validate_certificate_sender &&
603 - security_test_certificate(host->sender->ssl.conn)) {
604 - // certificate is not valid
605 -
606 - ND_LOG_STACK lgs[] = {
607 - ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_INVALID_SSL_CERTIFICATE),
608 - ND_LOG_FIELD_END(),
609 - };
610 - ND_LOG_STACK_PUSH(lgs);
611 -
612 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR);
613 - netdata_log_error("SSL: closing the stream connection, because the server SSL certificate is not valid.");
614 - rrdpush_sender_thread_close_socket(host);
615 - host->destination->reason = STREAM_HANDSHAKE_ERROR_INVALID_CERTIFICATE;
616 - host->destination->postpone_reconnection_until = now_realtime_sec() + 5 * 60;
617 - return false;
618 - }
619 -
620 - return true;
621 - }
622 -
623 - ND_LOG_STACK lgs[] = {
624 - ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_CANT_ESTABLISH_SSL_CONNECTION),
625 - ND_LOG_FIELD_END(),
626 - };
627 - ND_LOG_STACK_PUSH(lgs);
628 -
629 - netdata_log_error("SSL: failed to establish connection.");
630 - return false;
631 -}
632 -
633 -static int rrdpush_http_upgrade_prelude(RRDHOST *host, struct sender_state *s) {
634 -
635 - char http[HTTP_HEADER_SIZE + 1];
636 - snprintfz(http, HTTP_HEADER_SIZE,
637 - "GET " NETDATA_STREAM_URL HTTP_1_1 HTTP_ENDL
638 - "Upgrade: " NETDATA_STREAM_PROTO_NAME HTTP_ENDL
639 - "Connection: Upgrade"
640 - HTTP_HDR_END);
641 -
642 - ssize_t bytes = send_timeout(
643 - &host->sender->ssl,
644 - s->rrdpush_sender_socket,
645 - http,
646 - strlen(http),
647 - 0,
648 - 1000);
649 -
650 - bytes = recv_timeout(
651 - &host->sender->ssl,
652 - s->rrdpush_sender_socket,
653 - http,
654 - HTTP_HEADER_SIZE,
655 - 0,
656 - 1000);
657 -
658 - if (bytes <= 0) {
659 - error_report("Error reading from remote");
660 - return 1;
661 - }
662 -
663 - rbuf_t buf = rbuf_create(bytes);
664 - rbuf_push(buf, http, bytes);
665 -
666 - http_parse_ctx ctx;
667 - http_parse_ctx_create(&ctx, HTTP_PARSE_INITIAL);
668 - ctx.flags |= HTTP_PARSE_FLAG_DONT_WAIT_FOR_CONTENT;
669 -
670 - int rc;
671 -// while((rc = parse_http_response(buf, &ctx)) == HTTP_PARSE_NEED_MORE_DATA);
672 - rc = parse_http_response(buf, &ctx);
673 -
674 - if (rc != HTTP_PARSE_SUCCESS) {
675 - error_report("Failed to parse HTTP response sent. (%d)", rc);
676 - goto err_cleanup;
677 - }
678 - if (ctx.http_code == HTTP_RESP_MOVED_PERM) {
679 - const char *hdr = get_http_header_by_name(&ctx, "location");
680 - if (hdr)
681 - error_report("HTTP response is %d Moved Permanently (location: \"%s\") instead of expected %d Switching Protocols.", ctx.http_code, hdr, HTTP_RESP_SWITCH_PROTO);
682 - else
683 - error_report("HTTP response is %d instead of expected %d Switching Protocols.", ctx.http_code, HTTP_RESP_SWITCH_PROTO);
684 - goto err_cleanup;
685 - }
686 - if (ctx.http_code == HTTP_RESP_NOT_FOUND) {
687 - error_report("HTTP response is %d instead of expected %d Switching Protocols. Parent version too old.", ctx.http_code, HTTP_RESP_SWITCH_PROTO);
688 - // TODO set some flag here that will signify parent is older version
689 - // and to try connection without rrdpush_http_upgrade_prelude next time
690 - goto err_cleanup;
691 - }
692 - if (ctx.http_code != HTTP_RESP_SWITCH_PROTO) {
693 - error_report("HTTP response is %d instead of expected %d Switching Protocols", ctx.http_code, HTTP_RESP_SWITCH_PROTO);
694 - goto err_cleanup;
695 - }
696 -
697 - const char *hdr = get_http_header_by_name(&ctx, "connection");
698 - if (!hdr) {
699 - error_report("Missing \"connection\" header in reply");
700 - goto err_cleanup;
701 - }
702 - if (strncmp(hdr, CONN_UPGRADE_VAL, strlen(CONN_UPGRADE_VAL))) {
703 - error_report("Expected \"connection: " CONN_UPGRADE_VAL "\"");
704 - goto err_cleanup;
705 - }
706 -
707 - hdr = get_http_header_by_name(&ctx, "upgrade");
708 - if (!hdr) {
709 - error_report("Missing \"upgrade\" header in reply");
710 - goto err_cleanup;
711 - }
712 - if (strncmp(hdr, NETDATA_STREAM_PROTO_NAME, strlen(NETDATA_STREAM_PROTO_NAME))) {
713 - error_report("Expected \"upgrade: " NETDATA_STREAM_PROTO_NAME "\"");
714 - goto err_cleanup;
715 - }
716 -
717 - netdata_log_debug(D_STREAM, "Stream sender upgrade to \"" NETDATA_STREAM_PROTO_NAME "\" successful");
718 - rbuf_free(buf);
719 - http_parse_ctx_destroy(&ctx);
720 - return 0;
721 -err_cleanup:
722 - rbuf_free(buf);
723 - http_parse_ctx_destroy(&ctx);
724 - return 1;
725 -}
726 -
727 -static bool rrdpush_sender_thread_connect_to_parent(RRDHOST *host, int default_port, int timeout, struct sender_state *s) {
728 -
729 - struct timeval tv = {
730 - .tv_sec = timeout,
731 - .tv_usec = 0
732 - };
733 -
734 - // make sure the socket is closed
735 - rrdpush_sender_thread_close_socket(host);
736 -
737 - s->rrdpush_sender_socket = connect_to_one_of_destinations(
738 - host
739 - , default_port
740 - , &tv
741 - , &s->reconnects_counter
742 - , s->connected_to
743 - , sizeof(s->connected_to)-1
744 - , &host->destination
745 - );
746 -
747 - if(unlikely(s->rrdpush_sender_socket == -1)) {
748 - // netdata_log_error("STREAM %s [send to %s]: could not connect to parent node at this time.", rrdhost_hostname(host), host->rrdpush_send_destination);
749 - return false;
750 - }
751 -
752 - // netdata_log_info("STREAM %s [send to %s]: initializing communication...", rrdhost_hostname(host), s->connected_to);
753 -
754 - // reset our capabilities to default
755 - s->capabilities = stream_our_capabilities(host, true);
756 -
757 - /* TODO: During the implementation of #7265 switch the set of variables to HOST_* and CONTAINER_* if the
758 - version negotiation resulted in a high enough version.
759 - */
760 - stream_encoded_t se;
761 - rrdpush_encode_variable(&se, host);
762 -
763 - host->sender->hops = host->system_info->hops + 1;
764 -
765 - char http[HTTP_HEADER_SIZE + 1];
766 - int eol = snprintfz(http, HTTP_HEADER_SIZE,
767 - "STREAM "
768 - "key=%s"
769 - "&hostname=%s"
770 - "&registry_hostname=%s"
771 - "&machine_guid=%s"
772 - "&update_every=%d"
773 - "&os=%s"
774 - "&timezone=%s"
775 - "&abbrev_timezone=%s"
776 - "&utc_offset=%d"
777 - "&hops=%d"
778 - "&ml_capable=%d"
779 - "&ml_enabled=%d"
780 - "&mc_version=%d"
781 - "&ver=%u"
782 - "&NETDATA_INSTANCE_CLOUD_TYPE=%s"
783 - "&NETDATA_INSTANCE_CLOUD_INSTANCE_TYPE=%s"
784 - "&NETDATA_INSTANCE_CLOUD_INSTANCE_REGION=%s"
785 - "&NETDATA_SYSTEM_OS_NAME=%s"
786 - "&NETDATA_SYSTEM_OS_ID=%s"
787 - "&NETDATA_SYSTEM_OS_ID_LIKE=%s"
788 - "&NETDATA_SYSTEM_OS_VERSION=%s"
789 - "&NETDATA_SYSTEM_OS_VERSION_ID=%s"
790 - "&NETDATA_SYSTEM_OS_DETECTION=%s"
791 - "&NETDATA_HOST_IS_K8S_NODE=%s"
792 - "&NETDATA_SYSTEM_KERNEL_NAME=%s"
793 - "&NETDATA_SYSTEM_KERNEL_VERSION=%s"
794 - "&NETDATA_SYSTEM_ARCHITECTURE=%s"
795 - "&NETDATA_SYSTEM_VIRTUALIZATION=%s"
796 - "&NETDATA_SYSTEM_VIRT_DETECTION=%s"
797 - "&NETDATA_SYSTEM_CONTAINER=%s"
798 - "&NETDATA_SYSTEM_CONTAINER_DETECTION=%s"
799 - "&NETDATA_CONTAINER_OS_NAME=%s"
800 - "&NETDATA_CONTAINER_OS_ID=%s"
801 - "&NETDATA_CONTAINER_OS_ID_LIKE=%s"
802 - "&NETDATA_CONTAINER_OS_VERSION=%s"
803 - "&NETDATA_CONTAINER_OS_VERSION_ID=%s"
804 - "&NETDATA_CONTAINER_OS_DETECTION=%s"
805 - "&NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT=%s"
806 - "&NETDATA_SYSTEM_CPU_FREQ=%s"
807 - "&NETDATA_SYSTEM_TOTAL_RAM=%s"
808 - "&NETDATA_SYSTEM_TOTAL_DISK_SIZE=%s"
809 - "&NETDATA_PROTOCOL_VERSION=%s"
810 - HTTP_1_1 HTTP_ENDL
811 - "User-Agent: %s/%s\r\n"
812 - "Accept: */*\r\n\r\n"
813 - , host->rrdpush.send.api_key
814 - , rrdhost_hostname(host)
815 - , rrdhost_registry_hostname(host)
816 - , host->machine_guid
817 - , default_rrd_update_every
818 - , rrdhost_os(host)
819 - , rrdhost_timezone(host)
820 - , rrdhost_abbrev_timezone(host)
821 - , host->utc_offset
822 - , host->sender->hops
823 - , host->system_info->ml_capable
824 - , host->system_info->ml_enabled
825 - , host->system_info->mc_version
826 - , s->capabilities
827 - , (host->system_info->cloud_provider_type) ? host->system_info->cloud_provider_type : ""
828 - , (host->system_info->cloud_instance_type) ? host->system_info->cloud_instance_type : ""
829 - , (host->system_info->cloud_instance_region) ? host->system_info->cloud_instance_region : ""
830 - , se.os_name
831 - , se.os_id
832 - , (host->system_info->host_os_id_like) ? host->system_info->host_os_id_like : ""
833 - , se.os_version
834 - , (host->system_info->host_os_version_id) ? host->system_info->host_os_version_id : ""
835 - , (host->system_info->host_os_detection) ? host->system_info->host_os_detection : ""
836 - , (host->system_info->is_k8s_node) ? host->system_info->is_k8s_node : ""
837 - , se.kernel_name
838 - , se.kernel_version
839 - , (host->system_info->architecture) ? host->system_info->architecture : ""
840 - , (host->system_info->virtualization) ? host->system_info->virtualization : ""
841 - , (host->system_info->virt_detection) ? host->system_info->virt_detection : ""
842 - , (host->system_info->container) ? host->system_info->container : ""
843 - , (host->system_info->container_detection) ? host->system_info->container_detection : ""
844 - , (host->system_info->container_os_name) ? host->system_info->container_os_name : ""
845 - , (host->system_info->container_os_id) ? host->system_info->container_os_id : ""
846 - , (host->system_info->container_os_id_like) ? host->system_info->container_os_id_like : ""
847 - , (host->system_info->container_os_version) ? host->system_info->container_os_version : ""
848 - , (host->system_info->container_os_version_id) ? host->system_info->container_os_version_id : ""
849 - , (host->system_info->container_os_detection) ? host->system_info->container_os_detection : ""
850 - , (host->system_info->host_cores) ? host->system_info->host_cores : ""
851 - , (host->system_info->host_cpu_freq) ? host->system_info->host_cpu_freq : ""
852 - , (host->system_info->host_ram_total) ? host->system_info->host_ram_total : ""
853 - , (host->system_info->host_disk_space) ? host->system_info->host_disk_space : ""
854 - , STREAMING_PROTOCOL_VERSION
855 - , rrdhost_program_name(host)
856 - , rrdhost_program_version(host)
857 - );
858 - http[eol] = 0x00;
859 - rrdpush_clean_encoded(&se);
860 -
861 - if(!rrdpush_sender_connect_ssl(s))
862 - return false;
863 -
864 - if (s->parent_using_h2o && rrdpush_http_upgrade_prelude(host, s)) {
865 - ND_LOG_STACK lgs[] = {
866 - ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_CANT_UPGRADE_CONNECTION),
867 - ND_LOG_FIELD_END(),
868 - };
869 - ND_LOG_STACK_PUSH(lgs);
870 -
871 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_CANT_UPGRADE_CONNECTION);
872 - rrdpush_sender_thread_close_socket(host);
873 - host->destination->reason = STREAM_HANDSHAKE_ERROR_HTTP_UPGRADE;
874 - host->destination->postpone_reconnection_until = now_realtime_sec() + 1 * 60;
875 - return false;
876 - }
877 -
878 - ssize_t len = (ssize_t)strlen(http);
879 - ssize_t bytes = send_timeout(
880 - &host->sender->ssl,
881 - s->rrdpush_sender_socket,
882 - http,
883 - len,
884 - 0,
885 - timeout);
886 -
887 - if(bytes <= 0) { // timeout is 0
888 - ND_LOG_STACK lgs[] = {
889 - ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_TIMEOUT),
890 - ND_LOG_FIELD_END(),
891 - };
892 - ND_LOG_STACK_PUSH(lgs);
893 -
894 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT);
895 - rrdpush_sender_thread_close_socket(host);
896 -
897 - nd_log(NDLS_DAEMON, NDLP_ERR,
898 - "STREAM %s [send to %s]: failed to send HTTP header to remote netdata.",
899 - rrdhost_hostname(host), s->connected_to);
900 -
901 - host->destination->reason = STREAM_HANDSHAKE_ERROR_SEND_TIMEOUT;
902 - host->destination->postpone_reconnection_until = now_realtime_sec() + 1 * 60;
903 - return false;
904 - }
905 -
906 - bytes = recv_timeout(
907 - &host->sender->ssl,
908 - s->rrdpush_sender_socket,
909 - http,
910 - HTTP_HEADER_SIZE,
911 - 0,
912 - timeout);
913 -
914 - if(bytes <= 0) { // timeout is 0
915 - ND_LOG_STACK lgs[] = {
916 - ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_TIMEOUT),
917 - ND_LOG_FIELD_END(),
918 - };
919 - ND_LOG_STACK_PUSH(lgs);
920 -
921 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT);
922 - rrdpush_sender_thread_close_socket(host);
923 -
924 - nd_log(NDLS_DAEMON, NDLP_ERR,
925 - "STREAM %s [send to %s]: remote netdata does not respond.",
926 - rrdhost_hostname(host), s->connected_to);
927 -
928 - host->destination->reason = STREAM_HANDSHAKE_ERROR_RECEIVE_TIMEOUT;
929 - host->destination->postpone_reconnection_until = now_realtime_sec() + 30;
930 - return false;
931 - }
932 -
933 - if(sock_setnonblock(s->rrdpush_sender_socket) < 0)
934 - nd_log(NDLS_DAEMON, NDLP_WARNING,
935 - "STREAM %s [send to %s]: cannot set non-blocking mode for socket.",
936 - rrdhost_hostname(host), s->connected_to);
937 - sock_setcloexec(s->rrdpush_sender_socket);
148
939 - if(sock_enlarge_out(s->rrdpush_sender_socket) < 0)
940 - nd_log(NDLS_DAEMON, NDLP_WARNING,
941 - "STREAM %s [send to %s]: cannot enlarge the socket buffer.",
942 - rrdhost_hostname(host), s->connected_to);
943 -
944 - http[bytes] = '\0';
945 - if(!rrdpush_sender_validate_response(host, s, http, bytes))
946 - return false;
947 -
948 - rrdpush_compression_initialize(s);
949 -
950 - log_sender_capabilities(s);
951 -
952 - ND_LOG_STACK lgs[] = {
953 - ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_CONNECTED),
954 - ND_LOG_FIELD_END(),
955 - };
956 - ND_LOG_STACK_PUSH(lgs);
957 -
958 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
959 - "STREAM %s: connected to %s...",
960 - rrdhost_hostname(host), s->connected_to);
961 -
962 - return true;
963 -}
964 -
965 -static bool attempt_to_connect(struct sender_state *state) {
966 - ND_LOG_STACK lgs[] = {
967 - ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
968 - ND_LOG_FIELD_END(),
969 - };
970 - ND_LOG_STACK_PUSH(lgs);
971 -
972 - state->send_attempts = 0;
973 -
974 - // reset the bytes we have sent for this session
975 - state->sent_bytes_on_this_connection = 0;
976 - memset(state->sent_bytes_on_this_connection_per_type, 0, sizeof(state->sent_bytes_on_this_connection_per_type));
977 -
978 - if(rrdpush_sender_thread_connect_to_parent(state->host, state->default_port, state->timeout, state)) {
979 - // reset the buffer, to properly send charts and metrics
980 - rrdpush_sender_on_connect(state->host);
981 -
982 - // send from the beginning
983 - state->begin = 0;
984 -
985 - // make sure the next reconnection will be immediate
986 - state->not_connected_loops = 0;
987 -
988 - // let the data collection threads know we are ready
989 - rrdhost_flag_set(state->host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED);
990 -
991 - rrdpush_sender_after_connect(state->host);
992 -
993 - return true;
994 - }
995 -
996 - // we couldn't connect
997 -
998 - // increase the failed connections counter
999 - state->not_connected_loops++;
1000 -
1001 - // slow re-connection on repeating errors
1002 - usec_t now_ut = now_monotonic_usec();
1003 - usec_t end_ut = now_ut + USEC_PER_SEC * state->reconnect_delay;
1004 - while(now_ut < end_ut) {
1005 - if(nd_thread_signaled_to_cancel())
1006 - return false;
1007 -
1008 - sleep_usec(100 * USEC_PER_MS); // seconds
1009 - now_ut = now_monotonic_usec();
1010 - }
1011 -
1012 - return false;
149 + stream_path_parent_disconnected(host);
150 }
151
152 // TCP window is open, and we have data to transmit.
@@ -1042,7 +179,7 @@ static ssize_t attempt_to_send(struct sender_state *s) {
179 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
180 netdata_log_debug(D_STREAM, "STREAM: Send failed - closing socket...");
181 netdata_log_error("STREAM %s [send to %s]: failed to send metrics - closing connection - we have sent %zu bytes on this connection.", rrdhost_hostname(s->host), s->connected_to, s->sent_bytes_on_this_connection);
1045 - rrdpush_sender_thread_close_socket(s->host);
182 + rrdpush_sender_disconnect_and_cleanup(s->host);
183 }
184 else
185 netdata_log_debug(D_STREAM, "STREAM: send() returned 0 -> no error but no transmission");
@@ -1080,257 +217,11 @@ static ssize_t attempt_read(struct sender_state *s) {
217 netdata_log_error("STREAM %s [send to %s]: error during receive (%zd) - closing connection.", rrdhost_hostname(s->host), s->connected_to, ret);
218 }
219
1083 - rrdpush_sender_thread_close_socket(s->host);
220 + rrdpush_sender_disconnect_and_cleanup(s->host);
221
222 return ret;
223 }
224
1088 -struct inflight_stream_function {
1089 - struct sender_state *sender;
1090 - STRING *transaction;
1091 - usec_t received_ut;
1092 -};
1093 -
1094 -static void stream_execute_function_callback(BUFFER *func_wb, int code, void *data) {
1095 - struct inflight_stream_function *tmp = data;
1096 - struct sender_state *s = tmp->sender;
1097 -
1098 - if(rrdhost_can_send_definitions_to_parent(s->host)) {
1099 - BUFFER *wb = sender_start(s);
1100 -
1101 - pluginsd_function_result_begin_to_buffer(wb
1102 - , string2str(tmp->transaction)
1103 - , code
1104 - , content_type_id2string(func_wb->content_type)
1105 - , func_wb->expires);
1106 -
1107 - buffer_fast_strcat(wb, buffer_tostring(func_wb), buffer_strlen(func_wb));
1108 - pluginsd_function_result_end_to_buffer(wb);
1109 -
1110 - sender_commit(s, wb, STREAM_TRAFFIC_TYPE_FUNCTIONS);
1111 - sender_thread_buffer_free();
1112 -
1113 - internal_error(true, "STREAM %s [send to %s] FUNCTION transaction %s sending back response (%zu bytes, %"PRIu64" usec).",
1114 - rrdhost_hostname(s->host), s->connected_to,
1115 - string2str(tmp->transaction),
1116 - buffer_strlen(func_wb),
1117 - now_realtime_usec() - tmp->received_ut);
1118 - }
1119 -
1120 - string_freez(tmp->transaction);
1121 - buffer_free(func_wb);
1122 - freez(tmp);
1123 -}
1124 -
1125 -static void stream_execute_function_progress_callback(void *data, size_t done, size_t all) {
1126 - struct inflight_stream_function *tmp = data;
1127 - struct sender_state *s = tmp->sender;
1128 -
1129 - if(rrdhost_can_send_definitions_to_parent(s->host)) {
1130 - BUFFER *wb = sender_start(s);
1131 -
1132 - buffer_sprintf(wb, PLUGINSD_KEYWORD_FUNCTION_PROGRESS " '%s' %zu %zu\n",
1133 - string2str(tmp->transaction), done, all);
1134 -
1135 - sender_commit(s, wb, STREAM_TRAFFIC_TYPE_FUNCTIONS);
1136 - }
1137 -}
1138 -
1139 -static void execute_commands_function(struct sender_state *s, const char *command, const char *transaction, const char *timeout_s, const char *function, BUFFER *payload, const char *access, const char *source) {
1140 - worker_is_busy(WORKER_SENDER_JOB_FUNCTION_REQUEST);
1141 - nd_log(NDLS_ACCESS, NDLP_INFO, NULL);
1142 -
1143 - if(!transaction || !*transaction || !timeout_s || !*timeout_s || !function || !*function) {
1144 - netdata_log_error("STREAM %s [send to %s] %s execution command is incomplete (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.",
1145 - rrdhost_hostname(s->host), s->connected_to,
1146 - command,
1147 - transaction?transaction:"(unset)",
1148 - timeout_s?timeout_s:"(unset)",
1149 - function?function:"(unset)");
1150 - }
1151 - else {
1152 - int timeout = str2i(timeout_s);
1153 - if(timeout <= 0) timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT;
1154 -
1155 - struct inflight_stream_function *tmp = callocz(1, sizeof(struct inflight_stream_function));
1156 - tmp->received_ut = now_realtime_usec();
1157 - tmp->sender = s;
1158 - tmp->transaction = string_strdupz(transaction);
1159 - BUFFER *wb = buffer_create(1024, &netdata_buffers_statistics.buffers_functions);
1160 -
1161 - int code = rrd_function_run(s->host, wb, timeout,
1162 - http_access_from_hex_mapping_old_roles(access), function, false, transaction,
1163 - stream_execute_function_callback, tmp,
1164 - stream_has_capability(s, STREAM_CAP_PROGRESS) ? stream_execute_function_progress_callback : NULL,
1165 - stream_has_capability(s, STREAM_CAP_PROGRESS) ? tmp : NULL,
1166 - NULL, NULL, payload, source, true);
1167 -
1168 - if(code != HTTP_RESP_OK) {
1169 - if (!buffer_strlen(wb))
1170 - rrd_call_function_error(wb, "Failed to route request to collector", code);
1171 - }
1172 - }
1173 -}
1174 -
1175 -static void cleanup_intercepting_input(struct sender_state *s) {
1176 - freez((void *)s->functions.transaction);
1177 - freez((void *)s->functions.timeout_s);
1178 - freez((void *)s->functions.function);
1179 - freez((void *)s->functions.access);
1180 - freez((void *)s->functions.source);
1181 - buffer_free(s->functions.payload);
1182 -
1183 - s->functions.transaction = NULL;
1184 - s->functions.timeout_s = NULL;
1185 - s->functions.function = NULL;
1186 - s->functions.payload = NULL;
1187 - s->functions.access = NULL;
1188 - s->functions.source = NULL;
1189 - s->functions.intercept_input = false;
1190 -}
1191 -
1192 -static void execute_commands_cleanup(struct sender_state *s) {
1193 - cleanup_intercepting_input(s);
1194 -}
1195 -
1196 -// This is just a placeholder until the gap filling state machine is inserted
1197 -void execute_commands(struct sender_state *s) {
1198 - worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
1199 -
1200 - ND_LOG_STACK lgs[] = {
1201 - ND_LOG_FIELD_CB(NDF_REQUEST, line_splitter_reconstruct_line, &s->line),
1202 - ND_LOG_FIELD_END(),
1203 - };
1204 - ND_LOG_STACK_PUSH(lgs);
1205 -
1206 - char *start = s->read_buffer, *end = &s->read_buffer[s->read_len], *newline;
1207 - *end = '\0';
1208 - for( ; start < end ; start = newline + 1) {
1209 - newline = strchr(start, '\n');
1210 -
1211 - if(!newline) {
1212 - if(s->functions.intercept_input) {
1213 - buffer_strcat(s->functions.payload, start);
1214 - start = end;
1215 - }
1216 - break;
1217 - }
1218 -
1219 - *newline = '\0';
1220 - s->line.count++;
1221 -
1222 - if(s->functions.intercept_input) {
1223 - if(strcmp(start, PLUGINSD_CALL_FUNCTION_PAYLOAD_END) == 0) {
1224 - execute_commands_function(s,
1225 - PLUGINSD_CALL_FUNCTION_PAYLOAD_END,
1226 - s->functions.transaction, s->functions.timeout_s,
1227 - s->functions.function, s->functions.payload,
1228 - s->functions.access, s->functions.source);
1229 -
1230 - cleanup_intercepting_input(s);
1231 - }
1232 - else {
1233 - buffer_strcat(s->functions.payload, start);
1234 - buffer_fast_charcat(s->functions.payload, '\n');
1235 - }
1236 -
1237 - continue;
1238 - }
1239 -
1240 - s->line.num_words = quoted_strings_splitter_pluginsd(start, s->line.words, PLUGINSD_MAX_WORDS);
1241 - const char *command = get_word(s->line.words, s->line.num_words, 0);
1242 -
1243 - if(command && strcmp(command, PLUGINSD_CALL_FUNCTION) == 0) {
1244 - char *transaction = get_word(s->line.words, s->line.num_words, 1);
1245 - char *timeout_s = get_word(s->line.words, s->line.num_words, 2);
1246 - char *function = get_word(s->line.words, s->line.num_words, 3);
1247 - char *access = get_word(s->line.words, s->line.num_words, 4);
1248 - char *source = get_word(s->line.words, s->line.num_words, 5);
1249 -
1250 - execute_commands_function(s, command, transaction, timeout_s, function, NULL, access, source);
1251 - }
1252 - else if(command && strcmp(command, PLUGINSD_CALL_FUNCTION_PAYLOAD_BEGIN) == 0) {
1253 - char *transaction = get_word(s->line.words, s->line.num_words, 1);
1254 - char *timeout_s = get_word(s->line.words, s->line.num_words, 2);
1255 - char *function = get_word(s->line.words, s->line.num_words, 3);
1256 - char *access = get_word(s->line.words, s->line.num_words, 4);
1257 - char *source = get_word(s->line.words, s->line.num_words, 5);
1258 - char *content_type = get_word(s->line.words, s->line.num_words, 6);
1259 -
1260 - s->functions.transaction = strdupz(transaction ? transaction : "");
1261 - s->functions.timeout_s = strdupz(timeout_s ? timeout_s : "");
1262 - s->functions.function = strdupz(function ? function : "");
1263 - s->functions.access = strdupz(access ? access : "");
1264 - s->functions.source = strdupz(source ? source : "");
1265 - s->functions.payload = buffer_create(0, NULL);
1266 - s->functions.payload->content_type = content_type_string2id(content_type);
1267 - s->functions.intercept_input = true;
1268 - }
1269 - else if(command && strcmp(command, PLUGINSD_CALL_FUNCTION_CANCEL) == 0) {
1270 - worker_is_busy(WORKER_SENDER_JOB_FUNCTION_REQUEST);
1271 - nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
1272 -
1273 - char *transaction = get_word(s->line.words, s->line.num_words, 1);
1274 - if(transaction && *transaction)
1275 - rrd_function_cancel(transaction);
1276 - }
1277 - else if(command && strcmp(command, PLUGINSD_CALL_FUNCTION_PROGRESS) == 0) {
1278 - worker_is_busy(WORKER_SENDER_JOB_FUNCTION_REQUEST);
1279 - nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
1280 -
1281 - char *transaction = get_word(s->line.words, s->line.num_words, 1);
1282 - if(transaction && *transaction)
1283 - rrd_function_progress(transaction);
1284 - }
1285 - else if (command && strcmp(command, PLUGINSD_KEYWORD_REPLAY_CHART) == 0) {
1286 - worker_is_busy(WORKER_SENDER_JOB_REPLAY_REQUEST);
1287 - nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
1288 -
1289 - const char *chart_id = get_word(s->line.words, s->line.num_words, 1);
1290 - const char *start_streaming = get_word(s->line.words, s->line.num_words, 2);
1291 - const char *after = get_word(s->line.words, s->line.num_words, 3);
1292 - const char *before = get_word(s->line.words, s->line.num_words, 4);
1293 -
1294 - if (!chart_id || !start_streaming || !after || !before) {
1295 - netdata_log_error("STREAM %s [send to %s] %s command is incomplete"
1296 - " (chart=%s, start_streaming=%s, after=%s, before=%s)",
1297 - rrdhost_hostname(s->host), s->connected_to,
1298 - command,
1299 - chart_id ? chart_id : "(unset)",
1300 - start_streaming ? start_streaming : "(unset)",
1301 - after ? after : "(unset)",
1302 - before ? before : "(unset)");
1303 - }
1304 - else {
1305 - replication_add_request(s, chart_id,
1306 - strtoll(after, NULL, 0),
1307 - strtoll(before, NULL, 0),
1308 - !strcmp(start_streaming, "true")
1309 - );
1310 - }
1311 - }
1312 - else if(command && strcmp(command, PLUGINSD_KEYWORD_NODE_ID) == 0) {
1313 - rrdpush_sender_get_node_and_claim_id_from_parent(s);
1314 - }
1315 - else {
1316 - netdata_log_error("STREAM %s [send to %s] received unknown command over connection: %s",
1317 - rrdhost_hostname(s->host), s->connected_to, s->line.words[0]?s->line.words[0]:"(unset)");
1318 - }
1319 -
1320 - line_splitter_reset(&s->line);
1321 - worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
1322 - }
1323 -
1324 - if (start < end) {
1325 - memmove(s->read_buffer, start, end-start);
1326 - s->read_len = end - start;
1327 - }
1328 - else {
1329 - s->read_buffer[0] = '\0';
1330 - s->read_len = 0;
1331 - }
1332 -}
1333 -
225 struct rrdpush_sender_thread_data {
226 RRDHOST *host;
227 char *pipe_buffer;
@@ -1476,9 +367,9 @@ static void rrdpush_sender_thread_cleanup_callback(void *pptr) {
367 rrdhost_hostname(host),
368 host->sender->exit.reason != STREAM_HANDSHAKE_NEVER ? stream_handshake_error_to_string(host->sender->exit.reason) : "");
369
1479 - rrdpush_sender_thread_close_socket(host);
370 + rrdpush_sender_disconnect_and_cleanup(host);
371 rrdpush_sender_pipe_close(host, host->sender->rrdpush_sender_pipe, false);
1481 - execute_commands_cleanup(host->sender);
372 + rrdpush_sender_execute_commands_cleanup(host->sender);
373
374 rrdhost_clear_sender___while_having_sender_mutex(host);
375
@@ -1640,8 +531,7 @@ void *rrdpush_sender_thread(void *ptr) {
531 &stream_config, CONFIG_SECTION_STREAM, "parent using h2o", false);
532
533 // initialize rrdpush globals
1643 - rrdhost_flag_clear(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS);
1644 - rrdhost_flag_clear(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED);
534 + rrdhost_flag_clear(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED | RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS);
535
536 int pipe_buffer_size = 10 * 1024;
537 #ifdef F_GETPIPE_SZ
@@ -1673,7 +563,7 @@ void *rrdpush_sender_thread(void *ptr) {
563
564 now_s = now_monotonic_sec();
565 rrdpush_sender_cbuffer_recreate_timed(s, now_s, false, true);
1676 - execute_commands_cleanup(s);
566 + rrdpush_sender_execute_commands_cleanup(s);
567
568 rrdhost_flag_clear(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS);
569 s->flags &= ~SENDER_FLAG_OVERFLOW;
@@ -1688,6 +578,7 @@ void *rrdpush_sender_thread(void *ptr) {
578 break;
579
580 now_s = s->last_traffic_seen_t = now_monotonic_sec();
581 + stream_path_send_to_parent(s->host);
582 rrdpush_sender_send_claimed_id(s->host);
583 rrdpush_send_host_labels(s->host);
584 rrdpush_send_global_functions(s->host);
@@ -1712,7 +603,7 @@ void *rrdpush_sender_thread(void *ptr) {
603 )) {
604 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT);
605 netdata_log_error("STREAM %s [send to %s]: could not send metrics for %d seconds - closing connection - we have sent %zu bytes on this connection via %zu send attempts.", rrdhost_hostname(s->host), s->connected_to, s->timeout, s->sent_bytes_on_this_connection, s->send_attempts);
1715 - rrdpush_sender_thread_close_socket(s->host);
606 + rrdpush_sender_disconnect_and_cleanup(s->host);
607 continue;
608 }
609
@@ -1743,7 +634,7 @@ void *rrdpush_sender_thread(void *ptr) {
634 if(!rrdpush_sender_pipe_close(s->host, s->rrdpush_sender_pipe, true)) {
635 netdata_log_error("STREAM %s [send]: cannot create inter-thread communication pipe. Disabling streaming.",
636 rrdhost_hostname(s->host));
1746 - rrdpush_sender_thread_close_socket(s->host);
637 + rrdpush_sender_disconnect_and_cleanup(s->host);
638 break;
639 }
640 }
@@ -1794,7 +685,7 @@ void *rrdpush_sender_thread(void *ptr) {
685 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_POLL_ERROR);
686 netdata_log_error("STREAM %s [send to %s]: failed to poll(). Closing socket.", rrdhost_hostname(s->host), s->connected_to);
687 rrdpush_sender_pipe_close(s->host, s->rrdpush_sender_pipe, true);
1797 - rrdpush_sender_thread_close_socket(s->host);
688 + rrdpush_sender_disconnect_and_cleanup(s->host);
689 continue;
690 }
691
@@ -1828,7 +719,7 @@ void *rrdpush_sender_thread(void *ptr) {
719 }
720
721 if(unlikely(s->read_len))
1831 - execute_commands(s);
722 + rrdpush_sender_execute_commands(s);
723
724 if(unlikely(fds[Collector].revents & (POLLERR|POLLHUP|POLLNVAL))) {
725 char *error = NULL;
@@ -1861,7 +752,7 @@ void *rrdpush_sender_thread(void *ptr) {
752 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR);
753 netdata_log_error("STREAM %s [send to %s]: restarting connection: %s - %zu bytes transmitted.",
754 rrdhost_hostname(s->host), s->connected_to, error, s->sent_bytes_on_this_connection);
1864 - rrdpush_sender_thread_close_socket(s->host);
755 + rrdpush_sender_disconnect_and_cleanup(s->host);
756 }
757 }
758
@@ -1871,7 +762,7 @@ void *rrdpush_sender_thread(void *ptr) {
762 errno_clear();
763 netdata_log_error("STREAM %s [send to %s]: buffer full (allocated %zu bytes) after sending %zu bytes. Restarting connection",
764 rrdhost_hostname(s->host), s->connected_to, s->buffer->size, s->sent_bytes_on_this_connection);
1874 - rrdpush_sender_thread_close_socket(s->host);
765 + rrdpush_sender_disconnect_and_cleanup(s->host);
766 }
767
768 worker_set_metric(WORKER_SENDER_JOB_REPLAY_DICT_SIZE, (NETDATA_DOUBLE) dictionary_entries(s->replication.requests));
src/streaming/sender_commit.c new
+168
@@ -0,0 +1,168 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "sender_internals.h"
4 +
5 +static __thread BUFFER *sender_thread_buffer = NULL;
6 +static __thread bool sender_thread_buffer_used = false;
7 +static __thread time_t sender_thread_buffer_last_reset_s = 0;
8 +
9 +void sender_thread_buffer_free(void) {
10 + buffer_free(sender_thread_buffer);
11 + sender_thread_buffer = NULL;
12 + sender_thread_buffer_used = false;
13 +}
14 +
15 +// Collector thread starting a transmission
16 +BUFFER *sender_start(struct sender_state *s) {
17 + if(unlikely(sender_thread_buffer_used))
18 + fatal("STREAMING: thread buffer is used multiple times concurrently.");
19 +
20 + if(unlikely(rrdpush_sender_last_buffer_recreate_get(s) > sender_thread_buffer_last_reset_s)) {
21 + if(unlikely(sender_thread_buffer && sender_thread_buffer->size > THREAD_BUFFER_INITIAL_SIZE)) {
22 + buffer_free(sender_thread_buffer);
23 + sender_thread_buffer = NULL;
24 + }
25 + }
26 +
27 + if(unlikely(!sender_thread_buffer)) {
28 + sender_thread_buffer = buffer_create(THREAD_BUFFER_INITIAL_SIZE, &netdata_buffers_statistics.buffers_streaming);
29 + sender_thread_buffer_last_reset_s = rrdpush_sender_last_buffer_recreate_get(s);
30 + }
31 +
32 + sender_thread_buffer_used = true;
33 + buffer_flush(sender_thread_buffer);
34 + return sender_thread_buffer;
35 +}
36 +
37 +#define SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE 3
38 +
39 +// Collector thread finishing a transmission
40 +void sender_commit(struct sender_state *s, BUFFER *wb, STREAM_TRAFFIC_TYPE type) {
41 +
42 + if(unlikely(wb != sender_thread_buffer))
43 + fatal("STREAMING: sender is trying to commit a buffer that is not this thread's buffer.");
44 +
45 + if(unlikely(!sender_thread_buffer_used))
46 + fatal("STREAMING: sender is committing a buffer twice.");
47 +
48 + sender_thread_buffer_used = false;
49 +
50 + char *src = (char *)buffer_tostring(wb);
51 + size_t src_len = buffer_strlen(wb);
52 +
53 + if(unlikely(!src || !src_len))
54 + return;
55 +
56 + sender_lock(s);
57 +
58 +#ifdef NETDATA_LOG_STREAM_SENDER
59 + if(type == STREAM_TRAFFIC_TYPE_METADATA) {
60 + if(!s->stream_log_fp) {
61 + char filename[FILENAME_MAX + 1];
62 + snprintfz(filename, FILENAME_MAX, "/tmp/stream-sender-%s.txt", s->host ? rrdhost_hostname(s->host) : "unknown");
63 +
64 + s->stream_log_fp = fopen(filename, "w");
65 + }
66 +
67 + fprintf(s->stream_log_fp, "\n--- SEND MESSAGE START: %s ----\n"
68 + "%s"
69 + "--- SEND MESSAGE END ----------------------------------------\n"
70 + , rrdhost_hostname(s->host), src
71 + );
72 + }
73 +#endif
74 +
75 + if(unlikely(s->buffer->max_size < (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE)) {
76 + netdata_log_info("STREAM %s [send to %s]: max buffer size of %zu is too small for a data message of size %zu. Increasing the max buffer size to %d times the max data message size.",
77 + rrdhost_hostname(s->host), s->connected_to, s->buffer->max_size, buffer_strlen(wb) + 1, SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE);
78 +
79 + s->buffer->max_size = (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE;
80 + }
81 +
82 + if (s->compressor.initialized) {
83 + while(src_len) {
84 + size_t size_to_compress = src_len;
85 +
86 + if(unlikely(size_to_compress > COMPRESSION_MAX_MSG_SIZE)) {
87 + if (stream_has_capability(s, STREAM_CAP_BINARY))
88 + size_to_compress = COMPRESSION_MAX_MSG_SIZE;
89 + else {
90 + if (size_to_compress > COMPRESSION_MAX_MSG_SIZE) {
91 + // we need to find the last newline
92 + // so that the decompressor will have a whole line to work with
93 +
94 + const char *t = &src[COMPRESSION_MAX_MSG_SIZE];
95 + while (--t >= src)
96 + if (unlikely(*t == '\n'))
97 + break;
98 +
99 + if (t <= src) {
100 + size_to_compress = COMPRESSION_MAX_MSG_SIZE;
101 + } else
102 + size_to_compress = t - src + 1;
103 + }
104 + }
105 + }
106 +
107 + const char *dst;
108 + size_t dst_len = rrdpush_compress(&s->compressor, src, size_to_compress, &dst);
109 + if (!dst_len) {
110 + netdata_log_error("STREAM %s [send to %s]: COMPRESSION failed. Resetting compressor and re-trying",
111 + rrdhost_hostname(s->host), s->connected_to);
112 +
113 + rrdpush_compression_initialize(s);
114 + dst_len = rrdpush_compress(&s->compressor, src, size_to_compress, &dst);
115 + if(!dst_len) {
116 + netdata_log_error("STREAM %s [send to %s]: COMPRESSION failed again. Deactivating compression",
117 + rrdhost_hostname(s->host), s->connected_to);
118 +
119 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_NO_COMPRESSION);
120 + rrdpush_compression_deactivate(s);
121 + rrdpush_sender_disconnect_and_cleanup(s->host);
122 + sender_unlock(s);
123 + return;
124 + }
125 + }
126 +
127 + rrdpush_signature_t signature = rrdpush_compress_encode_signature(dst_len);
128 +
129 +#ifdef NETDATA_INTERNAL_CHECKS
130 + // check if reversing the signature provides the same length
131 + size_t decoded_dst_len = rrdpush_decompress_decode_signature((const char *)&signature, sizeof(signature));
132 + if(decoded_dst_len != dst_len)
133 + fatal("RRDPUSH COMPRESSION: invalid signature, original payload %zu bytes, "
134 + "compressed payload length %zu bytes, but signature says payload is %zu bytes",
135 + size_to_compress, dst_len, decoded_dst_len);
136 +#endif
137 +
138 + if(cbuffer_add_unsafe(s->buffer, (const char *)&signature, sizeof(signature)))
139 + s->flags |= SENDER_FLAG_OVERFLOW;
140 + else {
141 + if(cbuffer_add_unsafe(s->buffer, dst, dst_len))
142 + s->flags |= SENDER_FLAG_OVERFLOW;
143 + else
144 + s->sent_bytes_on_this_connection_per_type[type] += dst_len + sizeof(signature);
145 + }
146 +
147 + src = src + size_to_compress;
148 + src_len -= size_to_compress;
149 + }
150 + }
151 + else if(cbuffer_add_unsafe(s->buffer, src, src_len))
152 + s->flags |= SENDER_FLAG_OVERFLOW;
153 + else
154 + s->sent_bytes_on_this_connection_per_type[type] += src_len;
155 +
156 + replication_recalculate_buffer_used_ratio_unsafe(s);
157 +
158 + bool signal_sender = false;
159 + if(!rrdpush_sender_pipe_has_pending_data(s)) {
160 + rrdpush_sender_pipe_set_pending_data(s);
161 + signal_sender = true;
162 + }
163 +
164 + sender_unlock(s);
165 +
166 + if(signal_sender && (!stream_has_capability(s, STREAM_CAP_INTERPOLATED) || type != STREAM_TRAFFIC_TYPE_DATA))
167 + rrdpush_signal_sender_to_wake_up(s);
168 +}
src/streaming/sender_connect.c new
+671
@@ -0,0 +1,671 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "sender_internals.h"
4 +
5 +void rrdpush_sender_thread_close_socket(RRDHOST *host) {
6 + netdata_ssl_close(&host->sender->ssl);
7 +
8 + if(host->sender->rrdpush_sender_socket != -1) {
9 + close(host->sender->rrdpush_sender_socket);
10 + host->sender->rrdpush_sender_socket = -1;
11 + }
12 +}
13 +
14 +void rrdpush_encode_variable(stream_encoded_t *se, RRDHOST *host) {
15 + se->os_name = (host->system_info->host_os_name)?url_encode(host->system_info->host_os_name):strdupz("");
16 + se->os_id = (host->system_info->host_os_id)?url_encode(host->system_info->host_os_id):strdupz("");
17 + se->os_version = (host->system_info->host_os_version)?url_encode(host->system_info->host_os_version):strdupz("");
18 + se->kernel_name = (host->system_info->kernel_name)?url_encode(host->system_info->kernel_name):strdupz("");
19 + se->kernel_version = (host->system_info->kernel_version)?url_encode(host->system_info->kernel_version):strdupz("");
20 +}
21 +
22 +void rrdpush_clean_encoded(stream_encoded_t *se) {
23 + if (se->os_name) {
24 + freez(se->os_name);
25 + se->os_name = NULL;
26 + }
27 +
28 + if (se->os_id) {
29 + freez(se->os_id);
30 + se->os_id = NULL;
31 + }
32 +
33 + if (se->os_version) {
34 + freez(se->os_version);
35 + se->os_version = NULL;
36 + }
37 +
38 + if (se->kernel_name) {
39 + freez(se->kernel_name);
40 + se->kernel_name = NULL;
41 + }
42 +
43 + if (se->kernel_version) {
44 + freez(se->kernel_version);
45 + se->kernel_version = NULL;
46 + }
47 +}
48 +
49 +struct {
50 + const char *response;
51 + const char *status;
52 + size_t length;
53 + int32_t version;
54 + bool dynamic;
55 + const char *error;
56 + int worker_job_id;
57 + int postpone_reconnect_seconds;
58 + ND_LOG_FIELD_PRIORITY priority;
59 +} stream_responses[] = {
60 + {
61 + .response = START_STREAMING_PROMPT_VN,
62 + .length = sizeof(START_STREAMING_PROMPT_VN) - 1,
63 + .status = RRDPUSH_STATUS_CONNECTED,
64 + .version = STREAM_HANDSHAKE_OK_V3, // and above
65 + .dynamic = true, // dynamic = we will parse the version / capabilities
66 + .error = NULL,
67 + .worker_job_id = 0,
68 + .postpone_reconnect_seconds = 0,
69 + .priority = NDLP_INFO,
70 + },
71 + {
72 + .response = START_STREAMING_PROMPT_V2,
73 + .length = sizeof(START_STREAMING_PROMPT_V2) - 1,
74 + .status = RRDPUSH_STATUS_CONNECTED,
75 + .version = STREAM_HANDSHAKE_OK_V2,
76 + .dynamic = false,
77 + .error = NULL,
78 + .worker_job_id = 0,
79 + .postpone_reconnect_seconds = 0,
80 + .priority = NDLP_INFO,
81 + },
82 + {
83 + .response = START_STREAMING_PROMPT_V1,
84 + .length = sizeof(START_STREAMING_PROMPT_V1) - 1,
85 + .status = RRDPUSH_STATUS_CONNECTED,
86 + .version = STREAM_HANDSHAKE_OK_V1,
87 + .dynamic = false,
88 + .error = NULL,
89 + .worker_job_id = 0,
90 + .postpone_reconnect_seconds = 0,
91 + .priority = NDLP_INFO,
92 + },
93 + {
94 + .response = START_STREAMING_ERROR_SAME_LOCALHOST,
95 + .length = sizeof(START_STREAMING_ERROR_SAME_LOCALHOST) - 1,
96 + .status = RRDPUSH_STATUS_LOCALHOST,
97 + .version = STREAM_HANDSHAKE_ERROR_LOCALHOST,
98 + .dynamic = false,
99 + .error = "remote server rejected this stream, the host we are trying to stream is its localhost",
100 + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
101 + .postpone_reconnect_seconds = 60 * 60, // the IP may change, try it every hour
102 + .priority = NDLP_DEBUG,
103 + },
104 + {
105 + .response = START_STREAMING_ERROR_ALREADY_STREAMING,
106 + .length = sizeof(START_STREAMING_ERROR_ALREADY_STREAMING) - 1,
107 + .status = RRDPUSH_STATUS_ALREADY_CONNECTED,
108 + .version = STREAM_HANDSHAKE_ERROR_ALREADY_CONNECTED,
109 + .dynamic = false,
110 + .error = "remote server rejected this stream, the host we are trying to stream is already streamed to it",
111 + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
112 + .postpone_reconnect_seconds = 2 * 60, // 2 minutes
113 + .priority = NDLP_DEBUG,
114 + },
115 + {
116 + .response = START_STREAMING_ERROR_NOT_PERMITTED,
117 + .length = sizeof(START_STREAMING_ERROR_NOT_PERMITTED) - 1,
118 + .status = RRDPUSH_STATUS_PERMISSION_DENIED,
119 + .version = STREAM_HANDSHAKE_ERROR_DENIED,
120 + .dynamic = false,
121 + .error = "remote server denied access, probably we don't have the right API key?",
122 + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
123 + .postpone_reconnect_seconds = 1 * 60, // 1 minute
124 + .priority = NDLP_ERR,
125 + },
126 + {
127 + .response = START_STREAMING_ERROR_BUSY_TRY_LATER,
128 + .length = sizeof(START_STREAMING_ERROR_BUSY_TRY_LATER) - 1,
129 + .status = RRDPUSH_STATUS_RATE_LIMIT,
130 + .version = STREAM_HANDSHAKE_BUSY_TRY_LATER,
131 + .dynamic = false,
132 + .error = "remote server is currently busy, we should try later",
133 + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
134 + .postpone_reconnect_seconds = 2 * 60, // 2 minutes
135 + .priority = NDLP_NOTICE,
136 + },
137 + {
138 + .response = START_STREAMING_ERROR_INTERNAL_ERROR,
139 + .length = sizeof(START_STREAMING_ERROR_INTERNAL_ERROR) - 1,
140 + .status = RRDPUSH_STATUS_INTERNAL_SERVER_ERROR,
141 + .version = STREAM_HANDSHAKE_INTERNAL_ERROR,
142 + .dynamic = false,
143 + .error = "remote server is encountered an internal error, we should try later",
144 + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
145 + .postpone_reconnect_seconds = 5 * 60, // 5 minutes
146 + .priority = NDLP_CRIT,
147 + },
148 + {
149 + .response = START_STREAMING_ERROR_INITIALIZATION,
150 + .length = sizeof(START_STREAMING_ERROR_INITIALIZATION) - 1,
151 + .status = RRDPUSH_STATUS_INITIALIZATION_IN_PROGRESS,
152 + .version = STREAM_HANDSHAKE_INITIALIZATION,
153 + .dynamic = false,
154 + .error = "remote server is initializing, we should try later",
155 + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
156 + .postpone_reconnect_seconds = 2 * 60, // 2 minute
157 + .priority = NDLP_NOTICE,
158 + },
159 +
160 + // terminator
161 + {
162 + .response = NULL,
163 + .length = 0,
164 + .status = RRDPUSH_STATUS_BAD_HANDSHAKE,
165 + .version = STREAM_HANDSHAKE_ERROR_BAD_HANDSHAKE,
166 + .dynamic = false,
167 + .error = "remote node response is not understood, is it Netdata?",
168 + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE,
169 + .postpone_reconnect_seconds = 1 * 60, // 1 minute
170 + .priority = NDLP_ERR,
171 + }
172 +};
173 +
174 +static inline bool rrdpush_sender_validate_response(RRDHOST *host, struct sender_state *s, char *http, size_t http_length) {
175 + int32_t version = STREAM_HANDSHAKE_ERROR_BAD_HANDSHAKE;
176 +
177 + int i;
178 + for(i = 0; stream_responses[i].response ; i++) {
179 + if(stream_responses[i].dynamic &&
180 + http_length > stream_responses[i].length && http_length < (stream_responses[i].length + 30) &&
181 + strncmp(http, stream_responses[i].response, stream_responses[i].length) == 0) {
182 +
183 + version = str2i(&http[stream_responses[i].length]);
184 + break;
185 + }
186 + else if(http_length == stream_responses[i].length && strcmp(http, stream_responses[i].response) == 0) {
187 + version = stream_responses[i].version;
188 +
189 + break;
190 + }
191 + }
192 +
193 + if(version >= STREAM_HANDSHAKE_OK_V1) {
194 + host->destination->reason = version;
195 + host->destination->postpone_reconnection_until = now_realtime_sec() + s->reconnect_delay;
196 + s->capabilities = convert_stream_version_to_capabilities(version, host, true);
197 + return true;
198 + }
199 +
200 + ND_LOG_FIELD_PRIORITY priority = stream_responses[i].priority;
201 + const char *error = stream_responses[i].error;
202 + const char *status = stream_responses[i].status;
203 + int worker_job_id = stream_responses[i].worker_job_id;
204 + int delay = stream_responses[i].postpone_reconnect_seconds;
205 +
206 + worker_is_busy(worker_job_id);
207 + rrdpush_sender_thread_close_socket(host);
208 + host->destination->reason = version;
209 + host->destination->postpone_reconnection_until = now_realtime_sec() + delay;
210 +
211 + ND_LOG_STACK lgs[] = {
212 + ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, status),
213 + ND_LOG_FIELD_END(),
214 + };
215 + ND_LOG_STACK_PUSH(lgs);
216 +
217 + char buf[RFC3339_MAX_LENGTH];
218 + rfc3339_datetime_ut(buf, sizeof(buf), host->destination->postpone_reconnection_until * USEC_PER_SEC, 0, false);
219 +
220 + nd_log(NDLS_DAEMON, priority,
221 + "STREAM %s [send to %s]: %s - will retry in %d secs, at %s",
222 + rrdhost_hostname(host), s->connected_to, error, delay, buf);
223 +
224 + return false;
225 +}
226 +
227 +unsigned char alpn_proto_list[] = {
228 + 18, 'n', 'e', 't', 'd', 'a', 't', 'a', '_', 's', 't', 'r', 'e', 'a', 'm', '/', '2', '.', '0',
229 + 8, 'h', 't', 't', 'p', '/', '1', '.', '1'
230 +};
231 +
232 +#define CONN_UPGRADE_VAL "upgrade"
233 +
234 +static bool rrdpush_sender_connect_ssl(struct sender_state *s __maybe_unused) {
235 + RRDHOST *host = s->host;
236 + bool ssl_required = host->destination && host->destination->ssl;
237 +
238 + netdata_ssl_close(&host->sender->ssl);
239 +
240 + if(!ssl_required)
241 + return true;
242 +
243 + if (netdata_ssl_open_ext(&host->sender->ssl, netdata_ssl_streaming_sender_ctx, s->rrdpush_sender_socket, alpn_proto_list, sizeof(alpn_proto_list))) {
244 + if(!netdata_ssl_connect(&host->sender->ssl)) {
245 + // couldn't connect
246 +
247 + ND_LOG_STACK lgs[] = {
248 + ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_SSL_ERROR),
249 + ND_LOG_FIELD_END(),
250 + };
251 + ND_LOG_STACK_PUSH(lgs);
252 +
253 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR);
254 + rrdpush_sender_thread_close_socket(host);
255 + host->destination->reason = STREAM_HANDSHAKE_ERROR_SSL_ERROR;
256 + host->destination->postpone_reconnection_until = now_realtime_sec() + 5 * 60;
257 + return false;
258 + }
259 +
260 + if (netdata_ssl_validate_certificate_sender &&
261 + security_test_certificate(host->sender->ssl.conn)) {
262 + // certificate is not valid
263 +
264 + ND_LOG_STACK lgs[] = {
265 + ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_INVALID_SSL_CERTIFICATE),
266 + ND_LOG_FIELD_END(),
267 + };
268 + ND_LOG_STACK_PUSH(lgs);
269 +
270 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR);
271 + netdata_log_error("SSL: closing the stream connection, because the server SSL certificate is not valid.");
272 + rrdpush_sender_thread_close_socket(host);
273 + host->destination->reason = STREAM_HANDSHAKE_ERROR_INVALID_CERTIFICATE;
274 + host->destination->postpone_reconnection_until = now_realtime_sec() + 5 * 60;
275 + return false;
276 + }
277 +
278 + return true;
279 + }
280 +
281 + ND_LOG_STACK lgs[] = {
282 + ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_CANT_ESTABLISH_SSL_CONNECTION),
283 + ND_LOG_FIELD_END(),
284 + };
285 + ND_LOG_STACK_PUSH(lgs);
286 +
287 + netdata_log_error("SSL: failed to establish connection.");
288 + return false;
289 +}
290 +
291 +static int rrdpush_http_upgrade_prelude(RRDHOST *host, struct sender_state *s) {
292 +
293 + char http[HTTP_HEADER_SIZE + 1];
294 + snprintfz(http, HTTP_HEADER_SIZE,
295 + "GET " NETDATA_STREAM_URL HTTP_1_1 HTTP_ENDL
296 + "Upgrade: " NETDATA_STREAM_PROTO_NAME HTTP_ENDL
297 + "Connection: Upgrade"
298 + HTTP_HDR_END);
299 +
300 + ssize_t bytes = send_timeout(
301 + &host->sender->ssl,
302 + s->rrdpush_sender_socket,
303 + http,
304 + strlen(http),
305 + 0,
306 + 1000);
307 +
308 + bytes = recv_timeout(
309 + &host->sender->ssl,
310 + s->rrdpush_sender_socket,
311 + http,
312 + HTTP_HEADER_SIZE,
313 + 0,
314 + 1000);
315 +
316 + if (bytes <= 0) {
317 + error_report("Error reading from remote");
318 + return 1;
319 + }
320 +
321 + rbuf_t buf = rbuf_create(bytes);
322 + rbuf_push(buf, http, bytes);
323 +
324 + http_parse_ctx ctx;
325 + http_parse_ctx_create(&ctx, HTTP_PARSE_INITIAL);
326 + ctx.flags |= HTTP_PARSE_FLAG_DONT_WAIT_FOR_CONTENT;
327 +
328 + int rc;
329 + // while((rc = parse_http_response(buf, &ctx)) == HTTP_PARSE_NEED_MORE_DATA);
330 + rc = parse_http_response(buf, &ctx);
331 +
332 + if (rc != HTTP_PARSE_SUCCESS) {
333 + error_report("Failed to parse HTTP response sent. (%d)", rc);
334 + goto err_cleanup;
335 + }
336 + if (ctx.http_code == HTTP_RESP_MOVED_PERM) {
337 + const char *hdr = get_http_header_by_name(&ctx, "location");
338 + if (hdr)
339 + error_report("HTTP response is %d Moved Permanently (location: \"%s\") instead of expected %d Switching Protocols.", ctx.http_code, hdr, HTTP_RESP_SWITCH_PROTO);
340 + else
341 + error_report("HTTP response is %d instead of expected %d Switching Protocols.", ctx.http_code, HTTP_RESP_SWITCH_PROTO);
342 + goto err_cleanup;
343 + }
344 + if (ctx.http_code == HTTP_RESP_NOT_FOUND) {
345 + error_report("HTTP response is %d instead of expected %d Switching Protocols. Parent version too old.", ctx.http_code, HTTP_RESP_SWITCH_PROTO);
346 + // TODO set some flag here that will signify parent is older version
347 + // and to try connection without rrdpush_http_upgrade_prelude next time
348 + goto err_cleanup;
349 + }
350 + if (ctx.http_code != HTTP_RESP_SWITCH_PROTO) {
351 + error_report("HTTP response is %d instead of expected %d Switching Protocols", ctx.http_code, HTTP_RESP_SWITCH_PROTO);
352 + goto err_cleanup;
353 + }
354 +
355 + const char *hdr = get_http_header_by_name(&ctx, "connection");
356 + if (!hdr) {
357 + error_report("Missing \"connection\" header in reply");
358 + goto err_cleanup;
359 + }
360 + if (strncmp(hdr, CONN_UPGRADE_VAL, strlen(CONN_UPGRADE_VAL))) {
361 + error_report("Expected \"connection: " CONN_UPGRADE_VAL "\"");
362 + goto err_cleanup;
363 + }
364 +
365 + hdr = get_http_header_by_name(&ctx, "upgrade");
366 + if (!hdr) {
367 + error_report("Missing \"upgrade\" header in reply");
368 + goto err_cleanup;
369 + }
370 + if (strncmp(hdr, NETDATA_STREAM_PROTO_NAME, strlen(NETDATA_STREAM_PROTO_NAME))) {
371 + error_report("Expected \"upgrade: " NETDATA_STREAM_PROTO_NAME "\"");
372 + goto err_cleanup;
373 + }
374 +
375 + netdata_log_debug(D_STREAM, "Stream sender upgrade to \"" NETDATA_STREAM_PROTO_NAME "\" successful");
376 + rbuf_free(buf);
377 + http_parse_ctx_destroy(&ctx);
378 + return 0;
379 +err_cleanup:
380 + rbuf_free(buf);
381 + http_parse_ctx_destroy(&ctx);
382 + return 1;
383 +}
384 +
385 +static bool rrdpush_sender_thread_connect_to_parent(RRDHOST *host, int default_port, int timeout, struct sender_state *s) {
386 +
387 + struct timeval tv = {
388 + .tv_sec = timeout,
389 + .tv_usec = 0
390 + };
391 +
392 + // make sure the socket is closed
393 + rrdpush_sender_thread_close_socket(host);
394 +
395 + s->rrdpush_sender_socket = connect_to_one_of_destinations(
396 + host
397 + , default_port
398 + , &tv
399 + , &s->reconnects_counter
400 + , s->connected_to
401 + , sizeof(s->connected_to)-1
402 + , &host->destination
403 + );
404 +
405 + if(unlikely(s->rrdpush_sender_socket == -1)) {
406 + // netdata_log_error("STREAM %s [send to %s]: could not connect to parent node at this time.", rrdhost_hostname(host), host->rrdpush_send_destination);
407 + return false;
408 + }
409 +
410 + // netdata_log_info("STREAM %s [send to %s]: initializing communication...", rrdhost_hostname(host), s->connected_to);
411 +
412 + // reset our capabilities to default
413 + s->capabilities = stream_our_capabilities(host, true);
414 +
415 + /* TODO: During the implementation of #7265 switch the set of variables to HOST_* and CONTAINER_* if the
416 + version negotiation resulted in a high enough version.
417 + */
418 + stream_encoded_t se;
419 + rrdpush_encode_variable(&se, host);
420 +
421 + host->sender->hops = host->system_info->hops + 1;
422 +
423 + char http[HTTP_HEADER_SIZE + 1];
424 + int eol = snprintfz(http, HTTP_HEADER_SIZE,
425 + "STREAM "
426 + "key=%s"
427 + "&hostname=%s"
428 + "&registry_hostname=%s"
429 + "&machine_guid=%s"
430 + "&update_every=%d"
431 + "&os=%s"
432 + "&timezone=%s"
433 + "&abbrev_timezone=%s"
434 + "&utc_offset=%d"
435 + "&hops=%d"
436 + "&ml_capable=%d"
437 + "&ml_enabled=%d"
438 + "&mc_version=%d"
439 + "&ver=%u"
440 + "&NETDATA_INSTANCE_CLOUD_TYPE=%s"
441 + "&NETDATA_INSTANCE_CLOUD_INSTANCE_TYPE=%s"
442 + "&NETDATA_INSTANCE_CLOUD_INSTANCE_REGION=%s"
443 + "&NETDATA_SYSTEM_OS_NAME=%s"
444 + "&NETDATA_SYSTEM_OS_ID=%s"
445 + "&NETDATA_SYSTEM_OS_ID_LIKE=%s"
446 + "&NETDATA_SYSTEM_OS_VERSION=%s"
447 + "&NETDATA_SYSTEM_OS_VERSION_ID=%s"
448 + "&NETDATA_SYSTEM_OS_DETECTION=%s"
449 + "&NETDATA_HOST_IS_K8S_NODE=%s"
450 + "&NETDATA_SYSTEM_KERNEL_NAME=%s"
451 + "&NETDATA_SYSTEM_KERNEL_VERSION=%s"
452 + "&NETDATA_SYSTEM_ARCHITECTURE=%s"
453 + "&NETDATA_SYSTEM_VIRTUALIZATION=%s"
454 + "&NETDATA_SYSTEM_VIRT_DETECTION=%s"
455 + "&NETDATA_SYSTEM_CONTAINER=%s"
456 + "&NETDATA_SYSTEM_CONTAINER_DETECTION=%s"
457 + "&NETDATA_CONTAINER_OS_NAME=%s"
458 + "&NETDATA_CONTAINER_OS_ID=%s"
459 + "&NETDATA_CONTAINER_OS_ID_LIKE=%s"
460 + "&NETDATA_CONTAINER_OS_VERSION=%s"
461 + "&NETDATA_CONTAINER_OS_VERSION_ID=%s"
462 + "&NETDATA_CONTAINER_OS_DETECTION=%s"
463 + "&NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT=%s"
464 + "&NETDATA_SYSTEM_CPU_FREQ=%s"
465 + "&NETDATA_SYSTEM_TOTAL_RAM=%s"
466 + "&NETDATA_SYSTEM_TOTAL_DISK_SIZE=%s"
467 + "&NETDATA_PROTOCOL_VERSION=%s"
468 + HTTP_1_1 HTTP_ENDL
469 + "User-Agent: %s/%s\r\n"
470 + "Accept: */*\r\n\r\n"
471 + , host->rrdpush.send.api_key
472 + , rrdhost_hostname(host)
473 + , rrdhost_registry_hostname(host)
474 + , host->machine_guid
475 + , default_rrd_update_every
476 + , rrdhost_os(host)
477 + , rrdhost_timezone(host)
478 + , rrdhost_abbrev_timezone(host)
479 + , host->utc_offset
480 + , host->sender->hops
481 + , host->system_info->ml_capable
482 + , host->system_info->ml_enabled
483 + , host->system_info->mc_version
484 + , s->capabilities
485 + , (host->system_info->cloud_provider_type) ? host->system_info->cloud_provider_type : ""
486 + , (host->system_info->cloud_instance_type) ? host->system_info->cloud_instance_type : ""
487 + , (host->system_info->cloud_instance_region) ? host->system_info->cloud_instance_region : ""
488 + , se.os_name
489 + , se.os_id
490 + , (host->system_info->host_os_id_like) ? host->system_info->host_os_id_like : ""
491 + , se.os_version
492 + , (host->system_info->host_os_version_id) ? host->system_info->host_os_version_id : ""
493 + , (host->system_info->host_os_detection) ? host->system_info->host_os_detection : ""
494 + , (host->system_info->is_k8s_node) ? host->system_info->is_k8s_node : ""
495 + , se.kernel_name
496 + , se.kernel_version
497 + , (host->system_info->architecture) ? host->system_info->architecture : ""
498 + , (host->system_info->virtualization) ? host->system_info->virtualization : ""
499 + , (host->system_info->virt_detection) ? host->system_info->virt_detection : ""
500 + , (host->system_info->container) ? host->system_info->container : ""
501 + , (host->system_info->container_detection) ? host->system_info->container_detection : ""
502 + , (host->system_info->container_os_name) ? host->system_info->container_os_name : ""
503 + , (host->system_info->container_os_id) ? host->system_info->container_os_id : ""
504 + , (host->system_info->container_os_id_like) ? host->system_info->container_os_id_like : ""
505 + , (host->system_info->container_os_version) ? host->system_info->container_os_version : ""
506 + , (host->system_info->container_os_version_id) ? host->system_info->container_os_version_id : ""
507 + , (host->system_info->container_os_detection) ? host->system_info->container_os_detection : ""
508 + , (host->system_info->host_cores) ? host->system_info->host_cores : ""
509 + , (host->system_info->host_cpu_freq) ? host->system_info->host_cpu_freq : ""
510 + , (host->system_info->host_ram_total) ? host->system_info->host_ram_total : ""
511 + , (host->system_info->host_disk_space) ? host->system_info->host_disk_space : ""
512 + , STREAMING_PROTOCOL_VERSION
513 + , rrdhost_program_name(host)
514 + , rrdhost_program_version(host)
515 + );
516 + http[eol] = 0x00;
517 + rrdpush_clean_encoded(&se);
518 +
519 + if(!rrdpush_sender_connect_ssl(s))
520 + return false;
521 +
522 + if (s->parent_using_h2o && rrdpush_http_upgrade_prelude(host, s)) {
523 + ND_LOG_STACK lgs[] = {
524 + ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_CANT_UPGRADE_CONNECTION),
525 + ND_LOG_FIELD_END(),
526 + };
527 + ND_LOG_STACK_PUSH(lgs);
528 +
529 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_CANT_UPGRADE_CONNECTION);
530 + rrdpush_sender_thread_close_socket(host);
531 + host->destination->reason = STREAM_HANDSHAKE_ERROR_HTTP_UPGRADE;
532 + host->destination->postpone_reconnection_until = now_realtime_sec() + 1 * 60;
533 + return false;
534 + }
535 +
536 + ssize_t len = (ssize_t)strlen(http);
537 + ssize_t bytes = send_timeout(
538 + &host->sender->ssl,
539 + s->rrdpush_sender_socket,
540 + http,
541 + len,
542 + 0,
543 + timeout);
544 +
545 + if(bytes <= 0) { // timeout is 0
546 + ND_LOG_STACK lgs[] = {
547 + ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_TIMEOUT),
548 + ND_LOG_FIELD_END(),
549 + };
550 + ND_LOG_STACK_PUSH(lgs);
551 +
552 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT);
553 + rrdpush_sender_thread_close_socket(host);
554 +
555 + nd_log(NDLS_DAEMON, NDLP_ERR,
556 + "STREAM %s [send to %s]: failed to send HTTP header to remote netdata.",
557 + rrdhost_hostname(host), s->connected_to);
558 +
559 + host->destination->reason = STREAM_HANDSHAKE_ERROR_SEND_TIMEOUT;
560 + host->destination->postpone_reconnection_until = now_realtime_sec() + 1 * 60;
561 + return false;
562 + }
563 +
564 + bytes = recv_timeout(
565 + &host->sender->ssl,
566 + s->rrdpush_sender_socket,
567 + http,
568 + HTTP_HEADER_SIZE,
569 + 0,
570 + timeout);
571 +
572 + if(bytes <= 0) { // timeout is 0
573 + ND_LOG_STACK lgs[] = {
574 + ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_TIMEOUT),
575 + ND_LOG_FIELD_END(),
576 + };
577 + ND_LOG_STACK_PUSH(lgs);
578 +
579 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT);
580 + rrdpush_sender_thread_close_socket(host);
581 +
582 + nd_log(NDLS_DAEMON, NDLP_ERR,
583 + "STREAM %s [send to %s]: remote netdata does not respond.",
584 + rrdhost_hostname(host), s->connected_to);
585 +
586 + host->destination->reason = STREAM_HANDSHAKE_ERROR_RECEIVE_TIMEOUT;
587 + host->destination->postpone_reconnection_until = now_realtime_sec() + 30;
588 + return false;
589 + }
590 +
591 + if(sock_setnonblock(s->rrdpush_sender_socket) < 0)
592 + nd_log(NDLS_DAEMON, NDLP_WARNING,
593 + "STREAM %s [send to %s]: cannot set non-blocking mode for socket.",
594 + rrdhost_hostname(host), s->connected_to);
595 + sock_setcloexec(s->rrdpush_sender_socket);
596 +
597 + if(sock_enlarge_out(s->rrdpush_sender_socket) < 0)
598 + nd_log(NDLS_DAEMON, NDLP_WARNING,
599 + "STREAM %s [send to %s]: cannot enlarge the socket buffer.",
600 + rrdhost_hostname(host), s->connected_to);
601 +
602 + http[bytes] = '\0';
603 + if(!rrdpush_sender_validate_response(host, s, http, bytes))
604 + return false;
605 +
606 + rrdpush_compression_initialize(s);
607 +
608 + log_sender_capabilities(s);
609 +
610 + ND_LOG_STACK lgs[] = {
611 + ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, RRDPUSH_STATUS_CONNECTED),
612 + ND_LOG_FIELD_END(),
613 + };
614 + ND_LOG_STACK_PUSH(lgs);
615 +
616 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
617 + "STREAM %s: connected to %s...",
618 + rrdhost_hostname(host), s->connected_to);
619 +
620 + return true;
621 +}
622 +
623 +bool attempt_to_connect(struct sender_state *state) {
624 + ND_LOG_STACK lgs[] = {
625 + ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
626 + ND_LOG_FIELD_END(),
627 + };
628 + ND_LOG_STACK_PUSH(lgs);
629 +
630 + state->send_attempts = 0;
631 +
632 + // reset the bytes we have sent for this session
633 + state->sent_bytes_on_this_connection = 0;
634 + memset(state->sent_bytes_on_this_connection_per_type, 0, sizeof(state->sent_bytes_on_this_connection_per_type));
635 +
636 + if(rrdpush_sender_thread_connect_to_parent(state->host, state->default_port, state->timeout, state)) {
637 + // reset the buffer, to properly send charts and metrics
638 + rrdpush_sender_on_connect(state->host);
639 +
640 + // send from the beginning
641 + state->begin = 0;
642 +
643 + // make sure the next reconnection will be immediate
644 + state->not_connected_loops = 0;
645 +
646 + // let the data collection threads know we are ready
647 + rrdhost_flag_set(state->host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED);
648 +
649 + rrdpush_sender_after_connect(state->host);
650 +
651 + return true;
652 + }
653 +
654 + // we couldn't connect
655 +
656 + // increase the failed connections counter
657 + state->not_connected_loops++;
658 +
659 + // slow re-connection on repeating errors
660 + usec_t now_ut = now_monotonic_usec();
661 + usec_t end_ut = now_ut + USEC_PER_SEC * state->reconnect_delay;
662 + while(now_ut < end_ut) {
663 + if(nd_thread_signaled_to_cancel())
664 + return false;
665 +
666 + sleep_usec(100 * USEC_PER_MS); // seconds
667 + now_ut = now_monotonic_usec();
668 + }
669 +
670 + return false;
671 +}
src/streaming/sender_execute.c new
+294
@@ -0,0 +1,294 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "sender_internals.h"
4 +
5 +struct inflight_stream_function {
6 + struct sender_state *sender;
7 + STRING *transaction;
8 + usec_t received_ut;
9 +};
10 +
11 +static void stream_execute_function_callback(BUFFER *func_wb, int code, void *data) {
12 + struct inflight_stream_function *tmp = data;
13 + struct sender_state *s = tmp->sender;
14 +
15 + if(rrdhost_can_send_definitions_to_parent(s->host)) {
16 + BUFFER *wb = sender_start(s);
17 +
18 + pluginsd_function_result_begin_to_buffer(wb
19 + , string2str(tmp->transaction)
20 + , code
21 + , content_type_id2string(func_wb->content_type)
22 + , func_wb->expires);
23 +
24 + buffer_fast_strcat(wb, buffer_tostring(func_wb), buffer_strlen(func_wb));
25 + pluginsd_function_result_end_to_buffer(wb);
26 +
27 + sender_commit(s, wb, STREAM_TRAFFIC_TYPE_FUNCTIONS);
28 + sender_thread_buffer_free();
29 +
30 + internal_error(true, "STREAM %s [send to %s] FUNCTION transaction %s sending back response (%zu bytes, %"PRIu64" usec).",
31 + rrdhost_hostname(s->host), s->connected_to,
32 + string2str(tmp->transaction),
33 + buffer_strlen(func_wb),
34 + now_realtime_usec() - tmp->received_ut);
35 + }
36 +
37 + string_freez(tmp->transaction);
38 + buffer_free(func_wb);
39 + freez(tmp);
40 +}
41 +
42 +static void stream_execute_function_progress_callback(void *data, size_t done, size_t all) {
43 + struct inflight_stream_function *tmp = data;
44 + struct sender_state *s = tmp->sender;
45 +
46 + if(rrdhost_can_send_definitions_to_parent(s->host)) {
47 + BUFFER *wb = sender_start(s);
48 +
49 + buffer_sprintf(wb, PLUGINSD_KEYWORD_FUNCTION_PROGRESS " '%s' %zu %zu\n",
50 + string2str(tmp->transaction), done, all);
51 +
52 + sender_commit(s, wb, STREAM_TRAFFIC_TYPE_FUNCTIONS);
53 + }
54 +}
55 +
56 +static void execute_commands_function(struct sender_state *s, const char *command, const char *transaction, const char *timeout_s, const char *function, BUFFER *payload, const char *access, const char *source) {
57 + worker_is_busy(WORKER_SENDER_JOB_FUNCTION_REQUEST);
58 + nd_log(NDLS_ACCESS, NDLP_INFO, NULL);
59 +
60 + if(!transaction || !*transaction || !timeout_s || !*timeout_s || !function || !*function) {
61 + netdata_log_error("STREAM %s [send to %s] %s execution command is incomplete (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.",
62 + rrdhost_hostname(s->host), s->connected_to,
63 + command,
64 + transaction?transaction:"(unset)",
65 + timeout_s?timeout_s:"(unset)",
66 + function?function:"(unset)");
67 + }
68 + else {
69 + int timeout = str2i(timeout_s);
70 + if(timeout <= 0) timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT;
71 +
72 + struct inflight_stream_function *tmp = callocz(1, sizeof(struct inflight_stream_function));
73 + tmp->received_ut = now_realtime_usec();
74 + tmp->sender = s;
75 + tmp->transaction = string_strdupz(transaction);
76 + BUFFER *wb = buffer_create(1024, &netdata_buffers_statistics.buffers_functions);
77 +
78 + int code = rrd_function_run(s->host, wb, timeout,
79 + http_access_from_hex_mapping_old_roles(access), function, false, transaction,
80 + stream_execute_function_callback, tmp,
81 + stream_has_capability(s, STREAM_CAP_PROGRESS) ? stream_execute_function_progress_callback : NULL,
82 + stream_has_capability(s, STREAM_CAP_PROGRESS) ? tmp : NULL,
83 + NULL, NULL, payload, source, true);
84 +
85 + if(code != HTTP_RESP_OK) {
86 + if (!buffer_strlen(wb))
87 + rrd_call_function_error(wb, "Failed to route request to collector", code);
88 + }
89 + }
90 +}
91 +
92 +struct deferred_function {
93 + const char *transaction;
94 + const char *timeout_s;
95 + const char *function;
96 + const char *access;
97 + const char *source;
98 +};
99 +
100 +static void execute_deferred_function(struct sender_state *s, void *data) {
101 + struct deferred_function *dfd = data;
102 + execute_commands_function(s, s->defer.end_keyword,
103 + dfd->transaction, dfd->timeout_s,
104 + dfd->function, s->defer.payload,
105 + dfd->access, dfd->source);
106 +}
107 +
108 +static void execute_deferred_json(struct sender_state *s, void *data) {
109 + const char *keyword = data;
110 +
111 + if(strcmp(keyword, PLUGINSD_KEYWORD_STREAM_PATH) == 0)
112 + stream_path_set_from_json(s->host, buffer_tostring(s->defer.payload), true);
113 + else
114 + nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM: unknown JSON keyword '%s' with payload: %s", keyword, buffer_tostring(s->defer.payload));
115 +}
116 +
117 +static void cleanup_deferred_json(struct sender_state *s __maybe_unused, void *data) {
118 + const char *keyword = data;
119 + freez((void *)keyword);
120 +}
121 +
122 +static void cleanup_deferred_function(struct sender_state *s __maybe_unused, void *data) {
123 + struct deferred_function *dfd = data;
124 + freez((void *)dfd->transaction);
125 + freez((void *)dfd->timeout_s);
126 + freez((void *)dfd->function);
127 + freez((void *)dfd->access);
128 + freez((void *)dfd->source);
129 + freez(dfd);
130 +}
131 +
132 +static void cleanup_deferred_data(struct sender_state *s) {
133 + if(s->defer.cleanup)
134 + s->defer.cleanup(s, s->defer.action_data);
135 +
136 + buffer_free(s->defer.payload);
137 + s->defer.payload = NULL;
138 + s->defer.end_keyword = NULL;
139 + s->defer.action = NULL;
140 + s->defer.cleanup = NULL;
141 + s->defer.action_data = NULL;
142 +}
143 +
144 +void rrdpush_sender_execute_commands_cleanup(struct sender_state *s) {
145 + cleanup_deferred_data(s);
146 +}
147 +
148 +// This is just a placeholder until the gap filling state machine is inserted
149 +void rrdpush_sender_execute_commands(struct sender_state *s) {
150 + worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
151 +
152 + ND_LOG_STACK lgs[] = {
153 + ND_LOG_FIELD_CB(NDF_REQUEST, line_splitter_reconstruct_line, &s->line),
154 + ND_LOG_FIELD_END(),
155 + };
156 + ND_LOG_STACK_PUSH(lgs);
157 +
158 + char *start = s->read_buffer, *end = &s->read_buffer[s->read_len], *newline;
159 + *end = '\0';
160 + for( ; start < end ; start = newline + 1) {
161 + newline = strchr(start, '\n');
162 +
163 + if(!newline) {
164 + if(s->defer.end_keyword) {
165 + buffer_strcat(s->defer.payload, start);
166 + start = end;
167 + }
168 + break;
169 + }
170 +
171 + *newline = '\0';
172 + s->line.count++;
173 +
174 + if(s->defer.end_keyword) {
175 + if(strcmp(start, s->defer.end_keyword) == 0) {
176 + s->defer.action(s, s->defer.action_data);
177 + cleanup_deferred_data(s);
178 + }
179 + else {
180 + buffer_strcat(s->defer.payload, start);
181 + buffer_putc(s->defer.payload, '\n');
182 + }
183 +
184 + continue;
185 + }
186 +
187 + s->line.num_words = quoted_strings_splitter_pluginsd(start, s->line.words, PLUGINSD_MAX_WORDS);
188 + const char *command = get_word(s->line.words, s->line.num_words, 0);
189 +
190 + if(command && strcmp(command, PLUGINSD_CALL_FUNCTION) == 0) {
191 + char *transaction = get_word(s->line.words, s->line.num_words, 1);
192 + char *timeout_s = get_word(s->line.words, s->line.num_words, 2);
193 + char *function = get_word(s->line.words, s->line.num_words, 3);
194 + char *access = get_word(s->line.words, s->line.num_words, 4);
195 + char *source = get_word(s->line.words, s->line.num_words, 5);
196 +
197 + execute_commands_function(s, command, transaction, timeout_s, function, NULL, access, source);
198 + }
199 + else if(command && strcmp(command, PLUGINSD_CALL_FUNCTION_PAYLOAD_BEGIN) == 0) {
200 + char *transaction = get_word(s->line.words, s->line.num_words, 1);
201 + char *timeout_s = get_word(s->line.words, s->line.num_words, 2);
202 + char *function = get_word(s->line.words, s->line.num_words, 3);
203 + char *access = get_word(s->line.words, s->line.num_words, 4);
204 + char *source = get_word(s->line.words, s->line.num_words, 5);
205 + char *content_type = get_word(s->line.words, s->line.num_words, 6);
206 +
207 + s->defer.end_keyword = PLUGINSD_CALL_FUNCTION_PAYLOAD_END;
208 + s->defer.payload = buffer_create(0, NULL);
209 + s->defer.payload->content_type = content_type_string2id(content_type);
210 + s->defer.action = execute_deferred_function;
211 + s->defer.cleanup = cleanup_deferred_function;
212 +
213 + struct deferred_function *dfd = callocz(1, sizeof(*dfd));
214 + dfd->transaction = strdupz(transaction ? transaction : "");
215 + dfd->timeout_s = strdupz(timeout_s ? timeout_s : "");
216 + dfd->function = strdupz(function ? function : "");
217 + dfd->access = strdupz(access ? access : "");
218 + dfd->source = strdupz(source ? source : "");
219 +
220 + s->defer.action_data = dfd;
221 + }
222 + else if(command && strcmp(command, PLUGINSD_CALL_FUNCTION_CANCEL) == 0) {
223 + worker_is_busy(WORKER_SENDER_JOB_FUNCTION_REQUEST);
224 + nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
225 +
226 + char *transaction = get_word(s->line.words, s->line.num_words, 1);
227 + if(transaction && *transaction)
228 + rrd_function_cancel(transaction);
229 + }
230 + else if(command && strcmp(command, PLUGINSD_CALL_FUNCTION_PROGRESS) == 0) {
231 + worker_is_busy(WORKER_SENDER_JOB_FUNCTION_REQUEST);
232 + nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
233 +
234 + char *transaction = get_word(s->line.words, s->line.num_words, 1);
235 + if(transaction && *transaction)
236 + rrd_function_progress(transaction);
237 + }
238 + else if (command && strcmp(command, PLUGINSD_KEYWORD_REPLAY_CHART) == 0) {
239 + worker_is_busy(WORKER_SENDER_JOB_REPLAY_REQUEST);
240 + nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
241 +
242 + const char *chart_id = get_word(s->line.words, s->line.num_words, 1);
243 + const char *start_streaming = get_word(s->line.words, s->line.num_words, 2);
244 + const char *after = get_word(s->line.words, s->line.num_words, 3);
245 + const char *before = get_word(s->line.words, s->line.num_words, 4);
246 +
247 + if (!chart_id || !start_streaming || !after || !before) {
248 + netdata_log_error("STREAM %s [send to %s] %s command is incomplete"
249 + " (chart=%s, start_streaming=%s, after=%s, before=%s)",
250 + rrdhost_hostname(s->host), s->connected_to,
251 + command,
252 + chart_id ? chart_id : "(unset)",
253 + start_streaming ? start_streaming : "(unset)",
254 + after ? after : "(unset)",
255 + before ? before : "(unset)");
256 + }
257 + else {
258 + replication_add_request(s, chart_id,
259 + strtoll(after, NULL, 0),
260 + strtoll(before, NULL, 0),
261 + !strcmp(start_streaming, "true")
262 + );
263 + }
264 + }
265 + else if(command && strcmp(command, PLUGINSD_KEYWORD_NODE_ID) == 0) {
266 + rrdpush_sender_get_node_and_claim_id_from_parent(s);
267 + }
268 + else if(command && strcmp(command, PLUGINSD_KEYWORD_JSON) == 0) {
269 + char *keyword = get_word(s->line.words, s->line.num_words, 1);
270 +
271 + s->defer.end_keyword = PLUGINSD_KEYWORD_JSON_END;
272 + s->defer.payload = buffer_create(0, NULL);
273 + s->defer.action = execute_deferred_json;
274 + s->defer.cleanup = cleanup_deferred_json;
275 + s->defer.action_data = strdupz(keyword);
276 + }
277 + else {
278 + netdata_log_error("STREAM %s [send to %s] received unknown command over connection: %s",
279 + rrdhost_hostname(s->host), s->connected_to, s->line.words[0]?s->line.words[0]:"(unset)");
280 + }
281 +
282 + line_splitter_reset(&s->line);
283 + worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
284 + }
285 +
286 + if (start < end) {
287 + memmove(s->read_buffer, start, end-start);
288 + s->read_len = end - start;
289 + }
290 + else {
291 + s->read_buffer[0] = '\0';
292 + s->read_len = 0;
293 + }
294 +}
src/streaming/sender_internals.h new
+54
@@ -0,0 +1,54 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_SENDER_INTERNALS_H
4 +#define NETDATA_SENDER_INTERNALS_H
5 +
6 +#include "rrdpush.h"
7 +#include "common.h"
8 +#include "aclk/https_client.h"
9 +
10 +#define WORKER_SENDER_JOB_CONNECT 0
11 +#define WORKER_SENDER_JOB_PIPE_READ 1
12 +#define WORKER_SENDER_JOB_SOCKET_RECEIVE 2
13 +#define WORKER_SENDER_JOB_EXECUTE 3
14 +#define WORKER_SENDER_JOB_SOCKET_SEND 4
15 +#define WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE 5
16 +#define WORKER_SENDER_JOB_DISCONNECT_OVERFLOW 6
17 +#define WORKER_SENDER_JOB_DISCONNECT_TIMEOUT 7
18 +#define WORKER_SENDER_JOB_DISCONNECT_POLL_ERROR 8
19 +#define WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR 9
20 +#define WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR 10
21 +#define WORKER_SENDER_JOB_DISCONNECT_PARENT_CLOSED 11
22 +#define WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR 12
23 +#define WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR 13
24 +#define WORKER_SENDER_JOB_DISCONNECT_NO_COMPRESSION 14
25 +#define WORKER_SENDER_JOB_BUFFER_RATIO 15
26 +#define WORKER_SENDER_JOB_BYTES_RECEIVED 16
27 +#define WORKER_SENDER_JOB_BYTES_SENT 17
28 +#define WORKER_SENDER_JOB_BYTES_COMPRESSED 18
29 +#define WORKER_SENDER_JOB_BYTES_UNCOMPRESSED 19
30 +#define WORKER_SENDER_JOB_BYTES_COMPRESSION_RATIO 20
31 +#define WORKER_SENDER_JOB_REPLAY_REQUEST 21
32 +#define WORKER_SENDER_JOB_FUNCTION_REQUEST 22
33 +#define WORKER_SENDER_JOB_REPLAY_DICT_SIZE 23
34 +#define WORKER_SENDER_JOB_DISCONNECT_CANT_UPGRADE_CONNECTION 24
35 +
36 +#if WORKER_UTILIZATION_MAX_JOB_TYPES < 25
37 +#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 25
38 +#endif
39 +
40 +extern struct config stream_config;
41 +extern char *netdata_ssl_ca_path;
42 +extern char *netdata_ssl_ca_file;
43 +
44 +bool attempt_to_connect(struct sender_state *state);
45 +void rrdpush_sender_on_connect(RRDHOST *host);
46 +void rrdpush_sender_after_connect(RRDHOST *host);
47 +void rrdpush_sender_thread_close_socket(RRDHOST *host);
48 +
49 +void rrdpush_sender_disconnect_and_cleanup(RRDHOST *host);
50 +
51 +void rrdpush_sender_execute_commands_cleanup(struct sender_state *s);
52 +void rrdpush_sender_execute_commands(struct sender_state *s);
53 +
54 +#endif //NETDATA_SENDER_INTERNALS_H
src/streaming/stream_capabilities.c new
+169
@@ -0,0 +1,169 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "rrdpush.h"
4 +
5 +static STREAM_CAPABILITIES globally_disabled_capabilities = STREAM_CAP_NONE;
6 +
7 +static struct {
8 + STREAM_CAPABILITIES cap;
9 + const char *str;
10 +} capability_names[] = {
11 + {STREAM_CAP_V1, "V1" },
12 + {STREAM_CAP_V2, "V2" },
13 + {STREAM_CAP_VN, "VN" },
14 + {STREAM_CAP_VCAPS, "VCAPS" },
15 + {STREAM_CAP_HLABELS, "HLABELS" },
16 + {STREAM_CAP_CLAIM, "CLAIM" },
17 + {STREAM_CAP_CLABELS, "CLABELS" },
18 + {STREAM_CAP_LZ4, "LZ4" },
19 + {STREAM_CAP_FUNCTIONS, "FUNCTIONS" },
20 + {STREAM_CAP_REPLICATION, "REPLICATION" },
21 + {STREAM_CAP_BINARY, "BINARY" },
22 + {STREAM_CAP_INTERPOLATED, "INTERPOLATED" },
23 + {STREAM_CAP_IEEE754, "IEEE754" },
24 + {STREAM_CAP_DATA_WITH_ML, "ML" },
25 + {STREAM_CAP_DYNCFG, "DYNCFG" },
26 + {STREAM_CAP_SLOTS, "SLOTS" },
27 + {STREAM_CAP_ZSTD, "ZSTD" },
28 + {STREAM_CAP_GZIP, "GZIP" },
29 + {STREAM_CAP_BROTLI, "BROTLI" },
30 + {STREAM_CAP_PROGRESS, "PROGRESS" },
31 + {STREAM_CAP_NODE_ID, "NODEID" },
32 + {STREAM_CAP_PATHS, "PATHS" },
33 + {0 , NULL },
34 +};
35 +
36 +STREAM_CAPABILITIES stream_capabilities_parse_one(const char *str) {
37 + if (!str || !*str)
38 + return STREAM_CAP_NONE;
39 +
40 + for (size_t i = 0; capability_names[i].str; i++) {
41 + if (strcmp(capability_names[i].str, str) == 0)
42 + return capability_names[i].cap;
43 + }
44 +
45 + return STREAM_CAP_NONE;
46 +}
47 +
48 +void stream_capabilities_to_string(BUFFER *wb, STREAM_CAPABILITIES caps) {
49 + for(size_t i = 0; capability_names[i].str ; i++) {
50 + if(caps & capability_names[i].cap) {
51 + buffer_strcat(wb, capability_names[i].str);
52 + buffer_strcat(wb, " ");
53 + }
54 + }
55 +}
56 +
57 +void stream_capabilities_to_json_array(BUFFER *wb, STREAM_CAPABILITIES caps, const char *key) {
58 + if(key)
59 + buffer_json_member_add_array(wb, key);
60 + else
61 + buffer_json_add_array_item_array(wb);
62 +
63 + for(size_t i = 0; capability_names[i].str ; i++) {
64 + if(caps & capability_names[i].cap)
65 + buffer_json_add_array_item_string(wb, capability_names[i].str);
66 + }
67 +
68 + buffer_json_array_close(wb);
69 +}
70 +
71 +void log_receiver_capabilities(struct receiver_state *rpt) {
72 + BUFFER *wb = buffer_create(100, NULL);
73 + stream_capabilities_to_string(wb, rpt->capabilities);
74 +
75 + nd_log_daemon(NDLP_INFO, "STREAM %s [receive from [%s]:%s]: established link with negotiated capabilities: %s",
76 + rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, buffer_tostring(wb));
77 +
78 + buffer_free(wb);
79 +}
80 +
81 +void log_sender_capabilities(struct sender_state *s) {
82 + BUFFER *wb = buffer_create(100, NULL);
83 + stream_capabilities_to_string(wb, s->capabilities);
84 +
85 + nd_log_daemon(NDLP_INFO, "STREAM %s [send to %s]: established link with negotiated capabilities: %s",
86 + rrdhost_hostname(s->host), s->connected_to, buffer_tostring(wb));
87 +
88 + buffer_free(wb);
89 +}
90 +
91 +STREAM_CAPABILITIES stream_our_capabilities(RRDHOST *host, bool sender) {
92 + STREAM_CAPABILITIES disabled_capabilities = globally_disabled_capabilities;
93 +
94 + if(host && sender) {
95 + // we have DATA_WITH_ML capability
96 + // we should remove the DATA_WITH_ML capability if our database does not have anomaly info
97 + // this can happen under these conditions: 1. we don't run ML, and 2. we don't receive ML
98 + spinlock_lock(&host->receiver_lock);
99 +
100 + if(!ml_host_running(host) && !stream_has_capability(host->receiver, STREAM_CAP_DATA_WITH_ML))
101 + disabled_capabilities |= STREAM_CAP_DATA_WITH_ML;
102 +
103 + spinlock_unlock(&host->receiver_lock);
104 +
105 + if(host->sender)
106 + disabled_capabilities |= host->sender->disabled_capabilities;
107 + }
108 +
109 + return (STREAM_CAP_V1 |
110 + STREAM_CAP_V2 |
111 + STREAM_CAP_VN |
112 + STREAM_CAP_VCAPS |
113 + STREAM_CAP_HLABELS |
114 + STREAM_CAP_CLAIM |
115 + STREAM_CAP_CLABELS |
116 + STREAM_CAP_FUNCTIONS |
117 + STREAM_CAP_REPLICATION |
118 + STREAM_CAP_BINARY |
119 + STREAM_CAP_INTERPOLATED |
120 + STREAM_CAP_SLOTS |
121 + STREAM_CAP_PROGRESS |
122 + STREAM_CAP_COMPRESSIONS_AVAILABLE |
123 + STREAM_CAP_DYNCFG |
124 + STREAM_CAP_NODE_ID |
125 + STREAM_CAP_PATHS |
126 + STREAM_CAP_IEEE754 |
127 + STREAM_CAP_DATA_WITH_ML |
128 + 0) & ~disabled_capabilities;
129 +}
130 +
131 +STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version, RRDHOST *host, bool sender) {
132 + STREAM_CAPABILITIES caps = 0;
133 +
134 + if(version <= 1) caps = STREAM_CAP_V1;
135 + else if(version < STREAM_OLD_VERSION_CLAIM) caps = STREAM_CAP_V2 | STREAM_CAP_HLABELS;
136 + else if(version <= STREAM_OLD_VERSION_CLAIM) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM;
137 + else if(version <= STREAM_OLD_VERSION_CLABELS) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM | STREAM_CAP_CLABELS;
138 + else if(version <= STREAM_OLD_VERSION_LZ4) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM | STREAM_CAP_CLABELS | STREAM_CAP_LZ4_AVAILABLE;
139 + else caps = version;
140 +
141 + if(caps & STREAM_CAP_VCAPS)
142 + caps &= ~(STREAM_CAP_V1|STREAM_CAP_V2|STREAM_CAP_VN);
143 +
144 + if(caps & STREAM_CAP_VN)
145 + caps &= ~(STREAM_CAP_V1|STREAM_CAP_V2);
146 +
147 + if(caps & STREAM_CAP_V2)
148 + caps &= ~(STREAM_CAP_V1);
149 +
150 + STREAM_CAPABILITIES common_caps = caps & stream_our_capabilities(host, sender);
151 +
152 + if(!(common_caps & STREAM_CAP_INTERPOLATED))
153 + // DATA WITH ML requires INTERPOLATED
154 + common_caps &= ~STREAM_CAP_DATA_WITH_ML;
155 +
156 + return common_caps;
157 +}
158 +
159 +int32_t stream_capabilities_to_vn(uint32_t caps) {
160 + if(caps & STREAM_CAP_LZ4) return STREAM_OLD_VERSION_LZ4;
161 + if(caps & STREAM_CAP_CLABELS) return STREAM_OLD_VERSION_CLABELS;
162 + return STREAM_OLD_VERSION_CLAIM; // if(caps & STREAM_CAP_CLAIM)
163 +}
164 +
165 +void check_local_streaming_capabilities(void) {
166 + ieee754_doubles = is_system_ieee754_double();
167 + if(!ieee754_doubles)
168 + globally_disabled_capabilities |= STREAM_CAP_IEEE754;
169 +}
src/streaming/stream_capabilities.h new
+100
@@ -0,0 +1,100 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_STREAM_CAPABILITIES_H
4 +#define NETDATA_STREAM_CAPABILITIES_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +// ----------------------------------------------------------------------------
9 +// obsolete versions - do not use anymore
10 +
11 +#define STREAM_OLD_VERSION_CLAIM 3
12 +#define STREAM_OLD_VERSION_CLABELS 4
13 +#define STREAM_OLD_VERSION_LZ4 5
14 +
15 +// ----------------------------------------------------------------------------
16 +// capabilities negotiation
17 +
18 +typedef enum {
19 + STREAM_CAP_NONE = 0,
20 +
21 + // do not use the first 3 bits
22 + // they used to be versions 1, 2 and 3
23 + // before we introduce capabilities
24 +
25 + STREAM_CAP_V1 = (1 << 3), // v1 = the oldest protocol
26 + STREAM_CAP_V2 = (1 << 4), // v2 = the second version of the protocol (with host labels)
27 + STREAM_CAP_VN = (1 << 5), // version negotiation supported (for versions 3, 4, 5 of the protocol)
28 + // v3 = claiming supported
29 + // v4 = chart labels supported
30 + // v5 = lz4 compression supported
31 + STREAM_CAP_VCAPS = (1 << 6), // capabilities negotiation supported
32 + STREAM_CAP_HLABELS = (1 << 7), // host labels supported
33 + STREAM_CAP_CLAIM = (1 << 8), // claiming supported
34 + STREAM_CAP_CLABELS = (1 << 9), // chart labels supported
35 + STREAM_CAP_LZ4 = (1 << 10), // lz4 compression supported
36 + STREAM_CAP_FUNCTIONS = (1 << 11), // plugin functions supported
37 + STREAM_CAP_REPLICATION = (1 << 12), // replication supported
38 + STREAM_CAP_BINARY = (1 << 13), // streaming supports binary data
39 + STREAM_CAP_INTERPOLATED = (1 << 14), // streaming supports interpolated streaming of values
40 + STREAM_CAP_IEEE754 = (1 << 15), // streaming supports binary/hex transfer of double values
41 + STREAM_CAP_DATA_WITH_ML = (1 << 16), // streaming supports transferring anomaly bit
42 + // STREAM_CAP_DYNCFG = (1 << 17), // leave this unused for as long as possible
43 + STREAM_CAP_SLOTS = (1 << 18), // the sender can appoint a unique slot for each chart
44 + STREAM_CAP_ZSTD = (1 << 19), // ZSTD compression supported
45 + STREAM_CAP_GZIP = (1 << 20), // GZIP compression supported
46 + STREAM_CAP_BROTLI = (1 << 21), // BROTLI compression supported
47 + STREAM_CAP_PROGRESS = (1 << 22), // Functions PROGRESS support
48 + STREAM_CAP_DYNCFG = (1 << 23), // support for DYNCFG
49 + STREAM_CAP_NODE_ID = (1 << 24), // support for sending NODE_ID back to the child
50 + STREAM_CAP_PATHS = (1 << 25), // support for sending PATHS upstream and downstream
51 +
52 + STREAM_CAP_INVALID = (1 << 30), // used as an invalid value for capabilities when this is set
53 + // this must be signed int, so don't use the last bit
54 + // needed for negotiating errors between parent and child
55 +} STREAM_CAPABILITIES;
56 +
57 +#ifdef ENABLE_LZ4
58 +#define STREAM_CAP_LZ4_AVAILABLE STREAM_CAP_LZ4
59 +#else
60 +#define STREAM_CAP_LZ4_AVAILABLE 0
61 +#endif // ENABLE_LZ4
62 +
63 +#ifdef ENABLE_ZSTD
64 +#define STREAM_CAP_ZSTD_AVAILABLE STREAM_CAP_ZSTD
65 +#else
66 +#define STREAM_CAP_ZSTD_AVAILABLE 0
67 +#endif // ENABLE_ZSTD
68 +
69 +#ifdef ENABLE_BROTLI
70 +#define STREAM_CAP_BROTLI_AVAILABLE STREAM_CAP_BROTLI
71 +#else
72 +#define STREAM_CAP_BROTLI_AVAILABLE 0
73 +#endif // ENABLE_BROTLI
74 +
75 +#define STREAM_CAP_COMPRESSIONS_AVAILABLE (STREAM_CAP_LZ4_AVAILABLE|STREAM_CAP_ZSTD_AVAILABLE|STREAM_CAP_BROTLI_AVAILABLE|STREAM_CAP_GZIP)
76 +
77 +#define stream_has_capability(rpt, capability) ((rpt) && ((rpt)->capabilities & (capability)) == (capability))
78 +
79 +static inline bool stream_has_more_than_one_capability_of(STREAM_CAPABILITIES caps, STREAM_CAPABILITIES mask) {
80 + STREAM_CAPABILITIES common = (STREAM_CAPABILITIES)(caps & mask);
81 + return (common & (common - 1)) != 0 && common != 0;
82 +}
83 +
84 +struct sender_state;
85 +struct receiver_state;
86 +struct rrdhost;
87 +
88 +STREAM_CAPABILITIES stream_capabilities_parse_one(const char *str);
89 +
90 +void stream_capabilities_to_string(BUFFER *wb, STREAM_CAPABILITIES caps);
91 +void stream_capabilities_to_json_array(BUFFER *wb, STREAM_CAPABILITIES caps, const char *key);
92 +void log_receiver_capabilities(struct receiver_state *rpt);
93 +void log_sender_capabilities(struct sender_state *s);
94 +STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version, struct rrdhost *host, bool sender);
95 +int32_t stream_capabilities_to_vn(uint32_t caps);
96 +STREAM_CAPABILITIES stream_our_capabilities(struct rrdhost *host, bool sender);
97 +
98 +void check_local_streaming_capabilities(void);
99 +
100 +#endif //NETDATA_STREAM_CAPABILITIES_H
src/streaming/stream_path.c new
+331
@@ -0,0 +1,331 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "stream_path.h"
4 +#include "rrdpush.h"
5 +#include "collectors/plugins.d/pluginsd_internals.h"
6 +
7 +ENUM_STR_MAP_DEFINE(STREAM_PATH_FLAGS) = {
8 + { .id = STREAM_PATH_FLAG_ACLK, .name = "aclk" },
9 +
10 + // terminator
11 + { . id = 0, .name = NULL }
12 +};
13 +
14 +BITMAP_STR_DEFINE_FUNCTIONS(STREAM_PATH_FLAGS, STREAM_PATH_FLAG_NONE, "");
15 +
16 +static void stream_path_clear(STREAM_PATH *p) {
17 + string_freez(p->hostname);
18 + p->hostname = NULL;
19 + p->host_id = UUID_ZERO;
20 + p->node_id = UUID_ZERO;
21 + p->claim_id = UUID_ZERO;
22 + p->hops = 0;
23 + p->since = 0;
24 + p->first_time_t = 0;
25 + p->capabilities = 0;
26 + p->flags = STREAM_PATH_FLAG_NONE;
27 +}
28 +
29 +static void rrdhost_stream_path_clear_unsafe(RRDHOST *host, bool destroy) {
30 + for(size_t i = 0; i < host->rrdpush.path.used ; i++)
31 + stream_path_clear(&host->rrdpush.path.array[i]);
32 +
33 + host->rrdpush.path.used = 0;
34 +
35 + if(destroy) {
36 + freez(host->rrdpush.path.array);
37 + host->rrdpush.path.array = NULL;
38 + host->rrdpush.path.size = 0;
39 + }
40 +}
41 +
42 +void rrdhost_stream_path_clear(RRDHOST *host, bool destroy) {
43 + spinlock_lock(&host->rrdpush.path.spinlock);
44 + rrdhost_stream_path_clear_unsafe(host, destroy);
45 + spinlock_unlock(&host->rrdpush.path.spinlock);
46 +}
47 +
48 +static void stream_path_to_json_object(BUFFER *wb, STREAM_PATH *p) {
49 + buffer_json_add_array_item_object(wb);
50 + buffer_json_member_add_string(wb, "hostname", string2str(p->hostname));
51 + buffer_json_member_add_uuid(wb, "host_id", p->host_id.uuid);
52 + buffer_json_member_add_uuid(wb, "node_id", p->node_id.uuid);
53 + buffer_json_member_add_uuid(wb, "claim_id", p->claim_id.uuid);
54 + buffer_json_member_add_int64(wb, "hops", p->hops);
55 + buffer_json_member_add_uint64(wb, "since", p->since);
56 + buffer_json_member_add_uint64(wb, "first_time_t", p->first_time_t);
57 + stream_capabilities_to_json_array(wb, p->capabilities, "capabilities");
58 + STREAM_PATH_FLAGS_2json(wb, "flags", p->flags);
59 + buffer_json_object_close(wb);
60 +}
61 +
62 +static STREAM_PATH rrdhost_stream_path_self(RRDHOST *host) {
63 + STREAM_PATH p = { 0 };
64 +
65 + bool is_localhost = host == localhost || rrdhost_option_check(host, RRDHOST_OPTION_VIRTUAL_HOST);
66 +
67 + p.hostname = string_dup(localhost->hostname);
68 + p.host_id = localhost->host_id;
69 + p.node_id = localhost->node_id;
70 + p.claim_id = claim_id_get_uuid();
71 +
72 + p.flags = STREAM_PATH_FLAG_NONE;
73 + if(!UUIDiszero(p.claim_id))
74 + p.flags |= STREAM_PATH_FLAG_ACLK;
75 +
76 + bool has_receiver = false;
77 + spinlock_lock(&host->receiver_lock);
78 + if(host->receiver) {
79 + has_receiver = true;
80 + p.hops = (int16_t)host->receiver->hops;
81 + p.since = host->receiver->connected_since_s;
82 + }
83 + spinlock_unlock(&host->receiver_lock);
84 +
85 + if(!has_receiver) {
86 + p.hops = (is_localhost) ? 0 : -1; // -1 for stale nodes
87 + p.since = netdata_start_time;
88 + }
89 +
90 + // the following may get the receiver lock again!
91 + p.capabilities = stream_our_capabilities(host, true);
92 +
93 + rrdhost_retention(host, 0, false, &p.first_time_t, NULL);
94 +
95 + return p;
96 +}
97 +
98 +void rrdhost_stream_path_to_json(BUFFER *wb, struct rrdhost *host, const char *key, bool add_version) {
99 + if(add_version)
100 + buffer_json_member_add_uint64(wb, "version", 1);
101 +
102 + spinlock_lock(&host->rrdpush.path.spinlock);
103 + buffer_json_member_add_array(wb, key);
104 + {
105 + {
106 + STREAM_PATH tmp = rrdhost_stream_path_self(host);
107 +
108 + bool found_self = false;
109 + for (size_t i = 0; i < host->rrdpush.path.used; i++) {
110 + STREAM_PATH *p = &host->rrdpush.path.array[i];
111 + if(UUIDeq(localhost->host_id, p->host_id)) {
112 + // this is us, use the current data
113 + p = &tmp;
114 + found_self = true;
115 + }
116 + stream_path_to_json_object(wb, p);
117 + }
118 +
119 + if(!found_self) {
120 + // we didn't find ourselves in the list.
121 + // append us.
122 + stream_path_to_json_object(wb, &tmp);
123 + }
124 +
125 + stream_path_clear(&tmp);
126 + }
127 + }
128 + buffer_json_array_close(wb); // key
129 + spinlock_unlock(&host->rrdpush.path.spinlock);
130 +}
131 +
132 +static BUFFER *stream_path_payload(RRDHOST *host) {
133 + BUFFER *wb = buffer_create(0, NULL);
134 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
135 + rrdhost_stream_path_to_json(wb, host, STREAM_PATH_JSON_MEMBER, true);
136 + buffer_json_finalize(wb);
137 + return wb;
138 +}
139 +
140 +void stream_path_send_to_parent(RRDHOST *host) {
141 + struct sender_state *s = host->sender;
142 + if(!s || !stream_has_capability(s, STREAM_CAP_PATHS))
143 + return;
144 +
145 + CLEAN_BUFFER *payload = stream_path_payload(s->host);
146 +
147 + BUFFER *wb = sender_start(s);
148 + buffer_sprintf(wb, PLUGINSD_KEYWORD_JSON " " PLUGINSD_KEYWORD_STREAM_PATH "\n%s\n" PLUGINSD_KEYWORD_JSON_END "\n", buffer_tostring(payload));
149 + sender_commit(s, wb, STREAM_TRAFFIC_TYPE_METADATA);
150 +}
151 +
152 +void stream_path_send_to_child(RRDHOST *host) {
153 + if(host == localhost)
154 + return;
155 +
156 + CLEAN_BUFFER *payload = stream_path_payload(host);
157 +
158 + spinlock_lock(&host->receiver_lock);
159 + if(host->receiver && stream_has_capability(host->receiver, STREAM_CAP_PATHS)) {
160 +
161 + CLEAN_BUFFER *wb = buffer_create(0, NULL);
162 + buffer_sprintf(wb, PLUGINSD_KEYWORD_JSON " " PLUGINSD_KEYWORD_STREAM_PATH "\n%s\n" PLUGINSD_KEYWORD_JSON_END "\n", buffer_tostring(payload));
163 + send_to_plugin(buffer_tostring(wb), __atomic_load_n(&host->receiver->parser, __ATOMIC_RELAXED));
164 + }
165 + spinlock_unlock(&host->receiver_lock);
166 +}
167 +
168 +void stream_path_child_disconnected(RRDHOST *host) {
169 + rrdhost_stream_path_clear(host, true);
170 +}
171 +
172 +void stream_path_parent_disconnected(RRDHOST *host) {
173 + spinlock_lock(&host->rrdpush.path.spinlock);
174 +
175 + size_t cleared = 0;
176 + size_t used = host->rrdpush.path.used;
177 + for (size_t i = 0; i < used; i++) {
178 + STREAM_PATH *p = &host->rrdpush.path.array[i];
179 + if(UUIDeq(localhost->host_id, p->host_id)) {
180 + host->rrdpush.path.used = i + 1;
181 +
182 + for(size_t j = i + 1; j < used ;j++) {
183 + stream_path_clear(&host->rrdpush.path.array[j]);
184 + cleared++;
185 + }
186 +
187 + break;
188 + }
189 + }
190 +
191 + spinlock_unlock(&host->rrdpush.path.spinlock);
192 +
193 + if(cleared)
194 + stream_path_send_to_child(host);
195 +}
196 +
197 +void stream_path_retention_updated(RRDHOST *host) {
198 + if(!host || !localhost) return;
199 + stream_path_send_to_parent(host);
200 + stream_path_send_to_child(host);
201 +}
202 +
203 +void stream_path_node_id_updated(RRDHOST *host) {
204 + if(!host || !localhost) return;
205 + stream_path_send_to_parent(host);
206 + stream_path_send_to_child(host);
207 +}
208 +
209 +// --------------------------------------------------------------------------------------------------------------------
210 +
211 +
212 +static bool parse_single_path(json_object *jobj, const char *path, STREAM_PATH *p, BUFFER *error) {
213 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "hostname", p->hostname, error, true);
214 + JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "host_id", p->host_id.uuid, error, true);
215 + JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "node_id", p->node_id.uuid, error, true);
216 + JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "claim_id", p->claim_id.uuid, error, true);
217 + JSONC_PARSE_INT64_OR_ERROR_AND_RETURN(jobj, path, "hops", p->hops, error, true);
218 + JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "since", p->since, error, true);
219 + JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "first_time_t", p->first_time_t, error, true);
220 + JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "flags", STREAM_PATH_FLAGS_2id_one, p->flags, error, true);
221 + JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "capabilities", stream_capabilities_parse_one, p->capabilities, error, true);
222 +
223 + if(!p->hostname) {
224 + buffer_strcat(error, "hostname cannot be empty");
225 + return false;
226 + }
227 +
228 + if(UUIDiszero(p->host_id)) {
229 + buffer_strcat(error, "host_id cannot be zero");
230 + return false;
231 + }
232 +
233 + if(p->hops < 0) {
234 + buffer_strcat(error, "hops cannot be negative");
235 + return false;
236 + }
237 +
238 + if(p->capabilities == STREAM_CAP_NONE) {
239 + buffer_strcat(error, "capabilities cannot be empty");
240 + return false;
241 + }
242 +
243 + if(p->since <= 0) {
244 + buffer_strcat(error, "since cannot be <= 0");
245 + return false;
246 + }
247 +
248 + return true;
249 +}
250 +
251 +static XXH128_hash_t stream_path_hash_unsafe(RRDHOST *host) {
252 + if(!host->rrdpush.path.used)
253 + return (XXH128_hash_t){ 0 };
254 +
255 + return XXH3_128bits(host->rrdpush.path.array, sizeof(*host->rrdpush.path.array) * host->rrdpush.path.used);
256 +}
257 +
258 +static int compare_by_hops(const void *a, const void *b) {
259 + const STREAM_PATH *path1 = a;
260 + const STREAM_PATH *path2 = b;
261 +
262 + if (path1->hops < path2->hops)
263 + return -1;
264 + else if (path1->hops > path2->hops)
265 + return 1;
266 +
267 + return 0;
268 +}
269 +
270 +bool stream_path_set_from_json(RRDHOST *host, const char *json, bool from_parent) {
271 + if(!json || !*json)
272 + return false;
273 +
274 + CLEAN_JSON_OBJECT *jobj = json_tokener_parse(json);
275 + if(!jobj) {
276 + nd_log(NDLS_DAEMON, NDLP_ERR,
277 + "STREAM PATH: Cannot parse json: %s", json);
278 + return false;
279 + }
280 +
281 + spinlock_lock(&host->rrdpush.path.spinlock);
282 + XXH128_hash_t old_hash = stream_path_hash_unsafe(host);
283 + rrdhost_stream_path_clear_unsafe(host, true);
284 +
285 + CLEAN_BUFFER *error = buffer_create(0, NULL);
286 +
287 + json_object *_jarray;
288 + if (json_object_object_get_ex(jobj, STREAM_PATH_JSON_MEMBER, &_jarray) &&
289 + json_object_is_type(_jarray, json_type_array)) {
290 + size_t items = json_object_array_length(_jarray);
291 + host->rrdpush.path.array = callocz(items, sizeof(*host->rrdpush.path.array));
292 + host->rrdpush.path.size = items;
293 +
294 + for (size_t i = 0; i < items; ++i) {
295 + json_object *joption = json_object_array_get_idx(_jarray, i);
296 + if (!json_object_is_type(joption, json_type_object)) {
297 + nd_log(NDLS_DAEMON, NDLP_ERR,
298 + "STREAM PATH: Array item No %zu is not an object: %s", i, json);
299 + continue;
300 + }
301 +
302 + if(!parse_single_path(joption, "", &host->rrdpush.path.array[host->rrdpush.path.used], error)) {
303 + stream_path_clear(&host->rrdpush.path.array[host->rrdpush.path.used]);
304 + nd_log(NDLS_DAEMON, NDLP_ERR,
305 + "STREAM PATH: Array item No %zu cannot be parsed: %s: %s", i, buffer_tostring(error), json);
306 + }
307 + else
308 + host->rrdpush.path.used++;
309 + }
310 + }
311 +
312 + if(host->rrdpush.path.used > 1) {
313 + // sorting is required in order to support stream_path_parent_disconnected()
314 + qsort(host->rrdpush.path.array, host->rrdpush.path.used,
315 + sizeof(*host->rrdpush.path.array), compare_by_hops);
316 + }
317 +
318 + XXH128_hash_t new_hash = stream_path_hash_unsafe(host);
319 + spinlock_unlock(&host->rrdpush.path.spinlock);
320 +
321 + if(!XXH128_isEqual(old_hash, new_hash)) {
322 + if(!from_parent)
323 + stream_path_send_to_parent(host);
324 +
325 + // when it comes from the child, we still need to send it back to the child
326 + // including our own entry in it.
327 + stream_path_send_to_child(host);
328 + }
329 +
330 + return host->rrdpush.path.used > 0;
331 +}
src/streaming/stream_path.h new
+51
@@ -0,0 +1,51 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_STREAM_PATH_H
4 +#define NETDATA_STREAM_PATH_H
5 +
6 +#include "stream_capabilities.h"
7 +
8 +#define STREAM_PATH_JSON_MEMBER "streaming_path"
9 +
10 +typedef enum __attribute__((packed)) {
11 + STREAM_PATH_FLAG_NONE = 0,
12 + STREAM_PATH_FLAG_ACLK = (1 << 0),
13 +} STREAM_PATH_FLAGS;
14 +
15 +typedef struct stream_path {
16 + STRING *hostname; // the hostname of the agent
17 + ND_UUID host_id; // the machine guid of the agent
18 + ND_UUID node_id; // the cloud node id of the agent
19 + ND_UUID claim_id; // the cloud claim id of the agent
20 + time_t since; // the timestamp of the last update
21 + time_t first_time_t; // the oldest timestamp in the db
22 + int16_t hops; // -1 = stale node, 0 = localhost, >0 the hops count
23 + STREAM_PATH_FLAGS flags; // ACLK or NONE for the moment
24 + STREAM_CAPABILITIES capabilities; // streaming connection capabilities
25 +} STREAM_PATH;
26 +
27 +typedef struct rrdhost_stream_path {
28 + SPINLOCK spinlock;
29 + uint16_t size;
30 + uint16_t used;
31 + STREAM_PATH *array;
32 +} RRDHOST_STREAM_PATH;
33 +
34 +
35 +struct rrdhost;
36 +
37 +void stream_path_send_to_parent(struct rrdhost *host);
38 +void stream_path_send_to_child(struct rrdhost *host);
39 +
40 +void rrdhost_stream_path_to_json(BUFFER *wb, struct rrdhost *host, const char *key, bool add_version);
41 +void rrdhost_stream_path_clear(struct rrdhost *host, bool destroy);
42 +
43 +void stream_path_retention_updated(struct rrdhost *host);
44 +void stream_path_node_id_updated(struct rrdhost *host);
45 +
46 +void stream_path_child_disconnected(struct rrdhost *host);
47 +void stream_path_parent_disconnected(struct rrdhost *host);
48 +
49 +bool stream_path_set_from_json(struct rrdhost *host, const char *json, bool from_parent);
50 +
51 +#endif //NETDATA_STREAM_PATH_H
src/web/api/http_auth.c
+3 -3
@@ -83,7 +83,7 @@ static uint64_t bearer_token_signature(nd_uuid_t token, struct bearer_token *bt)
83 .created_s = bt->created_s,
84 .expires_s = bt->expires_s,
85 };
86 - uuid_copy(signature_payload.host_uuid, localhost->host_uuid);
86 + uuid_copy(signature_payload.host_uuid, localhost->host_id.uuid);
87 uuid_copy(signature_payload.token, token);
88 uuid_copy(signature_payload.cloud_account_id, bt->cloud_account_id);
89 memset(signature_payload.client_name, 0, sizeof(signature_payload.client_name));
@@ -96,7 +96,7 @@ static bool bearer_token_save_to_file(nd_uuid_t token, struct bearer_token *bt)
96 CLEAN_BUFFER *wb = buffer_create(0, NULL);
97 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
98 buffer_json_member_add_uint64(wb, "version", 1);
99 - buffer_json_member_add_uuid(wb, "host_uuid", localhost->host_uuid);
99 + buffer_json_member_add_uuid(wb, "host_uuid", localhost->host_id.uuid);
100 buffer_json_member_add_uuid(wb, "token", token);
101 buffer_json_member_add_uuid(wb, "cloud_account_id", bt->cloud_account_id);
102 buffer_json_member_add_string(wb, "client_name", bt->client_name);
@@ -207,7 +207,7 @@ static bool bearer_token_parse_json(nd_uuid_t token, struct json_object *jobj, B
207 return false;
208 }
209
210 - if(uuid_compare(host_uuid, localhost->host_uuid) != 0) {
210 + if(uuid_compare(host_uuid, localhost->host_id.uuid) != 0) {
211 buffer_flush(error);
212 buffer_strcat(error, "Host UUID in JSON file does not match our host UUID");
213 return false;
src/web/api/v1/api_v1_info.c
+1 -1
@@ -42,7 +42,7 @@ static inline void web_client_api_request_v1_info_mirrored_hosts_status(BUFFER *
42 buffer_json_member_add_boolean(wb, "reachable", (host == localhost || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)));
43
44 buffer_json_member_add_string(wb, "guid", host->machine_guid);
45 - buffer_json_member_add_uuid(wb, "node_id", host->node_id);
45 + buffer_json_member_add_uuid(wb, "node_id", host->node_id.uuid);
46 CLAIM_ID claim_id = rrdhost_claim_id_get(host);
47 buffer_json_member_add_string(wb, "claim_id", claim_id_is_set(claim_id) ? claim_id.str : NULL);
48
src/web/api/v2/api_v2_bearer.c
+2 -2
@@ -9,11 +9,11 @@ static bool verify_host_uuids(RRDHOST *host, const char *machine_guid, const cha
9 if(strcmp(machine_guid, host->machine_guid) != 0)
10 return false;
11
12 - if(uuid_is_null(host->node_id))
12 + if(UUIDiszero(host->node_id))
13 return false;
14
15 char buf[UUID_STR_LEN];
16 - uuid_unparse_lower(host->node_id, buf);
16 + uuid_unparse_lower(host->node_id.uuid, buf);
17
18 return strcmp(node_id, buf) == 0;
19 }
src/web/server/h2o/http_server.c
+1 -1
@@ -290,7 +290,7 @@ static int netdata_uberhandler(h2o_handler_t *self, h2o_req_t *req)
290 char host_uuid_str[UUID_STR_LEN];
291
292 if (host != NULL)
293 - uuid_unparse_lower(host->host_uuid, host_uuid_str);
293 + uuid_unparse_lower(host->host_id.uuid, host_uuid_str);
294
295 nd_log(NDLS_ACCESS, NDLP_DEBUG, "HTTPD OK method: " PRINTF_H2O_IOVEC_FMT
296 ", path: " PRINTF_H2O_IOVEC_FMT