--- /dev/null
+[
+ {
+ "name" : "IPFire Project",
+ "location" : {
+ "city" : "Cologne",
+ "country" : "Germany",
+ "country_code" : "de"
+ },
+ "hostname" : "mirror1.ipfire.org",
+ "path" : "",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : true,
+ "pakfire3" : true
+ }
+ },
+
+ {
+ "name" : "Ronald Wiesinger",
+ "location" : {
+ "city" : "Vienna",
+ "country" : "Austria",
+ "country_code" : "at"
+ },
+ "hostname" : "www.rowie.at",
+ "path" : "/ipfire",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : false,
+ "pakfire3" : false
+ }
+ },
+
+ {
+ "name" : "Jan Paul Tücking",
+ "location" : {
+ "city" : "Karlsruhe",
+ "country" : "Germany",
+ "country_code" : "de"
+ },
+ "hostname" : "ipfire.earl-net.com",
+ "path" : "",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : false,
+ "pakfire3" : false
+ }
+ },
+
+ {
+ "name" : "Markus Villwock",
+ "location" : {
+ "city" : "Hannover",
+ "country" : "Germany",
+ "country_code" : "de"
+ },
+ "hostname" : "kraefte.net",
+ "path" : "/ipfire",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : true,
+ "pakfire3" : false
+ }
+ },
+
+ {
+ "name" : "ISP42",
+ "location" : {
+ "city" : "Hannover",
+ "country" : "Germany",
+ "country_code" : "de"
+ },
+ "hostname" : "mirror2.ipfire.org",
+ "path" : "",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : true,
+ "pakfire3" : true
+ }
+ },
+
+ {
+ "name" : "Peter Schälchli",
+ "location" : {
+ "city" : "Paris",
+ "country" : "France",
+ "country_code" : "fr"
+ },
+ "hostname" : "mirror3.ipfire.org",
+ "path" : "",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : true,
+ "pakfire3" : false
+ }
+ },
+
+ {
+ "name" : "Peter Schälchli",
+ "location" : {
+ "city" : "Cologne",
+ "country" : "Germany",
+ "country_code" : "de"
+ },
+ "hostname" : "mirror5.ipfire.org",
+ "path" : "",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : true,
+ "pakfire3" : false
+ }
+ },
+
+ {
+ "name" : "Kim Barthel",
+ "location" : {
+ "city" : "Stuttgart",
+ "country" : "Germany",
+ "country_code" : "de"
+ },
+ "hostname" : "ipfire.kbarthel.de",
+ "path" : "",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : false,
+ "pakfire3" : false
+ }
+ },
+
+ {
+ "name" : "Kim Barthel",
+ "location" : {
+ "city" : "Stuttgart",
+ "country" : "Germany",
+ "country_code" : "de"
+ },
+ "hostname" : "ipfire.1l0v3u.com",
+ "path" : "",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : false,
+ "pakfire3" : false
+ }
+ },
+
+ {
+ "name" : "Sebastian Winter",
+ "location" : {
+ "city" : "Minden",
+ "country" : "Germany",
+ "country_code" : "de"
+ },
+ "hostname" : "ipfiremirror.wintertech.de",
+ "path" : "",
+ "serves" : {
+ "isos" : true,
+ "pakfire2" : false,
+ "pakfire3" : false
+ }
+ }
+]
table.download-torrents td.seeds,td.peers {
text-align: right;
}
+
+table.download-mirrors {
+ margin-bottom: 25px;
+ margin-left: 15px;
+ margin-top: 25px;
+ width: 700px;
+}
+
+table.download-mirrors tr {
+ height: 32px;
+}
+
+table.download-mirrors tr.legend {
+ text-align: right;
+}
+
+table.download-mirrors td {
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+table.download-mirrors tr.unreachable, td.unreachable {
+ border: 1px solid #f55;
+ background-color: #f99;
+}
+
+table.download-mirrors tr.reachable, td.reachable {
+ border: 1px solid #5f5;
+ background-color: #9f9;
+}
+
+table.download-mirrors td.latency {
+ width: 70px;
+ text-align: right;
+}
--- /dev/null
+{% extends "base.html" %}
+
+{% block content %}
+ <div class="post">
+ <a name="latest"></a>
+ <h3>{{ _("IPFire Mirrors") }}</h3>
+
+ {% if lang == "de" %}
+ <p>
+ Diese Seite zeigt eine Liste der Mirror-Server des IPFire-Projektes.
+ </p>
+
+ <p>
+ Bei einem Download wird einer der Server zufällig aus der Liste
+ gewählt und der User umgeleitet.
+ </p>
+
+ <ul>
+ <li>
+ <a href="http://wiki.ipfire.org/{{ lang }}/project/web"
+ target="_blank">Wie stelle ich selbst einen Mirror-Server bereit?</a>
+ </li>
+ </ul>
+ {% else %}
+ <p>
+ This page is an overview about our mirror servers.
+ </p>
+
+ <p>
+ When a user downloads a file, one of the servers is arbitrarily
+ choosen und the user gets reditected.
+ </p>
+
+ <ul>
+ <li>
+ <a href="http://wiki.ipfire.org/{{ lang }}/project/web"
+ target="_blank">How do I contribute a mirror server?</a>
+ </li>
+ </ul>
+ {% end %}
+
+ <table class="download-mirrors">
+ <tr>
+ <th>{{ _("Owner (Hostname)") }}</th>
+ <th>{{ _("Location") }}</th>
+ <th>{{ _("Latency") }}</th>
+ </tr>
+ {% for mirror in mirrors.all %}
+ <tr class="{% if not mirror.reachable %}un{% end %}reachable">
+ <td>{{ mirror.name }} ({{ mirror.hostname }})</td>
+ <td>
+ <img src="{{ static_url("images/flags/%s.png" % mirror.location["country_code"]) }}"
+ alt="{{ mirror.location["country_code"] }}" />
+ {{ mirror.location["country"] }}, {{ mirror.location["city"] }}
+ </td>
+ <td class="latency">{{ mirror.latency }} ms</td>
+ </tr>
+ {% end %}
+ <tr class="legend">
+ <td colspan="3"> </td>
+ </tr>
+ <tr class="legend">
+ <td><strong>{{ _("Legend") }}:</strong></td>
+ <td colspan="2" class="reachable">{{ _("Server is reachable") }}</td>
+ </tr>
+ <tr class="legend">
+ <td> </td>
+ <td colspan="2" class="unreachable">{{ _("Server is unreachable") }}</td>
+ </tr>
+ </table>
+
+ <br class="clear" />
+ </div>
+
+{% end block %}
"USB FDD Image","USB Floppy Image"
"USB HDD Image","USB Harddisk Image"
"Use this image to burn a CD and install IPFire from it.","Brennen Sie dieses Image und booten Sie die Installation davon."
+"Owner (Hostname)","Eigentümer (Hostname)"
+"Priority","Priorität"
+"Location","Standort"
+"Latency","Latenz"
+"Legend","Legende"
+"Server is reachable","Server ist erreichbar"
+"Server is unreachable","Server ist nicht erreichbar"
if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8080)
- tornado.ioloop.IOLoop.instance().start()
+
+ try:
+ tornado.ioloop.IOLoop.instance().start()
+ except KeyboardInterrupt:
+ # Shutdown mirror monitoring
+ from webapp.mirrors import mirrors
+ mirrors.shutdown()
+
+ raise
(r"/[A-Za-z]{2}/downloads?", DownloadHandler),
(r"/[A-Za-z]{2}/downloads?/all", DownloadAllHandler),
(r"/[A-Za-z]{2}/downloads?/development", DownloadDevelopmentHandler),
+ (r"/[A-Za-z]{2}/downloads?/mirrors", DownloadMirrorHandler),
(r"/[A-Za-z]{2}/downloads?/torrents", DownloadTorrentHandler),
# API
(r"/api/cluster_info", ApiClusterInfoHandler),
# download.ipfire.org
self.add_handlers(r"download\.ipfire\.org", [
- (r"/", MainHandler),
- (r"/[A-Za-z]{2}/?", MainHandler),
- (r"/[A-Za-z]{2}/index", DownloadHandler),
- ] + static_handlers)
+ (r"/", tornado.web.RedirectHandler, { "url" : "http://www.ipfire.org/" }),
+ (r"/(favicon\.ico)", tornado.web.StaticFileHandler, dict(path = static_path)),
+ (r"/(.*)", DownloadFileHandler),
+ ])
# source.ipfire.org
self.add_handlers(r"source\.ipfire\.org", [
] + static_handlers)
# ipfire.org
- self.add_handlers(r"ipfire\.org", [
+ self.add_handlers(r".*", [
(r".*", tornado.web.RedirectHandler, { "url" : "http://www.ipfire.org" })
])
+
+ def __del__(self):
+ from mirrors import mirrors
+ mirrors.stop()
from banners import banners
from helpers import size
from info import info
+from mirrors import mirrors
from news import news
from releases import releases
tracker=urlparse.urlparse(response.request.url).netloc)
+class DownloadMirrorHandler(BaseHandler):
+ def get(self):
+ self.render("downloads-mirrors.html", mirrors=mirrors)
+
+
class StaticHandler(BaseHandler):
@property
def static_path(self):
self.write(file.read())
finally:
file.close()
+
+
+class DownloadFileHandler(BaseHandler):
+ def get(self, path):
+ for mirror in mirrors.with_file(path):
+ if not mirror.reachable:
+ continue
+
+ self.redirect(mirror.url + path)
+ return
+
+ raise tornado.web.HTTPError(404)
+
+ def get_error_html(self, status_code, **kwargs):
+ return tornado.web.RequestHandler.get_error_html(self, status_code, **kwargs)
#!/usr/bin/python
+import subprocess
+
class Item(object):
def __init__(self, **args):
self.args = args
for key in d.keys():
ret[str(key)] = d[key]
return ret
+
+def ping(host, count=5, wait=10):
+ cmd = subprocess.Popen(
+ ["ping", "-c%d" % count, "-w%d" % wait, host],
+ stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE,
+ )
+
+ latency = None
+
+ out, error = cmd.communicate()
+
+ for line in out.split("\n"):
+ if not line.startswith("rtt"):
+ continue
+
+ line = line.split()
+ if len(line) < 4:
+ break
+
+ rtts = line[3].split("/")
+ if len(rtts) < 4:
+ break
+
+ latency = "%.1f" % float(rtts[1])
+
+ return latency
+
+
+if __name__ == "__main__":
+ print ping("www.ipfire.org")
+ print ping("www.rowie.at")
--- /dev/null
+#!/usr/bin/python
+
+import tornado.httpclient
+
+import random
+import simplejson
+import threading
+import time
+
+from helpers import Item, _stringify, ping
+
+class Mirrors(threading.Thread):
+ def __init__(self, filename):
+ threading.Thread.__init__(self, name="Mirror Monitor")
+
+ self.items = []
+ self.load(filename)
+
+ self.__running = True
+
+ self.start()
+
+ def load(self, filename):
+ f = open(filename)
+ data = f.read()
+ f.close()
+
+ for item in simplejson.loads(data):
+ self.items.append(MirrorItem(**_stringify(item)))
+
+ @property
+ def all(self):
+ return sorted(self.items)
+
+ @property
+ def random(self):
+ # Doesnt work :(
+ #return random.shuffle(self.items)
+ ret = []
+ items = self.items[:]
+ while items:
+ rnd = random.randint(0, len(items)-1)
+ ret.append(items.pop(rnd))
+ return ret
+
+ @property
+ def reachable(self):
+ ret = []
+ for mirror in self.items:
+ if not mirror.reachable:
+ continue
+ ret.append(mirror)
+ return ret
+
+ @property
+ def unreachable(self):
+ ret = []
+ for mirror in self.all:
+ if mirror in self.reachable:
+ continue
+ ret.append(mirror)
+ return ret
+
+ def pickone(self, reachable=False):
+ mirrors = self.items
+ if reachable:
+ mirrors = self.reachable
+ if not mirrors:
+ return None
+ return random.choice(mirrors)
+
+ def with_file(self, path):
+ ret = []
+ for mirror in self.random:
+ if not mirror["serves"]["isos"]:
+ continue
+ if path in mirror.files:
+ ret.append(mirror)
+ return ret
+
+ def shutdown(self):
+ self.__running = False
+
+ def run(self):
+ for mirror in self.random:
+ if not self.__running:
+ return
+ mirror.update()
+
+ count = 0
+ while self.__running:
+ if not count:
+ count = 300 # 30 secs
+ mirror = self.pickone()
+ if mirror:
+ mirror.update()
+
+ time.sleep(0.1)
+ count -= 1
+
+
+class MirrorItem(Item):
+ def __init__(self, *args, **kwargs):
+ Item.__init__(self, *args, **kwargs)
+
+ self.filelist = MirrorFilelist(self)
+ self.latency = "N/A"
+
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
+
+ def update(self):
+ self.latency = ping(self.hostname) or "N/A"
+ if self.filelist.outdated:
+ self.filelist.update()
+
+ @property
+ def reachable(self):
+ return not self.latency == "N/A"
+
+ @property
+ def url(self):
+ ret = "http://" + self.hostname
+ if not self.path.startswith("/"):
+ ret += "/"
+ ret += self.path
+ if not ret.endswith("/"):
+ ret += "/"
+ return ret
+
+ @property
+ def files(self):
+ return self.filelist.files
+
+ def has_file(self, path):
+ return path in self.files
+
+
+class MirrorFilelist(object):
+ def __init__(self, mirror):
+ self.mirror = mirror
+
+ self.__files = []
+ self.__time = 0
+
+ #self.update(now=True)
+
+ def update(self, now=False):
+ args = {}
+
+ if now:
+ while not self.mirror.reachable:
+ time.sleep(10)
+
+ http = tornado.httpclient.HTTPClient()
+
+ if not now:
+ http = tornado.httpclient.AsyncHTTPClient()
+ args["callback"] = self.on_response
+
+ try:
+ reponse = http.fetch(self.mirror.url + ".filelist", **args)
+ except tornado.httpclient.HTTPError:
+ self.__time = time.time()
+ return
+
+ if now:
+ self.on_response(reponse)
+
+ def on_response(self, response):
+ self.__files = []
+ self.__time = time.time()
+
+ if not response.code == 200:
+ return
+
+ # If invalid html content...
+ if response.body.startswith("<!"):
+ return
+
+ for line in response.body.split("\n"):
+ if not line:
+ continue
+ self.__files.append(line)
+
+ @property
+ def outdated(self):
+ return (time.time() - self.__time) > 60*60
+
+ @property
+ def files(self):
+ #if self.outdated:
+ # self.update()
+ return self.__files
+
+
+mirrors = Mirrors("mirrors.json")