Code

store: Apply filters when evaluating attribute values or expressions.
[sysdb.git] / src / core / store.c
index 3bc111496ab9a64f5c79a517ccec41e8298f24f2..81aedfb7600a9243accbe4e49cceffcf87f8c54b 100644 (file)
@@ -555,7 +555,7 @@ ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
                snprintf(end_str, sizeof(end_str), "<error>");
        end_str[sizeof(end_str) - 1] = '\0';
 
-       sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", {",
+       sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
                        start_str, end_str);
 
        for (i = 0; i < ts->data_names_len; ++i) {
@@ -894,21 +894,29 @@ sdb_store_fetch_timeseries(const char *hostname, const char *metric,
 int
 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
 {
-       if ((! obj) || (! res))
+       sdb_data_t tmp;
+
+       if (! obj)
                return -1;
 
        switch (field) {
+               case SDB_FIELD_NAME:
+                       tmp.type = SDB_TYPE_STRING;
+                       tmp.data.string = strdup(SDB_OBJ(obj)->name);
+                       if (! tmp.data.string)
+                               return -1;
+                       break;
                case SDB_FIELD_LAST_UPDATE:
-                       res->type = SDB_TYPE_DATETIME;
-                       res->data.datetime = obj->last_update;
+                       tmp.type = SDB_TYPE_DATETIME;
+                       tmp.data.datetime = obj->last_update;
                        break;
                case SDB_FIELD_AGE:
-                       res->type = SDB_TYPE_DATETIME;
-                       res->data.datetime = sdb_gettime() - obj->last_update;
+                       tmp.type = SDB_TYPE_DATETIME;
+                       tmp.data.datetime = sdb_gettime() - obj->last_update;
                        break;
                case SDB_FIELD_INTERVAL:
-                       res->type = SDB_TYPE_DATETIME;
-                       res->data.datetime = obj->interval;
+                       tmp.type = SDB_TYPE_DATETIME;
+                       tmp.data.datetime = obj->interval;
                        break;
                case SDB_FIELD_BACKEND:
                        /* TODO: add support for storing array values in a data object
@@ -916,9 +924,56 @@ sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
                default:
                        return -1;
        }
+       if (res)
+               *res = tmp;
+       else
+               sdb_data_free_datum(&tmp);
        return 0;
 } /* sdb_store_get_field */
 
+int
+sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
+               sdb_store_matcher_t *filter)
+{
+       sdb_avltree_t *tree = NULL;
+       sdb_avltree_iter_t *iter = NULL;
+
+       if ((! obj) || (! name))
+               return -1;
+
+       if (obj->type == SDB_HOST)
+               tree = HOST(obj)->attributes;
+       else if (obj->type == SDB_SERVICE)
+               tree = SVC(obj)->attributes;
+       else if (obj->type == SDB_METRIC)
+               tree = METRIC(obj)->attributes;
+
+       if (! tree)
+               return -1;
+
+       iter = sdb_avltree_get_iter(tree);
+       while (sdb_avltree_iter_has_next(iter)) {
+               sdb_object_t *attr = sdb_avltree_iter_get_next(iter);
+
+               if (strcasecmp(SDB_OBJ(attr)->name, name))
+                       continue;
+
+               if (filter&& (! sdb_store_matcher_matches(filter,
+                                               STORE_OBJ(attr), NULL)))
+                       break; /* found it but it's filtered */
+
+               assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
+               if (res)
+                       sdb_data_copy(res, &ATTR(attr)->value);
+               sdb_avltree_iter_destroy(iter);
+               return 0;
+       }
+       sdb_avltree_iter_destroy(iter);
+
+       /* not found */
+       return -1;
+} /* sdb_store_get_attr */
+
 int
 sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
                sdb_store_matcher_t *filter, int flags)
@@ -928,6 +983,10 @@ sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
        if ((! h) || (h->type != SDB_HOST) || (! buf))
                return -1;
 
+       /* This function ignores SKIP_EMPTY flags given that the current
+        * implementation sucks and it's nut currently used when calling this
+        * function directly. */
+
        sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name);
        store_common_tojson(h, buf);
 
@@ -950,6 +1009,26 @@ sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
        return 0;
 } /* sdb_store_host_tojson */
 
+static _Bool
+has_children(sdb_avltree_t *tree, sdb_store_matcher_t *filter)
+{
+       sdb_avltree_iter_t *iter;
+
+       if (! filter)
+               return sdb_avltree_size(tree) > 0;
+
+       iter = sdb_avltree_get_iter(tree);
+       while (sdb_avltree_iter_has_next(iter)) {
+               sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
+               if (sdb_store_matcher_matches(filter, sobj, NULL)) {
+                       sdb_avltree_iter_destroy(iter);
+                       return 1;
+               }
+       }
+       sdb_avltree_iter_destroy(iter);
+       return 0;
+} /* has_children */
+
 int
 sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
 {
@@ -967,7 +1046,7 @@ sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
                return -1;
        }
 
-       sdb_strbuf_append(buf, "{\"hosts\":[");
+       sdb_strbuf_append(buf, "[");
 
        len = sdb_strbuf_len(buf);
        while (sdb_avltree_iter_has_next(host_iter)) {
@@ -979,6 +1058,19 @@ sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
                if (filter && (! sdb_store_matcher_matches(filter, host, NULL)))
                        continue;
 
+               /*
+                * XXX: This approach sucks but it's the best we can do at the moment.
+                * In the future, all store lookups should be split into multiple
+                * steps instead: first, retrieve all relevant objects and apply all
+                * pre-processing operations and then format it for the wire.
+                */
+               if ((flags & SDB_SKIP_EMPTY_SERVICES)
+                               && (! has_children(HOST(host)->services, filter)))
+                       continue;
+               if ((flags & SDB_SKIP_EMPTY_METRICS)
+                               && (! has_children(HOST(host)->metrics, filter)))
+                       continue;
+
                if (sdb_strbuf_len(buf) > len)
                        sdb_strbuf_append(buf, ",");
                len = sdb_strbuf_len(buf);
@@ -987,7 +1079,7 @@ sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
                        return -1;
        }
 
-       sdb_strbuf_append(buf, "]}");
+       sdb_strbuf_append(buf, "]");
 
        sdb_avltree_iter_destroy(host_iter);
        pthread_rwlock_unlock(&host_lock);