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

Annotation of nagios/check_openbgpd/check_openbgpd, Revision 1.10

1.1       andrew      1: #!/usr/bin/perl -T
1.10    ! andrew      2: # $AFresh1: check_openbgpd,v 1.9 2015/03/26 03:42:23 andrew 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.1       andrew    102:     #open my $fh, '<', 'output'    # 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:
                    137:     return { match => [] } unless $check;
                    138:     my @values = split /,\s*/xms, $check;
                    139:
                    140:     my %c = ( match => [] );
                    141:     foreach my $v (@values) {
                    142:         if ( $v =~ /:/xms ) {
                    143:             ( $c{low}, $c{high} ) = split /:/xms, $v;
                    144:         }
                    145:         else {
                    146:             push @{ $c{match} }, $v;
                    147:         }
                    148:     }
                    149:
                    150:     foreach my $d ( 'low', 'high' ) {
                    151:         if ( defined $c{$d} ) {
1.3       andrew    152:             $c{$d} =~ s/[^-\d\.\%]//gxms;
1.1       andrew    153:             if ( !length $c{$d} ) {
                    154:                 delete $c{$d};
                    155:             }
                    156:         }
                    157:     }
                    158:
                    159:     return \%c;
                    160: }
                    161:
                    162: sub check_status {
                    163:     my ( $S, $C ) = @_;
                    164:
                    165:     my %states;
1.5       andrew    166:     my %neighbors = map { $_ => $C->{$_} } qw( _SOCKET _UNKNOWN );
1.1       andrew    167: STATE: foreach my $s ( @{$S} ) {
                    168:         my $n = $s->{neighbor};
                    169:         $neighbors{$n} = $s;
                    170:
                    171:         my $result;
                    172:
1.5       andrew    173:         if ( my $c = $C->{$n} || $C->{_UNKNOWN} ) {
1.1       andrew    174:         CODE: foreach my $code ( 'CRITICAL', 'WARNING' ) {
                    175:                 next CODE if ( ref $c->{$code} ne 'HASH' );
                    176:                 my $data = $s->{state};
                    177:
                    178:                 my $result = check_item( $data, $c->{$code} );
                    179:
                    180:                 if ($result) {
                    181:                     push @{ $states{$code} }, "[$n] $result";
                    182:                     next STATE;
                    183:                 }
                    184:             }
                    185:         }
                    186:         else {
                    187:             push @{ $states{CRITICAL} }, '[' . $n . '] Unknown Neighbor';
                    188:             next STATE;
                    189:         }
                    190:
                    191:         push @{ $states{OK} }, $n;
                    192:     }
                    193:
                    194:     foreach my $n ( keys %{$C} ) {
                    195:         if ( !exists $neighbors{$n} ) {
                    196:             push @{ $states{CRITICAL} }, '[' . $n . '] Missing Neighbor';
                    197:         }
                    198:     }
                    199:
                    200:     return %states;
                    201: }
                    202:
                    203: sub check_item {
                    204:     my ( $d, $c ) = @_;
                    205:
                    206:     my $result;
                    207:
                    208:     if ( $c->{match} && @{ $c->{match} } ) {
                    209:         foreach my $m ( @{ $c->{match} } ) {
                    210:             return if $m eq $d;
                    211:         }
                    212:         $result = 'State (' . $d . ') is outside of acceptable values';
                    213:     }
                    214:
                    215:     if ( $c->{low} || $c->{high} ) {
                    216:         $result = undef;
1.3       andrew    217:         my ( $num, $max ) = split m{/}xms, $d;
1.1       andrew    218:         $num =~ s/[^-\d\.]//gxms;
                    219:
                    220:         if ( !length $num ) {
                    221:             return 'State (' . $d . ') is not numeric';
                    222:         }
                    223:
1.6       andrew    224:     DIRECTION: foreach my $dir (qw( low high )) {
1.3       andrew    225:             if ( !$c->{$dir} ) { next DIRECTION; }
                    226:
                    227:             my $check = $c->{$dir};
                    228:             my $cnum  = $num;
                    229:
                    230:             if ( $check =~ s/\%$//xms ) {
                    231:                 if ( !defined $max ) {
                    232:                     return 'max-prefix not specified and % check requested';
                    233:                 }
                    234:
                    235:                 # convert to percent
                    236:                 $cnum = 100 * $cnum / $max;
                    237:             }
                    238:
                    239:             my @nums       = ( $cnum, $check );
                    240:             my $abovebelow = 'below';
                    241:             my $symbol     = '<';
                    242:             if ( $dir eq 'high' ) {
                    243:                 @nums       = ( $check, $cnum );
                    244:                 $abovebelow = 'above';
                    245:                 $symbol     = '>';
                    246:             }
1.1       andrew    247:
1.3       andrew    248:             if ( $nums[0] < $nums[1] ) {
                    249:                 return join q{ }, 'is', $abovebelow,
                    250:                     'threshold (' . $d,
                    251:                     $symbol, $c->{$dir} . ')';
                    252:             }
1.1       andrew    253:         }
                    254:     }
                    255:
                    256:     return $result;
                    257: }
                    258:
                    259: sub getopt {
                    260:     my (@argv) = @_;
                    261:
                    262:     my %checks;
                    263:     while (@argv) {
                    264:         state( $w, $c );
                    265:
                    266:         my $opt = shift @argv;
1.9       andrew    267:         for ($opt) {
1.1       andrew    268:             when ( '-V' || '--version' ) {
1.10    ! andrew    269:                 print_revision( $PROGNAME, '$Revision: 1.9 $ ' );
1.1       andrew    270:                 exit $ERRORS{'OK'}
                    271:             }
1.4       andrew    272:             when (/^-?-h(?:elp)?/xms) { print_help(); exit $ERRORS{'OK'} }
1.5       andrew    273:             when (/^-?-s(?:ocket)?/xms) { $checks{_SOCKET} = shift @argv }
1.2       andrew    274:             when (/^-?-w(?:arning)?/xms)  { $w = parse_check( shift @argv ) }
                    275:             when (/^-?-c(?:ritical)?/xms) { $c = parse_check( shift @argv ) }
1.4       andrew    276:             when (/^-?-u(?:nknown)?/xms) {
1.5       andrew    277:                 $checks{_UNKNOWN} = {
1.10    ! andrew    278:                     WARNING  => $w,
        !           279:                     CRITICAL => $c,
        !           280:                 };
1.4       andrew    281:             }
1.2       andrew    282:             when (/^-?-n(?:eighbor)?/xms) {
1.1       andrew    283:                 while ( @argv && $argv[0] !~ /^-/xms ) {
                    284:                     $checks{ shift @argv } = {
1.10    ! andrew    285:                         WARNING  => $w,
        !           286:                         CRITICAL => $c,
        !           287:                     };
1.1       andrew    288:                 }
                    289:             }
                    290:             default { print_help(); exit $ERRORS{'UNKNOWN'} }
                    291:         }
                    292:     }
                    293:     return %checks;
                    294: }
                    295:
                    296: sub print_help {
                    297:     print <<"EOL";
1.2       andrew    298: $PROGNAME - checks status of OpenBGPd peers
1.5       andrew    299:     $PROGNAME [ -s SOCKET ][ -w ENTRY ][ -c ENTRY ]( -u | -n NEIGHBOR )
1.1       andrew    300:
                    301: Usage:
1.5       andrew    302:     -s, --socket SOCKET
                    303:         Path to bgpd socket to use. See -r in bgpd(8).
1.1       andrew    304:     -w, --warning RANGE or single ENTRY
                    305:         Exit with WARNING status if outside of RANGE or if != ENTRY
1.4       andrew    306:         May be entered multiple times.
1.1       andrew    307:     -c, --critical RANGE or single ENTRY
                    308:         Exit with CRITICAL status if outside of RANGE or if != ENTRY
1.4       andrew    309:         May be entered multiple times.
1.1       andrew    310:     -n, --neighbor NEIGHBOR
1.4       andrew    311:         The name of the Neighbor, can be a space separated list of neighbors.
                    312:         May be entered multiple times.
                    313:     -u, --unknown
                    314:         As if you specified -n for all unknown neighbors
1.1       andrew    315:
                    316: ENTRY is a comma separated list of items to match against.  Each item can be
                    317: a RANGE or it will just be matched against the status.
                    318:
                    319: RANGE is specified as two optional numbers separated with a colon (:).  The
                    320: check is that the value is between the two numbers.  If either number is left
1.3       andrew    321: off, that check is ignored.
                    322:
                    323: If either number in a RANGE is specified as a percent, check is that
                    324: max-prefix is specified and that the number is within the specified percent.
1.1       andrew    325:
                    326: NEIGHBOR is the name that shows when running "bgpctl show summary"
                    327:
                    328: Examples:
1.2       andrew    329: (where many of the numbers would probably have to be multiplied by 1000)
1.1       andrew    330:
1.2       andrew    331: Any time a NEIGHBOR is specified on the command line but does NOT show up in
                    332: the output causes a CRITICAL result.
1.1       andrew    333:
1.2       andrew    334: Any time a NEIGHBOR that is NOT specified on the command line shows up in the
1.4       andrew    335: output causes a CRITICAL result.  If -u is specified, it treats NEIGHBOR as if
                    336: it were specified at that position.
1.2       andrew    337:
                    338:
                    339: $PROGNAME -c Idle -n P1 -c 1:1 -n P2 -w 200:300 -c Active,10: -n P3
                    340:
                    341: CRITICAL
                    342:     If P1 is any value but Idle.
                    343:     If P2 is any value but 1.
                    344:     If P3 is below 10 or any non-numeric value other than "Active".
                    345:
                    346: WARNING
                    347:     If P3 is above 10 and below 200 or above 300.
1.1       andrew    348:
1.3       andrew    349:
1.4       andrew    350: $PROGNAME -u -w 50%:70% -c 10%:90% -n P2 P3
1.3       andrew    351:
1.4       andrew    352: No checks of unknown neighbors.
1.3       andrew    353:
                    354: CRITICAL
                    355:     If P2 or P3 do not have max-prefix set or if they do but learned prefixes
1.4       andrew    356:     are below 10% or above 90% of max-prefix or any non-numeric value.
                    357:
                    358: WARNING
                    359:     If P2 or P3 have learned prefixes below 50% or above 70% of max-prefix.
                    360:
                    361:
                    362: $PROGNAME -w 50%:70% -c 10%:90% -u
                    363:
                    364: CRITICAL
                    365:     If any neighbor does not have max-prefix set or if they do but learned
                    366:     prefixes are below 10% or above 90% of max-prefix or any non-numeric value.
1.3       andrew    367:
                    368: WARNING
1.4       andrew    369:     If any neighbor have learned prefixes below 50% or above 70% of max-prefix.
1.3       andrew    370:
1.1       andrew    371: EOL
                    372:
1.10    ! andrew    373:     print_revision( $PROGNAME, '$Revision: 1.9 $' );
1.1       andrew    374:
                    375:     print $LICENSE;
                    376:
                    377:     return;
                    378: }
                    379:
                    380: sub print_revision {
                    381:     my ( $prog, $rev ) = @_;
                    382:     $rev =~ s/^\D+([\d\.]+)\D+$/v$1/xms;
                    383:
                    384:     say $prog, q{ }, $rev;
                    385:
                    386:     return;
                    387: }
                    388:

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