Code

store: Added field type VALUE for attribute values.
[sysdb.git] / src / core / store.c
index 52328f8cf4ad6aa89b8039e0e7b2372babaa2a40..ab37e689641ec52b42f844bb5f1f1d3cd6236d63 100644 (file)
@@ -43,6 +43,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <math.h>
 #include <pthread.h>
 
 /*
@@ -255,7 +256,7 @@ static sdb_type_t sdb_attribute_type = {
  */
 
 static sdb_host_t *
-lookup_host(const char *name, _Bool canonicalize)
+lookup_host(const char *name, bool canonicalize)
 {
        sdb_host_t *host;
        char *cname;
@@ -426,8 +427,9 @@ store_attr(sdb_store_obj_t *parent, sdb_avltree_t *attributes,
 static sdb_avltree_t *
 get_host_children(sdb_host_t *host, int type)
 {
-       assert((type == SDB_SERVICE) || (type == SDB_METRIC)
-                       || (type == SDB_ATTRIBUTE));
+       if ((type != SDB_SERVICE) && (type != SDB_METRIC)
+                       && (type != SDB_ATTRIBUTE))
+               return NULL;
 
        if (! host)
                return NULL;
@@ -440,109 +442,6 @@ get_host_children(sdb_host_t *host, int type)
                return host->services;
 } /* get_host_children */
 
-/*
- * store_common_tojson serializes common object attributes to JSON.
- *
- * The function never returns an error. Rather, an error message will be part
- * of the serialized data.
- */
-static void
-store_common_tojson(sdb_store_obj_t *obj, sdb_strbuf_t *buf)
-{
-       char time_str[64];
-       char interval_str[64];
-       size_t i;
-
-       /* TODO: make time and interval formats configurable */
-       if (! sdb_strftime(time_str, sizeof(time_str),
-                               "%F %T %z", obj->last_update))
-               snprintf(time_str, sizeof(time_str), "<error>");
-       time_str[sizeof(time_str) - 1] = '\0';
-
-       if (! sdb_strfinterval(interval_str, sizeof(interval_str),
-                               obj->interval))
-               snprintf(interval_str, sizeof(interval_str), "<error>");
-       interval_str[sizeof(interval_str) - 1] = '\0';
-
-       sdb_strbuf_append(buf, "\"last_update\": \"%s\", "
-                       "\"update_interval\": \"%s\", \"backends\": [",
-                       time_str, interval_str);
-
-       for (i = 0; i < obj->backends_num; ++i) {
-               sdb_strbuf_append(buf, "\"%s\"", obj->backends[i]);
-               if (i < obj->backends_num - 1)
-                       sdb_strbuf_append(buf, ",");
-       }
-       sdb_strbuf_append(buf, "]");
-} /* store_common_tojson */
-
-/*
- * store_obj_tojson serializes attribute / metric / service objects to JSON.
- *
- * The function never returns an error. Rather, an error message will be part
- * of the serialized data.
- */
-static void
-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;
-
-       assert((type == SDB_ATTRIBUTE)
-                       || (type == SDB_METRIC)
-                       || (type == SDB_SERVICE));
-
-       sdb_strbuf_append(buf, "[");
-       iter = sdb_avltree_get_iter(tree);
-       if (! iter) {
-               char errbuf[1024];
-               sdb_log(SDB_LOG_ERR, "store: Failed to retrieve %ss: %s\n",
-                               SDB_STORE_TYPE_TO_NAME(type),
-                               sdb_strerror(errno, errbuf, sizeof(errbuf)));
-               sdb_strbuf_append(buf, "{\"error\": \"failed to retrieve %ss: %s\"}",
-                               SDB_STORE_TYPE_TO_NAME(type), errbuf);
-       }
-
-       /* has_next returns false if the iterator is NULL */
-       while (sdb_avltree_iter_has_next(iter)) {
-               sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
-               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];
-                       sdb_data_format(&ATTR(sobj)->value, tmp, sizeof(tmp),
-                                       SDB_DOUBLE_QUOTED);
-                       sdb_strbuf_append(buf, "\"value\": %s, ", tmp);
-               }
-               store_common_tojson(sobj, buf);
-
-               if ((sobj->type == SDB_SERVICE)
-                               && (! (flags & SDB_SKIP_ATTRIBUTES))) {
-                       sdb_strbuf_append(buf, ", \"attributes\": ");
-                       store_obj_tojson(SVC(sobj)->attributes, SDB_ATTRIBUTE,
-                                       buf, filter, flags);
-               }
-               else if ((sobj->type == SDB_METRIC)
-                               && (! (flags & SDB_SKIP_ATTRIBUTES))) {
-                       sdb_strbuf_append(buf, ", \"attributes\": ");
-                       store_obj_tojson(METRIC(sobj)->attributes, SDB_ATTRIBUTE,
-                                       buf, filter, flags);
-               }
-               sdb_strbuf_append(buf, "}");
-
-               if (sdb_avltree_iter_has_next(iter))
-                       sdb_strbuf_append(buf, ",");
-       }
-
-       sdb_avltree_iter_destroy(iter);
-       sdb_strbuf_append(buf, "]");
-} /* store_obj_tojson */
-
 /*
  * ts_tojson serializes a time-series to JSON.
  *
@@ -582,8 +481,13 @@ ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
                                snprintf(time_str, sizeof(time_str), "<error>");
                        time_str[sizeof(time_str) - 1] = '\0';
 
-                       sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
-                                       "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
+                       /* Some GNU libc versions may print '-nan' which we dont' want */
+                       if (isnan(ts->data[i][j].value))
+                               sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
+                                               "\"value\": \"nan\"}", time_str);
+                       else
+                               sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
+                                               "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
 
                        if (j < ts->data_len - 1)
                                sdb_strbuf_append(buf, ",");
