#!/usr/local/bin/perl
#
# check sonet line error stats
# 
# by Doke Scott, doke@udel.edu, 2004 Apr 5 

#use strict;
use Socket;
use Getopt::Std;

$ENV{PATH} = "/usr/bin:/usr/local/bin:/usr/local/net-snmp/bin";

my $warn_cv = 1;  
my $warn_es = 1;  
my $warn_ses = 1;  
my $warn_sefs = 1;  
my $warn_uas = 1;  

my $crit_cv = 50;  
my $crit_es = 25;  
my $crit_ses = 10;  
my $crit_sefs = 5;  
my $crit_uas = 3;  

my $verbose = 0;
my $use_snmpv2 = 1;


######################


my( $community, $host, %line_curr_table, %line_interval_table, %opts,
    %section_curr_table, @crit_errors, @warn_errors, @unknown_errors, $rc );


sub usage { 
    my( $rc ) = @_;
    print qq{Usage: $0 [-w <thresholds>] [-c <thresholds>] -C <community> -H <host> [-v]
    threshold is "<cv>,<es>,<ses>,<sefs>,<uas>", ie "50,25,10,5,3"
};

    exit $rc;
    }

getopts( 'w:c:H:C:v', \%opts );

$verbose++ if ( $opts{ v } );

&usage() if ( ! $opts{ H } );
$host = $opts{ H };

&usage() if ( ! $opts{ C } );
$community = $opts{ C };

if ( $opts{ w } ) { 
    ( $warn_cv, $warn_es, $warn_ses, $warn_sefs, $warn_uas )
	= split( /,/, $opts{ w }, );
    }
if ( $opts{ c } ) { 
    ( $crit_cv, $crit_es, $crit_ses, $crit_sefs, $crit_uas )
	= split( /,/, $opts{ c }, );
    }

&get_info( $host, $community ); 
&check_current();
&check_prev();


$rc = 0;   # nagios ok exit code
$" = ", ";
if ( scalar( @crit_errors ) ) { 
    print "CRITICAL: @crit_errors ";
    $rc = 2;
    }
if ( scalar( @warn_errors ) ) { 
    print "; " if ( $rc != 0 );
    print "Warning: @warn_errors ";
    $rc = 1 if ( $rc == 0 );
    }
if ( scalar( @unknown_errors ) ) { 
    print "; " if ( $rc != 0 );
    print "Unknown: @unknown_errors ";
    $rc = -1 if ( $rc == 0 );
    }
elsif ( $rc == 0 ) { 
    print "OK"; 
    }
print "\n";
exit $rc;   


##########################






sub get_info { 
    my( $host, $community ) = @_;
    my( $col, $slot, $val, $interval );

    # get the sonetSectionCurrentTable
    &snmpwalk( *fH,  $host, $community, "mib-2.10.39.1.2.1" );
    while ( <fH> ) { 
	next unless ( m/\.2\.1\.1\.(\d+)\.(\d+) (\d+)/ );
	$col = $1;
	$slot = $2 >> 12;
	$val = $3;
	$section_curr_table{ $slot }{ $col } = $val;
	}
    close fH;

    # get the sonetLineCurrentTable
    &snmpwalk( *fH,  $host, $community, "mib-2.10.39.1.3.1" );
    while ( <fH> ) { 
	next unless ( m/\.3\.1\.1\.(\d+)\.(\d+) (\d+)/ );
	$col = $1;
	$slot = $2 >> 12;
	$val = $3;
	$line_curr_table{ $slot }{ $col } = $val;
	}
    close fH;

#    # get the sonetLineIntervalTable
#    &snmpwalk( *fH,  $host, $community, "mib-2.10.39.1.3.2" );
#    while ( <fH> ) { 
#	next unless ( m/\.3\.2\.1\.(\d+)\.(\d+)\.(\d+) (\d+)/ );
#	$col = $1;
#	$slot = $2 >> 12;
#	$interval = $3;
#	$val = $4;
#	$line_interval_table{ $slot }{ $col }{ $interval } = $val;
#	}

    # get selected bits of sonetLineIntervalTable, 
    # performance tweek, walking the interval table is slow, and we 
    # only want the most recent interval.
    $interval = 1;
    foreach $col ( 2, 3, 4, 5, 6 ) {
	foreach $slot ( 6, 12 ) {
	    $ifIndex = ( $slot << 12 ) + 2;
	    $oid = sprintf "mib-2.10.39.1.3.2.1.%d.%d.%d", 
		$col, $ifIndex, $interval;
	    $val = snmpget( $host, $community, $oid );
	    $line_interval_table{ $slot }{ $col }{ $interval } = $val;
	    }
	}
    close fH;

    }



