Code

timeseries::rrdtool: Added support for RRDCacheD.
authorSebastian Harl <sh@tokkee.org>
Thu, 28 Aug 2014 23:42:34 +0000 (16:42 -0700)
committerSebastian Harl <sh@tokkee.org>
Thu, 28 Aug 2014 23:44:24 +0000 (16:44 -0700)
After enabling RRDCacheD support, the plugin supports an additional
time-series fetcher callback which may be used to flush outstanding updates to
an RRD file before fetching data from it.

configure.ac
doc/sysdbd-timeseries-rrdtool.5.txt
src/plugins/timeseries/rrdtool.c

index ef6868e7bab7509c129a75fde51afcf83028b2f3..37d77ff9dcb18f202ffe40aab8b4af50aeee6ffa 100644 (file)
@@ -422,6 +422,10 @@ else
        AC_MSG_ERROR([Invalid value for option --with-librrd=$with_librrd (expected "yes", "no", or "auto")])
 fi; fi
 
+if test "x$have_librrd" = "xyes"; then
+       AC_CHECK_HEADERS([rrd_client.h])
+fi
+
 dnl Feature checks.
 build_documentation="yes"
 
index 693caadcbe960bc707ee38c5566b24baf7d8b399..b36a177c5c1c95b7cb60ab03e2b7b86ea4940471 100644 (file)
@@ -17,8 +17,15 @@ data from RRD files.
 
 CONFIGURATION
 -------------
-*timeseries::rrdtool* does not currently accept any configuration options. It
-registers a time-series fetcher callback called *rrdtool*.
+*timeseries::rrdtool* accepts the following configuration options. In addition
+to any configuration, the plugin always registers a time-series fetcher
+callback called *rrdtool* which may be used to access RRD files on local disk.
+
+*RRDCacheD* '<addr>'::
+       Enable support for RRDCacheD. When using this option, an additional
+       time-series fetcher callback called *rrdcached* is made available. It will
+       flush cached updates for an RRD file before fetching data from it.
+       Currently, local connections through a UNIX socket are supported only.
 
 SEE ALSO
 --------
index 572c033e0361bf13b49be6500d0287fcf768fabd..db24903f5513785852a22748f05de88b1a4b0f91 100644 (file)
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#if HAVE_CONFIG_H
+#      include "config.h"
+#endif /* HAVE_CONFIG_H */
+
 #include "sysdb.h"
 #include "core/plugin.h"
 #include "utils/error.h"
 
+#include "liboconfig/utils.h"
+
 #include <errno.h>
+#include <stdlib.h>
 #include <rrd.h>
+#ifdef HAVE_RRD_CLIENT_H
+#      include <rrd_client.h>
+#endif
 
 SDB_PLUGIN_MAGIC;
 
