webapi: pass client through constructor arguments, remove IClient, should make it...
authorBrian Warner <warner@lothar.com>
Fri, 20 Feb 2009 19:15:54 +0000 (12:15 -0700)
committerBrian Warner <warner@lothar.com>
Fri, 20 Feb 2009 19:15:54 +0000 (12:15 -0700)
14 files changed:
src/allmydata/client.py
src/allmydata/introducer/server.py
src/allmydata/test/test_web.py
src/allmydata/web/check_results.py
src/allmydata/web/common.py
src/allmydata/web/directory.py
src/allmydata/web/filenode.py
src/allmydata/web/helper.xhtml
src/allmydata/web/introweb.py
src/allmydata/web/root.py
src/allmydata/web/statistics.xhtml
src/allmydata/web/status.py
src/allmydata/web/unlinked.py
src/allmydata/webish.py

index a77bda4f8172dfc1bc2670af83b4fd3a36ce1977..766cae047eff087ac81613a03d21c24676a2608a 100644 (file)
@@ -91,6 +91,8 @@ class Client(node.Node, pollmixin.PollMixin):
             hotline = TimerService(1.0, self._check_hotline, hotline_file)
             hotline.setServiceParent(self)
 
+        # this needs to happen last, so it can use getServiceNamed() to
+        # acquire references to StorageServer and other web-statusable things
         webport = self.get_config("node", "web.port", None)
         if webport:
             self.init_web(webport) # strports string
@@ -269,7 +271,7 @@ class Client(node.Node, pollmixin.PollMixin):
         nodeurl_path = os.path.join(self.basedir, "node.url")
         staticdir = self.get_config("node", "web.static", "public_html")
         staticdir = os.path.expanduser(staticdir)
-        ws = WebishServer(webport, nodeurl_path, staticdir)
+        ws = WebishServer(self, webport, nodeurl_path, staticdir)
         self.add_service(ws)
 
     def init_ftp_server(self):
index 5e519533d7b68d035ade4be23451918220e66797..d0ed6412f87241f4781af34187fc2b5372bc12ed 100644 (file)
@@ -41,7 +41,7 @@ class IntroducerNode(node.Node):
 
         from allmydata.webish import IntroducerWebishServer
         nodeurl_path = os.path.join(self.basedir, "node.url")
-        ws = IntroducerWebishServer(webport, nodeurl_path)
+        ws = IntroducerWebishServer(self, webport, nodeurl_path)
         self.add_service(ws)
 
 class IntroducerService(service.MultiService, Referenceable):
index 940a1c8be379543b89c4ee03f2ec90d580900250..e2ae8fc426ad91b487f3da8d413809d75f36b4d7 100644 (file)
@@ -33,6 +33,11 @@ class FakeIntroducerClient:
     def get_all_peerids(self):
         return frozenset()
 
+class FakeStatsProvider:
+    def get_stats(self):
+        stats = {'stats': {}, 'counters': {}}
+        return stats
+
 class FakeClient(service.MultiService):
     nodeid = "fake_nodeid"
     nickname = "fake_nickname"
@@ -51,6 +56,7 @@ class FakeClient(service.MultiService):
     _all_publish_statuses = [publish.PublishStatus()]
     _all_retrieve_statuses = [retrieve.RetrieveStatus()]
     convergence = "some random string"
+    stats_provider = FakeStatsProvider()
 
     def connected_to_introducer(self):
         return False
@@ -134,7 +140,7 @@ class WebMixin(object):
         self.s = FakeClient()
         self.s.startService()
         self.staticdir = self.mktemp()
-        self.ws = s = webish.WebishServer("0", staticdir=self.staticdir)
+        self.ws = s = webish.WebishServer(self.s, "0", staticdir=self.staticdir)
         s.setServiceParent(self.s)
         self.webish_port = port = s.listener._port.getHost().port
         self.webish_url = "http://localhost:%d" % port
index bf7d75db0269646d5f246d99bf3b311e497c1ae1..eeb30709d52f4e5c2021d2a1464a576be3b73e5c 100644 (file)
@@ -3,8 +3,7 @@ import time
 import simplejson
 from nevow import rend, inevow, tags as T
 from twisted.web import http, html
-from allmydata.web.common import getxmlfile, get_arg, get_root, \
-     IClient, WebError
+from allmydata.web.common import getxmlfile, get_arg, get_root, WebError
 from allmydata.web.operations import ReloadMixin
 from allmydata.interfaces import ICheckAndRepairResults, ICheckResults
 from allmydata.util import base32, idlib
@@ -68,6 +67,9 @@ def json_check_and_repair_results(r):
     return data
 
 class ResultsBase:
+    # self.client must point to the Client, so we can get nicknames and
+    # determine the permuted peer order
+
     def _join_pathstring(self, path):
         if path:
             pathstring = "/".join(self._html(path))
@@ -77,7 +79,7 @@ class ResultsBase:
 
     def _render_results(self, ctx, cr):
         assert ICheckResults(cr)
-        c = IClient(ctx)
+        c = self.client
         data = cr.get_data()
         r = []
         def add(name, value):
@@ -178,6 +180,10 @@ class ResultsBase:
 class LiteralCheckResults(rend.Page, ResultsBase):
     docFactory = getxmlfile("literal-check-results.xhtml")
 
+    def __init__(self, client):
+        self.client = client
+        rend.Page.__init__(self, client)
+
     def renderHTTP(self, ctx):
         if self.want_json(ctx):
             return self.json(ctx)
@@ -217,8 +223,10 @@ class CheckerBase:
 class CheckResults(CheckerBase, rend.Page, ResultsBase):
     docFactory = getxmlfile("check-results.xhtml")
 
-    def __init__(self, results):
+    def __init__(self, client, results):
+        self.client = client
         self.r = ICheckResults(results)
+        rend.Page.__init__(self, results)
 
     def json(self, ctx):
         inevow.IRequest(ctx).setHeader("content-type", "text/plain")
@@ -227,18 +235,18 @@ class CheckResults(CheckerBase, rend.Page, ResultsBase):
 
     def render_summary(self, ctx, data):
         results = []
-        if self.r.is_healthy():
+        if data.is_healthy():
             results.append("Healthy")
-        elif self.r.is_recoverable():
+        elif data.is_recoverable():
             results.append("Not Healthy!")
         else:
             results.append("Not Recoverable!")
         results.append(" : ")
-        results.append(self._html(self.r.get_summary()))
+        results.append(self._html(data.get_summary()))
         return ctx.tag[results]
 
     def render_repair(self, ctx, data):
