retire da-backup checks
[mirror/dsa-nagios.git] / dsa-nagios-checks / checks / dsa-check-zone-rrsig-expiration-many
index 15305dd..674dd1c 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 
-# Copyright (c) 2010 Peter Palfrader <peter@palfrader.org>
+# Copyright (c) 2010,2012 Peter Palfrader <peter@palfrader.org>
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the
@@ -23,6 +23,8 @@
 
 use strict;
 use warnings;
+use threads;
+
 use English;
 use Getopt::Long;
 use FindBin qw($Bin);
@@ -59,52 +61,78 @@ sub convert_time {
        return $ticks;
 }
 
-my $USAGE = "Usage: $PROGRAM_NAME [--help] | [--warn=<nn>] [--critical=<nn>] [--geozonedir=<geodir>] <indir>\n";
-my $params = { 'warn' => '14d', 'critical' => '7d' };
+sub check_one {
+       $SIG{'KILL'} = sub { threads->exit(); };
+
+       my $zone = shift;
+       my $check = shift;
+       my $extra = shift;
+       my $params = shift;
+
+       my @cmd = ($check, '-w', $params->{'warn'}, '-c', $params->{'critical'});
+       push(@cmd, '-r', $extra->{'initial_refs'}) if exists $extra->{'initial_refs'};
+       push(@cmd, '-d') if $params->{'debug'};
+       push(@cmd, $zone);
+       open(P, '-|', @cmd) or die ("Cannot run $CHECK for $zone\n");
+       my @p = <P>;
+       close P;
+       $p[0] = $zone.': '. $p[0] if (scalar @p > 0);
+
+       my $res = $CHILD_ERROR >> 8;
+
+       return ($res, \@p);
+}
+
+my $USAGE = "Usage: $PROGRAM_NAME [--help] | [--debug] [--timeout=<nn>] [--warn=<nn>] [--critical=<nn>] [--geozonedir=<geodir>] <indir>\n";
+my $params = { 'timeout' => 45, 'warn' => '14d', 'critical' => '7d' };
 Getopt::Long::config('bundling');
 GetOptions (
        '--help' => \$params->{'help'},
+       '--timeout=i' => \$params->{'timeout'},
        '--warn=s' => \$params->{'warn'},
+       '--debug' => \$params->{'debug'},
        '--critical=s' => \$params->{'critical'},
        '--geozonedir=s' => \$params->{'geozonedir'},
 ) or die ($USAGE);
 if ($params->{'help'}) {
-        print $USAGE;
-        exit(0);
+       print $USAGE;
+       exit(0);
 };
 die ($USAGE) unless (scalar @ARGV == 1);
 my $INDIR = shift;
 
 
-my $count =
-       { 'ok' => [],
-         'warn' => [],
-         'critical' => [],
-         'unknown' => [],
-         'unsigned' => [],
-       };
+my $states = [qw{critical warn unknown ok unsigned}];
+my $count =   { map { $_ => [] } @$states };
+my $details = { map { $_ => [] } @$states };
 
 
-my @dnsseczones;
+my %dnsseczones;
 # load list of classic zones that will do DNSSEC
 chdir $INDIR or die "chdir $INDIR failed? $!\n";
 opendir INDIR, '.' or die ("Cannot opendir $INDIR\n");
 for my $file (sort {$a cmp $b} (readdir INDIR)) {
        next if ( -l "$file" );
        next unless ( -f "$file" );
-       next if $file =~ /^(dsset|keyset)-/;
 
-       my $do_dnssec = 0;
+       my $do_dnssec = 1;
+       my $delegated = 1;
+       my $initial_refs = undef;
        open(F, '<', $file) or die ("Cannot open $file: $!\n");
        for (<F>) {
-               if (/^; wzf:\s*dnssec\s*=\s*1\s*$/) { $do_dnssec = 1; last; }
+               if (/^; wzf:\s*dnssec\s*=\s*0\s*$/) { $do_dnssec = 0; }
+               if (/^; delegated\s*=\s*no\s*$/) { $delegated = 0; }
+               if (/^; check-initial-refs\s*=\s*(.*?)\s*$/) { $initial_refs = $1; }
        };
        close F;
 
-       if ($do_dnssec) {
-               push @dnsseczones, $file;
+       if ($do_dnssec && $delegated) {
+               die "Duplicate zone $file?\n" if exists $dnsseczones{$file};
+               $dnsseczones{$file} = {};
+               $dnsseczones{$file}->{'initial_refs'} = $initial_refs if defined $initial_refs;
        } else {
-               push @{$count->{'unsigned'}}, $file;
+               push @{$count  ->{'unsigned'}}, $file;
+               push @{$details->{'unsigned'}}, "$file: marked unsigned or undelegated.\n";
        };
 }
 closedir(INDIR);
@@ -116,57 +144,74 @@ if (defined $params->{'geozonedir'}) {
        for my $file (sort {$a cmp $b} (readdir INDIR)) {
                next unless $file =~ /\.zone$/;
 
-               open (F, '<', $file) or die "Cannot open $file: $!\n";
-               my ($zc, undef, undef) = Load(join "", (<F>));
-               close F;
-
                my $zone = basename($file, '.zone');
-
-               if ($zc->{'dnssec'}) {
-                       push @dnsseczones, $zone;
-               } else {
-                       push @{$count->{'unsigned'}}, $zone;
-               };
+               die "Duplicate zone $zone?\n" if exists $dnsseczones{$zone};
+               $dnsseczones{$zone} = {};
        }
        closedir(INDIR);
 }
 
+my %threads;
+for my $zone (sort {$a cmp $b} keys %dnsseczones) {
+       die "Duplicate zone $zone?\n"  if defined $threads{$zone};
+       my $thr = threads->create({'context' => 'list'},
+                                 \&check_one, $zone, $CHECK, $dnsseczones{$zone}, $params);
+       $threads{$zone} = $thr;
+}
 
-my @details;
+my $begin = time;
+while (time - $begin <= $params->{timeout}) {
+       for my $zone (sort {$a cmp $b} keys %threads) {
+               next unless $threads{$zone}->is_joinable();
 
-for my $zone (sort {$a cmp $b} @dnsseczones) {
+               my ($res, $det) = $threads{$zone}->join();
 
-       open(P, '-|', ($CHECK, '-w', $params->{'warn'}, '-c', $params->{'critical'}, $zone)) or die ("Cannot run $CHECK for $zone\n");
-       my @p = <P>;
-       close P;
-       $p[0] = $zone.': '. $p[0] if (scalar @p > 0);
-       push @details, @p;
+               my $type = ($res == 0) ? 'ok' :
+                          ($res == 1) ? 'warn' :
+                          ($res == 2) ? 'critical' :
+                                        'unknown';
 
-       my $res = $CHILD_ERROR >> 8;
-       if ($res == 0) { push @{$count->{'ok'}}, $zone; }
-       elsif ($res == 1) { push @{$count->{'warn'}}, $zone; }
-       elsif ($res == 2) { push @{$count->{'critical'}}, $zone; }
-       else { push @{$count->{'unknown'}}, $zone; };
-};
+               push @{$details->{$type}}, @$det;
+               push @{$count  ->{$type}}, $zone;
+               delete $threads{$zone};
+       }
+       last if scalar keys %threads == 0;
+       print STDERR (scalar keys %threads), " threads left: ", join(" ", keys %threads), "\n" if $params->{'debug'};
+       sleep 1;
+}
+for my $zone (sort {$a cmp $b} keys %threads) {
+       push @{$count  ->{'warn'}}, $zone;
+       push @{$details->{'warn'}}, "$zone: timeout during check\n";
+       $threads{$zone}->kill('KILL')->detach();
+}
 
-my $exit;
+my $exit = 0;
 my %state_mapping = (
        'unknown' => 255,
        'critical' => 2,
        'warn' => 1,
+       'unsigned' => 0,
        'ok' => 0 );
 
-for my $state (sort {$state_mapping{$b} <=> $state_mapping{$a}} keys %state_mapping) {
+for my $state (@$states) {
+       @{$count->{$state}}   = sort {$a cmp $b} @{$count->{$state}};
+       @{$details->{$state}} = sort {$a cmp $b} @{$details->{$state}};
+
        if (scalar @{$count->{$state}}) {
                printf "%s: %d", uc($state), scalar @{$count->{$state}};
                if ($state_mapping{$state} > 0) {
                        print ": ", join(', ', @{$count->{$state}});
                };
                print "; ";
-               $exit = $state_mapping{$state} unless defined $exit;
+               $exit = $state_mapping{$state} if ($state_mapping{$state} > $exit);
        };
 };
 printf "unsigned: %d", scalar @{$count->{'unsigned'}};
 print "\n";
-print $_ for (@details);
+for my $state (@$states) {
+       for (@{$details->{$state}}) {
+               s/\|/;/g;
+               print $_;
+       }
+}
 exit $exit;