diff options
Diffstat (limited to '')
-rw-r--r-- | src/database/rrdlabels.c (renamed from database/rrdlabels.c) | 387 |
1 files changed, 365 insertions, 22 deletions
diff --git a/database/rrdlabels.c b/src/database/rrdlabels.c index 69ee55526..b82fa76d2 100644 --- a/database/rrdlabels.c +++ b/src/database/rrdlabels.c @@ -448,7 +448,7 @@ __attribute__((constructor)) void initialize_labels_keys_char_map(void) { label_names_char_map[' '] = '_'; label_names_char_map['\\'] = '/'; - // create the spaces map + // create the space map for(i = 0; i < 256 ;i++) label_spaces_char_map[i] = (isspace(i) || iscntrl(i) || !isprint(i))?1:0; @@ -460,8 +460,8 @@ __attribute__((constructor)) void initialize_label_stats(void) { dictionary_stats_category_rrdlabels.memory.values = 0; } -size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length) { - if(unlikely(!dst_size)) return 0; +size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, const unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length) { + if(unlikely(!src || !dst_size)) return 0; if(unlikely(!src || !*src)) { strncpyz((char *)dst, empty, dst_size); @@ -476,7 +476,7 @@ size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_si // make room for the final string termination unsigned char *end = &d[dst_size - 1]; - // copy while converting, but keep only one white space + // copy while converting, but keep only one space // we start wil last_is_space = 1 to skip leading spaces int last_is_space = 1; @@ -671,8 +671,11 @@ void rrdlabels_destroy(RRDLABELS *labels) freez(labels); } +// // Check in labels to see if we have the key specified in label -static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABEL *label) +// same_value indicates if the value should also be matched +// +static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABEL *label, bool same_value) { if (unlikely(!labels)) return NULL; @@ -683,7 +686,7 @@ static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABE RRDLABEL *found = NULL; while ((PValue = JudyLFirstThenNext(labels->JudyL, &Index, &first_then_next))) { RRDLABEL *lb = (RRDLABEL *)Index; - if (lb->index.key == label->index.key && lb != label) { + if (lb->index.key == label->index.key && ((lb == label) == same_value)) { found = (RRDLABEL *)Index; break; } @@ -718,7 +721,7 @@ static void labels_add_already_sanitized(RRDLABELS *labels, const char *key, con new_ls |= RRDLABEL_FLAG_NEW; *((RRDLABEL_SRC *)PValue) = new_ls; - RRDLABEL *old_label_with_same_key = rrdlabels_find_label_with_key_unsafe(labels, new_label); + RRDLABEL *old_label_with_same_key = rrdlabels_find_label_with_key_unsafe(labels, new_label, false); if (old_label_with_same_key) { (void) JudyLDel(&labels->JudyL, (Word_t) old_label_with_same_key, PJE0); delete_label(old_label_with_same_key); @@ -846,6 +849,18 @@ void rrdlabels_value_to_buffer_array_item_or_null(RRDLABELS *labels, BUFFER *wb, string_freez(this_key); } +void rrdlabels_key_to_buffer_array_item(RRDLABELS *labels, BUFFER *wb) +{ + if(!labels) return; + + RRDLABEL *lb; + RRDLABEL_SRC ls; + lfe_start_read(labels, lb, ls) { + buffer_json_add_array_item_string(wb, string2str(lb->index.key)); + } + lfe_done(labels); +} + // ---------------------------------------------------------------------------- void rrdlabels_get_value_strcpyz(RRDLABELS *labels, char *dst, size_t dst_len, const char *key) { @@ -982,6 +997,25 @@ int rrdlabels_walkthrough_read(RRDLABELS *labels, int (*callback)(const char *na return ret; } +static SIMPLE_PATTERN_RESULT rrdlabels_walkthrough_read_sp(RRDLABELS *labels, SIMPLE_PATTERN_RESULT (*callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *data) +{ + SIMPLE_PATTERN_RESULT ret = SP_NOT_MATCHED; + + if(unlikely(!labels || !callback)) return 0; + + RRDLABEL *lb; + RRDLABEL_SRC ls; + lfe_start_read(labels, lb, ls) + { + ret = callback(string2str(lb->index.key), string2str(lb->index.value), ls, data); + if (ret != SP_NOT_MATCHED) + break; + } + lfe_done(labels); + + return ret; +} + // ---------------------------------------------------------------------------- // rrdlabels_migrate_to_these() // migrate an existing label list to a new list @@ -1027,6 +1061,39 @@ void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src) { spinlock_unlock(&dst->spinlock); } +// +// +// Return the common labels count in labels1, labels2 +// +size_t rrdlabels_common_count(RRDLABELS *labels1, RRDLABELS *labels2) +{ + if (!labels1 || !labels2) + return 0; + + if (labels1 == labels2) + return rrdlabels_entries(labels1); + + RRDLABEL *label; + RRDLABEL_SRC ls; + + spinlock_lock(&labels1->spinlock); + spinlock_lock(&labels2->spinlock); + + size_t count = 0; + lfe_start_nolock(labels2, label, ls) + { + RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(labels1, label, true); + if (old_label_with_key) + count++; + } + lfe_done_nolock(); + + spinlock_unlock(&labels2->spinlock); + spinlock_unlock(&labels1->spinlock); + return count; +} + + void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src) { if (!dst || !src || (dst == src)) @@ -1042,7 +1109,7 @@ void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src) bool update_statistics = false; lfe_start_nolock(src, label, ls) { - RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(dst, label); + RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(dst, label, false); Pvoid_t *PValue = JudyLIns(&dst->JudyL, (Word_t)label, PJE0); if(unlikely(!PValue || PValue == PJERR)) fatal("RRDLABELS: corrupted labels array"); @@ -1083,18 +1150,19 @@ struct simple_pattern_match_name_value { char equal; }; -static int simple_pattern_match_name_only_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) { +static SIMPLE_PATTERN_RESULT simple_pattern_match_name_only_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) { struct simple_pattern_match_name_value *t = (struct simple_pattern_match_name_value *)data; (void)value; // we return -1 to stop the walkthrough on first match t->searches++; - if(simple_pattern_matches(t->pattern, name)) return -1; - - return 0; + SIMPLE_PATTERN_RESULT ret = simple_pattern_matches_extract(t->pattern, name, NULL, 0); + if (ret == SP_MATCHED_NEGATIVE) + ret = SP_NOT_MATCHED; + return ret; } -static int simple_pattern_match_name_and_value_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) { +static SIMPLE_PATTERN_RESULT simple_pattern_match_name_and_value_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) { struct simple_pattern_match_name_value *t = (struct simple_pattern_match_name_value *)data; // we return -1 to stop the walkthrough on first match @@ -1118,13 +1186,10 @@ static int simple_pattern_match_name_and_value_callback(const char *name, const *dst = '\0'; t->searches++; - if(simple_pattern_matches_length_extract(t->pattern, tmp, dst - tmp, NULL, 0) == SP_MATCHED_POSITIVE) - return -1; - - return 0; + return simple_pattern_matches_length_extract(t->pattern, tmp, dst - tmp, NULL, 0); } -bool rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches) { +SIMPLE_PATTERN_RESULT rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches) { if (!labels) return false; struct simple_pattern_match_name_value t = { @@ -1133,12 +1198,12 @@ bool rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pa .equal = equal }; - int ret = rrdlabels_walkthrough_read(labels, equal?simple_pattern_match_name_and_value_callback:simple_pattern_match_name_only_callback, &t); + SIMPLE_PATTERN_RESULT ret = rrdlabels_walkthrough_read_sp(labels, equal?simple_pattern_match_name_and_value_callback:simple_pattern_match_name_only_callback, &t); if(searches) *searches = t.searches; - return (ret == -1)?true:false; + return ret; } bool rrdlabels_match_simple_pattern(RRDLABELS *labels, const char *simple_pattern_txt) { @@ -1155,11 +1220,11 @@ bool rrdlabels_match_simple_pattern(RRDLABELS *labels, const char *simple_patter } } - bool ret = rrdlabels_match_simple_pattern_parsed(labels, pattern, equal, NULL); + SIMPLE_PATTERN_RESULT ret = rrdlabels_match_simple_pattern_parsed(labels, pattern, equal, NULL); simple_pattern_free(pattern); - return ret; + return ret == SP_MATCHED_POSITIVE; } @@ -1311,6 +1376,153 @@ void rrdset_update_rrdlabels(RRDSET *st, RRDLABELS *new_rrdlabels) { rrdset_metadata_updated(st); } +struct pattern_array *pattern_array_allocate() +{ + struct pattern_array *pa = callocz(1, sizeof(*pa)); + return pa; +} + +static void pattern_array_add_lblkey_with_sp(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *sp) +{ + if (!pa || !key || !sp) + return; + + STRING *string_key = string_strdupz(key); + Pvoid_t *Pvalue = JudyLIns(&pa->JudyL, (Word_t) string_key, PJE0); + if (!Pvalue) { + string_freez(string_key); + simple_pattern_free(sp); + return; + } + + struct pattern_array_item *pai; + if (*Pvalue) { + pai = *Pvalue; + } else { + *Pvalue = pai = callocz(1, sizeof(*pai)); + pa->key_count++; + } + + pai->size++; + Pvalue = JudyLIns(&pai->JudyL, (Word_t) pai->size, PJE0); + if (!Pvalue) { + simple_pattern_free(sp); + return; + } + + *Pvalue = sp; +} + +bool pattern_array_label_match( + struct pattern_array *pa, + RRDLABELS *labels, + char eq, + size_t *searches) +{ + if (!pa || !labels) + return true; + + Pvoid_t *Pvalue; + Word_t Index = 0; + bool first_then_next = true; + while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first_then_next))) { + // for each label key in the patterns array + + struct pattern_array_item *pai = *Pvalue; + SIMPLE_PATTERN_RESULT match = SP_NOT_MATCHED ; + for (Word_t i = 1; i <= pai->size; i++) { + // for each pattern in the label key pattern list + + if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0)) || !*Pvalue) + continue; + + match = rrdlabels_match_simple_pattern_parsed(labels, (SIMPLE_PATTERN *)(*Pvalue), eq, searches); + + if(match != SP_NOT_MATCHED) + break; + } + + if (match != SP_MATCHED_POSITIVE) + return false; + } + return true; +} + +struct pattern_array *pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern) +{ + if (unlikely(!pattern || !key)) + return pa; + + if (!pa) + pa = pattern_array_allocate(); + + pattern_array_add_lblkey_with_sp(pa, key, pattern); + return pa; +} + +struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep) +{ + if (unlikely(!pattern)) + return pa; + + if (!pa) + pa = pattern_array_allocate(); + + char *label_key; + while (pattern && (label_key = simple_pattern_iterate(&pattern))) { + char key[RRDLABELS_MAX_NAME_LENGTH + 1], *key_sep; + + if (unlikely(!label_key || !(key_sep = strchr(label_key, sep)))) + return pa; + + *key_sep = '\0'; + strncpyz(key, label_key, RRDLABELS_MAX_NAME_LENGTH); + *key_sep = sep; + + pattern_array_add_lblkey_with_sp(pa, key, string_to_simple_pattern(label_key)); + } + return pa; +} + +struct pattern_array *pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep) +{ + if (unlikely(!key || !value)) + return pa; + + if (!pa) + pa = pattern_array_allocate(); + + char label_key[RRDLABELS_MAX_NAME_LENGTH + RRDLABELS_MAX_VALUE_LENGTH + 2]; + snprintfz(label_key, sizeof(label_key) - 1, "%s%c%s", key, sep, value); + pattern_array_add_lblkey_with_sp( + pa, key, simple_pattern_create(label_key, SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true)); + return pa; +} + +void pattern_array_free(struct pattern_array *pa) +{ + if (!pa) + return; + + Pvoid_t *Pvalue; + Word_t Index = 0; + while ((Pvalue = JudyLFirst(pa->JudyL, &Index, PJE0))) { + struct pattern_array_item *pai = *Pvalue; + + for (Word_t i = 1; i <= pai->size; i++) { + if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0))) + continue; + simple_pattern_free((SIMPLE_PATTERN *) (*Pvalue)); + } + JudyLFreeArray(&(pai->JudyL), PJE0); + + string_freez((STRING *)Index); + (void) JudyLDel(&(pa->JudyL), Index, PJE0); + freez(pai); + Index = 0; + } + freez(pa); +} // ---------------------------------------------------------------------------- // rrdlabels unit test @@ -1497,6 +1709,7 @@ static int rrdlabels_walkthrough_index_read(RRDLABELS *labels, int (*callback)(c ret = callback(string2str(lb->index.key), string2str(lb->index.value), ls, index, data); if (ret < 0) break; + index++; } lfe_done(labels); @@ -1513,6 +1726,70 @@ static int unittest_dump_labels(const char *name, const char *value, RRDLABEL_SR return 1; } +static int rrdlabels_unittest_pattern_check() +{ + fprintf(stderr, "\n%s() tests\n", __FUNCTION__); + int rc = 0; + + RRDLABELS *labels = NULL; + + labels = rrdlabels_create(); + + rrdlabels_add(labels, "_module", "disk_detection", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "_plugin", "super_plugin", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "key1", "value1", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "key2", "caterpillar", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "key3", "elephant", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "key4", "value4", RRDLABEL_SRC_CONFIG); + + bool match; + struct pattern_array *pa = pattern_array_add_key_value(NULL, "_module", "wrong_module", '='); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should not match: _module in ("wrong_module") + if (match) + rc++; + + pattern_array_add_key_value(pa, "_module", "disk_detection", '='); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should match: _module in ("wrong_module","disk_detection") + if (!match) + rc++; + + pattern_array_add_key_value(pa, "key1", "wrong_key1_value", '='); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should not match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value") + if (match) + rc++; + + pattern_array_add_key_value(pa, "key1", "value1", '='); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1") + if (!match) + rc++; + + SIMPLE_PATTERN *sp = simple_pattern_create("key2=cat*,!d*", SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true); + pattern_array_add_lblkey_with_sp(pa, "key2", sp); + + sp = simple_pattern_create("key3=*phant", SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true); + pattern_array_add_lblkey_with_sp(pa, "key3", sp); + + match = pattern_array_label_match(pa, labels, '=', NULL); + // 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") + if (!match) + rc++; + + rrdlabels_add(labels, "key3", "now_fail", RRDLABEL_SRC_CONFIG); + match = pattern_array_label_match(pa, labels, '=', NULL); + // 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") + if (match) + rc++; + + pattern_array_free(pa); + rrdlabels_destroy(labels); + + return rc; +} + static int rrdlabels_unittest_migrate_check() { fprintf(stderr, "\n%s() tests\n", __FUNCTION__); @@ -1594,6 +1871,69 @@ static int rrdlabels_unittest_migrate_check() return rc; } +struct pattern_array *trim_and_add_key_to_values(struct pattern_array *pa, const char *key, STRING *input); +static int rrdlabels_unittest_check_pattern_list(RRDLABELS *labels, const char *pattern, bool expected) { + fprintf(stderr, "rrdlabels_match_simple_pattern(labels, \"%s\") ... ", pattern); + + STRING *str = string_strdupz(pattern); + struct pattern_array *pa = trim_and_add_key_to_values(NULL, NULL, str); + + bool ret = pattern_array_label_match(pa, labels, '=', NULL); + + fprintf(stderr, "%s, got %s expected %s\n", (ret == expected)?"OK":"FAILED", ret?"true":"false", expected?"true":"false"); + + string_freez(str); + pattern_array_free(pa); + + return (ret == expected)?0:1; +} + +static int rrdlabels_unittest_host_chart_labels() { + fprintf(stderr, "\n%s() tests\n", __FUNCTION__); + + int errors = 0; + + RRDLABELS *labels = rrdlabels_create(); + rrdlabels_add(labels, "_hostname", "hostname1", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "_os", "linux", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "_distro", "ubuntu", RRDLABEL_SRC_CONFIG); + + // match a single key + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*", false); + + // conflicting keys (some positive, some negative) + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* _os=!*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!* _os=*", false); + + // the user uses a key that is not there + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=!*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=* _hostname=* _os=*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=!* _hostname=* _os=*", false); + + // positive and negative matches on the same key + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*bad* *name*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* !*invalid* !*bad*", true); + + // positive and negative matches on the same key with catch all + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*bad* *", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* !*invalid* !*bad*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*name* *", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* !*invalid* !*name*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* !*", true); + + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*name* _os=l*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_os=l* hostname=!*name*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* _hostname=*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* _os=l*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_os=l* _hostname=*name*", true); + + rrdlabels_destroy(labels); + + return errors; +} + static int rrdlabels_unittest_check_simple_pattern(RRDLABELS *labels, const char *pattern, bool expected) { fprintf(stderr, "rrdlabels_match_simple_pattern(labels, \"%s\") ... ", pattern); @@ -1686,9 +2026,12 @@ int rrdlabels_unittest(void) { errors += rrdlabels_unittest_sanitization(); errors += rrdlabels_unittest_add_pairs(); errors += rrdlabels_unittest_simple_pattern(); + errors += rrdlabels_unittest_host_chart_labels(); errors += rrdlabels_unittest_double_check(); errors += rrdlabels_unittest_migrate_check(); + errors += rrdlabels_unittest_pattern_check(); fprintf(stderr, "%d errors found\n", errors); return errors; } + |