#!/usr/bin/perl -w
# check_ont_condition - nagios plugin
#
# by Frank Bulk <frnkblk@iname.com>
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

use Net::Telnet::Cisco;
use Getopt::Long;

# change to your CMS' IP
my $cms = '0.0.0.0';
# change to your CMS' username and password
my @cms_node = ($cms, 'username', 'password', 0, 'CMS');
# list each *network* (not shelf) by it's name.  Two examples are listed.
my @networks = ('SHELF 200', 'NODE 100 VT');
my $network;
my @shelves;
my $shelf;
my $Aid;
my @temp_array;
my @results;
my $score = 0;
my $total_score = 0;
my $temp_score_ref = {};
my %score = (
	CR => 0,
	MJ => 0,
	MN => 0,
	NA => 0,
	NR => 0,
);
my %ERRORS = (
        'OK'       => '0',
        'WARNING'  => '1',
        'CRITICAL' => '2',
        'UNKNOWN'  => '3',
);
my $state = "UNKNOWN";
my $warn_level;
my $critical_level;

Getopt::Long::Configure("no_ignore_case");
my $status = GetOptions(
        "w|warn=i"		=> \$warn_level,
        "c|critical=i"		=> \$critical_level,
);

if (!defined($warn_level)) {
	$warn_level = 2;
}
if (!defined($critical_level)) {
	$critical_level = 5;
}

$c = calix_login(0, @cms_node);

if ($ARGV[0]) {
	@networks = $ARGV[0];
}

foreach $network (@networks) {
	if ($ARGV[1]) {
		$shelves[0] = $ARGV[1];
	}
	else {
		@shelves = get_calix_shelves($c, $network);
	}
	@shelves = unique_sort(@shelves);
	foreach $shelf (@shelves) {
		$Aid = $shelf . "-ALL";
		($temp_score_ref, @temp_array) = retrieve_cond_ont($c, $Aid);
		foreach my $key (keys %$temp_score_ref) {
			$score{$key} += $temp_score_ref->{$key};
		}
		push(@results, @temp_array);
	}
}
@results = reverse sort @results;

calix_logout(0, $c);

if ($score{CR} || $score{MJ} || ($score{MN} >= $critical_level)) {
	$state = "CRITICAL";
}
elsif ($total_score >= $warn_level) {
	$state = "WARNING";
}
else {
	$state = "OK";
}
print ("$state: Critical: $score{CR}; Major: $score{MJ}; Minor: $score{MN}\n");
print grep(!/Not Reported/, @results);
print grep(/Not Reported/, @results);
exit $ERRORS{$state};

exit;

sub calix_login {
	($print, @calix_node) = @_;

	my $login_count = 3;
	my $logged_in = 0;
	my $inhmsg_count = 3;
	my $inhibit_messages = 0;
	my $cmd_string = '';
	my @empty;
	my @lines;

	if ($print) {
		print "Logging into $calix_node[4] Calix ($calix_node[0])...";
	}

	my $c = Net::Telnet->new(
		Host => $calix_node[0],
		Port => 2626,
		ErrMode => 'return',
		Timeout => 15,
		TelnetMode => $calix_node[3],
	);

	if (!$c) {
		print "\n\007Timed out trying to login to $calix_node[4] Calix ($calix_node[0])!\n";
		return;
	}

	do {
		@empty = $c->waitfor('/> /');

		$cmd_string = 'ACT-USER::' . $calix_node[1] . ':::' . $calix_node[2];

		@lines = $c->cmd(String => $cmd_string,
			Prompt => '/;/');

		foreach (@lines) {
			if ($_ =~ 'DENY') {
				$c->close;
				if ($print) {
					print "failed";
				}
				$c = Net::Telnet->new(
					Host => $calix_node[0],
					Timeout => 15,
					TelnetMode => $calix_node[3],
				);
			}
			if ($_ =~ 'COMPLD') {
				$logged_in = 1;
			}
		}
		$login_count--;
		@empty = $c->telnetmode(1);
		if ($print) {
			print '.';
		}
	} while (($login_count) && (!$logged_in));

	if (!$logged_in) {
		$c->close;
		die "\007failed, exiting!\n";
	}

	do {
		@lines = $c->cmd(String => 'INH-MSG-ALL',
			Prompt => '/;/',
		);

		foreach (@lines) {
			if ($_ =~ 'COMPLD') {
				$inhibit_messages = 1;
			}
		}
		$inhmsg_count--;
		if ($print) {
			print '.';
		}
	} while (($inhmsg_count) && (!$inhibit_messages));

	if (!$inhibit_messages) {
		$c->close;
		die "\007incomplete, exiting!\n";
	}

	if ($print) {
		print "successful.\n\n";
	}

	return $c;
}

sub calix_logout {
	my $print = shift;
	my $c = shift;

	if ($print) {
		print "Logging out...";
	}

	my @nothing = $c->cmd(String => 'CANC-USER',
		Prompt => '/;/');

	$c->close;

	return;
}

sub retrieve_cond_ont {
	my ($c, $Aid) = @_;

	my %NotificationConds = (
		CR => 'Critical',
		MJ => 'Major',
		MN => 'Minor',
		NA => 'Not Alarmed',
		NR => 'Not Reported',
	);
	my %CondTypeOnt = (
		BADPID => 'ONT Password Mismatch.',
		BATMISS => 'Battery backup is enabled but not present.',
		BATTERY => 'Battery failure.',
		BE => 'Block error, upstream BIP8 error.',
		CPE => 'Cell Phase Error.',
		EQPT => 'Critical alarm caused by equipment failure.',
		EQPTCOMM => 'Equipment communications failure.',
		LCD => 'Loss of Cell Delineation.',
		LOA => 'Loss of ACK.',
		LOS => 'Loss of Signal.',
		LWBATVG => 'Low battery voltage.',
		MEA => 'Mismatch of equipment provisioning.',
		MEM => 'Message Error Message, unknown PLOAM message received from ONT.',
		NORMAL => 'Normal state.',
		OAML => 'Loss of PLOAM cells.',
		ONTDF => 'Deactivate Failure.',
		ONTDLFAIL => 'ONT software download failed.',
		OPENDR => 'Door or box open.',
		PEE => 'Physical Equipment Error.',
		POWER => 'Commercial power failure.',
		PROVFAIL => 'Provisioning Failure.',
		REI => 'Remote Error Indication, or downstream BIP8 error.',
		RFVIDRETLEOL => 'RF Video Return Laser End of Life',
		RINH => 'Receive Alarm Inhibit, ONT\'s dying grasp that it has departed due to loss of power without battery backup.',
		SDBER => 'Signal Degrade.',
		STF => 'ONT has failed autonomous self-test.',
		SUF => 'Startup Failure Indication.',
		SWDLINPROG => 'Software Download in Progress.',
		"T-BES" => 'Threshold violation for BIP8 Errored Seconds.',
		"T-BIP8" => 'Threshold violation for BIP8 errors.',
		"T-MES" => 'Threshold violation for Missing Errored Seconds.',
		"T-MISS" => 'Threshold violation for Missing Burst Errors.',
		"T-SES" => 'Threshold violation for BIP8 Severely Errored Seconds.',
		"T-UAS" => 'Threshold violation for BIP8 Unavailable Seconds.',
		VRPNOTSUPPORTED => 'Video Return Path Not Supported.',
	);
	my %ServiceEffect = (
		NSA => 'Non-Service affecting condition',
		SA => 'Service affecting condition',
	);
	my @monthnames = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec));
	my $tmpvar1;
	my $tmpvar2;
	my @raw_array;
	my @temp_array;
	my @temp_array2;
	my $score_ref = {};
	my $value;
	my $temp_string;
	my @temp_string_array;
	my $desc;
	my $NTFCNCDE;
	my $CONDTYPE;
	my $string = "RTRV-COND-ONT:" . $Aid;
	(my $network, undef) = split (/:/, $Aid);

	my @lines = $c->cmd(String => $string,
		Timeout => 30,
		Prompt => '/processed/');

	foreach $line (@lines) {
		chomp $line;
		if ($line =~ /ONT/) {
			$line = trimwhitespace($line);
			$line = substr($line, 1);	
			push (@raw_array, $line);
		}
	}

	foreach (@raw_array) {
		$desc = '';
		chomp;
		@temp_array = split (/:/, $_);
		@temp_array2 = split (/,/, $temp_array[0]);
		my $port = $temp_array2[0];
		@temp_array2 = split (/,|"/, $temp_array[1]);
		$value = $temp_array2[0];
		$score_ref->{$value}++;
		if ($value =~ /CR|MJ|MN/) {
			$desc = find_calix_ont_info($c, $network, $port);
		}
		$NTFCNCDE = $NotificationConds{$value};
		$value = $temp_array2[1];
		if ($value) {
			$CONDTYPE = $CondTypeOnt{$value};
		}
		else {
			$CONDTYPE = '';
		}
		$value = $temp_array2[2];
		if ($value) {
			$SRVEFF = $ServiceEffect{$value};
		}
		else {
			$SRVEFF = '';
		}
		(my $month, my $day) = split(/-/, $temp_array2[3]);
		my $time = $temp_array2[4];
		$time =~ tr/-/:/;
		if ($desc) {
			$temp_string = "$month-$day $time $port ($desc) $NTFCNCDE: $CONDTYPE $SRVEFF\n";
		}
		else {
			$temp_string = "$month-$day $time $port $NTFCNCDE: $CONDTYPE $SRVEFF\n";
		}
		push (@temp_string_array, $temp_string);
	}

	return $score_ref, @temp_string_array;
}

