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 types
51 */
53 struct sdb_store {
54 sdb_object_t super;
56 /* hosts are the top-level entries and
57 * reference everything else */
58 sdb_avltree_t *hosts;
59 pthread_rwlock_t host_lock;
60 };
62 /* internal representation of a to-be-stored object */
63 typedef struct {
64 sdb_store_obj_t *parent;
65 sdb_avltree_t *parent_tree;
66 int type;
67 const char *name;
68 sdb_time_t last_update;
69 const char **backends;
70 size_t backends_num;
71 } store_obj_t;
72 #define STORE_OBJ_INIT { NULL, NULL, 0, NULL, 0, NULL, 0 }
74 static sdb_type_t host_type;
75 static sdb_type_t service_type;
76 static sdb_type_t metric_type;
77 static sdb_type_t attribute_type;
79 static int
80 store_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
81 {
82 int err;
83 if (! (SDB_STORE(obj)->hosts = sdb_avltree_create()))
84 return -1;
85 if ((err = pthread_rwlock_init(&SDB_STORE(obj)->host_lock,
86 /* attr = */ NULL))) {
87 char errbuf[128];
88 sdb_log(SDB_LOG_ERR, "store: Failed to initialize lock: %s",
89 sdb_strerror(err, errbuf, sizeof(errbuf)));
90 return -1;
91 }
92 return 0;
93 } /* store_init */
95 static void
96 store_destroy(sdb_object_t *obj)
97 {
98 int err;
99 if ((err = pthread_rwlock_destroy(&SDB_STORE(obj)->host_lock))) {
100 char errbuf[128];
101 sdb_log(SDB_LOG_ERR, "store: Failed to destroy lock: %s",
102 sdb_strerror(err, errbuf, sizeof(errbuf)));
103 return;
104 }
105 sdb_avltree_destroy(SDB_STORE(obj)->hosts);
106 SDB_STORE(obj)->hosts = NULL;
107 } /* store_destroy */
109 static int
110 store_obj_init(sdb_object_t *obj, va_list ap)
111 {
112 sdb_store_obj_t *sobj = STORE_OBJ(obj);
114 sobj->type = va_arg(ap, int);
116 sobj->last_update = va_arg(ap, sdb_time_t);
117 sobj->interval = 0;
118 sobj->backends = NULL;
119 sobj->backends_num = 0;
120 sobj->parent = NULL;
121 return 0;
122 } /* store_obj_init */
124 static void
125 store_obj_destroy(sdb_object_t *obj)
126 {
127 sdb_store_obj_t *sobj = STORE_OBJ(obj);
128 size_t i;
130 for (i = 0; i < sobj->backends_num; ++i)
131 free(sobj->backends[i]);
132 free(sobj->backends);
133 sobj->backends = NULL;
134 sobj->backends_num = 0;
136 // We don't currently keep an extra reference for parent objects to
137 // avoid circular self-references which are not handled correctly by
138 // the ref-count base management layer.
139 //sdb_object_deref(SDB_OBJ(sobj->parent));
140 } /* store_obj_destroy */
142 static int
143 host_init(sdb_object_t *obj, va_list ap)
144 {
145 host_t *sobj = HOST(obj);
146 int ret;
148 /* this will consume the first argument (type) of ap */
149 ret = store_obj_init(obj, ap);
150 if (ret)
151 return ret;
153 sobj->services = sdb_avltree_create();
154 if (! sobj->services)
155 return -1;
156 sobj->metrics = sdb_avltree_create();
157 if (! sobj->metrics)
158 return -1;
159 sobj->attributes = sdb_avltree_create();
160 if (! sobj->attributes)
161 return -1;
162 return 0;
163 } /* host_init */
165 static void
166 host_destroy(sdb_object_t *obj)
167 {
168 host_t *sobj = HOST(obj);
169 assert(obj);
171 store_obj_destroy(obj);
173 if (sobj->services)
174 sdb_avltree_destroy(sobj->services);
175 if (sobj->metrics)
176 sdb_avltree_destroy(sobj->metrics);
177 if (sobj->attributes)
178 sdb_avltree_destroy(sobj->attributes);
179 } /* host_destroy */
181 static int
182 service_init(sdb_object_t *obj, va_list ap)
183 {
184 service_t *sobj = SVC(obj);
185 int ret;
187 /* this will consume the first argument (type) of ap */
188 ret = store_obj_init(obj, ap);
189 if (ret)
190 return ret;
192 sobj->attributes = sdb_avltree_create();
193 if (! sobj->attributes)
194 return -1;
195 return 0;
196 } /* service_init */
198 static void
199 service_destroy(sdb_object_t *obj)
200 {
201 service_t *sobj = SVC(obj);
202 assert(obj);
204 store_obj_destroy(obj);
206 if (sobj->attributes)
207 sdb_avltree_destroy(sobj->attributes);
208 } /* service_destroy */
210 static int
211 metric_init(sdb_object_t *obj, va_list ap)
212 {
213 sdb_metric_t *sobj = METRIC(obj);
214 int ret;
216 /* this will consume the first argument (type) of ap */
217 ret = store_obj_init(obj, ap);
218 if (ret)
219 return ret;
221 sobj->attributes = sdb_avltree_create();
222 if (! sobj->attributes)
223 return -1;
225 sobj->store.type = sobj->store.id = NULL;
226 return 0;
227 } /* metric_init */
229 static void
230 metric_destroy(sdb_object_t *obj)
231 {
232 sdb_metric_t *sobj = METRIC(obj);
233 assert(obj);
235 store_obj_destroy(obj);
237 if (sobj->attributes)
238 sdb_avltree_destroy(sobj->attributes);
240 if (sobj->store.type)
241 free(sobj->store.type);
242 if (sobj->store.id)
243 free(sobj->store.id);
244 } /* metric_destroy */
246 static int
247 attr_init(sdb_object_t *obj, va_list ap)
248 {
249 const sdb_data_t *value;
250 int ret;
252 /* this will consume the first two arguments
253 * (type and last_update) of ap */
254 ret = store_obj_init(obj, ap);
255 if (ret)
256 return ret;
257 value = va_arg(ap, const sdb_data_t *);
259 if (value)
260 if (sdb_data_copy(&ATTR(obj)->value, value))
261 return -1;
262 return 0;
263 } /* attr_init */
265 static void
266 attr_destroy(sdb_object_t *obj)
267 {
268 assert(obj);
270 store_obj_destroy(obj);
271 sdb_data_free_datum(&ATTR(obj)->value);
272 } /* attr_destroy */
274 static sdb_type_t store_type = {
275 /* size = */ sizeof(sdb_store_t),
276 /* init = */ store_init,
277 /* destroy = */ store_destroy,
278 };
280 static sdb_type_t host_type = {
281 /* size = */ sizeof(host_t),
282 /* init = */ host_init,
283 /* destroy = */ host_destroy
284 };
286 static sdb_type_t service_type = {
287 /* size = */ sizeof(service_t),
288 /* init = */ service_init,
289 /* destroy = */ service_destroy
290 };
292 static sdb_type_t metric_type = {
293 /* size = */ sizeof(sdb_metric_t),
294 /* init = */ metric_init,
295 /* destroy = */ metric_destroy
296 };
298 static sdb_type_t attribute_type = {
299 /* size = */ sizeof(attr_t),
300 /* init = */ attr_init,
301 /* destroy = */ attr_destroy
302 };
304 /*
305 * private helper functions
306 */
308 static int
309 record_backends(sdb_store_obj_t *obj,
310 const char **backends, size_t backends_num)
311 {
312 char **tmp;
313 size_t i;
315 for (i = 0; i < backends_num; i++) {
316 bool found = 0;
317 size_t j;
319 for (j = 0; j < obj->backends_num; ++j) {
320 if (!strcasecmp(obj->backends[j], backends[i])) {
321 found = 1;
322 break;
323 }
324 }
325 if (found)
326 continue;
328 tmp = realloc(obj->backends,
329 (obj->backends_num + 1) * sizeof(*obj->backends));
330 if (! tmp)
331 return -1;
333 obj->backends = tmp;
334 obj->backends[obj->backends_num] = strdup(backends[i]);
335 if (! obj->backends[obj->backends_num])
336 return -1;
338 ++obj->backends_num;
339 }
340 return 0;
341 } /* record_backends */
343 static int
344 store_obj(store_obj_t *obj, sdb_store_obj_t **updated_obj)
345 {
346 sdb_store_obj_t *old, *new;
347 int status = 0;
349 assert(obj->parent_tree);
351 if (obj->last_update <= 0)
352 obj->last_update = sdb_gettime();
354 old = STORE_OBJ(sdb_avltree_lookup(obj->parent_tree, obj->name));
355 if (old) {
356 if (old->last_update > obj->last_update) {
357 sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
358 "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
359 SDB_STORE_TYPE_TO_NAME(obj->type), obj->name,
360 obj->last_update, old->last_update);
361 /* don't report an error; the object may be updated by multiple
362 * backends */
363 status = 1;
364 }
365 else if (old->last_update == obj->last_update) {
366 /* don't report an error and also don't even log this to avoid
367 * excessive noise on high sampling frequencies */
368 status = 1;
369 }
370 else {
371 sdb_time_t interval = obj->last_update - old->last_update;
372 old->last_update = obj->last_update;
373 if (interval) {
374 if (old->interval)
375 old->interval = (sdb_time_t)((0.9 * (double)old->interval)
376 + (0.1 * (double)interval));
377 else
378 old->interval = interval;
379 }
380 }
382 new = old;
383 sdb_object_deref(SDB_OBJ(old));
384 }
385 else {
386 if (obj->type == SDB_ATTRIBUTE) {
387 /* the value will be updated by the caller */
388 new = STORE_OBJ(sdb_object_create(obj->name, attribute_type,
389 obj->type, obj->last_update, NULL));
390 }
391 else {
392 sdb_type_t t;
393 t = obj->type == SDB_HOST
394 ? host_type
395 : obj->type == SDB_SERVICE
396 ? service_type
397 : metric_type;
398 new = STORE_OBJ(sdb_object_create(obj->name, t,
399 obj->type, obj->last_update));
400 }
402 if (new) {
403 status = sdb_avltree_insert(obj->parent_tree, SDB_OBJ(new));
405 /* pass control to the tree or destroy in case of an error */
406 sdb_object_deref(SDB_OBJ(new));
407 }
408 else {
409 char errbuf[1024];
410 sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
411 SDB_STORE_TYPE_TO_NAME(obj->type), obj->name,
412 sdb_strerror(errno, errbuf, sizeof(errbuf)));
413 status = -1;
414 }
415 }
417 if (status < 0)
418 return status;
419 assert(new);
421 if (new->parent != obj->parent) {
422 // Avoid circular self-references which are not handled
423 // correctly by the ref-count based management layer.
424 //sdb_object_deref(SDB_OBJ(new->parent));
425 //sdb_object_ref(SDB_OBJ(obj->parent));
426 new->parent = obj->parent;
427 }
429 if (updated_obj)
430 *updated_obj = new;
432 if (record_backends(new, obj->backends, obj->backends_num))
433 return -1;
434 return status;
435 } /* store_obj */
437 static int
438 store_metric_store(sdb_metric_t *metric, sdb_store_metric_t *m)
439 {
440 char *type = metric->store.type;
441 char *id = metric->store.id;
443 if ((! metric->store.type) || strcasecmp(metric->store.type, m->store.type)) {
444 if (! (type = strdup(m->store.type)))
445 return -1;
446 }
447 if ((! metric->store.id) || strcasecmp(metric->store.id, m->store.id)) {
448 if (! (id = strdup(m->store.id))) {
449 if (type != metric->store.type)
450 free(type);
451 return -1;
452 }
453 }
455 if (type != metric->store.type) {
456 if (metric->store.type)
457 free(metric->store.type);
458 metric->store.type = type;
459 }
460 if (id != metric->store.id) {
461 if (metric->store.id)
462 free(metric->store.id);
463 metric->store.id = id;
464 }
465 return 0;
466 } /* store_metric_store */
468 /* The store's host_lock has to be acquired before calling this function. */
469 static sdb_avltree_t *
470 get_host_children(host_t *host, int type)
471 {
472 if ((type != SDB_SERVICE) && (type != SDB_METRIC)
473 && (type != SDB_ATTRIBUTE))
474 return NULL;
476 if (! host)
477 return NULL;
479 if (type == SDB_ATTRIBUTE)
480 return host->attributes;
481 else if (type == SDB_METRIC)
482 return host->metrics;
483 else
484 return host->services;
485 } /* get_host_children */
487 /*
488 * ts_tojson serializes a time-series to JSON.
489 *
490 * The function never returns an error. Rather, an error message will be part
491 * of the serialized data.
492 */
493 static void
494 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
495 {
496 char start_str[64];
497 char end_str[64];
499 size_t i;
501 /* TODO: make time format configurable */
502 if (! sdb_strftime(start_str, sizeof(start_str), ts->start))
503 snprintf(start_str, sizeof(start_str), "<error>");
504 start_str[sizeof(start_str) - 1] = '\0';
505 if (! sdb_strftime(end_str, sizeof(end_str), ts->end))
506 snprintf(end_str, sizeof(end_str), "<error>");
507 end_str[sizeof(end_str) - 1] = '\0';
509 sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
510 start_str, end_str);
512 for (i = 0; i < ts->data_names_len; ++i) {
513 size_t j;
514 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
516 for (j = 0; j < ts->data_len; ++j) {
517 char time_str[64];
519 if (! sdb_strftime(time_str, sizeof(time_str), ts->data[i][j].timestamp))
520 snprintf(time_str, sizeof(time_str), "<error>");
521 time_str[sizeof(time_str) - 1] = '\0';
523 /* Some GNU libc versions may print '-nan' which we dont' want */
524 if (isnan(ts->data[i][j].value))
525 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
526 "\"value\": \"nan\"}", time_str);
527 else
528 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
529 "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
531 if (j < ts->data_len - 1)
532 sdb_strbuf_append(buf, ",");
533 }
535 if (i < ts->data_names_len - 1)
536 sdb_strbuf_append(buf, "],");
537 else
538 sdb_strbuf_append(buf, "]");
539 }
540 sdb_strbuf_append(buf, "}}");
541 } /* ts_tojson */
543 /*
544 * store writer API
545 */
547 static int
548 store_attribute(sdb_store_attribute_t *attr, sdb_object_t *user_data)
549 {
550 sdb_store_t *st = SDB_STORE(user_data);
551 store_obj_t obj = STORE_OBJ_INIT;
552 sdb_store_obj_t *new = NULL;
553 const char *hostname;
554 host_t *host;
556 sdb_avltree_t *children = NULL;
557 int status = 0;
559 if ((! attr) || (! attr->parent) || (! attr->key))
560 return -1;
562 hostname = attr->hostname;
563 if (attr->parent_type == SDB_HOST)
564 hostname = attr->parent;
565 if (! hostname)
566 return -1;
568 pthread_rwlock_wrlock(&st->host_lock);
569 host = HOST(sdb_avltree_lookup(st->hosts, hostname));
570 if (! host) {
571 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
572 "host '%s' not found", attr->key, hostname);
573 status = -1;
574 }
576 switch (attr->parent_type) {
577 case SDB_HOST:
578 obj.parent = STORE_OBJ(host);
579 obj.parent_tree = get_host_children(host, SDB_ATTRIBUTE);
580 break;
581 case SDB_SERVICE:
582 children = get_host_children(host, SDB_SERVICE);
583 break;
584 case SDB_METRIC:
585 children = get_host_children(host, SDB_METRIC);
586 break;
587 default:
588 status = -1;
589 break;
590 }
592 if (children) {
593 obj.parent = STORE_OBJ(sdb_avltree_lookup(children, attr->parent));
594 if (! obj.parent) {
595 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
596 "%s '%s/%s' not found", attr->key,
597 SDB_STORE_TYPE_TO_NAME(attr->parent_type),
598 attr->hostname, attr->parent);
599 status = -1;
600 }
601 else
602 obj.parent_tree = attr->parent_type == SDB_SERVICE
603 ? SVC(obj.parent)->attributes
604 : METRIC(obj.parent)->attributes;
605 }
607 obj.type = SDB_ATTRIBUTE;
608 obj.name = attr->key;
609 obj.last_update = attr->last_update;
610 obj.backends = attr->backends;
611 obj.backends_num = attr->backends_num;
612 if (! status)
613 status = store_obj(&obj, &new);
615 if (! status) {
616 assert(new);
617 /* update the value if it changed */
618 if (sdb_data_cmp(&ATTR(new)->value, &attr->value))
619 if (sdb_data_copy(&ATTR(new)->value, &attr->value))
620 status = -1;
621 }
623 if (obj.parent != STORE_OBJ(host))
624 sdb_object_deref(SDB_OBJ(obj.parent));
625 sdb_object_deref(SDB_OBJ(host));
626 pthread_rwlock_unlock(&st->host_lock);
628 return status;
629 } /* store_attribute */
631 static int
632 store_host(sdb_store_host_t *host, sdb_object_t *user_data)
633 {
634 sdb_store_t *st = SDB_STORE(user_data);
635 store_obj_t obj = { NULL, st->hosts, SDB_HOST, NULL, 0, NULL, 0 };
636 int status = 0;
638 if ((! host) || (! host->name))
639 return -1;
641 obj.name = host->name;
642 obj.last_update = host->last_update;
643 obj.backends = host->backends;
644 obj.backends_num = host->backends_num;
645 pthread_rwlock_wrlock(&st->host_lock);
646 status = store_obj(&obj, NULL);
647 pthread_rwlock_unlock(&st->host_lock);
649 return status;
650 } /* store_host */
652 static int
653 store_service(sdb_store_service_t *service, sdb_object_t *user_data)
654 {
655 sdb_store_t *st = SDB_STORE(user_data);
656 store_obj_t obj = STORE_OBJ_INIT;
657 host_t *host;
659 int status = 0;
661 if ((! service) || (! service->hostname) || (! service->name))
662 return -1;
664 pthread_rwlock_wrlock(&st->host_lock);
665 host = HOST(sdb_avltree_lookup(st->hosts, service->hostname));
666 obj.parent = STORE_OBJ(host);
667 obj.parent_tree = get_host_children(host, SDB_SERVICE);
668 obj.type = SDB_SERVICE;
669 if (! obj.parent_tree) {
670 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
671 "host '%s' not found", service->name, service->hostname);
672 status = -1;
673 }
675 obj.name = service->name;
676 obj.last_update = service->last_update;
677 obj.backends = service->backends;
678 obj.backends_num = service->backends_num;
679 if (! status)
680 status = store_obj(&obj, NULL);
682 sdb_object_deref(SDB_OBJ(host));
683 pthread_rwlock_unlock(&st->host_lock);
684 return status;
685 } /* store_service */
687 static int
688 store_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
689 {
690 sdb_store_t *st = SDB_STORE(user_data);
691 store_obj_t obj = STORE_OBJ_INIT;
692 sdb_store_obj_t *new = NULL;
693 host_t *host;
695 int status = 0;
697 if ((! metric) || (! metric->hostname) || (! metric->name))
698 return -1;
700 if ((metric->store.type != NULL) != (metric->store.id != NULL))
701 return -1;
703 pthread_rwlock_wrlock(&st->host_lock);
704 host = HOST(sdb_avltree_lookup(st->hosts, metric->hostname));
705 obj.parent = STORE_OBJ(host);
706 obj.parent_tree = get_host_children(host, SDB_METRIC);
707 obj.type = SDB_METRIC;
708 if (! obj.parent_tree) {
709 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
710 "host '%s' not found", metric->name, metric->hostname);
711 status = -1;
712 }
714 obj.name = metric->name;
715 obj.last_update = metric->last_update;
716 obj.backends = metric->backends;
717 obj.backends_num = metric->backends_num;
718 if (! status)
719 status = store_obj(&obj, &new);
720 sdb_object_deref(SDB_OBJ(host));
722 if (status) {
723 pthread_rwlock_unlock(&st->host_lock);
724 return status;
725 }
727 assert(new);
728 if (metric->store.type && metric->store.id)
729 if (store_metric_store(METRIC(new), metric))
730 status = -1;
731 pthread_rwlock_unlock(&st->host_lock);
732 return status;
733 } /* store_metric */
735 sdb_store_writer_t sdb_store_writer = {
736 store_host, store_service, store_metric, store_attribute,
737 };
739 static sdb_object_t *
740 prepare_query(sdb_ast_node_t *ast,
741 sdb_strbuf_t __attribute__((unused)) *errbuf,
742 sdb_object_t __attribute__((unused)) *user_data)
743 {
744 return SDB_OBJ(sdb_store_query_prepare(ast));
745 } /* prepare_query */
747 static int
748 execute_query(sdb_object_t *q,
749 sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
750 sdb_object_t *user_data)
751 {
752 return sdb_store_query_execute(SDB_STORE(user_data),
753 QUERY(q), buf, errbuf);
754 } /* execute_query */
756 sdb_store_reader_t sdb_store_reader = {
757 prepare_query, execute_query,
758 };
760 /*
761 * public API
762 */
764 sdb_store_t *
765 sdb_store_create(void)
766 {
767 return SDB_STORE(sdb_object_create("store", store_type));
768 } /* sdb_store_create */
770 int
771 sdb_store_host(sdb_store_t *store, const char *name, sdb_time_t last_update)
772 {
773 sdb_store_host_t host = {
774 name, last_update, 0, NULL, 0,
775 };
776 return store_host(&host, SDB_OBJ(store));
777 } /* sdb_store_host */
779 int
780 sdb_store_service(sdb_store_t *store, const char *hostname, const char *name,
781 sdb_time_t last_update)
782 {
783 sdb_store_service_t service = {
784 hostname, name, last_update, 0, NULL, 0,
785 };
786 return store_service(&service, SDB_OBJ(store));
787 } /* sdb_store_service */
789 int
790 sdb_store_metric(sdb_store_t *store, const char *hostname, const char *name,
791 sdb_metric_store_t *metric_store, sdb_time_t last_update)
792 {
793 sdb_store_metric_t metric = {
794 hostname, name, { NULL, NULL }, last_update, 0, NULL, 0,
795 };
796 if (metric_store) {
797 metric.store.type = metric_store->type;
798 metric.store.id = metric_store->id;
799 }
800 return store_metric(&metric, SDB_OBJ(store));
801 } /* sdb_store_metric */
803 int
804 sdb_store_attribute(sdb_store_t *store, const char *hostname,
805 const char *key, const sdb_data_t *value, sdb_time_t last_update)
806 {
807 sdb_store_attribute_t attr = {
808 NULL, SDB_HOST, hostname, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
809 };
810 if (value) {
811 attr.value = *value;
812 }
813 return store_attribute(&attr, SDB_OBJ(store));
814 } /* sdb_store_attribute */
816 int
817 sdb_store_service_attr(sdb_store_t *store, const char *hostname,
818 const char *service, const char *key, const sdb_data_t *value,
819 sdb_time_t last_update)
820 {
821 sdb_store_attribute_t attr = {
822 hostname, SDB_SERVICE, service, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
823 };
824 if (value) {
825 attr.value = *value;
826 }
827 return store_attribute(&attr, SDB_OBJ(store));
828 } /* sdb_store_service_attr */
830 int
831 sdb_store_metric_attr(sdb_store_t *store, const char *hostname,
832 const char *metric, const char *key, const sdb_data_t *value,
833 sdb_time_t last_update)
834 {
835 sdb_store_attribute_t attr = {
836 hostname, SDB_METRIC, metric, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
837 };
838 if (value) {
839 attr.value = *value;
840 }
841 return store_attribute(&attr, SDB_OBJ(store));
842 } /* sdb_store_metric_attr */
844 sdb_store_obj_t *
845 sdb_store_get_host(sdb_store_t *store, const char *name)
846 {
847 host_t *host;
849 if ((! store) || (! name))
850 return NULL;
852 host = HOST(sdb_avltree_lookup(store->hosts, name));
853 if (! host)
854 return NULL;
856 return STORE_OBJ(host);
857 } /* sdb_store_get_host */
859 sdb_store_obj_t *
860 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
861 {
862 sdb_avltree_t *children;
864 if ((! host) || (host->type != SDB_HOST) || (! name))
865 return NULL;
867 children = get_host_children(HOST(host), type);
868 if (! children)
869 return NULL;
870 return STORE_OBJ(sdb_avltree_lookup(children, name));
871 } /* sdb_store_get_child */
873 int
874 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
875 {
876 sdb_data_t tmp;
878 if (! obj)
879 return -1;
881 switch (field) {
882 case SDB_FIELD_NAME:
883 tmp.type = SDB_TYPE_STRING;
884 tmp.data.string = strdup(SDB_OBJ(obj)->name);
885 if (! tmp.data.string)
886 return -1;
887 break;
888 case SDB_FIELD_LAST_UPDATE:
889 tmp.type = SDB_TYPE_DATETIME;
890 tmp.data.datetime = obj->last_update;
891 break;
892 case SDB_FIELD_AGE:
893 tmp.type = SDB_TYPE_DATETIME;
894 tmp.data.datetime = sdb_gettime() - obj->last_update;
895 break;
896 case SDB_FIELD_INTERVAL:
897 tmp.type = SDB_TYPE_DATETIME;
898 tmp.data.datetime = obj->interval;
899 break;
900 case SDB_FIELD_BACKEND:
901 if (! res)
902 return 0;
903 tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
904 tmp.data.array.length = obj->backends_num;
905 tmp.data.array.values = obj->backends;
906 return sdb_data_copy(res, &tmp);
907 case SDB_FIELD_VALUE:
908 if (obj->type != SDB_ATTRIBUTE)
909 return -1;
910 if (! res)
911 return 0;
912 return sdb_data_copy(res, &ATTR(obj)->value);
913 case SDB_FIELD_TIMESERIES:
914 if (obj->type != SDB_METRIC)
915 return -1;
916 tmp.type = SDB_TYPE_BOOLEAN;
917 tmp.data.boolean = METRIC(obj)->store.type != NULL;
918 default:
919 return -1;
920 }
921 if (res)
922 *res = tmp;
923 else
924 sdb_data_free_datum(&tmp);
925 return 0;
926 } /* sdb_store_get_field */
928 int
929 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
930 sdb_store_matcher_t *filter)
931 {
932 sdb_avltree_t *tree = NULL;
933 sdb_store_obj_t *attr;
935 if ((! obj) || (! name))
936 return -1;
938 if (obj->type == SDB_HOST)
939 tree = HOST(obj)->attributes;
940 else if (obj->type == SDB_SERVICE)
941 tree = SVC(obj)->attributes;
942 else if (obj->type == SDB_METRIC)
943 tree = METRIC(obj)->attributes;
945 if (! tree)
946 return -1;
948 attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
949 if (! attr)
950 return -1;
951 if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
952 sdb_object_deref(SDB_OBJ(attr));
953 return -1;
954 }
956 assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
957 if (res)
958 sdb_data_copy(res, &ATTR(attr)->value);
959 sdb_object_deref(SDB_OBJ(attr));
960 return 0;
961 } /* sdb_store_get_attr */
963 /* TODO: sdb_store_fetch_timeseries should move into the plugin module */
965 int
966 sdb_store_fetch_timeseries(sdb_store_t *store,
967 const char *hostname, const char *metric,
968 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
969 {
970 sdb_avltree_t *metrics;
971 host_t *host;
972 sdb_metric_t *m;
974 sdb_timeseries_t *ts;
976 int status = 0;
978 if ((! store) || (! hostname) || (! metric) || (! opts) || (! buf))
979 return -1;
981 pthread_rwlock_rdlock(&store->host_lock);
982 host = HOST(sdb_avltree_lookup(store->hosts, hostname));
983 metrics = get_host_children(host, SDB_METRIC);
984 sdb_object_deref(SDB_OBJ(host));
985 if (! metrics) {
986 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
987 "- host '%s' not found", hostname, metric, hostname);
988 pthread_rwlock_unlock(&store->host_lock);
989 return -1;
990 }
992 m = METRIC(sdb_avltree_lookup(metrics, metric));
993 if (! m) {
994 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
995 "- metric '%s' not found", hostname, metric, metric);
996 pthread_rwlock_unlock(&store->host_lock);
997 return -1;
998 }
1000 if ((! m->store.type) || (! m->store.id)) {
1001 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1002 "- no data-store configured for the stored metric",
1003 hostname, metric);
1004 sdb_object_deref(SDB_OBJ(m));
1005 pthread_rwlock_unlock(&store->host_lock);
1006 return -1;
1007 }
1009 {
1010 char type[strlen(m->store.type) + 1];
1011 char id[strlen(m->store.id) + 1];
1013 strncpy(type, m->store.type, sizeof(type));
1014 strncpy(id, m->store.id, sizeof(id));
1015 pthread_rwlock_unlock(&store->host_lock);
1017 ts = sdb_plugin_fetch_timeseries(type, id, opts);
1018 if (! ts) {
1019 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
1020 "- %s fetcher callback returned no data for '%s'",
1021 hostname, metric, type, id);
1022 status = -1;
1023 }
1024 }
1026 ts_tojson(ts, buf);
1027 sdb_object_deref(SDB_OBJ(m));
1028 sdb_timeseries_destroy(ts);
1029 return status;
1030 } /* sdb_store_fetch_timeseries */
1032 int
1033 sdb_store_scan(sdb_store_t *store, int type,
1034 sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
1035 sdb_store_lookup_cb cb, void *user_data)
1036 {
1037 sdb_avltree_iter_t *host_iter = NULL;
1038 int status = 0;
1040 if ((! store) || (! cb))
1041 return -1;
1043 if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
1044 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
1045 return -1;
1046 }
1048 pthread_rwlock_rdlock(&store->host_lock);
1049 host_iter = sdb_avltree_get_iter(store->hosts);
1050 if (! host_iter)
1051 status = -1;
1053 /* has_next returns false if the iterator is NULL */
1054 while (sdb_avltree_iter_has_next(host_iter)) {
1055 sdb_store_obj_t *host;
1056 sdb_avltree_iter_t *iter = NULL;
1058 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1059 assert(host);
1061 if (! sdb_store_matcher_matches(filter, host, NULL))
1062 continue;
1064 if (type == SDB_SERVICE)
1065 iter = sdb_avltree_get_iter(HOST(host)->services);
1066 else if (type == SDB_METRIC)
1067 iter = sdb_avltree_get_iter(HOST(host)->metrics);
1069 if (iter) {
1070 while (sdb_avltree_iter_has_next(iter)) {
1071 sdb_store_obj_t *obj;
1072 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1073 assert(obj);
1075 if (sdb_store_matcher_matches(m, obj, filter)) {
1076 if (cb(obj, filter, user_data)) {
1077 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1078 "an error while scanning");
1079 status = -1;
1080 break;
1081 }
1082 }
1083 }
1084 }
1085 else if (sdb_store_matcher_matches(m, host, filter)) {
1086 if (cb(host, filter, user_data)) {
1087 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1088 "an error while scanning");
1089 status = -1;
1090 }
1091 }
1093 sdb_avltree_iter_destroy(iter);
1094 if (status)
1095 break;
1096 }
1098 sdb_avltree_iter_destroy(host_iter);
1099 pthread_rwlock_unlock(&store->host_lock);
1100 return status;
1101 } /* sdb_store_scan */
1103 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */