#!/usr/bin/perl -w

# check_promise_chassis.pl   --  Nagios Plugin
#
# By Barry O'Donovan - http://www.barryodonovan.com/ - barry <at> opensolutions <dot> ie
#
# Extended and revised by Carsten Grammes at DIaLOGIKa (http://www.dialogika.de), 2010-2012
# now supports VTrak M200i, M200, E310, M300i and compatible models
#
# 20120524 Patch from Robert Eden (added E830 support and minor fix)
#
#
# Usage: ./check_promise_chassis.pl -h <host> -m <model> [ -c <community ] [ -d detailed ]
#
# Copyright (c) 2007 - 2012 Open Source Solutions Ltd - http://www.opensolutions.ie/
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


use strict;
use Net::SNMP;
use Getopt::Long;

my $status;
my $TIMEOUT = 60;

my %ERRORS = ('UNKNOWN' , '-1',
              'OK' , '0',
              'WARNING', '1',
              'CRITICAL', '2');

my %NAGRET = ('UNKNOWN' , '3',
              'OK' , '0',
              'WARNING', '1',
              'CRITICAL', '2');

my %NAGSTR = ('UNKNOWN' , 'UNK',
              'OK' , 'OK',
              'WARNING', 'WARN',
              'CRITICAL', 'CRIT');

my $state = "OK";
my $answer = "";
my $snmpkey;
my $key;
my $community = "public";
my $port = 161;

my $hostname;
my $session;
my $error;
my $response = undef;
my $usage;
my $detailed = 0;
my $model;
my $detailed_status = "";

my $snmpDiskStatusTable;
my $snmpBatteryStatus;
my $snmpPSUStatusTable;
my $snmpCoolingDeviceStatusTable;
my $snmpTemperatureStatusTable;
my $snmpDrivesOffline;
my $snmpDrivesWithPFA;
my $snmpDrivesRebuilding;
my $snmpDrivesMissing;

my $idx;
my $resp;


####################################################################################
sub setstate
{
    my $newstate = shift( @_ );

    if( $ERRORS{$newstate} > $ERRORS{$state} )
    {
        $state = $newstate;
        return();
    }

    if( $newstate eq 'UNKNOWN' && $state eq 'OK' )
    {
        $state = $newstate;
        return();
    }
}

####################################################################################
sub usage () {
    die << "EOF";
usage: check_promis_chassis <options>

where options is:
        -c, --community     SNMP community (default=public)
        -d, --detailed      enable detailed mode (detailed lotsa stuff on screen)
        -m, --model         Promise model family (M200, E310, E830, M300i) - mandatory
        -h, --host          IP address or DNS name to connect to - mandatory

EOF
};


####################################################################################
sub pdetail () {
	$detailed_status .= "@_\n" if ( $detailed eq 1 );
}

####################################################################################
sub oid_err () {
    my $oid = $_[0];
    my $featurestr = $_[1];

    if( $session->error_status == 2 )
    {
        $answer = $featurestr . " OID not supported.";
    }
    else
    {
        $answer=$session->error;
    }
    $session->close;
    $state = 'CRITICAL';
    print ( "$state: $answer,$oid" );
    exit $ERRORS{$state};
}

####################################################################################
sub oid_table () {
    my $oid = $_[0];
    my $featurestr = $_[1];

    if( !defined( $response = $session->get_table( $oid ) ) )
    {
	&oid_err ($oid, $featurestr);
    }
}

####################################################################################
sub oid_request () {
    my $oid = $_[0];
    my $featurestr = $_[1];

    if( !defined( $response = $session->get_request( $oid ) ) )
    {
	&oid_err ($oid, $featurestr);
    }
}

####################################################################################
sub query_multi_state () {
    my $oid = $_[0];
    my $featurestr = $_[1];
    my $checkstr1 = $_[2];
    my $checkstr2 = $_[3];

    my @stat_table = undef;
    if ( @_ < 4 ) {
	$checkstr2 = "jhgkkjhg";   # never matches...
    }

    &oid_table ($oid, $featurestr);

    foreach $snmpkey ( sort keys %{$response} )
    {
        $idx = substr($snmpkey,rindex($snmpkey,'.')+1);
        $stat_table[$idx] = $response->{$snmpkey};
        $stat_table[$idx] = 'not present' unless length( $stat_table[$idx] );
        $resp = $featurestr . " " . $idx . " in state " . $stat_table[$idx];
        &pdetail ($resp);

        if( !( $stat_table[$idx] =~ $checkstr1 || $stat_table[$idx] =~ $checkstr2 ) )
        {
            &setstate('CRITICAL');
            $answer .= "$resp";
        }
    }

}
####################################################################################


GetOptions (
	"c|community:s"		=> \$community,
	"d|detailed"		=> \$detailed,
	"m|model:s"		=> \$model,
	"h|host:s"		=> \$hostname);


unless ( defined ($hostname) )			{ usage (); };
unless ( defined ($model) )			{ usage (); };

&pdetail ("community=$community  detailed=$detailed model=$model host=$hostname\n");

