package YATG::Store::NSCA; use strict; use warnings FATAL => 'all'; use YATG::SharedStorage; YATG::SharedStorage->factory(qw(dns_cache)); use Net::DNS; use Regexp::Common 'net'; sub store { my ($config, $stamp, $results) = @_; my $dns_cache = YATG::SharedStorage->dns_cache; my $ignore_ports = $config->{nsca}->{ignore_ports}; my $ignore_descr = $config->{nsca}->{ignore_descr}; my $send_nsca_cmd = &find_command($config, 'send_nsca'); my $send_nsca_cfg = $config->{nsca}->{config_file}; my $service_name = $config->{nsca}->{service_name}; my $nsca_server = $config->{nsca}->{nsca_server} or die "Must specify an nsca server in configuration.\n"; # results look like this: # $results->{device}->{leaf}->{port} = {value} my $status = {}; # build $status->{host}->{port}->{descr => '', state => ''} foreach my $device (keys %$results) { foreach my $leaf (keys %{$results->{$device}}) { foreach my $port (keys %{$results->{$device}->{$leaf}}) { my $host = $dns_cache->{$device} || &get_hostname_for($device) || next; $status->{$host}->{$port}->{$leaf} = $results->{$device}->{$leaf}->{$port}; } # port } # leaf } # device # get handle in outer scope open my $oldout, '>&', \*STDOUT or die "Can't dup STDOUT: $!"; # back up STDOUT then redirect it to quieten send_nsca command unless ($ENV{YATG_DEBUG} || $config->{yatg}->{debug}) { open STDOUT, '>', '/dev/null' or die "Can't redirect STDOUT: $!"; } # open connection to send_nsca open(my $send_nsca, '|-', $send_nsca_cmd, '-H', $nsca_server, '-c', $send_nsca_cfg, '-d', '!', '-to', 1) or die "can't fork send_nsca: $!"; # build and send report for each host foreach my $host (keys %$status) { my $combined_error=q{}; # combine the error messages to fit in nagios report foreach my $port (keys %{$status->{$host}}) { next if $port =~ m/$ignore_ports/; my $ifOperStatus = $status->{$host}->{$port}->{ifOperStatus} || next; my $ifAlias = $status->{$host}->{$port}->{ifAlias} || ''; next if length $ifAlias and $ifAlias =~ m/$ignore_descr/; if ($ifOperStatus ne 'up') { $combined_error .= " WARN: $port ($ifAlias) is $ifOperStatus;"; } } # port # $ECHO "$SERVER;$SERVICE;$RESULT;$OUTPUT" | $CMD -H $DEST_HOST -c $CFG -d ";" if (length $combined_error) { print $send_nsca "$host!$service_name!2!$combined_error\n"; } else { print $send_nsca "$host!$service_name!0! OK: all activated interfaces are running\n"; } } # host # close connection to send_nsca (will chirp) close $send_nsca or die "can't close send_nsca: $!"; # restore STDOUT open STDOUT, '>&', $oldout or die "Can't dup \$oldout: $!"; return 1; } # get hostname for ip sub get_hostname_for { my $device = shift; return $device if $device !~ m/^$RE{net}{IPv4}$/; my $res = Net::DNS::Resolver->new; my $query = $res->search($device); my $hostname = $device; if ($query) { foreach my $rr ($query->answer) { next unless $rr->type eq 'PTR'; $hostname = $rr->ptrdname; last; } } return $hostname; } sub find_command { my ($config, $command) = @_; my $key = $command . '_cmd'; return $config->{nsca}->{$key} if exists $config->{nsca}->{$key} and defined $config->{nsca}->{$key}; use Config; require File::Spec; require ExtUtils::MakeMaker; if( File::Spec->file_name_is_absolute($command) ) { return MM->maybe_command($command); } else { for my $dir ( (split /\Q$Config{path_sep}\E/, $ENV{PATH}), File::Spec->curdir ) { my $abs = File::Spec->catfile($dir, $command); return $abs if $abs = MM->maybe_command($abs); } } die "Could not find command [$command] in path\n"; } 1; __END__ =head1 NAME YATG::Store::NSCA - Back-end module to send polled data to a Nagios service =head1 DESCRIPTION This module checks for device ports which are administratively enabled, but which are showing not connected to anything, at the time of polling. A Nagios CRITICAL result will be generated for such ports. Only one check result per device is submitted (i.e. I one result per port). If there are multiple ports in an alarm state on the same device, then they will all be mentioned in the single service check report. When all enabled ports are connected, an OK result is returned. =head1 CONFIGURATION At a minimum, you must provide details of the location of your Nagios NSCA server, in the main configuration file: nsca: nsca_server: '192.0.2.1' In your YATG configuration file, you must also include this store module on the OIDs required to generate a check result: oids: "ifOperStatus": [ifindex, nsca] "ifAlias": [ifindex, nsca] =head2 Optional Configuration You can also supply the following settings in the main configuration file to override builtin defaults, like so: nsca: send_nsca_cmd: '/usr/bin/send_nsca' config_file: '/etc/send_nsca.cfg' ignore_ports: '^(?:Vlan|Po)\d+$' ignore_descr: '(?:SPAN)' service_name: 'Interfaces Status' =over 4 =item C The location of the C command on your system. YATG will default to searching for C in the application's current PATH. =item C The location of the configuration file for the C program. This defaults to C. =item C Device port names (OID C) to skip when submitting results. This defaults to anything like a Vlan interface, or Cisco PortChannel. Supply the content of a Perl regular expression, as in the example above. =item C Device port description fields matching this value cause the port to be skipped when submitting results. This defaults to anything containing the word "SPAN". Supply the content of a Perl regular expression, as in the example above. =item C The Nagios Service Check name to use when submitting results. This must match the configured name on your Nagios server, and defaults to "Interfaces Status". =back =head1 SEE ALSO =over 4 =item Opsview at L =back =head1 AUTHOR Oliver Gorwits C<< >> =head1 COPYRIGHT & LICENSE Copyright (c) The University of Oxford 2009. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut