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';
38 This script reads it's configuration from F</etc/exec-nagios.conf>. The
39 configuration is read using C<Config::General> which understands a Apache-like
40 config syntax, so it's very similar to the F<collectd.conf> syntax, too.
42 Here's a short sample config:
44 NRPEConfig "/etc/nrpe.cfg"
46 <Script /usr/lib/nagios/check_tcp>
47 Arguments -H alice -p 22
50 <Script /usr/lib/nagios/check_dns>
55 The options have the following semantic (i.E<nbsp>e. meaning):
59 =item B<NRPEConfig> I<File>
61 Read the NRPE config and add the command definitions to an alias table. After
62 reading the file you can use the NRPE command name rather than the script's
63 filename within B<Script> blocks (see below). If both, the NRPE config and the
64 B<Script> block, define arguments they will be merged by concatenating the
65 arguments together in the order "NRPE-args Script-args".
67 Please note that this option is rather dumb. It does not support "command
68 argument processing" (i.e. replacing C<$ARG1$> and friends), inclusion of other
69 NRPE config files, include directories etc.
71 =item B<Interval> I<Seconds>
73 Sets the interval in which the plugins are executed. This doesn't need to match
74 the interval setting of the collectd daemon. Usually, you want to execute the
75 Nagios plugins much less often, e.E<nbsp>g. every 300 seconds versus every 10
78 =item E<lt>B<Script> I<File>E<gt>
80 Adds a script to the list of scripts to be executed once per I<Interval>
81 seconds. If the B<NRPEConfig> is given above the B<Script> block, you may use
82 the NRPE command name rather than the script's filename. You can use the
83 following optional arguments to specify the operation further:
87 =item B<Arguments> I<Arguments>
89 Pass the arguments I<Arguments> to the script. This is often needed with Nagios
90 plugins, because much of the logic is implemented in the plugins, not in the
91 daemon. If you need to specify a warning and/or critical range here, please
92 consider using collectd's own threshold mechanism, which is by far the more
93 elegant solution than this transition layer.
97 If the plugin provides "performance data" the performance data is dispatched to
98 collectd with this type. If no type is configured the data is ignored. Please
99 note that this is limited to types that take exactly one value, such as the
100 type C<delay> in the example above. If you need more complex performance data,
101 rewrite the plugin as a collectd plugin (or at least port it do run directly
102 with the C<exec-plugin>).
116 $status = open ($fh, '<', $file);
119 print STDERR "Reading NRPE config from \"$file\" failed: $!\n";
128 if ($line =~ m/^\s*command\[([^\]]+)\]\s*=\s*(.+)$/)
134 ($script, $arguments) = split (' ', $2, 2);
136 if ($NRPEMap->{$alias})
138 print STDERR "Warning: NRPE command \"$alias\" redefined.\n";
141 $NRPEMap->{$alias} = { script => $script };
144 $NRPEMap->{$alias}{'arguments'} = $arguments;
152 sub handle_config_addtype
156 for (my $i = 0; $i < @$list; $i++)
158 my ($to, @from) = split (' ', $list->[$i]);
159 for (my $j = 0; $j < @from; $j++)
161 $TypeMap->{$from[$j]} = $to;
164 } # handle_config_addtype
166 # Update the script record. This function adds the name of the script /
167 # executable to the hash and merges the configured and NRPE arguments if
169 sub update_script_opts
173 my $nrpe_args = shift;
175 $opts->{'script'} = $script;
179 if ($opts->{'arguments'})
181 $opts->{'arguments'} = $nrpe_args . ' ' . $opts->{'arguments'};
185 $opts->{'arguments'} = $nrpe_args;
188 } # update_script_opts
190 sub handle_config_script
197 my $opts = $scripts->{$script};
201 # Check if the script exists in the NRPE map. If so, replace the alias name
202 # with the actual script name.
203 if ($NRPEMap->{$script})
205 if ($NRPEMap->{$script}{'arguments'})
207 $nrpe_args = $NRPEMap->{$script}{'arguments'};
209 $script = $NRPEMap->{$script}{'script'};
212 # Check if the script exists and is executable.
215 print STDERR "Script `$script' doesn't exist.\n";
219 print STDERR "Script `$script' exists but is not executable.\n";
223 # Add the script to the global @$Script array.
224 if (ref ($opts) eq 'ARRAY')
229 update_script_opts ($opt, $script, $nrpe_args);
230 push (@$Scripts, $opt);
235 update_script_opts ($opts, $script, $nrpe_args);
236 push (@$Scripts, $opts);
239 } # for (keys %$scripts)
240 } # handle_config_script
246 if (defined ($config->{'nrpeconfig'}))
248 if (ref ($config->{'nrpeconfig'}) eq 'ARRAY')
250 for (@{$config->{'nrpeconfig'}})
252 parse_nrpe_conf ($_);
255 elsif (ref ($config->{'nrpeconfig'}) eq '')
257 parse_nrpe_conf ($config->{'nrpeconfig'});
261 print STDERR "Cannot handle ref type '"
262 . ref ($config->{'nrpeconfig'}) . "' for option 'NRPEConfig'.\n";
266 if (defined ($config->{'addtype'}))
268 if (ref ($config->{'addtype'}) eq 'ARRAY')
270 handle_config_addtype ($config->{'addtype'});
272 elsif (ref ($config->{'addtype'}) eq '')
274 handle_config_addtype ([$config->{'addtype'}]);
278 print STDERR "Cannot handle ref type '"
279 . ref ($config->{'addtype'}) . "' for option 'AddType'.\n";
283 if (defined ($config->{'script'}))
285 if (ref ($config->{'script'}) eq 'HASH')
287 handle_config_script ($config->{'script'});
291 print STDERR "Cannot handle ref type '"
292 . ref ($config->{'script'}) . "' for option 'Script'.\n";
296 if (defined ($config->{'interval'})
297 && (ref ($config->{'interval'}) eq ''))
299 my $num = int ($config->{'interval'});
305 } # handle_config }}}
317 if (($unit =~ m/^mb(yte)?$/i) || ($unit eq 'M'))
319 return ($value * 1000000);
321 elsif ($unit =~ m/^k(b(yte)?)?$/i)
323 return ($value * 1000);
329 sub sanitize_instance
338 $inst =~ s/[^A-Za-z_-]/_/g;
346 sub handle_performance_data
359 if ($line =~ m/^([^=]+)=($RE{num}{real})([^;]*)/)
361 $tinst = sanitize_instance ($1);
362 $value = scale_value ($2, $3);
369 print "PUTVAL $host/$plugin-$pinst/$type-$tinst interval=$Interval ${time}:$value\n";
379 my $host = hostname () || 'localhost';
384 my @longserviceoutput;
386 my $script_name = $script->{'script'};
388 if ($script->{'arguments'})
390 @args = split (' ', $script->{'arguments'});
393 if (!open ($fh, '-|', $script_name, @args))
395 print STDERR "Cannot execute $script_name: $!";
399 $pinst = sanitize_instance (basename ($script_name));
401 # Parse the output of the plugin. The format is seriously fucked up, because
402 # it got extended way beyond what it could handle.
403 while (my $line = <$fh>)
410 ($serviceoutput, $perfdata) = split (m/\s*\|\s*/, $line, 2);
414 push (@serviceperfdata, split (' ', $perfdata));
423 ($longoutput, $perfdata) = split (m/\s*\|\s*/, $line, 2);
425 push (@longserviceoutput, $longoutput);
429 push (@serviceperfdata, split (' ', $perfdata));
435 push (@serviceperfdata, split (' ', $line));
440 # Save the exit status of the check in $state
457 my $type = $script->{'type'} || 'nagios_check';
459 print "PUTNOTIF time=$time severity=$state host=$host plugin=nagios "
460 . "plugin_instance=$pinst type=$type message=$serviceoutput\n";
463 if ($script->{'type'})
465 for (@serviceperfdata)
467 handle_performance_data ($host, 'nagios', $pinst, $script->{'type'},
478 my %config = ParseConfig (-ConfigFile => $ConfigFile,
480 -LowerCaseNames => 1);
481 handle_config (\%config);
486 $next_run = $last_run + $Interval;
493 while ((my $timeleft = ($next_run - time ())) > 0)
502 This script requires the following Perl modules to be installed:
506 =item C<Config::General>
508 =item C<Regexp::Common>
514 L<http://www.nagios.org/>,
515 L<http://nagiosplugins.org/>,
516 L<http://collectd.org/>,
521 Florian octo Forster E<lt>octo at verplant.orgE<gt>
525 # vim: set sw=2 sts=2 ts=8 fdm=marker :