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 <pthread.h>
48 /*
49 * private variables
50 */
52 static sdb_avltree_t *hosts = NULL;
53 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
55 /*
56 * private types
57 */
59 static sdb_type_t sdb_host_type;
60 static sdb_type_t sdb_service_type;
61 static sdb_type_t sdb_metric_type;
62 static sdb_type_t sdb_attribute_type;
64 static int
65 store_obj_init(sdb_object_t *obj, va_list ap)
66 {
67 sdb_store_obj_t *sobj = STORE_OBJ(obj);
69 sobj->type = va_arg(ap, int);
71 sobj->last_update = va_arg(ap, sdb_time_t);
72 sobj->interval = 0;
73 sobj->backends = NULL;
74 sobj->backends_num = 0;
75 sobj->parent = NULL;
76 return 0;
77 } /* store_obj_init */
79 static void
80 store_obj_destroy(sdb_object_t *obj)
81 {
82 sdb_store_obj_t *sobj = STORE_OBJ(obj);
83 size_t i;
85 for (i = 0; i < sobj->backends_num; ++i)
86 free(sobj->backends[i]);
87 free(sobj->backends);
88 sobj->backends = NULL;
89 sobj->backends_num = 0;
91 // We don't currently keep an extra reference for parent objects to
92 // avoid circular self-references which are not handled correctly by
93 // the ref-count base management layer.
94 //sdb_object_deref(SDB_OBJ(sobj->parent));
95 } /* store_obj_destroy */
97 static int
98 sdb_host_init(sdb_object_t *obj, va_list ap)
99 {
100 sdb_host_t *sobj = HOST(obj);
101 int ret;
103 /* this will consume the first argument (type) of ap */
104 ret = store_obj_init(obj, ap);
105 if (ret)
106 return ret;
108 sobj->services = sdb_avltree_create();
109 if (! sobj->services)
110 return -1;
111 sobj->metrics = sdb_avltree_create();
112 if (! sobj->metrics)
113 return -1;
114 sobj->attributes = sdb_avltree_create();
115 if (! sobj->attributes)
116 return -1;
117 return 0;
118 } /* sdb_host_init */
120 static void
121 sdb_host_destroy(sdb_object_t *obj)
122 {
123 sdb_host_t *sobj = HOST(obj);
124 assert(obj);
126 store_obj_destroy(obj);
128 if (sobj->services)
129 sdb_avltree_destroy(sobj->services);
130 if (sobj->metrics)
131 sdb_avltree_destroy(sobj->metrics);
132 if (sobj->attributes)
133 sdb_avltree_destroy(sobj->attributes);
134 } /* sdb_host_destroy */
136 static int
137 sdb_service_init(sdb_object_t *obj, va_list ap)
138 {
139 sdb_service_t *sobj = SVC(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->attributes = sdb_avltree_create();
148 if (! sobj->attributes)
149 return -1;
150 return 0;
151 } /* sdb_service_init */
153 static void
154 sdb_service_destroy(sdb_object_t *obj)
155 {
156 sdb_service_t *sobj = SVC(obj);
157 assert(obj);
159 store_obj_destroy(obj);
161 if (sobj->attributes)
162 sdb_avltree_destroy(sobj->attributes);
163 } /* sdb_service_destroy */
165 static int
166 sdb_metric_init(sdb_object_t *obj, va_list ap)
167 {
168 sdb_metric_t *sobj = METRIC(obj);
169 int ret;
171 /* this will consume the first argument (type) of ap */
172 ret = store_obj_init(obj, ap);
173 if (ret)
174 return ret;
176 sobj->attributes = sdb_avltree_create();
177 if (! sobj->attributes)
178 return -1;
180 sobj->store.type = sobj->store.id = NULL;
181 return 0;
182 } /* sdb_metric_init */
184 static void
185 sdb_metric_destroy(sdb_object_t *obj)
186 {
187 sdb_metric_t *sobj = METRIC(obj);
188 assert(obj);
190 store_obj_destroy(obj);
192 if (sobj->attributes)
193 sdb_avltree_destroy(sobj->attributes);
195 if (sobj->store.type)
196 free(sobj->store.type);
197 if (sobj->store.id)
198 free(sobj->store.id);
199 } /* sdb_metric_destroy */
201 static int
202 sdb_attr_init(sdb_object_t *obj, va_list ap)
203 {
204 const sdb_data_t *value;
205 int ret;
207 /* this will consume the first two arguments
208 * (type and last_update) of ap */
209 ret = store_obj_init(obj, ap);
210 if (ret)
211 return ret;
212 value = va_arg(ap, const sdb_data_t *);
214 if (value)
215 if (sdb_data_copy(&ATTR(obj)->value, value))
216 return -1;
217 return 0;
218 } /* sdb_attr_init */
220 static void
221 sdb_attr_destroy(sdb_object_t *obj)
222 {
223 assert(obj);
225 store_obj_destroy(obj);
226 sdb_data_free_datum(&ATTR(obj)->value);
227 } /* sdb_attr_destroy */
229 static sdb_type_t sdb_host_type = {
230 sizeof(sdb_host_t),
231 sdb_host_init,
232 sdb_host_destroy
233 };
235 static sdb_type_t sdb_service_type = {
236 sizeof(sdb_service_t),
237 sdb_service_init,
238 sdb_service_destroy
239 };
241 static sdb_type_t sdb_metric_type = {
242 sizeof(sdb_metric_t),
243 sdb_metric_init,
244 sdb_metric_destroy
245 };
247 static sdb_type_t sdb_attribute_type = {
248 sizeof(sdb_attribute_t),
249 sdb_attr_init,
250 sdb_attr_destroy
251 };
253 /*
254 * private helper functions
255 */
257 static sdb_host_t *
258 lookup_host(const char *name, _Bool canonicalize)
259 {
260 sdb_host_t *host;
261 char *cname;
263 assert(name);
264 if (! canonicalize)
265 return HOST(sdb_avltree_lookup(hosts, name));
267 cname = strdup(name);
268 cname = sdb_plugin_cname(cname);
269 if (! cname) {
270 sdb_log(SDB_LOG_ERR, "store: strdup failed");
271 return NULL;
272 }
274 host = HOST(sdb_avltree_lookup(hosts, cname));
275 free(cname);
276 return host;
277 } /* lookup_host */
279 static int
280 record_backend(sdb_store_obj_t *obj)
281 {
282 const sdb_plugin_info_t *info;
283 char **tmp;
284 size_t i;
286 info = sdb_plugin_current();
287 if (! info)
288 return 0;
290 for (i = 0; i < obj->backends_num; ++i)
291 if (!strcasecmp(obj->backends[i], info->plugin_name))
292 return 0;
294 tmp = realloc(obj->backends,
295 (obj->backends_num + 1) * sizeof(*obj->backends));
296 if (! tmp)
297 return -1;
299 obj->backends = tmp;
300 obj->backends[obj->backends_num] = strdup(info->plugin_name);
301 if (! obj->backends[obj->backends_num])
302 return -1;
304 ++obj->backends_num;
305 return 0;
306 } /* record_backend */
308 static int
309 store_obj(sdb_store_obj_t *parent, sdb_avltree_t *parent_tree,
310 int type, const char *name, sdb_time_t last_update,
311 sdb_store_obj_t **updated_obj)
312 {
313 sdb_store_obj_t *old, *new;
314 int status = 0;
316 assert(parent_tree);
318 if (last_update <= 0)
319 last_update = sdb_gettime();
321 old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
322 if (old) {
323 if (old->last_update > last_update) {
324 sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
325 "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
326 SDB_STORE_TYPE_TO_NAME(type), name,
327 last_update, old->last_update);
328 /* don't report an error; the object may be updated by multiple
329 * backends */
330 status = 1;
331 }
332 else if (old->last_update == last_update) {
333 /* don't report an error and also don't even log this to avoid
334 * excessive noise on high sampling frequencies */
335 status = 1;
336 }
337 else {
338 sdb_time_t interval = last_update - old->last_update;
339 old->last_update = last_update;
340 if (interval) {
341 if (old->interval)
342 old->interval = (sdb_time_t)((0.9 * (double)old->interval)
343 + (0.1 * (double)interval));
344 else
345 old->interval = interval;
346 }
347 }
349 new = old;
350 sdb_object_deref(SDB_OBJ(old));
351 }
352 else {
353 if (type == SDB_ATTRIBUTE) {
354 /* the value will be updated by the caller */
355 new = STORE_OBJ(sdb_object_create(name, sdb_attribute_type,
356 type, last_update, NULL));
357 }
358 else {
359 sdb_type_t t;
360 t = type == SDB_HOST
361 ? sdb_host_type
362 : type == SDB_SERVICE
363 ? sdb_service_type
364 : sdb_metric_type;
365 new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
366 }
368 if (new) {
369 status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
371 /* pass control to the tree or destroy in case of an error */
372 sdb_object_deref(SDB_OBJ(new));
373 }
374 else {
375 char errbuf[1024];
376 sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
377 SDB_STORE_TYPE_TO_NAME(type), name,
378 sdb_strerror(errno, errbuf, sizeof(errbuf)));
379 status = -1;
380 }
381 }
383 if (status < 0)
384 return status;
385 assert(new);
387 if (new->parent != parent) {
388 // Avoid circular self-references which are not handled
389 // correctly by the ref-count based management layer.
390 //sdb_object_deref(SDB_OBJ(new->parent));
391 //sdb_object_ref(SDB_OBJ(parent));
392 new->parent = parent;
393 }
395 if (updated_obj)
396 *updated_obj = new;
398 if (record_backend(new))
399 return -1;
400 return status;
401 } /* store_obj */
403 static int
404 store_attr(sdb_store_obj_t *parent, sdb_avltree_t *attributes,
405 const char *key, const sdb_data_t *value, sdb_time_t last_update)
406 {
407 sdb_store_obj_t *attr = NULL;
408 int status;
410 status = store_obj(parent, attributes, SDB_ATTRIBUTE,
411 key, last_update, &attr);
412 if (status)
413 return status;
415 /* don't update unchanged values */
416 if (! sdb_data_cmp(&ATTR(attr)->value, value))
417 return status;
419 assert(attr);
420 if (sdb_data_copy(&ATTR(attr)->value, value))
421 return -1;
422 return status;
423 } /* store_attr */
425 /* The host_lock has to be acquired before calling this function. */
426 static sdb_avltree_t *
427 get_host_children(sdb_host_t *host, int type)
428 {
429 if ((type != SDB_SERVICE) && (type != SDB_METRIC)
430 && (type != SDB_ATTRIBUTE))
431 return NULL;
433 if (! host)
434 return NULL;
436 if (type == SDB_ATTRIBUTE)
437 return host->attributes;
438 else if (type == SDB_METRIC)
439 return host->metrics;
440 else
441 return host->services;
442 } /* get_host_children */
444 /*
445 * ts_tojson serializes a time-series to JSON.
446 *
447 * The function never returns an error. Rather, an error message will be part
448 * of the serialized data.
449 */
450 static void
451 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
452 {
453 char start_str[64];
454 char end_str[64];
456 size_t i;
458 /* TODO: make time format configurable */
459 if (! sdb_strftime(start_str, sizeof(start_str),
460 "%F %T %z", ts->start))
461 snprintf(start_str, sizeof(start_str), "<error>");
462 start_str[sizeof(start_str) - 1] = '\0';
463 if (! sdb_strftime(end_str, sizeof(end_str),
464 "%F %T %z", ts->end))
465 snprintf(end_str, sizeof(end_str), "<error>");
466 end_str[sizeof(end_str) - 1] = '\0';
468 sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
469 start_str, end_str);
471 for (i = 0; i < ts->data_names_len; ++i) {
472 size_t j;
473 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
475 for (j = 0; j < ts->data_len; ++j) {
476 char time_str[64];
478 if (! sdb_strftime(time_str, sizeof(time_str),
479 "%F %T %z", ts->data[i][j].timestamp))
480 snprintf(time_str, sizeof(time_str), "<error>");
481 time_str[sizeof(time_str) - 1] = '\0';
483 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
484 "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
486 if (j < ts->data_len - 1)
487 sdb_strbuf_append(buf, ",");
488 }
490 if (i < ts->data_names_len - 1)
491 sdb_strbuf_append(buf, "],");
492 else
493 sdb_strbuf_append(buf, "]");
494 }
495 sdb_strbuf_append(buf, "}}");
496 } /* ts_tojson */
498 /*
499 * public API
500 */
502 void
503 sdb_store_clear(void)
504 {
505 sdb_avltree_destroy(hosts);
506 hosts = NULL;
507 } /* sdb_store_clear */
509 int
510 sdb_store_host(const char *name, sdb_time_t last_update)
511 {
512 char *cname = NULL;
513 int status = 0;
515 if (! name)
516 return -1;
518 cname = sdb_plugin_cname(strdup(name));
519 if (! cname) {
520 sdb_log(SDB_LOG_ERR, "store: strdup failed");
521 return -1;
522 }
524 pthread_rwlock_wrlock(&host_lock);
525 if (! hosts)
526 if (! (hosts = sdb_avltree_create()))
527 status = -1;
529 if (! status)
530 status = store_obj(NULL, hosts, SDB_HOST, cname, last_update, NULL);
531 pthread_rwlock_unlock(&host_lock);
533 free(cname);
534 return status;
535 } /* sdb_store_host */
537 _Bool
538 sdb_store_has_host(const char *name)
539 {
540 sdb_host_t *host;
542 if (! name)
543 return NULL;
545 host = lookup_host(name, /* canonicalize = */ 0);
546 sdb_object_deref(SDB_OBJ(host));
547 return host != NULL;
548 } /* sdb_store_has_host */
550 sdb_store_obj_t *
551 sdb_store_get_host(const char *name)
552 {
553 sdb_host_t *host;
555 if (! name)
556 return NULL;
558 host = lookup_host(name, /* canonicalize = */ 0);
559 if (! host)
560 return NULL;
562 return STORE_OBJ(host);
563 } /* sdb_store_get_host */
565 int
566 sdb_store_attribute(const char *hostname,
567 const char *key, const sdb_data_t *value,
568 sdb_time_t last_update)
569 {
570 sdb_host_t *host;
571 sdb_avltree_t *attrs;
572 int status = 0;
574 if ((! hostname) || (! key))
575 return -1;
577 pthread_rwlock_wrlock(&host_lock);
578 host = lookup_host(hostname, /* canonicalize = */ 1);
579 attrs = get_host_children(host, SDB_ATTRIBUTE);
580 if (! attrs) {
581 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
582 "host '%s' not found", key, hostname);
583 status = -1;
584 }
586 if (! status)
587 status = store_attr(STORE_OBJ(host), attrs, key, value, last_update);
589 sdb_object_deref(SDB_OBJ(host));
590 pthread_rwlock_unlock(&host_lock);
591 return status;
592 } /* sdb_store_attribute */
594 int
595 sdb_store_service(const char *hostname, const char *name,
596 sdb_time_t last_update)
597 {
598 sdb_host_t *host;
599 sdb_avltree_t *services;
601 int status = 0;
603 if ((! hostname) || (! name))
604 return -1;
606 pthread_rwlock_wrlock(&host_lock);
607 host = lookup_host(hostname, /* canonicalize = */ 1);
608 services = get_host_children(host, SDB_SERVICE);
609 if (! services) {
610 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
611 "host '%s' not found", name, hostname);
612 status = -1;
613 }
615 if (! status)
616 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
617 name, last_update, NULL);
619 sdb_object_deref(SDB_OBJ(host));
620 pthread_rwlock_unlock(&host_lock);
621 return status;
622 } /* sdb_store_service */
624 int
625 sdb_store_service_attr(const char *hostname, const char *service,
626 const char *key, const sdb_data_t *value, sdb_time_t last_update)
627 {
628 sdb_host_t *host;
629 sdb_service_t *svc;
630 sdb_avltree_t *services;
631 int status = 0;
633 if ((! hostname) || (! service) || (! key))
634 return -1;
636 pthread_rwlock_wrlock(&host_lock);
637 host = lookup_host(hostname, /* canonicalize = */ 1);
638 services = get_host_children(host, SDB_SERVICE);
639 sdb_object_deref(SDB_OBJ(host));
640 if (! services) {
641 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
642 "for service '%s' - host '%ss' not found",
643 key, service, hostname);
644 pthread_rwlock_unlock(&host_lock);
645 return -1;
646 }
648 svc = SVC(sdb_avltree_lookup(services, service));
649 if (! svc) {
650 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
651 "service '%s/%s' not found", key, hostname, service);
652 status = -1;
653 }
655 if (! status)
656 status = store_attr(STORE_OBJ(svc), svc->attributes,
657 key, value, last_update);
659 sdb_object_deref(SDB_OBJ(svc));
660 pthread_rwlock_unlock(&host_lock);
661 return status;
662 } /* sdb_store_service_attr */
664 int
665 sdb_store_metric(const char *hostname, const char *name,
666 sdb_metric_store_t *store, sdb_time_t last_update)
667 {
668 sdb_store_obj_t *obj = NULL;
669 sdb_host_t *host;
670 sdb_metric_t *metric;
672 sdb_avltree_t *metrics;
674 int status = 0;
676 if ((! hostname) || (! name))
677 return -1;
678 if (store && ((! store->type) || (! store->id)))
679 return -1;
681 pthread_rwlock_wrlock(&host_lock);
682 host = lookup_host(hostname, /* canonicalize = */ 1);
683 metrics = get_host_children(host, SDB_METRIC);
684 if (! metrics) {
685 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
686 "host '%s' not found", name, hostname);
687 status = -1;
688 }
690 if (! status)
691 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
692 name, last_update, &obj);
693 sdb_object_deref(SDB_OBJ(host));
695 if (status || (! store)) {
696 pthread_rwlock_unlock(&host_lock);
697 return status;
698 }
700 assert(obj);
701 metric = METRIC(obj);
703 if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
704 if (metric->store.type)
705 free(metric->store.type);
706 metric->store.type = strdup(store->type);
707 }
708 if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
709 if (metric->store.id)
710 free(metric->store.id);
711 metric->store.id = strdup(store->id);
712 }
714 if ((! metric->store.type) || (! metric->store.id)) {
715 if (metric->store.type)
716 free(metric->store.type);
717 if (metric->store.id)
718 free(metric->store.id);
719 metric->store.type = metric->store.id = NULL;
720 status = -1;
721 }
722 pthread_rwlock_unlock(&host_lock);
723 return status;
724 } /* sdb_store_metric */
726 int
727 sdb_store_metric_attr(const char *hostname, const char *metric,
728 const char *key, const sdb_data_t *value, sdb_time_t last_update)
729 {
730 sdb_avltree_t *metrics;
731 sdb_host_t *host;
732 sdb_metric_t *m;
733 int status = 0;
735 if ((! hostname) || (! metric) || (! key))
736 return -1;
738 pthread_rwlock_wrlock(&host_lock);
739 host = lookup_host(hostname, /* canonicalize = */ 1);
740 metrics = get_host_children(host, SDB_METRIC);
741 sdb_object_deref(SDB_OBJ(host));
742 if (! metrics) {
743 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
744 "for metric '%s' - host '%s' not found",
745 key, metric, hostname);
746 pthread_rwlock_unlock(&host_lock);
747 return -1;
748 }
750 m = METRIC(sdb_avltree_lookup(metrics, metric));
751 if (! m) {
752 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
753 "metric '%s/%s' not found", key, hostname, metric);
754 status = -1;
755 }
757 if (! status)
758 status = store_attr(STORE_OBJ(m), m->attributes,
759 key, value, last_update);
761 sdb_object_deref(SDB_OBJ(m));
762 pthread_rwlock_unlock(&host_lock);
763 return status;
764 } /* sdb_store_metric_attr */
766 sdb_store_obj_t *
767 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
768 {
769 sdb_avltree_t *children;
771 if ((! host) || (host->type != SDB_HOST) || (! name))
772 return NULL;
774 children = get_host_children(HOST(host), type);
775 if (! children)
776 return NULL;
777 return STORE_OBJ(sdb_avltree_lookup(children, name));
778 } /* sdb_store_get_child */
780 int
781 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
782 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
783 {
784 sdb_avltree_t *metrics;
785 sdb_host_t *host;
786 sdb_metric_t *m;
788 sdb_timeseries_t *ts;
790 if ((! hostname) || (! metric) || (! opts) || (! buf))
791 return -1;
793 pthread_rwlock_rdlock(&host_lock);
794 host = lookup_host(hostname, /* canonicalize = */ 1);
795 metrics = get_host_children(host, SDB_METRIC);
796 sdb_object_deref(SDB_OBJ(host));
797 if (! metrics) {
798 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
799 "- host '%s' not found", hostname, metric, hostname);
800 pthread_rwlock_unlock(&host_lock);
801 return -1;
802 }
804 m = METRIC(sdb_avltree_lookup(metrics, metric));
805 if (! m) {
806 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
807 "- metric '%s' not found", hostname, metric, metric);
808 pthread_rwlock_unlock(&host_lock);
809 return -1;
810 }
812 if ((! m->store.type) || (! m->store.id)) {
813 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
814 "- no data-store configured for the stored metric",
815 hostname, metric);
816 pthread_rwlock_unlock(&host_lock);
817 return -1;
818 }
820 {
821 char type[strlen(m->store.type) + 1];
822 char id[strlen(m->store.id) + 1];
824 strncpy(type, m->store.type, sizeof(type));
825 strncpy(id, m->store.id, sizeof(id));
826 pthread_rwlock_unlock(&host_lock);
828 ts = sdb_plugin_fetch_timeseries(type, id, opts);
829 if (! ts) {
830 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
831 "- %s fetcher callback returned no data for '%s'",
832 hostname, metric, type, id);
833 return -1;
834 }
835 }
837 ts_tojson(ts, buf);
838 sdb_timeseries_destroy(ts);
839 return 0;
840 } /* sdb_store_fetch_timeseries */
842 int
843 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
844 {
845 sdb_data_t tmp;
847 if (! obj)
848 return -1;
850 switch (field) {
851 case SDB_FIELD_NAME:
852 tmp.type = SDB_TYPE_STRING;
853 tmp.data.string = strdup(SDB_OBJ(obj)->name);
854 if (! tmp.data.string)
855 return -1;
856 break;
857 case SDB_FIELD_LAST_UPDATE:
858 tmp.type = SDB_TYPE_DATETIME;
859 tmp.data.datetime = obj->last_update;
860 break;
861 case SDB_FIELD_AGE:
862 tmp.type = SDB_TYPE_DATETIME;
863 tmp.data.datetime = sdb_gettime() - obj->last_update;
864 break;
865 case SDB_FIELD_INTERVAL:
866 tmp.type = SDB_TYPE_DATETIME;
867 tmp.data.datetime = obj->interval;
868 break;
869 case SDB_FIELD_BACKEND:
870 {
871 tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
872 tmp.data.array.length = obj->backends_num;
873 tmp.data.array.values = obj->backends;
874 return sdb_data_copy(res, &tmp);
875 }
876 default:
877 return -1;
878 }
879 if (res)
880 *res = tmp;
881 else
882 sdb_data_free_datum(&tmp);
883 return 0;
884 } /* sdb_store_get_field */
886 int
887 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
888 sdb_store_matcher_t *filter)
889 {
890 sdb_avltree_t *tree = NULL;
891 sdb_store_obj_t *attr;
893 if ((! obj) || (! name))
894 return -1;
896 if (obj->type == SDB_HOST)
897 tree = HOST(obj)->attributes;
898 else if (obj->type == SDB_SERVICE)
899 tree = SVC(obj)->attributes;
900 else if (obj->type == SDB_METRIC)
901 tree = METRIC(obj)->attributes;
903 if (! tree)
904 return -1;
906 attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
907 if (! attr)
908 return -1;
909 if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
910 sdb_object_deref(SDB_OBJ(attr));
911 return -1;
912 }
914 assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
915 if (res)
916 sdb_data_copy(res, &ATTR(attr)->value);
917 sdb_object_deref(SDB_OBJ(attr));
918 return 0;
919 } /* sdb_store_get_attr */
921 int
922 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
923 sdb_store_lookup_cb cb, void *user_data)
924 {
925 sdb_avltree_iter_t *host_iter;
926 int status = 0;
928 if (! cb)
929 return -1;
931 if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
932 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
933 return -1;
934 }
936 pthread_rwlock_rdlock(&host_lock);
938 host_iter = sdb_avltree_get_iter(hosts);
939 if (! host_iter)
940 status = -1;
942 /* has_next returns false if the iterator is NULL */
943 while (sdb_avltree_iter_has_next(host_iter)) {
944 sdb_store_obj_t *host;
945 sdb_avltree_iter_t *iter = NULL;
947 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
948 assert(host);
950 if (! sdb_store_matcher_matches(filter, host, NULL))
951 continue;
953 if (type == SDB_SERVICE)
954 iter = sdb_avltree_get_iter(HOST(host)->services);
955 else if (type == SDB_METRIC)
956 iter = sdb_avltree_get_iter(HOST(host)->metrics);
958 if (iter) {
959 while (sdb_avltree_iter_has_next(iter)) {
960 sdb_store_obj_t *obj;
961 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
962 assert(obj);
964 if (sdb_store_matcher_matches(m, obj, filter)) {
965 if (cb(obj, filter, user_data)) {
966 sdb_log(SDB_LOG_ERR, "store: Callback returned "
967 "an error while scanning");
968 status = -1;
969 break;
970 }
971 }
972 }
973 }
974 else if (sdb_store_matcher_matches(m, host, filter)) {
975 if (cb(host, filter, user_data)) {
976 sdb_log(SDB_LOG_ERR, "store: Callback returned "
977 "an error while scanning");
978 status = -1;
979 }
980 }
982 sdb_avltree_iter_destroy(iter);
983 if (status)
984 break;
985 }
987 sdb_avltree_iter_destroy(host_iter);
988 pthread_rwlock_unlock(&host_lock);
989 return status;
990 } /* sdb_store_scan */
992 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */