Skip to content

Instantly share code, notes, and snippets.

@srinivasmohan
Created August 29, 2012 21:16
Show Gist options
  • Save srinivasmohan/3519093 to your computer and use it in GitHub Desktop.
Save srinivasmohan/3519093 to your computer and use it in GitHub Desktop.
Nagios check script to consolidate stats from multiple HAProxy servers and return summary of stats data for performance graphinc
#!/usr/bin/perl -w
#
#This polls multiple haproxy servers via their admin stats urls and sums up statistics.
#Scenario for usage is when you have multiple HaProxy boxes behind a load balancer and want to view the
# "sum total" of some key statistics like Bytes In/Out, Sessions etc across all HAProxy servers.
#Usage: Assuming you want to sum up stats across ha proxies lb1,2 and 3 which are have stats available via http://lbname:8080/statspath.
# /etc/nagios3/scripts/check_haproxy_all.pl -u lb1.domain.com,lb2.domain.com,lb3.domain.com -U admin -a '/statspath' -P 'PASSWORD'
# See http://www.onepwr.org/haproxy-consolidated-stats for detailed info.
use strict; # always! :)
use warnings;
use Locale::gettext;
use File::Basename; # get basename()
use POSIX qw(setlocale);
use Time::HiRes qw(time); # get microtime
use POSIX qw(mktime);
use Nagios::Plugin ;
use LWP::UserAgent; # http client
use HTTP::Request; # used by LWP::UserAgent
use HTTP::Status; # to get http err msg
use Data::Dumper;
my $PROGNAME = basename($0);
'$Revision: 1.0 $' =~ /^.*(\d+\.\d+) \$$/; # Use The Revision from RCS/CVS/SVN
my $VERSION = $1;
my $DEBUG = 0;
my $TIMEOUT = 5;
# i18n :
setlocale(LC_MESSAGES, '');
textdomain('nagios-plugins-perl');
my $np = Nagios::Plugin->new(
version => $VERSION,
blurb => _gt('Plugin to consolidate HAProxy stats'),
usage => "Usage: %s [ -v|--verbose ] [ -d ] -u lb1,lb2,lb3 -U admin -a '/statspath' -P PASSWORD ",
timeout => $TIMEOUT+1
);
$np->add_arg (
spec => 'debug|d',
help => _gt('Debug level'),
default => 0,
);
$np->add_arg (
spec => 'username|U=s',
help => _gt('Username for HTTP Auth'),
required => 0,
);
$np->add_arg (
spec => 'password|P=s',
help => _gt('Password for HTTP Auth'),
required => 0,
);
$np->add_arg (
spec => 'w=f',
help => _gt('Warning request time threshold (in seconds)'),
default => 2,
label => 'FLOAT'
);
$np->add_arg (
spec => 'c=f',
help => _gt('Critical request time threshold (in seconds)'),
default => 10,
label => 'FLOAT'
);
$np->add_arg (
spec => 'url|u=s',
help => _gt('Comma separated IP/hostname list of HAProxy csv statistics page - all of them expected to have same stats login.'),
required => 1,
);
$np->add_arg(
spec => 'port|p=s',
help => _gt('TCP Port number of stats uri.'),
default => 8080,
required => 0,
);
$np->add_arg(
spec => 'adminpath|a=s',
help => _gt('URI to admin stats e.g. /stats'),
default => '/stats',
required => 0,
);
$np->getopts;
$DEBUG = $np->opts->get('debug');
my $verbose = $np->opts->get('verbose');
my $username = $np->opts->get('username');
my $password = $np->opts->get('password');
my $uri=$np->opts->get('adminpath');
#my $port=$np->opts->get->('port') ;
# Thresholds :
# time
my $warn_t = $np->opts->get('w');
my $crit_t = $np->opts->get('c');
my $allurls = $np->opts->get('url');
my @urllist=split /,/,$allurls;
# Create a LWP user agent object:
my @lbsdown=();
my @statsarray;
my %watch=('load_balanced_http'=>'FRONTEND','load_balanced_ssl'=>'FRONTEND','image'=>'imagesrv','beanstalktomcat'=>'beanstalktomcatelb');
my %colsneeded=(scur=>undef,smax=>undef,stot=>undef,bin=>'B',bout=>'B');
my %results=();
my %summary=();
foreach my $url (@urllist)
{
my $ua = new LWP::UserAgent(
'env_proxy' => 0,
'timeout' => $TIMEOUT,
);
$ua->agent($PROGNAME);
# Workaround for LWP bug :
$ua->parse_head(0);
my $lbname=$url;
$url="http://$url:8080".$uri.';csv';
# Build and submit an http request :
my $request = HTTP::Request->new('GET', $url);
# Authenticate if username and password are supplied
if ( defined($username) && defined($password) ) {
$request->authorization_basic($username, $password);
}
my $timer = time();
my $http_response = $ua->request( $request );
$timer = time()-$timer;
if ( $http_response->is_error() || ! $http_response->is_success()) {
push(@lbsdown,[ split /\./,$lbname ]->[0]);
next;
}
if ( $http_response->is_success() ) {
# Get xml content ...
my $stats = $http_response->content;
#if ($DEBUG) {
# print "------------------===http output===------------------\n$stats\n-----------------------------------------------------\n";
# print "t=".$timer."s\n";
#};
my @fields = ();
my @rows = split(/\n/,$stats);
if ( $rows[0] =~ /#\ \w+/ ) {
$rows[0] =~ s/#\ //;
@fields = split(/\,/,$rows[0]);
} else {
$np->nagios_exit(UNKNOWN, _gt("Can't find csv header !") );
}
my %stats = ();
for ( my $y = 1; $y < $#rows; $y++ ) {
my @values = split(/\,/,$rows[$y]);
next unless exists $watch{$values[0]} && $values[1] eq $watch{$values[0]};
#if ( !defined($stats{$values[0]}) ) {
# $stats{$values[0]} = {};
#}
my $k=$values[0].':'.$values[1];
$results{$k}=() unless exists $results{$k};
for ( my $x = 2,; $x <= $#values; $x++ ) {
# $stats{pxname}{svname}{valuename}
next unless exists $colsneeded { $fields[$x] };
$stats{$k}{$fields[$x]} = $values[$x];
$results{$k}{$fields[$x]}=exists $results{$k}{$fields[$x]}? $results{$k}{$fields[$x]}+$values[$x]: $values[$x];
}
}
#push(@statsarray,\%stats);
print "This host: $lbname\n".Dumper(\%stats) if $DEBUG;
foreach my $svc (keys %stats)
{
$results{'Summary'}=() unless exists $results{'Summary'};
foreach my $item (keys %{$stats{$svc}})
{
$results{'Summary'}{$item}=exists $results{'Summary'}{$item}? $results{'Summary'}{$item}+$stats{$svc}{$item}:$stats{$svc}{$item};
}
}
}
}
print "ALL results\n".Dumper(\%results) if $DEBUG;
#Run through results hash and populate nagios perfdata
my %colsdesc=(scur=>'sess_cur','smax'=>'sess_max','stot'=>'sess_tot','bin'=>'BytesIn','bout'=>'BytesOut');
foreach my $service (sort keys %results)
{
my $short=[ split ':',$service]->[0];
foreach my $item (sort keys %{$results{$service}})
{
#print "$short:$colsdesc{$item} => $results{$service}{$item}\n";
$np->add_perfdata(
'label'=>"$short:$colsdesc{$item}",
'value' => $results{$service}{$item},
'uom'=> (exists $colsneeded{$item}? $colsneeded{$item}:undef)
);
}
}
my ($down,$all)=(scalar @lbsdown, scalar @urllist);
my ($status,$message)=();
if ($down>0)
{
$status= ($down==$all)? CRITICAL:WARNING;
my $str=join(',',@lbsdown);
$message="Problem - ($down/$all) HaProxies unreachable ($str)";
}
else {
($status,$message)=(OK,"All HAProxies OK ($all/$all)");
}
$np->nagios_exit($status, $message );
# Gettext wrapper
sub _gt {
return gettext($_[0]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment