#!/usr/bin/perl
# nagios: -epn
#
# Manuel Leiner, 2014, noris network AG
#
# erstmal für die A10
#

use strict;
use warnings;
use Getopt::Long;

use Net::SNMP qw(snmp_dispatcher oid_lex_sort snmp_type_ntop INTEGER);
use lib "/usr/lib/nagios/plugins";
use utils qw(%ERRORS $TIMEOUT);
use Data::Dumper;

my $base_statusoid=
	".1.3.6.1.4.1.22610";

# axServerPortStatusInServiceGroupMemberStat
my $rserver_statusoid=
	$base_statusoid.".2.4.3.3.4.1.1.12";

# axVirtualServerPortDisplayStatus
my $virtualserver_port_statusoid=
	$base_statusoid.".2.4.3.4.3.1.1.23";

# Beschreibungshash fuer Realserver
my %rserver_statemap = (
	0 => 'disabled',
	1 => 'up',
	2 => 'down',
);

#Beschreibungshash fuer servicegroupstatus
my %virtualserverport_statemap = (
	0 => 'disabled',
	1 => 'all_up',
	2 => 'functional_up',
	3 => 'partial_up',
	4 => 'stopped',
	99 => 'not_found',
);

# Zuordnung von Realserverstatus zu Nagios-Fehlercodes
my %rserver_nagios_map = (
	0 => $ERRORS{"UNKNOWN"},
	1 => $ERRORS{"OK"},
	2 => $ERRORS{"WARNING"},
);

# Zuordnung von Returnvode zu Nagios-Returncode
my %virtualserver_port_nagios_map = (
	0	=> $ERRORS{"UNKNOWN"},
	1	=> $ERRORS{"OK"},
	2	=> $ERRORS{"WARNING"},
	3	=> $ERRORS{"WARNING"},
	4	=> $ERRORS{"CRITICAL"},
	99	=> $ERRORS{"CRITICAL"},
);

my %nagios_error_strings=(
	$ERRORS{"OK"}   	=> "OK",
	$ERRORS{"WARNING"}	=> "WARNING",
	$ERRORS{"CRITICAL"}	=> "CRITICAL",
	$ERRORS{"UNKNOWN"}	=> "UNKNOWN",
);

my %a10_protocol_map = (
	'tcp'     => 2,
	'udp'     => 3,
	'http'    => 14,
	'https'   => 15,
	'dns-udp' => 22,
	'dns-tcp' => 24,
);

# Fuer Realserver
my %result = ();

my $params = {};

sub string2oid {
	my $string=shift(@_);
	my $stringlength=length($string);
	$string =~ s/(.)/sprintf("%d.",ord($1))/eg;
	return $stringlength.'.'.$string;
}

sub oid2string {
	my $oid=shift(@_);
	my @oidelements=split(/\./, $oid);
	my $string="";
	for my $oidelement (@oidelements) {
		next if ($oidelement eq "");
		$string.=chr($oidelement);
	}
	return $string;
}

sub show_help() {
	print "Usage: ./check_a10_rserver {Parameter}\n";
	print "\n";
	print "Beispiel-Parameter (alle Parameter sind notwendig!):\n";
	print "\t--host lb3-nbg6a.nms.noris.de       # Loadbalancer-Adresse\n";
	print "\t--community wirepubcis              # SNMP community\n";
	print "\t--partition peterhahn-np1           # Partition des Kunden auf dem LB\n";
	print "\t--virtualserver vs-www.peterhahn.de # Name des Servers 'nach aussen' (ace: serverfarm')\n";
	print "\t--servicegroup sg-peterhahn-de      # Gruppierte Dienste (ace: optional: ports)\n";
	print "\t--rserver-port 80                   # Port 'nach innen' zum Realserver\n";
	print "\t--rserver-protocol http             # Protokoll 'nach innen' zum Realserver\n";
	print "\t--virtualserver-port 443            # Port 'nach aussen'\n";
	print "\t--virtualserver-protocol https      # Protokoll 'nach aussen'\n";
	print "\n";
	print "Datenfluss (je partition):\n";
	print "\tClient -> virtualserver[protocol, port] -> servicegroup -> rserver[ protocol, port]\n";
	print "\n";
	exit(1);
}

show_help() if ( @ARGV < 1 or ! GetOptions(
	'help|?'        	=> \$params->{'help'},
	'hostname=s'        	=> \$params->{'hostname'},
	'community=s'        	=> \$params->{'community'},
	'servicegroup=s'	=> \$params->{'servicegroup'},
	'partition=s'        	=> \$params->{'partition'},
	'rserver-protocol=s'	=> \$params->{'rserver-protocol'},
	'rserver-port=i'	=> \$params->{'rserver-port'},
	'virtualserver=s'	=> \$params->{'virtualserver'},
	'virtualserver-protocol=s' => \$params->{'virtualserver-protocol'},
	'virtualserver-port=i'	=> \$params->{'virtualserver-port'},
	'debug'         	=> \$params->{'debug'},
) or defined $params->{'help'} );

