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

Annotation of nagios/check_openbgpd/check_openbgpd, Revision 1.8

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

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