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

Annotation of nagios/check_pf_limits/check_pf_limits, Revision 1.2

1.1       andrew      1: #!/usr/bin/perl -T
1.2     ! andrew      2: # $AFresh1: check_pf_limits,v 1.1 2010/01/14 22:21:34 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:
                     15: my $NAGIOS_OUTPUT = 1;
                     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:
1.2     ! andrew     32: our ($VERSION) = '$Revision: 1.1 $' =~ m{ \$Revision: \s+ (\S+) }xms;
1.1       andrew     33: my $PROGNAME = 'check_pf_limits';
                     34: my $PFCTL    = '/sbin/pfctl';
                     35:
                     36: #use POSIX;
                     37: #use Config;
                     38: my $PREFIX;
                     39:
                     40: BEGIN {
                     41:     $PREFIX = q{};
                     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( $CHECKS{_SOCKET} );
                     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 ($device) = @_;
                     93:
                     94:     my %commands = (
                     95:         memory => { parser => \&parse_memory },
                     96:         info   => { parser => \&parse_info, args => ['-v'] },
                     97:
                     98:         # XXX Not important enough to be supported
                     99:         #Tables => { parser => \&parse_tables },
                    100:     );
                    101:
                    102:     my %status;
                    103:     foreach my $command ( keys %commands ) {
                    104:         my @cmd = ($PFCTL);
                    105:         if ($device) {
                    106:             push @cmd, '-p', $device;
                    107:         }
                    108:
                    109:         push @cmd, '-s', $command;
                    110:
                    111:         if ( exists $commands{$command}{args} ) {
                    112:             push @cmd, @{ $commands{$command}{args} };
                    113:         }
                    114:
                    115:         my %S;
                    116:         if ( $command eq 'Tables' ) {
                    117:             $S{count} = 0;
                    118:         }
                    119:
                    120:         #open my $fh, '<', 'output'    # XXX
                    121:         open my $fh, q{-|}, @cmd or die qq{Couldn't open pfctl: $!\n};
                    122:         while (<$fh>) {
                    123:             $commands{$command}{parser}->( $_, \%S );
                    124:         }
                    125:         ## no critic 'die'
                    126:         close $fh
                    127:             or die $!
                    128:             ? "Error closing pfctl pipe: $!\n"
                    129:             : "Exit status $? from pfctl\n";
                    130:
                    131:         $status{$command} = \%S;
                    132:     }
                    133:
                    134:     return %status;
                    135: }
                    136:
                    137: sub parse_memory {
                    138:     my ( $line, $result ) = @_;
                    139:
                    140:     my ( $name, $type, $limit ) = unpack "A14 A10 A*", $line;
                    141:
                    142:     $limit =~ s/^\s+//xms;
                    143:
                    144:     $result->{$name} = {
                    145:         type  => $type,
                    146:         limit => $limit,
                    147:     };
                    148:
                    149:     return 1;
                    150: }
                    151:
                    152: sub parse_info {
                    153:     my ( $line, $result ) = @_;
                    154:
                    155:     state $section = 'Unknown';
                    156:
                    157:     chomp $line;
                    158:     given ($line) {
                    159:         when (/^\w+:/xms) {
                    160:             while (
                    161:                 $line =~ s{ \s*
                    162:                 (\w+): \s+
                    163:                 ( (?:[^:]|:\S)+ )
                    164:                 \s*$ }{}xms
                    165:                 )
                    166:             {
                    167:                 $result->{$1} = $2;
                    168:             }
                    169:         }
                    170:         when (/^\S/xms) { ($section) = unpack 'A27', $line }
                    171:         when (/^\s\s/xms) {
                    172:             my ( $name, $total, $rate ) = unpack 'x2 A25 x A14 A*';
                    173:             foreach ( $total, $rate ) {
                    174:                 s/^\s+//xms;
                    175:             }
                    176:             $result->{$section}->{$name} = {
                    177:                 total => $total,
                    178:                 rate  => $rate,
                    179:             };
                    180:         }
1.2     ! andrew    181:         default {return}
1.1       andrew    182:     }
                    183:
                    184:     return 1;
                    185: }
                    186:
                    187: sub parse_tables {
                    188:     my ( $line, $result ) = @_;
                    189:     $result->{count}++;
                    190:     return 1;
                    191: }
                    192:
                    193: sub parse_check {
                    194:     my $check = shift;
                    195:
                    196:     return { match => [] } if !$check;
                    197:     my @values = split /,\s*/xms, $check;
                    198:
                    199:     my %c = ( match => [] );
                    200:     foreach my $v (@values) {
                    201:         if ( $v =~ /:/xms ) {
                    202:             ( $c{low}, $c{high} ) = split /:/xms, $v;
                    203:         }
                    204:         else {
                    205:             push @{ $c{match} }, $v;
                    206:         }
                    207:     }
                    208:
                    209:     foreach my $d ( 'low', 'high' ) {
                    210:         if ( defined $c{$d} ) {
                    211:             $c{$d} =~ s/[^-\d\.\%]//gxms;
                    212:             if ( !length $c{$d} ) {
                    213:                 delete $c{$d};
                    214:             }
                    215:         }
                    216:     }
                    217:
                    218:     return \%c;
                    219: }
                    220:
                    221: sub check_status {
                    222:     my ( $S, $C ) = @_;
                    223:
                    224:     my %known_limits = (
                    225:         states => {
                    226:             limit   => $S->{memory}{states}{limit},
                    227:             current => $S->{info}{'State Table'}{'current entries'}{total},
                    228:
                    229:         },
                    230:         'src-nodes' => {
                    231:             limit => $S->{memory}{'src-nodes'}{limit},
                    232:             current =>
                    233:                 $S->{info}{'Source Tracking Table'}{'current entries'}{total},
                    234:         },
                    235:
                    236:         # XXX Can't check frags, don't know where to read current
                    237:         #frags => {
                    238:         #    limit   => $S->{memory}{frags}{limit},
                    239:         #    current => $S->{info}{Counters}{fragment},
                    240:         #},
                    241:
                    242:         # XXX It takes an additional call to pfctl and could be long
                    243:         #Tables => {
                    244:         #    limit   => $S->{memory}{tables}{limit},
                    245:         #    current => $S->{Tables}{count},
                    246:         #},
                    247:
                    248:         # XXX Can't check table-entries, don't know where to read current
                    249:         #table-entries => {},
                    250:     );
                    251:
                    252:     my %states;
                    253:     my %matched;
                    254: STATE: foreach my $k ( keys %known_limits ) {
                    255:         $matched{$k} = 1;
                    256:         if ( my $c = $C->{$k} || $C->{_UNKNOWN} ) {
                    257:         CODE: foreach my $code ( 'CRITICAL', 'WARNING' ) {
                    258:                 next CODE if ( ref $c->{$code} ne 'HASH' );
                    259:                 my $result = check_item( $known_limits{$k}, $c->{$code} );
                    260:
                    261:                 if ($result) {
                    262:                     push @{ $states{$code} }, "[$k] $result";
                    263:                     next STATE;
                    264:                 }
                    265:             }
                    266:         }
                    267:         else {
                    268:             push @{ $states{CRITICAL} }, '[' . $k . '] Unhandled Limit';
                    269:             next STATE;
                    270:         }
                    271:
                    272:         push @{ $states{OK} }, $k;
                    273:     }
                    274:
                    275:     foreach my $k ( keys %{$C} ) {
1.2     ! andrew    276:         next if 0 == index $k, '_';
1.1       andrew    277:         if ( !exists $matched{$k} ) {
                    278:             push @{ $states{CRITICAL} }, '[' . $k . '] Unsupported Limit';
                    279:         }
                    280:     }
                    281:
                    282:     return %states;
                    283: }
                    284:
                    285: sub check_item {
                    286:     my ( $d, $c ) = @_;
                    287:
                    288:     my $result;
                    289:     if ( $c->{match} && @{ $c->{match} } ) {
                    290:         foreach my $m ( @{ $c->{match} } ) {
                    291:             return if $m eq $d->{current};
                    292:         }
                    293:         $result
                    294:             = 'State (' . $d->{current} . ') is outside of acceptable values';
                    295:     }
                    296:
                    297:     if ( $c->{low} || $c->{high} ) {
                    298:         $result = undef;
                    299:         my $num = $d->{current};
                    300:         $num =~ s/[^-\d\.]//gxms;
                    301:
                    302:         if ( !length $num ) {
                    303:             return 'State (' . $d . ') is not numeric';
                    304:         }
                    305:
                    306:     DIRECTION: foreach my $dir qw( low high ) {
                    307:             if ( !$c->{$dir} ) { next DIRECTION; }
                    308:
                    309:             my $check = $c->{$dir};
                    310:             my $cnum  = $num;
                    311:
                    312:             my $max = $d->{limit};
                    313:             if ( $check =~ s/\%$//xms ) {
                    314:                 if ( !defined $max ) {
                    315:                     return 'max-prefix not specified and % check requested';
                    316:                 }
                    317:                 $num = "$num/$max";
                    318:
                    319:                 # convert to percent
                    320:                 $cnum = 100 * $cnum / $max;
                    321:             }
                    322:
                    323:             my @nums       = ( $cnum, $check );
                    324:             my $abovebelow = 'below';
                    325:             my $symbol     = '<';
                    326:             if ( $dir eq 'high' ) {
                    327:                 @nums       = ( $check, $cnum );
                    328:                 $abovebelow = 'above';
                    329:                 $symbol     = '>';
                    330:             }
                    331:
                    332:             if ( $nums[0] < $nums[1] ) {
                    333:                 return join q{ }, 'is', $abovebelow,
                    334:                     'threshold (' . $num,
                    335:                     $symbol, $c->{$dir} . ')';
                    336:             }
                    337:         }
                    338:     }
                    339:
                    340:     return $result;
                    341: }
                    342:
                    343: sub getopt {
                    344:     my (@argv) = @_;
                    345:
1.2     ! andrew    346:     my ( %checks, $w, $c );
1.1       andrew    347:     while (@argv) {
                    348:         my $opt = shift @argv;
                    349:         given ($opt) {
                    350:             when ( '-V' || '--version' ) {
1.2     ! andrew    351:                 print_revision( $PROGNAME, '$Revision: 1.1 $ ' );
1.1       andrew    352:                 exit $ERRORS{'OK'}
                    353:             }
                    354:             when (/^-?-h(?:elp)?/xms) { print_help(); exit $ERRORS{'OK'} }
                    355:             when (/^-?-p(?:ath)?/xms) { $checks{_DEVICE} = shift @argv }
                    356:             when (/^-?-w(?:arning)?/xms) {
                    357:                 $w = parse_check( shift @argv )
                    358:             }
                    359:             when (/^-?-c(?:ritical)?/xms) {
                    360:                 $c = parse_check( shift @argv )
                    361:             }
                    362:             when (/^-?-l(?:limit)?/xms) {
                    363:                 while ( @argv && $argv[0] !~ /^-/xms ) {
                    364:                     $checks{ shift @argv } = {
                    365:                         WARNING      => $w,
                    366:                             CRITICAL => $c,
                    367:                     }
                    368:                 }
                    369:             }
                    370:             default { print_help(); exit $ERRORS{'UNKNOWN'} }
                    371:         }
                    372:     }
1.2     ! andrew    373:
        !           374:     $checks{_UNKNOWN} = {
        !           375:         WARNING  => $w,
        !           376:         CRITICAL => $c,
        !           377:     };
1.1       andrew    378:     return %checks;
                    379: }
                    380:
                    381: sub print_help {
                    382:     print <<"EOL";
                    383: $PROGNAME - checks status of pf limits
                    384:     $PROGNAME [ -d device ][ -w ENTRY ][ -c ENTRY ][ -l limit ]
                    385:
                    386: Usage:
                    387:     -d, --path Path to pf device
                    388:         Path to pf device to use. See -p in pfctl(8).
                    389:     -w, --warning RANGE or single ENTRY
                    390:         Exit with WARNING status if outside of RANGE or if != ENTRY
                    391:         May be entered multiple times.
                    392:     -c, --critical RANGE or single ENTRY
                    393:         Exit with CRITICAL status if outside of RANGE or if != ENTRY
                    394:         May be entered multiple times.
                    395:     -l, --limit LIMIT
                    396:         The name of the limit, can be a space separated list of limits
                    397:         May be entered multiple times.
                    398:
                    399: ENTRY is a comma separated list of items to match against.  Each item can be
                    400: a RANGE or it will just be matched against the status.
                    401:
                    402: RANGE is specified as two optional numbers separated with a colon (:).  The
                    403: check is that the value is between the two numbers.  If either number is left
                    404: off, that check is ignored.
                    405:
                    406: If either number in a RANGE is specified as a percent, check is that
                    407: max-prefix is specified and that the number is within the specified percent.
                    408:
                    409: LIMIT is the name that shows when running 'pfctl -s memory'. Currently
                    410: supported limits are 'states' and 'src-nodes'.
                    411:
                    412: Any time a LIMIT is specified on the command line that is NOT a supported
                    413: limit causes a CRITICAL result.
                    414:
                    415: Any known limits that are unspecified are checked with the last specified
                    416: warning and criticical ENTRY.
                    417:
                    418: Example:
                    419:
                    420: $PROGNAME -c :90% -l src-nodes -w 10:75% -l states
                    421:
                    422: CRITICAL
                    423:     If states or src-nodes are above 90% of their limit
                    424:
                    425: WARNING
                    426:     If states is below 10 or above 75% of its limit
                    427:
                    428: EOL
                    429:
1.2     ! andrew    430:     print_revision( $PROGNAME, '$Revision: 1.1 $' );
1.1       andrew    431:
                    432:     print $LICENSE;
                    433:
                    434:     return;
                    435: }
                    436:
                    437: sub print_revision {
                    438:     my ( $prog, $rev ) = @_;
                    439:     $rev =~ s/^\D+([\d\.]+)\D+$/v$1/xms;
                    440:
                    441:     say $prog, q{ }, $rev;
                    442:
                    443:     return;
                    444: }
                    445:

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