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';
28 our $Interval = defined ($ENV{'COLLECTD_INTERVAL'}) ? (0 + $ENV{'COLLECTD_INTERVAL'}) : 300;
29 our $Hostname = defined ($ENV{'COLLECTD_HOSTNAME'}) ? $ENV{'COLLECTD_HOSTNAME'} : '';
39 This script reads its configuration from F</etc/exec-nagios.conf>. The
40 configuration is read using C<Config::General> which understands a Apache-like
41 config syntax, so it's very similar to the F<collectd.conf> syntax, too.
43 Here's a short sample config:
45 NRPEConfig "/etc/nrpe.cfg"
47 <Script /usr/lib/nagios/check_tcp>
48 Arguments -H alice -p 22
51 <Script /usr/lib/nagios/check_dns>
56 The options have the following semantic (i.E<nbsp>e. meaning):
60 =item B<NRPEConfig> I<File>
62 Read the NRPE config and add the command definitions to an alias table. After
63 reading the file you can use the NRPE command name rather than the script's
64 filename within B<Script> blocks (see below). If both, the NRPE config and the
65 B<Script> block, define arguments they will be merged by concatenating the
66 arguments together in the order "NRPE-args Script-args".
68 Please note that this option is rather dumb. It does not support "command
69 argument processing" (i.e. replacing C<$ARG1$> and friends), inclusion of other
70 NRPE config files, include directories etc.
72 =item B<Interval> I<Seconds>
74 Sets the interval in which the plugins are executed. This doesn't need to match
75 the interval setting of the collectd daemon. Usually, you want to execute the
76 Nagios plugins much less often, e.E<nbsp>g. every 300 seconds versus every 10
79 =item E<lt>B<Script> I<File>E<gt>
81 Adds a script to the list of scripts to be executed once per I<Interval>
82 seconds. If the B<NRPEConfig> is given above the B<Script> block, you may use
83 the NRPE command name rather than the script's filename. You can use the
84 following optional arguments to specify the operation further:
88 =item B<Arguments> I<Arguments>
90 Pass the arguments I<Arguments> to the script. This is often needed with Nagios
91 plugins, because much of the logic is implemented in the plugins, not in the
92 daemon. If you need to specify a warning and/or critical range here, please
93 consider using collectd's own threshold mechanism, which is by far the more
94 elegant solution than this transition layer.
98 If the plugin provides "performance data" the performance data is dispatched to
99 collectd with this type. If no type is configured the data is ignored. Please
100 note that this is limited to types that take exactly one value, such as the
101 type C<delay> in the example above. If you need more complex performance data,
102 rewrite the plugin as a collectd plugin (or at least port it do run directly
103 with the C<exec-plugin>).
117 $status = open ($fh, '<', $file);
120 print STDERR "Reading NRPE config from \"$file\" failed: $!\n";
129 if ($line =~ m/^\s*command\[([^\]]+)\]\s*=\s*(.+)$/)
135 ($script, $arguments) = split (' ', $2, 2);
137 if ($NRPEMap->{$alias})
139 print STDERR "Warning: NRPE command \"$alias\" redefined.\n";
142 $NRPEMap->{$alias} = { script => $script };
145 $NRPEMap->{$alias}{'arguments'} = $arguments;
153 sub handle_config_addtype
157 for (my $i = 0; $i < @$list; $i++)
159 my ($to, @from) = split (' ', $list->[$i]);
160 for (my $j = 0; $j < @from; $j++)
162 $TypeMap->{$from[$j]} = $to;
165 } # handle_config_addtype
167 # Update the script record. This function adds the name of the script /
168 # executable to the hash and merges the configured and NRPE arguments if
170 sub update_script_opts
174 my $nrpe_args = shift;
176 $opts->{'script'} = $script;
180 if ($opts->{'arguments'})
182 $opts->{'arguments'} = $nrpe_args . ' ' . $opts->{'arguments'};
186 $opts->{'arguments'} = $nrpe_args;
189 } # update_script_opts
191 sub handle_config_script
198 my $opts = $scripts->{$script};
202 # Check if the script exists in the NRPE map. If so, replace the alias name
203 # with the actual script name.
204 if ($NRPEMap->{$script})
206 if ($NRPEMap->{$script}{'arguments'})
208 $nrpe_args = $NRPEMap->{$script}{'arguments'};
210 $script = $NRPEMap->{$script}{'script'};
213 # Check if the script exists and is executable.
216 print STDERR "Script `$script' doesn't exist.\n";
220 print STDERR "Script `$script' exists but is not executable.\n";
224 # Add the script to the global @$Script array.
225 if (ref ($opts) eq 'ARRAY')
230 update_script_opts ($opt, $script, $nrpe_args);
231 push (@$Scripts, $opt);
236 update_script_opts ($opts, $script, $nrpe_args);
237 push (@$Scripts, $opts);
240 } # for (keys %$scripts)
241 } # handle_config_script
247 if (defined ($config->{'nrpeconfig'}))
249 if (ref ($config->{'nrpeconfig'}) eq 'ARRAY')
251 for (@{$config->{'nrpeconfig'}})
253 parse_nrpe_conf ($_);
256 elsif (ref ($config->{'nrpeconfig'}) eq '')
258 parse_nrpe_conf ($config->{'nrpeconfig'});
262 print STDERR "Cannot handle ref type '"
263 . ref ($config->{'nrpeconfig'}) . "' for option 'NRPEConfig'.\n";
267 if (defined ($config->{'addtype'}))
269 if (ref ($config->{'addtype'}) eq 'ARRAY')
271 handle_config_addtype ($config->{'addtype'});
273 elsif (ref ($config->{'addtype'}) eq '')
275 handle_config_addtype ([$config->{'addtype'}]);
279 print STDERR "Cannot handle ref type '"
280 . ref ($config->{'addtype'}) . "' for option 'AddType'.\n";
284 if (defined ($config->{'script'}))
286 if (ref ($config->{'script'}) eq 'HASH')
288 handle_config_script ($config->{'script'});
292 print STDERR "Cannot handle ref type '"
293 . ref ($config->{'script'}) . "' for option 'Script'.\n";
297 if (defined ($config->{'interval'})
298 && (ref ($config->{'interval'}) eq ''))
300 my $num = int ($config->{'interval'});
306 } # handle_config }}}
318 if (($unit =~ m/^mb(yte)?$/i) || ($unit eq 'M'))
320 return ($value * 1000000);
322 elsif ($unit =~ m/^k(b(yte)?)?$/i)
324 return ($value * 1000);
330 sub sanitize_instance
339 $inst =~ s/[^A-Za-z_-]/_/g;
347 sub handle_performance_data
355 my $ident = "$host/$plugin-$pinst/$type-$tinst";
361 if ($line =~ m/^([^=]+)=($RE{num}{real})([^;]*)/)
363 $tinst = sanitize_instance ($1);
364 $value = scale_value ($2, $3);
373 print qq(PUTVAL "$ident" interval=$Interval ${time}:$value\n);
383 my $host = $Hostname || hostname () || 'localhost';
388 my @longserviceoutput;
390 my $script_name = $script->{'script'};
392 if ($script->{'arguments'})
394 @args = split (' ', $script->{'arguments'});
397 if (!open ($fh, '-|', $script_name, @args))
399 print STDERR "Cannot execute $script_name: $!";
403 $pinst = sanitize_instance (basename ($script_name));
405 # Parse the output of the plugin. The format is seriously fucked up, because
406 # it got extended way beyond what it could handle.
407 while (my $line = <$fh>)
414 ($serviceoutput, $perfdata) = split (m/\s*\|\s*/, $line, 2);
418 push (@serviceperfdata, split (' ', $perfdata));
427 ($longoutput, $perfdata) = split (m/\s*\|\s*/, $line, 2);
429 push (@longserviceoutput, $longoutput);
433 push (@serviceperfdata, split (' ', $perfdata));
439 push (@serviceperfdata, split (' ', $line));
444 # Save the exit status of the check in $state
461 my $type = $script->{'type'} || 'nagios_check';
463 print "PUTNOTIF time=$time severity=$state host=$host plugin=nagios "
464 . "plugin_instance=$pinst type=$type message=$serviceoutput\n";
467 if ($script->{'type'})
469 for (@serviceperfdata)
471 handle_performance_data ($host, 'nagios', $pinst, $script->{'type'},
482 my %config = ParseConfig (-ConfigFile => $ConfigFile,
484 -LowerCaseNames => 1);
485 handle_config (\%config);
490 $next_run = $last_run + $Interval;
497 while ((my $timeleft = ($next_run - time ())) > 0)
506 This script requires the following Perl modules to be installed:
510 =item C<Config::General>
512 =item C<Regexp::Common>
518 L<http://www.nagios.org/>,
519 L<http://nagiosplugins.org/>,
520 L<http://collectd.org/>,
525 Florian octo Forster E<lt>octo at verplant.orgE<gt>
529 # vim: set sw=2 sts=2 ts=8 fdm=marker :