#! /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 "APPWALL_CPU: Service 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 => 4, -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 => 4, -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 => 4, -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 => 4, -retries => 2);
}elsif ($opt_p) {
    ($session, $error) = Net::SNMP->session(-hostname => $opt_H, -version => $snmp,  -username => $opt_u, -authpassword => $opt_p, -maxmsgsize => "5000", -timeout => 4, -retries => 2);
}elsif ($opt_k) {
    ($session, $error) = Net::SNMP->session(-hostname => $opt_H, -version => $snmp, -username => $opt_u, -authkey => $opt_k, -maxmsgsize => "5000", -timeout => 4, -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 CPU OIDs
my $entPhysicalName = '1.3.6.1.2.1.47.1.1.1.1.7';
my $cpmCPUTotalPhysicalIndex = '1.3.6.1.4.1.9.9.109.1.1.1.1.2';
my $cpmCPUTotal5minRev = '1.3.6.1.4.1.9.9.109.1.1.1.1.8';


$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 %cpu_id = ();
my %cpu_physical_names = ();
my %cpu_hist = ();

## We only support ASRs currently
if ($model eq 'asr1k' or $model eq 'asr9k') {
    %cpu_hist = get_snmp_entries(1, [$cpmCPUTotal5minRev]);
	%cpu_id = get_snmp_entries(1, [$cpmCPUTotalPhysicalIndex]);
    %cpu_physical_names = get_cpu_entries(1, [$entPhysicalName]);
#   print "%cpu_physical_names";
}

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

alarm(0);

my $message = '';
my $cpuloaddata = '';

### We only support ASR currently
if ($model eq 'asr1k' ) {
    my $cpu_index_id;
    my $cpu_name;
    my $cpu_hist_index_id;
    my $cpu_hist_id;
    my $cpu_5min_index_id;
    my $cpu_5min_load_avg;
    ### Get a list of CPU index and module names
    for my $cpu_index (keys %cpu_physical_names) {
        my $cpu_indexID = $cpu_physical_names{$cpu_index}{'id'};
        $cpu_indexID =~ s/^.//;
        $cpu_index_id = $cpu_indexID;
        $cpu_name = $cpu_physical_names{$cpu_index}{'1.3.6.1.2.1.47.1.1.1.1.7'};
        $cpu_name =~ s/cpu/CPU/g;
        $cpu_name =~ s/ /_/g;
        $cpu_name =~ s/\//_/g;
        ### Get a list of CPU history IDs
        for my $cpu_h (keys %cpu_id){
            $cpu_hist_index_id = $cpu_id{$cpu_h}{'1.3.6.1.4.1.9.9.109.1.1.1.1.2'};
            $cpu_hist_id = $cpu_id{$cpu_h}{'id'};
            ### Find only CPU indexes with history, match them
            if ((defined($cpu_index_id)) && ($cpu_hist_index_id eq $cpu_index_id)) {
                ### Get CPU history and match with history index
                for my $cpu_load (keys %cpu_hist) {
                    $cpu_5min_load_avg = $cpu_hist{$cpu_load}{'1.3.6.1.4.1.9.9.109.1.1.1.1.8'};
                    $cpu_5min_index_id =  $cpu_hist{$cpu_load}{'id'};
                    ### Match CPU history ID with 5 min history id, print real 5min history for each CPU
                    if ($cpu_5min_index_id eq $cpu_hist_id) {
                        ### Save cpuloaddata, check for high and low CPU
                        $cpuloaddata .= "$cpu_name" . "_5min_avg=$cpu_5min_load_avg%, ";
                        $message .= "$cpu_name" . "_5min_avg=$cpu_5min_load_avg%, ";
                            ### Check for high CPU
                            if (($cpu_5min_load_avg > $opt_w) && ($return_code == 0)) {
                                $return_code = $ERRORS{"WARNING"};
                            } elsif ($cpu_5min_load_avg > $opt_c) {
                                $return_code = $ERRORS{"CRITICAL"};
                            }
                        }
                    }
                }
            }
        }
} elsif ($model eq 'asr9k' ) {
	my $cpu_index_id;
	my $cpu_name;
	my $cpu_hist_index_id;
	my $cpu_hist_id;
	my $cpu_5min_index_id;
	my $cpu_5min_load_avg;
	### Get a list of CPU index and module names
	for my $cpu_index (keys %cpu_physical_names) {
		my $cpu_indexID = $cpu_physical_names{$cpu_index}{'id'};
		$cpu_indexID =~ s/^.//;
		$cpu_index_id = $cpu_indexID;
		$cpu_name = $cpu_physical_names{$cpu_index}{'1.3.6.1.2.1.47.1.1.1.1.7'};
        $cpu_name =~ s/module /Mod_/g;
        $cpu_name =~ s/\//_/g;
        print $cpu_name;
        ### Get a list of CPU history IDs
		for my $cpu_h (keys %cpu_id){ 		
			$cpu_hist_index_id = $cpu_id{$cpu_h}{'1.3.6.1.4.1.9.9.109.1.1.1.1.2'};
			$cpu_hist_id = $cpu_id{$cpu_h}{'id'};
			### Find only CPU indexes with history, match them
			if ((defined($cpu_index_id)) && ($cpu_hist_index_id eq $cpu_index_id)) {
				### Get CPU history and match with history index
				for my $cpu_load (keys %cpu_hist) {
					$cpu_5min_load_avg = $cpu_hist{$cpu_load}{'1.3.6.1.4.1.9.9.109.1.1.1.1.8'};
					$cpu_5min_index_id =  $cpu_hist{$cpu_load}{'id'};
					### Match CPU history ID with 5 min history id, print real 5min history for each CPU
					if ($cpu_5min_index_id eq $cpu_hist_id) {	
						### Save cpuloaddata, check for high and low CPU
						$cpuloaddata .= "$cpu_name" . "_5min_avg=$cpu_5min_load_avg%, ";
					    $message .= "$cpu_name" . "_5min_avg=$cpu_5min_load_avg%, ";   
                            ### Check for high CPU 
							if (($cpu_5min_load_avg > $opt_w) && ($return_code == 0)) {
								$return_code = $ERRORS{"WARNING"};
							} elsif ($cpu_5min_load_avg > $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 "CPU Critical: $message";
    print " | ";
    print $cpuloaddata;
    exit 2;

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

} else {
    print "CPU OK: $message";
    print " | ";
    print $cpuloaddata;
    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 "   -w (--warning level) set nagios warning alert ie 80%\n";
    print "   -c (--critical level) set nagios critical alert ie 90%\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_snmp_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_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 %snmp_value_return;
    my %cpu_value_return;
    my $id;
	if ($model eq 'asr1k' ) {
    while (my ($key, $value) = each %snmp_values) {
			### Find all line card CPUs
			if ($value =~ m/cpu/) {
            	$id = $key ;
            	$id =~ s/.*((\.[0-9]+){$num_indexes})$/$1/;
            	$key =~ s/(.*)(\.[0-9]+){$num_indexes}$/$1/;
            	verbose("key=$key, id=$id, value=$value", "15") ;
            	$cpu_value_return{$id}{$key} = $value;
            	$cpu_value_return{$id}{"id"} = $id ;
	
            	}
            }
    } elsif ($model eq 'asr9k' ) {
    while (my ($key, $value) = each %snmp_values) {
            ### Find all line card CPUs
            if ($value =~ m/module 0/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") ;
                $cpu_value_return{$id}{$key} = $value;
                $cpu_value_return{$id}{"id"} = $id ;

                }
            }    
    }
return(%cpu_value_return);
}
