#! /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_help();
    exit $ERRORS{'OK'};
}

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

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("APPWALL_CPU UNKNOWN: 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("APPWALL_CPU 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;
	

my $cempMemPoolHCFree = '.1.3.6.1.4.1.9.9.221.1.1.1.1.20';
my $cempMemPoolHCUsed = '.1.3.6.1.4.1.9.9.221.1.1.1.1.18';
my $cempMemPoolName = '.1.3.6.1.4.1.9.9.221.1.1.1.1.3';
my $entPhysicalName = '.1.3.6.1.2.1.47.1.1.1.1.7';

my $ciscoMemoryPoolFree = '.1.3.6.1.4.1.9.9.48.1.1.1.6';
my $ciscoMemoryPoolUsed = '.1.3.6.1.4.1.9.9.48.1.1.1.5';
my $ciscoMemoryPoolName = '.1.3.6.1.4.1.9.9.48.1.1.1.2';


# determine hardware model


$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 %mem_util = ();
my %physical_names = ();

if ($model eq 'asr1k' or $model eq 'asr9k') {
	
	%mem_util = get_entries(2, [$cempMemPoolHCFree, $cempMemPoolHCUsed, $cempMemPoolName]);	
	%physical_names = get_entries(1, [$entPhysicalName]);	
	
}

if ($model eq '3945' or $model eq 'sm' ) {
	%mem_util = get_entries(1, [$ciscoMemoryPoolFree, $ciscoMemoryPoolUsed, $ciscoMemoryPoolName]);
	
}

#print Dumper(\%physical_names), "\n";


alarm(0);


my $message = '';
my $perfdata = '';


if ($model eq '3945' or $model eq 'sm') {
	
	foreach my $pool_index (keys %mem_util) {
		if ($mem_util{$pool_index}{$ciscoMemoryPoolName} =~ /reserved/ or $mem_util{$pool_index}{$ciscoMemoryPoolName} =~ /image/ or $mem_util{$pool_index}{$ciscoMemoryPoolName} =~ /Driver/) {
			# ignore reserved and image memory pools
			next;	
		}

		
		my $total_mem = $mem_util{$pool_index}{$ciscoMemoryPoolFree} + $mem_util{$pool_index}{$ciscoMemoryPoolUsed};
		my $avg_mem_util = (sprintf "%d", $mem_util{$pool_index}{$ciscoMemoryPoolUsed} / $total_mem * 100);
		
		$message .= "mem$pool_index" . "_$mem_util{$pool_index}{$ciscoMemoryPoolName}=$avg_mem_util%, ";
        $perfdata .= " mem$pool_index" . "_$mem_util{$pool_index}{$ciscoMemoryPoolName}=$avg_mem_util%";
		
		if ($avg_mem_util > $opt_w) {
			if ($return_code == 0) {
				$return_code = $ERRORS{"WARNING"};
			}
		} 
		if ($avg_mem_util > $opt_c) {
			$return_code = $ERRORS{"CRITICAL"};	
		} 
		
	}

}


if ($model eq 'asr1k' or $model eq 'asr9k' ) { 

	foreach my $pool_index (keys %mem_util) {
		if ($mem_util{$pool_index}{$cempMemPoolName} =~ /reserved/ or $mem_util{$pool_index}{$cempMemPoolName} =~ /image/) {
			# ignore reserved and image memory pools
			next;	
		}

		$pool_index =~ /^\.(\d+)\./;
		my $entPhysicalIndex = $1;
        #print "entPhysicalIndex:$entPhysicalIndex\n";
		my $comp_name = $physical_names{$entPhysicalIndex}{$entPhysicalName};
		$comp_name =~ s/\s//g;
        $comp_name =~ s/\//_/g;
        $comp_name =~ s/module0_//g;
        $comp_name =~ s/_CPU0//g;
        #print "$comp_name\n";
        my $total_mem = $mem_util{$pool_index}{$cempMemPoolHCFree} + $mem_util{$pool_index}{$cempMemPoolHCUsed};
		my $avg_mem_util = (sprintf "%d", $mem_util{$pool_index}{$cempMemPoolHCUsed} / $total_mem * 100);
        #$message .= "$comp_name" . "_$mem_util{$pool_index}{$cempMemPoolName}_mem=$avg_mem_util%, ";
        $message .= "$comp_name" . "_mem=$avg_mem_util%, ";
        #$perfdata .= " $comp_name" . "_$mem_util{$pool_index}{$cempMemPoolName}_mem=$avg_mem_util%,";
        $perfdata .= " $comp_name" . "_mem=$avg_mem_util%,";

		if ($avg_mem_util > $opt_w) {
			if ($return_code == 0) {
				$return_code = $ERRORS{"WARNING"};
			}
		} 
		if ($avg_mem_util > $opt_c) {
			$return_code = $ERRORS{"CRITICAL"};	
		} 
		
	}	
	
}





if ($return_code == 2) {
		
	print "Memory Critical: $message";
	
	print " | ";
	
	print $perfdata;
	
	exit 2;
	
} elsif ($return_code == 1) {
	
	print "Memory Warning: $message";
	
	print " | ";
	
	print $perfdata;
	
	exit 1;
	
} else {
	
	print "Memory OK: $message";
	
	print " | ";
	
	print $perfdata;
	
	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_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 %nexus_values = %{$result} ;
    my $id;
    my %nexus_return;
    while(($key,$value) = each(%nexus_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") ;
			$nexus_return{$id}{$key} = $value;
			$nexus_return{$id}{"id"} = $id ;
        } else {
        	$id = $key ;
        	$id =~ s/.*((\.[0-9]+){$num_indexes})$/$1/;
        	$key =~ s/(.*)(\.[0-9]+){$num_indexes}$/$1/;
        	verbose("key=$key, id=$id, value=$value", "15") ;
			$nexus_return{$id}{$key} = $value;
			$nexus_return{$id}{"id"} = $id ;
        }
    }
    return(%nexus_return) ;
}
