add a webserver for the Introducer, showing service announcements and subscriber lists

This commit is contained in:
Brian Warner 2008-03-11 17:36:25 -07:00
parent f6fa813f74
commit 810ba68343
4 changed files with 134 additions and 3 deletions

View File

@ -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

View File

@ -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]

View 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

View File

@ -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