#!/usr/bin/perl
#/******************************************************************************
# *
# * CHECK_F5_POOL_MEMBERS
# * check-F5-poolsmembers nagios plugin to monitor F5 BigIP pool members
# *
# * Program: Linux plugin for Nagios
# * License: GPL
# * Copyright (c) 2009- Victor Ruiz (vruiz@adif.es)
# * Copyright (c) for all changes 2018- Martin Fuerstenau (martin.fuerstenau@oce.com)
# *
# * Description:
# *
# * This software checks some OID's from F5-BIGIP-LOCAL-MIB
# * ltmPools branch with pool members related objects
# *
# * License Information:
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 2 of the License, or
# * (at your option) any later version.
# *
# * 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. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# *
# *****************************************************************************/
#
# - 2009 Victor Ruiz
# - Version 0.9
#
# - 29 Jan 2018 M.Fuerstenau
# - Version 1.0
# - reformatted code for better readability
# - Changed Getopt::Std into Getopt::Long
# - Changed -h to -H and -c to -C because upper case characters are much more
# common for plugins. -h ist commonly used for help.
# - All variable defintions at the beginning of the script.
# - Removed unused parts
# - Bugfix: Unknown has now a returncode of 3 and not -1. Fixed.
# - Fixed exit on SNMP error or empty result. A linefeed (\n)
# doesn't make sense because the error message wouldn't be displayed
# in Nagios status overview
# - Added whitelist. This will set a filter for monitored pools so that not
# all pools will be listed. Pools checked will be listed in output as
# checked pools..
# - Added blacklist to ignore pools. Pools filtered out by blacklist
# will be listed as ignored pools.
# - Rewritten complete SNMP and outpu part. Some of the stuff used was replaced in SNMP output.
# - Fixed exit on SNMP error or empty result. A linefeed (\n)
# doesn't make sense because the error message wouldn't be displayed
# in Nagios status overview
use strict;
use Net::SNMP qw(:snmp);
use Getopt::Long;
use File::Basename;
# Let's catch some signals
# Handle SIGALRM (timeout triggered by alarm() call)
$SIG{ALRM} = 'catch_alarm';
$SIG{INT} = 'catch_intterm';
$SIG{TERM} = 'catch_intterm';
#--- Start presets and declarations -------------------------------------
# 1. Define variables
my $ProgName = basename($0); # Name of the program
my $version="1.0.0"; # Program version
my $hostname; # Host name
my $community; # SNMP community string
my $timeout = 10; # SNMP Timeout
my $timeout_alarm; # Timeout for alarm()
my $multiline; # Multiline output in overview. This mean technically that
# a multiline output uses a HTML
for the GUI instead of
# Be aware that your messing connections (email, SMS...) must use
# a filter to file out the
. A sed oneliner like the following
# will do the job:
# sed 's/<[^<>]*>//g'
my $multiline_def="\n"; # Default for $multiline;
my $baseoid='.1.3.6.1.4.1.3375.2.2.5';
my $ltmPoolMbrStatusAvailState = $baseoid . '.6.2.1.5';
my $ltmPoolMbrStatusEnabledState = $baseoid . '.6.2.1.6';
my $blacklist; # Contains the blacklist
my $whitelist; # Contains the whitelist
my $isregexp; # treat names, blacklist and whitelists as regexp
my $ignored_pools; # Blacklist: Names of ignored pools.
my $checked_pools; # Whitelist: Names of ignored pools.
my $NoA; # Number of arguments
my $snmp_session; # Store snmp session
my $snmp_error; # Error when openeing a session
my $snmp_result; # Stores result from request in Hash reference
my $snmp_result_key; # Keys of the Hash reference
my $snmp_result_value; # Stores of the Hash reference
my %PoolMbrStatus_Table; # Hash to store pool names and pool members names as key
# and member status and member enable state as values
my $PoolMbrEnabledStatus; # Stores the pool member enabled state
my $PoolMbrStatus; # Stores the pool member state
my $PoolName; # Stores the pool name
my $PoolMbrName; # Stores the pool member name
my $PoolMbrStatusKey; # Stores the key while processing %PoolMbrStatus_Table
my $PoolMbrStatusVal; # Stores the value while processing %PoolMbrStatus_Table
my $crit_members = "";
my $warn_members = "";
my $help; # Flag for help()
my @tmp; # Temporary array for work
my $tmp; # Temporary variable for work
my $tmp_oid; # Temporary variable for storing an OID
my $warn_state; # Used to display warning in case of critical too
my $actual_exit_state = 0; # Exit state from actual check
my $exit_state = 0; # Highest and final exit state
# 2. Define hashes and arrays
# The availability of the specified pool member indicated in color.
# none - error;
# green - available in some capacity;
# yellow - not currently available;
# red - not available;
# blue - availability is unknown;
# gray - unlicens
my %ltmPoolMbrStatusAvailState2Text = (
0 => 'none',
1 => 'green',
2 => 'yellow',
3 => 'red',
4 => 'blue',
5 => 'gray',
);
# The activity status of the specified pool member, as specified
# by the user.
my %ltmPoolMbrStatusEnabledState2Text = (
0 => 'none',
1 => 'enabled',
2 => 'disabled',
3 => 'disabledbyparent',
);
#--- End presets --------------------------------------------------------
# First we have to fix the number of arguments
$NoA=$#ARGV;
# Right number of arguments (therefore NoA :-)) )
if ( $NoA == -1 )
{
usage();
exit 1;
}
Getopt::Long::Configure('bundling');
GetOptions
("H=s" => \$hostname, "hostname=s" => \$hostname,
"C=s" => \$community, "community=s" => \$community,
"t=s" => \$timeout, "timeout=s" => \$timeout,
"multiline" => \$multiline,
"B=s" => \$blacklist, "exclude=s" => \$blacklist,
"W=s" => \$whitelist, "include=s" => \$whitelist,
"isregexp" => \$isregexp,
"h" => \$help, "help" => \$help,
"V" => \$version, "version" => \$version);
# Set timeout for alarm()
$timeout_alarm = $timeout + 30;
alarm ($timeout_alarm);
# Several checks to check parameters
if ($help)
{
help();
exit 0;
}
# Multiline output in GUI overview?
if (defined($multiline))
{
$multiline = "
";
}
else
{
$multiline = $multiline_def;
}
($snmp_session, $snmp_error) = Net::SNMP->session(-hostname => $hostname,
-version => 2,
-community => $community,
-port => 161,
-timeout => $timeout,
);
if (!defined($snmp_session))
{
print "ERROR: $snmp_error";
exit 3;
}
$snmp_result = $snmp_session->get_table(-baseoid => $ltmPoolMbrStatusAvailState,
-maxrepetitions => 0
);
# Getting the pool member status. Due to the fact that the pool name and the member
# is stored as part of the OID as ASCII code we can use a little trick and extract
# it. For a some unknown reason we have different not printable non ASCII characters
# between the OID stored in $ltmPoolMbrStatusAvailState and the ASCII part we have
# to eliminate.
#
# We also has to eliminate port numbers which can be interpreted as ASCII code. All
# other port numbers will be eliminated automatically.
#
# By replacing the base OID (AvailState with EnabledState) we can get the Enabled state
# with a get request.
#
# The results will be stored in a new hash in the following format:
#
# Key Value
# PoolName:MemberName MemberState:MemberEnabled:State
#
# So we have all things we need
foreach $snmp_result_key ( keys %$snmp_result)
{
$snmp_result_value = $$snmp_result{$snmp_result_key};
$snmp_result_key =~ s/^$ltmPoolMbrStatusAvailState\.//;
# After eliminating the base oid we can get the enable status
# by assembling a new OID to use a get request. This is more
# efficient than getting a table
$tmp_oid = $ltmPoolMbrStatusEnabledState . "." . $snmp_result_key;
$PoolMbrEnabledStatus = $snmp_session->get_request( -varbindlist => ["$tmp_oid"] );
if (defined($$PoolMbrEnabledStatus{$tmp_oid}))
{
$PoolMbrEnabledStatus = $$PoolMbrEnabledStatus{$tmp_oid};
}
else
{
print "Critical! No enabled status received!";
exit 2;
}
# First two nummbers follow by a dot have to be eliminate
# Don't know what it represents
$snmp_result_key =~ s/^..\.//;
# Kill port numbers at the end
$snmp_result_key =~ s/\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$//;
# Convert it to an arry
@tmp=split(/\./, $snmp_result_key);
# And now we convert it to ASCII
$tmp = pack("C*", @tmp);
# And remove no printable characters
$tmp =~ s/[^[:ascii:]]//g;
# For members not stored with it's hostname
# we have to filter out %number at the end of the IP address
$tmp =~ s/%.*$//;
# Now we have to add a unique seperator between the pool
# and member name
$tmp =~ s/pool.?\/Common/pool:\/Common/;
# Filling the new hash
$PoolMbrStatus_Table{ $tmp } = "$snmp_result_value:$PoolMbrEnabledStatus";
}
# OK - ready with snmp stuff
$snmp_session->close;
foreach $PoolMbrStatusKey (sort keys %PoolMbrStatus_Table)
{
$PoolMbrStatusVal = $PoolMbrStatus_Table{$PoolMbrStatusKey};
$PoolName = $PoolMbrStatusKey;
$PoolName =~ s/:.*$//;
$PoolMbrName = $PoolMbrStatusKey;
$PoolMbrName =~ s/^.*\///;
$PoolMbrStatus = $PoolMbrStatusVal;
$PoolMbrStatus =~ s/:.*$//;
$PoolMbrEnabledStatus = $PoolMbrStatusVal;
$PoolMbrEnabledStatus =~ s/^.*://;
if (defined($whitelist))
{
if (isnotwhitelisted(\$whitelist, $isregexp, $PoolName))
{
next;
}
else
{
if ($checked_pools !~ m/$PoolName/)
{
$checked_pools = $checked_pools . " " . $PoolName . $multiline;
}
}
}
if (defined($blacklist))
{
if (isblacklisted(\$blacklist, $isregexp, $PoolName))
{
if ($ignored_pools !~ m/$PoolName/)
{
$ignored_pools = $ignored_pools . " " . $PoolName . $multiline;
}
next;
}
}
if ( $PoolMbrEnabledStatus == 0)
{
if ( $PoolMbrStatus == 0)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 2)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 3)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 4)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 5)
{
$actual_exit_state = 2;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
}
if ( $PoolMbrEnabledStatus == 1)
{
if ( $PoolMbrStatus == 0)
{
$actual_exit_state = 2;
$exit_state = check_state($exit_state, $actual_exit_state);
$crit_members = $crit_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 2)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 3)
{
$actual_exit_state = 2;
$exit_state = check_state($exit_state, $actual_exit_state);
$crit_members = $crit_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 4)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 5)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
}
if ( $PoolMbrEnabledStatus == 3)
{
if ( $PoolMbrStatus == 0)
{
$actual_exit_state = 2;
$exit_state = check_state($exit_state, $actual_exit_state);
$crit_members = $crit_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 2)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 3)
{
$actual_exit_state = 2;
$exit_state = check_state($exit_state, $actual_exit_state);
$crit_members = $crit_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 4)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
if ( $PoolMbrStatus == 5)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_members = $warn_members . "Member:" . $PoolMbrName . " Status:" . $ltmPoolMbrStatusAvailState2Text{ $PoolMbrStatus } . " Pool:" . $PoolName . " PoolStatus:" . $ltmPoolMbrStatusEnabledState2Text{ $PoolMbrEnabledStatus } . $multiline;
}
}
}
if ( $exit_state == 0)
{
print "OK: No errors for any pool members found.";
}
if ( $exit_state == 1)
{
print "Warnings found for:" . $multiline;
print "$warn_members\n";
}
if ( $exit_state == 2)
{
print "Errors found for:" . $multiline;
print "$crit_members\n";
if (defined($warn_state))
{
print $multiline . "Warnings found for:" . $multiline;
print "$warn_members\n";
}
}
if (defined($whitelist))
{
print "Pools checked:" . $multiline;
print "$checked_pools";
}
if (defined($blacklist))
{
print "Pools ignored: " . $multiline;
print "$ignored_pools";
}
exit $exit_state;
# ---- Subroutines -------------------------------------------------------
# Catching some signals
sub catch_alarm
{
print "UNKNOWN: Script timed out.\n";
exit 3;
}
sub catch_intterm
{
print "UNKNOWN: Script killed by monitor.\n";
exit 3;
}
sub check_state
{
if (grep { $_ == 2 } @_)
{
return 2;
}
if (grep { $_ == 1 } @_)
{
return 1;
}
if (grep { $_ == 3 } @_)
{
return 3;
}
if (grep { $_ == 0 } @_)
{
return 0;
}
return 3;
}
sub isblacklisted()
{
my ($blacklist_ref,$regexpflag,$candidate) = @_;
my $ret = 0;
my @blacklist;
my $blacklist;
my $hitcount = 0;
if (!defined $$blacklist_ref)
{
return 0;
}
if ($regexpflag == 0)
{
$ret = grep(/$candidate/, $$blacklist_ref);
}
else
{
@blacklist = split(/,/, $$blacklist_ref);
foreach $blacklist (@blacklist)
{
if ($candidate =~ m/$blacklist/)
{
$hitcount++;
}
}
if ($hitcount >= 1)
{
$ret = 1;
}
}
return $ret;
}
sub isnotwhitelisted()
{
my ($whitelist_ref,$regexpflag,$candidate) = @_;
my $ret = 0;
my @whitelist;
my $whitelist;
my $hitcount = 0;
if (!defined $$whitelist_ref)
{
return $ret;
}
if ($regexpflag == 0)
{
$ret = ! grep(/$candidate/, $$whitelist_ref);
}
else
{
@whitelist = split(/,/, $$whitelist_ref);
foreach $whitelist (@whitelist)
{
if ($candidate =~ m/$whitelist/)
{
$hitcount++;
}
}
if ($hitcount == 0)
{
$ret = 1;
}
}
return $ret;
}
sub usage()
{
print "\nUsage:\n";
print "$ProgName ";
print "-H|--hostname= ";
print "-C|--community= ";
print "[-t|--timeout= timeout for snmp-response>] ";
print "[-W|--include=] ";
print "[-B, --exclude=] ";
print "[--isregexp] ";
print "[--multiline]\n\n";
print "or\n\n";
print "$ProgName -h\n\n";
}
sub help()
{
usage();
print"\n";
print "Be aware: Members of disabled pools will not be checked!\n";
print"\n";
print " -H, --hostname= Hostname or IP address of the\n";
print " monitored system.\n";
print " -C, --community= SNMP community string of the\n";
print " monitored system.\n";
print " -t, --timeout= SNMP timeout for response.\n";
print " -B, --exclude= Blacklist pools.In case of a blacklist\n";
print " all members of blacklisted\n";
print " pools will not be checked.\n";
print " Pools filtered out by blacklist\n";
print " will be listed as ignored pools.\n";
print " -W, --include= Whitelist pools. In case of a whitelist\n";
print " only members of whitelisted\n";
print " enabled pools will be checked.\n";
print " Pools checked will be listed\n";
print " in output as checked pools.\n";
print " --isregexp Whether to treat blacklist and whitelist\n";
print " as regexp.\n";
print " --multiline Multiline output in overview. This means\n";
print " technically that a multiline output uses\n";
print " a HTML
for the GUI instead of\n";
print " Be aware that your messing connections\n";
print " (email, SMS...) must use a filter to file\n";
print " out the
. A sed oneliner like the\n";
print " following will do the job:\n";
print "\n";
print " sed 's/<[^<>]*>//g'\n";
print "\n";
}