1 |
apollock |
1 |
#!/usr/bin/python |
2 |
|
|
|
3 |
|
|
import urllib2 |
4 |
|
|
import sys |
5 |
|
|
import time |
6 |
|
|
import stat |
7 |
|
|
import os |
8 |
|
|
from optparse import OptionParser |
9 |
|
|
|
10 |
|
|
### |
11 |
|
|
# Script to check if a local mirror is in sync with an HTTP-accessible remote |
12 |
|
|
# mirror (based on the /debian/project/trace file) |
13 |
|
|
# |
14 |
|
|
# Copyright (c) 2006 Andrew Pollock <me@andrew.net.au> |
15 |
|
|
# |
16 |
|
|
# Permission to use granted under the GNU General Public License v2 |
17 |
|
|
|
18 |
|
|
class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler): |
19 |
|
|
"""The class you need to have to get useful IMS functionality""" |
20 |
|
|
|
21 |
|
|
def http_error_default(self, req, fp, code, msg, headers): |
22 |
|
|
"""Returns the result code of the transaction""" |
23 |
|
|
|
24 |
|
|
result = urllib2.HTTPError( |
25 |
|
|
req.get_full_url(), code, msg, headers, fp) |
26 |
|
|
result.status = code |
27 |
|
|
return result |
28 |
|
|
|
29 |
|
|
|
30 |
apollock |
13 |
class Request2(urllib2.Request): |
31 |
|
|
|
32 |
|
|
def __init__(self, url, data=None, headers={}, |
33 |
|
|
origin_req_host=None, unverifiable=False, method="GET"): |
34 |
|
|
|
35 |
|
|
urllib2.Request.__init__(self, url, data, headers, origin_req_host, unverifiable) |
36 |
|
|
self.method = method |
37 |
|
|
|
38 |
|
|
def get_method(self): |
39 |
|
|
if self.has_data(): |
40 |
|
|
return "POST" |
41 |
|
|
else: |
42 |
|
|
return self.method |
43 |
|
|
|
44 |
|
|
|
45 |
apollock |
1 |
def getLastModifiedTime(url): |
46 |
|
|
"""Makes a request for url and returns the Last-Modified header""" |
47 |
|
|
|
48 |
apollock |
14 |
request = Request2(url) |
49 |
apollock |
1 |
opener = urllib2.build_opener() |
50 |
|
|
data = opener.open(request) |
51 |
|
|
return(data.headers.dict["last-modified"]) |
52 |
|
|
|
53 |
|
|
|
54 |
|
|
def URLisNewerThan(url, epoch): |
55 |
|
|
"""Makes an IMS request for url with epoch as the modification time and returns if URL has changed""" |
56 |
|
|
|
57 |
apollock |
14 |
request = Request2(url) |
58 |
apollock |
1 |
request.add_header('If-Modified-Since', time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(epoch))) |
59 |
|
|
opener = urllib2.build_opener(DefaultErrorHandler()) |
60 |
|
|
data = opener.open(request) |
61 |
|
|
if (hasattr(data, 'status') and data.status == 304): |
62 |
|
|
return False |
63 |
|
|
else: |
64 |
|
|
return True |
65 |
|
|
|
66 |
apollock |
13 |
def isSyncing(): |
67 |
|
|
"""Returns if the upstream mirror has a lock file indicating it is currently syncing with its upstream""" |
68 |
apollock |
1 |
|
69 |
apollock |
13 |
pass |
70 |
|
|
|
71 |
apollock |
1 |
def main(): |
72 |
|
|
parser = OptionParser() |
73 |
|
|
|
74 |
|
|
parser.set_description("Program to check if a local Debian mirror is out of sync with a remote Debian mirror") |
75 |
|
|
parser.add_option("-w", "--wait-time", dest="wait", default=300, type="int", |
76 |
|
|
help="Seconds to wait between checks for") |
77 |
|
|
parser.add_option("-m", "--max-retry", dest="maxretries", default=0, type="int", |
78 |
|
|
help="Maximum number of interations before giving up") |
79 |
|
|
parser.add_option("-v", "--verbose", action="store_true", |
80 |
|
|
dest="verbose", default=False, |
81 |
|
|
help="Print additional information about behaviour") |
82 |
|
|
|
83 |
|
|
(options, args) = parser.parse_args() |
84 |
|
|
|
85 |
|
|
if len(args) != 2: |
86 |
|
|
parser.print_usage() |
87 |
|
|
else: |
88 |
|
|
|
89 |
|
|
UPSTREAM=args[0] |
90 |
|
|
MIRROR_ROOT=args[1] |
91 |
|
|
upstream_trace = "".join(['http://', UPSTREAM, '/debian/project/trace/', UPSTREAM]) |
92 |
|
|
local_trace = os.path.join(MIRROR_ROOT, "debian", "project", "trace", UPSTREAM) |
93 |
|
|
|
94 |
|
|
#upstream_timestamp = calendar.timegm(time.strptime(getLastModifiedTime(upstream_trace), "%a, %d %b %Y %H:%M:%S %Z")) |
95 |
|
|
upstream_timestamp = getLastModifiedTime(upstream_trace) |
96 |
|
|
|
97 |
|
|
if options.verbose: |
98 |
|
|
print "Upstream trace file last modified on", upstream_timestamp |
99 |
|
|
|
100 |
|
|
local_trace_timestamp = os.stat(local_trace)[stat.ST_MTIME] |
101 |
|
|
|
102 |
|
|
if options.verbose: |
103 |
|
|
print "Our copy of trace file last modified on", time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(local_trace_timestamp)) |
104 |
|
|
|
105 |
|
|
if URLisNewerThan(upstream_trace, local_trace_timestamp): |
106 |
|
|
if options.verbose: |
107 |
|
|
print "Because the upstream trace file is newer than what is on the mirror, I think it's time to resync the mirror" |
108 |
|
|
sys.exit(0) |
109 |
|
|
else: |
110 |
|
|
if options.verbose: |
111 |
|
|
print "I believe our mirror to be in sync with the upstream" |
112 |
|
|
iterations = 0 |
113 |
|
|
while iterations < options.maxretries: |
114 |
|
|
if options.verbose: |
115 |
|
|
print "Sleeping for %d seconds" % options.wait |
116 |
|
|
time.sleep(options.wait) |
117 |
|
|
if URLisNewerThan(upstream_trace, local_trace_timestamp): |
118 |
|
|
upstream_timestamp = getLastModifiedTime(upstream_trace) |
119 |
|
|
break |
120 |
|
|
else: |
121 |
|
|
if options.verbose: |
122 |
|
|
print "Upstream trace file last modified on", upstream_timestamp |
123 |
|
|
iterations += 1 |
124 |
|
|
else: |
125 |
|
|
if options.verbose and iterations > 0: |
126 |
|
|
print "Giving up" |
127 |
|
|
sys.exit(1) |
128 |
|
|
if options.verbose: |
129 |
|
|
print "Upstream trace file last modified on", upstream_timestamp |
130 |
|
|
print "Because the upstream trace file is newer than what is on the mirror, I think it's time to resync the mirror" |
131 |
|
|
sys.exit(0) |
132 |
|
|
|
133 |
|
|
if __name__ == '__main__': |
134 |
|
|
main() |