]>
Commit | Line | Data |
---|---|---|
940227cb MT |
1 | #!/usr/bin/python |
2 | ||
9068dba1 MT |
3 | from __future__ import division |
4 | ||
5 | import datetime | |
940227cb | 6 | import logging |
0673d1b0 | 7 | import math |
54af860e | 8 | import os.path |
0673d1b0 | 9 | import random |
940227cb MT |
10 | import socket |
11 | import time | |
12 | import tornado.httpclient | |
9068dba1 MT |
13 | import tornado.netutil |
14 | import urlparse | |
940227cb | 15 | |
9068dba1 | 16 | from misc import Object |
60024cc8 | 17 | |
9068dba1 | 18 | class Downloads(Object): |
60024cc8 MT |
19 | @property |
20 | def total(self): | |
21 | ret = self.db.get("SELECT COUNT(*) AS total FROM log_download") | |
22 | ||
23 | return ret.total | |
24 | ||
25 | @property | |
26 | def today(self): | |
9068dba1 | 27 | ret = self.db.get("SELECT COUNT(*) AS today FROM log_download WHERE date::date = NOW()::date") |
60024cc8 MT |
28 | |
29 | return ret.today | |
30 | ||
31 | @property | |
32 | def yesterday(self): | |
9068dba1 | 33 | ret = self.db.get("SELECT COUNT(*) AS yesterday FROM log_download WHERE date::date = (NOW() - INTERVAL '1 day')::date") |
60024cc8 MT |
34 | |
35 | return ret.yesterday | |
36 | ||
37 | @property | |
38 | def daily_map(self): | |
9068dba1 MT |
39 | ret = self.db.query("SELECT date::date AS date, COUNT(*) AS downloads FROM log_download" |
40 | " WHERE date::date BETWEEN (NOW() - INTERVAL '30 days')::date AND NOW()::date GROUP BY date::date") | |
60024cc8 MT |
41 | |
42 | return ret | |
43 | ||
44 | def get_countries(self, duration="all"): | |
45 | query = "SELECT country_code, count(country_code) AS count FROM log_download" | |
46 | ||
47 | if duration == "today": | |
9068dba1 | 48 | query += " WHERE date::date = NOW()::date" |
60024cc8 MT |
49 | |
50 | query += " GROUP BY country_code ORDER BY count DESC" | |
51 | ||
52 | results = self.db.query(query) | |
53 | ret = {} | |
54 | ||
55 | count = sum([o.count for o in results]) | |
9068dba1 MT |
56 | if count: |
57 | for res in results: | |
58 | ret[res.country_code] = res.count / count | |
60024cc8 MT |
59 | |
60 | return ret | |
61 | ||
62 | def get_mirror_load(self, duration="all"): | |
63 | query = "SELECT mirror, COUNT(mirror) AS count FROM log_download" | |
64 | ||
65 | if duration == "today": | |
9068dba1 | 66 | query += " WHERE date::date = NOW()::date" |
60024cc8 MT |
67 | |
68 | query += " GROUP BY mirror ORDER BY count DESC" | |
69 | ||
70 | results = self.db.query(query) | |
71 | ret = {} | |
72 | ||
73 | count = sum([o.count for o in results]) | |
9068dba1 MT |
74 | if count: |
75 | for res in results: | |
76 | mirror = self.mirrors.get(res.mirror) | |
77 | ret[mirror.hostname] = res.count / count | |
60024cc8 MT |
78 | |
79 | return ret | |
80 | ||
81 | ||
9068dba1 | 82 | class Mirrors(Object): |
940227cb | 83 | def check_all(self): |
9068dba1 | 84 | for mirror in self.get_all(): |
940227cb MT |
85 | mirror.check() |
86 | ||
87 | def get(self, id): | |
9068dba1 | 88 | return Mirror(self.backend, id) |
940227cb | 89 | |
0673d1b0 | 90 | def get_all(self): |
9068dba1 MT |
91 | res = self.db.query("SELECT * FROM mirrors WHERE enabled = %s", True) |
92 | ||
93 | mirrors = [] | |
94 | for row in res: | |
95 | mirror = Mirror(self.backend, row.id, row) | |
96 | mirrors.append(mirror) | |
97 | ||
98 | return MirrorSet(self.backend, sorted(mirrors)) | |
0673d1b0 | 99 | |
bd17b7d1 | 100 | def get_all_up(self): |
9068dba1 MT |
101 | res = self.db.query("SELECT * FROM mirrors WHERE enabled = %s AND state = %s \ |
102 | ORDER BY hostname", True, "UP") | |
bd17b7d1 MT |
103 | |
104 | mirrors = [] | |
105 | for row in res: | |
9068dba1 | 106 | m = Mirror(self.backend, row.id, row) |
bd17b7d1 MT |
107 | mirrors.append(m) |
108 | ||
9068dba1 | 109 | return MirrorSet(self.backend, mirrors) |
bd17b7d1 | 110 | |
940227cb | 111 | def get_by_hostname(self, hostname): |
5488a9f4 | 112 | ret = self.db.get("SELECT * FROM mirrors WHERE hostname = %s", hostname) |
940227cb | 113 | |
5488a9f4 MT |
114 | if ret: |
115 | return Mirror(self.backend, ret.id, ret) | |
940227cb | 116 | |
54af860e MT |
117 | def get_with_file(self, filename, country=None): |
118 | # XXX quick and dirty solution - needs a performance boost | |
119 | mirror_ids = [m.mirror for m in self.db.query("SELECT mirror FROM mirror_files WHERE filename=%s", filename)] | |
120 | ||
121 | #if country: | |
122 | # # Sort out all mirrors that are not preferred to the given country | |
123 | # for mirror in self.get_for_country(country): | |
124 | # if not mirror.id in mirror_ids: | |
125 | # mirror_ids.remove(mirror.id) | |
126 | ||
127 | mirrors = [] | |
128 | for mirror_id in mirror_ids: | |
129 | mirror = self.get(mirror_id) | |
130 | if not mirror.state == "UP": | |
131 | continue | |
132 | mirrors.append(mirror) | |
133 | ||
54af860e MT |
134 | return mirrors |
135 | ||
5488a9f4 | 136 | def get_for_location(self, location, max_distance=4000, filename=None): |
119f55d7 | 137 | if not location: |
5488a9f4 MT |
138 | return [] |
139 | ||
140 | if filename: | |
141 | res = self.db.query("\ | |
142 | WITH client AS (SELECT point(%s, %s) AS location) \ | |
143 | SELECT * FROM mirrors WHERE mirrors.state = %s \ | |
144 | AND mirrors.id IN ( \ | |
145 | SELECT mirror FROM mirror_files WHERE filename = %s \ | |
146 | ) AND mirrors.id IN ( \ | |
147 | SELECT id FROM mirrors_locations, client \ | |
148 | WHERE geodistance(mirrors_locations.location, client.location) <= %s \ | |
149 | )", | |
150 | location.latitude, location.longitude, "UP", filename, max_distance) | |
151 | else: | |
152 | res = self.db.query("\ | |
153 | WITH client AS (SELECT point(%s, %s) AS location) \ | |
154 | SELECT * FROM mirrors WHERE mirrors.state = %s AND mirrors.id IN ( \ | |
155 | SELECT id FROM mirrors_locations, client \ | |
156 | WHERE geodistance(mirrors_locations.location, client.location) <= %s \ | |
157 | )", | |
158 | location.latitude, location.longitude, "UP", max_distance) | |
9068dba1 MT |
159 | |
160 | mirrors = [] | |
5488a9f4 MT |
161 | for row in res: |
162 | mirror = Mirror(self.backend, row.id, row) | |
163 | mirrors.append(mirror) | |
0673d1b0 | 164 | |
5488a9f4 | 165 | return sorted(mirrors) |
0673d1b0 | 166 | |
edd297c4 MT |
167 | def get_all_files(self): |
168 | files = [] | |
169 | ||
9068dba1 | 170 | for mirror in self.get_all(): |
edd297c4 MT |
171 | if not mirror.state == "UP": |
172 | continue | |
173 | ||
174 | for file in mirror.filelist: | |
175 | if not file in files: | |
176 | files.append(file) | |
177 | ||
178 | return files | |
179 | ||
5488a9f4 MT |
180 | def get_random(self, filename=None): |
181 | if filename: | |
182 | ret = self.db.get("SELECT * FROM mirrors WHERE state = %s \ | |
183 | AND mirrors.id IN (SELECT mirror FROM mirror_files \ | |
184 | WHERE filename = %s) ORDER BY RANDOM() LIMIT 1", "UP", filename) | |
185 | else: | |
186 | ret = self.db.get("SELECT * FROM mirrors WHERE state = %s \ | |
187 | ORDER BY RANDOM() LIMIT 1", "UP") | |
188 | ||
189 | if ret: | |
190 | return Mirror(self.backend, ret.id, ret) | |
191 | ||
192 | def file_exists(self, filename): | |
193 | ret = self.db.get("SELECT 1 FROM mirror_files \ | |
194 | WHERE filename = %s LIMIT 1", filename) | |
195 | ||
196 | if ret: | |
197 | return True | |
198 | ||
199 | return False | |
200 | ||
940227cb | 201 | |
9068dba1 MT |
202 | class MirrorSet(Object): |
203 | def __init__(self, backend, mirrors): | |
204 | Object.__init__(self, backend) | |
205 | ||
0673d1b0 MT |
206 | self._mirrors = mirrors |
207 | ||
208 | def __add__(self, other): | |
209 | mirrors = [] | |
210 | ||
211 | for mirror in self._mirrors + other._mirrors: | |
212 | if mirror in mirrors: | |
213 | continue | |
214 | ||
215 | mirrors.append(mirror) | |
216 | ||
9068dba1 | 217 | return MirrorSet(self.backend, mirrors) |
0673d1b0 MT |
218 | |
219 | def __sub__(self, other): | |
220 | mirrors = self._mirrors[:] | |
221 | ||
222 | for mirror in other._mirrors: | |
223 | if mirror in mirrors: | |
224 | mirrors.remove(mirror) | |
225 | ||
9068dba1 | 226 | return MirrorSet(self.backend, mirrors) |
0673d1b0 MT |
227 | |
228 | def __iter__(self): | |
229 | return iter(self._mirrors) | |
230 | ||
231 | def __len__(self): | |
232 | return len(self._mirrors) | |
233 | ||
234 | def __str__(self): | |
235 | return "<MirrorSet %s>" % ", ".join([m.hostname for m in self._mirrors]) | |
236 | ||
0673d1b0 MT |
237 | def get_with_file(self, filename): |
238 | with_file = [m.mirror for m in self.db.query("SELECT mirror FROM mirror_files WHERE filename=%s", filename)] | |
239 | ||
240 | mirrors = [] | |
241 | for mirror in self._mirrors: | |
242 | if mirror.id in with_file: | |
243 | mirrors.append(mirror) | |
244 | ||
9068dba1 | 245 | return MirrorSet(self.backend, mirrors) |
0673d1b0 MT |
246 | |
247 | def get_random(self): | |
248 | mirrors = [] | |
249 | for mirror in self._mirrors: | |
f1f7eb7e | 250 | for i in range(0, mirror.priority): |
0673d1b0 MT |
251 | mirrors.append(mirror) |
252 | ||
253 | return random.choice(mirrors) | |
254 | ||
9068dba1 MT |
255 | def get_for_location(self, location): |
256 | distance = 2500 | |
0673d1b0 MT |
257 | mirrors = [] |
258 | ||
119f55d7 | 259 | if location: |
9068dba1 | 260 | while len(mirrors) <= 3 and distance <= 8000: |
119f55d7 MT |
261 | for mirror in self._mirrors: |
262 | if mirror in mirrors: | |
263 | continue | |
0673d1b0 | 264 | |
9068dba1 MT |
265 | mirror_distance = mirror.distance_to(location) |
266 | if mirror_distance is None: | |
267 | continue | |
268 | ||
269 | if mirror_distance <= distance: | |
119f55d7 | 270 | mirrors.append(mirror) |
0673d1b0 | 271 | |
119f55d7 | 272 | distance *= 1.2 |
0673d1b0 | 273 | |
9068dba1 | 274 | return MirrorSet(self.backend, mirrors) |
0673d1b0 MT |
275 | |
276 | def get_with_state(self, state): | |
277 | mirrors = [] | |
278 | ||
279 | for mirror in self._mirrors: | |
280 | if mirror.state == state: | |
281 | mirrors.append(mirror) | |
282 | ||
9068dba1 MT |
283 | return MirrorSet(self.backend, mirrors) |
284 | ||
0673d1b0 | 285 | |
9068dba1 MT |
286 | class Mirror(Object): |
287 | def __init__(self, backend, id, data=None): | |
288 | Object.__init__(self, backend) | |
0673d1b0 | 289 | |
940227cb MT |
290 | self.id = id |
291 | ||
bd17b7d1 MT |
292 | if data: |
293 | self._info = data | |
294 | else: | |
295 | self._info = self.db.get("SELECT * FROM mirrors WHERE id = %s", self.id) | |
296 | self._info["url"] = self.generate_url() | |
940227cb | 297 | |
119f55d7 MT |
298 | self.__location = None |
299 | self.__country_name = None | |
300 | ||
54af860e MT |
301 | def __repr__(self): |
302 | return "<%s %s>" % (self.__class__.__name__, self.url) | |
303 | ||
304 | def __cmp__(self, other): | |
9068dba1 | 305 | ret = cmp(self.country_code, other.country_code) |
54af860e | 306 | |
9068dba1 MT |
307 | if not ret: |
308 | ret = cmp(self.hostname, other.hostname) | |
309 | ||
310 | return ret | |
940227cb | 311 | |
940227cb MT |
312 | def generate_url(self): |
313 | url = "http://%s" % self.hostname | |
314 | if not self.path.startswith("/"): | |
315 | url += "/" | |
316 | url += "%s" % self.path | |
317 | if not self.path.endswith("/"): | |
318 | url += "/" | |
319 | return url | |
320 | ||
9068dba1 MT |
321 | @property |
322 | def hostname(self): | |
323 | return self._info.hostname | |
324 | ||
325 | @property | |
326 | def path(self): | |
327 | return self._info.path | |
940227cb MT |
328 | |
329 | @property | |
330 | def address(self): | |
b898caea MT |
331 | for addr in self.addresses4: |
332 | return addr | |
333 | ||
334 | for addr in self.addresses6: | |
335 | return addr | |
940227cb | 336 | |
9068dba1 MT |
337 | @property |
338 | def owner(self): | |
339 | return self._info.owner | |
340 | ||
0673d1b0 MT |
341 | @property |
342 | def location(self): | |
119f55d7 | 343 | if self.__location is None: |
9068dba1 | 344 | self.__location = self.geoip.get_location(self.address) |
0673d1b0 MT |
345 | |
346 | return self.__location | |
347 | ||
348 | @property | |
349 | def latitude(self): | |
9068dba1 MT |
350 | if self.location: |
351 | return self.location.latitude | |
0673d1b0 MT |
352 | |
353 | @property | |
354 | def longitude(self): | |
9068dba1 MT |
355 | if self.location: |
356 | return self.location.longitude | |
0673d1b0 MT |
357 | |
358 | @property | |
359 | def coordinates(self): | |
360 | return (self.latitude, self.longitude) | |
361 | ||
362 | @property | |
363 | def coordiante_str(self): | |
364 | coordinates = [] | |
365 | ||
366 | for i in self.coordinates: | |
367 | coordinates.append("%s" % i) | |
368 | ||
369 | return ",".join(coordinates) | |
370 | ||
940227cb MT |
371 | @property |
372 | def country_code(self): | |
9068dba1 MT |
373 | if self.location: |
374 | return self.location.country | |
940227cb | 375 | |
0673d1b0 MT |
376 | @property |
377 | def country_name(self): | |
119f55d7 | 378 | if self.__country_name is None: |
9068dba1 | 379 | self.__country_name = self.geoip.get_country_name(self.country_code) |
119f55d7 MT |
380 | |
381 | return self.__country_name | |
0673d1b0 MT |
382 | |
383 | @property | |
9068dba1 MT |
384 | def location_str(self): |
385 | location = [] | |
386 | ||
387 | if self._info.location: | |
388 | location.append(self._info.location) | |
0673d1b0 | 389 | |
9068dba1 MT |
390 | elif self.location: |
391 | location.append(self.location.city) | |
392 | location.append(self.country_name) | |
393 | ||
394 | return ", ".join([s for s in location if s]) | |
0673d1b0 MT |
395 | |
396 | @property | |
9068dba1 MT |
397 | def asn(self): |
398 | if not hasattr(self, "__asn"): | |
399 | self.__asn = self.geoip.get_asn(self.address) | |
0673d1b0 | 400 | |
9068dba1 | 401 | return self.__asn |
0673d1b0 | 402 | |
940227cb MT |
403 | @property |
404 | def filelist(self): | |
405 | filelist = self.db.query("SELECT filename FROM mirror_files WHERE mirror=%s ORDER BY filename", self.id) | |
406 | return [f.filename for f in filelist] | |
407 | ||
54af860e MT |
408 | @property |
409 | def prefix(self): | |
54af860e MT |
410 | return "" |
411 | ||
9068dba1 MT |
412 | @property |
413 | def url(self): | |
414 | return self._info.url | |
415 | ||
416 | def build_url(self, filename): | |
417 | return urlparse.urljoin(self.url, filename) | |
418 | ||
419 | @property | |
420 | def last_update(self): | |
421 | return self._info.last_update | |
422 | ||
423 | @property | |
424 | def state(self): | |
425 | return self._info.state | |
426 | ||
940227cb MT |
427 | def set_state(self, state): |
428 | logging.info("Setting state of %s to %s" % (self.hostname, state)) | |
429 | ||
430 | if self.state == state: | |
431 | return | |
432 | ||
9068dba1 | 433 | self.db.execute("UPDATE mirrors SET state = %s WHERE id = %s", state, self.id) |
940227cb MT |
434 | |
435 | # Reload changed settings | |
bd17b7d1 MT |
436 | if hasattr(self, "_info"): |
437 | self._info["state"] = state | |
940227cb | 438 | |
9068dba1 MT |
439 | @property |
440 | def enabled(self): | |
441 | return self._info.enabled | |
442 | ||
443 | @property | |
444 | def disabled(self): | |
445 | return not self.enabled | |
446 | ||
940227cb MT |
447 | def check(self): |
448 | logging.info("Running check for mirror %s" % self.hostname) | |
449 | ||
450 | self.check_timestamp() | |
451 | self.check_filelist() | |
452 | ||
453 | def check_state(self): | |
454 | logging.debug("Checking state of mirror %s" % self.id) | |
455 | ||
9068dba1 | 456 | if not self.enabled: |
940227cb | 457 | self.set_state("DOWN") |
9068dba1 MT |
458 | return |
459 | ||
460 | now = datetime.datetime.utcnow() | |
461 | ||
462 | time_delta = now - self.last_update | |
463 | time_diff = time_delta.total_seconds() | |
940227cb | 464 | |
9068dba1 MT |
465 | time_down = self.settings.get_int("mirrors_time_down", 3*24*60*60) |
466 | if time_diff >= time_down: | |
940227cb | 467 | self.set_state("DOWN") |
9068dba1 | 468 | return |
940227cb | 469 | |
9068dba1 MT |
470 | time_outofsync = self.settings.get_int("mirrors_time_outofsync", 6*60*60) |
471 | if time_diff >= time_outofsync: | |
472 | self.set_state("OUTOFSYNC") | |
940227cb MT |
473 | return |
474 | ||
9068dba1 MT |
475 | self.set_state("UP") |
476 | ||
477 | def check_timestamp(self): | |
940227cb MT |
478 | http = tornado.httpclient.AsyncHTTPClient() |
479 | ||
480 | http.fetch(self.url + ".timestamp", | |
54af860e | 481 | headers={ "Pragma" : "no-cache" }, |
940227cb MT |
482 | callback=self.__check_timestamp_response) |
483 | ||
484 | def __check_timestamp_response(self, response): | |
485 | if response.error: | |
486 | logging.debug("Error getting timestamp from %s" % self.hostname) | |
a3ee39ce | 487 | self.set_state("DOWN") |
940227cb MT |
488 | return |
489 | ||
490 | try: | |
491 | timestamp = int(response.body.strip()) | |
492 | except ValueError: | |
493 | timestamp = 0 | |
494 | ||
ea324f48 | 495 | timestamp = datetime.datetime.utcfromtimestamp(timestamp) |
9068dba1 MT |
496 | |
497 | self.db.execute("UPDATE mirrors SET last_update = %s WHERE id = %s", | |
940227cb MT |
498 | timestamp, self.id) |
499 | ||
500 | # Reload changed settings | |
bd17b7d1 MT |
501 | if hasattr(self, "_info"): |
502 | self._info["timestamp"] = timestamp | |
940227cb MT |
503 | |
504 | self.check_state() | |
505 | ||
506 | logging.info("Successfully updated timestamp from %s" % self.hostname) | |
507 | ||
508 | def check_filelist(self): | |
54af860e | 509 | # XXX need to remove data from disabled mirrors |
9068dba1 | 510 | if not self.enabled: |
940227cb MT |
511 | return |
512 | ||
513 | http = tornado.httpclient.AsyncHTTPClient() | |
514 | ||
515 | http.fetch(self.url + ".filelist", | |
54af860e | 516 | headers={ "Pragma" : "no-cache" }, |
940227cb MT |
517 | callback=self.__check_filelist_response) |
518 | ||
519 | def __check_filelist_response(self, response): | |
520 | if response.error: | |
521 | logging.debug("Error getting timestamp from %s" % self.hostname) | |
522 | return | |
523 | ||
56b9c1d8 | 524 | files = self.filelist |
940227cb MT |
525 | |
526 | for file in response.body.splitlines(): | |
56b9c1d8 MT |
527 | file = os.path.join(self.prefix, file) |
528 | ||
529 | if file in files: | |
530 | files.remove(file) | |
531 | continue | |
532 | ||
940227cb | 533 | self.db.execute("INSERT INTO mirror_files(mirror, filename) VALUES(%s, %s)", |
56b9c1d8 MT |
534 | self.id, file) |
535 | ||
536 | for file in files: | |
537 | self.db.execute("DELETE FROM mirror_files WHERE mirror=%s AND filename=%s", | |
538 | self.id, file) | |
940227cb MT |
539 | |
540 | logging.info("Successfully updated mirror filelist from %s" % self.hostname) | |
541 | ||
54af860e MT |
542 | @property |
543 | def prefer_for_countries(self): | |
0673d1b0 MT |
544 | countries = self._info.get("prefer_for_countries", "") |
545 | if countries: | |
546 | return sorted(countries.split(", ")) | |
54af860e | 547 | |
0673d1b0 MT |
548 | return [] |
549 | ||
550 | @property | |
551 | def prefer_for_countries_names(self): | |
9068dba1 MT |
552 | countries = [self.geoip.get_country_name(c.upper()) for c in self.prefer_for_countries] |
553 | ||
554 | return sorted(countries) | |
54af860e | 555 | |
119f55d7 | 556 | def distance_to(self, location, ignore_preference=False): |
0673d1b0 | 557 | if not location: |
9068dba1 | 558 | return None |
940227cb | 559 | |
9068dba1 MT |
560 | country_code = None |
561 | if location.country: | |
562 | country_code = location.country.lower() | |
563 | ||
564 | if not ignore_preference and country_code in self.prefer_for_countries: | |
0673d1b0 MT |
565 | return 0 |
566 | ||
9068dba1 MT |
567 | # http://www.movable-type.co.uk/scripts/latlong.html |
568 | ||
569 | if self.latitude is None: | |
570 | return None | |
571 | ||
572 | if self.longitude is None: | |
573 | return None | |
574 | ||
575 | earth = 6371 # km | |
576 | delta_lat = math.radians(self.latitude - location.latitude) | |
577 | delta_lon = math.radians(self.longitude - location.longitude) | |
578 | ||
579 | lat1 = math.radians(self.latitude) | |
580 | lat2 = math.radians(location.latitude) | |
581 | ||
582 | a = math.sin(delta_lat / 2) ** 2 | |
583 | a += math.cos(lat1) * math.cos(lat2) * (math.sin(delta_lon / 2) ** 2) | |
0673d1b0 | 584 | |
9068dba1 MT |
585 | b1 = math.sqrt(a) |
586 | b2 = math.sqrt(1 - a) | |
0673d1b0 | 587 | |
9068dba1 MT |
588 | c = 2 * math.atan2(b1, b2) |
589 | ||
590 | return c * earth | |
0673d1b0 MT |
591 | |
592 | def traffic(self, since): | |
593 | # XXX needs to be done better | |
594 | ||
595 | files = {} | |
596 | for entry in self.db.query("SELECT filename, filesize FROM files"): | |
597 | files[entry.filename] = entry.filesize | |
598 | ||
599 | query = "SELECT COUNT(filename) as count, filename FROM log_download WHERE mirror = %s" | |
600 | query += " AND date >= %s GROUP BY filename" | |
601 | ||
602 | traffic = 0 | |
603 | for entry in self.db.query(query, self.id, since): | |
604 | if files.has_key(entry.filename): | |
605 | traffic += entry.count * files[entry.filename] | |
606 | ||
607 | return traffic | |
608 | ||
609 | @property | |
610 | def priority(self): | |
f1f7eb7e | 611 | return self._info.get("priority", 10) |
940227cb | 612 | |
bd17b7d1 MT |
613 | @property |
614 | def development(self): | |
ea324f48 | 615 | return self._info.get("mirrorlist_devel", False) |
bd17b7d1 MT |
616 | |
617 | @property | |
618 | def mirrorlist(self): | |
9068dba1 MT |
619 | return self._info.get("mirrorlist", False) |
620 | ||
621 | @property | |
622 | def addresses(self): | |
623 | if not hasattr(self, "__addresses"): | |
b898caea MT |
624 | try: |
625 | addrinfo = socket.getaddrinfo(self.hostname, 0, socket.AF_UNSPEC, socket.SOCK_STREAM) | |
626 | except: | |
627 | raise Exception("Could not resolve %s" % self.hostname) | |
9068dba1 MT |
628 | |
629 | ret = [] | |
630 | for family, socktype, proto, canonname, address in addrinfo: | |
631 | if family == socket.AF_INET: | |
632 | address, port = address | |
633 | elif family == socket.AF_INET6: | |
634 | address, port, flowid, scopeid = address | |
635 | ret.append((family, address)) | |
636 | ||
637 | self.__addresses = ret | |
638 | ||
639 | return self.__addresses | |
640 | ||
641 | @property | |
642 | def addresses6(self): | |
643 | return [address for family, address in self.addresses if family == socket.AF_INET6] | |
644 | ||
645 | @property | |
646 | def addresses4(self): | |
647 | return [address for family, address in self.addresses if family == socket.AF_INET] |