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

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

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