#!/usr/local/bin/perl
# 
# Nagios plugin to check Airtalk uM260 monitoring device.
# Checks Alarm Center/Receiver log for new errors, then 
# checks readings file. 
# 
# by Doke Scott, 2008.7.7
#
# $Header: /home/metal1.nss/usra/monitor/airtalk/RCS/check_airtalk,v 1.12 2009/06/19 06:25:24 doke Exp $

use vars qw( $airtalk_dir $logfile $warn_percent );

$airtalk_dir = "/home/metal1.nss/usra/monitor/airtalk";

$logfile = "$airtalk_dir/airtalk_alarm.log";

$warn_percent = 5;  # warn if within 5% of threshold

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

use warnings;
no warnings 'redefine';
use strict;
use Getopt::Long;
use Date::Parse;


use vars qw( $host $location_target $verbose $help @crits @warns @unknowns @oks $rc $sep );

$host = '';
$location_target = '';
$verbose = 0;
$help = 0;

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

sub usage { 
    my( $rc ) = @_; 
    warn "Usage: $0 [-vh] -H <host> [-l location]\n";
    exit $rc;
    }


Getopt::Long::Configure ("bundling");
GetOptions( 
    'H=s' => \$host,
    'l=s' => \$location_target,
    'w=i' => \$warn_percent,
    'v+' => \$verbose,
    'h' => \$help,
    );
&usage( 0 ) if ( $help );
&usage( 1 ) if ( ! $host );

check_airtalk();

$rc = 0;
$sep = '';
if ( $#crits >= 0 ) {
    $rc = 2;
    print "CRITICAL ", join( ", ", @crits );
    $sep = '; ';
    }
if ( $#warns >= 0 ) {
    $rc = 1 if ( $rc == 0 );
    print $sep, "Warning ", join( ", ", @warns );
    $sep = '; ';
    }
if ( $#unknowns >= 0 ) {
    $rc = -1 if ( $rc == 0 );
    print $sep, "Unknown ", join( ", ", @unknowns );
    }
if ( $rc == 0 ) {
    print "Ok ", join( ", ", @oks );
    }
print "\n";
exit $rc;


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


