From ab0cdca7fb4f505307f5f0fc4cdcb3ef804e32d8 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Tue, 29 Jul 2014 22:18:14 +0200 Subject: [PATCH] store: Let JSON serializers support (object) filters. Only objects covered by the specified filter will be included in the JSON output. --- src/core/store.c | 29 +++++++++----- src/frontend/query.c | 9 +++-- src/include/core/store.h | 13 +++++-- t/unit/core/store_test.c | 83 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 109 insertions(+), 25 deletions(-) diff --git a/src/core/store.c b/src/core/store.c index 800059c..bb6cb96 100644 --- a/src/core/store.c +++ b/src/core/store.c @@ -415,7 +415,8 @@ store_common_tojson(sdb_store_obj_t *obj, sdb_strbuf_t *buf) * of the serialized data. */ static void -store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf, int flags) +store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf, + sdb_store_matcher_t *filter, int flags) { sdb_avltree_iter_t *iter; @@ -438,6 +439,9 @@ store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf, int flags) assert(sobj); assert(sobj->type == type); + if (filter && (! sdb_store_matcher_matches(filter, sobj, NULL))) + continue; + sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(sobj)->name); if (sobj->type == SDB_ATTRIBUTE) { char tmp[sdb_data_strlen(&ATTR(sobj)->value) + 1]; @@ -451,7 +455,7 @@ store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf, int flags) && (! (flags & SDB_SKIP_ATTRIBUTES))) { sdb_strbuf_append(buf, ", \"attributes\": "); store_obj_tojson(SVC(sobj)->attributes, SDB_ATTRIBUTE, - buf, flags); + buf, filter, flags); } sdb_strbuf_append(buf, "}"); @@ -617,26 +621,28 @@ sdb_store_service_attr(const char *hostname, const char *service, } /* sdb_store_service_attr */ int -sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf, int flags) +sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf, + sdb_store_matcher_t *filter, int flags) { - sdb_host_t *host; + sdb_host_t *host = HOST(h); if ((! h) || (h->type != SDB_HOST) || (! buf)) return -1; - host = HOST(h); + if (filter && (! sdb_store_matcher_matches(filter, h, NULL))) + return 0; sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name); store_common_tojson(h, buf); if (! (flags & SDB_SKIP_ATTRIBUTES)) { sdb_strbuf_append(buf, ", \"attributes\": "); - store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, flags); + store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, filter, flags); } if (! (flags & SDB_SKIP_SERVICES)) { sdb_strbuf_append(buf, ", \"services\": "); - store_obj_tojson(host->services, SDB_SERVICE, buf, flags); + store_obj_tojson(host->services, SDB_SERVICE, buf, filter, flags); } sdb_strbuf_append(buf, "}"); @@ -644,7 +650,7 @@ sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf, int flags) } /* sdb_store_host_tojson */ int -sdb_store_tojson(sdb_strbuf_t *buf, int flags) +sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags) { sdb_avltree_iter_t *host_iter; @@ -663,14 +669,17 @@ sdb_store_tojson(sdb_strbuf_t *buf, int flags) while (sdb_avltree_iter_has_next(host_iter)) { sdb_store_obj_t *host; + size_t len = sdb_strbuf_len(buf); host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter)); assert(host); - if (sdb_store_host_tojson(host, buf, flags)) + if (sdb_store_host_tojson(host, buf, filter, flags)) return -1; - if (sdb_avltree_iter_has_next(host_iter)) + /* sdb_store_host_tojson may leave the buffer unmodified */ + if ((sdb_avltree_iter_has_next(host_iter)) + && (sdb_strbuf_len(buf) != len)) sdb_strbuf_append(buf, ","); } diff --git a/src/frontend/query.c b/src/frontend/query.c index 675b396..53ba81a 100644 --- a/src/frontend/query.c +++ b/src/frontend/query.c @@ -44,7 +44,8 @@ lookup_tojson(sdb_store_obj_t *obj, void *user_data) sdb_strbuf_t *buf = user_data; if (sdb_strbuf_len(buf) > 1) sdb_strbuf_append(buf, ","); - return sdb_store_host_tojson(obj, buf, /* flags = */ 0); + return sdb_store_host_tojson(obj, buf, + /* filter = */ NULL, /* flags = */ 0); } /* lookup_tojson */ /* @@ -100,7 +101,8 @@ sdb_fe_fetch(sdb_conn_t *conn, const char *name) return -1; } - if (sdb_store_host_tojson(host, buf, /* flags = */ 0)) { + if (sdb_store_host_tojson(host, buf, + /* filter = */ NULL, /* flags = */ 0)) { sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize " "host '%s' to JSON", name); sdb_strbuf_sprintf(conn->errbuf, "Out of memory"); @@ -133,7 +135,8 @@ sdb_fe_list(sdb_conn_t *conn) return -1; } - if (sdb_store_tojson(buf, /* flags = */ SDB_SKIP_ALL)) { + if (sdb_store_tojson(buf, + /* filter = */ NULL, /* flags = */ SDB_SKIP_ALL)) { sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize " "store to JSON"); sdb_strbuf_sprintf(conn->errbuf, "Out of memory"); diff --git a/src/include/core/store.h b/src/include/core/store.h index a9788f4..494f8d6 100644 --- a/src/include/core/store.h +++ b/src/include/core/store.h @@ -405,26 +405,31 @@ enum { /* * sdb_store_tojson: * Serialize the entire store to JSON and append the result to the specified - * buffer. + * buffer. If specified, only objects matching the filter will be included in + * the result (see sdb_store_host_tojson for details). * * Returns: * - 0 on success * - a negative value on error */ int -sdb_store_tojson(sdb_strbuf_t *buf, int flags); +sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags); /* * sdb_store_host_tojson: * Serialize a host object to JSON and append the result to the specified - * buffer. + * buffer. If specified, only objects matching the filter will be included in + * the result. The filter is applied to each object individually and, thus, + * should not be of any object-type specific kind. If the filter rejects the + * host object, the function returns success but leaves the buffer unmodified. * * Returns: * - 0 on success * - a negative value on error */ int -sdb_store_host_tojson(sdb_store_obj_t *host, sdb_strbuf_t *buf, int flags); +sdb_store_host_tojson(sdb_store_obj_t *host, sdb_strbuf_t *buf, + sdb_store_matcher_t *filter, int flags); /* * sdb_store_iter_cb: diff --git a/t/unit/core/store_test.c b/t/unit/core/store_test.c index 0d0c56b..bc2066a 100644 --- a/t/unit/core/store_test.c +++ b/t/unit/core/store_test.c @@ -38,7 +38,7 @@ populate(void) sdb_data_t datum; sdb_store_host("h1", 1); - sdb_store_host("h2", 1); + sdb_store_host("h2", 3); datum.type = SDB_TYPE_STRING; datum.data.string = "v1"; @@ -54,13 +54,15 @@ populate(void) sdb_store_attribute("h1", "k3", &datum, 2); sdb_store_service("h2", "s1", 1); - sdb_store_service("h2", "s2", 1); + sdb_store_service("h2", "s2", 2); datum.type = SDB_TYPE_INTEGER; datum.data.integer = 123; sdb_store_service_attr("h2", "s2", "k1", &datum, 2); + datum.data.integer = 4711; + sdb_store_service_attr("h2", "s2", "k2", &datum, 1); - /* don't overwrite */ + /* don't overwrite k1 */ datum.data.integer = 666; sdb_store_service_attr("h2", "s2", "k1", &datum, 2); } /* populate */ @@ -334,10 +336,16 @@ START_TEST(test_store_tojson) size_t i; struct { + struct { + sdb_store_matcher_t *(*m)(sdb_store_cond_t *); + int field; + sdb_data_t value; + } filter; int flags; const char *expected; } golden_data[] = { - { 0, "{\"hosts\":[" + { { NULL, 0, SDB_DATA_INIT }, 0, + "{\"hosts\":[" "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": [], " "\"attributes\": [" @@ -365,12 +373,15 @@ START_TEST(test_store_tojson) "\"update_interval\": \"0s\", \"backends\": [], " "\"attributes\": [" "{\"name\": \"k1\", \"value\": 123, " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}," + "{\"name\": \"k2\", \"value\": 4711, " "\"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": []}" "]}" "]}" "]}" }, - { SDB_SKIP_SERVICES, + { { NULL, 0, SDB_DATA_INIT }, SDB_SKIP_SERVICES, "{\"hosts\":[" "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": [], " @@ -389,7 +400,7 @@ START_TEST(test_store_tojson) "\"update_interval\": \"0s\", \"backends\": [], " "\"attributes\": []}" "]}" }, - { SDB_SKIP_ATTRIBUTES, + { { NULL, 0, SDB_DATA_INIT }, SDB_SKIP_ATTRIBUTES, "{\"hosts\":[" "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": [], " @@ -405,13 +416,50 @@ START_TEST(test_store_tojson) "\"update_interval\": \"0s\", \"backends\": []}" "]}" "]}" }, - { SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES, + { { NULL, 0, SDB_DATA_INIT }, SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES, "{\"hosts\":[" "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": []}," "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": []}" "]}" }, + { { sdb_store_gt_matcher, SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 1 } } }, 0, + "{\"hosts\":[" + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"attributes\": [], " + "\"services\": [" + "{\"name\": \"s2\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"attributes\": [" + "{\"name\": \"k1\", \"value\": 123, " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}," + "]}" + "]}" + "]}" }, + { { sdb_store_le_matcher, SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 1 } } }, 0, + "{\"hosts\":[" + "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"attributes\": [" + "{\"name\": \"k1\", \"value\": \"v1\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}," + "], " + "\"services\": []}," + "]}" }, + { { sdb_store_ge_matcher, SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 3 } } }, 0, + "{\"hosts\":[" + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"attributes\": [], " + "\"services\": []}" + "]}" }, }; buf = sdb_strbuf_create(0); @@ -419,16 +467,35 @@ START_TEST(test_store_tojson) populate(); for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) { + sdb_store_matcher_t *filter = NULL; int status; sdb_strbuf_clear(buf); - status = sdb_store_tojson(buf, golden_data[i].flags); + if (golden_data[i].filter.m) { + sdb_store_expr_t *expr; + sdb_store_cond_t *c = NULL; + + expr = sdb_store_expr_constvalue(&golden_data[i].filter.value); + fail_unless(expr != NULL, + "INTERNAL ERROR: sdb_store_expr_constvalue() = NULL"); + c = sdb_store_obj_cond(golden_data[i].filter.field, expr); + sdb_object_deref(SDB_OBJ(expr)); + fail_unless(c != NULL, + "INTERNAL ERROR: sdb_store_obj_cond() = NULL"); + filter = golden_data[i].filter.m(c); + sdb_object_deref(SDB_OBJ(c)); + fail_unless(filter != NULL, + "INTERNAL ERROR: sdb_store_*_matcher() = NULL"); + } + + status = sdb_store_tojson(buf, filter, golden_data[i].flags); fail_unless(status == 0, "sdb_store_tojson(%x) = %d; expected: 0", golden_data[i].flags, status); verify_json_output(buf, golden_data[i].expected, golden_data[i].flags); + sdb_object_deref(SDB_OBJ(filter)); } sdb_strbuf_destroy(buf); } -- 2.30.2