12 This script allows you to use plugins that were written for Nagios with
13 collectd's C<exec-plugin>. If the plugin checks some kind of threshold, please
14 consider configuring the threshold using collectd's own facilities instead of
15 using this transition layer.
19 use Sys::Hostname ('hostname');
20 use File::Basename ('basename');
21 use Config::General ('ParseConfig');
22 use Regexp::Common ('number');
24 our $ConfigFile = '/etc/exec-nagios.conf';
37 This script reads it's configuration from F</etc/exec-nagios.conf>. The
38 configuration is read using C<Config::General> which understands a Apache-like
39 config syntax, so it's very similar to the F<collectd.conf> syntax, too.
41 Here's a short sample config:
44 <Script /usr/lib/nagios/check_tcp>
45 Arguments -H alice -p 22
48 <Script /usr/lib/nagios/check_dns>
53 The options have the following semantic (i.E<nbsp>e. meaning):
57 =item B<Interval> I<Seconds>
59 Sets the interval in which the plugins are executed. This doesn't need to match
60 the interval setting of the collectd daemon. Usually, you want to execute the
61 Nagios plugins much less often, e.E<nbsp>g. every 300 seconds versus every 10
64 =item E<lt>B<Script> I<File>E<gt>
66 Adds a script to the list of scripts to be executed once per I<Interval>
67 seconds. You can use the following optional arguments to specify the operation
72 =item B<Arguments> I<Arguments>
74 Pass the arguments I<Arguments> to the script. This is often needed with Nagios
75 plugins, because much of the logic is implemented in the plugins, not in the
76 daemon. If you need to specify a warning and/or critical range here, please
77 consider using collectd's own threshold mechanism, which is by far the more
78 elegant solution than this transition layer.
82 If the plugin provides "performance data" the performance data is dispatched to
83 collectd with this type. If no type is configured the data is ignored. Please
84 note that this is limited to types that take exactly one value, such as the
85 type C<delay> in the example above. If you need more complex performance data,
86 rewrite the plugin as a collectd plugin (or at least port it do run directly
87 with the C<exec-plugin>).
95 sub handle_config_addtype
99 for (my $i = 0; $i < @$list; $i++)
101 my ($to, @from) = split (' ', $list->[$i]);
102 for (my $j = 0; $j < @from; $j++)
104 $TypeMap->{$from[$j]} = $to;
107 } # handle_config_addtype
109 sub handle_config_script
116 my $opts = $scripts->{$script};
120 print STDERR "Script `$script' doesn't exist.\n";
124 print STDERR "Script `$script' exists but is not executable.\n";
128 if (ref ($opts) eq 'ARRAY')
133 $opt->{'script'} = $script;
134 push (@$Scripts, $opt);
139 $opts->{'script'} = $script;
140 push (@$Scripts, $opts);
143 } # for (keys %$scripts)
144 } # handle_config_script
150 if (defined ($config->{'addtype'}))
152 if (ref ($config->{'addtype'}) eq 'ARRAY')
154 handle_config_addtype ($config->{'addtype'});
156 elsif (ref ($config->{'addtype'}) eq '')
158 handle_config_addtype ([$config->{'addtype'}]);
162 print STDERR "Cannot handle ref type '"
163 . ref ($config->{'addtype'}) . "' for option 'AddType'.\n";
167 if (defined ($config->{'script'}))
169 if (ref ($config->{'script'}) eq 'HASH')
171 handle_config_script ($config->{'script'});
175 print STDERR "Cannot handle ref type '"
176 . ref ($config->{'script'}) . "' for option 'Script'.\n";
180 if (defined ($config->{'interval'})
181 && (ref ($config->{'interval'}) eq ''))
183 my $num = int ($config->{'interval'});
189 } # handle_config }}}
201 if (($unit =~ m/^mb(yte)?$/i) || ($unit eq 'M'))
203 return ($value * 1000000);
205 elsif ($unit =~ m/^k(b(yte)?)?$/i)
207 return ($value * 1000);
213 sub sanitize_instance
222 $inst =~ s/[^A-Za-z_-]/_/g;
230 sub handle_performance_data
243 if ($line =~ m/^([^=]+)=($RE{num}{real})([^;]*)/)
245 $tinst = sanitize_instance ($1);
246 $value = scale_value ($2, $3);
253 print "PUTVAL $host/$plugin-$pinst/$type-$tinst interval=$Interval ${time}:$value\n";
263 my $host = hostname () || 'localhost';
268 my @longserviceoutput;
270 my $script_name = $script->{'script'};
272 if ($script->{'arguments'})
274 @args = split (' ', $script->{'arguments'});
277 if (!open ($fh, '-|', $script_name, @args))
279 print STDERR "Cannot execute $script_name: $!";
283 $pinst = sanitize_instance (basename ($script_name));
285 # Parse the output of the plugin. The format is seriously fucked up, because
286 # it got extended way beyond what it could handle.
287 while (my $line = <$fh>)
294 ($serviceoutput, $perfdata) = split (m/\s*\|\s*/, $line, 2);
298 push (@serviceperfdata, split (' ', $perfdata));
307 ($longoutput, $perfdata) = split (m/\s*\|\s*/, $line, 2);
309 push (@longserviceoutput, $longoutput);
313 push (@serviceperfdata, split (' ', $perfdata));
319 push (@serviceperfdata, split (' ', $line));
324 # Save the exit status of the check in $state
341 my $type = $script->{'type'} || 'nagios_check';
343 print "PUTNOTIF time=$time severity=$state host=$host plugin=nagios "
344 . "plugin_instance=$pinst type=$type message=$serviceoutput\n";
347 if ($script->{'type'})
349 for (@serviceperfdata)
351 handle_performance_data ($host, 'nagios', $pinst, $script->{'type'},
362 my %config = ParseConfig (-ConfigFile => $ConfigFile,
364 -LowerCaseNames => 1);
365 handle_config (\%config);
370 $next_run = $last_run + $Interval;
377 while ((my $timeleft = ($next_run - time ())) > 0)
386 This script requires the following Perl modules to be installed:
390 =item C<Config::General>
392 =item C<Regexp::Common>
398 L<http://www.nagios.org/>,
399 L<http://nagiosplugins.org/>,
400 L<http://collectd.org/>,
405 Florian octo Forster E<lt>octo at verplant.orgE<gt>
409 # vim: set sw=2 sts=2 ts=8 fdm=marker :