[Nagiosplug-devel] check_sonet plugin

Jim Mozley jim.mozley at exponential-e.com
Thu Apr 22 01:43:05 CEST 2004


Below is a plugin for checking sonet/sdh interfaces. I'm unsure of the 
submission process for contributed plugins but anyone is welcome to use 
it. I've aimed to abide by the plug-in guidelines. One obvious 
difference I can see is that I've used warnings rather than perl -w 
(which I guess could cause problems with anyone using pre 5.6 perl?). 
Happy to correct anything like this should the plugin be deemed useful 
enough or suitable to include in any contrib type distribution.

This has been tested on RiverStone switches so I'd welcome any feedback 
from anyone who can test it on other vendors equipment.

Jim Mozley





#!/usr/local/bin/perl

=head1 Description

This plug-in uses SNMP to check the Sonet MIB (RFC 2558) for the
status of SONET or SDH interfaces.

The information is collected by querying the ifType table to determine
the Sonet interfaces. Each physical sonet interface also has a logical
path interface. If these interfaces are administratively up then the
Sonet MIB status tables for the line sections and path are checked.

The status is defined in the Sonet MIB as an integer representing a
binary whose bit positions indicate problems. See RFC 2558. Line and
section problems are flagged against the physical interface, while path
errors are flagged against the logical path interface.

It is intended to check the sonet interfaces on a host as one service 
rather than treating them as individual services to reduce the number of
SNMP queries to the device.

=cut


use strict;
use warnings;

use POSIX;
use Net::SNMP;
use Getopt::Long;
Getopt::Long::Configure('bundling');

use lib "/usr/local/nagios/libexec"  ;
use utils qw($TIMEOUT %ERRORS &print_revision &support);

my $PROGNAME = "check_sonet";

my $status;
my $state = "UNKNOWN";
my $answer = "";
my $community = "public";
my $port = 161;
my @snmpoids;

my $ifType = '1.3.6.1.2.1.2.2.1.3';
my $ifAdminStatus = '1.3.6.1.2.1.2.2.1.7';
my $ifName = '1.3.6.1.2.1.31.1.1.1.1';
my $ifDescr = '1.3.6.1.2.1.2.2.1.2';
my $lineStatus = '1.3.6.1.2.1.10.39.1.3.1.1.1';
my $sectionStatus = '1.3.6.1.2.1.10.39.1.2.1.1.1';
my $pathStatus = '1.3.6.1.2.1.10.39.2.1.1.1.2';

my $hostname;
my %ifStatus;
my $lineup = 0;
my $sectionup = 0;
my $pathup = 0;
my $iferr = 0;
my $ifmessage = "";
my $snmp_version = 1;

my $opt_h;
my $opt_V;


# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
      print ("ERROR: No snmp response from $hostname (alarm timeout)\n");
      exit $ERRORS{"UNKNOWN"};
};
alarm($TIMEOUT);


#Option checking
$status = GetOptions(
      "V"   => \$opt_V,        "version"         => \$opt_V,
      "h"   => \$opt_h,        "help"            => \$opt_h,
      "v=i" => \$snmp_version, "snmp_version=i"  => \$snmp_version,
      "C=s" => \$community,    "community=s"     => \$community,
      "p=i" => \$port,         "port=i"          => \$port,
      "H=s" => \$hostname,     "hostname=s"      => \$hostname,
      );

if ($status == 0)
{
      print_help() ;
      exit $ERRORS{'OK'};
}


if ($opt_V) {
     print_revision($PROGNAME,'$Revision: 1.0 $ ');
     exit $ERRORS{'OK'};
}

if ($opt_h) {
     print_help();
     exit $ERRORS{'OK'};
}

if (! utils::is_hostname($hostname)){
     usage();
     exit $ERRORS{"UNKNOWN"};
}

unless  ( $snmp_version =~ /[12]/ ) {
     $state='UNKNOWN';
     print ("$state: No support for SNMP v$snmp_version yet\n");
     exit $ERRORS{$state};
}


my ($session, $error) = Net::SNMP->session(
     -hostname  => $hostname,
     -community => $community,
     -port      => $port,
     -version   => $snmp_version
);

unless ( $session ) {
         $state='UNKNOWN';
         $answer=$error;
         print ("$state: $answer");
         exit $ERRORS{$state};
}

push(@snmpoids,
      $ifType,
      $ifAdminStatus,
      $ifDescr,
      $lineStatus,
      $sectionStatus,
      $pathStatus
      );

