#! /usr/bin/perl -wT
#----------------------------------------------------------------------
#
# Author: Martin Fuerstenau, martin.fuerstenau_at_genesix.de
# Date:   08.08.2006
#
# Purpose:
# Checking the status of network interface bonding on Linux. 
#
# Changes:
# 
# - None
#
# How it works:
# 
# First you have to enhance your SNMP by the output of a shell script. I call
# this snmp_bond and it is placed in /opt/bin:
#
#      #!/bin/bash
#      #set -x
#      #
#      # Author: Martin Fuerstenau, martin.fuerstenau_at_genesix.de
#      # Date  : 02082006
#      # Purpose: Pipes the data from /proc/net/bonding to snmp for NAGIOS
# 
#      BONDING_STAT_DIR=/proc/net/bonding/
# 
#      for BONDING_INTERFACE in $(ls $BONDING_STAT_DIR)
#      do
#      ACTIVE=""
#      INTERFACE=""
#      RETURN=$BONDING_INTERFACE
# 
#      while read LINE; do
#            case "$LINE" in
#              "Bonding Mode: "*)
#                  MODE="${LINE##Bonding Mode: }"
#                  RETURN="$RETURN $MODE" ;;
#              "Currently Active Slave: "*)
#                  ACTIVE="${LINE##Currently Active Slave: }"
#                  RETURN="$RETURN $ACTIVE" ;;
#              "Slave Interface: "*)
#                  INTERFACE="${LINE##Slave Interface: }"
#                  RETURN="$RETURN $INTERFACE" ;;
#              "MII Status: "*)
#                  INTERFACE_STATUS="${LINE##MII Status: }" 
#                  RETURN="$RETURN $INTERFACE_STATUS" ;;
#            esac
# 
#      done < $BONDING_STAT_DIR/$BONDING_INTERFACE
#      echo $RETURN
#      done
#
# Next you have to add the following line to your snmpd.conf and restart
# your snmpd:
#
#       exec .1.3.6.1.4.1.2021.170 bonding /opt/bin/snmp_bond
#
# If you do know a snmpwalk you will see the status of your bonding
# interfaces via snmp. Now you can set up a Nagios check for bonding with this
# perl module.
#
# Synopsis:
# 
# check_teaming_broadcom -H <ip-address/hostname> [-C <SNMP community>] [-v <SNMP Version>]
#
#----------------------------------------------------------------------
use strict;
use Getopt::Long;
use vars qw($VERSION $HELP $hostname $host @OIDS $PROGNAME %INTERFACE_ERROR);
use vars qw($community @result $snmpversion $warning $critical $WHATSUP $error);
use lib "/usr/lib/nagios/ops_plugins" ;
use utils qw(%ERRORS &print_revision &support &usage);

# Get the right package from CPAN and install it

use SNMP::Util;
use SNMP::Util_env;
&SNMP::initMib();

$PROGNAME = "check_bonding_linux";

$ENV{'PATH'}='';
$ENV{'BASH_ENV'}=''; 
$ENV{'ENV'}='';
		    
Getopt::Long::Configure('bundling');
GetOptions
	("V"   => \$VERSION, "version"    => \$VERSION,
	 "h"   => \$HELP, "help"       => \$HELP,
	 "v=s" => \$snmpversion, "snmpversion=s" => \$snmpversion,
	 "H=s" => \$hostname, "hostname=s" => \$hostname,
	 "C=s" => \$community, "community=s" => \$community);

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

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

if (!$hostname)
    {
    print_usage();
    usage("Host name/address not specified\n\n");
    }

if ($hostname =~ /([-.A-Za-z0-9]+)/)
   {
   $host = $1;
   }

if (!$host)
    {
    print_usage();
    usage("Invalid host: $hostname\n\n");
    }

if (!$community)
   {
   $community = "public";
   }

if (!$snmpversion)
   {
   $snmpversion = "1";
   }

if (!($snmpversion eq "1" || $snmpversion eq "2c"))
   {
   print "Error! Only SNMP V1 or 2c supported!\n";
   print "Wrong version submitted.\n";
   exit 2;
   }

$WHATSUP=bonding();


if ($WHATSUP == 0 )
   {
   exit $ERRORS{'OK'};
   }
   
if ($WHATSUP == 1 )
   {
   exit $ERRORS{'WARNING'};
   }

if ($WHATSUP == 2 )
   {
   exit $ERRORS{'CRITICAL'};
   }


# --------------- Begin subroutines ----------------------------------------

# We initialize the snmp connection

