#!/usr/bin/perl
#/******************************************************************************
# *
# * check-F5-pools nagios plugin to monitor F5 BigIP pools
# *
# * 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
# * ltmPoolStatus branch with pool 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
#
# - 09 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.
# - Bugfix: Handing over $status to subroutine ended in a Hash instead of a variable. Fixed
# - Added multiline output
# - Enhenced buffer for NET::SNMP (-maxmsgsize). Default was to small
# - --maxmsgsize Message buffer size adjustible
# - Added whitelist. This will set a filter for monitored pools so that not
# all pools will be listed. Whitelisted pools will be listed in output.
# - Added blacklist to ignore pools. Pools filtered out by blacklist
# will be listed as ignored pools.
# - Added --full for full output of all pools.
#
# - 23 Jan 2018 M.Fuerstenau
# - Version 1.1
# - 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 -------------------------------------
my $ProgName = basename($0); # Name of the program
my $version="1.1.0"; # Program version
my $hostname; # Host name
my $community; # SNMP community string
my $timeout = 10; # SNMP Timeout
my $timeout_alarm; # Timeout for alarm()
my $status = 0; # Returncode
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 %ltmPoolStatusTable;
my $PoolName; # Stores pools name in foreach loops
my $oid; # Stores OID in foreach loops
my $baseoid='.1.3.6.1.4.1.3375.2.2.5';
my $ltmPoolStatus = $baseoid . '.5';
my $ltmPoolStatusNumber = $baseoid . '.5.1.0';
my $ltmPoolStatusName = $baseoid . '.5.2.1.1';
my $ltmPoolStatusAvailState = $baseoid . '.5.2.1.2';
my $ltmPoolStatusEnabledState = $baseoid . '.5.2.1.3';
my $ltmPoolStatusDetailReason = $baseoid . '.5.2.1.5';
my %ltmPoolMbrStatusTable;
my $ltmPoolMemberStatus = $baseoid . '.6';
my $ltmPoolMbrStatusNumber = $baseoid . '.6.1.0';
my $ltmPoolMbrStatusPoolName = $baseoid . '.6.2.1.1';
my $ltmPoolMbrStatusAvailState = $baseoid . '.6.2.1.5';
my $ltmPoolMbrStatusEnabledState = $baseoid . '.6.2.1.6';
my $ltmPoolMbrStatusDetailReason = $baseoid . '.6.2.1.8';
my $NoA; # Number of arguments
my $snmp_session; # Store snmp session
my $snmp_error; # Error when openeing a session
my $result; # Stores result from request
my $blacklist; # Contains the blacklist
my $whitelist; # Contains the whitelist
my $isregexp; # treat names, blacklist and whitelists as regexp
my $full; # List all pools
my $maxmsgsize; # Messagebuffer for SNMP request. Can be overwritten
my $maxmsgsize_def=5000; # Default size messagebuffer.
my $oids_cnt = 0;
my %hiddenStrings;
my $mbrs_cnt = 0;
my %pool_enabled_members;
my $enabled_pools_cnt = 0; # Number of enabled pools.
my $enabled_pools = ""; # Names of enabled pools.
my $disabled_pools_cnt = 0; # Number of disabled pools.
my $disabled_pools = ""; # Names of disabled pools.
my $available_pools_cnt = 0; # Number of available pools.
my $available_pools; # Names of available pools.
my $unavailable_pools_cnt = 0; # Number of unavailable pools.
my $unavailable_pools; # Names of unavailable pools.
my $ignored_pools_cnt = 0; # Number of ignored pools.
my $ignored_pools; # Names of ignored pools.
my $counter;
my $help;
my $table;
my $OID_base;
my $nextoid;
#--- 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,
"maxmsgsize" => \$maxmsgsize,
"B=s" => \$blacklist, "exclude=s" => \$blacklist,
"W=s" => \$whitelist, "include=s" => \$whitelist,
"isregexp" => \$isregexp,
"full" => \$full,
"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;
}
# So if we have a whitelist we set output to full. Otherwise we woudln't know
# what is been check
if (defined($whitelist))
{
$full = 1;
}
# Set message buffer max size
if (!defined($maxmsgsize))
{
$maxmsgsize = $maxmsgsize_def;
}
($snmp_session, $snmp_error) = Net::SNMP->session(-hostname => $hostname,
-version => 'snmpv2c',
-nonblocking => 1,
-community => $community,
-port => 161,
-timeout => $timeout,
-maxmsgsize => $maxmsgsize
);
if (!defined($snmp_session))
{
print "ERROR: $snmp_error";
exit 3;
}
$result = $snmp_session->get_bulk_request(-callback => [\&table_cb, \%ltmPoolStatusTable, $ltmPoolStatus],
-maxrepetitions => 10,
-varbindlist => [$ltmPoolStatus]
);
if (!defined($result))
{
print "ERROR:" . $snmp_session->error;
$snmp_session->close;
exit 3;
}
$result = $snmp_session->get_bulk_request(-callback => [\&table_cb, \%ltmPoolMbrStatusTable, $ltmPoolMemberStatus],
-maxrepetitions => 10,
-varbindlist => [$ltmPoolMemberStatus]
);
if (!defined($result))
{
print "ERROR:" . $snmp_session->error;
$snmp_session->close;
exit 3;
}
snmp_dispatcher();
$snmp_session->close;
foreach $oid (oid_lex_sort(keys(%ltmPoolStatusTable)))
{
if ( oid_base_match($ltmPoolStatusName, $oid) )
{
$oids_cnt++;
$hiddenStrings{$ltmPoolStatusTable{$oid}} = substr($oid, length($ltmPoolStatusName));
}
else
{
if ( $oids_cnt >= $ltmPoolStatusTable{$ltmPoolStatusNumber} )
{
last;
}
}
}
foreach $oid (oid_lex_sort(keys(%ltmPoolMbrStatusTable)))
{
if ( oid_base_match($ltmPoolMbrStatusEnabledState, $oid) )
{
$mbrs_cnt++;
if ( $ltmPoolMbrStatusTable{ $oid } == 1 )
{
$counter = $pool_enabled_members{ $ltmPoolMbrStatusTable{ $ltmPoolMbrStatusPoolName . substr($oid, length($ltmPoolMbrStatusEnabledState))}};
if (defined($counter))
{
$counter++;
$pool_enabled_members{ $ltmPoolMbrStatusTable{ $ltmPoolMbrStatusPoolName . substr($oid, length($ltmPoolMbrStatusEnabledState))}} = $counter;
}
else
{
$pool_enabled_members{ $ltmPoolMbrStatusTable{ $ltmPoolMbrStatusPoolName . substr($oid, length($ltmPoolMbrStatusEnabledState))}} = 1;
}
}
}
else
{
if ( $mbrs_cnt >= $ltmPoolMbrStatusTable{$ltmPoolMbrStatusNumber} )
{
last;
}
}
}
foreach $PoolName (oid_lex_sort(keys(%hiddenStrings)))
{
if ( $ltmPoolStatusTable{$ltmPoolStatusEnabledState . $hiddenStrings{$PoolName}} == 1)
{
if (defined($whitelist))
{
if (isnotwhitelisted(\$whitelist, $isregexp, $PoolName))
{
next;
}
else
{
$enabled_pools_cnt++;
$enabled_pools = $enabled_pools . " " . $PoolName . $multiline;
}
}
else
{
$enabled_pools_cnt++;
$enabled_pools = $enabled_pools . " " . $PoolName . $multiline;
}
if (defined($blacklist))
{
if (isblacklisted(\$blacklist, $isregexp, $PoolName))
{
$ignored_pools_cnt++;
$ignored_pools = $ignored_pools . " " . $PoolName . $multiline;
next;
}
}
if ( $ltmPoolStatusTable{$ltmPoolStatusAvailState . $hiddenStrings{$PoolName}} == 1 )
{
$available_pools_cnt++;
$available_pools = $available_pools . " " . $PoolName . $multiline;
}
else
{
$unavailable_pools_cnt++;
if ( $pool_enabled_members{ $PoolName } > 0 )
{
$unavailable_pools = $unavailable_pools . "'" . $PoolName . "' (" . $ltmPoolStatusTable{$ltmPoolStatusDetailReason . $hiddenStrings{$PoolName}} . ") " . $multiline;
$status = 2;
}
else
{
$unavailable_pools = $unavailable_pools . "'" . $PoolName . "' ( All pool-members disabled ) " . $multiline;
if ($status == 0)
{
$status = 1;
}
}
}
}
else
{
$disabled_pools_cnt++;
$disabled_pools = $disabled_pools . " " . $PoolName . $multiline;
}
}
if ( !defined ($ltmPoolStatusTable{$ltmPoolStatusNumber}) )
{
$status = 2;
}
# ---- Start creating the output -----------------------------------------
if (defined($whitelist))
{
print "Pools(".$ltmPoolStatusTable{$ltmPoolStatusNumber}.")
Whitelisted only: ";
}
else
{
print "Pools(".$ltmPoolStatusTable{$ltmPoolStatusNumber}.")-";
}
print "Enabled(" . $enabled_pools_cnt . ")-";
print "Disabled(" . $disabled_pools_cnt . ")-";
print "Available(" . $available_pools_cnt . ")-";
print "Unavailable(" . $unavailable_pools_cnt . ")-";
print "Ignored(" . $ignored_pools_cnt . ")";
if (defined($full))
{
print $multiline . "Enabled pools:" . $multiline . $enabled_pools;
}
if ($disabled_pools_cnt > 0)
{
print $multiline . "Disabled pools:" . $multiline . $disabled_pools;
}
if (defined($full))
{
print $multiline . "Available pools:" . $multiline . $available_pools;
}
if ($unavailable_pools_cnt > 0)
{
print $multiline . "Unavailable pools:" . $multiline . $unavailable_pools;
}
if ($ignored_pools_cnt > 0)
{
print $multiline . "Ignored pools:" . $multiline . $ignored_pools;
}
exit $status;
# ---- 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 table_cb
{
($snmp_session, $table, $OID_base) = @_;
if (!defined($snmp_session->var_bind_list))
{
print "ERROR:" . $snmp_session->error;
$status = 2;
}
else
{
# Loop through each of the OIDs in the response and assign
# the key/value pairs to the anonymous hash that is passed
# to the callback. Make sure that we are still in the table
# before assigning the key/values.
foreach $oid (oid_lex_sort(keys(%{$snmp_session->var_bind_list})))
{
if (!oid_base_match($OID_base, $oid))
{
$nextoid = undef;
last;
}
$nextoid = $oid;
$table->{$oid} = $snmp_session->var_bind_list->{$oid};
}
# If $nextoid is defined we need to send another request
# to get more of the table.
if (defined($nextoid))
{
$result = $snmp_session->get_bulk_request(-callback => [\&table_cb, $table, $OID_base],
-maxrepetitions => 10,
-varbindlist => [$nextoid]
);
if (!defined($result))
{
print "ERROR:" . $snmp_session->error;
$status = 2;
}
}
}
}
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 "[--maxmsgsize] ";
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 "Options are:\n";
print "\n";
print " -H, --hostname= Hostname or IP address of the monitored\n";
print " system.\n";
print " -C, --community= SNMP community string of the monitored\n";
print " system.\n";
print " --maxmsgsize Message buffer size for SNMP request.\n";
print " Default size of 5000 can be set higher\n";
print " if needed.\n";
print " -t, --timeout= SNMP timeout for response.\n";
print " --full Full output - lists all pools and their\n";
print " status.\n";
print " -B, --exclude= Blacklist pools.In case of a blacklist all\n";
print " blacklistedlisted enabled pools are listed\n";
print " as ignored. Otherwise you won't know what\n";
print " you are checking.\n";
print " -W, --include= Whitelist pools. In case of a whitelist all\n";
print " whitelisted enabled pools are listed.Otherwise\n";
print " you won't know what you are checking.\n";
print " --isregexp Whether to treat blacklist and whitelist as\n";
print " 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. Be\n";
print " aware that your messing connections (email,\n";
print " SMS...) must use a filter to file out the
.\n";
print " A sed oneliner like the following the job:\n";
print "\n";
print " sed 's/<[^<>]*>//g'\n";
print "\n";
}