#!/usr/local/bin/perl
#
# test Xytratex X24 array controller
#
# $Header: /opt/home/doke/work/nagios/RCS/check_x24,v 1.3 2008/08/12 16:59:58 doke Exp $

my $community = 'public';

use strict;   # for testing
use warnings;   # for testing
use Getopt::Long;
use Net::SNMP;

my $age_limit = 60 * 60 * 100;  # in timeticks, ie centi-seconds
my $host = '';
my $verbose = 0;
my $help = 0;
my $mib2 = '1.3.6.1.2.1';
my $enterprises = '1.3.6.1.4.1';

my( @crits, @warns, @unknowns );

sub usage {
    my( $rc ) = @_;
    print "Usage: $0 [-vh] -H <host> [-c <community>]
    -H s  hostname
    -c s  snmp community
    -v    verbose
    -h    help
";
    exit $rc;
    }


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

&usage( 0 ) if ( ! $host );

&check();

my $rc = 0;
my $sep = '';
if ( $#crits >= 0 ) {
    $rc = 2;
    print "CRITICAL ", join( ", ", @crits );
    $sep = '; ';
    }
if ( $#warns >= 0 ) {
    $rc = 1 if ( $rc == 0 );
    print $sep, "Warning ", join( ", ", @warns );
    $sep = '; ';
    }
if ( $#unknowns >= 0 ) {
    $rc = -1 if ( $rc == 0 );
    print $sep, "Unknown ", join( ", ", @unknowns );
    }
if ( $rc == 0 ) {
    print "Ok"
    }
print "\n";
exit $rc;


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


