diff --git a/haproxy/files/check_haproxy_stats.pl b/haproxy/files/check_haproxy_stats.pl new file mode 100644 index 00000000..14d4fa45 --- /dev/null +++ b/haproxy/files/check_haproxy_stats.pl @@ -0,0 +1,282 @@ +#!/usr/bin/env perl +# vim: se et ts=4: + +# +# Copyright (C) 2012, Giacomo Montagner +# 2015, Yann Fertat, Romain Dessort, Jeff Palmer, +# Christophe Drevet-Droguet +# +# This program is free software; you can redistribute it and/or modify it +# under the same terms as Perl 5.10.1. +# For more details, see http://dev.perl.org/licenses/artistic.html +# +# This program is distributed in the hope that it will be +# useful, but without any warranty; without even the implied +# warranty of merchantability or fitness for a particular purpose. +# + +our $VERSION = "1.1.1"; + +open(STDERR, ">&STDOUT"); + +# CHANGELOG: +# 1.0.0 - first release +# 1.0.1 - fixed empty message if all proxies are OK +# 1.0.2 - add perfdata +# 1.0.3 - redirect stderr to stdout +# 1.0.4 - fix undef vars +# 1.0.5 - fix thresholds +# 1.1.0 - support for HTTP interface +# 1.1.1 - drop perl 5.10 requirement + +use strict; +use warnings; +use File::Basename qw/basename/; +use IO::Socket::UNIX; +use Getopt::Long; +my $lwp = eval { + require LWP::Simple; + LWP::Simple->import; + 1; +}; + +sub usage { + my $me = basename $0; + print <. + $me is distributed under GPL and the Artistic License 2.0 +SEE ALSO + Check out online haproxy documentation at +EOU +} + +my %check_statuses = ( + UNK => "unknown", + INI => "initializing", + SOCKERR => "socket error", + L4OK => "layer 4 check OK", + L4CON => "connection error", + L4TMOUT => "layer 1-4 timeout", + L6OK => "layer 6 check OK", + L6TOUT => "layer 6 (SSL) timeout", + L6RSP => "layer 6 protocol error", + L7OK => "layer 7 check OK", + L7OKC => "layer 7 conditionally OK", + L7TOUT => "layer 7 (HTTP/SMTP) timeout", + L7RSP => "layer 7 protocol error", + L7STS => "layer 7 status error", +); + +my @status_names = (qw/OK WARNING CRITICAL UNKNOWN/); + +# Defaults +my $swarn = 80.0; +my $scrit = 90.0; +my $sock = "/var/run/haproxy.sock"; +my $url; +my $user = ''; +my $pass = ''; +my $dump; +my $ignore_maint; +my $proxy; +my $no_proxy; +my $help; + +# Read command line +Getopt::Long::Configure ("bundling"); +GetOptions ( + "c|critical=i" => \$scrit, + "d|dump" => \$dump, + "h|help" => \$help, + "m|ignore-maint" => \$ignore_maint, + "p|proxy=s" => \$proxy, + "P|no-proxy=s" => \$no_proxy, + "s|sock|socket=s" => \$sock, + "U|url=s" => \$url, + "u|user|username=s" => \$user, + "x|pass|password=s" => \$pass, + "w|warning=i" => \$swarn, +); + +# Want help? +if ($help) { + usage; + exit 3; +} + +my $haproxy; +if ($url and $lwp) { + my $geturl = $url; + if ($user ne '') { + $url =~ /^([^:]*:\/\/)(.*)/; + $geturl = $1.$user.':'.$pass.'@'.$2; + } + $geturl .= ';csv'; + $haproxy = get($geturl); +} elsif ($url) { + my $haproxyio; + my $getcmd = "curl --insecure -s --fail " + . "--user '$user:$pass' '".$url.";csv'"; + open $haproxyio, "-|", $getcmd; + while (<$haproxyio>) { + $haproxy .= $_; + } + close($haproxyio); +} else { + # Connect to haproxy socket and get stats + my $haproxyio = new IO::Socket::UNIX ( + Peer => $sock, + Type => SOCK_STREAM, + ); + die "Unable to connect to haproxy socket: $sock\n$@" unless $haproxyio; + print $haproxyio "show stat\n" or die "Print to socket failed: $!"; + $haproxy = ''; + while (<$haproxyio>) { + $haproxy .= $_; + } + close($haproxyio); +} + +# Dump stats and exit if requested +if ($dump) { + print($haproxy); + exit 0; +} + +# Get labels from first output line and map them to their position in the line +my @hastats = ( split /\n/, $haproxy ); +my $labels = $hastats[0]; +die "Unable to retrieve haproxy stats" unless $labels; +chomp($labels); +$labels =~ s/^# // or die "Data format not supported."; +my @labels = split /,/, $labels; +{ + no strict "refs"; + my $idx = 0; + map { $$_ = $idx++ } @labels; +} + +# Variables I will use from here on: +our $pxname; +our $svname; +our $status; +our $slim; +our $scur; + +my @proxies = split ',', $proxy if $proxy; +my @no_proxies = split ',', $no_proxy if $no_proxy; +my $exitcode = 0; +my $msg; +my $checked = 0; +my $perfdata = ""; + +# Remove excluded proxies from the list if both -p and -P options are +# specified. +my %hash; +@hash{@no_proxies} = undef; +@proxies = grep{ not exists $hash{$_} } @proxies; + +foreach (@hastats) { + chomp; + next if /^#/; + next if /^[[:space:]]*$/; + my @data = split /,/, $_; + if (@proxies) { next unless grep {$data[$pxname] eq $_} @proxies; }; + if (@no_proxies) { next if grep {$data[$pxname] eq $_} @no_proxies; }; + + # Is session limit enforced? + if ($data[$slim]) { + $perfdata .= sprintf "%s-%s=%u;%u;%u;0;%u;", $data[$pxname], $data[$svname], $data[$scur], $swarn * $data[$slim] / 100, $scrit * $data[$slim] / 100, $data[$slim]; + + # Check current session # against limit + my $sratio = $data[$scur]/$data[$slim]; + if ($sratio >= $scrit / 100 || $sratio >= $swarn / 100) { + $exitcode = $sratio >= $scrit / 100 ? 2 : + $exitcode < 2 ? 1 : $exitcode; + $msg .= sprintf "%s:%s sessions: %.2f%%; ", $data[$pxname], $data[$svname], $sratio * 100; + } + } + + # Check of BACKENDS + if ($data[$svname] eq 'BACKEND') { + if ($data[$status] ne 'UP') { + $msg .= sprintf "BACKEND: %s is %s; ", $data[$pxname], $data[$status]; + $exitcode = 2; + } + # Check of FRONTENDS + } elsif ($data[$svname] eq 'FRONTEND') { + if ($data[$status] ne 'OPEN') { + $msg .= sprintf "FRONTEND: %s is %s; ", $data[$pxname], $data[$status]; + $exitcode = 2; + } + # Check of servers + } else { + if ($data[$status] ne 'UP') { + next if ($ignore_maint && $data[$status] eq 'MAINT'); + next if $data[$status] eq 'no check'; # Ignore server if no check is configured to be run + next if $data[$svname] eq 'sock-1'; + $exitcode = 2; + our $check_status; + $msg .= sprintf "server: %s:%s is %s", $data[$pxname], $data[$svname], $data[$status]; + $msg .= sprintf " (check status: %s)", $check_statuses{$data[$check_status]} if $check_statuses{$data[$check_status]}; + $msg .= "; "; + } + } + ++$checked; +} + +unless ($msg) { + $msg = @proxies ? sprintf("checked proxies: %s", join ', ', sort @proxies) : "checked $checked proxies."; +} +print "Check haproxy $status_names[$exitcode] - $msg|$perfdata\n"; +exit $exitcode; diff --git a/haproxy/tasks/main.yml b/haproxy/tasks/main.yml index 43e3c4b5..054aec1a 100644 --- a/haproxy/tasks/main.yml +++ b/haproxy/tasks/main.yml @@ -32,3 +32,5 @@ tags: - haproxy - config + +- include: nagios.yml diff --git a/haproxy/tasks/nagios.yml b/haproxy/tasks/nagios.yml new file mode 100644 index 00000000..eff711ac --- /dev/null +++ b/haproxy/tasks/nagios.yml @@ -0,0 +1,20 @@ +--- + +- name: "Install check_haproxy_stats script" + copy: + src: check_haproxy_stats.pl + dest: /usr/local/lib/nagios/plugins/check_haproxy_stats.pl + mode: "0755" + tags: + - haproxy + - nrpe + +- name: "Add check_haproxy to sudoers" + lineinfile: + dest: /etc/sudoers.d/evolinux + line: 'nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_haproxy_stats.pl' + insertafter: '^nagios' + tags: + - haproxy + - nrpe + - sudo