1#!@@PERL@@
2#
3# Plugin to monitor connections per second, for LVS loadbalancers.
4#
5# Wildcard names:
6#
7#      cps_<port>
8#      cps_<vip>_<port>
9#
10# Examples:
11#
12#      cps_smtp
13#      cps_mail.foo.boo_smtp
14#      cps_pop3
15#      cps_www.foo.boo_www
16#      cps_vvv.foo.boo_www
17#
18# Parameters understood:
19#
20#      config   (required)
21#      autoconf (optional - used by munin-config)
22#      suggest  (optional - used by munin-config)
23#
24# Magic markers - optional - used by installation scripts and munin-config:
25#
26#%# family=auto
27#%# capabilities=autoconf suggest
28#
29
30use warnings;
31use strict;
32
33if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) {
34    autoconf ();
35}
36
37if ( defined $ARGV[0] and $ARGV[0] eq "suggest" ) {
38    my $sipvs;
39    $sipvs = &ipvs (".", ".", $sipvs);
40    exit 0 if $sipvs == undef;
41    suggest ($sipvs);
42}
43
44unless ($0 =~ /cps(?:_([^_]+)|)_(.+)\s*$/) {
45    die "Could not parse name $0.\n";
46}
47
48my $vip  = $1;
49my $port = $2;
50my $ipvs;
51
52#print "$vip:$port\n";
53
54use Socket;
55my $name;
56my $address;
57$address = inet_aton($vip);
58$name = gethostbyaddr($address,AF_INET);
59
60#print "$vip:$port\n";
61#print "Name: $0\nPort: $port\nVip : $vip\n";
62
63# Read ipvsadm-output
64
65$ipvs = &ipvs ($vip, $port, $ipvs);
66
67if ( defined $ARGV[0] and $ARGV[0] eq "config" ) {
68    config ($vip, $port, $ipvs);
69}
70
71$vip = $vip || "";
72if (exists ($ipvs->{$vip}) and exists ($ipvs->{$vip}->{$port})) {
73    foreach my $host (sort keys %{$ipvs->{$vip}->{$port}}) {
74	(my $fname = $host) =~ s/[.-]/_/g;
75	print "$fname.value ", $ipvs->{$vip}->{$port}->{$host}, "\n";;
76    }
77}
78
79
80sub autoconf {
81    system ("/sbin/ipvsadm -L -n --stats >/dev/null 2>/dev/null");
82
83    if ($? == 0) {
84	print "yes\n";
85	exit 0;
86    }
87    elsif ($? & 127) {
88	    print "no (system call exited with %d)", $? & 127;
89    }
90    elsif (($?>>8) == 2) {
91	print "no (permission denied)\n";
92	exit 0;
93    }
94    elsif (($?>>8) == 127) {
95	print "no (ipvsadm not found)\n";
96	exit 0;
97    } else {
98	print "no (unknown ipvsadm return value: $?, %d)\n", $? >> 8;
99	exit 0;
100    }
101}
102
103
104sub suggest {
105    my $ipvs = shift;
106    exit 0 unless $ipvs;
107
108    foreach my $vip (sort keys %{$ipvs}) {
109	foreach my $port (sort keys %{$ipvs->{$vip}}) {
110	    print "${vip}_$port\n";
111	}
112    }
113    exit 0;
114}
115
116
117sub config {
118    my $vip  = shift;
119    my $port = shift;
120    my $ipvs = shift;
121
122    print "graph_title Loadbalanced ",($name?$name:"*"),"->",$port," connections\n";
123    print "graph_args -l 0\n";
124    print "graph_total total\n";
125    print "graph_vlabel connections / \${graph_period}\n";
126    print "graph_category network\n";
127    my $first=1;
128    $vip = $vip || "";
129    if (exists ($ipvs->{$vip}) and exists ($ipvs->{$vip}->{$port})) {
130	foreach my $host (sort keys %{$ipvs->{$vip}->{$port}}) {
131	    (my $fname = $host) =~ s/[.-]/_/g;
132	    if ( $first == 1 ) {
133		print "$fname.draw AREA\n";
134		$first=0
135	    } else {
136		print "$fname.draw STACK\n";
137	    }
138	    print "$fname.type DERIVE\n";
139	    $host =~ s/-bak//;
140	    print "$fname.label $host\n";
141	    print "$fname.max 1000\n";
142	    print "$fname.min 0\n";
143	}
144    }
145    exit 0;
146}
147
148sub ipvs {
149    my $vip  = shift;
150    my $port = shift;
151    my $ipvs = shift;
152    open (IPVS, "/sbin/ipvsadm -L -n --stats 2>/dev/null|") or return undef;
153    my $cvip  = "";
154    my $cport = "";
155    while (<IPVS>) {
156	next if /^IP Virtual Server/;
157	next if /^Prot\s+LocalAddress/;
158	if (/^(\w+)\s+([\w\.-]+):([\w\d]+)\s+(\d+)[KMG]?\s+/) {
159	    $cvip  = ($vip?$2:"");
160	    $cport = $3;
161	}
162	elsif (/^\s+->\s+([^:]+):(\S+)\s+(\d+)G\s+/) {
163	    $ipvs->{$cvip}->{$cport}->{$1} += ($3*1000000000);
164	}
165	elsif (/^\s+->\s+([^:]+):(\S+)\s+(\d+)M\s+/) {
166	    $ipvs->{$cvip}->{$cport}->{$1} += ($3*1000000);
167	}
168	elsif (/^\s+->\s+([^:]+):(\S+)\s+(\d+)K\s+/) {
169	    $ipvs->{$cvip}->{$cport}->{$1} += ($3*1000);
170	}
171	elsif (/^\s+->\s+([^:]+):(\S+)\s+(\d+)\s+/) {
172	    $ipvs->{$cvip}->{$cport}->{$1} += $3;
173	}
174    }
175    close (IPVS) or return undef;
176    return $ipvs;
177}
178# vim:syntax=perl
179