#!/usr/bin/perl -w
##############################################################################
# $Id$
##############################################################################
# This script performs what F5 calls Extended Content Verification (ECV).
# It checks that each of the servers we load balance LDAP requests to is
# responding with valid data.  Any servers which aren't providing valid
# data are removed from the load balancing pool.
##############################################################################

use IPC::Open3;

my $BALANCE = '/usr/local/bin/balance';
my %TYPES = (
	389 => 'ldap',
	636 => 'ldaps',
);
my $BINDHOST = 'ldap.example.com';  # Hostname or IP balance is bound to
my $TESTUSER = 'temp1';  # User to lookup in LDAP

sub usage
{
	die "Usage:  $0 389|636\n";
}

my $port;
if (scalar(@ARGV) == 1 && $ARGV[0])
{
	$port = $ARGV[0];
	if (! exists $TYPES{$port})
	{
		usage();
	}
}
else
{
	usage();
}

my $type = $TYPES{$port};

my @failed_channels;
open(B, "$BALANCE -b $BINDHOST -c 'show' $port |") || die;
while(<B>)
{
	next if (/^GRP/);  # Header line, skip it
	next if (/^$/);  # Skip blank lines

	my ($group, undef, $channel, $status, $ip, $chanport) = split;
	# Groups and channels can be 0 (zero) so we just make sure they
	# are defined.
	if (
		!defined($group) ||
		!defined($channel) ||
		!$status ||
		!$ip ||
		!$chanport)
	{
		warn "Bad line:  $_";
		next;
	}

	# Don't check channels outside of group zero.  It's not clear
	# how to disable them from the command line because specifying
	# the current working group is a seperate command and there
	# doesn't seem to be a way to chain commands.
	next if ($group != 0);

	my $pid = open3(\*CMDIN, \*CMDOUT, \*CMDERR,
		'ldapsearch',
		'-x',
		'-H', "$type://$ip:$chanport/",
		"uid=$TESTUSER");
	die "Fork failed: $!" if (! $pid);

	close(CMDIN);
	close(CMDERR);

	my $good = 0;
	while(<CMDOUT>)
	{
		if (/^uid: $TESTUSER/)
		{
			$good = 1;
		}
	}
	close(CMDOUT);

	waitpid($pid, 0);

	if ($good && $status eq 'dis')
	{
		warn "Channel $channel with IP $ip has recovered, enabling\n";
		system("$BALANCE -b $BINDHOST -c 'enable $channel' $port");
	}
	elsif (! $good && $status eq 'ENA')
	{
		warn "Channel $channel with IP $ip has failed, disabling\n";
		system("$BALANCE -b $BINDHOST -c 'disable $channel' $port");
	}
}
close(B);

