table.mirrors td.hostname {
text-align: right;
padding-left: 2em;
+ width: 10em;
}
table.mirrors td.down {
<td>{{ _("Owner") }}</td>
<td>{{ item.owner }}</td>
</tr>
+ {% if item.prefer_for_countries %}
+ <tr>
+ <td>{{ _("Preferred for") }}:</td>
+ <td>{{ locale.list(item.prefer_for_countries_names) }}</td>
+ </tr>
+ {% end %}
</table>
<p class="links">
{{ _("The mirror <em>%s</em> is located in %s.") % (item.hostname, item.location_str) }}
<br class="clear" />
<img class="map"
- src="http://maps.google.com/maps/api/staticmap?center={{ item.coordiantes }}&size=640x280&zoom=6&markers=color:blue|label:.|{{ item.coordiantes }}&sensor=false"
+ src="http://maps.google.com/maps/api/staticmap?center={{ item.coordiante_str }}&size=640x280&zoom=6&markers=color:blue|label:.|{{ item.coordiante_str }}&sensor=false"
alt="{{ _("Location of the server") }}" />
</p>
{% end block %}
<br class="clear" />
- <h3>{{ _("List of servers") }}</h3>
- <table class="mirrors">
- {% for mirror in mirrors %}
- <tr>
- <td class="hostname {{ mirror.state.lower() }}">
- <a href="/mirror/{{ mirror.id }}">{{ mirror.hostname }}</a>
- </td>
- {% if not mirror.state == "UP" %}
- <td>{{ _("Last update") }}: {{ locale.format_date(mirror.last_update) }}.</td>
- {% else %}
- <td> </td>
- {% end %}
- </tr>
- {% end %}
- </table>
+ {% if other_mirrors %}
+ <h3>{{ _("Mirror servers nearby") }}</h3>
+ {{ modules.MirrorsTable(preferred_mirrors) }}
+
+ <h3>{{ _("Worldwide mirror servers") }}</h3>
+ {{ modules.MirrorsTable(other_mirrors) }}
+ {% else %}
+ <h3>{{ _("Worldwide mirror servers") }}</h3>
+ {{ modules.MirrorsTable(preferred_mirrors) }}
+ {% end %}
{% end block %}
{% block sidebar %}
--- /dev/null
+<table class="mirrors">
+ {% for mirror in mirrors %}
+ <tr>
+ <td class="hostname {{ mirror.state.lower() }}">
+ <a href="/mirror/{{ mirror.id }}">{{ mirror.hostname }}</a>
+ </td>
+ <td>
+ <img src="{{ static_url("images/flags/%s.png") % mirror.country_code }}" alt="{{ mirror.country_code }}" />
+ {{ mirror.location_str }}
+ </td>
+ </tr>
+ {% end %}
+</table>
+<br class="clear" />
+
"Go to the wiki","Zum Wiki"
"Documentation","Dokumentation"
"Configuration","Konfiguration"
+"Mirror servers nearby","Mirror-Server in der Nähe"
+"Worldwide mirror servers","Weltweite Mirror-Server"
+"Preferred for","Bevorzugt für"
def __init__(self):
settings = dict(
cookie_secret = "aXBmaXJlY29va2llc2VjcmV0Cg==",
- debug = False,
+ debug = True,
gzip = True,
login_url = "/login",
template_path = os.path.join(BASEDIR, "templates"),
ui_modules = {
"Menu" : MenuModule,
"MirrorItem" : MirrorItemModule,
+ "MirrorsTable" : MirrorsTableModule,
"NewsItem" : NewsItemModule,
"NewsLine" : NewsLineModule,
"PlanetEntry" : PlanetEntryModule,
import re
from databases import Databases
+from memcached import Memcached
from misc import Singleton
class GeoIP(object):
def db(self):
return Databases().geoip
+ @property
+ def memcached(self):
+ return Memcached()
+
def __encode_ip(self, addr):
# We get a tuple if there were proxy headers.
addr = addr.split(", ")
return int(((int(a1) * 256 + int(a2)) * 256 + int(a3)) * 256 + int(a4) + 100)
def get_country(self, addr):
- return self.db.get("SELECT * FROM ip_group_country WHERE ip_start <= %s \
- ORDER BY ip_start DESC LIMIT 1;", self.__encode_ip(addr)).country_code.lower()
+ addr = self.__encode_ip(addr)
+
+ mem_id = "geoip-country-%s" % addr
+ ret = self.memcached.get(mem_id)
+
+ if not ret:
+ ret = self.db.get("SELECT * FROM ip_group_country WHERE ip_start <= %s \
+ ORDER BY ip_start DESC LIMIT 1;", addr).country_code.lower()
+ self.memcached.set(mem_id, ret, 3600)
+
+ return ret
def get_all(self, addr):
- # XXX should be done with a join
- location = self.db.get("SELECT location FROM ip_group_city WHERE ip_start <= %s \
- ORDER BY ip_start DESC LIMIT 1;", self.__encode_ip(addr)).location
-
- return self.db.get("SELECT * FROM locations WHERE id = %s", int(location))
+ addr = self.__encode_ip(addr)
+
+ mem_id = "geoip-all-%s" % addr
+ ret = self.memcached.get(mem_id)
+
+ if not ret:
+ # XXX should be done with a join
+ location = self.db.get("SELECT location FROM ip_group_city WHERE ip_start <= %s \
+ ORDER BY ip_start DESC LIMIT 1;", addr).location
+
+ ret = self.db.get("SELECT * FROM locations WHERE id = %s", int(location))
+ self.memcached.set(mem_id, ret, 3600)
+
+ # If location was not determinable
+ if ret.latitude == 0 and ret.longitude == 0:
+ return None
+
+ return ret
def get_country_name(self, code):
name = "Unknown"
#!/usr/bin/python
import logging
+import math
import os.path
+import random
import socket
import time
import tornado.httpclient
from databases import Databases
from geoip import GeoIP
+from memcached import Memcached
from misc import Singleton
class Mirrors(object):
def db(self):
return Databases().webapp
+ @property
+ def memcached(self):
+ return Memcached()
+
def list(self):
return [Mirror(m.id) for m in self.db.query("SELECT id FROM mirrors ORDER BY state")]
def get(self, id):
return Mirror(id)
+ def get_all(self):
+ return MirrorSet(self.list())
+
def get_by_hostname(self, hostname):
mirror = self.db.get("SELECT id FROM mirrors WHERE hostname=%s", hostname)
for mirror in mirrors:
yield self.get(mirror.id)
+ def get_for_location(self, addr):
+ distance = 10
+
+ mirrors = []
+ all_mirrors = self.list()
+
+ while all_mirrors and len(mirrors) <= 2 and distance <= 270:
+ for mirror in all_mirrors:
+ if mirror.distance_to(addr) <= distance:
+ mirrors.append(mirror)
+ all_mirrors.remove(mirror)
+
+ distance *= 1.2
+
+ return mirrors
+
def get_all_files(self):
files = []
return files
+class MirrorSet(object):
+ def __init__(self, mirrors):
+ self._mirrors = mirrors
+
+ def __add__(self, other):
+ mirrors = []
+
+ for mirror in self._mirrors + other._mirrors:
+ if mirror in mirrors:
+ continue
+
+ mirrors.append(mirror)
+
+ return MirrorSet(mirrors)
+
+ def __sub__(self, other):
+ mirrors = self._mirrors[:]
+
+ for mirror in other._mirrors:
+ if mirror in mirrors:
+ mirrors.remove(mirror)
+
+ return MirrorSet(mirrors)
+
+ def __iter__(self):
+ return iter(self._mirrors)
+
+ def __len__(self):
+ return len(self._mirrors)
+
+ def __str__(self):
+ return "<MirrorSet %s>" % ", ".join([m.hostname for m in self._mirrors])
+
+ @property
+ def db(self):
+ return Mirrors().db
+
+ def get_with_file(self, filename):
+ with_file = [m.mirror for m in self.db.query("SELECT mirror FROM mirror_files WHERE filename=%s", filename)]
+
+ mirrors = []
+ for mirror in self._mirrors:
+ if mirror.id in with_file:
+ mirrors.append(mirror)
+
+ return MirrorSet(mirrors)
+
+ def get_random(self):
+ mirrors = []
+ for mirror in self._mirrors:
+ for i in range(0, mirror.priority + 1):
+ mirrors.append(mirror)
+
+ return random.choice(mirrors)
+
+ def get_for_country(self, country):
+ mirrors = []
+
+ for mirror in self._mirrors:
+ if country in mirror.prefer_for_countries:
+ mirrors.append(mirror)
+
+ return MirrorSet(mirrors)
+
+ def get_for_location(self, addr):
+ distance = 10
+
+ mirrors = []
+
+ while len(mirrors) <= 2 and distance <= 270:
+ for mirror in self._mirrors:
+ if mirror in mirrors:
+ continue
+
+ if mirror.distance_to(addr) <= distance:
+ mirrors.append(mirror)
+
+ distance *= 1.2
+
+ return MirrorSet(mirrors)
+
+ def get_with_state(self, state):
+ mirrors = []
+
+ for mirror in self._mirrors:
+ if mirror.state == state:
+ mirrors.append(mirror)
+
+ return MirrorSet(mirrors)
+
+
class Mirror(object):
def __init__(self, id):
self.id = id
return Databases().webapp
def reload(self):
- self._info = self.db.get("SELECT * FROM mirrors WHERE id=%s", self.id)
- self._info["url"] = self.generate_url()
+ memcached = Memcached()
+ mem_id = "mirror-%s" % self.id
+
+ self._info = memcached.get(mem_id)
+ if not self._info:
+ self._info = self.db.get("SELECT * FROM mirrors WHERE id=%s", self.id)
+ self._info["url"] = self.generate_url()
+
+ memcached.set(mem_id, self._info, 60)
def generate_url(self):
url = "http://%s" % self.hostname
def address(self):
return socket.gethostbyname(self.hostname)
+ @property
+ def location(self):
+ if not hasattr(self, "__location"):
+ self.__location = GeoIP().get_all(self.address)
+
+ return self.__location
+
+ @property
+ def latitude(self):
+ return self.location.latitude
+
+ @property
+ def longitude(self):
+ return self.location.longitude
+
+ @property
+ def coordinates(self):
+ return (self.latitude, self.longitude)
+
+ @property
+ def coordiante_str(self):
+ coordinates = []
+
+ for i in self.coordinates:
+ coordinates.append("%s" % i)
+
+ return ",".join(coordinates)
+
@property
def country_code(self):
return GeoIP().get_country(self.address).lower() or "unknown"
+ @property
+ def country_name(self):
+ return GeoIP().get_country_name(self.country_code)
+
+ @property
+ def city(self):
+ if self._info["city"]:
+ return self._info["city"]
+
+ return self.location.city
+
+ @property
+ def location_str(self):
+ s = self.country_name
+ if self.city:
+ s = "%s, %s" % (self.city, s)
+
+ return s
+
@property
def filelist(self):
filelist = self.db.query("SELECT filename FROM mirror_files WHERE mirror=%s ORDER BY filename", self.id)
@property
def prefer_for_countries(self):
- return self._info.get("prefer_for_countries", "").split()
+ countries = self._info.get("prefer_for_countries", "")
+ if countries:
+ return sorted(countries.split(", "))
+ return []
+
+ @property
+ def prefer_for_countries_names(self):
+ return sorted([GeoIP().get_country_name(c) for c in self.prefer_for_countries])
+ def distance_to(self, addr):
+ location = GeoIP().get_all(addr)
+ if not location:
+ return 0
-if __name__ == "__main__":
- m = Mirrors()
+ if location.country_code.lower() in self.prefer_for_countries:
+ return 0
+
+ distance_vector = (
+ self.latitude - location.latitude,
+ self.longitude - location.longitude
+ )
+
+ distance = 0
+ for i in distance_vector:
+ distance += i**2
+
+ return math.sqrt(distance)
+
+ def traffic(self, since):
+ # XXX needs to be done better
+
+ files = {}
+ for entry in self.db.query("SELECT filename, filesize FROM files"):
+ files[entry.filename] = entry.filesize
+
+ query = "SELECT COUNT(filename) as count, filename FROM log_download WHERE mirror = %s"
+ query += " AND date >= %s GROUP BY filename"
+
+ traffic = 0
+ for entry in self.db.query(query, self.id, since):
+ if files.has_key(entry.filename):
+ traffic += entry.count * files[entry.filename]
+
+ return traffic
+
+ @property
+ def priority(self):
+ return self._info.get("priority", 1) * 10
- for mirror in m.list():
- print mirror.hostname, mirror.country_code
class DownloadFileHandler(BaseHandler):
def get(self, filename):
- country_code = self.geoip.get_country(self.request.remote_ip)
-
self.set_header("Pragma", "no-cache")
- self.set_header("X-Mirror-Client-Country", country_code)
- mirrors = self.mirrors.get_with_file(filename, country=country_code)
- if not mirrors:
- self.mirrors.get_with_file(filename)
+ # Get all mirrors...
+ mirrors = self.mirrors.get_all()
+ mirrors = mirrors.get_with_file(filename)
+ mirrors = mirrors.get_with_state("UP")
if not mirrors:
raise tornado.web.HTTPError(404, "File not found: %s" % filename)
- mirror = random.choice(mirrors)
+ # Find mirrors located near to the user.
+ # If we have not found any, we use all.
+ if len(mirrors) <= 3:
+ #mirrors_nearby = mirrors.get_for_location(self.request.remote_ip)
+ mirrors_nearby = mirrors.get_for_location("193.59.194.101")
+ if mirrors_nearby:
+ mirrors = mirrors_nearby
+
+ mirror = mirrors.get_random()
self.redirect(mirror.url + filename[len(mirror.prefix):])
class MirrorIndexHandler(BaseHandler):
def get(self):
- mirrors = self.mirrors.list()
+ ip_addr = self.get_argument("addr", self.request.remote_ip)
- self.render("mirrors.html", mirrors=mirrors)
+ # Get a list of all mirrors.
+ all_mirrors = self.mirrors.get_all()
+
+ # Choose the preferred ones by their location.
+ preferred_mirrors = all_mirrors.get_for_location(ip_addr)
+
+ # Remove the preferred ones from the list of the rest.
+ other_mirrors = all_mirrors - preferred_mirrors
+
+ self.render("mirrors.html",
+ preferred_mirrors=preferred_mirrors, other_mirrors=other_mirrors)
class MirrorItemHandler(BaseHandler):
def get(self, id):
+ _ = self.locale.translate
+
mirror = self.mirrors.get(id)
if not mirror:
raise tornado.web.HTTPError(404)
- ip = socket.gethostbyname(mirror.hostname)
- mirror.location = self.geoip.get_all(ip)
-
- # Shortcut for coordiantes
- mirror.coordiantes = "%s,%s" % \
- (mirror.location.latitude, mirror.location.longitude)
-
- # Nice string for the user
- mirror.location_str = mirror.location.country_code
- if mirror.location.city:
- mirror.location_str = "%s, %s" % \
- (mirror.location.city, mirror.location_str)
-
self.render("mirrors-item.html", item=mirror)
countries.append(country)
return self.render_string("modules/stasy-table-geo.html", countries=countries)
+
+
+class MirrorsTableModule(UIModule):
+ def render(self, mirrors):
+ return self.render_string("modules/mirrors-table.html", mirrors=mirrors)