#!/usr/bin/env perl

#####################################
#####################################
### ______               _     =) ###
### | ___ \             | |       ###
### | |_/ / __ _  _ __  | |       ###
### |    / / _` || '_ \ | |       ###
### | |\ \| (_| || | | || |____   ###
### \_| \_|\__,_||_| |_|\_____/   ###
#####################################
#####################################

use strict;
use Net::SNMP;
use Try::Tiny;
my $stat;
my $msg;
my $perf;
my $days = 14;
my $script_name = "check-cisco.pl";

### SNMP OIDs
###############
# Temperature
my $S_temp = ".1.3.6.1.4.1.9.9.13.1.3.1.3";
# Memory
my $S_mem_used = ".1.3.6.1.4.1.9.9.48.1.1.1.5.1"; # Byte
my $S_mem_free = ".1.3.6.1.4.1.9.9.48.1.1.1.6.1"; # Byte
# CPU Load
my $S_load_5s = ".1.3.6.1.4.1.9.2.1.56.0";
my $S_load_1m = ".1.3.6.1.4.1.9.2.1.57.0";
my $S_load_5m = ".1.3.6.1.4.1.9.2.1.58.0";
# Power Supply
my $S_ps = ".1.3.6.1.4.1.9.9.13.1.5.1";
my $S_ps_name = "$S_ps.2";
my $S_ps_stat = "$S_ps.3";
# Fan
my $S_fan = ".1.3.6.1.4.1.9.9.13.1.4.1";
my $S_fan_name = "$S_fan.2";
my $S_fan_stat = "$S_fan.3";
# Module
my $S_module_status = ".1.3.6.1.4.1.9.9.117.1.2.1.1.2";
# Interfaces
my $S_int_entry = ".1.3.6.1.2.1.2.2.1";
my $S_int_desc = "$S_int_entry.2";
my $S_int_adminstatus = "$S_int_entry.7";
my $S_int_operstatus = "$S_int_entry.8";
my $S_int_lastchange = "$S_int_entry.9";
my $S_int_InOctets = "$S_int_entry.10";
my $S_int_OutOctets = "$S_int_entry.16";
my $S_int_number = ".1.3.6.1.2.1.2.1.0";

# SNMP Status Codes
my %phy_dev_status = (
	1 => 'normal',
	2 => 'warning',
	3 => 'critical',
	4 => 'shutdown',
	5 => 'notPresent',
	6 => 'notFunctioning',
);
my %module_status_code = (
	1 => 'unknown',
	2 => 'ok',
	3 => 'disabled',
	4 => 'okButDiagFailed',
	5 => 'boot',
	6 => 'selfTest',
	7 => 'failed',
	8 => 'missing',
	9 => 'mismatchWithParent',
	10 => 'mismatchConfig',
	11 => 'diagFailed',
	12 => 'dormant',
	13 => 'outOfServiceAdmin',
	14 => 'outOfServiceEnvTemp',
	15 => 'poweredDown',
	16 => 'poweredUp',
	17 => 'powerDenied',
	18 => 'powerCycled',
	19 => 'okButPowerOverWarning',
	20 => 'okButPowerOverCritical',
	21 => 'syncInProgress',
);
my %int_status_index = (
	1 => 'up',
	2 => 'down',
	3 => 'testing',
	4 => 'unknown',
	5 => 'notPresent',
	6 => 'lowerLayerDown',
);

### Functions
###############
sub _create_session {
	my ($server, $comm) = @_;
	my $version = 1;
	my ($sess, $err) = Net::SNMP->session( -hostname => $server, -version => $version, -community => $comm);
	if (!defined($sess)) {
		print "Can't create SNMP session to $server\n";
		exit(1);
	}
	return $sess;
}

sub FSyntaxError {
	print "Syntax Error !\n";
# 	print "$0 -H [ip|dnsname] -C [snmp community] -t [temp|fan|ps|cpu|mem|module|freeint] -w [warning value] -c [critical value] -d [days]\n";
	print "$script_name\n";
	print "-H = Ip/Dns Name of the Switch\n";
	print "-C = SNMP Community\n";
	print "-t = Check type\n";
	print "\ttemp   	- Temperature\n";
	print "\tfan    	- Fan Fail\n";
	print "\tps     	- Power Supply Fail\n";
	print "\tcpu    	- CPU Load\n";
	print "\tmem    	- Memory\n";
	print "\tmodule		- Module Health\n";
	print "\tfreeint - Free eth interfaces for X days (-d)\n";
	print "\tint - Interface Operation Stat (use with -i or -o)\n";
	print "-w = Warning Value\n";
	print "-c = Critical Value\n";
	print "-d = number of days that th ethernet interface hasn't change state, default is 14 (only for -t freeint)\n";
	print "-i = Interface Name (only for -t int)\n";
	print "-o = Interface OID (only for -t int)\n";
	exit(3);
}

if($#ARGV < 5 or $#ARGV > 11) {
	print "Not enough arguments!";
        FSyntaxError;
}

### Gather input from user
#############################
my $switch;
my $community;
my $check_type;
my $warn = 0;
my $crit = 0;
my $int;
my $oidint;

while(@ARGV) {
	my $temp = shift(@ARGV);
	if("$temp" eq '-H') {
		$switch = shift(@ARGV);
	} elsif("$temp" eq '-C') {
		$community = shift(@ARGV);
	} elsif("$temp" eq '-t') {
		$check_type = shift(@ARGV);
	} elsif("$temp" eq '-w') {
		$warn = shift(@ARGV);
	} elsif("$temp" eq '-c') {
		$crit = shift(@ARGV);
	} elsif("$temp" eq '-i') {
		$int = shift(@ARGV);
	} elsif("$temp" eq '-o') {
		$oidint = shift(@ARGV);
	} elsif("$temp" eq '-d') {
		$days = shift(@ARGV);
		if("$days" eq "") {
			$days = 14;
		}
	} else {
		FSyntaxError();
	}
}

# Validate Warning
if("$check_type" ne "temp") {
	if($warn > $crit and "$check_type" ne "freeint" and "$check_type" ne "mem") {
		print "Warning can't be larger then Critical: $warn > $crit\n";
		FSyntaxError();
	} elsif($warn < $crit and "$check_type" eq "freeint") {
		print "Warning can't be smaller then Critical: $warn < $crit in intfree check\n";
		FSyntaxError();
	} elsif($warn < $crit and "$check_type" eq "mem") {
		print "Warning can't be smaller then Critical: $warn < $crit in intfree check\n";
		FSyntaxError();
	}
}

# Establish SNMP Session
#try {
	our $snmp_session = _create_session($switch,$community);
#} catch {
#	print "UNABLE TO CONNECT TO $switch!";
#	$stat = 2;
#	exit($stat);
#};

