0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-16 18:37:50 +00:00

Parse host tags ()

* Fix memory leaks

* Check for configuration options

* Parse simple tags

* Parse JSON tags

* Remove an unnecessary check

* Parse a JSON object

* Parse a JSON array

* Update the documentation

* Fix host locks
This commit is contained in:
Vladimir Kobal 2020-02-01 00:05:45 +02:00 committed by GitHub
parent 43bc627b1d
commit 177af26ea8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 204 additions and 4 deletions
backends
daemon
database
exporting
graphite
json
opentsdb
health
libnetdata/config
streaming
web/api

View file

@ -183,9 +183,14 @@ from your Netdata):
are different: disks with device-mapper, interrupts, QoS classes, statsd synthetic charts, etc.
- `host tags = list of TAG=VALUE` defines tags that should be appended on all metrics for the given host. These are
currently only sent to opentsdb and prometheus. Please use the appropriate format for each time-series db. For
example opentsdb likes them like `TAG1=VALUE1 TAG2=VALUE2`, but prometheus like `tag1="value1",tag2="value2"`. Host
tags are mirrored with database replication (streaming of metrics between Netdata servers).
currently only sent to graphite, json, opentsdb and prometheus. Please use the appropriate format for each
time-series db. For example opentsdb likes them like `TAG1=VALUE1 TAG2=VALUE2`, but prometheus like `tag1="value1",
tag2="value2"`. Host tags are mirrored with database replication (streaming of metrics between Netdata servers).
Starting from Netdata v1.20 the host tags are parsed in accordance with a configured backend type and stored as
host labels so that they can be reused in API responses and exporting connectors. The parsing is supported for
graphite, json, opentsdb, and prometheus (default) backend types. You can check how the host tags were parsed using
the /api/v1/info API call.
## monitoring operation

View file

@ -203,6 +203,7 @@ static cmd_status_t cmd_reload_labels_execute(char *args, char **message)
BUFFER *wb = buffer_create(10);
rrdhost_rdlock(localhost);
netdata_rwlock_rdlock(&localhost->labels_rwlock);
struct label *l=localhost->labels;
while (l != NULL) {
@ -210,6 +211,7 @@ static cmd_status_t cmd_reload_labels_execute(char *args, char **message)
l = l->next;
}
netdata_rwlock_unlock(&localhost->labels_rwlock);
rrdhost_unlock(localhost);
(*message)=strdupz(buffer_tostring(wb));
buffer_free(wb);

View file

