master
c 407 lines 13.5 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "opentsdb.h"
4 #include "../json/json.h"
5
6 /**
7 * Initialize OpenTSDB telnet connector instance
8 *
9 * @param instance an instance data structure.
10 * @return Returns 0 on success, 1 on failure.
11 */
12 int init_opentsdb_telnet_instance(struct instance *instance)
13 {
14 instance->worker = simple_connector_worker;
15
16 struct simple_connector_config *connector_specific_config = callocz(1, sizeof(struct simple_connector_config));
17 instance->config.connector_specific_config = (void *)connector_specific_config;
18 connector_specific_config->default_port = 4242;
19
20 struct simple_connector_data *connector_specific_data = callocz(1, sizeof(struct simple_connector_data));
21 instance->connector_specific_data = connector_specific_data;
22
23 connector_specific_data->ssl = NETDATA_SSL_UNSET_CONNECTION;
24 if (instance->config.options & EXPORTING_OPTION_USE_TLS) {
25 netdata_ssl_initialize_ctx(NETDATA_SSL_EXPORTING_CTX);
26 }
27
28 instance->start_batch_formatting = NULL;
29 instance->start_host_formatting = format_host_labels_opentsdb_telnet;
30 instance->start_chart_formatting = NULL;
31
32 if (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED)
33 instance->metric_formatting = format_dimension_collected_opentsdb_telnet;
34 else
35 instance->metric_formatting = format_dimension_stored_opentsdb_telnet;
36
37 instance->end_chart_formatting = NULL;
38 instance->variables_formatting = NULL;
39 instance->end_host_formatting = flush_host_labels;
40 instance->end_batch_formatting = simple_connector_end_batch;
41
42 instance->prepare_header = NULL;
43 instance->check_response = exporting_discard_response;
44
45 instance->buffer = (void *)buffer_create(0, &netdata_buffers_statistics.buffers_exporters);
46 if (!instance->buffer) {
47 netdata_log_error("EXPORTING: cannot create buffer for opentsdb telnet exporting connector instance %s", instance->config.name);
48 return 1;
49 }
50
51 simple_connector_init(instance);
52
53 if (netdata_mutex_init(&instance->mutex))
54 return 1;
55 if (netdata_cond_init(&instance->cond_var))
56 return 1;
57
58 return 0;
59 }
60
61 /**
62 * Initialize OpenTSDB HTTP connector instance
63 *
64 * @param instance an instance data structure.
65 * @return Returns 0 on success, 1 on failure.
66 */
67 int init_opentsdb_http_instance(struct instance *instance)
68 {
69 instance->worker = simple_connector_worker;
70
71 struct simple_connector_config *connector_specific_config = callocz(1, sizeof(struct simple_connector_config));
72 instance->config.connector_specific_config = (void *)connector_specific_config;
73 connector_specific_config->default_port = 4242;
74
75 struct simple_connector_data *connector_specific_data = callocz(1, sizeof(struct simple_connector_data));
76 connector_specific_data->ssl = NETDATA_SSL_UNSET_CONNECTION;
77 if (instance->config.options & EXPORTING_OPTION_USE_TLS) {
78 netdata_ssl_initialize_ctx(NETDATA_SSL_EXPORTING_CTX);
79 }
80 instance->connector_specific_data = connector_specific_data;
81
82 instance->start_batch_formatting = open_batch_json_http;
83 instance->start_host_formatting = format_host_labels_opentsdb_http;
84 instance->start_chart_formatting = NULL;
85
86 if (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED)
87 instance->metric_formatting = format_dimension_collected_opentsdb_http;
88 else
89 instance->metric_formatting = format_dimension_stored_opentsdb_http;
90
91 instance->end_chart_formatting = NULL;
92 instance->variables_formatting = NULL;
93 instance->end_host_formatting = flush_host_labels;
94 instance->end_batch_formatting = close_batch_json_http;
95
96 instance->prepare_header = opentsdb_http_prepare_header;
97 instance->check_response = exporting_discard_response;
98
99 instance->buffer = (void *)buffer_create(0, &netdata_buffers_statistics.buffers_exporters);
100 if (!instance->buffer) {
101 netdata_log_error("EXPORTING: cannot create buffer for opentsdb HTTP exporting connector instance %s", instance->config.name);
102 return 1;
103 }
104
105 simple_connector_init(instance);
106
107 if (netdata_mutex_init(&instance->mutex))
108 return 1;
109 if (netdata_cond_init(&instance->cond_var))
110 return 1;
111
112 return 0;
113 }
114
115 /**
116 * Copy a label value and substitute underscores in place of characters which can't be used in OpenTSDB output
117 *
118 * @param dst a destination string.
119 * @param src a source string.
120 * @param len the maximum number of characters copied.
121 */
122
123 void sanitize_opentsdb_label_value(char *dst, const char *src, size_t len)
124 {
125 while (*src != '\0' && len) {
126 if (isalpha((uint8_t)*src) || isdigit((uint8_t)*src) || *src == '-' || *src == '.' || *src == '/' || IS_UTF8_BYTE(*src))
127 *dst++ = *src;
128 else
129 *dst++ = '_';
130
131 src++;
132 len--;
133 }
134 *dst = '\0';
135 }
136
137 /**
138 * Format host labels for JSON connector
139 *
140 * @param instance an instance data structure.
141 * @param host a data collecting host.
142 * @return Always returns 0.
143 */
144
145 int format_host_labels_opentsdb_telnet(struct instance *instance, RRDHOST *host) {
146 if(!instance->labels_buffer)
147 instance->labels_buffer = buffer_create(1024, &netdata_buffers_statistics.buffers_exporters);
148
149 if (unlikely(!sending_labels_configured(instance)))
150 return 0;
151
152 buffer_strcat(instance->labels_buffer, " ");
153 rrdlabels_to_buffer(host->rrdlabels, instance->labels_buffer, "", "=", "", " ",
154 exporting_labels_filter_callback, instance,
155 NULL, sanitize_opentsdb_label_value);
156 return 0;
157 }
158
159 /**
160 * Format dimension using collected data for OpenTSDB telnet connector
161 *
162 * @param instance an instance data structure.
163 * @param rd a dimension.
164 * @return Always returns 0.
165 */
166 int format_dimension_collected_opentsdb_telnet(struct instance *instance, RRDDIM *rd)
167 {
168 RRDSET *st = rd->rrdset;
169 RRDHOST *host = st->rrdhost;
170
171 char chart_name[RRD_ID_LENGTH_MAX + 1];
172 exporting_name_copy(
173 chart_name,
174 (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st),
175 RRD_ID_LENGTH_MAX);
176
177 char dimension_name[RRD_ID_LENGTH_MAX + 1];
178 exporting_name_copy(
179 dimension_name,
180 (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd),
181 RRD_ID_LENGTH_MAX);
182
183 if(rrddim_is_float(rd))
184 buffer_sprintf(
185 instance->buffer,
186 "put %s.%s.%s %llu " NETDATA_DOUBLE_FORMAT " host=%s%s\n",
187 instance->config.prefix,
188 chart_name,
189 dimension_name,
190 (unsigned long long)rd->collector.last_collected_time.tv_sec,
191 rrddim_last_collected_as_double(rd),
192 (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
193 (instance->labels_buffer) ? buffer_tostring(instance->labels_buffer) : "");
194 else
195 buffer_sprintf(
196 instance->buffer,
197 "put %s.%s.%s %llu " COLLECTED_NUMBER_FORMAT " host=%s%s\n",
198 instance->config.prefix,
199 chart_name,
200 dimension_name,
201 (unsigned long long)rd->collector.last_collected_time.tv_sec,
202 (collected_number)rrddim_last_collected_raw_int(rd),
203 (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
204 (instance->labels_buffer) ? buffer_tostring(instance->labels_buffer) : "");
205
206 return 0;
207 }
208
209 /**
210 * Format dimension using a calculated value from stored data for OpenTSDB telnet connector
211 *
212 * @param instance an instance data structure.
213 * @param rd a dimension.
214 * @return Always returns 0.
215 */
216 int format_dimension_stored_opentsdb_telnet(struct instance *instance, RRDDIM *rd)
217 {
218 RRDSET *st = rd->rrdset;
219 RRDHOST *host = st->rrdhost;
220
221 char chart_name[RRD_ID_LENGTH_MAX + 1];
222 exporting_name_copy(
223 chart_name,
224 (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st),
225 RRD_ID_LENGTH_MAX);
226
227 char dimension_name[RRD_ID_LENGTH_MAX + 1];
228 exporting_name_copy(
229 dimension_name,
230 (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd),
231 RRD_ID_LENGTH_MAX);
232
233 time_t last_t;
234 NETDATA_DOUBLE value = exporting_calculate_value_from_stored_data(instance, rd, &last_t);
235
236 if(isnan(value))
237 return 0;
238
239 buffer_sprintf(
240 instance->buffer,
241 "put %s.%s.%s %llu " NETDATA_DOUBLE_FORMAT " host=%s%s\n",
242 instance->config.prefix,
243 chart_name,
244 dimension_name,
245 (unsigned long long)last_t,
246 value,
247 (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
248 (instance->labels_buffer) ? buffer_tostring(instance->labels_buffer) : "");
249
250 return 0;
251 }
252
253 /**
254 * Prepare HTTP header
255 *
256 * @param instance an instance data structure.
257 * @return Returns 0 on success, 1 on failure.
258 */
259 void opentsdb_http_prepare_header(struct instance *instance)
260 {
261 struct simple_connector_data *simple_connector_data = instance->connector_specific_data;
262
263 buffer_sprintf(
264 simple_connector_data->last_buffer->header,
265 "POST /api/put HTTP/1.1\r\n"
266 "Host: %s\r\n"
267 "%s"
268 "Content-Type: application/json\r\n"
269 "Content-Length: %lu\r\n"
270 "\r\n",
271 instance->config.destination,
272 simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
273 (unsigned long int) buffer_strlen(simple_connector_data->last_buffer->buffer));
274
275 return;
276 }
277
278 /**
279 * Format host labels for OpenTSDB HTTP connector
280 *
281 * @param instance an instance data structure.
282 * @param host a data collecting host.
283 * @return Always returns 0.
284 */
285
286 int format_host_labels_opentsdb_http(struct instance *instance, RRDHOST *host) {
287 if (!instance->labels_buffer)
288 instance->labels_buffer = buffer_create(1024, &netdata_buffers_statistics.buffers_exporters);
289
290 if (unlikely(!sending_labels_configured(instance)))
291 return 0;
292
293 rrdlabels_to_buffer(host->rrdlabels, instance->labels_buffer, ",", ":", "\"", "",
294 exporting_labels_filter_callback, instance,
295 NULL, sanitize_opentsdb_label_value);
296 return 0;
297 }
298
299 /**
300 * Format dimension using collected data for OpenTSDB HTTP connector
301 *
302 * @param instance an instance data structure.
303 * @param rd a dimension.
304 * @return Always returns 0.
305 */
306 int format_dimension_collected_opentsdb_http(struct instance *instance, RRDDIM *rd)
307 {
308 RRDSET *st = rd->rrdset;
309 RRDHOST *host = st->rrdhost;
310
311 char chart_name[RRD_ID_LENGTH_MAX + 1];
312 exporting_name_copy(
313 chart_name,
314 (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st),
315 RRD_ID_LENGTH_MAX);
316
317 char dimension_name[RRD_ID_LENGTH_MAX + 1];
318 exporting_name_copy(
319 dimension_name,
320 (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd),
321 RRD_ID_LENGTH_MAX);
322
323 if (buffer_strlen((BUFFER *)instance->buffer) > 2)
324 buffer_strcat(instance->buffer, ",\n");
325
326 buffer_sprintf(
327 instance->buffer,
328 "{"
329 "\"metric\":\"%s.%s.%s\","
330 "\"timestamp\":%llu,"
331 "\"value\":",
332 instance->config.prefix,
333 chart_name,
334 dimension_name,
335 (unsigned long long)rd->collector.last_collected_time.tv_sec);
336
337 if(rrddim_is_float(rd))
338 buffer_sprintf(instance->buffer, NETDATA_DOUBLE_FORMAT, rrddim_last_collected_as_double(rd));
339 else
340 buffer_sprintf(instance->buffer, COLLECTED_NUMBER_FORMAT, (collected_number)rrddim_last_collected_raw_int(rd));
341
342 buffer_sprintf(
343 instance->buffer,
344 ","
345 "\"tags\":{"
346 "\"host\":\"%s\"%s"
347 "}"
348 "}",
349 (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
350 instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "");
351
352 return 0;
353 }
354
355 /**
356 * Format dimension using a calculated value from stored data for OpenTSDB HTTP connector
357 *
358 * @param instance an instance data structure.
359 * @param rd a dimension.
360 * @return Always returns 0.
361 */
362 int format_dimension_stored_opentsdb_http(struct instance *instance, RRDDIM *rd)
363 {
364 RRDSET *st = rd->rrdset;
365 RRDHOST *host = st->rrdhost;
366
367 char chart_name[RRD_ID_LENGTH_MAX + 1];
368 exporting_name_copy(
369 chart_name,
370 (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st),
371 RRD_ID_LENGTH_MAX);
372
373 char dimension_name[RRD_ID_LENGTH_MAX + 1];
374 exporting_name_copy(
375 dimension_name,
376 (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd),
377 RRD_ID_LENGTH_MAX);
378
379 time_t last_t;
380 NETDATA_DOUBLE value = exporting_calculate_value_from_stored_data(instance, rd, &last_t);
381
382 if(isnan(value))
383 return 0;
384
385 if (buffer_strlen((BUFFER *)instance->buffer) > 2)
386 buffer_strcat(instance->buffer, ",\n");
387
388 buffer_sprintf(
389 instance->buffer,
390 "{"
391 "\"metric\":\"%s.%s.%s\","
392 "\"timestamp\":%llu,"
393 "\"value\":" NETDATA_DOUBLE_FORMAT ","
394 "\"tags\":{"
395 "\"host\":\"%s\"%s"
396 "}"
397 "}",
398 instance->config.prefix,
399 chart_name,
400 dimension_name,
401 (unsigned long long)last_t,
402 value,
403 (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
404 instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "");
405
406 return 0;
407 }