foreach my $tableoid (@snmpoids) {
     my $varbind_ref;
     my $oid;

     unless (defined($varbind_ref = $session->get_table($tableoid))) {
         $answer = $session->error;
         $session->close;
         $state = 'CRITICAL';
         print ("$state: $answer for $tableoid with snmp version 
$snmp_version\n");
         exit $ERRORS{$state};
     }

     foreach (keys %{$varbind_ref}) {
         my ( $index ) = /.*\.(\d+)$/;
         $ifStatus{$index}{$tableoid} = $varbind_ref->{$_};
     }
}

$session->close;

foreach my $index (keys %ifStatus) {

     # check only if interface is administratively up
     if ($ifStatus{$index}{$ifAdminStatus} == 1 ) {

         # if interface is a sonet (39) check sonetLineCurrentStatus
         # and sonetSectionCurrentStatus tables
         if ( $ifStatus{$index}{$ifType} == 39 ) {
             if ( $ifStatus{$index}{$lineStatus} == 1 ) {
                  $lineup++;
             }
             else {
                 $iferr++;
                 my @status = line_status(
                     bit_positions( $ifStatus{$index}{$lineStatus} )
                     );
                 $ifmessage .= sprintf(
                                       "%s: line status %s ",
                                       $ifStatus{$index}{$ifDescr},
                                       join ',', @status
                                       );
             }
             if ( $ifStatus{$index}{$sectionStatus} == 1 ) {
                 $sectionup++;
             }
             else {
                 $iferr++;
                 my @status = section_status(
                     bit_positions( $ifStatus{$index}{$sectionStatus} )
                     );
                 $ifmessage .= sprintf(
                                       "%s: section status is %s ",
                                       $ifStatus{$index}{$ifDescr},
                                       join ',', @status
                                       );
             }
         }

         # if interface is a sonetPath (50) check
         # sonetPathCurrentStatus table
         if ( $ifStatus{$index}{$ifType} == 50 ) {
             if ( $ifStatus{$index}{$pathStatus} == 1 ) {
                 $pathup++;
             }
             else {
                 $iferr++;
                 my @status = path_status(
                     bit_positions( $ifStatus{$index}{$pathStatus} )
                     );
                 $ifmessage .= sprintf(
                                       "%s: path status is %s ",
                                       $ifStatus{$index}{$ifDescr},
                                       join ',', @status
                                       );
             }
         }

     }
}


if ($iferr > 0) {
     $state = 'CRITICAL';
     $answer = sprintf(
                       "host '%s' - %d errors: ",
                       $hostname,
                       $iferr,
                       );
     $answer = $answer . $ifmessage . "\n";
}
else {
     $state = 'OK';
     $answer = sprintf(
                       "host '%s', %d lines, sections and paths up",
                       $hostname,
                       $lineup + $sectionup + $pathup,
                       );
     $answer = $answer . $ifmessage . "\n";
}

print ("$state: $answer");
exit $ERRORS{$state};


# Name: bit_positions
# Input: integer
# Description: Takes an integer and converts this to a binary. Each bit
# that is set to one is converted back to its decimal value and the list
# of these is returned.
# e.g. we are passed integer 20 representing binary 10100 so we need
# to return 16 and 4.
# Returns: list of bit positions
sub bit_positions {
     my @bits = split //, dec2bin( shift );
     my @bit_pos;

     # go through the bits looking for 1's
     while ( @bits ) {
	my $length = scalar @bits;
	next unless shift @bits; # skip if this bit is zero

	# Build the binary number and add the decimal conversion of
	# it to the list of bit positions.
	my $format = "%-$length" . "s";
	my $binnum = sprintf( $format, 1 );
	$binnum =~ tr/ /0/;
	push @bit_pos, bin2dec( $binnum );
     }

     return reverse @bit_pos;
} # end bit_positions

# Names: line_status, section_status, path_status
# Input: list of numbers
# Description: These return the status values correponding to the
# bit positions as in the SONET-MIB
# Returns: list of status values

sub line_status {
     my %status = (
		  1 => 'sonetLineNoDefect',
		  2 => 'sonetLineAIS',
		  4 => 'sonetLineRDI',
		 );

     return @status{ @_ };
} # end line_status

sub section_status {
     my %status = (
		  1 => 'sonetSectionNoDefect',
		  2 => 'sonetSectionLOS',
		  4 => 'sonetSectionLOF',
		 );

     return @status{ @_ };
} # end section_status

sub path_status {
     my %status = (
		  1 => 'sonetPathNoDefect',
		  2 => 'sonetPathSTSLOP',
		  4 => 'sonetPathSTSAIS',
		  8 => 'sonetPathSTSRDI',
		  16 => 'sonetPathUnequipped',
		  32 => 'sonetPathSignalLabelMismatch',
		 );

     return @status{ @_ };
} # end path_status

# Names: dec2bin, bin2dec
# Input: decimal or binary
# Description: These convert decimal to binary
# Returns: converted number

sub dec2bin {
     my $str = unpack("B32", pack("N", shift));
     $str =~ s/^0+(?=\d)//;   # remove leading zeros
     return $str;
}

sub bin2dec {
     return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
}

sub usage {
     (my $usage = <<'    USAGEEND') =~ s/^\s+//gm;
     Usage:

     check_sonet -H <hostname> -p <port> -v <snmp-version> -C 
<read-community>

     check_sonet -h  # for help
     check_sonet -V  # for version of this plug-in
     USAGEEND
     print $usage;
     support();
     exit $ERRORS{"UNKNOWN"};
} # end usage

sub print_help  {
     (my $help = <<'    HELPEND') =~ s/^\s+//gm;
     Help:

     check_sonet plugin for Nagios monitors the status
     of Sonet network interface on the target host

     Options:

     -H (--hostname)      Hostname to query - (required)
     -p (--port)          SNMP port (default 161)
     -C (--community)     SNMP read community (defaults to public)
     -v (--snmp_version)  1 for SNMPv1 (default) 2 for SNMPv2c
     -V                   Plugin version
     -h (--help)          help

     HELPEND
     print $help;
     print_revision($PROGNAME, '$Revision: 1.0 $');
} # end print_help






More information about the Devel mailing list