171e31935bb898eefb5e6e95b46000a20f3cb1a6
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 static sdb_avltree_t *hosts = NULL;
54 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
56 /*
57 * private types
58 */
60 static sdb_type_t sdb_host_type;
61 static sdb_type_t sdb_service_type;
62 static sdb_type_t sdb_metric_type;
63 static sdb_type_t sdb_attribute_type;
65 static int
66 store_obj_init(sdb_object_t *obj, va_list ap)
67 {
68 sdb_store_obj_t *sobj = STORE_OBJ(obj);
70 sobj->type = va_arg(ap, int);
72 sobj->last_update = va_arg(ap, sdb_time_t);
73 sobj->interval = 0;
74 sobj->backends = NULL;
75 sobj->backends_num = 0;
76 sobj->parent = NULL;
77 return 0;
78 } /* store_obj_init */
80 static void
81 store_obj_destroy(sdb_object_t *obj)
82 {
83 sdb_store_obj_t *sobj = STORE_OBJ(obj);
84 size_t i;
86 for (i = 0; i < sobj->backends_num; ++i)
87 free(sobj->backends[i]);
88 free(sobj->backends);
89 sobj->backends = NULL;
90 sobj->backends_num = 0;
92 // We don't currently keep an extra reference for parent objects to
93 // avoid circular self-references which are not handled correctly by
94 // the ref-count base management layer.
95 //sdb_object_deref(SDB_OBJ(sobj->parent));
96 } /* store_obj_destroy */
98 static int
99 sdb_host_init(sdb_object_t *obj, va_list ap)
100 {
101 sdb_host_t *sobj = HOST(obj);
102 int ret;
104 /* this will consume the first argument (type) of ap */
105 ret = store_obj_init(obj, ap);
106 if (ret)
107 return ret;
109 sobj->services = sdb_avltree_create();
110 if (! sobj->services)
111 return -1;
112 sobj->metrics = sdb_avltree_create();
113 if (! sobj->metrics)
114 return -1;
115 sobj->attributes = sdb_avltree_create();
116 if (! sobj->attributes)
117 return -1;
118 return 0;
119 } /* sdb_host_init */
121 static void
122 sdb_host_destroy(sdb_object_t *obj)
123 {
124 sdb_host_t *sobj = HOST(obj);
125 assert(obj);
127 store_obj_destroy(obj);
129 if (sobj->services)
130 sdb_avltree_destroy(sobj->services);
131 if (sobj->metrics)
132 sdb_avltree_destroy(sobj->metrics);
133 if (sobj->attributes)
134 sdb_avltree_destroy(sobj->attributes);
135 } /* sdb_host_destroy */
137 static int
138 sdb_service_init(sdb_object_t *obj, va_list ap)
139 {
140 sdb_service_t *sobj = SVC(obj);
141 int ret;
143 /* this will consume the first argument (type) of ap */
144 ret = store_obj_init(obj, ap);
145 if (ret)
146 return ret;
148 sobj->attributes = sdb_avltree_create();
149 if (! sobj->attributes)
150 return -1;
151 return 0;
152 } /* sdb_service_init */
154 static void
155 sdb_service_destroy(sdb_object_t *obj)
156 {
157 sdb_service_t *sobj = SVC(obj);
158 assert(obj);
160 store_obj_destroy(obj);
162 if (sobj->attributes)
163 sdb_avltree_destroy(sobj->attributes);
164 } /* sdb_service_destroy */
166 static int
167 sdb_metric_init(sdb_object_t *obj, va_list ap)
168 {
169 sdb_metric_t *sobj = METRIC(obj);
170 int ret;
172 /* this will consume the first argument (type) of ap */
173 ret = store_obj_init(obj, ap);
174 if (ret)
175 return ret;
177 sobj->attributes = sdb_avltree_create();
178 if (! sobj->attributes)
179 return -1;
181 sobj->store.type = sobj->store.id = NULL;
182 return 0;
183 } /* sdb_metric_init */
185 static void
186 sdb_metric_destroy(sdb_object_t *obj)
187 {
188 sdb_metric_t *sobj = METRIC(obj);
189 assert(obj);
191 store_obj_destroy(obj);
193 if (sobj->attributes)
194 sdb_avltree_destroy(sobj->attributes);
196 if (sobj->store.type)
197 free(sobj->store.type);
198 if (sobj->store.id)
199 free(sobj->store.id);
200 } /* sdb_metric_destroy */
202 static int
203 sdb_attr_init(sdb_object_t *obj, va_list ap)
204 {
205 const sdb_data_t *value;
206 int ret;
208 /* this will consume the first two arguments
209 * (type and last_update) of ap */
210 ret = store_obj_init(obj, ap);
211 if (ret)
212 return ret;
213 value = va_arg(ap, const sdb_data_t *);
215 if (value)
216 if (sdb_data_copy(&ATTR(obj)->value, value))
217 return -1;
218 return 0;
219 } /* sdb_attr_init */
221 static void
222 sdb_attr_destroy(sdb_object_t *obj)
223 {
224 assert(obj);
226 store_obj_destroy(obj);
227 sdb_data_free_datum(&ATTR(obj)->value);
228 } /* sdb_attr_destroy */
230 static sdb_type_t sdb_host_type = {
231 sizeof(sdb_host_t),
232 sdb_host_init,
233 sdb_host_destroy
234 };
236 static sdb_type_t sdb_service_type = {
237 sizeof(sdb_service_t),
238 sdb_service_init,
239 sdb_service_destroy
240 };
242 static sdb_type_t sdb_metric_type = {
243 sizeof(sdb_metric_t),
244 sdb_metric_init,
245 sdb_metric_destroy
246 };
248 static sdb_type_t sdb_attribute_type = {
249 sizeof(sdb_attribute_t),
250 sdb_attr_init,
251 sdb_attr_destroy
252 };
254 /*
255 * private helper functions
256 */
258 static sdb_host_t *
259 lookup_host(const char *name, bool canonicalize)
260 {
261 sdb_host_t *host;
262 char *cname;
264 assert(name);
265 if (! canonicalize)
266 return HOST(sdb_avltree_lookup(hosts, name));
268 cname = strdup(name);
269 cname = sdb_plugin_cname(cname);
270 if (! cname) {
271 sdb_log(SDB_LOG_ERR, "store: strdup failed");
272 return NULL;
273 }
275 host = HOST(sdb_avltree_lookup(hosts, cname));
276 free(cname);
277 return host;
278 } /* lookup_host */
280 static int
281 record_backend(sdb_store_obj_t *obj)
282 {
283 const sdb_plugin_info_t *info;
284 char **tmp;
285 size_t i;
287 info = sdb_plugin_current();
288 if (! info)
289 return 0;
291 for (i = 0; i < obj->backends_num; ++i)
292 if (!strcasecmp(obj->backends[i], info->plugin_name))
293 return 0;
295 tmp = realloc(obj->backends,
296 (obj->backends_num + 1) * sizeof(*obj->backends));
297 if (! tmp)
298 return -1;
300 obj->backends = tmp;
301 obj->backends[obj->backends_num] = strdup(info->plugin_name);
302 if (! obj->backends[obj->backends_num])
303 return -1;
305 ++obj->backends_num;
306 return 0;
307 } /* record_backend */
309 static int
310 store_obj(sdb_store_obj_t *parent, sdb_avltree_t *parent_tree,
311 int type, const char *name, sdb_time_t last_update,
312 sdb_store_obj_t **updated_obj)
313 {
314 sdb_store_obj_t *old, *new;
315 int status = 0;
317 assert(parent_tree);
319 if (last_update <= 0)
320 last_update = sdb_gettime();
322 old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
323 if (old) {
324 if (old->last_update > last_update) {
325 sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
326 "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
327 SDB_STORE_TYPE_TO_NAME(type), name,
328 last_update, old->last_update);
329 /* don't report an error; the object may be updated by multiple
330 * backends */
331 status = 1;
332 }
333 else if (old->last_update == last_update) {
334 /* don't report an error and also don't even log this to avoid
335 * excessive noise on high sampling frequencies */
336 status = 1;
337 }
338 else {
339 sdb_time_t interval = last_update - old->last_update;
340 old->last_update = last_update;
341 if (interval) {
342 if (old->interval)
343 old->interval = (sdb_time_t)((0.9 * (double)old->interval)
344 + (0.1 * (double)interval));
345 else
346 old->interval = interval;
347 }
348 }
350 new = old;
351 sdb_object_deref(SDB_OBJ(old));
352 }
353 else {
354 if (type == SDB_ATTRIBUTE) {
355 /* the value will be updated by the caller */
356 new = STORE_OBJ(sdb_object_create(name, sdb_attribute_type,
357 type, last_update, NULL));
358 }
359 else {
360 sdb_type_t t;
361 t = type == SDB_HOST
362 ? sdb_host_type
363 : type == SDB_SERVICE
364 ? sdb_service_type
365 : sdb_metric_type;
366 new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
367 }
369 if (new) {
370 status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
372 /* pass control to the tree or destroy in case of an error */
373 sdb_object_deref(SDB_OBJ(new));
374 }
375 else {
376 char errbuf[1024];
377 sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
378 SDB_STORE_TYPE_TO_NAME(type), name,
379 sdb_strerror(errno, errbuf, sizeof(errbuf)));
380 status = -1;
381 }
382 }
384 if (status < 0)
385 return status;
386 assert(new);
388 if (new->parent != parent) {
389 // Avoid circular self-references which are not handled
390 // correctly by the ref-count based management layer.
391 //sdb_object_deref(SDB_OBJ(new->parent));
392 //sdb_object_ref(SDB_OBJ(parent));
393 new->parent = parent;
394 }
396 if (updated_obj)
397 *updated_obj = new;
399 if (record_backend(new))
400 return -1;
401 return status;
402 } /* store_obj */
404 static int
405 store_attr(sdb_store_obj_t *parent, sdb_avltree_t *attributes,
406 const char *key, const sdb_data_t *value, sdb_time_t last_update)
407 {
408 sdb_store_obj_t *attr = NULL;
409 int status;
411 status = store_obj(parent, attributes, SDB_ATTRIBUTE,
412 key, last_update, &attr);
413 if (status)
414 return status;
416 /* don't update unchanged values */
417 if (! sdb_data_cmp(&ATTR(attr)->value, value))
418 return status;
420 assert(attr);
421 if (sdb_data_copy(&ATTR(attr)->value, value))
422 return -1;
423 return status;
424 } /* store_attr */
426 static int
427 store_metric_store(sdb_metric_t *metric, sdb_metric_store_t *store)
428 {
429 char *type = metric->store.type;
430 char *id = metric->store.id;
432 if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
433 if (! (type = strdup(store->type)))
434 return -1;
435 }
436 if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
437 if (! (id = strdup(store->id))) {
438 if (type != metric->store.type)
439 free(type);
440 return -1;
441 }
442 }
444 if (type != metric->store.type) {
445 if (metric->store.type)
446 free(metric->store.type);
447 metric->store.type = type;
448 }
449 if (id != metric->store.id) {
450 if (metric->store.id)
451 free(metric->store.id);
452 metric->store.id = id;
453 }
454 return 0;
455 } /* store_metric_store */
457 /* The host_lock has to be acquired before calling this function. */
458 static sdb_avltree_t *
459 get_host_children(sdb_host_t *host, int type)
460 {
461 if ((type != SDB_SERVICE) && (type != SDB_METRIC)
462 && (type != SDB_ATTRIBUTE))
463 return NULL;
465 if (! host)
466 return NULL;
468 if (type == SDB_ATTRIBUTE)
469 return host->attributes;
470 else if (type == SDB_METRIC)
471 return host->metrics;
472 else
473 return host->services;
474 } /* get_host_children */
476 /*
477 * ts_tojson serializes a time-series to JSON.
478 *
479 * The function never returns an error. Rather, an error message will be part
480 * of the serialized data.
481 */
482 static void
483 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
484 {
485 char start_str[64];
486 char end_str[64];
488 size_t i;
490 /* TODO: make time format configurable */
491 if (! sdb_strftime(start_str, sizeof(start_str), ts->start))
492 snprintf(start_str, sizeof(start_str), "<error>");
493 start_str[sizeof(start_str) - 1] = '\0';
494 if (! sdb_strftime(end_str, sizeof(end_str), ts->end))
495 snprintf(end_str, sizeof(end_str), "<error>");
496 end_str[sizeof(end_str) - 1] = '\0';
498 sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
499 start_str, end_str);
501 for (i = 0; i < ts->data_names_len; ++i) {
502 size_t j;
503 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
505 for (j = 0; j < ts->data_len; ++j) {
506 char time_str[64];
508 if (! sdb_strftime(time_str, sizeof(time_str), ts->data[i][j].timestamp))
509 snprintf(time_str, sizeof(time_str), "<error>");
510 time_str[sizeof(time_str) - 1] = '\0';
512 /* Some GNU libc versions may print '-nan' which we dont' want */
513 if (isnan(ts->data[i][j].value))
514 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
515 "\"value\": \"nan\"}", time_str);
516 else
517 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
518 "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
520 if (j < ts->data_len - 1)
521 sdb_strbuf_append(buf, ",");
522 }
524 if (i < ts->data_names_len - 1)
525 sdb_strbuf_append(buf, "],");
526 else
527 sdb_strbuf_append(buf, "]");
528 }
529 sdb_strbuf_append(buf, "}}");
530 } /* ts_tojson */
532 /*
533 * public API
534 */
536 void
537 sdb_store_clear(void)
538 {
539 sdb_avltree_destroy(hosts);
540 hosts = NULL;
541 } /* sdb_store_clear */
543 int
544 sdb_store_host(const char *name, sdb_time_t last_update)
545 {
546 char *cname = NULL;
547 int status = 0;
549 if (! name)
550 return -1;
552 cname = sdb_plugin_cname(strdup(name));
553 if (! cname) {
554 sdb_log(SDB_LOG_ERR, "store: strdup failed");
555 return -1;
556 }
558 pthread_rwlock_wrlock(&host_lock);
559 if (! hosts)
560 if (! (hosts = sdb_avltree_create()))
561 status = -1;
563 if (! status)
564 status = store_obj(NULL, hosts, SDB_HOST, cname, last_update, NULL);
565 pthread_rwlock_unlock(&host_lock);
567 if (sdb_plugin_store_host(name, last_update))
568 status = -1;
570 free(cname);
571 return status;
572 } /* sdb_store_host */
574 bool
575 sdb_store_has_host(const char *name)
576 {
577 sdb_host_t *host;
579 if (! name)
580 return NULL;
582 host = lookup_host(name, /* canonicalize = */ 0);
583 sdb_object_deref(SDB_OBJ(host));
584 return host != NULL;
585 } /* sdb_store_has_host */
587 sdb_store_obj_t *
588 sdb_store_get_host(const char *name)
589 {
590 sdb_host_t *host;
592 if (! name)
593 return NULL;
595 host = lookup_host(name, /* canonicalize = */ 0);
596 if (! host)
597 return NULL;
599 return STORE_OBJ(host);
600 } /* sdb_store_get_host */
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 ((! hostname) || (! key))
612 return -1;
614 pthread_rwlock_wrlock(&host_lock);
615 host = lookup_host(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(&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_service(const char *hostname, const char *name,
636 sdb_time_t last_update)
637 {
638 sdb_host_t *host;
639 sdb_avltree_t *services;
640 sdb_data_t d;
642 int status = 0;
644 if ((! hostname) || (! name))
645 return -1;
647 pthread_rwlock_wrlock(&host_lock);
648 host = lookup_host(hostname, /* canonicalize = */ 1);
649 services = get_host_children(host, SDB_SERVICE);
650 if (! services) {
651 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
652 "host '%s' not found", name, hostname);
653 status = -1;
654 }
656 if (! status)
657 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
658 name, last_update, NULL);
660 sdb_object_deref(SDB_OBJ(host));
661 pthread_rwlock_unlock(&host_lock);
663 if (status)
664 return status;
666 if (sdb_plugin_store_service(hostname, name, last_update))
667 status = -1;
669 /* record the hostname as an attribute */
670 d.type = SDB_TYPE_STRING;
671 d.data.string = SDB_OBJ(host)->name;
672 if (sdb_store_service_attr(hostname, name, "hostname", &d, last_update))
673 status = -1;
674 return status;
675 } /* sdb_store_service */
677 int
678 sdb_store_service_attr(const char *hostname, const char *service,
679 const char *key, const sdb_data_t *value, sdb_time_t last_update)
680 {
681 sdb_host_t *host;
682 sdb_service_t *svc;
683 sdb_avltree_t *services;
684 int status = 0;
686 if ((! hostname) || (! service) || (! key))
687 return -1;
689 pthread_rwlock_wrlock(&host_lock);
690 host = lookup_host(hostname, /* canonicalize = */ 1);
691 services = get_host_children(host, SDB_SERVICE);
692 sdb_object_deref(SDB_OBJ(host));
693 if (! services) {
694 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
695 "for service '%s' - host '%ss' not found",
696 key, service, hostname);
697 pthread_rwlock_unlock(&host_lock);
698 return -1;
699 }
701 svc = SVC(sdb_avltree_lookup(services, service));
702 if (! svc) {
703 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
704 "service '%s/%s' not found", key, hostname, service);
705 status = -1;
706 }
708 if (! status)
709 status = store_attr(STORE_OBJ(svc), svc->attributes,
710 key, value, last_update);
712 sdb_object_deref(SDB_OBJ(svc));
713 pthread_rwlock_unlock(&host_lock);
715 if (sdb_plugin_store_service_attribute(hostname, service,
716 key, value, last_update))
717 status = -1;
718 return status;
719 } /* sdb_store_service_attr */
721 int
722 sdb_store_metric(const char *hostname, const char *name,
723 sdb_metric_store_t *store, sdb_time_t last_update)
724 {
725 sdb_store_obj_t *obj = NULL;
726 sdb_host_t *host;
727 sdb_metric_t *metric;
728 sdb_data_t d;
730 sdb_avltree_t *metrics;
732 int status = 0;
734 if ((! hostname) || (! name))
735 return -1;
737 if (store) {
738 if ((store->type != NULL) != (store->id != NULL))
739 return -1;
740 else if (! store->type)
741 store = NULL;
742 }
744 pthread_rwlock_wrlock(&host_lock);
745 host = lookup_host(hostname, /* canonicalize = */ 1);
746 metrics = get_host_children(host, SDB_METRIC);
747 if (! metrics) {
748 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
749 "host '%s' not found", name, hostname);
750 status = -1;
751 }
753 if (! status)
754 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
755 name, last_update, &obj);
756 sdb_object_deref(SDB_OBJ(host));
758 if (status) {
759 pthread_rwlock_unlock(&host_lock);
760 return status;
761 }
763 assert(obj);
764 metric = METRIC(obj);
766 if (store)
767 if (store_metric_store(metric, store))
768 status = -1;
769 pthread_rwlock_unlock(&host_lock);
771 if (sdb_plugin_store_metric(hostname, name, store, last_update))
772 status = -1;
774 /* record the hostname as an attribute */
775 d.type = SDB_TYPE_STRING;
776 d.data.string = SDB_OBJ(host)->name;
777 if (sdb_store_metric_attr(hostname, name, "hostname", &d, last_update))
778 status = -1;
779 return status;
780 } /* sdb_store_metric */
782 int
783 sdb_store_metric_attr(const char *hostname, const char *metric,
784 const char *key, const sdb_data_t *value, sdb_time_t last_update)
785 {
786 sdb_avltree_t *metrics;
787 sdb_host_t *host;
788 sdb_metric_t *m;
789 int status = 0;
791 if ((! hostname) || (! metric) || (! key))
792 return -1;
794 pthread_rwlock_wrlock(&host_lock);
795 host = lookup_host(hostname, /* canonicalize = */ 1);
796 metrics = get_host_children(host, SDB_METRIC);
797 sdb_object_deref(SDB_OBJ(host));
798 if (! metrics) {
799 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
800 "for metric '%s' - host '%s' not found",
801 key, metric, hostname);
802 pthread_rwlock_unlock(&host_lock);
803 return -1;
804 }
806 m = METRIC(sdb_avltree_lookup(metrics, metric));
807 if (! m) {
808 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
809 "metric '%s/%s' not found", key, hostname, metric);
810 status = -1;
811 }
813 if (! status)
814 status = store_attr(STORE_OBJ(m), m->attributes,
815 key, value, last_update);
817 sdb_object_deref(SDB_OBJ(m));
818 pthread_rwlock_unlock(&host_lock);
820 if (sdb_plugin_store_metric_attribute(hostname, metric,
821 key, value, last_update))
822 status = -1;
823 return status;
824 } /* sdb_store_metric_attr */
826 sdb_store_obj_t *
827 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
828 {
829 sdb_avltree_t *children;
831 if ((! host) || (host->type != SDB_HOST) || (! name))
832 return NULL;
834 children = get_host_children(HOST(host), type);
835 if (! children)
836 return NULL;
837 return STORE_OBJ(sdb_avltree_lookup(children, name));
838 } /* sdb_store_get_child */
840 int
841 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
842 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
843 {
844 sdb_avltree_t *metrics;
845 sdb_host_t *host;
846 sdb_metric_t *m;
848 sdb_timeseries_t *ts;
850 int status = 0;
852 if ((! hostname) || (! metric) || (! opts) || (! buf))
853 return -1;
855 pthread_rwlock_rdlock(&host_lock);
856 host = lookup_host(hostname, /* canonicalize = */ 1);
857 metrics = get_host_children(host, SDB_METRIC);
858 sdb_object_deref(SDB_OBJ(host));
859 if (! metrics) {
860 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
861 "- host '%s' not found", hostname, metric, hostname);
862 pthread_rwlock_unlock(&host_lock);
863 return -1;
864 }
866 m = METRIC(sdb_avltree_lookup(metrics, metric));
867 if (! m) {
868 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
869 "- metric '%s' not found", hostname, metric, metric);
870 pthread_rwlock_unlock(&host_lock);
871 return -1;
872 }
874 if ((! m->store.type) || (! m->store.id)) {
875 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
876 "- no data-store configured for the stored metric",
877 hostname, metric);
878 sdb_object_deref(SDB_OBJ(m));
879 pthread_rwlock_unlock(&host_lock);
880 return -1;
881 }
883 {
884 char type[strlen(m->store.type) + 1];
885 char id[strlen(m->store.id) + 1];
887 strncpy(type, m->store.type, sizeof(type));
888 strncpy(id, m->store.id, sizeof(id));
889 pthread_rwlock_unlock(&host_lock);
891 ts = sdb_plugin_fetch_timeseries(type, id, opts);
892 if (! ts) {
893 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
894 "- %s fetcher callback returned no data for '%s'",
895 hostname, metric, type, id);
896 status = -1;
897 }
898 }
900 ts_tojson(ts, buf);
901 sdb_object_deref(SDB_OBJ(m));
902 sdb_timeseries_destroy(ts);
903 return status;
904 } /* sdb_store_fetch_timeseries */
906 int
907 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
908 {
909 sdb_data_t tmp;
911 if (! obj)
912 return -1;
914 switch (field) {
915 case SDB_FIELD_NAME:
916 tmp.type = SDB_TYPE_STRING;
917 tmp.data.string = strdup(SDB_OBJ(obj)->name);
918 if (! tmp.data.string)
919 return -1;
920 break;
921 case SDB_FIELD_LAST_UPDATE:
922 tmp.type = SDB_TYPE_DATETIME;
923 tmp.data.datetime = obj->last_update;
924 break;
925 case SDB_FIELD_AGE:
926 tmp.type = SDB_TYPE_DATETIME;
927 tmp.data.datetime = sdb_gettime() - obj->last_update;
928 break;
929 case SDB_FIELD_INTERVAL:
930 tmp.type = SDB_TYPE_DATETIME;
931 tmp.data.datetime = obj->interval;
932 break;
933 case SDB_FIELD_BACKEND:
934 if (! res)
935 return 0;
936 tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
937 tmp.data.array.length = obj->backends_num;
938 tmp.data.array.values = obj->backends;
939 return sdb_data_copy(res, &tmp);
940 case SDB_FIELD_VALUE:
941 if (obj->type != SDB_ATTRIBUTE)
942 return -1;
943 if (! res)
944 return 0;
945 return sdb_data_copy(res, &ATTR(obj)->value);
946 case SDB_FIELD_TIMESERIES:
947 if (obj->type != SDB_METRIC)
948 return -1;
949 tmp.type = SDB_TYPE_BOOLEAN;
950 tmp.data.boolean = METRIC(obj)->store.type != NULL;
951 default:
952 return -1;
953 }
954 if (res)
955 *res = tmp;
956 else
957 sdb_data_free_datum(&tmp);
958 return 0;
959 } /* sdb_store_get_field */
961 int
962 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
963 sdb_store_matcher_t *filter)
964 {
965 sdb_avltree_t *tree = NULL;
966 sdb_store_obj_t *attr;
968 if ((! obj) || (! name))
969 return -1;
971 if (obj->type == SDB_HOST)
972 tree = HOST(obj)->attributes;
973 else if (obj->type == SDB_SERVICE)
974 tree = SVC(obj)->attributes;
975 else if (obj->type == SDB_METRIC)
976 tree = METRIC(obj)->attributes;
978 if (! tree)
979 return -1;
981 attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
982 if (! attr)
983 return -1;
984 if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
985 sdb_object_deref(SDB_OBJ(attr));
986 return -1;
987 }
989 assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
990 if (res)
991 sdb_data_copy(res, &ATTR(attr)->value);
992 sdb_object_deref(SDB_OBJ(attr));
993 return 0;
994 } /* sdb_store_get_attr */
996 int
997 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
998 sdb_store_lookup_cb cb, void *user_data)
999 {
1000 sdb_avltree_iter_t *host_iter = NULL;
1001 int status = 0;
1003 if (! cb)
1004 return -1;
1006 if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
1007 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
1008 return -1;
1009 }
1011 pthread_rwlock_rdlock(&host_lock);
1013 if (hosts) {
1014 host_iter = sdb_avltree_get_iter(hosts);
1015 if (! host_iter)
1016 status = -1;
1017 }
1019 /* has_next returns false if the iterator is NULL */
1020 while (sdb_avltree_iter_has_next(host_iter)) {
1021 sdb_store_obj_t *host;
1022 sdb_avltree_iter_t *iter = NULL;
1024 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1025 assert(host);
1027 if (! sdb_store_matcher_matches(filter, host, NULL))
1028 continue;
1030 if (type == SDB_SERVICE)
1031 iter = sdb_avltree_get_iter(HOST(host)->services);
1032 else if (type == SDB_METRIC)
1033 iter = sdb_avltree_get_iter(HOST(host)->metrics);
1035 if (iter) {
1036 while (sdb_avltree_iter_has_next(iter)) {
1037 sdb_store_obj_t *obj;
1038 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1039 assert(obj);
1041 if (sdb_store_matcher_matches(m, obj, filter)) {
1042 if (cb(obj, filter, user_data)) {
1043 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1044 "an error while scanning");
1045 status = -1;
1046 break;
1047 }
1048 }
1049 }
1050 }
1051 else if (sdb_store_matcher_matches(m, host, filter)) {
1052 if (cb(host, filter, user_data)) {
1053 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1054 "an error while scanning");
1055 status = -1;
1056 }
1057 }
1059 sdb_avltree_iter_destroy(iter);
1060 if (status)
1061 break;
1062 }
1064 sdb_avltree_iter_destroy(host_iter);
1065 pthread_rwlock_unlock(&host_lock);
1066 return status;
1067 } /* sdb_store_scan */
1069 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */