#! /usr/bin/perl -w

## Michael Buschmann / Cisco
## Added support for ASR1k & ASR9k 10/19/17

## License: This nagios plugin comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of
## the plugins under the terms of the GNU General Public License. For more information about these
## matters, see the GNU General Public License.

use strict;
use Switch ;
use List::Util qw[min max];
use Net::SNMP qw(:snmp);
use FindBin;
use lib "$FindBin::Bin";
use lib "/usr/local/nagios/libexec";
#use utils qw($TIMEOUT %ERRORS &print_revision &support);
use Nagios::Plugin qw(%ERRORS);
use Data::Dumper ;
my $TIMEOUT = 60;

use vars qw($PROGNAME);
use Getopt::Long;
use vars qw($opt_h $opt_V $opt_H $opt_C $opt_v $opt_o $opt_c $opt_w $opt_t $opt_p $opt_k $opt_u $opt_l $opt_d $opt_i $opt_authproto $opt_priv $opt_privproto);
use constant true  => "1" ;
use constant false => "0" ;
$PROGNAME = $0;
my $version = 1;
my $release = 0;
sub print_help ();
sub print_usage ();
sub verbose ;

$SIG{'ALRM'} = sub {
        print "uknown error at line 28 check timed out";
        exit $ERRORS{"UNKNOWN"};
};
alarm($TIMEOUT);


my $opt_d = 0 ;
Getopt::Long::Configure('bundling');
GetOptions
    ("h"   => \$opt_h, "help"         => \$opt_h,
     "u=s" => \$opt_u, "username=s"   => \$opt_u,
     "p=s" => \$opt_p, "password=s"   => \$opt_p,
                       "authprotocol=s" => \$opt_authproto,
     "k=s" => \$opt_k, "key=s"        => \$opt_k,
                       "priv=s"       => \$opt_priv,
                       "privprotocol=s" => \$opt_privproto,
     "V"   => \$opt_V, "version"      => \$opt_V,
     "v=s" => \$opt_v, "snmp=s"       => \$opt_v,
     "C=s" => \$opt_C, "community=s"  => \$opt_C,
     "w=s" => \$opt_w, "warning=s"    => \$opt_w,
     "c=s" => \$opt_c, "critical=s"   => \$opt_c,
     "H=s" => \$opt_H, "hostname=s"   => \$opt_H,
     "d=s" => \$opt_d, "debug=s"      => \$opt_d,
     "i"   => \$opt_i, "sysdescr"     => \$opt_i,
     "l"   => \$opt_l, "list"         => \$opt_l);

if ($opt_V) {
    print($PROGNAME.': $Revision: '.$version.'.'.$release."\n");
    exit $ERRORS{'OK'};
}


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

$opt_H = shift unless ($opt_H);
(print_usage() && exit $ERRORS{'OK'}) unless ($opt_H);

#if(!$snmp_vars{hostname}){&usage_and_exit();}

my $snmp = "2";
if ($opt_v && $opt_v =~ /^[0-9]$/) {
    $snmp = $opt_v;
}

if ($snmp eq "3") {
    if (!$opt_u) {
        print "Option -u (--username) is required for snmpV3\n";
        exit $ERRORS{'OK'};
    }
    if (!$opt_p && !$opt_k) {
        print "Option -k (--key) or -p (--password) is required for snmpV3\n";
        exit $ERRORS{'OK'};
    }elsif ($opt_p && $opt_k) {
        print "Only option -k (--key) or -p (--password) is needed for snmpV3\n";
        exit $ERRORS{'OK'};
    }
}

($opt_C) || ($opt_C = shift) || ($opt_C = "public");

my $name = $0;
$name =~ s/\.pl.*//g;

my ($session, $error);
if ($snmp eq "1" || $snmp eq "2") {
    ($session, $error) = Net::SNMP->session(-hostname => $opt_H, -community => $opt_C, -version => $snmp, -maxmsgsize => "5000", -timeout => 2, -retries => 2);
}elsif ($opt_p && $opt_priv && $opt_authproto && $opt_privproto) {
    ($session, $error) = Net::SNMP->session(-hostname => $opt_H, -version => $snmp,  -username => $opt_u, -authpassword => $opt_p, -authprotocol => $opt_authproto, -privpassword => $opt_priv, -privprotocol => $opt_privproto, -maxmsgsize => "5000", -timeout => 2, -retries => 2);
}elsif ($opt_p && $opt_priv) {
    ($session, $error) = Net::SNMP->session(-hostname => $opt_H, -version => $snmp,  -username => $opt_u, -authpassword => $opt_p, -privpassword => $opt_priv, -maxmsgsize => "5000", -timeout => 2, -retries => 2);
}elsif ($opt_p && $opt_authproto) {
    ($session, $error) = Net::SNMP->session(-hostname => $opt_H, -version => $snmp,  -username => $opt_u, -authpassword => $opt_p, -authprotocol => $opt_authproto, -maxmsgsize => "5000", -timeout => 2, -retries => 2);
}elsif ($opt_p) {
    ($session, $error) = Net::SNMP->session(-hostname => $opt_H, -version => $snmp,  -username => $opt_u, -authpassword => $opt_p, -maxmsgsize => "5000", -timeout => 2, -retries => 2);
}elsif ($opt_k) {
    ($session, $error) = Net::SNMP->session(-hostname => $opt_H, -version => $snmp, -username => $opt_u, -authkey => $opt_k, -maxmsgsize => "5000", -timeout => 2, -retries => 2);
}
# check that session opened
if (!defined($session)) {
    print("SNMP Session : $error\n");
    exit $ERRORS{'UNKNOWN'};
}


my $loglevel = $opt_d ;
my $result ;
my $label ;
my $oid ;
my $unit = "";
my $return_result ;
my $output ;
my $total_connection = 0;
my $key ;
my $value ;
my @perfparse ;
# parse sysDescr
my $outlabel_oid;
if ($opt_i) {
    # sysdescr
    $outlabel_oid = ".1.3.6.1.2.1.1.1.0" ;
}else {
    # sysname
    $outlabel_oid = ".1.3.6.1.2.1.1.5.0" ;
}

verbose("get sysdescr ($outlabel_oid)", "5") ;
my $sysdescr = $session->get_request(-varbindlist => [$outlabel_oid]) ;
if (!defined($sysdescr)) {
    print("DEVICE UNKNOWN: SNMP get_request : ".$session->error()."\n");
    exit $ERRORS{'UNKNOWN'};
}
verbose(" sysdescr is ".$sysdescr->{$outlabel_oid}, "5") ;
my $outlabel = $sysdescr->{$outlabel_oid}.": " ;


my $sysObjectID = '1.3.6.1.2.1.1.2.0';
my %power_status = ();
my $return_code = 0;

# ASR Sensor OIDs
my $entPhysicalDescr = '1.3.6.1.2.1.47.1.1.1.1.7';
my $entSensorDescr = '1.3.6.1.2.1.47.1.1.1.1.2';


$result = $session->get_request(-varbindlist => [$sysObjectID]) ;
if (!defined($result)) {
    print("UNKNOWN: SNMP get_request : ".$session->error()."\n");
    exit $ERRORS{'UNKNOWN'};
}

my $systemOID = $result->{$sysObjectID};
my $model = '';

if ($systemOID eq '1.3.6.1.4.1.9.1.1041') {
    $model = '3945';
} elsif ( $systemOID eq '1.3.6.1.4.1.9.1.1054') {
    $model = 'sm';
} elsif ( $systemOID eq '1.3.6.1.4.1.9.1.1525' or $systemOID eq '1.3.6.1.4.1.9.1.2144') {
    $model = 'asr1k';
} elsif ( $systemOID eq '1.3.6.1.4.1.9.1.1017' or $systemOID eq '1.3.6.1.4.1.9.12.3.1.3.1507' or $systemOID eq '1.3.6.1.4.1.9.1.1018') {
    $model = 'asr9k';
} else {
    print "UNKNOWN: Unknonwn SNMP sysObjectID: $systemOID";
    exit $ERRORS{'UNKNOWN'};
}

my %Sensor_list = ();
my %cpu_physical_names = ();
my %cpu_temps = ();

## Only support ASRs currently
if ($model eq 'asr1k' or $model eq 'asr9k') {
    %Sensor_list = get_sensor_temps(1, [$entSensorDescr]);
    %cpu_physical_names = get_cpu_entries(1, [$entPhysicalDescr]);
}

#print Dumper(\%Sensor_list), "\n";
#print Dumper(\%cpu_physical_names), "\n";

alarm(0);

my $message = '';
my $tempdata = '';

### We only support ASRs currently
if ($model eq 'asr1k') {
    my $sensor_temp;
    my $modules;
    my $cpu_index_id;
    my $cpu_name;
    for my $Hot_Sensor_Index (keys %Sensor_list) {
        my $Hot_Sensor_IndexID = $Sensor_list{$Hot_Sensor_Index}{'id'};
        $Hot_Sensor_IndexID =~ s/^.//;
        ### Get a list of CPU index and module names
        for my $cpu_index (keys %cpu_physical_names) {
            ## We look for power supplies and CPUs, we monitor on the CPU only
            my $cpu_ID = $cpu_physical_names{$cpu_index}{'id'};
            $cpu_name = $cpu_physical_names{$cpu_index}{'1.3.6.1.2.1.47.1.1.1.1.7'};
            $cpu_name =~ s/Temp1    P/PS/g;
            $cpu_name =~ s/Temp: Center/CPU_/g;
            $cpu_name =~ s/\s+//g;
            $cpu_name =~ s/\/\d+//g;
            ### Match to Sensor and continue
            if ($Hot_Sensor_IndexID eq $cpu_ID) {
                my $entSensorValue = "1.3.6.1.4.1.9.9.91.1.1.1.1.4.$Hot_Sensor_IndexID";
                my $cpu_temps = $session->get_request(-varbindlist => [ $entSensorValue ]);
                ($key,$value) = %{$cpu_temps};
                $sensor_temp = $value;
                $sensor_temp =~ s/..\K(?=.)/./sg;
                ### Match Sensor ID with Actual Temp ID
                $tempdata .= "$cpu_name=$sensor_temp,";
                $message .= "$cpu_name=$sensor_temp,";
                if (($sensor_temp > $opt_w) && ($return_code == 0)) {
                    $return_code = $ERRORS{"WARNING"};
                } elsif ($sensor_temp > $opt_c) {
                    $return_code = $ERRORS{"CRITICAL"};
                }
            }
        }
    }
    } elsif ( $model eq 'asr9k' ){
    my $sensor_temp;
    my $modules;
    my $cpu_index_id;
    my $cpu_name;
    for my $Hot_Sensor_Index (keys %Sensor_list) {
        my $Hot_Sensor_IndexID = $Sensor_list{$Hot_Sensor_Index}{'id'};
        $Hot_Sensor_IndexID =~ s/^.//;
        ### Get a list of CPU index and module names
        for my $cpu_index (keys %cpu_physical_names) {
            my $cpu_ID = $cpu_physical_names{$cpu_index}{'id'};
            $cpu_name = $cpu_physical_names{$cpu_index}{'1.3.6.1.2.1.47.1.1.1.1.7'};
            $cpu_name =~ s/temperature/cur_temp/g;
            $cpu_name =~ s/ /_/g;
            $cpu_name =~ s/\//_/g;
            ### Match to Sensor and continue
            if ($Hot_Sensor_IndexID eq $cpu_ID) {
                my $entSensorValue = "1.3.6.1.4.1.9.9.91.1.1.1.1.4.$Hot_Sensor_IndexID";
                my $cpu_temps = $session->get_request(-varbindlist => [ $entSensorValue ]);
                ($key,$value) = %{$cpu_temps};
                $sensor_temp = $value;
                $sensor_temp =~ s/..\K(?=.)/./sg;
                ### Match Sensor ID with Actual Temp ID
                $tempdata .= "$cpu_name=$sensor_temp,";
                $message .= "$cpu_name=$sensor_temp,";
                if (($sensor_temp > $opt_w) && ($return_code == 0)) {
                    $return_code = $ERRORS{"WARNING"};
                } elsif ($sensor_temp > $opt_c) {
                    $return_code = $ERRORS{"CRITICAL"};
                }
            }
        }
    }
    }else{
    print "Sorry this may be an unsupported device, confirm this is a Cisco ASR\n";
    $return_code = $ERRORS{"UNKNOWN"};
}

if ($return_code == 2) {
    print "Temp Critical: $message";
    print " | ";
    print $tempdata;
    exit 2;

} elsif ($return_code == 1) {
    print "Temp Warning: $message";
    print " | ";
    print $tempdata;
    exit 1;

} else {
    print "Temps OK: $message";
    print " | ";
    print $tempdata;
    exit 0;
}


