#!/usr/bin/perl
# ============================================================================
# ============================== INFO ========================================
# ============================================================================
# Version	: 0.1
# Date		: July 16 2015
# Author	: Michiel Timmers ( michiel.timmers AT gmx.net)
# Licence 	: GPL - summary below
#
# ============================================================================
# ============================== SUMMARY =====================================
# ============================================================================
#
# Some checks for OSPF Area event changes
#
# Thanks to Utelisys for providing the code for reading status file
#
# Check the http://exchange.nagios.org website for new versions.
# For comments, questions, problems and patches send me an 
# e-mail (michiel.timmmers AT gmx.net). 
#
# ============================================================================
# ============================== LICENCE =====================================
# ============================================================================
# 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 3 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, see <http://www.gnu.org/licenses/>
#
# ============================================================================
# ============================== HELP ========================================
# ============================================================================
# Help : ./check_snmp_cisco_ospf-area.pl --help
#
# ============================================================================

use warnings;
use strict;
use Net::SNMP;
use Getopt::Long;
use Time::Piece;
use POSIX;
use Tie::File;

#use lib "/usr/local/nagios/libexec";
#use utils qw(%ERRORS $TIMEOUT);


# ============================================================================
# ============================== NAGIOS VARIABLES ============================
# ============================================================================

my $TIMEOUT 				= 15;	# This is the global script timeout, not the SNMP timeout
my %ERRORS				= ('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4);
my @Nagios_state 			= ("UNKNOWN","OK","WARNING","CRITICAL"); # Nagios states coding

# ============================================================================
# ============================== OID VARIABLES ===============================
# ============================================================================

my $cisco_ospfAreaId_oid     		= "1.3.6.1.2.1.14.2.1.1";	
my $cisco_ospfImportAsExtern_oid    	= "1.3.6.1.2.1.14.2.1.3";
my $cisco_ospfAuthType_oid     		= "1.3.6.1.2.1.14.2.1.2";
my $cisco_ospfSpfRuns_oid     		= "1.3.6.1.2.1.14.2.1.4";
my $cisco_ospfAreaLsaCksumSum_oid  	= "1.3.6.1.2.1.14.2.1.8";
my $cisco_ospfAreaStatus_oid     	= "1.3.6.1.2.1.14.2.1.10";	

# ============================================================================
# ============================== GLOBAL VARIABLES ============================
# ============================================================================

my $Version			= '0.1';	# Version number of this script
my $o_host			= undef; 	# Hostname
my $o_community 		= undef; 	# Community
my $o_port	 		= 161; 		# Port
my $o_help			= undef; 	# Want some help ?
my $o_verb			= undef;	# Verbose mode
my $o_version			= undef;	# Print version
my $o_timeout			= undef; 	# Timeout (Default 5)
my $o_version1			= undef;	# Use SNMPv1
my $o_version2			= undef;	# Use SNMPv2c
my $o_domain			= undef;	# Use IPv6
my $o_login			= undef;	# Login for SNMPv3
my $o_passwd			= undef;	# Pass for SNMPv3
my $v3protocols			= undef;	# V3 protocol list.
my $o_authproto			= 'sha';	# Auth protocol
my $o_privproto			= 'aes';	# Priv protocol
my $o_privpass			= undef;	# priv password



# ============================================================================
# ============================== SUBROUTINES (FUNCTIONS) =====================
# ============================================================================

# Subroutine: Print version
sub p_version { 
	print "check_snmp_cisco_ospf-area version : $Version\n"; 
}

# Subroutine: Print Usage
sub print_usage {
    print "Usage: $0 [-v] -H <host> [-6] -C <snmp_community> [-2] | (-l login -x passwd [-X pass -L <authp>,<privp>])  [-p <port>] [-t <timeout>] [-V]\n";
}

# Subroutine: Check number
sub isnnum { # Return true if arg is not a number
	my $num = shift;
	if ( $num =~ /^(\d+\.?\d*)|(^\.\d+)$/ ) { return 0 ;}
	return 1;
}

# Subroutine: Set final status
sub set_status { # Return worst status with this order : OK, unknown, warning, critical 
	my $new_status = shift;
	my $cur_status = shift;
	if ($new_status == 1 && $cur_status != 2) {$cur_status = $new_status;}
	if ($new_status == 2) {$cur_status = $new_status;}
	if ($new_status == 3 && $cur_status == 0) {$cur_status = $new_status;}
	return $cur_status;
}

# Subroutine: Check if SNMP table could be retrieved, otherwise give error
sub check_snmp_result {
	my $snmp_table		= shift;
	my $snmp_error_mesg	= shift;

	# Check if table is defined and does not contain specified error message.
	# Had to do string compare it will not work with a status code
	if (!defined($snmp_table) && $snmp_error_mesg !~ /table is empty or does not exist/) {
		printf("ERROR: ". $snmp_error_mesg . " : UNKNOWN\n");
		exit $ERRORS{"UNKNOWN"};
	}
}

# Subroutine: Print complete help
sub help {
	print "\nCisco OSPF AREA plugin for Nagios\nVersion: ",$Version,"\n\n";
	print_usage();
	print <<EOT;

Options:
-v, --verbose
   Print extra debugging information 
-h, --help
   Print this help message
-H, --hostname=HOST
   Hostname or IPv4/IPv6 address of host to check
-6, --use-ipv6
   Use IPv6 connection
-C, --community=COMMUNITY NAME
   Community name for the host's SNMP agent
-1, --v1
   Use SNMPv1
-2, --v2c
   Use SNMPv2c (default)
-l, --login=LOGIN ; -x, --passwd=PASSWD
   Login and auth password for SNMPv3 authentication 
   If no priv password exists, implies AuthNoPriv 
-X, --privpass=PASSWD
   Priv password for SNMPv3 (AuthPriv protocol)
-L, --protocols=<authproto>,<privproto>
   <authproto> : Authentication protocol (md5|sha : default sha)
   <privproto> : Priv protocole (des|aes : default aes) 
-P, --port=PORT
   SNMP port (Default 161)
-t, --timeout=INTEGER
   Timeout for SNMP in seconds (Default: 15)
-V, --version
   Prints version number

Notes:
- Check the http://exchange.nagios.org website for new versions.
- For questions, problems and patches send me an e-mail (michiel.timmmers AT gmx.net).

EOT
}

# Subroutine: Verbose output
sub verb { 
	my $t=shift; 
	print $t,"\n" if defined($o_verb); 
}

# Subroutine: Verbose output
sub check_options {
	Getopt::Long::Configure ("bundling");
	GetOptions(
		'v'	=> \$o_verb,		'verbose'	=> \$o_verb,
	        'h'     => \$o_help,    	'help'        	=> \$o_help,
	        'H:s'   => \$o_host,		'hostname:s'	=> \$o_host,
	        'p:i'   => \$o_port,   		'port:i'	=> \$o_port,
	        'C:s'   => \$o_community,	'community:s'	=> \$o_community,
		'l:s'	=> \$o_login,		'login:s'	=> \$o_login,
		'x:s'	=> \$o_passwd,		'passwd:s'	=> \$o_passwd,
		'X:s'	=> \$o_privpass,	'privpass:s'	=> \$o_privpass,
		'L:s'	=> \$v3protocols,	'protocols:s'	=> \$v3protocols,   
	        't:i'   => \$o_timeout,       	'timeout:i'     => \$o_timeout,
		'V'	=> \$o_version,		'version'	=> \$o_version,
		'6'     => \$o_domain,        	'use-ipv6'      => \$o_domain,
		'1'     => \$o_version1,        'v1'            => \$o_version1,
		'2'     => \$o_version2,        'v2c'           => \$o_version2
	);


	# Basic checks
	if (defined($o_timeout) && (isnnum($o_timeout) || ($o_timeout < 2) || ($o_timeout > 60))) { 
		print "Timeout must be >1 and <60 !\n";
		print_usage();
		exit $ERRORS{"UNKNOWN"};
	}
	if (!defined($o_timeout)) {
		$o_timeout=15;
	}
	if (defined ($o_help) ) {
		help();
		exit $ERRORS{"UNKNOWN"};
	}

	if (defined($o_version)) { 
		p_version(); 
		exit $ERRORS{"UNKNOWN"};
	}

	# check host and filter 
	if ( ! defined($o_host) ) {
		print_usage();
		exit $ERRORS{"UNKNOWN"};
	}

	# Check IPv6 
	if (defined ($o_domain)) {
		$o_domain="udp/ipv6";
	} else {
		$o_domain="udp/ipv4";
	}

	# Check SNMP information
	if ( !defined($o_community) && (!defined($o_login) || !defined($o_passwd)) ){ 
		print "Put SNMP login info!\n"; 
		print_usage(); 
		exit $ERRORS{"UNKNOWN"};
	}
	if ((defined($o_login) || defined($o_passwd)) && (defined($o_community) || defined($o_version2)) ){ 
		print "Can't mix SNMP v1,v2c,v3 protocols!\n"; 
		print_usage(); 
		exit $ERRORS{"UNKNOWN"};
	}

	# Check SNMPv3 information
	if (defined ($v3protocols)) {
		if (!defined($o_login)) { 
			print "Put SNMP V3 login info with protocols!\n"; 
			print_usage(); 
			exit $ERRORS{"UNKNOWN"};
		}
		my @v3proto=split(/,/,$v3protocols);
		if ((defined ($v3proto[0])) && ($v3proto[0] ne "")) {
			$o_authproto=$v3proto[0];
		}
		if (defined ($v3proto[1])) {
			$o_privproto=$v3proto[1];
		}
		if ((defined ($v3proto[1])) && (!defined($o_privpass))) {
			print "Put SNMP v3 priv login info with priv protocols!\n";
			print_usage(); 
			exit $ERRORS{"UNKNOWN"};
		}
	}
}


# ============================================================================
# ============================== MAIN ========================================
# ============================================================================

check_options();

# Check gobal timeout if SNMP screws up
if (defined($TIMEOUT)) {
	verb("Alarm at ".$TIMEOUT." + ".$o_timeout);
	alarm($TIMEOUT+$o_timeout);
} else {
	verb("no global timeout defined : ".$o_timeout." + 15");
	alarm ($o_timeout+15);
}

# Report when the script gets "stuck" in a loop or takes to long
$SIG{'ALRM'} = sub {
	print "UNKNOWN: Script timed out\n";
	exit $ERRORS{"UNKNOWN"};
};

# Connect to host
my ($session,$error);
if (defined($o_login) && defined($o_passwd)) {
	# SNMPv3 login
	verb("SNMPv3 login");
	if (!defined ($o_privpass)) {
		# SNMPv3 login (Without encryption)
		verb("SNMPv3 AuthNoPriv login : $o_login, $o_authproto");
		($session, $error) = Net::SNMP->session(
		-domain		=> $o_domain,
		-hostname	=> $o_host,
		-version	=> 3,
		-username	=> $o_login,
		-authpassword	=> $o_passwd,
		-authprotocol	=> $o_authproto,
		-timeout	=> $o_timeout
	);  
	} else {
		# SNMPv3 login (With encryption)
		verb("SNMPv3 AuthPriv login : $o_login, $o_authproto, $o_privproto");
		($session, $error) = Net::SNMP->session(
		-domain		=> $o_domain,
		-hostname	=> $o_host,
		-version	=> 3,
		-username	=> $o_login,
		-authpassword	=> $o_passwd,
		-authprotocol	=> $o_authproto,
		-privpassword	=> $o_privpass,
		-privprotocol	=> $o_privproto,
		-timeout	=> $o_timeout
		);
	}
} else {
	if ((defined ($o_version2)) || (!defined ($o_version1))) {
		# SNMPv2 login
		verb("SNMP v2c login");
		($session, $error) = Net::SNMP->session(
		-domain		=> $o_domain,
		-hostname	=> $o_host,
		-version	=> 2,
		-community	=> $o_community,
		-port		=> $o_port,
		-timeout	=> $o_timeout
		);
	} else {
		# SNMPv1 login
		verb("SNMP v1 login");
		($session, $error) = Net::SNMP->session(
		-domain		=> $o_domain,
		-hostname	=> $o_host,
		-version	=> 1,
		-community	=> $o_community,
		-port		=> $o_port,
		-timeout	=> $o_timeout
		);
	}
}

# Check if there are any problems with the session
if (!defined($session)) {
	printf("ERROR opening session: %s.\n", $error);
	exit $ERRORS{"UNKNOWN"};
}

my $exit_val=undef;

# ============================================================================
# ======================= Cisco - OSPF AREA CHECKS ===========================
# ============================================================================

# Define variables
my $output			= "Area ID Status: ";	
my $output_print		= "";					
my $final_status		= 0;
my @temp_oid;
my @values;
my @values2;
my @values3;
my $result_t;
my ($ospfAreaId,$ospfImportAsExtern,$ospfAuthType,$ospfSpfRuns,$ospfAreaLsaCksumSum,$ospfAreaStatus)=(undef,undef,undef,undef,undef,undef);
my $base_file="/tmp/cisco_snmp_ospf-area_nagios_".$o_host;


my $cisco_ospfAreaId  =  $session->get_table(Baseoid => $cisco_ospfAreaId_oid );
&check_snmp_result($cisco_ospfAreaId,$session->error);

my $cisco_ospfImportAsExtern  =  $session->get_table(Baseoid => $cisco_ospfImportAsExtern_oid );
&check_snmp_result($cisco_ospfImportAsExtern,$session->error);

my $cisco_ospfAuthType  =  $session->get_table(Baseoid => $cisco_ospfAuthType_oid );
&check_snmp_result($cisco_ospfAuthType,$session->error);

my $cisco_ospfSpfRuns  =  $session->get_table(Baseoid => $cisco_ospfSpfRuns_oid );
&check_snmp_result($cisco_ospfSpfRuns,$session->error);

my $cisco_ospfAreaLsaCksumSum  =  $session->get_table(Baseoid => $cisco_ospfAreaLsaCksumSum_oid );
&check_snmp_result($cisco_ospfAreaLsaCksumSum,$session->error);

my $cisco_ospfAreaStatus  =  $session->get_table(Baseoid => $cisco_ospfAreaStatus_oid );
&check_snmp_result($cisco_ospfAreaStatus,$session->error);

# Clear the SNMP Transport Domain and any errors associated with the object.
$session->close;


#Get the date format
my $datetime = strftime( '%Y/%m/%d-%H:%M:%S', localtime );

# Open file, or otherwise create it with some foo date. 
unless(open FILE, $base_file) {
	open FILE, '>'.$base_file;
	print FILE "$datetime --FILE STARTED--\n";
}
close FILE;


# Read file and count number of lines
my $cnt;
open(FH,$base_file) or die "problem opening $base_file\n";
$cnt++ while <FH>;
close FH;

if (defined($cisco_ospfAreaId)){

	if (defined($cisco_ospfAreaId)) {
		foreach my $key ( keys %$cisco_ospfAreaId) {
			if ($key =~ /$cisco_ospfAreaId_oid/) {

				$key =~ s/$cisco_ospfAreaId_oid//;

				$ospfAreaId 		= $$cisco_ospfAreaId{$cisco_ospfAreaId_oid.$key};	
				$ospfImportAsExtern 	= $$cisco_ospfImportAsExtern{$cisco_ospfImportAsExtern_oid.$key};	
				$ospfAuthType 		= $$cisco_ospfAuthType{$cisco_ospfAuthType_oid.$key};	
				$ospfSpfRuns 		= $$cisco_ospfSpfRuns{$cisco_ospfSpfRuns_oid.$key};	
				$ospfAreaLsaCksumSum 	= $$cisco_ospfAreaLsaCksumSum{$cisco_ospfAreaLsaCksumSum_oid.$key};	
				$ospfAreaStatus 	= $$cisco_ospfAreaStatus{$cisco_ospfAreaStatus_oid.$key};	

				if ($ospfAreaStatus eq "1"){$output.= $ospfAreaId." = active, ";}
				if ($ospfAreaStatus eq "2"){$output.= $ospfAreaId." = NOTINSERVICE!, ";$final_status = &set_status(2,$final_status);}
				if ($ospfAreaStatus eq "3"){$output.= $ospfAreaId." = NOTREADY!, ";$final_status = &set_status(2,$final_status);}
				if ($ospfAreaStatus eq "4"){$output.= $ospfAreaId." = CREATEANDGO!, ";$final_status = &set_status(2,$final_status);}
				if ($ospfAreaStatus eq "5"){$output.= $ospfAreaId." = CREATEANDWAIT!, ";$final_status = &set_status(2,$final_status);}
				if ($ospfAreaStatus eq "6"){$output.= $ospfAreaId." = DESTROY!, ";$final_status = &set_status(2,$final_status);}

				$output_print.= $ospfAreaId."-".$ospfSpfRuns."|";
			}
		}

		# See if we need to remove the first line, 144 lines with 10 minute interval is 24 hours
		my $maxmimum_number_lines_file = 144;
		if ($cnt > $maxmimum_number_lines_file) {
			tie my @array, 'Tie::File', $base_file or die $!;
			shift @array;
			untie @array;
		}else{
			$output.= "Populating $base_file with entries ($cnt of $maxmimum_number_lines_file entries). ";
			$final_status = &set_status(1,$final_status);
		}

		# Print to file
		my $outfile = $base_file;
		open (FILE, ">> $outfile") || die "problem opening $outfile\n";
		print FILE $datetime."|".$output_print."\n";
	}


my $ip_list_hash = {};

next if ($base_file =~ m/^\./);

open(my $FH, '<', "$base_file") or die "Couldn't Open file  $base_file $!";

my $ip_count;
my $count = 0;
my $privious_input = '';

while (my $line = <$FH>) {
	chomp($line);
	my ($date,@area_data) = split(/\|/, $line);
	my $current_input = join('|',@area_data) ;

        if ($current_input eq $privious_input) {
        }

	else{
		foreach my $ip_list (@area_data){
			my($ip,$no_of_run) = split(/-/, $ip_list);	
			$ip_list_hash->{$base_file}->{$ip}->{$no_of_run} = $no_of_run;
		}
        }

	map{
		my($ipx,$no_of_runx) = split(/-/, $_);
		my $match = 1;
		foreach my $ip_list (@area_data){
			my($ip,$no_of_run) = split(/-/, $ip_list);



			if($ip eq $ipx) {
				$match = 0;
				last;
			}
		}
	$ip_list_hash->{$base_file}->{$ipx}->{inconsistent} = qq{inconsistent} if ($match);
	}split(/\|/, $privious_input);

	$privious_input = $current_input;
}

$output.= "Re-calculations in last 24 hours:";

map{
	my $base_file = $_;
	foreach my $ipx ( keys %{$ip_list_hash->{$base_file}}){
		if (exists $ip_list_hash->{$base_file}->{$ipx}->{inconsistent}) {
			$output.= " $ipx = INCONSISTENT!, ";
			$final_status = &set_status(1,$final_status);
		}else{
            		my $size = keys %{$ip_list_hash->{$base_file}->{$ipx}};

			if ($size > 1){
				$output.= " $ipx = $size!, ";
				$final_status = &set_status(1,$final_status);
			}

			if ($size == 1){
				$output.= " $ipx = 0,";
			}

			#$output.= " $ipx = $size!, " if $size > 1;
			#$output.= " $ipx = 0," if $size == 1;
		}
	}
} sort keys %{$ip_list_hash};

}else{
	$final_status = 3;
	$output = "Can't get SNMP OSPF";
}

close FILE;

if ($final_status == 3) {
	print $output,": UNKNOWN\n";
	exit $ERRORS{"UNKNOWN"};
}
	
if ($final_status == 2) {
	print $output,": CRITICAL\n";
	exit $ERRORS{"CRITICAL"};
}

if ($final_status == 1) {
	print $output,": WARNING\n";
	exit $ERRORS{"WARNING"};
}

print $output,": OK\n";
exit $ERRORS{"OK"};


# ============================================================================
# ============================== NO CHECK DEFINED ============================
# ============================================================================

print "Unknown check type : UNKNOWN\n";
exit $ERRORS{"UNKNOWN"};