sub bonding
    {
    my $NumOIDs = 0;
    my $OIDROWCNT = 0;
    my $ROWCNT = 0;
    my $WARN_ME = 0;
    my $OID_LOOP_CNT;
    my $NIC_LOOPCNT;
    my $NIC_CNT;
    my $OFFSET;
    my $OID2GET;
    my $result;
    
    
    my $snmp = new SNMP::Util(-device => $hostname,
                              -community => $community,
			      -snmpversion => $snmpversion,
                              -timeout => 5,
                              -retry => 0,
                              -poll => 'off',
                              -verbose => 'on',
                              -errmode => 'return',
                              -delimiter => ' ',
                             );
    if (!$snmp)
       {
       print "Warning! ";
       print "Server $hostname not reachable!";
       $WARN_ME = 1;
       }

    # Well - we get the f...ing data, but only is there is somethinf in $snmp

    if ($snmp)
       {
       
       @OIDS = $snmp->walk(-format => 'o',
                           -oids =>['.1.3.6.1.4.1.2021.170.101'],
                           -print => 'off');

       if ($snmp->error)
          {
          print "Critical! ";
          $error = $snmp->errmsg;
          print "snmp error = $error";
          $WARN_ME = 2;
	  }
        else
	  {

          # How much elements ( in this case network interfaces) we have in the array?

          $NumOIDs = @OIDS;
          
          # If it is zero make a warning give up

          if ($NumOIDs == 0)
             {
             print "Warning! No data received from host!";
             $WARN_ME = 1;
             }
           else
	     {

             for ( $OID_LOOP_CNT=0;$OID_LOOP_CNT<$NumOIDs;$OID_LOOP_CNT++)
                 {
                 
                 # Now we extract the OIDS out of the array and kick out the trailing dot.
                 
                 $OID2GET = $OIDS[$OID_LOOP_CNT];
                 $OID2GET =~ s/\.$//;
                 
                 # Now we redirect STDERR to /dev/null to suppress some error messages
                 # These messages a caused by the fact tthat there is no describing MIB
                 # for the OIDs used. The perl module tries to buffer the MIBs in a hash
                 # and this causes an error message 
                 
                 # Unfortunalety the missing MIB is the reason for the delivery form of the
                 # result.
                 # 
                 # - One string instead of an array
                 # - Hex instead of ASCII
                 #
                 # Therefore we have to do some conversation work

                 # From here... ---------------------------------------------------------------
               
                 open (STDERR, ">>/dev/null");                
                 $result = $snmp->get('e',$OID2GET);
                 @result = split / /, $result;
                 $result="";

                 # Ok. We take back the redirection.
                 
                 close (STDERR);
                 open(STDERR, ">&STDOUT");
                 
                 # Now we take all the elemments in the array. With each
                 # array loop me make a shift and place the next element in $hex.
                 # A prefix 0x is added so chr can convert the hex value now in $hex
                 # to ASCII.
                 #
                 # As the next step all these values are chained in a scalar ($result).
                 # This scalar is converted to an array like before. this is not the
                 # standard way to fill an array, but it is faster.
                 
                 while(@result)
                      {
                      my $hex=shift(@result);
                      $hex = "0x$hex";
                      my $ascii = chr(hex($hex));
                      $result=$result.$ascii;
                      }

                 @result = split / /, $result;

                 # to here .. --------------------------------------------------------------- 
                 # can be replaced by:
                 #
                 # @result=` /usr/bin/snmpget -c $community -v $snmpversion $hostname $OID2GET `;
                 #
                 # This will work and the result is the same. But system calls should for security 
                 # normally only be done if there is no other possibility.
                 
	         print "<U>Interface</U>: $result[$ROWCNT]<br>";
                 print "Mode : $result[$ROWCNT + 1] $result[$ROWCNT + 2]<br>";
	         print "Active: $result[$ROWCNT + 3]<br>";

                 if ( $result[$ROWCNT + 4] ne "up" )
	            {
	            print "<b>Critical!</b> $result[$ROWCNT + 3] no link signal.<br>";
                    $WARN_ME = 2;
                    }

                 # Ok - now we process the rest of the bunch.
                 # First lets look how much NICs we have per bonding. That means
                 # take the number of elements of @result minus the 5 used above
                 # and divide the rest by two because one value is the interface name
                 # and the second value is the interface status
                
                 $NIC_CNT=@result;
                 $NIC_CNT=($NIC_CNT - 5 ) / 2;
                 $OFFSET=5;
                 
                 for ( $NIC_LOOPCNT=0;$NIC_LOOPCNT<$NIC_CNT;$NIC_LOOPCNT++)
                     {
                     if ($result[$ROWCNT  + $OFFSET + 1] eq "up" )
                        {
 	                print "$result[$ROWCNT + $OFFSET] link signal ok.<br>";
                        }
                     else
                        {
 	                print "<B>Critical!</B> $result[$ROWCNT + $OFFSET] no link signal<br>";
                        $WARN_ME = 2;
                        }
                     $OFFSET=$OFFSET + 2;
                     }
                 }
              }
	   }
       }   
    return $WARN_ME; 
    }

sub print_usage
    {
    print "\nUsage: $PROGNAME -H <host> [-C community] [-v SNMP Version]\n\n";
    print "or\n";
    print "\nUsage: $PROGNAME -V for version.\n\n";
    print "or\n";
    print "\nUsage: $PROGNAME -h for help.\n\n";
    }

sub print_help
    {
    print_revision($PROGNAME,'$Revision: 1.0 $');
    print "Copyright (c) 2006 Martin Fuerstenau - martin.fuerstenauatgenesix.de\n";
    print "This plugin checks the status of network bonding on linux via SNMP.\n";
    print_usage();
    print "       -H, --hostname=HOST             Name or IP address of host to check\n";
    print "       -C, --community=community       SNMPv1 community (default public)\n\n";
    print "       -v, --snmpversion=snmpversion   Version of the SNMP protocol. At present version 1 or 2c\n\n";
    print "       -h, --help                      Short help message\n\n";
    print "       -V, --version                   Prints version of the plugin\n\n";
    print "This plugin uses the 'snmpget' command included with the NET-SNMP package.\n";
    print "If you don't have the package installed, you will need to download it from\n";
    print "http://net-snmp.sourceforge.net before you can use this plugin.\n\n";
    print "This plugin also make use of a enhanced version the perl module SNMP::Util .\n";
    print "You will find it enclosed with this plugin. Be shure it is installed.\n\n";
    support();
    print "\n";
    }