sub check_current() { 
    my( $slot, $es, $ses, $sefs, $cv, $uas );
    
    if ( scalar( keys %section_curr_table ) < 1 ) { 
	push @unknown_errors, "couldn't get section current table";
	}

    foreach $slot ( sort keys %section_curr_table ) { 
	$verbose && print "slot $slot s-status $section_curr_table{ $slot }{ 1 }\n";
	if ( $section_curr_table{ $slot }{ 1 } & 0x2 ) { 
	    # sonet section LOS
	    push @crit_errors, "slot $slot s-LOS";
	    }
	if ( $section_curr_table{ $slot }{ 1 } & 0x4 ) { 
	    # sonet section LOF
	    push @warn_errors, "slot $slot s-LOF";
	    }
	$es = $section_curr_table{ $slot }{ 2 };
	$verbose && print "slot $slot s-es $es\n";
	if ( $es >= $crit_es ) { 
	    push @crit_errors, "slot $slot s-es $es ge $crit_es";
	    }
	elsif ( $es >= $warn_es ) { 
	    push @warn_errors, "slot $slot s-es $es ge $warn_es";
	    }
	$ses = $section_curr_table{ $slot }{ 3 };
	$verbose && print "slot $slot s-ses $ses\n";
	if ( $ses >= $crit_ses ) { 
	    push @crit_errors, "slot $slot s-ses $ses ge $crit_ses";
	    }
	elsif ( $ses >= $warn_ses ) { 
	    push @warn_errors, "slot $slot s-ses $ses ge $warn_ses";
	    }
	$sefs = $section_curr_table{ $slot }{ 4 };
	$verbose && print "slot $slot s-sefs $sefs\n";
	if ( $sefs >= $crit_sefs ) { 
	    push @crit_errors, "slot $slot s-sefs $sefs ge $crit_sefs";
	    }
	elsif ( $sefs >= $warn_sefs ) { 
	    push @warn_errors, "slot $slot s-sefs $sefs ge $warn_sefs";
	    }
	$cv = $section_curr_table{ $slot }{ 5 };
	$verbose && print "slot $slot s-cv $cv\n";
	if ( $cv >= $crit_cv ) { 
	    push @crit_errors, "slot $slot s-cv $cv ge $crit_cv";
	    }
	elsif ( $cv >= $warn_cv ) { 
	    push @warn_errors, "slot $slot s-cv $cv ge $warn_cv";
	    }
	}

    if ( scalar( keys %line_curr_table ) < 1 ) { 
	push @unknown_errors, "couldn't get line current table";
	}

    foreach $slot ( sort keys %line_curr_table ) { 
	$verbose && print "slot $slot l-status $line_curr_table{ $slot }{ 1 }\n";
	if ( $line_curr_table{ $slot }{ 1 } & 0x2 ) { 
	    # sonet line AIS
	    push @warn_errors, "slot $slot l-AIS";
	    }
	if ( $line_curr_table{ $slot }{ 1 } & 0x4 ) { 
	    # sonet line RDI
	    push @warn_errors, "slot $slot l-RDI";
	    }
	$es = $line_curr_table{ $slot }{ 2 };
	$verbose && print "slot $slot l-es $es\n";
	if ( $es >= $crit_es ) { 
	    push @crit_errors, "slot $slot l-es $es ge $crit_es";
	    }
	elsif ( $es >= $warn_es ) { 
	    push @warn_errors, "slot $slot l-es $es ge $warn_es";
	    }
	$ses = $line_curr_table{ $slot }{ 3 };
	$verbose && print "slot $slot l-ses $ses\n";
	if ( $ses >= $crit_ses ) { 
	    push @crit_errors, "slot $slot l-ses $ses ge $crit_ses";
	    }
	elsif ( $ses >= $warn_ses ) { 
	    push @warn_errors, "slot $slot l-ses $ses ge $warn_ses";
	    }
	$cv = $line_curr_table{ $slot }{ 4 };
	$verbose && print "slot $slot l-cv $cv\n";
	if ( $cv >= $crit_cv ) { 
	    push @crit_errors, "slot $slot l-cv $cv ge $crit_cv";
	    }
	elsif ( $cv >= $warn_cv ) { 
	    push @warn_errors, "slot $slot l-cv $cv ge $warn_cv";
	    }
	$uas = $line_curr_table{ $slot }{ 5 };
	$verbose && print "slot $slot l-uas $uas\n";
	if ( $uas >= $crit_uas ) { 
	    push @crit_errors, "slot $slot l-uas $uas ge $crit_uas";
	    }
	elsif ( $uas >= $warn_uas ) { 
	    push @warn_errors, "slot $slot l-uas $uas ge $warn_uas";
	    }
	}

    }