### Temperature ###
if($check_type =~ /^temp/) {
	my $temp;
	my $R_tbl = $snmp_session->get_table($S_temp);
	foreach my $oid ( keys %$R_tbl) {
		$temp = "$$R_tbl{$oid}";
		last;
	}
	
	if("$temp" eq "") {
		print "The switch $switch can't report temperature via SNMP\n";
		FSyntaxError();
	}
	
	if($temp > 1) {
		if($warn > $crit and "$check_type") {
			print "Warning can't be larger then Critical: $warn > $crit\n";
			FSyntaxError();
		}
		if($temp <= $warn) {
			$stat = 0;
			$msg = "Temperature: OK - Tempeture is $temp Celsius";
		}  elsif($temp > $warn and $temp < $crit) {
			$stat = 1;
			$msg = "Temperature: Warn - Tempeture is $temp Celsius";
		} elsif($temp >= $crit) {
			$stat = 2;
			$msg = "Temperature: CRIT - Tempeture is $temp Celsius";
		}
		$perf = "temperature=$temp;$warn;$crit";
	} else {
		if($warn > 0 or $crit > 0) {
			print "ERR:\nSome switches only show boolean value 0=OK 1=ERROR\nplease dont use -w and -c arguments\n\n";
			FSyntaxError();
		}
		if($temp == 1) {
			$stat = 0;
			$msg = "Temperature: OK";
		} else {
			$stat = 2;
			$msg = "Temperature: CRIT";
		}
		$perf = "temperature=$temp";
	}

### Memory ###

} elsif($check_type eq "mem") {
	my $R_mem_used = $snmp_session->get_request(-varbindlist => [$S_mem_used]);
	my $mem_used = "$R_mem_used->{$S_mem_used}";
	my $R_mem_free = $snmp_session->get_request(-varbindlist => [$S_mem_free]);
	my $mem_free = "$R_mem_free->{$S_mem_free}";
	my $mem_total = $mem_free + $mem_used;
	
	$mem_used = int($mem_used / 1024 / 1024);
	$mem_free = int($mem_free / 1024 / 1024);
	$mem_total = int($mem_total / 1024 / 1024);
	
	my $mem_free_perc = int($mem_free / $mem_total * 100);
	
	if($mem_free_perc > $warn) {
		$stat = 0;
		$msg = "Memory: OK - Free Memory $mem_free_perc%";
	}  elsif($mem_free_perc <= $warn and $mem_free_perc > $crit) {
		$stat = 1;
		$msg = "Memory: Warn - Free Memory $mem_free_perc %";
	} elsif($mem_free_perc <= $crit) {
		$stat = 2;
		$msg = "Memory: CRIT - Free Memory $mem_free_perc %";
	}

	$perf = "memory_total=$mem_total\MB memory_used=$mem_used\MB";

### Interface Stat ###

} elsif($check_type eq "int") {
	my $R_tbl;
	if ($oidint) {
		$R_tbl = $snmp_session->get_request(-varbindlist => ["$oidint"]);
		$int = $$R_tbl{"$oidint"};
	} else {
		$R_tbl = $snmp_session->get_table($S_int_desc);
	}
	my $is_int_exists = 0;
	foreach my $oid ( keys %$R_tbl) {
		my $name = "$$R_tbl{$oid}";
		if($name eq $int) {
			$is_int_exists++;
			my $id = "$oid";
			$id =~ s/$S_int_desc\.//;
			my $R_stat = $snmp_session->get_request(-varbindlist => ["$S_int_operstatus.$id"]);
			my $int_stat = $R_stat->{"$S_int_operstatus.$id"};
			if($int_stat != 1) {
				$stat = 2;
				$msg = "CRIT: $int -> $int_status_index{$int_stat}";
				$perf = "int=0";
			} else {
				$stat = 0;
				$msg = "OK: $int -> $int_status_index{$int_stat}";
				$perf = "int=1";
			}
			last;
		}
		
	}
	
	if($is_int_exists == 0) {
		$stat = 3;
		$msg = "UNKNOWN: $int does not exists";
		$perf = "int=0";
	}

### CPU Load ###

} elsif($check_type eq "cpu") {
	my $R_load_5s = $snmp_session->get_request(-varbindlist => [$S_load_5s]);
	my $load_5s = "$R_load_5s->{$S_load_5s}";
	my $R_load_1m = $snmp_session->get_request(-varbindlist => [$S_load_1m]);
	my $load_1m = "$R_load_1m->{$S_load_1m}";
	my $R_load_5m = $snmp_session->get_request(-varbindlist => [$S_load_5m]);
	my $load_5m = "$R_load_5m->{$S_load_5m}";
	
	if($load_5s <= $warn) {
		$stat = 0;
		$msg = "Cpu: OK - Cpu Load $load_5s% $load_1m% $load_5m%";
	}  elsif($load_5s > $warn and $load_5s < $crit) {
		$stat = 1;
		$msg = "Cpu: Warn - Cpu Load $load_5s% $load_1m% $load_5m%";
	} elsif($load_5s >= $crit) {
		$stat = 2;
		$msg = "Cpu: CRIT - Cpu Load $load_5s% $load_1m% $load_5m%";
	}

	$perf = "cpu_5s=$load_5s\percent;$warn;$crit cpu_1m=$load_1m\percent cpu_5m=$load_5m\percent";

### Fan Status ###

} elsif($check_type eq "fan") {
	my $R_tbl = $snmp_session->get_table($S_fan_name);
	my $total_err = 0;
	my $err_msg;
	my $sum = 0;
	foreach my $oid ( keys %$R_tbl) {
		$sum = $sum + 1;
		my $name = "$$R_tbl{$oid}";
		my $id = "$oid";
		$id =~ s/$S_fan_name\.//;
		my $R_stat = $snmp_session->get_request(-varbindlist => ["$S_fan_stat.$id"]);
		my $stat = $R_stat->{"$S_fan_stat.$id"};
		if($stat != 1) {
			$total_err = $total_err + 1;
			$err_msg = "$err_msg $name -> $phy_dev_status{$stat}";
		}
	}
	if($sum == 0) {
		$msg = "Fans: unknown, no fans were detected.";
		print "$msg";
		$stat = 3;
	}
	else {
		
		if($total_err != 0) {
			$err_msg = ", $err_msg have an error";
		} else {
			$err_msg = "all good";
		}	
		if($total_err <= $warn) {
			$stat = 0;
			$msg = "Fans: OK - $sum Fans are running $err_msg";
		}  elsif($total_err > $warn and $total_err < $crit) {
			$stat = 1;
			$msg = "Fans: Warn - $sum Fans are running $err_msg";
		} elsif($total_err >= $crit) {
			$stat = 2;
			$msg = "Fans: Crit - $sum Fans are running $err_msg";
		}	
	}
	
	$perf = "total=$sum err=$total_err";

### Power Supplies ###

} elsif($check_type eq "ps") {
	my $R_tbl = $snmp_session->get_table($S_ps_name);
	my $total_err = 0;
	my $err_msg;
	my $sum = 0;
	foreach my $oid ( keys %$R_tbl) {
		$sum = $sum + 1;
		my $name = "$$R_tbl{$oid}";
		my $id = "$oid";
		$id =~ s/$S_ps_name\.//;
		my $R_stat = $snmp_session->get_request(-varbindlist => ["$S_ps_stat.$id"]);
		my $stat = $R_stat->{"$S_ps_stat.$id"};
		if($stat != 1) {
			$total_err = $total_err + 1;
			$err_msg = "$err_msg $name -> $phy_dev_status{$stat}";
		}
	}
	
	if($total_err != 0) {
		$err_msg = ", $err_msg have an error";
	} else {
		$err_msg = "all good";
	}
	
	if($total_err <= $warn) {
		$stat = 0;
		$msg = "PS: OK - $sum PS are running $err_msg";
	}  elsif($total_err > $warn and $total_err < $crit) {
		$stat = 1;
		$msg = "PS: Warn - $sum PS are running $err_msg";
	} elsif($total_err >= $crit) {
		$stat = 2;
		$msg = "PS: Crit - $sum PS are running $err_msg";
	}
	
	$perf = "total=$sum err=$total_err";

### Module Status ###

} elsif($check_type eq "module") {
	my $R_tbl = $snmp_session->get_table($S_module_status);
	my $total_err = 0;
	my $err_msg;
	my $sum = 0;
	foreach my $oid ( keys %$R_tbl) {
		$sum = $sum + 1;
		my $module_status = "$$R_tbl{$oid}";
		my $id = "$oid";
		$id =~ s/$S_module_status\.//;
		if($module_status != 2) {
			$total_err = $total_err + 1;
			$err_msg = "$err_msg $id -> $module_status_code{$module_status}";
		}
	}
	
	if($sum == 0) {
		print "The switch $switch doesn't have any modules\n";
		FSyntaxError();
	}
	
	if($total_err != 0) {
		$err_msg = ", $err_msg have an error";
	} else {
		$err_msg = "all good";
	}
	
	if($total_err <= $warn) {
		$stat = 0;
		$msg = "Modules: OK - $sum Modules are running $err_msg";
	}  elsif($total_err > $warn and $total_err < $crit) {
		$stat = 1;
		$msg = "Modules: Warn - $sum Modules are running $err_msg";
	} elsif($total_err >= $crit) {
		$stat = 2;
		$msg = "Modules: Crit - $sum Modules are running $err_msg";
	}
	
	$perf = "total=$sum err=$total_err";

### Free Interfaces ###

} elsif($check_type eq "freeint") {
	
	my $R_int_number = $snmp_session->get_request(-varbindlist => [$S_int_number]);
	my $int_number = $R_int_number->{$S_int_number};
	
	my $R_tbl = $snmp_session->get_table($S_int_desc);
	my @ints;
	my $down = 0;
	my $sum = 0;
	
	foreach my $oid ( keys %$R_tbl) {
		if($$R_tbl{$oid} =~ /Ethernet/) {
			$sum++;
			my $id = "$oid";
			$id =~ s/$S_int_desc\.//;
			
			# Admin Status
			my $R_int_adminstatus = $snmp_session->get_request(-varbindlist => ["$S_int_adminstatus.$id"]);
			my $int_adminstatus = $R_int_adminstatus->{"$S_int_adminstatus.$id"};
			# Oper Status
			my $R_int_operstatus = $snmp_session->get_request(-varbindlist => ["$S_int_operstatus.$id"]);
			my $int_operstatus = $R_int_operstatus->{"$S_int_operstatus.$id"};
			# Inbout
			my $R_int_InOctets = $snmp_session->get_request(-varbindlist => ["$S_int_InOctets.$id"]);
			my $int_InOctets = $R_int_InOctets->{"$S_int_InOctets.$id"};
			# Outbound
			my $R_int_OutOctets = $snmp_session->get_request(-varbindlist => ["$S_int_OutOctets.$id"]);
			my $int_OutOctets = $R_int_OutOctets->{"$S_int_OutOctets.$id"};
			# Last Change
			my $R_int_lastchange = $snmp_session->get_request(-varbindlist => ["$S_int_lastchange.$id"]);
			my $int_lastchange = $R_int_lastchange->{"$S_int_lastchange.$id"};
			my @lastchanged = split(" ",$int_lastchange);
			
			if($int_adminstatus == 2 or $int_operstatus == 2) {
				if(("$lastchanged[1]" eq "days," and $lastchanged[1] => $days) or ($int_OutOctets == 0 and $int_InOctets == 0)) {
					$down++;
				}
			}
			
		}
	}
	
	if($down >= $warn) {
		$stat = 0;
		$msg = "Free Interfaces: OK - $down/$sum free interfaces for $days days";
	}  elsif($down < $warn and $down > $crit) {
		$stat = 1;
		$msg = "Free Interfaces: Warn - $down/$sum free interfaces for $days days";
	} elsif($down <= $crit) {
		$stat = 2;
		$msg = "Free Interfaces: CRIT - $down/$sum free interfaces for $days days";
	}
	
	$perf = "total_int=$int_number total_eth=$sum total_eth_free=$down";

### Bad Syntax ###

} else {
	FSyntaxError();
}

#} catch {
#	print "UNABLE TO CONNECT TO $switch!";
#	$stat = 2;
#	exit($stat);
#};
print "$msg | $perf\n";
exit($stat);
