#! /usr/bin/perl -w
#
# check_ift_dev - nagios plugin
# 
#
# Copyright (C) 2007 Marius Rieder <marius.rieder@inf.ethz.ch>
# Copyright (C) 2010 Stephan Kranz <s.kranz@storitback.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

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

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

my $PROGNAME = "check_ift_dev";
my $VERSION  = "v1.1";

sub print_help ();
sub usage ();
sub process_arguments ();
sub snmp_connect();
sub split_range;

my $status;

my %FanModesCode = (
      1 => 'Lowest Speed',
      2 => 'Second Lowest Speed',
      3 => 'Speed 3',
      4 => 'Speed 4',
      5 => 'Speed 5',
      6 => 'Intermediate Speed',
      7 => 'Higest Speed'
   );

my %FanModesCode2 = (
      0 => 'Low Speed',
      1 => 'High Speed',
   );

my $timeout=100;
my $state = "UNKNOWN";
my $answer = "";
my $snmpkey=0;
my $snmpoid=0;
my $key=0;
my $community;
my $maxmsgsize = 1472 ; # Net::SNMP default is 1472
my ($seclevel, $authproto, $secname, $authpass, $privpass, $auth, $priv, $context);
my $port = 161;
my $snmpDevTable = '.1.3.6.1.4.1.1714.1.1.9';
my %devStatus;
my %PSUstatus;
my %SLOTstatus;
my %FANstatus;
my %TEMPstatus;
my $hostname;
my $session;
my $error;
my $response;
my $snmp_version = 2;
my $opt_h ;
my $opt_V ;
my $opt_x ;
my $opt_psu = '1';
my $opt_slot = '1';
my $opt_fan;
my $opt_temp;
my @psus;
my @slots;
my %excluded ;
my $perfdata = '';

# 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"};
};


#Option checking
$status = process_arguments();

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

alarm($timeout);

# Opening SNMP Session
snmp_connect();

if ( $snmp_version =~ /3/ ) {
   if (!defined($response = $session->get_table($snmpDevTable, -contextname => $context))) {
      $answer=$session->error;
      $session->close;
      $state = 'CRITICAL';
      print ("$state: $answer for $snmpoid\n");
      exit $ERRORS{$state};
   }
} else {
   if (!defined($response = $session->get_table($snmpDevTable))) {
      $answer=$session->error;
      $session->close;
      $state = 'CRITICAL';
      print ("$state: $answer for $snmpoid\n");
      exit $ERRORS{$state};
}


foreach $snmpkey (keys %{$response}) {
   $snmpkey =~ /.*\.(\d+)\.(\d+)$/;
      $devStatus{$2}{$1} = $response->{$snmpkey};
#      print ("$devStatus{$1}{$1}\n");
   }
}

$session->close;

alarm(0);

