@cryptotaxi247 / netdata-1 / commits / a0e176013

HEALTH: eliminate fields that should be labels (#17048)

* eliminate fields that should be labels * remaining * remove comma * add _os and _hostname host labels * systemd-journal dynamic configuration updates * move systemd-journal dynamic configuration to logs * copied and integrated rrdlabels update from #16953 * strict type checking on SIMPLE_PATTERN and fixes for wrong uses * add _os and _hostname on children that do not advertise them * remove instance names support for alerts * removed charts and families from docs * Adjust alert config store statement * Remove os, host, plugin, module, charts from alert configuration * Fix compilation warning, remove unused keys (charts, foreach) --------- Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>

Costa Tsaousis committed Mar 5, 2024 at 12:11 UTC a0e176013a740590d9556cac5c30d64a7b4f437d
21 files changed +427 -431
src/collectors/plugins.d/pluginsd_parser.c
+7
@@ -673,6 +673,13 @@ static inline PARSER_RC pluginsd_overwrite(char **words __maybe_unused, size_t n
673 rrdlabels_migrate_to_these(host->rrdlabels, parser->user.new_host_labels);
674 if (rrdhost_option_check(host, RRDHOST_OPTION_EPHEMERAL_HOST))
675 rrdlabels_add(host->rrdlabels, HOST_LABEL_IS_EPHEMERAL, "true", RRDLABEL_SRC_CONFIG);
676 +
677 + if(!rrdlabels_exist(host->rrdlabels, "_os"))
678 + rrdlabels_add(host->rrdlabels, "_os", string2str(host->os), RRDLABEL_SRC_AUTO);
679 +
680 + if(!rrdlabels_exist(host->rrdlabels, "_hostname"))
681 + rrdlabels_add(host->rrdlabels, "_hostname", string2str(host->hostname), RRDLABEL_SRC_AUTO);
682 +
683 rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_LABELS | RRDHOST_FLAG_METADATA_UPDATE);
684
685 rrdlabels_destroy(parser->user.new_host_labels);
src/collectors/systemd-journal.plugin/schema.d/systemd-journal:monitored-directories.json
+6 -2
@@ -4,13 +4,16 @@
4 "type": "object",
5 "properties": {
6 "journalDirectories": {
7 + "title": "systemd-journal directories",
8 + "description": "The list of directories `systemd-journald` and `systemd-journal-remote` store journal files. Netdata monitors these directories to automatically detect changes.",
9 "type": "array",
10 "items": {
11 + "title": "Absolute Path",
12 "type": "string",
10 - "pattern": "^/.*$"
13 + "pattern": "^/.+$"
14 },
15 "maxItems": 100,
13 - "uniqueItems": true
16 + "uniqueItems": true
17 }
18 },
19 "required": [
@@ -19,6 +22,7 @@
22 },
23 "uiSchema": {
24 "journalDirectories": {
25 + "ui:listFlavour": "list",
26 "ui:options": {
27 "addable": true,
28 "orderable": false,
src/collectors/systemd-journal.plugin/systemd-journal-dyncfg.c
+64 -3
@@ -4,6 +4,49 @@
4
5 #define JOURNAL_DIRECTORIES_JSON_NODE "journalDirectories"
6
7 +static bool is_directory(const char *dir) {
8 + struct stat statbuf;
9 + if (stat(dir, &statbuf) != 0) {
10 + // Error in stat() means the path probably doesn't exist or can't be accessed.
11 + return false;
12 + }
13 + // S_ISDIR macro is true if the path is a directory.
14 + return S_ISDIR(statbuf.st_mode) ? true : false;
15 +}
16 +
17 +static const char *is_valid_dir(const char *dir) {
18 + if(strcmp(dir, "/") == 0)
19 + return "/ is not acceptable";
20 +
21 + if(!strstartswith(dir, "/"))
22 + return "only directories starting with / are accepted";
23 +
24 + if(strstr(dir, "/./"))
25 + return "directory contains /./";
26 +
27 + if(strstr(dir, "/../") || strendswith(dir, "/.."))
28 + return "directory contains /../";
29 +
30 + if(strstartswith(dir, "/dev/") || strcmp(dir, "/dev") == 0)
31 + return "directory contains /dev";
32 +
33 + if(strstartswith(dir, "/proc/") || strcmp(dir, "/proc") == 0)
34 + return "directory contains /proc";
35 +
36 + if(strstartswith(dir, "/sys/") || strcmp(dir, "/sys") == 0)
37 + return "directory contains /sys";
38 +
39 + if(strstartswith(dir, "/etc/") || strcmp(dir, "/etc") == 0)
40 + return "directory contains /etc";
41 +
42 + if(strstartswith(dir, "/lib/") || strcmp(dir, "/lib") == 0
43 + || strstartswith(dir, "/lib32/") || strcmp(dir, "/lib32") == 0
44 + || strstartswith(dir, "/lib64/") || strcmp(dir, "/lib64") == 0)
45 + return "directory contains /lib";
46 +
47 + return NULL;
48 +}
49 +
50 static int systemd_journal_directories_dyncfg_update(BUFFER *result, BUFFER *payload) {
51 if(!payload || !buffer_strlen(payload))
52 return dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "empty payload received");
@@ -16,14 +59,30 @@ static int systemd_journal_directories_dyncfg_update(BUFFER *result, BUFFER *pay
59 json_object_object_get_ex(jobj, JOURNAL_DIRECTORIES_JSON_NODE, &journalDirectories);
60
61 size_t n_directories = json_object_array_length(journalDirectories);
62 + if(n_directories > MAX_JOURNAL_DIRECTORIES)
63 + return dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "too many directories configured");
64
20 - size_t added = 0;
65 + // validate the directories
66 + for(size_t i = 0; i < n_directories; i++) {
67 + struct json_object *dir = json_object_array_get_idx(journalDirectories, i);
68 + const char *s = json_object_get_string(dir);
69 + if(s && *s) {
70 + const char *msg = is_valid_dir(s);
71 + if(msg)
72 + return dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, msg);
73 + }
74 + }
75 +
76 + size_t added = 0, not_found = 0;
77 for(size_t i = 0; i < n_directories; i++) {
78 struct json_object *dir = json_object_array_get_idx(journalDirectories, i);
79 const char *s = json_object_get_string(dir);
80 if(s && *s) {
81 string_freez(journal_directories[added].path);
82 journal_directories[added++].path = string_strdupz(s);
83 +
84 + if(!is_directory(s))
85 + not_found++;
86 }
87 }
88
@@ -36,7 +95,9 @@ static int systemd_journal_directories_dyncfg_update(BUFFER *result, BUFFER *pay
95 }
96 }
97
39 - return dyncfg_default_response(result, HTTP_RESP_OK, "applied");
98 + journal_watcher_restart();
99 +
100 + return dyncfg_default_response(result, HTTP_RESP_OK, not_found ? "added, but some directories are not found in the filesystem" : "");
101 }
102
103 static int systemd_journal_directories_dyncfg_get(BUFFER *wb) {
@@ -89,7 +150,7 @@ void systemd_journal_dyncfg_init(struct functions_evloop_globals *wg) {
150 functions_evloop_dyncfg_add(
151 wg,
152 "systemd-journal:monitored-directories",
92 - "/collectors/logs/systemd-journal",
153 + "/logs/systemd-journal",
154 DYNCFG_STATUS_RUNNING,
155 DYNCFG_TYPE_SINGLE,
156 DYNCFG_SOURCE_TYPE_INTERNAL,
src/collectors/systemd-journal.plugin/systemd-journal-watcher.c
+1 -1
@@ -300,7 +300,7 @@ void journal_watcher_restart(void) {
300
301 void *journal_watcher_main(void *arg __maybe_unused) {
302 while(1) {
303 - size_t journal_watcher_session_id = journal_watcher_wanted_session_id;
303 + size_t journal_watcher_session_id = __atomic_load_n(&journal_watcher_wanted_session_id, __ATOMIC_RELAXED);
304
305 Watcher watcher = {
306 .watchList = mallocz(INITIAL_WATCHES * sizeof(WatchEntry)),
src/database/contexts/api_v2.c
-5
@@ -1319,14 +1319,9 @@ static void contexts_v2_alert_config_to_json_from_sql_alert_config_data(struct s
1319 buffer_json_member_add_string(wb, "type", is_template ? "template" : "alarm");
1320 buffer_json_member_add_string(wb, "on", is_template ? t->selectors.on_template : t->selectors.on_key);
1321
1322 - buffer_json_member_add_string(wb, "os", t->selectors.os);
1323 - buffer_json_member_add_string(wb, "hosts", t->selectors.hosts);
1322 buffer_json_member_add_string(wb, "families", t->selectors.families);
1325 - buffer_json_member_add_string(wb, "plugin", t->selectors.plugin);
1326 - buffer_json_member_add_string(wb, "module", t->selectors.module);
1323 buffer_json_member_add_string(wb, "host_labels", t->selectors.host_labels);
1324 buffer_json_member_add_string(wb, "chart_labels", t->selectors.chart_labels);
1329 - buffer_json_member_add_string(wb, "charts", t->selectors.charts);
1325 }
1326 buffer_json_object_close(wb); // selectors
1327
src/database/contexts/query_target.c
+8 -109
@@ -11,7 +11,6 @@ static void query_dimension_release(QUERY_DIMENSION *qd);
11 static void query_instance_release(QUERY_INSTANCE *qi);
12 static void query_context_release(QUERY_CONTEXT *qc);
13 static void query_node_release(QUERY_NODE *qn);
14 -static void free_label_pattern_list(struct label_pattern_list *lpl);
14
15 static __thread QUERY_TARGET *thread_qt = NULL;
16 static struct {
@@ -83,10 +82,6 @@ void query_target_release(QUERY_TARGET *qt) {
82
83 simple_pattern_free(qt->instances.labels_pattern);
84 qt->instances.labels_pattern = NULL;
86 -
87 - free_label_pattern_list(qt->instances.label_pattern_list);
88 - qt->instances.label_pattern_list = NULL;
89 -
85 simple_pattern_free(qt->query.pattern);
86 qt->query.pattern = NULL;
87
@@ -734,24 +729,19 @@ static inline SIMPLE_PATTERN_RESULT query_instance_matches(QUERY_INSTANCE *qi,
729 static inline bool query_instance_matches_labels(
730 RRDINSTANCE *ri,
731 SIMPLE_PATTERN *chart_label_key_sp,
737 - SIMPLE_PATTERN *labels_sp,
738 - struct label_pattern_list *lpl)
732 + SIMPLE_PATTERN *labels_sp)
733 {
734
735 if (chart_label_key_sp && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, chart_label_key_sp, '\0', NULL))
736 return false;
737
744 - if (lpl) {
745 - for (size_t i = 0; i < lpl->size; i++) {
746 - if (!rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, lpl->labels_pattern[i], ':', NULL))
747 - return false;
748 - }
749 - return true;
738 + if (labels_sp) {
739 + struct pattern_array *pa = pattern_array_add_simple_pattern(NULL, labels_sp, ':');
740 + bool found = pattern_array_label_match(pa, ri->rrdlabels, ':', NULL, rrdlabels_match_simple_pattern_parsed);
741 + pattern_array_free(pa);
742 + return found;
743 }
744
752 - if (labels_sp && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, labels_sp, ':', NULL))
753 - return false;
754 -
745 return true;
746 }
747
@@ -775,8 +765,7 @@ static bool query_instance_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_C
765 queryable_instance = query_instance_matches_labels(
766 ri,
767 qt->instances.chart_label_key_pattern,
778 - qt->instances.labels_pattern,
779 - qt->instances.label_pattern_list);
768 + qt->instances.labels_pattern);
769
770 if(queryable_instance) {
771 if(qt->instances.alerts_pattern && !query_target_match_alert_pattern(ria, qt->instances.alerts_pattern))
@@ -1045,84 +1034,6 @@ void query_target_generate_name(QUERY_TARGET *qt) {
1034 json_fix_string(qt->id);
1035 }
1036
1048 -static void add_label_pattern(struct label_pattern_list *lpl, char *label_key_value)
1049 -{
1050 - char *label_key;
1051 -
1052 - if (unlikely(!label_key_value || !(label_key = strchr(label_key_value, ':'))))
1053 - return;
1054 -
1055 - *label_key = '\0';
1056 - STRING *key_match = string_strdupz(label_key_value);
1057 - *label_key = ':';
1058 -
1059 - size_t index;
1060 - bool need_to_add = true;
1061 -
1062 - for (size_t i = 0; i < lpl->size; i++) {
1063 - if (lpl->key[i] == key_match) {
1064 - index = i;
1065 - need_to_add = false;
1066 - break;
1067 - }
1068 - }
1069 -
1070 - if (need_to_add) {
1071 - index = lpl->size++;
1072 - lpl->buffer_list = reallocz(lpl->buffer_list, lpl->size * sizeof(BUFFER *));
1073 - lpl->key = reallocz(lpl->key, lpl->size * sizeof(STRING *));
1074 -
1075 - lpl->buffer_list[index] = buffer_create(128, NULL);
1076 - lpl->key[index] = key_match;
1077 - } else {
1078 - string_freez(key_match);
1079 - buffer_strncat(lpl->buffer_list[index], ",", 1);
1080 - }
1081 -
1082 - buffer_strcat(lpl->buffer_list[index], label_key_value);
1083 -}
1084 -
1085 -static struct label_pattern_list *build_pattern_list(SIMPLE_PATTERN *pattern)
1086 -{
1087 - if (unlikely(!pattern))
1088 - return NULL;
1089 -
1090 - char *label_key = NULL;
1091 -
1092 - struct label_pattern_list *lpl = callocz(1, sizeof(*lpl));
1093 -
1094 - while (pattern && (label_key = simple_pattern_iterate(&pattern)))
1095 - add_label_pattern(lpl, label_key);
1096 -
1097 - lpl->labels_pattern = callocz(lpl->size, sizeof(SIMPLE_PATTERN *));
1098 -
1099 - for (size_t i = 0; i < lpl->size; i++) {
1100 - lpl->labels_pattern[i] = string_to_simple_pattern(buffer_tostring(lpl->buffer_list[i]));
1101 - buffer_free(lpl->buffer_list[i]);
1102 - string_freez(lpl->key[i]);
1103 - }
1104 -
1105 - freez(lpl->buffer_list);
1106 - lpl->buffer_list = NULL;
1107 -
1108 - freez(lpl->key);
1109 - lpl->key = NULL;
1110 - return lpl;
1111 -}
1112 -
1113 -static void free_label_pattern_list(struct label_pattern_list *lpl)
1114 -{
1115 - if (unlikely(!lpl))
1116 - return;
1117 -
1118 - for(size_t i = 0; i < lpl->size; i++)
1119 - simple_pattern_free(lpl->labels_pattern[i]);
1120 -
1121 - freez(lpl->labels_pattern);
1122 - freez(lpl);
1123 -}
1124 -
1125 -
1037 QUERY_TARGET *query_target_create(QUERY_TARGET_REQUEST *qtr) {
1038 if(!service_running(ABILITY_DATA_QUERIES))
1039 return NULL;
@@ -1187,10 +1098,6 @@ QUERY_TARGET *query_target_create(QUERY_TARGET_REQUEST *qtr) {
1098 qt->query.pattern = string_to_simple_pattern(qtl.dimensions);
1099 qt->instances.chart_label_key_pattern = string_to_simple_pattern(qtl.chart_label_key);
1100 qt->instances.labels_pattern = string_to_simple_pattern(qtl.labels);
1190 -
1191 - if (qt->instances.labels_pattern)
1192 - qt->instances.label_pattern_list = build_pattern_list(qt->instances.labels_pattern);
1193 -
1101 qt->instances.alerts_pattern = string_to_simple_pattern(qtl.alerts);
1102
1103 qtl.match_ids = qt->request.options & RRDR_OPTION_MATCH_IDS;
@@ -1262,11 +1169,6 @@ ssize_t weights_foreach_rrdmetric_in_context(RRDCONTEXT_ACQUIRED *rca,
1169
1170 ssize_t count = 0;
1171 RRDINSTANCE *ri;
1265 -
1266 - struct label_pattern_list *lpl = NULL;
1267 - if (labels_sp)
1268 - lpl = build_pattern_list(labels_sp);
1269 -
1172 dfe_start_read(rc->rrdinstances, ri) {
1173 if(rrd_flag_is_deleted(ri))
1174 continue;
@@ -1283,7 +1185,7 @@ ssize_t weights_foreach_rrdmetric_in_context(RRDCONTEXT_ACQUIRED *rca,
1185 continue;
1186 }
1187
1286 - if(!query_instance_matches_labels(ri, chart_label_key_sp, labels_sp, lpl))
1188 + if(!query_instance_matches_labels(ri, chart_label_key_sp, labels_sp))
1189 continue;
1190
1191 if(alerts_sp && !query_target_match_alert_pattern(ria, alerts_sp))
@@ -1327,8 +1229,5 @@ ssize_t weights_foreach_rrdmetric_in_context(RRDCONTEXT_ACQUIRED *rca,
1229 break;
1230 }
1231 dfe_done(ri);
1330 -
1331 - free_label_pattern_list(lpl);
1332 -
1232 return count;
1233 }
src/database/contexts/rrdcontext.h
-13
@@ -329,13 +329,6 @@ struct query_timings {
329 usec_t finished_ut;
330 };
331
332 -struct label_pattern_list {
333 - BUFFER **buffer_list;
334 - STRING **key;
335 - SIMPLE_PATTERN **labels_pattern;
336 - size_t size;
337 -};
338 -
332 #define query_view_update_every(qt) ((qt)->window.group * (qt)->window.query_granularity)
333
334 typedef struct query_target {
@@ -386,7 +379,6 @@ typedef struct query_target {
379 uint32_t size; // the size of the array
380 SIMPLE_PATTERN *pattern;
381 SIMPLE_PATTERN *labels_pattern;
389 - struct label_pattern_list *label_pattern_list;
382 SIMPLE_PATTERN *alerts_pattern;
383 SIMPLE_PATTERN *chart_label_key_pattern;
384 } instances;
@@ -469,14 +461,9 @@ struct sql_alert_config_data {
461 const char *on_template;
462 const char *on_key;
463
472 - const char *os;
473 - const char *hosts;
464 const char *families;
475 - const char *plugin;
476 - const char *module;
465 const char *host_labels;
466 const char *chart_labels;
479 - const char *charts;
467 } selectors;
468
469 const char *info;
src/database/rrdhost.c
+3
@@ -1438,6 +1438,9 @@ static void rrdhost_load_auto_labels(void) {
1438
1439 rrdlabels_add(labels, "_is_parent", (localhost->connected_children_count > 0) ? "true" : "false", RRDLABEL_SRC_AUTO);
1440
1441 + rrdlabels_add(labels, "_hostname", string2str(localhost->hostname), RRDLABEL_SRC_AUTO);
1442 + rrdlabels_add(labels, "_os", string2str(localhost->os), RRDLABEL_SRC_AUTO);
1443 +
1444 if (localhost->rrdpush_send_destination)
1445 rrdlabels_add(labels, "_streams_to", localhost->rrdpush_send_destination, RRDLABEL_SRC_AUTO);
1446 }
src/database/rrdlabels.c
+203
@@ -1347,6 +1347,143 @@ void rrdset_update_rrdlabels(RRDSET *st, RRDLABELS *new_rrdlabels) {
1347 rrdset_metadata_updated(st);
1348 }
1349
1350 +struct pattern_array *pattern_array_allocate()
1351 +{
1352 + struct pattern_array *pa = callocz(1, sizeof(*pa));
1353 + return pa;
1354 +}
1355 +
1356 +static void pattern_array_add_lblkey_with_sp(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *sp)
1357 +{
1358 + if (!pa || !key || !sp)
1359 + return;
1360 +
1361 + STRING *string_key = string_strdupz(key);
1362 + Pvoid_t *Pvalue = JudyLIns(&pa->JudyL, (Word_t) string_key, PJE0);
1363 + if (!Pvalue) {
1364 + string_freez(string_key);
1365 + simple_pattern_free(sp);
1366 + return;
1367 + }
1368 +
1369 + struct pattern_array_item *pai;
1370 + if (*Pvalue) {
1371 + pai = *Pvalue;
1372 + } else {
1373 + *Pvalue = pai = callocz(1, sizeof(*pai));
1374 + pa->key_count++;
1375 + }
1376 +
1377 + pai->size++;
1378 + Pvalue = JudyLIns(&pai->JudyL, (Word_t) pai->size, PJE0);
1379 + if (!Pvalue) {
1380 + simple_pattern_free(sp);
1381 + return;
1382 + }
1383 +
1384 + *Pvalue = sp;
1385 +}
1386 +
1387 +bool pattern_array_label_match(
1388 + struct pattern_array *pa,
1389 + RRDLABELS *labels,
1390 + char eq,
1391 + size_t *searches,
1392 + bool (*callback_function)(RRDLABELS *, SIMPLE_PATTERN *, char, size_t *))
1393 +{
1394 + if (!pa || !labels)
1395 + return true;
1396 +
1397 + Pvoid_t *Pvalue;
1398 + Word_t Index = 0;
1399 + bool first_then_next = true;
1400 + while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first_then_next))) {
1401 + struct pattern_array_item *pai = *Pvalue;
1402 + bool match = false;
1403 + for (Word_t i = 1; !match && i <= pai->size; i++) {
1404 + if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0)) || !*Pvalue)
1405 + continue;
1406 + match = callback_function(labels, (SIMPLE_PATTERN *)(*Pvalue), eq, searches);
1407 + }
1408 + if (!match)
1409 + return false;
1410 + }
1411 + return true;
1412 +}
1413 +
1414 +struct pattern_array *pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern)
1415 +{
1416 + if (unlikely(!pattern || !key))
1417 + return pa;
1418 +
1419 + if (!pa)
1420 + pa = pattern_array_allocate();
1421 +
1422 + pattern_array_add_lblkey_with_sp(pa, key, pattern);
1423 + return pa;
1424 +}
1425 +
1426 +struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep)
1427 +{
1428 + if (unlikely(!pattern))
1429 + return pa;
1430 +
1431 + if (!pa)
1432 + pa = pattern_array_allocate();
1433 +
1434 + char *label_key;
1435 + while (pattern && (label_key = simple_pattern_iterate(&pattern))) {
1436 + char key[RRDLABELS_MAX_NAME_LENGTH + 1], *key_sep;
1437 +
1438 + if (unlikely(!label_key || !(key_sep = strchr(label_key, sep))))
1439 + return pa;
1440 +
1441 + *key_sep = '\0';
1442 + strncpyz(key, label_key, RRDLABELS_MAX_NAME_LENGTH);
1443 + *key_sep = sep;
1444 +
1445 + pattern_array_add_lblkey_with_sp(pa, key, string_to_simple_pattern(label_key));
1446 + }
1447 + return pa;
1448 +}
1449 +
1450 +struct pattern_array *pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep)
1451 +{
1452 + if (unlikely(!key || !value))
1453 + return pa;
1454 +
1455 + if (!pa)
1456 + pa = pattern_array_allocate();
1457 +
1458 + char label_key[RRDLABELS_MAX_NAME_LENGTH + RRDLABELS_MAX_VALUE_LENGTH + 2];
1459 + snprintfz(label_key, sizeof(label_key) - 1, "%s%c%s", key, sep, value);
1460 + pattern_array_add_lblkey_with_sp(
1461 + pa, key, simple_pattern_create(label_key, SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true));
1462 + return pa;
1463 +}
1464 +
1465 +void pattern_array_free(struct pattern_array *pa)
1466 +{
1467 + if (!pa)
1468 + return;
1469 +
1470 + Pvoid_t *Pvalue;
1471 + Word_t Index = 0;
1472 + while ((Pvalue = JudyLFirst(pa->JudyL, &Index, PJE0))) {
1473 + struct pattern_array_item *pai = *Pvalue;
1474 +
1475 + for (Word_t i = 1; i <= pai->size; i++) {
1476 + if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0)))
1477 + continue;
1478 + simple_pattern_free((SIMPLE_PATTERN *) (*Pvalue));
1479 + }
1480 + JudyLFreeArray(&(pai->JudyL), PJE0);
1481 +
1482 + string_freez((STRING *)Index);
1483 + (void) JudyLDel(&(pa->JudyL), Index, PJE0);
1484 + Index = 0;
1485 + }
1486 +}
1487
1488 // ----------------------------------------------------------------------------
1489 // rrdlabels unit test
@@ -1549,6 +1686,70 @@ static int unittest_dump_labels(const char *name, const char *value, RRDLABEL_SR
1686 return 1;
1687 }
1688
1689 +static int rrdlabels_unittest_pattern_check()
1690 +{
1691 + fprintf(stderr, "\n%s() tests\n", __FUNCTION__);
1692 + int rc = 0;
1693 +
1694 + RRDLABELS *labels = NULL;
1695 +
1696 + labels = rrdlabels_create();
1697 +
1698 + rrdlabels_add(labels, "_module", "disk_detection", RRDLABEL_SRC_CONFIG);
1699 + rrdlabels_add(labels, "_plugin", "super_plugin", RRDLABEL_SRC_CONFIG);
1700 + rrdlabels_add(labels, "key1", "value1", RRDLABEL_SRC_CONFIG);
1701 + rrdlabels_add(labels, "key2", "caterpillar", RRDLABEL_SRC_CONFIG);
1702 + rrdlabels_add(labels, "key3", "elephant", RRDLABEL_SRC_CONFIG);
1703 + rrdlabels_add(labels, "key4", "value4", RRDLABEL_SRC_CONFIG);
1704 +
1705 + bool match;
1706 + struct pattern_array *pa = pattern_array_add_key_value(NULL, "_module", "wrong_module", '=');
1707 + match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1708 + // This should not match: _module in ("wrong_module")
1709 + if (match)
1710 + rc++;
1711 +
1712 + pattern_array_add_key_value(pa, "_module", "disk_detection", '=');
1713 + match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1714 + // This should match: _module in ("wrong_module","disk_detection")
1715 + if (!match)
1716 + rc++;
1717 +
1718 + pattern_array_add_key_value(pa, "key1", "wrong_key1_value", '=');
1719 + match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1720 + // This should not match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value")
1721 + if (match)
1722 + rc++;
1723 +
1724 + pattern_array_add_key_value(pa, "key1", "value1", '=');
1725 + match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1726 + // This should match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1")
1727 + if (!match)
1728 + rc++;
1729 +
1730 + SIMPLE_PATTERN *sp = simple_pattern_create("key2=cat*,!d*", SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true);
1731 + pattern_array_add_lblkey_with_sp(pa, "key2", sp);
1732 +
1733 + sp = simple_pattern_create("key3=*phant", SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true);
1734 + pattern_array_add_lblkey_with_sp(pa, "key3", sp);
1735 +
1736 + match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1737 + // This should match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1") AND key2 in ("cat* !d*") AND key3 in ("*phant")
1738 + if (!match)
1739 + rc++;
1740 +
1741 + rrdlabels_add(labels, "key3", "now_fail", RRDLABEL_SRC_CONFIG);
1742 + match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1743 + // This should not match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1") AND key2 in ("cat* !d*") AND key3 in ("*phant")
1744 + if (match)
1745 + rc++;
1746 +
1747 + pattern_array_free(pa);
1748 + rrdlabels_destroy(labels);
1749 +
1750 + return rc;
1751 +}
1752 +
1753 static int rrdlabels_unittest_migrate_check()
1754 {
1755 fprintf(stderr, "\n%s() tests\n", __FUNCTION__);
@@ -1724,7 +1925,9 @@ int rrdlabels_unittest(void) {
1925 errors += rrdlabels_unittest_simple_pattern();
1926 errors += rrdlabels_unittest_double_check();
1927 errors += rrdlabels_unittest_migrate_check();
1928 + errors += rrdlabels_unittest_pattern_check();
1929
1930 fprintf(stderr, "%d errors found\n", errors);
1931 return errors;
1932 }
1933 +
src/database/rrdlabels.h
+24
@@ -5,6 +5,16 @@
5
6 #include "rrd.h"
7
8 +struct pattern_array_item {
9 + Word_t size;
10 + Pvoid_t JudyL;
11 +};
12 +
13 +struct pattern_array {
14 + Word_t key_count;
15 + Pvoid_t JudyL;
16 +};
17 +
18 typedef enum __attribute__ ((__packed__)) rrdlabel_source {
19 RRDLABEL_SRC_AUTO = (1 << 0), // set when Netdata found the label by some automation
20 RRDLABEL_SRC_CONFIG = (1 << 1), // set when the user configured the label
@@ -52,6 +62,20 @@ void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src);
62 void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src);
63 size_t rrdlabels_common_count(RRDLABELS *labels1, RRDLABELS *labels2);
64
65 +struct pattern_array *pattern_array_allocate();
66 +struct pattern_array *
67 +pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep);
68 +bool pattern_array_label_match(
69 + struct pattern_array *pa,
70 + RRDLABELS *labels,
71 + char eq,
72 + size_t *searches,
73 + bool (*callback_function)(RRDLABELS *, SIMPLE_PATTERN *, char, size_t *));
74 +struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep);
75 +struct pattern_array *
76 +pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern);
77 +void pattern_array_free(struct pattern_array *pa);
78 +
79 int rrdlabels_unittest(void);
80
81 // unfortunately this break when defined in exporting_engine.h
src/database/sqlite/sqlite_health.c
+6 -31
@@ -897,12 +897,12 @@ void sql_health_alarm_log_load(RRDHOST *host)
897 */
898 #define SQL_STORE_ALERT_CONFIG_HASH \
899 "insert or replace into alert_hash (hash_id, date_updated, alarm, template, " \
900 - "on_key, class, component, type, os, hosts, lookup, every, units, calc, plugin, module, " \
901 - "charts, green, red, warn, crit, exec, to_key, info, delay, options, repeat, host_labels, " \
900 + "on_key, class, component, type, lookup, every, units, calc, " \
901 + "green, red, warn, crit, exec, to_key, info, delay, options, repeat, host_labels, " \
902 "p_db_lookup_dimensions, p_db_lookup_method, p_db_lookup_options, p_db_lookup_after, " \
903 "p_db_lookup_before, p_update_every, source, chart_labels, summary) values (@hash_id,UNIXEPOCH(),@alarm,@template," \
904 - "@on_key,@class,@component,@type,@os,@hosts,@lookup,@every,@units,@calc,@plugin,@module," \
905 - "@charts,@green,@red,@warn,@crit,@exec,@to_key,@info,@delay,@options,@repeat,@host_labels," \
904 + "@on_key,@class,@component,@type,@lookup,@every,@units,@calc," \
905 + "@green,@red,@warn,@crit,@exec,@to_key,@info,@delay,@options,@repeat,@host_labels," \
906 "@p_db_lookup_dimensions,@p_db_lookup_method,@p_db_lookup_options,@p_db_lookup_after," \
907 "@p_db_lookup_before,@p_update_every,@source,@chart_labels,@summary)"
908
@@ -966,14 +966,6 @@ int sql_alert_store_config(RRD_ALERT_PROTOTYPE *ap __maybe_unused)
966 if (unlikely(rc != SQLITE_OK))
967 goto bind_fail;
968
969 - rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.os, ++param);
970 - if (unlikely(rc != SQLITE_OK))
971 - goto bind_fail;
972 -
973 - rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.host, ++param);
974 - if (unlikely(rc != SQLITE_OK))
975 - goto bind_fail;
976 -
969 rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.lookup, ++param);
970 if (unlikely(rc != SQLITE_OK))
971 goto bind_fail;
@@ -993,18 +985,6 @@ int sql_alert_store_config(RRD_ALERT_PROTOTYPE *ap __maybe_unused)
985 if (unlikely(rc != SQLITE_OK))
986 goto bind_fail;
987
996 - rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.plugin, ++param);
997 - if (unlikely(rc != SQLITE_OK))
998 - goto bind_fail;
999 -
1000 - rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.module, ++param);
1001 - if (unlikely(rc != SQLITE_OK))
1002 - goto bind_fail;
1003 -
1004 - rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.charts, ++param);
1005 - if (unlikely(rc != SQLITE_OK))
1006 - goto bind_fail;
1007 -
988 rc = sqlite3_bind_double(res, ++param, ap->config.green);
989 if (unlikely(rc != SQLITE_OK))
990 goto bind_fail;
@@ -1855,8 +1835,8 @@ done_only_drop:
1835 #define SQL_POPULATE_TEMP_CONFIG_TARGET_TABLE "INSERT INTO c_%p (hash_id) VALUES (@hash_id)"
1836
1837 #define SQL_SEARCH_CONFIG_LIST \
1858 - "SELECT ah.hash_id, alarm, template, on_key, class, component, type, os, hosts, lookup, every, " \
1859 - " units, calc, families, plugin, module, charts, green, red, warn, crit, " \
1838 + "SELECT ah.hash_id, alarm, template, on_key, class, component, type, lookup, every, " \
1839 + " units, calc, families, green, red, warn, crit, " \
1840 " exec, to_key, info, delay, options, repeat, host_labels, p_db_lookup_dimensions, p_db_lookup_method, " \
1841 " p_db_lookup_options, p_db_lookup_after, p_db_lookup_before, p_update_every, source, chart_labels, summary " \
1842 " FROM alert_hash ah, c_%p t where ah.hash_id = t.hash_id"
@@ -1938,16 +1918,11 @@ int sql_get_alert_configuration(
1918 acd.classification = (const char *) sqlite3_column_text(res, param++);
1919 acd.component = (const char *) sqlite3_column_text(res, param++);
1920 acd.type = (const char *) sqlite3_column_text(res, param++);
1941 - acd.selectors.os = (const char *) sqlite3_column_text(res, param++);
1942 - acd.selectors.hosts = (const char *) sqlite3_column_text(res, param++);
1921 acd.value.db.lookup = (const char *) sqlite3_column_text(res, param++);
1922 acd.value.every = (const char *) sqlite3_column_text(res, param++);
1923 acd.value.units = (const char *) sqlite3_column_text(res, param++);
1924 acd.value.calc = (const char *) sqlite3_column_text(res, param++);
1925 acd.selectors.families = (const char *) sqlite3_column_text(res, param++);
1948 - acd.selectors.plugin = (const char *) sqlite3_column_text(res, param++);
1949 - acd.selectors.module = (const char *) sqlite3_column_text(res, param++);
1950 - acd.selectors.charts = (const char *) sqlite3_column_text(res, param++);
1926 acd.status.green = (const char *) sqlite3_column_text(res, param++);
1927 acd.status.red = (const char *) sqlite3_column_text(res, param++);
1928 acd.status.warn = (const char *) sqlite3_column_text(res, param++);
src/health/REFERENCE.md
-18
@@ -242,7 +242,6 @@ Netdata parses the following lines. Beneath the table is an in-depth explanation
242 | [`hosts`](#alert-line-hosts) | no | Which hostnames will run this alert. |
243 | [`plugin`](#alert-line-plugin) | no | Restrict an alert or template to only a certain plugin. |
244 | [`module`](#alert-line-module) | no | Restrict an alert or template to only a certain module. |
245 -| [`charts`](#alert-line-charts) | no | Restrict an alert or template to only certain charts. |
245 | [`lookup`](#alert-line-lookup) | yes | The database lookup to find and process metrics for the chart specified through `on`. |
246 | [`calc`](#alert-line-calc) | yes (see above) | A calculation to apply to the value found via `lookup` or another variable. |
247 | [`every`](#alert-line-every) | no | The frequency of the alert. |
@@ -433,19 +432,6 @@ plugin: python.d.plugin
432 module: isc_dhcpd
433 ```
434
436 -#### Alert line `charts`
437 -
438 -The `charts` line filters which chart this alert should apply to. It is only available on entities using the
439 -[`template`](#alert-line-alarm-or-template) line.
440 -The value is a space-separated list of [simple patterns](https://github.com/netdata/netdata/blob/master/src/libnetdata/simple_pattern/README.md). For
441 -example, a template that applies to `disk.svctm` (Average Service Time) context, but excludes the disk `sdb` from alerts:
442 -
443 -```yaml
444 -template: disk_svctm_alert
445 - on: disk.svctm
446 - charts: !*sdb* *
447 -```
448 -
435 #### Alert line `lookup`
436
437 This line makes a database lookup to find a value. This result of this lookup is available as `$this`.
@@ -897,10 +883,6 @@ context are essentially identical, with the only difference being the family tha
883 that resolves to unix timestamp the dimension was last collected (there may be dimensions
884 that fail to be collected while others continue normally).
885
900 -- **family variables**. Families are used to group charts together. For example all `eth0`
901 - charts, have `family = eth0`. This index includes all local variables, but if there are
902 - overlapping variables, only the first are exposed.
903 -
886 - **host variables**. All the dimensions of all charts, including all alerts, in fullname.
887 Fullname is `CHART.VARIABLE`, where `CHART` is either the chart id or the chart name (both
888 are supported).
src/health/health_config.c
+38 -21
@@ -340,7 +340,7 @@ static inline void strip_quotes(char *s) {
340 }
341 }
342
343 -#define PARSE_HEALTH_CONFIG_DUPLICATE_STRING_MSG(ax, member) do { \
343 +#define PARSE_HEALTH_CONFIG_LOG_DUPLICATE_STRING_MSG(ax, member) do { \
344 if(strcmp(string2str(ax->member), value) != 0) \
345 netdata_log_error( \
346 "Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, " \
@@ -351,26 +351,48 @@ static inline void strip_quotes(char *s) {
351
352 #define PARSE_HEALTH_CONFIG_LINE_STRING(ax, member) do { \
353 if(ax->member) { \
354 - PARSE_HEALTH_CONFIG_DUPLICATE_STRING_MSG(ax, member); \
354 + PARSE_HEALTH_CONFIG_LOG_DUPLICATE_STRING_MSG(ax, member); \
355 string_freez(ax->member); \
356 } \
357 ax->member = string_strdupz(value); \
358 } while(0)
359
360 -#define PARSE_HEALTH_CONFIG_LINE_PATTERN(ax, member) do { \
361 - if(ax->member) { \
362 - PARSE_HEALTH_CONFIG_DUPLICATE_STRING_MSG(ax, member); \
363 - string_freez(ax->member); \
364 - } \
365 - if(value && strcmp(value, "*") == 0) \
360 +#define PARSE_HEALTH_CONFIG_LINE_PATTERN_APPEND(ax, member, label) do { \
361 + const char *_label = label; \
362 + if(_label && !*_label) \
363 + _label = NULL; \
364 + \
365 + if(value && (!*value || strcmp(value, "*") == 0)) \
366 value = NULL; \
367 else if(value && (strcmp(value, "!* *") == 0 || strcmp(value, "!*") == 0)) { \
368 value = NULL; \
369 ap->match.enabled = false; \
370 } \
371 - ax->member = string_strdupz(value); \
371 + \
372 + if(value && !_label && !strchr(value, '=')) { \
373 + netdata_log_error( \
374 + "Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' " \
375 + "with value '%s' that does not match label=pattern. Ignoring it.", \
376 + line, filename, string2str(ac->name), key, value); \
377 + value = NULL; \
378 + } \
379 + \
380 + if(value) { \
381 + typeof(ax->member) _old = ax->member; \
382 + char _buf[strlen(value) + string_strlen(_old) + (_label ? strlen(_label) : 0) + 3]; \
383 + snprintfz(_buf, sizeof(_buf), "%s%s%s%s%s", \
384 + _label ? _label : "", \
385 + _label ? "=" : "", \
386 + value, \
387 + _old ? " " : "", \
388 + _old ? string2str(_old) : ""); \
389 + string_freez(_old); \
390 + ax->member = string_strdupz(_buf); \
391 + } \
392 } while(0)
393
394 +
395 +
396 int health_readfile(const char *filename, void *data __maybe_unused, bool stock_config) {
397 netdata_log_debug(D_HEALTH, "Health configuration reading file '%s'", filename);
398
@@ -382,7 +404,6 @@ int health_readfile(const char *filename, void *data __maybe_unused, bool stock_
404 hash_host = 0,
405 hash_plugin = 0,
406 hash_module = 0,
385 - hash_charts = 0,
407 hash_calc = 0,
408 hash_green = 0,
409 hash_red = 0,
@@ -414,7 +435,6 @@ int health_readfile(const char *filename, void *data __maybe_unused, bool stock_
435 hash_host = simple_uhash(HEALTH_HOST_KEY);
436 hash_plugin = simple_uhash(HEALTH_PLUGIN_KEY);
437 hash_module = simple_uhash(HEALTH_MODULE_KEY);
417 - hash_charts = simple_uhash(HEALTH_CHARTS_KEY);
438 hash_calc = simple_uhash(HEALTH_CALC_KEY);
439 hash_lookup = simple_uhash(HEALTH_LOOKUP_KEY);
440 hash_green = simple_uhash(HEALTH_GREEN_KEY);
@@ -542,26 +562,23 @@ int health_readfile(const char *filename, void *data __maybe_unused, bool stock_
562 else if(am->is_template && hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
563 PARSE_HEALTH_CONFIG_LINE_STRING(am, on.context);
564 }
545 - else if(am->is_template && hash == hash_charts && !strcasecmp(key, HEALTH_CHARTS_KEY)) {
546 - PARSE_HEALTH_CONFIG_LINE_PATTERN(am, charts);
547 - }
565 else if(hash == hash_os && !strcasecmp(key, HEALTH_OS_KEY)) {
549 - PARSE_HEALTH_CONFIG_LINE_PATTERN(am, os);
566 + PARSE_HEALTH_CONFIG_LINE_PATTERN_APPEND(am, host_labels, "_os");
567 }
568 else if(hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
552 - PARSE_HEALTH_CONFIG_LINE_PATTERN(am, host);
569 + PARSE_HEALTH_CONFIG_LINE_PATTERN_APPEND(am, host_labels, "_hostname");
570 }
571 else if(hash == hash_host_label && !strcasecmp(key, HEALTH_HOST_LABEL_KEY)) {
555 - PARSE_HEALTH_CONFIG_LINE_PATTERN(am, host_labels);
572 + PARSE_HEALTH_CONFIG_LINE_PATTERN_APPEND(am, host_labels, NULL);
573 }
574 else if(hash == hash_plugin && !strcasecmp(key, HEALTH_PLUGIN_KEY)) {
558 - PARSE_HEALTH_CONFIG_LINE_PATTERN(am, plugin);
575 + PARSE_HEALTH_CONFIG_LINE_PATTERN_APPEND(am, chart_labels, "_collect_plugin");
576 }
577 else if(hash == hash_module && !strcasecmp(key, HEALTH_MODULE_KEY)) {
561 - PARSE_HEALTH_CONFIG_LINE_PATTERN(am, module);
578 + PARSE_HEALTH_CONFIG_LINE_PATTERN_APPEND(am, chart_labels, "_collect_module");
579 }
580 else if(hash == hash_chart_label && !strcasecmp(key, HEALTH_CHART_LABEL_KEY)) {
564 - PARSE_HEALTH_CONFIG_LINE_PATTERN(am, chart_labels);
581 + PARSE_HEALTH_CONFIG_LINE_PATTERN_APPEND(am, chart_labels, NULL);
582 }
583 else if(hash == hash_class && !strcasecmp(key, HEALTH_CLASS_KEY)) {
584 strip_quotes(value);
@@ -680,7 +697,7 @@ int health_readfile(const char *filename, void *data __maybe_unused, bool stock_
697 ac->has_custom_repeat_config = true;
698 }
699 else {
683 - if (strcmp(key, "families") != 0)
700 + if (strcmp(key, "families") != 0 && strcmp(key, "charts") != 0)
701 netdata_log_error(
702 "Health configuration at line %zu of file '%s' for alarm/template '%s' has unknown key '%s'.",
703 line, filename, string2str(ac->name), key);
src/health/health_dyncfg.c
-13
@@ -17,14 +17,6 @@ static bool parse_match(json_object *jobj, const char *path, struct rrd_alert_ma
17 else
18 match->on.chart = on;
19
20 - JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "os", match->os, error);
21 - JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "host", match->host, error);
22 -
23 - if(match->is_template)
24 - JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "instances", match->charts, error);
25 -
26 - JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "plugin", match->plugin, error);
27 - JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "module", match->module, error);
20 JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "host_labels", match->host_labels, error);
21 JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "instance_labels", match->chart_labels, error);
22
@@ -219,11 +211,6 @@ static inline void health_prototype_rule_to_json_array_member(BUFFER *wb, RRD_AL
211 else
212 buffer_json_member_add_string(wb, "on", string2str(ap->match.on.chart));
213
222 - buffer_json_member_add_string_or_empty(wb, "os", ap->match.os ? string2str(ap->match.os) : "*");
223 - buffer_json_member_add_string_or_empty(wb, "host", ap->match.host ? string2str(ap->match.host) : "*");
224 - buffer_json_member_add_string_or_empty(wb, "instances", ap->match.charts ? string2str(ap->match.charts) : "*");
225 - buffer_json_member_add_string_or_empty(wb, "plugin", ap->match.charts ? string2str(ap->match.plugin) : "*");
226 - buffer_json_member_add_string_or_empty(wb, "module", ap->match.module ? string2str(ap->match.module) : "*");
214 buffer_json_member_add_string_or_empty(wb, "host_labels", ap->match.host_labels ? string2str(ap->match.host_labels) : "*");
215 buffer_json_member_add_string_or_empty(wb, "instance_labels", ap->match.chart_labels ? string2str(ap->match.chart_labels) : "*");
216 }
src/health/health_internals.h
-2
@@ -22,7 +22,6 @@
22 #define HEALTH_OS_KEY "os"
23 #define HEALTH_PLUGIN_KEY "plugin"
24 #define HEALTH_MODULE_KEY "module"
25 -#define HEALTH_CHARTS_KEY "charts"
25 #define HEALTH_LOOKUP_KEY "lookup"
26 #define HEALTH_CALC_KEY "calc"
27 #define HEALTH_EVERY_KEY "every"
@@ -42,7 +41,6 @@
41 #define HEALTH_OPTIONS_KEY "options"
42 #define HEALTH_REPEAT_KEY "repeat"
43 #define HEALTH_HOST_LABEL_KEY "host labels"
45 -#define HEALTH_FOREACH_KEY "foreach"
44 #define HEALTH_CHART_LABEL_KEY "chart labels"
45
46 void alert_action_options_to_buffer_json_array(BUFFER *wb, const char *key, ALERT_ACTION_OPTIONS options);
src/health/health_prototypes.c
+49 -99
@@ -166,16 +166,20 @@ void health_init_prototypes(void) {
166
167 // ---------------------------------------------------------------------------------------------------------------------
168
169 -// If needed, add a prefix key to all possible values in the range
170 -static inline char *health_config_add_key_to_values(char *value) {
171 - BUFFER *wb = buffer_create(HEALTH_CONF_MAX_LINE + 1, NULL);
169 +static inline struct pattern_array *health_config_add_key_to_values(struct pattern_array *pa, const char *input_key, char *value)
170 +{
171 char key[HEALTH_CONF_MAX_LINE + 1];
172 char data[HEALTH_CONF_MAX_LINE + 1];
173
174 char *s = value;
175 size_t i = 0;
176
178 - key[0] = '\0';
177 + char pair[HEALTH_CONF_MAX_LINE + 1];
178 + if (input_key)
179 + strncpyz(key, input_key, HEALTH_CONF_MAX_LINE);
180 + else
181 + key[0] = '\0';
182 +
183 while(*s) {
184 if (*s == '=') {
185 //hold the key
@@ -185,94 +189,69 @@ static inline char *health_config_add_key_to_values(char *value) {
189 } else if (*s == ' ') {
190 data[i]='\0';
191 if (data[0]=='!')
188 - buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
192 + snprintfz(pair, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
193 else
190 - buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
194 + snprintfz(pair, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
195 +
196 + pa = pattern_array_add_key_simple_pattern(pa, key, simple_pattern_create(pair, NULL, SIMPLE_PATTERN_EXACT, true));
197 i=0;
198 } else {
199 data[i++] = *s;
200 }
201 s++;
202 }
197 -
203 data[i]='\0';
204 if (data[0]) {
205 if (data[0]=='!')
201 - buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
206 + snprintfz(pair, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
207 else
203 - buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
204 - }
205 -
206 - char *final = strdupz(buffer_tostring(wb));
207 - buffer_free(wb);
208 -
209 - return final;
210 -}
208 + snprintfz(pair, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
209
212 -static void health_prototype_activate_match_patterns(struct rrd_alert_match *am) {
213 - if(am->os) {
214 - simple_pattern_free(am->os_pattern);
215 -
216 - char *tmp = simple_pattern_trim_around_equal(string2str(am->os));
217 - am->os_pattern = simple_pattern_create(
218 - tmp, NULL, SIMPLE_PATTERN_EXACT, true);
219 - freez(tmp);
210 + pa = pattern_array_add_key_simple_pattern(pa, key, simple_pattern_create(pair, NULL, SIMPLE_PATTERN_EXACT, true));
211 }
212
222 - if(am->host) {
223 - simple_pattern_free(am->host_pattern);
224 -
225 - char *tmp = simple_pattern_trim_around_equal(string2str(am->host));
226 - am->host_pattern = simple_pattern_create(
227 - tmp, NULL, SIMPLE_PATTERN_EXACT, true);
228 - freez(tmp);
229 - }
213 + return pa;
214 +}
215
231 - if(am->charts) {
232 - simple_pattern_free(am->charts_pattern);
216 +static char *simple_pattern_trim_around_equal(const char *src) {
217 + char *store = mallocz(strlen(src) + 1);
218
234 - char *tmp = simple_pattern_trim_around_equal(string2str(am->charts));
235 - am->charts_pattern = simple_pattern_create(
236 - tmp, NULL, SIMPLE_PATTERN_EXACT, true);
237 - freez(tmp);
238 - }
219 + char *dst = store;
220 + while (*src) {
221 + if (*src == '=') {
222 + if (*(dst -1) == ' ')
223 + dst--;
224
240 - if(am->plugin) {
241 - simple_pattern_free(am->plugin_pattern);
225 + *dst++ = *src++;
226 + if (*src == ' ')
227 + src++;
228 + }
229
243 - char *tmp = simple_pattern_trim_around_equal(string2str(am->plugin));
244 - am->plugin_pattern = simple_pattern_create(
245 - tmp, NULL, SIMPLE_PATTERN_EXACT, true);
246 - freez(tmp);
230 + *dst++ = *src++;
231 }
232 + *dst = 0x00;
233
249 - if(am->module) {
250 - simple_pattern_free(am->module_pattern);
234 + return store;
235 +}
236
252 - char *tmp = simple_pattern_trim_around_equal(string2str(am->module));
253 - am->module_pattern = simple_pattern_create(
254 - tmp, NULL, SIMPLE_PATTERN_EXACT, true);
255 - freez(tmp);
256 - }
237 +static struct pattern_array *trim_and_add_key_to_values(struct pattern_array *pa, const char *key, STRING *input) {
238 + char *tmp = simple_pattern_trim_around_equal(string2str(input));
239 + pa = health_config_add_key_to_values(pa, key, tmp);
240 + freez(tmp);
241 + return pa;
242 +}
243
244 +static void health_prototype_activate_match_patterns(struct rrd_alert_match *am) {
245 if(am->host_labels) {
259 - simple_pattern_free(am->host_labels_pattern);
260 -
261 - char *tmp = simple_pattern_trim_around_equal(string2str(am->host_labels));
262 - am->host_labels_pattern = simple_pattern_create(
263 - tmp, NULL, SIMPLE_PATTERN_EXACT, true);
264 - freez(tmp);
246 + pattern_array_free(am->host_labels_pattern);
247 + am->host_labels_pattern = NULL;
248 + am->host_labels_pattern = trim_and_add_key_to_values(am->host_labels_pattern, NULL, am->host_labels);
249 }
250
251 if(am->chart_labels) {
268 - simple_pattern_free(am->chart_labels_pattern);
269 -
270 - char *tmp = simple_pattern_trim_around_equal(string2str(am->chart_labels));
271 - char *tmp2 = health_config_add_key_to_values(tmp);
272 - am->chart_labels_pattern = simple_pattern_create(
273 - tmp2, NULL, SIMPLE_PATTERN_EXACT, true);
274 - freez(tmp2);
275 - freez(tmp);
252 + pattern_array_free(am->chart_labels_pattern);
253 + am->chart_labels_pattern = NULL;
254 + am->chart_labels_pattern = trim_and_add_key_to_values(am->chart_labels_pattern, NULL, am->chart_labels);
255 }
256 }
257
@@ -387,15 +366,8 @@ static bool prototype_matches_host(RRDHOST *host, RRD_ALERT_PROTOTYPE *ap) {
366 !simple_pattern_matches(health_globals.config.enabled_alerts, string2str(ap->config.name)))
367 return false;
368
390 - if(ap->match.os_pattern && !simple_pattern_matches_string(ap->match.os_pattern, host->os))
391 - return false;
392 -
393 - if(ap->match.host_pattern && !simple_pattern_matches_string(ap->match.host_pattern, host->hostname))
394 - return false;
395 -
396 - if(host->rrdlabels && ap->match.host_labels_pattern &&
397 - !rrdlabels_match_simple_pattern_parsed(
398 - host->rrdlabels, ap->match.host_labels_pattern, '=', NULL))
369 + if (host->rrdlabels && ap->match.host_labels_pattern &&
370 + !pattern_array_label_match(ap->match.host_labels_pattern, host->rrdlabels, '=', NULL, rrdlabels_match_simple_pattern_parsed))
371 return false;
372
373 return true;
@@ -412,25 +384,8 @@ static bool prototype_matches_rrdset(RRDSET *st, RRD_ALERT_PROTOTYPE *ap) {
384 ap->match.on.context != st->context)
385 return false;
386
415 - // match the chart pattern
416 - if(ap->match.is_template && ap->match.charts && ap->match.charts_pattern &&
417 - !simple_pattern_matches_string(ap->match.charts_pattern, st->id) &&
418 - !simple_pattern_matches_string(ap->match.charts_pattern, st->name))
419 - return false;
420 -
421 - // match the plugin pattern
422 - if(ap->match.plugin && ap->match.plugin_pattern &&
423 - !simple_pattern_matches_string(ap->match.plugin_pattern, st->plugin_name))
424 - return false;
425 -
426 - // match the module pattern
427 - if(ap->match.module && ap->match.module_pattern &&
428 - !simple_pattern_matches_string(ap->match.module_pattern, st->module_name))
429 - return false;
430 -
431 - if (st->rrdlabels && ap->match.chart_labels && ap->match.chart_labels_pattern &&
432 - !rrdlabels_match_simple_pattern_parsed(
433 - st->rrdlabels, ap->match.chart_labels_pattern, '=', NULL))
387 + if (st->rrdlabels && ap->match.chart_labels_pattern &&
388 + !pattern_array_label_match(ap->match.chart_labels_pattern, st->rrdlabels, '=', NULL, rrdlabels_match_simple_pattern_parsed))
389 return false;
390
391 return true;
@@ -445,11 +400,6 @@ void health_prototype_copy_match_without_patterns(struct rrd_alert_match *dst, s
400 else
401 dst->on.chart = string_dup(src->on.chart);
402
448 - dst->os = string_dup(src->os);
449 - dst->host = string_dup(src->host);
450 - dst->charts = string_dup(src->charts);
451 - dst->plugin = string_dup(src->plugin);
452 - dst->module = string_dup(src->module);
403 dst->host_labels = string_dup(src->host_labels);
404 dst->chart_labels = string_dup(src->chart_labels);
405 }
src/health/health_prototypes.h
+2 -12
@@ -19,21 +19,11 @@ struct rrd_alert_match {
19 STRING *context;
20 } on;
21
22 - STRING *os;
23 - STRING *host;
24 - STRING *charts; // the charts that should be linked to (for templates)
25 - STRING *plugin; // the plugin name that should be linked to
26 - STRING *module; // the module name that should be linked to
22 STRING *host_labels; // the label read from an alarm file
23 STRING *chart_labels; // the chart label read from an alarm file
24
30 - SIMPLE_PATTERN *os_pattern;
31 - SIMPLE_PATTERN *host_pattern;
32 - SIMPLE_PATTERN *charts_pattern; // the simple pattern of charts
33 - SIMPLE_PATTERN *plugin_pattern; // the simple pattern of plugin
34 - SIMPLE_PATTERN *module_pattern; // the simple pattern of module
35 - SIMPLE_PATTERN *host_labels_pattern; // the simple pattern of labels
36 - SIMPLE_PATTERN *chart_labels_pattern; // the simple pattern of chart labels
25 + struct pattern_array *host_labels_pattern;
26 + struct pattern_array *chart_labels_pattern;
27 };
28 void rrd_alert_match_cleanup(struct rrd_alert_match *am);
29
src/health/rrdcalc.c
+2 -35
@@ -289,24 +289,6 @@ static void rrdcalc_unlink_from_rrdset(RRDCALC *rc, bool having_ll_wrlock) {
289 rc->rrdset = NULL;
290 }
291
292 -static inline bool rrdcalc_check_if_it_matches_rrdset(RRDCALC *rc, RRDSET *st) {
293 - if ( (rc->chart != st->id)
294 - && (rc->chart != st->name))
295 - return false;
296 -
297 - if (rc->match.module_pattern && !simple_pattern_matches_string(rc->match.module_pattern, st->module_name))
298 - return false;
299 -
300 - if (rc->match.plugin_pattern && !simple_pattern_matches_string(rc->match.plugin_pattern, st->module_name))
301 - return false;
302 -
303 - if (st->rrdlabels && rc->match.chart_labels_pattern && !rrdlabels_match_simple_pattern_parsed(
304 - st->rrdlabels, rc->match.chart_labels_pattern, '=', NULL))
305 - return false;
306 -
307 - return true;
308 -}
309 -
292 // ----------------------------------------------------------------------------
293 // RRDCALC rrdhost index management - constructor
294
@@ -493,26 +475,11 @@ void rrd_alert_match_cleanup(struct rrd_alert_match *am) {
475 else
476 string_freez(am->on.chart);
477
496 - string_freez(am->os);
497 - simple_pattern_free(am->os_pattern);
498 -
499 - string_freez(am->host);
500 - simple_pattern_free(am->host_pattern);
501 -
502 - string_freez(am->plugin);
503 - simple_pattern_free(am->plugin_pattern);
504 -
505 - string_freez(am->module);
506 - simple_pattern_free(am->module_pattern);
507 -
508 - string_freez(am->charts);
509 - simple_pattern_free(am->charts_pattern);
510 -
478 string_freez(am->host_labels);
512 - simple_pattern_free(am->host_labels_pattern);
479 + pattern_array_free(am->host_labels_pattern);
480
481 string_freez(am->chart_labels);
515 - simple_pattern_free(am->chart_labels_pattern);
482 + pattern_array_free(am->chart_labels_pattern);
483 }
484
485 void rrd_alert_config_cleanup(struct rrd_alert_config *ac) {
src/health/schema.d/health:alert:prototype.json
+12 -41
@@ -12,11 +12,6 @@
12 "default": "*",
13 "title": "Only for nodes with these host labels"
14 },
15 - "matchHostnames": {
16 - "type": "string",
17 - "default": "*",
18 - "title": "Only for these hostnames"
19 - },
15 "matchInstance": {
16 "type": "object",
17 "title": "Apply this rule to a single instance",
@@ -29,15 +24,10 @@
24 "description": "You can find the instance names on all charts at the instances drop down menu. Do not include the host name in this field."
25 },
26 "host_labels": { "$ref": "#/definitions/matchHostLabels" },
32 - "host": { "$ref": "#/definitions/matchHostnames" },
27 "instance_labels": { "$ref": "#/definitions/matchInstanceLabels" }
28 },
29 "required": [
30 "on",
37 - "os",
38 - "host",
39 - "plugin",
40 - "module",
31 "host_labels",
32 "instance_labels"
33 ]
@@ -54,22 +44,11 @@
44 "description": "The context is the code-name of each chart on the dashboard, that appears at the chart title bar, between the chart title and its unit of measurement, like: system.cpu, disk.io, etc."
45 },
46 "host_labels": { "$ref": "#/definitions/matchHostLabels" },
57 - "host": { "$ref": "#/definitions/matchHostnames" },
58 - "instance_labels": { "$ref": "#/definitions/matchInstanceLabels" },
59 - "instances": {
60 - "type": "string",
61 - "default": "*",
62 - "title": "On on these instances"
63 - }
47 + "instance_labels": { "$ref": "#/definitions/matchInstanceLabels" }
48 },
49 "required": [
50 "on",
67 - "os",
68 - "host",
69 - "plugin",
70 - "module",
51 "host_labels",
72 - "instances",
52 "instance_labels"
53 ]
54 },
@@ -105,7 +84,7 @@
84 },
85 "value": {
86 "type": "object",
108 - "title": "Alert Value Calculation",
87 + "title": "",
88 "description": "Each alert has a value. This section defines how this value is calculated.",
89 "properties": {
90 "database_lookup": {
@@ -210,7 +189,7 @@
189 },
190 "conditions": {
191 "type": "object",
213 - "title": "Conditions to trigger the alert",
192 + "title": "",
193 "properties": {
194 "warning_condition": {
195 "type": "string",
@@ -242,7 +221,7 @@
221 },
222 "action": {
223 "type": "object",
245 - "title": "Alert Action (notification or automation)",
224 + "title": "",
225 "description": "The action the alert should take when it transitions states",
226 "properties": {
227 "execute": {
@@ -393,19 +372,11 @@
372 },
373 "host_labels": {
374 "ui:help": "A simple pattern to match the node labels of the nodes this rule is to be applied to. A space separated list of label=value pairs is accepted. Asterisks can be placed anywhere, including the label key. The label keys and their values are available at the labels filter of the charts on the dashboard.",
396 - "ui:classNames": "dyncfg-grid-col-span-1-4"
397 - },
398 - "host": {
399 - "ui:classNames": "dyncfg-grid-col-span-5-2",
400 - "ui:help": "A simple pattern to match the hostnames of the nodes this rule is to be applied to."
375 + "ui:classNames": "dyncfg-grid-col-span-1-3"
376 },
377 "instance_labels": {
403 - "ui:classNames": "dyncfg-grid-col-span-1-4",
378 + "ui:classNames": "dyncfg-grid-col-span-4-3",
379 "ui:help": "A simple pattern to match the instance labels of the instances this rule is to be applied to. A space separated list of label=value pairs is accepted. Asterisks can be placed anywhere, including the label key. The label keys and their values are available at the labels filter of the charts on the dashboard."
405 - },
406 - "instances": {
407 - "ui:classNames": "dyncfg-grid-col-span-5-2",
408 - "ui:help": "A simple pattern to match the instance names of the instances this rule is to be applied to."
380 }
381 },
382 "config": {
@@ -462,8 +433,8 @@
433 "ui:classNames": "dyncfg-grid dyncfg-grid-col-6 dyncfg-grid-col-span-1-6",
434 "database_lookup": {
435 "ui:classNames": "dyncfg-grid dyncfg-grid-col-6 dyncfg-grid-col-span-1-6",
465 - "ui:Collapsible": true,
466 - "ui:InitiallyExpanded": true,
436 + "ui:collapsible": true,
437 + "ui:initiallyExpanded": true,
438 "after": {
439 "ui:help": "The oldest timestamp of the time-series data to be included in the query. Negative values define a duration in seconds in the past of 'To' (so, -60 means a minute ago from 'To').",
440 "ui:classNames": "dyncfg-grid-col-span-1-1"
@@ -523,8 +494,8 @@
494 "ui:help": "Options related to the actions this alert will take."
495 },
496 "delay": {
526 - "ui:Collapsible": true,
527 - "ui:InitiallyExpanded": false,
497 + "ui:collapsible": true,
498 + "ui:initiallyExpanded": false,
499 "ui:classNames": "dyncfg-grid dyncfg-grid-col-6 dyncfg-grid-col-span-1-6",
500 "up": {
501 "ui:classNames": "dyncfg-grid-col-span-1-2",
@@ -544,8 +515,8 @@
515 }
516 },
517 "repeat": {
547 - "ui:Collapsible": true,
548 - "ui:InitiallyExpanded": false,
518 + "ui:collapsible": true,
519 + "ui:initiallyExpanded": false,
520 "ui:classNames": "dyncfg-grid dyncfg-grid-col-6 dyncfg-grid-col-span-1-6",
521 "enabled": {
522 "ui:classNames": "dyncfg-grid-col-span-1-2"
src/libnetdata/simple_pattern/simple_pattern.c
-21
@@ -396,27 +396,6 @@ extern int simple_pattern_is_potential_name(SIMPLE_PATTERN *p)
396 return (alpha || wildcards) && !colon;
397 }
398
399 -char *simple_pattern_trim_around_equal(const char *src) {
400 - char *store = mallocz(strlen(src) + 1);
401 -
402 - char *dst = store;
403 - while (*src) {
404 - if (*src == '=') {
405 - if (*(dst -1) == ' ')
406 - dst--;
407 -
408 - *dst++ = *src++;
409 - if (*src == ' ')
410 - src++;
411 - }
412 -
413 - *dst++ = *src++;
414 - }
415 - *dst = 0x00;
416 -
417 - return store;
418 -}
419 -
399 char *simple_pattern_iterate(SIMPLE_PATTERN **p)
400 {
401 struct simple_pattern *root = (struct simple_pattern *) *p;
src/libnetdata/simple_pattern/simple_pattern.h
+2 -5
@@ -5,7 +5,6 @@
5
6 #include "../libnetdata.h"
7
8 -
8 typedef enum __attribute__ ((__packed__)) {
9 SIMPLE_PATTERN_EXACT,
10 SIMPLE_PATTERN_PREFIX,
@@ -19,7 +18,8 @@ typedef enum __attribute__ ((__packed__)) {
18 SP_MATCHED_POSITIVE,
19 } SIMPLE_PATTERN_RESULT;
20
22 -typedef void SIMPLE_PATTERN;
21 +struct simple_pattern;
22 +typedef struct simple_pattern SIMPLE_PATTERN;
23
24 // create a simple_pattern from the string given
25 // default_mode is used in cases where EXACT matches, without an asterisk,
@@ -47,9 +47,6 @@ void simple_pattern_dump(uint64_t debug_type, SIMPLE_PATTERN *p) ;
47 int simple_pattern_is_potential_name(SIMPLE_PATTERN *p) ;
48 char *simple_pattern_iterate(SIMPLE_PATTERN **p);
49
50 -// Auxiliary function to create a pattern
51 -char *simple_pattern_trim_around_equal(const char *src);
52 -
50 #define SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS ",|\t\r\n\f\v"
51
52 #define is_valid_sp(x) ((x) && *(x) && !((x)[0] == '*' && (x)[1] == '\0'))