+/* Current versions of RRDtool do not support multiple RRDCacheD client
+ * connections. Use this to guard against multiple configured RRDCacheD
+ * instances. */
+static _Bool rrdcached_in_use = 0;
+
 /*
  * plugin API
  */
 
 static sdb_timeseries_t *
 sdb_rrd_fetch(const char *id, sdb_timeseries_opts_t *opts,
-               sdb_object_t __attribute__((unused)) *user_data)
+               sdb_object_t *user_data)
 {
        sdb_timeseries_t *ts;
 
@@ -54,6 +69,34 @@ sdb_rrd_fetch(const char *id, sdb_timeseries_opts_t *opts,
        char **ds_namv = NULL;
        rrd_value_t *data = NULL, *data_ptr;
 
+       if (user_data) {
+#ifdef HAVE_RRD_CLIENT_H
+               /* -> use RRDCacheD */
+               char *addr = SDB_OBJ_WRAPPER(user_data)->data;
+
+               rrd_clear_error();
+               if (! rrdc_is_connected(addr)) {
+                       if (rrdc_connect(addr)) {
+                               sdb_log(SDB_LOG_ERR, "timeseries::rrdtool: Failed to "
+                                               "connectd to RRDCacheD at %s: %s",
+                                               addr, rrd_get_error());
+                               return NULL;
+                       }
+               }
+
+               if (rrdc_flush(id)) {
+                       sdb_log(SDB_LOG_ERR, "timeseries::rrdtool: Failed to flush "
+                                       "'%s' through RRDCacheD: %s", id, rrd_get_error());
+                       return NULL;
+               }
+#else
+               sdb_log(SDB_LOG_ERR, "timeseries::rrdtool: Callback called with "
+                               "RRDCacheD address but your build of SysDB does not support "
+                               "that");
+               return NULL;
+#endif
+       }
+
 #define FREE_RRD_DATA() \
        do { \
                size_t i; \
@@ -104,6 +147,95 @@ sdb_rrd_fetch(const char *id, sdb_timeseries_opts_t *opts,
        return ts;
 } /* sdb_rrd_fetch */
 
+static int
+sdb_rrdcached_shutdown(sdb_object_t __attribute__((unused)) *user_data)
+{
+#ifdef HAVE_RRD_CLIENT_H
+       rrdc_disconnect();
+#endif
+       return 0;
+} /* sdb_rrdcached_shutdown */
+
+static int
+sdb_rrd_config_rrdcached(oconfig_item_t *ci)
+{
+       sdb_object_t *ud;
+       char *addr = NULL;
+
+       if (rrdcached_in_use) {
+               sdb_log(SDB_LOG_ERR, "timeseries::rrdtool: RRDCacheD does "
+                               "not support multiple connections");
+               return -1;
+       }
+
+#ifndef HAVE_RRD_CLIENT_H
+       sdb_log(SDB_LOG_ERR, "timeseries::rrdtool: RRDCacheD client "
+                       "support not available in your SysDB build");
+       return -1;
+#else
+       if (oconfig_get_string(ci, &addr)) {
+               sdb_log(SDB_LOG_ERR, "timeseries::unixsock: RRDCacheD requires "
+                               "a single string argument\n\tUsage <RRDCacheD ADDR>");
+               return -1;
+       }
+       if ((*addr != '/') && strncmp(addr, "unix:", strlen("unix:"))) {
+               /* XXX: add (optional) support for rrdc_fetch if available */
+               sdb_log(SDB_LOG_ERR, "timeseries::unixsock: RRDCacheD only "
+                               "supports local (UNIX socket) addresses");
+               return -1;
+       }
+
+       addr = strdup(addr);
+       if (! addr) {
+               char errbuf[1024];
+               sdb_log(SDB_LOG_ERR, "timeseries::unixsock: Failed to duplicate "
+                               "string: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
+               return -1;
+       }
+       if (ci->children_num)
+               sdb_log(SDB_LOG_WARNING, "timeseries::unixsock: RRDCacheD does "
+                               "not support any child config options");
+
+       ud = sdb_object_create_wrapper("rrdcached-addr", addr, free);
+       if (! ud) {
+               char errbuf[1024];
+               sdb_log(SDB_LOG_ERR, "timeseries::unixsock: Failed to create "
+                               "user-data object: %s",
+                               sdb_strerror(errno, errbuf, sizeof(errbuf)));
+               free(addr);
+               return -1;
+       }
+
+       sdb_plugin_register_ts_fetcher("rrdcached", sdb_rrd_fetch, ud);
+       sdb_plugin_register_shutdown("rrdcached", sdb_rrdcached_shutdown, NULL);
+       sdb_object_deref(ud);
+       rrdcached_in_use = 1;
+       return 0;
+#endif
+} /* sdb_rrd_config_rrdcached */
+
+static int
+sdb_rrd_config(oconfig_item_t *ci)
+{
+       int i;
+
+       if (! ci) { /* reconfigure */
+               rrdcached_in_use = 0;
+               return 0;
+       }
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *child = ci->children + i;
+
+               if (! strcasecmp(child->key, "RRDCacheD"))
+                       sdb_rrd_config_rrdcached(child);
+               else
+                       sdb_log(SDB_LOG_WARNING, "timeseries::rrdtool: Ignoring "
+                                       "unknown config option '%s'.", child->key);
+       }
+       return 0;
+} /* sdb_rrd_config */
+
 int
 sdb_module_init(sdb_plugin_info_t *info)
 {
@@ -116,6 +248,7 @@ sdb_module_init(sdb_plugin_info_t *info)
        sdb_plugin_set_info(info, SDB_PLUGIN_INFO_PLUGIN_VERSION, SDB_VERSION);
 
        sdb_plugin_register_ts_fetcher("rrdtool", sdb_rrd_fetch, NULL);
+       sdb_plugin_register_config(sdb_rrd_config);
        return 0;
 } /* sdb_module_init */