#!/usr/bin/perl -w

# Version:    1.5
# Date:       Mar 13, 2018
# Author:     ivan.stetka@live.com
# Summary:    This is a nagios plugin for check SNMP OID time (last write to disk)
#             of Retia recording equipment ReDat3 and eXperience
#             (RETIA-cz-MIB.mib)
#             for time format: "2000/01/31_23:59:59" - ReDat3
#                              "2000-01-31 23:59:59" - eXperience

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

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

my %TIMEOID = (
    "R3"    => '1.3.6.1.4.1.17607.1.4.1.6.1',
    "REX"   => '1.3.6.1.4.1.17607.5.4.1.4.8'
);

my %TIMEZONES = (
    "UTC"     => "date -u ",
    "UTC1"    => "date -u -d '+1 hour' ",
    "UTC2"    => "date -u -d '+2 hour' ",
    "CZ"      => "date "
);

my $host = undef;
my $community = undef;
my $equipment = undef;
my $protocolV = 1;
my $crit = 20;
my $warn = 5;
my $timeZone = 'UTC2';
my $timeout = 15;
my $version = 0;
my $help = 0;
my $oidTime = undef;
my $systemTime = undef;

GetOptions(
    'H|host:s'         => \$host,
    'C|community:s'    => \$community,
    'E|equipment:s'    => \$equipment,
    'P|protocolV:n'    => \$protocolV,
    'w|warning:n'      => \$warn,
    'c|critical:n'     => \$crit,
    'z|timeZone:s'     => \$timeZone,
    '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 ($TIMEOID{$equipment} )) {
    print "Bad format of the equipment '$equipment'.\n";
    usage();
    exit($STATES{UNKNOWN});
}

if (!defined ($TIMEZONES{$timeZone} )) {
    print "Bad format of time zone '$timeZone'.\n";
    usage();
    exit($STATES{UNKNOWN});
}

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

# 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 (time) from the remote agent (reference to hash)
my @oids = ($TIMEOID{$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{$TIMEOID{$equipment}};
if (!defined $valueOid) {
    print "OID '$TIMEOID{$equipment}' does not contain a value!\n";
    badChoice();
    $session->close();
    exit($STATES{UNKNOWN});
}

# parsing time from OID value
if ($valueOid =~ /^(\d{4})\/(\d{2})\/(\d{2})_(\d{2}):(\d{2}):(\d{2})$/) {
    $oidTime = Time::Piece->strptime($valueOid, "%Y/%m/%d_%H:%M:%S");
} elsif ($valueOid =~ /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/) {
    $oidTime = Time::Piece->strptime($valueOid, "%Y-%m-%d %H:%M:%S");
} else {
    print "Wrong OID time format: '$valueOid'.\n";
    badChoice();
    $session->close();
    exit($STATES{UNKNOWN});
}

# retrieving time from the console (Nagios-Linux)
open CH, "$TIMEZONES{$timeZone} '+%F %T' |";
my $consoleTime = <CH>;
close CH;
$consoleTime =~ s/\n//;

# parsing time from the console
if ($consoleTime =~ /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/) {
    $systemTime = Time::Piece->strptime($consoleTime, "%Y-%m-%d %H:%M:%S");
} else {
    print "Wrong system time format: '$consoleTime'.\n";
    $session->close();
    exit($STATES{UNKNOWN});
}

# calculation of time differences
my $diffInS = $systemTime - $oidTime;
my $diffInM = int($diffInS / 60);
my $diffInH = int($diffInM / 60);
my $restInM = $diffInM - ($diffInH * 60);
my $restInS = $diffInS - ($diffInM * 60);

# check the system time delay
if ($diffInM < 0) { 
    print "System time error or wrong time zone " . $timeZone . "\n"
    . "(system time '$systemTime' is delayed over OID time '$oidTime')\n";
    $session->close();
    exit($STATES{UNKNOWN});
}

# text output, long text and perf data
my $outputText = "Last write to disc: " . $oidTime
    . "\nWrite before: "
    . sprintf("%02d:%02d:%02d", $diffInH, $restInM, $restInS)
    . "\nSystem time: " . $systemTime
    . "\nTime zone: " . $timeZone
    . "|LastWrite=$diffInS" . "seconds;"
    . ($warn * 60) . ";" . ($crit * 60) . "\n";

# evaluating states according to thresholds
if ($diffInM >= $crit) {
    print "CRITICAL: " . $outputText;
    $session->close();
    exit($STATES{CRITICAL});
} elsif ($diffInM >= $warn) {
    print "WARNING: " . $outputText;
    $session->close();
    exit($STATES{WARNING});
} else {
    print "OK: " . $outputText;
    $session->close();
    exit($STATES{OK});
}

$session->close();
exit($STATES{UNKNOWN});

# # # 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";
}

# Usage 
sub usage {
    print "Usage: $0 -H <host> -C <community> -E <equipment>"
    . " [-P <protocol version>] [-w <warning>] -c [<critical>] [-z <timeZone>]"
    . " [-t <timeout>] \n For more information, use the -h (help).\n"
}

# Help
sub help {
    print <<HELP;

This is a nagios plugin for check SNMP OID time (last write to disk) 
of Retia recording equipment ReDat3 and eXperience. 

Example ReDat3:
$0 -H 192.168.0.30 -C public -E R3 -P 1 -w 20 -c 60 -z UTC1 -t 20

Example REX:
$0 -H 192.168.0.40 -C public -E REX -P 1 -w 20 -c 60 -z UTC1 -t 20

-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
-P, --protocolV     # SNMP protocol version - required value
-w, --warning       # warning threshold in minutes [default 5]
-c, --critical      # critical threshold in minutes [default 20]]
-z, --timeZone      # time zone [default UTC1]
                    UTC  - Coordinated Universal Time
                    UTC1 - UTC +1h, CZ winter time, Central European Time
                    UTC2 - UTC +2h, CZ summer time, Central European Summer Time
                    CZ   - respect CZ summer/winter time CET/CEST
-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.5 (13.03.2019)
 - check the value of the equipment and the timeZone variable changed
version 1.4 (09.03.2019)
 - code cleanup
version 1.3 (2.3.2018)
 - changed the way of inserting the OID, code cleanup
version 1.2 (23.01.2018)
 - changed output text and PERFDATA
version 1.1 (19.09.2017)
 - added sub usage, changed and added help text
version 1.0 (13.09.2017)
 - added Archive-Version history and ABOUT, changed help and print text
 - added input control timeZone, code cleanup
version 0.9 (30.08.2017)
 - added timeZone, OID time format for ReDat REX add hash TIMEZONE
version 0.8 (30.08.2017)
 - testing OID time formats
version 0.7 (30.08.2017)
 - builds on the v 0.4 (no functions)
 - added arguments warning, critical  and case sensitive in Getopt::Long
version 0.6 (19.07.2017)
 - only test v05 - value consoleTime (directly entered value)
version 0.5 (19.07.2017)
 - added FUNCTIONS
version 0.4 (19.07.2017)
 - module DateTime replaced Time::Piece
version 0.3 (19.07.2017)
 - added options, module Time::Piece replaced DateTime
version 0.2 (19.07.2017)
 - test system time from Nagios console, DateTime replaced Time::Piece
version 0.1 (18.07.2017)
 - new plugin to check SNMP OID time for Retia logger ReDat3
 - test system time from DOS, module DateTime
VERSION
}