-        if self.r.is_healthy():
+        if data.is_healthy():
             return ""
         repair = T.form(action=".", method="post",
                         enctype="multipart/form-data")[
@@ -250,14 +258,16 @@ class CheckResults(CheckerBase, rend.Page, ResultsBase):
         return ctx.tag[repair]
 
     def render_results(self, ctx, data):
-        cr = self._render_results(ctx, self.r)
+        cr = self._render_results(ctx, data)
         return ctx.tag[cr]
 
 class CheckAndRepairResults(CheckerBase, rend.Page, ResultsBase):
     docFactory = getxmlfile("check-and-repair-results.xhtml")
 
-    def __init__(self, results):
+    def __init__(self, client, results):
+        self.client = client
         self.r = ICheckAndRepairResults(results)
+        rend.Page.__init__(self, results)
 
     def json(self, ctx):
         inevow.IRequest(ctx).setHeader("content-type", "text/plain")
@@ -265,7 +275,7 @@ class CheckAndRepairResults(CheckerBase, rend.Page, ResultsBase):
         return simplejson.dumps(data, indent=1) + "\n"
 
     def render_summary(self, ctx, data):
-        cr = self.r.get_post_repair_results()
+        cr = data.get_post_repair_results()
         results = []
         if cr.is_healthy():
             results.append("Healthy")
@@ -278,20 +288,20 @@ class CheckAndRepairResults(CheckerBase, rend.Page, ResultsBase):
         return ctx.tag[results]
 
     def render_repair_results(self, ctx, data):
-        if self.r.get_repair_attempted():
-            if self.r.get_repair_successful():
+        if data.get_repair_attempted():
+            if data.get_repair_successful():
                 return ctx.tag["Repair successful"]
             else:
                 return ctx.tag["Repair unsuccessful"]
         return ctx.tag["No repair necessary"]
 
     def render_post_repair_results(self, ctx, data):
-        cr = self._render_results(ctx, self.r.get_post_repair_results())
+        cr = self._render_results(ctx, data.get_post_repair_results())
         return ctx.tag[T.div["Post-Repair Checker Results:"], cr]
 
     def render_maybe_pre_repair_results(self, ctx, data):
-        if self.r.get_repair_attempted():
-            cr = self._render_results(ctx, self.r.get_pre_repair_results())
+        if data.get_repair_attempted():
+            cr = self._render_results(ctx, data.get_pre_repair_results())
             return ctx.tag[T.div["Pre-Repair Checker Results:"], cr]
         return ""
 
@@ -299,7 +309,8 @@ class CheckAndRepairResults(CheckerBase, rend.Page, ResultsBase):
 class DeepCheckResults(rend.Page, ResultsBase, ReloadMixin):
     docFactory = getxmlfile("deep-check-results.xhtml")
 
-    def __init__(self, monitor):
+    def __init__(self, client, monitor):
+        self.client = client
         self.monitor = monitor
 
     def childFactory(self, ctx, name):
@@ -310,7 +321,8 @@ class DeepCheckResults(rend.Page, ResultsBase, ReloadMixin):
         si = base32.a2b(name)
         r = self.monitor.get_status()
         try:
-            return CheckResults(r.get_results_for_storage_index(si))
+            return CheckResults(self.client,
+                                r.get_results_for_storage_index(si))
         except KeyError:
             raise WebError("No detailed results for SI %s" % html.escape(name),
                            http.NOT_FOUND)
@@ -397,8 +409,7 @@ class DeepCheckResults(rend.Page, ResultsBase, ReloadMixin):
     def render_server_problem(self, ctx, data):
         serverid = data
         data = [idlib.shortnodeid_b2a(serverid)]
-        c = IClient(ctx)
-        nickname = c.get_nickname_for_peerid(serverid)
+        nickname = self.client.get_nickname_for_peerid(serverid)
         if nickname:
             data.append(" (%s)" % self._html(nickname))
         return ctx.tag[data]
@@ -412,7 +423,7 @@ class DeepCheckResults(rend.Page, ResultsBase, ReloadMixin):
         return self.monitor.get_status().get_corrupt_shares()
     def render_share_problem(self, ctx, data):
         serverid, storage_index, sharenum = data
-        nickname = IClient(ctx).get_nickname_for_peerid(serverid)
+        nickname = self.client.get_nickname_for_peerid(serverid)
         ctx.fillSlots("serverid", idlib.shortnodeid_b2a(serverid))
         if nickname:
             ctx.fillSlots("nickname", self._html(nickname))
@@ -450,9 +461,8 @@ class DeepCheckResults(rend.Page, ResultsBase, ReloadMixin):
 class DeepCheckAndRepairResults(rend.Page, ResultsBase, ReloadMixin):
     docFactory = getxmlfile("deep-check-and-repair-results.xhtml")
 
-    def __init__(self, monitor):
-        #assert IDeepCheckAndRepairResults(results)
-        #self.r = results
+    def __init__(self, client, monitor):
+        self.client = client
         self.monitor = monitor
 
     def childFactory(self, ctx, name):
@@ -463,7 +473,8 @@ class DeepCheckAndRepairResults(rend.Page, ResultsBase, ReloadMixin):
         si = base32.a2b(name)
         r = self.monitor.get_status()
         try:
-            return CheckAndRepairResults(r.get_results_for_storage_index(si))
+            return CheckAndRepairResults(self.client,
+                                         r.get_results_for_storage_index(si))
         except KeyError:
             raise WebError("No detailed results for SI %s" % html.escape(name),
                            http.NOT_FOUND)
index a831b80d6036640d8a10a87eff5ae823d2fcbd3a..ec86d46a5785d8230eb4438194990e05d5b215cb 100644 (file)
@@ -7,8 +7,6 @@ from nevow.util import resource_filename
 from allmydata.interfaces import ExistingChildError, NoSuchChildError, \
      FileTooLargeError, NotEnoughSharesError
 
-class IClient(Interface):
-    pass
 class IOpHandleTable(Interface):
     pass
 
index e8ce3f7a44d6f14dadc6a8c93d81692923795821..d47986652fcdda84f5cbdd587c4f22ec9a7cdfb5 100644 (file)
@@ -20,7 +20,7 @@ from allmydata.interfaces import IDirectoryNode, IFileNode, IMutableFileNode, \
 from allmydata.monitor import Monitor, OperationCancelledError
 from allmydata import dirnode
 from allmydata.web.common import text_plain, WebError, \
-     IClient, IOpHandleTable, NeedOperationHandleError, \
+     IOpHandleTable, NeedOperationHandleError, \
      boolean_of_arg, get_arg, get_root, \
      should_create_intermediate_directories, \
      getxmlfile, RenderMixin
@@ -38,22 +38,23 @@ class BlockingFileError(Exception):
     """We cannot auto-create a parent directory, because there is a file in
     the way"""
 
-def make_handler_for(node, parentnode=None, name=None):
+def make_handler_for(node, client, parentnode=None, name=None):
     if parentnode:
         assert IDirectoryNode.providedBy(parentnode)
     if IMutableFileNode.providedBy(node):
-        return FileNodeHandler(node, parentnode, name)
+        return FileNodeHandler(client, node, parentnode, name)
     if IFileNode.providedBy(node):
-        return FileNodeHandler(node, parentnode, name)
+        return FileNodeHandler(client, node, parentnode, name)
     if IDirectoryNode.providedBy(node):
-        return DirectoryNodeHandler(node, parentnode, name)
+        return DirectoryNodeHandler(client, node, parentnode, name)
     raise WebError("Cannot provide handler for '%s'" % node)
 
 class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
     addSlash = True
 
-    def __init__(self, node, parentnode=None, name=None):
+    def __init__(self, client, node, parentnode=None, name=None):
         rend.Page.__init__(self)
+        self.client = client
         assert node
         self.node = node
         self.parentnode = parentnode
@@ -87,7 +88,8 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
                     # create intermediate directories
                     if DEBUG: print " making intermediate directory"
                     d = self.node.create_empty_directory(name)
-                    d.addCallback(make_handler_for, self.node, name)
+                    d.addCallback(make_handler_for,
+                                  self.client, self.node, name)
                     return d
             else:
                 if DEBUG: print " terminal"
@@ -96,7 +98,8 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
                     if DEBUG: print " making final directory"
                     # final directory
                     d = self.node.create_empty_directory(name)
-                    d.addCallback(make_handler_for, self.node, name)
+                    d.addCallback(make_handler_for,
+                                  self.client, self.node, name)
                     return d
                 if (method,t) in ( ("PUT",""), ("PUT","uri"), ):
                     if DEBUG: print " PUT, making leaf placeholder"
@@ -105,7 +108,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
                     # since that's the leaf node that we're about to create.
                     # We make a dummy one, which will respond to the PUT
                     # request by replacing itself.
-                    return PlaceHolderNodeHandler(self.node, name)
+                    return PlaceHolderNodeHandler(self.client, self.node, name)
             if DEBUG: print " 404"
             # otherwise, we just return a no-such-child error
             return rend.FourOhFour()
@@ -120,7 +123,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
                                "a file was in the way" % name,
                                http.CONFLICT)
         if DEBUG: print "good child"
-        return make_handler_for(node, self.node, name)
+        return make_handler_for(node, self.client, self.node, name)
 
     def render_DELETE(self, ctx):
         assert self.parentnode and self.name
@@ -129,7 +132,6 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         return d
 
     def render_GET(self, ctx):
-        client = IClient(ctx)
         req = IRequest(ctx)
         # This is where all of the directory-related ?t=* code goes.
         t = get_arg(req, "t", "").strip()
@@ -164,7 +166,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
                 # they're trying to set_uri and that name is already occupied
                 # (by us).
                 raise ExistingChildError()
-            d = self.replace_me_with_a_childcap(ctx, replace)
+            d = self.replace_me_with_a_childcap(req, self.client, replace)
             # TODO: results
             return d
 
@@ -279,10 +281,10 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
                 f = node_or_failure
                 f.trap(NoSuchChildError)
                 # create a placeholder which will see POST t=upload
-                return PlaceHolderNodeHandler(self.node, name)
+                return PlaceHolderNodeHandler(self.client, self.node, name)
             else:
                 node = node_or_failure
-                return make_handler_for(node, self.node, name)
+                return make_handler_for(node, self.client, self.node, name)
         d.addBoth(_maybe_got_node)
         # now we have a placeholder or a filenodehandler, and we can just
         # delegate to it. We could return the resource back out of
@@ -358,10 +360,10 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         add_lease = boolean_of_arg(get_arg(req, "add-lease", "false"))
         if repair:
             d = self.node.check_and_repair(Monitor(), verify, add_lease)
-            d.addCallback(lambda res: CheckAndRepairResults(res))
+            d.addCallback(lambda res: CheckAndRepairResults(self.client, res))
         else:
             d = self.node.check(Monitor(), verify, add_lease)
-            d.addCallback(lambda res: CheckResults(res))
+            d.addCallback(lambda res: CheckResults(self.client, res))
         return d
 
     def _start_operation(self, monitor, renderer, ctx):
@@ -378,10 +380,10 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         add_lease = boolean_of_arg(get_arg(ctx, "add-lease", "false"))
         if repair:
             monitor = self.node.start_deep_check_and_repair(verify, add_lease)
-            renderer = DeepCheckAndRepairResults(monitor)
+            renderer = DeepCheckAndRepairResults(self.client, monitor)
         else:
             monitor = self.node.start_deep_check(verify, add_lease)
-            renderer = DeepCheckResults(monitor)
+            renderer = DeepCheckResults(self.client, monitor)
         return self._start_operation(monitor, renderer, ctx)
 
     def _POST_stream_deep_check(self, ctx):
@@ -408,21 +410,21 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         if not get_arg(ctx, "ophandle"):
             raise NeedOperationHandleError("slow operation requires ophandle=")
         monitor = self.node.build_manifest()
-        renderer = ManifestResults(monitor)
+        renderer = ManifestResults(self.client, monitor)
         return self._start_operation(monitor, renderer, ctx)
 
     def _POST_start_deep_size(self, ctx):
         if not get_arg(ctx, "ophandle"):
             raise NeedOperationHandleError("slow operation requires ophandle=")
         monitor = self.node.start_deep_stats()
-        renderer = DeepSizeResults(monitor)
+        renderer = DeepSizeResults(self.client, monitor)
         return self._start_operation(monitor, renderer, ctx)
 
     def _POST_start_deep_stats(self, ctx):
         if not get_arg(ctx, "ophandle"):
             raise NeedOperationHandleError("slow operation requires ophandle=")
         monitor = self.node.start_deep_stats()
-        renderer = DeepStatsResults(monitor)
+        renderer = DeepStatsResults(self.client, monitor)
         return self._start_operation(monitor, renderer, ctx)
 
     def _POST_stream_manifest(self, ctx):
@@ -761,15 +763,17 @@ class RenameForm(rend.Page):
 class ManifestResults(rend.Page, ReloadMixin):
     docFactory = getxmlfile("manifest.xhtml")
 
-    def __init__(self, monitor):
+    def __init__(self, client, monitor):
+        self.client = client
         self.monitor = monitor
 
     def renderHTTP(self, ctx):
-        output = get_arg(inevow.IRequest(ctx), "output", "html").lower()
+        req = inevow.IRequest(ctx)
+        output = get_arg(req, "output", "html").lower()
         if output == "text":
-            return self.text(ctx)
+            return self.text(req)
         if output == "json":
-            return self.json(ctx)
+            return self.json(req)
         return rend.Page.renderHTTP(self, ctx)
 
     def slashify_path(self, path):
@@ -777,8 +781,8 @@ class ManifestResults(rend.Page, ReloadMixin):
             return ""
         return "/".join([p.encode("utf-8") for p in path])
 
-    def text(self, ctx):
-        inevow.IRequest(ctx).setHeader("content-type", "text/plain")
+    def text(self, req):
+        req.setHeader("content-type", "text/plain")
         lines = []
         is_finished = self.monitor.is_finished()
         lines.append("finished: " + {True: "yes", False: "no"}[is_finished])
@@ -786,8 +790,8 @@ class ManifestResults(rend.Page, ReloadMixin):
             lines.append(self.slashify_path(path) + " " + cap)
         return "\n".join(lines) + "\n"
 
-    def json(self, ctx):
-        inevow.IRequest(ctx).setHeader("content-type", "text/plain")
+    def json(self, req):
+        req.setHeader("content-type", "text/plain")
         m = self.monitor
         s = m.get_status()
 
@@ -839,14 +843,16 @@ class ManifestResults(rend.Page, ReloadMixin):
         return ctx.tag
 
 class DeepSizeResults(rend.Page):
-    def __init__(self, monitor):
+    def __init__(self, client, monitor):
+        self.client = client
         self.monitor = monitor
 
     def renderHTTP(self, ctx):