sub print_usage () {
    print "Usage:";
    print "$PROGNAME\n";
    print "   -H (--hostname)   Hostname to query - (required)\n";
    print "   -C (--community)  SNMP read community (defaults to public,\n";
    print "                     used with SNMP v1 and v2c\n";
    print "   -v (--snmp_version)  1 for SNMP v1\n";
    print "                        2 for SNMP v2c (deafult)\n";
    print "                        3 for SNMP v3\n";
    print "   -k (--key)        snmp V3 key\n";
    print "   -p (--password)   snmp V3 password\n";
    print "   -u (--username)   snmp v3 username \n";
    print "   --authprotocol    snmp v3 authprotocol md5|sha \n";
    print "   --priv            snmp V3 priv password\n";
    print "   --privprotocol    snmp v3 privprotocol des|aes \n";

    print "   -V (--version)    Plugin version\n";
    print "   -h (--help)       usage help\n\n" ;
    print "   -l (--list)       list probes\n";
    print "   -i (--sysdescr)   use sysdescr instead of sysname for label display\n";
    print "\n" ;
    print "   -d (--debug)      debug level (1 -> 15)";
    print "\n";
}

sub verbose {
    my $message = $_[0];
    my $messagelevel = $_[1] ;


    if ($messagelevel <= $loglevel) {
        print "$message\n" ;
    }
}

sub get_cpu_entries {
    my $num_indexes = shift @_;
    my (@columns) = @_ ;
    verbose("get entries", "10") ;
    if ($snmp == 1) {
        $result = $session->get_entries(-columns => @columns) ;
    }else {
        $result = $session->get_entries(-columns => @columns) ;
    }
    if (!defined($result)) {
        print("UNKNOWN: SNMP get_entries : ".$session->error()."\n");
        exit $ERRORS{'UNKNOWN'};
    }
    my %snmp_values = %{$result} ;
    my $id;
    my %snmp_value_return;
    while(($key,$value) = each(%snmp_values)) {
        if ( $num_indexes eq 1 ) {
            $id = $key ;
            $id =~ s/.*\.([0-9]+)$/$1/;
            $key =~ s/(.*)\.[0-9]*/$1/ ;
            verbose("key=$key, id=$id, value=$value", "15") ;
            $snmp_value_return{$id}{$key} = $value;
            $snmp_value_return{$id}{"id"} = $id ;
        } else {
            $id = $key ;
            verbose("key=$key, id=$id, value=$value", "15") ;
            $snmp_value_return{$id}{$key} = $value;
            $snmp_value_return{$id}{"id"} = $id ;
        }
    }
    return(%snmp_value_return) ;
}

sub get_sensor_temps {
    my $num_indexes = shift @_;
    my (@columns) = @_ ;
    verbose("get entries", "10") ;
    if ($snmp == 1) {
        $result = $session->get_entries(-columns => @columns) ;
    }else {
        $result = $session->get_entries(-columns => @columns) ;
    }
    if (!defined($result)) {
        print("UNKNOWN: SNMP get_entries : ".$session->error()."\n");
        exit $ERRORS{'UNKNOWN'};
    }
    my %snmp_values = %{$result} ;
    my %snmp_value_return;
    my %sensor_temp_return;
    my $id;
	if ($model eq 'asr1k') {
    while (my ($key, $value) = each %snmp_values) {
			### Find all Sensors 
            if ($value =~ m/Center/i || $value =~ m/Temp1/i) {
            	$id = $key ;
            	$id =~ s/.*((\.[0-9]+){$num_indexes})$/$1/;
            	$key =~ s/(.*)(\.[0-9]+){$num_indexes}$/$1/;
            	verbose("key=$key, id=$id, value=$value", "15") ;
            	$sensor_temp_return{$id}{$key} = $value;
            	$sensor_temp_return{$id}{"id"} = $id ;
	
            	}
            }
    } elsif ($model eq 'asr9k') {
    while (my ($key, $value) = each %snmp_values) {
            ### Find all Sensors
            if ($value =~ m/Hot Temperature SensorHotspot0/i) {
                $id = $key ;
                $id =~ s/.*((\.[0-9]+){$num_indexes})$/$1/;
                $key =~ s/(.*)(\.[0-9]+){$num_indexes}$/$1/;
                verbose("key=$key, id=$id, value=$value", "15") ;
                $sensor_temp_return{$id}{$key} = $value;
                $sensor_temp_return{$id}{"id"} = $id ;

                }
            }
    }
    return(%sensor_temp_return);
}
