]>
Commit | Line | Data |
---|---|---|
940227cb MT |
1 | #!/usr/bin/python |
2 | ||
3 | import logging | |
4 | import socket | |
5 | import time | |
6 | import tornado.httpclient | |
7 | ||
8 | from databases import Databases | |
9 | from geoip import GeoIP | |
10 | from misc import Singleton | |
11 | ||
12 | class Mirrors(object): | |
13 | __metaclass__ = Singleton | |
14 | ||
15 | @property | |
16 | def db(self): | |
17 | return Databases().webapp | |
18 | ||
19 | def list(self): | |
20 | return [Mirror(m.id) for m in self.db.query("SELECT id FROM mirrors ORDER BY state")] | |
21 | ||
22 | def check_all(self): | |
23 | for mirror in self.list(): | |
24 | mirror.check() | |
25 | ||
26 | def get(self, id): | |
27 | return Mirror( id) | |
28 | ||
29 | def get_by_hostname(self, hostname): | |
30 | mirror = self.db.get("SELECT id FROM mirrors WHERE hostname=%s", hostname) | |
31 | ||
32 | return Mirror(mirror.id) | |
33 | ||
34 | def get_with_file(self, filename): | |
35 | return [Mirror(m.mirror) for m in \ | |
36 | self.db.query("SELECT mirror FROM mirror_files WHERE filename=%s", filename)] | |
37 | ||
38 | ||
39 | class Mirror(object): | |
40 | def __init__(self, id): | |
41 | self.id = id | |
42 | ||
43 | self.reload() | |
44 | ||
45 | @property | |
46 | def db(self): | |
47 | return Databases().webapp | |
48 | ||
49 | def reload(self): | |
50 | self._info = self.db.get("SELECT * FROM mirrors WHERE id=%s", self.id) | |
51 | self._info["url"] = self.generate_url() | |
52 | ||
53 | def generate_url(self): | |
54 | url = "http://%s" % self.hostname | |
55 | if not self.path.startswith("/"): | |
56 | url += "/" | |
57 | url += "%s" % self.path | |
58 | if not self.path.endswith("/"): | |
59 | url += "/" | |
60 | return url | |
61 | ||
62 | def __getattr__(self, key): | |
63 | try: | |
64 | return self._info[key] | |
65 | except KeyError: | |
66 | raise AttributeError(key) | |
67 | ||
68 | @property | |
69 | def address(self): | |
70 | return socket.gethostbyname(self.hostname) | |
71 | ||
72 | @property | |
73 | def country_code(self): | |
74 | return GeoIP().get_country(self.address).lower() or "unknown" | |
75 | ||
76 | @property | |
77 | def filelist(self): | |
78 | filelist = self.db.query("SELECT filename FROM mirror_files WHERE mirror=%s ORDER BY filename", self.id) | |
79 | return [f.filename for f in filelist] | |
80 | ||
81 | def set_state(self, state): | |
82 | logging.info("Setting state of %s to %s" % (self.hostname, state)) | |
83 | ||
84 | if self.state == state: | |
85 | return | |
86 | ||
87 | self.db.execute("UPDATE mirrors SET state=%s WHERE id=%s", | |
88 | state, self.id) | |
89 | ||
90 | # Reload changed settings | |
91 | self.reload() | |
92 | ||
93 | def check(self): | |
94 | logging.info("Running check for mirror %s" % self.hostname) | |
95 | ||
96 | self.check_timestamp() | |
97 | self.check_filelist() | |
98 | ||
99 | def check_state(self): | |
100 | logging.debug("Checking state of mirror %s" % self.id) | |
101 | ||
102 | if self.disabled == "Y": | |
103 | self.set_state("DOWN") | |
104 | ||
105 | time_diff = time.time() - self.last_update | |
106 | if time_diff > 3*24*60*60: # XXX get this into Settings | |
107 | self.set_state("DOWN") | |
108 | elif time_diff > 6*60*60: | |
109 | self.set_state("OUTOFSYNC") | |
110 | else: | |
111 | self.set_state("UP") | |
112 | ||
113 | def check_timestamp(self): | |
114 | if self.releases == "N": | |
115 | return | |
116 | ||
117 | http = tornado.httpclient.AsyncHTTPClient() | |
118 | ||
119 | http.fetch(self.url + ".timestamp", | |
120 | headers={"Pragma" : "no-cache", }, | |
121 | callback=self.__check_timestamp_response) | |
122 | ||
123 | def __check_timestamp_response(self, response): | |
124 | if response.error: | |
125 | logging.debug("Error getting timestamp from %s" % self.hostname) | |
126 | return | |
127 | ||
128 | try: | |
129 | timestamp = int(response.body.strip()) | |
130 | except ValueError: | |
131 | timestamp = 0 | |
132 | ||
133 | self.db.execute("UPDATE mirrors SET last_update=%s WHERE id=%s", | |
134 | timestamp, self.id) | |
135 | ||
136 | # Reload changed settings | |
137 | self.reload() | |
138 | ||
139 | self.check_state() | |
140 | ||
141 | logging.info("Successfully updated timestamp from %s" % self.hostname) | |
142 | ||
143 | def check_filelist(self): | |
144 | if self.releases == "N": | |
145 | return | |
146 | ||
147 | http = tornado.httpclient.AsyncHTTPClient() | |
148 | ||
149 | http.fetch(self.url + ".filelist", | |
150 | headers={"Pragma" : "no-cache", }, | |
151 | callback=self.__check_filelist_response) | |
152 | ||
153 | def __check_filelist_response(self, response): | |
154 | if response.error: | |
155 | logging.debug("Error getting timestamp from %s" % self.hostname) | |
156 | return | |
157 | ||
158 | self.db.execute("DELETE FROM mirror_files WHERE mirror=%s", self.id) | |
159 | ||
160 | for file in response.body.splitlines(): | |
161 | self.db.execute("INSERT INTO mirror_files(mirror, filename) VALUES(%s, %s)", | |
162 | self.id, file) | |
163 | ||
164 | logging.info("Successfully updated mirror filelist from %s" % self.hostname) | |
165 | ||
166 | ||
167 | if __name__ == "__main__": | |
168 | m = Mirrors() | |
169 | ||
170 | for mirror in m.list(): | |
171 | print mirror.hostname, mirror.country_code |