Fixed a small typo in the ethstat plugin
[collectd.git] / src / rrdcached.c
index fef78ce..11c1c6a 100644 (file)
 #include "collectd.h"
 #include "plugin.h"
 #include "common.h"
+#include "utils_rrdcreate.h"
 
+#undef HAVE_CONFIG_H
+#include <rrd.h>
 #include <rrd_client.h>
 
 /*
  * Private variables
  */
-static const char *config_keys[] =
+static char *datadir = NULL;
+static char *daemon_address = NULL;
+static int config_create_files = 1;
+static int config_collect_stats = 1;
+static rrdcreate_config_t rrdcreate_config =
 {
-  "DaemonAddress",
-  "DataDir"
+       /* stepsize = */ 0,
+       /* heartbeat = */ 0,
+       /* rrarows = */ 1200,
+       /* xff = */ 0.1,
+
+       /* timespans = */ NULL,
+       /* timespans_num = */ 0,
+
+       /* consolidation_functions = */ NULL,
+       /* consolidation_functions_num = */ 0
 };
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-static char *datadir = NULL;
-static char *daemon_address = NULL;
+/*
+ * Prototypes.
+ */
+static int rc_write (const data_set_t *ds, const value_list_t *vl,
+    user_data_t __attribute__((unused)) *user_data);
+static int rc_flush (__attribute__((unused)) cdtime_t timeout,
+    const char *identifier, __attribute__((unused)) user_data_t *ud);
 
 static int value_list_to_string (char *buffer, int buffer_len,
     const data_set_t *ds, const value_list_t *vl)
@@ -44,12 +63,14 @@ static int value_list_to_string (char *buffer, int buffer_len,
   int offset;
   int status;
   int i;
+  time_t t;
 
   assert (0 == strcmp (ds->type, vl->type));
 
   memset (buffer, '\0', buffer_len);
 
-  status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
+  t = CDTIME_T_TO_TIME_T (vl->time);
+  status = ssnprintf (buffer, buffer_len, "%lu", (unsigned long) t);
   if ((status < 1) || (status >= buffer_len))
     return (-1);
   offset = status;
@@ -57,18 +78,29 @@ static int value_list_to_string (char *buffer, int buffer_len,
   for (i = 0; i < ds->ds_num; i++)
   {
     if ((ds->ds[i].type != DS_TYPE_COUNTER)
-        && (ds->ds[i].type != DS_TYPE_GAUGE))
+        && (ds->ds[i].type != DS_TYPE_GAUGE)
+       && (ds->ds[i].type != DS_TYPE_DERIVE)
+       && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
       return (-1);
 
     if (ds->ds[i].type == DS_TYPE_COUNTER)
     {
       status = ssnprintf (buffer + offset, buffer_len - offset,
-          ",%llu", vl->values[i].counter);
+          ":%llu", vl->values[i].counter);
     }
-    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+    else if (ds->ds[i].type == DS_TYPE_GAUGE) 
     {
       status = ssnprintf (buffer + offset, buffer_len - offset,
-          ",%lf", vl->values[i].gauge);
+          ":%f", vl->values[i].gauge);
+    }
+    else if (ds->ds[i].type == DS_TYPE_DERIVE) {
+      status = ssnprintf (buffer + offset, buffer_len - offset,
+         ":%"PRIi64, vl->values[i].derive);
+    }
+    else /* if (ds->ds[i].type == DS_TYPE_ABSOLUTE) */ {
+      status = ssnprintf (buffer + offset, buffer_len - offset,
+         ":%"PRIu64, vl->values[i].absolute);
     }
 
     if ((status < 1) || (status >= (buffer_len - offset)))
@@ -129,48 +161,209 @@ static int value_list_to_filename (char *buffer, int buffer_len,
   return (0);
 } /* int value_list_to_filename */
 
-static int rc_config (const char *key, const char *value)
+static const char *config_get_string (oconfig_item_t *ci)
 {
-  if (strcasecmp ("DataDir", key) == 0)
+  if ((ci->children_num != 0) || (ci->values_num != 1)
+      || ((ci->values[0].type != OCONFIG_TYPE_STRING)
+        && (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)))
   {
-    if (datadir != NULL)
-      free (datadir);
-    datadir = strdup (value);
-    if (datadir != NULL)
+    ERROR ("rrdcached plugin: %s expects a single string argument.",
+        ci->key);
+    return (NULL);
+  }
+
+  if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN) {
+    if (ci->values[0].value.boolean)
+      return "true";
+    else
+      return "false";
+  }
+  return (ci->values[0].value.string);
+} /* const char *config_get_string */
+
+static int rc_config (oconfig_item_t *ci)
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; ++i) {
+    const char *key = ci->children[i].key;
+    const char *value = config_get_string (ci->children + i);
+
+    if (value == NULL) /* config_get_strings prints error message */
+      continue;
+
+    if (strcasecmp ("DataDir", key) == 0)
     {
-      int len = strlen (datadir);
-      while ((len > 0) && (datadir[len - 1] == '/'))
+      if (datadir != NULL)
+        free (datadir);
+      datadir = strdup (value);
+      if (datadir != NULL)
       {
-        len--;
-        datadir[len] = '\0';
+        int len = strlen (datadir);
+        while ((len > 0) && (datadir[len - 1] == '/'))
+        {
+          len--;
+          datadir[len] = '\0';
+        }
+        if (len <= 0)
+        {
+          free (datadir);
+          datadir = NULL;
+        }
       }
-      if (len <= 0)
+    }
+    else if (strcasecmp ("DaemonAddress", key) == 0)
+    {
+      sfree (daemon_address);
+      daemon_address = strdup (value);
+      if (daemon_address == NULL)
       {
-        free (datadir);
-        datadir = NULL;
+        ERROR ("rrdcached plugin: strdup failed.");
+        continue;
       }
     }
-  }
-  else if (strcasecmp ("DaemonAddress", key) == 0)
-  {
-    sfree (daemon_address);
-    daemon_address = strdup (value);
-    if (daemon_address == NULL)
+    else if (strcasecmp ("CreateFiles", key) == 0)
     {
-      ERROR ("rrdcached plugin: strdup failed.");
-      return (1);
+      if (IS_FALSE (value))
+        config_create_files = 0;
+      else
+        config_create_files = 1;
+    }
+    else if (strcasecmp ("CollectStatistics", key) == 0)
+    {
+      if (IS_FALSE (value))
+        config_collect_stats = 0;
+      else
+        config_collect_stats = 1;
+    }
+    else
+    {
+      WARNING ("rrdcached plugin: Ignoring invalid option %s.", key);
+      continue;
     }
   }
+
+  if (daemon_address != NULL) {
+    plugin_register_write ("rrdcached", rc_write, /* user_data = */ NULL);
+    plugin_register_flush ("rrdcached", rc_flush, /* user_data = */ NULL);
+  }
+  return (0);
+} /* int rc_config */
+
+static int rc_read (void)
+{
+  int status;
+  rrdc_stats_t *head;
+  rrdc_stats_t *ptr;
+
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  if (daemon_address == NULL)
+    return (-1);
+
+  if (config_collect_stats == 0)
+    return (-1);
+
+  vl.values = values;
+  vl.values_len = 1;
+
+  if ((strncmp ("unix:", daemon_address, strlen ("unix:")) == 0)
+      || (daemon_address[0] == '/'))
+    sstrncpy (vl.host, hostname_g, sizeof (vl.host));
   else
+    sstrncpy (vl.host, daemon_address, sizeof (vl.host));
+  sstrncpy (vl.plugin, "rrdcached", sizeof (vl.plugin));
+
+  head = NULL;
+  status = rrdc_stats_get (&head);
+  if (status != 0)
   {
+    ERROR ("rrdcached plugin: rrdc_stats_get failed with status %i.", status);
     return (-1);
   }
+
+  for (ptr = head; ptr != NULL; ptr = ptr->next)
+  {
+    if (ptr->type == RRDC_STATS_TYPE_GAUGE)
+      values[0].gauge = (gauge_t) ptr->value.gauge;
+    else if (ptr->type == RRDC_STATS_TYPE_COUNTER)
+      values[0].counter = (counter_t) ptr->value.counter;
+    else
+      continue;
+
+    if (strcasecmp ("QueueLength", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "queue_length", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("UpdatesWritten", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "operations", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "write-updates", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("DataSetsWritten", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "operations", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "write-data_sets",
+          sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("TreeNodesNumber", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "gauge", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "tree_nodes", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("TreeDepth", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "gauge", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "tree_depth", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("FlushesReceived", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "operations", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "receive-flush", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("JournalBytes", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "counter", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "journal-bytes", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("JournalRotate", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "counter", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "journal-rotates", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("UpdatesReceived", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "operations", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "receive-update", sizeof (vl.type_instance));
+    }
+    else
+    {
+      DEBUG ("rrdcached plugin: rc_read: Unknown statistic `%s'.", ptr->name);
+      continue;
+    }
+
+    plugin_dispatch_values (&vl);
+  } /* for (ptr = head; ptr != NULL; ptr = ptr->next) */
+
+  rrdc_stats_free (head);
+
   return (0);
-} /* int rc_config */
+} /* int rc_read */
+
+static int rc_init (void)
+{
+  if (config_collect_stats != 0)
+    plugin_register_read ("rrdcached", rc_read);
 
-static int rc_write (const data_set_t *ds, const value_list_t *vl)
+  return (0);
+} /* int rc_init */
+
+static int rc_write (const data_set_t *ds, const value_list_t *vl,
+    user_data_t __attribute__((unused)) *user_data)
 {
-  char filename[512];
+  char filename[PATH_MAX];
   char values[512];
   char *values_array[2];
   int status;
@@ -203,7 +396,30 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl)
   values_array[0] = values;
   values_array[1] = NULL;
 
-  /* TODO: Check if the file exists. */
+  if (config_create_files != 0)
+  {
+    struct stat statbuf;
+
+    status = stat (filename, &statbuf);
+    if (status != 0)
+    {
+      if (errno != ENOENT)
+      {
+        char errbuf[1024];
+        ERROR ("rrdcached plugin: stat (%s) failed: %s",
+            filename, sstrerror (errno, errbuf, sizeof (errbuf)));
+        return (-1);
+      }
+
+      status = cu_rrd_create_file (filename, ds, vl, &rrdcreate_config);
+      if (status != 0)
+      {
+        ERROR ("rrdcached plugin: cu_rrd_create_file (%s) failed.",
+            filename);
+        return (-1);
+      }
+    }
+  }
 
   status = rrdc_connect (daemon_address);
   if (status != 0)
@@ -225,6 +441,41 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl)
   return (0);
 } /* int rc_write */
 
+static int rc_flush (__attribute__((unused)) cdtime_t timeout, /* {{{ */
+    const char *identifier,
+    __attribute__((unused)) user_data_t *ud)
+{
+  char filename[PATH_MAX + 1];
+  int status;
+
+  if (identifier == NULL)
+    return (EINVAL);
+
+  if (datadir != NULL)
+    ssnprintf (filename, sizeof (filename), "%s/%s.rrd", datadir, identifier);
+  else
+    ssnprintf (filename, sizeof (filename), "%s.rrd", identifier);
+
+  status = rrdc_connect (daemon_address);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_connect (%s) failed with status %i.",
+        daemon_address, status);
+    return (-1);
+  }
+
+  status = rrdc_flush (filename);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_flush (%s) failed with status %i.",
+        filename, status);
+    return (-1);
+  }
+  DEBUG ("rrdcached plugin: rrdc_flush (%s): Success.", filename);
+
+  return (0);
+} /* }}} int rc_flush */
+
 static int rc_shutdown (void)
 {
   rrdc_disconnect ();
@@ -233,9 +484,8 @@ static int rc_shutdown (void)
 
 void module_register (void)
 {
-  plugin_register_config ("rrdcached", rc_config,
-      config_keys, config_keys_num);
-  plugin_register_write ("rrdcached", rc_write);
+  plugin_register_complex_config ("rrdcached", rc_config);
+  plugin_register_init ("rrdcached", rc_init);
   plugin_register_shutdown ("rrdcached", rc_shutdown);
 } /* void module_register */