-        output = get_arg(inevow.IRequest(ctx), "output", "html").lower()
-        inevow.IRequest(ctx).setHeader("content-type", "text/plain")
+        req = inevow.IRequest(ctx)
+        output = get_arg(req, "output", "html").lower()
+        req.setHeader("content-type", "text/plain")
         if output == "json":
-            return self.json(ctx)
+            return self.json(req)
         # plain text
         is_finished = self.monitor.is_finished()
         output = "finished: " + {True: "yes", False: "no"}[is_finished] + "\n"
@@ -858,14 +864,15 @@ class DeepSizeResults(rend.Page):
             output += "size: %d\n" % total
         return output
 
-    def json(self, ctx):
+    def json(self, req):
         status = {"finished": self.monitor.is_finished(),
                   "size": self.monitor.get_status(),
                   }
         return simplejson.dumps(status)
 
 class DeepStatsResults(rend.Page):
-    def __init__(self, monitor):
+    def __init__(self, client, monitor):
+        self.client = client
         self.monitor = monitor
 
     def renderHTTP(self, ctx):
index 0a2081a787b872560a68d47de4de4612b27a028a..78e235b60cedb7e762b01cb3752dcb01621baee9 100644 (file)
@@ -12,7 +12,7 @@ from allmydata.immutable.upload import FileHandle
 from allmydata.immutable.filenode import LiteralFileNode
 from allmydata.util import log, base32
 
-from allmydata.web.common import text_plain, WebError, IClient, RenderMixin, \
+from allmydata.web.common import text_plain, WebError, RenderMixin, \
      boolean_of_arg, get_arg, should_create_intermediate_directories
 from allmydata.web.check_results import CheckResults, \
      CheckAndRepairResults, LiteralCheckResults
@@ -20,10 +20,8 @@ from allmydata.web.info import MoreInfo
 
 class ReplaceMeMixin:
 
-    def replace_me_with_a_child(self, ctx, replace):
+    def replace_me_with_a_child(self, req, client, replace):
         # a new file is being uploaded in our place.
-        req = IRequest(ctx)
-        client = IClient(ctx)
         mutable = boolean_of_arg(get_arg(req, "mutable", "false"))
         if mutable:
             req.content.seek(0)
@@ -53,11 +51,9 @@ class ReplaceMeMixin:
         d.addCallback(_done)
         return d
 
-    def replace_me_with_a_childcap(self, ctx, replace):
-        req = IRequest(ctx)
+    def replace_me_with_a_childcap(self, req, client, replace):
         req.content.seek(0)
         childcap = req.content.read()
-        client = IClient(ctx)
         childnode = client.create_node_from_uri(childcap)
         d = self.parentnode.set_node(self.name, childnode, overwrite=replace)
         d.addCallback(lambda res: childnode.get_uri())
@@ -71,10 +67,8 @@ class ReplaceMeMixin:
         data = contents.file.read()
         return data
 
-    def replace_me_with_a_formpost(self, ctx, replace):
+    def replace_me_with_a_formpost(self, req, client, replace):
         # create a new file, maybe mutable, maybe immutable
-        req = IRequest(ctx)
-        client = IClient(ctx)
         mutable = boolean_of_arg(get_arg(req, "mutable", "false"))
 
         if mutable:
@@ -95,8 +89,9 @@ class ReplaceMeMixin:
         return d
 
 class PlaceHolderNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
-    def __init__(self, parentnode, name):
+    def __init__(self, client, parentnode, name):
         rend.Page.__init__(self)
+        self.client = client
         assert parentnode
         self.parentnode = parentnode
         self.name = name
@@ -111,9 +106,9 @@ class PlaceHolderNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
             raise WebError("Content-Range in PUT not yet supported",
                            http.NOT_IMPLEMENTED)
         if not t:
-            return self.replace_me_with_a_child(ctx, replace)
+            return self.replace_me_with_a_child(req, self.client, replace)
         if t == "uri":
-            return self.replace_me_with_a_childcap(ctx, replace)
+            return self.replace_me_with_a_childcap(req, self.client, replace)
 
         raise WebError("PUT to a file: bad t=%s" % t)
 
@@ -127,7 +122,7 @@ class PlaceHolderNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
             # or POST /uri/path/file?t=upload, or
             # POST /uri/path/dir?t=upload&name=foo . All have the same
             # behavior, we just ignore any name= argument
-            d = self.replace_me_with_a_formpost(ctx, replace)
+            d = self.replace_me_with_a_formpost(req, self.client, replace)
         else:
             # t=mkdir is handled in DirectoryNodeHandler._POST_mkdir, so
             # there are no other t= values left to be handled by the
@@ -141,8 +136,9 @@ class PlaceHolderNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
 
 
 class FileNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
-    def __init__(self, node, parentnode=None, name=None):
+    def __init__(self, client, node, parentnode=None, name=None):
         rend.Page.__init__(self)
+        self.client = client
         assert node
         self.node = node
         self.parentnode = parentnode
@@ -210,19 +206,19 @@ class FileNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         replace = boolean_of_arg(get_arg(req, "replace", "true"))
         if not t:
             if self.node.is_mutable():
-                return self.replace_my_contents(ctx)
+                return self.replace_my_contents(req)
             if not replace:
                 # this is the early trap: if someone else modifies the
                 # directory while we're uploading, the add_file(overwrite=)
                 # call in replace_me_with_a_child will do the late trap.
                 raise ExistingChildError()
             assert self.parentnode and self.name
-            return self.replace_me_with_a_child(ctx, replace)
+            return self.replace_me_with_a_child(req, self.client, replace)
         if t == "uri":
             if not replace:
                 raise ExistingChildError()
             assert self.parentnode and self.name
-            return self.replace_me_with_a_childcap(ctx, replace)
+            return self.replace_me_with_a_childcap(req, self.client, replace)
 
         raise WebError("PUT to a file: bad t=%s" % t)
 
@@ -239,12 +235,12 @@ class FileNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
             # POST /uri/path/dir?t=upload&name=foo . All have the same
             # behavior, we just ignore any name= argument
             if self.node.is_mutable():
-                d = self.replace_my_contents_with_a_formpost(ctx)
+                d = self.replace_my_contents_with_a_formpost(req)
             else:
                 if not replace:
                     raise ExistingChildError()
                 assert self.parentnode and self.name
-                d = self.replace_me_with_a_formpost(ctx, replace)
+                d = self.replace_me_with_a_formpost(req, self.client, replace)
         else:
             raise WebError("POST to file: bad t=%s" % t)
 
@@ -258,13 +254,13 @@ class FileNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         repair = boolean_of_arg(get_arg(req, "repair", "false"))
         add_lease = boolean_of_arg(get_arg(req, "add-lease", "false"))
         if isinstance(self.node, LiteralFileNode):
-            return defer.succeed(LiteralCheckResults())
+            return defer.succeed(LiteralCheckResults(self.client))
         if repair:
             d = self.node.check_and_repair(Monitor(), verify, add_lease)
-            d.addCallback(lambda res: CheckAndRepairResults(res))
+            d.addCallback(lambda res: CheckAndRepairResults(self.client, res))
         else:
             d = self.node.check(Monitor(), verify, add_lease)
-            d.addCallback(lambda res: CheckResults(res))
+            d.addCallback(lambda res: CheckResults(self.client, res))
         return d
 
     def render_DELETE(self, ctx):