sub trimwhitespace {
	my $string = shift;

	if ($string) {
		$string =~ s/^\s+//;
		$string =~ s/\s+$//;
	}
	else {
		$string = '';
	}

	return $string;
}

sub get_calix_shelves {
	my $c = shift;
	my $network = shift;

	my $string;
	my @lines;
	my $shelf;
	my @return_array;

	$string = "RTRV-PON:" . $network . ":ALL";

	@lines = $c->cmd(String => $string,
		Timeout => 30,
		Prompt => '/processed/');

	foreach $line (@lines) {
		chomp $line;
		if ($line =~ /TYPE=GPON/) {
			(my @temp_array) = split (/"|-/, $line);
			$shelf = $network . ":" . join('-', @temp_array[1 .. 2]);
			push (@return_array, $shelf);
		}
	}

	return @return_array;
}

sub unique_sort {
        my @array = @_;

        my @temp_array;
        my $last_value = '';
        my $i = 0;

        @array = sort(@array);

        foreach (@array) {
                if ($last_value ne $_)
                {
                        $temp_array[$i] = $_;
                        $last_value = $_;
                        $i++;
                }
        }

        return @temp_array;
}


sub find_calix_ont_info {
	(my $c, my $network, my $Aid) = @_;

	my @lines;
	my $temp_str = '';
	my @temp_array = '';
	my $desc;

	my $string = "RTRV-ONT:" . $network . ":" . $Aid;

	@lines = $c->cmd(String => $string,
		Prompt => '/processed/');

	foreach (@lines) {
		if ($_ =~ /$Aid:/) {
			(undef, undef, $temp_str) = split /:/;
			@temp_array = split (/,/, $temp_str);
		}			
	}

	foreach (@temp_array) {
		if ($_ =~ 'DESC') {
			(undef, $desc, undef) = split /\\"/;
		}
	}

	if (!$desc) {
		$desc = find_calix_ont_eth_info($c, $network, $Aid);
	}

	return $desc;
}


sub find_calix_ont_eth_info {
        (my $c,  my $network, my $Aid) = @_;

        my @lines = '';
        my $temp_str = '';
        my @temp_array = '';
        my $i = 0;
	my $desc;

        my $string = "RTRV-ETH:" . $network . ":" . $Aid . "-ALL";

        @lines = $c->cmd(String => $string,
                Prompt => '/processed/');

        foreach (@lines) {
                if ($_ =~ /$Aid/) {
                        (undef, undef, $temp_str) = split /:/;
                        @temp_array = split (/,/, $temp_str);
		}
	}
	
	foreach (@temp_array) {
		if ($_ =~ 'DESC') {
			(undef, $desc, undef) = split /\\"/;
		}
	}

	return $desc;
}