$state = 'OK';
foreach $key (sort keys %devStatus) {

  # if ($devStatus{$key}{8} =~ m/^PSU\s?(\d+)/) {
   if ($devStatus{$key}{8} =~ m/PSU\s?(\d+)/) {
  #    next if (not $opt_psu);
      my $num = $1;
      if (not grep(/^$num$/,@psus) and @psus gt 0) {
	 next;
      }
      $PSUstatus{$num} = "" if (not defined $PSUstatus{$num});
      if ($devStatus{$key}{6} == 1) {
	 if ($devStatus{$key}{13} & 0x01) {
	    $PSUstatus{$num} .= ' Malfunctioning,';
	    $state = 'CRITICAL';
	 }
	 if ($devStatus{$key}{13} & 0x40) {
	    $PSUstatus{$num} .= ' OFF,';
	    $state = 'WARNING' if ($state eq 'OK');
	 }
	 if ($devStatus{$key}{13} & 0x80) {
	    $PSUstatus{$num} .= ' Not Present,';
	    $state = 'WARNING' if ($state eq 'OK');
	 }
      } elsif ($devStatus{$key}{6} == 2) {
	 $devStatus{$key}{8} =~ m/\s(\d+)$/;
         if ($devStatus{$key}{13} & 0x01) {
            $PSUstatus{$num} .= ' Fan Malfunctioning,';
            $state = 'WARNING';
         }
         if ($devStatus{$key}{13} & 0x40) {
            $PSUstatus{$num} .= ' Fan OFF,';
            $state = 'WARNING' if ($state eq 'OK');
         }
         if ($devStatus{$key}{13} & 0x80) {
            $PSUstatus{$num} .= ' Fan not Present,';
            $state = 'WARNING' if ($state eq 'OK');
         }
      } elsif ($devStatus{$key}{6} == 5) {
	 $devStatus{$key}{8} =~ m/\s(\S+)$/;
	 my $v = $1;
	 if ($devStatus{$key}{13} & 0x01) {
            $PSUstatus{$num} .= " $v-Sensor Malfunctioning,";
            $state = 'WARNING';
         }
	 if ($devStatus{$key}{13} & 0x0E) {
	    if ($devStatus{$key}{13} & 0x02) {
	       $PSUstatus{$num} .= " $v Low Voltage";
	    } else {
	       $PSUstatus{$num} .= " $v High Voltage";
	    }
	    if ($devStatus{$key}{13} & 0x04) {
	       $PSUstatus{$num} .= ' Warning,';
	       $state = 'WARNING' if ($state eq 'OK');
	    } else {
	       $PSUstatus{$num} .= ' Critical,';
	       $state = 'CRITICAL';
	    }
	 }
         if ($devStatus{$key}{13} & 0x40) {
            $PSUstatus{$num} .= " $v-Sensor OFF,";
            $state = 'WARNING' if ($state eq 'OK');
         }
         if ($devStatus{$key}{13} & 0x80) {
            $PSUstatus{$num} .= " $v-Sensor not Present,";
            $state = 'WARNING' if ($state eq 'OK');
         }
      }
   } elsif ($devStatus{$key}{6} == 17 and $opt_slot) {
      $devStatus{$key}{8} =~ m/\s(\d+)$/;
      my $num = $1;
      if (not grep(/^$num$/,@slots) and @slots gt 0) {
	 next;
      }
      $SLOTstatus{$num} = "" if (not defined $SLOTstatus{$num});
      if ($devStatus{$key}{13} & 0x01) {
	 $SLOTstatus{$num} .= " Malfunctioning,";
	 $state = 'CRITICAL';
      }
      if ($devStatus{$key}{13} & 0x02) {
	 $SLOTstatus{$num} .= " Needing Replacement,";
	 $state = 'CRITICAL';
      }
      if ($devStatus{$key}{13} & 0x04) {
	 $SLOTstatus{$num} .= " Inactive,";
	 $state = 'WARNING' if ($state eq 'OK');
      }
      if ($devStatus{$key}{13} & 0x80) {
	 $SLOTstatus{$num} .= " Empty,";
      }
   } elsif ($devStatus{$key}{6} == 2 and $opt_fan) {
      $devStatus{$key}{8} =~ m/fan(\d+)$/;
      my $num = $1;
      #$perfdata .= " fan$num=".$devStatus{$key}{9}."rpm;";
      if ($devStatus{$key}{4} == 4 and $devStatus{$key}{10} == 0 and $devStatus{$key}{9} > 0) {
      $perfdata .= " fan$num=".$FanModesCode{$devStatus{$key}{9}};
	}
      if ($devStatus{$key}{4} == 3 and $devStatus{$key}{10} == 0) {
      $perfdata .= " fan$num=".$FanModesCode2{$devStatus{$key}{9}};
	}
      if ($devStatus{$key}{9} > 0 and $devStatus{$key}{10} > 0) {
      $perfdata .= " fan$num=".$devStatus{$key}{9};
	}
      $FANstatus{$num} = "" if (not defined $SLOTstatus{$num});
      if ($devStatus{$key}{13} & 0x01) {
	 $FANstatus{$num} .= " Malfunctioning,";
	 $state = 'CRITICAL';
      }
      if ($devStatus{$key}{13} & 0x01) {
	 $FANstatus{$num} .= " is OFF,";
	 $state = 'WARNING' if ($state eq 'OK');
      }
   } elsif ($devStatus{$key}{6} == 3 and $opt_temp) {
      my $name = $devStatus{$key}{8};
      $name =~ s/\ /\-/g;
    #  $perfdata .= " $name=".($devStatus{$key}{9}/1000/$devStatus{$key}{10}).";";
      $perfdata .= " $name=".($devStatus{$key}{9}/1000*$devStatus{$key}{10}-273).";";
      if ($devStatus{$key}{13} & 0x01) {
	 $TEMPstatus{$name} .= " Malfunctioning,";
	 $state = 'WARNING';
      }
      if ($devStatus{$key}{13} & 0x0E) {
	 if ($devStatus{$key}{13} & 0x02) {
	    $TEMPstatus{$name} .= " Cold Temp";
	 } else {
	    $TEMPstatus{$name} .= " Hot Temp";
	 }
	 if ($devStatus{$key}{13} & 0x04) {
	    $TEMPstatus{$name} .= ' Warning,';
	    $state = 'WARNING' if ($state eq 'OK');
	 } else {
	    $TEMPstatus{$name} .= ' Critical,';
	    $state = 'CRITICAL';
	 }
      }
      if ($devStatus{$key}{13} & 0x40) {
	 $TEMPstatus{$name} .= " Inactive,";
	 $state = 'WARNING';
      }
      if ($devStatus{$key}{13} & 0x80) {
	 $TEMPstatus{$name} .= " Not Present,";
	 $state = 'WARNING';
      }
#   } else {
#	 print $devStatus{$key}{8}.":".$devStatus{$key}{6}.".".$devStatus{$key}{13}."\n";
   }

}

$answer="";

if ($opt_psu) {
   my $psuanswer;
   map {if (not defined($PSUstatus{$_})){$psuanswer .= " PSU$_: Not Found!"; $state = 'CRITICAL';}} (@psus);
   foreach $key (sort {$a <=> $b} keys %PSUstatus) {
      chop($PSUstatus{$key});
      $psuanswer .= " PSU$key:". $PSUstatus{$key} if ($PSUstatus{$key});
   }
   if ($psuanswer) {
      $answer .= $psuanswer;
   } else {
      $answer .= " PSUs: OK";
   }
}

if ($opt_slot) {
   my $slotanswer;
   foreach $key (sort {$a <=> $b} keys %SLOTstatus) {
      chop($SLOTstatus{$key});
      $slotanswer .= " SLOT$key:". $SLOTstatus{$key} if ($SLOTstatus{$key});
   }
   if ($slotanswer) {
      $answer .= $slotanswer;
   } else {
      $answer .= " SLOTs: OK";
   }
}

if ($opt_fan) {
   my $fananswer;
   foreach $key (sort {$a <=> $b} keys %FANstatus) {
      chop($FANstatus{$key});
      $fananswer .= " FAN$key:". $FANstatus{$key} if ($FANstatus{$key});
   }
   if ($fananswer) {
      $answer .= $fananswer;
   } else {
      $answer .= " FANs: OK";
   }
}

if ($opt_temp) {
   my $tempanswer;
   foreach $key (sort keys %TEMPstatus) {
      chop($TEMPstatus{$key});
      $tempanswer .= "$key:" . $TEMPstatus{$key} if ($TEMPstatus{$key});
   }
   if ($tempanswer) {
      $answer .= $tempanswer;
   } else {
      $answer .= " Temp: OK";
   }
}

print ("$state:$answer|$perfdata\n");
exit $ERRORS{$state};


sub usage (){
	printf "\nMissing arguments!\n";
	printf "\n";
	printf "$PROGNAME -H <HOSTNAME> [-C <COMUNITY>]\n";
	printf "Copyright (C) 2007 Marius Rieder\n";
	printf "\n\n";
	support();
	exit $ERRORS{"UNKNOWN"};
}

sub print_help (){
	printf "$PROGNAME plugin for Nagios monitors Infortrend Device\n";
	printf "status over snmp.\n";
	printf "\nUsage:\n";
	printf "   -H (--hostname)   Hostname to query - (required)\n";
	printf "   -C (--community)  SNMP read community (defaults to public,\n";
	printf "                     used with SNMP v1 and v2c\n";
	printf "   -v (--snmp_version)  1 for SNMP v1 (default)\n";
	printf "                        2 for SNMP v2c\n";
	printf "                          SNMP v2c will use get_bulk for less overhead\n";
	printf "                        3 for SNMPv3 (requires -U option)";
	printf "   -p (--port)       SNMP port (default 161)\n";
	printf "   --psu             Check PSUs (default all)\n";
	printf "   --slot            Check slots (default all)\n";
	printf "   --fan             Check Cooling Fans\n";
	printf "   --temp            Check Temp Sensors\n";
	printf "   -x (--exclude)    A comma separated list of ifType values that should be excluded \n";
	printf "                     from the report (default for an empty list is PPP(23).\n";
	printf "   -L (--seclevel)   choice of \"noAuthNoPriv\", \"authNoPriv\", or	\"authPriv\"\n";
	printf "   -U (--secname)    username for SNMPv3 context\n";
	printf "   -c (--context)    SNMPv3 context name (default is empty string)";
	printf "   -A (--authpass)   authentication password (cleartext ascii or localized key\n";
	printf "                     in hex with 0x prefix generated by using	\"snmpkey\" utility\n"; 
	printf "                     auth password and authEngineID\n";
	printf "   -a (--authproto)  Authentication protocol ( MD5 or SHA1)\n";
	printf "   -X (--privpass)   privacy password (cleartext ascii or localized key\n";
	printf "                     in hex with 0x prefix generated by using	\"snmpkey\" utility\n"; 
	printf "                     privacy password and authEngineID\n";
	printf "   -M (--maxmsgsize) Max message size - usefull only for v1 or v2c\n";
	printf "   -t (--timeout)    seconds before the plugin times out (default=$TIMEOUT)\n";
	printf "   -V (--version)    Plugin version\n";
	printf "   -h (--help)       usage help \n\n";
	print_revision($PROGNAME, $VERSION);
	
}

