#!/usr/bin/perl
#/******************************************************************************
# *
# * CHECK_F5_Nodes
# * check-F5-nodes nagios plugin to monitor F5 BigIP nodes
# *
# * Program: Linux plugin for Nagios
# * License: GPL
# * Copyright (c) for all changes 2018- Martin Fuerstenau (martin.fuerstenau@oce.com)
# * Portions copyright (c) 2009- Victor Ruiz (vruiz@adif.es)
# *
# * Description:
# *
# * This software checks some OID's from F5-BIGIP-LOCAL-MIB
# * ltmNodes branch with nodes related objects
# *
# * This plugin is based on check_pool_members which is based on the pool
# * member plugin of Victor Ruiz
# *
# * 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.
# *
# *****************************************************************************/
#
# - 9 Feb 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.4';
my $ltmNodeAddrStatusAvailState = $baseoid . '.3.2.1.3';
my $ltmNodeAddrStatusEnabledState = $baseoid . '.3.2.1.3';
my $blacklist; # Contains the blacklist
my $whitelist; # Contains the whitelist
my $isregexp; # treat names, blacklist and whitelists as regexp
my $ignored_nodes; # Blacklist: Names of ignored nodes.
my $checked_nodes; # Whitelist: Names of ignored nodes.
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 %NodeMbrStatus_Table; # Hash to store node names as key and member status and
# member enable state as values
my $NodeEnabledStatus; # Stores the node member enabled state
my $NodeStatus; # Stores the node member state
my $NodeName; # Stores the node name
my $NodeStatusKey; # Stores the key while processing %NodeMbrStatus_Table
my $NodeStatusVal; # Stores the value while processing %NodeMbrStatus_Table
my $crit_nodes = "";
my $warn_nodes = "";
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 node indicated in color.
# none - error;
# green - available in some capacity;
# yellow - not currently available;
# red - not available;
# blue - availability is unknown;
# gray - unlicens
my %ltmNodeStatusAvailState2Text = (
0 => 'none',
1 => 'green',
2 => 'yellow',
3 => 'red',
4 => 'blue',
5 => 'gray',
);
# The activity status of the specified node addressr, as specified
# by the user.
my %ltmNodeStatusEnabledState2Text = (
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 => $ltmNodeAddrStatusAvailState,
-maxrepetitions => 0
);
# Getting the node status. Due to the fact that the node name is stored as part
# of the OID as ASCII code we can use a little trick and extract
# it
#
# 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
# NodeName 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/^$ltmNodeAddrStatusAvailState\.//;
# 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 = $ltmNodeAddrStatusEnabledState . "." . $snmp_result_key;
$NodeEnabledStatus = $snmp_session->get_request( -varbindlist => ["$tmp_oid"] );
if (defined($$NodeEnabledStatus{$tmp_oid}))
{
$NodeEnabledStatus = $$NodeEnabledStatus{$tmp_oid};
}
else
{
print "Critical! No Status(Enabled) received!";
exit 2;
}
# First two nummbers follow by a dot have to be eliminate
# Don't know what it represents
$snmp_result_key =~ s/^..\.//;
# 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/%.*$//;
# Filling the new hash
$NodeMbrStatus_Table{ $tmp } = "$snmp_result_value:$NodeEnabledStatus";
}
# OK - ready with snmp stuff
$snmp_session->close;
foreach $NodeStatusKey (sort keys %NodeMbrStatus_Table)
{
$NodeStatusVal = $NodeMbrStatus_Table{$NodeStatusKey};
$NodeName = $NodeStatusKey;
$NodeStatus = $NodeStatusVal;
$NodeStatus =~ s/:.*$//;
$NodeEnabledStatus = $NodeStatusVal;
$NodeEnabledStatus =~ s/^.*://;
if (defined($whitelist))
{
if (isnotwhitelisted(\$whitelist, $isregexp, $NodeName))
{
next;
}
else
{
if ($checked_nodes !~ m/$NodeName/)
{
$checked_nodes = $checked_nodes . " " . $NodeName . $multiline;
}
}
}
if (defined($blacklist))
{
if (isblacklisted(\$blacklist, $isregexp, $NodeName))
{
if ($ignored_nodes !~ m/$NodeName/)
{
$ignored_nodes = $ignored_nodes . " " . $NodeName . $multiline;
}
next;
}
}
if ( $NodeEnabledStatus == 0)
{
if ( $NodeStatus == 0)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 2)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 3)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 4)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 5)
{
$actual_exit_state = 2;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
}
if ( $NodeEnabledStatus == 1)
{
if ( $NodeStatus == 0)
{
$actual_exit_state = 2;
$exit_state = check_state($exit_state, $actual_exit_state);
$crit_nodes = $crit_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 2)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 3)
{
$actual_exit_state = 2;
$exit_state = check_state($exit_state, $actual_exit_state);
$crit_nodes = $crit_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 4)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 5)
{
$actual_exit_state = 2;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
}
if ( $NodeEnabledStatus == 3)
{
if ( $NodeStatus == 0)
{
$actual_exit_state = 2;
$exit_state = check_state($exit_state, $actual_exit_state);
$crit_nodes = $crit_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 2)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 3)
{
$actual_exit_state = 2;
$exit_state = check_state($exit_state, $actual_exit_state);
$crit_nodes = $crit_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 4)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
if ( $NodeStatus == 5)
{
$actual_exit_state = 1;
$warn_state =1;
$exit_state = check_state($exit_state, $actual_exit_state);
$warn_nodes = $warn_nodes . "Node:" . $NodeName . " - Status(Node):" . $ltmNodeStatusAvailState2Text{ $NodeStatus } . " - Status(Enabled):" . $ltmNodeStatusEnabledState2Text{ $NodeEnabledStatus } . $multiline;
}
}
}
if ( $exit_state == 0)
{
print "OK: No errors for nodes found.";
}
if ( $exit_state == 1)
{
print "Warnings found:" . $multiline;
print "$warn_nodes\n";
}
if ( $exit_state == 2)
{
print "Errors found:" . $multiline;
print "$crit_nodes\n";
if (defined($warn_state))
{
print $multiline . "Warnings found for:" . $multiline;
print "$warn_nodes\n";
}
}
if (defined($whitelist))
{
print "Nodes checked:" . $multiline;
print "$checked_nodes";
}
if (defined($blacklist))
{
print "Nodes ignored: " . $multiline;
print "$ignored_nodes";
}
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 nodes.In case of a blacklist\n";
print " all blacklisted nodes will not be checked.\n";
print " Nodes filtered out by blacklist\n";
print " will be listed as ignored nodes.\n";
print " -W, --include= Whitelist nodes. In case of a whitelist\n";
print " only whitelisted nodes will be checked.\n";
print " Nodes checked will be listed\n";
print " in output as checked nodes.\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";
}