if ( $model eq "M200" || $model eq "M300i") {
	$snmpDiskStatusTable = '1.3.6.1.4.1.7933.1.10.2.1.1.1.8';
	$snmpBatteryStatus = '.1.3.6.1.4.1.7933.2.1.7.1.1.14.1.1';
	$snmpPSUStatusTable = '.1.3.6.1.4.1.7933.2.1.4.1.1.2.1';
	$snmpCoolingDeviceStatusTable = '.1.3.6.1.4.1.7933.2.1.3.1.1.3.1';
	$snmpTemperatureStatusTable = '.1.3.6.1.4.1.7933.2.1.5.1.1.3';
	$snmpDrivesOffline = '.1.3.6.1.4.1.7933.1.10.1.2.1.1.22.1';
	$snmpDrivesWithPFA = '.1.3.6.1.4.1.7933.1.10.1.2.1.1.23.1';
	$snmpDrivesRebuilding = '.1.3.6.1.4.1.7933.1.10.1.2.1.1.24.1';
	$snmpDrivesMissing = '.1.3.6.1.4.1.7933.1.10.1.2.1.1.25.1';
} elsif ( $model eq "E310" || $model eq 'E830' ) {
	$snmpDiskStatusTable = '1.3.6.1.4.1.7933.1.20.2.1.1.8.1';
	$snmpBatteryStatus = '.1.3.6.1.4.1.7933.1.20.1.15.1.14.1.1.1';
	$snmpPSUStatusTable = '.1.3.6.1.4.1.7933.1.20.1.12.1.2';
	$snmpCoolingDeviceStatusTable = '.1.3.6.1.4.1.7933.1.20.1.11.1.3';
	$snmpTemperatureStatusTable = '.1.3.6.1.4.1.7933.1.20.1.13.1.3';
	$snmpDrivesOffline = '.1.3.6.1.4.1.7933.1.20.1.3.1.21.1.1';
	$snmpDrivesWithPFA = '.1.3.6.1.4.1.7933.1.20.1.3.1.22.1.1';
	$snmpDrivesRebuilding = '.1.3.6.1.4.1.7933.1.20.1.3.1.23.1.1';
	$snmpDrivesMissing = '.1.3.6.1.4.1.7933.1.20.1.3.1.24.1.1';
}
else { usage (); }

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

( $session, $error ) = Net::SNMP->session(
 -hostname  => $hostname,
 -community => $community,
 -timeout   => 30,
 -port      => $port
);

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


####################################################################################
# Query the Disk States

&query_multi_state ($snmpDiskStatusTable, "Disk State", "OK");


# Query the Battery States
&oid_request ($snmpBatteryStatus, "Battery");

$resp = "Battery status is: " . $response->{$snmpBatteryStatus};
&pdetail ( "$resp" );

if( $response->{$snmpBatteryStatus} &&
    !( $response->{$snmpBatteryStatus} =~ "FullyCharged" ||
       $response->{$snmpBatteryStatus} =~ "Charging" ) )
{
    &setstate('WARNING');
    $answer .= "$resp";
}


####################################################################################
# Query the PSU States

&query_multi_state ($snmpPSUStatusTable, "PSU", "Powered On and Functional");


####################################################################################
# Query the Cooling Device States

&query_multi_state ($snmpCoolingDeviceStatusTable, "Cooling Device", "Functional");


####################################################################################
# Query the Temperature States

&query_multi_state ($snmpTemperatureStatusTable, "Temperature State", "normal", "not present");


####################################################################################
# Query Some Specific Drive States

&oid_request ($snmpDrivesOffline, "Drives Offline");
$resp = "There are " . $response->{$snmpDrivesOffline} . " drives offline";
&pdetail ( "$resp" );

if( $response->{$snmpDrivesOffline} > 0 )
{
    &setstate('CRITICAL');
    $answer .= "$resp";
}


&oid_request ($snmpDrivesWithPFA, "Drives with PFA");
$resp = "There are " . $response->{$snmpDrivesWithPFA} . " drives with PFA status";
&pdetail ( "$resp" );
if( $response->{$snmpDrivesWithPFA} > 0 )
{
    &setstate('WARNING');
    $answer .= "$resp";
}


&oid_request ($snmpDrivesRebuilding, "Drives rebuilding");
$resp = "There are " . $response->{$snmpDrivesRebuilding} . " drives rebuilding";
&pdetail ( "$resp" );
if( $response->{$snmpDrivesRebuilding} > 0 )
{
    &setstate('WARNING');
    $answer .= "$resp";
}


&oid_request ($snmpDrivesMissing, "Drives missing");
$resp = "There are " . $response->{$snmpDrivesMissing} . " drives missing";
&pdetail ( "$resp" );
if( $response->{$snmpDrivesMissing} > 0 )
{
    &setstate('CRITICAL');
    $answer .= "$resp";
}


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

$session->close;


if($state eq 'OK')
{
    print "$NAGSTR{$state}: Chassis OK\n";
}
else
{
    print "$NAGSTR{$state}: $answer\n";
}

print $detailed_status;

exit $NAGRET{$state};