sub process_arguments() {
   $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,
         "L=s" => \$seclevel,		"seclevel=s"     => \$seclevel,
         "a=s" => \$authproto,		"authproto=s"    => \$authproto,
         "U=s" => \$secname,		"secname=s"      => \$secname,
         "A=s" => \$authpass,		"authpass=s"     => \$authpass,
         "X=s" => \$privpass,		"privpass=s"     => \$privpass,
         "c=s" => \$context,		"context=s"      => \$context,
         "p=i" => \$port,		"port=i"         => \$port,
         "H=s" => \$hostname,		"hostname=s"     => \$hostname,
         "x:s" => \$opt_x,		"exclude:s"      => \$opt_x,
         "M=i" => \$maxmsgsize,		"maxmsgsize=i"   => \$maxmsgsize,
         "t=i" => \$timeout,		"timeout=i"      => \$timeout,
	 "psu:s" => \$opt_psu,
	 "slot:s" => \$opt_slot,
	 "fan" => \$opt_fan,
	 "temp" => \$opt_temp,
      );

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

   if ($opt_V) {
      print_revision($PROGNAME, $VERSION);
      exit $ERRORS{'OK'};
   }

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

   unless (defined $timeout) {
      $timeout = $TIMEOUT;
   }

   if ($snmp_version eq '3' ) {
      unless (defined $seclevel) {
         $seclevel = 'noAuthNoPriv';
      }
      unless ($seclevel eq ('noAuthNoPriv' || 'authNoPriv' || 'authPriv' ) and defined($secname)) {
         usage();
         exit $ERRORS{"UNKNOWN"};
      }

      if ($seclevel =~ /^auth/ and not defined($authpass)) {
         usage();
         exit $ERRORS{"UNKNOWN"};
      } elsif ($seclevel eq 'authPriv'  and not defined($privpass)) {
	 usage();
	 exit $ERRORS{"UNKNOWN"};
      }
   } elsif (not $snmp_version eq '1') {
      $snmp_version = '2c';
   }

   unless (defined $community) {
      $community = 'public';
   }

   # Excluded hdd
   if (defined $opt_x) {
      my @x = split(/,/, $opt_x);
      if (@x) {
	 foreach $key (@x){
	    $excluded{$key} = 1;
	 }
      }
   }

   if ($opt_psu eq 'none') {
      $opt_psu = 0;
   } else {
      @psus = split_range($opt_psu);
      $opt_psu = 1;
   }

   if ($opt_slot eq 'none') {
      $opt_slot = 0;
   } else {
      @slots = split_range($opt_slot);
      $opt_slot = 1;
   }

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

}

sub snmp_connect() {
   if ( $snmp_version =~ /3/ ) {
      if ($seclevel eq 'noAuthNoPriv') {
         ($session, $error) = Net::SNMP->session(
               -hostname  => $hostname,
               -port      => $port,
               -version  => $snmp_version,
               -username => $secname,
            );
      }elsif ( $seclevel eq 'authNoPriv' ) {
         ($session, $error) = Net::SNMP->session(
               -hostname  => $hostname,
               -port      => $port,
               -version  => $snmp_version,
               -username => $secname,
               -authprotocol => $authproto,
               -authpassword => $authpass
            );
      }elsif ($seclevel eq 'authPriv' ) {
         ($session, $error) = Net::SNMP->session(
               -hostname  => $hostname,
               -port      => $port,
               -version  => $snmp_version,
               -username => $secname,
               -authprotocol => $authproto,
               -authpassword => $authpass,
               -privpassword => $privpass
            );
      }
   } else {
      ($session, $error) = Net::SNMP->session(
            -hostname   => $hostname,
            -community  => $community,
            -port       => $port,
            -version    => $snmp_version,
         );
   }

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

sub split_range {
   my ($range) = @_;
   my @ret;
   foreach my $item (split(/,/, $range)) {
      if ($item =~ m/^(\d+)\-(\d+)$/) {
         map{push(@ret,$_);}($1 .. $2);
      } elsif ($item =~ m/^(\d+)$/) {
         push(@ret,$1);
      }
   }
   return @ret;
}
