From c6c30ac5d4965dc4b480e374dec5d4cb4e6815f1 Mon Sep 17 00:00:00 2001 From: Brian Warner <warner@lothar.com> Date: Fri, 20 Feb 2009 12:15:54 -0700 Subject: [PATCH] webapi: pass client through constructor arguments, remove IClient, should make it easier to test web renderers in isolation --- src/allmydata/client.py | 4 +- src/allmydata/introducer/server.py | 2 +- src/allmydata/test/test_web.py | 8 ++- src/allmydata/web/check_results.py | 61 +++++++++++-------- src/allmydata/web/common.py | 2 - src/allmydata/web/directory.py | 77 +++++++++++++----------- src/allmydata/web/filenode.py | 50 +++++++--------- src/allmydata/web/helper.xhtml | 2 +- src/allmydata/web/introweb.py | 30 +++++----- src/allmydata/web/root.py | 89 +++++++++++++++------------ src/allmydata/web/statistics.xhtml | 2 +- src/allmydata/web/status.py | 96 +++++++++++++++++------------- src/allmydata/web/unlinked.py | 35 ++++------- src/allmydata/webish.py | 33 +++++----- 14 files changed, 265 insertions(+), 226 deletions(-) diff --git a/src/allmydata/client.py b/src/allmydata/client.py index a77bda4f..766cae04 100644 --- a/src/allmydata/client.py +++ b/src/allmydata/client.py @@ -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): diff --git a/src/allmydata/introducer/server.py b/src/allmydata/introducer/server.py index 5e519533..d0ed6412 100644 --- a/src/allmydata/introducer/server.py +++ b/src/allmydata/introducer/server.py @@ -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): diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py index 940a1c8b..e2ae8fc4 100644 --- a/src/allmydata/test/test_web.py +++ b/src/allmydata/test/test_web.py @@ -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 diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index bf7d75db..eeb30709 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -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) diff --git a/src/allmydata/web/common.py b/src/allmydata/web/common.py index a831b80d..ec86d46a 100644 --- a/src/allmydata/web/common.py +++ b/src/allmydata/web/common.py @@ -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 diff --git a/src/allmydata/web/directory.py b/src/allmydata/web/directory.py index e8ce3f7a..d4798665 100644 --- a/src/allmydata/web/directory.py +++ b/src/allmydata/web/directory.py @@ -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): diff --git a/src/allmydata/web/filenode.py b/src/allmydata/web/filenode.py index 0a2081a7..78e235b6 100644 --- a/src/allmydata/web/filenode.py +++ b/src/allmydata/web/filenode.py @@ -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) diff --git a/src/allmydata/web/helper.xhtml b/src/allmydata/web/helper.xhtml index bea58017..f5c9e298 100644 --- a/src/allmydata/web/helper.xhtml +++ b/src/allmydata/web/helper.xhtml @@ -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> diff --git a/src/allmydata/web/introweb.py b/src/allmydata/web/introweb.py index f1a6ad02..3210fda1 100644 --- a/src/allmydata/web/introweb.py +++ b/src/allmydata/web/introweb.py @@ -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 diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index f429e05f..e82d5758 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -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) diff --git a/src/allmydata/web/statistics.xhtml b/src/allmydata/web/statistics.xhtml index fa956ab7..bbd7c184 100644 --- a/src/allmydata/web/statistics.xhtml +++ b/src/allmydata/web/statistics.xhtml @@ -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> diff --git a/src/allmydata/web/status.py b/src/allmydata/web/status.py index 01bfa284..d5ce1c69 100644 --- a/src/allmydata/web/status.py +++ b/src/allmydata/web/status.py @@ -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")) diff --git a/src/allmydata/web/unlinked.py b/src/allmydata/web/unlinked.py index 3b8d9538..d3ef96f6 100644 --- a/src/allmydata/web/unlinked.py +++ b/src/allmydata/web/unlinked.py @@ -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): diff --git a/src/allmydata/webish.py b/src/allmydata/webish.py index e3f56070..57b84ca5 100644 --- a/src/allmydata/webish.py +++ b/src/allmydata/webish.py @@ -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) + -- 2.45.2