Code

Replaced sdb_store_<type> with sdb_plugin_store_<type>.
[sysdb.git] / src / core / store.c
1 /*
2  * SysDB - src/core/store.c
3  * Copyright (C) 2012-2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "sysdb.h"
33 #include "core/store-private.h"
34 #include "core/plugin.h"
35 #include "utils/avltree.h"
36 #include "utils/error.h"
38 #include <assert.h>
40 #include <errno.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
46 #include <math.h>
47 #include <pthread.h>
49 /*
50  * private variables
51  */
53 typedef struct {
54         sdb_object_t super;
56         /* hosts are the top-level entries and
57          * reference everything else */
58         sdb_avltree_t *hosts;
59         pthread_rwlock_t host_lock;
60 } sdb_store_t;
61 #define ST(obj) ((sdb_store_t *)(obj))
63 sdb_store_t *global_store = NULL;
65 /*
66  * private types
67  */
69 static sdb_type_t host_type;
70 static sdb_type_t service_type;
71 static sdb_type_t metric_type;
72 static sdb_type_t attribute_type;
74 static int
75 store_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
76 {
77         int err;
78         if (! (ST(obj)->hosts = sdb_avltree_create()))
79                 return -1;
80         if ((err = pthread_rwlock_init(&ST(obj)->host_lock, /* attr = */ NULL))) {
81                 char errbuf[128];
82                 sdb_log(SDB_LOG_ERR, "store: Failed to initialize lock: %s",
83                                 sdb_strerror(err, errbuf, sizeof(errbuf)));
84                 return -1;
85         }
86         return 0;
87 } /* store_init */
89 static void
90 store_destroy(sdb_object_t *obj)
91 {
92         int err;
93         if ((err = pthread_rwlock_destroy(&ST(obj)->host_lock))) {
94                 char errbuf[128];
95                 sdb_log(SDB_LOG_ERR, "store: Failed to destroy lock: %s",
96                                 sdb_strerror(err, errbuf, sizeof(errbuf)));
97                 return;
98         }
99         sdb_avltree_destroy(ST(obj)->hosts);
100         ST(obj)->hosts = NULL;
101 } /* store_destroy */
103 static int
104 store_obj_init(sdb_object_t *obj, va_list ap)
106         sdb_store_obj_t *sobj = STORE_OBJ(obj);
108         sobj->type = va_arg(ap, int);
110         sobj->last_update = va_arg(ap, sdb_time_t);
111         sobj->interval = 0;
112         sobj->backends = NULL;
113         sobj->backends_num = 0;
114         sobj->parent = NULL;
115         return 0;
116 } /* store_obj_init */
118 static void
119 store_obj_destroy(sdb_object_t *obj)
121         sdb_store_obj_t *sobj = STORE_OBJ(obj);
122         size_t i;
124         for (i = 0; i < sobj->backends_num; ++i)
125                 free(sobj->backends[i]);
126         free(sobj->backends);
127         sobj->backends = NULL;
128         sobj->backends_num = 0;
130         // We don't currently keep an extra reference for parent objects to
131         // avoid circular self-references which are not handled correctly by
132         // the ref-count base management layer.
133         //sdb_object_deref(SDB_OBJ(sobj->parent));
134 } /* store_obj_destroy */
136 static int
137 host_init(sdb_object_t *obj, va_list ap)
139         sdb_host_t *sobj = HOST(obj);
140         int ret;
142         /* this will consume the first argument (type) of ap */
143         ret = store_obj_init(obj, ap);
144         if (ret)
145                 return ret;
147         sobj->services = sdb_avltree_create();
148         if (! sobj->services)
149                 return -1;
150         sobj->metrics = sdb_avltree_create();
151         if (! sobj->metrics)
152                 return -1;
153         sobj->attributes = sdb_avltree_create();
154         if (! sobj->attributes)
155                 return -1;
156         return 0;
157 } /* host_init */
159 static void
160 host_destroy(sdb_object_t *obj)
162         sdb_host_t *sobj = HOST(obj);
163         assert(obj);
165         store_obj_destroy(obj);
167         if (sobj->services)
168                 sdb_avltree_destroy(sobj->services);
169         if (sobj->metrics)
170                 sdb_avltree_destroy(sobj->metrics);
171         if (sobj->attributes)
172                 sdb_avltree_destroy(sobj->attributes);
173 } /* host_destroy */
175 static int
176 service_init(sdb_object_t *obj, va_list ap)
178         sdb_service_t *sobj = SVC(obj);
179         int ret;
181         /* this will consume the first argument (type) of ap */
182         ret = store_obj_init(obj, ap);
183         if (ret)
184                 return ret;
186         sobj->attributes = sdb_avltree_create();
187         if (! sobj->attributes)
188                 return -1;
189         return 0;
190 } /* service_init */
192 static void
193 service_destroy(sdb_object_t *obj)
195         sdb_service_t *sobj = SVC(obj);
196         assert(obj);
198         store_obj_destroy(obj);
200         if (sobj->attributes)
201                 sdb_avltree_destroy(sobj->attributes);
202 } /* service_destroy */
204 static int
205 metric_init(sdb_object_t *obj, va_list ap)
207         sdb_metric_t *sobj = METRIC(obj);
208         int ret;
210         /* this will consume the first argument (type) of ap */
211         ret = store_obj_init(obj, ap);
212         if (ret)
213                 return ret;
215         sobj->attributes = sdb_avltree_create();
216         if (! sobj->attributes)
217                 return -1;
219         sobj->store.type = sobj->store.id = NULL;
220         return 0;
221 } /* metric_init */
223 static void
224 metric_destroy(sdb_object_t *obj)
226         sdb_metric_t *sobj = METRIC(obj);
227         assert(obj);
229         store_obj_destroy(obj);
231         if (sobj->attributes)
232                 sdb_avltree_destroy(sobj->attributes);
234         if (sobj->store.type)
235                 free(sobj->store.type);
236         if (sobj->store.id)
237                 free(sobj->store.id);
238 } /* metric_destroy */
240 static int
241 attr_init(sdb_object_t *obj, va_list ap)
243         const sdb_data_t *value;
244         int ret;
246         /* this will consume the first two arguments
247          * (type and last_update) of ap */
248         ret = store_obj_init(obj, ap);
249         if (ret)
250                 return ret;
251         value = va_arg(ap, const sdb_data_t *);
253         if (value)
254                 if (sdb_data_copy(&ATTR(obj)->value, value))
255                         return -1;
256         return 0;
257 } /* attr_init */
259 static void
260 attr_destroy(sdb_object_t *obj)
262         assert(obj);
264         store_obj_destroy(obj);
265         sdb_data_free_datum(&ATTR(obj)->value);
266 } /* attr_destroy */
268 static sdb_type_t store_type = {
269         /* size = */ sizeof(sdb_store_t),
270         /* init = */ store_init,
271         /* destroy = */ store_destroy,
272 };
274 static sdb_type_t host_type = {
275         /* size = */ sizeof(sdb_host_t),
276         /* init = */ host_init,
277         /* destroy = */ host_destroy
278 };
280 static sdb_type_t service_type = {
281         /* size = */ sizeof(sdb_service_t),
282         /* init = */ service_init,
283         /* destroy = */ service_destroy
284 };
286 static sdb_type_t metric_type = {
287         /* size = */ sizeof(sdb_metric_t),
288         /* init = */ metric_init,
289         /* destroy = */ metric_destroy
290 };
292 static sdb_type_t attribute_type = {
293         /* size = */ sizeof(sdb_attribute_t),
294         /* init = */ attr_init,
295         /* destroy = */ attr_destroy
296 };
298 /*
299  * private helper functions
300  */
302 static sdb_host_t *
303 lookup_host(sdb_store_t *st, const char *name, bool canonicalize)
305         sdb_host_t *host;
306         char *cname;
308         assert(name);
309         if (! canonicalize)
310                 return HOST(sdb_avltree_lookup(st->hosts, name));
312         cname = strdup(name);
313         cname = sdb_plugin_cname(cname);
314         if (! cname) {
315                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
316                 return NULL;
317         }
319         host = HOST(sdb_avltree_lookup(st->hosts, cname));
320         free(cname);
321         return host;
322 } /* lookup_host */
324 static int
325 record_backend(sdb_store_obj_t *obj)
327         const sdb_plugin_info_t *info;
328         char **tmp;
329         size_t i;
331         info = sdb_plugin_current();
332         if (! info)
333                 return 0;
335         for (i = 0; i < obj->backends_num; ++i)
336                 if (!strcasecmp(obj->backends[i], info->plugin_name))
337                         return 0;
339         tmp = realloc(obj->backends,
340                         (obj->backends_num + 1) * sizeof(*obj->backends));
341         if (! tmp)
342                 return -1;
344         obj->backends = tmp;
345         obj->backends[obj->backends_num] = strdup(info->plugin_name);
346         if (! obj->backends[obj->backends_num])
347                 return -1;
349         ++obj->backends_num;
350         return 0;
351 } /* record_backend */
353 static int
354 store_obj(sdb_store_obj_t *parent, sdb_avltree_t *parent_tree,
355                 int type, const char *name, sdb_time_t last_update,
356                 sdb_store_obj_t **updated_obj)
358         sdb_store_obj_t *old, *new;
359         int status = 0;
361         assert(parent_tree);
363         if (last_update <= 0)
364                 last_update = sdb_gettime();
366         old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
367         if (old) {
368                 if (old->last_update > last_update) {
369                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
370                                         "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
371                                         SDB_STORE_TYPE_TO_NAME(type), name,
372                                         last_update, old->last_update);
373                         /* don't report an error; the object may be updated by multiple
374                          * backends */
375                         status = 1;
376                 }
377                 else if (old->last_update == last_update) {
378                         /* don't report an error and also don't even log this to avoid
379                          * excessive noise on high sampling frequencies */
380                         status = 1;
381                 }
382                 else {
383                         sdb_time_t interval = last_update - old->last_update;
384                         old->last_update = last_update;
385                         if (interval) {
386                                 if (old->interval)
387                                         old->interval = (sdb_time_t)((0.9 * (double)old->interval)
388                                                         + (0.1 * (double)interval));
389                                 else
390                                         old->interval = interval;
391                         }
392                 }
394                 new = old;
395                 sdb_object_deref(SDB_OBJ(old));
396         }
397         else {
398                 if (type == SDB_ATTRIBUTE) {
399                         /* the value will be updated by the caller */
400                         new = STORE_OBJ(sdb_object_create(name, attribute_type,
401                                                 type, last_update, NULL));
402                 }
403                 else {
404                         sdb_type_t t;
405                         t = type == SDB_HOST
406                                 ? host_type
407                                 : type == SDB_SERVICE
408                                         ? service_type
409                                         : metric_type;
410                         new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
411                 }
413                 if (new) {
414                         status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
416                         /* pass control to the tree or destroy in case of an error */
417                         sdb_object_deref(SDB_OBJ(new));
418                 }
419                 else {
420                         char errbuf[1024];
421                         sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
422                                         SDB_STORE_TYPE_TO_NAME(type), name,
423                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
424                         status = -1;
425                 }
426         }
428         if (status < 0)
429                 return status;
430         assert(new);
432         if (new->parent != parent) {
433                 // Avoid circular self-references which are not handled
434                 // correctly by the ref-count based management layer.
435                 //sdb_object_deref(SDB_OBJ(new->parent));
436                 //sdb_object_ref(SDB_OBJ(parent));
437                 new->parent = parent;
438         }
440         if (updated_obj)
441                 *updated_obj = new;
443         if (record_backend(new))
444                 return -1;
445         return status;
446 } /* store_obj */
448 static int
449 store_attr(sdb_store_obj_t *parent, sdb_avltree_t *attributes,
450                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
452         sdb_store_obj_t *attr = NULL;
453         int status;
455         status = store_obj(parent, attributes, SDB_ATTRIBUTE,
456                         key, last_update, &attr);
457         if (status)
458                 return status;
460         /* don't update unchanged values */
461         if (! sdb_data_cmp(&ATTR(attr)->value, value))
462                 return status;
464         assert(attr);
465         if (sdb_data_copy(&ATTR(attr)->value, value))
466                 return -1;
467         return status;
468 } /* store_attr */
470 static int
471 store_metric_store(sdb_metric_t *metric, sdb_metric_store_t *store)
473         char *type = metric->store.type;
474         char *id = metric->store.id;
476         if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
477                 if (! (type = strdup(store->type)))
478                         return -1;
479         }
480         if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
481                 if (! (id = strdup(store->id))) {
482                         if (type != metric->store.type)
483                                 free(type);
484                         return -1;
485                 }
486         }
488         if (type != metric->store.type) {
489                 if (metric->store.type)
490                         free(metric->store.type);
491                 metric->store.type = type;
492         }
493         if (id != metric->store.id) {
494                 if (metric->store.id)
495                         free(metric->store.id);
496                 metric->store.id = id;
497         }
498         return 0;
499 } /* store_metric_store */
501 /* The store's host_lock has to be acquired before calling this function. */
502 static sdb_avltree_t *
503 get_host_children(sdb_host_t *host, int type)
505         if ((type != SDB_SERVICE) && (type != SDB_METRIC)
506                         && (type != SDB_ATTRIBUTE))
507                 return NULL;
509         if (! host)
510                 return NULL;
512         if (type == SDB_ATTRIBUTE)
513                 return host->attributes;
514         else if (type == SDB_METRIC)
515                 return host->metrics;
516         else
517                 return host->services;
518 } /* get_host_children */
520 /*
521  * ts_tojson serializes a time-series to JSON.
522  *
523  * The function never returns an error. Rather, an error message will be part
524  * of the serialized data.
525  */
526 static void
527 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
529         char start_str[64];
530         char end_str[64];
532         size_t i;
534         /* TODO: make time format configurable */
535         if (! sdb_strftime(start_str, sizeof(start_str), ts->start))
536                 snprintf(start_str, sizeof(start_str), "<error>");
537         start_str[sizeof(start_str) - 1] = '\0';
538         if (! sdb_strftime(end_str, sizeof(end_str), ts->end))
539                 snprintf(end_str, sizeof(end_str), "<error>");
540         end_str[sizeof(end_str) - 1] = '\0';
542         sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
543                         start_str, end_str);
545         for (i = 0; i < ts->data_names_len; ++i) {
546                 size_t j;
547                 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
549                 for (j = 0; j < ts->data_len; ++j) {
550                         char time_str[64];
552                         if (! sdb_strftime(time_str, sizeof(time_str), ts->data[i][j].timestamp))
553                                 snprintf(time_str, sizeof(time_str), "<error>");
554                         time_str[sizeof(time_str) - 1] = '\0';
556                         /* Some GNU libc versions may print '-nan' which we dont' want */
557                         if (isnan(ts->data[i][j].value))
558                                 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
559                                                 "\"value\": \"nan\"}", time_str);
560                         else
561                                 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
562                                                 "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
564                         if (j < ts->data_len - 1)
565                                 sdb_strbuf_append(buf, ",");
566                 }
568                 if (i < ts->data_names_len - 1)
569                         sdb_strbuf_append(buf, "],");
570                 else
571                         sdb_strbuf_append(buf, "]");
572         }
573         sdb_strbuf_append(buf, "}}");
574 } /* ts_tojson */
576 /*
577  * store writer API
578  */
580 static int
581 store_attribute(const char *hostname,
582                 const char *key, const sdb_data_t *value,
583                 sdb_time_t last_update, sdb_object_t *user_data)
585         sdb_store_t *st = ST(user_data);
587         sdb_host_t *host;
588         sdb_avltree_t *attrs;
589         int status = 0;
591         if ((! hostname) || (! key))
592                 return -1;
594         pthread_rwlock_wrlock(&st->host_lock);
595         host = lookup_host(st, hostname, /* canonicalize = */ 1);
596         attrs = get_host_children(host, SDB_ATTRIBUTE);
597         if (! attrs) {
598                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
599                                 "host '%s' not found", key, hostname);
600                 status = -1;
601         }
603         if (! status)
604                 status = store_attr(STORE_OBJ(host), attrs, key, value, last_update);
606         sdb_object_deref(SDB_OBJ(host));
607         pthread_rwlock_unlock(&st->host_lock);
609         return status;
610 } /* store_attribute */
612 static int
613 store_host(const char *name, sdb_time_t last_update, sdb_object_t *user_data)
615         sdb_store_t *st = ST(user_data);
617         char *cname = NULL;
618         int status = 0;
620         if (! name)
621                 return -1;
623         cname = sdb_plugin_cname(strdup(name));
624         if (! cname) {
625                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
626                 return -1;
627         }
629         pthread_rwlock_wrlock(&st->host_lock);
630         status = store_obj(NULL, st->hosts,
631                         SDB_HOST, cname, last_update, NULL);
632         pthread_rwlock_unlock(&st->host_lock);
634         free(cname);
635         return status;
636 } /* store_host */
638 static int
639 store_service_attr(const char *hostname, const char *service,
640                 const char *key, const sdb_data_t *value, sdb_time_t last_update,
641                 sdb_object_t *user_data)
643         sdb_store_t *st = ST(user_data);
645         sdb_host_t *host;
646         sdb_service_t *svc;
647         sdb_avltree_t *services;
648         int status = 0;
650         if ((! hostname) || (! service) || (! key))
651                 return -1;
653         pthread_rwlock_wrlock(&st->host_lock);
654         host = lookup_host(st, hostname, /* canonicalize = */ 1);
655         services = get_host_children(host, SDB_SERVICE);
656         sdb_object_deref(SDB_OBJ(host));
657         if (! services) {
658                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
659                                 "for service '%s' - host '%ss' not found",
660                                 key, service, hostname);
661                 pthread_rwlock_unlock(&st->host_lock);
662                 return -1;
663         }
665         svc = SVC(sdb_avltree_lookup(services, service));
666         if (! svc) {
667                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
668                                 "service '%s/%s' not found", key, hostname, service);
669                 status = -1;
670         }
672         if (! status)
673                 status = store_attr(STORE_OBJ(svc), svc->attributes,
674                                 key, value, last_update);
676         sdb_object_deref(SDB_OBJ(svc));
677         pthread_rwlock_unlock(&st->host_lock);
679         return status;
680 } /* store_service_attr */
682 static int
683 store_service(const char *hostname, const char *name,
684                 sdb_time_t last_update, sdb_object_t *user_data)
686         sdb_store_t *st = ST(user_data);
688         sdb_host_t *host;
689         sdb_avltree_t *services;
690         sdb_data_t d;
692         int status = 0;
694         if ((! hostname) || (! name))
695                 return -1;
697         pthread_rwlock_wrlock(&st->host_lock);
698         host = lookup_host(st, hostname, /* canonicalize = */ 1);
699         services = get_host_children(host, SDB_SERVICE);
700         if (! services) {
701                 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
702                                 "host '%s' not found", name, hostname);
703                 status = -1;
704         }
706         if (! status)
707                 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
708                                 name, last_update, NULL);
710         sdb_object_deref(SDB_OBJ(host));
711         pthread_rwlock_unlock(&st->host_lock);
713         if (status)
714                 return status;
716         /* record the hostname as an attribute */
717         d.type = SDB_TYPE_STRING;
718         d.data.string = SDB_OBJ(host)->name;
719         if (store_service_attr(hostname, name, "hostname", &d, last_update, user_data))
720                 status = -1;
721         return status;
722 } /* store_service */
724 static int
725 store_metric_attr(const char *hostname, const char *metric,
726                 const char *key, const sdb_data_t *value, sdb_time_t last_update,
727                 sdb_object_t *user_data)
729         sdb_store_t *st = ST(user_data);
731         sdb_avltree_t *metrics;
732         sdb_host_t *host;
733         sdb_metric_t *m;
734         int status = 0;
736         if ((! hostname) || (! metric) || (! key))
737                 return -1;
739         pthread_rwlock_wrlock(&st->host_lock);
740         host = lookup_host(st, hostname, /* canonicalize = */ 1);
741         metrics = get_host_children(host, SDB_METRIC);
742         sdb_object_deref(SDB_OBJ(host));
743         if (! metrics) {
744                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
745                                 "for metric '%s' - host '%s' not found",
746                                 key, metric, hostname);
747                 pthread_rwlock_unlock(&st->host_lock);
748                 return -1;
749         }
751         m = METRIC(sdb_avltree_lookup(metrics, metric));
752         if (! m) {
753                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
754                                 "metric '%s/%s' not found", key, hostname, metric);
755                 status = -1;
756         }
758         if (! status)
759                 status = store_attr(STORE_OBJ(m), m->attributes,
760                                 key, value, last_update);
762         sdb_object_deref(SDB_OBJ(m));
763         pthread_rwlock_unlock(&st->host_lock);
765         return status;
766 } /* store_metric_attr */
768 static int
769 store_metric(const char *hostname, const char *name,
770                 sdb_metric_store_t *store, sdb_time_t last_update,
771                 sdb_object_t *user_data)
773         sdb_store_t *st = ST(user_data);
775         sdb_store_obj_t *obj = NULL;
776         sdb_host_t *host;
777         sdb_metric_t *metric;
778         sdb_data_t d;
780         sdb_avltree_t *metrics;
782         int status = 0;
784         if ((! hostname) || (! name))
785                 return -1;
787         if (store) {
788                 if ((store->type != NULL) != (store->id != NULL))
789                         return -1;
790                 else if (! store->type)
791                         store = NULL;
792         }
794         pthread_rwlock_wrlock(&st->host_lock);
795         host = lookup_host(st, hostname, /* canonicalize = */ 1);
796         metrics = get_host_children(host, SDB_METRIC);
797         if (! metrics) {
798                 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
799                                 "host '%s' not found", name, hostname);
800                 status = -1;
801         }
803         if (! status)
804                 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
805                                 name, last_update, &obj);
806         sdb_object_deref(SDB_OBJ(host));
808         if (status) {
809                 pthread_rwlock_unlock(&st->host_lock);
810                 return status;
811         }
813         assert(obj);
814         metric = METRIC(obj);
816         if (store)
817                 if (store_metric_store(metric, store))
818                         status = -1;
819         pthread_rwlock_unlock(&st->host_lock);
821         /* record the hostname as an attribute */
822         d.type = SDB_TYPE_STRING;
823         d.data.string = SDB_OBJ(host)->name;
824         if (store_metric_attr(hostname, name, "hostname", &d, last_update, user_data))
825                 status = -1;
826         return status;
827 } /* store_metric */
829 static sdb_store_writer_t store_writer = {
830         store_host, store_service, store_metric,
831         store_attribute, store_service_attr, store_metric_attr,
832 };
834 /*
835  * public API
836  */
838 int
839 sdb_store_init(void)
841         if (global_store)
842                 return 0;
844         global_store = ST(sdb_object_create("store", store_type));
845         if (! global_store) {
846                 sdb_log(SDB_LOG_ERR, "store: Failed to allocate store");
847                 return -1;
848         }
849         return sdb_plugin_register_writer("memstore",
850                         &store_writer, SDB_OBJ(global_store));
851 } /* sdb_store_init */
853 void
854 sdb_store_clear(void)
856         if (! global_store)
857                 return;
858         sdb_avltree_clear(global_store->hosts);
859 } /* sdb_store_clear */
861 bool
862 sdb_store_has_host(const char *name)
864         sdb_host_t *host;
866         if ((! global_store) || (! name))
867                 return false;
869         host = lookup_host(global_store, name, /* canonicalize = */ 0);
870         sdb_object_deref(SDB_OBJ(host));
871         return host != NULL;
872 } /* sdb_store_has_host */
874 sdb_store_obj_t *
875 sdb_store_get_host(const char *name)
877         sdb_host_t *host;
879         if ((! global_store) || (! name))
880                 return NULL;
882         host = lookup_host(global_store, name, /* canonicalize = */ 0);
883         if (! host)
884                 return NULL;
886         return STORE_OBJ(host);
887 } /* sdb_store_get_host */
889 sdb_store_obj_t *
890 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
892         sdb_avltree_t *children;
894         if ((! host) || (host->type != SDB_HOST) || (! name))
895                 return NULL;
897         children = get_host_children(HOST(host), type);
898         if (! children)
899                 return NULL;
900         return STORE_OBJ(sdb_avltree_lookup(children, name));
901 } /* sdb_store_get_child */
903 int
904 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
906         sdb_data_t tmp;
908         if (! obj)
909                 return -1;
911         switch (field) {
912                 case SDB_FIELD_NAME:
913                         tmp.type = SDB_TYPE_STRING;
914                         tmp.data.string = strdup(SDB_OBJ(obj)->name);
915                         if (! tmp.data.string)
916                                 return -1;
917                         break;
918                 case SDB_FIELD_LAST_UPDATE:
919                         tmp.type = SDB_TYPE_DATETIME;
920                         tmp.data.datetime = obj->last_update;
921                         break;
922                 case SDB_FIELD_AGE:
923                         tmp.type = SDB_TYPE_DATETIME;
924                         tmp.data.datetime = sdb_gettime() - obj->last_update;
925                         break;
926                 case SDB_FIELD_INTERVAL:
927                         tmp.type = SDB_TYPE_DATETIME;
928                         tmp.data.datetime = obj->interval;
929                         break;
930                 case SDB_FIELD_BACKEND:
931                         if (! res)
932                                 return 0;
933                         tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
934                         tmp.data.array.length = obj->backends_num;
935                         tmp.data.array.values = obj->backends;
936                         return sdb_data_copy(res, &tmp);
937                 case SDB_FIELD_VALUE:
938                         if (obj->type != SDB_ATTRIBUTE)
939                                 return -1;
940                         if (! res)
941                                 return 0;
942                         return sdb_data_copy(res, &ATTR(obj)->value);
943                 case SDB_FIELD_TIMESERIES:
944                         if (obj->type != SDB_METRIC)
945                                 return -1;
946                         tmp.type = SDB_TYPE_BOOLEAN;
947                         tmp.data.boolean = METRIC(obj)->store.type != NULL;
948                 default:
949                         return -1;
950         }
951         if (res)
952                 *res = tmp;
953         else
954                 sdb_data_free_datum(&tmp);
955         return 0;
956 } /* sdb_store_get_field */
958 int
959 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
960                 sdb_store_matcher_t *filter)
962         sdb_avltree_t *tree = NULL;
963         sdb_store_obj_t *attr;
965         if ((! obj) || (! name))
966                 return -1;
968         if (obj->type == SDB_HOST)
969                 tree = HOST(obj)->attributes;
970         else if (obj->type == SDB_SERVICE)
971                 tree = SVC(obj)->attributes;
972         else if (obj->type == SDB_METRIC)
973                 tree = METRIC(obj)->attributes;
975         if (! tree)
976                 return -1;
978         attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
979         if (! attr)
980                 return -1;
981         if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
982                 sdb_object_deref(SDB_OBJ(attr));
983                 return -1;
984         }
986         assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
987         if (res)
988                 sdb_data_copy(res, &ATTR(attr)->value);
989         sdb_object_deref(SDB_OBJ(attr));
990         return 0;
991 } /* sdb_store_get_attr */
993 int
994 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
995                 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
997         sdb_avltree_t *metrics;
998         sdb_host_t *host;
999         sdb_metric_t *m;
1001         sdb_timeseries_t *ts;
1003         int status = 0;
1005         if ((! global_store) || (! hostname) || (! metric) || (! opts) || (! buf))
1006                 return -1;
1008         pthread_rwlock_rdlock(&global_store->host_lock);
1009         host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
1010         metrics = get_host_children(host, SDB_METRIC);
1011         sdb_object_deref(SDB_OBJ(host));
1012         if (! metrics) {
1013                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1014                                 "- host '%s' not found", hostname, metric, hostname);
1015                 pthread_rwlock_unlock(&global_store->host_lock);
1016                 return -1;
1017         }
1019         m = METRIC(sdb_avltree_lookup(metrics, metric));
1020         if (! m) {
1021                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1022                                 "- metric '%s' not found", hostname, metric, metric);
1023                 pthread_rwlock_unlock(&global_store->host_lock);
1024                 return -1;
1025         }
1027         if ((! m->store.type) || (! m->store.id)) {
1028                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1029                                 "- no data-store configured for the stored metric",
1030                                 hostname, metric);
1031                 sdb_object_deref(SDB_OBJ(m));
1032                 pthread_rwlock_unlock(&global_store->host_lock);
1033                 return -1;
1034         }
1036         {
1037                 char type[strlen(m->store.type) + 1];
1038                 char id[strlen(m->store.id) + 1];
1040                 strncpy(type, m->store.type, sizeof(type));
1041                 strncpy(id, m->store.id, sizeof(id));
1042                 pthread_rwlock_unlock(&global_store->host_lock);
1044                 ts = sdb_plugin_fetch_timeseries(type, id, opts);
1045                 if (! ts) {
1046                         sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1047                                         "- %s fetcher callback returned no data for '%s'",
1048                                         hostname, metric, type, id);
1049                         status = -1;
1050                 }
1051         }
1053         ts_tojson(ts, buf);
1054         sdb_object_deref(SDB_OBJ(m));
1055         sdb_timeseries_destroy(ts);
1056         return status;
1057 } /* sdb_store_fetch_timeseries */
1059 int
1060 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
1061                 sdb_store_lookup_cb cb, void *user_data)
1063         sdb_avltree_iter_t *host_iter = NULL;
1064         int status = 0;
1066         if ((! global_store) || (! cb))
1067                 return -1;
1069         if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
1070                 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
1071                 return -1;
1072         }
1074         pthread_rwlock_rdlock(&global_store->host_lock);
1075         host_iter = sdb_avltree_get_iter(global_store->hosts);
1076         if (! host_iter)
1077                 status = -1;
1079         /* has_next returns false if the iterator is NULL */
1080         while (sdb_avltree_iter_has_next(host_iter)) {
1081                 sdb_store_obj_t *host;
1082                 sdb_avltree_iter_t *iter = NULL;
1084                 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1085                 assert(host);
1087                 if (! sdb_store_matcher_matches(filter, host, NULL))
1088                         continue;
1090                 if (type == SDB_SERVICE)
1091                         iter = sdb_avltree_get_iter(HOST(host)->services);
1092                 else if (type == SDB_METRIC)
1093                         iter = sdb_avltree_get_iter(HOST(host)->metrics);
1095                 if (iter) {
1096                         while (sdb_avltree_iter_has_next(iter)) {
1097                                 sdb_store_obj_t *obj;
1098                                 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1099                                 assert(obj);
1101                                 if (sdb_store_matcher_matches(m, obj, filter)) {
1102                                         if (cb(obj, filter, user_data)) {
1103                                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1104                                                                 "an error while scanning");
1105                                                 status = -1;
1106                                                 break;
1107                                         }
1108                                 }
1109                         }
1110                 }
1111                 else if (sdb_store_matcher_matches(m, host, filter)) {
1112                         if (cb(host, filter, user_data)) {
1113                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1114                                                 "an error while scanning");
1115                                 status = -1;
1116                         }
1117                 }
1119                 sdb_avltree_iter_destroy(iter);
1120                 if (status)
1121                         break;
1122         }
1124         sdb_avltree_iter_destroy(host_iter);
1125         pthread_rwlock_unlock(&global_store->host_lock);
1126         return status;
1127 } /* sdb_store_scan */
1129 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */