Annotation of nagios/check_openbgpd/check_openbgpd, Revision 1.1
1.1 ! andrew 1: #!/usr/bin/perl -T
! 2: # $RedRiver$
! 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:
! 15: my $NAGIOS_OUTPUT = 0;
! 16:
! 17: my $LICENSE = <<'EOL';
! 18: Copyright (c) 2009 Andrew Fresh <andrew@afresh1.com>
! 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:
! 59: my @STATUS = read_status();
! 60: my %STATES = check_status( \@STATUS, \%CHECKS );
! 61:
! 62: my $have_results = 0;
! 63: my $state = 'OK';
! 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 {
! 92: my ($status) = @_;
! 93: my @S;
! 94:
! 95: #open my $fh, '<', 'output' # XXX
! 96: open my $fh , '-|', $BGPCTL, 'show', 'summary'
! 97: or die "Couldn't open bgpctl: $!\n";
! 98: while (<$fh>) {
! 99: chomp;
! 100: push @S, parse_line($_);
! 101: }
! 102: ## no critic 'die'
! 103: close $fh
! 104: or die $!
! 105: ? "Error closing sysctl pipe: $!\n"
! 106: : "Exit status $? from sysctl\n";
! 107:
! 108: return grep { exists $_->{neighbor} && $_->{as} ne 'AS' } @S;
! 109: }
! 110:
! 111: sub parse_line {
! 112: my ($c) = @_;
! 113: my ( $neighbor, $as, $rcvd, $sent, $outq, $updown, $state, )
! 114: = $c
! 115: =~ /^(.*?)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/xms;
! 116: return {
! 117: neighbor => $neighbor,
! 118: as => $as,
! 119: rcvd => $rcvd,
! 120: sent => $sent,
! 121: outq => $outq,
! 122: updown => $updown,
! 123: state => $state,
! 124: line => $c,
! 125: };
! 126: }
! 127:
! 128: sub parse_check {
! 129: my $check = shift;
! 130:
! 131: return { match => [] } unless $check;
! 132: my @values = split /,\s*/xms, $check;
! 133:
! 134: my %c = ( match => [] );
! 135: foreach my $v (@values) {
! 136: if ( $v =~ /:/xms ) {
! 137: ( $c{low}, $c{high} ) = split /:/xms, $v;
! 138: }
! 139: else {
! 140: push @{ $c{match} }, $v;
! 141: }
! 142: }
! 143:
! 144: foreach my $d ( 'low', 'high' ) {
! 145: if ( defined $c{$d} ) {
! 146: $c{$d} =~ s/[^-\d\.]//gxms;
! 147: if ( !length $c{$d} ) {
! 148: delete $c{$d};
! 149: }
! 150: }
! 151: }
! 152:
! 153: return \%c;
! 154: }
! 155:
! 156: sub check_status {
! 157: my ( $S, $C ) = @_;
! 158:
! 159: my %states;
! 160: my %neighbors;
! 161: STATE: foreach my $s ( @{$S} ) {
! 162: my $n = $s->{neighbor};
! 163: $neighbors{$n} = $s;
! 164:
! 165: my $result;
! 166:
! 167: if ( my $c = $C->{$n} ) {
! 168: CODE: foreach my $code ( 'CRITICAL', 'WARNING' ) {
! 169: next CODE if ( ref $c->{$code} ne 'HASH' );
! 170: my $data = $s->{state};
! 171:
! 172: my $result = check_item( $data, $c->{$code} );
! 173:
! 174: if ($result) {
! 175: push @{ $states{$code} }, "[$n] $result";
! 176: next STATE;
! 177: }
! 178: }
! 179: }
! 180: else {
! 181: push @{ $states{CRITICAL} }, '[' . $n . '] Unknown Neighbor';
! 182: next STATE;
! 183: }
! 184:
! 185: push @{ $states{OK} }, $n;
! 186: }
! 187:
! 188: foreach my $n ( keys %{$C} ) {
! 189: if ( !exists $neighbors{$n} ) {
! 190: push @{ $states{CRITICAL} }, '[' . $n . '] Missing Neighbor';
! 191: }
! 192: }
! 193:
! 194: return %states;
! 195: }
! 196:
! 197: sub check_item {
! 198: my ( $d, $c ) = @_;
! 199:
! 200: my $result;
! 201:
! 202: if ( $c->{match} && @{ $c->{match} } ) {
! 203: foreach my $m ( @{ $c->{match} } ) {
! 204: return if $m eq $d;
! 205: }
! 206: $result = 'State (' . $d . ') is outside of acceptable values';
! 207: }
! 208:
! 209: if ( $c->{low} || $c->{high} ) {
! 210: $result = undef;
! 211: my $num = $d;
! 212: $num =~ s/[^-\d\.]//gxms;
! 213:
! 214: if ( !length $num ) {
! 215: return 'State (' . $d . ') is not numeric';
! 216: }
! 217:
! 218: if ( $c->{low} && $num < $c->{low} ) {
! 219: return 'is below threshold (' . $d . ' < ' . $c->{low} . ')';
! 220: }
! 221:
! 222: if ( $c->{high} && $num > $c->{high} ) {
! 223: return 'is above threshold (' . $d . ' > ' . $c->{high} . ')';
! 224: }
! 225: }
! 226:
! 227: return $result;
! 228: }
! 229:
! 230: sub getopt {
! 231: my (@argv) = @_;
! 232:
! 233: my %checks;
! 234: while (@argv) {
! 235: state( $w, $c );
! 236:
! 237: my $opt = shift @argv;
! 238: given ($opt) {
! 239: when ( '-V' || '--version' ) {
! 240: print_revision( $PROGNAME, '$Revision: 1.10 $ ' );
! 241: exit $ERRORS{'OK'}
! 242: }
! 243: when ( /^-?-h(?:elp)?/xms ) { print_help(); exit $ERRORS{'OK'} }
! 244: when ( /^-?-w(?:arning)?/xms ) { $w = parse_check( shift @argv ) }
! 245: when ( /^-?-c(?:ritical)?/xms ) { $c = parse_check( shift @argv ) }
! 246: when ( /^-?-n(?:eighbor)?/xms ) {
! 247: while ( @argv && $argv[0] !~ /^-/xms ) {
! 248: $checks{ shift @argv } = {
! 249: WARNING => $w,
! 250: CRITICAL => $c
! 251: }
! 252: }
! 253: }
! 254: default { print_help(); exit $ERRORS{'UNKNOWN'} }
! 255: }
! 256: }
! 257: return %checks;
! 258: }
! 259:
! 260: sub print_help {
! 261: print <<"EOL";
! 262: $PROGNAME - monitors sysctl hw.bgpstatus on OpenBSD
! 263: $PROGNAME [ -w ENTRY ][ -c ENTRY ][ -n NEIGHBOR [ NEIGHBOR2 ] ]
! 264:
! 265: Usage:
! 266: -w, --warning RANGE or single ENTRY
! 267: Exit with WARNING status if outside of RANGE or if != ENTRY
! 268: -c, --critical RANGE or single ENTRY
! 269: Exit with CRITICAL status if outside of RANGE or if != ENTRY
! 270: -n, --neighbor NEIGHBOR
! 271: The name of the Neighbor
! 272:
! 273: ENTRY is a comma separated list of items to match against. Each item can be
! 274: a RANGE or it will just be matched against the status.
! 275:
! 276: RANGE is specified as two optional numbers separated with a colon (:). The
! 277: check is that the value is between the two numbers. If either number is left
! 278: off, that check is ignored
! 279:
! 280: NEIGHBOR is the name that shows when running "bgpctl show summary"
! 281:
! 282: Examples:
! 283:
! 284: $PROGNAME -w 10000:300000 -c 1000:500000 -n eBGP1 eBGP2 -n eBGP3
! 285:
! 286: Checks that peers eBGP1, eBGP2 and eBGP2 are within the ranges specified
! 287:
! 288: $PROGNAME -c 1:1 -n eBGP1 -w 200000:300000 -c 100: -n iBGP1
! 289:
! 290: Checks that 1 and only 1 prefix is received from from eBGP1, warns if less
! 291: than 200000 or more than 300000 prefixes are recieved from peer iBGP1 and is
! 292: critical if less than 100 prefixes are recieved from peer iBGP1
! 293:
! 294: $PROGNAME -c Idle -n iBGP1 -w
! 295:
! 296: EOL
! 297:
! 298: print_revision( $PROGNAME, '$Revision: 1.40 $' );
! 299:
! 300: print $LICENSE;
! 301:
! 302: return;
! 303: }
! 304:
! 305: sub print_revision {
! 306: my ( $prog, $rev ) = @_;
! 307: $rev =~ s/^\D+([\d\.]+)\D+$/v$1/xms;
! 308:
! 309: say $prog, q{ }, $rev;
! 310:
! 311: return;
! 312: }
! 313:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>