sub check_prev() { 
    my( $slot, $es, $ses, $cv, $uas );

    if ( scalar( keys %line_interval_table ) < 1 ) { 
	push @unknown_errors, "couldn't get line interval table";
	}

    # check the previous interval, if any counts are above critical, then 
    # raise a warning.  We don't raise a crit because this is older data.
    foreach $slot ( sort keys %line_interval_table ) { 
	$verbose && print "prev slot $slot valid $line_interval_table{ $slot }{ 6 }{ 1 }\n";
	# check if data interval holds valid data
	next if ( $line_interval_table{ $slot }{ 6 }{ 1 } != 1 );
	$es = $line_interval_table{ $slot }{ 2 }{ 1 };
	$verbose && print "prev slot $slot l-es $es\n";
	if ( $es >= $crit_es ) { 
	    push @warn_errors, "prev slot $slot l-es $es ge $crit_es";
	    }
	$ses = $line_interval_table{ $slot }{ 3 }{ 1 };
	$verbose && print "prev slot $slot l-ses $ses\n";
	if ( $ses >= $crit_ses ) { 
	    push @warn_errors, "prev slot $slot l-ses $ses ge $crit_ses";
	    }
	$cv = $line_interval_table{ $slot }{ 4 }{ 1 };
	$verbose && print "prev slot $slot l-cv $cv\n";
	if ( $cv >= $crit_cv ) { 
	    push @warn_errors, "prev slot $slot l-cv $cv ge $crit_cv";
	    }
	$uas = $line_interval_table{ $slot }{ 5 }{ 1 };
	$verbose && print "prev slot $slot l-uas $uas\n";
	if ( $uas >= $crit_uas ) { 
	    push @warn_errors, "prev slot $slot l-uas $uas ge $crit_uas";
	    }
	}
    }



sub snmpwalk { 
    my( $fH, $host, $community, $oid ) = @_;
    my( $cmd );

    if ( $use_snmpv2 ) { 
	$cmd = "snmpbulkwalk -v 2c -Oq -c $community $host $oid |";
	}
    else { 
	$cmd = "snmpwalk -v 1 -Oq -c $community $host $oid |";
	}
    $verbose && print "cmd = '$cmd'\n";
    open( $fH, $cmd ) || die "can't snmpwalk: $!\n";
    }




sub snmpget { 
    my( $host, $community, $oid ) = @_;
    my( $cmd, $val ); 

    $cmd = "snmpget -v 1 -Oq -Ov -c $community $host $oid |";
    $verbose && print "cmd = '$cmd'\n";
    open( getH, $cmd ) || die "can't snmpget: $!\n";
    $val = <getH>;
    close getH;
    chomp $val;
    return $val;
    }




sub ip2name { 
    my( $ipaddr ) = @_;
    my( $ippat, $themaddr, $host );
    $ippat = '(\d+)\.' x 3 . '(\d+)';
    $ipaddr =~ m/$ippat/o;
    $themaddr = pack( "C4", $1, $2, $3, $4 );
    $host = gethostbyaddr( $themaddr, &AF_INET );
    $host = "unknown" if ! $host;
    return $host;
    }
    









