read_config.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "exporting_engine.h"
  3. struct config exporting_config = { .first_section = NULL,
  4. .last_section = NULL,
  5. .mutex = NETDATA_MUTEX_INITIALIZER,
  6. .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
  7. .rwlock = AVL_LOCK_INITIALIZER } };
  8. struct instance *prometheus_exporter_instance = NULL;
  9. static _CONNECTOR_INSTANCE *find_instance(const char *section)
  10. {
  11. _CONNECTOR_INSTANCE *local_ci;
  12. local_ci = add_connector_instance(NULL, NULL); // Get root section
  13. if (unlikely(!local_ci))
  14. return local_ci;
  15. if (!section)
  16. return local_ci;
  17. while (local_ci) {
  18. if (!strcmp(local_ci->instance_name, section))
  19. break;
  20. local_ci = local_ci->next;
  21. }
  22. return local_ci;
  23. }
  24. char *expconfig_get(struct config *root, const char *section, const char *name, const char *default_value)
  25. {
  26. _CONNECTOR_INSTANCE *local_ci;
  27. if (!strcmp(section, CONFIG_SECTION_EXPORTING))
  28. return appconfig_get(root, CONFIG_SECTION_EXPORTING, name, default_value);
  29. local_ci = find_instance(section);
  30. if (!local_ci)
  31. return NULL; // TODO: Check if it is meaningful to return default_value
  32. return appconfig_get(
  33. root, local_ci->instance_name, name,
  34. appconfig_get(
  35. root, local_ci->connector_name, name, appconfig_get(root, CONFIG_SECTION_EXPORTING, name, default_value)));
  36. }
  37. int expconfig_get_boolean(struct config *root, const char *section, const char *name, int default_value)
  38. {
  39. _CONNECTOR_INSTANCE *local_ci;
  40. if (!strcmp(section, CONFIG_SECTION_EXPORTING))
  41. return appconfig_get_boolean(root, CONFIG_SECTION_EXPORTING, name, default_value);
  42. local_ci = find_instance(section);
  43. if (!local_ci)
  44. return 0; // TODO: Check if it is meaningful to return default_value
  45. return appconfig_get_boolean(
  46. root, local_ci->instance_name, name,
  47. appconfig_get_boolean(
  48. root, local_ci->connector_name, name,
  49. appconfig_get_boolean(root, CONFIG_SECTION_EXPORTING, name, default_value)));
  50. }
  51. long long expconfig_get_number(struct config *root, const char *section, const char *name, long long default_value)
  52. {
  53. _CONNECTOR_INSTANCE *local_ci;
  54. if (!strcmp(section, CONFIG_SECTION_EXPORTING))
  55. return appconfig_get_number(root, CONFIG_SECTION_EXPORTING, name, default_value);
  56. local_ci = find_instance(section);
  57. if (!local_ci)
  58. return 0; // TODO: Check if it is meaningful to return default_value
  59. return appconfig_get_number(
  60. root, local_ci->instance_name, name,
  61. appconfig_get_number(
  62. root, local_ci->connector_name, name,
  63. appconfig_get_number(root, CONFIG_SECTION_EXPORTING, name, default_value)));
  64. }
  65. /*
  66. * Get the next connector instance that we need to activate
  67. *
  68. * @param @target_ci will be filled with instance name and connector name
  69. *
  70. * @return - 1 if more connectors to be fetched, 0 done
  71. *
  72. */
  73. int get_connector_instance(struct connector_instance *target_ci)
  74. {
  75. static _CONNECTOR_INSTANCE *local_ci = NULL;
  76. _CONNECTOR_INSTANCE *global_connector_instance;
  77. global_connector_instance = find_instance(NULL); // Fetch head of instances
  78. if (unlikely(!global_connector_instance))
  79. return 0;
  80. if (target_ci == NULL) {
  81. local_ci = NULL;
  82. return 1;
  83. }
  84. if (local_ci == NULL)
  85. local_ci = global_connector_instance;
  86. else {
  87. local_ci = local_ci->next;
  88. if (local_ci == NULL)
  89. return 0;
  90. }
  91. strcpy(target_ci->instance_name, local_ci->instance_name);
  92. strcpy(target_ci->connector_name, local_ci->connector_name);
  93. return 1;
  94. }
  95. /**
  96. * Select Type
  97. *
  98. * Select the connector type based on the user input
  99. *
  100. * @param type is the string that defines the connector type
  101. *
  102. * @return It returns the connector id.
  103. */
  104. EXPORTING_CONNECTOR_TYPE exporting_select_type(const char *type)
  105. {
  106. if (!strcmp(type, "graphite") || !strcmp(type, "graphite:plaintext")) {
  107. return EXPORTING_CONNECTOR_TYPE_GRAPHITE;
  108. } else if (!strcmp(type, "opentsdb") || !strcmp(type, "opentsdb:telnet")) {
  109. return EXPORTING_CONNECTOR_TYPE_OPENTSDB_USING_TELNET;
  110. } else if (!strcmp(type, "opentsdb:http") || !strcmp(type, "opentsdb:https")) {
  111. return EXPORTING_CONNECTOR_TYPE_OPENTSDB_USING_HTTP;
  112. } else if (!strcmp(type, "json") || !strcmp(type, "json:plaintext")) {
  113. return EXPORTING_CONNECTOR_TYPE_JSON;
  114. } else if (!strcmp(type, "prometheus_remote_write")) {
  115. return EXPORTING_CONNECTOR_TYPE_PROMETHEUS_REMOTE_WRITE;
  116. } else if (!strcmp(type, "kinesis") || !strcmp(type, "kinesis:plaintext")) {
  117. return EXPORTING_CONNECTOR_TYPE_KINESIS;
  118. } else if (!strcmp(type, "pubsub") || !strcmp(type, "pubsub:plaintext")) {
  119. return EXPORTING_CONNECTOR_TYPE_PUBSUB;
  120. } else if (!strcmp(type, "mongodb") || !strcmp(type, "mongodb:plaintext"))
  121. return EXPORTING_CONNECTOR_TYPE_MONGODB;
  122. return EXPORTING_CONNECTOR_TYPE_UNKNOWN;
  123. }
  124. EXPORTING_OPTIONS exporting_parse_data_source(const char *data_source, EXPORTING_OPTIONS exporting_options)
  125. {
  126. if (!strcmp(data_source, "raw") || !strcmp(data_source, "as collected") || !strcmp(data_source, "as-collected") ||
  127. !strcmp(data_source, "as_collected") || !strcmp(data_source, "ascollected")) {
  128. exporting_options |= EXPORTING_SOURCE_DATA_AS_COLLECTED;
  129. exporting_options &= ~(EXPORTING_OPTIONS_SOURCE_BITS ^ EXPORTING_SOURCE_DATA_AS_COLLECTED);
  130. } else if (!strcmp(data_source, "average")) {
  131. exporting_options |= EXPORTING_SOURCE_DATA_AVERAGE;
  132. exporting_options &= ~(EXPORTING_OPTIONS_SOURCE_BITS ^ EXPORTING_SOURCE_DATA_AVERAGE);
  133. } else if (!strcmp(data_source, "sum") || !strcmp(data_source, "volume")) {
  134. exporting_options |= EXPORTING_SOURCE_DATA_SUM;
  135. exporting_options &= ~(EXPORTING_OPTIONS_SOURCE_BITS ^ EXPORTING_SOURCE_DATA_SUM);
  136. } else {
  137. error("EXPORTING: invalid data data_source method '%s'.", data_source);
  138. }
  139. return exporting_options;
  140. }
  141. /**
  142. * Read configuration
  143. *
  144. * Based on read configuration an engine data structure is filled with exporting connector instances.
  145. *
  146. * @return Returns a filled engine data structure or NULL if there are no connector instances configured.
  147. */
  148. struct engine *read_exporting_config()
  149. {
  150. int instances_to_activate = 0;
  151. int exporting_config_exists = 0;
  152. static struct engine *engine = NULL;
  153. struct connector_instance_list {
  154. struct connector_instance local_ci;
  155. EXPORTING_CONNECTOR_TYPE backend_type;
  156. struct connector_instance_list *next;
  157. };
  158. struct connector_instance local_ci;
  159. struct connector_instance_list *tmp_ci_list, *tmp_ci_list1, *tmp_ci_list_prev = NULL;
  160. if (unlikely(engine))
  161. return engine;
  162. char *filename = strdupz_path_subpath(netdata_configured_user_config_dir, EXPORTING_CONF);
  163. exporting_config_exists = appconfig_load(&exporting_config, filename, 0, NULL);
  164. if (!exporting_config_exists) {
  165. info("CONFIG: cannot load user exporting config '%s'. Will try the stock version.", filename);
  166. freez(filename);
  167. filename = strdupz_path_subpath(netdata_configured_stock_config_dir, EXPORTING_CONF);
  168. exporting_config_exists = appconfig_load(&exporting_config, filename, 0, NULL);
  169. if (!exporting_config_exists)
  170. info("CONFIG: cannot load stock exporting config '%s'. Running with internal defaults.", filename);
  171. }
  172. freez(filename);
  173. #define prometheus_config_get(name, value) \
  174. appconfig_get( \
  175. &exporting_config, CONFIG_SECTION_PROMETHEUS, name, \
  176. appconfig_get(&exporting_config, CONFIG_SECTION_EXPORTING, name, value))
  177. #define prometheus_config_get_number(name, value) \
  178. appconfig_get_number( \
  179. &exporting_config, CONFIG_SECTION_PROMETHEUS, name, \
  180. appconfig_get_number(&exporting_config, CONFIG_SECTION_EXPORTING, name, value))
  181. #define prometheus_config_get_boolean(name, value) \
  182. appconfig_get_boolean( \
  183. &exporting_config, CONFIG_SECTION_PROMETHEUS, name, \
  184. appconfig_get_boolean(&exporting_config, CONFIG_SECTION_EXPORTING, name, value))
  185. if (!prometheus_exporter_instance) {
  186. prometheus_exporter_instance = callocz(1, sizeof(struct instance));
  187. prometheus_exporter_instance->config.update_every =
  188. prometheus_config_get_number(EXPORTING_UPDATE_EVERY_OPTION_NAME, EXPORTING_UPDATE_EVERY_DEFAULT);
  189. if (prometheus_config_get_boolean("send names instead of ids", CONFIG_BOOLEAN_YES))
  190. prometheus_exporter_instance->config.options |= EXPORTING_OPTION_SEND_NAMES;
  191. else
  192. prometheus_exporter_instance->config.options &= ~EXPORTING_OPTION_SEND_NAMES;
  193. if (prometheus_config_get_boolean("send configured labels", CONFIG_BOOLEAN_YES))
  194. prometheus_exporter_instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
  195. else
  196. prometheus_exporter_instance->config.options &= ~EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
  197. if (prometheus_config_get_boolean("send automatic labels", CONFIG_BOOLEAN_NO))
  198. prometheus_exporter_instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
  199. else
  200. prometheus_exporter_instance->config.options &= ~EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
  201. prometheus_exporter_instance->config.charts_pattern =
  202. simple_pattern_create(prometheus_config_get("send charts matching", "*"), NULL, SIMPLE_PATTERN_EXACT);
  203. prometheus_exporter_instance->config.hosts_pattern = simple_pattern_create(
  204. prometheus_config_get("send hosts matching", "localhost *"), NULL, SIMPLE_PATTERN_EXACT);
  205. }
  206. // TODO: change BACKEND to EXPORTING
  207. while (get_connector_instance(&local_ci)) {
  208. info("Processing connector instance (%s)", local_ci.instance_name);
  209. if (exporter_get_boolean(local_ci.instance_name, "enabled", 0)) {
  210. info(
  211. "Instance (%s) on connector (%s) is enabled and scheduled for activation",
  212. local_ci.instance_name, local_ci.connector_name);
  213. tmp_ci_list = (struct connector_instance_list *)callocz(1, sizeof(struct connector_instance_list));
  214. memcpy(&tmp_ci_list->local_ci, &local_ci, sizeof(local_ci));
  215. tmp_ci_list->backend_type = exporting_select_type(local_ci.connector_name);
  216. tmp_ci_list->next = tmp_ci_list_prev;
  217. tmp_ci_list_prev = tmp_ci_list;
  218. instances_to_activate++;
  219. } else
  220. info("Instance (%s) on connector (%s) is not enabled", local_ci.instance_name, local_ci.connector_name);
  221. }
  222. if (unlikely(!instances_to_activate)) {
  223. info("No connector instances to activate");
  224. return NULL;
  225. }
  226. engine = (struct engine *)callocz(1, sizeof(struct engine));
  227. // TODO: Check and fill engine fields if actually needed
  228. if (exporting_config_exists) {
  229. engine->config.hostname =
  230. strdupz(exporter_get(CONFIG_SECTION_EXPORTING, "hostname", netdata_configured_hostname));
  231. engine->config.prefix = strdupz(exporter_get(CONFIG_SECTION_EXPORTING, "prefix", "netdata"));
  232. engine->config.update_every = exporter_get_number(
  233. CONFIG_SECTION_EXPORTING, EXPORTING_UPDATE_EVERY_OPTION_NAME, EXPORTING_UPDATE_EVERY_DEFAULT);
  234. }
  235. while (tmp_ci_list) {
  236. struct instance *tmp_instance;
  237. char *instance_name;
  238. char *default_destination = "localhost";
  239. info("Instance %s on %s", tmp_ci_list->local_ci.instance_name, tmp_ci_list->local_ci.connector_name);
  240. if (tmp_ci_list->backend_type == EXPORTING_CONNECTOR_TYPE_UNKNOWN) {
  241. error("Unknown exporting connector type");
  242. goto next_connector_instance;
  243. }
  244. #ifndef ENABLE_PROMETHEUS_REMOTE_WRITE
  245. if (tmp_ci_list->backend_type == EXPORTING_CONNECTOR_TYPE_PROMETHEUS_REMOTE_WRITE) {
  246. error("Prometheus Remote Write support isn't compiled");
  247. goto next_connector_instance;
  248. }
  249. #endif
  250. #ifndef HAVE_KINESIS
  251. if (tmp_ci_list->backend_type == EXPORTING_CONNECTOR_TYPE_KINESIS) {
  252. error("AWS Kinesis support isn't compiled");
  253. goto next_connector_instance;
  254. }
  255. #endif
  256. #ifndef ENABLE_EXPORTING_PUBSUB
  257. if (tmp_ci_list->backend_type == EXPORTING_CONNECTOR_TYPE_PUBSUB) {
  258. error("Google Cloud Pub/Sub support isn't compiled");
  259. goto next_connector_instance;
  260. }
  261. #endif
  262. #ifndef HAVE_MONGOC
  263. if (tmp_ci_list->backend_type == EXPORTING_CONNECTOR_TYPE_MONGODB) {
  264. error("MongoDB support isn't compiled");
  265. goto next_connector_instance;
  266. }
  267. #endif
  268. tmp_instance = (struct instance *)callocz(1, sizeof(struct instance));
  269. tmp_instance->next = engine->instance_root;
  270. engine->instance_root = tmp_instance;
  271. tmp_instance->engine = engine;
  272. tmp_instance->config.type = tmp_ci_list->backend_type;
  273. instance_name = tmp_ci_list->local_ci.instance_name;
  274. tmp_instance->config.name = strdupz(tmp_ci_list->local_ci.instance_name);
  275. tmp_instance->config.update_every =
  276. exporter_get_number(instance_name, EXPORTING_UPDATE_EVERY_OPTION_NAME, EXPORTING_UPDATE_EVERY_DEFAULT);
  277. tmp_instance->config.buffer_on_failures = exporter_get_number(instance_name, "buffer on failures", 10);
  278. tmp_instance->config.timeoutms = exporter_get_number(instance_name, "timeout ms", 10000);
  279. tmp_instance->config.charts_pattern =
  280. simple_pattern_create(exporter_get(instance_name, "send charts matching", "*"), NULL, SIMPLE_PATTERN_EXACT);
  281. tmp_instance->config.hosts_pattern = simple_pattern_create(
  282. exporter_get(instance_name, "send hosts matching", "localhost *"), NULL, SIMPLE_PATTERN_EXACT);
  283. char *data_source = exporter_get(instance_name, "data source", "average");
  284. tmp_instance->config.options = exporting_parse_data_source(data_source, tmp_instance->config.options);
  285. if (exporter_get_boolean(instance_name, "send configured labels", CONFIG_BOOLEAN_YES))
  286. tmp_instance->config.options |= EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
  287. else
  288. tmp_instance->config.options &= ~EXPORTING_OPTION_SEND_CONFIGURED_LABELS;
  289. if (exporter_get_boolean(instance_name, "send automatic labels", CONFIG_BOOLEAN_NO))
  290. tmp_instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
  291. else
  292. tmp_instance->config.options &= ~EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
  293. if (exporter_get_boolean(instance_name, "send names instead of ids", CONFIG_BOOLEAN_YES))
  294. tmp_instance->config.options |= EXPORTING_OPTION_SEND_NAMES;
  295. else
  296. tmp_instance->config.options &= ~EXPORTING_OPTION_SEND_NAMES;
  297. if (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_PROMETHEUS_REMOTE_WRITE) {
  298. struct prometheus_remote_write_specific_config *connector_specific_config =
  299. callocz(1, sizeof(struct prometheus_remote_write_specific_config));
  300. tmp_instance->config.connector_specific_config = connector_specific_config;
  301. connector_specific_config->remote_write_path =
  302. strdupz(exporter_get(instance_name, "remote write URL path", "/receive"));
  303. }
  304. if (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_KINESIS) {
  305. struct aws_kinesis_specific_config *connector_specific_config =
  306. callocz(1, sizeof(struct aws_kinesis_specific_config));
  307. default_destination = "us-east-1";
  308. tmp_instance->config.connector_specific_config = connector_specific_config;
  309. connector_specific_config->stream_name = strdupz(exporter_get(instance_name, "stream name", "netdata"));
  310. connector_specific_config->auth_key_id = strdupz(exporter_get(instance_name, "aws_access_key_id", ""));
  311. connector_specific_config->secure_key = strdupz(exporter_get(instance_name, "aws_secret_access_key", ""));
  312. }
  313. if (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_PUBSUB) {
  314. struct pubsub_specific_config *connector_specific_config =
  315. callocz(1, sizeof(struct pubsub_specific_config));
  316. default_destination = "pubsub.googleapis.com";
  317. tmp_instance->config.connector_specific_config = connector_specific_config;
  318. connector_specific_config->credentials_file = strdupz(exporter_get(instance_name, "credentials file", ""));
  319. connector_specific_config->project_id = strdupz(exporter_get(instance_name, "project id", ""));
  320. connector_specific_config->topic_id = strdupz(exporter_get(instance_name, "topic id", ""));
  321. }
  322. if (tmp_instance->config.type == EXPORTING_CONNECTOR_TYPE_MONGODB) {
  323. struct mongodb_specific_config *connector_specific_config =
  324. callocz(1, sizeof(struct mongodb_specific_config));
  325. tmp_instance->config.connector_specific_config = connector_specific_config;
  326. connector_specific_config->database = strdupz(exporter_get(
  327. instance_name, "database", ""));
  328. connector_specific_config->collection = strdupz(exporter_get(
  329. instance_name, "collection", ""));
  330. }
  331. tmp_instance->config.destination = strdupz(exporter_get(instance_name, "destination", default_destination));
  332. #ifdef NETDATA_INTERNAL_CHECKS
  333. info(
  334. " Dest=[%s], upd=[%d], buffer=[%d] timeout=[%ld] options=[%u]",
  335. tmp_instance->config.destination,
  336. tmp_instance->config.update_every,
  337. tmp_instance->config.buffer_on_failures,
  338. tmp_instance->config.timeoutms,
  339. tmp_instance->config.options);
  340. #endif
  341. if (unlikely(!exporting_config_exists) && !engine->config.hostname) {
  342. engine->config.hostname = strdupz(config_get(instance_name, "hostname", netdata_configured_hostname));
  343. engine->config.prefix = strdupz(config_get(instance_name, "prefix", "netdata"));
  344. engine->config.update_every =
  345. config_get_number(instance_name, EXPORTING_UPDATE_EVERY_OPTION_NAME, EXPORTING_UPDATE_EVERY_DEFAULT);
  346. }
  347. next_connector_instance:
  348. tmp_ci_list1 = tmp_ci_list->next;
  349. freez(tmp_ci_list);
  350. tmp_ci_list = tmp_ci_list1;
  351. }
  352. return engine;
  353. }