Code

store: Let sdb_store_scan() pass on filters to callback functions.
[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 <pthread.h>
48 /*
49  * private variables
50  */
52 static sdb_avltree_t *hosts = NULL;
53 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
55 /*
56  * private types
57  */
59 static sdb_type_t sdb_host_type;
60 static sdb_type_t sdb_service_type;
61 static sdb_type_t sdb_metric_type;
62 static sdb_type_t sdb_attribute_type;
64 static int
65 store_obj_init(sdb_object_t *obj, va_list ap)
66 {
67         sdb_store_obj_t *sobj = STORE_OBJ(obj);
69         sobj->type = va_arg(ap, int);
71         sobj->last_update = va_arg(ap, sdb_time_t);
72         sobj->interval = 0;
73         sobj->backends = NULL;
74         sobj->backends_num = 0;
75         sobj->parent = NULL;
76         return 0;
77 } /* store_obj_init */
79 static void
80 store_obj_destroy(sdb_object_t *obj)
81 {
82         sdb_store_obj_t *sobj = STORE_OBJ(obj);
83         size_t i;
85         for (i = 0; i < sobj->backends_num; ++i)
86                 free(sobj->backends[i]);
87         free(sobj->backends);
88         sobj->backends = NULL;
89         sobj->backends_num = 0;
91         if (sobj->parent)
92                 sdb_object_deref(SDB_OBJ(sobj->parent));
93 } /* store_obj_destroy */
95 static int
96 sdb_host_init(sdb_object_t *obj, va_list ap)
97 {
98         sdb_host_t *sobj = HOST(obj);
99         int ret;
101         /* this will consume the first argument (type) of ap */
102         ret = store_obj_init(obj, ap);
103         if (ret)
104                 return ret;
106         sobj->services = sdb_avltree_create();
107         if (! sobj->services)
108                 return -1;
109         sobj->metrics = sdb_avltree_create();
110         if (! sobj->metrics)
111                 return -1;
112         sobj->attributes = sdb_avltree_create();
113         if (! sobj->attributes)
114                 return -1;
115         return 0;
116 } /* sdb_host_init */
118 static void
119 sdb_host_destroy(sdb_object_t *obj)
121         sdb_host_t *sobj = HOST(obj);
122         assert(obj);
124         store_obj_destroy(obj);
126         if (sobj->services)
127                 sdb_avltree_destroy(sobj->services);
128         if (sobj->metrics)
129                 sdb_avltree_destroy(sobj->metrics);
130         if (sobj->attributes)
131                 sdb_avltree_destroy(sobj->attributes);
132 } /* sdb_host_destroy */
134 static int
135 sdb_service_init(sdb_object_t *obj, va_list ap)
137         sdb_service_t *sobj = SVC(obj);
138         int ret;
140         /* this will consume the first argument (type) of ap */
141         ret = store_obj_init(obj, ap);
142         if (ret)
143                 return ret;
145         sobj->attributes = sdb_avltree_create();
146         if (! sobj->attributes)
147                 return -1;
148         return 0;
149 } /* sdb_service_init */
151 static void
152 sdb_service_destroy(sdb_object_t *obj)
154         sdb_service_t *sobj = SVC(obj);
155         assert(obj);
157         store_obj_destroy(obj);
159         if (sobj->attributes)
160                 sdb_avltree_destroy(sobj->attributes);
161 } /* sdb_service_destroy */
163 static int
164 sdb_metric_init(sdb_object_t *obj, va_list ap)
166         sdb_metric_t *sobj = METRIC(obj);
167         int ret;
169         /* this will consume the first argument (type) of ap */
170         ret = store_obj_init(obj, ap);
171         if (ret)
172                 return ret;
174         sobj->attributes = sdb_avltree_create();
175         if (! sobj->attributes)
176                 return -1;
178         sobj->store.type = sobj->store.id = NULL;
179         return 0;
180 } /* sdb_metric_init */
182 static void
183 sdb_metric_destroy(sdb_object_t *obj)
185         sdb_metric_t *sobj = METRIC(obj);
186         assert(obj);
188         store_obj_destroy(obj);
190         if (sobj->attributes)
191                 sdb_avltree_destroy(sobj->attributes);
193         if (sobj->store.type)
194                 free(sobj->store.type);
195         if (sobj->store.id)
196                 free(sobj->store.id);
197 } /* sdb_metric_destroy */
199 static int
200 sdb_attr_init(sdb_object_t *obj, va_list ap)
202         const sdb_data_t *value;
203         int ret;
205         /* this will consume the first two arguments
206          * (type and last_update) of ap */
207         ret = store_obj_init(obj, ap);
208         if (ret)
209                 return ret;
210         value = va_arg(ap, const sdb_data_t *);
212         if (value)
213                 if (sdb_data_copy(&ATTR(obj)->value, value))
214                         return -1;
215         return 0;
216 } /* sdb_attr_init */
218 static void
219 sdb_attr_destroy(sdb_object_t *obj)
221         assert(obj);
223         store_obj_destroy(obj);
224         sdb_data_free_datum(&ATTR(obj)->value);
225 } /* sdb_attr_destroy */
227 static sdb_type_t sdb_host_type = {
228         sizeof(sdb_host_t),
229         sdb_host_init,
230         sdb_host_destroy
231 };
233 static sdb_type_t sdb_service_type = {
234         sizeof(sdb_service_t),
235         sdb_service_init,
236         sdb_service_destroy
237 };
239 static sdb_type_t sdb_metric_type = {
240         sizeof(sdb_metric_t),
241         sdb_metric_init,
242         sdb_metric_destroy
243 };
245 static sdb_type_t sdb_attribute_type = {
246         sizeof(sdb_attribute_t),
247         sdb_attr_init,
248         sdb_attr_destroy
249 };
251 /*
252  * private helper functions
253  */
255 static sdb_host_t *
256 lookup_host(const char *name)
258         return HOST(sdb_avltree_lookup(hosts, name));
259 } /* lookup_host */
261 static int
262 record_backend(sdb_store_obj_t *obj)
264         const sdb_plugin_info_t *info;
265         char **tmp;
266         size_t i;
268         info = sdb_plugin_current();
269         if (! info)
270                 return 0;
272         for (i = 0; i < obj->backends_num; ++i)
273                 if (!strcasecmp(obj->backends[i], info->plugin_name))
274                         return 0;
276         tmp = realloc(obj->backends,
277                         (obj->backends_num + 1) * sizeof(*obj->backends));
278         if (! tmp)
279                 return -1;
281         obj->backends = tmp;
282         obj->backends[obj->backends_num] = strdup(info->plugin_name);
283         if (! obj->backends[obj->backends_num])
284                 return -1;
286         ++obj->backends_num;
287         return 0;
288 } /* record_backend */
290 static int
291 store_obj(sdb_avltree_t *parent_tree, int type, const char *name,
292                 sdb_time_t last_update, sdb_store_obj_t **updated_obj)
294         sdb_store_obj_t *old, *new;
295         int status = 0;
297         assert(parent_tree);
299         if (last_update <= 0)
300                 last_update = sdb_gettime();
302         old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
303         if (old) {
304                 if (old->last_update > last_update) {
305                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
306                                         "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
307                                         SDB_STORE_TYPE_TO_NAME(type), name,
308                                         last_update, old->last_update);
309                         /* don't report an error; the object may be updated by multiple
310                          * backends */
311                         status = 1;
312                 }
313                 else if (old->last_update == last_update) {
314                         /* don't report an error and also don't even log this to avoid
315                          * excessive noise on high sampling frequencies */
316                         status = 1;
317                 }
318                 else {
319                         sdb_time_t interval = last_update - old->last_update;
320                         old->last_update = last_update;
321                         if (interval) {
322                                 if (old->interval)
323                                         old->interval = (sdb_time_t)((0.9 * (double)old->interval)
324                                                         + (0.1 * (double)interval));
325                                 else
326                                         old->interval = interval;
327                         }
328                 }
330                 new = old;
331                 sdb_object_deref(SDB_OBJ(old));
332         }
333         else {
334                 if (type == SDB_ATTRIBUTE) {
335                         /* the value will be updated by the caller */
336                         new = STORE_OBJ(sdb_object_create(name, sdb_attribute_type,
337                                                 type, last_update, NULL));
338                 }
339                 else {
340                         sdb_type_t t;
341                         t = type == SDB_HOST
342                                 ? sdb_host_type
343                                 : type == SDB_SERVICE
344                                         ? sdb_service_type
345                                         : sdb_metric_type;
346                         new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
347                 }
349                 if (new) {
350                         status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
352                         /* pass control to the tree or destroy in case of an error */
353                         sdb_object_deref(SDB_OBJ(new));
354                 }
355                 else {
356                         char errbuf[1024];
357                         sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
358                                         SDB_STORE_TYPE_TO_NAME(type), name,
359                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
360                         status = -1;
361                 }
362         }
364         if (status < 0)
365                 return status;
366         assert(new);
368         if (updated_obj)
369                 *updated_obj = new;
371         if (record_backend(new))
372                 return -1;
373         return status;
374 } /* store_obj */
376 static int
377 store_attr(sdb_avltree_t *attributes, const char *key, const sdb_data_t *value,
378                 sdb_time_t last_update)
380         sdb_store_obj_t *attr = NULL;
381         int status;
383         status = store_obj(attributes, SDB_ATTRIBUTE, key, last_update, &attr);
384         if (status)
385                 return status;
387         /* don't update unchanged values */
388         if (! sdb_data_cmp(&ATTR(attr)->value, value))
389                 return status;
391         assert(attr);
392         if (sdb_data_copy(&ATTR(attr)->value, value))
393                 return -1;
394         return status;
395 } /* store_attr */
397 /* The host_lock has to be acquired before calling this function. */
398 static sdb_avltree_t *
399 get_host_children(const char *hostname, int type)
401         char *cname = NULL;
402         sdb_host_t *host;
404         assert(hostname);
405         assert((type == SDB_SERVICE) || (type == SDB_METRIC)
406                         || (type == SDB_ATTRIBUTE));
408         if (! hosts)
409                 return NULL;
411         cname = sdb_plugin_cname(strdup(hostname));
412         if (! cname) {
413                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
414                 return NULL;
415         }
417         host = lookup_host(cname);
418         free(cname);
419         if (! host)
420                 return NULL;
422         sdb_object_deref(SDB_OBJ(host));
423         if (type == SDB_ATTRIBUTE)
424                 return host->attributes;
425         else if (type == SDB_METRIC)
426                 return host->metrics;
427         else
428                 return host->services;
429 } /* get_host_children */
431 /*
432  * store_common_tojson serializes common object attributes to JSON.
433  *
434  * The function never returns an error. Rather, an error message will be part
435  * of the serialized data.
436  */
437 static void
438 store_common_tojson(sdb_store_obj_t *obj, sdb_strbuf_t *buf)
440         char time_str[64];
441         char interval_str[64];
442         size_t i;
444         /* TODO: make time and interval formats configurable */
445         if (! sdb_strftime(time_str, sizeof(time_str),
446                                 "%F %T %z", obj->last_update))
447                 snprintf(time_str, sizeof(time_str), "<error>");
448         time_str[sizeof(time_str) - 1] = '\0';
450         if (! sdb_strfinterval(interval_str, sizeof(interval_str),
451                                 obj->interval))
452                 snprintf(interval_str, sizeof(interval_str), "<error>");
453         interval_str[sizeof(interval_str) - 1] = '\0';
455         sdb_strbuf_append(buf, "\"last_update\": \"%s\", "
456                         "\"update_interval\": \"%s\", \"backends\": [",
457                         time_str, interval_str);
459         for (i = 0; i < obj->backends_num; ++i) {
460                 sdb_strbuf_append(buf, "\"%s\"", obj->backends[i]);
461                 if (i < obj->backends_num - 1)
462                         sdb_strbuf_append(buf, ",");
463         }
464         sdb_strbuf_append(buf, "]");
465 } /* store_common_tojson */
467 /*
468  * store_obj_tojson serializes attribute / metric / service objects to JSON.
469  *
470  * The function never returns an error. Rather, an error message will be part
471  * of the serialized data.
472  */
473 static void
474 store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf,
475                 sdb_store_matcher_t *filter, int flags)
477         sdb_avltree_iter_t *iter;
479         assert((type == SDB_ATTRIBUTE)
480                         || (type == SDB_METRIC)
481                         || (type == SDB_SERVICE));
483         sdb_strbuf_append(buf, "[");
484         iter = sdb_avltree_get_iter(tree);
485         if (! iter) {
486                 char errbuf[1024];
487                 sdb_log(SDB_LOG_ERR, "store: Failed to retrieve %ss: %s\n",
488                                 SDB_STORE_TYPE_TO_NAME(type),
489                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
490                 sdb_strbuf_append(buf, "{\"error\": \"failed to retrieve %ss: %s\"}",
491                                 SDB_STORE_TYPE_TO_NAME(type), errbuf);
492         }
494         /* has_next returns false if the iterator is NULL */
495         while (sdb_avltree_iter_has_next(iter)) {
496                 sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
497                 assert(sobj);
498                 assert(sobj->type == type);
500                 if (filter && (! sdb_store_matcher_matches(filter, sobj, NULL)))
501                         continue;
503                 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(sobj)->name);
504                 if (sobj->type == SDB_ATTRIBUTE) {
505                         char tmp[sdb_data_strlen(&ATTR(sobj)->value) + 1];
506                         sdb_data_format(&ATTR(sobj)->value, tmp, sizeof(tmp),
507                                         SDB_DOUBLE_QUOTED);
508                         sdb_strbuf_append(buf, "\"value\": %s, ", tmp);
509                 }
510                 store_common_tojson(sobj, buf);
512                 if ((sobj->type == SDB_SERVICE)
513                                 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
514                         sdb_strbuf_append(buf, ", \"attributes\": ");
515                         store_obj_tojson(SVC(sobj)->attributes, SDB_ATTRIBUTE,
516                                         buf, filter, flags);
517                 }
518                 else if ((sobj->type == SDB_METRIC)
519                                 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
520                         sdb_strbuf_append(buf, ", \"attributes\": ");
521                         store_obj_tojson(METRIC(sobj)->attributes, SDB_ATTRIBUTE,
522                                         buf, filter, flags);
523                 }
524                 sdb_strbuf_append(buf, "}");
526                 if (sdb_avltree_iter_has_next(iter))
527                         sdb_strbuf_append(buf, ",");
528         }
530         sdb_avltree_iter_destroy(iter);
531         sdb_strbuf_append(buf, "]");
532 } /* store_obj_tojson */
534 /*
535  * ts_tojson serializes a time-series to JSON.
536  *
537  * The function never returns an error. Rather, an error message will be part
538  * of the serialized data.
539  */
540 static void
541 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
543         char start_str[64];
544         char end_str[64];
546         size_t i;
548         /* TODO: make time format configurable */
549         if (! sdb_strftime(start_str, sizeof(start_str),
550                                 "%F %T %z", ts->start))
551                 snprintf(start_str, sizeof(start_str), "<error>");
552         start_str[sizeof(start_str) - 1] = '\0';
553         if (! sdb_strftime(end_str, sizeof(end_str),
554                                 "%F %T %z", ts->end))
555                 snprintf(end_str, sizeof(end_str), "<error>");
556         end_str[sizeof(end_str) - 1] = '\0';
558         sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
559                         start_str, end_str);
561         for (i = 0; i < ts->data_names_len; ++i) {
562                 size_t j;
563                 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
565                 for (j = 0; j < ts->data_len; ++j) {
566                         char time_str[64];
568                         if (! sdb_strftime(time_str, sizeof(time_str),
569                                                 "%F %T %z", ts->data[i][j].timestamp))
570                                 snprintf(time_str, sizeof(time_str), "<error>");
571                         time_str[sizeof(time_str) - 1] = '\0';
573                         sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
574                                         "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
576                         if (j < ts->data_len - 1)
577                                 sdb_strbuf_append(buf, ",");
578                 }
580                 if (i < ts->data_names_len - 1)
581                         sdb_strbuf_append(buf, "],");
582                 else
583                         sdb_strbuf_append(buf, "]");
584         }
585         sdb_strbuf_append(buf, "}}");
586 } /* ts_tojson */
588 /*
589  * public API
590  */
592 void
593 sdb_store_clear(void)
595         sdb_avltree_destroy(hosts);
596         hosts = NULL;
597 } /* sdb_store_clear */
599 int
600 sdb_store_host(const char *name, sdb_time_t last_update)
602         char *cname = NULL;
603         int status = 0;
605         if (! name)
606                 return -1;
608         cname = sdb_plugin_cname(strdup(name));
609         if (! cname) {
610                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
611                 return -1;
612         }
614         pthread_rwlock_wrlock(&host_lock);
615         if (! hosts)
616                 if (! (hosts = sdb_avltree_create()))
617                         status = -1;
619         if (! status)
620                 status = store_obj(hosts, SDB_HOST, cname, last_update, NULL);
621         pthread_rwlock_unlock(&host_lock);
623         free(cname);
624         return status;
625 } /* sdb_store_host */
627 _Bool
628 sdb_store_has_host(const char *name)
630         sdb_host_t *host;
632         if (! name)
633                 return NULL;
635         host = lookup_host(name);
636         return host != NULL;
637 } /* sdb_store_has_host */
639 sdb_store_obj_t *
640 sdb_store_get_host(const char *name)
642         sdb_host_t *host;
644         if (! name)
645                 return NULL;
647         host = lookup_host(name);
648         if (! host)
649                 return NULL;
651         return STORE_OBJ(host);
652 } /* sdb_store_get_host */
654 int
655 sdb_store_attribute(const char *hostname,
656                 const char *key, const sdb_data_t *value,
657                 sdb_time_t last_update)
659         sdb_avltree_t *attrs;
660         int status = 0;
662         if ((! hostname) || (! key))
663                 return -1;
665         pthread_rwlock_wrlock(&host_lock);
666         attrs = get_host_children(hostname, SDB_ATTRIBUTE);
667         if (! attrs) {
668                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
669                                 "host '%s' not found", key, hostname);
670                 status = -1;
671         }
673         if (! status)
674                 status = store_attr(attrs, key, value, last_update);
676         pthread_rwlock_unlock(&host_lock);
677         return status;
678 } /* sdb_store_attribute */
680 int
681 sdb_store_service(const char *hostname, const char *name,
682                 sdb_time_t last_update)
684         sdb_avltree_t *services;
686         int status = 0;
688         if ((! hostname) || (! name))
689                 return -1;
691         pthread_rwlock_wrlock(&host_lock);
692         services = get_host_children(hostname, SDB_SERVICE);
693         if (! services) {
694                 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
695                                 "host '%s' not found", name, hostname);
696                 status = -1;
697         }
699         if (! status)
700                 status = store_obj(services, SDB_SERVICE, name, last_update, NULL);
701         pthread_rwlock_unlock(&host_lock);
702         return status;
703 } /* sdb_store_service */
705 int
706 sdb_store_service_attr(const char *hostname, const char *service,
707                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
709         sdb_avltree_t *services;
710         sdb_service_t *svc;
711         int status = 0;
713         if ((! hostname) || (! service) || (! key))
714                 return -1;
716         pthread_rwlock_wrlock(&host_lock);
717         services = get_host_children(hostname, SDB_SERVICE);
718         if (! services) {
719                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
720                                 "for service '%s' - host '%ss' not found",
721                                 key, service, hostname);
722                 pthread_rwlock_unlock(&host_lock);
723                 return -1;
724         }
726         svc = SVC(sdb_avltree_lookup(services, service));
727         if (! svc) {
728                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
729                                 "service '%s/%s' not found", key, hostname, service);
730                 status = -1;
731         }
733         if (! status)
734                 status = store_attr(svc->attributes, key, value, last_update);
736         sdb_object_deref(SDB_OBJ(svc));
737         pthread_rwlock_unlock(&host_lock);
738         return status;
739 } /* sdb_store_service_attr */
741 int
742 sdb_store_metric(const char *hostname, const char *name,
743                 sdb_metric_store_t *store, sdb_time_t last_update)
745         sdb_store_obj_t *obj = NULL;
746         sdb_metric_t *metric;
748         sdb_avltree_t *metrics;
750         int status = 0;
752         if ((! hostname) || (! name))
753                 return -1;
754         if (store && ((! store->type) || (! store->id)))
755                 return -1;
757         pthread_rwlock_wrlock(&host_lock);
758         metrics = get_host_children(hostname, SDB_METRIC);
759         if (! metrics) {
760                 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
761                                 "host '%s' not found", name, hostname);
762                 status = -1;
763         }
765         if (! status)
766                 status = store_obj(metrics, SDB_METRIC, name, last_update, &obj);
768         if (status || (! store)) {
769                 pthread_rwlock_unlock(&host_lock);
770                 return status;
771         }
773         assert(obj);
774         metric = METRIC(obj);
776         if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
777                 if (metric->store.type)
778                         free(metric->store.type);
779                 metric->store.type = strdup(store->type);
780         }
781         if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
782                 if (metric->store.id)
783                         free(metric->store.id);
784                 metric->store.id = strdup(store->id);
785         }
787         if ((! metric->store.type) || (! metric->store.id)) {
788                 if (metric->store.type)
789                         free(metric->store.type);
790                 if (metric->store.id)
791                         free(metric->store.id);
792                 metric->store.type = metric->store.id = NULL;
793                 status = -1;
794         }
795         pthread_rwlock_unlock(&host_lock);
796         return status;
797 } /* sdb_store_metric */
799 int
800 sdb_store_metric_attr(const char *hostname, const char *metric,
801                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
803         sdb_avltree_t *metrics;
804         sdb_metric_t *m;
805         int status = 0;
807         if ((! hostname) || (! metric) || (! key))
808                 return -1;
810         pthread_rwlock_wrlock(&host_lock);
811         metrics = get_host_children(hostname, SDB_METRIC);
812         if (! metrics) {
813                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
814                                 "for metric '%s' - host '%s' not found",
815                                 key, metric, hostname);
816                 pthread_rwlock_unlock(&host_lock);
817                 return -1;
818         }
820         m = METRIC(sdb_avltree_lookup(metrics, metric));
821         if (! m) {
822                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
823                                 "metric '%s/%s' not found", key, hostname, metric);
824                 status = -1;
825         }
827         if (! status)
828                 status = store_attr(m->attributes, key, value, last_update);
830         sdb_object_deref(SDB_OBJ(m));
831         pthread_rwlock_unlock(&host_lock);
832         return status;
833 } /* sdb_store_metric_attr */
835 int
836 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
837                 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
839         sdb_avltree_t *metrics;
840         sdb_metric_t *m;
842         sdb_timeseries_t *ts;
844         if ((! hostname) || (! metric) || (! opts) || (! buf))
845                 return -1;
847         pthread_rwlock_rdlock(&host_lock);
848         metrics = get_host_children(hostname, SDB_METRIC);
849         if (! metrics) {
850                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
851                                 "- host '%s' not found", hostname, metric, hostname);
852                 pthread_rwlock_unlock(&host_lock);
853                 return -1;
854         }
856         m = METRIC(sdb_avltree_lookup(metrics, metric));
857         if (! m) {
858                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
859                                 "- metric '%s' not found", hostname, metric, metric);
860                 pthread_rwlock_unlock(&host_lock);
861                 return -1;
862         }
864         if ((! m->store.type) || (! m->store.id)) {
865                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
866                                 "- no data-store configured for the stored metric",
867                                 hostname, metric);
868                 pthread_rwlock_unlock(&host_lock);
869                 return -1;
870         }
872         {
873                 char type[strlen(m->store.type) + 1];
874                 char id[strlen(m->store.id) + 1];
876                 strncpy(type, m->store.type, sizeof(type));
877                 strncpy(id, m->store.id, sizeof(id));
878                 pthread_rwlock_unlock(&host_lock);
880                 ts = sdb_plugin_fetch_timeseries(type, id, opts);
881                 if (! ts) {
882                         sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
883                                         "- %s fetcher callback returned no data for '%s'",
884                                         hostname, metric, type, id);
885                         return -1;
886                 }
887         }
889         ts_tojson(ts, buf);
890         sdb_timeseries_destroy(ts);
891         return 0;
892 } /* sdb_store_fetch_timeseries */
894 int
895 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
897         sdb_data_t tmp;
899         if (! obj)
900                 return -1;
902         switch (field) {
903                 case SDB_FIELD_NAME:
904                         tmp.type = SDB_TYPE_STRING;
905                         tmp.data.string = strdup(SDB_OBJ(obj)->name);
906                         if (! tmp.data.string)
907                                 return -1;
908                         break;
909                 case SDB_FIELD_LAST_UPDATE:
910                         tmp.type = SDB_TYPE_DATETIME;
911                         tmp.data.datetime = obj->last_update;
912                         break;
913                 case SDB_FIELD_AGE:
914                         tmp.type = SDB_TYPE_DATETIME;
915                         tmp.data.datetime = sdb_gettime() - obj->last_update;
916                         break;
917                 case SDB_FIELD_INTERVAL:
918                         tmp.type = SDB_TYPE_DATETIME;
919                         tmp.data.datetime = obj->interval;
920                         break;
921                 case SDB_FIELD_BACKEND:
922                 {
923                         tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
924                         tmp.data.array.length = obj->backends_num;
925                         tmp.data.array.values = obj->backends;
926                         return sdb_data_copy(res, &tmp);
927                         break;
928                 }
929                 default:
930                         return -1;
931         }
932         if (res)
933                 *res = tmp;
934         else
935                 sdb_data_free_datum(&tmp);
936         return 0;
937 } /* sdb_store_get_field */
939 int
940 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
941                 sdb_store_matcher_t *filter)
943         sdb_avltree_t *tree = NULL;
944         sdb_store_obj_t *attr;
946         if ((! obj) || (! name))
947                 return -1;
949         if (obj->type == SDB_HOST)
950                 tree = HOST(obj)->attributes;
951         else if (obj->type == SDB_SERVICE)
952                 tree = SVC(obj)->attributes;
953         else if (obj->type == SDB_METRIC)
954                 tree = METRIC(obj)->attributes;
956         if (! tree)
957                 return -1;
959         attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
960         if (! attr)
961                 return -1;
962         if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
963                 sdb_object_deref(SDB_OBJ(attr));
964                 return -1;
965         }
967         assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
968         if (res)
969                 sdb_data_copy(res, &ATTR(attr)->value);
970         sdb_object_deref(SDB_OBJ(attr));
971         return 0;
972 } /* sdb_store_get_attr */
974 int
975 sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
976                 sdb_store_matcher_t *filter, int flags)
978         sdb_host_t *host = HOST(h);
980         if ((! h) || (h->type != SDB_HOST) || (! buf))
981                 return -1;
983         /* This function ignores SKIP_EMPTY flags given that the current
984          * implementation sucks and it's nut currently used when calling this
985          * function directly. */
987         sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name);
988         store_common_tojson(h, buf);
990         if (! (flags & SDB_SKIP_ATTRIBUTES)) {
991                 sdb_strbuf_append(buf, ", \"attributes\": ");
992                 store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, filter, flags);
993         }
995         if (! (flags & SDB_SKIP_METRICS)) {
996                 sdb_strbuf_append(buf, ", \"metrics\": ");
997                 store_obj_tojson(host->metrics, SDB_METRIC, buf, filter, flags);
998         }
1000         if (! (flags & SDB_SKIP_SERVICES)) {
1001                 sdb_strbuf_append(buf, ", \"services\": ");
1002                 store_obj_tojson(host->services, SDB_SERVICE, buf, filter, flags);
1003         }
1005         sdb_strbuf_append(buf, "}");
1006         return 0;
1007 } /* sdb_store_host_tojson */
1009 static _Bool
1010 has_children(sdb_avltree_t *tree, sdb_store_matcher_t *filter)
1012         sdb_avltree_iter_t *iter;
1014         if (! filter)
1015                 return sdb_avltree_size(tree) > 0;
1017         iter = sdb_avltree_get_iter(tree);
1018         while (sdb_avltree_iter_has_next(iter)) {
1019                 sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1020                 if (sdb_store_matcher_matches(filter, sobj, NULL)) {
1021                         sdb_avltree_iter_destroy(iter);
1022                         return 1;
1023                 }
1024         }
1025         sdb_avltree_iter_destroy(iter);
1026         return 0;
1027 } /* has_children */
1029 int
1030 sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
1032         sdb_avltree_iter_t *host_iter;
1033         size_t len;
1035         if (! buf)
1036                 return -1;
1038         pthread_rwlock_rdlock(&host_lock);
1040         host_iter = sdb_avltree_get_iter(hosts);
1041         if (! host_iter) {
1042                 pthread_rwlock_unlock(&host_lock);
1043                 return -1;
1044         }
1046         sdb_strbuf_append(buf, "[");
1048         len = sdb_strbuf_len(buf);
1049         while (sdb_avltree_iter_has_next(host_iter)) {
1050                 sdb_store_obj_t *host;
1052                 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1053                 assert(host);
1055                 if (filter && (! sdb_store_matcher_matches(filter, host, NULL)))
1056                         continue;
1058                 /*
1059                  * XXX: This approach sucks but it's the best we can do at the moment.
1060                  * In the future, all store lookups should be split into multiple
1061                  * steps instead: first, retrieve all relevant objects and apply all
1062                  * pre-processing operations and then format it for the wire.
1063                  */
1064                 if ((flags & SDB_SKIP_EMPTY_SERVICES)
1065                                 && (! has_children(HOST(host)->services, filter)))
1066                         continue;
1067                 if ((flags & SDB_SKIP_EMPTY_METRICS)
1068                                 && (! has_children(HOST(host)->metrics, filter)))
1069                         continue;
1071                 if (sdb_strbuf_len(buf) > len)
1072                         sdb_strbuf_append(buf, ",");
1073                 len = sdb_strbuf_len(buf);
1075                 if (sdb_store_host_tojson(host, buf, filter, flags))
1076                         return -1;
1077         }
1079         sdb_strbuf_append(buf, "]");
1081         sdb_avltree_iter_destroy(host_iter);
1082         pthread_rwlock_unlock(&host_lock);
1083         return 0;
1084 } /* sdb_store_tojson */
1086 int
1087 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
1088                 sdb_store_lookup_cb cb, void *user_data)
1090         sdb_avltree_iter_t *host_iter;
1091         int status = 0;
1093         if (! cb)
1094                 return -1;
1096         if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC))
1097                 return -1;
1099         pthread_rwlock_rdlock(&host_lock);
1101         host_iter = sdb_avltree_get_iter(hosts);
1102         if (! host_iter)
1103                 status = -1;
1105         /* has_next returns false if the iterator is NULL */
1106         while (sdb_avltree_iter_has_next(host_iter)) {
1107                 sdb_store_obj_t *host;
1108                 sdb_avltree_iter_t *iter = NULL;
1110                 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1111                 assert(host);
1113                 if (! sdb_store_matcher_matches(filter, host, NULL))
1114                         continue;
1116                 if (type == SDB_SERVICE)
1117                         iter = sdb_avltree_get_iter(HOST(host)->services);
1118                 else if (type == SDB_METRIC)
1119                         iter = sdb_avltree_get_iter(HOST(host)->metrics);
1121                 if (iter) {
1122                         while (sdb_avltree_iter_has_next(iter)) {
1123                                 sdb_store_obj_t *obj;
1124                                 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1125                                 assert(obj);
1127                                 if (sdb_store_matcher_matches(m, obj, filter)) {
1128                                         if (cb(obj, filter, user_data)) {
1129                                                 status = -1;
1130                                                 break;
1131                                         }
1132                                 }
1133                         }
1134                 }
1135                 else if (sdb_store_matcher_matches(m, host, filter)) {
1136                         if (cb(host, filter, user_data))
1137                                 status = -1;
1138                 }
1140                 sdb_avltree_iter_destroy(iter);
1141                 if (status)
1142                         break;
1143         }
1145         sdb_avltree_iter_destroy(host_iter);
1146         pthread_rwlock_unlock(&host_lock);
1147         return status;
1148 } /* sdb_store_scan */
1150 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */