Code

data: Use a predefined format for sdb_strftime().
[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 static sdb_avltree_t *hosts = NULL;
54 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
56 /*
57  * private types
58  */
60 static sdb_type_t sdb_host_type;
61 static sdb_type_t sdb_service_type;
62 static sdb_type_t sdb_metric_type;
63 static sdb_type_t sdb_attribute_type;
65 static int
66 store_obj_init(sdb_object_t *obj, va_list ap)
67 {
68         sdb_store_obj_t *sobj = STORE_OBJ(obj);
70         sobj->type = va_arg(ap, int);
72         sobj->last_update = va_arg(ap, sdb_time_t);
73         sobj->interval = 0;
74         sobj->backends = NULL;
75         sobj->backends_num = 0;
76         sobj->parent = NULL;
77         return 0;
78 } /* store_obj_init */
80 static void
81 store_obj_destroy(sdb_object_t *obj)
82 {
83         sdb_store_obj_t *sobj = STORE_OBJ(obj);
84         size_t i;
86         for (i = 0; i < sobj->backends_num; ++i)
87                 free(sobj->backends[i]);
88         free(sobj->backends);
89         sobj->backends = NULL;
90         sobj->backends_num = 0;
92         // We don't currently keep an extra reference for parent objects to
93         // avoid circular self-references which are not handled correctly by
94         // the ref-count base management layer.
95         //sdb_object_deref(SDB_OBJ(sobj->parent));
96 } /* store_obj_destroy */
98 static int
99 sdb_host_init(sdb_object_t *obj, va_list ap)
101         sdb_host_t *sobj = HOST(obj);
102         int ret;
104         /* this will consume the first argument (type) of ap */
105         ret = store_obj_init(obj, ap);
106         if (ret)
107                 return ret;
109         sobj->services = sdb_avltree_create();
110         if (! sobj->services)
111                 return -1;
112         sobj->metrics = sdb_avltree_create();
113         if (! sobj->metrics)
114                 return -1;
115         sobj->attributes = sdb_avltree_create();
116         if (! sobj->attributes)
117                 return -1;
118         return 0;
119 } /* sdb_host_init */
121 static void
122 sdb_host_destroy(sdb_object_t *obj)
124         sdb_host_t *sobj = HOST(obj);
125         assert(obj);
127         store_obj_destroy(obj);
129         if (sobj->services)
130                 sdb_avltree_destroy(sobj->services);
131         if (sobj->metrics)
132                 sdb_avltree_destroy(sobj->metrics);
133         if (sobj->attributes)
134                 sdb_avltree_destroy(sobj->attributes);
135 } /* sdb_host_destroy */
137 static int
138 sdb_service_init(sdb_object_t *obj, va_list ap)
140         sdb_service_t *sobj = SVC(obj);
141         int ret;
143         /* this will consume the first argument (type) of ap */
144         ret = store_obj_init(obj, ap);
145         if (ret)
146                 return ret;
148         sobj->attributes = sdb_avltree_create();
149         if (! sobj->attributes)
150                 return -1;
151         return 0;
152 } /* sdb_service_init */
154 static void
155 sdb_service_destroy(sdb_object_t *obj)
157         sdb_service_t *sobj = SVC(obj);
158         assert(obj);
160         store_obj_destroy(obj);
162         if (sobj->attributes)
163                 sdb_avltree_destroy(sobj->attributes);
164 } /* sdb_service_destroy */
166 static int
167 sdb_metric_init(sdb_object_t *obj, va_list ap)
169         sdb_metric_t *sobj = METRIC(obj);
170         int ret;
172         /* this will consume the first argument (type) of ap */
173         ret = store_obj_init(obj, ap);
174         if (ret)
175                 return ret;
177         sobj->attributes = sdb_avltree_create();
178         if (! sobj->attributes)
179                 return -1;
181         sobj->store.type = sobj->store.id = NULL;
182         return 0;
183 } /* sdb_metric_init */
185 static void
186 sdb_metric_destroy(sdb_object_t *obj)
188         sdb_metric_t *sobj = METRIC(obj);
189         assert(obj);
191         store_obj_destroy(obj);
193         if (sobj->attributes)
194                 sdb_avltree_destroy(sobj->attributes);
196         if (sobj->store.type)
197                 free(sobj->store.type);
198         if (sobj->store.id)
199                 free(sobj->store.id);
200 } /* sdb_metric_destroy */
202 static int
203 sdb_attr_init(sdb_object_t *obj, va_list ap)
205         const sdb_data_t *value;
206         int ret;
208         /* this will consume the first two arguments
209          * (type and last_update) of ap */
210         ret = store_obj_init(obj, ap);
211         if (ret)
212                 return ret;
213         value = va_arg(ap, const sdb_data_t *);
215         if (value)
216                 if (sdb_data_copy(&ATTR(obj)->value, value))
217                         return -1;
218         return 0;
219 } /* sdb_attr_init */
221 static void
222 sdb_attr_destroy(sdb_object_t *obj)
224         assert(obj);
226         store_obj_destroy(obj);
227         sdb_data_free_datum(&ATTR(obj)->value);
228 } /* sdb_attr_destroy */
230 static sdb_type_t sdb_host_type = {
231         sizeof(sdb_host_t),
232         sdb_host_init,
233         sdb_host_destroy
234 };
236 static sdb_type_t sdb_service_type = {
237         sizeof(sdb_service_t),
238         sdb_service_init,
239         sdb_service_destroy
240 };
242 static sdb_type_t sdb_metric_type = {
243         sizeof(sdb_metric_t),
244         sdb_metric_init,
245         sdb_metric_destroy
246 };
248 static sdb_type_t sdb_attribute_type = {
249         sizeof(sdb_attribute_t),
250         sdb_attr_init,
251         sdb_attr_destroy
252 };
254 /*
255  * private helper functions
256  */
258 static sdb_host_t *
259 lookup_host(const char *name, bool canonicalize)
261         sdb_host_t *host;
262         char *cname;
264         assert(name);
265         if (! canonicalize)
266                 return HOST(sdb_avltree_lookup(hosts, name));
268         cname = strdup(name);
269         cname = sdb_plugin_cname(cname);
270         if (! cname) {
271                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
272                 return NULL;
273         }
275         host = HOST(sdb_avltree_lookup(hosts, cname));
276         free(cname);
277         return host;
278 } /* lookup_host */
280 static int
281 record_backend(sdb_store_obj_t *obj)
283         const sdb_plugin_info_t *info;
284         char **tmp;
285         size_t i;
287         info = sdb_plugin_current();
288         if (! info)
289                 return 0;
291         for (i = 0; i < obj->backends_num; ++i)
292                 if (!strcasecmp(obj->backends[i], info->plugin_name))
293                         return 0;
295         tmp = realloc(obj->backends,
296                         (obj->backends_num + 1) * sizeof(*obj->backends));
297         if (! tmp)
298                 return -1;
300         obj->backends = tmp;
301         obj->backends[obj->backends_num] = strdup(info->plugin_name);
302         if (! obj->backends[obj->backends_num])
303                 return -1;
305         ++obj->backends_num;
306         return 0;
307 } /* record_backend */
309 static int
310 store_obj(sdb_store_obj_t *parent, sdb_avltree_t *parent_tree,
311                 int type, const char *name, sdb_time_t last_update,
312                 sdb_store_obj_t **updated_obj)
314         sdb_store_obj_t *old, *new;
315         int status = 0;
317         assert(parent_tree);
319         if (last_update <= 0)
320                 last_update = sdb_gettime();
322         old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
323         if (old) {
324                 if (old->last_update > last_update) {
325                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
326                                         "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
327                                         SDB_STORE_TYPE_TO_NAME(type), name,
328                                         last_update, old->last_update);
329                         /* don't report an error; the object may be updated by multiple
330                          * backends */
331                         status = 1;
332                 }
333                 else if (old->last_update == last_update) {
334                         /* don't report an error and also don't even log this to avoid
335                          * excessive noise on high sampling frequencies */
336                         status = 1;
337                 }
338                 else {
339                         sdb_time_t interval = last_update - old->last_update;
340                         old->last_update = last_update;
341                         if (interval) {
342                                 if (old->interval)
343                                         old->interval = (sdb_time_t)((0.9 * (double)old->interval)
344                                                         + (0.1 * (double)interval));
345                                 else
346                                         old->interval = interval;
347                         }
348                 }
350                 new = old;
351                 sdb_object_deref(SDB_OBJ(old));
352         }
353         else {
354                 if (type == SDB_ATTRIBUTE) {
355                         /* the value will be updated by the caller */
356                         new = STORE_OBJ(sdb_object_create(name, sdb_attribute_type,
357                                                 type, last_update, NULL));
358                 }
359                 else {
360                         sdb_type_t t;
361                         t = type == SDB_HOST
362                                 ? sdb_host_type
363                                 : type == SDB_SERVICE
364                                         ? sdb_service_type
365                                         : sdb_metric_type;
366                         new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
367                 }
369                 if (new) {
370                         status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
372                         /* pass control to the tree or destroy in case of an error */
373                         sdb_object_deref(SDB_OBJ(new));
374                 }
375                 else {
376                         char errbuf[1024];
377                         sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
378                                         SDB_STORE_TYPE_TO_NAME(type), name,
379                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
380                         status = -1;
381                 }
382         }
384         if (status < 0)
385                 return status;
386         assert(new);
388         if (new->parent != parent) {
389                 // Avoid circular self-references which are not handled
390                 // correctly by the ref-count based management layer.
391                 //sdb_object_deref(SDB_OBJ(new->parent));
392                 //sdb_object_ref(SDB_OBJ(parent));
393                 new->parent = parent;
394         }
396         if (updated_obj)
397                 *updated_obj = new;
399         if (record_backend(new))
400                 return -1;
401         return status;
402 } /* store_obj */
404 static int
405 store_attr(sdb_store_obj_t *parent, sdb_avltree_t *attributes,
406                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
408         sdb_store_obj_t *attr = NULL;
409         int status;
411         status = store_obj(parent, attributes, SDB_ATTRIBUTE,
412                         key, last_update, &attr);
413         if (status)
414                 return status;
416         /* don't update unchanged values */
417         if (! sdb_data_cmp(&ATTR(attr)->value, value))
418                 return status;
420         assert(attr);
421         if (sdb_data_copy(&ATTR(attr)->value, value))
422                 return -1;
423         return status;
424 } /* store_attr */
426 static int
427 store_metric_store(sdb_metric_t *metric, sdb_metric_store_t *store)
429         char *type = metric->store.type;
430         char *id = metric->store.id;
432         if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
433                 if (! (type = strdup(store->type)))
434                         return -1;
435         }
436         if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
437                 if (! (id = strdup(store->id))) {
438                         if (type != metric->store.type)
439                                 free(type);
440                         return -1;
441                 }
442         }
444         if (type != metric->store.type) {
445                 if (metric->store.type)
446                         free(metric->store.type);
447                 metric->store.type = type;
448         }
449         if (id != metric->store.id) {
450                 if (metric->store.id)
451                         free(metric->store.id);
452                 metric->store.id = id;
453         }
454         return 0;
455 } /* store_metric_store */
457 /* The host_lock has to be acquired before calling this function. */
458 static sdb_avltree_t *
459 get_host_children(sdb_host_t *host, int type)
461         if ((type != SDB_SERVICE) && (type != SDB_METRIC)
462                         && (type != SDB_ATTRIBUTE))
463                 return NULL;
465         if (! host)
466                 return NULL;
468         if (type == SDB_ATTRIBUTE)
469                 return host->attributes;
470         else if (type == SDB_METRIC)
471                 return host->metrics;
472         else
473                 return host->services;
474 } /* get_host_children */
476 /*
477  * ts_tojson serializes a time-series to JSON.
478  *
479  * The function never returns an error. Rather, an error message will be part
480  * of the serialized data.
481  */
482 static void
483 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
485         char start_str[64];
486         char end_str[64];
488         size_t i;
490         /* TODO: make time format configurable */
491         if (! sdb_strftime(start_str, sizeof(start_str), ts->start))
492                 snprintf(start_str, sizeof(start_str), "<error>");
493         start_str[sizeof(start_str) - 1] = '\0';
494         if (! sdb_strftime(end_str, sizeof(end_str), ts->end))
495                 snprintf(end_str, sizeof(end_str), "<error>");
496         end_str[sizeof(end_str) - 1] = '\0';
498         sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
499                         start_str, end_str);
501         for (i = 0; i < ts->data_names_len; ++i) {
502                 size_t j;
503                 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
505                 for (j = 0; j < ts->data_len; ++j) {
506                         char time_str[64];
508                         if (! sdb_strftime(time_str, sizeof(time_str), ts->data[i][j].timestamp))
509                                 snprintf(time_str, sizeof(time_str), "<error>");
510                         time_str[sizeof(time_str) - 1] = '\0';
512                         /* Some GNU libc versions may print '-nan' which we dont' want */
513                         if (isnan(ts->data[i][j].value))
514                                 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
515                                                 "\"value\": \"nan\"}", time_str);
516                         else
517                                 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
518                                                 "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
520                         if (j < ts->data_len - 1)
521                                 sdb_strbuf_append(buf, ",");
522                 }
524                 if (i < ts->data_names_len - 1)
525                         sdb_strbuf_append(buf, "],");
526                 else
527                         sdb_strbuf_append(buf, "]");
528         }
529         sdb_strbuf_append(buf, "}}");
530 } /* ts_tojson */
532 /*
533  * public API
534  */
536 void
537 sdb_store_clear(void)
539         sdb_avltree_destroy(hosts);
540         hosts = NULL;
541 } /* sdb_store_clear */
543 int
544 sdb_store_host(const char *name, sdb_time_t last_update)
546         char *cname = NULL;
547         int status = 0;
549         if (! name)
550                 return -1;
552         cname = sdb_plugin_cname(strdup(name));
553         if (! cname) {
554                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
555                 return -1;
556         }
558         pthread_rwlock_wrlock(&host_lock);
559         if (! hosts)
560                 if (! (hosts = sdb_avltree_create()))
561                         status = -1;
563         if (! status)
564                 status = store_obj(NULL, hosts, SDB_HOST, cname, last_update, NULL);
565         pthread_rwlock_unlock(&host_lock);
567         if (sdb_plugin_store_host(name, last_update))
568                 status = -1;
570         free(cname);
571         return status;
572 } /* sdb_store_host */
574 bool
575 sdb_store_has_host(const char *name)
577         sdb_host_t *host;
579         if (! name)
580                 return NULL;
582         host = lookup_host(name, /* canonicalize = */ 0);
583         sdb_object_deref(SDB_OBJ(host));
584         return host != NULL;
585 } /* sdb_store_has_host */
587 sdb_store_obj_t *
588 sdb_store_get_host(const char *name)
590         sdb_host_t *host;
592         if (! name)
593                 return NULL;
595         host = lookup_host(name, /* canonicalize = */ 0);
596         if (! host)
597                 return NULL;
599         return STORE_OBJ(host);
600 } /* sdb_store_get_host */
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 ((! hostname) || (! key))
612                 return -1;
614         pthread_rwlock_wrlock(&host_lock);
615         host = lookup_host(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(&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_service(const char *hostname, const char *name,
636                 sdb_time_t last_update)
638         sdb_host_t *host;
639         sdb_avltree_t *services;
640         sdb_data_t d;
642         int status = 0;
644         if ((! hostname) || (! name))
645                 return -1;
647         pthread_rwlock_wrlock(&host_lock);
648         host = lookup_host(hostname, /* canonicalize = */ 1);
649         services = get_host_children(host, SDB_SERVICE);
650         if (! services) {
651                 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
652                                 "host '%s' not found", name, hostname);
653                 status = -1;
654         }
656         if (! status)
657                 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
658                                 name, last_update, NULL);
660         sdb_object_deref(SDB_OBJ(host));
661         pthread_rwlock_unlock(&host_lock);
663         if (status)
664                 return status;
666         if (sdb_plugin_store_service(hostname, name, last_update))
667                 status = -1;
669         /* record the hostname as an attribute */
670         d.type = SDB_TYPE_STRING;
671         d.data.string = SDB_OBJ(host)->name;
672         if (sdb_store_service_attr(hostname, name, "hostname", &d, last_update))
673                 status = -1;
674         return status;
675 } /* sdb_store_service */
677 int
678 sdb_store_service_attr(const char *hostname, const char *service,
679                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
681         sdb_host_t *host;
682         sdb_service_t *svc;
683         sdb_avltree_t *services;
684         int status = 0;
686         if ((! hostname) || (! service) || (! key))
687                 return -1;
689         pthread_rwlock_wrlock(&host_lock);
690         host = lookup_host(hostname, /* canonicalize = */ 1);
691         services = get_host_children(host, SDB_SERVICE);
692         sdb_object_deref(SDB_OBJ(host));
693         if (! services) {
694                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
695                                 "for service '%s' - host '%ss' not found",
696                                 key, service, hostname);
697                 pthread_rwlock_unlock(&host_lock);
698                 return -1;
699         }
701         svc = SVC(sdb_avltree_lookup(services, service));
702         if (! svc) {
703                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
704                                 "service '%s/%s' not found", key, hostname, service);
705                 status = -1;
706         }
708         if (! status)
709                 status = store_attr(STORE_OBJ(svc), svc->attributes,
710                                 key, value, last_update);
712         sdb_object_deref(SDB_OBJ(svc));
713         pthread_rwlock_unlock(&host_lock);
715         if (sdb_plugin_store_service_attribute(hostname, service,
716                                 key, value, last_update))
717                 status = -1;
718         return status;
719 } /* sdb_store_service_attr */
721 int
722 sdb_store_metric(const char *hostname, const char *name,
723                 sdb_metric_store_t *store, sdb_time_t last_update)
725         sdb_store_obj_t *obj = NULL;
726         sdb_host_t *host;
727         sdb_metric_t *metric;
728         sdb_data_t d;
730         sdb_avltree_t *metrics;
732         int status = 0;
734         if ((! hostname) || (! name))
735                 return -1;
737         if (store) {
738                 if ((store->type != NULL) != (store->id != NULL))
739                         return -1;
740                 else if (! store->type)
741                         store = NULL;
742         }
744         pthread_rwlock_wrlock(&host_lock);
745         host = lookup_host(hostname, /* canonicalize = */ 1);
746         metrics = get_host_children(host, SDB_METRIC);
747         if (! metrics) {
748                 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
749                                 "host '%s' not found", name, hostname);
750                 status = -1;
751         }
753         if (! status)
754                 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
755                                 name, last_update, &obj);
756         sdb_object_deref(SDB_OBJ(host));
758         if (status) {
759                 pthread_rwlock_unlock(&host_lock);
760                 return status;
761         }
763         assert(obj);
764         metric = METRIC(obj);
766         if (store)
767                 if (store_metric_store(metric, store))
768                         status = -1;
769         pthread_rwlock_unlock(&host_lock);
771         if (sdb_plugin_store_metric(hostname, name, store, last_update))
772                 status = -1;
774         /* record the hostname as an attribute */
775         d.type = SDB_TYPE_STRING;
776         d.data.string = SDB_OBJ(host)->name;
777         if (sdb_store_metric_attr(hostname, name, "hostname", &d, last_update))
778                 status = -1;
779         return status;
780 } /* sdb_store_metric */
782 int
783 sdb_store_metric_attr(const char *hostname, const char *metric,
784                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
786         sdb_avltree_t *metrics;
787         sdb_host_t *host;
788         sdb_metric_t *m;
789         int status = 0;
791         if ((! hostname) || (! metric) || (! key))
792                 return -1;
794         pthread_rwlock_wrlock(&host_lock);
795         host = lookup_host(hostname, /* canonicalize = */ 1);
796         metrics = get_host_children(host, SDB_METRIC);
797         sdb_object_deref(SDB_OBJ(host));
798         if (! metrics) {
799                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
800                                 "for metric '%s' - host '%s' not found",
801                                 key, metric, hostname);
802                 pthread_rwlock_unlock(&host_lock);
803                 return -1;
804         }
806         m = METRIC(sdb_avltree_lookup(metrics, metric));
807         if (! m) {
808                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
809                                 "metric '%s/%s' not found", key, hostname, metric);
810                 status = -1;
811         }
813         if (! status)
814                 status = store_attr(STORE_OBJ(m), m->attributes,
815                                 key, value, last_update);
817         sdb_object_deref(SDB_OBJ(m));
818         pthread_rwlock_unlock(&host_lock);
820         if (sdb_plugin_store_metric_attribute(hostname, metric,
821                                 key, value, last_update))
822                 status = -1;
823         return status;
824 } /* sdb_store_metric_attr */
826 sdb_store_obj_t *
827 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
829         sdb_avltree_t *children;
831         if ((! host) || (host->type != SDB_HOST) || (! name))
832                 return NULL;
834         children = get_host_children(HOST(host), type);
835         if (! children)
836                 return NULL;
837         return STORE_OBJ(sdb_avltree_lookup(children, name));
838 } /* sdb_store_get_child */
840 int
841 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
842                 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
844         sdb_avltree_t *metrics;
845         sdb_host_t *host;
846         sdb_metric_t *m;
848         sdb_timeseries_t *ts;
850         int status = 0;
852         if ((! hostname) || (! metric) || (! opts) || (! buf))
853                 return -1;
855         pthread_rwlock_rdlock(&host_lock);
856         host = lookup_host(hostname, /* canonicalize = */ 1);
857         metrics = get_host_children(host, SDB_METRIC);
858         sdb_object_deref(SDB_OBJ(host));
859         if (! metrics) {
860                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
861                                 "- host '%s' not found", hostname, metric, hostname);
862                 pthread_rwlock_unlock(&host_lock);
863                 return -1;
864         }
866         m = METRIC(sdb_avltree_lookup(metrics, metric));
867         if (! m) {
868                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
869                                 "- metric '%s' not found", hostname, metric, metric);
870                 pthread_rwlock_unlock(&host_lock);
871                 return -1;
872         }
874         if ((! m->store.type) || (! m->store.id)) {
875                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
876                                 "- no data-store configured for the stored metric",
877                                 hostname, metric);
878                 sdb_object_deref(SDB_OBJ(m));
879                 pthread_rwlock_unlock(&host_lock);
880                 return -1;
881         }
883         {
884                 char type[strlen(m->store.type) + 1];
885                 char id[strlen(m->store.id) + 1];
887                 strncpy(type, m->store.type, sizeof(type));
888                 strncpy(id, m->store.id, sizeof(id));
889                 pthread_rwlock_unlock(&host_lock);
891                 ts = sdb_plugin_fetch_timeseries(type, id, opts);
892                 if (! ts) {
893                         sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
894                                         "- %s fetcher callback returned no data for '%s'",
895                                         hostname, metric, type, id);
896                         status = -1;
897                 }
898         }
900         ts_tojson(ts, buf);
901         sdb_object_deref(SDB_OBJ(m));
902         sdb_timeseries_destroy(ts);
903         return status;
904 } /* sdb_store_fetch_timeseries */
906 int
907 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
909         sdb_data_t tmp;
911         if (! obj)
912                 return -1;
914         switch (field) {
915                 case SDB_FIELD_NAME:
916                         tmp.type = SDB_TYPE_STRING;
917                         tmp.data.string = strdup(SDB_OBJ(obj)->name);
918                         if (! tmp.data.string)
919                                 return -1;
920                         break;
921                 case SDB_FIELD_LAST_UPDATE:
922                         tmp.type = SDB_TYPE_DATETIME;
923                         tmp.data.datetime = obj->last_update;
924                         break;
925                 case SDB_FIELD_AGE:
926                         tmp.type = SDB_TYPE_DATETIME;
927                         tmp.data.datetime = sdb_gettime() - obj->last_update;
928                         break;
929                 case SDB_FIELD_INTERVAL:
930                         tmp.type = SDB_TYPE_DATETIME;
931                         tmp.data.datetime = obj->interval;
932                         break;
933                 case SDB_FIELD_BACKEND:
934                         if (! res)
935                                 return 0;
936                         tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
937                         tmp.data.array.length = obj->backends_num;
938                         tmp.data.array.values = obj->backends;
939                         return sdb_data_copy(res, &tmp);
940                 case SDB_FIELD_VALUE:
941                         if (obj->type != SDB_ATTRIBUTE)
942                                 return -1;
943                         if (! res)
944                                 return 0;
945                         return sdb_data_copy(res, &ATTR(obj)->value);
946                 case SDB_FIELD_TIMESERIES:
947                         if (obj->type != SDB_METRIC)
948                                 return -1;
949                         tmp.type = SDB_TYPE_BOOLEAN;
950                         tmp.data.boolean = METRIC(obj)->store.type != NULL;
951                 default:
952                         return -1;
953         }
954         if (res)
955                 *res = tmp;
956         else
957                 sdb_data_free_datum(&tmp);
958         return 0;
959 } /* sdb_store_get_field */
961 int
962 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
963                 sdb_store_matcher_t *filter)
965         sdb_avltree_t *tree = NULL;
966         sdb_store_obj_t *attr;
968         if ((! obj) || (! name))
969                 return -1;
971         if (obj->type == SDB_HOST)
972                 tree = HOST(obj)->attributes;
973         else if (obj->type == SDB_SERVICE)
974                 tree = SVC(obj)->attributes;
975         else if (obj->type == SDB_METRIC)
976                 tree = METRIC(obj)->attributes;
978         if (! tree)
979                 return -1;
981         attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
982         if (! attr)
983                 return -1;
984         if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
985                 sdb_object_deref(SDB_OBJ(attr));
986                 return -1;
987         }
989         assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
990         if (res)
991                 sdb_data_copy(res, &ATTR(attr)->value);
992         sdb_object_deref(SDB_OBJ(attr));
993         return 0;
994 } /* sdb_store_get_attr */
996 int
997 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
998                 sdb_store_lookup_cb cb, void *user_data)
1000         sdb_avltree_iter_t *host_iter = NULL;
1001         int status = 0;
1003         if (! cb)
1004                 return -1;
1006         if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
1007                 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
1008                 return -1;
1009         }
1011         pthread_rwlock_rdlock(&host_lock);
1013         if (hosts) {
1014                 host_iter = sdb_avltree_get_iter(hosts);
1015                 if (! host_iter)
1016                         status = -1;
1017         }
1019         /* has_next returns false if the iterator is NULL */
1020         while (sdb_avltree_iter_has_next(host_iter)) {
1021                 sdb_store_obj_t *host;
1022                 sdb_avltree_iter_t *iter = NULL;
1024                 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1025                 assert(host);
1027                 if (! sdb_store_matcher_matches(filter, host, NULL))
1028                         continue;
1030                 if (type == SDB_SERVICE)
1031                         iter = sdb_avltree_get_iter(HOST(host)->services);
1032                 else if (type == SDB_METRIC)
1033                         iter = sdb_avltree_get_iter(HOST(host)->metrics);
1035                 if (iter) {
1036                         while (sdb_avltree_iter_has_next(iter)) {
1037                                 sdb_store_obj_t *obj;
1038                                 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1039                                 assert(obj);
1041                                 if (sdb_store_matcher_matches(m, obj, filter)) {
1042                                         if (cb(obj, filter, user_data)) {
1043                                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1044                                                                 "an error while scanning");
1045                                                 status = -1;
1046                                                 break;
1047                                         }
1048                                 }
1049                         }
1050                 }
1051                 else if (sdb_store_matcher_matches(m, host, filter)) {
1052                         if (cb(host, filter, user_data)) {
1053                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1054                                                 "an error while scanning");
1055                                 status = -1;
1056                         }
1057                 }
1059                 sdb_avltree_iter_destroy(iter);
1060                 if (status)
1061                         break;
1062         }
1064         sdb_avltree_iter_destroy(host_iter);
1065         pthread_rwlock_unlock(&host_lock);
1066         return status;
1067 } /* sdb_store_scan */
1069 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */