@cryptotaxi247 / netdata-1 / commits / 177af26ea

Parse host tags (#7702)

* 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

Vladimir Kobal committed Feb 1, 2020 at 00:05 UTC 177af26ea878e673166ebbeb4518539f624a81f0
12 files changed +204 -4
backends/README.md
+8 -3
@@ -183,9 +183,14 @@ from your Netdata):
183 are different: disks with device-mapper, interrupts, QoS classes, statsd synthetic charts, etc.
184
185 - `host tags = list of TAG=VALUE` defines tags that should be appended on all metrics for the given host. These are
186 - currently only sent to opentsdb and prometheus. Please use the appropriate format for each time-series db. For
187 - example opentsdb likes them like `TAG1=VALUE1 TAG2=VALUE2`, but prometheus like `tag1="value1",tag2="value2"`. Host
188 - tags are mirrored with database replication (streaming of metrics between Netdata servers).
186 + currently only sent to graphite, json, opentsdb and prometheus. Please use the appropriate format for each
187 + time-series db. For example opentsdb likes them like `TAG1=VALUE1 TAG2=VALUE2`, but prometheus like `tag1="value1",
188 + tag2="value2"`. Host tags are mirrored with database replication (streaming of metrics between Netdata servers).
189 +
190 + Starting from Netdata v1.20 the host tags are parsed in accordance with a configured backend type and stored as
191 + host labels so that they can be reused in API responses and exporting connectors. The parsing is supported for
192 + graphite, json, opentsdb, and prometheus (default) backend types. You can check how the host tags were parsed using
193 + the /api/v1/info API call.
194
195 ## monitoring operation
196
daemon/commands.c
+2
@@ -203,6 +203,7 @@ static cmd_status_t cmd_reload_labels_execute(char *args, char **message)
203
204 BUFFER *wb = buffer_create(10);
205
206 + rrdhost_rdlock(localhost);
207 netdata_rwlock_rdlock(&localhost->labels_rwlock);
208 struct label *l=localhost->labels;
209 while (l != NULL) {
@@ -210,6 +211,7 @@ static cmd_status_t cmd_reload_labels_execute(char *args, char **message)
211 l = l->next;
212 }
213 netdata_rwlock_unlock(&localhost->labels_rwlock);
214 + rrdhost_unlock(localhost);
215
216 (*message)=strdupz(buffer_tostring(wb));
217 buffer_free(wb);
database/rrdcalc.c
+1
@@ -662,6 +662,7 @@ static void rrdcalc_labels_unlink_alarm_loop(RRDHOST *host, RRDCALC *alarms) {
662 }
663
664 void rrdcalc_labels_unlink_alarm_from_host(RRDHOST *host) {
665 + rrdhost_check_rdlock(host);
666 netdata_rwlock_rdlock(&host->labels_rwlock);
667
668 rrdcalc_labels_unlink_alarm_loop(host, host->alarms);
database/rrdcalctemplate.c
+1
@@ -15,6 +15,7 @@ static int rrdcalctemplate_is_there_label_restriction(RRDCALCTEMPLATE *rt, RRDH
15
16 int ret;
17 if(move) {
18 + rrdhost_check_rdlock(host);
19 netdata_rwlock_rdlock(&host->labels_rwlock);
20 while(move) {
21 snprintfz(cmp, CONFIG_FILE_LINE_MAX, "%s=%s", move->key, move->value);
database/rrdhost.c
+180
@@ -156,6 +156,7 @@ RRDHOST *rrdhost_create(const char *hostname,
156
157 netdata_mutex_init(&host->rrdpush_sender_buffer_mutex);
158 netdata_rwlock_init(&host->rrdhost_rwlock);
159 + netdata_rwlock_init(&host->labels_rwlock);
160
161 rrdhost_init_hostname(host, hostname);
162 rrdhost_init_machine_guid(host, guid);
@@ -664,6 +665,7 @@ void rrdhost_free(RRDHOST *host) {
665 // free it
666
667 freez((void *)host->tags);
668 + free_host_labels(host->labels);
669 freez((void *)host->os);
670 freez((void *)host->timezone);
671 freez(host->program_version);
@@ -680,6 +682,7 @@ void rrdhost_free(RRDHOST *host) {
682 freez(host->registry_hostname);
683 simple_pattern_free(host->rrdpush_send_charts_matching);
684 rrdhost_unlock(host);
685 + netdata_rwlock_destroy(&host->labels_rwlock);
686 netdata_rwlock_destroy(&host->health_log.alarm_log_rwlock);
687 netdata_rwlock_destroy(&host->rrdhost_rwlock);
688 freez(host);
@@ -842,6 +845,178 @@ struct label *load_config_labels()
845 return l;
846 }
847
848 +typedef enum strip_quotes {
849 + DO_NOT_STRIP_QUOTES,
850 + STRIP_QUOTES
851 +} STRIP_QUOTES_OPTION;
852 +
853 +typedef enum skip_escaped_characters {
854 + DO_NOT_SKIP_ESCAPED_CHARACTERS,
855 + SKIP_ESCAPED_CHARACTERS
856 +} SKIP_ESCAPED_CHARACTERS_OPTION;
857 +
858 +static inline void strip_last_symbol(
859 + char *str,
860 + char symbol,
861 + SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
862 +{
863 + char *end = str;
864 +
865 + while (*end && *end != symbol) {
866 + if (unlikely(skip_escaped_characters && *end == '\\')) {
867 + end++;
868 + if (unlikely(!*end))
869 + break;
870 + }
871 + end++;
872 + }
873 + if (likely(*end == symbol))
874 + *end = '\0';
875 +}
876 +
877 +static inline char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
878 +{
879 + if (*str == '"') {
880 + str++;
881 + strip_last_symbol(str, '"', skip_escaped_characters);
882 + }
883 +
884 + return str;
885 +}
886 +
887 +struct label *parse_simple_tags(
888 + struct label *label_list,
889 + const char *tags,
890 + char key_value_separator,
891 + char label_separator,
892 + STRIP_QUOTES_OPTION strip_quotes_from_key,
893 + STRIP_QUOTES_OPTION strip_quotes_from_value,
894 + SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
895 +{
896 + const char *end = tags;
897 +
898 + while (*end) {
899 + const char *start = end;
900 + char key[CONFIG_MAX_VALUE + 1];
901 + char value[CONFIG_MAX_VALUE + 1];
902 +
903 + while (*end && *end != key_value_separator)
904 + end++;
905 + strncpyz(key, start, end - start);
906 +
907 + if (*end)
908 + start = ++end;
909 + while (*end && *end != label_separator)
910 + end++;
911 + strncpyz(value, start, end - start);
912 +
913 + label_list = add_label_to_list(
914 + label_list,
915 + strip_quotes_from_key ? strip_double_quotes(trim(key), skip_escaped_characters) : trim(key),
916 + strip_quotes_from_value ? strip_double_quotes(trim(value), skip_escaped_characters) : trim(value),
917 + LABEL_SOURCE_NETDATA_CONF);
918 +
919 + if (*end)
920 + end++;
921 + }
922 +
923 + return label_list;
924 +}
925 +
926 +struct label *parse_json_tags(struct label *label_list, const char *tags)
927 +{
928 + char tags_buf[CONFIG_MAX_VALUE + 1];
929 + strncpy(tags_buf, tags, CONFIG_MAX_VALUE);
930 + char *str = tags_buf;
931 +
932 + switch (*str) {
933 + case '{':
934 + str++;
935 + strip_last_symbol(str, '}', SKIP_ESCAPED_CHARACTERS);
936 +
937 + label_list = parse_simple_tags(label_list, str, ':', ',', STRIP_QUOTES, STRIP_QUOTES, SKIP_ESCAPED_CHARACTERS);
938 +
939 + break;
940 + case '[':
941 + str++;
942 + strip_last_symbol(str, ']', SKIP_ESCAPED_CHARACTERS);
943 +
944 + char *end = str + strlen(str);
945 + size_t i = 0;
946 +
947 + while (str < end) {
948 + char key[CONFIG_MAX_VALUE + 1];
949 + snprintfz(key, CONFIG_MAX_VALUE, "host_tag%zu", i);
950 +
951 + str = strip_double_quotes(trim(str), SKIP_ESCAPED_CHARACTERS);
952 +
953 + label_list = add_label_to_list(label_list, key, str, LABEL_SOURCE_NETDATA_CONF);
954 +
955 + // skip to the next element in the array
956 + str += strlen(str) + 1;
957 + while (*str && *str != ',')
958 + str++;
959 + str++;
960 + i++;
961 + }
962 +
963 + break;
964 + case '"':
965 + label_list = add_label_to_list(
966 + label_list, "host_tag", strip_double_quotes(str, SKIP_ESCAPED_CHARACTERS), LABEL_SOURCE_NETDATA_CONF);
967 + break;
968 + default:
969 + label_list = add_label_to_list(label_list, "host_tag", str, LABEL_SOURCE_NETDATA_CONF);
970 + break;
971 + }
972 +
973 + return label_list;
974 +}
975 +
976 +struct label *load_labels_from_tags()
977 +{
978 + if (!localhost->tags)
979 + return NULL;
980 +
981 + struct label *label_list = NULL;
982 + BACKEND_TYPE type = BACKEND_TYPE_UNKNOWN;
983 +
984 + if (config_exists(CONFIG_SECTION_BACKEND, "enabled")) {
985 + if (config_get_boolean(CONFIG_SECTION_BACKEND, "enabled", CONFIG_BOOLEAN_NO) != CONFIG_BOOLEAN_NO) {
986 + const char *type_name = config_get(CONFIG_SECTION_BACKEND, "type", "graphite");
987 + type = backend_select_type(type_name);
988 + }
989 + }
990 +
991 + switch (type) {
992 + case BACKEND_TYPE_GRAPHITE:
993 + label_list = parse_simple_tags(
994 + label_list, localhost->tags, '=', ';', DO_NOT_STRIP_QUOTES, DO_NOT_STRIP_QUOTES,
995 + DO_NOT_SKIP_ESCAPED_CHARACTERS);
996 + break;
997 + case BACKEND_TYPE_OPENTSDB_USING_TELNET:
998 + label_list = parse_simple_tags(
999 + label_list, localhost->tags, '=', ' ', DO_NOT_STRIP_QUOTES, DO_NOT_STRIP_QUOTES,
1000 + DO_NOT_SKIP_ESCAPED_CHARACTERS);
1001 + break;
1002 + case BACKEND_TYPE_OPENTSDB_USING_HTTP:
1003 + label_list = parse_simple_tags(
1004 + label_list, localhost->tags, ':', ',', STRIP_QUOTES, STRIP_QUOTES,
1005 + DO_NOT_SKIP_ESCAPED_CHARACTERS);
1006 + break;
1007 + case BACKEND_TYPE_JSON:
1008 + label_list = parse_json_tags(label_list, localhost->tags);
1009 + break;
1010 + default:
1011 + label_list = parse_simple_tags(
1012 + label_list, localhost->tags, '=', ',', DO_NOT_STRIP_QUOTES, STRIP_QUOTES,
1013 + DO_NOT_SKIP_ESCAPED_CHARACTERS);
1014 + break;
1015 + }
1016 +
1017 + return label_list;
1018 +}
1019 +
1020 struct label *load_kubernetes_labels()
1021 {
1022 struct label *l=NULL;
@@ -931,6 +1106,7 @@ void free_host_labels(struct label *labels)
1106
1107 void replace_label_list(RRDHOST *host, struct label *new_labels)
1108 {
1109 + rrdhost_check_rdlock(host);
1110 netdata_rwlock_wrlock(&host->labels_rwlock);
1111 struct label *old_labels = host->labels;
1112 host->labels = new_labels;
@@ -982,13 +1158,17 @@ void reload_host_labels()
1158 struct label *from_auto = load_auto_labels();
1159 struct label *from_k8s = load_kubernetes_labels();
1160 struct label *from_config = load_config_labels();
1161 + struct label *from_tags = load_labels_from_tags();
1162
1163 struct label *new_labels = merge_label_lists(from_auto, from_k8s);
1164 + new_labels = merge_label_lists(new_labels, from_tags);
1165 new_labels = merge_label_lists(new_labels, from_config);
1166
1167 + rrdhost_rdlock(localhost);
1168 replace_label_list(localhost, new_labels);
1169
1170 health_label_log_save(localhost);
1171 + rrdhost_unlock(localhost);
1172
1173 if(localhost->rrdpush_send_enabled && localhost->rrdpush_sender_buffer){
1174 localhost->labels_flag |= LABEL_FLAG_UPDATE_STREAM;
exporting/graphite/graphite.c
+1
@@ -87,6 +87,7 @@ int format_host_labels_graphite_plaintext(struct instance *instance, RRDHOST *ho
87 if (unlikely(!sending_labels_configured(instance)))
88 return 0;
89
90 + rrdhost_check_rdlock(host);
91 netdata_rwlock_rdlock(&host->labels_rwlock);
92 for (struct label *label = host->labels; label; label = label->next) {
93 if (!should_send_label(instance, label))
exporting/json/json.c
+1
@@ -69,6 +69,7 @@ int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
69 buffer_strcat(instance->labels, "\"labels\":{");
70
71 int count = 0;
72 + rrdhost_check_rdlock(host);
73 netdata_rwlock_rdlock(&host->labels_rwlock);
74 for (struct label *label = host->labels; label; label = label->next) {
75 if (!should_send_label(instance, label))
exporting/opentsdb/opentsdb.c
+2
@@ -119,6 +119,7 @@ int format_host_labels_opentsdb_telnet(struct instance *instance, RRDHOST *host)
119 if (unlikely(!sending_labels_configured(instance)))
120 return 0;
121
122 + rrdhost_check_rdlock(localhost);
123 netdata_rwlock_rdlock(&host->labels_rwlock);
124 for (struct label *label = host->labels; label; label = label->next) {
125 if (!should_send_label(instance, label))
@@ -261,6 +262,7 @@ int format_host_labels_opentsdb_http(struct instance *instance, RRDHOST *host)
262 if (unlikely(!sending_labels_configured(instance)))
263 return 0;
264
265 + rrdhost_check_rdlock(host);
266 netdata_rwlock_rdlock(&host->labels_rwlock);
267 for (struct label *label = host->labels; label; label = label->next) {
268 if (!should_send_label(instance, label))
health/health_log.c
+1
@@ -72,6 +72,7 @@ inline void health_label_log_save(RRDHOST *host) {
72
73 if(likely(host->health_log_fp)) {
74 BUFFER *wb = buffer_create(1024);
75 + rrdhost_check_rdlock(host);
76 netdata_rwlock_rdlock(&host->labels_rwlock);
77 struct label *l=localhost->labels;
78 while (l != NULL) {
libnetdata/config/appconfig.c
+3 -1
@@ -566,7 +566,9 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used, cons
566 error("INTERNAL ERROR: Cannot remove '%s' from section '%s', it was not inserted before.",
567 cv2->name, co->name);
568
569 - free(cv2);
569 + freez(cv2->name);
570 + freez(cv2->value);
571 + freez(cv2);
572 cv2 = save;
573 }
574 co->values = NULL;
streaming/rrdpush.c
+2
@@ -347,6 +347,7 @@ void rrdpush_send_labels(RRDHOST *host) {
347 return;
348
349 rrdpush_buffer_lock(host);
350 + rrdhost_rdlock(host);
351 netdata_rwlock_rdlock(&host->labels_rwlock);
352
353 struct label *labels = host->labels;
@@ -364,6 +365,7 @@ void rrdpush_send_labels(RRDHOST *host) {
365 , "OVERWRITE %s\n", "labels");
366
367 netdata_rwlock_unlock(&host->labels_rwlock);
368 + rrdhost_unlock(host);
369
370 if(host->rrdpush_sender_pipe[PIPE_WRITE] != -1 && write(host->rrdpush_sender_pipe[PIPE_WRITE], " ", 1) == -1)
371 error("STREAM %s [send]: cannot write to internal pipe", host->hostname);
web/api/web_api_v1.c
+2
@@ -776,6 +776,7 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
776 }
777
778 int count = 0;
779 + rrdhost_rdlock(host);
780 netdata_rwlock_rdlock(&host->labels_rwlock);
781 for (struct label *label = host->labels; label; label = label->next) {
782 if(count > 0) buffer_strcat(wb, ",\n");
@@ -789,6 +790,7 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
790 }
791 buffer_strcat(wb, "\n");
792 netdata_rwlock_unlock(&host->labels_rwlock);
793 + rrdhost_unlock(host);
794 }
795
796 inline int web_client_api_request_v1_info(RRDHOST *host, struct web_client *w, char *url) {