#!/usr/bin/perl -w

# Version:    0.6
# Date:       Feb 5, 2021
# Author:     ivan.stetka@live.com
# Summary:    This is a Nagios plugin to check services on balanced hosts by NCPA API

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

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

my ($hosts, $check, $cName, $critLimit, $args, $checkText) = ('','','','','','');
my $warn = 1;
my $crit = 2;
my $okCount = 0;
my $critCount = 0;
my $state = 3;
my $help = 0;
my $pluralOhost = 's are';
my $pluralChost = 's are';
my $token = '';
my @a_hosts= ();
my @okHosts = ();
my @critHosts = ();
my @longText = ();
my @perfData = ();
my @advText = ();

GetOptions(
    'H|hosts:s'     => \$hosts,
    'T|checkType:s' => \$check,
    'C|critLimit:s' => \$critLimit, # critical
    'A|args:s'      => \$args,      # args, logged_after, sleep
    'N|name:s'      => \$cName,
    'w|warning:n'   => \$warn,
    'c|critical:n'  => \$crit,
    't|token:s'     => \$token,
    'h|help'        => \$help
);

my %CHECKS = (
    "CPU"       => "/api/cpu/percent?token=$token&aggregate=avg&critical=$critLimit&check=1",
    "Disk"      => "/api/disk/logical/$cName?token=$token&units=G&critical=$critLimit&check=1",
    "Plugins"   => "/api/plugins/$cName?token=$token&args=$args&check=1",
    "Interface" => "/api/interface/$cName?token=$token&units=M&critical=$critLimit&check=1",
    "Memory"    => "/api/memory/virtual/percent?token=$token&critical=$critLimit&check=1",
    "Process"   => "/api/processes?token=$token&name=$cName&critical=$critLimit&check=1",
    "Service"   => "/api/services?token=$token&service=$cName&status=running&check=1",
    "ErrorLog"  => "/api/logs?token=$token&name=$cName&severity=ERROR&logged_after=$args&critical=$critLimit&check=1",
    "Counter"   => "/api/windowscounters$cName?token=$token&sleep=$args&critical=$critLimit&check=1",
    "Node"      => "/api/system/node/?token=$token&check=true"
);

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

if (defined($hosts)) {
    @a_hosts=split(/,/, $hosts);
}else {
    print "Hosts not defined\n";
    exit($STATES{CRITICAL});
}

my $a_hostsCount = @a_hosts;

if (!defined ($CHECKS{$check})) {
    print "Incorrect format of the check '$check'.\n";
    help();
    exit($STATES{UNKNOWN});
}

if ($check eq "Node" && $a_hostsCount != 1){
    print "Node check is only for balancig server (one host).\n";
    help();
    exit($STATES{UNKNOWN});
}

if ($check eq "Service" || $check eq "Process" || $check eq "Disk" || $check eq "ErrorLog"){
    $checkText = $check . "($cName)";
} else {
    $checkText = $check;
}
$checkText =~ s/\|//g;
$checkText =~ s/\%20/ /g;

my ($line, $rCode, $pData, $stdout, $nameHosts, $ipDNS) = ('','','','','','');
foreach my $i (@a_hosts) {
    my $sOut = 'unreachable';
    $nameHosts = gethostbyaddr(inet_aton($i), AF_INET ) or $nameHosts = "no DNS";
    if ($nameHosts eq "no DNS") {
        $ipDNS = "$i";
    }else {
        $ipDNS = "$nameHosts";
    }
    open CHECK_NCPA,"/usr/lib/nagios/plugins/check_http -I $i -u '$CHECKS{$check}' -p 5693 -S -v | " or die "Could not open http plugin!";
    while (defined($line=<CHECK_NCPA>)) {
        if ($line =~ /\"returncode\": (\d),/) {
            $rCode = $1;
            if ($rCode == 0) {
                $okCount++;
                push @okHosts, $ipDNS;
            }elsif ($rCode == 1 || $rCode == 2 || $rCode == 3 ) {
                $critCount++;
                push @critHosts, $ipDNS;
            }else {
                print "CRITICAL: Wrong NCPA returncode '$rCode'\n";
                exit($STATES{CRITICAL});
            }
        }elsif ($line =~ /\"stdout\": \"(.*)\"/) {
            $stdout = $1;
            if ($stdout =~ /^([A-Z]*: .*)(\|) (.*)/) {
                $sOut = $1;
                $pData = $3;
                perfData();
            }elsif ($stdout =~ /OK: Node was \[(.*)\]/) {
                $sOut = $1;
            }elsif ($stdout =~ /^([A-Z]*: .*)/) {
                $sOut = $1;
            }else {
                print "CRITICAL: Wrong NCPA stdout format: '$stdout'\n";
                exit($STATES{CRITICAL});
            }
        }elsif ($line =~ /^(HTTP WARNING: .*|HTTP WARNING - .*|WARNING - .*|HTTP CRITICAL: .*|HTTP CRITICAL - .*|CRITICAL - .*)/) {
                $sOut = $1;
                $critCount++;
                push @critHosts, $ipDNS;
        }
    }
    close CHECK_NCPA;
    if ($checkText eq "Node") {
        push @longText, "Active host on cluster/balancing server $ipDNS is $sOut";
    }else {
        push @longText, "$checkText on the host $ipDNS is $sOut";
            }
}

