scale target: Add a target to scale arbitrary values.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 6 Sep 2009 15:06:27 +0000 (17:06 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 6 Sep 2009 15:06:27 +0000 (17:06 +0200)
configure.in
src/Makefile.am
src/target_scale.c [new file with mode: 0644]

index 11ca85e..d30fe84 100644 (file)
@@ -3774,6 +3774,7 @@ AC_PLUGIN([tail],        [yes],                [Parsing of logfiles])
 AC_PLUGIN([tape],        [$plugin_tape],       [Tape drive statistics])
 AC_PLUGIN([target_notification], [yes],        [The notification target])
 AC_PLUGIN([target_replace], [yes],             [The replace target])
+AC_PLUGIN([target_scale],[yes],                [The scale target])
 AC_PLUGIN([target_set],  [yes],                [The set target])
 AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
 AC_PLUGIN([teamspeak2],  [yes],                [TeamSpeak2 server statistics])
@@ -4074,6 +4075,7 @@ Configuration:
     tape  . . . . . . . . $enable_tape
     target_notification . $enable_target_notification
     target_replace  . . . $enable_target_replace
+    target_scale  . . . . $enable_target_scale
     target_set  . . . . . $enable_target_set
     tcpconns  . . . . . . $enable_tcpconns
     teamspeak2  . . . . . $enable_teamspeak2
index e91df83..d10d51d 100644 (file)
@@ -910,6 +910,14 @@ collectd_LDADD += "-dlopen" target_replace.la
 collectd_DEPENDENCIES += target_replace.la
 endif
 
+if BUILD_PLUGIN_TARGET_SCALE
+pkglib_LTLIBRARIES += target_scale.la
+target_scale_la_SOURCES = target_scale.c
+target_scale_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" target_scale.la
+collectd_DEPENDENCIES += target_scale.la
+endif
+
 if BUILD_PLUGIN_TARGET_SET
 pkglib_LTLIBRARIES += target_set.la
 target_set_la_SOURCES = target_set.c
diff --git a/src/target_scale.c b/src/target_scale.c
new file mode 100644 (file)
index 0000000..7ee4ca8
--- /dev/null
@@ -0,0 +1,420 @@
+/**
+ * collectd - src/target_set.c
+ * Copyright (C) 2008  Florian Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "filter_chain.h"
+
+#include "utils_cache.h"
+
+struct ts_data_s
+{
+       double factor;
+       double offset;
+};
+typedef struct ts_data_s ts_data_t;
+
+static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               ts_data_t *data, int dsrc_index)
+{
+       uint64_t curr_counter;
+       int status;
+       int failure;
+
+       /* Required meta data */
+       uint64_t prev_counter;
+       char key_prev_counter[128];
+       uint64_t int_counter;
+       char key_int_counter[128];
+       double int_fraction;
+       char key_int_fraction[128];
+
+       curr_counter = (uint64_t) vl->values[dsrc_index].counter;
+
+       ssnprintf (key_prev_counter, sizeof (key_prev_counter),
+                       "target_scale[%p,%i]:prev_counter",
+                       (void *) data, dsrc_index);
+       ssnprintf (key_int_counter, sizeof (key_int_counter),
+                       "target_scale[%p,%i]:int_counter",
+                       (void *) data, dsrc_index);
+       ssnprintf (key_int_fraction, sizeof (key_int_fraction),
+                       "target_scale[%p,%i]:int_fraction",
+                       (void *) data, dsrc_index);
+
+       prev_counter = curr_counter;
+       int_counter = 0;
+       int_fraction = 0.0;
+
+       /* Query the meta data */
+       failure = 0;
+
+       status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
+                       &prev_counter);
+       if (status != 0)
+               failure++;
+
+       status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
+       if (status != 0)
+               failure++;
+
+       status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
+       if (status != 0)
+               failure++;
+
+       if (failure == 0)
+       {
+               uint64_t difference;
+               double rate;
+
+               /* Calcualte the rate */
+               if (prev_counter > curr_counter) /* => counter overflow */
+               {
+                       if (prev_counter <= 4294967295) /* 32 bit overflow */
+                               difference = (4294967295 - prev_counter) + curr_counter;
+                       else /* 64 bit overflow */
+                               difference = (18446744073709551615U - prev_counter) + curr_counter;
+               }
+               else /* no overflow */
+               {
+                       difference = curr_counter - prev_counter;
+               }
+               rate = ((double) difference) / ((double) vl->interval);
+
+               /* Modify the rate. */
+               if (!isnan (data->factor))
+                       rate *= data->factor;
+               if (!isnan (data->offset))
+                       rate += data->offset;
+
+               /* Calculate the internal counter. */
+               int_fraction += (rate * ((double) vl->interval));
+               difference = (uint64_t) int_fraction;
+               int_fraction -= ((double) difference);
+               int_counter  += difference;
+
+               assert (int_fraction >= 0.0);
+               assert (int_fraction <  1.0);
+
+               DEBUG ("Target `scale': ts_invoke_counter: %"PRIu64" -> %g -> %"PRIu64
+                               "(+%g)",
+                               curr_counter, rate, int_counter, int_fraction);
+       }
+       else /* (failure != 0) */
+       {
+               int_counter = 0;
+               int_fraction = 0.0;
+       }
+
+       vl->values[dsrc_index].counter = (counter_t) int_counter;
+
+       /* Update to the new counter value */
+       uc_meta_data_add_unsigned_int (vl, key_prev_counter, curr_counter);
+       uc_meta_data_add_unsigned_int (vl, key_int_counter, int_counter);
+       uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
+
+
+       return (0);
+} /* }}} int ts_invoke_counter */
+
+static int ts_invoke_gauge (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               ts_data_t *data, int dsrc_index)
+{
+       if (!isnan (data->factor))
+               vl->values[dsrc_index].gauge *= data->factor;
+       if (!isnan (data->offset))
+               vl->values[dsrc_index].gauge += data->offset;
+
+       return (0);
+} /* }}} int ts_invoke_gauge */
+
+static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               ts_data_t *data, int dsrc_index)
+{
+       int64_t curr_derive;
+       int status;
+       int failure;
+
+       /* Required meta data */
+       int64_t prev_derive;
+       char key_prev_derive[128];
+       int64_t int_derive;
+       char key_int_derive[128];
+       double int_fraction;
+       char key_int_fraction[128];
+
+       curr_derive = (int64_t) vl->values[dsrc_index].derive;
+
+       ssnprintf (key_prev_derive, sizeof (key_prev_derive),
+                       "target_scale[%p,%i]:prev_derive",
+                       (void *) data, dsrc_index);
+       ssnprintf (key_int_derive, sizeof (key_int_derive),
+                       "target_scale[%p,%i]:int_derive",
+                       (void *) data, dsrc_index);
+       ssnprintf (key_int_fraction, sizeof (key_int_fraction),
+                       "target_scale[%p,%i]:int_fraction",
+                       (void *) data, dsrc_index);
+
+       prev_derive = curr_derive;
+       int_derive = 0;
+       int_fraction = 0.0;
+
+       /* Query the meta data */
+       failure = 0;
+
+       status = uc_meta_data_get_signed_int (vl, key_prev_derive,
+                       &prev_derive);
+       if (status != 0)
+               failure++;
+
+       status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
+       if (status != 0)
+               failure++;
+
+       status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
+       if (status != 0)
+               failure++;
+
+       if (failure == 0)
+       {
+               int64_t difference;
+               double rate;
+
+               /* Calcualte the rate */
+               difference = curr_derive - prev_derive;
+               rate = ((double) difference) / ((double) vl->interval);
+
+               /* Modify the rate. */
+               if (!isnan (data->factor))
+                       rate *= data->factor;
+               if (!isnan (data->offset))
+                       rate += data->offset;
+
+               /* Calculate the internal derive. */
+               int_fraction += (rate * ((double) vl->interval));
+               if (int_fraction < 0.0) /* handle negative integer rounding correctly */
+                       difference = ((int64_t) int_fraction) - 1;
+               else
+                       difference = (int64_t) int_fraction;
+               int_fraction -= ((double) difference);
+               int_derive  += difference;
+
+               assert (int_fraction >= 0.0);
+               assert (int_fraction <  1.0);
+
+               DEBUG ("Target `scale': ts_invoke_derive: %"PRIu64" -> %g -> %"PRIu64
+                               "(+%g)",
+                               curr_derive, rate, int_derive, int_fraction);
+       }
+       else /* (failure != 0) */
+       {
+               int_derive = 0;
+               int_fraction = 0.0;
+       }
+
+       vl->values[dsrc_index].derive = (derive_t) int_derive;
+
+       /* Update to the new derive value */
+       uc_meta_data_add_signed_int (vl, key_prev_derive, curr_derive);
+       uc_meta_data_add_signed_int (vl, key_int_derive, int_derive);
+       uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
+
+       return (0);
+} /* }}} int ts_invoke_derive */
+
+static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               ts_data_t *data, int dsrc_index)
+{
+       uint64_t curr_absolute;
+       double rate;
+       int status;
+
+       /* Required meta data */
+       double int_fraction;
+       char key_int_fraction[128];
+
+       curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
+
+       ssnprintf (key_int_fraction, sizeof (key_int_fraction),
+                       "target_scale[%p,%i]:int_fraction",
+                       (void *) data, dsrc_index);
+
+       int_fraction = 0.0;
+
+       /* Query the meta data */
+       status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
+       if (status != 0)
+               int_fraction = 0.0;
+
+       rate = ((double) curr_absolute) / ((double) vl->interval);
+
+       /* Modify the rate. */
+       if (!isnan (data->factor))
+               rate *= data->factor;
+       if (!isnan (data->offset))
+               rate += data->offset;
+
+       /* Calculate the new absolute. */
+       int_fraction += (rate * ((double) vl->interval));
+       curr_absolute = (uint64_t) int_fraction;
+       int_fraction -= ((double) curr_absolute);
+
+       vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
+
+       /* Update to the new absolute value */
+       uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
+
+       return (0);
+} /* }}} int ts_invoke_absolute */
+
+static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
+{
+       if ((ci->values_num != 1)
+                       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+       {
+               WARNING ("scale target: The `%s' config option needs "
+                               "exactly one numeric argument.", ci->key);
+               return (-1);
+       }
+
+       *ret = ci->values[0].value.number;
+       DEBUG ("ts_config_set_double: *ret = %g", *ret);
+
+       return (0);
+} /* }}} int ts_config_set_double */
+
+static int ts_destroy (void **user_data) /* {{{ */
+{
+       if (user_data == NULL)
+               return (-EINVAL);
+
+       free (*user_data);
+       *user_data = NULL;
+
+       return (0);
+} /* }}} int ts_destroy */
+
+static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+       ts_data_t *data;
+       int status;
+       int i;
+
+       data = (ts_data_t *) malloc (sizeof (*data));
+       if (data == NULL)
+       {
+               ERROR ("ts_create: malloc failed.");
+               return (-ENOMEM);
+       }
+       memset (data, 0, sizeof (*data));
+
+       data->factor = NAN;
+       data->offset = NAN;
+
+       status = 0;
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("Factor", child->key) == 0)
+                               status = ts_config_set_double (&data->factor, child);
+               else if (strcasecmp ("Offset", child->key) == 0)
+                               status = ts_config_set_double (&data->offset, child);
+               else
+               {
+                       ERROR ("Target `scale': The `%s' configuration option is not understood "
+                                       "and will be ignored.", child->key);
+                       status = 0;
+               }
+
+               if (status != 0)
+                       break;
+       }
+
+       /* Additional sanity-checking */
+       while (status == 0)
+       {
+               if (isnan (data->factor) && isnan (data->offset))
+               {
+                       ERROR ("Target `scale': You need to at least set either the `Factor' "
+                                       "or `Offset' option!");
+                       status = -1;
+               }
+
+               break;
+       }
+
+       if (status != 0)
+       {
+               ts_destroy ((void *) &data);
+               return (status);
+       }
+
+       *user_data = data;
+       return (0);
+} /* }}} int ts_create */
+
+static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               notification_meta_t __attribute__((unused)) **meta, void **user_data)
+{
+       ts_data_t *data;
+       int i;
+
+       if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
+               return (-EINVAL);
+
+       data = *user_data;
+       if (data == NULL)
+       {
+               ERROR ("Target `scale': Invoke: `data' is NULL.");
+               return (-EINVAL);
+       }
+
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+                       ts_invoke_counter (ds, vl, data, i);
+               else if (ds->ds[i].type == DS_TYPE_GAUGE)
+                       ts_invoke_gauge (ds, vl, data, i);
+               else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                       ts_invoke_derive (ds, vl, data, i);
+               else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+                       ts_invoke_absolute (ds, vl, data, i);
+               else
+                       ERROR ("Target `scale': Ignoring unknown data source type %i",
+                                       ds->ds[i].type);
+       }
+
+       return (FC_TARGET_CONTINUE);
+} /* }}} int ts_invoke */
+
+void module_register (void)
+{
+       target_proc_t tproc;
+
+       memset (&tproc, 0, sizeof (tproc));
+       tproc.create  = ts_create;
+       tproc.destroy = ts_destroy;
+       tproc.invoke  = ts_invoke;
+       fc_register_target ("scale", tproc);
+} /* module_register */
+
+/* vim: set sw=2 ts=2 tw=78 fdm=marker : */
+