sub check_airtalk { 
    my( $readingsfile, $junk, $whenstr, $when, $error, %logerrors, $device,
	$location, $type, $thresh, $reading, $last, $oks, $n );

    $readingsfile = "$airtalk_dir/$host.readings";
    # get the mtime for the readings file.
    my( $dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,  $atime, $mtime, $ctime, 
	$blksize, $blocks ) = stat( $readingsfile );

    if ( $mtime < ( time - 600 ) ) { 
	push @warns, sprintf( "readings file is %0.1f min old", ( time - $mtime ) / 60 );
	}

    # scan the end of the alarm receiver log file for errors 
    # that came in since the last readings file.
    #
    if ( ! open( fH, $logfile ) ) { 
	push @unknowns, "can't open $logfile: $!\n";
	return;
	}
    seek fH, -10240, 2;  # seek to almost end
    $junk = <fH>;  # discard partial line
    while ( <fH> ) { 
	chomp;
	s/\r//g;
	$verbose && print ">$_\n";
	if ( m/^(\w+ \s+ \w+ \s+ \d+ \s+ \d+:\d\d:\d\d \s+ \d+) \s+ (\d-\d+) \s+ (.*)/ix ) { 
	    $whenstr = $1;
	    $device = $2;
	    $error = $3;
	    $error =~ s/\s\s+/ /g;
	    $verbose && print "    $whenstr, $device, $error\n";
	    $when = str2time( $whenstr );
	    $verbose && print "    $when $mtime\n";
	    if ( $when >= $mtime ) { 
		$verbose && print "    $whenstr, $device, $error\n";
		$logerrors{ $device } = $error;
		}
	    }
	}
    close fH;
    
    # scan the readings file.
    if ( ! open( fH, $readingsfile ) ) { 
	push @unknowns, "can't open $readingsfile: $!\n";
	return;
	}

#Device     Location         Type      Thresh. Reading     Last
#--------------------------------------------------------------
# 1-1   MORRIS POWER OFF     CPAMS TD  CLOSED      OK        OK 
# 1-2   MORRIS EXCESS RUN    CPAMS TD  CLOSED      OK        OK 
# 1-3   HULLIHEN POWER OFF   CPAMS TD  CLOSED      OK        OK 
# 1-4   HULLIHEN EXCESS RUN  CPAMS TD  CLOSED      OK        OK 
# 2-1   MORRIS DRYER 018A    CPA/30       9.0    11.1      10.7 
# 2-2   HULLIHEN DRYER 007   CPA/30       9.0    10.5      10.2 
# 2-3   MANIFOLD FLOW        CF/47.5     65.0    61.5      50.0 
# 2-4   MANIFOLD PRESSURE    CPA/30       9.0    10.1       9.9 
# 2-5   WOLF BASEMENT        CPA/30       4.0     5.6       5.6 
# 2-6   DUPONT COMM ROOM     CPA/30       4.0     5.7       5.7 
# 2-7   SHARP LAB BASEMENT   CPA/30       7.0     8.9       8.9 
# 2-8   EVANS BASEMENT       CPA/30       4.0     5.1       5.1 
# 2-9   BROWN MH-NC28MH      CPA/30       4.0     4.8       4.8 
# 2-10  MITCHELL MH-NC18MH   CPA/30       8.0     9.2       9.2 
# 2-11  ALISON BASEMENT      CPA/30       8.0     8.6       8.6 
# 2-12  CANNON MH-NC40MH     CPA/30       7.0     7.2       7.2 
# 2-13  ALISON MH-NC33MH     CPA/30       8.0     8.8       8.7 
# 2-14  MORRIS MH-NC32MH     CPA/30       7.5     8.4       8.4 
# 2-15                       -------   ------  ------    ------ 
# 2-16                       -------   ------  ------    ------
# 3-1   mySwitch_50

    # errors look like this
    # Bad TD = Bad Transducer
# 2-3   MANIFOLD FLOW        CF/47.5     65.0    66.5 *    62.0 
# 2-6   DUPONT COMM ROOM     CPA/30       4.0    OPEN     SHORT 
# 2-12  CANNON MH-NC40MH     CPA/30       7.0  BAD TD    BAD TD 
#
# 1-3   HULLIHEN POWER OFF   CPAMS TD  CLOSED   ALARM     ALARM 

    $n = $oks = 0;
    while ( <fH> ) { 
	$verbose && print ">$_"; 
	chomp;
	if ( m{^ \s* (\d-\d+) 
		\s+ (.*?)
		\s+ (CPAMS\sTD|CPA/\d+|CF/[\d\.]+|-+) 
		\s+ (OK|[\d\.]+|OPEN|CLOSED|SHORT|BAD\sTD|ALARM|-+)
		\s+ (OK|[\d\.]+|OPEN|CLOSED|SHORT|BAD\sTD|ALARM|-+) \s* (\*)?
		\s+ (OK|[\d\.]+|OPEN|CLOSED|SHORT|BAD\sTD|ALARM|-+) 
		\s* $}ix ) { 
	    ( $device, $location, $type, $thresh, $reading, $error, $last ) 
		= ( $1, $2, $3, $4, $5, $6, $7 );
	    $n++;
	    if ( $verbose ) { 
		if ( ! defined $error ) { 
		    $error = '';
		    }
		print "    $device, $location, $type, $thresh, $reading, $error, $last\n"; 
		}

	    if ( $location_target && $location ne $location_target ) { 
		next;
		}

	    if ( exists $logerrors{ $device } ) { 
		$verbose && print "logerror $logerrors{ $device }\n"; 
		push @crits, "$device $logerrors{ $device } thresh $thresh";
		}
	    elsif ( defined $error && $error eq '*' ) { 
		if ( $reading =~ m/OPEN|SHORT|CLOSED|BAD TD|ALARM/i ) { 
		    push @crits, "$device $location $reading";
		    }
		else { 
		    push @crits, "$device $location $reading thresh $thresh";
		    }
		}
	    elsif ( $type eq 'CPAMS TD' ) { 
		if ( $reading ne 'OK' ) { 
		    push @crits, "$device $location $reading";
		    }
		else { 
		    $oks++;
		    }
		}
	    elsif ( $type eq 'CPA/30' ) { 
		if ( $reading =~ m/OPEN|SHORT|CLOSED|BAD TD|ALARM/i ) { 
		    push @crits, "$device $location $reading";
		    }
		elsif ( $reading <= $thresh ) { 
		    push @crits, "$device $location $reading le thresh $thresh";
		    }
		elsif ( $reading <= ( $thresh * ( 1 + $warn_percent / 100 ) ) ) { 
		    push @warns, "$device $location $reading near thresh $thresh";
		    }
		else { 
		    $oks++;
		    }
		}
	    elsif ( $type eq 'CF/47.5' ) { 
		if ( $reading =~ m/OPEN|SHORT|CLOSED|BAD TD|ALARM/i ) { 
		    push @crits, "$device $location $reading";
		    }
		elsif ( $reading >= $thresh ) { 
		    push @crits, "$device $location $reading le thresh $thresh";
		    }
		elsif ( $reading >= ( $thresh * ( 1 - $warn_percent / 100 ) ) ) { 
		    push @warns, "$device $location $reading near thresh $thresh";
		    }
		else { 
		    $oks++;
		    }
		}
	    elsif ( $type eq '-------' ) { 
		# ignore it, unused device line
		}
	    else { 
		push @unknowns, "unknown type '$_'";
		}
	    }
	elsif ( m/^Device.*Location|^----+|^\d-\d+.*------|3-1 *mySwitch_50/i ) { 
	    # ignore
	    }
	else { 
	    push @unknowns, "can't parse '$_'";
	    }
	}

    if ( $n < 1 ) { 
	push @unknowns, "no devices found in readings file";
	}
    elsif ( $oks > 0 ) { 
	push @oks, "$oks devices ok";
	}
    }