@ -662,6 +662,7 @@ static void rrdcalc_labels_unlink_alarm_loop(RRDHOST *host, RRDCALC *alarms) {
}
void rrdcalc_labels_unlink_alarm_from_host(RRDHOST *host) {
rrdhost_check_rdlock(host);
netdata_rwlock_rdlock(&host->labels_rwlock);
rrdcalc_labels_unlink_alarm_loop(host, host->alarms);

View file

@ -15,6 +15,7 @@ static int rrdcalctemplate_is_there_label_restriction(RRDCALCTEMPLATE *rt, RRDH
int ret;
if(move) {
rrdhost_check_rdlock(host);
netdata_rwlock_rdlock(&host->labels_rwlock);
while(move) {
snprintfz(cmp, CONFIG_FILE_LINE_MAX, "%s=%s", move->key, move->value);

View file

@ -156,6 +156,7 @@ RRDHOST *rrdhost_create(const char *hostname,
netdata_mutex_init(&host->rrdpush_sender_buffer_mutex);
netdata_rwlock_init(&host->rrdhost_rwlock);
netdata_rwlock_init(&host->labels_rwlock);
rrdhost_init_hostname(host, hostname);
rrdhost_init_machine_guid(host, guid);
@ -664,6 +665,7 @@ void rrdhost_free(RRDHOST *host) {
// free it
freez((void *)host->tags);
free_host_labels(host->labels);
freez((void *)host->os);
freez((void *)host->timezone);
freez(host->program_version);
@ -680,6 +682,7 @@ void rrdhost_free(RRDHOST *host) {
freez(host->registry_hostname);
simple_pattern_free(host->rrdpush_send_charts_matching);
rrdhost_unlock(host);
netdata_rwlock_destroy(&host->labels_rwlock);
netdata_rwlock_destroy(&host->health_log.alarm_log_rwlock);
netdata_rwlock_destroy(&host->rrdhost_rwlock);
freez(host);
@ -842,6 +845,178 @@ struct label *load_config_labels()
return l;
}
typedef enum strip_quotes {
DO_NOT_STRIP_QUOTES,
STRIP_QUOTES
} STRIP_QUOTES_OPTION;
typedef enum skip_escaped_characters {
DO_NOT_SKIP_ESCAPED_CHARACTERS,
SKIP_ESCAPED_CHARACTERS
} SKIP_ESCAPED_CHARACTERS_OPTION;
static inline void strip_last_symbol(
char *str,
char symbol,
SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
{
char *end = str;
while (*end && *end != symbol) {
if (unlikely(skip_escaped_characters && *end == '\\')) {
end++;
if (unlikely(!*end))
break;
}
end++;
}
if (likely(*end == symbol))
*end = '\0';
}
static inline char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
{
if (*str == '"') {
str++;
strip_last_symbol(str, '"', skip_escaped_characters);
}
return str;
}
struct label *parse_simple_tags(
struct label *label_list,
const char *tags,
char key_value_separator,
char label_separator,
STRIP_QUOTES_OPTION strip_quotes_from_key,
STRIP_QUOTES_OPTION strip_quotes_from_value,
SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
{
const char *end = tags;
while (*end) {
const char *start = end;
char key[CONFIG_MAX_VALUE + 1];
char value[CONFIG_MAX_VALUE + 1];
while (*end && *end != key_value_separator)
end++;
strncpyz(key, start, end - start);
if (*end)
start = ++end;
while (*end && *end != label_separator)
end++;
strncpyz(value, start, end - start);
label_list = add_label_to_list(
label_list,
strip_quotes_from_key ? strip_double_quotes(trim(key), skip_escaped_characters) : trim(key),
strip_quotes_from_value ? strip_double_quotes(trim(value), skip_escaped_characters) : trim(value),
LABEL_SOURCE_NETDATA_CONF);
if (*end)
end++;
}
return label_list;
}
struct label *parse_json_tags(struct label *label_list, const char *tags)
{
char tags_buf[CONFIG_MAX_VALUE + 1];
strncpy(tags_buf, tags, CONFIG_MAX_VALUE);
char *str = tags_buf;
switch (*str) {
case '{':
str++;
strip_last_symbol(str, '}', SKIP_ESCAPED_CHARACTERS);
label_list = parse_simple_tags(label_list, str, ':', ',', STRIP_QUOTES, STRIP_QUOTES, SKIP_ESCAPED_CHARACTERS);
break;
case '[':
str++;
strip_last_symbol(str, ']', SKIP_ESCAPED_CHARACTERS);
char *end = str + strlen(str);
size_t i = 0;
while (str < end) {
char key[CONFIG_MAX_VALUE + 1];
snprintfz(key, CONFIG_MAX_VALUE, "host_tag%zu", i);
str = strip_double_quotes(trim(str), SKIP_ESCAPED_CHARACTERS);
label_list = add_label_to_list(label_list, key, str, LABEL_SOURCE_NETDATA_CONF);
// skip to the next element in the array
str += strlen(str) + 1;
while (*str && *str != ',')
str++;
str++;
i++;
}
break;
case '"':
label_list = add_label_to_list(
label_list, "host_tag", strip_double_quotes(str, SKIP_ESCAPED_CHARACTERS), LABEL_SOURCE_NETDATA_CONF);
break;
default:
label_list = add_label_to_list(label_list, "host_tag", str, LABEL_SOURCE_NETDATA_CONF);
break;
}
return label_list;
}
struct label *load_labels_from_tags()
{
if (!localhost->tags)
return NULL;
struct label *label_list = NULL;
BACKEND_TYPE type = BACKEND_TYPE_UNKNOWN;
if (config_exists(CONFIG_SECTION_BACKEND, "enabled")) {
if (config_get_boolean(CONFIG_SECTION_BACKEND, "enabled", CONFIG_BOOLEAN_NO) != CONFIG_BOOLEAN_NO) {
const char *type_name = config_get(CONFIG_SECTION_BACKEND, "type", "graphite");
type = backend_select_type(type_name);
}
}
switch (type) {
case BACKEND_TYPE_GRAPHITE:
label_list = parse_simple_tags(
label_list, localhost->tags, '=', ';', DO_NOT_STRIP_QUOTES, DO_NOT_STRIP_QUOTES,
DO_NOT_SKIP_ESCAPED_CHARACTERS);
break;
case BACKEND_TYPE_OPENTSDB_USING_TELNET:
label_list = parse_simple_tags(
label_list, localhost->tags, '=', ' ', DO_NOT_STRIP_QUOTES, DO_NOT_STRIP_QUOTES,
DO_NOT_SKIP_ESCAPED_CHARACTERS);
break;
case BACKEND_TYPE_OPENTSDB_USING_HTTP:
label_list = parse_simple_tags(
label_list, localhost->tags, ':', ',', STRIP_QUOTES, STRIP_QUOTES,
DO_NOT_SKIP_ESCAPED_CHARACTERS);
break;
case BACKEND_TYPE_JSON:
label_list = parse_json_tags(label_list, localhost->tags);
break;
default:
label_list = parse_simple_tags(
label_list, localhost->tags, '=', ',', DO_NOT_STRIP_QUOTES, STRIP_QUOTES,
DO_NOT_SKIP_ESCAPED_CHARACTERS);
break;
}
return label_list;
}
struct label *load_kubernetes_labels()
{
struct label *l=NULL;
@ -931,6 +1106,7 @@ void free_host_labels(struct label *labels)
void replace_label_list(RRDHOST *host, struct label *new_labels)
{
rrdhost_check_rdlock(host);
netdata_rwlock_wrlock(&host->labels_rwlock);
struct label *old_labels = host->labels;
host->labels = new_labels;
@ -982,13 +1158,17 @@ void reload_host_labels()
struct label *from_auto = load_auto_labels();
struct label *from_k8s = load_kubernetes_labels();
struct label *from_config = load_config_labels();
struct label *from_tags = load_labels_from_tags();
struct label *new_labels = merge_label_lists(from_auto, from_k8s);
new_labels = merge_label_lists(new_labels, from_tags);
new_labels = merge_label_lists(new_labels, from_config);
rrdhost_rdlock(localhost);
replace_label_list(localhost, new_labels);
health_label_log_save(localhost);
rrdhost_unlock(localhost);
if(localhost->rrdpush_send_enabled && localhost->rrdpush_sender_buffer){
localhost->labels_flag |= LABEL_FLAG_UPDATE_STREAM;

View file

@ -87,6 +87,7 @@ int format_host_labels_graphite_plaintext(struct instance *instance, RRDHOST *ho
if (unlikely(!sending_labels_configured(instance)))
return 0;
rrdhost_check_rdlock(host);
netdata_rwlock_rdlock(&host->labels_rwlock);
for (struct label *label = host->labels; label; label = label->next) {
if (!should_send_label(instance, label))

View file

@ -69,6 +69,7 @@ int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
buffer_strcat(instance->labels, "\"labels\":{");
int count = 0;
rrdhost_check_rdlock(host);
netdata_rwlock_rdlock(&host->labels_rwlock);
for (struct label *label = host->labels; label; label = label->next) {
if (!should_send_label(instance, label))

View file

@ -119,6 +119,7 @@ int format_host_labels_opentsdb_telnet(struct instance *instance, RRDHOST *host)
if (unlikely(!sending_labels_configured(instance)))
return 0;
rrdhost_check_rdlock(localhost);
netdata_rwlock_rdlock(&host->labels_rwlock);
for (struct label *label = host->labels; label; label = label->next) {
if (!should_send_label(instance, label))
@ -261,6 +262,7 @@ int format_host_labels_opentsdb_http(struct instance *instance, RRDHOST *host)
if (unlikely(!sending_labels_configured(instance)))
return 0;
rrdhost_check_rdlock(host);
netdata_rwlock_rdlock(&host->labels_rwlock);
for (struct label *label = host->labels; label; label = label->next) {
if (!should_send_label(instance, label))

View file

@ -72,6 +72,7 @@ inline void health_label_log_save(RRDHOST *host) {
if(likely(host->health_log_fp)) {
BUFFER *wb = buffer_create(1024);
rrdhost_check_rdlock(host);
netdata_rwlock_rdlock(&host->labels_rwlock);
struct label *l=localhost->labels;
while (l != NULL) {

View file

@ -566,7 +566,9 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used, cons
error("INTERNAL ERROR: Cannot remove '%s' from section '%s', it was not inserted before.",
cv2->name, co->name);
free(cv2);
freez(cv2->name);
freez(cv2->value);
freez(cv2);
cv2 = save;
}
co->values = NULL;

View file

@ -347,6 +347,7 @@ void rrdpush_send_labels(RRDHOST *host) {
return;
rrdpush_buffer_lock(host);
rrdhost_rdlock(host);
netdata_rwlock_rdlock(&host->labels_rwlock);
struct label *labels = host->labels;
@ -364,6 +365,7 @@ void rrdpush_send_labels(RRDHOST *host) {
, "OVERWRITE %s\n", "labels");
netdata_rwlock_unlock(&host->labels_rwlock);
rrdhost_unlock(host);
if(host->rrdpush_sender_pipe[PIPE_WRITE] != -1 && write(host->rrdpush_sender_pipe[PIPE_WRITE], " ", 1) == -1)
error("STREAM %s [send]: cannot write to internal pipe", host->hostname);

View file

@ -776,6 +776,7 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
}
int count = 0;
rrdhost_rdlock(host);
netdata_rwlock_rdlock(&host->labels_rwlock);
for (struct label *label = host->labels; label; label = label->next) {
if(count > 0) buffer_strcat(wb, ",\n");
@ -789,6 +790,7 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
}
buffer_strcat(wb, "\n");
netdata_rwlock_unlock(&host->labels_rwlock);
rrdhost_unlock(host);
}
inline int web_client_api_request_v1_info(RRDHOST *host, struct web_client *w, char *url) {