sub check {
    my( $session, $error, $result, $oid, $val, $i, %names, $name, %msgs,
	$msg, %portstates, %portnames, $n, $sysDescr, $sysUpTime, $sysName,
	@conUnitSEventTime, @conUnitEventDescr, @oids, $cmd, $pingout );

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

    my $sysDescr_oid = "$mib2.1.1.0";
    my $sysUpTime_oid = "$mib2.1.3.0";
    my $sysName_oid = "$mib2.1.5.0";
    @oids = ( $sysDescr_oid, $sysUpTime_oid, $sysName_oid );
    $result = $session->get_request( -varbindlist => \@oids );
    if ( ! defined( $result ) ) { 
	$verbose && print "snmp error ", $session->error(), "\n";
	push @unknowns, "couldn't get sysDescr" . $session->error();

	# we can't snmp to it, let's see if we can ping it
	$cmd = "ping $host 1 2>&1 |";
	$verbose && print "+ $cmd\n";
	if ( open( pH, $cmd ) ) {
	    $pingout = '';
	    while ( <pH> ) {
		$verbose && print ">$_";
		chomp;
		$pingout .= $_ . ', ';
		}
	    close pH;
	    $rc = $? >> 8;
	    if ( $rc ) {
		$pingout =~ s/, $//;
		push @crits, "ping failed: $pingout";
		}
	    else {
		push @unknowns, "ping worked, snmp may not be configured\n";
		}
	    }
	else {
	    push @unknowns, "unable to run ping: $!\n";
	    }
	return;
	}
    $sysDescr = $result->{ $sysDescr_oid };
    $sysUpTime = $result->{ $sysUpTime_oid };
    $sysName = $result->{ $sysName_oid };

    if ( $verbose ) {
	print "sysDescr = $sysDescr\n";
	print "sysUpTime = $sysUpTime\n";
	print "sysName = $sysName\n";
	}


    # FCMGMT-MIB::connUnitStatus
    $result = walk_table( $session, 'connUnitStatus', ".1.3.6.1.3.94.1.6.1.6" );
    if ( $result ) { 
	# there should only be one result
	my $ok = 0;
	foreach $oid ( sort keys %$result ) {
	    $val = $result->{ $oid };
	    next if ( $val eq 'endOfMibView' );
	    $verbose && print "unit status $val\n";
	    if ( $val == 1 ) { 
		push @unknowns, "unit status unknown";
		$ok = -1;
		}
	    elsif ( $val == 2 ) { 
		push @unknowns, "unit cannot report status";
		$ok = -1;
		}
	    elsif ( $val == 3 ) { 
		# ok
		$ok = 1 if ( $ok == 0 );
		}
	    elsif ( $val == 4 ) { 
		push @warns, "unit needs attention";
		$ok = -1;
		}
	    elsif ( $val == 5 ) { 
		push @crits, "unit failure";
		$ok = -1;
		}
	    else { 
		push @unknowns, "unknown unit status code";
		$ok = -1;
		}
	    }
	if ( $ok == 0 ) {
	    push @unknowns, "didn't get unit status";
	    }
	}

    # connUnitSensorStatus
    $result = walk_table( $session, 'connUnitSensorName', 
    	".1.3.6.1.3.94.1.8.1.3" );
    if ( $result ) { 
	$n = 0;
	foreach $oid ( keys %$result ) {
	    $val = $result->{ $oid };
	    next if ( $val eq 'endOfMibView' );
	    ( $i = $oid ) =~ s/.*\.(\d+)$/$1/;
	    $names{ $i } = $val;
	    $n++;
	    }
	if ( ! $n ) { 
	    push @unknowns, "got 0 sensors";
	    }

	# connUnitSensorMessage
	$result = walk_table( $session, 'connUnitSensorMessage', 
	    ".1.3.6.1.3.94.1.8.1.6" );
	if ( $result ) { 
	    foreach $oid ( keys %$result ) {
		$val = $result->{ $oid };
		next if ( $val eq 'endOfMibView' );
		( $i = $oid ) =~ s/.*\.(\d+)$/$1/;
		$msgs{ $i } = $val;
		}

	    # connUnitSensorStatus
	    $result = walk_table( $session, 'connUnitSensorStatus', 
		".1.3.6.1.3.94.1.8.1.4" );
	    if ( $result ) { 
		foreach $oid ( sort keys %$result ) {
		    $val = $result->{ $oid };
		    next if ( $val eq 'endOfMibView' );
		    ( $i = $oid ) =~ s/.*\.(\d+)$/$1/;
		    $name = $names{ $i };
		    $msg = $msgs{ $i };
		    if ( $val == 1 ) { 
			push @unknowns, "sensor $name status unknown $msg";
			}
		    elsif ( $val == 2 ) { 
			push @unknowns, "sensor $name status is 'other' $msg";
			}
		    elsif ( $val == 3 ) { 
			# ok
			$verbose && print "sensor $name ok $msg\n";
			}
		    elsif ( $val == 4 ) { 
			push @warns, "sensor $name warning $msg";
			}
		    elsif ( $val == 5 ) { 
			push @crits, "sensor $name failure $msg";
			}
		    else { 
			push @unknowns, "sensor $name unknown status code $msg";
			}
		    }
		}
	    }
	}

    # connUnitPortState
    $result = walk_table( $session, 'connUnitPortState', 
	".1.3.6.1.3.94.1.10.1.6" );
    if ( $result ) { 
	$n = 0;
	foreach $oid ( keys %$result ) {
	    $val = $result->{ $oid };
	    next if ( $val eq 'endOfMibView' );
	    ( $i = $oid ) =~ s/.*\.(\d+)$/$1/;
	    $portstates{ $i } = $val;
	    $n++;
	    }
	if ( ! $n ) { 
	    push @unknowns, "got 0 ports";
	    }


	# connUnitPortName
	$result = walk_table( $session, 'connUnitPortName', 
	    ".1.3.6.1.3.94.1.10.1.17" );
	if ( $result ) { 
	    foreach $oid ( keys %$result ) {
		$val = $result->{ $oid };
		next if ( $val eq 'endOfMibView' );
		( $i = $oid ) =~ s/.*\.(\d+)$/$1/;
		$portnames{ $i } = $val;
		}

	    # connUnitPortStatus
	    $result = walk_table( $session, 'connUnitPortStatus', 
		".1.3.6.1.3.94.1.10.1.7" );
	    if ( $result ) { 
		foreach $oid ( sort keys %$result ) {
		    $val = $result->{ $oid };
		    next if ( $val eq 'endOfMibView' );
		    ( $i = $oid ) =~ s/.*\.(\d+)$/$1/;
		    if ( $portstates{ $i } != 2 )  { 
			# only care about ones that are supposed to be online
			next;
			}
		    $name = $portnames{ $i };
		    if ( $val != 3 ) { 
			push @crits, "port $i $name is not ready";
			}
		    else { 
			$verbose && print "port $i $name ready\n";
			}
		    }
		}
	    }
	}

    # connUnitEventTable
    $result = walk_table( $session, 'connUnitSEventTime', 
	".1.3.6.1.3.94.1.11.1.5" );
    if ( $result ) { 
	$n = 0;
	foreach $oid ( keys %$result ) {
	    $val = $result->{ $oid };
	    next if ( $val eq 'endOfMibView' );
	    next if ( $val > ( $sysUpTime + 1000 ) );
	    ( $i = $oid ) =~ s/.*\.(\d+)$/$1/;
	    $conUnitSEventTime[ $i ] = $val;
	    $n++
	    }
	$verbose && print "got $n fresh SEventTimes\n";

	if ( $n > 0 ) { 
	    $result = walk_table( $session, 'connUnitEventDescr', 
		".1.3.6.1.3.94.1.11.1.9" );
	    if ( $result ) { 
		# two passes is cheapest way to sort it 
		foreach $oid ( keys %$result ) {
		    $val = $result->{ $oid };
		    next if ( $val eq 'endOfMibView' );
		    ( $i = $oid ) =~ s/.*\.(\d+)$/$1/;
		    $conUnitEventDescr[ $i ] = $val;
		    }
		foreach $i ( 0 .. $#conUnitEventDescr ) { 
		    #$verbose && print "$i  $conUnitSEventTime[ $i ]  $val\n";
		    $verbose && printf "%d %d %s\n", 
			$i, $sysUpTime - $conUnitSEventTime[ $i ], 
			$conUnitEventDescr[ $i ];
		    next if ( ! defined $conUnitSEventTime[ $i ] );
		    next if ( $sysUpTime < $conUnitSEventTime[ $i ] );
		    next if ( ( $sysUpTime - $conUnitSEventTime[ $i ] ) > $age_limit );
		    next if ( $conUnitEventDescr[ $i ] =~ m/Battery is now completely charged/ ); 
		    push @warns, $conUnitEventDescr[ $i ];
		    }
		}
	    }
	}
    }




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

    $result = $session->get_table( -baseoid => $baseoid );
    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 $result;
    }



