]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
Add the 'vdrive' service, for clients to access the public/private root dirs.
authorBrian Warner <warner@allmydata.com>
Thu, 28 Jun 2007 00:11:06 +0000 (17:11 -0700)
committerBrian Warner <warner@allmydata.com>
Thu, 28 Jun 2007 00:11:06 +0000 (17:11 -0700)
These allow client-side code to conveniently retrieve the IDirectoryNode
instances for both the global shared public root directory, and the per-user
private root directory.

docs/codemap.txt
src/allmydata/client.py
src/allmydata/interfaces.py
src/allmydata/test/test_system.py
src/allmydata/vdrive.py [new file with mode: 0644]
src/allmydata/webish.py

index b61a36816f3dc5fef6c509749994663fdaf5f53b..d5e559411c2666c3391aa585f88ec181afdb5ea6 100644 (file)
@@ -23,8 +23,10 @@ Within src/allmydata/ :
  node.py: the base Node, which handles connection establishment and
           application startup
 
- client.py, introducer_and_vdrive.py: two specialized subclasses of Node, for users
-                      and the central introducer/vdrive handler, respectively
+ client.py, introducer_and_vdrive.py:
+   these are two specialized subclasses of Node, for users and the central
+   introducer/vdrive handler, respectively. Each works by assembling a
+   collection of services underneath a top-level Node instance.
 
  introducer.py: node introduction handlers, client is used by client.py,
                 server is used by introducer_and_vdrive.py
@@ -38,20 +40,21 @@ Within src/allmydata/ :
 
  download.py: download-side peer selection, share retrieval, decoding
 
