����JFIF��x�x����'
Server IP : 78.140.185.180 / Your IP : 3.149.249.124 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 : /usr/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;