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 assert((type == SDB_SERVICE) || (type == SDB_METRIC)
430 || (type == SDB_ATTRIBUTE));
432 if (! host)
433 return NULL;
435 if (type == SDB_ATTRIBUTE)
436 return host->attributes;
437 else if (type == SDB_METRIC)
438 return host->metrics;
439 else
440 return host->services;
441 } /* get_host_children */
443 /*
444 * ts_tojson serializes a time-series to JSON.
445 *
446 * The function never returns an error. Rather, an error message will be part
447 * of the serialized data.
448 */
449 static void
450 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
451 {
452 char start_str[64];
453 char end_str[64];
455 size_t i;
457 /* TODO: make time format configurable */
458 if (! sdb_strftime(start_str, sizeof(start_str),
459 "%F %T %z", ts->start))
460 snprintf(start_str, sizeof(start_str), "<error>");
461 start_str[sizeof(start_str) - 1] = '\0';
462 if (! sdb_strftime(end_str, sizeof(end_str),
463 "%F %T %z", ts->end))
464 snprintf(end_str, sizeof(end_str), "<error>");
465 end_str[sizeof(end_str) - 1] = '\0';
467 sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
468 start_str, end_str);
470 for (i = 0; i < ts->data_names_len; ++i) {
471 size_t j;
472 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
474 for (j = 0; j < ts->data_len; ++j) {
475 char time_str[64];
477 if (! sdb_strftime(time_str, sizeof(time_str),
478 "%F %T %z", ts->data[i][j].timestamp))
479 snprintf(time_str, sizeof(time_str), "<error>");
480 time_str[sizeof(time_str) - 1] = '\0';
482 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
483 "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
485 if (j < ts->data_len - 1)
486 sdb_strbuf_append(buf, ",");
487 }
489 if (i < ts->data_names_len - 1)
490 sdb_strbuf_append(buf, "],");
491 else
492 sdb_strbuf_append(buf, "]");
493 }
494 sdb_strbuf_append(buf, "}}");
495 } /* ts_tojson */
497 /*
498 * public API
499 */
501 void
502 sdb_store_clear(void)
503 {
504 sdb_avltree_destroy(hosts);
505 hosts = NULL;
506 } /* sdb_store_clear */
508 int
509 sdb_store_host(const char *name, sdb_time_t last_update)
510 {
511 char *cname = NULL;
512 int status = 0;
514 if (! name)
515 return -1;
517 cname = sdb_plugin_cname(strdup(name));
518 if (! cname) {
519 sdb_log(SDB_LOG_ERR, "store: strdup failed");
520 return -1;
521 }
523 pthread_rwlock_wrlock(&host_lock);
524 if (! hosts)
525 if (! (hosts = sdb_avltree_create()))
526 status = -1;
528 if (! status)
529 status = store_obj(NULL, hosts, SDB_HOST, cname, last_update, NULL);
530 pthread_rwlock_unlock(&host_lock);
532 free(cname);
533 return status;
534 } /* sdb_store_host */
536 _Bool
537 sdb_store_has_host(const char *name)
538 {
539 sdb_host_t *host;
541 if (! name)
542 return NULL;
544 host = lookup_host(name, /* canonicalize = */ 0);
545 sdb_object_deref(SDB_OBJ(host));
546 return host != NULL;
547 } /* sdb_store_has_host */
549 sdb_store_obj_t *
550 sdb_store_get_host(const char *name)
551 {
552 sdb_host_t *host;
554 if (! name)
555 return NULL;
557 host = lookup_host(name, /* canonicalize = */ 0);
558 if (! host)
559 return NULL;
561 return STORE_OBJ(host);
562 } /* sdb_store_get_host */
564 int
565 sdb_store_attribute(const char *hostname,
566 const char *key, const sdb_data_t *value,
567 sdb_time_t last_update)
568 {
569 sdb_host_t *host;
570 sdb_avltree_t *attrs;
571 int status = 0;
573 if ((! hostname) || (! key))
574 return -1;
576 pthread_rwlock_wrlock(&host_lock);
577 host = lookup_host(hostname, /* canonicalize = */ 1);
578 attrs = get_host_children(host, SDB_ATTRIBUTE);
579 if (! attrs) {
580 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
581 "host '%s' not found", key, hostname);
582 status = -1;
583 }
585 if (! status)
586 status = store_attr(STORE_OBJ(host), attrs, key, value, last_update);
588 sdb_object_deref(SDB_OBJ(host));
589 pthread_rwlock_unlock(&host_lock);
590 return status;
591 } /* sdb_store_attribute */
593 int
594 sdb_store_service(const char *hostname, const char *name,
595 sdb_time_t last_update)
596 {
597 sdb_host_t *host;
598 sdb_avltree_t *services;
600 int status = 0;
602 if ((! hostname) || (! name))
603 return -1;
605 pthread_rwlock_wrlock(&host_lock);
606 host = lookup_host(hostname, /* canonicalize = */ 1);
607 services = get_host_children(host, SDB_SERVICE);
608 if (! services) {
609 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
610 "host '%s' not found", name, hostname);
611 status = -1;
612 }
614 if (! status)
615 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
616 name, last_update, NULL);
618 sdb_object_deref(SDB_OBJ(host));
619 pthread_rwlock_unlock(&host_lock);
620 return status;
621 } /* sdb_store_service */
623 int
624 sdb_store_service_attr(const char *hostname, const char *service,
625 const char *key, const sdb_data_t *value, sdb_time_t last_update)
626 {
627 sdb_host_t *host;
628 sdb_service_t *svc;
629 sdb_avltree_t *services;
630 int status = 0;
632 if ((! hostname) || (! service) || (! key))
633 return -1;
635 pthread_rwlock_wrlock(&host_lock);
636 host = lookup_host(hostname, /* canonicalize = */ 1);
637 services = get_host_children(host, SDB_SERVICE);
638 sdb_object_deref(SDB_OBJ(host));
639 if (! services) {
640 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
641 "for service '%s' - host '%ss' not found",
642 key, service, hostname);
643 pthread_rwlock_unlock(&host_lock);
644 return -1;
645 }
647 svc = SVC(sdb_avltree_lookup(services, service));
648 if (! svc) {
649 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
650 "service '%s/%s' not found", key, hostname, service);
651 status = -1;
652 }
654 if (! status)
655 status = store_attr(STORE_OBJ(svc), svc->attributes,
656 key, value, last_update);
658 sdb_object_deref(SDB_OBJ(svc));
659 pthread_rwlock_unlock(&host_lock);
660 return status;
661 } /* sdb_store_service_attr */
663 int
664 sdb_store_metric(const char *hostname, const char *name,
665 sdb_metric_store_t *store, sdb_time_t last_update)
666 {
667 sdb_store_obj_t *obj = NULL;
668 sdb_host_t *host;
669 sdb_metric_t *metric;
671 sdb_avltree_t *metrics;
673 int status = 0;
675 if ((! hostname) || (! name))
676 return -1;
677 if (store && ((! store->type) || (! store->id)))
678 return -1;
680 pthread_rwlock_wrlock(&host_lock);
681 host = lookup_host(hostname, /* canonicalize = */ 1);
682 metrics = get_host_children(host, SDB_METRIC);
683 if (! metrics) {
684 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
685 "host '%s' not found", name, hostname);
686 status = -1;
687 }
689 if (! status)
690 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
691 name, last_update, &obj);
692 sdb_object_deref(SDB_OBJ(host));
694 if (status || (! store)) {
695 pthread_rwlock_unlock(&host_lock);
696 return status;
697 }
699 assert(obj);
700 metric = METRIC(obj);
702 if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
703 if (metric->store.type)
704 free(metric->store.type);
705 metric->store.type = strdup(store->type);
706 }
707 if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
708 if (metric->store.id)
709 free(metric->store.id);
710 metric->store.id = strdup(store->id);
711 }
713 if ((! metric->store.type) || (! metric->store.id)) {
714 if (metric->store.type)
715 free(metric->store.type);
716 if (metric->store.id)
717 free(metric->store.id);
718 metric->store.type = metric->store.id = NULL;
719 status = -1;
720 }
721 pthread_rwlock_unlock(&host_lock);
722 return status;
723 } /* sdb_store_metric */
725 int
726 sdb_store_metric_attr(const char *hostname, const char *metric,
727 const char *key, const sdb_data_t *value, sdb_time_t last_update)
728 {
729 sdb_avltree_t *metrics;
730 sdb_host_t *host;
731 sdb_metric_t *m;
732 int status = 0;
734 if ((! hostname) || (! metric) || (! key))
735 return -1;
737 pthread_rwlock_wrlock(&host_lock);
738 host = lookup_host(hostname, /* canonicalize = */ 1);
739 metrics = get_host_children(host, SDB_METRIC);
740 sdb_object_deref(SDB_OBJ(host));
741 if (! metrics) {
742 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
743 "for metric '%s' - host '%s' not found",
744 key, metric, hostname);
745 pthread_rwlock_unlock(&host_lock);
746 return -1;
747 }
749 m = METRIC(sdb_avltree_lookup(metrics, metric));
750 if (! m) {
751 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
752 "metric '%s/%s' not found", key, hostname, metric);
753 status = -1;
754 }
756 if (! status)
757 status = store_attr(STORE_OBJ(m), m->attributes,
758 key, value, last_update);
760 sdb_object_deref(SDB_OBJ(m));
761 pthread_rwlock_unlock(&host_lock);
762 return status;
763 } /* sdb_store_metric_attr */
765 int
766 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
767 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
768 {
769 sdb_avltree_t *metrics;
770 sdb_host_t *host;
771 sdb_metric_t *m;
773 sdb_timeseries_t *ts;
775 if ((! hostname) || (! metric) || (! opts) || (! buf))
776 return -1;
778 pthread_rwlock_rdlock(&host_lock);
779 host = lookup_host(hostname, /* canonicalize = */ 1);
780 metrics = get_host_children(host, SDB_METRIC);
781 sdb_object_deref(SDB_OBJ(host));
782 if (! metrics) {
783 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
784 "- host '%s' not found", hostname, metric, hostname);
785 pthread_rwlock_unlock(&host_lock);
786 return -1;
787 }
789 m = METRIC(sdb_avltree_lookup(metrics, metric));
790 if (! m) {
791 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
792 "- metric '%s' not found", hostname, metric, metric);
793 pthread_rwlock_unlock(&host_lock);
794 return -1;
795 }
797 if ((! m->store.type) || (! m->store.id)) {
798 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
799 "- no data-store configured for the stored metric",
800 hostname, metric);
801 pthread_rwlock_unlock(&host_lock);
802 return -1;
803 }
805 {
806 char type[strlen(m->store.type) + 1];
807 char id[strlen(m->store.id) + 1];
809 strncpy(type, m->store.type, sizeof(type));
810 strncpy(id, m->store.id, sizeof(id));
811 pthread_rwlock_unlock(&host_lock);
813 ts = sdb_plugin_fetch_timeseries(type, id, opts);
814 if (! ts) {
815 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
816 "- %s fetcher callback returned no data for '%s'",
817 hostname, metric, type, id);
818 return -1;
819 }
820 }
822 ts_tojson(ts, buf);
823 sdb_timeseries_destroy(ts);
824 return 0;
825 } /* sdb_store_fetch_timeseries */
827 int
828 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
829 {
830 sdb_data_t tmp;
832 if (! obj)
833 return -1;
835 switch (field) {
836 case SDB_FIELD_NAME:
837 tmp.type = SDB_TYPE_STRING;
838 tmp.data.string = strdup(SDB_OBJ(obj)->name);
839 if (! tmp.data.string)
840 return -1;
841 break;
842 case SDB_FIELD_LAST_UPDATE:
843 tmp.type = SDB_TYPE_DATETIME;
844 tmp.data.datetime = obj->last_update;
845 break;
846 case SDB_FIELD_AGE:
847 tmp.type = SDB_TYPE_DATETIME;
848 tmp.data.datetime = sdb_gettime() - obj->last_update;
849 break;
850 case SDB_FIELD_INTERVAL:
851 tmp.type = SDB_TYPE_DATETIME;
852 tmp.data.datetime = obj->interval;
853 break;
854 case SDB_FIELD_BACKEND:
855 {
856 tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
857 tmp.data.array.length = obj->backends_num;
858 tmp.data.array.values = obj->backends;
859 return sdb_data_copy(res, &tmp);
860 break;
861 }
862 default:
863 return -1;
864 }
865 if (res)
866 *res = tmp;
867 else
868 sdb_data_free_datum(&tmp);
869 return 0;
870 } /* sdb_store_get_field */
872 int
873 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
874 sdb_store_matcher_t *filter)
875 {
876 sdb_avltree_t *tree = NULL;
877 sdb_store_obj_t *attr;
879 if ((! obj) || (! name))
880 return -1;
882 if (obj->type == SDB_HOST)
883 tree = HOST(obj)->attributes;
884 else if (obj->type == SDB_SERVICE)
885 tree = SVC(obj)->attributes;
886 else if (obj->type == SDB_METRIC)
887 tree = METRIC(obj)->attributes;
889 if (! tree)
890 return -1;
892 attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
893 if (! attr)
894 return -1;
895 if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
896 sdb_object_deref(SDB_OBJ(attr));
897 return -1;
898 }
900 assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
901 if (res)
902 sdb_data_copy(res, &ATTR(attr)->value);
903 sdb_object_deref(SDB_OBJ(attr));
904 return 0;
905 } /* sdb_store_get_attr */
907 int
908 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
909 sdb_store_lookup_cb cb, void *user_data)
910 {
911 sdb_avltree_iter_t *host_iter;
912 int status = 0;
914 if (! cb)
915 return -1;
917 if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
918 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
919 return -1;
920 }
922 pthread_rwlock_rdlock(&host_lock);
924 host_iter = sdb_avltree_get_iter(hosts);
925 if (! host_iter)
926 status = -1;
928 /* has_next returns false if the iterator is NULL */
929 while (sdb_avltree_iter_has_next(host_iter)) {
930 sdb_store_obj_t *host;
931 sdb_avltree_iter_t *iter = NULL;
933 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
934 assert(host);
936 if (! sdb_store_matcher_matches(filter, host, NULL))
937 continue;
939 if (type == SDB_SERVICE)
940 iter = sdb_avltree_get_iter(HOST(host)->services);
941 else if (type == SDB_METRIC)
942 iter = sdb_avltree_get_iter(HOST(host)->metrics);
944 if (iter) {
945 while (sdb_avltree_iter_has_next(iter)) {
946 sdb_store_obj_t *obj;
947 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
948 assert(obj);
950 if (sdb_store_matcher_matches(m, obj, filter)) {
951 if (cb(obj, filter, user_data)) {
952 sdb_log(SDB_LOG_ERR, "store: Callback returned "
953 "an error while scanning");
954 status = -1;
955 break;
956 }
957 }
958 }
959 }
960 else if (sdb_store_matcher_matches(m, host, filter)) {
961 if (cb(host, filter, user_data)) {
962 sdb_log(SDB_LOG_ERR, "store: Callback returned "
963 "an error while scanning");
964 status = -1;
965 }
966 }
968 sdb_avltree_iter_destroy(iter);
969 if (status)
970 break;
971 }
973 sdb_avltree_iter_destroy(host_iter);
974 pthread_rwlock_unlock(&host_lock);
975 return status;
976 } /* sdb_store_scan */
978 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */