Code

store: Let JSON serializers support (object) filters.
authorSebastian Harl <sh@tokkee.org>
Tue, 29 Jul 2014 20:18:14 +0000 (22:18 +0200)
committerSebastian Harl <sh@tokkee.org>
Tue, 29 Jul 2014 20:18:14 +0000 (22:18 +0200)
Only objects covered by the specified filter will be included in the JSON
output.

src/core/store.c
src/frontend/query.c
src/include/core/store.h
t/unit/core/store_test.c

index 800059cf62112f0b3615258b731d728595805553..bb6cb96a45bb4b91d934705324e06b179b4742b4 100644 (file)
@@ -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, ",");
        }
 
index 675b3960a48c7680a75a1a0ef1c34d808635144d..53ba81aac38b03dd24bc07922b146a1dc6d23c4c 100644 (file)
@@ -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");
index a9788f44bb5b59ac7b166b46667bdb0acc9a70f1..494f8d648c241834100c56a29560b6d58fd5ca6f 100644 (file)
@@ -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:
index 0d0c56bce6df859074e64590cf910eea3bfa67f5..bc2066a208b98afabc1921efb6c8b8dd683370f5 100644 (file)
@@ -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);
 }