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 struct sdb_store {
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 };
62 sdb_store_t *global_store = NULL;
64 /*
65 * private types
66 */
68 static sdb_type_t host_type;
69 static sdb_type_t service_type;
70 static sdb_type_t metric_type;
71 static sdb_type_t attribute_type;
73 static int
74 store_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
75 {
76 int err;
77 if (! (SDB_STORE(obj)->hosts = sdb_avltree_create()))
78 return -1;
79 if ((err = pthread_rwlock_init(&SDB_STORE(obj)->host_lock,
80 /* 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(&SDB_STORE(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(SDB_STORE(obj)->hosts);
100 SDB_STORE(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 = SDB_STORE(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 = SDB_STORE(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 = SDB_STORE(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 = SDB_STORE(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 = SDB_STORE(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 = SDB_STORE(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 sdb_store_writer_t sdb_store_writer = {
830 store_host, store_service, store_metric,
831 store_attribute, store_service_attr, store_metric_attr,
832 };
834 /*
835 * TODO: let prepare and execute accept a store object as their user_data
836 * object
837 */
839 static sdb_object_t *
840 prepare_query(sdb_ast_node_t *ast,
841 sdb_strbuf_t __attribute__((unused)) *errbuf,
842 sdb_object_t __attribute__((unused)) *user_data)
843 {
844 return SDB_OBJ(sdb_store_query_prepare(ast));
845 } /* prepare_query */
847 static int
848 execute_query(sdb_object_t *q,
849 sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
850 sdb_object_t __attribute__((unused)) *user_data)
851 {
852 return sdb_store_query_execute(QUERY(q), buf, errbuf);
853 } /* execute_query */
855 sdb_store_reader_t sdb_store_reader = {
856 prepare_query, execute_query,
857 };
859 /*
860 * public API
861 */
863 sdb_store_t *
864 sdb_store_create(void)
865 {
866 return SDB_STORE(sdb_object_create("store", store_type));
867 } /* sdb_store_create */
869 int
870 sdb_store_init(void)
871 {
872 if (global_store)
873 return 0;
875 global_store = SDB_STORE(sdb_object_create("store", store_type));
876 if (! global_store) {
877 sdb_log(SDB_LOG_ERR, "store: Failed to allocate store");
878 return -1;
879 }
880 if (sdb_plugin_register_writer("memstore",
881 &sdb_store_writer, SDB_OBJ(global_store)))
882 return -1;
883 return sdb_plugin_register_reader("memstore",
884 &sdb_store_reader, SDB_OBJ(global_store));
885 } /* sdb_store_init */
887 void
888 sdb_store_clear(void)
889 {
890 if (! global_store)
891 return;
892 sdb_avltree_clear(global_store->hosts);
893 } /* sdb_store_clear */
895 int
896 sdb_store_host(sdb_store_t *store, const char *name, sdb_time_t last_update)
897 {
898 return store_host(name, last_update, SDB_OBJ(store));
899 } /* sdb_store_host */
901 int
902 sdb_store_service(sdb_store_t *store, const char *hostname, const char *name,
903 sdb_time_t last_update)
904 {
905 return store_service(hostname, name, last_update, SDB_OBJ(store));
906 } /* sdb_store_service */
908 int
909 sdb_store_metric(sdb_store_t *store, const char *hostname, const char *name,
910 sdb_metric_store_t *metric_store, sdb_time_t last_update)
911 {
912 return store_metric(hostname, name, metric_store, last_update, SDB_OBJ(store));
913 } /* sdb_store_metric */
915 int
916 sdb_store_attribute(sdb_store_t *store, const char *hostname,
917 const char *key, const sdb_data_t *value, sdb_time_t last_update)
918 {
919 return store_attribute(hostname, key, value, last_update, SDB_OBJ(store));
920 } /* sdb_store_attribute */
922 int
923 sdb_store_service_attr(sdb_store_t *store, const char *hostname,
924 const char *service, const char *key, const sdb_data_t *value,
925 sdb_time_t last_update)
926 {
927 return store_service_attr(hostname, service, key, value,
928 last_update, SDB_OBJ(store));
929 } /* sdb_store_service_attr */
931 int
932 sdb_store_metric_attr(sdb_store_t *store, const char *hostname,
933 const char *metric, const char *key, const sdb_data_t *value,
934 sdb_time_t last_update)
935 {
936 return store_metric_attr(hostname, metric, key, value,
937 last_update, SDB_OBJ(store));
938 } /* sdb_store_metric_attr */
940 sdb_store_obj_t *
941 sdb_store_get_host(const char *name)
942 {
943 sdb_host_t *host;
945 if ((! global_store) || (! name))
946 return NULL;
948 host = lookup_host(global_store, name, /* canonicalize = */ 0);
949 if (! host)
950 return NULL;
952 return STORE_OBJ(host);
953 } /* sdb_store_get_host */
955 sdb_store_obj_t *
956 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
957 {
958 sdb_avltree_t *children;
960 if ((! host) || (host->type != SDB_HOST) || (! name))
961 return NULL;
963 children = get_host_children(HOST(host), type);
964 if (! children)
965 return NULL;
966 return STORE_OBJ(sdb_avltree_lookup(children, name));
967 } /* sdb_store_get_child */
969 int
970 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
971 {
972 sdb_data_t tmp;
974 if (! obj)
975 return -1;
977 switch (field) {
978 case SDB_FIELD_NAME:
979 tmp.type = SDB_TYPE_STRING;
980 tmp.data.string = strdup(SDB_OBJ(obj)->name);
981 if (! tmp.data.string)
982 return -1;
983 break;
984 case SDB_FIELD_LAST_UPDATE:
985 tmp.type = SDB_TYPE_DATETIME;
986 tmp.data.datetime = obj->last_update;
987 break;
988 case SDB_FIELD_AGE:
989 tmp.type = SDB_TYPE_DATETIME;
990 tmp.data.datetime = sdb_gettime() - obj->last_update;
991 break;
992 case SDB_FIELD_INTERVAL:
993 tmp.type = SDB_TYPE_DATETIME;
994 tmp.data.datetime = obj->interval;
995 break;
996 case SDB_FIELD_BACKEND:
997 if (! res)
998 return 0;
999 tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
1000 tmp.data.array.length = obj->backends_num;
1001 tmp.data.array.values = obj->backends;
1002 return sdb_data_copy(res, &tmp);
1003 case SDB_FIELD_VALUE:
1004 if (obj->type != SDB_ATTRIBUTE)
1005 return -1;
1006 if (! res)
1007 return 0;
1008 return sdb_data_copy(res, &ATTR(obj)->value);
1009 case SDB_FIELD_TIMESERIES:
1010 if (obj->type != SDB_METRIC)
1011 return -1;
1012 tmp.type = SDB_TYPE_BOOLEAN;
1013 tmp.data.boolean = METRIC(obj)->store.type != NULL;
1014 default:
1015 return -1;
1016 }
1017 if (res)
1018 *res = tmp;
1019 else
1020 sdb_data_free_datum(&tmp);
1021 return 0;
1022 } /* sdb_store_get_field */
1024 int
1025 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
1026 sdb_store_matcher_t *filter)
1027 {
1028 sdb_avltree_t *tree = NULL;
1029 sdb_store_obj_t *attr;
1031 if ((! obj) || (! name))
1032 return -1;
1034 if (obj->type == SDB_HOST)
1035 tree = HOST(obj)->attributes;
1036 else if (obj->type == SDB_SERVICE)
1037 tree = SVC(obj)->attributes;
1038 else if (obj->type == SDB_METRIC)
1039 tree = METRIC(obj)->attributes;
1041 if (! tree)
1042 return -1;
1044 attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
1045 if (! attr)
1046 return -1;
1047 if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
1048 sdb_object_deref(SDB_OBJ(attr));
1049 return -1;
1050 }
1052 assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
1053 if (res)
1054 sdb_data_copy(res, &ATTR(attr)->value);
1055 sdb_object_deref(SDB_OBJ(attr));
1056 return 0;
1057 } /* sdb_store_get_attr */
1059 /* TODO: sdb_store_fetch_timeseries should move into the plugin module */
1061 int
1062 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
1063 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
1064 {
1065 sdb_avltree_t *metrics;
1066 sdb_host_t *host;
1067 sdb_metric_t *m;
1069 sdb_timeseries_t *ts;
1071 int status = 0;
1073 if ((! global_store) || (! hostname) || (! metric) || (! opts) || (! buf))
1074 return -1;
1076 pthread_rwlock_rdlock(&global_store->host_lock);
1077 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
1078 metrics = get_host_children(host, SDB_METRIC);
1079 sdb_object_deref(SDB_OBJ(host));
1080 if (! metrics) {
1081 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1082 "- host '%s' not found", hostname, metric, hostname);
1083 pthread_rwlock_unlock(&global_store->host_lock);
1084 return -1;
1085 }
1087 m = METRIC(sdb_avltree_lookup(metrics, metric));
1088 if (! m) {
1089 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1090 "- metric '%s' not found", hostname, metric, metric);
1091 pthread_rwlock_unlock(&global_store->host_lock);
1092 return -1;
1093 }
1095 if ((! m->store.type) || (! m->store.id)) {
1096 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1097 "- no data-store configured for the stored metric",
1098 hostname, metric);
1099 sdb_object_deref(SDB_OBJ(m));
1100 pthread_rwlock_unlock(&global_store->host_lock);
1101 return -1;
1102 }
1104 {
1105 char type[strlen(m->store.type) + 1];
1106 char id[strlen(m->store.id) + 1];
1108 strncpy(type, m->store.type, sizeof(type));
1109 strncpy(id, m->store.id, sizeof(id));
1110 pthread_rwlock_unlock(&global_store->host_lock);
1112 ts = sdb_plugin_fetch_timeseries(type, id, opts);
1113 if (! ts) {
1114 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1115 "- %s fetcher callback returned no data for '%s'",
1116 hostname, metric, type, id);
1117 status = -1;
1118 }
1119 }
1121 ts_tojson(ts, buf);
1122 sdb_object_deref(SDB_OBJ(m));
1123 sdb_timeseries_destroy(ts);
1124 return status;
1125 } /* sdb_store_fetch_timeseries */
1127 int
1128 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
1129 sdb_store_lookup_cb cb, void *user_data)
1130 {
1131 sdb_avltree_iter_t *host_iter = NULL;
1132 int status = 0;
1134 if ((! global_store) || (! cb))
1135 return -1;
1137 if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
1138 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
1139 return -1;
1140 }
1142 pthread_rwlock_rdlock(&global_store->host_lock);
1143 host_iter = sdb_avltree_get_iter(global_store->hosts);
1144 if (! host_iter)
1145 status = -1;
1147 /* has_next returns false if the iterator is NULL */
1148 while (sdb_avltree_iter_has_next(host_iter)) {
1149 sdb_store_obj_t *host;
1150 sdb_avltree_iter_t *iter = NULL;
1152 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1153 assert(host);
1155 if (! sdb_store_matcher_matches(filter, host, NULL))
1156 continue;
1158 if (type == SDB_SERVICE)
1159 iter = sdb_avltree_get_iter(HOST(host)->services);
1160 else if (type == SDB_METRIC)
1161 iter = sdb_avltree_get_iter(HOST(host)->metrics);
1163 if (iter) {
1164 while (sdb_avltree_iter_has_next(iter)) {
1165 sdb_store_obj_t *obj;
1166 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1167 assert(obj);
1169 if (sdb_store_matcher_matches(m, obj, filter)) {
1170 if (cb(obj, filter, user_data)) {
1171 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1172 "an error while scanning");
1173 status = -1;
1174 break;
1175 }
1176 }
1177 }
1178 }
1179 else if (sdb_store_matcher_matches(m, host, filter)) {
1180 if (cb(host, filter, user_data)) {
1181 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1182 "an error while scanning");
1183 status = -1;
1184 }
1185 }
1187 sdb_avltree_iter_destroy(iter);
1188 if (status)
1189 break;
1190 }
1192 sdb_avltree_iter_destroy(host_iter);
1193 pthread_rwlock_unlock(&global_store->host_lock);
1194 return status;
1195 } /* sdb_store_scan */
1197 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */