retire da-backup checks
[mirror/dsa-nagios.git] / dsa-nagios-checks / checks / dsa-check-mirrorsync
1 #!/usr/bin/perl -w
2
3 # nagios check for debian security sync checks
4 #
5 #  Copyright (c) 2008 Alexander Wirt <formorer@debian.org>
6 #  Copyright (c) 2009, 2010, 2016 Peter Palfrader <peter@palfrader.org>
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 # USA
22
23 use LWP::UserAgent;
24 use Socket;
25 use strict;
26 use Date::Parse;
27 use Getopt::Long;
28 use Date::Parse;
29 use Date::Format;
30 use File::Basename;
31 use English;
32 use warnings;
33
34
35 sub usage($$) {
36         my ($fh, $exit) = @_;
37         my $basename = basename($PROGRAM_NAME);
38         my $VERSION = '0.1';
39
40         print $fh "$basename $VERSION\n";
41         print $fh "Usage: $basename [--help|--version] [--verbose]\n";
42         print $fh "\n";
43         print $fh "  --help              Print this short help.\n";
44         print $fh "  --version           Report version number.\n";
45         print $fh "  --verbose           Be a little verbose.\n";
46         print $fh "  --host              hostname to check.\n";
47         print $fh "  --path              path to tracefile.\n";
48         print $fh "  --unix              tracefile has a unix timestamp.\n";
49         print $fh "  --ssl               use https.\n";
50         print $fh "  --allow-skew=<foo>:<bar> if the newest timestamp is newer than <foo>secs\n";
51         print $fh "                      then the mirror sync is still ok, assuming the oldest\n";
52         print $fh "                      trace file is no older than <bar>\n";
53         print $fh "\n";
54         exit ($exit);
55 };
56
57 # Work around LWP not being able to verify service certs directly
58 my $ca_dir = '/etc/ssl/ca-debian';
59 $ENV{'PERL_LWP_SSL_CA_PATH'} = $ca_dir if -d $ca_dir;
60
61 $ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin';
62 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
63
64 my $params;
65
66 $params->{'host'} = 'security.debian.org'; #which host to check
67 $params->{'path'} = 'project/trace/security-master.debian.org'; 
68
69 my $OK = 0;
70 my $WARNING = 1;
71 my $CRITICAL = 2;
72 my $UNKNOWN = 3;
73
74 if (!GetOptions (
75                 '--help'                => \$params->{'help'},
76                 '--verbose'             => \$params->{'verbose'},
77                 '--version'             => \$params->{'version'},
78                 '--unix'                => \$params->{'unix'},
79                 '--ssl'                 => \$params->{'ssl'},
80                 '--host=s'              => \$params->{'host'},
81                 '--path=s'              => \$params->{'path'},
82                 '--allow-skew=s'        => \$params->{'skew'},
83                 )) {
84                 usage(*STDERR,1)
85 };
86 usage(*STDOUT,0) if ($params->{'help'});
87 usage(*STDERR,1) if (scalar @ARGV);
88
89 if (defined $params->{'skew'}) {
90         if (not $params->{'skew'} =~ /^([0-9]+):([0-9]+)$/) {
91                 print STDERR "Invalid allow-skew format\n";
92                 usage(*STDERR,1);
93         };
94         $params->{'skew-new'} = $1;
95         $params->{'skew-old'} = $2;
96 };
97
98 my $host = $params->{'host'};
99 my $path = $params->{'path'};
100 my @slaves;
101 my $status;
102 my @exitstatus;
103 my $exitcode = $OK;
104
105 @slaves = gethostbyname($params->{'host'})   or die "Can't resolve " . $params->{'host'} .": $!\n";
106 @slaves = map { inet_ntoa($_) } @slaves[4 .. $#slaves];
107 print "Checking the following hosts:\n" . join("\n", @slaves) . "\n" if $params->{'verbose'};
108
109 my @critical;
110 my $schema = $params->{'ssl'} ? 'https' : 'http';
111
112 foreach my $slave (@slaves) {
113         my $ua = LWP::UserAgent->new;
114         $ua->agent("dsa-check-mirrorsync");
115         $ua->timeout(10);
116         $ua->proxy('http', "$schema://$slave");
117         print "Requesting $schema://$host/$path from $slave\n" if $params->{'verbose'};
118         my $response = $ua->get("$schema://$host/$path");
119
120
121         if ($response->is_success) {
122                 my $content = $response->content;  # or whatever
123                 my ($date, $foo, $bar) = split("\n", $content);
124                 my $synctime;
125
126                 if ($params->{'unix'}) {
127                         if ($date =~ /^([0-9]+)$/) {
128                                 $synctime = $1;
129                         }
130                 } else {
131                         $synctime = str2time($date);
132                 }
133
134                 if (defined $synctime) {
135                         print "$slave last synced $synctime ", scalar(gmtime($synctime)), "\n" if $params->{'verbose'};
136                         $status->{$slave}->{'synced'} = $synctime;
137                 } else {
138                         $synctime = 0;
139                         $exitcode = $UNKNOWN;
140                         push @exitstatus, "Cannot parse tracefile on $slave";
141                 }
142         } else {
143                 push @exitstatus, "$slave broken: " . $response->status_line; 
144                 $status->{$slave}->{'error'} = $response->status_line;
145                 $status->{$slave}->{'synced'} = 0;
146                 $exitcode = $CRITICAL;
147                 push @critical, $slave;
148         }
149 }
150
151
152 my %seen;
153 my $o_sync = scalar(grep !$seen{$_}++, map{$status->{$_}->{'synced'}} keys(%{$status}));
154 if ($o_sync > 1) {
155         my @mirrors =  sort { $status->{$a}->{'synced'} <=> $status->{$b}->{'synced'}  } keys %{$status};
156         my @not_most_recent = grep { $status->{$_}->{'synced'} <=> $status->{$mirrors[-1]}->{'synced'} } @mirrors;
157         $o_sync = scalar @not_most_recent;
158
159         my $newest = time - $status->{$mirrors[-1]}->{'synced'};
160         my $oldest = time - $status->{$mirrors[0]}->{'synced'};
161         my $skew_ok = ( defined $params->{'skew-new'} &&
162                         defined $params->{'skew-old'} &&
163                         $newest <= $params->{'skew-new'} &&
164                         $oldest <= $params->{'skew-old'});
165
166         my $msg;
167         if ($skew_ok) {
168                 $exitcode = $OK;
169                 $msg = "$o_sync mirror(s) not in sync (from oldest to newest), but still within acceptable skew: ";
170         } else {
171                 $exitcode = $CRITICAL;
172                 $msg = "$o_sync mirror(s) not in sync (from oldest to newest): ";
173         };
174         push @exitstatus, $msg . join(", ", @not_most_recent);
175 } else {
176         print "All mirrors on same sync timestamp\n" if $params->{'verbose'};
177 }
178
179 if ($exitcode == $CRITICAL) {
180         print "CRITICAL: " . join(', ',@exitstatus) . "\n";
181 } elsif ($exitcode == $OK) {
182         if (scalar @exitstatus > 0) {
183                 print "OK: " . join(', ',@exitstatus) . "\n";
184         } else {
185                 print "OK: all mirrors up2date\n";
186         }
187 } else {
188         print "UNKNOWN: ", join(', ',@exitstatus) . "\n";
189 }
190
191 foreach my $mirror (keys(%{$status})) {
192         if ($status->{$mirror}->{'error'}) {
193                 print "$mirror broken: " . $status->{$mirror}->{'error'} . "\n";
194         } else {
195                 print "$mirror last synced: " . localtime($status->{$mirror}->{'synced'}) ."\n";
196         }
197 }
198
199 exit $exitcode;