if(
	   ! $params->{'hostname'}
	|| ! $params->{'community'}
	|| ! $params->{'servicegroup'}
	|| ! $params->{'partition'}
	|| ! $params->{'rserver-protocol'}
	|| ! $params->{'rserver-port'}
	|| ! $params->{'virtualserver'}
) {
	print "$nagios_error_strings{$ERRORS{'CRITICAL'}} - Missing parameters, have a look at the help meesage.\n";
	exit $ERRORS{'CRITICAL'};
};

if(!$params->{'rserver-protocol'}) {
	print "Unknown protocol $params->{'rserver-protocol'}";
	exit 1;
}
if($a10_protocol_map{$params->{'rserver-protocol'}}) {
	$params->{'rserver-protocol'}=$a10_protocol_map{$params->{'rserver-protocol'}};
}
	
if(!$params->{'virtualserver-protocol'}) {
	print "Unknown protocol $params->{'virtualserver-protocol'}";
	exit 1;
}
if($a10_protocol_map{$params->{'virtualserver-protocol'}}) {
	$params->{'virtualserver-protocol'}=$a10_protocol_map{$params->{'virtualserver-protocol'}};
}

my ($session, $error) = Net::SNMP->session(
	-hostname	=> $params->{'hostname'},
	-community	=> $params->{'community'},
);

if (!defined $session) {
	printf "ERROR1: %s.\n", $error;
	exit 1;
}

# A10-AX-MIB::axVirtualServerPortDisplayStatus.""peterhahn-np1":vs-prod.peterhahn.com".http.80 = INTEGER: allUp(1)
my $target = $virtualserver_port_statusoid.'.'.string2oid('"'.$params->{'partition'}.'":'.$params->{'virtualserver'}).$params->{'virtualserver-protocol'}.'.'.$params->{'virtualserver-port'};
printf "Looking for OID: %s\n", $target if ($params->{'debug'});
my $snmpresult = $session->get_request(-varbindlist => [ $target ],);
if (!defined $snmpresult) {
	$result{'status-virtualserverport'}=99; # == 'not found'
} else {
	$result{'status-virtualserverport'}=$snmpresult->{$target};
}

# A10-AX-MIB::axServerPortStatusInServiceGroupMemberStat.""peterhahn-np2":sg-peterhahn-com".tcp.""peterhahn-np2":web04vt".80 = INTEGER: up(1)
print "Suche nach Realserver unter $rserver_statusoid ... \n" if ($params->{'debug'});
if (defined (my $snmp_result = $session->get_table (-baseoid => $rserver_statusoid))) {
	my $target=$rserver_statusoid;
	for ($session->var_bind_names()) {
		my $oid=$_;
		my $filtered_oid=$_;
		next unless $filtered_oid =~ s/^$target\.[^.]+\.//;
		next unless $filtered_oid =~s/\.$params->{'rserver-port'}$//;
		
		my $printable_oid=oid2string($filtered_oid);

		next unless $printable_oid =~ s/^.$params->{'partition'}":$params->{'servicegroup'}.."$params->{'partition'}"://;
		my $snmpresult = $session->get_request(-varbindlist => [ $oid ],);
		if(!defined $snmpresult) {
			$session->close();
		}
		$result{'rserver-'.$printable_oid}=$snmpresult->{$oid};
	}
}

$session->close();

my $returncode=$virtualserver_port_nagios_map{$result{'status-virtualserverport'}};
my @result_rservers;
foreach my $key (keys %result) {
	if($key =~ /^rserver-(.*)/) {
		my $state=$rserver_statemap{$result{$key}};
		if($state) {
			push @result_rservers, "$1: ".$state;
			next unless $returncode==$ERRORS{'OK'};
			$returncode=$rserver_nagios_map{$result{$key}};
		} elsif (!$result{$key}) {
			print "No result for $key\n";
		} else {
			print "No statemap entry for $key: $result{$key}\n";
		}
	}
}

print Dumper(%result) if $params->{'debug'};

my $resultline=$nagios_error_strings{$returncode};
$resultline.= " - Virtual Server \"".$params->{'virtualserver'}."\"";
$resultline.= " is \"".$virtualserverport_statemap{$result{'status-virtualserverport'}}."\"";
$resultline.= " (Partition: ".$params->{'partition'}.") ";
$resultline.= " (Servicegroup: ".$params->{'servicegroup'}.") ";
$resultline.= " (Realserver: ".join(", ", sort @result_rservers).") ";
$resultline.="\n";

print $resultline;
exit($returncode);
