[BACK]Return to check_openbgpd CVS log [TXT][DIR] Up to [local] / nagios / check_openbgpd

Annotation of nagios/check_openbgpd/check_openbgpd, Revision 1.12

1.1       andrew      1: #!/usr/bin/perl -T
1.12    ! afresh1     2: # $AFresh1: check_openbgpd,v 1.11 2024/06/09 22:34:58 afresh1 Exp $
1.1       andrew      3: ########################################################################
                      4: # check_openbgpd *** A nagios check for OpenBSD bgpd
                      5: #
                      6: # 2009.11.12 #*#*# andrew fresh <andrew@afresh1.com>
                      7: ########################################################################
                      8: use strict;
                      9: use warnings;
                     10:
                     11: use 5.010;
1.9       andrew     12: use if $] >= 5.016, experimental => 'switch';
1.1       andrew     13:
                     14: local %ENV = ();
                     15:
1.2       andrew     16: my $NAGIOS_OUTPUT = 1;
1.1       andrew     17:
                     18: my $LICENSE = <<'EOL';
1.7       andrew     19: Copyright (c) 2009-2015 Andrew Fresh <andrew@afresh1.com>
1.1       andrew     20: Permission to use, copy, modify, and distribute this software for any
                     21: purpose with or without fee is hereby granted, provided that the above
                     22: copyright notice and this permission notice appear in all copies.
                     23:
                     24: THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     25: WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     26: MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     27: ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     28: WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     29: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     30: OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     31: EOL
                     32:
                     33: my $PROGNAME = 'check_openbgpd';
                     34: my $BGPCTL   = '/usr/sbin/bgpctl';
                     35:
                     36: use POSIX;
                     37: use Config;
                     38: my $PREFIX;
                     39:
                     40: BEGIN {
                     41:     ## no critic 'warnings'
                     42:     no warnings 'uninitialized';
                     43:     $PREFIX = "${PREFIX}" || '/usr/local';    # Magic for OpenBSD ports tree
                     44: }
                     45: use lib $PREFIX . '/libexec/nagios';
                     46: use utils qw($TIMEOUT %ERRORS &support);
                     47:
                     48: $SIG{'ALRM'} = sub {
                     49:     print("ERROR: $PROGNAME timeout\n");
                     50:     exit $ERRORS{'UNKNOWN'};
                     51: };
                     52: alarm($TIMEOUT);
                     53:
                     54: my %CHECKS = getopt(@ARGV);
                     55: if ( !%CHECKS ) {
                     56:     print_help();
                     57:     exit $ERRORS{'OK'};
                     58: }
                     59:
1.5       andrew     60: my @STATUS = read_status( $CHECKS{_SOCKET} );
1.1       andrew     61: my %STATES = check_status( \@STATUS, \%CHECKS );
                     62:
                     63: my $have_results = 0;