@@ -273,18 +269,16 @@ class FileNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         d.addCallback(lambda res: self.node.get_uri())
         return d
 
-    def replace_my_contents(self, ctx):
-        req = IRequest(ctx)
+    def replace_my_contents(self, req):
         req.content.seek(0)
         new_contents = req.content.read()
         d = self.node.overwrite(new_contents)
         d.addCallback(lambda res: self.node.get_uri())
         return d
 
-    def replace_my_contents_with_a_formpost(self, ctx):
+    def replace_my_contents_with_a_formpost(self, req):
         # we have a mutable file. Get the data from the formpost, and replace
         # the mutable file's contents with it.
-        req = IRequest(ctx)
         new_contents = self._read_data_from_formpost(req)
         d = self.node.overwrite(new_contents)
         d.addCallback(lambda res: self.node.get_uri())
@@ -449,4 +443,4 @@ def FileReadOnlyURI(ctx, filenode):
 
 class FileNodeDownloadHandler(FileNodeHandler):
     def childFactory(self, ctx, name):
-        return FileNodeDownloadHandler(self.node, name=name)
+        return FileNodeDownloadHandler(self.client, self.node, name=name)
index bea580171c498bbee90f817ec1920a3c7feb4edc..f5c9e298a5a4897189063c570de7a74ec0793c50 100644 (file)
@@ -11,7 +11,7 @@
 <h1>Helper Status</h1>
 
 <h2>Immutable Uploads</h2>
-<ul>
+<ul n:data="helper_stats">
   <li>Active: <span n:render="active_uploads" /></li>
   <li>--</li>
   <li>Bytes Fetched: <span n:render="upload_bytes_fetched" /></li>
index f1a6ad02638ddb68f3ddc3d69deccb3efdcc6ff9..3210fda156297297a9ed128cef1bfad101a82aca 100644 (file)
@@ -7,7 +7,7 @@ import allmydata
 import simplejson
 from allmydata import get_package_versions_string
 from allmydata.util import idlib
-from common import getxmlfile, get_arg, IClient
+from common import getxmlfile, get_arg
 
 class IntroducerRoot(rend.Page):
 
@@ -16,6 +16,11 @@ class IntroducerRoot(rend.Page):
 
     child_operations = None
 
+    def __init__(self, introducer_node):
+        self.introducer_node = introducer_node
+        self.introducer_service = introducer_node.getServiceNamed("introducer")
+        rend.Page.__init__(self, introducer_node)
+
     def renderHTTP(self, ctx):
         t = get_arg(inevow.IRequest(ctx), "t")
         if t == "json":
@@ -23,16 +28,15 @@ class IntroducerRoot(rend.Page):
         return rend.Page.renderHTTP(self, ctx)
 
     def render_JSON(self, ctx):
-        i = IClient(ctx).getServiceNamed("introducer")
         res = {}
-        clients = i.get_subscribers()
+        clients = self.introducer_service.get_subscribers()
         subscription_summary = dict([ (name, len(clients[name]))
                                       for name in clients ])
         res["subscription_summary"] = subscription_summary
 
         announcement_summary = {}
         service_hosts = {}
-        for (ann,when) in i.get_announcements().values():
+        for (ann,when) in self.introducer_service.get_announcements().values():
             (furl, service_name, ri_name, nickname, ver, oldest) = ann
             if service_name not in announcement_summary:
                 announcement_summary[service_name] = 0
@@ -65,12 +69,11 @@ class IntroducerRoot(rend.Page):
     def data_import_path(self, ctx, data):
         return str(allmydata)
     def data_my_nodeid(self, ctx, data):
-        return idlib.nodeid_b2a(IClient(ctx).nodeid)
+        return idlib.nodeid_b2a(self.introducer_node.nodeid)
 
     def render_announcement_summary(self, ctx, data):
-        i = IClient(ctx).getServiceNamed("introducer")
         services = {}
-        for (ann,when) in i.get_announcements().values():
+        for (ann,when) in self.introducer_service.get_announcements().values():
             (furl, service_name, ri_name, nickname, ver, oldest) = ann
             if service_name not in services:
                 services[service_name] = 0
@@ -81,17 +84,16 @@ class IntroducerRoot(rend.Page):
                           for service_name in service_names])
 
     def render_client_summary(self, ctx, data):
-        i = IClient(ctx).getServiceNamed("introducer")
-        clients = i.get_subscribers()
+        clients = self.introducer_service.get_subscribers()
         service_names = clients.keys()
         service_names.sort()
         return ", ".join(["%s: %d" % (service_name, len(clients[service_name]))
                           for service_name in service_names])
 
     def data_services(self, ctx, data):
-        i = IClient(ctx).getServiceNamed("introducer")
+        introsvc = self.introducer_service
         ann = [(since,a)
-               for (a,since) in i.get_announcements().values()
+               for (a,since) in introsvc.get_announcements().values()
                if a[1] != "stub_client"]
         ann.sort(lambda a,b: cmp( (a[1][1], a), (b[1][1], b) ) )
         return ann
@@ -112,10 +114,9 @@ class IntroducerRoot(rend.Page):
         return ctx.tag
 
     def data_subscribers(self, ctx, data):
-        i = IClient(ctx).getServiceNamed("introducer")
         # use the "stub_client" announcements to get information per nodeid
         clients = {}
-        for (ann,when) in i.get_announcements().values():
+        for (ann,when) in self.introducer_service.get_announcements().values():
             if ann[1] != "stub_client":
                 continue
             (furl, service_name, ri_name, nickname, ver, oldest) = ann
@@ -125,7 +126,8 @@ class IntroducerRoot(rend.Page):
 
         # then we actually provide information per subscriber
         s = []
-        for service_name, subscribers in i.get_subscribers().items():
+        introsvc = self.introducer_service
+        for service_name, subscribers in introsvc.get_subscribers().items():
             for (rref, timestamp) in subscribers.items():
                 sr = rref.getSturdyRef()
                 nodeid = sr.tubID
index f429e05f5befec824104c0f07dc4af443b42a042..e82d575812055eae11ea65c686e8a88d043fb970 100644 (file)
@@ -14,15 +14,19 @@ from allmydata import provisioning
 from allmydata.util import idlib, log
 from allmydata.interfaces import IFileNode
 from allmydata.web import filenode, directory, unlinked, status, operations
-from allmydata.web import reliability
-from allmydata.web.common import abbreviate_size, IClient, \
-     getxmlfile, WebError, get_arg, RenderMixin
+from allmydata.web import reliability, storage
+from allmydata.web.common import abbreviate_size, getxmlfile, WebError, \
+     get_arg, RenderMixin
 
 
 class URIHandler(RenderMixin, rend.Page):
     # I live at /uri . There are several operations defined on /uri itself,
     # mostly involved with creation of unlinked files and directories.
 
+    def __init__(self, client):
+        rend.Page.__init__(self, client)
+        self.client = client
+
     def render_GET(self, ctx):
         req = IRequest(ctx)
         uri = get_arg(req, "uri", None)
@@ -43,11 +47,11 @@ class URIHandler(RenderMixin, rend.Page):
         if t == "":
             mutable = bool(get_arg(req, "mutable", "").strip())
             if mutable:
-                return unlinked.PUTUnlinkedSSK(ctx)
+                return unlinked.PUTUnlinkedSSK(req, self.client)
             else:
-                return unlinked.PUTUnlinkedCHK(ctx)
+                return unlinked.PUTUnlinkedCHK(req, self.client)
         if t == "mkdir":
-            return unlinked.PUTUnlinkedCreateDirectory(ctx)
+            return unlinked.PUTUnlinkedCreateDirectory(req, self.client)
         errmsg = ("/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, "
                   "and POST?t=mkdir")
         raise WebError(errmsg, http.BAD_REQUEST)
@@ -61,21 +65,20 @@ class URIHandler(RenderMixin, rend.Page):
         if t in ("", "upload"):
             mutable = bool(get_arg(req, "mutable", "").strip())
             if mutable:
-                return unlinked.POSTUnlinkedSSK(ctx)
+                return unlinked.POSTUnlinkedSSK(req, self.client)
             else:
-                return unlinked.POSTUnlinkedCHK(ctx)
+                return unlinked.POSTUnlinkedCHK(req, self.client)
         if t == "mkdir":
-            return unlinked.POSTUnlinkedCreateDirectory(ctx)
+            return unlinked.POSTUnlinkedCreateDirectory(req, self.client)
         errmsg = ("/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, "
                   "and POST?t=mkdir")
         raise WebError(errmsg, http.BAD_REQUEST)
 
     def childFactory(self, ctx, name):
         # 'name' is expected to be a URI
-        client = IClient(ctx)
         try:
-            node = client.create_node_from_uri(name)
-            return directory.make_handler_for(node)
+            node = self.client.create_node_from_uri(name)
+            return directory.make_handler_for(node, self.client)
         except (TypeError, AssertionError):
             raise WebError("'%s' is not a valid file- or directory- cap"
                            % name)
@@ -84,20 +87,23 @@ class FileHandler(rend.Page):
     # I handle /file/$FILECAP[/IGNORED] , which provides a URL from which a
     # file can be downloaded correctly by tools like "wget".
 
+    def __init__(self, client):
+        rend.Page.__init__(self, client)
+        self.client = client
+
     def childFactory(self, ctx, name):
         req = IRequest(ctx)
         if req.method not in ("GET", "HEAD"):
             raise WebError("/file can only be used with GET or HEAD")
         # 'name' must be a file URI
-        client = IClient(ctx)
         try:
-            node = client.create_node_from_uri(name)
+            node = self.client.create_node_from_uri(name)
         except (TypeError, AssertionError):
             raise WebError("'%s' is not a valid file- or directory- cap"
                            % name)
         if not IFileNode.providedBy(node):
             raise WebError("'%s' is not a file-cap" % name)
-        return filenode.FileNodeDownloadHandler(node)
+        return filenode.FileNodeDownloadHandler(self.client, node)
 
     def renderHTTP(self, ctx):
         raise WebError("/file must be followed by a file-cap and a name",
@@ -132,14 +138,27 @@ class Root(rend.Page):
     addSlash = True
     docFactory = getxmlfile("welcome.xhtml")
 
-    def __init__(self, original=None):
-        rend.Page.__init__(self, original)
+    def __init__(self, client):
+        rend.Page.__init__(self, client)
+        self.client = client
         self.child_operations = operations.OphandleTable()
 
-    child_uri = URIHandler()
-    child_cap = URIHandler()
-    child_file = FileHandler()
-    child_named = FileHandler()
+        self.child_uri = URIHandler(client)
+        self.child_cap = URIHandler(client)
+
+        self.child_file = FileHandler(client)
+        self.child_named = FileHandler(client)
+        self.child_status = status.Status(client) # TODO: use client.history
+        self.child_statistics = status.Statistics(client.stats_provider)
+
+    def child_helper_status(self, ctx):
+        # the Helper isn't attached until after the Tub starts, so this child
+        # needs to created on each request
+        try:
+            helper = self.client.getServiceNamed("helper")
+        except KeyError:
+            helper = None
+        return status.HelperStatus(helper)
 
     child_webform_css = webform.defaultCSS
     child_tahoe_css = nevow_File(resource_filename('allmydata.web', 'tahoe.css'))
@@ -149,9 +168,6 @@ class Root(rend.Page):
         child_reliability = reliability.ReliabilityTool()
     else:
         child_reliability = NoReliability()
-    child_status = status.Status()
-    child_helper_status = status.HelperStatus()
-    child_statistics = status.Statistics()
 
     child_report_incident = IncidentReporter()
 
@@ -160,15 +176,14 @@ class Root(rend.Page):
     def data_import_path(self, ctx, data):
         return str(allmydata)
     def data_my_nodeid(self, ctx, data):
-        return idlib.nodeid_b2a(IClient(ctx).nodeid)
+        return idlib.nodeid_b2a(self.client.nodeid)
     def data_my_nickname(self, ctx, data):
-        return IClient(ctx).nickname
+        return self.client.nickname
 
     def render_services(self, ctx, data):
         ul = T.ul()
-        client = IClient(ctx)
         try:
-            ss = client.getServiceNamed("storage")
+            ss = self.client.getServiceNamed("storage")
             allocated_s = abbreviate_size(ss.allocated_size())
             allocated = "about %s allocated" % allocated_s
             reserved = "%s reserved" % abbreviate_size(ss.reserved_space)
@@ -177,7 +192,7 @@ class Root(rend.Page):
             ul[T.li["Not running storage server"]]
 
         try:
-            h = client.getServiceNamed("helper")
+            h = self.client.getServiceNamed("helper")
             stats = h.get_stats()
             active_uploads = stats["chk_upload_helper.active_uploads"]
             ul[T.li["Helper: %d active uploads" % (active_uploads,)]]
@@ -187,22 +202,22 @@ class Root(rend.Page):
         return ctx.tag[ul]
 
     def data_introducer_furl(self, ctx, data):
-        return IClient(ctx).introducer_furl
+        return self.client.introducer_furl
     def data_connected_to_introducer(self, ctx, data):
-        if IClient(ctx).connected_to_introducer():
+        if self.client.connected_to_introducer():
             return "yes"
         return "no"
 
     def data_helper_furl(self, ctx, data):
         try:
-            uploader = IClient(ctx).getServiceNamed("uploader")
+            uploader = self.client.getServiceNamed("uploader")
         except KeyError:
             return None
         furl, connected = uploader.get_helper_info()
         return furl
     def data_connected_to_helper(self, ctx, data):
         try:
-            uploader = IClient(ctx).getServiceNamed("uploader")
+            uploader = self.client.getServiceNamed("uploader")
         except KeyError:
             return "no" # we don't even have an Uploader
         furl, connected = uploader.get_helper_info()
@@ -211,18 +226,18 @@ class Root(rend.Page):
         return "no"
 
     def data_known_storage_servers(self, ctx, data):
-        ic = IClient(ctx).introducer_client
+        ic = self.client.introducer_client
         servers = [c
                    for c in ic.get_all_connectors().values()
                    if c.service_name == "storage"]
         return len(servers)
 
     def data_connected_storage_servers(self, ctx, data):
-        ic = IClient(ctx).introducer_client
+        ic = self.client.introducer_client
         return len(ic.get_all_connections_for("storage"))
 
     def data_services(self, ctx, data):
-        ic = IClient(ctx).introducer_client
+        ic = self.client.introducer_client
         c = [ (service_name, nodeid, rsc)
               for (nodeid, service_name), rsc
               in ic.get_all_connectors().items() ]
@@ -235,7 +250,7 @@ class Root(rend.Page):
         ctx.fillSlots("nickname", rsc.nickname)
         if rsc.rref:
             rhost = rsc.remote_host
-            if nodeid == IClient(ctx).nodeid:
+            if nodeid == self.client.nodeid:
                 rhost_s = "(loopback)"
             elif isinstance(rhost, address.IPv4Address):
                 rhost_s = "%s:%d" % (rhost.host, rhost.port)
index fa956ab7785c90c3bca4f9065dfedc9a9fb95bfd..bbd7c1849aa0978e9388ae12a3381a75962cf202 100644 (file)
@@ -6,7 +6,7 @@
     <link href="/webform_css" rel="stylesheet" type="text/css"/>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   </head>
-  <body>
+  <body n:data="get_stats">
 
 <h1>Node Statistics</h1>
 
index 01bfa284dfa76254300c1fb154c9a20802d3db95..d5ce1c697b1cf3693590a086238275dd30b1f391 100644 (file)
@@ -4,8 +4,8 @@ import simplejson
 from twisted.internet import defer
 from nevow import rend, inevow, tags as T
 from allmydata.util import base32, idlib
-from allmydata.web.common import IClient, getxmlfile, abbreviate_time, \
-     abbreviate_rate, abbreviate_size, get_arg
+from allmydata.web.common import getxmlfile, get_arg, \
+     abbreviate_time, abbreviate_rate, abbreviate_size
 from allmydata.interfaces import IUploadStatus, IDownloadStatus, \
      IPublishStatus, IRetrieveStatus, IServermapUpdaterStatus
 
@@ -773,18 +773,22 @@ class Status(rend.Page):
     docFactory = getxmlfile("status.xhtml")
     addSlash = True
 
+    def __init__(self, client):
+        rend.Page.__init__(self, client)
+        self.client = client
+
     def renderHTTP(self, ctx):
-        t = get_arg(inevow.IRequest(ctx), "t")
+        req = inevow.IRequest(ctx)
+        t = get_arg(req, "t")
         if t == "json":
-            return self.json(ctx)
+            return self.json(req)
         return rend.Page.renderHTTP(self, ctx)
 
-    def json(self, ctx):
-        inevow.IRequest(ctx).setHeader("content-type", "text/plain")
-        client = IClient(ctx)
+    def json(self, req):
+        req.setHeader("content-type", "text/plain")
         data = {}
         data["active"] = active = []
-        for s in self.data_active_operations(ctx, None):
+        for s in self._get_active_operations():
             si_s = base32.b2a_or_none(s.get_storage_index())
             size = s.get_size()
             status = s.get_status()
@@ -808,26 +812,31 @@ class Status(rend.Page):
 
         return simplejson.dumps(data, indent=1) + "\n"
 
-    def _get_all_statuses(self, client):
-        return itertools.chain(client.list_all_upload_statuses(),
-                               client.list_all_download_statuses(),
-                               client.list_all_mapupdate_statuses(),
-                               client.list_all_publish_statuses(),
-                               client.list_all_retrieve_statuses(),
-                               client.list_all_helper_statuses(),
+    def _get_all_statuses(self):
+        c = self.client
+        return itertools.chain(c.list_all_upload_statuses(),
+                               c.list_all_download_statuses(),
+                               c.list_all_mapupdate_statuses(),
+                               c.list_all_publish_statuses(),
+                               c.list_all_retrieve_statuses(),
+                               c.list_all_helper_statuses(),
                                )
 
     def data_active_operations(self, ctx, data):
-        client = IClient(ctx)
+        return self._get_active_operations()
+
+    def _get_active_operations(self):
         active = [s
-                  for s in self._get_all_statuses(client)
+                  for s in self._get_all_statuses()
                   if s.get_active()]
         return active
 
     def data_recent_operations(self, ctx, data):
-        client = IClient(ctx)
+        return self._get_recent_operations()
+
+    def _get_recent_operations(self):
         recent = [s
-                  for s in self._get_all_statuses(client)
+                  for s in self._get_all_statuses()
                   if not s.get_active()]
         recent.sort(lambda a,b: cmp(a.get_started(), b.get_started()))
         recent.reverse()
@@ -887,7 +896,7 @@ class Status(rend.Page):
         return ctx.tag
 
     def childFactory(self, ctx, name):
-        client = IClient(ctx)
+        client = self.client
         stype,count_s = name.split("-")
         count = int(count_s)
         if stype == "up":
@@ -918,24 +927,26 @@ class Status(rend.Page):
 class HelperStatus(rend.Page):
     docFactory = getxmlfile("helper.xhtml")
 
+    def __init__(self, helper):
+        rend.Page.__init__(self, helper)
+        self.helper = helper
+
     def renderHTTP(self, ctx):
-        t = get_arg(inevow.IRequest(ctx), "t")
+        req = inevow.IRequest(ctx)
+        t = get_arg(req, "t")
         if t == "json":
-            return self.render_JSON(ctx)
-        # is there a better way to provide 'data' to all rendering methods?
-        helper = IClient(ctx).getServiceNamed("helper")
-        self.original = helper.get_stats()
+            return self.render_JSON(req)
         return rend.Page.renderHTTP(self, ctx)
 
-    def render_JSON(self, ctx):
-        inevow.IRequest(ctx).setHeader("content-type", "text/plain")
-        try:
-            h = IClient(ctx).getServiceNamed("helper")
-        except KeyError:
-            return simplejson.dumps({}) + "\n"
+    def data_helper_stats(self, ctx, data):
+        return self.helper.get_stats()
 
-        stats = h.get_stats()
-        return simplejson.dumps(stats, indent=1) + "\n"
+    def render_JSON(self, req):
+        req.setHeader("content-type", "text/plain")
+        if self.helper:
+            stats = self.helper.get_stats()
+            return simplejson.dumps(stats, indent=1) + "\n"
+        return simplejson.dumps({}) + "\n"
 
     def render_active_uploads(self, ctx, data):
         return data["chk_upload_helper.active_uploads"]
@@ -967,19 +978,22 @@ class HelperStatus(rend.Page):
 class Statistics(rend.Page):
     docFactory = getxmlfile("statistics.xhtml")
 
+    def __init__(self, provider):
+        rend.Page.__init__(self, provider)
+        self.provider = provider
+
     def renderHTTP(self, ctx):
-        provider = IClient(ctx).stats_provider
-        stats = {'stats': {}, 'counters': {}}
-        if provider:
-            stats = provider.get_stats()
-        t = get_arg(inevow.IRequest(ctx), "t")
+        req = inevow.IRequest(ctx)
+        t = get_arg(req, "t")
         if t == "json":
-            inevow.IRequest(ctx).setHeader("content-type", "text/plain")
+            stats = self.provider.get_stats()
+            req.setHeader("content-type", "text/plain")
             return simplejson.dumps(stats, indent=1) + "\n"
-        # is there a better way to provide 'data' to all rendering methods?
-        self.original = stats
         return rend.Page.renderHTTP(self, ctx)
 
+    def data_get_stats(self, ctx, data):
+        return self.provider.get_stats()
+
     def render_load_average(self, ctx, data):
         return str(data["stats"].get("load_monitor.avg_load"))
 
index 3b8d95380377eb0e616eab9425663b3a1fafcabc..d3ef96f67148db175ab77d89c325c238e7d14593 100644 (file)
@@ -3,42 +3,35 @@ import urllib
 from twisted.web import http
 from twisted.internet import defer
 from nevow import rend, url, tags as T
-from nevow.inevow import IRequest
 from allmydata.immutable.upload import FileHandle
-from allmydata.web.common import IClient, getxmlfile, get_arg, boolean_of_arg
+from allmydata.web.common import getxmlfile, get_arg, boolean_of_arg
 from allmydata.web import status
 
-def PUTUnlinkedCHK(ctx):
-    req = IRequest(ctx)
+def PUTUnlinkedCHK(req, client):
     # "PUT /uri", to create an unlinked file.
-    client = IClient(ctx)
     uploadable = FileHandle(req.content, client.convergence)
     d = client.upload(uploadable)
     d.addCallback(lambda results: results.uri)
     # that fires with the URI of the new file
     return d
 
-def PUTUnlinkedSSK(ctx):
-    req = IRequest(ctx)
+def PUTUnlinkedSSK(req, client):
     # SDMF: files are small, and we can only upload data
     req.content.seek(0)
     data = req.content.read()
-    d = IClient(ctx).create_mutable_file(data)
+    d = client.create_mutable_file(data)
     d.addCallback(lambda n: n.get_uri())
     return d
 
-def PUTUnlinkedCreateDirectory(ctx):
-    req = IRequest(ctx)
+def PUTUnlinkedCreateDirectory(req, client):
     # "PUT /uri?t=mkdir", to create an unlinked directory.
-    d = IClient(ctx).create_empty_dirnode()
+    d = client.create_empty_dirnode()
     d.addCallback(lambda dirnode: dirnode.get_uri())
     # XXX add redirect_to_result
     return d
 
 
-def POSTUnlinkedCHK(ctx):
-    req = IRequest(ctx)
-    client = IClient(ctx)
+def POSTUnlinkedCHK(req, client):
     fileobj = req.fields["file"].file
     uploadable = FileHandle(fileobj, client.convergence)
     d = client.upload(uploadable)
@@ -54,7 +47,7 @@ def POSTUnlinkedCHK(ctx):
         d.addCallback(_done, when_done)
     else:
         # return the Upload Results page, which includes the URI
-        d.addCallback(UploadResultsPage, ctx)
+        d.addCallback(UploadResultsPage)
     return d
 
 
@@ -62,7 +55,7 @@ class UploadResultsPage(status.UploadResultsRendererMixin, rend.Page):
     """'POST /uri', to create an unlinked file."""
     docFactory = getxmlfile("upload-results.xhtml")
 
-    def __init__(self, upload_results, ctx):
+    def __init__(self, upload_results):
         rend.Page.__init__(self)
         self.results = upload_results
 
@@ -85,21 +78,19 @@ class UploadResultsPage(status.UploadResultsRendererMixin, rend.Page):
                       ["/uri/" + res.uri])
         return d
 
-def POSTUnlinkedSSK(ctx):
-    req = IRequest(ctx)
+def POSTUnlinkedSSK(req, client):
     # "POST /uri", to create an unlinked file.
     # SDMF: files are small, and we can only upload data
     contents = req.fields["file"]
     contents.file.seek(0)
     data = contents.file.read()
-    d = IClient(ctx).create_mutable_file(data)
+    d = client.create_mutable_file(data)
     d.addCallback(lambda n: n.get_uri())
     return d
 
-def POSTUnlinkedCreateDirectory(ctx):
-    req = IRequest(ctx)
+def POSTUnlinkedCreateDirectory(req, client):
     # "POST /uri?t=mkdir", to create an unlinked directory.
-    d = IClient(ctx).create_empty_dirnode()
+    d = client.create_empty_dirnode()
     redirect = get_arg(req, "redirect_to_result", "false")
     if boolean_of_arg(redirect):
         def _then_redir(res):
index e3f560709bcad33468c0f6196d8af1921d79c0ba..57b84ca582e0d4a913ff274eafcb289a3aba8ff8 100644 (file)
@@ -6,7 +6,7 @@ from nevow import appserver, inevow, static
 from allmydata.util import log
 
 from allmydata.web import introweb, root
-from allmydata.web.common import IClient, IOpHandleTable, MyExceptionHandler
+from allmydata.web.common import IOpHandleTable, MyExceptionHandler
 
 # we must override twisted.web.http.Request.requestReceived with a version
 # that doesn't use cgi.parse_multipart() . Since we actually use Nevow, we
@@ -120,17 +120,21 @@ class MyRequest(appserver.NevowRequest):
 
 class WebishServer(service.MultiService):
     name = "webish"
-    root_class = root.Root
 
-    def __init__(self, webport, nodeurl_path=None, staticdir=None):
+    def __init__(self, client, webport, nodeurl_path=None, staticdir=None):
         service.MultiService.__init__(self)
-        self.webport = webport
-        self.root = self.root_class()
-        self.site = site = appserver.NevowSite(self.root)
-        self.site.requestFactory = MyRequest
+        # the 'data' argument to all render() methods default to the Client
+        self.root = root.Root(client)
+        self.buildServer(webport, nodeurl_path, staticdir)
         if self.root.child_operations:
             self.site.remember(self.root.child_operations, IOpHandleTable)
             self.root.child_operations.setServiceParent(self)
+
+    def buildServer(self, webport, nodeurl_path, staticdir):
+        self.webport = webport
+        self.site = site = appserver.NevowSite(self.root)
+        self.site.requestFactory = MyRequest
+        self.site.remember(MyExceptionHandler(), inevow.ICanHandleException)
         if staticdir:
             self.root.putChild("static", static.File(staticdir))
         s = strports.service(webport, site)
@@ -142,15 +146,6 @@ class WebishServer(service.MultiService):
 
     def startService(self):
         service.MultiService.startService(self)
-        # to make various services available to render_* methods, we stash a
-        # reference to the client on the NevowSite. This will be available by
-        # adapting the 'context' argument to a special marker interface named
-        # IClient.
-        self.site.remember(self.parent, IClient)
-        # I thought you could do the same with an existing interface, but
-        # apparently 'ISite' does not exist
-        #self.site._client = self.parent
-        self.site.remember(MyExceptionHandler(), inevow.ICanHandleException)
         self._started.callback(None)
 
     def _write_nodeurl_file(self, junk, nodeurl_path):
@@ -169,4 +164,8 @@ class WebishServer(service.MultiService):
             f.close()
 
 class IntroducerWebishServer(WebishServer):
-    root_class = introweb.IntroducerRoot
+    def __init__(self, introducer, webport, nodeurl_path=None, staticdir=None):
+        service.MultiService.__init__(self)
+        self.root = introweb.IntroducerRoot(introducer)
+        self.buildServer(webport, nodeurl_path, staticdir)
+