From: Florian Forster Date: Sat, 17 Nov 2012 08:11:52 +0000 (+0100) Subject: Merge branch 'libvirt-interfacenumber' X-Git-Tag: collectd-5.2.0~15 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=dff1246579ac6bea70331e560d2e1da0c7429b11;hp=589e5633ccc0bd838e8ef797188a77fae8d9dbc7;p=collectd.git Merge branch 'libvirt-interfacenumber' --- diff --git a/ChangeLog b/ChangeLog index 4b90b152..ef2023a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,62 @@ +2012-11-11, Version 5.1.1 + * collectd: Create new directories with mode 0777 and let umask remove + unwanted permission bits. + * collectd: Build issues have been fixed. + * collectd: An incorrect assertion has been fixed in some common code + for Solaris. This should resolve pseudo-random assertion failures + under Solaris. Thanks to Jeff Blane for his help debugging this. + * collectd: A couple of memory leaks through PThread thread attributes + have been fixed. Thanks to Gerrie Roos for fixing these. + * collectdctl: Fix PUTVAL for data sets with multiple data sources. + Thanks to Cyril Feraudet for reporting this problem. + * contrib/migrate-4-5.px: Handle to "df" to "df_complex" conversion + correctly. + * apcups plugin: Improve the reconnect behavior. + * curl_xml plugin: The "Host" setting was silently ignored. Thanks to + Fabien Wernli for fixing this. + * df plugin: Ignore "rootfs" devices under Linux to avoid having them + reported twice. Thanks to Brune Prémont for fixing this. + * disk plugin: Fix incorrect computation of read and write latency (the + "disk_time" type). Previously, the numbers reported where too small + by a factor of "interval", e.g. when the interval is set to 10 + seconds, the values were too low by a factor of 10. Thanks to Manuel + Sanmartin for reporting this problem. + * dns plugin: A build issue under Solaris has been fixed. A erroneous + define that could lead to the reporting of bad data has been fixed by + Daniel Sutto. + * ethstat plugin: An off-by-one error and potential use of + uninitialized memory has been fixed. Thanks to Mark Voelker for + reporting these problems. + * memcachec plugin: A bug in the configuration handling has been fixed. + Thanks to Pascal Hofmann for fixing this issue. + * mysql plugin: Fix a bug when registering multiple databases. Thanks + to Sebastian Harl for fixing this. + * netapp plugin: Correctly close the connection on communication + errors. + * netlink plugin: The function used to query statistics has been + changed to be more in line with iproute2's behavior. Thanks to + "KIvosak" for the patch. + * network plugin: Initialization of libgcrypt has been fixed. Thanks to + Chris Lundquist for his patch. + * oracle plugin: Error messages have been improved. + * ping plugin: Don't enter the exponential back-off mode when + ping_send() fails. This should make recovery after a network failure + much faster. + * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert + and Sven Trenkel for fixing this. + * redis plugin: Fix a compilation problem on FreeBSD. Thanks to + "biancalana" for the fix. + * rrdtool plugin: Fix an out-of-bounds array access when printing a + warning message. Thanks to Will Hawkins for fixing this bug. + * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been + added. Support for more complex / unusual MIBs / subtrees has been + added. Thanks to Mark Juric to test the changes and point out these + problems. + * varnish plugin: Support for multiple instances of Varnish 3 has been + fixed. Thanks to Jonathan Huot for the patch. + * write_mongodb plugin: Add compatibility with libmongo 0.6.0 and + later. Thanks to Chris Lundquist for this patch. + 2012-04-01, Version 5.1.0 * Build system, iptables plugin: The shipped version of libiptc has been removed. @@ -51,6 +110,57 @@ * scale target: Support for scaling specific data sources only has been added. Thanks to Gerrie Roos for his patch. +2012-11-11, Version 5.0.5 + * collectd: Create new directories with mode 0777 and let umask remove + unwanted permission bits. + * collectd: Build issues have been fixed. + * collectd: An incorrect assertion has been fixed in some common code + for Solaris. This should resolve pseudo-random assertion failures + under Solaris. Thanks to Jeff Blane for his help debugging this. + * collectd: A couple of memory leaks through PThread thread attributes + have been fixed. Thanks to Gerrie Roos for fixing these. + * collectdctl: Fix PUTVAL for data sets with multiple data sources. + Thanks to Cyril Feraudet for reporting this problem. + * contrib/migrate-4-5.px: Handle to "df" to "df_complex" conversion + correctly. + * apcups plugin: Improve the reconnect behavior. + * curl_xml plugin: The "Host" setting was silently ignored. Thanks to + Fabien Wernli for fixing this. + * df plugin: Ignore "rootfs" devices under Linux to avoid having them + reported twice. Thanks to Brune Prémont for fixing this. + * disk plugin: Fix incorrect computation of read and write latency (the + "disk_time" type). Previously, the numbers reported where too small + by a factor of "interval", e.g. when the interval is set to 10 + seconds, the values were too low by a factor of 10. Thanks to Manuel + Sanmartin for reporting this problem. + * dns plugin: A build issue under Solaris has been fixed. A erroneous + define that could lead to the reporting of bad data has been fixed by + Daniel Sutto. + * memcachec plugin: A bug in the configuration handling has been fixed. + Thanks to Pascal Hofmann for fixing this issue. + * mysql plugin: Fix a bug when registering multiple databases. Thanks + to Sebastian Harl for fixing this. + * netapp plugin: Correctly close the connection on communication + errors. + * netlink plugin: The function used to query statistics has been + changed to be more in line with iproute2's behavior. Thanks to + "KIvosak" for the patch. + * network plugin: Initialization of libgcrypt has been fixed. Thanks to + Chris Lundquist for his patch. + * oracle plugin: Error messages have been improved. + * ping plugin: Don't enter the exponential back-off mode when + ping_send() fails. This should make recovery after a network failure + much faster. + * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert + and Sven Trenkel for fixing this. + * redis plugin: Fix a compilation problem on FreeBSD. Thanks to + "biancalana" for the fix. + * rrdtool plugin: Fix an out-of-bounds array access when printing a + warning message. Thanks to Will Hawkins for fixing this bug. + * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been + added. Support for more complex / unusual MIBs / subtrees has been + added. Thanks to Mark Juric to test the changes and point out these + problems. 2012-04-01, Version 5.0.4 * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang @@ -223,6 +333,48 @@ * v5upgrade target: Target for converting v4 data sets to the v5 schema. +2012-11-11, Version 4.10.8 + * collectd: Create new directories with mode 0777 and let umask remove + unwanted permission bits. + * collectd: Build issues have been fixed. + * collectd: An incorrect assertion has been fixed in some common code + for Solaris. This should resolve pseudo-random assertion failures + under Solaris. Thanks to Jeff Blane for his help debugging this. + * collectd: A couple of memory leaks through PThread thread attributes + have been fixed. Thanks to Gerrie Roos for fixing these. + * apcups plugin: Improve the reconnect behavior. + * df plugin: Ignore "rootfs" devices under Linux to avoid having them + reported twice. Thanks to Brune Prémont for fixing this. + * disk plugin: Fix incorrect computation of read and write latency (the + "disk_time" type). Previously, the numbers reported where too small + by a factor of "interval", e.g. when the interval is set to 10 + seconds, the values were too low by a factor of 10. Thanks to Manuel + Sanmartin for reporting this problem. + * dns plugin: A build issue under Solaris has been fixed. A erroneous + define that could lead to the reporting of bad data has been fixed by + Daniel Sutto. + * memcachec plugin: A bug in the configuration handling has been fixed. + Thanks to Pascal Hofmann for fixing this issue. + * netapp plugin: Correctly close the connection on communication + errors. + * netlink plugin: The function used to query statistics has been + changed to be more in line with iproute2's behavior. Thanks to + "KIvosak" for the patch. + * network plugin: Initialization of libgcrypt has been fixed. Thanks to + Chris Lundquist for his patch. + * oracle plugin: Error messages have been improved. + * ping plugin: Don't enter the exponential back-off mode when + ping_send() fails. This should make recovery after a network failure + much faster. + * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert + and Sven Trenkel for fixing this. + * rrdtool plugin: Fix an out-of-bounds array access when printing a + warning message. Thanks to Will Hawkins for fixing this bug. + * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been + added. Support for more complex / unusual MIBs / subtrees has been + added. Thanks to Mark Juric to test the changes and point out these + problems. + 2012-04-01, Version 4.10.7 * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang for fixing this. Adresses some issues with building the iptables diff --git a/Makefile.am b/Makefile.am index 9e3feac4..52671235 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ACLOCAL_AMFLAGS = -I libltdl/m4 -SUBDIRS = libltdl src bindings +SUBDIRS = libltdl src bindings . INCLUDES = $(LTDLINCL) @@ -10,3 +10,8 @@ install-exec-hook: $(mkinstalldirs) $(DESTDIR)$(localstatedir)/run $(mkinstalldirs) $(DESTDIR)$(localstatedir)/lib/$(PACKAGE_NAME) $(mkinstalldirs) $(DESTDIR)$(localstatedir)/log + +maintainer-clean-local: + -rm -f -r libltdl + -rm -f INSTALL + -rm -f aclocal.m4 diff --git a/bindings/Makefile.am b/bindings/Makefile.am index f39e9bbb..3876cc2c 100644 --- a/bindings/Makefile.am +++ b/bindings/Makefile.am @@ -10,28 +10,55 @@ EXTRA_DIST = perl/Makefile.PL \ perl/lib/Collectd/Plugins/Monitorus.pm \ perl/lib/Collectd/Plugins/OpenVZ.pm +CLEANFILES = \ + buildperl/Collectd.pm \ + buildperl/Collectd/Plugins/OpenVZ.pm \ + buildperl/Collectd/Unixsock.pm \ + buildperl/Makefile.PL \ + .perl-directory-stamp + +DISTCLEANFILES = \ + buildperl/Collectd.pm \ + buildperl/Collectd/Plugins/OpenVZ.pm \ + buildperl/Collectd/Unixsock.pm \ + buildperl/Makefile.PL \ + .perl-directory-stamp + all-local: @PERL_BINDINGS@ + install-exec-local: - [ ! -f perl/Makefile ] || ( cd perl && $(MAKE) install ) + [ ! -f buildperl/Makefile ] || ( cd buildperl && $(MAKE) install ) + +# Perl 'make uninstall' does not work as well as wanted. +# So we do the work here. +uninstall-local: + rm -f $(DESTDIR)$(mandir)/man3/Collectd::Unixsock.3pm + rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd.pm + rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd/Plugins/OpenVZ.pm + rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd/Unixsock.pm + rm -f $(DESTDIR)$(prefix)/lib64/perl5/perllocal.pod + rm -f $(DESTDIR)$(prefix)/lib64/perl5/auto/Collectd/.packlist clean-local: - [ ! -f perl/Makefile ] || ( cd perl && $(MAKE) realclean ) + rm -rf buildperl -perl: perl/Makefile - cd perl && $(MAKE) +perl: buildperl/Makefile + cd buildperl && $(MAKE) -perl/Makefile: .perl-directory-stamp perl/Makefile.PL \ +buildperl/Makefile: .perl-directory-stamp buildperl/Makefile.PL \ $(top_builddir)/config.status - cd perl && @PERL@ Makefile.PL PREFIX=$(prefix) @PERL_BINDINGS_OPTIONS@ + cd buildperl && @PERL@ Makefile.PL INSTALL_BASE=$(prefix) @PERL_BINDINGS_OPTIONS@ + +buildperl/Makefile.PL: .perl-directory-stamp $(top_builddir)/config.status .perl-directory-stamp: - if test ! -d perl; then \ - mkdir -p perl/Collectd/Plugins; \ - cp $(srcdir)/perl/Collectd.pm perl/; \ - cp $(srcdir)/perl/Makefile.PL perl/; \ - cp $(srcdir)/perl/Collectd/Unixsock.pm perl/Collectd/; \ - cp $(srcdir)/perl/Collectd/Plugins/OpenVZ.pm perl/Collectd/Plugins/; \ + if test ! -d buildperl; then \ + mkdir -p buildperl/Collectd/Plugins; \ + cp $(srcdir)/perl/lib/Collectd.pm buildperl/; \ + cp $(srcdir)/perl/Makefile.PL buildperl/; \ + cp $(srcdir)/perl/lib/Collectd/Unixsock.pm buildperl/Collectd/; \ + cp $(srcdir)/perl/lib/Collectd/Plugins/OpenVZ.pm buildperl/Collectd/Plugins/; \ fi touch $@ diff --git a/bindings/java/org/collectd/java/GenericJMXConfConnection.java b/bindings/java/org/collectd/java/GenericJMXConfConnection.java index 0c81bc9a..9f062a9c 100644 --- a/bindings/java/org/collectd/java/GenericJMXConfConnection.java +++ b/bindings/java/org/collectd/java/GenericJMXConfConnection.java @@ -1,6 +1,6 @@ /* * collectd/java - org/collectd/java/GenericJMXConfConnection.java - * Copyright (C) 2009,2010 Florian octo Forster + * Copyright (C) 2009-2012 Florian octo 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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster */ package org.collectd.java; @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Iterator; import java.util.ArrayList; import java.util.HashMap; +import java.net.InetAddress; import javax.management.MBeanServerConnection; import javax.management.ObjectName; @@ -77,6 +78,24 @@ class GenericJMXConfConnection return (v.getString ()); } /* }}} String getConfigString */ + private String getHost () /* {{{ */ + { + if (this._host != null) + { + return (this._host); + } + + try + { + InetAddress localHost = InetAddress.getLocalHost(); + return (localHost.getHostName ()); + } + catch (UnknownHostException e) + { + return ("localhost"); + } + } /* }}} String getHost */ + private void connect () /* {{{ */ { JMXServiceURL service_url; @@ -211,7 +230,7 @@ private void connect () /* {{{ */ + ((this._host != null) ? this._host : "(null)")); pd = new PluginData (); - pd.setHost ((this._host != null) ? this._host : "localhost"); + pd.setHost (this.getHost ()); pd.setPlugin ("GenericJMX"); for (int i = 0; i < this._mbeans.size (); i++) diff --git a/bindings/perl/lib/Collectd.pm b/bindings/perl/lib/Collectd.pm index ca3b5d23..c1adf442 100644 --- a/bindings/perl/lib/Collectd.pm +++ b/bindings/perl/lib/Collectd.pm @@ -42,6 +42,7 @@ our %EXPORT_TAGS = ( plugin_register plugin_unregister plugin_dispatch_values + plugin_get_interval plugin_write plugin_flush plugin_flush_one @@ -171,6 +172,7 @@ sub plugin_call_all { my $type = shift; my %plugins; + my $interval; our $cb_name = undef; @@ -194,13 +196,15 @@ sub plugin_call_all { %plugins = %{$plugins[$type]}; } + $interval = plugin_get_interval (); + foreach my $plugin (keys %plugins) { my $p = $plugins{$plugin}; my $status = 0; if ($p->{'wait_left'} > 0) { - $p->{'wait_left'} -= $interval_g; + $p->{'wait_left'} -= $interval; } next if ($p->{'wait_left'} > 0); @@ -227,11 +231,11 @@ sub plugin_call_all { if ($status) { $p->{'wait_left'} = 0; - $p->{'wait_time'} = $interval_g; + $p->{'wait_time'} = $interval; } elsif (TYPE_READ == $type) { - if ($p->{'wait_time'} < $interval_g) { - $p->{'wait_time'} = $interval_g; + if ($p->{'wait_time'} < $interval) { + $p->{'wait_time'} = $interval; } $p->{'wait_left'} = $p->{'wait_time'}; @@ -313,7 +317,7 @@ sub plugin_register { } %p = ( - wait_time => $interval_g, + wait_time => plugin_get_interval (), wait_left => 0, cb_name => $data, ); diff --git a/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm b/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm index 29441574..ea3cee99 100644 --- a/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm +++ b/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm @@ -36,7 +36,7 @@ my $last_stat = {}; sub openvz_read { - my %v = (time => time(), interval => $interval_g); + my %v = (time => time(), interval => plugin_get_interval()); my (@veids, $veid, $name, $key, $val, $i, @lines, @parts, @counters); @veids = map { s/ //g; $_; } split(/\n/, `$vzlist -Ho veid`); diff --git a/configure.in b/configure.in index 1cfc2fca..7c4ec3a2 100644 --- a/configure.in +++ b/configure.in @@ -91,6 +91,7 @@ fi 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 @@ -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"]) @@ -1225,6 +1228,7 @@ AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct have_struct_kinfo_proc_freebsd="no" ], [ +AC_INCLUDES_DEFAULT #include #include #include @@ -1241,6 +1245,7 @@ AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc], have_struct_kinfo_proc_openbsd="no" ], [ +AC_INCLUDES_DEFAULT #include #include #include @@ -1900,10 +1905,12 @@ fi # 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 @@ -2459,21 +2466,20 @@ then #include #include ]) - AC_COMPILE_IFELSE( -[#include -#include -#include -#include -#include -#include - -int main (void) -{ - int retval = TCA_STATS2; - return (retval); -}], - [AC_DEFINE([HAVE_TCA_STATS2], 1, [True if the enum-member TCA_STATS2 exists])] - []); + AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [ + #include + #include + #include + #include + #include + #include + ], [ + int retval = TCA_STATS2; + return (retval); + ] + )], + [AC_DEFINE([HAVE_TCA_STATS2], [1], [True if the enum-member TCA_STATS2 exists])]) AC_COMPILE_IFELSE( [#include @@ -2502,6 +2508,76 @@ 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 +#include +#if HAVE_LIBNETLINK_H +# include +#elif HAVE_IPROUTE_LIBNETLINK_H +# include +#elif HAVE_LINUX_LIBNETLINK_H +# include +#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 +#include +#if HAVE_LIBNETLINK_H +# include +#elif HAVE_IPROUTE_LIBNETLINK_H +# include +#elif HAVE_LINUX_LIBNETLINK_H +# include +#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) @@ -2909,7 +2985,7 @@ AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes") 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" @@ -4057,7 +4133,7 @@ AC_ARG_WITH(libvarnish, [AS_HELP_STRING([--with-libvarnish@<:@=PREFIX@:>@], [Pat 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 ], @@ -4560,11 +4636,13 @@ 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" @@ -4774,6 +4852,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]) @@ -5023,7 +5102,7 @@ AC_SUBST(LCC_VERSION_PATCH) AC_SUBST(LCC_VERSION_EXTRA) AC_SUBST(LCC_VERSION_STRING) -AC_CONFIG_FILES(src/libcollectdclient/lcc_features.h) +AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h) AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile) @@ -5106,6 +5185,7 @@ Configuration: perl . . . . . . . . $with_perl_bindings Modules: + aggregation . . . . . $enable_aggregation amqp . . . . . . . $enable_amqp apache . . . . . . . $enable_apache apcups . . . . . . . $enable_apcups diff --git a/contrib/collectd_network.py b/contrib/collectd_network.py index 445b1838..9af0fb00 100644 --- a/contrib/collectd_network.py +++ b/contrib/collectd_network.py @@ -4,19 +4,23 @@ # # Copyright © 2009 Adrian Perez # -# Distributed under terms of the GPLv2 license. +# Distributed under terms of the GPLv2 license or newer. +# +# Frank Marien (frank@apsu.be) 6 Sep 2012 +# - quick fixes for 5.1 binary protocol +# - updated to python 3 +# - fixed for larger packet sizes (possible on lo interface) +# - fixed comment typo (decode_network_string decodes a string) """ Collectd network protocol implementation. """ -import socket -import struct - +import socket,struct,sys try: - from cStringIO import StringIO + from io import StringIO except ImportError: - from StringIO import StringIO + from cStringIO import StringIO from datetime import datetime from copy import deepcopy @@ -31,17 +35,19 @@ DEFAULT_IPv4_GROUP = "239.192.74.66" DEFAULT_IPv6_GROUP = "ff18::efc0:4a42" """Default IPv6 multicast group""" - +HR_TIME_DIV = (2.0**30) # Message kinds TYPE_HOST = 0x0000 TYPE_TIME = 0x0001 +TYPE_TIME_HR = 0x0008 TYPE_PLUGIN = 0x0002 TYPE_PLUGIN_INSTANCE = 0x0003 TYPE_TYPE = 0x0004 TYPE_TYPE_INSTANCE = 0x0005 TYPE_VALUES = 0x0006 TYPE_INTERVAL = 0x0007 +TYPE_INTERVAL_HR = 0x0009 # For notifications TYPE_MESSAGE = 0x0100 @@ -50,14 +56,14 @@ TYPE_SEVERITY = 0x0101 # DS kinds DS_TYPE_COUNTER = 0 DS_TYPE_GAUGE = 1 - +DS_TYPE_DERIVE = 2 +DS_TYPE_ABSOLUTE = 3 header = struct.Struct("!2H") number = struct.Struct("!Q") short = struct.Struct("!H") double = struct.Struct("= 9) && ($n <= 13)) + { + next; + } + elsif ($n < 32) + { + return (1); + } + } + + return; +} + sub show_selector { my $timespan_selection = get_timespan_selection (); @@ -254,6 +276,7 @@ sub show_selector HTML for (sort (keys %$host_selection)) { + next if contains_invalid_chars ($_); my $host = encode_entities ($_); my $selected = $host_selection->{$_} ? ' selected="selected"' @@ -266,6 +289,7 @@ HTML HTML for (sort (keys %$plugin_selection)) { + next if contains_invalid_chars ($_); my $plugin = encode_entities ($_); my $selected = $plugin_selection->{$_} ? ' selected="selected"' @@ -278,6 +302,7 @@ HTML HTML for (sort { $TimeSpans->{$a} <=> $TimeSpans->{$b} } (keys (%$TimeSpans))) { + next if contains_invalid_chars ($_); my $name = encode_entities ($_); my $value = $TimeSpans->{$_}; my $selected = ($value == $timespan_selection) @@ -304,6 +329,7 @@ sub action_list_hosts for (sort @hosts) { my $url = encode_entities (script_name () . "?action=show_selection;hostname=$_"); + next if contains_invalid_chars ($_); my $name = encode_entities ($_); print qq#
  • $name
  • \n#; } diff --git a/contrib/collection3/lib/Collectd/Graph/Type/Df.pm b/contrib/collection3/lib/Collectd/Graph/Type/Df.pm index 0fbd0d35..4a70c41a 100644 --- a/contrib/collection3/lib/Collectd/Graph/Type/Df.pm +++ b/contrib/collection3/lib/Collectd/Graph/Type/Df.pm @@ -34,7 +34,7 @@ sub new my $obj = Collectd::Graph::Type->new (@_); $obj->{'data_sources'} = [qw(free used)]; $obj->{'rrd_opts'} = ['-v', 'Bytes']; - $obj->{'rrd_title'} = 'Disk space ({type_instance})'; + $obj->{'rrd_title'} = 'Disk space ({instance})'; $obj->{'rrd_format'} = '%5.1lf%sB'; $obj->{'colors'} = [qw(00b000 ff0000)]; @@ -58,7 +58,7 @@ sub getRRDArgs my $faded_green = get_faded_color ('00ff00'); my $faded_red = get_faded_color ('ff0000'); - return (['-t', 'Diskspace (' . $ident->{'type_instance'} . ')', '-v', 'Bytes', '-l', '0', + return (['-t', $obj->getTitle ($ident), '-v', 'Bytes', '-l', '0', "DEF:free_min=${filename}:free:MIN", "DEF:free_avg=${filename}:free:AVERAGE", "DEF:free_max=${filename}:free:MAX", diff --git a/contrib/migrate-4-5.px b/contrib/migrate-4-5.px index d3ff796d..c2a95558 100755 --- a/contrib/migrate-4-5.px +++ b/contrib/migrate-4-5.px @@ -33,6 +33,7 @@ use File::Basename ('dirname'); our $InDir = '/var/lib/collectd'; our $RRDtool = 'rrdtool'; +our $RRDFilter = 'rrd_filter.px'; our %TypesCounterToDerive = # {{{ ( @@ -184,7 +185,15 @@ sub handle_file # {{{ { my $dir = join ('/', @path); print "mkdir -p \"$dir/$plugin-$type_inst\"\n"; - print "mv \"$path\" \"$dir/$plugin-$type_inst/$type.rrd\"\n"; + if (($plugin eq 'df') and ($type eq 'df')) + { + print "$RRDFilter --infile=\"$path\" --outfile=\"$dir/$plugin-$type_inst/df_complex-free.rrd\" --map free:value\n"; + print "$RRDFilter --infile=\"$path\" --outfile=\"$dir/$plugin-$type_inst/df_complex-used.rrd\" --map used:value\n"; + } + else + { + print "mv \"$path\" \"$dir/$plugin-$type_inst/$type.rrd\"\n"; + } } } # }}} sub handle_file @@ -223,10 +232,12 @@ migrate-4-5.px [OPTIONS] Valid options are: - --indir Source directory - Default: $InDir - --rrdtool Path to the RRDtool binary - Default: $RRDtool + --indir Source directory + Default: $InDir + --rrdtool Path to the RRDtool binary + Default: $RRDtool + --rrdfilter Path to the rrd_filter.px script + Default: $RRDFilter EOF exit (1); @@ -234,8 +245,11 @@ EOF GetOptions ("indir|i=s" => \$InDir, "rrdtool=s" => \$RRDtool, + "rrdfilter=s" => \$RRDFilter, "help|h" => \&exit_usage) or exit_usage (); +print "#!/bin/bash\n\n"; + scan_dir ($InDir); # vim: set sw=2 sts=2 et fdm=marker : diff --git a/src/Makefile.am b/src/Makefile.am index ab580d75..3532e7b2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"' AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"' sbin_PROGRAMS = collectd collectdmon -bin_PROGRAMS = collectd-nagios collectdctl +bin_PROGRAMS = collectd-nagios collectdctl collectd-tg collectd_SOURCES = collectd.c collectd.h \ common.c common.h \ @@ -43,7 +43,7 @@ collectd_SOURCES = collectd.c collectd.h \ 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,7 +70,6 @@ collectd_LDADD += -ldevinfo 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, @@ -114,12 +113,34 @@ endif collectdctl_LDADD += libcollectdclient/libcollectdclient.la collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la +collectd_tg_SOURCES = collectd-tg.c \ + utils_heap.c utils_heap.h +collectd_tg_LDADD = +if BUILD_WITH_LIBSOCKET +collectd_tg_LDADD += -lsocket +endif +if BUILD_AIX +collectd_tg_LDADD += -lm +endif +collectd_tg_LDADD += libcollectdclient/libcollectdclient.la +collectd_tg_DEPENDENCIES = libcollectdclient/libcollectdclient.la + 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 \ @@ -214,6 +235,10 @@ if BUILD_PLUGIN_CONTEXTSWITCH 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 @@ -1193,6 +1218,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 @@ -1320,6 +1348,8 @@ collectd_LDADD += "-dlopen" zfs_arc.la collectd_DEPENDENCIES += zfs_arc.la endif +BUILT_SOURCES += $(dist_man_MANS) + dist_man_MANS = collectd.1 \ collectd.conf.5 \ collectd-email.5 \ @@ -1372,7 +1402,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) @@ -1386,3 +1416,21 @@ install-exec-hook: $(INSTALL) -m 0644 $(srcdir)/types.db $(DESTDIR)$(pkgdatadir)/types.db; $(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 --git a/src/aggregation.c b/src/aggregation.c new file mode 100644 index 00000000..db33c177 --- /dev/null +++ b/src/aggregation.c @@ -0,0 +1,684 @@ +/** + * collectd - src/aggregation.c + * Copyright (C) 2012 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian Forster + **/ + +#include "collectd.h" +#include "plugin.h" +#include "common.h" +#include "configfile.h" +#include "meta_data.h" +#include "utils_cache.h" /* for uc_get_rate() */ +#include "utils_vl_lookup.h" + +#include + +struct aggregation_s /* {{{ */ +{ + identifier_t ident; + + _Bool calc_num; + _Bool calc_sum; + _Bool calc_average; + _Bool calc_min; + _Bool calc_max; + _Bool calc_stddev; +}; /* }}} */ +typedef struct aggregation_s aggregation_t; + +struct agg_instance_s; +typedef struct agg_instance_s agg_instance_t; +struct agg_instance_s /* {{{ */ +{ + pthread_mutex_t lock; + identifier_t ident; + + int ds_type; + + derive_t num; + gauge_t sum; + gauge_t squares_sum; + + gauge_t min; + gauge_t max; + + rate_to_value_state_t *state_num; + rate_to_value_state_t *state_sum; + rate_to_value_state_t *state_average; + rate_to_value_state_t *state_min; + rate_to_value_state_t *state_max; + rate_to_value_state_t *state_stddev; + + agg_instance_t *next; +}; /* }}} */ + +static lookup_t *lookup = NULL; + +static pthread_mutex_t agg_instance_list_lock = PTHREAD_MUTEX_INITIALIZER; +static agg_instance_t *agg_instance_list_head = NULL; + +static void agg_destroy (aggregation_t *agg) /* {{{ */ +{ + sfree (agg); +} /* }}} void agg_destroy */ + +/* Frees all dynamically allocated memory within the instance. */ +static void agg_instance_destroy (agg_instance_t *inst) /* {{{ */ +{ + if (inst == NULL) + return; + + /* Remove this instance from the global list of instances. */ + pthread_mutex_lock (&agg_instance_list_lock); + if (agg_instance_list_head == inst) + agg_instance_list_head = inst->next; + else if (agg_instance_list_head != NULL) + { + agg_instance_t *prev = agg_instance_list_head; + while ((prev != NULL) && (prev->next != inst)) + prev = prev->next; + if (prev != NULL) + prev->next = inst->next; + } + pthread_mutex_unlock (&agg_instance_list_lock); + + sfree (inst->state_num); + sfree (inst->state_sum); + sfree (inst->state_average); + sfree (inst->state_min); + sfree (inst->state_max); + sfree (inst->state_stddev); + + memset (inst, 0, sizeof (*inst)); + inst->ds_type = -1; + inst->min = NAN; + inst->max = NAN; +} /* }}} void agg_instance_destroy */ + +/* Create a new aggregation instance. */ +static agg_instance_t *agg_instance_create (data_set_t const *ds, /* {{{ */ + value_list_t const *vl, aggregation_t *agg) +{ + agg_instance_t *inst; + + DEBUG ("aggregation plugin: Creating new instance."); + + inst = malloc (sizeof (*inst)); + if (inst == NULL) + { + ERROR ("aggregation plugin: malloc() failed."); + return (NULL); + } + memset (inst, 0, sizeof (*inst)); + pthread_mutex_init (&inst->lock, /* attr = */ NULL); + + inst->ds_type = ds->ds[0].type; + +#define COPY_FIELD(fld) do { \ + sstrncpy (inst->ident.fld, \ + LU_IS_ANY (agg->ident.fld) ? vl->fld : agg->ident.fld, \ + sizeof (inst->ident.fld)); \ +} while (0) + + COPY_FIELD (host); + COPY_FIELD (plugin); + COPY_FIELD (plugin_instance); + COPY_FIELD (type); + COPY_FIELD (type_instance); + +#undef COPY_FIELD + + inst->min = NAN; + inst->max = NAN; + +#define INIT_STATE(field) do { \ + inst->state_ ## field = NULL; \ + if (agg->calc_ ## field) { \ + inst->state_ ## field = malloc (sizeof (*inst->state_ ## field)); \ + if (inst->state_ ## field == NULL) { \ + agg_instance_destroy (inst); \ + ERROR ("aggregation plugin: malloc() failed."); \ + return (NULL); \ + } \ + memset (inst->state_ ## field, 0, sizeof (*inst->state_ ## field)); \ + } \ +} while (0) + + INIT_STATE (num); + INIT_STATE (sum); + INIT_STATE (average); + INIT_STATE (min); + INIT_STATE (max); + INIT_STATE (stddev); + +#undef INIT_STATE + + pthread_mutex_lock (&agg_instance_list_lock); + inst->next = agg_instance_list_head; + agg_instance_list_head = inst; + pthread_mutex_unlock (&agg_instance_list_lock); + + return (inst); +} /* }}} agg_instance_t *agg_instance_create */ + +/* Update the num, sum, min, max, ... fields of the aggregation instance, if + * the rate of the value list is available. Value lists with more than one data + * source are not supported and will return an error. Returns zero on success + * and non-zero otherwise. */ +static int agg_instance_update (agg_instance_t *inst, /* {{{ */ + data_set_t const *ds, value_list_t const *vl) +{ + gauge_t *rate; + + if (ds->ds_num != 1) + { + ERROR ("aggregation plugin: The \"%s\" type (data set) has more than one " + "data source. This is currently not supported by this plugin. " + "Sorry.", ds->type); + return (EINVAL); + } + + rate = uc_get_rate (ds, vl); + if (rate == NULL) + { + char ident[6 * DATA_MAX_NAME_LEN]; + FORMAT_VL (ident, sizeof (ident), vl); + ERROR ("aggregation plugin: Unable to read the current rate of \"%s\".", + ident); + return (ENOENT); + } + + if (isnan (rate[0])) + { + sfree (rate); + return (0); + } + + pthread_mutex_lock (&inst->lock); + + inst->num++; + inst->sum += rate[0]; + inst->squares_sum += (rate[0] * rate[0]); + + if (isnan (inst->min) || (inst->min > rate[0])) + inst->min = rate[0]; + if (isnan (inst->max) || (inst->max < rate[0])) + inst->max = rate[0]; + + pthread_mutex_unlock (&inst->lock); + + sfree (rate); + return (0); +} /* }}} int agg_instance_update */ + +static int agg_instance_read_func (agg_instance_t *inst, /* {{{ */ + char const *func, gauge_t rate, rate_to_value_state_t *state, + value_list_t *vl, char const *pi_prefix, cdtime_t t) +{ + value_t v; + int status; + + if (pi_prefix[0] != 0) + ssnprintf (vl->plugin_instance, sizeof (vl->plugin_instance), "%s-%s", + pi_prefix, func); + else + sstrncpy (vl->plugin_instance, func, sizeof (vl->plugin_instance)); + + memset (&v, 0, sizeof (v)); + status = rate_to_value (&v, rate, state, inst->ds_type, t); + if (status != 0) + { + /* If this is the first iteration and rate_to_value() was asked to return a + * COUNTER or a DERIVE, it will return EAGAIN. Catch this and handle + * gracefully. */ + if (status == EAGAIN) + return (0); + + WARNING ("aggregation plugin: rate_to_value failed with status %i.", + status); + return (-1); + } + + vl->values = &v; + vl->values_len = 1; + + plugin_dispatch_values_secure (vl); + + vl->values = NULL; + vl->values_len = 0; + + return (0); +} /* }}} int agg_instance_read_func */ + +static int agg_instance_read (agg_instance_t *inst, cdtime_t t) /* {{{ */ +{ + value_list_t vl = VALUE_LIST_INIT; + char pi_prefix[DATA_MAX_NAME_LEN]; + + /* Pre-set all the fields in the value list that will not change per + * aggregation type (sum, average, ...). The struct will be re-used and must + * therefore be dispatched using the "secure" function. */ + + vl.time = t; + vl.interval = 0; + + vl.meta = meta_data_create (); + if (vl.meta == NULL) + { + ERROR ("aggregation plugin: meta_data_create failed."); + return (-1); + } + meta_data_add_boolean (vl.meta, "aggregation:created", 1); + + if (LU_IS_ALL (inst->ident.host)) + sstrncpy (vl.host, "global", sizeof (vl.host)); + else + sstrncpy (vl.host, inst->ident.host, sizeof (vl.host)); + + sstrncpy (vl.plugin, "aggregation", sizeof (vl.plugin)); + + if (LU_IS_ALL (inst->ident.plugin)) + { + if (LU_IS_ALL (inst->ident.plugin_instance)) + sstrncpy (pi_prefix, "", sizeof (pi_prefix)); + else + sstrncpy (pi_prefix, inst->ident.plugin_instance, sizeof (pi_prefix)); + } + else + { + if (LU_IS_ALL (inst->ident.plugin_instance)) + sstrncpy (pi_prefix, inst->ident.plugin, sizeof (pi_prefix)); + else + ssnprintf (pi_prefix, sizeof (pi_prefix), + "%s-%s", inst->ident.plugin, inst->ident.plugin_instance); + } + + sstrncpy (vl.type, inst->ident.type, sizeof (vl.type)); + + if (!LU_IS_ALL (inst->ident.type_instance)) + sstrncpy (vl.type_instance, inst->ident.type_instance, + sizeof (vl.type_instance)); + +#define READ_FUNC(func, rate) do { \ + if (inst->state_ ## func != NULL) { \ + agg_instance_read_func (inst, #func, rate, \ + inst->state_ ## func, &vl, pi_prefix, t); \ + } \ +} while (0) + + pthread_mutex_lock (&inst->lock); + + READ_FUNC (num, (gauge_t) inst->num); + + /* All other aggregations are only defined when there have been any values + * at all. */ + if (inst->num > 0) + { + READ_FUNC (sum, inst->sum); + READ_FUNC (average, (inst->sum / ((gauge_t) inst->num))); + READ_FUNC (min, inst->min); + READ_FUNC (max, inst->max); + READ_FUNC (stddev, sqrt((((gauge_t) inst->num) * inst->squares_sum) + - (inst->sum * inst->sum)) / ((gauge_t) inst->num)); + } + + /* Reset internal state. */ + inst->num = 0; + inst->sum = 0.0; + inst->squares_sum = 0.0; + inst->min = NAN; + inst->max = NAN; + + pthread_mutex_unlock (&inst->lock); + + meta_data_destroy (vl.meta); + vl.meta = NULL; + + return (0); +} /* }}} int agg_instance_read */ + +/* lookup_class_callback_t for utils_vl_lookup */ +static void *agg_lookup_class_callback ( /* {{{ */ + __attribute__((unused)) data_set_t const *ds, + value_list_t const *vl, void *user_class) +{ + return (agg_instance_create (ds, vl, (aggregation_t *) user_class)); +} /* }}} void *agg_class_callback */ + +/* lookup_obj_callback_t for utils_vl_lookup */ +static int agg_lookup_obj_callback (data_set_t const *ds, /* {{{ */ + value_list_t const *vl, + __attribute__((unused)) void *user_class, + void *user_obj) +{ + return (agg_instance_update ((agg_instance_t *) user_obj, ds, vl)); +} /* }}} int agg_lookup_obj_callback */ + +/* lookup_free_class_callback_t for utils_vl_lookup */ +static void agg_lookup_free_class_callback (void *user_class) /* {{{ */ +{ + agg_destroy ((aggregation_t *) user_class); +} /* }}} void agg_lookup_free_class_callback */ + +/* lookup_free_obj_callback_t for utils_vl_lookup */ +static void agg_lookup_free_obj_callback (void *user_obj) /* {{{ */ +{ + agg_instance_destroy ((agg_instance_t *) user_obj); +} /* }}} void agg_lookup_free_obj_callback */ + +/* + * + * + * Plugin "cpu" + * Type "cpu" + * + * GroupBy Host + * GroupBy TypeInstance + * + * CalculateNum true + * CalculateSum true + * CalculateAverage true + * CalculateMinimum true + * CalculateMaximum true + * CalculateStddev true + * + * + */ +static int agg_config_handle_group_by (oconfig_item_t const *ci, /* {{{ */ + aggregation_t *agg) +{ + int i; + + for (i = 0; i < ci->values_num; i++) + { + char const *value; + + if (ci->values[i].type != OCONFIG_TYPE_STRING) + { + ERROR ("aggregation plugin: Argument %i of the \"GroupBy\" option " + "is not a string.", i + 1); + continue; + } + + value = ci->values[i].value.string; + + if (strcasecmp ("Host", value) == 0) + sstrncpy (agg->ident.host, LU_ANY, sizeof (agg->ident.host)); + else if (strcasecmp ("Plugin", value) == 0) + sstrncpy (agg->ident.plugin, LU_ANY, sizeof (agg->ident.plugin)); + else if (strcasecmp ("PluginInstance", value) == 0) + sstrncpy (agg->ident.plugin_instance, LU_ANY, + sizeof (agg->ident.plugin_instance)); + else if (strcasecmp ("TypeInstance", value) == 0) + sstrncpy (agg->ident.type_instance, LU_ANY, sizeof (agg->ident.type_instance)); + else if (strcasecmp ("Type", value) == 0) + ERROR ("aggregation plugin: Grouping by type is not supported."); + else + WARNING ("aggregation plugin: The \"%s\" argument to the \"GroupBy\" " + "option is invalid and will be ignored.", value); + } /* for (ci->values) */ + + return (0); +} /* }}} int agg_config_handle_group_by */ + +static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */ +{ + aggregation_t *agg; + _Bool is_valid; + int status; + int i; + + agg = malloc (sizeof (*agg)); + if (agg == NULL) + { + ERROR ("aggregation plugin: malloc failed."); + return (-1); + } + memset (agg, 0, sizeof (*agg)); + + sstrncpy (agg->ident.host, LU_ALL, sizeof (agg->ident.host)); + sstrncpy (agg->ident.plugin, LU_ALL, sizeof (agg->ident.plugin)); + sstrncpy (agg->ident.plugin_instance, LU_ALL, + sizeof (agg->ident.plugin_instance)); + sstrncpy (agg->ident.type, LU_ALL, sizeof (agg->ident.type)); + sstrncpy (agg->ident.type_instance, LU_ALL, + sizeof (agg->ident.type_instance)); + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Host", child->key) == 0) + cf_util_get_string_buffer (child, agg->ident.host, + sizeof (agg->ident.host)); + else if (strcasecmp ("Plugin", child->key) == 0) + cf_util_get_string_buffer (child, agg->ident.plugin, + sizeof (agg->ident.plugin)); + else if (strcasecmp ("PluginInstance", child->key) == 0) + cf_util_get_string_buffer (child, agg->ident.plugin_instance, + sizeof (agg->ident.plugin_instance)); + else if (strcasecmp ("Type", child->key) == 0) + cf_util_get_string_buffer (child, agg->ident.type, + sizeof (agg->ident.type)); + else if (strcasecmp ("TypeInstance", child->key) == 0) + cf_util_get_string_buffer (child, agg->ident.type_instance, + sizeof (agg->ident.type_instance)); + else if (strcasecmp ("GroupBy", child->key) == 0) + agg_config_handle_group_by (child, agg); + else if (strcasecmp ("CalculateNum", child->key) == 0) + cf_util_get_boolean (child, &agg->calc_num); + else if (strcasecmp ("CalculateSum", child->key) == 0) + cf_util_get_boolean (child, &agg->calc_sum); + else if (strcasecmp ("CalculateAverage", child->key) == 0) + cf_util_get_boolean (child, &agg->calc_average); + else if (strcasecmp ("CalculateMinimum", child->key) == 0) + cf_util_get_boolean (child, &agg->calc_min); + else if (strcasecmp ("CalculateMaximum", child->key) == 0) + cf_util_get_boolean (child, &agg->calc_max); + else if (strcasecmp ("CalculateStddev", child->key) == 0) + cf_util_get_boolean (child, &agg->calc_stddev); + else + WARNING ("aggregation plugin: The \"%s\" key is not allowed inside " + " blocks and will be ignored.", child->key); + } + + /* Sanity checking */ + is_valid = 1; + if (LU_IS_ALL (agg->ident.type)) /* {{{ */ + { + ERROR ("aggregation plugin: It appears you did not specify the required " + "\"Type\" option in this aggregation. " + "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", " + "Type \"%s\", TypeInstance \"%s\")", + agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance, + agg->ident.type, agg->ident.type_instance); + is_valid = 0; + } + else if (strchr (agg->ident.type, '/') != NULL) + { + ERROR ("aggregation plugin: The \"Type\" may not contain the '/' " + "character. Especially, it may not be a wildcard. The current " + "value is \"%s\".", agg->ident.type); + is_valid = 0; + } /* }}} */ + + if (!LU_IS_ALL (agg->ident.host) /* {{{ */ + && !LU_IS_ALL (agg->ident.plugin) + && !LU_IS_ALL (agg->ident.plugin_instance) + && !LU_IS_ALL (agg->ident.type_instance)) + { + ERROR ("aggregation plugin: An aggregation must contain at least one " + "wildcard. This is achieved by leaving at least one of the \"Host\", " + "\"Plugin\", \"PluginInstance\" and \"TypeInstance\" options blank " + "and not grouping by that field. " + "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", " + "Type \"%s\", TypeInstance \"%s\")", + agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance, + agg->ident.type, agg->ident.type_instance); + is_valid = 0; + } /* }}} */ + + if (!agg->calc_num && !agg->calc_sum && !agg->calc_average /* {{{ */ + && !agg->calc_min && !agg->calc_max && !agg->calc_stddev) + { + ERROR ("aggregation plugin: No aggregation function has been specified. " + "Without this, I don't know what I should be calculating. " + "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", " + "Type \"%s\", TypeInstance \"%s\")", + agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance, + agg->ident.type, agg->ident.type_instance); + is_valid = 0; + } /* }}} */ + + if (!is_valid) /* {{{ */ + { + sfree (agg); + return (-1); + } /* }}} */ + + status = lookup_add (lookup, &agg->ident, agg); + if (status != 0) + { + ERROR ("aggregation plugin: lookup_add failed with status %i.", status); + sfree (agg); + return (-1); + } + + DEBUG ("aggregation plugin: Successfully added aggregation: " + "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", " + "Type \"%s\", TypeInstance \"%s\")", + agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance, + agg->ident.type, agg->ident.type_instance); + return (0); +} /* }}} int agg_config_aggregation */ + +static int agg_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + pthread_mutex_lock (&agg_instance_list_lock); + + if (lookup == NULL) + { + lookup = lookup_create (agg_lookup_class_callback, + agg_lookup_obj_callback, + agg_lookup_free_class_callback, + agg_lookup_free_obj_callback); + if (lookup == NULL) + { + pthread_mutex_unlock (&agg_instance_list_lock); + ERROR ("aggregation plugin: lookup_create failed."); + return (-1); + } + } + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Aggregation", child->key) == 0) + agg_config_aggregation (child); + else + WARNING ("aggregation plugin: The \"%s\" key is not allowed inside " + " blocks and will be ignored.", child->key); + } + + pthread_mutex_unlock (&agg_instance_list_lock); + + return (0); +} /* }}} int agg_config */ + +static int agg_read (void) /* {{{ */ +{ + agg_instance_t *this; + cdtime_t t; + int success; + + t = cdtime (); + success = 0; + + pthread_mutex_lock (&agg_instance_list_lock); + + /* agg_instance_list_head only holds data, after the "write" callback has + * been called with a matching value list at least once. So on startup, + * there's a race between the aggregations read() and write() callback. If + * the read() callback is called first, agg_instance_list_head is NULL and + * "success" may be zero. This is expected and should not result in an error. + * Therefore we need to handle this case separately. */ + if (agg_instance_list_head == NULL) + { + pthread_mutex_unlock (&agg_instance_list_lock); + return (0); + } + + for (this = agg_instance_list_head; this != NULL; this = this->next) + { + int status; + + status = agg_instance_read (this, t); + if (status != 0) + WARNING ("aggregation plugin: Reading an aggregation instance " + "failed with status %i.", status); + else + success++; + } + + pthread_mutex_unlock (&agg_instance_list_lock); + + return ((success > 0) ? 0 : -1); +} /* }}} int agg_read */ + +static int agg_write (data_set_t const *ds, value_list_t const *vl, /* {{{ */ + __attribute__((unused)) user_data_t *user_data) +{ + _Bool created_by_aggregation = 0; + int status; + + /* Ignore values that were created by the aggregation plugin to avoid weird + * effects. */ + (void) meta_data_get_boolean (vl->meta, "aggregation:created", + &created_by_aggregation); + if (created_by_aggregation) + return (0); + + if (lookup == NULL) + status = ENOENT; + else + { + status = lookup_search (lookup, ds, vl); + if (status > 0) + status = 0; + } + + return (status); +} /* }}} int agg_write */ + +void module_register (void) +{ + plugin_register_complex_config ("aggregation", agg_config); + plugin_register_read ("aggregation", agg_read); + plugin_register_write ("aggregation", agg_write, /* user_data = */ NULL); +} + +/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */ diff --git a/src/amqp.c b/src/amqp.c index c9929dc0..9c8c6e53 100644 --- a/src/amqp.c +++ b/src/amqp.c @@ -600,6 +600,8 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */ camqp_config_t *conf = user_data; int status; + cdtime_t interval = plugin_get_interval (); + while (subscriber_threads_running) { amqp_frame_t frame; @@ -610,8 +612,8 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */ struct timespec ts_interval; ERROR ("amqp plugin: camqp_connect failed. " "Will sleep for %.3f seconds.", - CDTIME_T_TO_DOUBLE (interval_g)); - CDTIME_T_TO_TIMESPEC (interval_g, &ts_interval); + CDTIME_T_TO_DOUBLE (interval)); + CDTIME_T_TO_TIMESPEC (interval, &ts_interval); nanosleep (&ts_interval, /* remaining = */ NULL); continue; } @@ -622,9 +624,9 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */ struct timespec ts_interval; ERROR ("amqp plugin: amqp_simple_wait_frame failed. " "Will sleep for %.3f seconds.", - CDTIME_T_TO_DOUBLE (interval_g)); + CDTIME_T_TO_DOUBLE (interval)); camqp_close_connection (conf); - CDTIME_T_TO_TIMESPEC (interval_g, &ts_interval); + CDTIME_T_TO_TIMESPEC (interval, &ts_interval); nanosleep (&ts_interval, /* remaining = */ NULL); continue; } @@ -650,6 +652,7 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */ camqp_config_free (conf); pthread_exit (NULL); + return (NULL); } /* }}} void *camqp_subscribe_thread */ static int camqp_subscribe_init (camqp_config_t *conf) /* {{{ */ @@ -669,7 +672,7 @@ static int camqp_subscribe_init (camqp_config_t *conf) /* {{{ */ tmp = subscriber_threads + subscriber_threads_num; memset (tmp, 0, sizeof (*tmp)); - status = pthread_create (tmp, /* attr = */ NULL, + status = plugin_thread_create (tmp, /* attr = */ NULL, camqp_subscribe_thread, conf); if (status != 0) { @@ -790,7 +793,8 @@ static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */ else if (conf->format == CAMQP_FORMAT_GRAPHITE) { status = format_graphite (buffer, sizeof (buffer), ds, vl, - conf->prefix, conf->postfix, conf->escape_char); + conf->prefix, conf->postfix, conf->escape_char, + conf->store_rates); if (status != 0) { ERROR ("amqp plugin: format_graphite failed with status %i.", diff --git a/src/apcups.c b/src/apcups.c index a0629d5f..4fbbde63 100644 --- a/src/apcups.c +++ b/src/apcups.c @@ -1,6 +1,6 @@ /* * collectd - src/apcups.c - * Copyright (C) 2006-2007 Florian octo Forster + * Copyright (C) 2006-2012 Florian octo Forster * Copyright (C) 2006 Anthony Gialluca * Copyright (C) 2000-2004 Kern Sibbald * Copyright (C) 1996-1999 Andre M. Hedrick @@ -73,6 +73,10 @@ static int conf_port = NISPORT; static int global_sockfd = -1; +static int count_retries = 0; +static int count_iterations = 0; +static _Bool close_socket = 0; + static const char *config_keys[] = { "Host", @@ -81,26 +85,31 @@ static const char *config_keys[] = }; static int config_keys_num = 2; -/* Close the network connection */ -static int apcups_shutdown (void) +static int net_shutdown (int *fd) { uint16_t packet_size = 0; - if (global_sockfd < 0) - return (0); + if ((fd == NULL) || (*fd < 0)) + return (EINVAL); - DEBUG ("Gracefully shutting down socket %i.", global_sockfd); + swrite (*fd, (void *) &packet_size, sizeof (packet_size)); + close (*fd); + *fd = -1; - /* send EOF sentinel */ - swrite (global_sockfd, (void *) &packet_size, sizeof (packet_size)); + return (0); +} /* int net_shutdown */ - close (global_sockfd); - global_sockfd = -1; +/* Close the network connection */ +static int apcups_shutdown (void) +{ + if (global_sockfd < 0) + return (0); + net_shutdown (&global_sockfd); return (0); } /* int apcups_shutdown */ -/* +/* * Open a TCP connection to the UPS network server * Returns -1 on error * Returns socket file descriptor otherwise @@ -170,7 +179,7 @@ static int net_open (char *host, int port) return (sd); } /* int net_open (char *host, char *service, int port) */ -/* +/* * Receive a message from the other end. Each message consists of * two packets. The first is a header that contains the size * of the data that follows in the second packet. @@ -186,6 +195,7 @@ static int net_recv (int *sockfd, char *buf, int buflen) /* get data size -- in short */ if (sread (*sockfd, (void *) &packet_size, sizeof (packet_size)) != 0) { + close (*sockfd); *sockfd = -1; return (-1); } @@ -193,7 +203,11 @@ static int net_recv (int *sockfd, char *buf, int buflen) packet_size = ntohs (packet_size); if (packet_size > buflen) { - DEBUG ("record length too large"); + ERROR ("apcups plugin: Received %"PRIu16" bytes of payload " + "but have only %i bytes of buffer available.", + packet_size, buflen); + close (*sockfd); + *sockfd = -1; return (-2); } @@ -203,6 +217,7 @@ static int net_recv (int *sockfd, char *buf, int buflen) /* now read the actual data */ if (sread (*sockfd, (void *) buf, packet_size) != 0) { + close (*sockfd); *sockfd = -1; return (-1); } @@ -229,6 +244,7 @@ static int net_send (int *sockfd, char *buff, int len) if (swrite (*sockfd, (void *) &packet_size, sizeof (packet_size)) != 0) { + close (*sockfd); *sockfd = -1; return (-1); } @@ -236,6 +252,7 @@ static int net_send (int *sockfd, char *buff, int len) /* send data packet */ if (swrite (*sockfd, (void *) buff, len) != 0) { + close (*sockfd); *sockfd = -1; return (-2); } @@ -253,6 +270,8 @@ static int apc_query_server (char *host, int port, char *toksaveptr; char *key; double value; + _Bool retry = 1; + int status; #if APCMAIN # define PRINT_VALUE(name, val) printf(" Found property: name = %s; value = %f;\n", name, val) @@ -260,21 +279,51 @@ static int apc_query_server (char *host, int port, # define PRINT_VALUE(name, val) /**/ #endif - if (global_sockfd < 0) + while (retry) { - global_sockfd = net_open (host, port); if (global_sockfd < 0) { - ERROR ("apcups plugin: Connecting to the " - "apcupsd failed."); + global_sockfd = net_open (host, port); + if (global_sockfd < 0) + { + ERROR ("apcups plugin: Connecting to the " + "apcupsd failed."); + return (-1); + } + } + + + status = net_send (&global_sockfd, "status", strlen ("status")); + if (status != 0) + { + /* net_send is closing the socket on error. */ + assert (global_sockfd < 0); + if (retry) + { + retry = 0; + count_retries++; + continue; + } + + ERROR ("apcups plugin: Writing to the socket failed."); return (-1); } - } - if (net_send (&global_sockfd, "status", 6) < 0) + break; + } /* while (retry) */ + + /* When collectd's collection interval is larger than apcupsd's + * timeout, we would have to retry / re-connect each iteration. Try to + * detect this situation and shut down the socket gracefully in that + * case. Otherwise, keep the socket open to avoid overhead. */ + count_iterations++; + if ((count_iterations == 10) && (count_retries > 2)) { - ERROR ("apcups plugin: Writing to the socket failed."); - return (-1); + NOTICE ("apcups plugin: There have been %i retries in the " + "first %i iterations. Will close the socket " + "in future iterations.", + count_retries, count_iterations); + close_socket = 1; } while ((n = net_recv (&global_sockfd, recvline, sizeof (recvline) - 1)) > 0) @@ -298,7 +347,7 @@ static int apc_query_server (char *host, int port, if (strcmp ("LINEV", key) == 0) apcups_detail->linev = value; - else if (strcmp ("BATTV", key) == 0) + else if (strcmp ("BATTV", key) == 0) apcups_detail->battv = value; else if (strcmp ("ITEMP", key) == 0) apcups_detail->itemp = value; @@ -316,10 +365,16 @@ static int apc_query_server (char *host, int port, tokptr = strtok_r (NULL, ":", &toksaveptr); } /* while (tokptr != NULL) */ } - + status = errno; /* save errno, net_shutdown() may re-set it. */ + + if (close_socket) + net_shutdown (&global_sockfd); + if (n < 0) { - WARNING ("apcups plugin: Error reading from socket"); + char errbuf[1024]; + ERROR ("apcups plugin: Reading from socket failed: %s", + sstrerror (status, errbuf, sizeof (errbuf))); return (-1); } @@ -398,12 +453,12 @@ static int apcups_read (void) apcups_detail.timeleft = -1.0; apcups_detail.itemp = -300.0; apcups_detail.linefreq = -1.0; - + status = apc_query_server (conf_host == NULL ? APCUPS_DEFAULT_HOST : conf_host, conf_port, &apcups_detail); - + /* * if we did not connect then do not bother submitting * zeros. We want rrd files to have NAN. diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c index 88a53023..e31d95ca 100644 --- a/src/collectd-nagios.c +++ b/src/collectd-nagios.c @@ -66,7 +66,7 @@ # endif #endif /* NAN_ZERO_ZERO */ -#include "libcollectdclient/client.h" +#include "libcollectdclient/collectd/client.h" #define RET_OKAY 0 #define RET_WARNING 1 diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod index d5401dd4..ee05e002 100644 --- a/src/collectd-perl.pod +++ b/src/collectd-perl.pod @@ -208,7 +208,7 @@ layout looks like this: { values => [123, 0.5], time => time (), - interval => $interval_g, + interval => plugin_get_interval (), host => $hostname_g, plugin => 'myplugin', type => 'myplugin', @@ -415,6 +415,13 @@ The message is passed to all log-callbacks that are registered with collectd. Wrappers around B, using B, B, B, B and B respectively as I. +=item B () + +Returns the interval of the current plugin as a floating point number in +seconds. This value depends on the interval configured within the +C block or the global interval (see L for +details). + =back The following function provides the filter chain C-interface to Perl-modules. @@ -494,6 +501,11 @@ B configuration options (see L for details). This variable keeps the interval in seconds in which the read functions are queried (see the B configuration option). +B This variable should no longer be used in favor of +C (see above). This function takes any plugin-specific +interval settings into account (see the C option of C in +L for details). + =back Any changes to these variables will be globally visible in collectd. diff --git a/src/collectd-python.pod b/src/collectd-python.pod index 5fd1f4f9..39503ad0 100644 --- a/src/collectd-python.pod +++ b/src/collectd-python.pod @@ -37,7 +37,7 @@ for collectd in Python. This is a lot more efficient than executing a Python-script every time you want to read a value with the C (see L) and provides a lot more functionality, too. -At least python I is required. +The minimum required Python version is I<2.3>. =head1 CONFIGURATION @@ -47,9 +47,9 @@ At least python I is required. Loads the Python plugin I. Unlike most other LoadPlugin lines, this one should be a block containing the line "Globals true". This will cause collectd -to export the name of all objects in the python interpreter for all plugins to +to export the name of all objects in the Python interpreter for all plugins to see. If you don't do this or your platform does not support it, the embedded -interpreter will start anyway but you won't be able to load certain python +interpreter will start anyway but you won't be able to load certain Python modules, e.g. "time". =item B I @@ -68,25 +68,25 @@ use multiple B lines to add more than one directory. =item B I -If a python script throws an exception it will be logged by collectd with the +If a Python script throws an exception it will be logged by collectd with the name of the exception and the message. If you set this option to true it will also log the full stacktrace just like the default output of an interactive -python interpreter. This should probably be set to false most of the time but +Python interpreter. This should probably be set to false most of the time but is very useful for development and debugging of new modules. =item B I -This option will cause the module to launch an interactive python interpreter +This option will cause the module to launch an interactive Python interpreter that reads from and writes to the terminal. Note that collectd will terminate right after starting up if you try to run it as a daemon while this option is -enabled to make sure to start collectd with the B<-f> option. +enabled so make sure to start collectd with the B<-f> option. The B module is I imported into the interpreter's globals. You have to do it manually. Be sure to read the help text of the module, it can be used as a reference guide during coding. This interactive session will behave slightly differently from a daemonized -collectd script as well as from a normal python interpreter: +collectd script as well as from a normal Python interpreter: =over 4 @@ -107,22 +107,22 @@ To quit collectd send I (press I at the beginning of a new line). =item -B<3.> collectd handles I. This means that python won't be able to +B<3.> collectd handles I. This means that Python won't be able to determine the return code of spawned processes with system(), popen() and -subprocess. This will result in python not using external programs like less +subprocess. This will result in Python not using external programs like less to display help texts. You can override this behavior with the B environment variable, e.g. I before starting collectd. -Depending on your version of python this might or might not result in an +Depending on your version of Python this might or might not result in an B exception which can be ignored. -If you really need to spawn new processes from python you can register an init +If you really need to spawn new processes from Python you can register an init callback and reset the action for SIGCHLD to the default behavior. Please note that this I break the exec plugin. Do not even load the exec plugin if you intend to do this! There is an example script located in B to do this. If you import this from I SIGCHLD will be handled -normally and spawning processes from python will work as intended. +normally and spawning processes from Python will work as intended. =back @@ -139,22 +139,22 @@ The I identifies the callback. =head1 STRINGS -There are a lot of places where strings are send from collectd to python and -from python to collectd. How exactly this works depends on wheather byte or -unicode strings or python2 or python3 are used. +There are a lot of places where strings are sent from collectd to Python and +from Python to collectd. How exactly this works depends on whether byte or +unicode strings or Python2 or Python3 are used. Python2 has I, which is just bytes, and I. Python3 has I, which is a unicode object, and I. -When passing strings from python to collectd all of these object are supported +When passing strings from Python to collectd all of these object are supported in all places, however I should be used if possible. These strings must not contain a NUL byte. Ignoring this will result in a I exception. If a byte string was used it will be used as is by collectd. If a unicode object was used it will be encoded using the default encoding (see above). If -this is not possible python will raise a I exception. +this is not possible Python will raise a I exception. -Wenn passing strings from collectd to python the behavior depends on the -python version used. Python2 will always receive a I object. Python3 will +When passing strings from collectd to Python the behavior depends on the +Python version used. Python2 will always receive a I object. Python3 will usually receive a I object as well, however the original string will be decoded to unicode using the default encoding. If this fails because the string is not a valid sequence for this encoding a I object will be @@ -174,7 +174,7 @@ example. The following types of B are known to collectd =item configuration functions -This type of functions is called during configuration if an appropriate +These are called during configuration if an appropriate B block has been encountered. It is called once for each B block which matches the name of the callback as provided with the B method - see below. @@ -184,14 +184,14 @@ threading functions here! =item init functions -This type of functions is called once after loading the module and before any +These are called once after loading the module and before any calls to the read and write functions. It should be used to initialize the internal state of the plugin (e.Eg. open sockets, ...). This is the earliest point where you may use threads. =item read functions -This type of function is used to collect the actual data. It is called once +These are used to collect the actual data. It is called once per interval (see the B configuration option of collectd). Usually it will call B to dispatch the values to collectd which will pass them on to all registered B. If this function @@ -200,23 +200,23 @@ amount of time until it returns normally again. =item write functions -This type of function is used to write the dispatched values. It is called +These are used to write the dispatched values. It is called once for every value that was dispatched by any plugin. =item flush functions -This type of function is used to flush internal caches of plugins. It is +These are used to flush internal caches of plugins. It is usually triggered by the user only. Any plugin which caches data before writing it to disk should provide this kind of callback function. =item log functions -This type of function is used to pass messages of plugins or the daemon itself +These are used to pass messages of plugins or the daemon itself to the user. =item notification function -This type of function is used to act upon notifications. In general, a +These are used to act upon notifications. In general, a notification is a status message that may be associated with a data instance. Usually, a notification is generated by the daemon if a configured threshold has been exceeded (see the section "THRESHOLD CONFIGURATION" in @@ -225,12 +225,12 @@ notifications as well. =item shutdown functions -This type of function is called once before the daemon shuts down. It should +These are called once before the daemon shuts down. It should be used to clean up the plugin (e.g. close sockets, ...). =back -Any function (except log functions) may set throw an exception in case of any +Any function (except log functions) may throw an exception in case of errors. The exception will be passed on to the user using collectd's logging mechanism. If a log callback throws an exception it will be printed to standard error instead. @@ -306,7 +306,7 @@ config file. It will always be a string. This is a tuple (which might be empty) of all value, i.e. words following the keyword in any given line in the config file. -Every item in this tuple will be either a string or a float or a boolean, +Every item in this tuple will be either a string, a float or a boolean, depending on the contents of the configuration file. =item children @@ -367,7 +367,7 @@ Type instance string. May be empty. =head2 Values -A Value is an object which features a sequence of values. It is based on then +A Value is an object which features a sequence of values. It is based on the I type and uses its members to identify the values. class Values(PluginData) @@ -495,7 +495,7 @@ Data descriptors defined here: =item message -Some kind of description what's going on and why this Notification was +Some kind of description of what's going on and why this Notification was generated. =item severity @@ -534,9 +534,12 @@ your callback, not even None. I is an optional identifier for this callback. The default name is B.I. I is taken from the B<__module__> attribute of your callback function. Every callback needs a unique identifier, so if you -want to register the same callback multiple time in the same module you need to -specify a name here. Otherwise it's save to ignore this parameter I -is the full identifier assigned to this callback. +want to register the same callback multiple times in the same module you need to +specify a name here. Otherwise it's safe to ignore this parameter. + +=item + +I is the full identifier assigned to this callback. =back @@ -549,7 +552,7 @@ L<"WRITING YOUR OWN PLUGINS"> above) and are passed the following arguments: The only argument passed is a I object. See above for the layout of this data type. -Note that you can not receive the whole config files this way, only B +Note that you cannot receive the whole config files this way, only B blocks inside the Python configuration block. Additionally you will only receive blocks where your callback identifier matches BI. @@ -570,7 +573,7 @@ The callback will be called without arguments. =item register_write -The callback function will be called with one arguments passed, which will be a +The callback function will be called with one argument passed, which will be a I object. For the layout of I see above. If this callback function throws an exception the next call will be delayed by an increasing interval. @@ -657,7 +660,7 @@ types used by the read, write and match functions. =item -Please feel free to send in new plugins to collectd's mailinglist at +Please feel free to send in new plugins to collectd's mailing list at EcollectdEatEverplant.orgE for review and, possibly, inclusion in the main distribution. In the latter case, we will take care of keeping the plugin up to date and adapting it to new versions of collectd. @@ -673,7 +676,7 @@ L. =item -collectd is heavily multi-threaded. Each collectd thread accessing the python +collectd is heavily multi-threaded. Each collectd thread accessing the Python plugin will be mapped to a Python interpreter thread. Any such thread will be created and destroyed transparently and on-the-fly. @@ -703,7 +706,7 @@ dispatched by the python plugin after upgrades. =item -Not all aspects of the collectd API are accessible from python. This includes +Not all aspects of the collectd API are accessible from Python. This includes but is not limited to filters and data sets. =back diff --git a/src/collectd-tg.c b/src/collectd-tg.c new file mode 100644 index 00000000..e5b2d1f1 --- /dev/null +++ b/src/collectd-tg.c @@ -0,0 +1,405 @@ +/** + * collectd-td - collectd traffic generator + * Copyright (C) 2010 Florian octo 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 + **/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE +#endif + +#ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200809L +#endif + +#ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 700 +#endif + +#if !__GNUC__ +# define __attribute__(x) /**/ +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "utils_heap.h" + +#include "libcollectdclient/collectd/client.h" +#include "libcollectdclient/collectd/network.h" +#include "libcollectdclient/collectd/network_buffer.h" + +#define DEF_NUM_HOSTS 1000 +#define DEF_NUM_PLUGINS 20 +#define DEF_NUM_VALUES 100000 +#define DEF_INTERVAL 10 + +static int conf_num_hosts = DEF_NUM_HOSTS; +static int conf_num_plugins = DEF_NUM_PLUGINS; +static int conf_num_values = DEF_NUM_VALUES; +static int conf_interval = DEF_INTERVAL; +static const char *conf_destination = NET_DEFAULT_V6_ADDR; +static const char *conf_service = NET_DEFAULT_PORT; + +static lcc_network_t *net; + +static c_heap_t *values_heap = NULL; + +static struct sigaction sigint_action; +static struct sigaction sigterm_action; + +static _Bool loop = 1; + +__attribute__((noreturn)) +static void exit_usage (int exit_status) /* {{{ */ +{ + fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout, + "collectd-tg -- collectd traffic generator\n" + "\n" + " Usage: collectd-ng [OPTION]\n" + "\n" + " Valid options:\n" + " -n Number of value lists. (Default: %i)\n" + " -H Number of hosts to emulate. (Default: %i)\n" + " -p Number of plugins to emulate. (Default: %i)\n" + " -i Interval of each value in seconds. (Default: %i)\n" + " -d Destination address of the network packets.\n" + " (Default: %s)\n" + " -D Destination port of the network packets.\n" + " (Default: %s)\n" + " -h Print usage information (this output).\n" + "\n" + "Copyright (C) 2010 Florian Forster\n" + "Licensed under the GNU General Public License, version 2 (GPLv2)\n", + DEF_NUM_VALUES, DEF_NUM_HOSTS, DEF_NUM_PLUGINS, + DEF_INTERVAL, + NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT); + exit (exit_status); +} /* }}} void exit_usage */ + +static void signal_handler (int signal) /* {{{ */ +{ + loop = 0; +} /* }}} void signal_handler */ + +static int compare_time (const void *v0, const void *v1) /* {{{ */ +{ + const lcc_value_list_t *vl0 = v0; + const lcc_value_list_t *vl1 = v1; + + if (vl0->time < vl1->time) + return (-1); + else if (vl0->time > vl1->time) + return (1); + else + return (0); +} /* }}} int compare_time */ + +static int get_boundet_random (int min, int max) /* {{{ */ +{ + int range; + + if (min >= max) + return (-1); + if (min == (max - 1)) + return (min); + + range = max - min; + + return (min + ((int) (((double) range) * ((double) random ()) / (((double) RAND_MAX) + 1.0)))); +} /* }}} int get_boundet_random */ + +static lcc_value_list_t *create_value_list (void) /* {{{ */ +{ + lcc_value_list_t *vl; + int host_num; + + vl = malloc (sizeof (*vl)); + if (vl == NULL) + { + fprintf (stderr, "malloc failed.\n"); + return (NULL); + } + memset (vl, 0, sizeof (*vl)); + + vl->values = calloc (/* nmemb = */ 1, sizeof (*vl->values)); + if (vl->values == NULL) + { + fprintf (stderr, "calloc failed.\n"); + free (vl); + return (NULL); + } + + vl->values_types = calloc (/* nmemb = */ 1, sizeof (*vl->values_types)); + if (vl->values_types == NULL) + { + fprintf (stderr, "calloc failed.\n"); + free (vl->values); + free (vl); + return (NULL); + } + + vl->values_len = 1; + + host_num = get_boundet_random (0, conf_num_hosts); + + vl->interval = conf_interval; + vl->time = time (NULL) + (host_num % vl->interval) + 1; + + if (get_boundet_random (0, 2) == 0) + vl->values_types[0] = LCC_TYPE_GAUGE; + else + vl->values_types[0] = LCC_TYPE_DERIVE; + + snprintf (vl->identifier.host, sizeof (vl->identifier.host), + "host%04i", host_num); + snprintf (vl->identifier.plugin, sizeof (vl->identifier.plugin), + "plugin%03i", get_boundet_random (0, conf_num_plugins)); + strncpy (vl->identifier.type, + (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive", + sizeof (vl->identifier.type)); + snprintf (vl->identifier.type_instance, sizeof (vl->identifier.type_instance), + "ti%li", random ()); + + return (vl); +} /* }}} int create_value_list */ + +static void destroy_value_list (lcc_value_list_t *vl) /* {{{ */ +{ + if (vl == NULL) + return; + + free (vl->values); + free (vl->values_types); + free (vl); +} /* }}} void destroy_value_list */ + +static int send_value (lcc_value_list_t *vl) /* {{{ */ +{ + int status; + + if (vl->values_types[0] == LCC_TYPE_GAUGE) + vl->values[0].gauge = 100.0 * ((gauge_t) random ()) / (((gauge_t) RAND_MAX) + 1.0); + else + vl->values[0].derive += get_boundet_random (0, 100); + + status = lcc_network_values_send (net, vl); + if (status != 0) + fprintf (stderr, "lcc_network_values_send failed with status %i.\n", status); + + vl->time += vl->interval; + + return (0); +} /* }}} int send_value */ + +static int get_integer_opt (const char *str, int *ret_value) /* {{{ */ +{ + char *endptr; + int tmp; + + errno = 0; + endptr = NULL; + tmp = (int) strtol (str, &endptr, /* base = */ 0); + if (errno != 0) + { + fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n", + str, strerror (errno)); + exit (EXIT_FAILURE); + } + else if (endptr == str) + { + fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str); + exit (EXIT_FAILURE); + } + else if (*endptr != 0) + { + fprintf (stderr, "Garbage after end of value: \"%s\"\n", str); + exit (EXIT_FAILURE); + } + + *ret_value = tmp; + return (0); +} /* }}} int get_integer_opt */ + +static int read_options (int argc, char **argv) /* {{{ */ +{ + int opt; + + while ((opt = getopt (argc, argv, "n:H:p:i:d:D:h")) != -1) + { + switch (opt) + { + case 'n': + get_integer_opt (optarg, &conf_num_values); + break; + + case 'H': + get_integer_opt (optarg, &conf_num_hosts); + break; + + case 'p': + get_integer_opt (optarg, &conf_num_plugins); + break; + + case 'i': + get_integer_opt (optarg, &conf_interval); + break; + + case 'd': + conf_destination = optarg; + break; + + case 'D': + conf_service = optarg; + break; + + case 'h': + exit_usage (EXIT_SUCCESS); + + default: + exit_usage (EXIT_FAILURE); + } /* switch (opt) */ + } /* while (getopt) */ + + return (0); +} /* }}} int read_options */ + +int main (int argc, char **argv) /* {{{ */ +{ + int i; + time_t last_time; + int values_sent = 0; + + read_options (argc, argv); + + sigint_action.sa_handler = signal_handler; + sigaction (SIGINT, &sigint_action, /* old = */ NULL); + + sigterm_action.sa_handler = signal_handler; + sigaction (SIGTERM, &sigterm_action, /* old = */ NULL); + + + values_heap = c_heap_create (compare_time); + if (values_heap == NULL) + { + fprintf (stderr, "c_heap_create failed.\n"); + exit (EXIT_FAILURE); + } + + net = lcc_network_create (); + if (net == NULL) + { + fprintf (stderr, "lcc_network_create failed.\n"); + exit (EXIT_FAILURE); + } + else + { + lcc_server_t *srv; + + srv = lcc_server_create (net, conf_destination, conf_service); + if (srv == NULL) + { + fprintf (stderr, "lcc_server_create failed.\n"); + exit (EXIT_FAILURE); + } + + lcc_server_set_ttl (srv, 42); +#if 0 + lcc_server_set_security_level (srv, ENCRYPT, + "admin", "password1"); +#endif + } + + fprintf (stdout, "Creating %i values ... ", conf_num_values); + fflush (stdout); + for (i = 0; i < conf_num_values; i++) + { + lcc_value_list_t *vl; + + vl = create_value_list (); + if (vl == NULL) + { + fprintf (stderr, "create_value_list failed.\n"); + exit (EXIT_FAILURE); + } + + c_heap_insert (values_heap, vl); + } + fprintf (stdout, "done\n"); + + last_time = 0; + while (loop) + { + lcc_value_list_t *vl = c_heap_get_root (values_heap); + + if (vl == NULL) + break; + + if (vl->time != last_time) + { + printf ("%i values have been sent.\n", values_sent); + + /* Check if we need to sleep */ + time_t now = time (NULL); + + while (now < vl->time) + { + /* 1 / 100 second */ + struct timespec ts = { 0, 10000000 }; + nanosleep (&ts, /* remaining = */ NULL); + now = time (NULL); + + if (!loop) + break; + } + last_time = vl->time; + } + + send_value (vl); + values_sent++; + + c_heap_insert (values_heap, vl); + } + + fprintf (stdout, "Shutting down.\n"); + fflush (stdout); + + while (42) + { + lcc_value_list_t *vl = c_heap_get_root (values_heap); + if (vl == NULL) + break; + destroy_value_list (vl); + } + c_heap_destroy (values_heap); + + lcc_network_destroy (net); + exit (EXIT_SUCCESS); + return (0); +} /* }}} int main */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/collectd.c b/src/collectd.c index d33d1d66..d96d6767 100644 --- a/src/collectd.c +++ b/src/collectd.c @@ -79,6 +79,7 @@ static void sig_usr1_handler (int __attribute__((unused)) signal) pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); pthread_create (&thread, &attr, do_flush, NULL); + pthread_attr_destroy (&attr); } static int init_hostname (void) @@ -137,28 +138,10 @@ static int init_hostname (void) static int init_global_variables (void) { - const char *str; - - str = global_option_get ("Interval"); - if (str == NULL) - { - interval_g = TIME_T_TO_CDTIME_T (10); - } - else - { - double tmp; + char const *str; - tmp = atof (str); - if (tmp <= 0.0) - { - fprintf (stderr, "Cannot set the interval to a " - "correct value.\n" - "Please check your settings.\n"); - return (-1); - } - - interval_g = DOUBLE_TO_CDTIME_T (tmp); - } + interval_g = cf_get_default_interval (); + assert (interval_g > 0); DEBUG ("interval_g = %.3f;", CDTIME_T_TO_DOUBLE (interval_g)); str = global_option_get ("Timeout"); @@ -182,10 +165,11 @@ static int init_global_variables (void) static int change_basedir (const char *orig_dir) { - char *dir = strdup (orig_dir); - int dirlen; + char *dir; + size_t dirlen; int status; + dir = strdup (orig_dir); if (dir == NULL) { char errbuf[1024]; @@ -202,39 +186,41 @@ static int change_basedir (const char *orig_dir) return (-1); status = chdir (dir); - free (dir); + if (status == 0) + { + free (dir); + return (0); + } + else if (errno != ENOENT) + { + char errbuf[1024]; + ERROR ("change_basedir: chdir (%s): %s", dir, + sstrerror (errno, errbuf, sizeof (errbuf))); + free (dir); + return (-1); + } + status = mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO); if (status != 0) { - if (errno == ENOENT) - { - if (mkdir (orig_dir, 0755) == -1) - { - char errbuf[1024]; - ERROR ("change_basedir: mkdir (%s): %s", orig_dir, - sstrerror (errno, errbuf, - sizeof (errbuf))); - return (-1); - } - else if (chdir (orig_dir) == -1) - { - char errbuf[1024]; - ERROR ("chdir (%s): %s", orig_dir, - sstrerror (errno, errbuf, - sizeof (errbuf))); - return (-1); - } - } - else - { - char errbuf[1024]; - ERROR ("chdir (%s): %s", orig_dir, - sstrerror (errno, errbuf, - sizeof (errbuf))); - return (-1); - } + char errbuf[1024]; + ERROR ("change_basedir: mkdir (%s): %s", dir, + sstrerror (errno, errbuf, sizeof (errbuf))); + free (dir); + return (-1); } + status = chdir (dir); + if (status != 0) + { + char errbuf[1024]; + ERROR ("change_basedir: chdir (%s): %s", dir, + sstrerror (errno, errbuf, sizeof (errbuf))); + free (dir); + return (-1); + } + + free (dir); return (0); } /* static int change_basedir (char *dir) */ @@ -323,9 +309,10 @@ static int do_init (void) static int do_loop (void) { + cdtime_t interval = cf_get_default_interval (); cdtime_t wait_until; - wait_until = cdtime () + interval_g; + wait_until = cdtime () + interval; while (loop == 0) { @@ -345,12 +332,12 @@ static int do_loop (void) WARNING ("Not sleeping because the next interval is " "%.3f seconds in the past!", CDTIME_T_TO_DOUBLE (now - wait_until)); - wait_until = now + interval_g; + wait_until = now + interval; continue; } CDTIME_T_TO_TIMESPEC (wait_until - now, &ts_wait); - wait_until = wait_until + interval_g; + wait_until = wait_until + interval; while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) != 0)) { @@ -468,6 +455,8 @@ int main (int argc, char **argv) if (optind < argc) exit_usage (1); + plugin_init_ctx (); + /* * Read options from the config file, the environment and the command * line (in that order, with later options overwriting previous ones in diff --git a/src/collectd.conf.in b/src/collectd.conf.in index f3ef6759..9f5dedcd 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -52,6 +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 @@ -161,6 +162,26 @@ # ription of those options is available in the collectd.conf(5) manual page. # ############################################################################## +# +# +# #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 +# +# + # # # Host "localhost" @@ -465,8 +486,10 @@ # # -# Host "127.0.0.1" -# Port "11211" +# +# Host "127.0.0.1" +# Port "11211" +# # # @@ -626,6 +649,7 @@ # Host "localhost" # Port 123 # ReverseLookups false +# IncludeUnitID true # # @@ -867,6 +891,7 @@ # # ReportByDevice false +# ReportBytes true # # diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 5eb3af47..15b6f680 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -25,22 +25,32 @@ controls which plugins to load. These plugins ultimately define collectd's behavior. The syntax of this config file is similar to the config file of the famous -B. 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.Ee. either B or -B. 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.Ee. 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 option B occur -B the CPlugin ...E> block. +I 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 are unquoted +strings, consisting only of alphanumeric characters and the underscore (C<_>) +character. Keys are handled case insensitive by I itself and all +plugins included with it. I can either be an I, a +I (enclosed in double-quotes) a I or a I +expression. I consist of only alphanumeric characters and +underscores (C<_>) and do not need to be quoted. I are +enclosed in double quotes (C<">). You can use the backslash character (C<\>) +to include double quotes as part of the string. I 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 values are either B or B. + +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 option B occur B +the appropriate CPlugin ...E> block. =head1 GLOBAL OPTIONS @@ -86,6 +96,12 @@ By default, this is disabled. As a special exception, if the plugin name is either C or C, the default is changed to enabled in order to keep the average user from ever having to deal with this low level linking stuff. +=item B I + +Sets a plugin-specific interval for collecting metrics. This overrides the +global B setting. If a plugin provides own support for specifying an +interval, that setting will take precedence. + =back =item B I @@ -183,12 +199,122 @@ C-Section. Which options exist depends on the plugin used. Some plugins require external configuration, too. The C, for example, required C 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 file shipped with the sourcecode and hopefully binary packets as well. +=head2 Plugin C + +The I makes it possible to aggregate several values into +one using aggregation functions such as I, I, I and I. +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 and +C. The other values are left unspecified, meaning "all values". The +I, I, I, I and I options +work as if they were specified in the C clause of an C