1.2       andrew     64: my $state        = 'OK';
1.1       andrew     65: foreach
                     66:     my $error ( reverse sort { $ERRORS{$a} <=> $ERRORS{$b} } keys %ERRORS )
                     67: {
                     68:     if ( exists $STATES{$error} ) {
                     69:         $have_results++;
                     70:         $state = $error if $ERRORS{$state} < $ERRORS{$error};
                     71:
                     72:         if ($NAGIOS_OUTPUT) {
                     73:             print $error . ' (' . scalar( @{ $STATES{$error} } ) . ')';
                     74:             if ( $error ne 'OK' ) {
                     75:                 print '<br>';
                     76:                 print map {" - $_<br>"} @{ $STATES{$error} };
                     77:             }
                     78:         }
                     79:         else {
                     80:             print $error . ' (' . scalar( @{ $STATES{$error} } ) . "):\n";
                     81:             foreach ( @{ $STATES{$error} } ) {
                     82:                 print "   $_\n";
                     83:             }
                     84:         }
                     85:     }
                     86: }
                     87: if ( $have_results == 0 ) {
                     88:     print "No results found\n";
                     89: }
                     90: exit $ERRORS{$state};
                     91:
                     92: sub read_status {
1.5       andrew     93:     my ($socket) = @_;
1.1       andrew     94:     my @S;
                     95:
1.5       andrew     96:     my @cmd = ($BGPCTL);
                     97:     if ($socket) {
                     98:         push @cmd, '-s', $socket;
                     99:     }
                    100:     push @cmd, 'show', 'summary';
                    101:
1.12    ! afresh1   102:     #open my $fh, '<', 'output' or die $!; # XXX
1.5       andrew    103:     open my $fh, '-|', @cmd or die "Couldn't open bgpctl: $!\n";
1.1       andrew    104:     while (<$fh>) {
                    105:         chomp;
                    106:         push @S, parse_line($_);
                    107:     }
                    108:     ## no critic 'die'
                    109:     close $fh
                    110:         or die $!
                    111:         ? "Error closing sysctl pipe: $!\n"
                    112:         : "Exit status $? from sysctl\n";
                    113:
                    114:     return grep { exists $_->{neighbor} && $_->{as} ne 'AS' } @S;
                    115: }
                    116:
                    117: sub parse_line {
                    118:     my ($c) = @_;
                    119:     my ( $neighbor, $as, $rcvd, $sent, $outq, $updown, $state, )
                    120:         = $c
                    121:         =~ /^(.*?)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/xms;
                    122:     return {
                    123:         neighbor => $neighbor,
                    124:         as       => $as,
                    125:         rcvd     => $rcvd,
                    126:         sent     => $sent,
                    127:         outq     => $outq,
                    128:         updown   => $updown,
                    129:         state    => $state,
                    130:         line     => $c,
                    131:     };
                    132: }
                    133:
                    134: sub parse_check {
                    135:     my $check = shift;
                    136:
1.11      afresh1   137:     unless ($check) {
                    138:         print_help();
                    139:         exit $ERRORS{'OK'}
                    140:     }
                    141:
1.1       andrew    142:     my @values = split /,\s*/xms, $check;
                    143:
                    144:     my %c = ( match => [] );
                    145:     foreach my $v (@values) {
                    146:         if ( $v =~ /:/xms ) {
                    147:             ( $c{low}, $c{high} ) = split /:/xms, $v;
                    148:         }
                    149:         else {
                    150:             push @{ $c{match} }, $v;
                    151:         }
                    152:     }
                    153:
                    154:     foreach my $d ( 'low', 'high' ) {
                    155:         if ( defined $c{$d} ) {
1.3       andrew    156:             $c{$d} =~ s/[^-\d\.\%]//gxms;
1.1       andrew    157:             if ( !length $c{$d} ) {
                    158:                 delete $c{$d};
                    159:             }
                    160:         }
                    161:     }
                    162:
                    163:     return \%c;
                    164: }
                    165:
                    166: sub check_status {
                    167:     my ( $S, $C ) = @_;
                    168:
                    169:     my %states;
1.5       andrew    170:     my %neighbors = map { $_ => $C->{$_} } qw( _SOCKET _UNKNOWN );
1.1       andrew    171: STATE: foreach my $s ( @{$S} ) {
                    172:         my $n = $s->{neighbor};
                    173:         $neighbors{$n} = $s;
                    174:
                    175:         my $result;
                    176:
1.5       andrew    177:         if ( my $c = $C->{$n} || $C->{_UNKNOWN} ) {
1.1       andrew    178:         CODE: foreach my $code ( 'CRITICAL', 'WARNING' ) {
                    179:                 next CODE if ( ref $c->{$code} ne 'HASH' );
                    180:                 my $data = $s->{state};
                    181:
                    182:                 my $result = check_item( $data, $c->{$code} );
                    183:
                    184:                 if ($result) {
                    185:                     push @{ $states{$code} }, "[$n] $result";
                    186:                     next STATE;
                    187:                 }
                    188:             }
                    189:         }
                    190:         else {
                    191:             push @{ $states{CRITICAL} }, '[' . $n . '] Unknown Neighbor';
                    192:             next STATE;
                    193:         }
                    194:
                    195:         push @{ $states{OK} }, $n;
                    196:     }
                    197:
                    198:     foreach my $n ( keys %{$C} ) {
                    199:         if ( !exists $neighbors{$n} ) {
                    200:             push @{ $states{CRITICAL} }, '[' . $n . '] Missing Neighbor';
                    201:         }
                    202:     }
                    203:
                    204:     return %states;
                    205: }
                    206:
                    207: sub check_item {
                    208:     my ( $d, $c ) = @_;
                    209:
                    210:     my $result;
                    211:
                    212:     if ( $c->{match} && @{ $c->{match} } ) {
                    213:         foreach my $m ( @{ $c->{match} } ) {
                    214:             return if $m eq $d;
                    215:         }
                    216:         $result = 'State (' . $d . ') is outside of acceptable values';
                    217:     }
                    218:
                    219:     if ( $c->{low} || $c->{high} ) {
                    220:         $result = undef;
1.3       andrew    221:         my ( $num, $max ) = split m{/}xms, $d;
1.1       andrew    222:         $num =~ s/[^-\d\.]//gxms;
                    223:
                    224:         if ( !length $num ) {
                    225:             return 'State (' . $d . ') is not numeric';
                    226:         }
                    227:
1.6       andrew    228:     DIRECTION: foreach my $dir (qw( low high )) {
1.3       andrew    229:             if ( !$c->{$dir} ) { next DIRECTION; }
                    230:
                    231:             my $check = $c->{$dir};
                    232:             my $cnum  = $num;
                    233:
                    234:             if ( $check =~ s/\%$//xms ) {
                    235:                 if ( !defined $max ) {
                    236:                     return 'max-prefix not specified and % check requested';
                    237:                 }
                    238:
                    239:                 # convert to percent
                    240:                 $cnum = 100 * $cnum / $max;
                    241:             }
                    242:
                    243:             my @nums       = ( $cnum, $check );
                    244:             my $abovebelow = 'below';
                    245:             my $symbol     = '<';
                    246:             if ( $dir eq 'high' ) {
                    247:                 @nums       = ( $check, $cnum );
                    248:                 $abovebelow = 'above';
                    249:                 $symbol     = '>';
                    250:             }
1.1       andrew    251:
1.3       andrew    252:             if ( $nums[0] < $nums[1] ) {
                    253:                 return join q{ }, 'is', $abovebelow,
                    254:                     'threshold (' . $d,
                    255:                     $symbol, $c->{$dir} . ')';
                    256:             }
1.1       andrew    257:         }
                    258:     }
                    259:
                    260:     return $result;
                    261: }
                    262:
                    263: sub getopt {
                    264:     my (@argv) = @_;
                    265:
                    266:     my %checks;
                    267:     while (@argv) {
                    268:         state( $w, $c );
                    269:
                    270:         my $opt = shift @argv;
1.9       andrew    271:         for ($opt) {
1.11      afresh1   272:             if ( $_ eq '-V' || $_ eq '--version' ) {
1.12    ! afresh1   273:                 print_revision( $PROGNAME, '$Revision: 1.11 $ ' );
1.1       andrew    274:                 exit $ERRORS{'OK'}
                    275:             }
1.11      afresh1   276:             elsif (/^-?-h(?:elp)?\Z/xms) { print_help(); exit $ERRORS{'OK'} }
                    277:             elsif (/^-?-s(?:ocket)?\Z/xms) {
                    278:                 $checks{_SOCKET} = shift @argv
                    279:                     || do { print_help(); exit $ERRORS{'OK'} }
                    280:             }
                    281:             elsif (/^-?-w(?:arning)?\Z/xms)  { $w = parse_check( shift @argv ) }
                    282:             elsif (/^-?-c(?:ritical)?\Z/xms) { $c = parse_check( shift @argv ) }
                    283:             elsif (/^-?-u(?:nknown)?\Z/xms) {
1.5       andrew    284:                 $checks{_UNKNOWN} = {
1.10      andrew    285:                     WARNING  => $w,
                    286:                     CRITICAL => $c,
                    287:                 };
1.4       andrew    288:             }
1.11      afresh1   289:             elsif (/^-?-n(?:eighbor)?\Z/xms) {
1.1       andrew    290:                 while ( @argv && $argv[0] !~ /^-/xms ) {
                    291:                     $checks{ shift @argv } = {
1.10      andrew    292:                         WARNING  => $w,
                    293:                         CRITICAL => $c,
                    294:                     };
1.1       andrew    295:                 }
                    296:             }
1.11      afresh1   297:             else { print_help(); exit $ERRORS{'UNKNOWN'} }
1.1       andrew    298:         }
                    299:     }
                    300:     return %checks;
                    301: }
                    302:
                    303: sub print_help {
                    304:     print <<"EOL";
1.2       andrew    305: $PROGNAME - checks status of OpenBGPd peers
1.5       andrew    306:     $PROGNAME [ -s SOCKET ][ -w ENTRY ][ -c ENTRY ]( -u | -n NEIGHBOR )
1.1       andrew    307:
                    308: Usage:
1.5       andrew    309:     -s, --socket SOCKET
                    310:         Path to bgpd socket to use. See -r in bgpd(8).
1.1       andrew    311:     -w, --warning RANGE or single ENTRY
                    312:         Exit with WARNING status if outside of RANGE or if != ENTRY
1.4       andrew    313:         May be entered multiple times.
1.1       andrew    314:     -c, --critical RANGE or single ENTRY
                    315:         Exit with CRITICAL status if outside of RANGE or if != ENTRY
1.4       andrew    316:         May be entered multiple times.
1.1       andrew    317:     -n, --neighbor NEIGHBOR
1.4       andrew    318:         The name of the Neighbor, can be a space separated list of neighbors.
                    319:         May be entered multiple times.
                    320:     -u, --unknown
                    321:         As if you specified -n for all unknown neighbors
1.1       andrew    322:
                    323: ENTRY is a comma separated list of items to match against.  Each item can be
                    324: a RANGE or it will just be matched against the status.
                    325:
                    326: RANGE is specified as two optional numbers separated with a colon (:).  The
                    327: check is that the value is between the two numbers.  If either number is left
1.3       andrew    328: off, that check is ignored.
                    329:
                    330: If either number in a RANGE is specified as a percent, check is that
                    331: max-prefix is specified and that the number is within the specified percent.
1.1       andrew    332:
                    333: NEIGHBOR is the name that shows when running "bgpctl show summary"
                    334:
                    335: Examples:
1.2       andrew    336: (where many of the numbers would probably have to be multiplied by 1000)
1.1       andrew    337:
1.2       andrew    338: Any time a NEIGHBOR is specified on the command line but does NOT show up in
                    339: the output causes a CRITICAL result.
1.1       andrew    340:
1.2       andrew    341: Any time a NEIGHBOR that is NOT specified on the command line shows up in the
1.4       andrew    342: output causes a CRITICAL result.  If -u is specified, it treats NEIGHBOR as if
                    343: it were specified at that position.
1.2       andrew    344:
                    345:
                    346: $PROGNAME -c Idle -n P1 -c 1:1 -n P2 -w 200:300 -c Active,10: -n P3
                    347:
                    348: CRITICAL
                    349:     If P1 is any value but Idle.
                    350:     If P2 is any value but 1.
                    351:     If P3 is below 10 or any non-numeric value other than "Active".
                    352:
                    353: WARNING
                    354:     If P3 is above 10 and below 200 or above 300.
1.1       andrew    355:
1.3       andrew    356:
1.4       andrew    357: $PROGNAME -u -w 50%:70% -c 10%:90% -n P2 P3
1.3       andrew    358:
1.4       andrew    359: No checks of unknown neighbors.
1.3       andrew    360:
                    361: CRITICAL
                    362:     If P2 or P3 do not have max-prefix set or if they do but learned prefixes
1.4       andrew    363:     are below 10% or above 90% of max-prefix or any non-numeric value.
                    364:
                    365: WARNING
                    366:     If P2 or P3 have learned prefixes below 50% or above 70% of max-prefix.
                    367:
                    368:
                    369: $PROGNAME -w 50%:70% -c 10%:90% -u
                    370:
                    371: CRITICAL
                    372:     If any neighbor does not have max-prefix set or if they do but learned
                    373:     prefixes are below 10% or above 90% of max-prefix or any non-numeric value.
1.3       andrew    374:
                    375: WARNING
1.4       andrew    376:     If any neighbor have learned prefixes below 50% or above 70% of max-prefix.
1.3       andrew    377:
1.1       andrew    378: EOL
                    379:
1.12    ! afresh1   380:     print_revision( $PROGNAME, '$Revision: 1.11 $' );
1.1       andrew    381:
                    382:     print $LICENSE;
                    383:
                    384:     return;
                    385: }
                    386:
                    387: sub print_revision {
                    388:     my ( $prog, $rev ) = @_;
                    389:     $rev =~ s/^\D+([\d\.]+)\D+$/v$1/xms;
                    390:
                    391:     say $prog, q{ }, $rev;
                    392:
                    393:     return;
                    394: }
                    395:

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>