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 if (sobj->parent)
92 sdb_object_deref(SDB_OBJ(sobj->parent));
93 } /* store_obj_destroy */
95 static int
96 sdb_host_init(sdb_object_t *obj, va_list ap)
97 {
98 sdb_host_t *sobj = HOST(obj);
99 int ret;
101 /* this will consume the first argument (type) of ap */
102 ret = store_obj_init(obj, ap);
103 if (ret)
104 return ret;
106 sobj->services = sdb_avltree_create();
107 if (! sobj->services)
108 return -1;
109 sobj->metrics = sdb_avltree_create();
110 if (! sobj->metrics)
111 return -1;
112 sobj->attributes = sdb_avltree_create();
113 if (! sobj->attributes)
114 return -1;
115 return 0;
116 } /* sdb_host_init */
118 static void
119 sdb_host_destroy(sdb_object_t *obj)
120 {
121 sdb_host_t *sobj = HOST(obj);
122 assert(obj);
124 store_obj_destroy(obj);
126 if (sobj->services)
127 sdb_avltree_destroy(sobj->services);
128 if (sobj->metrics)
129 sdb_avltree_destroy(sobj->metrics);
130 if (sobj->attributes)
131 sdb_avltree_destroy(sobj->attributes);
132 } /* sdb_host_destroy */
134 static int
135 sdb_service_init(sdb_object_t *obj, va_list ap)
136 {
137 sdb_service_t *sobj = SVC(obj);
138 int ret;
140 /* this will consume the first argument (type) of ap */
141 ret = store_obj_init(obj, ap);
142 if (ret)
143 return ret;
145 sobj->attributes = sdb_avltree_create();
146 if (! sobj->attributes)
147 return -1;
148 return 0;
149 } /* sdb_service_init */
151 static void
152 sdb_service_destroy(sdb_object_t *obj)
153 {
154 sdb_service_t *sobj = SVC(obj);
155 assert(obj);
157 store_obj_destroy(obj);
159 if (sobj->attributes)
160 sdb_avltree_destroy(sobj->attributes);
161 } /* sdb_service_destroy */
163 static int
164 sdb_metric_init(sdb_object_t *obj, va_list ap)
165 {
166 sdb_metric_t *sobj = METRIC(obj);
167 int ret;
169 /* this will consume the first argument (type) of ap */
170 ret = store_obj_init(obj, ap);
171 if (ret)
172 return ret;
174 sobj->attributes = sdb_avltree_create();
175 if (! sobj->attributes)
176 return -1;
178 sobj->store.type = sobj->store.id = NULL;
179 return 0;
180 } /* sdb_metric_init */
182 static void
183 sdb_metric_destroy(sdb_object_t *obj)
184 {
185 sdb_metric_t *sobj = METRIC(obj);
186 assert(obj);
188 store_obj_destroy(obj);
190 if (sobj->attributes)
191 sdb_avltree_destroy(sobj->attributes);
193 if (sobj->store.type)
194 free(sobj->store.type);
195 if (sobj->store.id)
196 free(sobj->store.id);
197 } /* sdb_metric_destroy */
199 static int
200 sdb_attr_init(sdb_object_t *obj, va_list ap)
201 {
202 const sdb_data_t *value;
203 int ret;
205 /* this will consume the first two arguments
206 * (type and last_update) of ap */
207 ret = store_obj_init(obj, ap);
208 if (ret)
209 return ret;
210 value = va_arg(ap, const sdb_data_t *);
212 if (value)
213 if (sdb_data_copy(&ATTR(obj)->value, value))
214 return -1;
215 return 0;
216 } /* sdb_attr_init */
218 static void
219 sdb_attr_destroy(sdb_object_t *obj)
220 {
221 assert(obj);
223 store_obj_destroy(obj);
224 sdb_data_free_datum(&ATTR(obj)->value);
225 } /* sdb_attr_destroy */
227 static sdb_type_t sdb_host_type = {
228 sizeof(sdb_host_t),
229 sdb_host_init,
230 sdb_host_destroy
231 };
233 static sdb_type_t sdb_service_type = {
234 sizeof(sdb_service_t),
235 sdb_service_init,
236 sdb_service_destroy
237 };
239 static sdb_type_t sdb_metric_type = {
240 sizeof(sdb_metric_t),
241 sdb_metric_init,
242 sdb_metric_destroy
243 };
245 static sdb_type_t sdb_attribute_type = {
246 sizeof(sdb_attribute_t),
247 sdb_attr_init,
248 sdb_attr_destroy
249 };
251 /*
252 * private helper functions
253 */
255 static sdb_host_t *
256 lookup_host(const char *name)
257 {
258 return HOST(sdb_avltree_lookup(hosts, name));
259 } /* lookup_host */
261 static int
262 record_backend(sdb_store_obj_t *obj)
263 {
264 const sdb_plugin_info_t *info;
265 char **tmp;
266 size_t i;
268 info = sdb_plugin_current();
269 if (! info)
270 return 0;
272 for (i = 0; i < obj->backends_num; ++i)
273 if (!strcasecmp(obj->backends[i], info->plugin_name))
274 return 0;
276 tmp = realloc(obj->backends,
277 (obj->backends_num + 1) * sizeof(*obj->backends));
278 if (! tmp)
279 return -1;
281 obj->backends = tmp;
282 obj->backends[obj->backends_num] = strdup(info->plugin_name);
283 if (! obj->backends[obj->backends_num])
284 return -1;
286 ++obj->backends_num;
287 return 0;
288 } /* record_backend */
290 static int
291 store_obj(sdb_avltree_t *parent_tree, int type, const char *name,
292 sdb_time_t last_update, sdb_store_obj_t **updated_obj)
293 {
294 sdb_store_obj_t *old, *new;
295 int status = 0;
297 assert(parent_tree);
299 if (last_update <= 0)
300 last_update = sdb_gettime();
302 old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
303 if (old) {
304 if (old->last_update > last_update) {
305 sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
306 "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
307 SDB_STORE_TYPE_TO_NAME(type), name,
308 last_update, old->last_update);
309 /* don't report an error; the object may be updated by multiple
310 * backends */
311 status = 1;
312 }
313 else if (old->last_update == last_update) {
314 /* don't report an error and also don't even log this to avoid
315 * excessive noise on high sampling frequencies */
316 status = 1;
317 }
318 else {
319 sdb_time_t interval = last_update - old->last_update;
320 old->last_update = last_update;
321 if (interval) {
322 if (old->interval)
323 old->interval = (sdb_time_t)((0.9 * (double)old->interval)
324 + (0.1 * (double)interval));
325 else
326 old->interval = interval;
327 }
328 }
330 new = old;
331 sdb_object_deref(SDB_OBJ(old));
332 }
333 else {
334 if (type == SDB_ATTRIBUTE) {
335 /* the value will be updated by the caller */
336 new = STORE_OBJ(sdb_object_create(name, sdb_attribute_type,
337 type, last_update, NULL));
338 }
339 else {
340 sdb_type_t t;
341 t = type == SDB_HOST
342 ? sdb_host_type
343 : type == SDB_SERVICE
344 ? sdb_service_type
345 : sdb_metric_type;
346 new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
347 }
349 if (new) {
350 status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
352 /* pass control to the tree or destroy in case of an error */
353 sdb_object_deref(SDB_OBJ(new));
354 }
355 else {
356 char errbuf[1024];
357 sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
358 SDB_STORE_TYPE_TO_NAME(type), name,
359 sdb_strerror(errno, errbuf, sizeof(errbuf)));
360 status = -1;
361 }
362 }
364 if (status < 0)
365 return status;
366 assert(new);
368 if (updated_obj)
369 *updated_obj = new;
371 if (record_backend(new))
372 return -1;
373 return status;
374 } /* store_obj */
376 static int
377 store_attr(sdb_avltree_t *attributes, const char *key, const sdb_data_t *value,
378 sdb_time_t last_update)
379 {
380 sdb_store_obj_t *attr = NULL;
381 int status;
383 status = store_obj(attributes, SDB_ATTRIBUTE, key, last_update, &attr);
384 if (status)
385 return status;
387 /* don't update unchanged values */
388 if (! sdb_data_cmp(&ATTR(attr)->value, value))
389 return status;
391 assert(attr);
392 if (sdb_data_copy(&ATTR(attr)->value, value))
393 return -1;
394 return status;
395 } /* store_attr */
397 /* The host_lock has to be acquired before calling this function. */
398 static sdb_avltree_t *
399 get_host_children(const char *hostname, int type)
400 {
401 char *cname = NULL;
402 sdb_host_t *host;
404 assert(hostname);
405 assert((type == SDB_SERVICE) || (type == SDB_METRIC)
406 || (type == SDB_ATTRIBUTE));
408 if (! hosts)
409 return NULL;
411 cname = sdb_plugin_cname(strdup(hostname));
412 if (! cname) {
413 sdb_log(SDB_LOG_ERR, "store: strdup failed");
414 return NULL;
415 }
417 host = lookup_host(cname);
418 free(cname);
419 if (! host)
420 return NULL;
422 sdb_object_deref(SDB_OBJ(host));
423 if (type == SDB_ATTRIBUTE)
424 return host->attributes;
425 else if (type == SDB_METRIC)
426 return host->metrics;
427 else
428 return host->services;
429 } /* get_host_children */
431 /*
432 * store_common_tojson serializes common object attributes to JSON.
433 *
434 * The function never returns an error. Rather, an error message will be part
435 * of the serialized data.
436 */
437 static void
438 store_common_tojson(sdb_store_obj_t *obj, sdb_strbuf_t *buf)
439 {
440 char time_str[64];
441 char interval_str[64];
442 size_t i;
444 /* TODO: make time and interval formats configurable */
445 if (! sdb_strftime(time_str, sizeof(time_str),
446 "%F %T %z", obj->last_update))
447 snprintf(time_str, sizeof(time_str), "<error>");
448 time_str[sizeof(time_str) - 1] = '\0';
450 if (! sdb_strfinterval(interval_str, sizeof(interval_str),
451 obj->interval))
452 snprintf(interval_str, sizeof(interval_str), "<error>");
453 interval_str[sizeof(interval_str) - 1] = '\0';
455 sdb_strbuf_append(buf, "\"last_update\": \"%s\", "
456 "\"update_interval\": \"%s\", \"backends\": [",
457 time_str, interval_str);
459 for (i = 0; i < obj->backends_num; ++i) {
460 sdb_strbuf_append(buf, "\"%s\"", obj->backends[i]);
461 if (i < obj->backends_num - 1)
462 sdb_strbuf_append(buf, ",");
463 }
464 sdb_strbuf_append(buf, "]");
465 } /* store_common_tojson */
467 /*
468 * store_obj_tojson serializes attribute / metric / service objects to JSON.
469 *
470 * The function never returns an error. Rather, an error message will be part
471 * of the serialized data.
472 */
473 static void
474 store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf,
475 sdb_store_matcher_t *filter, int flags)
476 {
477 sdb_avltree_iter_t *iter;
479 assert((type == SDB_ATTRIBUTE)
480 || (type == SDB_METRIC)
481 || (type == SDB_SERVICE));
483 sdb_strbuf_append(buf, "[");
484 iter = sdb_avltree_get_iter(tree);
485 if (! iter) {
486 char errbuf[1024];
487 sdb_log(SDB_LOG_ERR, "store: Failed to retrieve %ss: %s\n",
488 SDB_STORE_TYPE_TO_NAME(type),
489 sdb_strerror(errno, errbuf, sizeof(errbuf)));
490 sdb_strbuf_append(buf, "{\"error\": \"failed to retrieve %ss: %s\"}",
491 SDB_STORE_TYPE_TO_NAME(type), errbuf);
492 }
494 /* has_next returns false if the iterator is NULL */
495 while (sdb_avltree_iter_has_next(iter)) {
496 sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
497 assert(sobj);
498 assert(sobj->type == type);
500 if (filter && (! sdb_store_matcher_matches(filter, sobj, NULL)))
501 continue;
503 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(sobj)->name);
504 if (sobj->type == SDB_ATTRIBUTE) {
505 char tmp[sdb_data_strlen(&ATTR(sobj)->value) + 1];
506 sdb_data_format(&ATTR(sobj)->value, tmp, sizeof(tmp),
507 SDB_DOUBLE_QUOTED);
508 sdb_strbuf_append(buf, "\"value\": %s, ", tmp);
509 }
510 store_common_tojson(sobj, buf);
512 if ((sobj->type == SDB_SERVICE)
513 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
514 sdb_strbuf_append(buf, ", \"attributes\": ");
515 store_obj_tojson(SVC(sobj)->attributes, SDB_ATTRIBUTE,
516 buf, filter, flags);
517 }
518 else if ((sobj->type == SDB_METRIC)
519 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
520 sdb_strbuf_append(buf, ", \"attributes\": ");
521 store_obj_tojson(METRIC(sobj)->attributes, SDB_ATTRIBUTE,
522 buf, filter, flags);
523 }
524 sdb_strbuf_append(buf, "}");
526 if (sdb_avltree_iter_has_next(iter))
527 sdb_strbuf_append(buf, ",");
528 }
530 sdb_avltree_iter_destroy(iter);
531 sdb_strbuf_append(buf, "]");
532 } /* store_obj_tojson */
534 /*
535 * ts_tojson serializes a time-series to JSON.
536 *
537 * The function never returns an error. Rather, an error message will be part
538 * of the serialized data.
539 */
540 static void
541 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
542 {
543 char start_str[64];
544 char end_str[64];
546 size_t i;
548 /* TODO: make time format configurable */
549 if (! sdb_strftime(start_str, sizeof(start_str),
550 "%F %T %z", ts->start))
551 snprintf(start_str, sizeof(start_str), "<error>");
552 start_str[sizeof(start_str) - 1] = '\0';
553 if (! sdb_strftime(end_str, sizeof(end_str),
554 "%F %T %z", ts->end))
555 snprintf(end_str, sizeof(end_str), "<error>");
556 end_str[sizeof(end_str) - 1] = '\0';
558 sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", {",
559 start_str, end_str);
561 for (i = 0; i < ts->data_names_len; ++i) {
562 size_t j;
563 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
565 for (j = 0; j < ts->data_len; ++j) {
566 char time_str[64];
568 if (! sdb_strftime(time_str, sizeof(time_str),
569 "%F %T %z", ts->data[i][j].timestamp))
570 snprintf(time_str, sizeof(time_str), "<error>");
571 time_str[sizeof(time_str) - 1] = '\0';
573 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
574 "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
576 if (j < ts->data_len - 1)
577 sdb_strbuf_append(buf, ",");
578 }
580 if (i < ts->data_names_len - 1)
581 sdb_strbuf_append(buf, "],");
582 else
583 sdb_strbuf_append(buf, "]");
584 }
585 sdb_strbuf_append(buf, "}}");
586 } /* ts_tojson */
588 /*
589 * public API
590 */
592 void
593 sdb_store_clear(void)
594 {
595 sdb_avltree_destroy(hosts);
596 hosts = NULL;
597 } /* sdb_store_clear */
599 int
600 sdb_store_host(const char *name, sdb_time_t last_update)
601 {
602 char *cname = NULL;
603 int status = 0;
605 if (! name)
606 return -1;
608 cname = sdb_plugin_cname(strdup(name));
609 if (! cname) {
610 sdb_log(SDB_LOG_ERR, "store: strdup failed");
611 return -1;
612 }
614 pthread_rwlock_wrlock(&host_lock);
615 if (! hosts)
616 if (! (hosts = sdb_avltree_create()))
617 status = -1;
619 if (! status)
620 status = store_obj(hosts, SDB_HOST, cname, last_update, NULL);
621 pthread_rwlock_unlock(&host_lock);
623 free(cname);
624 return status;
625 } /* sdb_store_host */
627 _Bool
628 sdb_store_has_host(const char *name)
629 {
630 sdb_host_t *host;
632 if (! name)
633 return NULL;
635 host = lookup_host(name);
636 return host != NULL;
637 } /* sdb_store_has_host */
639 sdb_store_obj_t *
640 sdb_store_get_host(const char *name)
641 {
642 sdb_host_t *host;
644 if (! name)
645 return NULL;
647 host = lookup_host(name);
648 if (! host)
649 return NULL;
651 return STORE_OBJ(host);
652 } /* sdb_store_get_host */
654 int
655 sdb_store_attribute(const char *hostname,
656 const char *key, const sdb_data_t *value,
657 sdb_time_t last_update)
658 {
659 sdb_avltree_t *attrs;
660 int status = 0;
662 if ((! hostname) || (! key))
663 return -1;
665 pthread_rwlock_wrlock(&host_lock);
666 attrs = get_host_children(hostname, SDB_ATTRIBUTE);
667 if (! attrs) {
668 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
669 "host '%s' not found", key, hostname);
670 status = -1;
671 }
673 if (! status)
674 status = store_attr(attrs, key, value, last_update);
676 pthread_rwlock_unlock(&host_lock);
677 return status;
678 } /* sdb_store_attribute */
680 int
681 sdb_store_service(const char *hostname, const char *name,
682 sdb_time_t last_update)
683 {
684 sdb_avltree_t *services;
686 int status = 0;
688 if ((! hostname) || (! name))
689 return -1;
691 pthread_rwlock_wrlock(&host_lock);
692 services = get_host_children(hostname, SDB_SERVICE);
693 if (! services) {
694 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
695 "host '%s' not found", name, hostname);
696 status = -1;
697 }
699 if (! status)
700 status = store_obj(services, SDB_SERVICE, name, last_update, NULL);
701 pthread_rwlock_unlock(&host_lock);
702 return status;
703 } /* sdb_store_service */
705 int
706 sdb_store_service_attr(const char *hostname, const char *service,
707 const char *key, const sdb_data_t *value, sdb_time_t last_update)
708 {
709 sdb_avltree_t *services;
710 sdb_service_t *svc;
711 int status = 0;
713 if ((! hostname) || (! service) || (! key))
714 return -1;
716 pthread_rwlock_wrlock(&host_lock);
717 services = get_host_children(hostname, SDB_SERVICE);
718 if (! services) {
719 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
720 "for service '%s' - host '%ss' not found",
721 key, service, hostname);
722 pthread_rwlock_unlock(&host_lock);
723 return -1;
724 }
726 svc = SVC(sdb_avltree_lookup(services, service));
727 if (! svc) {
728 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
729 "service '%s/%s' not found", key, hostname, service);
730 status = -1;
731 }
733 if (! status)
734 status = store_attr(svc->attributes, key, value, last_update);
736 sdb_object_deref(SDB_OBJ(svc));
737 pthread_rwlock_unlock(&host_lock);
738 return status;
739 } /* sdb_store_service_attr */
741 int
742 sdb_store_metric(const char *hostname, const char *name,
743 sdb_metric_store_t *store, sdb_time_t last_update)
744 {
745 sdb_store_obj_t *obj = NULL;
746 sdb_metric_t *metric;
748 sdb_avltree_t *metrics;
750 int status = 0;
752 if ((! hostname) || (! name))
753 return -1;
754 if (store && ((! store->type) || (! store->id)))
755 return -1;
757 pthread_rwlock_wrlock(&host_lock);
758 metrics = get_host_children(hostname, SDB_METRIC);
759 if (! metrics) {
760 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
761 "host '%s' not found", name, hostname);
762 status = -1;
763 }
765 if (! status)
766 status = store_obj(metrics, SDB_METRIC, name, last_update, &obj);
768 if (status || (! store)) {
769 pthread_rwlock_unlock(&host_lock);
770 return status;
771 }
773 assert(obj);
774 metric = METRIC(obj);
776 if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
777 if (metric->store.type)
778 free(metric->store.type);
779 metric->store.type = strdup(store->type);
780 }
781 if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
782 if (metric->store.id)
783 free(metric->store.id);
784 metric->store.id = strdup(store->id);
785 }
787 if ((! metric->store.type) || (! metric->store.id)) {
788 if (metric->store.type)
789 free(metric->store.type);
790 if (metric->store.id)
791 free(metric->store.id);
792 metric->store.type = metric->store.id = NULL;
793 status = -1;
794 }
795 pthread_rwlock_unlock(&host_lock);
796 return status;
797 } /* sdb_store_metric */
799 int
800 sdb_store_metric_attr(const char *hostname, const char *metric,
801 const char *key, const sdb_data_t *value, sdb_time_t last_update)
802 {
803 sdb_avltree_t *metrics;
804 sdb_metric_t *m;
805 int status = 0;
807 if ((! hostname) || (! metric) || (! key))
808 return -1;
810 pthread_rwlock_wrlock(&host_lock);
811 metrics = get_host_children(hostname, SDB_METRIC);
812 if (! metrics) {
813 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
814 "for metric '%s' - host '%s' not found",
815 key, metric, hostname);
816 pthread_rwlock_unlock(&host_lock);
817 return -1;
818 }
820 m = METRIC(sdb_avltree_lookup(metrics, metric));
821 if (! m) {
822 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
823 "metric '%s/%s' not found", key, hostname, metric);
824 status = -1;
825 }
827 if (! status)
828 status = store_attr(m->attributes, key, value, last_update);
830 sdb_object_deref(SDB_OBJ(m));
831 pthread_rwlock_unlock(&host_lock);
832 return status;
833 } /* sdb_store_metric_attr */
835 int
836 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
837 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
838 {
839 sdb_avltree_t *metrics;
840 sdb_metric_t *m;
842 sdb_timeseries_t *ts;
844 if ((! hostname) || (! metric) || (! opts) || (! buf))
845 return -1;
847 pthread_rwlock_rdlock(&host_lock);
848 metrics = get_host_children(hostname, SDB_METRIC);
849 if (! metrics) {
850 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
851 "- host '%s' not found", hostname, metric, hostname);
852 pthread_rwlock_unlock(&host_lock);
853 return -1;
854 }
856 m = METRIC(sdb_avltree_lookup(metrics, metric));
857 if (! m) {
858 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
859 "- metric '%s' not found", hostname, metric, metric);
860 pthread_rwlock_unlock(&host_lock);
861 return -1;
862 }
864 if ((! m->store.type) || (! m->store.id)) {
865 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
866 "- no data-store configured for the stored metric",
867 hostname, metric);
868 pthread_rwlock_unlock(&host_lock);
869 return -1;
870 }
872 {
873 char type[strlen(m->store.type) + 1];
874 char id[strlen(m->store.id) + 1];
876 strncpy(type, m->store.type, sizeof(type));
877 strncpy(id, m->store.id, sizeof(id));
878 pthread_rwlock_unlock(&host_lock);
880 ts = sdb_plugin_fetch_timeseries(type, id, opts);
881 if (! ts) {
882 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
883 "- %s fetcher callback returned no data for '%s'",
884 hostname, metric, type, id);
885 return -1;
886 }
887 }
889 ts_tojson(ts, buf);
890 sdb_timeseries_destroy(ts);
891 return 0;
892 } /* sdb_store_fetch_timeseries */
894 int
895 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
896 {
897 if ((! obj) || (! res))
898 return -1;
900 switch (field) {
901 case SDB_FIELD_LAST_UPDATE:
902 res->type = SDB_TYPE_DATETIME;
903 res->data.datetime = obj->last_update;
904 break;
905 case SDB_FIELD_AGE:
906 res->type = SDB_TYPE_DATETIME;
907 res->data.datetime = sdb_gettime() - obj->last_update;
908 break;
909 case SDB_FIELD_INTERVAL:
910 res->type = SDB_TYPE_DATETIME;
911 res->data.datetime = obj->interval;
912 break;
913 case SDB_FIELD_BACKEND:
914 /* TODO: add support for storing array values in a data object
915 * for now, fall thru to the error case */
916 default:
917 return -1;
918 }
919 return 0;
920 } /* sdb_store_get_field */
922 int
923 sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
924 sdb_store_matcher_t *filter, int flags)
925 {
926 sdb_host_t *host = HOST(h);
928 if ((! h) || (h->type != SDB_HOST) || (! buf))
929 return -1;
931 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name);
932 store_common_tojson(h, buf);
934 if (! (flags & SDB_SKIP_ATTRIBUTES)) {
935 sdb_strbuf_append(buf, ", \"attributes\": ");
936 store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, filter, flags);
937 }
939 if (! (flags & SDB_SKIP_METRICS)) {
940 sdb_strbuf_append(buf, ", \"metrics\": ");
941 store_obj_tojson(host->metrics, SDB_METRIC, buf, filter, flags);
942 }
944 if (! (flags & SDB_SKIP_SERVICES)) {
945 sdb_strbuf_append(buf, ", \"services\": ");
946 store_obj_tojson(host->services, SDB_SERVICE, buf, filter, flags);
947 }
949 sdb_strbuf_append(buf, "}");
950 return 0;
951 } /* sdb_store_host_tojson */
953 int
954 sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
955 {
956 sdb_avltree_iter_t *host_iter;
957 size_t len;
959 if (! buf)
960 return -1;
962 pthread_rwlock_rdlock(&host_lock);
964 host_iter = sdb_avltree_get_iter(hosts);
965 if (! host_iter) {
966 pthread_rwlock_unlock(&host_lock);
967 return -1;
968 }
970 sdb_strbuf_append(buf, "{\"hosts\":[");
972 len = sdb_strbuf_len(buf);
973 while (sdb_avltree_iter_has_next(host_iter)) {
974 sdb_store_obj_t *host;
976 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
977 assert(host);
979 if (filter && (! sdb_store_matcher_matches(filter, host, NULL)))
980 continue;
982 if (sdb_strbuf_len(buf) > len)
983 sdb_strbuf_append(buf, ",");
984 len = sdb_strbuf_len(buf);
986 if (sdb_store_host_tojson(host, buf, filter, flags))
987 return -1;
988 }
990 sdb_strbuf_append(buf, "]}");
992 sdb_avltree_iter_destroy(host_iter);
993 pthread_rwlock_unlock(&host_lock);
994 return 0;
995 } /* sdb_store_tojson */
997 /* TODO: actually support hierarchical data */
998 int
999 sdb_store_iterate(sdb_store_iter_cb cb, void *user_data)
1000 {
1001 sdb_avltree_iter_t *host_iter;
1002 int status = 0;
1004 pthread_rwlock_rdlock(&host_lock);
1006 host_iter = sdb_avltree_get_iter(hosts);
1007 if (! host_iter)
1008 status = -1;
1010 /* has_next returns false if the iterator is NULL */
1011 while (sdb_avltree_iter_has_next(host_iter)) {
1012 sdb_store_obj_t *host;
1014 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1015 assert(host);
1017 if (cb(host, user_data)) {
1018 status = -1;
1019 break;
1020 }
1021 }
1023 sdb_avltree_iter_destroy(host_iter);
1024 pthread_rwlock_unlock(&host_lock);
1025 return status;
1026 } /* sdb_store_iterate */
1028 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */