]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blame - www/webapp/mirrors.py
Merge branch 'planet' into next
[people/shoehn/ipfire.org.git] / www / webapp / mirrors.py
CommitLineData
3add293a
MT
1#!/usr/bin/python
2
3import tornado.httpclient
4
5import random
3add293a
MT
6import threading
7import time
8
befc2e59 9from helpers import Item, _stringify, ping, json_loads
3add293a
MT
10
11class 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
101class 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
138class 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
196mirrors = Mirrors("mirrors.json")