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)
105 {
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)
120 {
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)
138 {
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)
161 {
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)
177 {
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)
194 {
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)
206 {
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)
225 {
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)
242 {
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)
261 {
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)
304 {
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)
326 {
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)
357 {
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)
451 {
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)
472 {
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)
504 {
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)
528 {
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)
582 {
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)
596 {
597 if (! global_store)
598 return;
599 sdb_avltree_clear(global_store->hosts);
600 } /* sdb_store_clear */
602 int
603 sdb_store_host(const char *name, sdb_time_t last_update)
604 {
605 char *cname = NULL;
606 int status = 0;
608 if ((! global_store) || (! name))
609 return -1;
611 cname = sdb_plugin_cname(strdup(name));
612 if (! cname) {
613 sdb_log(SDB_LOG_ERR, "store: strdup failed");
614 return -1;
615 }
617 pthread_rwlock_wrlock(&global_store->host_lock);
618 status = store_obj(NULL, global_store->hosts,
619 SDB_HOST, cname, last_update, NULL);
620 pthread_rwlock_unlock(&global_store->host_lock);
622 if (sdb_plugin_store_host(name, last_update))
623 status = -1;
625 free(cname);
626 return status;
627 } /* sdb_store_host */
629 bool
630 sdb_store_has_host(const char *name)
631 {
632 sdb_host_t *host;
634 if ((! global_store) || (! name))
635 return false;
637 host = lookup_host(global_store, name, /* canonicalize = */ 0);
638 sdb_object_deref(SDB_OBJ(host));
639 return host != NULL;
640 } /* sdb_store_has_host */
642 sdb_store_obj_t *
643 sdb_store_get_host(const char *name)
644 {
645 sdb_host_t *host;
647 if ((! global_store) || (! name))
648 return NULL;
650 host = lookup_host(global_store, name, /* canonicalize = */ 0);
651 if (! host)
652 return NULL;
654 return STORE_OBJ(host);
655 } /* sdb_store_get_host */
657 int
658 sdb_store_attribute(const char *hostname,
659 const char *key, const sdb_data_t *value,
660 sdb_time_t last_update)
661 {
662 sdb_host_t *host;
663 sdb_avltree_t *attrs;
664 int status = 0;
666 if ((! global_store) || (! hostname) || (! key))
667 return -1;
669 pthread_rwlock_wrlock(&global_store->host_lock);
670 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
671 attrs = get_host_children(host, SDB_ATTRIBUTE);
672 if (! attrs) {
673 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
674 "host '%s' not found", key, hostname);
675 status = -1;
676 }
678 if (! status)
679 status = store_attr(STORE_OBJ(host), attrs, key, value, last_update);
681 sdb_object_deref(SDB_OBJ(host));
682 pthread_rwlock_unlock(&global_store->host_lock);
684 if (sdb_plugin_store_attribute(hostname, key, value, last_update))
685 status = -1;
686 return status;
687 } /* sdb_store_attribute */
689 int
690 sdb_store_service(const char *hostname, const char *name,
691 sdb_time_t last_update)
692 {
693 sdb_host_t *host;
694 sdb_avltree_t *services;
695 sdb_data_t d;
697 int status = 0;
699 if ((! global_store) || (! hostname) || (! name))
700 return -1;
702 pthread_rwlock_wrlock(&global_store->host_lock);
703 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
704 services = get_host_children(host, SDB_SERVICE);
705 if (! services) {
706 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
707 "host '%s' not found", name, hostname);
708 status = -1;
709 }
711 if (! status)
712 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
713 name, last_update, NULL);
715 sdb_object_deref(SDB_OBJ(host));
716 pthread_rwlock_unlock(&global_store->host_lock);
718 if (status)
719 return status;
721 if (sdb_plugin_store_service(hostname, name, last_update))
722 status = -1;
724 /* record the hostname as an attribute */
725 d.type = SDB_TYPE_STRING;
726 d.data.string = SDB_OBJ(host)->name;
727 if (sdb_store_service_attr(hostname, name, "hostname", &d, last_update))
728 status = -1;
729 return status;
730 } /* sdb_store_service */
732 int
733 sdb_store_service_attr(const char *hostname, const char *service,
734 const char *key, const sdb_data_t *value, sdb_time_t last_update)
735 {
736 sdb_host_t *host;
737 sdb_service_t *svc;
738 sdb_avltree_t *services;
739 int status = 0;
741 if ((! global_store) || (! hostname) || (! service) || (! key))
742 return -1;
744 pthread_rwlock_wrlock(&global_store->host_lock);
745 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
746 services = get_host_children(host, SDB_SERVICE);
747 sdb_object_deref(SDB_OBJ(host));
748 if (! services) {
749 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
750 "for service '%s' - host '%ss' not found",
751 key, service, hostname);
752 pthread_rwlock_unlock(&global_store->host_lock);
753 return -1;
754 }
756 svc = SVC(sdb_avltree_lookup(services, service));
757 if (! svc) {
758 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
759 "service '%s/%s' not found", key, hostname, service);
760 status = -1;
761 }
763 if (! status)
764 status = store_attr(STORE_OBJ(svc), svc->attributes,
765 key, value, last_update);
767 sdb_object_deref(SDB_OBJ(svc));
768 pthread_rwlock_unlock(&global_store->host_lock);
770 if (sdb_plugin_store_service_attribute(hostname, service,
771 key, value, last_update))
772 status = -1;
773 return status;
774 } /* sdb_store_service_attr */
776 int
777 sdb_store_metric(const char *hostname, const char *name,
778 sdb_metric_store_t *store, sdb_time_t last_update)
779 {
780 sdb_store_obj_t *obj = NULL;
781 sdb_host_t *host;
782 sdb_metric_t *metric;
783 sdb_data_t d;
785 sdb_avltree_t *metrics;
787 int status = 0;
789 if ((! global_store) || (! hostname) || (! name))
790 return -1;
792 if (store) {
793 if ((store->type != NULL) != (store->id != NULL))
794 return -1;
795 else if (! store->type)
796 store = NULL;
797 }
799 pthread_rwlock_wrlock(&global_store->host_lock);
800 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
801 metrics = get_host_children(host, SDB_METRIC);
802 if (! metrics) {
803 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
804 "host '%s' not found", name, hostname);
805 status = -1;
806 }
808 if (! status)
809 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
810 name, last_update, &obj);
811 sdb_object_deref(SDB_OBJ(host));
813 if (status) {
814 pthread_rwlock_unlock(&global_store->host_lock);
815 return status;
816 }
818 assert(obj);
819 metric = METRIC(obj);
821 if (store)
822 if (store_metric_store(metric, store))
823 status = -1;
824 pthread_rwlock_unlock(&global_store->host_lock);
826 if (sdb_plugin_store_metric(hostname, name, store, last_update))
827 status = -1;
829 /* record the hostname as an attribute */
830 d.type = SDB_TYPE_STRING;
831 d.data.string = SDB_OBJ(host)->name;
832 if (sdb_store_metric_attr(hostname, name, "hostname", &d, last_update))
833 status = -1;
834 return status;
835 } /* sdb_store_metric */
837 int
838 sdb_store_metric_attr(const char *hostname, const char *metric,
839 const char *key, const sdb_data_t *value, sdb_time_t last_update)
840 {
841 sdb_avltree_t *metrics;
842 sdb_host_t *host;
843 sdb_metric_t *m;
844 int status = 0;
846 if ((! global_store) || (! hostname) || (! metric) || (! key))
847 return -1;
849 pthread_rwlock_wrlock(&global_store->host_lock);
850 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
851 metrics = get_host_children(host, SDB_METRIC);
852 sdb_object_deref(SDB_OBJ(host));
853 if (! metrics) {
854 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
855 "for metric '%s' - host '%s' not found",
856 key, metric, hostname);
857 pthread_rwlock_unlock(&global_store->host_lock);
858 return -1;
859 }
861 m = METRIC(sdb_avltree_lookup(metrics, metric));
862 if (! m) {
863 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
864 "metric '%s/%s' not found", key, hostname, metric);
865 status = -1;
866 }
868 if (! status)
869 status = store_attr(STORE_OBJ(m), m->attributes,
870 key, value, last_update);
872 sdb_object_deref(SDB_OBJ(m));
873 pthread_rwlock_unlock(&global_store->host_lock);
875 if (sdb_plugin_store_metric_attribute(hostname, metric,
876 key, value, last_update))
877 status = -1;
878 return status;
879 } /* sdb_store_metric_attr */
881 sdb_store_obj_t *
882 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
883 {
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_fetch_timeseries(const char *hostname, const char *metric,
897 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
898 {
899 sdb_avltree_t *metrics;
900 sdb_host_t *host;
901 sdb_metric_t *m;
903 sdb_timeseries_t *ts;
905 int status = 0;
907 if ((! global_store) || (! hostname) || (! metric) || (! opts) || (! buf))
908 return -1;
910 pthread_rwlock_rdlock(&global_store->host_lock);
911 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
912 metrics = get_host_children(host, SDB_METRIC);
913 sdb_object_deref(SDB_OBJ(host));
914 if (! metrics) {
915 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
916 "- host '%s' not found", hostname, metric, hostname);
917 pthread_rwlock_unlock(&global_store->host_lock);
918 return -1;
919 }
921 m = METRIC(sdb_avltree_lookup(metrics, metric));
922 if (! m) {
923 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
924 "- metric '%s' not found", hostname, metric, metric);
925 pthread_rwlock_unlock(&global_store->host_lock);
926 return -1;
927 }
929 if ((! m->store.type) || (! m->store.id)) {
930 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
931 "- no data-store configured for the stored metric",
932 hostname, metric);
933 sdb_object_deref(SDB_OBJ(m));
934 pthread_rwlock_unlock(&global_store->host_lock);
935 return -1;
936 }
938 {
939 char type[strlen(m->store.type) + 1];
940 char id[strlen(m->store.id) + 1];
942 strncpy(type, m->store.type, sizeof(type));
943 strncpy(id, m->store.id, sizeof(id));
944 pthread_rwlock_unlock(&global_store->host_lock);
946 ts = sdb_plugin_fetch_timeseries(type, id, opts);
947 if (! ts) {
948 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
949 "- %s fetcher callback returned no data for '%s'",
950 hostname, metric, type, id);
951 status = -1;
952 }
953 }
955 ts_tojson(ts, buf);
956 sdb_object_deref(SDB_OBJ(m));
957 sdb_timeseries_destroy(ts);
958 return status;
959 } /* sdb_store_fetch_timeseries */
961 int
962 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
963 {
964 sdb_data_t tmp;
966 if (! obj)
967 return -1;
969 switch (field) {
970 case SDB_FIELD_NAME:
971 tmp.type = SDB_TYPE_STRING;
972 tmp.data.string = strdup(SDB_OBJ(obj)->name);
973 if (! tmp.data.string)
974 return -1;
975 break;
976 case SDB_FIELD_LAST_UPDATE:
977 tmp.type = SDB_TYPE_DATETIME;
978 tmp.data.datetime = obj->last_update;
979 break;
980 case SDB_FIELD_AGE:
981 tmp.type = SDB_TYPE_DATETIME;
982 tmp.data.datetime = sdb_gettime() - obj->last_update;
983 break;
984 case SDB_FIELD_INTERVAL:
985 tmp.type = SDB_TYPE_DATETIME;
986 tmp.data.datetime = obj->interval;
987 break;
988 case SDB_FIELD_BACKEND:
989 if (! res)
990 return 0;
991 tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
992 tmp.data.array.length = obj->backends_num;
993 tmp.data.array.values = obj->backends;
994 return sdb_data_copy(res, &tmp);
995 case SDB_FIELD_VALUE:
996 if (obj->type != SDB_ATTRIBUTE)
997 return -1;
998 if (! res)
999 return 0;
1000 return sdb_data_copy(res, &ATTR(obj)->value);
1001 case SDB_FIELD_TIMESERIES:
1002 if (obj->type != SDB_METRIC)
1003 return -1;
1004 tmp.type = SDB_TYPE_BOOLEAN;
1005 tmp.data.boolean = METRIC(obj)->store.type != NULL;
1006 default:
1007 return -1;
1008 }
1009 if (res)
1010 *res = tmp;
1011 else
1012 sdb_data_free_datum(&tmp);
1013 return 0;
1014 } /* sdb_store_get_field */
1016 int
1017 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
1018 sdb_store_matcher_t *filter)
1019 {
1020 sdb_avltree_t *tree = NULL;
1021 sdb_store_obj_t *attr;
1023 if ((! obj) || (! name))
1024 return -1;
1026 if (obj->type == SDB_HOST)
1027 tree = HOST(obj)->attributes;
1028 else if (obj->type == SDB_SERVICE)
1029 tree = SVC(obj)->attributes;
1030 else if (obj->type == SDB_METRIC)
1031 tree = METRIC(obj)->attributes;
1033 if (! tree)
1034 return -1;
1036 attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
1037 if (! attr)
1038 return -1;
1039 if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
1040 sdb_object_deref(SDB_OBJ(attr));
1041 return -1;
1042 }
1044 assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
1045 if (res)
1046 sdb_data_copy(res, &ATTR(attr)->value);
1047 sdb_object_deref(SDB_OBJ(attr));
1048 return 0;
1049 } /* sdb_store_get_attr */
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)
1054 {
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 : */