master
c 518 lines 22.5 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "exporting_engine.h"
4
5 EXPORTING_OPTIONS global_exporting_options = EXPORTING_SOURCE_DATA_AVERAGE | EXPORTING_OPTION_SEND_NAMES;
6 const char *global_exporting_prefix = "netdata";
7
8 struct config exporting_config = APPCONFIG_INITIALIZER;
9
10 /**
11 * Free exporting configuration
12 *
13 * Free all memory associated with the exporting configuration.
14 * Called during shutdown to prevent memory leaks.
15 */
16 void exporting_config_free(void) {
17 inicfg_free(&exporting_config);
18 }
19
20 struct instance *prometheus_exporter_instance = NULL;
21
22 static _CONNECTOR_INSTANCE *find_instance(const char *section)
23 {
24 _CONNECTOR_INSTANCE *local_ci;
25
26 local_ci = add_connector_instance(NULL, NULL); // Get root section
27 if (unlikely(!local_ci))
28 return local_ci;
29
30 if (!section)
31 return local_ci;
32
33 while (local_ci) {
34 if (!strcmp(local_ci->instance_name, section))
35 break;
36 local_ci = local_ci->next;
37 }
38 return local_ci;
39 }
40
41 static const char *expconfig_get(struct config *root, const char *section, const char *name, const char *default_value)
42 {
43 _CONNECTOR_INSTANCE *local_ci;
44
45 if (!strcmp(section, CONFIG_SECTION_EXPORTING))
46 return inicfg_get(root, CONFIG_SECTION_EXPORTING, name, default_value);
47
48 local_ci = find_instance(section);
49
50 if (!local_ci)
51 return NULL; // TODO: Check if it is meaningful to return default_value
52
53 return inicfg_get(
54 root, local_ci->instance_name, name,
55 inicfg_get(
56 root, local_ci->connector_name, name, inicfg_get(root, CONFIG_SECTION_EXPORTING, name, default_value)));
57 }
58
59 int expconfig_get_boolean(struct config *root, const char *section, const char *name, int default_value)
60 {
61 _CONNECTOR_INSTANCE *local_ci;
62
63 if (!strcmp(section, CONFIG_SECTION_EXPORTING))
64 return inicfg_get_boolean(root, CONFIG_SECTION_EXPORTING, name, default_value);
65
66 local_ci = find_instance(section);
67
68 if (!local_ci)
69 return 0; // TODO: Check if it is meaningful to return default_value
70
71 return inicfg_get_boolean(
72 root, local_ci->instance_name, name,
73 inicfg_get_boolean(
74 root, local_ci->connector_name, name,
75 inicfg_get_boolean(root, CONFIG_SECTION_EXPORTING, name, default_value)));
76 }
77
78 long long expconfig_get_number(struct config *root, const char *section, const char *name, long long default_value)
79 {
80 _CONNECTOR_INSTANCE *local_ci;
81
82 if (!strcmp(section, CONFIG_SECTION_EXPORTING))
83 return inicfg_get_number(root, CONFIG_SECTION_EXPORTING, name, default_value);
84
85 local_ci = find_instance(section);
86
87 if (!local_ci)
88 return 0; // TODO: Check if it is meaningful to return default_value
89
90 return inicfg_get_number(
91 root, local_ci->instance_name, name,
92 inicfg_get_number(
93 root, local_ci->connector_name, name,
94 inicfg_get_number(root, CONFIG_SECTION_EXPORTING, name, default_value)));
95 }
96
97 /*
98 * Get the next connector instance that we need to activate
99 *
100 * @param @target_ci will be filled with instance name and connector name
101 *
102 * @return - 1 if more connectors to be fetched, 0 done
103 *
104 */
105
106 int get_connector_instance(struct connector_instance *target_ci)
107 {
108 static _CONNECTOR_INSTANCE *local_ci = NULL;
109 _CONNECTOR_INSTANCE *global_connector_instance;
110
111 global_connector_instance = find_instance(NULL); // Fetch head of instances
112
113 if (unlikely(!global_connector_instance))
114 return 0;
115
116 if (target_ci == NULL) {
117 local_ci = NULL;
118 return 1;
119 }
120 if (local_ci == NULL)
121 local_ci = global_connector_instance;
122 else {
123 local_ci = local_ci->next;
124 if (local_ci == NULL)
125 return 0;
126 }
127
128 strcpy(target_ci->instance_name, local_ci->instance_name);
129 strcpy(target_ci->connector_name, local_ci->connector_name);
130
131 return 1;
132 }
133
134 /**
135 * Select Type
136 *
137 * Select the connector type based on the user input
138 *
139 * @param type is the string that defines the connector type
140 *
141 * @return It returns the connector id.
142 */
143 EXPORTING_CONNECTOR_TYPE exporting_select_type(const char *type)
144 {
145 if (!strcmp(type, "graphite") || !strcmp(type, "graphite:plaintext")) {
146 return EXPORTING_CONNECTOR_TYPE_GRAPHITE;
147 } else if (!strcmp(type, "graphite:http") || !strcmp(type, "graphite:https")) {
148 return EXPORTING_CONNECTOR_TYPE_GRAPHITE_HTTP;
149 } else if (!strcmp(type, "json") || !strcmp(type, "json:plaintext")) {
150 return EXPORTING_CONNECTOR_TYPE_JSON;
151 } else if (!strcmp(type, "json:http") || !strcmp(type, "json:https")) {
152 return EXPORTING_CONNECTOR_TYPE_JSON_HTTP;
153 } else if (!strcmp(type, "opentsdb") || !strcmp(type, "opentsdb:telnet")) {
154 return EXPORTING_CONNECTOR_TYPE_OPENTSDB;
155 } else if (!strcmp(type, "opentsdb:http") || !strcmp(type, "opentsdb:https")) {
156 return EXPORTING_CONNECTOR_TYPE_OPENTSDB_HTTP;
157 } else if (
158 !strcmp(type, "prometheus_remote_write") ||
159 !strcmp(type, "prometheus_remote_write:http") ||
160 !strcmp(type, "prometheus_remote_write:https")) {
161 return EXPORTING_CONNECTOR_TYPE_PROMETHEUS_REMOTE_WRITE;
162 } else if (!strcmp(type, "kinesis") || !strcmp(type, "kinesis:plaintext")) {
163 return EXPORTING_CONNECTOR_TYPE_KINESIS;
164 } else if (!strcmp(type, "pubsub") || !strcmp(type, "pubsub:plaintext")) {
165 return EXPORTING_CONNECTOR_TYPE_PUBSUB;
166 } else if (!strcmp(type, "mongodb") || !strcmp(type, "mongodb:plaintext"))
167 return EXPORTING_CONNECTOR_TYPE_MONGODB;
168
169 return EXPORTING_CONNECTOR_TYPE_UNKNOWN;
170 }
171
172 inline EXPORTING_OPTIONS exporting_parse_data_source(const char *data_source, EXPORTING_OPTIONS exporting_options)
173 {
174 if (!strcmp(data_source, "raw") || !strcmp(data_source, "as collected") || !strcmp(data_source, "as-collected") ||
175 !strcmp(data_source, "as_collected") || !strcmp(data_source, "ascollected")) {
176 exporting_options |= EXPORTING_SOURCE_DATA_AS_COLLECTED;
177 exporting_options &= ~(EXPORTING_OPTIONS_SOURCE_BITS ^ EXPORTING_SOURCE_DATA_AS_COLLECTED);
178 } else if (!strcmp(data_source, "average")) {
179 exporting_options |= EXPORTING_SOURCE_DATA_AVERAGE;
180 exporting_options &= ~(EXPORTING_OPTIONS_SOURCE_BITS ^ EXPORTING_SOURCE_DATA_AVERAGE);
181 } else if (!strcmp(data_source, "sum") || !strcmp(data_source, "volume")) {
182 exporting_options |= EXPORTING_SOURCE_DATA_SUM;
183 exporting_options &= ~(EXPORTING_OPTIONS_SOURCE_BITS ^ EXPORTING_SOURCE_DATA_SUM);
184 } else {
185 netdata_log_error("EXPORTING: invalid data data_source method '%s'.", data_source);
186 }
187
188 return exporting_options;
189 }
190
191 /**
192 * Read configuration
193 *
194 * Based on read configuration an engine data structure is filled with exporting connector instances.
195 *
196 * @return Returns a filled engine data structure or NULL if there are no connector instances configured.
197 */
198 struct engine *read_exporting_config()
199 {
200 int instances_to_activate = 0;
201 int exporting_config_exists = 0;
202
203 static struct engine *engine = NULL;
204 struct connector_instance_list {
205 struct connector_instance local_ci;
206 EXPORTING_CONNECTOR_TYPE exporting_type;
207
208 struct connector_instance_list *next;
209 };
210 struct connector_instance local_ci;
211 struct connector_instance_list *tmp_ci_list = NULL, *tmp_ci_list1 = NULL, *tmp_ci_list_prev = NULL;
212
213 if (unlikely(engine))
214 return engine;
215
216 char *filename = filename_from_path_entry_strdupz(netdata_configured_user_config_dir, EXPORTING_CONF);
217
218 exporting_config_exists = inicfg_load(&exporting_config, filename, 0, NULL);
219 if (!exporting_config_exists) {
220 netdata_log_info("CONFIG: cannot load user exporting config '%s'. Will try the stock version.", filename);
221 freez(filename);
222
223 filename = filename_from_path_entry_strdupz(netdata_configured_stock_config_dir, EXPORTING_CONF);
224 exporting_config_exists = inicfg_load(&exporting_config, filename, 0, NULL);
225 if (!exporting_config_exists)
226 netdata_log_info("CONFIG: cannot load stock exporting config '%s'. Running with internal defaults.", filename);
227 }
228
229 freez(filename);
230
231 #define prometheus_config_get(name, value) \
232 inicfg_get( \
233 &exporting_config, CONFIG_SECTION_PROMETHEUS, name, \
234 inicfg_get(&exporting_config, CONFIG_SECTION_EXPORTING, name, value))
235 #define prometheus_config_get_number(name, value) \
236 inicfg_get_number( \
237 &exporting_config, CONFIG_SECTION_PROMETHEUS, name, \
238 inicfg_get_number(&exporting_config, CONFIG_SECTION_EXPORTING, name, value))
239 #define prometheus_config_get_boolean(name, value) \
240 inicfg_get_boolean( \
241 &exporting_config, CONFIG_SECTION_PROMETHEUS, name, \
242 inicfg_get_boolean(&exporting_config, CONFIG_SECTION_EXPORTING, name, value))
243
244 if (!prometheus_exporter_instance) {
245 prometheus_exporter_instance = callocz(1, sizeof(struct instance));
246
247 prometheus_exporter_instance->config.update_every =
248 prometheus_config_get_number(EXPORTING_UPDATE_EVERY_OPTION_NAME, EXPORTING_UPDATE_EVERY_DEFAULT);
249
250 prometheus_exporter_instance->config.options |= global_exporting_options & EXPORTING_OPTIONS_SOURCE_BITS;
251
252 const char *data_source = prometheus_config_get("data source", "average");
253 prometheus_exporter_instance->config.options =
254 exporting_parse_data_source(data_source, prometheus_exporter_instance->config.options);
255
256 if (prometheus_config_get_boolean(
257 "send names instead of ids", global_exporting_options & EXPORTING_OPTION_SEND_NAMES))
258 prometheus_exporter_instance->config.options |= EXPORTING_OPTION_SEND_NAMES;
259 else
260 prometheus_exporter_instance->config.options &= ~EXPORTING_OPTION_SEND_NAMES;
261
262 if (prometheus_config_get_boolean("send configured labels", CONFIG_BOOLEAN_YES))
263 prometheus_exporter_instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
264 else
265 prometheus_exporter_instance->config.options &= ~EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
266
267 if (prometheus_config_get_boolean("send automatic labels", CONFIG_BOOLEAN_NO))
268 prometheus_exporter_instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
269 else
270 prometheus_exporter_instance->config.options &= ~EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
271
272 prometheus_exporter_instance->config.charts_pattern = simple_pattern_create(
273 prometheus_config_get("send charts matching", "*"),
274 NULL,
275 SIMPLE_PATTERN_EXACT, true);
276 prometheus_exporter_instance->config.hosts_pattern = simple_pattern_create(
277 prometheus_config_get("send hosts matching", "localhost *"), NULL, SIMPLE_PATTERN_EXACT, true);
278
279 prometheus_exporter_instance->config.prefix = prometheus_config_get("prefix", global_exporting_prefix);
280
281 prometheus_exporter_instance->config.label_prefix = prometheus_config_get("netdata label prefix", "");
282
283 prometheus_exporter_instance->config.initialized = 1;
284 }
285
286 while (get_connector_instance(&local_ci)) {
287 netdata_log_info("Processing connector instance (%s)", local_ci.instance_name);
288
289 if (exporter_get_boolean(local_ci.instance_name, "enabled", 0)) {
290 netdata_log_info(
291 "Instance (%s) on connector (%s) is enabled and scheduled for activation",
292 local_ci.instance_name, local_ci.connector_name);
293
294 tmp_ci_list = (struct connector_instance_list *)callocz(1, sizeof(struct connector_instance_list));
295 memcpy(&tmp_ci_list->local_ci, &local_ci, sizeof(local_ci));
296 tmp_ci_list->exporting_type = exporting_select_type(local_ci.connector_name);
297 tmp_ci_list->next = tmp_ci_list_prev;
298 tmp_ci_list_prev = tmp_ci_list;
299 instances_to_activate++;
300 } else
301 netdata_log_info("Instance (%s) on connector (%s) is not enabled", local_ci.instance_name, local_ci.connector_name);
302 }
303
304 if (unlikely(!instances_to_activate)) {
305 netdata_log_info("No connector instances to activate");
306 return NULL;
307 }
308
309 engine = (struct engine *)callocz(1, sizeof(struct engine));
310 // TODO: Check and fill engine fields if actually needed
311
312 if (exporting_config_exists) {
313 engine->config.hostname =
314 strdupz(exporter_get(CONFIG_SECTION_EXPORTING, "hostname", netdata_configured_hostname));
315 engine->config.update_every = exporter_get_number(
316 CONFIG_SECTION_EXPORTING, EXPORTING_UPDATE_EVERY_OPTION_NAME, EXPORTING_UPDATE_EVERY_DEFAULT);
317 }
318
319 while (tmp_ci_list) {
320 struct instance *tmp_instance;
321 char *instance_name;
322 char *default_destination = "localhost";
323
324 netdata_log_info("Instance %s on %s", tmp_ci_list->local_ci.instance_name, tmp_ci_list->local_ci.connector_name);
325
326 if (tmp_ci_list->exporting_type == EXPORTING_CONNECTOR_TYPE_UNKNOWN) {
327 netdata_log_error("Unknown exporting connector type");
328 goto next_connector_instance;
329 }
330
331 #ifndef ENABLE_PROMETHEUS_REMOTE_WRITE
332 if (tmp_ci_list->exporting_type == EXPORTING_CONNECTOR_TYPE_PROMETHEUS_REMOTE_WRITE) {
333 netdata_log_error("Prometheus Remote Write support isn't compiled");
334 goto next_connector_instance;
335 }
336 #endif
337
338 #ifndef HAVE_KINESIS
339 if (tmp_ci_list->exporting_type == EXPORTING_CONNECTOR_TYPE_KINESIS) {
340 netdata_log_error("AWS Kinesis support isn't compiled");
341 goto next_connector_instance;
342 }
343 #endif
344
345 #ifndef ENABLE_EXPORTING_PUBSUB
346 if (tmp_ci_list->exporting_type == EXPORTING_CONNECTOR_TYPE_PUBSUB) {
347 netdata_log_error("Google Cloud Pub/Sub support isn't compiled");
348 goto next_connector_instance;
349 }
350 #endif
351
352 #ifndef HAVE_MONGOC
353 if (tmp_ci_list->exporting_type == EXPORTING_CONNECTOR_TYPE_MONGODB) {
354 netdata_log_error("MongoDB support isn't compiled");
355 goto next_connector_instance;
356 }
357 #endif
358
359 tmp_instance = (struct instance *)callocz(1, sizeof(struct instance));
360 tmp_instance->next = engine->instance_root;
361 engine->instance_root = tmp_instance;
362
363 tmp_instance->engine = engine;
364 tmp_instance->config.type = tmp_ci_list->exporting_type;
365
366 instance_name = tmp_ci_list->local_ci.instance_name;
367
368 tmp_instance->config.type_name = strdupz(tmp_ci_list->local_ci.connector_name);
369 tmp_instance->config.name = strdupz(tmp_ci_list->local_ci.instance_name);
370
371
372 tmp_instance->config.update_every =
373 exporter_get_number(instance_name, EXPORTING_UPDATE_EVERY_OPTION_NAME, EXPORTING_UPDATE_EVERY_DEFAULT);
374
375 tmp_instance->config.buffer_on_failures = exporter_get_number(instance_name, "buffer on failures", 10);
376
377 tmp_instance->config.timeoutms = exporter_get_number(instance_name, "timeout ms", 10000);
378
379 tmp_instance->config.charts_pattern =
380 simple_pattern_create(exporter_get(instance_name, "send charts matching", "*"), NULL,
381 SIMPLE_PATTERN_EXACT,
382 true);
383
384 tmp_instance->config.hosts_pattern = simple_pattern_create(
385 exporter_get(instance_name, "send hosts matching", "localhost *"), NULL, SIMPLE_PATTERN_EXACT, true);
386
387 const char *data_source = exporter_get(instance_name, "data source", "average");
388
389 tmp_instance->config.options = exporting_parse_data_source(data_source, tmp_instance->config.options);
390 if (EXPORTING_OPTIONS_DATA_SOURCE(tmp_instance->config.options) != EXPORTING_SOURCE_DATA_AS_COLLECTED &&
391 tmp_instance->config.update_every % localhost->rrd_update_every)
392 netdata_log_info(
393 "The update interval %d for instance %s is not a multiple of the database update interval %d. "
394 "Metric values will deviate at different points in time.",
395 tmp_instance->config.update_every, tmp_instance->config.name, localhost->rrd_update_every);
396
397 if (exporter_get_boolean(instance_name, "send configured labels", CONFIG_BOOLEAN_YES))
398 tmp_instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
399 else
400 tmp_instance->config.options &= ~EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
401
402 if (exporter_get_boolean(instance_name, "send automatic labels", CONFIG_BOOLEAN_NO))
403 tmp_instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
404 else
405 tmp_instance->config.options &= ~EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
406
407 if (exporter_get_boolean(instance_name, "send names instead of ids", CONFIG_BOOLEAN_YES))
408 tmp_instance->config.options |= EXPORTING_OPTION_SEND_NAMES;
409 else
410 tmp_instance->config.options &= ~EXPORTING_OPTION_SEND_NAMES;
411
412 if (exporter_get_boolean(instance_name, "send variables", CONFIG_BOOLEAN_YES))
413 tmp_instance->config.options |= EXPORTING_OPTION_SEND_VARIABLES;
414 else
415 tmp_instance->config.options &= ~EXPORTING_OPTION_SEND_VARIABLES;
416
417 if (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_PROMETHEUS_REMOTE_WRITE) {
418 struct prometheus_remote_write_specific_config *connector_specific_config =
419 callocz(1, sizeof(struct prometheus_remote_write_specific_config));
420
421 tmp_instance->config.connector_specific_config = connector_specific_config;
422
423 connector_specific_config->remote_write_path =
424 strdupz(exporter_get(instance_name, "remote write URL path", "/receive"));
425 }
426
427 if (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_KINESIS) {
428 struct aws_kinesis_specific_config *connector_specific_config =
429 callocz(1, sizeof(struct aws_kinesis_specific_config));
430
431 default_destination = "us-east-1";
432
433 tmp_instance->config.connector_specific_config = connector_specific_config;
434
435 connector_specific_config->stream_name = strdupz(exporter_get(instance_name, "stream name", ""));
436
437 connector_specific_config->auth_key_id = strdupz(exporter_get(instance_name, "aws_access_key_id", ""));
438 connector_specific_config->secure_key = strdupz(exporter_get(instance_name, "aws_secret_access_key", ""));
439 }
440
441 if (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_PUBSUB) {
442 struct pubsub_specific_config *connector_specific_config =
443 callocz(1, sizeof(struct pubsub_specific_config));
444
445 default_destination = "pubsub.googleapis.com";
446
447 tmp_instance->config.connector_specific_config = connector_specific_config;
448
449 connector_specific_config->credentials_file = strdupz(exporter_get(instance_name, "credentials file", ""));
450 connector_specific_config->project_id = strdupz(exporter_get(instance_name, "project id", ""));
451 connector_specific_config->topic_id = strdupz(exporter_get(instance_name, "topic id", ""));
452 }
453
454 if (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_MONGODB) {
455 struct mongodb_specific_config *connector_specific_config =
456 callocz(1, sizeof(struct mongodb_specific_config));
457
458 tmp_instance->config.connector_specific_config = connector_specific_config;
459
460 connector_specific_config->database = strdupz(exporter_get(
461 instance_name, "database", ""));
462
463 connector_specific_config->collection = strdupz(exporter_get(
464 instance_name, "collection", ""));
465 }
466
467 tmp_instance->config.destination = strdupz(exporter_get(instance_name, "destination", default_destination));
468
469 tmp_instance->config.username = strdupz(exporter_get(instance_name, "username", ""));
470
471 tmp_instance->config.password = strdupz(exporter_get(instance_name, "password", ""));
472
473 tmp_instance->config.prefix = strdupz(exporter_get(instance_name, "prefix", "netdata"));
474
475 tmp_instance->config.hostname = strdupz(exporter_get(instance_name, "hostname", engine->config.hostname));
476
477 #define STR_GRAPHITE_HTTPS "graphite:https"
478 #define STR_JSON_HTTPS "json:https"
479 #define STR_OPENTSDB_HTTPS "opentsdb:https"
480 #define STR_PROMETHEUS_REMOTE_WRITE_HTTPS "prometheus_remote_write:https"
481
482 if ((tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_GRAPHITE_HTTP &&
483 !strncmp(tmp_ci_list->local_ci.connector_name, STR_GRAPHITE_HTTPS, strlen(STR_GRAPHITE_HTTPS))) ||
484 (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP &&
485 !strncmp(tmp_ci_list->local_ci.connector_name, STR_JSON_HTTPS, strlen(STR_JSON_HTTPS))) ||
486 (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_OPENTSDB_HTTP &&
487 !strncmp(tmp_ci_list->local_ci.connector_name, STR_OPENTSDB_HTTPS, strlen(STR_OPENTSDB_HTTPS))) ||
488 (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_PROMETHEUS_REMOTE_WRITE &&
489 !strncmp(
490 tmp_ci_list->local_ci.connector_name, STR_PROMETHEUS_REMOTE_WRITE_HTTPS,
491 strlen(STR_PROMETHEUS_REMOTE_WRITE_HTTPS)))) {
492 tmp_instance->config.options |= EXPORTING_OPTION_USE_TLS;
493 }
494
495 #ifdef NETDATA_INTERNAL_CHECKS
496 netdata_log_info(
497 " Dest=[%s], upd=[%d], buffer=[%d] timeout=[%ld] options=[%u]",
498 tmp_instance->config.destination,
499 tmp_instance->config.update_every,
500 tmp_instance->config.buffer_on_failures,
501 tmp_instance->config.timeoutms,
502 tmp_instance->config.options);
503 #endif
504
505 if (unlikely(!exporting_config_exists) && !engine->config.hostname) {
506 engine->config.hostname = strdupz(inicfg_get(&netdata_config, instance_name, "hostname", netdata_configured_hostname));
507 engine->config.update_every =
508 inicfg_get_number(&netdata_config, instance_name, EXPORTING_UPDATE_EVERY_OPTION_NAME, EXPORTING_UPDATE_EVERY_DEFAULT);
509 }
510
511 next_connector_instance:
512 tmp_ci_list1 = tmp_ci_list->next;
513 freez(tmp_ci_list);
514 tmp_ci_list = tmp_ci_list1;
515 }
516
517 return engine;
518 }