diff options
author | Vladimir Kobal <vlad@prokk.net> | 2019-12-12 21:41:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-12 21:41:11 +0200 |
commit | 6f270819121afb743d783f9a72786e367d1048a3 (patch) | |
tree | 6e17d6764e5c6d72bb53bbfcc35cecce4b18d325 /libnetdata/config | |
parent | 7278d5bcd987fb9646da4a8a837173bae3b68459 (diff) |
Implement the main flow for the Exporting Engine (#7149)
* Add top level tests
* Add a skeleton for preparing buffers
* Initialize graphite instance
* Prepare buffers for all instances
* Add Grafite collected value formatter
* Add support for exporting.conf read and parsing
* - Use new exporting_config instead of netdata_config
* Implement Grafite worker
* Disable exporting engine compilation if libuv is not available
* Add mutex locks
- Configure connectors as connector_<type> in sections of exporting.conf
- Change exporting_select_type to check for connector_ fields
* - Override exporting_config structure if there no exporting.conf so that
look ups don't fail and we maintain backwards compatibility
* Separate fixtures in unit tests
* Test exporting_discard_responce
* Test response receiving
* Test buffer sending
* Test simple connector worker
- Instance section has the format connector:instance_name
e.g graphite:my_graphite_instance
- Connectors with : in their name e.g graphite:plaintext are reserved
So graphite:plaintext is not accepted because it would activate an
instance with name "plaintext"
It should be graphite:plaintext:instance_name
* - Enable the add_connector_instance to cleanup the internal structure
by passing NULL,not NULL arguments
* Implement configurable update interval
- Add additional check to verify instance uniqueness across connectors
* Add host and chart filters
* Add the value calculation over a database series
* Add the calculated over stored data graphite connector
* Add tests for graphite connector
* Add JSON connector
* Add tests for JSON formatting functions
* Add OpenTSDB connector
* Add tests for the OpenTSDB connector
* Add temporaty notes to the documentation
Diffstat (limited to 'libnetdata/config')
-rw-r--r-- | libnetdata/config/appconfig.c | 165 | ||||
-rw-r--r-- | libnetdata/config/appconfig.h | 41 |
2 files changed, 190 insertions, 16 deletions
diff --git a/libnetdata/config/appconfig.c b/libnetdata/config/appconfig.c index 65c36c2818..3774db5549 100644 --- a/libnetdata/config/appconfig.c +++ b/libnetdata/config/appconfig.c @@ -42,6 +42,89 @@ struct section { // readers are protected using the rwlock in avl_tree_lock }; +/* + * @Input: + * Connector / instance to add to an internal structure + * @Return + * The current head of the linked list of connector_instance + * + */ + +_CONNECTOR_INSTANCE *add_connector_instance(struct section *connector, struct section *instance) +{ + static struct _connector_instance *global_connector_instance = NULL; + struct _connector_instance *local_ci, *local_ci_tmp; + + if (unlikely(!connector)) { + if (unlikely(!instance)) + return global_connector_instance; + + local_ci = global_connector_instance; + while (local_ci) { + local_ci_tmp = local_ci->next; + freez(local_ci); + local_ci = local_ci_tmp; + } + global_connector_instance = NULL; + return NULL; + } + + local_ci = callocz(1, sizeof(struct _connector_instance)); + local_ci->instance = instance; + local_ci->connector = connector; + strncpy(local_ci->instance_name, instance->name, CONFIG_MAX_NAME); + strncpy(local_ci->connector_name, connector->name, CONFIG_MAX_NAME); + local_ci->next = global_connector_instance; + global_connector_instance = local_ci; + + return global_connector_instance; +} + +int is_valid_connector(char *type, int check_reserved) +{ + int rc = 1; + + if (unlikely(!type)) + return 0; + + if (!check_reserved) { + if (unlikely(is_valid_connector(type,1))) { + return 0; + } + //if (unlikely(*type == ':') + // return 0; + char *separator = strrchr(type, ':'); + if (likely(separator)) { + *separator = '\0'; + rc = separator - type; + } else + return 0; + } +// else { +// if (unlikely(is_valid_connector(type,1))) { +// error("Section %s invalid -- reserved name", type); +// return 0; +// } +// } + + if (!strcmp(type, "graphite") || !strcmp(type, "graphite:plaintext")) { + return rc; + } else if (!strcmp(type, "opentsdb") || !strcmp(type, "opentsdb:telnet")) { + return rc; + } else if (!strcmp(type, "opentsdb:http") || !strcmp(type, "opentsdb:https")) { + return rc; + } else if (!strcmp(type, "json") || !strcmp(type, "json:plaintext")) { + return rc; + } else if (!strcmp(type, "prometheus_remote_write")) { + return rc; + } else if (!strcmp(type, "kinesis") || !strcmp(type, "kinesis:plaintext")) { + return rc; + } else if (!strcmp(type, "mongodb") || !strcmp(type, "mongodb:plaintext")) { + return rc; + } + + return 0; +} // ---------------------------------------------------------------------------- // locking @@ -241,6 +324,7 @@ cleanup: return ret; } + char *appconfig_get(struct config *root, const char *section, const char *name, const char *default_value) { struct config_option *cv; @@ -440,6 +524,14 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) { int line = 0; struct section *co = NULL; + int is_exporter_config = 0; + int is_backend_config = 0; + int have_backend_config = 0; + int _backends = 0; // number of backend sections we have + char working_instance[CONFIG_MAX_NAME + 1]; + char working_connector[CONFIG_MAX_NAME + 1]; + struct section *working_connector_section = NULL; + int global_exporting_section = 0; char buffer[CONFIG_FILE_LINE_MAX + 1], *s; @@ -453,6 +545,8 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) return 0; } + is_exporter_config = (strstr(filename, EXPORTING_CONF) != NULL); + while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) { buffer[CONFIG_FILE_LINE_MAX] = '\0'; line++; @@ -469,6 +563,46 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) s[len - 1] = '\0'; s++; + if (is_exporter_config) { + global_exporting_section = !(strcmp(s, CONFIG_SECTION_EXPORTING)); + if (unlikely(!global_exporting_section)) { + int rc; + rc = is_valid_connector(s, 0); + if (likely(rc)) { + strncpy(working_connector, s, CONFIG_MAX_NAME); + s = s + rc + 1; + if (unlikely(!(*s))) { + _backends++; + sprintf(buffer, "instance_%d", _backends); + s = buffer; + } + strncpy(working_instance, s, CONFIG_MAX_NAME); + working_connector_section = NULL; + if (unlikely(appconfig_section_find(root, working_instance))) { + error("Instance (%s) already exists", working_instance); + co = NULL; + continue; + } + } else { + co = NULL; + error("Section (%s) does not specify a valid connector", s); + continue; + } + } + } + + is_backend_config = !(strcmp(s, CONFIG_SECTION_BACKEND)); + if (!have_backend_config) + have_backend_config = is_backend_config; + + if (is_backend_config) { + if (_backends) { + sprintf(buffer, CONFIG_SECTION_BACKEND "/%d", _backends); + s = buffer; + } + _backends++; + } + co = appconfig_section_find(root, s); if(!co) co = appconfig_section_create(root, s); @@ -502,15 +636,32 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) struct config_option *cv = appconfig_option_index_find(co, name, 0); - if(!cv) cv = appconfig_value_create(co, name, value); - else { - if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) { - debug(D_CONFIG, "CONFIG: line %d of file '%s', overwriting '%s/%s'.", line, filename, co->name, cv->name); + if (!cv) { + cv = appconfig_value_create(co, name, value); + if (likely(is_exporter_config) && unlikely(!global_exporting_section)) { + if (unlikely(!working_connector_section)) { + working_connector_section = appconfig_section_find(root, working_connector); + if (!working_connector_section) + working_connector_section = appconfig_section_create(root, working_connector); + if (likely(working_connector_section)) { + add_connector_instance(working_connector_section, co); + } + } + } + } else { + if (((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) { + debug( + D_CONFIG, "CONFIG: line %d of file '%s', overwriting '%s/%s'.", line, filename, co->name, cv->name); freez(cv->value); cv->value = strdupz(value); - } - else - debug(D_CONFIG, "CONFIG: ignoring line %d of file '%s', '%s/%s' is already present and used.", line, filename, co->name, cv->name); + } else + debug( + D_CONFIG, + "CONFIG: ignoring line %d of file '%s', '%s/%s' is already present and used.", + line, + filename, + co->name, + cv->name); } cv->flags |= CONFIG_VALUE_LOADED; } diff --git a/libnetdata/config/appconfig.h b/libnetdata/config/appconfig.h index 32e289f9cd..1a60c46399 100644 --- a/libnetdata/config/appconfig.h +++ b/libnetdata/config/appconfig.h @@ -82,15 +82,17 @@ #define CONFIG_FILENAME "netdata.conf" -#define CONFIG_SECTION_GLOBAL "global" -#define CONFIG_SECTION_WEB "web" -#define CONFIG_SECTION_STATSD "statsd" -#define CONFIG_SECTION_PLUGINS "plugins" -#define CONFIG_SECTION_CLOUD "cloud" -#define CONFIG_SECTION_REGISTRY "registry" -#define CONFIG_SECTION_HEALTH "health" -#define CONFIG_SECTION_BACKEND "backend" -#define CONFIG_SECTION_STREAM "stream" +#define CONFIG_SECTION_GLOBAL "global" +#define CONFIG_SECTION_WEB "web" +#define CONFIG_SECTION_STATSD "statsd" +#define CONFIG_SECTION_PLUGINS "plugins" +#define CONFIG_SECTION_CLOUD "cloud" +#define CONFIG_SECTION_REGISTRY "registry" +#define CONFIG_SECTION_HEALTH "health" +#define CONFIG_SECTION_BACKEND "backend" +#define CONFIG_SECTION_STREAM "stream" +#define CONFIG_SECTION_EXPORTING "exporting:global" +#define EXPORTING_CONF "exporting.conf" // these are used to limit the configuration names and values lengths // they are not enforced by config.c functions (they will strdup() all strings, no matter of their length) @@ -103,6 +105,11 @@ struct config { avl_tree_lock index; }; +//struct connector_instance { +// char instance_name[CONFIG_MAX_NAME + 1]; +// char connector_name[CONFIG_MAX_NAME + 1]; +//}; + #define CONFIG_BOOLEAN_INVALID 100 // an invalid value to check for validity (used as default initialization when needed) #define CONFIG_BOOLEAN_NO 0 // disabled @@ -136,4 +143,20 @@ extern int appconfig_section_compare(void *a, void *b); extern int config_parse_duration(const char* string, int* result); + +struct connector_instance { + char instance_name[CONFIG_MAX_NAME + 1]; + char connector_name[CONFIG_MAX_NAME + 1]; +}; + +typedef struct _connector_instance { + struct section *connector; // actual connector + struct section *instance; // This instance + char instance_name[CONFIG_MAX_NAME + 1]; + char connector_name[CONFIG_MAX_NAME + 1]; + struct _connector_instance *next; // Next instance +} _CONNECTOR_INSTANCE; + +extern _CONNECTOR_INSTANCE *add_connector_instance(struct section *connector, struct section *instance); + #endif /* NETDATA_CONFIG_H */ |