if ($okCount == 1) {($pluralOhost = ' is');}
if ($critCount == 1) {($pluralChost = ' is');}

my $okHostJ = join ', ', @okHosts;
my $critHostJ = join ', ', @critHosts;

if ($okCount == $a_hostsCount)  {
    if ($checkText eq "Node") {
        print "OK: ";
    }else {
        print "OK: $checkText on $okCount host$pluralOhost OK: $okHostJ.\n";
    }
    $state = ($STATES{OK});
} elsif (($okCount + $critCount) != $a_hostsCount)  {
    print "CRITICAL: Total hosts are $a_hostsCount but only $okCount host$pluralOhost OK and $critCount host$pluralChost CRITICAL.\n";
    $state = ($STATES{CRITICAL});
} elsif ($critCount >= $crit)  {
    print "CRITICAL: $checkText on $critCount host$pluralChost CRITICAL: $critHostJ.\n";
    $state = ($STATES{CRITICAL});
} elsif ($critCount >= $warn)  {
    print "WARNING: $checkText on $critCount host$pluralChost CRITICAL: $critHostJ.\n";
    $state = ($STATES{WARNING});
} else {
    print "CRITICAL: unexpected plugin error.\n";
    $state = ($STATES{CRITICAL});
}

print "$_\n" for @longText;

if ($check eq "ErrorLog") {
    print "$_\n" for @advText;
}

if (!@perfData)  {
    print "No perf data for $check.\n";
} else {
   my $perfDataJ = join ' ', @perfData;
   print "|$perfDataJ\n";
}

exit($state);

# # # SUBROUTINES # # #

# Perf data
sub perfData {
    if ($check eq "CPU" || $check eq "Memory") {
        $pData =~ /.*'percent'(=\d{1,3}.\d{1,2}%;;\d{1,3};).*/;
        push @perfData, "\'$checkText " . $ipDNS . "\'" . $1;
    }elsif ($check eq "Process") {
        $pData =~ /.*'cpu'(=\d{1,3}.\d{1,2}%;;;) .*/;
        push @perfData, "\'$checkText " . $ipDNS . "\'" . $1;
    }elsif ($check eq "Disk") {
        $pData =~ /.*'used'(=\d{1,4}.\d{1,2}GB;;\d{1,4};) .*/;
        push @perfData, "\'$checkText " . $ipDNS . "\'" . $1;
    }elsif ($check eq "Interface") {
        $pData =~ /.*'(bytes_sent)'(=\d{1,8}.\d{1,2}MB;;\d{1,8};) .* '(bytes_recv)'(=\d{1,8}.\d{1,2}MB;;\d{1,8};) .*/;
        push @perfData, "\'$checkText $1 " . $ipDNS . "\'" . $2 . " \'$checkText $3 " . $ipDNS . "\'" . $4;
    }elsif ($check eq "ErrorLog") {
        $pData =~ /(.*) \'Total.*Message(\\n-{35}.*)/;
        push @perfData, "\'$checkText " . $ipDNS . "\'" . $1;
        push @advText, (split(/\\n/, $2));
    }
# TODO perf data for: Plugins and Counter
}

# Help
sub help {
    print <<HELP;
H E L P:

This is a nagios plugin to check services on balanced hosts by https NCPA API

-H, --hosts             # comma-separated list of IP addresses of balanced hosts
-T, --checkType         # type of NCPA check
                        CPU
                        Disk
                        Plugins
                        Interface
                        Memory
                        Process
                        Service
                        ErrorLog
                        Counter
                        Node
-C, --critLimit         # critical threshold for NCPA check
-N, --name              # name of disk, plugin, interface, process, service, eventlog or counter
-A, --args              # arguments for Plugin, ErrorLog (logged_after) or Counter (sleep)
-w, --warning           # number of critical hosts for the warning alert [default: 1]
-c, --critical          # number of critical hosts for the critical alert [default: 2]
-t, --token             # token string for NCPA API
-h, --help              # print this help
&#124;
Examples:
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T CPU -w 1 -c 2 -C 90
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T Disk -N 'C:!' -w 1 -c 2 -C 90 #exclamation replace pipe
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T Plugins -N 'check_RDP_session.bat' -w 1 -c 2 -A 2%202
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T Interface -N 'Ethernet1' -w 1 -c 2 -C 10000
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T Memory -w 1 -c 2 -C 90
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T Process -N 'ncpa_listener.exe' -w 1 -c 2 -C 1:1
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T Service -N 'ncpalistener' -w 1 -c 2 
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T ErrorLog -N 'Aplication' -w 1 -c 2 -C 10 -A 3h
check_balanced_NCPA.pl -H 192.168.0.11,192.168.0.12,192.168.0.13 -t 'sometoken' -T Counter -N '/TCPv4/Segments%20Received/sec' -w 1 -c 2 -C 1000 -A 1
check_balanced_NCPA.pl -H 192.168.0.10 -t 'sometoken' -T Node -w 1 -c 1

HELP
}
