#!/usr/bin/perl -w

# Version:  1.6
# Date:     Jul 15, 2020
# Author:   ivan.stetka@live.com
# Summary:  This is a nagios plugin for check SNMP error table of Retia
#           recording equipment ReDat3, eXperience and VoIP Recorder 
#           (RETIA-cz-MIB.mib)

use strict;
use Net::SNMP;
use Getopt::Long qw(:config no_ignore_case bundling);

my $ESOURCE = 3;
my $ESEVERITY = 4;
my $ETEXT = 5;

my %STATES = (
    OK       => 0,
    WARNING  => 1,
    CRITICAL => 2,
    UNKNOWN  => 3
);

my %SEVERITYTEXT = (
    0 => 'OK',
    1 => 'Warning',
    2 => 'Error',
    3 => 'FatalError'
);

my %ESTATUS = (
    "R3"   => '1.3.6.1.4.1.17607.1.1.0',
    "REX"  => '1.3.6.1.4.1.17607.5.2.0',
    "RVR"  => '1.3.6.1.4.1.17607.3.2.0'
);

my %ETABLE = (
    "R3"   => '1.3.6.1.4.1.17607.1.8.1',
    "REX"  => '1.3.6.1.4.1.17607.5.1.1',
    "RVR"  => '1.3.6.1.4.1.17607.3.4.1'
);

my $eId = undef;
my $host = undef;
my $community = undef;
my $equipment = undef;
my $protocolV = 1;
my $mibFilePath = '/usr/local/bin/RETIA-cz-MIB.mib';
my $timeout = 15;
my $version = 0;
my $help = 0;
my $rowCounter = 0;
my $commaCounter = 0;
my $plural = '';
my $exitStatus = undef;
my %eTextValues = ();
my %eIdValues = ();
my %eSeverityValues = ();
my %eSourceValues = ();
my %longText = (); 

GetOptions(
    'H|host:s'       => \$host,
    'C|community:s'  => \$community,
    'E|equipment:s'  => \$equipment,
    'P|protocolV:n'  => \$protocolV,
    'm|mibFile:s'    => \$mibFilePath,
    't|timeout:n'    => \$timeout,
    'V|version'      => \$version,
    'h|help'         => \$help
);

if ($help) { 
    help();
    exit($STATES{OK});
}

if ($version) {
    version();
    exit($STATES{OK});
}

# timeout control
$SIG{ALRM} = sub {
    print "Something is wrong, exceeded timeout parameter.\n";
    usage();
    exit($STATES{UNKNOWN});
};
alarm $timeout;

# input control
if (!defined $host || !defined $community || !defined $equipment) {
    print "Missing required parameter (host, community, equipment).\n";
    usage();
    exit($STATES{UNKNOWN});
}

if (!defined ($ETABLE{$equipment} )) {
    print "Bad format of the equipment '$equipment'.\n";
    usage();
    exit($STATES{UNKNOWN});
}

if ($protocolV != 1 && $protocolV != 2) {
    print "Protocol version can be only 1 or 2.\n";
    usage();
    exit($STATES{UNKNOWN});
}

# load the MIB file
open FH, $mibFilePath or die("Can not open the MIB file.\n");
my %MIBIDINFO = ();
while (my $line = <FH>) {
    if ($line =~ /e(\d+)-([a-zA-Z0-9-]+)/) {
        $MIBIDINFO{$1} = $2;
    }
}
close(FH);

# opening of the SNMP session
my ($session, $error) = Net::SNMP->session(
    -hostname   => $host,
    -community  => $community,
    -version    => $protocolV
);

# check of the session
if (!defined $session) {
    print "Missing session: $error\n";
    badChoice();
    exit($STATES{UNKNOWN});
}

# getting of the OID data (status) from the remote agent (reference to hash)
my @oids = ($ESTATUS{$equipment});
my $resultOid = $session->get_request(
    -varbindlist => \@oids
);

# checking of the result (existence of the OID status)
if (!defined $resultOid) {
    print "Error in fetching OID request: ", $session->error(), "\n";
    badChoice();
    $session->close();
    exit($STATES{UNKNOWN});
}

# dereference of resultOid 
my %hashOid = %{$resultOid};

# checking of the data existence in the return value
my $valueOid = $hashOid{$ESTATUS{$equipment}};
if (!defined $valueOid) {
    print "OID '$ESTATUS{$equipment}' does not contain a value!\n";
    badChoice();
$session->close();
    exit($STATES{UNKNOWN});
}

# checking acceptable OID values
if ($valueOid !~ /^([0-3])$/) {
    print "Bad OID value '$valueOid'.\n";
    badChoice();
    $session->close();
    exit($STATES{UNKNOWN});
}

# retrieving the table error data from the remote agent (reference to hash)
my $result = $session->get_table(
    -baseoid => ($ETABLE{$equipment})
);

# checking of the result (existence of the error table)
if (!defined $result) {
    if ($session->error() gt 'The requested table is empty') {
        checkOK();
    } else {
        print "Error in fetching table request: ", $session->error(), "\n";
        badChoice();
        $session->close();
        exit($STATES{UNKNOWN});
    }
}

# dereference of the result (error table)
my %values = %{$result};

# check if the table is empty
my @values = %values;
if (scalar @values == 0) {
    checkOK();
}

# assign a column number eID by equipment
if ($equipment eq 'R3') {
    $eId = 7;
} else {
    $eId = 9;
}

