#!/usr/local/bin/perl
#
# check PowerDsine power over ethernet injector
# 
# by Doke Scott, doke@udel.edu, 2004.Aug.25 
#
# $Header: /opt/home/doke/work/nagios/RCS/check_powerdsine,v 1.11 2012/06/01 18:58:45 doke Exp $

use strict;
use warnings;
no warnings 'redefine';
use Getopt::Long;
use Net::SNMP;
use lib '/usr/local/nagios/libexec';
use utils qw( %ERRORS );

use vars qw( $verbose $help $community $host @crits @warns @unknowns @oks
    $rc $mib2 $enterprises );


######################


$ENV{PATH} .= "/usr/local/bin:/usr/local/net-snmp/bin:/usr/sfw/bin";

$verbose = 0;

$mib2 = '1.3.6.1.2.1';
$enterprises = '1.3.6.1.4.1';



###########################

sub print_usage { 
    my( $rc ) = @_;
    print qq{Usage: $0 [-hv] -C <community> -H <host> 
    -v   verbose
    -h   help
};
    exit $rc;
    }


Getopt::Long::Configure ("bundling");
GetOptions(
    'H=s' => \$host,
    'C=s' => \$community,
    'v+' => \$verbose,
    'h' => \$help,
    );
&print_usage( 0 ) if ( $help );
&print_usage( 1 ) if ( ! $host );
&print_usage( 1 ) if ( ! $community );



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


check_powerdsine( $host, $community ); 


$rc = 0;   # nagios ok exit code
$" = ", ";
if ( scalar( @crits ) ) { 
    print "CRITICAL: @crits ";
    $rc = 2;
    }
if ( scalar( @warns ) ) { 
    print "; " if ( $rc != 0 );
    print "Warning: @warns ";
    $rc = 1 if ( $rc == 0 );
    }
if ( scalar( @unknowns ) ) { 
    print "; " if ( $rc != 0 );
    print "Unknown: @unknowns ";
    $rc = 3 if ( $rc == 0 );
    }
elsif ( $rc == 0 ) { 
    print "OK -- @oks"; 
    }
print "\n";
exit $rc;   



##########################


sub check_powerdsine {  
    my( $host, $community ) = @_;
    my( $session, $error, $sysDescr, $sysName, $val, @oids, $result, $version, $serialnum );

    ( $session, $error ) = Net::SNMP->session(
        -version => 'snmpv1',    
        -hostname => $host,
        -community => $community,
        #-debug => 0x02
        );
    if ( ! defined( $session ) ) {
        push @unknowns, "snmp setup error: $error";
        return;
        }
    $session->translate( [ '-octetstring' => 0 ] );
    #$session->translate( '-all' => 0 );
    #$session->translate( '-unsigned' => 1 );

    my $sysDescr_oid = "$mib2.1.1.0";
    my $sysName_oid = "$mib2.1.5.0";
    @oids = ( $sysDescr_oid, $sysName_oid );
    $result = $session->get_request( -varbindlist => \@oids );
    $sysName = $result->{ $sysName_oid };
    $sysDescr = $result->{ $sysDescr_oid };
    if ( $verbose ) { 
	print "sysName $sysName\n";
	print "sysDescr $sysDescr\n";
	}
    if ( ! $sysDescr ) { 
	push @crits, "unable to get sysDescr: " . $session->error;
	return;
	}
    elsif ( $sysDescr =~ m/POL AGENT Version\s*(\S+)/i ) { 
	# it's a 6000 series PowerDsine midspan power injector
	$version = $1;
	check_6000( $session );
	push @oks, "version $version";
	}
    elsif ( $sysDescr =~ m/Midspan. App Ver=([\d\.]+)/i ) { 
	# it's a 6500 series PowerDsine midspan power injector
	$version = $1;
	check_6500( $session );
	push @oks, "version $version";
	}
    elsif ( $sysDescr =~ m!Midspan.  Unit S/N=(\d+).  App Ver=([\d\.]+)!i ) { 
	# it's a newer 6500 or 7000 series PowerDsine midspan power injector
	$serialnum = $1;
	$version = $2;
	check_6500( $session );
	push @oks, "version $version, serialnum $serialnum";
	}
    else { 
	push @unknowns, "can't identify model sysDescr '$sysDescr'";
	}
	
    }





sub check_6000 { 
    my( $session ) = @_;
    my( $oid, @oids, $result, $val, $nports, $nactive, $noff, $nerror, 
	$nports2, $nnofault, $nfault );

    my %polSystemLedAc_msgs = ( 
	0 => "unknown",
	1 => "on",
	2 => "off",
	3 => "blink",
	);

    my %polPortLed_msgs = ( 
	0 => "unknown",
	1 => "greenOffOrangeOff",
	2 => "greenOffOrangeOn",
	3 => "greenOnOrangeOff",
	4 => "greenOnOrangeOn",
	5 => "greenOffOrangeBlink",
	6 => "greenBlinkOrangeOff",
	);

    my %polPortFaultDetail_msgs = ( 
	1 => "noFault",
	2 => "voltTooHigh",
	3 => "voltTooLow",
	4 => "tempShutDown",
	5 => "portNotActive",
	6 => "unDefined",
	7 => "hardwareError",
	8 => "unknown error",
	9 => "unknown error",
	10 => "unknown error",
	11 => "powerPlanning",
	12 => "overLoadAndUnderLoad",
	13 => "underLoad",
	14 => "overLoad",
	15 => "unknown error",
	16 => "hardwareCommandError",
	17 => "voltFeed",
	18 => "voltFeedAfterDiode",
	19 => "disChargeLoad",
	20 => "unknown error",
	21 => "notRes",
	22 => "highImpedance",
	);


    # enterprises.polPowerOverLan.powerOverLanProduct.polSystem.polSystemProductTable.polSystemProductEntry.polSystemLedAc
    $oid = "$enterprises.7428.3.3.1.1.11.1";
    @oids = ( $oid );
    $result = $session->get_request( -varbindlist => \@oids );
    $val = $result->{ $oid };
    if ( ! defined $val || $val eq '' ) { 
	push @unknowns, "couldn't get polSystemLedAc: " . $session->error;
	}
    elsif ( $val != 1 ) { 
	push @crits, "system main AC LED is $polSystemLedAc_msgs{ $val }";
	}


    # enterprises.polPowerOverLan.powerOverLanProduct.polSystem.powerOverLanProduct.polPortTable.polPortEntry.polPortLed
    $nports = 0;
    $nactive = 0;
    $noff = 0;
    $nerror = 0;
    $result = snmp_walk( $session, 'polPortLed', "$enterprises.7428.3.6.1.5" );
    for $oid ( keys %$result ) { 
	next unless ( $oid =~ m/7428.3.6.1.5\.(\d+)\.(\d+)$/ );
	$val = $result->{ $oid };
	$verbose && print "port $1.2 $val $polPortLed_msgs{ $val }\n";
	$nports++;
	if ( $val == 3 ) { 
	    # greenOnOrangeOff  active
	    $nactive++;
	    }
	elsif ( $val == 1 ) { 
	    # greenOffOrangeOff  sensing, probably nothing plugged in
	    $noff++;
	    }
	else { 
	    push @crits, "port $1.$2 is $polPortLed_msgs{ $val }";
	    $nerror++;
	    }
	}
    close fH;
    if ( $nports < 1 ) { 
	push @unknowns, "couldn't get polPortLed column: " . $session->error;
	}

    push @oks, "$nports ports, $nactive active"; 
    push @oks, "$noff off" if ( $noff ); 
    push @oks, sprintf( "PD60%02d", $nports );


    # enterprises.polPowerOverLan.powerOverLanProduct.polSystem.powerOverLanProduct.polPortTable.polPortEntry.polPortFaultDetail
    $nports2 = 0;
    $nnofault = 0;
    $nfault = 0;
    $result = snmp_walk( $session, 'polPortFaultDetail', "$enterprises.7428.3.6.1.6" );
    for $oid ( keys %$result ) { 
	next unless ( $oid =~ m/7428.3.6.1.6\.(\d+)\.(\d+)$/ );
	$val = $result->{ $oid };
	$verbose && print "port $1.2 $val $polPortFaultDetail_msgs{ $val }\n";
	$nports2++;
	if ( $val == 1 ) { 
	    # no fault
	    $nnofault++;
	    }
	else { 
	    # oops
	    push @crits, "port $1.$2 is $polPortFaultDetail_msgs{ $val }";
	    $nfault++;
	    }
	}
    close fH;
    if ( $nports2 < 1 ) { 
	push @unknowns, "couldn't get polPortFaultDetail column: " . $session->error;
	}


    if ( $nports2 != $nports ) { 
	push @unknowns, "didn't get as many ports in polPortFaultDetail as in polPortLed column";
	}

    }





sub check_6500 {  
    my( $session ) = @_;
    my( $oid, @oids, $result, $val, $nports, $nactive, $noff, $nerror,
	$pethMainPsePower, $pethMainPseOperStatus,
	$pethMainPseConsumptionPower, $max_power_per_port, $model );

    # mib-2.powerEthernetMIB.pethObjects.pethMainPseObjects.pethMainPseTable.pethMainPseEntry
    $pethMainPsePower = undef;
    $pethMainPseOperStatus = undef;
    $pethMainPseConsumptionPower = undef;
    $result = snmp_walk( $session, 'pethMainPseTable', "$mib2.105.1.3.1.1" );
    for $oid ( keys %$result ) { 
	next unless ( $oid =~ m/105.1.3.1.1\.(\d+)\.(\d+)$/ );
	if ( $1 == 2 ) { 
	    $pethMainPsePower = $result->{ $oid };
	    }
	elsif ( $1 == 3 ) { 
	    $pethMainPseOperStatus = $result->{ $oid };
	    }
	elsif ( $1 == 4 ) { 
	    $pethMainPseConsumptionPower = $result->{ $oid };
	    }
	}
    close fH;
    if ( ! defined $pethMainPseOperStatus ) { 
	push @unknowns, "couldn't get pethMainPseOperStatus: " . $session->error;
	return;
	}
    elsif ( $pethMainPseOperStatus != 1 ) { 
	push @crits, "pethMainPseOperStatus bad";
	return;
	}
    if ( ! defined $pethMainPsePower ) { 
	push @unknowns, "couldn't get pethMainPsePower" . $session->error;
	return;
	}
    $verbose && printf "power available %d\n", $pethMainPsePower;
    if ( ! defined $pethMainPseConsumptionPower ) { 
	push @unknowns, "couldn't get pethMainPseConsumptionPower" 
	    . $session->error;
	return;
	}
    $verbose && printf "power used %d\n", $pethMainPseConsumptionPower;
    if ( $pethMainPseConsumptionPower > ( $pethMainPsePower * 0.80 ) ) { 
	push @unknowns, 
	    "pethMainPseConsumptionPower too high $pethMainPseConsumptionPower";
	}



    my %pethPsePortDetectionStatus_msgs = ( 
	0 => "unknown",
	1 => "disabled",
	2 => "searching",
	3 => "deliveringPower",
	4 => "fault",
	5 => "test",
	6 => "otherfault",
	);


    # mib-2.powerEthernetMIB.pethObjects.pethPsePortTable.pethPsePortEntry.pethPsePortDetectionStatus
    $nports = 0;
    $nactive = 0;
    $noff = 0;
    $nerror = 0;
    $result = snmp_walk( $session, 'pethPsePortTable', "$mib2.105.1.1.1.6" );
    for $oid ( keys %$result ) { 
	next unless ( $oid =~ m/105.1.1.1.6\.(\d+)\.(\d+)$/ );
	$val = $result->{ $oid };
	$verbose && print "port $2 $val $pethPsePortDetectionStatus_msgs{ $val }\n";
	$nports++;
	if ( $val == 3 ) { 
	    # active
	    $nactive++;
	    }
	elsif ( $val == 2 ) { 
	    # searching, probably nothing plugged in
	    $noff++;
	    }
	else { 
	    push @crits, "port $2 is $pethPsePortDetectionStatus_msgs{ $val }";
	    $nerror++;
	    }
	}
    close fH;
    if ( $nports < 1 ) { 
	push @unknowns, "couldn't get pethPsePortDetectionStatus column: " 
	    . $session->error;
	return;
	}

    push @oks, "$nports ports, $nactive active"; 
    push @oks, "$noff off" if ( $noff ); 
    
    $max_power_per_port = $pethMainPsePower / $nports;
    if ( $max_power_per_port > 60 ) { 
	$model = sprintf( "PD70%02d", $nports );
	}
    elsif ( $max_power_per_port > 28 ) { 
	$model = sprintf( "PD65%02d", $nports );
	}
    if ( $model ) { 
	push @oks, $model;
	}
    }






sub snmp_walk {
    my( $session, $name, $baseoid ) = @_;
    my( $result );

    $result = $session->get_table( -baseoid => $baseoid );
    #print "session error ", $session->error(), "\n";
    if ( ! defined( $result )
            && $session->error() !~ m/Requested table is empty/ ) {
        push @unknowns, sprintf( "error walking $name table on %s: %s",
            $session->hostname, $session->error() );
	return undef;
        }
    return $result;
    }

