]>
Commit | Line | Data |
---|---|---|
3add293a MT |
1 | #!/usr/bin/python |
2 | ||
3 | import tornado.httpclient | |
4 | ||
5 | import random | |
3add293a MT |
6 | import threading |
7 | import time | |
8 | ||
befc2e59 | 9 | from helpers import Item, _stringify, ping, json_loads |
3add293a MT |
10 | |
11 | class Mirrors(threading.Thread): | |
12 | def __init__(self, filename): | |
13 | threading.Thread.__init__(self, name="Mirror Monitor") | |
14 | ||
15 | self.items = [] | |
16 | self.load(filename) | |
17 | ||
18 | self.__running = True | |
19 | ||
20 | self.start() | |
21 | ||
22 | def load(self, filename): | |
23 | f = open(filename) | |
24 | data = f.read() | |
25 | f.close() | |
26 | ||
befc2e59 | 27 | for item in json_loads(data): |
3add293a MT |
28 | self.items.append(MirrorItem(**_stringify(item))) |
29 | ||
30 | @property | |
31 | def all(self): | |
32 | return sorted(self.items) | |
33 | ||
34 | @property | |
35 | def random(self): | |
36 | # Doesnt work :( | |
37 | #return random.shuffle(self.items) | |
38 | ret = [] | |
39 | items = self.items[:] | |
40 | while items: | |
41 | rnd = random.randint(0, len(items)-1) | |
42 | ret.append(items.pop(rnd)) | |
43 | return ret | |
44 | ||
45 | @property | |
46 | def reachable(self): | |
47 | ret = [] | |
48 | for mirror in self.items: | |
49 | if not mirror.reachable: | |
50 | continue | |
51 | ret.append(mirror) | |
52 | return ret | |
53 | ||
54 | @property | |
55 | def unreachable(self): | |
56 | ret = [] | |
57 | for mirror in self.all: | |
58 | if mirror in self.reachable: | |
59 | continue | |
60 | ret.append(mirror) | |
61 | return ret | |
62 | ||
63 | def pickone(self, reachable=False): | |
64 | mirrors = self.items | |
65 | if reachable: | |
66 | mirrors = self.reachable | |
67 | if not mirrors: | |
68 | return None | |
69 | return random.choice(mirrors) | |
70 | ||
71 | def with_file(self, path): | |
72 | ret = [] | |
73 | for mirror in self.random: | |
74 | if not mirror["serves"]["isos"]: | |
75 | continue | |
76 | if path in mirror.files: | |
77 | ret.append(mirror) | |
78 | return ret | |
79 | ||
80 | def shutdown(self): | |
81 | self.__running = False | |
82 | ||
83 | def run(self): | |
84 | for mirror in self.random: | |
85 | if not self.__running: | |
86 | return | |
87 | mirror.update() | |
88 | ||
89 | count = 0 | |
90 | while self.__running: | |
91 | if not count: | |
92 | count = 300 # 30 secs | |
93 | mirror = self.pickone() | |
94 | if mirror: | |
95 | mirror.update() | |
96 | ||
97 | time.sleep(0.1) | |
98 | count -= 1 | |
99 | ||
100 | ||
101 | class MirrorItem(Item): | |
102 | def __init__(self, *args, **kwargs): | |
103 | Item.__init__(self, *args, **kwargs) | |
104 | ||
105 | self.filelist = MirrorFilelist(self) | |
106 | self.latency = "N/A" | |
107 | ||
108 | def __cmp__(self, other): | |
109 | return cmp(self.name, other.name) | |
110 | ||
111 | def update(self): | |
112 | self.latency = ping(self.hostname) or "N/A" | |
113 | if self.filelist.outdated: | |
114 | self.filelist.update() | |
115 | ||
116 | @property | |
117 | def reachable(self): | |
118 | return not self.latency == "N/A" | |
119 | ||
120 | @property | |
121 | def url(self): | |
122 | ret = "http://" + self.hostname | |
123 | if not self.path.startswith("/"): | |
124 | ret += "/" | |
125 | ret += self.path | |
126 | if not ret.endswith("/"): | |
127 | ret += "/" | |
128 | return ret | |
129 | ||
130 | @property | |
131 | def files(self): | |
132 | return self.filelist.files | |
133 | ||
134 | def has_file(self, path): | |
135 | return path in self.files | |
136 | ||
137 | ||
138 | class MirrorFilelist(object): | |
139 | def __init__(self, mirror): | |
140 | self.mirror = mirror | |
141 | ||
142 | self.__files = [] | |
143 | self.__time = 0 | |
144 | ||
145 | #self.update(now=True) | |
146 | ||
147 | def update(self, now=False): | |
148 | args = {} | |
149 | ||
150 | if now: | |
151 | while not self.mirror.reachable: | |
152 | time.sleep(10) | |
153 | ||
154 | http = tornado.httpclient.HTTPClient() | |
155 | ||
156 | if not now: | |
157 | http = tornado.httpclient.AsyncHTTPClient() | |
158 | args["callback"] = self.on_response | |
159 | ||
160 | try: | |
161 | reponse = http.fetch(self.mirror.url + ".filelist", **args) | |
162 | except tornado.httpclient.HTTPError: | |
163 | self.__time = time.time() | |
164 | return | |
165 | ||
166 | if now: | |
167 | self.on_response(reponse) | |
168 | ||
169 | def on_response(self, response): | |
170 | self.__files = [] | |
171 | self.__time = time.time() | |
172 | ||
173 | if not response.code == 200: | |
174 | return | |
175 | ||
176 | # If invalid html content... | |
177 | if response.body.startswith("<!"): | |
178 | return | |
179 | ||
180 | for line in response.body.split("\n"): | |
181 | if not line: | |
182 | continue | |
183 | self.__files.append(line) | |
184 | ||
185 | @property | |
186 | def outdated(self): | |
187 | return (time.time() - self.__time) > 60*60 | |
188 | ||
189 | @property | |
190 | def files(self): | |
191 | #if self.outdated: | |
192 | # self.update() | |
193 | return self.__files | |
194 | ||
195 | ||
196 | mirrors = Mirrors("mirrors.json") |