mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-30 01:38:55 +00:00
add a webserver for the Introducer, showing service announcements and subscriber lists
This commit is contained in:
parent
f6fa813f74
commit
810ba68343
@ -1,5 +1,5 @@
|
||||
|
||||
import re, time, sha
|
||||
import re, time, sha, os.path
|
||||
from base64 import b32decode
|
||||
from zope.interface import implements
|
||||
from twisted.application import service
|
||||
@ -16,6 +16,9 @@ class IntroducerNode(node.Node):
|
||||
def __init__(self, basedir="."):
|
||||
node.Node.__init__(self, basedir)
|
||||
self.init_introducer()
|
||||
webport = self.get_config("webport")
|
||||
if webport:
|
||||
self.init_web(webport) # strports string
|
||||
|
||||
def init_introducer(self):
|
||||
introducerservice = IntroducerService(self.basedir)
|
||||
@ -30,6 +33,14 @@ class IntroducerNode(node.Node):
|
||||
d.addCallback(_publish)
|
||||
d.addErrback(log.err, facility="tahoe.init", level=log.BAD)
|
||||
|
||||
def init_web(self, webport):
|
||||
self.log("init_web(webport=%s)", args=(webport,))
|
||||
|
||||
from allmydata.webish import IntroducerWebishServer
|
||||
nodeurl_path = os.path.join(self.basedir, "node.url")
|
||||
ws = IntroducerWebishServer(webport, nodeurl_path)
|
||||
self.add_service(ws)
|
||||
|
||||
class IntroducerService(service.MultiService, Referenceable):
|
||||
implements(RIIntroducerPublisherAndSubscriberService)
|
||||
name = "introducer"
|
||||
@ -45,6 +56,11 @@ class IntroducerService(service.MultiService, Referenceable):
|
||||
kwargs["facility"] = "tahoe.introducer"
|
||||
return log.msg(*args, **kwargs)
|
||||
|
||||
def get_announcements(self):
|
||||
return frozenset(self._announcements)
|
||||
def get_subscribers(self):
|
||||
return self._subscribers
|
||||
|
||||
def remote_publish(self, announcement):
|
||||
self.log("introducer: announcement published: %s" % (announcement,) )
|
||||
(furl, service_name, ri_name, nickname, ver, oldest) = announcement
|
||||
|
@ -7,6 +7,7 @@ from twisted.internet import defer, reactor
|
||||
from twisted.internet import threads # CLI tests use deferToThread
|
||||
from twisted.internet.error import ConnectionDone, ConnectionLost
|
||||
from twisted.application import service
|
||||
import allmydata
|
||||
from allmydata import client, uri, download, upload, storage, mutable, offloaded
|
||||
from allmydata.introducer import IntroducerNode
|
||||
from allmydata.util import deferredutil, fileutil, idlib, mathutil, testutil
|
||||
@ -71,14 +72,23 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
|
||||
iv_dir = self.getdir("introducer")
|
||||
if not os.path.isdir(iv_dir):
|
||||
fileutil.make_dirs(iv_dir)
|
||||
f = open(os.path.join(iv_dir, "webport"), "w")
|
||||
f.write("tcp:0:interface=127.0.0.1\n")
|
||||
f.close()
|
||||
iv = IntroducerNode(basedir=iv_dir)
|
||||
self.introducer = self.add_service(iv)
|
||||
d = self.introducer.when_tub_ready()
|
||||
d.addCallback(self._get_introducer_web)
|
||||
d.addCallback(self._set_up_stats_gatherer)
|
||||
d.addCallback(self._set_up_nodes_2)
|
||||
d.addCallback(self._grab_stats)
|
||||
return d
|
||||
|
||||
def _get_introducer_web(self, res):
|
||||
f = open(os.path.join(self.getdir("introducer"), "node.url"), "r")
|
||||
self.introweb_url = f.read().strip()
|
||||
f.close()
|
||||
|
||||
def _set_up_stats_gatherer(self, res):
|
||||
statsdir = self.getdir("stats_gatherer")
|
||||
fileutil.make_dirs(statsdir)
|
||||
@ -266,6 +276,7 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
|
||||
permuted_peers = list(c.get_permuted_peers("storage", "a"))
|
||||
self.failUnlessEqual(len(permuted_peers), self.numclients)
|
||||
d.addCallback(_check_connections)
|
||||
|
||||
def _do_upload(res):
|
||||
log.msg("UPLOADING")
|
||||
u = self.clients[0].getServiceNamed("uploader")
|
||||
@ -821,6 +832,7 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
|
||||
self.basedir = "system/SystemTest/test_vdrive"
|
||||
self.data = LARGE_DATA
|
||||
d = self.set_up_nodes(createprivdir=True)
|
||||
d.addCallback(self._test_introweb)
|
||||
d.addCallback(self.log, "starting publish")
|
||||
d.addCallback(self._do_publish1)
|
||||
d.addCallback(self._test_runner)
|
||||
@ -862,6 +874,21 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
|
||||
return d
|
||||
test_vdrive.timeout = 1100
|
||||
|
||||
def _test_introweb(self, res):
|
||||
d = getPage(self.introweb_url, method="GET", followRedirect=True)
|
||||
def _check(res):
|
||||
try:
|
||||
self.failUnless("allmydata: %s" % str(allmydata.__version__)
|
||||
in res)
|
||||
self.failUnless("Clients:" in res)
|
||||
except unittest.FailTest:
|
||||
print
|
||||
print "GET %s output was:" % self.introweb_url
|
||||
print res
|
||||
raise
|
||||
d.addCallback(_check)
|
||||
return d
|
||||
|
||||
def _do_publish1(self, res):
|
||||
ut = upload.Data(self.data)
|
||||
c0 = self.clients[0]
|
||||
|
85
src/allmydata/web/introweb.py
Normal file
85
src/allmydata/web/introweb.py
Normal file
@ -0,0 +1,85 @@
|
||||
|
||||
from nevow import rend
|
||||
from foolscap.referenceable import SturdyRef
|
||||
from twisted.internet import address
|
||||
import allmydata
|
||||
from allmydata import get_package_versions_string
|
||||
from allmydata.util import idlib
|
||||
from common import getxmlfile, IClient
|
||||
|
||||
class IntroducerRoot(rend.Page):
|
||||
|
||||
addSlash = True
|
||||
docFactory = getxmlfile("introducer.xhtml")
|
||||
|
||||
def data_version(self, ctx, data):
|
||||
return get_package_versions_string()
|
||||
def data_import_path(self, ctx, data):
|
||||
return str(allmydata)
|
||||
def data_my_nodeid(self, ctx, data):
|
||||
return idlib.nodeid_b2a(IClient(ctx).nodeid)
|
||||
|
||||
def data_known_storage_servers(self, ctx, data):
|
||||
i = IClient(ctx).getServiceNamed("introducer")
|
||||
storage = [1
|
||||
for (furl, service_name, ri_name, nickname, ver, oldest)
|
||||
in i.get_announcements()
|
||||
if service_name == "storage"]
|
||||
return len(storage)
|
||||
|
||||
def data_num_clients(self, ctx, data):
|
||||
i = IClient(ctx).getServiceNamed("introducer")
|
||||
num_clients = 0
|
||||
subscribers = i.get_subscribers()
|
||||
for service_name,who in subscribers.items():
|
||||
num_clients += len(who)
|
||||
return num_clients
|
||||
|
||||
def data_services(self, ctx, data):
|
||||
i = IClient(ctx).getServiceNamed("introducer")
|
||||
ann = list(i.get_announcements())
|
||||
ann.sort(lambda a,b: cmp( (a[1], a), (b[1], b) ) )
|
||||
return ann
|
||||
|
||||
def render_service_row(self, ctx, announcement):
|
||||
(furl, service_name, ri_name, nickname, ver, oldest) = announcement
|
||||
sr = SturdyRef(furl)
|
||||
nodeid = sr.tubID
|
||||
advertised = [loc.split(":")[0] for loc in sr.locationHints]
|
||||
ctx.fillSlots("peerid", "%s %s" % (idlib.nodeid_b2a(nodeid), nickname))
|
||||
ctx.fillSlots("advertised", " ".join(advertised))
|
||||
ctx.fillSlots("connected", "?")
|
||||
ctx.fillSlots("since", "?")
|
||||
ctx.fillSlots("announced", "?")
|
||||
ctx.fillSlots("version", ver)
|
||||
ctx.fillSlots("service_name", service_name)
|
||||
return ctx.tag
|
||||
|
||||
def data_subscribers(self, ctx, data):
|
||||
i = IClient(ctx).getServiceNamed("introducer")
|
||||
s = []
|
||||
for service_name, subscribers in i.get_subscribers().items():
|
||||
for rref in subscribers:
|
||||
s.append( (service_name, rref) )
|
||||
s.sort()
|
||||
return s
|
||||
|
||||
def render_subscriber_row(self, ctx, s):
|
||||
(service_name, rref) = s
|
||||
sr = rref.getSturdyRef()
|
||||
nodeid = sr.tubID
|
||||
ctx.fillSlots("peerid", "%s" % idlib.nodeid_b2a(nodeid))
|
||||
advertised = [loc.split(":")[0] for loc in sr.locationHints]
|
||||
ctx.fillSlots("advertised", " ".join(advertised))
|
||||
remote_host = rref.tracker.broker.transport.getPeer()
|
||||
if isinstance(remote_host, address.IPv4Address):
|
||||
remote_host_s = "%s:%d" % (remote_host.host, remote_host.port)
|
||||
else:
|
||||
# loopback is a non-IPv4Address
|
||||
remote_host_s = str(remote_host)
|
||||
ctx.fillSlots("connected", remote_host_s)
|
||||
ctx.fillSlots("since", "?")
|
||||
ctx.fillSlots("service_name", service_name)
|
||||
return ctx.tag
|
||||
|
||||
|
@ -22,7 +22,7 @@ from foolscap.eventual import fireEventually
|
||||
|
||||
from nevow.util import resource_filename
|
||||
|
||||
from allmydata.web import status, unlinked
|
||||
from allmydata.web import status, unlinked, introweb
|
||||
from allmydata.web.common import IClient, getxmlfile, get_arg, \
|
||||
boolean_of_arg, abbreviate_size
|
||||
|
||||
@ -1544,11 +1544,12 @@ class LocalAccess:
|
||||
|
||||
class WebishServer(service.MultiService):
|
||||
name = "webish"
|
||||
root_class = Root
|
||||
|
||||
def __init__(self, webport, nodeurl_path=None):
|
||||
service.MultiService.__init__(self)
|
||||
self.webport = webport
|
||||
self.root = Root()
|
||||
self.root = self.root_class()
|
||||
self.site = site = appserver.NevowSite(self.root)
|
||||
self.site.requestFactory = MyRequest
|
||||
self.allow_local = LocalAccess()
|
||||
@ -1590,3 +1591,5 @@ class WebishServer(service.MultiService):
|
||||
f.write(base_url + "\n")
|
||||
f.close()
|
||||
|
||||
class IntroducerWebishServer(WebishServer):
|
||||
root_class = introweb.IntroducerRoot
|
||||
|
Loading…
Reference in New Issue
Block a user