Annotation of nagios/check_openbgpd/check_openbgpd, Revision 1.2
1.1 andrew 1: #!/usr/bin/perl -T
1.2 ! andrew 2: # $RedRiver: check_openbgpd,v 1.1 2009/11/13 02:05:20 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;
12:
13: local %ENV = ();
14:
1.2 ! andrew 15: my $NAGIOS_OUTPUT = 1;
1.1 andrew 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;
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 {
92: my ($status) = @_;
93: my @S;
94:
95: #open my $fh, '<', 'output' # XXX
1.2 ! andrew 96: open my $fh, '-|', $BGPCTL, 'show', 'summary'
1.1 andrew 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' ) {
1.2 ! andrew 240: print_revision( $PROGNAME, '$Revision: 1.1 $ ' );
1.1 andrew 241: exit $ERRORS{'OK'}
242: }
1.2 ! andrew 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) {
1.1 andrew 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";
1.2 ! andrew 262: $PROGNAME - checks status of OpenBGPd peers
1.1 andrew 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:
1.2 ! andrew 283: (where many of the numbers would probably have to be multiplied by 1000)
1.1 andrew 284:
1.2 ! andrew 285: Any time a NEIGHBOR is specified on the command line but does NOT show up in
! 286: the output causes a CRITICAL result.
1.1 andrew 287:
1.2 ! andrew 288: Any time a NEIGHBOR that is NOT specified on the command line shows up in the
! 289: output causes a CRITICAL result.
1.1 andrew 290:
291:
1.2 ! andrew 292: $PROGNAME -w 10:300 -c 10:500 -n P1 P2 -n P3
1.1 andrew 293:
1.2 ! andrew 294: CRITICAL
! 295: If any of P1, P2, P3 are below 10, above 500 or any non-numeric value.
! 296:
! 297: WARNING
! 298: If any of P1, P2, P3 are above 300.
! 299:
! 300:
! 301: $PROGNAME -c Idle -n P1 -c 1:1 -n P2 -w 200:300 -c Active,10: -n P3
! 302:
! 303: CRITICAL
! 304: If P1 is any value but Idle.
! 305: If P2 is any value but 1.
! 306: If P3 is below 10 or any non-numeric value other than "Active".
! 307:
! 308: WARNING
! 309: If P3 is above 10 and below 200 or above 300.
1.1 andrew 310:
311: EOL
312:
1.2 ! andrew 313: print_revision( $PROGNAME, '$Revision: 1.1 $' );
1.1 andrew 314:
315: print $LICENSE;
316:
317: return;
318: }
319:
320: sub print_revision {
321: my ( $prog, $rev ) = @_;
322: $rev =~ s/^\D+([\d\.]+)\D+$/v$1/xms;
323:
324: say $prog, q{ }, $rev;
325:
326: return;
327: }
328:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>