# identification of rows and columns, putting into hashes 
foreach my $key (keys %values ) {
    my @nodes = split /\./, $key;
    my $row = $nodes[-1];
    my $column = $nodes[-2];
    if ($column == $ETEXT) {
        $eTextValues{$row} = $values{$key};
        $rowCounter++;
    } elsif ($column == $eId) {
        $eIdValues{$row} = $values{$key};
    } elsif ($column == $ESEVERITY) {
        $eSeverityValues{$row} = $values{$key};
    } elsif ($column == $ESOURCE) {
        $eSourceValues{$row} = $values{$key};
    }
}

# text output
if ($rowCounter > 1) { 
    $plural = "s";
}

if ($valueOid == 1) {
    $exitStatus = 'WARNING';
} else {
    $exitStatus = 'CRITICAL';
}

print "$exitStatus: Status is '$SEVERITYTEXT{$valueOid}'"
. " and the table contains $rowCounter error$plural: ";
foreach my $key (sort keys %eTextValues) {
    my $eIdValue = $eIdValues{$key};
    my $eTextValue = $eTextValues{$key};
    my $eSourceValue = $eSourceValues{$key};
    my $eSeverityValue = $eSeverityValues{$key};
    my $eInfo = "MIB: unknown Id error info";
    $commaCounter++;
    if (defined $MIBIDINFO{$eIdValue}) {
        $eInfo = $MIBIDINFO{$eIdValue};
    }
    if ($equipment eq 'R3') {
       print "$eTextValue - $eInfo";
    } else {
       print "$eSourceValue - $eInfo";
    }
    if ($commaCounter < $rowCounter) {
       print ", ";
    }
    $longText{$key} = ("$eIdValue, $SEVERITYTEXT{$eSeverityValue}, "
    . "$eSourceValue: $eTextValue, Info: $eInfo");
}

# long text output
print "\n";
foreach my $key (sort keys %longText){
        print "$longText{$key}\n";
}

# perf data output (call subroutine perf)
perf();

# closing the session and returning Warning state
$session->close();
exit($STATES{$exitStatus});

# # # SUBROUTINES # # #

# Bad choice text 
sub badChoice {
    print "Is your choice of the equipment: '$equipment',"
    . " SNMP protocol version: '$protocolV'"
    . " or the host IP address: '$host' correct?\n";
}

# Check state OK
sub checkOK {
    if ($valueOid eq '0') {
        print "OK: No errors, $equipment status is OK.\n",
        $session->error(), "\n";
        perf();
        $session->close();
        exit($STATES{OK});
    } else {
        print "Status is not OK ("
        . $SEVERITYTEXT{$valueOid}
        . ") but the error table is empty or does not exist.\n";
        badChoice();
        $session->close();
        exit($STATES{UNKNOWN});
    }
}

# Perf data
sub perf {
    print "|Errors=$rowCounter\n";
}

# Usage 
sub usage {
    print "Usage: $0 -H <host> -C <community> -E <equipment>"
    . " [-P <protocol version>] [-m <MIB path>] [-t <timeout>]"
    . "\nFor more information, use the -h (help).\n"
}

# Help
sub help {
    print <<HELP;

This is a nagios plugin for check SNMP error table of Retia 
recording equipment ReDat3, eXperience and VoIP Recorder. 

Example ReDat3:
$0 -H 192.168.0.30 -C public -E R3 -P 1 -m /mibs/RETIA-cz-MIB.mib -t 5

Example REX:
$0 -H 192.168.0.40 -C public -E REX -P 1 -m /mibs/RETIA-cz-MIB.mib -t 5

-H, --host          # Hostname or IP - required value
-C, --community     # SNMP community for read - required value
-E, --equipment     # ReDat equipment - required value
                    R3  - ReDat3 
                    REX - ReDat eXperience
                    RVR - ReDat VoIP Recorder
-P, --protocolV     # SNMP protocol version [default: 1]
-m, --mibFile       # Path to MIB [default: /usr/local/bin/RETIA-cz-MIB.mib]
-t, --timeout       # Timeout in seconds [default: 15]
-V, --version       # Print plugin version history
-h, --help          # Print this help
HELP
}

# Version history
sub version {
    print "\n$0\n";
    print <<VERSION;

version 1.6 (15.07.2020)
 - added CRITICAL exit status
version 1.5 (30.04.2019)
 - without tabs
version 1.4 (12.03.2019)
 - check the value of the equipment variable changed
version 1.3 (09.03.2019)
 - code cleanup
version 1.2 (27.02.2018)
 - simplified long text printing 
version 1.1 (23.02.2018)
 - added checking Status equipment from OID, sub checkOK 
version 1.0 (21.02.2018)
 - changed arguments and checking the result    
version 0.9 (20.02.2018)
 - changed output text, long text and perf data
version 0.8 (18.09.2017)
 - added sub usage, changed and added help text
version 0.7 (14.09.2017)
 - changed help text, changed Version history, code cleanup
version 0.6 (23.08.2017)
 - added variable erSource (optimalization for REX - ReDat eXperience)
version 0.5 (09.08.2017)
 - changed name of variables equal retia error table, changed help text, 
   added Version history and ABOUT
version 0.4 (08.08.2017)
 - added severity text
version 0.3 (20.07.2017)
 - added variables severity and errorCounter, changed print text
version 0.2 (19.07.2017)
 - fixed FH and values
version 0.1 (19.07.2017)
 - new plugin for check SNMP error table of Retia logger ReDat3
VERSION
}  
