f39f6784a9e788f2606011fcd11115ea1704f5ec
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_attribute(const char *hostname,
604 const char *key, const sdb_data_t *value,
605 sdb_time_t last_update)
606 {
607 sdb_host_t *host;
608 sdb_avltree_t *attrs;
609 int status = 0;
611 if ((! global_store) || (! hostname) || (! key))
612 return -1;
614 pthread_rwlock_wrlock(&global_store->host_lock);
615 host = lookup_host(global_store, 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(&global_store->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_host(const char *name, sdb_time_t last_update)
636 {
637 char *cname = NULL;
638 int status = 0;
640 if ((! global_store) || (! name))
641 return -1;
643 cname = sdb_plugin_cname(strdup(name));
644 if (! cname) {
645 sdb_log(SDB_LOG_ERR, "store: strdup failed");
646 return -1;
647 }
649 pthread_rwlock_wrlock(&global_store->host_lock);
650 status = store_obj(NULL, global_store->hosts,
651 SDB_HOST, cname, last_update, NULL);
652 pthread_rwlock_unlock(&global_store->host_lock);
654 if (sdb_plugin_store_host(name, last_update))
655 status = -1;
657 free(cname);
658 return status;
659 } /* sdb_store_host */
661 int
662 sdb_store_service_attr(const char *hostname, const char *service,
663 const char *key, const sdb_data_t *value, sdb_time_t last_update)
664 {
665 sdb_host_t *host;
666 sdb_service_t *svc;
667 sdb_avltree_t *services;
668 int status = 0;
670 if ((! global_store) || (! hostname) || (! service) || (! key))
671 return -1;
673 pthread_rwlock_wrlock(&global_store->host_lock);
674 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
675 services = get_host_children(host, SDB_SERVICE);
676 sdb_object_deref(SDB_OBJ(host));
677 if (! services) {
678 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
679 "for service '%s' - host '%ss' not found",
680 key, service, hostname);
681 pthread_rwlock_unlock(&global_store->host_lock);
682 return -1;
683 }
685 svc = SVC(sdb_avltree_lookup(services, service));
686 if (! svc) {
687 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
688 "service '%s/%s' not found", key, hostname, service);
689 status = -1;
690 }
692 if (! status)
693 status = store_attr(STORE_OBJ(svc), svc->attributes,
694 key, value, last_update);
696 sdb_object_deref(SDB_OBJ(svc));
697 pthread_rwlock_unlock(&global_store->host_lock);
699 if (sdb_plugin_store_service_attribute(hostname, service,
700 key, value, last_update))
701 status = -1;
702 return status;
703 } /* sdb_store_service_attr */
705 int
706 sdb_store_service(const char *hostname, const char *name,
707 sdb_time_t last_update)
708 {
709 sdb_host_t *host;
710 sdb_avltree_t *services;
711 sdb_data_t d;
713 int status = 0;
715 if ((! global_store) || (! hostname) || (! name))
716 return -1;
718 pthread_rwlock_wrlock(&global_store->host_lock);
719 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
720 services = get_host_children(host, SDB_SERVICE);
721 if (! services) {
722 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
723 "host '%s' not found", name, hostname);
724 status = -1;
725 }
727 if (! status)
728 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
729 name, last_update, NULL);
731 sdb_object_deref(SDB_OBJ(host));
732 pthread_rwlock_unlock(&global_store->host_lock);
734 if (status)
735 return status;
737 if (sdb_plugin_store_service(hostname, name, last_update))
738 status = -1;
740 /* record the hostname as an attribute */
741 d.type = SDB_TYPE_STRING;
742 d.data.string = SDB_OBJ(host)->name;
743 if (sdb_store_service_attr(hostname, name, "hostname", &d, last_update))
744 status = -1;
745 return status;
746 } /* sdb_store_service */
748 int
749 sdb_store_metric_attr(const char *hostname, const char *metric,
750 const char *key, const sdb_data_t *value, sdb_time_t last_update)
751 {
752 sdb_avltree_t *metrics;
753 sdb_host_t *host;
754 sdb_metric_t *m;
755 int status = 0;
757 if ((! global_store) || (! hostname) || (! metric) || (! key))
758 return -1;
760 pthread_rwlock_wrlock(&global_store->host_lock);
761 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
762 metrics = get_host_children(host, SDB_METRIC);
763 sdb_object_deref(SDB_OBJ(host));
764 if (! metrics) {
765 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
766 "for metric '%s' - host '%s' not found",
767 key, metric, hostname);
768 pthread_rwlock_unlock(&global_store->host_lock);
769 return -1;
770 }
772 m = METRIC(sdb_avltree_lookup(metrics, metric));
773 if (! m) {
774 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
775 "metric '%s/%s' not found", key, hostname, metric);
776 status = -1;
777 }
779 if (! status)
780 status = store_attr(STORE_OBJ(m), m->attributes,
781 key, value, last_update);
783 sdb_object_deref(SDB_OBJ(m));
784 pthread_rwlock_unlock(&global_store->host_lock);
786 if (sdb_plugin_store_metric_attribute(hostname, metric,
787 key, value, last_update))
788 status = -1;
789 return status;
790 } /* sdb_store_metric_attr */
792 int
793 sdb_store_metric(const char *hostname, const char *name,
794 sdb_metric_store_t *store, sdb_time_t last_update)
795 {
796 sdb_store_obj_t *obj = NULL;
797 sdb_host_t *host;
798 sdb_metric_t *metric;
799 sdb_data_t d;
801 sdb_avltree_t *metrics;
803 int status = 0;
805 if ((! global_store) || (! hostname) || (! name))
806 return -1;
808 if (store) {
809 if ((store->type != NULL) != (store->id != NULL))
810 return -1;
811 else if (! store->type)
812 store = NULL;
813 }
815 pthread_rwlock_wrlock(&global_store->host_lock);
816 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
817 metrics = get_host_children(host, SDB_METRIC);
818 if (! metrics) {
819 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
820 "host '%s' not found", name, hostname);
821 status = -1;
822 }
824 if (! status)
825 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
826 name, last_update, &obj);
827 sdb_object_deref(SDB_OBJ(host));
829 if (status) {
830 pthread_rwlock_unlock(&global_store->host_lock);
831 return status;
832 }
834 assert(obj);
835 metric = METRIC(obj);
837 if (store)
838 if (store_metric_store(metric, store))
839 status = -1;
840 pthread_rwlock_unlock(&global_store->host_lock);
842 if (sdb_plugin_store_metric(hostname, name, store, last_update))
843 status = -1;
845 /* record the hostname as an attribute */
846 d.type = SDB_TYPE_STRING;
847 d.data.string = SDB_OBJ(host)->name;
848 if (sdb_store_metric_attr(hostname, name, "hostname", &d, last_update))
849 status = -1;
850 return status;
851 } /* sdb_store_metric */
853 bool
854 sdb_store_has_host(const char *name)
855 {
856 sdb_host_t *host;
858 if ((! global_store) || (! name))
859 return false;
861 host = lookup_host(global_store, name, /* canonicalize = */ 0);
862 sdb_object_deref(SDB_OBJ(host));
863 return host != NULL;
864 } /* sdb_store_has_host */
866 sdb_store_obj_t *
867 sdb_store_get_host(const char *name)
868 {
869 sdb_host_t *host;
871 if ((! global_store) || (! name))
872 return NULL;
874 host = lookup_host(global_store, name, /* canonicalize = */ 0);
875 if (! host)
876 return NULL;
878 return STORE_OBJ(host);
879 } /* sdb_store_get_host */
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_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
897 {
898 sdb_data_t tmp;
900 if (! obj)
901 return -1;
903 switch (field) {
904 case SDB_FIELD_NAME:
905 tmp.type = SDB_TYPE_STRING;
906 tmp.data.string = strdup(SDB_OBJ(obj)->name);
907 if (! tmp.data.string)
908 return -1;
909 break;
910 case SDB_FIELD_LAST_UPDATE:
911 tmp.type = SDB_TYPE_DATETIME;
912 tmp.data.datetime = obj->last_update;
913 break;
914 case SDB_FIELD_AGE:
915 tmp.type = SDB_TYPE_DATETIME;
916 tmp.data.datetime = sdb_gettime() - obj->last_update;
917 break;
918 case SDB_FIELD_INTERVAL:
919 tmp.type = SDB_TYPE_DATETIME;
920 tmp.data.datetime = obj->interval;
921 break;
922 case SDB_FIELD_BACKEND:
923 if (! res)
924 return 0;
925 tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
926 tmp.data.array.length = obj->backends_num;
927 tmp.data.array.values = obj->backends;
928 return sdb_data_copy(res, &tmp);
929 case SDB_FIELD_VALUE:
930 if (obj->type != SDB_ATTRIBUTE)
931 return -1;
932 if (! res)
933 return 0;
934 return sdb_data_copy(res, &ATTR(obj)->value);
935 case SDB_FIELD_TIMESERIES:
936 if (obj->type != SDB_METRIC)
937 return -1;
938 tmp.type = SDB_TYPE_BOOLEAN;
939 tmp.data.boolean = METRIC(obj)->store.type != NULL;
940 default:
941 return -1;
942 }
943 if (res)
944 *res = tmp;
945 else
946 sdb_data_free_datum(&tmp);
947 return 0;
948 } /* sdb_store_get_field */
950 int
951 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
952 sdb_store_matcher_t *filter)
953 {
954 sdb_avltree_t *tree = NULL;
955 sdb_store_obj_t *attr;
957 if ((! obj) || (! name))
958 return -1;
960 if (obj->type == SDB_HOST)
961 tree = HOST(obj)->attributes;
962 else if (obj->type == SDB_SERVICE)
963 tree = SVC(obj)->attributes;
964 else if (obj->type == SDB_METRIC)
965 tree = METRIC(obj)->attributes;
967 if (! tree)
968 return -1;
970 attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
971 if (! attr)
972 return -1;
973 if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
974 sdb_object_deref(SDB_OBJ(attr));
975 return -1;
976 }
978 assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
979 if (res)
980 sdb_data_copy(res, &ATTR(attr)->value);
981 sdb_object_deref(SDB_OBJ(attr));
982 return 0;
983 } /* sdb_store_get_attr */
985 int
986 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
987 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
988 {
989 sdb_avltree_t *metrics;
990 sdb_host_t *host;
991 sdb_metric_t *m;
993 sdb_timeseries_t *ts;
995 int status = 0;
997 if ((! global_store) || (! hostname) || (! metric) || (! opts) || (! buf))
998 return -1;
1000 pthread_rwlock_rdlock(&global_store->host_lock);
1001 host = lookup_host(global_store, hostname, /* canonicalize = */ 1);
1002 metrics = get_host_children(host, SDB_METRIC);
1003 sdb_object_deref(SDB_OBJ(host));
1004 if (! metrics) {
1005 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1006 "- host '%s' not found", hostname, metric, hostname);
1007 pthread_rwlock_unlock(&global_store->host_lock);
1008 return -1;
1009 }
1011 m = METRIC(sdb_avltree_lookup(metrics, metric));
1012 if (! m) {
1013 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1014 "- metric '%s' not found", hostname, metric, metric);
1015 pthread_rwlock_unlock(&global_store->host_lock);
1016 return -1;
1017 }
1019 if ((! m->store.type) || (! m->store.id)) {
1020 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1021 "- no data-store configured for the stored metric",
1022 hostname, metric);
1023 sdb_object_deref(SDB_OBJ(m));
1024 pthread_rwlock_unlock(&global_store->host_lock);
1025 return -1;
1026 }
1028 {
1029 char type[strlen(m->store.type) + 1];
1030 char id[strlen(m->store.id) + 1];
1032 strncpy(type, m->store.type, sizeof(type));
1033 strncpy(id, m->store.id, sizeof(id));
1034 pthread_rwlock_unlock(&global_store->host_lock);
1036 ts = sdb_plugin_fetch_timeseries(type, id, opts);
1037 if (! ts) {
1038 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1039 "- %s fetcher callback returned no data for '%s'",
1040 hostname, metric, type, id);
1041 status = -1;
1042 }
1043 }
1045 ts_tojson(ts, buf);
1046 sdb_object_deref(SDB_OBJ(m));
1047 sdb_timeseries_destroy(ts);
1048 return status;
1049 } /* sdb_store_fetch_timeseries */
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 : */