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