- filecount
Count the number of files in directories.
+ - gmond
+ Receive multicast traffic from Ganglia instances.
+
- hddtemp
Harddisk temperatures using hddtempd.
that the compiled binary actually behaves as it should, but since NANs
are likely never passed to the libm you have a good chance to be lucky.
+ Likewise, collectd needs to know the layout of doubles in memory, in order
+ to craft uniform network packets over different architectures. For this, it
+ needs to know how to convert doubles into the memory layout used by x86. The
+ configure script tries to figure this out by compiling and running a few
+ small test programs. This is of course not possible when cross-compiling.
+ You can use the `--with-fp-layout' option to tell the configure script which
+ conversion method to assume. Valid arguments are:
+
+ * `nothing' (12345678 -> 12345678)
+ * `endianflip' (12345678 -> 87654321)
+ * `intswap' (12345678 -> 56781234)
+
Contact
-------
- For questions, bugreports, development information and basically all other
- concerns please send an email to collectd's mailinglist at
+ For questions, bug reports, development information and basically all other
+ concerns please send an email to collectd's mailing list at
<collectd at verplant.org>.
For live discussion and more personal contact visit us in IRC, we're in
Sebastian tokkee Harl <sh at tokkee.org>,
and many contributors (see `AUTHORS').
- Please send bugreports and patches to the mailinglist, see `Contact' above.
+ Please send bug reports and patches to the mailing list, see `Contact'
+ above.
AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes")
# }}}
+# --with-libganglia {{{
+AC_ARG_WITH(libganglia, [AS_HELP_STRING([--with-libganglia@<:@=PREFIX@:>@], [Path to libganglia.])],
+[
+ if test -f "$withval" && test -x "$withval"
+ then
+ with_libganglia_config="$withval"
+ with_libganglia="yes"
+ else if test -f "$withval/bin/ganglia-config" && test -x "$withval/bin/ganglia-config"
+ then
+ with_libganglia_config="$withval/bin/ganglia-config"
+ with_libganglia="yes"
+ else if test -d "$withval"
+ then
+ GANGLIA_CPPFLAGS="-I$withval/include"
+ GANGLIA_LDFLAGS="-L$withval/lib"
+ with_libganglia="yes"
+ else
+ with_libganglia_config="ganglia-config"
+ with_libganglia="$withval"
+ fi; fi; fi
+],
+[
+ with_libganglia_config="ganglia-config"
+ with_libganglia="yes"
+])
+
+if test "x$with_libganglia" = "xyes" && test "x$with_libganglia_config" != "x"
+then
+ if test "x$GANGLIA_CPPFLAGS" = "x"
+ then
+ GANGLIA_CPPFLAGS=`"$with_libganglia_config" --cflags 2>/dev/null`
+ fi
+
+ if test "x$GANGLIA_LDFLAGS" = "x"
+ then
+ GANGLIA_LDFLAGS=`"$with_libganglia_config" --ldflags 2>/dev/null`
+ fi
+
+ if test "x$GANGLIA_LIBS" = "x"
+ then
+ GANGLIA_LIBS=`"$with_libganglia_config" --libs 2>/dev/null`
+ fi
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $GANGLIA_CPPFLAGS"
+LDFLAGS="$LDFLAGS $GANGLIA_LDFLAGS"
+
+if test "x$with_libganglia" = "xyes"
+then
+ AC_CHECK_HEADERS(gm_protocol.h,
+ [
+ AC_DEFINE(HAVE_GM_PROTOCOL_H, 1,
+ [Define to 1 if you have the <gm_protocol.h> header file.])
+ ], [with_libganglia="no (gm_protocol.h not found)"])
+fi
+
+if test "x$with_libganglia" = "xyes"
+then
+ AC_CHECK_LIB(ganglia, xdr_Ganglia_value_msg,
+ [
+ AC_DEFINE(HAVE_LIBGANGLIA, 1,
+ [Define to 1 if you have the ganglia library (-lganglia).])
+ ], [with_libganglia="no (symbol xdr_Ganglia_value_msg not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+AC_SUBST(GANGLIA_CPPFLAGS)
+AC_SUBST(GANGLIA_LDFLAGS)
+AC_SUBST(GANGLIA_LIBS)
+AM_CONDITIONAL(BUILD_WITH_LIBGANGLIA, test "x$with_libganglia" = "xyes")
+# }}}
+
# --with-libiptc {{{
with_own_libiptc="no"
AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics])
AC_PLUGIN([exec], [yes], [Execution of external programs])
AC_PLUGIN([filecount], [yes], [Count files in directories])
+AC_PLUGIN([gmond], [$with_libganglia], [Ganglia plugin])
AC_PLUGIN([hddtemp], [yes], [Query hddtempd])
AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics])
AC_PLUGIN([ipmi], [$plugin_ipmi], [IPMI sensor statistics])
entropy . . . . . . . $enable_entropy
exec . . . . . . . . $enable_exec
filecount . . . . . . $enable_filecount
+ gmond . . . . . . . . $enable_gmond
hddtemp . . . . . . . $enable_hddtemp
interface . . . . . . $enable_interface
ipmi . . . . . . . . $enable_ipmi
--- /dev/null
+Options +ExecCGI
+AddHandler cgi-script .cgi
our $Begin = param ('begin');
our $End = param ('end');
our $GraphWidth = param ('width');
+our $GraphHeight = param ('height');
+our $Index = param ('index') || 0;
our $OutputFormat = 'PNG';
our $ContentType = 'image/png';
$GraphWidth = gc_get_scalar ('GraphWidth', 400);
}
+if ($GraphHeight)
+{
+ $GraphHeight =~ s/\D//g;
+}
+
+if (!$GraphHeight)
+{
+ $GraphHeight = gc_get_scalar ('GraphHeight', 100);
+}
+
{ # Sanitize begin and end times
$End ||= 0;
$Begin ||= 0;
print "\$expires = $expires;\n";
}
-my $args = $obj->getRRDArgs (0);
+my $args = $obj->getRRDArgs (0 + $Index);
if ($Debug)
{
}
$| = 1;
- RRDs::graph ('-', '-a', $OutputFormat, '--width', $GraphWidth, @timesel, @$args);
+ RRDs::graph ('-', '-a', $OutputFormat, '--width', $GraphWidth, '--height', $GraphHeight, @timesel, @$args);
if (my $err = RRDs::error ())
{
print STDERR "RRDs::graph failed: $err\n";
use lib ('../lib');
use utf8;
+use Carp (qw(cluck confess));
use FindBin ('$RealBin');
use CGI (':cgi');
use CGI::Carp ('fatalsToBrowser');
<title>collection.cgi, Version 3</title>
<link rel="icon" href="../share/shortcut-icon.png" type="image/png" />
<link rel="stylesheet" href="../share/style.css" type="text/css" />
- <script type="text/javascript" src="../share/navigate.js" />
+ <script type="text/javascript" src="../share/navigate.js"></script>
</head>
<body onload="nav_init ($begin, $end);">
HTML
use CGI (':cgi');
use CGI::Carp ('fatalsToBrowser');
use URI::Escape ('uri_escape');
+use JSON ('objToJson');
use Data::Dumper;
use Collectd::Graph::Type ();
our $Debug = param ('debug') ? 1 : 0;
+our $ServerName = 'collect.noris.net';
gc_read_config ("$RealBin/../etc/collection.conf");
print "Content-Type: application/json; charset=utf-8\n\n";
}
-print "{\n";
-
+my $obj = {};
my @hosts = get_all_hosts ();
for (my $i = 0; $i < @hosts; $i++)
{
+ my $host_obj = {};
my $host = $hosts[$i];
my $files = get_files_for_host ($host);
my %graphs = ();
$graphs{$type}->addFiles ($file);
} # for (@$files)
- print qq( "$host":\n {\n);
+ #print qq( ") . objToJson ({ foo => 123 }) . qq(":\n {\n);
@graphs = keys %graphs;
for (my $j = 0; $j < @graphs; $j++)
{
my $type = $graphs[$j];
my $graphs_num = $graphs{$type}->getGraphsNum ();
- my @args = ();
- for (my $k = 0; $k < $graphs_num; $k++)
+ if (!defined ($host_obj->{$type}))
{
- my $args = $graphs{$type}->getGraphArgs ($k);
- my $url = 'http://' . $ENV{'SERVER_NAME'} . "/cgi-bin/graph.cgi?" . $args;
- push (@args, $url);
+ $host_obj->{$type} = [];
}
- print qq( "$type": [ )
- . join (', ', map { qq("$_") } (@args));
-
- if ($j == (@graphs - 1))
- {
- print qq( ]\n);
- }
- else
+ for (my $k = 0; $k < $graphs_num; $k++)
{
- print qq( ],\n);
+ my $args = $graphs{$type}->getGraphArgs ($k);
+ my $url = "http://$ServerName/cgi-bin/collection3/bin/graph.cgi?" . $args;
+ push (@{$host_obj->{$type}}, $url);
}
} # for (keys %graphs)
- if ($i == (@hosts - 1))
- {
- print qq( }\n);
- }
- else
- {
- print qq( },\n\n);
- }
+ $obj->{$host} = $host_obj;
} # for (my $i = 0; $i < @hosts; $i++)
-print "}\n";
+print STDOUT objToJson ($obj, { pretty => 1, indent => 2 });
exit (0);
+#DataDir "/var/lib/collectd/rrd"
GraphWidth 400
#UnixSockAddr "/var/run/collectd-unixsock"
<Type apache_bytes>
RRDFormat "%5.1lf%ss"
Scale 0.001
</Type>
+<Type dns_opcode>
+ DataSources value
+ DSName "value Queries/s"
+ RRDTitle "DNS Opcode {type_instance}"
+ RRDVerticalLabel "Queries/s"
+ RRDFormat "%6.1lf"
+</Type>
<Type entropy>
DataSources entropy
DSName entropy Entropy bits
DataSources rx tx
DSName rx RX
DSName tx TX
- RRDTitle "Interface Traffic ({type_instance})"
+ RRDTitle "Interface Traffic ({instance})"
RRDVerticalLabel "Bits per second"
# RRDOptions ...
RRDFormat "%5.1lf%s"
# RRDOptions ...
RRDFormat "%5.1lf%s"
</Type>
+<Type io_octets>
+ Module GenericIO
+ DataSources rx tx
+ DSName "rx Read "
+ DSName "tx Written"
+ RRDTitle "IO Traffic ({instance})"
+ RRDVerticalLabel "Bytes per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf%s"
+</Type>
<Type ipt_bytes>
DataSources value
DSName value Bytes/s
DSName "free Free "
DSName "cached Cached "
DSName "buffered Buffered"
+ DSName "locked Locked "
DSName "used Used "
#Order used buffered cached free
Order free cached buffered used
Color free 00e000
Color cached 0000ff
Color buffered ffb000
+ Color locked ff00ff
Color used ff0000
</Type>
<Type mysql_commands>
RRDVerticalLabel "Invocations"
RRDFormat "%6.2lf"
+
DSName admin_commands admin_commands
DSName alter_table alter_table
DSName begin begin
RRDFormat "%6.2lf%sW"
Color value 008080
</Type>
+<Type ps_cputime>
+ Module PsCputime
+</Type>
<Type ps_rss>
DataSources value
DSName value RSS
Color cached 0000ff
Color used ff0000
</Type>
+<Type table_size>
+ Module TableSize
+ DataSources value
+ DSName value Bytes
+ RRDTitle "Table size ({instance})"
+ RRDVerticalLabel "Size [Bytes]"
+# RRDOptions ...
+ RRDFormat "%5.1lf%s"
+</Type>
<Type tcp_connections>
Module GenericStacked
DataSources value
RRDFormat "%4.1lfV"
Color value f00000
</Type>
+<Type wirkleistung>
+ Module Wirkleistung
+ DataSources kWh
+ DSName value Wh
+ RRDTitle "Watt"
+ RRDVerticalLabel "W"
+ RRDFormat "%4.1lfW"
+</Type>
# vim: set sw=2 sts=2 et syntax=apache fileencoding=latin-1 :
use vars (qw($ColorCanvas $ColorFullBlue $ColorHalfBlue));
-use Collectd::Unixsock;
-
+use Collectd::Unixsock ();
use Carp (qw(confess cluck));
use CGI (':cgi');
use Exporter;
+use Collectd::Graph::Config (qw(gc_get_scalar));
$ColorCanvas = 'FFFFFF';
$ColorFullBlue = '0000FF';
get_timespan_selection
get_host_selection
get_plugin_selection
+ get_random_color
get_faded_color
sort_idents_by_type_instance
type_to_module_name
flush_files
));
-our $DataDir = '/var/lib/collectd/rrd';
+our $DefaultDataDir = '/var/lib/collectd/rrd';
return (1);
sub ident_to_filename
{
my $ident = shift;
+ my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
my $ret = '';
}
else
{
- $ret .= "$DataDir/";
+ $ret .= "$data_dir/";
}
if (!$ident->{'hostname'})
{
my $dh;
my @ret = ();
+ my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
- opendir ($dh, "$DataDir") or confess ("opendir ($DataDir): $!");
+ opendir ($dh, "$data_dir") or confess ("opendir ($data_dir): $!");
while (my $entry = readdir ($dh))
{
next if ($entry =~ m/^\./);
- next if (!-d "$DataDir/$entry");
+ next if (!-d "$data_dir/$entry");
push (@ret, sanitize_hostname ($entry));
}
closedir ($dh);
my @hosts = @_;
my $ret = {};
my $dh;
+ my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
if (!@hosts)
{
for (@hosts)
{
my $host = $_;
- opendir ($dh, "$DataDir/$host") or next;
+ opendir ($dh, "$data_dir/$host") or next;
while (my $entry = readdir ($dh))
{
my $plugin;
my $plugin_instance = '';
next if ($entry =~ m/^\./);
- next if (!-d "$DataDir/$host/$entry");
+ next if (!-d "$data_dir/$host/$entry");
if ($entry =~ m#^([^-]+)-(.+)$#)
{
sub get_files_for_host
{
my $host = sanitize_hostname (shift);
- return (get_files_from_directory ("$DataDir/$host", 2));
+ my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+ return (get_files_from_directory ("$data_dir/$host", 2));
} # get_files_for_host
sub _filter_ident
my $ident = shift;
my $all_files;
my @ret = ();
+ my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
#if ($ident->{'hostname'})
#{
#}
#else
#{
- $all_files = get_files_from_directory ($DataDir, 3);
+ $all_files = get_files_from_directory ($data_dir, 3);
#}
@ret = grep { _filter_ident ($ident, $_) == 0 } (@$all_files);
return (sprintf ('%02hx%02hx%02hx', map { int (255.0 * $_) } @{$_[0]}));
} # _color_to_string
+sub get_random_color
+{
+ my ($r, $g, $b) = (rand (), rand ());
+ my $min = 0.0;
+ my $max = 1.0;
+
+ if (($r + $g) < 1.0)
+ {
+ $min = 1.0 - ($r + $g);
+ }
+ else
+ {
+ $max = 2.0 - ($r + $g);
+ }
+
+ $b = $min + (rand () * ($max - $min));
+
+ return (_color_to_string ([$r, $g, $b]));
+} # get_random_color
+
sub get_faded_color
{
my $fg = shift;
=cut
-# Copyright (C) 2008 Florian octo Forster <octo at verplant.org>
+# Copyright (C) 2008,2009 Florian octo Forster <octo at verplant.org>
#
# 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
use Carp (qw(cluck confess));
use Exporter ();
use Config::General ('ParseConfig');
-use Collectd::Graph::Type ();
@Collectd::Graph::Config::ISA = ('Exporter');
@Collectd::Graph::Config::EXPORT_OK = (qw(gc_read_config gc_get_config
package Collectd::Graph::Type::Df;
+# Copyright (C) 2008,2009 Florian octo Forster <octo at verplant.org>
+#
+# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
use strict;
use warnings;
use base ('Collectd::Graph::Type');
my $faded_green = get_faded_color ('00ff00');
my $faded_red = get_faded_color ('ff0000');
- return (['-t', 'Free space (' . $ident->{'type_instance'} . ')', '-v', 'Bytes', '-l', '0',
+ return (['-t', 'Diskspace (' . $ident->{'type_instance'} . ')', '-v', 'Bytes', '-l', '0',
"DEF:free_min=${filename}:free:MIN",
"DEF:free_avg=${filename}:free:AVERAGE",
"DEF:free_max=${filename}:free:MAX",
package Collectd::Graph::Type::GenericIO;
+# Copyright (C) 2008,2009 Florian octo Forster <octo at verplant.org>
+#
+# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
use strict;
use warnings;
use base ('Collectd::Graph::Type');
}
push (@ret,
+ "CDEF:avg_rx_bytes=avg_rx,8,/",
+ "VDEF:global_min_rx=min_rx,MINIMUM",
+ "VDEF:global_avg_rx=avg_rx,AVERAGE",
+ "VDEF:global_max_rx=max_rx,MAXIMUM",
+ "VDEF:global_tot_rx=avg_rx_bytes,TOTAL",
+ "CDEF:avg_tx_bytes=avg_tx,8,/",
+ "VDEF:global_min_tx=min_tx,MINIMUM",
+ "VDEF:global_avg_tx=avg_tx,AVERAGE",
+ "VDEF:global_max_tx=max_tx,MAXIMUM",
+ "VDEF:global_tot_tx=avg_tx_bytes,TOTAL");
+
+ push (@ret,
"CDEF:overlap=avg_rx,avg_tx,LT,avg_rx,avg_tx,IF",
"AREA:avg_rx#${rx_color_bg}",
"AREA:avg_tx#${tx_color_bg}",
"AREA:overlap#${overlap_color}",
"LINE1:avg_rx#${rx_color_fg}:${rx_ds_name}",
- "GPRINT:min_rx:MIN:${format} Min,",
- "GPRINT:avg_rx:AVERAGE:${format} Avg,",
- "GPRINT:max_rx:MAX:${format} Max,",
- "GPRINT:avg_rx:LAST:${format} Last\\l",
+ "GPRINT:global_min_rx:${format} Min,",
+ "GPRINT:global_avg_rx:${format} Avg,",
+ "GPRINT:global_max_rx:${format} Max,",
+ "GPRINT:global_tot_rx:ca. ${format} Total\\l",
"LINE1:avg_tx#${tx_color_fg}:${tx_ds_name}",
- "GPRINT:min_tx:MIN:${format} Min,",
- "GPRINT:avg_tx:AVERAGE:${format} Avg,",
- "GPRINT:max_tx:MAX:${format} Max,",
- "GPRINT:avg_tx:LAST:${format} Last\\l");
+ "GPRINT:global_min_tx:${format} Min,",
+ "GPRINT:global_avg_tx:${format} Avg,",
+ "GPRINT:global_max_tx:${format} Max,",
+ "GPRINT:global_tot_tx:ca. ${format} Total\\l");
return (\@ret);
} # getRRDArgs
package Collectd::Graph::Type::GenericStacked;
+# Copyright (C) 2008,2009 Florian octo Forster <octo at verplant.org>
+#
+# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
use strict;
use warnings;
use base ('Collectd::Graph::Type');
my $colors = $obj->{'rrd_colors'} || {};
my @ret = ('-t', $rrd_title, @$rrd_opts);
+ my $ignore_unknown = $obj->{'ignore_unknown'} || 0;
+ if ($ignore_unknown)
+ {
+ if ($ignore_unknown =~ m/^(yes|true|on)$/i)
+ {
+ $ignore_unknown = 1;
+ }
+ else
+ {
+ $ignore_unknown = 0;
+ }
+ }
+
if (defined $obj->{'rrd_vertical'})
{
push (@ret, '-v', $obj->{'rrd_vertical'});
sort_idents_by_type_instance ($idents, $obj->{'custom_order'});
}
+ if ($ignore_unknown)
+ {
+ my $new_idents = [];
+ for (@$idents)
+ {
+ if (exists ($obj->{'ds_names'}{$_->{'type_instance'}}))
+ {
+ push (@$new_idents, $_);
+ }
+ }
+
+ if (@$new_idents)
+ {
+ $idents = $new_idents;
+ }
+ }
+
$obj->{'ds_names'} ||= {};
my @names = map { $obj->{'ds_names'}{$_->{'type_instance'}} || $_->{'type_instance'} } (@$idents);
if ($i == (@$idents - 1))
{
push (@ret,
- "CDEF:cdef${i}=avg${i}");
+ "CDEF:cdef${i}=avg${i},UN,0,avg${i},IF");
}
else
{
my $j = $i + 1;
push (@ret,
- "CDEF:cdef${i}=cdef${j},avg${i},+");
+ "CDEF:cdef${i}=avg${i},UN,0,avg${i},IF,cdef${j},+");
}
}
--- /dev/null
+package Collectd::Graph::Type::PsCputime;
+
+# Copyright (C) 2008,2009 Florian octo Forster <octo at verplant.org>
+#
+# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+use strict;
+use warnings;
+use base ('Collectd::Graph::Type');
+
+use Collectd::Graph::Common (qw(ident_to_filename get_faded_color));
+
+return (1);
+
+sub getDataSources
+{
+ return ([qw(syst user)]);
+} # getDataSources
+
+sub new
+{
+ my $pkg = shift;
+ my $obj = Collectd::Graph::Type->new (@_);
+ $obj->{'data_sources'} = [qw(syst user)];
+ $obj->{'rrd_opts'} = ['-v', 'CPU time [s]', '-l', '0'];
+ $obj->{'rrd_title'} = 'CPU time used by {plugin_instance}';
+ $obj->{'rrd_format'} = '%5.1lf';
+ $obj->{'colors'} = [qw(00b000 ff0000)];
+
+ return (bless ($obj, $pkg));
+} # new
+
+sub getRRDArgs
+{
+ my $obj = shift;
+ my $index = shift;
+
+ my $ident = $obj->{'files'}[$index];
+ if (!$ident)
+ {
+ cluck ("Invalid index: $index");
+ return;
+ }
+ my $filename = ident_to_filename ($ident);
+ $filename =~ s#:#\\:#g;
+
+ my $faded_blue = get_faded_color ('0000ff');
+ my $faded_red = get_faded_color ('ff0000');
+
+ return (['-t', $obj->getTitle (), @{$obj->{'rrd_opts'}},
+ "DEF:user_min_raw=${filename}:user:MIN",
+ "DEF:user_avg_raw=${filename}:user:AVERAGE",
+ "DEF:user_max_raw=${filename}:user:MAX",
+ "DEF:syst_min_raw=${filename}:syst:MIN",
+ "DEF:syst_avg_raw=${filename}:syst:AVERAGE",
+ "DEF:syst_max_raw=${filename}:syst:MAX",
+ "CDEF:user_min=0.000001,user_min_raw,*",
+ "CDEF:user_avg=0.000001,user_avg_raw,*",
+ "CDEF:user_max=0.000001,user_max_raw,*",
+ "CDEF:syst_min=0.000001,syst_min_raw,*",
+ "CDEF:syst_avg=0.000001,syst_avg_raw,*",
+ "CDEF:syst_max=0.000001,syst_max_raw,*",
+ "VDEF:v_user_min=user_min,MINIMUM",
+ "VDEF:v_user_avg=user_avg,AVERAGE",
+ "VDEF:v_user_max=user_max,MAXIMUM",
+ "VDEF:v_user_lst=syst_avg,LAST",
+ "VDEF:v_syst_min=syst_min,MINIMUM",
+ "VDEF:v_syst_avg=syst_avg,AVERAGE",
+ "VDEF:v_syst_max=syst_max,MAXIMUM",
+ "VDEF:v_syst_lst=syst_avg,LAST",
+ "CDEF:user_stack=syst_avg,user_avg,+",
+ "AREA:user_stack#${faded_blue}",
+ 'LINE1:user_stack#0000ff:User ',
+ 'GPRINT:v_user_min:%5.1lf%ss Min,',
+ 'GPRINT:v_user_avg:%5.1lf%ss Avg,',
+ 'GPRINT:v_user_max:%5.1lf%ss Max,',
+ 'GPRINT:v_user_lst:%5.1lf%ss Last\l',
+ "AREA:syst_avg#${faded_red}",
+ 'LINE1:syst_avg#ff0000:System',
+ 'GPRINT:v_syst_min:%5.1lf%ss Min,',
+ 'GPRINT:v_syst_avg:%5.1lf%ss Avg,',
+ 'GPRINT:v_syst_max:%5.1lf%ss Max,',
+ 'GPRINT:v_syst_lst:%5.1lf%ss Last\l']);
+} # getRRDArgs
+
+# vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
--- /dev/null
+package Collectd::Graph::Type::TableSize;
+
+# Copyright (C) 2008,2009 Florian octo Forster <octo at verplant.org>
+#
+# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+use strict;
+use warnings;
+use base ('Collectd::Graph::Type');
+
+use Collectd::Graph::Common (qw($ColorCanvas $ColorFullBlue $ColorHalfBlue
+ ident_to_filename sanitize_type_instance
+ get_random_color sort_idents_by_type_instance));
+
+use RRDs ();
+
+return (1);
+
+sub _get_last_value
+{
+ my $ident = shift;
+ my $value = undef;
+ my $filename = ident_to_filename ($ident);
+ my ($start ,$step ,$names ,$data) = RRDs::fetch ($filename, 'AVERAGE', '-s', '-3600');
+ if (my $errmsg = RRDs::error ())
+ {
+ print STDERR "RRDs::fetch ($filename) failed: $errmsg\n";
+ return;
+ }
+
+ for (@$data)
+ {
+ my $line = $_;
+
+ for (@$line)
+ {
+ my $ds = $_;
+
+ if (defined ($ds))
+ {
+ $value = $ds;
+ }
+ }
+ } # for (@$data)
+
+ return ($value);
+} # _get_last_value
+
+sub getLastValues
+{
+ my $obj = shift;
+ my $last_value = {};
+
+ if (exists ($obj->{'last_value'}))
+ {
+ return ($obj->{'last_value'});
+ }
+
+ for (@{$obj->{'files'}})
+ {
+ my $file = $_;
+
+ $last_value->{$file} = _get_last_value ($file);
+ }
+ $obj->{'last_value'} = $last_value;
+ return ($obj->{'last_value'});
+} # getLastValues
+
+sub _group_files
+{
+ my $obj = shift;
+ my $data = [];
+ my @files;
+ my $last_values;
+
+ $last_values = $obj->getLastValues ();
+
+ @files = sort
+ {
+ if (!defined ($last_values->{$a}) && !defined ($last_values->{$b}))
+ {
+ return (0);
+ }
+ elsif (!defined ($last_values->{$a}))
+ {
+ return (1);
+ }
+ elsif (!defined ($last_values->{$b}))
+ {
+ return (-1);
+ }
+ else
+ {
+ return ($last_values->{$a} <=> $last_values->{$b});
+ }
+ } (@{$obj->{'files'}});
+
+ for (my $i = 0; $i < @files; $i++)
+ {
+ my $file = $files[$i];
+ my $j = int ($i / 10);
+
+ $data->[$j] ||= [];
+ push (@{$data->[$j]}, $file);
+ }
+
+ return ($data);
+} # _group_files
+
+sub getGraphsNum
+{
+ my $obj = shift;
+ my $group = _group_files ($obj);
+
+ return (0 + @$group);
+}
+
+sub getRRDArgs
+{
+ my $obj = shift;
+ my $index = shift;
+
+ my $group = _group_files ($obj);
+
+ my $rrd_opts = $obj->{'rrd_opts'} || [];
+ my $format = $obj->{'rrd_format'} || '%5.1lf';
+
+ my $idents = $group->[$index];
+ my $ds_name_len = 0;
+
+ my $ds = $obj->getDataSources ();
+ if (!$ds)
+ {
+ confess ("obj->getDataSources failed.");
+ }
+ if (@$ds != 1)
+ {
+ confess ("I can only work with RRD files that have "
+ . "exactly one data source!");
+ }
+ my $data_source = $ds->[0];
+
+ my $rrd_title = $obj->getTitle ($idents->[0]);
+
+ my $colors = $obj->{'rrd_colors'} || {};
+ my @ret = ('-t', $rrd_title, @$rrd_opts);
+
+ if (defined $obj->{'rrd_vertical'})
+ {
+ push (@ret, '-v', $obj->{'rrd_vertical'});
+ }
+
+ if ($obj->{'custom_order'})
+ {
+ sort_idents_by_type_instance ($idents, $obj->{'custom_order'});
+ }
+
+ $obj->{'ds_names'} ||= {};
+ my @names = map { $obj->{'ds_names'}{$_->{'type_instance'}} || $_->{'type_instance'} } (@$idents);
+
+ for (my $i = 0; $i < @$idents; $i++)
+ {
+ my $ident = $idents->[$i];
+ my $filename = ident_to_filename ($ident);
+
+ if ($ds_name_len < length ($names[$i]))
+ {
+ $ds_name_len = length ($names[$i]);
+ }
+
+ # Escape colons _after_ the length has been checked.
+ $names[$i] =~ s/:/\\:/g;
+
+ push (@ret,
+ "DEF:min${i}=${filename}:${data_source}:MIN",
+ "DEF:avg${i}=${filename}:${data_source}:AVERAGE",
+ "DEF:max${i}=${filename}:${data_source}:MAX");
+ }
+
+ for (my $i = 0; $i < @$idents; $i++)
+ {
+ my $type_instance = $idents->[$i]{'type_instance'};
+ my $ds_name = sprintf ("%-*s", $ds_name_len, $names[$i]);
+ my $color = '000000';
+ if (exists $colors->{$type_instance})
+ {
+ $color = $colors->{$type_instance};
+ }
+ else
+ {
+ $color = get_random_color ();
+ }
+ push (@ret,
+ "LINE1:avg${i}#${color}:${ds_name}",
+ "GPRINT:min${i}:MIN:${format} Min,",
+ "GPRINT:avg${i}:AVERAGE:${format} Avg,",
+ "GPRINT:max${i}:MAX:${format} Max,",
+ "GPRINT:avg${i}:LAST:${format} Last\\l");
+ }
+
+ return (\@ret);
+}
+
+sub getGraphArgs
+{
+ my $obj = shift;
+ my $index = shift;
+
+ my $group = _group_files ($obj);
+ my $idents = $group->[$index];
+
+ my @args = ();
+ for (qw(hostname plugin plugin_instance type))
+ {
+ if (defined ($idents->[0]{$_}))
+ {
+ push (@args, $_ . '=' . $idents->[0]{$_});
+ }
+ }
+ push (@args, "index=$index");
+
+ return (join (';', @args));
+} # getGraphArgs
+
+# vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
--- /dev/null
+package Collectd::Graph::Type::Wirkleistung;
+
+# Copyright (C) 2009 Stefan Pfab <spfab at noris.net>
+#
+# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+use strict;
+use warnings;
+use base ('Collectd::Graph::Type');
+
+use Collectd::Graph::Common (qw(ident_to_filename get_faded_color));
+
+return (1);
+
+sub getDataSources
+{
+ return ([qw(wirkleistung)]);
+} # getDataSources
+
+sub new
+{
+ my $pkg = shift;
+ my $obj = Collectd::Graph::Type->new (@_);
+ $obj->{'data_sources'} = [qw(wirkleistung)];
+ $obj->{'rrd_opts'} = ['-v', 'Watt'];
+ $obj->{'rrd_title'} = 'Wirkleistung ({type_instance})';
+ $obj->{'rrd_format'} = '%5.1lf%s W';
+ $obj->{'colors'} = [qw(000000 f00000)];
+
+ return (bless ($obj, $pkg));
+} # new
+
+sub getRRDArgs
+{
+ my $obj = shift;
+ my $index = shift;
+
+ my $ident = $obj->{'files'}[$index];
+ if (!$ident)
+ {
+ cluck ("Invalid index: $index");
+ return;
+ }
+ my $filename = ident_to_filename ($ident);
+ $filename =~ s#:#\\:#g;
+
+ my $faded_green = get_faded_color ('00ff00');
+ my $faded_red = get_faded_color ('ff0000');
+
+ return (['-t', 'Wirkleistung (' . $ident->{'type_instance'} . ')', '-v', 'Watt', '-l', '0',
+ "DEF:min0=${filename}:kWh:MIN",
+ "DEF:avg0=${filename}:kWh:AVERAGE",
+ "DEF:max0=${filename}:kWh:MAX",
+ 'AREA:max0#bfbfbf',
+ 'AREA:min0#FFFFFF',
+ 'CDEF:watt_avg0=avg0,36000,*,',
+ 'CDEF:watt_min0=min0,36000,*,',
+ 'CDEF:watt_max0=max0,36000,*,',
+ 'CDEF:watt_total=avg0,10,*,',
+ 'VDEF:total=watt_total,TOTAL',
+ 'VDEF:first=watt_total,FIRST',
+ 'VDEF:last=watt_total,LAST',
+ #'CDEF:first_value=first,POP',
+ #'CDEF:first_time=first,POP',
+ 'LINE1:watt_avg0#000000:W',
+ 'HRULE:190#ff0000',
+ 'GPRINT:watt_min0:MIN:%4.1lfW Min,',
+ 'GPRINT:watt_avg0:AVERAGE:%4.1lfW Avg,',
+ 'GPRINT:watt_max0:MAX:%4.1lfW Max,',
+ 'GPRINT:watt_avg0:LAST:%4.1lfW Last\l',
+ 'GPRINT:total:%4.1lf%sWh Gesamtverbrauch im angezeigten Zeitraum\l',
+ 'GPRINT:first:erster Wert %c:strftime',
+ 'GPRINT:last:letzter Wert %c:strftime']);
+
+ # HRULE:190\ ff0000
+
+} # getRRDArgs
+
+# vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
=cut
-# Copyright (C) 2008 Florian octo Forster <octo at verplant.org>
+# Copyright (C) 2008,2009 Florian octo Forster <octo at verplant.org>
#
# 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
@Collectd::Graph::TypeLoader::EXPORT_OK = ('tl_load_type');
our @ArrayMembers = (qw(data_sources rrd_opts custom_order));
-our @ScalarMembers = (qw(rrd_title rrd_format rrd_vertical scale));
+our @ScalarMembers = (qw(rrd_title rrd_format rrd_vertical scale ignore_unknown));
our @DSMappedMembers = (qw(ds_names rrd_colors));
our %MemberToConfigMap =
rrd_vertical => 'rrdverticallabel',
rrd_colors => 'color',
scale => 'scale', # GenericIO only
- custom_order => 'order' # GenericStacked only
+ custom_order => 'order', # GenericStacked only
+ ignore_unknown => 'ignoreunknown' # GenericStacked only
);
return (1);
my $module = shift;
my $obj;
- local $SIG{__WARN__} = sub {};
- local $SIG{__DIE__} = sub {};
+ local $SIG{__WARN__} = sub { print STDERR "WARNING: " . join (', ', @_) . "\n"; };
+ local $SIG{__DIE__} = sub { print STDERR "FATAL: " . join (', ', @_) . "\n"; };
eval <<PERL;
require $module;
}
else
{
- $opts->{'script'} = $script;
- push (@$Scripts, $opts);
+ if (ref ($opts) eq 'ARRAY')
+ {
+ for (@$opts)
+ {
+ my $opt = $_;
+ $opt->{'script'} = $script;
+ push (@$Scripts, $opt);
+ }
+ }
+ else
+ {
+ $opts->{'script'} = $script;
+ push (@$Scripts, $opts);
+ }
}
} # for (keys %$scripts)
} # handle_config_script
}
}
-function GraphListFromCookie() {
+function GraphListFromCookie(lname) {
if (document.cookie.length > 0) {
- cookies = document.cookie.split('; ');
+ var cname= 'graphLst'+lname+'=';
+ var cookies = document.cookie.split('; ');
for (i = 0; i < cookies.length; i++)
- if (cookies[i].substring(0, 9) == 'graphLst=')
- return cookies[i].substring(9).split('/');
+ if (cookies[i].substring(0, cname.length) == cname)
+ return cookies[i].substring(cname.length).split('/');
}
return new Array();
}
+function GraphListNameSort(a, b) {
+ if (a[0] > b[0])
+ return 1
+ else if (a[0] < b[0])
+ return -1;
+ else
+ return 0;
+}
+
+function GraphListRefresh() {
+ var select = document.getElementById('GraphList');
+ var childCnt = select.childNodes.length;
+ var oldValue = select.selectedIndex > 0 ? select.options[select.selectedIndex].value : '/';
+ while (childCnt > 0)
+ select.removeChild(select.childNodes[--childCnt]);
+
+ // Determine available names
+ var options = new Array();
+ if (document.cookie.length > 0) {
+ var cookies = document.cookie.split('; ');
+ for (i = 0; i < cookies.length; i++)
+ if (cookies[i].substring(0, 8) == 'graphLst') {
+ var p = cookies[i].indexOf('=');
+ if (p < 0)
+ continue;
+ options.push(new Array(cookies[i].substring(8, p), cookies[i].substring(p+1).split('/').length));
+ }
+ }
+ options.sort(GraphListNameSort);
+
+ var optCnt = options ? options.length : 0;
+ if (optCnt == 0) {
+ select.setAttribute('disabled', 'disabled');
+ return -1;
+ } else {
+ select.removeAttribute('disabled');
+ for (i = 0; i < optCnt; i++) {
+ newOption = document.createElement("option");
+ newOption.value = options[i][0];
+ if (newOption.value == oldValue)
+ newOption.setAttribute('selected', 'selected');
+ if (options[i][1] == 1)
+ newOption.appendChild(document.createTextNode(newOption.value+' (1 graph)'));
+ else
+ newOption.appendChild(document.createTextNode(newOption.value+' ('+options[i][1]+' graphs)'));
+ select.appendChild(newOption);
+ }
+ return select.selectedIndex;
+ }
+}
+
+function GraphListCheckName(doalert) {
+ var lname = document.getElementById('GraphListName');
+ if (lname) {
+ if (lname.value.match(/^[a-zA-Z0-9_-]+$/)) {
+ lname.style.backgroundColor = '';
+ return lname.value;
+ } else {
+ lname.style.backgroundColor = '#ffdddd';
+ if (doalert && lname.value.length == 0)
+ alert('Graph list name is empty.\n\n'+
+ 'Please fill in a name and try again.');
+ else if (doalert)
+ alert('Graph list name contains non-permitted character.\n\n'+
+ 'Only anlphanumerical characters (a-z, A-Z, 0-9), hyphen (-) and underscore (_) are permitted.\n'+
+ 'Please correct and try again.');
+ lname.focus();
+ }
+ }
+ return '';
+}
+
function GraphSave() {
+ var lstName = GraphListCheckName(true);
+ if (lstName.length == 0)
+ return;
if (graphList.length > 0) {
// Save graph list to cookie
var str = '';
str += graphList[i].substring(g+1);
}
- document.cookie = 'graphLst='+str;
- if (GraphListFromCookie().length == 0)
- alert("Failed to save graph list to cookie.");
+ document.cookie = 'graphLst'+lstName+'='+str;
+ if (GraphListFromCookie(lstName).length == 0)
+ alert("Failed to save graph list '"+lstName+"' to cookie.");
else
alert("Successfully saved current graph list.");
} else {
- document.cookie = 'graphLst=; expires='+new Date().toGMTString();
+ document.cookie = 'graphLst'+lstName+'=; expires='+new Date().toGMTString();
alert("Cleared saved graph list.");
}
+ GraphListRefresh();
+}
+
+function GraphDrop() {
+ var cname = document.getElementById('GraphList');
+ if (cname && cname.selectedIndex >= 0) {
+ cname = cname.options[cname.selectedIndex].value;
+ document.cookie = 'graphLst'+cname+'=; expires='+new Date().toGMTString();
+ GraphListRefresh();
+ } else
+ return;
}
function GraphLoad() {
+ var cname = document.getElementById('GraphList');
+ if (cname && cname.selectedIndex >= 0)
+ cname = cname.options[cname.selectedIndex].value;
+ else
+ return;
// Load graph list from cookie
- var grLst = GraphListFromCookie();
+ var grLst = GraphListFromCookie(cname);
+ var oldLength = graphList.length;
for (i = 0; i < grLst.length; i++) {
var host = '';
var plugin = '';
GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, logarithmic);
}
if (grLst.length == 0)
- alert("No list found for loading.");
- else if (grLst.length != graphList.length)
+ alert("No list '"+cname+"' found for loading.");
+ else if (grLst.length + oldLength != graphList.length)
alert("Could not load all graphs, probably damaged cookie.");
}
$MetaGraphDefs['apache_scoreboard'] = 'meta_graph_apache_scoreboard';
$MetaGraphDefs['mysql_commands'] = 'meta_graph_mysql_commands';
$MetaGraphDefs['mysql_handler'] = 'meta_graph_mysql_commands';
+ $MetaGraphDefs['tcp_connections'] = 'meta_graph_tcp_connections';
+ $MetaGraphDefs['dns_opcode'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_qtype'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_rcode'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_request'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_resolver'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_update'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_zops'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_response'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_query'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_reject'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_notify'] = 'meta_graph_dns_event';
+ $MetaGraphDefs['dns_transfer'] = 'meta_graph_dns_event';
if (function_exists('load_graph_definitions_local'))
load_graph_definitions_local($logarithmic, $tinylegend);
$files = array();
$opts['colors'] = array(
+ // Linux - System memoery
'free' => '00e000',
'cached' => '0000ff',
'buffered' => 'ffb000',
- 'used' => 'ff0000'
+ 'used' => 'ff0000',
+ // Bind - Server memory
+ 'TotalUse' => '00e000',
+ 'InUse' => 'ff0000',
+ 'BlockSize' => '8888dd',
+ 'ContextSize' => '444499',
+ 'Lost' => '222222'
);
- $type_instances = array('free', 'cached', 'buffered', 'used');
+ $type_instances = array('free', 'cached', 'buffered', 'used', 'TotalUse', 'InUse', 'BlockSize', 'ContextSize', 'Lost');
while (list($k, $inst) = each($type_instances)) {
$file = '';
foreach ($config['datadirs'] as $datadir)
if ($file == '')
continue;
- $sources[] = array('name'=>$inst, 'file'=>$file, 'ds'=>'count');
+ $sources[] = array('name'=>$inst, 'file'=>$file, 'ds'=>'value');
}
return collectd_draw_meta_stack($opts, $sources);
}
+function meta_graph_dns_event($host, $plugin, $plugin_instance, $type, $type_instances, $opts = array()) {
+ global $config;
+ $sources = array();
+
+ $title = "$host/$plugin".(!is_null($plugin_instance) ? "-$plugin_instance" : '')."/$type";
+ if (!isset($opts['title']))
+ $opts['title'] = $title;
+ $opts['rrd_opts'] = array('-v', 'Events', '-r', '-l', '0');
+
+ $files = array();
+// $opts['colors'] = array(
+// );
+
+// $type_instances = array('IQUERY', 'NOTIFY');
+ while (list($k, $inst) = each($type_instances)) {
+ $file = '';
+ $title = $opts['title'];
+ foreach ($config['datadirs'] as $datadir)
+ if (is_file($datadir.'/'.$title.'-'.$inst.'.rrd')) {
+ $file = $datadir.'/'.$title.'-'.$inst.'.rrd';
+ break;
+ }
+ if ($file == '')
+ continue;
+
+ $sources[] = array('name'=>$inst, 'file'=>$file);
+ }
+ return collectd_draw_meta_stack($opts, $sources);
+}
+
?>
if (is_null($identifier) || (is_array($identifier) && count($identifier) == 0) || !(is_string($identifier) || is_array($identifier)))
return false;
- if (is_null($host) || !is_string($host) || strlen($host) == 0)
- return false;
- if (is_null($plugin) || !is_string($plugin) || strlen($plugin) == 0)
- return false;
- if (is_null($pinst) || !is_string($pinst))
- return false;
- if (is_null($type) || !is_string($type) || strlen($type) == 0)
- return false;
- if (is_null($tinst) || (is_array($tinst) && count($tinst) == 0) || !(is_string($tinst) || is_array($tinst)))
- return false;
-
$u_errno = 0;
$u_errmsg = '';
if ($socket = @fsockopen($config['collectd_sock'], 0, $u_errno, $u_errmsg)) {
return $str;
}
+function rrd_escape($str) {
+ return str_replace(array('\\', ':'), array('\\\\', '\\:'), $str);
+}
+
/**
* Determine useful information about RRD file
* @file Name of RRD file to analyse
if (strlen($k) > $l_max)
$l_max = strlen($k);
if ($has_min)
- $graph[] = sprintf('DEF:%s_min=%s:%s:MIN', $k, $rrdinfo['filename'], $k);
+ $graph[] = sprintf('DEF:%s_min=%s:%s:MIN', $k, rrd_escape($rrdinfo['filename']), $k);
if ($has_avg)
- $graph[] = sprintf('DEF:%s_avg=%s:%s:AVERAGE', $k, $rrdinfo['filename'], $k);
+ $graph[] = sprintf('DEF:%s_avg=%s:%s:AVERAGE', $k, rrd_escape($rrdinfo['filename']), $k);
if ($has_max)
- $graph[] = sprintf('DEF:%s_max=%s:%s:MAX', $k, $rrdinfo['filename'], $k);
+ $graph[] = sprintf('DEF:%s_max=%s:%s:MAX', $k, rrd_escape($rrdinfo['filename']), $k);
}
if ($has_min && $has_max || $has_min && $has_avg || $has_avg && $has_max) {
$n = 1;
continue;
$file = str_replace(":", "\\:", $file);
- $rrd_args = str_replace('{file}', $file, $rrd_args);
+ $rrd_args = str_replace('{file}', rrd_escape($file), $rrd_args);
$rrdgraph = array_merge($rrd_cmd, $rrd_args);
$cmd = RRDTOOL;
$max_inst_name = 0;
foreach($sources as &$inst_data) {
- $inst_name = $inst_data['name'];
+ $inst_name = str_replace('!', '_', $inst_data['name']);
$file = $inst_data['file'];
$ds = isset($inst_data['ds']) ? $inst_data['ds'] : 'value';
if (!is_file($file))
continue;
- $cmd[] = 'DEF:'.$inst_name.'_min='.$file.':'.$ds.':MIN';
- $cmd[] = 'DEF:'.$inst_name.'_avg='.$file.':'.$ds.':AVERAGE';
- $cmd[] = 'DEF:'.$inst_name.'_max='.$file.':'.$ds.':MAX';
+ $cmd[] = 'DEF:'.$inst_name.'_min='.rrd_escape($file).':'.$ds.':MIN';
+ $cmd[] = 'DEF:'.$inst_name.'_avg='.rrd_escape($file).':'.$ds.':AVERAGE';
+ $cmd[] = 'DEF:'.$inst_name.'_max='.rrd_escape($file).':'.$ds.':MAX';
$cmd[] = 'CDEF:'.$inst_name.'_nnl='.$inst_name.'_avg,UN,0,'.$inst_name.'_avg,IF';
}
$inst_data = end($sources);
$inst_data1 = end($sources);
while (($inst_data0 = prev($sources)) !== false) {
- $inst_name0 = $inst_data0['name'];
- $inst_name1 = $inst_data1['name'];
+ $inst_name0 = str_replace('!', '_', $inst_data0['name']);
+ $inst_name1 = str_replace('!', '_', $inst_data1['name']);
$cmd[] = 'CDEF:'.$inst_name0.'_stk='.$inst_name0.'_nnl,'.$inst_name1.'_stk,+';
$inst_data1 = $inst_data0;
}
foreach($sources as &$inst_data) {
- $inst_name = $inst_data['name'];
- $legend = sprintf('%s', $inst_name);
+ $inst_name = str_replace('!', '_', $inst_data['name']);
+ $legend = sprintf('%s', $inst_data['name']);
while (strlen($legend) < $max_inst_name)
$legend .= ' ';
$number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf';
$max_inst_name = 0;
foreach ($sources as &$inst_data) {
- $inst_name = $inst_data['name'];
+ $inst_name = str_replace('!', '_', $inst_data['name']);
$file = $inst_data['file'];
$ds = isset($inst_data['ds']) ? $inst_data['ds'] : 'value';
if (!is_file($file))
continue;
- $cmd[] = 'DEF:'.$inst_name.'_min='.$file.':'.$ds.':MIN';
- $cmd[] = 'DEF:'.$inst_name.'_avg='.$file.':'.$ds.':AVERAGE';
- $cmd[] = 'DEF:'.$inst_name.'_max='.$file.':'.$ds.':MAX';
+ $cmd[] = 'DEF:'.$inst_name.'_min='.rrd_escape($file).':'.$ds.':MIN';
+ $cmd[] = 'DEF:'.$inst_name.'_avg='.rrd_escape($file).':'.$ds.':AVERAGE';
+ $cmd[] = 'DEF:'.$inst_name.'_max='.rrd_escape($file).':'.$ds.':MAX';
}
foreach ($sources as &$inst_data) {
- $inst_name = $inst_data['name'];
+ $inst_name = str_replace('!', '_', $inst_data['name']);
$legend = sprintf('%s', $inst_name);
while (strlen($legend) < $max_inst_name)
$legend .= ' ';
return error(400, "Bad request", $title, $msg);
}
+/**
+ * Incomplete / invalid request
+ */
+function error500($title, $msg) {
+ return error(500, "Internal error", $title, $msg);
+}
+
// Process input arguments
$host = read_var('host', $_GET, null);
if (is_null($host))
<script type="text/javascript" src="<?php echo htmlspecialchars($url_base.'browser.js'); ?>"></script>
</head>
- <body onload="ListRefreshHost()"><div class="body">
+ <body onload="ListRefreshHost(); GraphListRefresh(); "><div class="body">
<h1><img src="collectd-logo.png" align="bottom" alt="" /> Collectd browser for system statistics graphs</h1>
<div class="selector"><a href="javascript:toggleDiv('selector')"><span id="selector_sw">Hide</span> graph selection tool</a><div id="selector" class="selectorbox">
<table>
<tr>
<td class="sc" colspan="3"><input id="btnAdd" name="btnAdd" type="button" disabled="disabled" onclick="GraphAppend()" value="Add graph" />
<input id="btnClear" name="btnClear" type="button" disabled="disabled" onclick="GraphDropAll()" value="Remove all graphs" />
- <input id="btnRefresh" name="btnRefresh" type="button" disabled="disabled" onclick="GraphRefresh(null)" value="Refresh all graphs" />
- <input id="btnSave" name="btnSave" type="button" onclick="GraphSave()" value="Save" title="Save graph list to cookie" />
- <input id="btnLoad" name="btnLoad" type="button" onclick="GraphLoad()" value="Load" title="Load graph list from cookie" /></td>
+ <input id="btnRefresh" name="btnRefresh" type="button" disabled="disabled" onclick="GraphRefresh(null)" value="Refresh all graphs" /></td>
+ </tr>
+ <tr>
+ <td class="s1" rowspan="2">Graph list favorites:</td>
+ <td class="s3"><input type="text" style="width: 100%" maxlength="30" id="GraphListName" name="GraphListName" value="default" onchange="GraphListCheckName(false)" /></td>
+ <td class="s3"><a href="javascript:GraphSave()"><img src="save.png" width="16" height="16" alt="S" title="Save graph list to cookie" /></a></td>
+ </tr>
+ <tr>
+ <td class="s2"><select id="GraphList" name="GraphList" onfocus="GraphListRefresh()">
+ <option value="default">default</option>
+ </select></td>
+ <td class="s3"><a href="javascript:GraphLoad()"><img src="load.png" width="16" height="16" alt="L" title="Load graph list from cookie" /></a><a href="javascript:GraphDrop()"><img src="delete.png" width="16" height="16" alt="D" title="Delete graph list from cookie" /></a></td>
</tr>
</table>
</div></div>
collectd_DEPENDENCIES += filecount.la
endif
+if BUILD_PLUGIN_GMOND
+pkglib_LTLIBRARIES += gmond.la
+gmond_la_SOURCES = gmond.c
+gmond_la_CPPFLAGS = $(AM_CPPFLAGS) $(GANGLIA_CPPFLAGS)
+gmond_la_LDFLAGS = -module -avoid-version $(GANGLIA_LDFLAGS)
+gmond_la_LIBADD = $(GANGLIA_LIBS)
+collectd_LDADD += "-dlopen" gmond.la
+collectd_DEPENDENCIES += gmond.la
+endif
+
if BUILD_PLUGIN_HDDTEMP
pkglib_LTLIBRARIES += hddtemp.la
hddtemp_la_SOURCES = hddtemp.c
list_callback_t list_callback,
void *user_data,
xmlDoc *doc, xmlXPathContext *xpathCtx,
- time_t current_time)
+ time_t current_time, int ds_type)
{
xmlXPathObject *xpathObj = NULL;
int num_entries;
value_t value;
int status;
- status = bind_xml_read_counter (doc, counter, &value.counter);
+ if (ds_type == DS_TYPE_GAUGE)
+ status = bind_xml_read_gauge (doc, counter, &value.gauge);
+ else
+ status = bind_xml_read_counter (doc, counter, &value.counter);
if (status != 0)
continue;
list_info_ptr_t list_info =
{
plugin_instance,
- /* type = */ "dns_qtype"
+ /* type = */ "dns_qtype_gauge"
};
ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-qtypes",
bind_parse_generic_name_value (/* xpath = */ "rdtype",
/* callback = */ bind_xml_list_callback,
/* user_data = */ &list_info,
- doc, path_ctx, current_time);
+ doc, path_ctx, current_time, DS_TYPE_COUNTER);
} /* }}} */
if (view->resolver_stats != 0) /* {{{ */
bind_parse_generic_name_value ("resstat",
/* callback = */ bind_xml_table_callback,
/* user_data = */ &table_ptr,
- doc, path_ctx, current_time);
+ doc, path_ctx, current_time, DS_TYPE_COUNTER);
} /* }}} */
if (view->cacherrsets != 0) /* {{{ */
list_info_ptr_t list_info =
{
plugin_instance,
- /* type = */ "dns_qtype"
+ /* type = */ "dns_qtype_gauge"
};
ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-cache_rr_sets",
bind_parse_generic_name_value (/* xpath = */ "cache/rrset",
/* callback = */ bind_xml_list_callback,
/* user_data = */ &list_info,
- doc, path_ctx, current_time);
+ doc, path_ctx, current_time, DS_TYPE_GAUGE);
} /* }}} */
if (view->zones_num > 0)
bind_parse_generic_name_value (/* xpath = */ "server/requests/opcode",
/* callback = */ bind_xml_list_callback,
/* user_data = */ &list_info,
- doc, xpathCtx, current_time);
+ doc, xpathCtx, current_time, DS_TYPE_COUNTER);
}
/* XPath: server/queries-in/rdtype
bind_parse_generic_name_value (/* xpath = */ "server/queries-in/rdtype",
/* callback = */ bind_xml_list_callback,
/* user_data = */ &list_info,
- doc, xpathCtx, current_time);
+ doc, xpathCtx, current_time, DS_TYPE_COUNTER);
}
/* XPath: server/nsstats, server/nsstat
bind_parse_generic_name_value ("server/nsstat",
/* callback = */ bind_xml_table_callback,
/* user_data = */ &table_ptr,
- doc, xpathCtx, current_time);
+ doc, xpathCtx, current_time, DS_TYPE_COUNTER);
}
}
bind_parse_generic_name_value ("server/zonestat",
/* callback = */ bind_xml_table_callback,
/* user_data = */ &table_ptr,
- doc, xpathCtx, current_time);
+ doc, xpathCtx, current_time, DS_TYPE_COUNTER);
}
}
bind_parse_generic_name_value ("server/resstat",
/* callback = */ bind_xml_table_callback,
/* user_data = */ &table_ptr,
- doc, xpathCtx, current_time);
+ doc, xpathCtx, current_time, DS_TYPE_COUNTER);
}
}
#@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy
#@BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec
#@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount
+#@BUILD_PLUGIN_GMOND_TRUE@LoadPlugin gmond
#@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
@BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
#@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
# </Directory>
#</Plugin>
+#<Plugin "gmond">
+# MCReceiveFrom "239.2.11.71" "8649"
+# <Metric "swap_total">
+# Type "swap"
+# TypeInstance "total"
+# DataSource "value"
+# </Metric>
+# <Metric "swap_free">
+# Type "swap"
+# TypeInstance "free"
+# DataSource "value"
+# </Metric>
+#</Plugin>
+
#<Plugin hddtemp>
# Host "127.0.0.1"
# Port "7634"
<Match>
Regex "<span +class=\"pr\"[^>]*> *([0-9]*\\.[0-9]+) *</span>"
DSType "GaugeAverage"
- # Note: `stock_value' is not a standard type.
+ # Note: `stock_value' is not a standard type.
Type "stock_value"
Instance "AMD"
</Match>
use a more strict database server, you may have to select from a dummy table or
something.)
+Please note that some databases, for example B<Oracle>, will fail if you
+include a semicolon at the end of the statement.
+
=item B<MinVersion> I<Version>
=item B<MaxVersion> I<Value>
=back
+=head2 Plugin C<gmond>
+
+The I<gmond> plugin received the multicast traffic sent by B<gmond>, the
+statistics collection daemon of Ganglia. Mappings for the standard "metrics"
+are built-in, custom mappings may be added via B<Metric> blocks, see below.
+
+Synopsis:
+
+ <Plugin "gmond">
+ MCReceiveFrom "239.2.11.71" "8649"
+ <Metric "swap_total">
+ Type "swap"
+ TypeInstance "total"
+ DataSource "value"
+ </Metric>
+ <Metric "swap_free">
+ Type "swap"
+ TypeInstance "free"
+ DataSource "value"
+ </Metric>
+ </Plugin>
+
+The following metrics are built-in:
+
+=over 4
+
+=item *
+
+load_one, load_five, load_fifteen
+
+=item *
+
+cpu_user, cpu_system, cpu_idle, cpu_nice, cpu_wio
+
+=item *
+
+mem_free, mem_shared, mem_buffers, mem_cached, mem_total
+
+=item *
+
+bytes_in, bytes_out
+
+=item *
+
+pkts_in, pkts_out
+
+=back
+
+Available configuration options:
+
+=over 4
+
+=item B<MCReceiveFrom> I<MCGroup> [I<Port>]
+
+Sets sets the multicast group and UDP port to which to subscribe.
+
+Default: B<239.2.11.71>E<nbsp>/E<nbsp>B<8649>
+
+=item E<lt>B<Metric> I<Name>E<gt>
+
+These blocks add a new metric conversion to the internal table. I<Name>, the
+string argument to the B<Metric> block, is the metric name as used by Ganglia.
+
+=over 4
+
+=item B<Type> I<Type>
+
+Type to map this metric to. Required.
+
+=item B<TypeInstance> I<Instance>
+
+Type-instance to use. Optional.
+
+=item B<DataSource> I<Name>
+
+Data source to map this metric to. If the configured type has exactly one data
+source, this is optional. Otherwise the option is required.
+
+=back
+
+=back
+
=head2 Plugin C<hddtemp>
To get values from B<hddtemp> collectd connects to B<localhost> (127.0.0.1),
int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
{
char ident[128];
+
+ *ksp_ptr = NULL;
if (kc == NULL)
return (-1);
ssnprintf (ident, sizeof (ident), "%s,%i,%s", module, instance, name);
+ *ksp_ptr = kstat_lookup (kc, module, instance, name);
if (*ksp_ptr == NULL)
{
- if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
- {
- ERROR ("Cound not find kstat %s", ident);
- return (-1);
- }
+ ERROR ("get_kstat: Cound not find kstat %s", ident);
+ return (-1);
+ }
- if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
- {
- WARNING ("kstat %s has wrong type", ident);
- *ksp_ptr = NULL;
- return (-1);
- }
+ if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
+ {
+ ERROR ("get_kstat: kstat %s has wrong type", ident);
+ *ksp_ptr = NULL;
+ return (-1);
}
#ifdef assert
if (kstat_read (kc, *ksp_ptr, NULL) == -1)
{
- WARNING ("kstat %s could not be read", ident);
+ ERROR ("get_kstat: kstat %s could not be read", ident);
return (-1);
}
if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
{
- WARNING ("kstat %s has wrong type", ident);
+ ERROR ("get_kstat: kstat %s has wrong type", ident);
return (-1);
}
--- /dev/null
+/**
+ * collectd - src/gmond.c
+ * Copyright (C) 2005-2009 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 octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "utils_avltree.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#if HAVE_POLL_H
+# include <poll.h>
+#endif
+
+#include <gm_protocol.h>
+
+#ifndef IPV6_ADD_MEMBERSHIP
+# ifdef IPV6_JOIN_GROUP
+# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+# else
+# error "Neither IP_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP is defined"
+# endif
+#endif /* !IP_ADD_MEMBERSHIP */
+
+#ifdef GANGLIA_MAX_MESSAGE_LEN
+# define BUFF_SIZE GANGLIA_MAX_MESSAGE_LEN
+#else
+# define BUFF_SIZE 1400
+#endif
+
+struct socket_entry_s
+{
+ int fd;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+};
+typedef struct socket_entry_s socket_entry_t;
+
+struct staging_entry_s
+{
+ char key[2 * DATA_MAX_NAME_LEN];
+ value_list_t vl;
+ int flags;
+};
+typedef struct staging_entry_s staging_entry_t;
+
+struct metric_map_s
+{
+ char *ganglia_name;
+ char *type;
+ char *type_instance;
+ char *ds_name;
+ int ds_type;
+ int ds_index;
+};
+typedef struct metric_map_s metric_map_t;
+
+#define MC_RECEIVE_GROUP_DEFAULT "239.2.11.71"
+static char *mc_receive_group = NULL;
+#define MC_RECEIVE_PORT_DEFAULT "8649"
+static char *mc_receive_port = NULL;
+
+static struct pollfd *mc_receive_sockets = NULL;
+static size_t mc_receive_sockets_num = 0;
+
+static socket_entry_t *mc_send_sockets = NULL;
+static size_t mc_send_sockets_num = 0;
+static pthread_mutex_t mc_send_sockets_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int mc_receive_thread_loop = 0;
+static int mc_receive_thread_running = 0;
+static pthread_t mc_receive_thread_id;
+
+static metric_map_t metric_map_default[] =
+{ /*---------------+-------------+-----------+-------------+------+-----*
+ * ganglia_name ! type ! type_inst ! data_source ! type ! idx *
+ *---------------+-------------+-----------+-------------+------+-----*/
+ { "load_one", "load", "", "shortterm", -1, -1 },
+ { "load_five", "load", "", "midterm", -1, -1 },
+ { "load_fifteen", "load", "", "longterm", -1, -1 },
+ { "cpu_user", "cpu", "user", "value", -1, -1 },
+ { "cpu_system", "cpu", "system", "value", -1, -1 },
+ { "cpu_idle", "cpu", "idle", "value", -1, -1 },
+ { "cpu_nice", "cpu", "nice", "value", -1, -1 },
+ { "cpu_wio", "cpu", "wait", "value", -1, -1 },
+ { "mem_free", "memory", "free", "value", -1, -1 },
+ { "mem_shared", "memory", "shared", "value", -1, -1 },
+ { "mem_buffers", "memory", "buffered", "value", -1, -1 },
+ { "mem_cached", "memory", "cached", "value", -1, -1 },
+ { "mem_total", "memory", "total", "value", -1, -1 },
+ { "bytes_in", "if_octets", "", "rx", -1, -1 },
+ { "bytes_out", "if_octets", "", "tx", -1, -1 },
+ { "pkts_in", "if_packets", "", "rx", -1, -1 },
+ { "pkts_out", "if_packets", "", "tx", -1, -1 }
+};
+static size_t metric_map_len_default = STATIC_ARRAY_SIZE (metric_map_default);
+
+static metric_map_t *metric_map = NULL;
+static size_t metric_map_len = 0;
+
+static c_avl_tree_t *staging_tree;
+static pthread_mutex_t staging_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static metric_map_t *metric_lookup (const char *key) /* {{{ */
+{
+ metric_map_t *map;
+ size_t map_len;
+ size_t i;
+
+ /* Search the user-supplied table first.. */
+ map = metric_map;
+ map_len = metric_map_len;
+ for (i = 0; i < map_len; i++)
+ if (strcmp (map[i].ganglia_name, key) == 0)
+ break;
+
+ /* .. and fall back to the built-in table if nothing is found. */
+ if (i >= map_len)
+ {
+ map = metric_map_default;
+ map_len = metric_map_len_default;
+
+ for (i = 0; i < map_len; i++)
+ if (strcmp (map[i].ganglia_name, key) == 0)
+ break;
+ }
+
+ if (i >= map_len)
+ return (NULL);
+
+ /* Look up the DS type and ds_index. */
+ if ((map[i].ds_type < 0) || (map[i].ds_index < 0)) /* {{{ */
+ {
+ const data_set_t *ds;
+
+ ds = plugin_get_ds (map[i].type);
+ if (ds == NULL)
+ {
+ WARNING ("gmond plugin: Type not defined: %s", map[i].type);
+ return (NULL);
+ }
+
+ if ((map[i].ds_name == NULL) && (ds->ds_num != 1))
+ {
+ WARNING ("gmond plugin: No data source name defined for metric %s, "
+ "but type %s has more than one data source.",
+ map[i].ganglia_name, map[i].type);
+ return (NULL);
+ }
+
+ if (map[i].ds_name == NULL)
+ {
+ map[i].ds_index = 0;
+ }
+ else
+ {
+ int j;
+
+ for (j = 0; j < ds->ds_num; j++)
+ if (strcasecmp (ds->ds[j].name, map[i].ds_name) == 0)
+ break;
+
+ if (j >= ds->ds_num)
+ {
+ WARNING ("gmond plugin: There is no data source "
+ "named `%s' in type `%s'.",
+ map[i].ds_name, ds->type);
+ return (NULL);
+ }
+ map[i].ds_index = j;
+ }
+
+ map[i].ds_type = ds->ds[map[i].ds_index].type;
+ } /* }}} if ((map[i].ds_type < 0) || (map[i].ds_index < 0)) */
+
+ return (map + i);
+} /* }}} metric_map_t *metric_lookup */
+
+static int create_sockets (socket_entry_t **ret_sockets, /* {{{ */
+ size_t *ret_sockets_num,
+ const char *node, const char *service, int listen)
+{
+ struct addrinfo ai_hints;
+ struct addrinfo *ai_list;
+ struct addrinfo *ai_ptr;
+ int ai_return;
+
+ socket_entry_t *sockets;
+ size_t sockets_num;
+
+ int status;
+
+ sockets = *ret_sockets;
+ sockets_num = *ret_sockets_num;
+
+ memset (&ai_hints, 0, sizeof (ai_hints));
+ ai_hints.ai_flags = 0;
+#ifdef AI_PASSIVE
+ ai_hints.ai_flags |= AI_PASSIVE;
+#endif
+#ifdef AI_ADDRCONFIG
+ ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ ai_hints.ai_family = AF_UNSPEC;
+ ai_hints.ai_socktype = SOCK_DGRAM;
+ ai_hints.ai_protocol = IPPROTO_UDP;
+
+ ai_return = getaddrinfo (node, service, &ai_hints, &ai_list);
+ if (ai_return != 0)
+ {
+ char errbuf[1024];
+ ERROR ("gmond plugin: getaddrinfo (%s, %s) failed: %s",
+ (node == NULL) ? "(null)" : node,
+ (service == NULL) ? "(null)" : service,
+ (ai_return == EAI_SYSTEM)
+ ? sstrerror (errno, errbuf, sizeof (errbuf))
+ : gai_strerror (ai_return));
+ return (-1);
+ }
+
+ for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) /* {{{ */
+ {
+ socket_entry_t *tmp;
+
+ tmp = realloc (sockets, (sockets_num + 1) * sizeof (*sockets));
+ if (tmp == NULL)
+ {
+ ERROR ("gmond plugin: realloc failed.");
+ continue;
+ }
+ sockets = tmp;
+
+ sockets[sockets_num].fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
+ ai_ptr->ai_protocol);
+ if (sockets[sockets_num].fd < 0)
+ {
+ char errbuf[1024];
+ ERROR ("gmond plugin: socket failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ continue;
+ }
+
+ assert (sizeof (sockets[sockets_num].addr) >= ai_ptr->ai_addrlen);
+ memcpy (&sockets[sockets_num].addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+ sockets[sockets_num].addrlen = ai_ptr->ai_addrlen;
+
+ /* Sending socket: Open only one socket and don't bind it. */
+ if (listen == 0)
+ {
+ sockets_num++;
+ break;
+ }
+ else
+ {
+ int yes = 1;
+
+ setsockopt (sockets[sockets_num].fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *) &yes, sizeof (yes));
+ }
+
+ status = bind (sockets[sockets_num].fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("gmond plugin: bind failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ close (sockets[sockets_num].fd);
+ continue;
+ }
+
+ if (ai_ptr->ai_family == AF_INET)
+ {
+ struct sockaddr_in *addr;
+ struct ip_mreq mreq;
+ int loop;
+
+ addr = (struct sockaddr_in *) ai_ptr->ai_addr;
+
+ if (!IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+ {
+ sockets_num++;
+ continue;
+ }
+
+ loop = 1;
+ setsockopt (sockets[sockets_num].fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (void *) &loop, sizeof (loop));
+
+ memset (&mreq, 0, sizeof (mreq));
+ mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+ mreq.imr_interface.s_addr = htonl (INADDR_ANY);
+ setsockopt (sockets[sockets_num].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (void *) &mreq, sizeof (mreq));
+ } /* if (ai_ptr->ai_family == AF_INET) */
+ else if (ai_ptr->ai_family == AF_INET6)
+ {
+ struct sockaddr_in6 *addr;
+ struct ipv6_mreq mreq;
+ int loop;
+
+ addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
+
+ if (!IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+ {
+ sockets_num++;
+ continue;
+ }
+
+ loop = 1;
+ setsockopt (sockets[sockets_num].fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ (void *) &loop, sizeof (loop));
+
+ memset (&mreq, 0, sizeof (mreq));
+ memcpy (&mreq.ipv6mr_multiaddr,
+ &addr->sin6_addr, sizeof (addr->sin6_addr));
+ mreq.ipv6mr_interface = 0; /* any */
+ setsockopt (sockets[sockets_num].fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
+ (void *) &mreq, sizeof (mreq));
+ } /* if (ai_ptr->ai_family == AF_INET6) */
+
+ sockets_num++;
+ } /* }}} for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) */
+
+ freeaddrinfo (ai_list);
+
+ if ((*ret_sockets_num) >= sockets_num)
+ return (-1);
+
+ *ret_sockets = sockets;
+ *ret_sockets_num = sockets_num;
+ return (0);
+} /* }}} int create_sockets */
+
+static int request_meta_data (const char *host, const char *name) /* {{{ */
+{
+ Ganglia_metadata_msg msg;
+ char buffer[BUFF_SIZE];
+ unsigned int buffer_size;
+ XDR xdr;
+ size_t i;
+
+ memset (&msg, 0, sizeof (msg));
+
+ msg.id = gmetadata_request;
+ msg.Ganglia_metadata_msg_u.grequest.metric_id.host = strdup (host);
+ msg.Ganglia_metadata_msg_u.grequest.metric_id.name = strdup (name);
+
+ if ((msg.Ganglia_metadata_msg_u.grequest.metric_id.host == NULL)
+ || (msg.Ganglia_metadata_msg_u.grequest.metric_id.name == NULL))
+ {
+ sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.host);
+ sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.name);
+ return (-1);
+ }
+
+ memset (buffer, 0, sizeof (buffer));
+ xdrmem_create (&xdr, buffer, sizeof (buffer), XDR_ENCODE);
+
+ if (!xdr_Ganglia_metadata_msg (&xdr, &msg))
+ {
+ sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.host);
+ sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.name);
+ return (-1);
+ }
+
+ buffer_size = xdr_getpos (&xdr);
+
+ DEBUG ("gmond plugin: Requesting meta data for %s/%s.",
+ host, name);
+
+ pthread_mutex_lock (&mc_send_sockets_lock);
+ for (i = 0; i < mc_send_sockets_num; i++)
+ sendto (mc_send_sockets[i].fd, buffer, (size_t) buffer_size,
+ /* flags = */ 0,
+ (struct sockaddr *) &mc_send_sockets[i].addr,
+ mc_send_sockets[i].addrlen);
+ pthread_mutex_unlock (&mc_send_sockets_lock);
+
+ sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.host);
+ sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.name);
+ return (0);
+} /* }}} int request_meta_data */
+
+static staging_entry_t *staging_entry_get (const char *host, /* {{{ */
+ const char *name,
+ const char *type, const char *type_instance,
+ int values_len)
+{
+ char key[2 * DATA_MAX_NAME_LEN];
+ staging_entry_t *se;
+ int status;
+
+ if (staging_tree == NULL)
+ return (NULL);
+
+ ssnprintf (key, sizeof (key), "%s/%s/%s", host, type,
+ (type_instance != NULL) ? type_instance : "");
+
+ se = NULL;
+ status = c_avl_get (staging_tree, key, (void *) &se);
+ if (status == 0)
+ return (se);
+
+ /* insert new entry */
+ se = (staging_entry_t *) malloc (sizeof (*se));
+ if (se == NULL)
+ return (NULL);
+ memset (se, 0, sizeof (*se));
+
+ sstrncpy (se->key, key, sizeof (se->key));
+ se->flags = 0;
+
+ se->vl.values = (value_t *) calloc (values_len, sizeof (*se->vl.values));
+ if (se->vl.values == NULL)
+ {
+ sfree (se);
+ return (NULL);
+ }
+ se->vl.values_len = values_len;
+
+ se->vl.time = 0;
+ se->vl.interval = 0;
+ sstrncpy (se->vl.host, host, sizeof (se->vl.host));
+ sstrncpy (se->vl.plugin, "gmond", sizeof (se->vl.plugin));
+ sstrncpy (se->vl.type, type, sizeof (se->vl.type));
+ if (type_instance != NULL)
+ sstrncpy (se->vl.type_instance, type_instance,
+ sizeof (se->vl.type_instance));
+
+ status = c_avl_insert (staging_tree, se->key, se);
+ if (status != 0)
+ {
+ ERROR ("gmond plugin: c_avl_insert failed.");
+ sfree (se->vl.values);
+ sfree (se);
+ return (NULL);
+ }
+
+ return (se);
+} /* }}} staging_entry_t *staging_entry_get */
+
+static int staging_entry_submit (const char *host, const char *name, /* {{{ */
+ staging_entry_t *se)
+{
+ value_list_t vl;
+ value_t values[se->vl.values_len];
+
+ if (se->vl.interval == 0)
+ {
+ /* No meta data has been received for this metric yet. */
+ se->flags = 0;
+ pthread_mutex_unlock (&staging_lock);
+ request_meta_data (host, name);
+ return (0);
+ }
+
+ se->flags = 0;
+
+ memcpy (values, se->vl.values, sizeof (values));
+ memcpy (&vl, &se->vl, sizeof (vl));
+
+ /* Unlock before calling `plugin_dispatch_values'.. */
+ pthread_mutex_unlock (&staging_lock);
+
+ vl.values = values;
+
+ plugin_dispatch_values (&vl);
+
+ return (0);
+} /* }}} int staging_entry_submit */
+
+static int staging_entry_update (const char *host, const char *name, /* {{{ */
+ const char *type, const char *type_instance,
+ int ds_index, int ds_type, value_t value)
+{
+ const data_set_t *ds;
+ staging_entry_t *se;
+
+ ds = plugin_get_ds (type);
+ if (ds == NULL)
+ {
+ ERROR ("gmond plugin: Looking up type %s failed.", type);
+ return (-1);
+ }
+
+ if (ds->ds_num <= ds_index)
+ {
+ ERROR ("gmond plugin: Invalid index %i: %s has only %i data source(s).",
+ ds_index, ds->type, ds->ds_num);
+ return (-1);
+ }
+
+ pthread_mutex_lock (&staging_lock);
+
+ se = staging_entry_get (host, name, type, type_instance, ds->ds_num);
+ if (se == NULL)
+ {
+ pthread_mutex_unlock (&staging_lock);
+ ERROR ("gmond plugin: staging_entry_get failed.");
+ return (-1);
+ }
+ if (se->vl.values_len != ds->ds_num)
+ {
+ pthread_mutex_unlock (&staging_lock);
+ return (-1);
+ }
+
+ if (ds_type == DS_TYPE_COUNTER)
+ se->vl.values[ds_index].counter += value.counter;
+ else if (ds_type == DS_TYPE_GAUGE)
+ se->vl.values[ds_index].gauge = value.gauge;
+ se->flags |= (0x01 << ds_index);
+
+ /* Check if all values have been set and submit if so. */
+ if (se->flags == ((0x01 << se->vl.values_len) - 1))
+ {
+ /* `staging_lock' is unlocked in `staging_entry_submit'. */
+ staging_entry_submit (host, name, se);
+ }
+ else
+ {
+ pthread_mutex_unlock (&staging_lock);
+ }
+
+ return (0);
+} /* }}} int staging_entry_update */
+
+static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
+{
+ const char *host;
+ const char *name;
+ metric_map_t *map;
+
+ value_t value_counter;
+ value_t value_gauge;
+
+ /* Fill in `host', `name', `value_counter', and `value_gauge' according to
+ * the value type, or return with an error. */
+ switch (msg->id) /* {{{ */
+ {
+ case gmetric_uint:
+ {
+ Ganglia_gmetric_uint msg_uint;
+
+ msg_uint = msg->Ganglia_value_msg_u.gu_int;
+
+ host = msg_uint.metric_id.host;
+ name = msg_uint.metric_id.name;
+ value_counter.counter = (counter_t) msg_uint.ui;
+ value_gauge.gauge = (gauge_t) msg_uint.ui;
+ break;
+ }
+
+ case gmetric_string:
+ {
+ Ganglia_gmetric_string msg_string;
+ char *endptr;
+
+ msg_string = msg->Ganglia_value_msg_u.gstr;
+
+ host = msg_string.metric_id.host;
+ name = msg_string.metric_id.name;
+
+ endptr = NULL;
+ errno = 0;
+ value_counter.counter = (counter_t) strtoll (msg_string.str,
+ &endptr, /* base = */ 0);
+ if ((endptr == msg_string.str) || (errno != 0))
+ value_counter.counter = -1;
+
+ endptr = NULL;
+ errno = 0;
+ value_gauge.gauge = (gauge_t) strtod (msg_string.str, &endptr);
+ if ((endptr == msg_string.str) || (errno != 0))
+ value_gauge.gauge = NAN;
+
+ break;
+ }
+
+ case gmetric_float:
+ {
+ Ganglia_gmetric_float msg_float;
+
+ msg_float = msg->Ganglia_value_msg_u.gf;
+
+ host = msg_float.metric_id.host;
+ name = msg_float.metric_id.name;
+ value_counter.counter = (counter_t) msg_float.f;
+ value_gauge.gauge = (gauge_t) msg_float.f;
+ break;
+ }
+
+ case gmetric_double:
+ {
+ Ganglia_gmetric_double msg_double;
+
+ msg_double = msg->Ganglia_value_msg_u.gd;
+
+ host = msg_double.metric_id.host;
+ name = msg_double.metric_id.name;
+ value_counter.counter = (counter_t) msg_double.d;
+ value_gauge.gauge = (gauge_t) msg_double.d;
+ break;
+ }
+ default:
+ DEBUG ("gmond plugin: Value type not handled: %i", msg->id);
+ return (-1);
+ } /* }}} switch (msg->id) */
+
+ assert (host != NULL);
+ assert (name != NULL);
+
+ map = metric_lookup (name);
+ if (map != NULL)
+ return (staging_entry_update (host, name,
+ map->type, map->type_instance,
+ map->ds_index, map->ds_type,
+ (map->ds_type == DS_TYPE_COUNTER) ? value_counter : value_gauge));
+
+ DEBUG ("gmond plugin: Cannot find a translation for %s.", name);
+ return (-1);
+} /* }}} int mc_handle_value_msg */
+
+static int mc_handle_metadata_msg (Ganglia_metadata_msg *msg) /* {{{ */
+{
+ switch (msg->id)
+ {
+ case gmetadata_full:
+ {
+ Ganglia_metadatadef msg_meta;
+ staging_entry_t *se;
+ const data_set_t *ds;
+ metric_map_t *map;
+
+ msg_meta = msg->Ganglia_metadata_msg_u.gfull;
+
+ if (msg_meta.metric.tmax <= 0)
+ return (-1);
+
+ map = metric_lookup (msg_meta.metric_id.name);
+ if (map == NULL)
+ {
+ DEBUG ("gmond plugin: Not handling meta data %s.",
+ msg_meta.metric_id.name);
+ return (0);
+ }
+
+ ds = plugin_get_ds (map->type);
+ if (ds == NULL)
+ {
+ WARNING ("gmond plugin: Could not find data set %s.", map->type);
+ return (-1);
+ }
+
+ DEBUG ("gmond plugin: Received meta data for %s/%s.",
+ msg_meta.metric_id.host, msg_meta.metric_id.name);
+
+ pthread_mutex_lock (&staging_lock);
+ se = staging_entry_get (msg_meta.metric_id.host,
+ msg_meta.metric_id.name,
+ map->type, map->type_instance,
+ ds->ds_num);
+ if (se != NULL)
+ se->vl.interval = (int) msg_meta.metric.tmax;
+ pthread_mutex_unlock (&staging_lock);
+
+ if (se == NULL)
+ {
+ ERROR ("gmond plugin: staging_entry_get failed.");
+ return (-1);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ return (-1);
+ }
+ }
+
+ return (0);
+} /* }}} int mc_handle_metadata_msg */
+
+static int mc_handle_metric (void *buffer, size_t buffer_size) /* {{{ */
+{
+ XDR xdr;
+ Ganglia_msg_formats format;
+
+ xdrmem_create (&xdr, buffer, buffer_size, XDR_DECODE);
+
+ xdr_Ganglia_msg_formats (&xdr, &format);
+ xdr_setpos (&xdr, 0);
+
+ switch (format)
+ {
+ case gmetric_ushort:
+ case gmetric_short:
+ case gmetric_int:
+ case gmetric_uint:
+ case gmetric_string:
+ case gmetric_float:
+ case gmetric_double:
+ {
+ Ganglia_value_msg msg;
+
+ memset (&msg, 0, sizeof (msg));
+ if (xdr_Ganglia_value_msg (&xdr, &msg))
+ mc_handle_value_msg (&msg);
+ break;
+ }
+
+ case gmetadata_full:
+ case gmetadata_request:
+ {
+ Ganglia_metadata_msg msg;
+ memset (&msg, 0, sizeof (msg));
+ if (xdr_Ganglia_metadata_msg (&xdr, &msg))
+ mc_handle_metadata_msg (&msg);
+ break;
+ }
+
+ default:
+ DEBUG ("gmond plugin: Unknown format: %i", format);
+ return (-1);
+ } /* switch (format) */
+
+
+ return (0);
+} /* }}} int mc_handle_metric */
+
+static int mc_handle_socket (struct pollfd *p) /* {{{ */
+{
+ char buffer[BUFF_SIZE];
+ ssize_t buffer_size;
+
+ if ((p->revents & (POLLIN | POLLPRI)) == 0)
+ {
+ p->revents = 0;
+ return (-1);
+ }
+
+ buffer_size = recv (p->fd, buffer, sizeof (buffer), /* flags = */ 0);
+ if (buffer_size <= 0)
+ {
+ char errbuf[1024];
+ ERROR ("gmond plugin: recv failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ p->revents = 0;
+ return (-1);
+ }
+
+ mc_handle_metric (buffer, (size_t) buffer_size);
+ return (0);
+} /* }}} int mc_handle_socket */
+
+static void *mc_receive_thread (void *arg) /* {{{ */
+{
+ socket_entry_t *mc_receive_socket_entries;
+ int status;
+ size_t i;
+
+ mc_receive_socket_entries = NULL;
+ status = create_sockets (&mc_receive_socket_entries, &mc_receive_sockets_num,
+ (mc_receive_group != NULL) ? mc_receive_group : MC_RECEIVE_GROUP_DEFAULT,
+ (mc_receive_port != NULL) ? mc_receive_port : MC_RECEIVE_PORT_DEFAULT,
+ /* listen = */ 1);
+ if (status != 0)
+ {
+ ERROR ("gmond plugin: create_sockets failed.");
+ return ((void *) -1);
+ }
+
+ mc_receive_sockets = (struct pollfd *) calloc (mc_receive_sockets_num,
+ sizeof (*mc_receive_sockets));
+ if (mc_receive_sockets == NULL)
+ {
+ ERROR ("gmond plugin: calloc failed.");
+ for (i = 0; i < mc_receive_sockets_num; i++)
+ close (mc_receive_socket_entries[i].fd);
+ free (mc_receive_socket_entries);
+ mc_receive_socket_entries = NULL;
+ mc_receive_sockets_num = 0;
+ return ((void *) -1);
+ }
+
+ for (i = 0; i < mc_receive_sockets_num; i++)
+ {
+ mc_receive_sockets[i].fd = mc_receive_socket_entries[i].fd;
+ mc_receive_sockets[i].events = POLLIN | POLLPRI;
+ mc_receive_sockets[i].revents = 0;
+ }
+
+ while (mc_receive_thread_loop != 0)
+ {
+ status = poll (mc_receive_sockets, mc_receive_sockets_num, -1);
+ if (status <= 0)
+ {
+ char errbuf[1024];
+ if (errno == EINTR)
+ continue;
+ ERROR ("gmond plugin: poll failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ break;
+ }
+
+ for (i = 0; i < mc_receive_sockets_num; i++)
+ {
+ if (mc_receive_sockets[i].revents != 0)
+ mc_handle_socket (mc_receive_sockets + i);
+ }
+ } /* while (mc_receive_thread_loop != 0) */
+
+ return ((void *) 0);
+} /* }}} void *mc_receive_thread */
+
+static int mc_receive_thread_start (void) /* {{{ */
+{
+ int status;
+
+ if (mc_receive_thread_running != 0)
+ return (-1);
+
+ mc_receive_thread_loop = 1;
+
+ status = pthread_create (&mc_receive_thread_id, /* attr = */ NULL,
+ mc_receive_thread, /* args = */ NULL);
+ if (status != 0)
+ {
+ ERROR ("gmond plugin: Starting receive thread failed.");
+ mc_receive_thread_loop = 0;
+ return (-1);
+ }
+
+ mc_receive_thread_running = 1;
+ return (0);
+} /* }}} int start_receive_thread */
+
+static int mc_receive_thread_stop (void) /* {{{ */
+{
+ if (mc_receive_thread_running == 0)
+ return (-1);
+
+ mc_receive_thread_loop = 0;
+
+ INFO ("gmond plugin: Stopping receive thread.");
+ pthread_kill (mc_receive_thread_id, SIGTERM);
+ pthread_join (mc_receive_thread_id, /* return value = */ NULL);
+ memset (&mc_receive_thread_id, 0, sizeof (mc_receive_thread_id));
+
+ mc_receive_thread_running = 0;
+
+ return (0);
+} /* }}} int mc_receive_thread_stop */
+
+/*
+ * Config:
+ *
+ * <Plugin gmond>
+ * MCReceiveFrom "239.2.11.71" "8649"
+ * <Metric "load_one">
+ * Type "load"
+ * [TypeInstance "foo"]
+ * [DataSource "bar"]
+ * </Metric>
+ * </Plugin>
+ */
+static int gmond_config_set_string (oconfig_item_t *ci, char **str) /* {{{ */
+{
+ char *tmp;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("gmond plugin: The `%s' option needs "
+ "exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ tmp = strdup (ci->values[0].value.string);
+ if (tmp == NULL)
+ {
+ ERROR ("gmond plugin: strdup failed.");
+ return (-1);
+ }
+
+ sfree (*str);
+ *str = tmp;
+ return (0);
+} /* }}} int gmond_config_set_string */
+
+static int gmond_config_add_metric (oconfig_item_t *ci) /* {{{ */
+{
+ metric_map_t *map;
+ int i;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("gmond plugin: `Metric' blocks need "
+ "exactly one string argument.");
+ return (-1);
+ }
+
+ map = realloc (metric_map, (metric_map_len + 1) * sizeof (*metric_map));
+ if (map == NULL)
+ {
+ ERROR ("gmond plugin: realloc failed.");
+ return (-1);
+ }
+ metric_map = map;
+ map = metric_map + metric_map_len;
+
+ memset (map, 0, sizeof (*map));
+ map->type = NULL;
+ map->type_instance = NULL;
+ map->ds_name = NULL;
+ map->ds_type = -1;
+ map->ds_index = -1;
+
+ map->ganglia_name = strdup (ci->values[0].value.string);
+ if (map->ganglia_name == NULL)
+ {
+ ERROR ("gmond plugin: strdup failed.");
+ return (-1);
+ }
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ if (strcasecmp ("Type", child->key) == 0)
+ gmond_config_set_string (child, &map->type);
+ else if (strcasecmp ("TypeInstance", child->key) == 0)
+ gmond_config_set_string (child, &map->type_instance);
+ else if (strcasecmp ("DataSource", child->key) == 0)
+ gmond_config_set_string (child, &map->ds_name);
+ else
+ {
+ WARNING ("gmond plugin: Unknown configuration option `%s' ignored.",
+ child->key);
+ }
+ }
+
+ if (map->type == NULL)
+ {
+ ERROR ("gmond plugin: No type is set for metric %s.",
+ map->ganglia_name);
+ sfree (map->ganglia_name);
+ sfree (map->type_instance);
+ return (-1);
+ }
+
+ metric_map_len++;
+ return (0);
+} /* }}} int gmond_config_add_metric */
+
+static int gmond_config_set_address (oconfig_item_t *ci, /* {{{ */
+ char **ret_addr, char **ret_port)
+{
+ char *addr;
+ char *port;
+
+ if ((ci->values_num != 1) && (ci->values_num != 2))
+ {
+ WARNING ("gmond plugin: The `%s' config option needs "
+ "one or two string arguments.",
+ ci->key);
+ return (-1);
+ }
+ if ((ci->values[0].type != OCONFIG_TYPE_STRING)
+ || ((ci->values_num == 2)
+ && (ci->values[1].type != OCONFIG_TYPE_STRING)))
+ {
+ WARNING ("gmond plugin: The `%s' config option needs "
+ "one or two string arguments.",
+ ci->key);
+ return (-1);
+ }
+
+ addr = strdup (ci->values[0].value.string);
+ if (ci->values_num == 2)
+ port = strdup (ci->values[1].value.string);
+ else
+ port = NULL;
+
+ if ((addr == NULL) || ((ci->values_num == 2) && (port == NULL)))
+ {
+ ERROR ("gmond plugin: strdup failed.");
+ sfree (addr);
+ sfree (port);
+ return (-1);
+ }
+
+ sfree (*ret_addr);
+ sfree (*ret_port);
+
+ *ret_addr = addr;
+ *ret_port = port;
+
+ return (0);
+} /* }}} int gmond_config_set_address */
+
+static int gmond_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ if (strcasecmp ("MCReceiveFrom", child->key) == 0)
+ gmond_config_set_address (child, &mc_receive_group, &mc_receive_port);
+ else if (strcasecmp ("Metric", child->key) == 0)
+ gmond_config_add_metric (child);
+ else
+ {
+ WARNING ("gmond plugin: Unknown configuration option `%s' ignored.",
+ child->key);
+ }
+ }
+
+ return (0);
+} /* }}} int gmond_config */
+
+static int gmond_init (void) /* {{{ */
+{
+ create_sockets (&mc_send_sockets, &mc_send_sockets_num,
+ (mc_receive_group != NULL) ? mc_receive_group : MC_RECEIVE_GROUP_DEFAULT,
+ (mc_receive_port != NULL) ? mc_receive_port : MC_RECEIVE_PORT_DEFAULT,
+ /* listen = */ 0);
+
+ staging_tree = c_avl_create ((void *) strcmp);
+ if (staging_tree == NULL)
+ {
+ ERROR ("gmond plugin: c_avl_create failed.");
+ return (-1);
+ }
+
+ mc_receive_thread_start ();
+
+ return (0);
+} /* }}} int gmond_init */
+
+static int gmond_shutdown (void) /* {{{ */
+{
+ size_t i;
+
+ mc_receive_thread_stop ();
+
+ pthread_mutex_lock (&mc_send_sockets_lock);
+ for (i = 0; i < mc_send_sockets_num; i++)
+ {
+ close (mc_send_sockets[i].fd);
+ mc_send_sockets[i].fd = -1;
+ }
+ sfree (mc_send_sockets);
+ mc_send_sockets_num = 0;
+ pthread_mutex_unlock (&mc_send_sockets_lock);
+
+
+ return (0);
+} /* }}} int gmond_shutdown */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("gmond", gmond_config);
+ plugin_register_init ("gmond", gmond_init);
+ plugin_register_shutdown ("gmond", gmond_shutdown);
+}
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
lcc_response_t res;
int status;
+ if (c->fh == NULL)
+ {
+ lcc_set_errno (c, EBADF);
+ return (-1);
+ }
+
status = lcc_send (c, command);
if (status != 0)
return (status);
int lcc_connect (const char *address, lcc_connection_t **ret_con) /* {{{ */
{
lcc_connection_t *c;
+ int status;
if (address == NULL)
return (-1);
return (-1);
memset (c, 0, sizeof (*c));
+ status = lcc_open_socket (c, address);
+ if (status != 0)
+ {
+ lcc_disconnect (c);
+ return (status);
+ }
+
*ret_con = c;
- return (lcc_open_socket (c, address));
+ return (0);
} /* }}} int lcc_connect */
int lcc_disconnect (lcc_connection_t *c) /* {{{ */
+++ /dev/null
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "liboping.h"
-
-int main (int argc, char **argv)
-{
- pingobj_t *ping;
- pingobj_iter_t *iter;
-
- int i;
-
- if (argc < 2)
- {
- printf ("Usage: %s <host> [host [host [...]]]\n", argv[0]);
- return (1);
- }
-
- if ((ping = ping_construct ()) == NULL)
- {
- fprintf (stderr, "ping_construct failed\n");
- return (-1);
- }
-
- for (i = 1; i < argc; i++)
- {
- printf ("Adding host `%s'..\n", argv[i]);
-
- if (ping_host_add (ping, argv[i]) > 0)
- {
- fprintf (stderr, "ping_host_add (verplant.org) failed\n");
- return (-1);
- }
- }
-
- while (1)
- {
- if (ping_send (ping) < 0)
- {
- fprintf (stderr, "ping_send failed\n");
- return (-1);
- }
-
- for (iter = ping_iterator_get (ping); iter != NULL; iter = ping_iterator_next (iter))
- {
- const char *host;
- double latency;
-
- host = ping_iterator_get_host (iter);
- latency = ping_iterator_get_latency (iter);
-
- printf ("host = %s, latency = %f\n", host, latency);
- }
-
- sleep (5);
- }
-
- return (0);
-}
#elif defined(HAVE_LIBKSTAT)
/* getpagesize(3C) tells me this does not fail.. */
pagesize = getpagesize ();
- if (get_kstat (&ksp, "unix", 0, "system_pages"))
+ if (get_kstat (&ksp, "unix", 0, "system_pages") != 0)
+ {
ksp = NULL;
+ return (-1);
+ }
#endif /* HAVE_LIBKSTAT */
return (0);
static int create_register_callback (llist_t **list, /* {{{ */
const char *name, void *callback, user_data_t *ud)
{
- char *key;
callback_func_t *cf;
cf = (callback_func_t *) malloc (sizeof (*cf));
if (cf == NULL)
{
ERROR ("plugin: create_register_callback: malloc failed.");
- sfree (key);
return (-1);
}
memset (cf, 0, sizeof (*cf));
int rows_num;
int status;
- int i;
+ int row, col;
/* The user data may hold parameter information, but may be NULL. */
data = udb_query_get_user_data (q);
BAIL_OUT (-1);
}
- for (i = 0; i < column_num; i++) {
+ for (col = 0; col < column_num; ++col) {
/* Pointers returned by `PQfname' are freed by `PQclear' via
* `BAIL_OUT'. */
- column_names[i] = PQfname (res, i);
- if (NULL == column_names[i]) {
- log_err ("PQfname (%i) failed.", i);
+ column_names[col] = PQfname (res, col);
+ if (NULL == column_names[col]) {
+ log_err ("Failed to resolv name of column %i.", col);
BAIL_OUT (-1);
}
}
BAIL_OUT (-1);
}
- for (i = 0; i < rows_num; ++i) {
- int j;
-
- for (j = 0; j < column_num; j++) {
+ for (row = 0; row < rows_num; ++row) {
+ for (col = 0; col < column_num; ++col) {
/* Pointers returned by `PQgetvalue' are freed by `PQclear' via
* `BAIL_OUT'. */
- column_values[j] = PQgetvalue (res, /* row = */ i, /* col = */ j);
- if (NULL == column_values[j]) {
- log_err ("PQgetvalue (%i, %i) failed.", i, j);
+ column_values[col] = PQgetvalue (res, row, col);
+ if (NULL == column_values[col]) {
+ log_err ("Failed to get value at (row = %i, col = %i).",
+ row, col);
break;
}
}
/* check for an error */
- if (j < column_num)
+ if (col < column_num)
continue;
status = udb_query_handle_result (q, column_values);
log_err ("udb_query_handle_result failed with status %i.",
status);
}
- } /* for (i = 0; i < rows_num; ++i) */
+ } /* for (row = 0; row < rows_num; ++row) */
BAIL_OUT (0);
#undef BAIL_OUT
# Pre-defined queries of collectd's postgresql plugin.
+#
+# Do not edit this file. If you want to change any of the query definitions,
+# overwrite them in collectd.conf instead.
+#
+# This file is distributed under the same terms as collectd itself.
<Query backends>
Statement "SELECT count(*) AS count \
ValuesFrom "del"
</Result>
- MaxPGVersion 80299
+ MaxVersion 80299
</Query>
<Query queries>
ValuesFrom "hot_upd"
</Result>
- MinPGVersion 80300
+ MinVersion 80300
</Query>
<Query query_plans>
ValuesFrom "dead"
</Result>
- MinPGVersion 80300
+ MinVersion 80300
</Query>
<Query disk_io>
- Statement "SELECT sum(heap_blks_read) AS heap_read, \
- sum(heap_blks_hit) AS heap_hit, \
- sum(idx_blks_read) AS idx_read, \
- sum(idx_blks_hit) AS idx_hit, \
- sum(toast_blks_read) AS toast_read, \
- sum(toast_blks_hit) AS toast_hit, \
- sum(tidx_blks_read) AS tidx_read, \
- sum(tidx_blks_hit) AS tidx_hit \
+ Statement "SELECT coalesce(sum(heap_blks_read), 0) AS heap_read, \
+ coalesce(sum(heap_blks_hit), 0) AS heap_hit, \
+ coalesce(sum(idx_blks_read), 0) AS idx_read, \
+ coalesce(sum(idx_blks_hit), 0) AS idx_hit, \
+ coalesce(sum(toast_blks_read), 0) AS toast_read, \
+ coalesce(sum(toast_blks_hit), 0) AS toast_hit, \
+ coalesce(sum(tidx_blks_read), 0) AS tidx_read, \
+ coalesce(sum(tidx_blks_hit), 0) AS tidx_hit \
FROM pg_statio_user_tables;"
<Result>
rrd_cache_t *cache_entry;
char **values;
int values_num;
+ int status;
int i;
pthread_mutex_lock (&queue_lock);
while (true)
{
struct timespec ts_wait;
- int status;
while ((flushq_head == NULL) && (queue_head == NULL)
&& (do_shutdown == 0))
* we make a copy of it's values */
pthread_mutex_lock (&cache_lock);
- c_avl_get (cache, queue_entry->filename, (void *) &cache_entry);
+ status = c_avl_get (cache, queue_entry->filename,
+ (void *) &cache_entry);
- values = cache_entry->values;
- values_num = cache_entry->values_num;
+ if (status == 0)
+ {
+ values = cache_entry->values;
+ values_num = cache_entry->values_num;
- cache_entry->values = NULL;
- cache_entry->values_num = 0;
- cache_entry->flags = FLAG_NONE;
+ cache_entry->values = NULL;
+ cache_entry->values_num = 0;
+ cache_entry->flags = FLAG_NONE;
+ }
pthread_mutex_unlock (&cache_lock);
+ if (status != 0)
+ {
+ sfree (queue_entry->filename);
+ sfree (queue_entry);
+ continue;
+ }
+
/* Update `tv_next_update' */
if (write_rate > 0.0)
{
/* Write the values to the RRD-file */
srrd_update (queue_entry->filename, NULL,
values_num, (const char **)values);
- DEBUG ("rrdtool plugin: queue thread: Wrote %i values to %s",
- values_num, queue_entry->filename);
+ DEBUG ("rrdtool plugin: queue thread: Wrote %i value%s to %s",
+ values_num, (values_num == 1) ? "" : "s",
+ queue_entry->filename);
for (i = 0; i < values_num; i++)
{
pthread_mutex_lock (&cache_lock);
+ /* This shouldn't happen, but it did happen at least once, so we'll be
+ * careful. */
+ if (cache == NULL)
+ {
+ pthread_mutex_unlock (&cache_lock);
+ WARNING ("rrdtool plugin: cache == NULL.");
+ return (-1);
+ }
+
c_avl_get (cache, filename, (void *) &rc);
if (rc == NULL)
}
} /* void csnmp_host_open_session */
+/* TODO: Check if negative values wrap around. Problem: negative temperatures. */
static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
double scale, double shift)
{
disk_ops read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
disk_time read:COUNTER:0:1000000, write:COUNTER:0:1000000
dns_answer value:COUNTER:0:65535
+dns_notify value:COUNTER:0:65535
dns_octets queries:COUNTER:0:125000000, responses:COUNTER:0:125000000
dns_opcode value:COUNTER:0:65535
dns_qtype value:COUNTER:0:65535
+dns_qtype_cached value:GAUGE:0:4294967295
+dns_query value:COUNTER:0:65535
dns_question value:COUNTER:0:65535
dns_rcode value:COUNTER:0:65535
+dns_reject value:COUNTER:0:65535
dns_request value:COUNTER:0:65535
dns_resolver value:COUNTER:0:65535
+dns_response value:COUNTER:0:65535
+dns_transfer value:COUNTER:0:65535
dns_update value:COUNTER:0:65535
dns_zops value:COUNTER:0:65535
-dns_notify value:COUNTER:0:65535
-dns_transfer value:COUNTER:0:65535
-dns_query value:COUNTER:0:65535
-dns_response value:COUNTER:0:65535
-dns_reject value:COUNTER:0:65535
email_check value:GAUGE:0:U
email_count value:GAUGE:0:U
email_size value:GAUGE:0:U
{
c_avl_node_t *n;
+ assert (t != NULL);
+
n = search (t, key);
if (n == NULL)
return (-1);