- filetable.py, vdrive.py: implements the current one-global-vdrive layer,
-                          part runs on client nodes, part runs on the
-                          central vdrive handler
+ dirnode.py: implements the directory nodes. One part runs on the
+             global vdrive server, the other runs inside a client
+             (starting with vdrive.py)
+
+ vdrive.py: provides a client-side service that accesses the global
+            shared virtual drive and the per-user private drive.
 
  webish.py, web/*.xhtml: provides the web frontend, using a Nevow server
 
- workqueue.py, filetree/*.py: building blocks for the future filetree work
+ uri.py: URI packing/parsing routines
 
  hashtree.py: Merkle hash tree classes
 
  debugshell.py, manhole.py: SSH-connected python shell, for debug purposes
 
- uri.py: URI packing/parsing routines
-
  util/*.py: misc utility classes
 
  test/*.py: unit tests
@@ -59,9 +62,9 @@ Within src/allmydata/ :
 
 Both the client and the central introducer-and-vdrive node runs as a tree of
 (twisted.application.service) Service instances. The Foolscap "Tub" is one of
-these. Client nodes have an Uploader service and a Downloader service that turn
-data into URIs and back again. They also have a VDrive service which provides
-access to the single global shared filesystem.
+these. Client nodes have an Uploader service and a Downloader service that
+turn data into URIs and back again. They also have a VirtualDrive service
+which provides access to the single global shared filesystem.
 
 The Uploader is given an "upload source" (which could be an open filehandle,
 a filename on local disk, or even a string), and returns a Deferred that
index c6e20ba85d7a6a21745dad2aa9e978357e9a2887..a87dd0b3f2565c6b5c114d189ec041a72ee568cb 100644 (file)
@@ -2,8 +2,8 @@
 import os, sha, stat, time
 from foolscap import Referenceable, SturdyRef
 from zope.interface import implements
-from allmydata.interfaces import RIClient, IDirectoryNode
-from allmydata import node, uri
+from allmydata.interfaces import RIClient
+from allmydata import node
 
 from twisted.internet import defer, reactor
 from twisted.application.internet import TimerService
@@ -16,7 +16,7 @@ from allmydata.download import Downloader
 from allmydata.webish import WebishServer
 from allmydata.control import ControlServer
 from allmydata.introducer import IntroducerClient
-from allmydata.dirnode import create_directory_node, create_directory
+from allmydata.vdrive import VirtualDrive
 
 class Client(node.Node, Referenceable):
     implements(RIClient)
@@ -25,10 +25,8 @@ class Client(node.Node, Referenceable):
     NODETYPE = "client"
     WEBPORTFILE = "webport"
     INTRODUCER_FURL_FILE = "introducer.furl"
-    GLOBAL_VDRIVE_FURL_FILE = "vdrive.furl"
     MY_FURL_FILE = "myself.furl"
     SUICIDE_PREVENTION_HOTLINE_FILE = "suicide_prevention_hotline"
-    MY_VDRIVE_URI_FILE = "my_vdrive.uri"
 
     # we're pretty narrow-minded right now
     OLDEST_SUPPORTED_VERSION = allmydata.__version__
@@ -37,10 +35,10 @@ class Client(node.Node, Referenceable):
         node.Node.__init__(self, basedir)
         self.my_furl = None
         self.introducer_client = None
-        self._connected_to_vdrive = False
         self.add_service(StorageServer(os.path.join(basedir, self.STOREDIR)))
         self.add_service(Uploader())
         self.add_service(Downloader())
+        self.add_service(VirtualDrive())
         WEBPORTFILE = os.path.join(self.basedir, self.WEBPORTFILE)
         if os.path.exists(WEBPORTFILE):
             f = open(WEBPORTFILE, "r")
@@ -54,16 +52,6 @@ class Client(node.Node, Referenceable):
         self.introducer_furl = f.read().strip()
         f.close()
 
-        self.global_vdrive_furl = None
-        GLOBAL_VDRIVE_FURL_FILE = os.path.join(self.basedir,
-                                               self.GLOBAL_VDRIVE_FURL_FILE)
-        if os.path.exists(GLOBAL_VDRIVE_FURL_FILE):
-            f = open(GLOBAL_VDRIVE_FURL_FILE, "r")
-            self.global_vdrive_furl = f.read().strip()
-            f.close()
-            #self.add_service(VDrive())
-        self._my_vdrive = None
-
         hotline_file = os.path.join(self.basedir,
                                     self.SUICIDE_PREVENTION_HOTLINE_FILE)
         if os.path.exists(hotline_file):
@@ -99,10 +87,6 @@ class Client(node.Node, Referenceable):
 
         self.register_control()
 
-        if self.global_vdrive_furl:
-            self.vdrive_connector = self.tub.connectTo(self.global_vdrive_furl,
-                                                       self._got_vdrive)
-
     def register_control(self):
         c = ControlServer()
         c.setServiceParent(self)
@@ -112,60 +96,6 @@ class Client(node.Node, Referenceable):
         f.close()
         os.chmod("control.furl", 0600)
 
-    def _got_vdrive(self, vdrive_server):
-        # vdrive_server implements RIVirtualDriveServer
-        self.log("connected to vdrive server")
-        d = vdrive_server.callRemote("get_public_root_uri")
-        d.addCallback(self._got_vdrive_uri)
-        d.addCallback(self._got_vdrive_rootnode)
-        d.addCallback(self._create_my_vdrive, vdrive_server)
-        d.addCallback(self._got_my_vdrive)
-
-    def _got_vdrive_uri(self, root_uri):
-        furl, wk = uri.unpack_dirnode_uri(root_uri)
-        self._vdrive_furl = furl
-        return create_directory_node(self, root_uri)
-
-    def _got_vdrive_rootnode(self, rootnode):
-        self.log("got vdrive root")
-        self._vdrive_root = rootnode
-        self._connected_to_vdrive = True
-
-        #vdrive = self.getServiceNamed("vdrive")
-        #vdrive.set_server(vdrive_server)
-        #vdrive.set_root(vdrive_root)
-
-        if "webish" in self.namedServices:
-            webish = self.getServiceNamed("webish")
-            webish.set_vdrive_rootnode(rootnode)
-
-    def _create_my_vdrive(self, ignored, vdrive_server):
-        MY_VDRIVE_URI_FILE = os.path.join(self.basedir,
-                                           self.MY_VDRIVE_URI_FILE)
-        try:
-            f = open(MY_VDRIVE_URI_FILE, "r")
-            my_vdrive_uri = f.read().strip()
-            f.close()
-            return create_directory_node(self, my_vdrive_uri)
-        except EnvironmentError:
-            assert self._vdrive_furl
-            d = create_directory(self, self._vdrive_furl)
-            def _got_directory(dirnode):
-                f = open(MY_VDRIVE_URI_FILE, "w")
-                f.write(dirnode.get_uri() + "\n")
-                f.close()
-                return dirnode
-            d.addCallback(_got_directory)
-            return d
-
-    def _got_my_vdrive(self, my_vdrive):
-        IDirectoryNode(my_vdrive)
-        self._my_vdrive = my_vdrive
-
-        if "webish" in self.namedServices:
-            webish = self.getServiceNamed("webish")
-            webish.set_my_vdrive_rootnode(my_vdrive)
-
 
     def remote_get_versions(self):
         return str(allmydata.__version__), str(self.OLDEST_SUPPORTED_VERSION)
index db39c68a17509aea28800ac616d3b092c2c04b03..20d3ca9344721a1fe6e1909c4edab10a09069989 100644 (file)
@@ -655,6 +655,35 @@ class IUploader(Interface):
     def upload_filehandle(filehane):
         """Like upload(), but accepts an open filehandle."""
 
+class IVirtualDrive(Interface):
+    """I am a service that may be available to a client.
+
+    Within any client program, this service can be retrieved by using
+    client.getService('vdrive').
+    """
+
+    def have_public_root():
+        """Return a Boolean, True if get_public_root() will work."""
+    def get_public_root():
+        """Get the public read-write directory root.
+
+        This returns a Deferred that fires with an IDirectoryNode instance
+        corresponding to the global shared root directory."""
+
+
+    def have_private_root():
+        """Return a Boolean, True if get_public_root() will work."""
+    def get_private_root():
+        """Get the private directory root.
+
+        This returns a Deferred that fires with an IDirectoryNode instance
+        corresponding to this client's private root directory."""
+
+    def get_node(self, uri):
+        """Transform a URI into an IDirectoryNode or IFileNode.
+
+        This returns a Deferred that will fire with an instance that provides
+        either IDirectoryNode or IFileNode, as appropriate."""
 
 class NotCapableError(Exception):
     """You have tried to write to a read-only node."""
index 4f14731c2527643ca665c494a4d8ac7e2dc4db8a..651dfc1f74208bc6e0bdd0952c8ae20a5345245f 100644 (file)
@@ -238,7 +238,8 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase):
             log.msg("PUBLISHING")
             ut = upload.Data(DATA)
             c0 = self.clients[0]
-            d1 = c0._vdrive_root.create_empty_directory("subdir1")
+            d1 = c0.getServiceNamed("vdrive").get_public_root()
+            d1.addCallback(lambda root: root.create_empty_directory("subdir1"))
             d1.addCallback(lambda subdir1_node:
                            subdir1_node.add_file("mydata567", ut))
             def _stash_uri(filenode):
@@ -251,7 +252,8 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase):
             log.msg("publish finished")
 
             c1 = self.clients[1]
-            d1 = c1._vdrive_root.get("subdir1")
+            d1 = c1.getServiceNamed("vdrive").get_public_root()
+            d1.addCallback(lambda root: root.get("subdir1"))
             d1.addCallback(lambda subdir1: subdir1.get("mydata567"))
             d1.addCallback(lambda filenode: filenode.download_to_data())
             return d1
diff --git a/src/allmydata/vdrive.py b/src/allmydata/vdrive.py
new file mode 100644 (file)
index 0000000..b231a84
--- /dev/null
@@ -0,0 +1,91 @@
+
+import os
+from twisted.application import service
+from zope.interface import implements
+from allmydata.interfaces import IVirtualDrive
+from allmydata import dirnode, uri
+from twisted.internet import defer
+
+class NoGlobalVirtualDriveError(Exception):
+    pass
+class NoPrivateVirtualDriveError(Exception):
+    pass
+
+class VirtualDrive(service.MultiService):
+    implements(IVirtualDrive)
+    name = "vdrive"
+
+    GLOBAL_VDRIVE_FURL_FILE = "vdrive.furl"
+
+    GLOBAL_VDRIVE_URI_FILE = "global_root.uri"
+    MY_VDRIVE_URI_FILE = "my_vdrive.uri"
+
+    def __init__(self):
+        service.MultiService.__init__(self)
+        self._global_uri = None
+        self._private_uri = None
+
+    def startService(self):
+        service.MultiService.startService(self)
+        basedir = self.parent.basedir
+        client = self.parent
+        tub = self.parent.tub
+
+        global_vdrive_furl = None
+        furl_file = os.path.join(basedir, self.GLOBAL_VDRIVE_FURL_FILE)
+        if os.path.exists(furl_file):
+            f = open(furl_file, "r")
+            global_vdrive_furl = f.read().strip()
+            f.close()
+
+        global_uri_file = os.path.join(basedir,
+                                       self.GLOBAL_VDRIVE_URI_FILE)
+        if os.path.exists(global_uri_file):
+            f = open(global_uri_file)
+            self._global_uri = f.read().strip()
+            f.close()
+        elif global_vdrive_furl:
+            d = tub.getReference(global_vdrive_furl)
+            d.addCallback(lambda vdrive_server:
+                          vdrive_server.callRemote("get_public_root_uri"))
+            def _got_global_uri(global_uri):
+                self._global_uri = global_uri
+            d.addCallback(_got_global_uri)
+
+        private_uri_file = os.path.join(basedir,
+                                        self.MY_VDRIVE_URI_FILE)
+        if os.path.exists(private_uri_file):
+            f = open(private_uri_file)
+            private_vdrive_uri = f.read().strip()
+            f.close()
+        elif global_vdrive_furl:
+            d = dirnode.create_directory(client, global_vdrive_furl)
+            def _got_directory(dirnode):
+                private_uri = dirnode.get_uri()
+                self._private_uri = private_uri
+                f = open(private_uri_file, "w")
+                f.write(private_uri + "\n")
+                f.close()
+            d.addCallback(_got_directory)
+
+
+    def get_node(self, node_uri):
+        if uri.is_dirnode_uri(node_uri):
+            return dirnode.create_directory_node(self.parent, node_uri)
+        else:
+            return defer.succeed(dirnode.FileNode(node_uri, self.parent))
+
+    def have_public_root(self):
+        return bool(self._global_uri)
+    def get_public_root(self):
+        if not self._global_uri:
+            return defer.fail(NoGlobalVirtualDriveError())
+        return self.get_node(self._global_uri)
+
+    def have_private_root(self):
+        return bool(self._private_uri)
+    def get_private_root(self):
+        if not self._private_uri:
+            return defer.fail(NoPrivateVirtualDriveError())
+        return self.get_node(self._private_uri)
+
index 70db09509d930b4e6a5f1d9e7d3ce668364db21a..e92b8db94f4754c4e6956d54a99d60283a8da285 100644 (file)
@@ -42,7 +42,7 @@ class Welcome(rend.Page):
             return "yes"
         return "no"
     def data_connected_to_vdrive(self, ctx, data):
-        if IClient(ctx).connected_to_vdrive():
+        if IClient(ctx).getServiceNamed("vdrive").have_public_root():
             return "yes"
         return "no"
     def data_num_peers(self, ctx, data):
@@ -64,7 +64,7 @@ class Welcome(rend.Page):
         return ctx.tag
 
     def render_global_vdrive(self, ctx, data):
-        if self.has_global_vdrive:
+        if IClient(ctx).getServiceNamed("vdrive").have_public_root():
             return T.p["To view the global shared filestore, ",
                        T.a(href="../global_vdrive")["Click Here!"],
                        ]
@@ -72,7 +72,7 @@ class Welcome(rend.Page):
                    "responding), no vdrive available."]
 
     def render_my_vdrive(self, ctx, data):
-        if self.has_my_vdrive:
+        if IClient(ctx).getServiceNamed("vdrive").have_private_root():
             return T.p["To view your personal private non-shared filestore, ",
                        T.a(href="../my_vdrive")["Click Here!"],
                        ]
@@ -397,6 +397,26 @@ class Root(rend.Page):
 
     child_welcome = Welcome()
 
+    def child_global_vdrive(self, ctx):
+        client = IClient(ctx)
+        vdrive = client.getServiceNamed("vdrive")
+        if vdrive.have_public_root():
+            d = vdrive.get_public_root()
+            d.addCallback(lambda dirnode: Directory(dirnode, "/"))
+            return d
+        else:
+            return static.Data("sorry, still initializing", "text/plain")
+
+    def child_private_vdrive(self, ctx):
+        client = IClient(ctx)
+        vdrive = client.getServiceNamed("vdrive")
+        if vdrive.have_private_root():
+            d = vdrive.get_private_root()
+            d.addCallback(lambda dirnode: Directory(dirnode, "~"))
+            return d
+        else:
+            return static.Data("sorry, still initializing", "text/plain")
+
 
 class WebishServer(service.MultiService):
     name = "webish"
@@ -404,10 +424,6 @@ class WebishServer(service.MultiService):
     def __init__(self, webport):
         service.MultiService.__init__(self)
         self.root = Root()
-        self.root.child_welcome.has_global_vdrive = False
-        self.root.child_welcome.has_my_vdrive = False
-        placeholder = static.Data("sorry, still initializing", "text/plain")
-        self.root.putChild("vdrive", placeholder)
         self.root.putChild("", url.here.child("welcome"))#Welcome())
                            
         self.site = site = appserver.NevowSite(self.root)
@@ -425,15 +441,3 @@ class WebishServer(service.MultiService):
         # I thought you could do the same with an existing interface, but
         # apparently 'ISite' does not exist
         #self.site._client = self.parent
-
-    def set_vdrive_rootnode(self, root):
-        self.root.putChild("global_vdrive", Directory(root, "/"))
-        self.root.child_welcome.has_global_vdrive = True
-        # I tried doing it this way and for some reason it didn't seem to work
-        #print "REMEMBERING", self.site, dl, IDownloader
-        #self.site.remember(dl, IDownloader)
-
-    def set_my_vdrive_rootnode(self, my_vdrive):
-        self.root.putChild("my_vdrive", Directory(my_vdrive, "~"))
-        self.root.child_welcome.has_my_vdrive = True
-