[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.4

1.1       andrew      1: #!/usr/bin/perl -T
1.4     ! andrew      2: # $AFresh1: check_pf_limits,v 1.3 2010/01/14 22:42:12 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.4     ! andrew     32: our ($VERSION) = '$Revision: 1.3 $' =~ 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;
1.4     ! andrew    253:     my $status = $S->{info}{Status};
        !           254:     if (0 == index $status, 'Enabled') {
        !           255:         push @{ $states{OK} }, $status;
        !           256:     }
        !           257:     else {
        !           258:         push @{ $states{CRITICAL} }, $status;
        !           259:     }
        !           260:
        !           261:
1.1       andrew    262:     my %matched;
                    263: STATE: foreach my $k ( keys %known_limits ) {
                    264:         $matched{$k} = 1;
                    265:         if ( my $c = $C->{$k} || $C->{_UNKNOWN} ) {
                    266:         CODE: foreach my $code ( 'CRITICAL', 'WARNING' ) {
                    267:                 next CODE if ( ref $c->{$code} ne 'HASH' );
                    268:                 my $result = check_item( $known_limits{$k}, $c->{$code} );
                    269:
                    270:                 if ($result) {
                    271:                     push @{ $states{$code} }, "[$k] $result";
                    272:                     next STATE;
                    273:                 }
                    274:             }
                    275:         }
                    276:         else {
                    277:             push @{ $states{CRITICAL} }, '[' . $k . '] Unhandled Limit';
                    278:             next STATE;
                    279:         }
                    280:
                    281:         push @{ $states{OK} }, $k;
                    282:     }
                    283:
                    284:     foreach my $k ( keys %{$C} ) {
1.2       andrew    285:         next if 0 == index $k, '_';
1.1       andrew    286:         if ( !exists $matched{$k} ) {
                    287:             push @{ $states{CRITICAL} }, '[' . $k . '] Unsupported Limit';
                    288:         }
                    289:     }
                    290:
                    291:     return %states;
                    292: }
                    293:
                    294: sub check_item {
                    295:     my ( $d, $c ) = @_;
                    296:
                    297:     my $result;
                    298:     if ( $c->{match} && @{ $c->{match} } ) {
                    299:         foreach my $m ( @{ $c->{match} } ) {
                    300:             return if $m eq $d->{current};
                    301:         }
                    302:         $result
                    303:             = 'State (' . $d->{current} . ') is outside of acceptable values';
                    304:     }
                    305:
                    306:     if ( $c->{low} || $c->{high} ) {
                    307:         $result = undef;
                    308:         my $num = $d->{current};
                    309:         $num =~ s/[^-\d\.]//gxms;
                    310:
                    311:         if ( !length $num ) {
                    312:             return 'State (' . $d . ') is not numeric';
                    313:         }
                    314:
                    315:     DIRECTION: foreach my $dir qw( low high ) {
                    316:             if ( !$c->{$dir} ) { next DIRECTION; }
                    317:
                    318:             my $check = $c->{$dir};
                    319:             my $cnum  = $num;
                    320:
                    321:             my $max = $d->{limit};
                    322:             if ( $check =~ s/\%$//xms ) {
                    323:                 if ( !defined $max ) {
                    324:                     return 'max-prefix not specified and % check requested';
                    325:                 }
                    326:                 $num = "$num/$max";
                    327:
                    328:                 # convert to percent
                    329:                 $cnum = 100 * $cnum / $max;
                    330:             }
                    331:
                    332:             my @nums       = ( $cnum, $check );
                    333:             my $abovebelow = 'below';
                    334:             my $symbol     = '<';
                    335:             if ( $dir eq 'high' ) {
                    336:                 @nums       = ( $check, $cnum );
                    337:                 $abovebelow = 'above';
                    338:                 $symbol     = '>';
                    339:             }
                    340:
                    341:             if ( $nums[0] < $nums[1] ) {
                    342:                 return join q{ }, 'is', $abovebelow,
                    343:                     'threshold (' . $num,
                    344:                     $symbol, $c->{$dir} . ')';
                    345:             }
                    346:         }
                    347:     }
                    348:
                    349:     return $result;
                    350: }
                    351:
                    352: sub getopt {
                    353:     my (@argv) = @_;
                    354:
1.2       andrew    355:     my ( %checks, $w, $c );
1.1       andrew    356:     while (@argv) {
                    357:         my $opt = shift @argv;
                    358:         given ($opt) {
                    359:             when ( '-V' || '--version' ) {
1.4     ! andrew    360:                 print_revision( $PROGNAME, '$Revision: 1.3 $ ' );
1.1       andrew    361:                 exit $ERRORS{'OK'}
                    362:             }
                    363:             when (/^-?-h(?:elp)?/xms) { print_help(); exit $ERRORS{'OK'} }
                    364:             when (/^-?-p(?:ath)?/xms) { $checks{_DEVICE} = shift @argv }
                    365:             when (/^-?-w(?:arning)?/xms) {
                    366:                 $w = parse_check( shift @argv )
                    367:             }
                    368:             when (/^-?-c(?:ritical)?/xms) {
                    369:                 $c = parse_check( shift @argv )
                    370:             }
                    371:             when (/^-?-l(?:limit)?/xms) {
                    372:                 while ( @argv && $argv[0] !~ /^-/xms ) {
                    373:                     $checks{ shift @argv } = {
                    374:                         WARNING      => $w,
                    375:                             CRITICAL => $c,
                    376:                     }
                    377:                 }
                    378:             }
                    379:             default { print_help(); exit $ERRORS{'UNKNOWN'} }
                    380:         }
                    381:     }
1.2       andrew    382:
1.3       andrew    383:     if (defined $w) {
                    384:         $checks{_UNKNOWN}{WARNING} = $w;
                    385:     }
                    386:     if (defined $c) {
                    387:         $checks{_UNKNOWN}{CRITICAL} = $c;
1.2       andrew    388:     };
1.3       andrew    389:
1.1       andrew    390:     return %checks;
                    391: }
                    392:
                    393: sub print_help {
                    394:     print <<"EOL";
                    395: $PROGNAME - checks status of pf limits
                    396:     $PROGNAME [ -d device ][ -w ENTRY ][ -c ENTRY ][ -l limit ]
                    397:
                    398: Usage:
                    399:     -d, --path Path to pf device
                    400:         Path to pf device to use. See -p in pfctl(8).
                    401:     -w, --warning RANGE or single ENTRY
                    402:         Exit with WARNING status if outside of RANGE or if != ENTRY
                    403:         May be entered multiple times.
                    404:     -c, --critical RANGE or single ENTRY
                    405:         Exit with CRITICAL status if outside of RANGE or if != ENTRY
                    406:         May be entered multiple times.
                    407:     -l, --limit LIMIT
                    408:         The name of the limit, can be a space separated list of limits
                    409:         May be entered multiple times.
                    410:
                    411: ENTRY is a comma separated list of items to match against.  Each item can be
                    412: a RANGE or it will just be matched against the status.
                    413:
                    414: RANGE is specified as two optional numbers separated with a colon (:).  The
                    415: check is that the value is between the two numbers.  If either number is left
                    416: off, that check is ignored.
                    417:
                    418: If either number in a RANGE is specified as a percent, check is that
                    419: max-prefix is specified and that the number is within the specified percent.
                    420:
                    421: LIMIT is the name that shows when running 'pfctl -s memory'. Currently
                    422: supported limits are 'states' and 'src-nodes'.
                    423:
                    424: Any time a LIMIT is specified on the command line that is NOT a supported
                    425: limit causes a CRITICAL result.
                    426:
                    427: Any known limits that are unspecified are checked with the last specified
                    428: warning and criticical ENTRY.
                    429:
1.4     ! andrew    430: Also checks if pf is enabled and is CRITICAL if not.
        !           431:
1.1       andrew    432: Example:
                    433:
                    434: $PROGNAME -c :90% -l src-nodes -w 10:75% -l states
                    435:
                    436: CRITICAL
                    437:     If states or src-nodes are above 90% of their limit
                    438:
                    439: WARNING
                    440:     If states is below 10 or above 75% of its limit
                    441:
                    442: EOL
                    443:
1.4     ! andrew    444:     print_revision( $PROGNAME, '$Revision: 1.3 $' );
1.1       andrew    445:
                    446:     print $LICENSE;
                    447:
                    448:     return;
                    449: }
                    450:
                    451: sub print_revision {
                    452:     my ( $prog, $rev ) = @_;
                    453:     $rev =~ s/^\D+([\d\.]+)\D+$/v$1/xms;
                    454:
                    455:     say $prog, q{ }, $rev;
                    456:
                    457:     return;
                    458: }
                    459:

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