����JFIF��x�x����'
| Server IP : 78.140.185.180 / Your IP : 216.73.216.170 Web Server : LiteSpeed System : Linux cpanel13.v.fozzy.com 4.18.0-513.11.1.lve.el8.x86_64 #1 SMP Thu Jan 18 16:21:02 UTC 2024 x86_64 User : builderbox ( 1072) PHP Version : 7.3.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /proc/1630575/task/1630575/root/proc/1630575/root/lib64/nagios/plugins/base/ |
Upload File : |
#!/usr/bin/perl
#
# DESCRIPTION: Nagios plugin for checking the status of bonded network
# interfaces (masters and slaves) on Linux servers.
#
# AUTHOR: Trond H. Amundsen <t.h.amundsen@usit.uio.no>
#
# Copyright (C) 2009-2012 Trond H. Amundsen
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
#
use strict;
use warnings;
use POSIX qw(isatty);
use Getopt::Long qw(:config no_ignore_case);
# Global (package) variables used throughout the code
use vars qw( $NAME $VERSION $AUTHOR $CONTACT $E_OK $E_WARNING $E_CRITICAL
$E_UNKNOWN $USAGE $HELP $LICENSE $linebreak $counter $exit_code
%opt %reverse_exitcode %text2exit %bonding %nagios_level_count
@perl_warnings @reports @blacklist @ok_reports
);
#---------------------------------------------------------------------
# Initialization and global variables
#---------------------------------------------------------------------
# Collect perl warnings in an array
$SIG{__WARN__} = sub { push @perl_warnings, [@_]; };
# Version and similar info
$NAME = 'check_linux_bonding';
$VERSION = '1.3.2';
$AUTHOR = 'Trond H. Amundsen';
$CONTACT = 't.h.amundsen@usit.uio.no';
# Exit codes
$E_OK = 0;
$E_WARNING = 1;
$E_CRITICAL = 2;
$E_UNKNOWN = 3;
# Nagios error levels reversed
%reverse_exitcode
= (
0 => 'OK',
1 => 'WARNING',
2 => 'CRITICAL',
3 => 'UNKNOWN',
);
# Usage text
$USAGE = <<"END_USAGE";
Usage: $NAME [OPTION]...
END_USAGE
# Help text
$HELP = <<'END_HELP';
OPTIONS:
-t, --timeout Plugin timeout in seconds [5]
-s, --state Prefix alerts with alert state
-S, --short-state Prefix alerts with alert state abbreviated
-n, --no-bonding Alert level if no bonding interfaces found [ok]
--slave-down Alert level if a slave is down [warning]
--disable-sysfs Don't use sysfs (default), use procfs
--ignore-num-ad (IEEE 802.3ad) Don't warn if num_ad_ports != num_slaves
-b, --blacklist Blacklist failed interfaces
-v, --verbose Debug/Verbose output, reports everything
-h, --help Display this help text
-V, --version Display version info
For more information and advanced options, see the manual page or URL:
http://folk.uio.no/trondham/software/check_linux_bonding.html
END_HELP
# Version and license text
$LICENSE = <<"END_LICENSE";
$NAME $VERSION
Copyright (C) 2009-2012 $AUTHOR
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by $AUTHOR <$CONTACT>
END_LICENSE
# Options with default values
%opt
= ( 'timeout' => 5, # default timeout is 5 seconds
'help' => 0,
'version' => 0,
'blacklist' => [],
'no_bonding' => 'ok',
'state' => 0,
'shortstate' => 0,
'linebreak' => undef,
'verbose' => 0,
'disable_sysfs' => 0,
'slave_down' => 'warning',
'ignore_num_ad' => 0,
);
# Get options
GetOptions('t|timeout=i' => \$opt{timeout},
'h|help' => \$opt{help},
'V|version' => \$opt{version},
'b|blacklist=s' => \@{ $opt{blacklist} },
'n|no-bonding=s' => \$opt{no_bonding},
's|state' => \$opt{state},
'S|short-state' => \$opt{shortstate},
'linebreak=s' => \$opt{linebreak},
'v|verbose' => \$opt{verbose},
'disable-sysfs' => \$opt{disable_sysfs},
'slave-down=s' => \$opt{slave_down},
'ignore-num-ad' => \$opt{ignore_num_ad},
) or do { print $USAGE; exit $E_UNKNOWN };
# If user requested help
if ($opt{'help'}) {
print $USAGE, $HELP;
exit $E_OK;
}
# If user requested version info
if ($opt{'version'}) {
print $LICENSE;
exit $E_OK;
}
# Reports (messages) are gathered in this array
@reports = ();
# Setting timeout
$SIG{ALRM} = sub {
print "PLUGIN TIMEOUT: $NAME timed out after $opt{timeout} seconds\n";
exit $E_UNKNOWN;
};
alarm $opt{timeout};
# Default line break
$linebreak = isatty(*STDOUT) ? "\n" : '<br/>';
# Line break from option
if (defined $opt{linebreak}) {
if ($opt{linebreak} eq 'REG') {
$linebreak = "\n";
}
elsif ($opt{linebreak} eq 'HTML') {
$linebreak = '<br/>';
}
else {
$linebreak = $opt{linebreak};
}
}
# Blacklisted interfaces
@blacklist = defined $opt{blacklist} ? @{ get_blacklist() } : ();
# Translate text exit codes to values
%text2exit
= ( 'ok' => $E_OK,
'warning' => $E_WARNING,
'critical' => $E_CRITICAL,
'unknown' => $E_UNKNOWN,
);
# Check syntax of '--no-bonding' option
if (!exists $text2exit{$opt{no_bonding}}) {
unknown_error("Wrong usage of '--no-bonding' option: '"
. $opt{no_bonding}
. "' is not a recognized keyword");
}
# Check syntax of '--slave-down' option
if (!exists $text2exit{$opt{slave_down}}) {
unknown_error("Wrong usage of '--slave-down' option: '"
. $opt{slave_down}
. "' is not a recognized keyword");
}
#---------------------------------------------------------------------
# Functions
#---------------------------------------------------------------------
#
# Store a message in the message array
#
sub report {
my ($msg, $exval) = @_;
return push @reports, [ $msg, $exval ];
}
#
# Give an error and exit with unknown state
#
sub unknown_error {
my $msg = shift;
print "ERROR: $msg\n";
exit $E_UNKNOWN;
}
#
# Read the blacklist option and return a hash containing the
# blacklisted components
#
sub get_blacklist {
my @bl = ();
my @blacklist = ();
if (scalar @{ $opt{blacklist} } >= 0) {
foreach my $black (@{ $opt{blacklist} }) {
my $tmp = q{};
if (-f $black) {
open my $BL, '<', $black
or do { report('other', "Couldn't open blacklist file $black: $!", $E_UNKNOWN)
and return {} };
chomp($tmp = <$BL>);
close $BL;
}
else {
$tmp = $black;
}
push @bl, $tmp;
}
}
return [] if $#bl < 0;
# Parse blacklist string, put in hash
foreach my $black (@bl) {
push @blacklist, split m{,}xms, $black;
}
return \@blacklist;
}
#
# Find bonding interfaces using sysfs
#
sub find_bonding_sysfs {
my $sysdir = '/sys/class/net';
my $masters_file = "$sysdir/bonding_masters";
my @bonds = ();
my %bonding = ();
if (! -f $masters_file) {
return {};
}
# get bonding masters
open my $MASTER, '<', $masters_file
or unknown_error("Couldn't open $masters_file: $!");
@bonds = split m{\s+}xms, <$MASTER>;
close $MASTER;
foreach my $bond (@bonds) {
# get bonding mode
open my $MODE, '<', "$sysdir/$bond/bonding/mode"
or unknown_error("ERROR: Couldn't open $sysdir/$bond/bonding/mode: $!");
my ($mode, $nr) = split m/\s+/xms, <$MODE>;
close $MODE;
$bonding{$bond}{mode} = "mode=$nr ($mode)";
# get 802.3ad number of ports
if ($bonding{$bond}{mode} eq 'mode=4 (802.3ad)') {
open my $AD_NUM, '<', "$sysdir/$bond/bonding/ad_num_ports"
or unknown_error("ERROR: Couldn't open $sysdir/$bond/bonding/ad_num_ports: $!");
my $ad_num = <$AD_NUM>;
close $AD_NUM;
$bonding{$bond}{ad_num} = $ad_num;
}
# get slaves
my @slaves = ();
open my $SLAVES, '<', "$sysdir/$bond/bonding/slaves"
or unknown_error("Couldn't open $sysdir/$bond/bonding/slaves: $!");
@slaves = split m/\s+/xms, <$SLAVES>;
close $SLAVES;
# get active slave
open my $ACTIVE, '<', "$sysdir/$bond/bonding/active_slave"
or unknown_error("Couldn't open $sysdir/$bond/bonding/active_slave: $!");
$bonding{$bond}{active} = <$ACTIVE>;
close $ACTIVE;
if (defined $bonding{$bond}{active}) {
chop $bonding{$bond}{active};
}
# get primary slave
open my $PRIMARY, '<', "$sysdir/$bond/bonding/primary"
or unknown_error("Couldn't open $sysdir/$bond/bonding/primary: $!");
$bonding{$bond}{primary} = <$PRIMARY>;
close $PRIMARY;
if (defined $bonding{$bond}{primary}) {
chop $bonding{$bond}{primary};
}
# get slave status
foreach my $slave (@slaves) {
my $prefix = -d "$sysdir/$bond/slave_$slave" ? 'slave' : 'lower';
open my $STATE, '<', "$sysdir/$bond/$prefix" . "_$slave/operstate"
or unknown_error("Couldn't open $sysdir/$bond/$prefix" . "_$slave/operstate: $!");
chop($bonding{$bond}{slave}{$slave} = <$STATE>);
close $STATE;
}
# get bond state
open my $BSTATE, '<', "$sysdir/$bond/operstate"
or unknown_error("Couldn't open $sysdir/$bond/operstate: $!");
chop($bonding{$bond}{status} = <$BSTATE>);
close $BSTATE;
}
return \%bonding;
}
#
# Find bonding interfaces using procfs (fallback, deprecated)
#
sub find_bonding_procfs {
my $procdir = '/proc/net/bonding';
my @bonds = ();
my %bonding = ();
opendir(my $DIR, $procdir);
@bonds = grep { m{\A bond\d+ \z}xms && -f "$procdir/$_" } readdir $DIR;
closedir $DIR;
if ($#bonds == -1) {
return {};
}
foreach my $b (@bonds) {
my $slave = undef;
open my $BOND, '<', "$procdir/$b"
or unknown_error("Couldn't open $procdir/$b: $!");
while (<$BOND>) {
# get bonding mode
if (m{\A Bonding \s Mode: \s (.+) \z}xms) {
chop($bonding{$b}{mode} = $1);
}
# get 802.3ad number of ports
elsif (defined $bonding{$b}{mode} and $bonding{$b}{mode} =~ m{802\.3ad}xms
and m{\A\s+ Number \s of \s ports: \s (\d+) .*\z}xms) {
chomp($bonding{$b}{ad_num} = $1);
}
# get slave
elsif (m{\A Slave \s Interface: \s (.+) \z}xms) {
chop($slave = $1);
}
# get slave and bonding status
elsif (m{\A MII \s Status: \s (.+) \z}xms) {
if (defined $slave) {
chop($bonding{$b}{slave}{$slave} = $1);
}
else {
chop($bonding{$b}{status} = $1);
}
}
# get primary slave
elsif (m{\A Primary \s Slave: \s (\S+) .* \z}xms) {
chomp($bonding{$b}{primary} = $1);
}
# get active slave
elsif (m{\A Currently \s Active \s Slave: \s (.+) \z}xms) {
chop($bonding{$b}{active} = $1);
}
}
}
return \%bonding;
}
#
# Find bonding interfaces
#
sub find_bonding {
my $bonding = undef;
if ($opt{disable_sysfs}) {
$bonding = find_bonding_procfs();
}
else {
# first try sysfs
$bonding = find_bonding_sysfs();
# second try procfs
if (scalar keys %{ $bonding } == 0) {
$bonding = find_bonding_procfs();
}
}
# if no bonding interfaces found, exit
if (scalar keys %{ $bonding } == 0) {
print $reverse_exitcode{$text2exit{$opt{no_bonding}}}
. ": No bonding interfaces found\n";
exit $text2exit{$opt{no_bonding}};
}
return $bonding;
}
#
# Returns true if an interface is blacklisted
#
sub blacklisted {
return 0 if !defined $opt{blacklist};
my $if = shift;
foreach $b (@blacklist) {
if ($if eq $b) {
return 1;
}
}
return 0;
}
#=====================================================================
# Main program
#=====================================================================
%bonding = %{ find_bonding() };
MASTER:
foreach my $b (sort keys %bonding) {
# If the master interface is blacklisted
if (blacklisted($b)) {
my $msg = sprintf 'Bonding interface %s [%s] is %s, but IGNORED',
$b, $bonding{$b}{mode}, $bonding{$b}{status};
report($msg, $E_OK);
next MASTER;
}
if ($bonding{$b}{status} ne 'up') {
my $msg = sprintf 'Bonding interface %s [%s] is %s',
$b, $bonding{$b}{mode}, $bonding{$b}{status};
report($msg, $E_CRITICAL);
}
else {
my $slaves_are_up = 1; # flag
SLAVE:
foreach my $i (sort keys %{ $bonding{$b}{slave} }) {
# If the slave interface is blacklisted
if (blacklisted($i)) {
my $msg = sprintf 'Slave interface %s [member of %s] is %s, but IGNORED',
$i, $b, $bonding{$b}{slave}{$i};
report($msg, $E_OK);
next SLAVE;
}
if ($bonding{$b}{slave}{$i} ne 'up') {
$slaves_are_up = 0; # not all slaves are up
my $msg = sprintf 'Bonding interface %s [%s]: Slave %s is %s',
$b, $bonding{$b}{mode}, $i, $bonding{$b}{slave}{$i};
report($msg, $text2exit{$opt{slave_down}});
}
}
if ($slaves_are_up) {
my %slave = map { $_ => q{} } keys %{ $bonding{$b}{slave} };
foreach my $s (keys %slave) {
if (defined $bonding{$b}{primary} and $bonding{$b}{primary} eq $s) {
$slave{$s} .= '*';
}
if (defined $bonding{$b}{active} and $bonding{$b}{active} eq $s) {
$slave{$s} .= '!';
}
}
if (scalar keys %slave == 1) {
my @slaves = keys %slave;
my $msg = sprintf 'Bonding interface %s [%s] has only one slave (%s)',
$b, $bonding{$b}{mode}, $slaves[0];
report($msg, $E_CRITICAL);
}
elsif (scalar keys %slave == 0) { # FIXME: does this ever happen?
my $msg = sprintf 'Bonding interface %s [%s] has zero slaves!',
$b, $bonding{$b}{mode};
report($msg, $E_WARNING);
}
elsif (defined $bonding{$b}{ad_num} and $bonding{$b}{ad_num} != scalar keys %slave
and $opt{ignore_num_ad} == 0) {
my $msg = sprintf 'Bonding interface %s [%s]: Number of AD ports (%d) does not equal the number of slaves (%d)',
$b, $bonding{$b}{mode}, $bonding{$b}{ad_num}, scalar keys %slave;
report($msg, $E_WARNING);
}
else {
my @slaves = map { $_ . $slave{$_} } sort keys %slave;
my $msg = sprintf 'Interface %s is %s: %s, %d slaves: %s',
$b, $bonding{$b}{status}, $bonding{$b}{mode},
scalar @slaves, join q{, }, @slaves;
report($msg, $E_OK);
}
}
}
}
# Counter variable
%nagios_level_count
= (
'OK' => 0,
'WARNING' => 0,
'CRITICAL' => 0,
'UNKNOWN' => 0,
);
# holds only ok messages
@ok_reports = ();
# Reset the WARN signal
$SIG{__WARN__} = 'DEFAULT';
# Print any perl warnings that have occured
if (@perl_warnings) {
foreach (@perl_warnings) {
chop @$_;
report("INTERNAL ERROR: @$_", $E_UNKNOWN);
}
}
$counter = 0;
ALERT:
foreach (sort {$a->[1] < $b->[1]} @reports) {
my ($msg, $level) = @{ $_ };
$nagios_level_count{$reverse_exitcode{$level}}++;
if ($level == $E_OK && !$opt{verbose}) {
push @ok_reports, $msg;
next ALERT;
}
# Prefix with nagios level if specified with option '--state'
$msg = $reverse_exitcode{$level} . ": $msg" if $opt{state};
# Prefix with one-letter nagios level if specified with option '--short-state'
$msg = (substr $reverse_exitcode{$level}, 0, 1) . ": $msg" if $opt{shortstate};
($counter++ == 0) ? print $msg : print $linebreak, $msg;
}
# Determine our exit code
$exit_code = $E_OK;
if ($nagios_level_count{UNKNOWN} > 0) { $exit_code = $E_UNKNOWN; }
if ($nagios_level_count{WARNING} > 0) { $exit_code = $E_WARNING; }
if ($nagios_level_count{CRITICAL} > 0) { $exit_code = $E_CRITICAL; }
# Print OK messages
$counter = 0;
if ($exit_code == $E_OK && !$opt{verbose}) {
foreach my $msg (@ok_reports) {
# Prefix with nagios level if specified with option '--state'
$msg = "OK: $msg" if $opt{state};
# Prefix with one-letter nagios level if specified with option '--short-state'
$msg = "O: $msg" if $opt{shortstate};
($counter++ == 0) ? print $msg : print $linebreak, $msg;
}
}
print "\n";
# Exit with proper exit code
exit $exit_code;