@@ -632,11 +536,14 @@ sdb_store_host(const char *name, sdb_time_t last_update)
                status = store_obj(NULL, hosts, SDB_HOST, cname, last_update, NULL);
        pthread_rwlock_unlock(&host_lock);
 
+       if (sdb_plugin_store_host(name, last_update))
+               status = -1;
+
        free(cname);
        return status;
 } /* sdb_store_host */
 
-_Bool
+bool
 sdb_store_has_host(const char *name)
 {
        sdb_host_t *host;
@@ -690,6 +597,9 @@ sdb_store_attribute(const char *hostname,
 
        sdb_object_deref(SDB_OBJ(host));
        pthread_rwlock_unlock(&host_lock);
+
+       if (sdb_plugin_store_attribute(hostname, key, value, last_update))
+               status = -1;
        return status;
 } /* sdb_store_attribute */
 
@@ -720,6 +630,9 @@ sdb_store_service(const char *hostname, const char *name,
 
        sdb_object_deref(SDB_OBJ(host));
        pthread_rwlock_unlock(&host_lock);
+
+       if (sdb_plugin_store_service(hostname, name, last_update))
+               status = -1;
        return status;
 } /* sdb_store_service */
 
@@ -760,6 +673,10 @@ sdb_store_service_attr(const char *hostname, const char *service,
 
        sdb_object_deref(SDB_OBJ(svc));
        pthread_rwlock_unlock(&host_lock);
+
+       if (sdb_plugin_store_service_attribute(hostname, service,
+                               key, value, last_update))
+               status = -1;
        return status;
 } /* sdb_store_service_attr */
 
@@ -777,8 +694,13 @@ sdb_store_metric(const char *hostname, const char *name,
 
        if ((! hostname) || (! name))
                return -1;
-       if (store && ((! store->type) || (! store->id)))
-               return -1;
+
+       if (store) {
+               if ((store->type != NULL) != (store->id != NULL))
+                       return -1;
+               else if (! store->type)
+                       store = NULL;
+       }
 
        pthread_rwlock_wrlock(&host_lock);
        host = lookup_host(hostname, /* canonicalize = */ 1);
@@ -822,6 +744,9 @@ sdb_store_metric(const char *hostname, const char *name,
                status = -1;
        }
        pthread_rwlock_unlock(&host_lock);
+
+       if (sdb_plugin_store_metric(hostname, name, store, last_update))
+               status = -1;
        return status;
 } /* sdb_store_metric */
 
@@ -862,9 +787,27 @@ sdb_store_metric_attr(const char *hostname, const char *metric,
 
        sdb_object_deref(SDB_OBJ(m));
        pthread_rwlock_unlock(&host_lock);
+
+       if (sdb_plugin_store_metric_attribute(hostname, metric,
+                               key, value, last_update))
+               status = -1;
        return status;
 } /* sdb_store_metric_attr */
 
+sdb_store_obj_t *
+sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
+{
+       sdb_avltree_t *children;
+
+       if ((! host) || (host->type != SDB_HOST) || (! name))
+               return NULL;
+
+       children = get_host_children(HOST(host), type);
+       if (! children)
+               return NULL;
+       return STORE_OBJ(sdb_avltree_lookup(children, name));
+} /* sdb_store_get_child */
+
 int
 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
                sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
@@ -955,13 +898,18 @@ sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
                        tmp.data.datetime = obj->interval;
                        break;
                case SDB_FIELD_BACKEND:
-               {
+                       if (! res)
+                               return 0;
                        tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
                        tmp.data.array.length = obj->backends_num;
                        tmp.data.array.values = obj->backends;
                        return sdb_data_copy(res, &tmp);
-                       break;
-               }
+               case SDB_FIELD_VALUE:
+                       if (obj->type != SDB_ATTRIBUTE)
+                               return -1;
+                       if (! res)
+                               return 0;
+                       return sdb_data_copy(res, &ATTR(obj)->value);
                default:
                        return -1;
        }
@@ -1007,123 +955,11 @@ sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
        return 0;
 } /* 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)
-{
-       sdb_host_t *host = HOST(h);
-
-       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);
-
-       if (! (flags & SDB_SKIP_ATTRIBUTES)) {
-               sdb_strbuf_append(buf, ", \"attributes\": ");
-               store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, filter, flags);
-       }
-
-       if (! (flags & SDB_SKIP_METRICS)) {
-               sdb_strbuf_append(buf, ", \"metrics\": ");
-               store_obj_tojson(host->metrics, SDB_METRIC, buf, filter, flags);
-       }
-
-       if (! (flags & SDB_SKIP_SERVICES)) {
-               sdb_strbuf_append(buf, ", \"services\": ");
-               store_obj_tojson(host->services, SDB_SERVICE, buf, filter, flags);
-       }
-
-       sdb_strbuf_append(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)
-{
-       sdb_avltree_iter_t *host_iter;
-       size_t len;
-
-       if (! buf)
-               return -1;
-
-       pthread_rwlock_rdlock(&host_lock);
-
-       host_iter = sdb_avltree_get_iter(hosts);
-       if (! host_iter) {
-               pthread_rwlock_unlock(&host_lock);
-               return -1;
-       }
-
-       sdb_strbuf_append(buf, "[");
-
-       len = sdb_strbuf_len(buf);
-       while (sdb_avltree_iter_has_next(host_iter)) {
-               sdb_store_obj_t *host;
-
-               host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
-               assert(host);
-
-               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);
-
-               if (sdb_store_host_tojson(host, buf, filter, flags))
-                       return -1;
-       }
-
-       sdb_strbuf_append(buf, "]");
-
-       sdb_avltree_iter_destroy(host_iter);
-       pthread_rwlock_unlock(&host_lock);
-       return 0;
-} /* sdb_store_tojson */
-
 int
 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
                sdb_store_lookup_cb cb, void *user_data)
 {
-       sdb_avltree_iter_t *host_iter;
+       sdb_avltree_iter_t *host_iter = NULL;
        int status = 0;
 
        if (! cb)
@@ -1136,9 +972,11 @@ sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
 
        pthread_rwlock_rdlock(&host_lock);
 
-       host_iter = sdb_avltree_get_iter(hosts);
-       if (! host_iter)
-               status = -1;
+       if (hosts) {
+               host_iter = sdb_avltree_get_iter(hosts);
+               if (! host_iter)
+                       status = -1;
+       }
 
        /* has_next returns false if the iterator is NULL */
        while (sdb_avltree_iter_has_next(host_iter)) {