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 * store writer API
578 */
580 static int
581 store_attribute(const char *hostname,
582 const char *key, const sdb_data_t *value,
583 sdb_time_t last_update, sdb_object_t *user_data)
584 {
585 sdb_store_t *st = ST(user_data);
587 sdb_host_t *host;
588 sdb_avltree_t *attrs;
589 int status = 0;
591 if ((! hostname) || (! key))
592 return -1;
594 pthread_rwlock_wrlock(&st->host_lock);
595 host = lookup_host(st, hostname, /* canonicalize = */ 1);
596 attrs = get_host_children(host, SDB_ATTRIBUTE);
597 if (! attrs) {
598 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
599 "host '%s' not found", key, hostname);
600 status = -1;
601 }
603 if (! status)
604 status = store_attr(STORE_OBJ(host), attrs, key, value, last_update);
606 sdb_object_deref(SDB_OBJ(host));
607 pthread_rwlock_unlock(&st->host_lock);
609 return status;
610 } /* store_attribute */
612 static int
613 store_host(const char *name, sdb_time_t last_update, sdb_object_t *user_data)
614 {
615 sdb_store_t *st = ST(user_data);
617 char *cname = NULL;
618 int status = 0;
620 if (! name)
621 return -1;
623 cname = sdb_plugin_cname(strdup(name));
624 if (! cname) {
625 sdb_log(SDB_LOG_ERR, "store: strdup failed");
626 return -1;
627 }
629 pthread_rwlock_wrlock(&st->host_lock);
630 status = store_obj(NULL, st->hosts,
631 SDB_HOST, cname, last_update, NULL);
632 pthread_rwlock_unlock(&st->host_lock);
634 free(cname);
635 return status;
636 } /* store_host */
638 static int
639 store_service_attr(const char *hostname, const char *service,
640 const char *key, const sdb_data_t *value, sdb_time_t last_update,
641 sdb_object_t *user_data)
642 {
643 sdb_store_t *st = ST(user_data);
645 sdb_host_t *host;
646 sdb_service_t *svc;
647 sdb_avltree_t *services;
648 int status = 0;
650 if ((! hostname) || (! service) || (! key))
651 return -1;
653 pthread_rwlock_wrlock(&st->host_lock);
654 host = lookup_host(st, hostname, /* canonicalize = */ 1);
655 services = get_host_children(host, SDB_SERVICE);
656 sdb_object_deref(SDB_OBJ(host));
657 if (! services) {
658 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
659 "for service '%s' - host '%ss' not found",
660 key, service, hostname);
661 pthread_rwlock_unlock(&st->host_lock);
662 return -1;
663 }
665 svc = SVC(sdb_avltree_lookup(services, service));
666 if (! svc) {
667 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
668 "service '%s/%s' not found", key, hostname, service);
669 status = -1;
670 }
672 if (! status)
673 status = store_attr(STORE_OBJ(svc), svc->attributes,
674 key, value, last_update);
676 sdb_object_deref(SDB_OBJ(svc));
677 pthread_rwlock_unlock(&st->host_lock);
679 return status;
680 } /* store_service_attr */
682 static int
683 store_service(const char *hostname, const char *name,
684 sdb_time_t last_update, sdb_object_t *user_data)
685 {
686 sdb_store_t *st = ST(user_data);
688 sdb_host_t *host;
689 sdb_avltree_t *services;
690 sdb_data_t d;
692 int status = 0;
694 if ((! hostname) || (! name))
695 return -1;
697 pthread_rwlock_wrlock(&st->host_lock);
698 host = lookup_host(st, hostname, /* canonicalize = */ 1);
699 services = get_host_children(host, SDB_SERVICE);
700 if (! services) {
701 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
702 "host '%s' not found", name, hostname);
703 status = -1;
704 }
706 if (! status)
707 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
708 name, last_update, NULL);
710 sdb_object_deref(SDB_OBJ(host));
711 pthread_rwlock_unlock(&st->host_lock);
713 if (status)
714 return status;
716 /* record the hostname as an attribute */
717 d.type = SDB_TYPE_STRING;
718 d.data.string = SDB_OBJ(host)->name;
719 if (store_service_attr(hostname, name, "hostname", &d, last_update, user_data))
720 status = -1;
721 return status;
722 } /* store_service */
724 static int
725 store_metric_attr(const char *hostname, const char *metric,
726 const char *key, const sdb_data_t *value, sdb_time_t last_update,
727 sdb_object_t *user_data)
728 {
729 sdb_store_t *st = ST(user_data);
731 sdb_avltree_t *metrics;
732 sdb_host_t *host;
733 sdb_metric_t *m;
734 int status = 0;
736 if ((! hostname) || (! metric) || (! key))
737 return -1;
739 pthread_rwlock_wrlock(&st->host_lock);
740 host = lookup_host(st, hostname, /* canonicalize = */ 1);
741 metrics = get_host_children(host, SDB_METRIC);
742 sdb_object_deref(SDB_OBJ(host));
743 if (! metrics) {
744 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
745 "for metric '%s' - host '%s' not found",
746 key, metric, hostname);
747 pthread_rwlock_unlock(&st->host_lock);
748 return -1;
749 }
751 m = METRIC(sdb_avltree_lookup(metrics, metric));
752 if (! m) {
753 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
754 "metric '%s/%s' not found", key, hostname, metric);
755 status = -1;
756 }
758 if (! status)
759 status = store_attr(STORE_OBJ(m), m->attributes,
760 key, value, last_update);
762 sdb_object_deref(SDB_OBJ(m));
763 pthread_rwlock_unlock(&st->host_lock);
765 return status;
766 } /* store_metric_attr */
768 static int
769 store_metric(const char *hostname, const char *name,
770 sdb_metric_store_t *store, sdb_time_t last_update,
771 sdb_object_t *user_data)
772 {
773 sdb_store_t *st = ST(user_data);
775 sdb_store_obj_t *obj = NULL;
776 sdb_host_t *host;
777 sdb_metric_t *metric;
778 sdb_data_t d;
780 sdb_avltree_t *metrics;
782 int status = 0;
784 if ((! hostname) || (! name))
785 return -1;
787 if (store) {
788 if ((store->type != NULL) != (store->id != NULL))
789 return -1;
790 else if (! store->type)
791 store = NULL;
792 }
794 pthread_rwlock_wrlock(&st->host_lock);
795 host = lookup_host(st, hostname, /* canonicalize = */ 1);
796 metrics = get_host_children(host, SDB_METRIC);
797 if (! metrics) {
798 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
799 "host '%s' not found", name, hostname);
800 status = -1;
801 }
803 if (! status)
804 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
805 name, last_update, &obj);
806 sdb_object_deref(SDB_OBJ(host));
808 if (status) {
809 pthread_rwlock_unlock(&st->host_lock);
810 return status;
811 }
813 assert(obj);
814 metric = METRIC(obj);
816 if (store)
817 if (store_metric_store(metric, store))
818 status = -1;
819 pthread_rwlock_unlock(&st->host_lock);
821 /* record the hostname as an attribute */
822 d.type = SDB_TYPE_STRING;
823 d.data.string = SDB_OBJ(host)->name;
824 if (store_metric_attr(hostname, name, "hostname", &d, last_update, user_data))
825 status = -1;
826 return status;
827 } /* store_metric */
829 static sdb_store_writer_t store_writer = {
830 store_host, store_service, store_metric,
831 store_attribute, store_service_attr, store_metric_attr,
832 };
834 /*
835 * public API
836 */
838 int
839 sdb_store_init(void)
840 {
841 if (global_store)
842 return 0;
844 global_store = ST(sdb_object_create("store", store_type));
845 if (! global_store) {
846 sdb_log(SDB_LOG_ERR, "store: Failed to allocate store");
847 return -1;
848 }
849 return sdb_plugin_register_writer("memstore",
850 &store_writer, SDB_OBJ(global_store));
851 } /* sdb_store_init */
853 void
854 sdb_store_clear(void)
855 {
856 if (! global_store)
857 return;
858 sdb_avltree_clear(global_store->hosts);
859 } /* sdb_store_clear */
861 int
862 sdb_store_host(const char *name, sdb_time_t last_update)
863 {
864 return sdb_plugin_store_host(name, last_update);
865 } /* sdb_store_host */
867 int
868 sdb_store_attribute(const char *hostname,
869 const char *key, const sdb_data_t *value,
870 sdb_time_t last_update)
871 {
872 return sdb_plugin_store_attribute(hostname, key, value, last_update);
873 } /* sdb_store_attribute */
875 int
876 sdb_store_service(const char *hostname, const char *name,
877 sdb_time_t last_update)
878 {
879 return sdb_plugin_store_service(hostname, name, last_update);
880 } /* sdb_store_service */
882 int
883 sdb_store_service_attr(const char *hostname, const char *service,
884 const char *key, const sdb_data_t *value, sdb_time_t last_update)
885 {
886 return sdb_plugin_store_service_attribute(hostname, service,
887 key, value, last_update);
888 } /* sdb_store_service_attr */
890 int
891 sdb_store_metric(const char *hostname, const char *name,
892 sdb_metric_store_t *store, sdb_time_t last_update)
893 {
894 return sdb_plugin_store_metric(hostname, name, store, last_update);
895 } /* sdb_store_metric */
897 int
898 sdb_store_metric_attr(const char *hostname, const char *metric,
899 const char *key, const sdb_data_t *value, sdb_time_t last_update)
900 {
901 return sdb_plugin_store_metric_attribute(hostname, metric,
902 key, value, last_update);
903 } /* sdb_store_metric_attr */
905 bool
906 sdb_store_has_host(const char *name)
907 {
908 sdb_host_t *host;
910 if ((! global_store) || (! name))
911 return false;
913 host = lookup_host(global_store, name, /* canonicalize = */ 0);
914 sdb_object_deref(SDB_OBJ(host));
915 return host != NULL;
916 } /* sdb_store_has_host */
918 sdb_store_obj_t *
919 sdb_store_get_host(const char *name)
920 {
921 sdb_host_t *host;
923 if ((! global_store) || (! name))
924 return NULL;
926 host = lookup_host(global_store, name, /* canonicalize = */ 0);
927 if (! host)
928 return NULL;
930 return STORE_OBJ(host);
931 } /* sdb_store_get_host */
933 sdb_store_obj_t *
934 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
935 {
936 sdb_avltree_t *children;
938 if ((! host) || (host->type != SDB_HOST) || (! name))
939 return NULL;
941 children = get_host_children(HOST(host), type);
942 if (! children)
943 return NULL;
944 return STORE_OBJ(sdb_avltree_lookup(children, name));
945 } /* sdb_store_get_child */
947 int
948 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
949 {
950 sdb_data_t tmp;
952 if (! obj)
953 return -1;
955 switch (field) {
956 case SDB_FIELD_NAME:
957 tmp.type = SDB_TYPE_STRING;
958 tmp.data.string = strdup(SDB_OBJ(obj)->name);
959 if (! tmp.data.string)
960 return -1;
961 break;
962 case SDB_FIELD_LAST_UPDATE:
963 tmp.type = SDB_TYPE_DATETIME;
964 tmp.data.datetime = obj->last_update;
965 break;
966 case SDB_FIELD_AGE:
967 tmp.type = SDB_TYPE_DATETIME;
968 tmp.data.datetime = sdb_gettime() - obj->last_update;
969 break;
970 case SDB_FIELD_INTERVAL:
971 tmp.type = SDB_TYPE_DATETIME;
972 tmp.data.datetime = obj->interval;
973 break;
974 case SDB_FIELD_BACKEND:
975 if (! res)
976 return 0;
977 tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
978 tmp.data.array.length = obj->backends_num;
979 tmp.data.array.values = obj->backends;
980 return sdb_data_copy(res, &tmp);
981 case SDB_FIELD_VALUE:
982 if (obj->type != SDB_ATTRIBUTE)
983 return -1;
984 if (! res)
985 return 0;
986 return sdb_data_copy(res, &ATTR(obj)->value);
987 case SDB_FIELD_TIMESERIES:
988 if (obj->type != SDB_METRIC)
989 return -1;
990 tmp.type = SDB_TYPE_BOOLEAN;
991 tmp.data.boolean = METRIC(obj)->store.type != NULL;
992 default:
993 return -1;
994 }
995 if (res)
996 *res = tmp;
997 else
998 sdb_data_free_datum(&tmp);
999 return 0;
1000 } /* sdb_store_get_field */
1002 int
1003 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
1004 sdb_store_matcher_t *filter)
1005 {
1006 sdb_avltree_t *tree = NULL;
1007 sdb_store_obj_t *attr;
1009 if ((! obj) || (! name))
1010 return -1;
1012 if (obj->type == SDB_HOST)
1013 tree = HOST(obj)->attributes;
1014 else if (obj->type == SDB_SERVICE)
1015 tree = SVC(obj)->attributes;
1016 else if (obj->type == SDB_METRIC)
1017 tree = METRIC(obj)->attributes;
1019 if (! tree)
1020 return -1;
1022 attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
1023 if (! attr)
1024 return -1;
1025 if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
1026 sdb_object_deref(SDB_OBJ(attr));
1027 return -1;
1028 }
1030 assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
1031 if (res)
1032 sdb_data_copy(res, &ATTR(attr)->value);
1033 sdb_object_deref(SDB_OBJ(attr));
1034 return 0;
1035 } /* sdb_store_get_attr */
1037 int
1038 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
1039 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
1040 {
1041 sdb_avltree_t *metrics;
1042 sdb_host_t *host;
1043 sdb_metric_t *m;
1045 sdb_timeseries_t *ts;
1047 int status = 0;
1049 if ((! global_store) || (! hostname) || (! metric) || (! opts) || (! buf))
1050 return -1;
1052 pthread_rwlock_rdlock(&global_store->host_lock);
1053 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
1054 metrics = get_host_children(host, SDB_METRIC);
1055 sdb_object_deref(SDB_OBJ(host));
1056 if (! metrics) {
1057 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1058 "- host '%s' not found", hostname, metric, hostname);
1059 pthread_rwlock_unlock(&global_store->host_lock);
1060 return -1;
1061 }
1063 m = METRIC(sdb_avltree_lookup(metrics, metric));
1064 if (! m) {
1065 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1066 "- metric '%s' not found", hostname, metric, metric);
1067 pthread_rwlock_unlock(&global_store->host_lock);
1068 return -1;
1069 }
1071 if ((! m->store.type) || (! m->store.id)) {
1072 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1073 "- no data-store configured for the stored metric",
1074 hostname, metric);
1075 sdb_object_deref(SDB_OBJ(m));
1076 pthread_rwlock_unlock(&global_store->host_lock);
1077 return -1;
1078 }
1080 {
1081 char type[strlen(m->store.type) + 1];
1082 char id[strlen(m->store.id) + 1];
1084 strncpy(type, m->store.type, sizeof(type));
1085 strncpy(id, m->store.id, sizeof(id));
1086 pthread_rwlock_unlock(&global_store->host_lock);
1088 ts = sdb_plugin_fetch_timeseries(type, id, opts);
1089 if (! ts) {
1090 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1091 "- %s fetcher callback returned no data for '%s'",
1092 hostname, metric, type, id);
1093 status = -1;
1094 }
1095 }
1097 ts_tojson(ts, buf);
1098 sdb_object_deref(SDB_OBJ(m));
1099 sdb_timeseries_destroy(ts);
1100 return status;
1101 } /* sdb_store_fetch_timeseries */
1103 int
1104 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
1105 sdb_store_lookup_cb cb, void *user_data)
1106 {
1107 sdb_avltree_iter_t *host_iter = NULL;
1108 int status = 0;
1110 if ((! global_store) || (! cb))
1111 return -1;
1113 if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
1114 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
1115 return -1;
1116 }
1118 pthread_rwlock_rdlock(&global_store->host_lock);
1119 host_iter = sdb_avltree_get_iter(global_store->hosts);
1120 if (! host_iter)
1121 status = -1;
1123 /* has_next returns false if the iterator is NULL */
1124 while (sdb_avltree_iter_has_next(host_iter)) {
1125 sdb_store_obj_t *host;
1126 sdb_avltree_iter_t *iter = NULL;
1128 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1129 assert(host);
1131 if (! sdb_store_matcher_matches(filter, host, NULL))
1132 continue;
1134 if (type == SDB_SERVICE)
1135 iter = sdb_avltree_get_iter(HOST(host)->services);
1136 else if (type == SDB_METRIC)
1137 iter = sdb_avltree_get_iter(HOST(host)->metrics);
1139 if (iter) {
1140 while (sdb_avltree_iter_has_next(iter)) {
1141 sdb_store_obj_t *obj;
1142 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1143 assert(obj);
1145 if (sdb_store_matcher_matches(m, obj, filter)) {
1146 if (cb(obj, filter, user_data)) {
1147 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1148 "an error while scanning");
1149 status = -1;
1150 break;
1151 }
1152 }
1153 }
1154 }
1155 else if (sdb_store_matcher_matches(m, host, filter)) {
1156 if (cb(host, filter, user_data)) {
1157 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1158 "an error while scanning");
1159 status = -1;
1160 }
1161 }
1163 sdb_avltree_iter_destroy(iter);
1164 if (status)
1165 break;
1166 }
1168 sdb_avltree_iter_destroy(host_iter);
1169 pthread_rwlock_unlock(&global_store->host_lock);
1170 return status;
1171 } /* sdb_store_scan */
1173 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */