Code

store: Reordered some functions to group by general purpose.
[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  * public API
578  */
580 int
581 sdb_store_init(void)
583         if (global_store)
584                 return 0;
586         global_store = ST(sdb_object_create("store", store_type));
587         if (! global_store) {
588                 sdb_log(SDB_LOG_ERR, "store: Failed to allocate store");
589                 return -1;
590         }
591         return 0;
592 } /* sdb_store_init */
594 void
595 sdb_store_clear(void)
597         if (! global_store)
598                 return;
599         sdb_avltree_clear(global_store->hosts);
600 } /* sdb_store_clear */
602 int
603 sdb_store_attribute(const char *hostname,
604                 const char *key, const sdb_data_t *value,
605                 sdb_time_t last_update)
607         sdb_host_t *host;
608         sdb_avltree_t *attrs;
609         int status = 0;
611         if ((! global_store) || (! hostname) || (! key))
612                 return -1;
614         pthread_rwlock_wrlock(&global_store->host_lock);
615         host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
616         attrs = get_host_children(host, SDB_ATTRIBUTE);
617         if (! attrs) {
618                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
619                                 "host '%s' not found", key, hostname);
620                 status = -1;
621         }
623         if (! status)
624                 status = store_attr(STORE_OBJ(host), attrs, key, value, last_update);
626         sdb_object_deref(SDB_OBJ(host));
627         pthread_rwlock_unlock(&global_store->host_lock);
629         if (sdb_plugin_store_attribute(hostname, key, value, last_update))
630                 status = -1;
631         return status;
632 } /* sdb_store_attribute */
634 int
635 sdb_store_host(const char *name, sdb_time_t last_update)
637         char *cname = NULL;
638         int status = 0;
640         if ((! global_store) || (! name))
641                 return -1;
643         cname = sdb_plugin_cname(strdup(name));
644         if (! cname) {
645                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
646                 return -1;
647         }
649         pthread_rwlock_wrlock(&global_store->host_lock);
650         status = store_obj(NULL, global_store->hosts,
651                         SDB_HOST, cname, last_update, NULL);
652         pthread_rwlock_unlock(&global_store->host_lock);
654         if (sdb_plugin_store_host(name, last_update))
655                 status = -1;
657         free(cname);
658         return status;
659 } /* sdb_store_host */
661 int
662 sdb_store_service_attr(const char *hostname, const char *service,
663                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
665         sdb_host_t *host;
666         sdb_service_t *svc;
667         sdb_avltree_t *services;
668         int status = 0;
670         if ((! global_store) || (! hostname) || (! service) || (! key))
671                 return -1;
673         pthread_rwlock_wrlock(&global_store->host_lock);
674         host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
675         services = get_host_children(host, SDB_SERVICE);
676         sdb_object_deref(SDB_OBJ(host));
677         if (! services) {
678                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
679                                 "for service '%s' - host '%ss' not found",
680                                 key, service, hostname);
681                 pthread_rwlock_unlock(&global_store->host_lock);
682                 return -1;
683         }
685         svc = SVC(sdb_avltree_lookup(services, service));
686         if (! svc) {
687                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
688                                 "service '%s/%s' not found", key, hostname, service);
689                 status = -1;
690         }
692         if (! status)
693                 status = store_attr(STORE_OBJ(svc), svc->attributes,
694                                 key, value, last_update);
696         sdb_object_deref(SDB_OBJ(svc));
697         pthread_rwlock_unlock(&global_store->host_lock);
699         if (sdb_plugin_store_service_attribute(hostname, service,
700                                 key, value, last_update))
701                 status = -1;
702         return status;
703 } /* sdb_store_service_attr */
705 int
706 sdb_store_service(const char *hostname, const char *name,
707                 sdb_time_t last_update)
709         sdb_host_t *host;
710         sdb_avltree_t *services;
711         sdb_data_t d;
713         int status = 0;
715         if ((! global_store) || (! hostname) || (! name))
716                 return -1;
718         pthread_rwlock_wrlock(&global_store->host_lock);
719         host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
720         services = get_host_children(host, SDB_SERVICE);
721         if (! services) {
722                 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
723                                 "host '%s' not found", name, hostname);
724                 status = -1;
725         }
727         if (! status)
728                 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
729                                 name, last_update, NULL);
731         sdb_object_deref(SDB_OBJ(host));
732         pthread_rwlock_unlock(&global_store->host_lock);
734         if (status)
735                 return status;
737         if (sdb_plugin_store_service(hostname, name, last_update))
738                 status = -1;
740         /* record the hostname as an attribute */
741         d.type = SDB_TYPE_STRING;
742         d.data.string = SDB_OBJ(host)->name;
743         if (sdb_store_service_attr(hostname, name, "hostname", &d, last_update))
744                 status = -1;
745         return status;
746 } /* sdb_store_service */
748 int
749 sdb_store_metric_attr(const char *hostname, const char *metric,
750                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
752         sdb_avltree_t *metrics;
753         sdb_host_t *host;
754         sdb_metric_t *m;
755         int status = 0;
757         if ((! global_store) || (! hostname) || (! metric) || (! key))
758                 return -1;
760         pthread_rwlock_wrlock(&global_store->host_lock);
761         host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
762         metrics = get_host_children(host, SDB_METRIC);
763         sdb_object_deref(SDB_OBJ(host));
764         if (! metrics) {
765                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
766                                 "for metric '%s' - host '%s' not found",
767                                 key, metric, hostname);
768                 pthread_rwlock_unlock(&global_store->host_lock);
769                 return -1;
770         }
772         m = METRIC(sdb_avltree_lookup(metrics, metric));
773         if (! m) {
774                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
775                                 "metric '%s/%s' not found", key, hostname, metric);
776                 status = -1;
777         }
779         if (! status)
780                 status = store_attr(STORE_OBJ(m), m->attributes,
781                                 key, value, last_update);
783         sdb_object_deref(SDB_OBJ(m));
784         pthread_rwlock_unlock(&global_store->host_lock);
786         if (sdb_plugin_store_metric_attribute(hostname, metric,
787                                 key, value, last_update))
788                 status = -1;
789         return status;
790 } /* sdb_store_metric_attr */
792 int
793 sdb_store_metric(const char *hostname, const char *name,
794                 sdb_metric_store_t *store, sdb_time_t last_update)
796         sdb_store_obj_t *obj = NULL;
797         sdb_host_t *host;
798         sdb_metric_t *metric;
799         sdb_data_t d;
801         sdb_avltree_t *metrics;
803         int status = 0;
805         if ((! global_store) || (! hostname) || (! name))
806                 return -1;
808         if (store) {
809                 if ((store->type != NULL) != (store->id != NULL))
810                         return -1;
811                 else if (! store->type)
812                         store = NULL;
813         }
815         pthread_rwlock_wrlock(&global_store->host_lock);
816         host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
817         metrics = get_host_children(host, SDB_METRIC);
818         if (! metrics) {
819                 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
820                                 "host '%s' not found", name, hostname);
821                 status = -1;
822         }
824         if (! status)
825                 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
826                                 name, last_update, &obj);
827         sdb_object_deref(SDB_OBJ(host));
829         if (status) {
830                 pthread_rwlock_unlock(&global_store->host_lock);
831                 return status;
832         }
834         assert(obj);
835         metric = METRIC(obj);
837         if (store)
838                 if (store_metric_store(metric, store))
839                         status = -1;
840         pthread_rwlock_unlock(&global_store->host_lock);
842         if (sdb_plugin_store_metric(hostname, name, store, last_update))
843                 status = -1;
845         /* record the hostname as an attribute */
846         d.type = SDB_TYPE_STRING;
847         d.data.string = SDB_OBJ(host)->name;
848         if (sdb_store_metric_attr(hostname, name, "hostname", &d, last_update))
849                 status = -1;
850         return status;
851 } /* sdb_store_metric */
853 bool
854 sdb_store_has_host(const char *name)
856         sdb_host_t *host;
858         if ((! global_store) || (! name))
859                 return false;
861         host = lookup_host(global_store, name, /* canonicalize = */ 0);
862         sdb_object_deref(SDB_OBJ(host));
863         return host != NULL;
864 } /* sdb_store_has_host */
866 sdb_store_obj_t *
867 sdb_store_get_host(const char *name)
869         sdb_host_t *host;
871         if ((! global_store) || (! name))
872                 return NULL;
874         host = lookup_host(global_store, name, /* canonicalize = */ 0);
875         if (! host)
876                 return NULL;
878         return STORE_OBJ(host);
879 } /* sdb_store_get_host */
881 sdb_store_obj_t *
882 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
884         sdb_avltree_t *children;
886         if ((! host) || (host->type != SDB_HOST) || (! name))
887                 return NULL;
889         children = get_host_children(HOST(host), type);
890         if (! children)
891                 return NULL;
892         return STORE_OBJ(sdb_avltree_lookup(children, name));
893 } /* sdb_store_get_child */
895 int
896 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
898         sdb_data_t tmp;
900         if (! obj)
901                 return -1;
903         switch (field) {
904                 case SDB_FIELD_NAME:
905                         tmp.type = SDB_TYPE_STRING;
906                         tmp.data.string = strdup(SDB_OBJ(obj)->name);
907                         if (! tmp.data.string)
908                                 return -1;
909                         break;
910                 case SDB_FIELD_LAST_UPDATE:
911                         tmp.type = SDB_TYPE_DATETIME;
912                         tmp.data.datetime = obj->last_update;
913                         break;
914                 case SDB_FIELD_AGE:
915                         tmp.type = SDB_TYPE_DATETIME;
916                         tmp.data.datetime = sdb_gettime() - obj->last_update;
917                         break;
918                 case SDB_FIELD_INTERVAL:
919                         tmp.type = SDB_TYPE_DATETIME;
920                         tmp.data.datetime = obj->interval;
921                         break;
922                 case SDB_FIELD_BACKEND:
923                         if (! res)
924                                 return 0;
925                         tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
926                         tmp.data.array.length = obj->backends_num;
927                         tmp.data.array.values = obj->backends;
928                         return sdb_data_copy(res, &tmp);
929                 case SDB_FIELD_VALUE:
930                         if (obj->type != SDB_ATTRIBUTE)
931                                 return -1;
932                         if (! res)
933                                 return 0;
934                         return sdb_data_copy(res, &ATTR(obj)->value);
935                 case SDB_FIELD_TIMESERIES:
936                         if (obj->type != SDB_METRIC)
937                                 return -1;
938                         tmp.type = SDB_TYPE_BOOLEAN;
939                         tmp.data.boolean = METRIC(obj)->store.type != NULL;
940                 default:
941                         return -1;
942         }
943         if (res)
944                 *res = tmp;
945         else
946                 sdb_data_free_datum(&tmp);
947         return 0;
948 } /* sdb_store_get_field */
950 int
951 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
952                 sdb_store_matcher_t *filter)
954         sdb_avltree_t *tree = NULL;
955         sdb_store_obj_t *attr;
957         if ((! obj) || (! name))
958                 return -1;
960         if (obj->type == SDB_HOST)
961                 tree = HOST(obj)->attributes;
962         else if (obj->type == SDB_SERVICE)
963                 tree = SVC(obj)->attributes;
964         else if (obj->type == SDB_METRIC)
965                 tree = METRIC(obj)->attributes;
967         if (! tree)
968                 return -1;
970         attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
971         if (! attr)
972                 return -1;
973         if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
974                 sdb_object_deref(SDB_OBJ(attr));
975                 return -1;
976         }
978         assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
979         if (res)
980                 sdb_data_copy(res, &ATTR(attr)->value);
981         sdb_object_deref(SDB_OBJ(attr));
982         return 0;
983 } /* sdb_store_get_attr */
985 int
986 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
987                 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
989         sdb_avltree_t *metrics;
990         sdb_host_t *host;
991         sdb_metric_t *m;
993         sdb_timeseries_t *ts;
995         int status = 0;
997         if ((! global_store) || (! hostname) || (! metric) || (! opts) || (! buf))
998                 return -1;
1000         pthread_rwlock_rdlock(&global_store->host_lock);
1001         host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
1002         metrics = get_host_children(host, SDB_METRIC);
1003         sdb_object_deref(SDB_OBJ(host));
1004         if (! metrics) {
1005                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1006                                 "- host '%s' not found", hostname, metric, hostname);
1007                 pthread_rwlock_unlock(&global_store->host_lock);
1008                 return -1;
1009         }
1011         m = METRIC(sdb_avltree_lookup(metrics, metric));
1012         if (! m) {
1013                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1014                                 "- metric '%s' not found", hostname, metric, metric);
1015                 pthread_rwlock_unlock(&global_store->host_lock);
1016                 return -1;
1017         }
1019         if ((! m->store.type) || (! m->store.id)) {
1020                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1021                                 "- no data-store configured for the stored metric",
1022                                 hostname, metric);
1023                 sdb_object_deref(SDB_OBJ(m));
1024                 pthread_rwlock_unlock(&global_store->host_lock);
1025                 return -1;
1026         }
1028         {
1029                 char type[strlen(m->store.type) + 1];
1030                 char id[strlen(m->store.id) + 1];
1032                 strncpy(type, m->store.type, sizeof(type));
1033                 strncpy(id, m->store.id, sizeof(id));
1034                 pthread_rwlock_unlock(&global_store->host_lock);
1036                 ts = sdb_plugin_fetch_timeseries(type, id, opts);
1037                 if (! ts) {
1038                         sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1039                                         "- %s fetcher callback returned no data for '%s'",
1040                                         hostname, metric, type, id);
1041                         status = -1;
1042                 }
1043         }
1045         ts_tojson(ts, buf);
1046         sdb_object_deref(SDB_OBJ(m));
1047         sdb_timeseries_destroy(ts);
1048         return status;
1049 } /* sdb_store_fetch_timeseries */
1051 int
1052 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
1053                 sdb_store_lookup_cb cb, void *user_data)
1055         sdb_avltree_iter_t *host_iter = NULL;
1056         int status = 0;
1058         if ((! global_store) || (! cb))
1059                 return -1;
1061         if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
1062                 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
1063                 return -1;
1064         }
1066         pthread_rwlock_rdlock(&global_store->host_lock);
1067         host_iter = sdb_avltree_get_iter(global_store->hosts);
1068         if (! host_iter)
1069                 status = -1;
1071         /* has_next returns false if the iterator is NULL */
1072         while (sdb_avltree_iter_has_next(host_iter)) {
1073                 sdb_store_obj_t *host;
1074                 sdb_avltree_iter_t *iter = NULL;
1076                 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1077                 assert(host);
1079                 if (! sdb_store_matcher_matches(filter, host, NULL))
1080                         continue;
1082                 if (type == SDB_SERVICE)
1083                         iter = sdb_avltree_get_iter(HOST(host)->services);
1084                 else if (type == SDB_METRIC)
1085                         iter = sdb_avltree_get_iter(HOST(host)->metrics);
1087                 if (iter) {
1088                         while (sdb_avltree_iter_has_next(iter)) {
1089                                 sdb_store_obj_t *obj;
1090                                 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1091                                 assert(obj);
1093                                 if (sdb_store_matcher_matches(m, obj, filter)) {
1094                                         if (cb(obj, filter, user_data)) {
1095                                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1096                                                                 "an error while scanning");
1097                                                 status = -1;
1098                                                 break;
1099                                         }
1100                                 }
1101                         }
1102                 }
1103                 else if (sdb_store_matcher_matches(m, host, filter)) {
1104                         if (cb(host, filter, user_data)) {
1105                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1106                                                 "an error while scanning");
1107                                 status = -1;
1108                         }
1109                 }
1111                 sdb_avltree_iter_destroy(iter);
1112                 if (status)
1113                         break;
1114         }
1116         sdb_avltree_iter_destroy(host_iter);
1117         pthread_rwlock_unlock(&global_store->host_lock);
1118         return status;
1119 } /* sdb_store_scan */
1121 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */