Merge branch 'ff/aggregate'
authorFlorian Forster <octo@collectd.org>
Sun, 11 Nov 2012 07:58:18 +0000 (08:58 +0100)
committerFlorian Forster <octo@collectd.org>
Sun, 11 Nov 2012 07:58:18 +0000 (08:58 +0100)
Conflicts:
src/Makefile.am

1  2 
configure.in
src/Makefile.am
src/collectd.conf.in
src/collectd.conf.pod
src/common.c

diff --combined configure.in
@@@ -91,7 -91,6 +91,7 @@@ f
  if test "x$ac_system" = "xSolaris"
  then
        AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Define to enforce POSIX thread semantics under Solaris.])
 +      AC_DEFINE(_REENTRANT,               1, [Define to enable reentrancy interfaces.])
  fi
  if test "x$ac_system" = "xAIX"
  then
@@@ -533,8 -532,6 +533,8 @@@ AC_CHECK_HEADERS(netinet/if_ether.h, []
  #endif
  ])
  
 +AC_CHECK_HEADERS(netinet/ip_compat.h)
 +
  # For the multimeter plugin
  have_termios_h="no"
  AC_CHECK_HEADERS(termios.h, [have_termios_h="yes"])
@@@ -1228,7 -1225,6 +1228,7 @@@ AC_CHECK_MEMBERS([struct kinfo_proc.ki_
                have_struct_kinfo_proc_freebsd="no"
        ],
        [
 +AC_INCLUDES_DEFAULT
  #include <kvm.h>
  #include <sys/param.h>
  #include <sys/sysctl.h>
@@@ -1245,7 -1241,6 +1245,7 @@@ AC_CHECK_MEMBERS([struct kinfo_proc.kp_
                have_struct_kinfo_proc_openbsd="no"
        ],
        [
 +AC_INCLUDES_DEFAULT
  #include <sys/param.h>
  #include <sys/sysctl.h>
  #include <kvm.h>
  # This could be in iptc or ip4tc
  if test "x$with_libiptc" = "xpkgconfig"
  then
 +      SAVE_LIBS="$LIBS"
        AC_SEARCH_LIBS(iptc_init, [iptc ip4tc],
                        [with_libiptc="pkgconfig"],
                        [with_libiptc="no"],
                        [$with_libiptc_libs])
 +      LIBS="$SAVE_LIBS"
  fi
  if test "x$with_libiptc" = "xpkgconfig"
  then
  fi
  if test "x$with_libnetlink" = "xyes"
  then
 +      SAVE_CFLAGS="$CFLAGS"
 +      CFLAGS="$CFLAGS $with_libnetlink_cflags"
 +
 +      AC_CACHE_CHECK(
 +              [if function 'rtnl_dump_filter' expects five arguments],
 +              [c_cv_rtnl_dump_filter_five_args],
 +              AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
 +                              [
 +AC_INCLUDES_DEFAULT
 +#include <asm/types.h>
 +#include <sys/socket.h>
 +#if HAVE_LIBNETLINK_H
 +# include <libnetlink.h>
 +#elif HAVE_IPROUTE_LIBNETLINK_H
 +# include <iproute/libnetlink.h>
 +#elif HAVE_LINUX_LIBNETLINK_H
 +# include <linux/libnetlink.h>
 +#endif
 +                              ],
 +                              [
 +if (rtnl_dump_filter(NULL, NULL, NULL, NULL, NULL))
 +      return 1;
 +return 0;
 +                              ]
 +                      )],
 +                      [c_cv_rtnl_dump_filter_five_args="yes"],
 +                      [c_cv_rtnl_dump_filter_five_args="no"]
 +              )
 +      )
 +
 +      AC_CACHE_CHECK(
 +              [if function 'rtnl_dump_filter' expects three arguments],
 +              [c_cv_rtnl_dump_filter_three_args],
 +              AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
 +                              [
 +AC_INCLUDES_DEFAULT
 +#include <asm/types.h>
 +#include <sys/socket.h>
 +#if HAVE_LIBNETLINK_H
 +# include <libnetlink.h>
 +#elif HAVE_IPROUTE_LIBNETLINK_H
 +# include <iproute/libnetlink.h>
 +#elif HAVE_LINUX_LIBNETLINK_H
 +# include <linux/libnetlink.h>
 +#endif
 +                              ],
 +                              [
 +if (rtnl_dump_filter(NULL, NULL, NULL))
 +      return 1;
 +return 0;
 +                              ]
 +                      )],
 +                      [c_cv_rtnl_dump_filter_three_args="yes"],
 +                      [c_cv_rtnl_dump_filter_three_args="no"]
 +              )
 +      )
 +
 +      CFLAGS="$SAVE_CFLAGS"
 +
 +      if test "x$c_cv_rtnl_dump_filter_five_args" = "xyes"
 +      then
 +              AC_DEFINE(RTNL_DUMP_FILTER_FIVE_ARGS, 1,
 +                              [Define to 1 if function 'rtnl_dump_filter' expects five arguments.])
 +      fi
 +      if test "x$c_cv_rtnl_dump_filter_three_args" = "xyes"
 +      then
 +              AC_DEFINE(RTNL_DUMP_FILTER_THREE_ARGS, 1,
 +                              [Define to 1 if function 'rtnl_dump_filter' expects three arguments.])
 +      fi
 +
        BUILD_WITH_LIBNETLINK_CFLAGS="$with_libnetlink_cflags"
        BUILD_WITH_LIBNETLINK_LIBS="$with_libnetlink_libs"
        AC_SUBST(BUILD_WITH_LIBNETLINK_CFLAGS)
@@@ -2986,7 -2909,7 +2986,7 @@@ AM_CONDITIONAL(BUILD_WITH_LIBPCAP, tes
  perl_interpreter="perl"
  AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
  [
 -      if test -x "$withval"
 +      if test -f "$withval" && test -x "$withval"
        then
                perl_interpreter="$withval"
                with_libperl="yes"
@@@ -4134,7 -4057,7 +4134,7 @@@ AC_ARG_WITH(libvarnish, [AS_HELP_STRING
        then
                AC_MSG_NOTICE([Not checking for libvarnish: Manually configured])
                with_libvarnish_cflags="-I$withval/include"
 -              with_libvarnish_libs="-L$withval/lib -lvarnish -lvarnishcompat -lvarnishapi"
 +              with_libvarnish_libs="-L$withval/lib -lvarnishapi"
                with_libvarnish="yes"
        fi; fi; fi
  ],
  if test "x$with_perfstat" = "xyes"
  then
        plugin_cpu="yes"
 +      plugin_contextswitch="yes"
        plugin_disk="yes"
        plugin_memory="yes"
        plugin_swap="yes"
        plugin_interface="yes"
        plugin_load="yes"
 +      plugin_uptime="yes"
  fi
  
  if test "x$with_procinfo" = "xyes"
@@@ -4853,6 -4774,7 +4853,7 @@@ AC_ARG_ENABLE([all-plugins]
  
  m4_divert_once([HELP_ENABLE], [])
  
+ AC_PLUGIN([aggregation], [yes],                [Aggregation plugin])
  AC_PLUGIN([amqp],        [$with_librabbitmq],  [AMQP output plugin])
  AC_PLUGIN([apache],      [$with_libcurl],      [Apache httpd statistics])
  AC_PLUGIN([apcups],      [yes],                [Statistics of UPSes by APC])
@@@ -5185,6 -5107,7 +5186,7 @@@ Configuration
      perl  . . . . . . . . $with_perl_bindings
  
    Modules:
+     aggregation . . . . . $enable_aggregation
      amqp    . . . . . . . $enable_amqp
      apache  . . . . . . . $enable_apache
      apcups  . . . . . . . $enable_apcups
diff --combined src/Makefile.am
@@@ -43,7 -43,7 +43,7 @@@ collectd_SOURCES = collectd.c collectd.
  collectd_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL)
  collectd_CFLAGS = $(AM_CFLAGS)
  collectd_LDFLAGS = -export-dynamic
 -collectd_LDADD =
 +collectd_LDADD = -lm
  collectd_DEPENDENCIES =
  
  # Link to these libraries..
@@@ -70,6 -70,7 +70,6 @@@ collectd_LDADD += -ldevinf
  endif
  if BUILD_AIX
  collectd_LDFLAGS += -Wl,-bexpall,-brtllib
 -collectd_LDADD += -lm
  endif
  
  # The daemon needs to call sg_init, so we need to link it against libstatgrab,
@@@ -119,11 -120,20 +119,21 @@@ pkglib_LTLIBRARIES 
  BUILT_SOURCES = 
  CLEANFILES = 
  
+ if BUILD_PLUGIN_AGGREGATION
+ pkglib_LTLIBRARIES += aggregation.la
+ aggregation_la_SOURCES = aggregation.c \
+                          utils_vl_lookup.c utils_vl_lookup.h
+ aggregation_la_LDFLAGS = -module -avoid-version
+ aggregation_la_LIBADD =
+ collectd_LDADD += "-dlopen" aggregation.la
+ collectd_DEPENDENCIES += aggregation.la
+ endif
  if BUILD_PLUGIN_AMQP
  pkglib_LTLIBRARIES += amqp.la
  amqp_la_SOURCES = amqp.c \
                  utils_cmd_putval.c utils_cmd_putval.h \
 +                utils_format_graphite.c utils_format_graphite.h \
                  utils_format_json.c utils_format_json.h
  amqp_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
  amqp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
@@@ -213,10 -223,6 +223,10 @@@ if BUILD_PLUGIN_CONTEXTSWITC
  pkglib_LTLIBRARIES += contextswitch.la
  contextswitch_la_SOURCES = contextswitch.c
  contextswitch_la_LDFLAGS = -module -avoid-version
 +contextswitch_la_LIBADD =
 +if BUILD_WITH_PERFSTAT
 +contextswitch_la_LIBADD += -lperfstat
 +endif
  collectd_LDADD += "-dlopen" contextswitch.la
  collectd_DEPENDENCIES += contextswitch.la
  endif
@@@ -1196,9 -1202,6 +1206,9 @@@ uptime_la_LIBADD 
  if BUILD_WITH_LIBKSTAT
  uptime_la_LIBADD += -lkstat
  endif
 +if BUILD_WITH_PERFSTAT
 +uptime_la_LIBADD += -lperfstat
 +endif
  collectd_LDADD += "-dlopen" uptime.la
  collectd_DEPENDENCIES += uptime.la
  endif
@@@ -1264,8 -1267,7 +1274,8 @@@ endi
  if BUILD_PLUGIN_WRITE_GRAPHITE
  pkglib_LTLIBRARIES += write_graphite.la
  write_graphite_la_SOURCES = write_graphite.c \
 -                      utils_format_json.c utils_format_json.h
 +                        utils_format_graphite.c utils_format_graphite.h \
 +                        utils_format_json.c utils_format_json.h
  write_graphite_la_LDFLAGS = -module -avoid-version
  collectd_LDADD += "-dlopen" write_graphite.la
  collectd_DEPENDENCIES += write_graphite.la
@@@ -1378,7 -1380,7 +1388,7 @@@ EXTRA_DIST +=   collectd.conf.pod 
        fi
  
  pinba.pb-c.c pinba.pb-c.h: pinba.proto
 -      protoc-c --c_out $(builddir) pinba.proto
 +      protoc-c --c_out . pinba.proto
  
  install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
        $(INSTALL) -m 0644 $(srcdir)/postgresql_default.conf \
                $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
  
 +uninstall-hook:
 +      rm -f $(DESTDIR)$(pkgdatadir)/types.db;
 +      rm -f $(DESTDIR)$(sysconfdir)/collectd.conf
 +      rm -f $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
++
+ if BUILD_FEATURE_DEBUG
+ bin_PROGRAMS += utils_vl_lookup_test
+ utils_vl_lookup_test_SOURCES = utils_vl_lookup_test.c \
+                                utils_vl_lookup.h utils_vl_lookup.c \
+                                utils_avltree.c utils_avltree.h \
+                                common.h
+ utils_vl_lookup_test_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL) -DBUILD_TEST=1
+ utils_vl_lookup_test_CFLAGS = $(AM_CFLAGS)
+ utils_vl_lookup_test_LDFLAGS = -export-dynamic
+ utils_vl_lookup_test_LDADD =
+ endif
diff --combined src/collectd.conf.in
@@@ -52,6 -52,7 +52,7 @@@
  # to missing dependencies or because they have been deactivated explicitly.  #
  ##############################################################################
  
+ #@BUILD_PLUGIN_AGGREGATION_TRUE@LoadPlugin aggregation
  #@BUILD_PLUGIN_AMQP_TRUE@LoadPlugin amqp
  #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
  #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
  # ription of those options is available in the collectd.conf(5) manual page. #
  ##############################################################################
  
+ #<Plugin "aggregation">
+ #  <Aggregation>
+ #    #Host "unspecified"
+ #    Plugin "cpu"
+ #    #PluginInstance "unspecified"
+ #    Type "cpu"
+ #    #TypeInstance "unspecified"
+ #
+ #    GroupBy "Host"
+ #    GroupBy "TypeInstance"
+ #
+ #    CalculateNum false
+ #    CalculateSum false
+ #    CalculateAverage true
+ #    CalculateMinimum false
+ #    CalculateMaximum false
+ #    CalculateStddev false
+ #  </Aggregation>
+ #</Plugin>
  #<Plugin "amqp">
  #  <Publish "name">
  #    Host "localhost"
  #</Plugin>
  
  #<Plugin memcached>
 -#     Host "127.0.0.1"
 -#     Port "11211"
 +#     <Instance "local">
 +#             Host "127.0.0.1"
 +#             Port "11211"
 +#     </Instance>
  #</Plugin>
  
  #<Plugin modbus>
  #     Host "localhost"
  #     Port 123
  #     ReverseLookups false
 +#     IncludeUnitID true
  #</Plugin>
  
  #<Plugin nut>
  
  #<Plugin "swap">
  #     ReportByDevice false
 +#     ReportBytes true
  #</Plugin>
  
  #<Plugin "table">
diff --combined src/collectd.conf.pod
@@@ -25,32 -25,22 +25,32 @@@ controls which plugins to load. These p
  behavior.
  
  The syntax of this config file is similar to the config file of the famous
 -B<Apache Webserver>. Each line contains either a key-value-pair or a
 -section-start or -end. Empty lines and everything after the hash-symbol `#' is
 -ignored. Values are either string, enclosed in double-quotes,
 -(floating-point-)numbers or a boolean expression, i.E<nbsp>e. either B<true> or
 -B<false>. String containing of only alphanumeric characters and underscores do
 -not need to be quoted. Lines may be wrapped by using `\' as the last character
 -before the newline. This allows long lines to be split into multiple lines.
 -Quoted strings may be wrapped as well. However, those are treated special in
 -that whitespace at the beginning of the following lines will be ignored, which
 -allows for nicely indenting the wrapped lines.
 -
 -The configuration is read and processed in order, i.E<nbsp>e. from top to
 -bottom. So the plugins are loaded in the order listed in this config file. It
 -is a good idea to load any logging plugins first in order to catch messages
 -from plugins during configuration. Also, the C<LoadPlugin> option B<must> occur
 -B<before> the C<E<lt>Plugin ...E<gt>> block.
 +I<Apache> webserver. Each line contains either an option (a key and a list of
 +one or more values) or a section-start or -end. Empty lines and everything
 +after a non-quoted hash-symbol (C<#>) is ignored. I<Keys> are unquoted
 +strings, consisting only of alphanumeric characters and the underscore (C<_>)
 +character. Keys are handled case insensitive by I<collectd> itself and all
 +plugins included with it. I<Values> can either be an I<unquoted string>, a
 +I<quoted string> (enclosed in double-quotes) a I<number> or a I<boolean>
 +expression. I<Unquoted strings> consist of only alphanumeric characters and
 +underscores (C<_>) and do not need to be quoted. I<Quoted strings> are
 +enclosed in double quotes (C<">). You can use the backslash character (C<\>)
 +to include double quotes as part of the string. I<Numbers> can be specified in
 +decimal and floating point format (using a dot C<.> as decimal separator),
 +hexadecimal when using the C<0x> prefix and octal with a leading zero (C<0>).
 +I<Boolean> values are either B<true> or B<false>.
 +
 +Lines may be wrapped by using C<\> as the last character before the newline.
 +This allows long lines to be split into multiple lines. Quoted strings may be
 +wrapped as well. However, those are treated special in that whitespace at the
 +beginning of the following lines will be ignored, which allows for nicely
 +indenting the wrapped lines.
 +
 +The configuration is read and processed in order, i.e. from top to bottom. So
 +the plugins are loaded in the order listed in this config file. It is a good
 +idea to load any logging plugins first in order to catch messages from plugins
 +during configuration. Also, the C<LoadPlugin> option B<must> occur B<before>
 +the appropriate C<E<lt>Plugin ...E<gt>> block.
  
  =head1 GLOBAL OPTIONS
  
@@@ -193,12 -183,122 +193,122 @@@ C<Plugin>-Section. Which options exist 
  require external configuration, too. The C<apache plugin>, for example,
  required C<mod_status> to be configured in the webserver you're going to
  collect data from. These plugins are listed below as well, even if they don't
- require any configuration within collectd's configfile.
+ require any configuration within collectd's configuration file.
  
  A list of all plugins and a short summary for each plugin can be found in the
  F<README> file shipped with the sourcecode and hopefully binary packets as
  well.
  
+ =head2 Plugin C<aggregation>
+ The I<Aggregation plugin> makes it possible to aggregate several values into
+ one using aggregation functions such as I<sum>, I<average>, I<min> and I<max>.
+ This can be put to a wide variety of uses, e.g. average and total CPU
+ statistics for your entire fleet.
+ The grouping is powerful but, as with many powerful tools, may be a bit
+ difficult to wrap your head around. The grouping will therefore be
+ demonstrated using an example: The average and sum of the CPU usage across
+ all CPUs of each host is to be calculated.
+ To select all the affected values for our example, set C<Plugin cpu> and
+ C<Type cpu>. The other values are left unspecified, meaning "all values". The
+ I<Host>, I<Plugin>, I<PluginInstance>, I<Type> and I<TypeInstance> options
+ work as if they were specified in the C<WHERE> clause of an C<SELECT> SQL
+ statement.
+   Plugin "cpu"
+   Type "cpu"
+ Although the I<Host>, I<PluginInstance> (CPU number, i.e. 0, 1, 2, ...)  and
+ I<TypeInstance> (idle, user, system, ...) fields are left unspecified in the
+ example, the intention is to have a new value for each host / type instance
+ pair. This is achieved by "grouping" the values using the C<GroupBy> option.
+ It can be specified multiple times to group by more than one field.
+   GroupBy "Host"
+   GroupBy "TypeInstance"
+ We do neither specify nor group by I<plugin instance> (the CPU number), so all
+ metrics that differ in the CPU number only will be aggregated. Each
+ aggregation needs I<at least one> such field, otherwise no aggregation would
+ take place.
+ The full example configuration looks like this:
+  <Plugin "aggregation">
+    <Aggregation>
+      Plugin "cpu"
+      Type "cpu"
+      
+      GroupBy "Host"
+      GroupBy "TypeInstance"
+      
+      CalculateSum true
+      CalculateAverage true
+    </Aggregation>
+  </Plugin>
+ There are a couple of limitations you should be aware of:
+ =over 4
+ =item
+ The I<Type> cannot be left unspecified, because it is not reasonable to add
+ apples to oranges. Also, the internal lookup structure won't work if you try
+ to group by type.
+ =item
+ There must be at least one unspecified, ungrouped field. Otherwise nothing
+ will be aggregated.
+ =back
+ As you can see in the example above, each aggregation has its own
+ B<Aggregation> block. You can have multiple aggregation blocks and aggregation
+ blocks may match the same values, i.e. one value list can update multiple
+ aggregations. The following options are valid inside B<Aggregation> blocks:
+ =over 4
+ =item B<Host> I<Host>
+ =item B<Plugin> I<Plugin>
+ =item B<PluginInstance> I<PluginInstance>
+ =item B<Type> I<Type>
+ =item B<TypeInstance> I<TypeInstance>
+ Selects the value lists to be added to this aggregation. B<Type> must be a
+ valid data set name, see L<types.db(5)> for details.
+ =item B<GroupBy> B<Host>|B<Plugin>|B<PluginInstance>|B<TypeInstance>
+ Group valued by the specified field. The B<GroupBy> option may be repeated to
+ group by multiple fields.
+ =item B<CalculateNum> B<true>|B<false>
+ =item B<CalculateSum> B<true>|B<false>
+ =item B<CalculateAverage> B<true>|B<false>
+ =item B<CalculateMinimum> B<true>|B<false>
+ =item B<CalculateMaximum> B<true>|B<false>
+ =item B<CalculateStddev> B<true>|B<false>
+ Boolean options for enabling calculation of the number of value lists, their
+ sum, average, minimum, maximum andE<nbsp>/ or standard deviation. All options
+ are disabled by default.
+ =back
  =head2 Plugin C<amqp>
  
  The I<AMQMP plugin> can be used to communicate with other instances of
@@@ -220,8 -320,6 +330,8 @@@ possibly filtering or messages
   #   Persistent false
   #   Format "command"
   #   StoreRates false
 + #   GraphitePrefix "collectd."
 + #   GraphiteEscapeChar "_"
     </Publish>
     
     # Receive values from an AMQP broker
@@@ -322,10 -420,6 +432,10 @@@ If set to B<JSON>, the values are encod
  an easy and straight forward exchange format. The C<Content-Type> header field
  will be set to C<application/json>.
  
 +If set to B<Graphite>, values are encoded in the I<Graphite> format, which is
 +"<metric> <value> <timestamp>\n". The C<Content-Type> header field will be set to
 +C<text/graphite>.
 +
  A subscribing client I<should> use the C<Content-Type> header field to
  determine how to decode the values. Currently, the I<AMQP plugin> itself can
  only decode the B<Command> format.
@@@ -340,25 -434,6 +450,25 @@@ using the internal value cache
  Please note that currently this option is only used if the B<Format> option has
  been set to B<JSON>.
  
 +=item B<GraphitePrefix> (Publish and B<Format>=I<Graphite> only)
 +
 +A prefix can be added in the metric name when outputting in the I<Graphite> format.
 +It's added before the I<Host> name.
 +Metric name will be "<prefix><host><postfix><plugin><type><name>"
 +
 +=item B<GraphitePostfix> (Publish and B<Format>=I<Graphite> only)
 +
 +A postfix can be added in the metric name when outputting in the I<Graphite> format.
 +It's added after the I<Host> name.
 +Metric name will be "<prefix><host><postfix><plugin><type><name>"
 +
 +=item B<GraphiteEscapeChar> (Publish and B<Format>=I<Graphite> only)
 +
 +Specify a character to replace dots (.) in the host part of the metric name.
 +In I<Graphite> metric name, dots are used as separators between different
 +metric parts (host, plugin, type).
 +Default is "_" (I<Underscore>).
 +
  =back
  
  =head2 Plugin C<apache>
@@@ -1974,17 -2049,6 +2084,17 @@@ The C<memcached plugin> connects to a m
  about cache utilization, memory and bandwidth used.
  L<http://www.danga.com/memcached/>
  
 + <Plugin "memcached">
 +   <Instance "name">
 +     Host "memcache.example.com"
 +     Port 11211
 +   </Instance>
 + </Plugin>
 +
 +The plugin configuration consists of one or more B<Instance> blocks which
 +specify one I<memcached> connection each. Within the B<Instance> blocks, the
 +following options are allowed:
 +
  =over 4
  
  =item B<Host> I<Hostname>
@@@ -1995,11 -2059,6 +2105,11 @@@ Hostname to connect to. Defaults to B<1
  
  TCP-Port to connect to. Defaults to B<11211>.
  
 +=item B<Socket> I<Path>
 +
 +Connect to I<memcached> using the UNIX domain socket at I<Path>. If this
 +setting is given, the B<Host> and B<Port> settings are ignored.
 +
  =back
  
  =head2 Plugin C<modbus>
@@@ -3094,16 -3153,6 +3204,16 @@@ IP-address may be used in a filename i
  lookups. The default is to do reverse lookups to preserve backwards
  compatibility, though.
  
 +=item B<IncludeUnitID> B<true>|B<false>
 +
 +When a peer is a refclock, include the unit ID in the I<type instance>.
 +Defaults to B<false> for backward compatibility.
 +
 +If two refclock peers use the same driver and this is B<false>, the plugin will
 +try to write simultaneous measurements from both to the same type instance.
 +This will result in error messages in the log and only one set of measurements
 +making it through.
 +
  =back
  
  =head2 Plugin C<nut>
@@@ -3604,11 -3653,6 +3714,11 @@@ used, the parameter expands to "localho
  
  The name of the database of the current connection.
  
 +=item I<instance>
 +
 +The name of the database plugin instance. See the B<Instance> option of the
 +database specification below for details.
 +
  =item I<username>
  
  The username used to connect to the database.
@@@ -3767,13 -3811,6 +3877,13 @@@ Specify the password to be used when co
  Specify whether to use an SSL connection when contacting the server. The
  following modes are supported:
  
 +=item B<Instance> I<name>
 +
 +Specify the plugin instance name that should be used instead of the database
 +name (which is the default, if this option has not been specified). This
 +allows to query multiple databases of the same name on the same host (e.g.
 +when running multiple database server versions in parallel).
 +
  =over 4
  
  =item I<disable>
@@@ -4134,10 -4171,6 +4244,10 @@@ The B<Port> option is the TCP port on w
  connections. Either a service name of a port number may be given. Please note
  that numerical port numbers must be given as a string, too.
  
 +=item B<Password> I<Password>
 +
 +Use I<Password> to authenticate when connecting to I<Redis>.
 +
  =item B<Timeout> I<Timeout in miliseconds>
  
  The B<Timeout> option set the socket timeout for node response. Since the Redis
@@@ -4195,50 -4228,6 +4305,50 @@@ Enables or disables the creation of RR
  locally, or B<DataDir> is set to a relative path, this will not work as
  expected. Default is B<true>.
  
 +=item B<StepSize> I<Seconds>
 +
 +B<Force> the stepsize of newly created RRD-files. Ideally (and per default)
 +this setting is unset and the stepsize is set to the interval in which the data
 +is collected. Do not use this option unless you absolutely have to for some
 +reason. Setting this option may cause problems with the C<snmp plugin>, the
 +C<exec plugin> or when the daemon is set up to receive data from other hosts.
 +
 +=item B<HeartBeat> I<Seconds>
 +
 +B<Force> the heartbeat of newly created RRD-files. This setting should be unset
 +in which case the heartbeat is set to twice the B<StepSize> which should equal
 +the interval in which data is collected. Do not set this option unless you have
 +a very good reason to do so.
 +
 +=item B<RRARows> I<NumRows>
 +
 +The C<rrdtool plugin> calculates the number of PDPs per CDP based on the
 +B<StepSize>, this setting and a timespan. This plugin creates RRD-files with
 +three times five RRAs, i. e. five RRAs with the CFs B<MIN>, B<AVERAGE>, and
 +B<MAX>. The five RRAs are optimized for graphs covering one hour, one day, one
 +week, one month, and one year.
 +
 +So for each timespan, it calculates how many PDPs need to be consolidated into
 +one CDP by calculating:
 +  number of PDPs = timespan / (stepsize * rrarows)
 +
 +Bottom line is, set this no smaller than the width of you graphs in pixels. The
 +default is 1200.
 +
 +=item B<RRATimespan> I<Seconds>
 +
 +Adds an RRA-timespan, given in seconds. Use this option multiple times to have
 +more then one RRA. If this option is never used, the built-in default of (3600,
 +86400, 604800, 2678400, 31622400) is used.
 +
 +For more information on how RRA-sizes are calculated see B<RRARows> above.
 +
 +=item B<XFF> I<Factor>
 +
 +Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
 +I<Factor> must be in the range C<[0.0-1.0)>, i.e. between zero (inclusive) and
 +one (exclusive).
 +
  =back
  
  =head2 Plugin C<rrdtool>
@@@ -4296,8 -4285,6 +4406,8 @@@ For more information on how RRA-sizes a
  =item B<XFF> I<Factor>
  
  Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
 +I<Factor> must be in the range C<[0.0-1.0)>, i.e. between zero (inclusive) and
 +one (exclusive).
  
  =item B<CacheFlush> I<Seconds>
  
@@@ -4413,11 -4400,6 +4523,11 @@@ and available space of each device wil
  This option is only available if the I<Swap plugin> can read C</proc/swaps>
  (under Linux) or use the L<swapctl(2)> mechanism (under I<Solaris>).
  
 +=item B<ReportBytes> B<false>|B<true>
 +
 +When enabled, the I<swap I/O> is reported in bytes. When disabled, the default,
 +I<swap I/O> is reported in pages. This option is available under Linux only.
 +
  =back
  
  =head2 Plugin C<syslog>
@@@ -5048,7 -5030,7 +5158,7 @@@ number
  If set to B<true>, the plugin instance and type instance will be in their own
  path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
  default), the plugin and plugin instance (and likewise the type and type
 -instance) are put into once component, for example C<host.cpu-0.cpu-idle>.
 +instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
  
  =item B<AlwaysAppendDS> B<false>|B<true>
  
diff --combined src/common.c
@@@ -548,7 -548,7 +548,7 @@@ int check_create_dir (const char *file_
                        {
                                if (errno == ENOENT)
                                {
 -                                      if (mkdir (dir, 0755) == 0)
 +                                      if (mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
                                                break;
  
                                        /* this might happen, if a different thread created
@@@ -636,23 -636,24 +636,23 @@@ long long get_kstat_value (kstat_t *ksp
        kstat_named_t *kn;
        long long retval = -1LL;
  
 -#ifdef assert
 -      assert (ksp != NULL);
 -      assert (ksp->ks_type == KSTAT_TYPE_NAMED);
 -#else
        if (ksp == NULL)
        {
 -              ERROR ("ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
 +              ERROR ("get_kstat_value (\"%s\"): ksp is NULL.", name);
                return (-1LL);
        }
        else if (ksp->ks_type != KSTAT_TYPE_NAMED)
        {
 -              ERROR ("ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
 +              ERROR ("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
 +                              "is not KSTAT_TYPE_NAMED (%#x).",
 +                              name,
 +                              (unsigned int) ksp->ks_type,
 +                              (unsigned int) KSTAT_TYPE_NAMED);
                return (-1LL);
        }
 -#endif
  
        if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
 -              return (retval);
 +              return (-1LL);
  
        if (kn->data_type == KSTAT_DATA_INT32)
                retval = (long long) kn->value.i32;
@@@ -1229,7 -1230,102 +1229,102 @@@ counter_t counter_diff (counter_t old_v
        }
  
        return (diff);
- } /* counter_t counter_to_gauge */
+ } /* counter_t counter_diff */
+ int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
+               rate_to_value_state_t *state,
+               int ds_type, cdtime_t t)
+ {
+       gauge_t delta_gauge;
+       cdtime_t delta_t;
+       if (ds_type == DS_TYPE_GAUGE)
+       {
+               state->last_value.gauge = rate;
+               state->last_time = t;
+               *ret_value = state->last_value;
+               return (0);
+       }
+       /* Counter and absolute can't handle negative rates. Reset "last time"
+        * to zero, so that the next valid rate will re-initialize the
+        * structure. */
+       if ((rate < 0.0)
+                       && ((ds_type == DS_TYPE_COUNTER)
+                               || (ds_type == DS_TYPE_ABSOLUTE)))
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+       /* Another invalid state: The time is not increasing. */
+       if (t <= state->last_time)
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+       delta_t = t - state->last_time;
+       delta_gauge = (rate * CDTIME_T_TO_DOUBLE (delta_t)) + state->residual;
+       /* Previous value is invalid. */
+       if (state->last_time == 0) /* {{{ */
+       {
+               if (ds_type == DS_TYPE_DERIVE)
+               {
+                       state->last_value.derive = (derive_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.derive);
+               }
+               else if (ds_type == DS_TYPE_COUNTER)
+               {
+                       state->last_value.counter = (counter_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.counter);
+               }
+               else if (ds_type == DS_TYPE_ABSOLUTE)
+               {
+                       state->last_value.absolute = (absolute_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.absolute);
+               }
+               else
+               {
+                       assert (23 == 42);
+               }
+               state->last_time = t;
+               return (EAGAIN);
+       } /* }}} */
+       if (ds_type == DS_TYPE_DERIVE)
+       {
+               derive_t delta_derive = (derive_t) delta_gauge;
+               state->last_value.derive += delta_derive;
+               state->residual = delta_gauge - ((gauge_t) delta_derive);
+       }
+       else if (ds_type == DS_TYPE_COUNTER)
+       {
+               counter_t delta_counter = (counter_t) delta_gauge;
+               state->last_value.counter += delta_counter;
+               state->residual = delta_gauge - ((gauge_t) delta_counter);
+       }
+       else if (ds_type == DS_TYPE_ABSOLUTE)
+       {
+               absolute_t delta_absolute = (absolute_t) delta_gauge;
+               state->last_value.absolute = delta_absolute;
+               state->residual = delta_gauge - ((gauge_t) delta_absolute);
+       }
+       else
+       {
+               assert (23 == 42);
+       }
+         state->last_time = t;
+       *ret_value = state->last_value;
+       return (0);
+ } /* }}} value_t rate_to_value */
  
  int service_name_to_port_number (const char *service_name)
  {