#!@@PERL@@
#
# Plugin to monitor connections per second, for LVS loadbalancers.
#
# Wildcard names:
#
#      cps_<port>
#      cps_<vip>_<port>
#
# Examples:
#
#      cps_smtp
#      cps_mail.foo.boo_smtp
#      cps_pop3
#      cps_www.foo.boo_www
#      cps_vvv.foo.boo_www
#
# Parameters understood:
#
#      config   (required)
#      autoconf (optional - used by munin-config)
#      suggest  (optional - used by munin-config)
#
# Magic markers - optional - used by installation scripts and munin-config:
#
#%# family=auto
#%# capabilities=autoconf suggest
#

use warnings;
use strict;

if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) {
    autoconf ();
}

if ( defined $ARGV[0] and $ARGV[0] eq "suggest" ) {
    my $sipvs;
    $sipvs = &ipvs (".", ".", $sipvs);
    exit 0 if $sipvs == undef;
    suggest ($sipvs);
}

unless ($0 =~ /cps(?:_([^_]+)|)_(.+)\s*$/) {
    die "Could not parse name $0.\n";
}

my $vip  = $1;
my $port = $2;
my $ipvs;

#print "$vip:$port\n";

use Socket;
my $name;
my $address;
$address = inet_aton($vip);
$name = gethostbyaddr($address,AF_INET);

#print "$vip:$port\n";
#print "Name: $0\nPort: $port\nVip : $vip\n";

# Read ipvsadm-output

$ipvs = &ipvs ($vip, $port, $ipvs);

if ( defined $ARGV[0] and $ARGV[0] eq "config" ) {
    config ($vip, $port, $ipvs);
}

$vip = $vip || "";
if (exists ($ipvs->{$vip}) and exists ($ipvs->{$vip}->{$port})) {
    foreach my $host (sort keys %{$ipvs->{$vip}->{$port}}) {
	(my $fname = $host) =~ s/[.-]/_/g;
	print "$fname.value ", $ipvs->{$vip}->{$port}->{$host}, "\n";;
    }
}


sub autoconf {
    system ("/sbin/ipvsadm -L -n --stats >/dev/null 2>/dev/null");

    if ($? == 0) {
	print "yes\n";
	exit 0;
    }
    elsif ($? & 127) {
	    print "no (system call exited with %d)", $? & 127;
    }
    elsif (($?>>8) == 2) {
	print "no (permission denied)\n";
	exit 0;
    }
    elsif (($?>>8) == 127) {
	print "no (ipvsadm not found)\n";
	exit 0;
    } else {
	print "no (unknown ipvsadm return value: $?, %d)\n", $? >> 8;
	exit 0;
    }
}


sub suggest {
    my $ipvs = shift;
    exit 0 unless $ipvs;

    foreach my $vip (sort keys %{$ipvs}) {
	foreach my $port (sort keys %{$ipvs->{$vip}}) {
	    print "${vip}_$port\n";
	}
    }
    exit 0;
}


sub config {
    my $vip  = shift;
    my $port = shift;
    my $ipvs = shift;

    print "graph_title Loadbalanced ",($name?$name:"*"),"->",$port," connections\n";
    print "graph_args -l 0\n";
    print "graph_total total\n";
    print "graph_vlabel connections / \${graph_period}\n";
    print "graph_category network\n";
    my $first=1;
    $vip = $vip || "";
    if (exists ($ipvs->{$vip}) and exists ($ipvs->{$vip}->{$port})) {
	foreach my $host (sort keys %{$ipvs->{$vip}->{$port}}) {
	    (my $fname = $host) =~ s/[.-]/_/g;
	    if ( $first == 1 ) {
		print "$fname.draw AREA\n";
		$first=0
	    } else {
		print "$fname.draw STACK\n";
	    }
	    print "$fname.type DERIVE\n";
	    $host =~ s/-bak//;
	    print "$fname.label $host\n";
	    print "$fname.max 1000\n";
	    print "$fname.min 0\n";
	}
    }
    exit 0;
}

sub ipvs {
    my $vip  = shift;
    my $port = shift;
    my $ipvs = shift;
    open (IPVS, "/sbin/ipvsadm -L -n --stats 2>/dev/null|") or return undef;
    my $cvip  = "";
    my $cport = "";
    while (<IPVS>) {
	next if /^IP Virtual Server/;
	next if /^Prot\s+LocalAddress/;
	if (/^(\w+)\s+([\w\.-]+):([\w\d]+)\s+(\d+)[KMG]?\s+/) {
	    $cvip  = ($vip?$2:"");
	    $cport = $3;
	}
	elsif (/^\s+->\s+([^:]+):(\S+)\s+(\d+)G\s+/) {
	    $ipvs->{$cvip}->{$cport}->{$1} += ($3*1000000000);
	}
	elsif (/^\s+->\s+([^:]+):(\S+)\s+(\d+)M\s+/) {
	    $ipvs->{$cvip}->{$cport}->{$1} += ($3*1000000);
	}
	elsif (/^\s+->\s+([^:]+):(\S+)\s+(\d+)K\s+/) {
	    $ipvs->{$cvip}->{$cport}->{$1} += ($3*1000);
	}
	elsif (/^\s+->\s+([^:]+):(\S+)\s+(\d+)\s+/) {
	    $ipvs->{$cvip}->{$cport}->{$1} += $3;
	}
    }
    close (IPVS) or return undef;
    return $ipvs;
}
# vim:syntax=perl
