mirror of https://github.com/sipwise/collectd.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
410 lines
8.0 KiB
410 lines
8.0 KiB
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
=head1 NAME
|
|
|
|
exec-nagios.px
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This script allows you to use plugins that were written for Nagios with
|
|
collectd's C<exec-plugin>. If the plugin checks some kind of threshold, please
|
|
consider configuring the threshold using collectd's own facilities instead of
|
|
using this transition layer.
|
|
|
|
=cut
|
|
|
|
use Sys::Hostname ('hostname');
|
|
use File::Basename ('basename');
|
|
use Config::General ('ParseConfig');
|
|
use Regexp::Common ('number');
|
|
|
|
our $ConfigFile = '/etc/exec-nagios.conf';
|
|
our $TypeMap = {};
|
|
our $Scripts = [];
|
|
our $Interval = 300;
|
|
|
|
main ();
|
|
exit (0);
|
|
|
|
# Configuration
|
|
# {{{
|
|
|
|
=head1 CONFIGURATION
|
|
|
|
This script reads it's configuration from F</etc/exec-nagios.conf>. The
|
|
configuration is read using C<Config::General> which understands a Apache-like
|
|
config syntax, so it's very similar to the F<collectd.conf> syntax, too.
|
|
|
|
Here's a short sample config:
|
|
|
|
Interval 300
|
|
<Script /usr/lib/nagios/check_tcp>
|
|
Arguments -H alice -p 22
|
|
Type delay
|
|
</Script>
|
|
<Script /usr/lib/nagios/check_dns>
|
|
Arguments -H alice
|
|
Type delay
|
|
</Script>
|
|
|
|
The options have the following semantic (i.E<nbsp>e. meaning):
|
|
|
|
=over 4
|
|
|
|
=item B<Interval> I<Seconds>
|
|
|
|
Sets the interval in which the plugins are executed. This doesn't need to match
|
|
the interval setting of the collectd daemon. Usually, you want to execute the
|
|
Nagios plugins much less often, e.E<nbsp>g. every 300 seconds versus every 10
|
|
seconds.
|
|
|
|
=item E<lt>B<Script> I<File>E<gt>
|
|
|
|
Adds a script to the list of scripts to be executed once per I<Interval>
|
|
seconds. You can use the following optional arguments to specify the operation
|
|
further:
|
|
|
|
=over 4
|
|
|
|
=item B<Arguments> I<Arguments>
|
|
|
|
Pass the arguments I<Arguments> to the script. This is often needed with Nagios
|
|
plugins, because much of the logic is implemented in the plugins, not in the
|
|
daemon. If you need to specify a warning and/or critical range here, please
|
|
consider using collectd's own threshold mechanism, which is by far the more
|
|
elegant solution than this transition layer.
|
|
|
|
=item B<Type> I<Type>
|
|
|
|
If the plugin provides "performance data" the performance data is dispatched to
|
|
collectd with this type. If no type is configured the data is ignored. Please
|
|
note that this is limited to types that take exactly one value, such as the
|
|
type C<delay> in the example above. If you need more complex performance data,
|
|
rewrite the plugin as a collectd plugin (or at least port it do run directly
|
|
with the C<exec-plugin>).
|
|
|
|
=back
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub handle_config_addtype
|
|
{
|
|
my $list = shift;
|
|
|
|
for (my $i = 0; $i < @$list; $i++)
|
|
{
|
|
my ($to, @from) = split (' ', $list->[$i]);
|
|
for (my $j = 0; $j < @from; $j++)
|
|
{
|
|
$TypeMap->{$from[$j]} = $to;
|
|
}
|
|
}
|
|
} # handle_config_addtype
|
|
|
|
sub handle_config_script
|
|
{
|
|
my $scripts = shift;
|
|
|
|
for (keys %$scripts)
|
|
{
|
|
my $script = $_;
|
|
my $opts = $scripts->{$script};
|
|
|
|
if (!-e $script)
|
|
{
|
|
print STDERR "Script `$script' doesn't exist.\n";
|
|
}
|
|
elsif (!-x $script)
|
|
{
|
|
print STDERR "Script `$script' exists but is not executable.\n";
|
|
}
|
|
else
|
|
{
|
|
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
|
|
|
|
sub handle_config
|
|
{
|
|
my $config = shift;
|
|
|
|
if (defined ($config->{'addtype'}))
|
|
{
|
|
if (ref ($config->{'addtype'}) eq 'ARRAY')
|
|
{
|
|
handle_config_addtype ($config->{'addtype'});
|
|
}
|
|
elsif (ref ($config->{'addtype'}) eq '')
|
|
{
|
|
handle_config_addtype ([$config->{'addtype'}]);
|
|
}
|
|
else
|
|
{
|
|
print STDERR "Cannot handle ref type '"
|
|
. ref ($config->{'addtype'}) . "' for option 'AddType'.\n";
|
|
}
|
|
}
|
|
|
|
if (defined ($config->{'script'}))
|
|
{
|
|
if (ref ($config->{'script'}) eq 'HASH')
|
|
{
|
|
handle_config_script ($config->{'script'});
|
|
}
|
|
else
|
|
{
|
|
print STDERR "Cannot handle ref type '"
|
|
. ref ($config->{'script'}) . "' for option 'Script'.\n";
|
|
}
|
|
}
|
|
|
|
if (defined ($config->{'interval'})
|
|
&& (ref ($config->{'interval'}) eq ''))
|
|
{
|
|
my $num = int ($config->{'interval'});
|
|
if ($num > 0)
|
|
{
|
|
$Interval = $num;
|
|
}
|
|
}
|
|
} # handle_config }}}
|
|
|
|
sub scale_value
|
|
{
|
|
my $value = shift;
|
|
my $unit = shift;
|
|
|
|
if (!$unit)
|
|
{
|
|
return ($value);
|
|
}
|
|
|
|
if (($unit =~ m/^mb(yte)?$/i) || ($unit eq 'M'))
|
|
{
|
|
return ($value * 1000000);
|
|
}
|
|
elsif ($unit =~ m/^k(b(yte)?)?$/i)
|
|
{
|
|
return ($value * 1000);
|
|
}
|
|
|
|
return ($value);
|
|
}
|
|
|
|
sub sanitize_instance
|
|
{
|
|
my $inst = shift;
|
|
|
|
if ($inst eq '/')
|
|
{
|
|
return ('root');
|
|
}
|
|
|
|
$inst =~ s/[^A-Za-z_-]/_/g;
|
|
$inst =~ s/__+/_/g;
|
|
$inst =~ s/^_//;
|
|
$inst =~ s/_$//;
|
|
|
|
return ($inst);
|
|
}
|
|
|
|
sub handle_performance_data
|
|
{
|
|
my $host = shift;
|
|
my $plugin = shift;
|
|
my $pinst = shift;
|
|
my $type = shift;
|
|
my $time = shift;
|
|
my $line = shift;
|
|
|
|
my $tinst;
|
|
my $value;
|
|
my $unit;
|
|
|
|
if ($line =~ m/^([^=]+)=($RE{num}{real})([^;]*)/)
|
|
{
|
|
$tinst = sanitize_instance ($1);
|
|
$value = scale_value ($2, $3);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
print "PUTVAL $host/$plugin-$pinst/$type-$tinst interval=$Interval ${time}:$value\n";
|
|
}
|
|
|
|
sub execute_script
|
|
{
|
|
my $fh;
|
|
my $pinst;
|
|
my $time = time ();
|
|
my $script = shift;
|
|
my @args = ();
|
|
my $host = hostname () || 'localhost';
|
|
|
|
my $state = 0;
|
|
my $serviceoutput;
|
|
my @serviceperfdata;
|
|
my @longserviceoutput;
|
|
|
|
my $script_name = $script->{'script'};
|
|
|
|
if ($script->{'arguments'})
|
|
{
|
|
@args = split (' ', $script->{'arguments'});
|
|
}
|
|
|
|
if (!open ($fh, '-|', $script_name, @args))
|
|
{
|
|
print STDERR "Cannot execute $script_name: $!";
|
|
return;
|
|
}
|
|
|
|
$pinst = sanitize_instance (basename ($script_name));
|
|
|
|
# Parse the output of the plugin. The format is seriously fucked up, because
|
|
# it got extended way beyond what it could handle.
|
|
while (my $line = <$fh>)
|
|
{
|
|
chomp ($line);
|
|
|
|
if ($state == 0)
|
|
{
|
|
my $perfdata;
|
|
($serviceoutput, $perfdata) = split (m/\s*\|\s*/, $line, 2);
|
|
|
|
if ($perfdata)
|
|
{
|
|
push (@serviceperfdata, split (' ', $perfdata));
|
|
}
|
|
|
|
$state = 1;
|
|
}
|
|
elsif ($state == 1)
|
|
{
|
|
my $longoutput;
|
|
my $perfdata;
|
|
($longoutput, $perfdata) = split (m/\s*\|\s*/, $line, 2);
|
|
|
|
push (@longserviceoutput, $longoutput);
|
|
|
|
if ($perfdata)
|
|
{
|
|
push (@serviceperfdata, split (' ', $perfdata));
|
|
$state = 2;
|
|
}
|
|
}
|
|
else # ($state == 2)
|
|
{
|
|
push (@serviceperfdata, split (' ', $line));
|
|
}
|
|
}
|
|
|
|
close ($fh);
|
|
# Save the exit status of the check in $state
|
|
$state = $?;
|
|
|
|
if ($state == 0)
|
|
{
|
|
$state = 'okay';
|
|
}
|
|
elsif ($state == 1)
|
|
{
|
|
$state = 'warning';
|
|
}
|
|
else
|
|
{
|
|
$state = 'failure';
|
|
}
|
|
|
|
{
|
|
my $type = $script->{'type'} || 'nagios_check';
|
|
|
|
print "PUTNOTIF time=$time severity=$state host=$host plugin=nagios "
|
|
. "plugin_instance=$pinst type=$type message=$serviceoutput\n";
|
|
}
|
|
|
|
if ($script->{'type'})
|
|
{
|
|
for (@serviceperfdata)
|
|
{
|
|
handle_performance_data ($host, 'nagios', $pinst, $script->{'type'},
|
|
$time, $_);
|
|
}
|
|
}
|
|
} # execute_script
|
|
|
|
sub main
|
|
{
|
|
my $last_run;
|
|
my $next_run;
|
|
|
|
my %config = ParseConfig (-ConfigFile => $ConfigFile,
|
|
-AutoTrue => 1,
|
|
-LowerCaseNames => 1);
|
|
handle_config (\%config);
|
|
|
|
while (42)
|
|
{
|
|
$last_run = time ();
|
|
$next_run = $last_run + $Interval;
|
|
|
|
for (@$Scripts)
|
|
{
|
|
execute_script ($_);
|
|
}
|
|
|
|
while ((my $timeleft = ($next_run - time ())) > 0)
|
|
{
|
|
sleep ($timeleft);
|
|
}
|
|
}
|
|
} # main
|
|
|
|
=head1 REQUIREMENTS
|
|
|
|
This script requires the following Perl modules to be installed:
|
|
|
|
=over 4
|
|
|
|
=item C<Config::General>
|
|
|
|
=item C<Regexp::Common>
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<http://www.nagios.org/>,
|
|
L<http://nagiosplugins.org/>,
|
|
L<http://collectd.org/>,
|
|
L<collectd-exec(5)>
|
|
|
|
=head1 AUTHOR
|
|
|
|
Florian octo Forster E<lt>octo at verplant.orgE<gt>
|
|
|
|
=cut
|
|
|
|
# vim: set sw=2 sts=2 ts=8 fdm=marker :
|