]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
dirnode: add build_manifest() and introduce 'refresh capabilities'
authorBrian Warner <warner@lothar.com>
Wed, 27 Jun 2007 02:41:20 +0000 (19:41 -0700)
committerBrian Warner <warner@lothar.com>
Wed, 27 Jun 2007 02:41:20 +0000 (19:41 -0700)
src/allmydata/dirnode.py
src/allmydata/interfaces.py
src/allmydata/test/test_dirnode.py

index 8a519c51d9e124d3291810e530511a119edd3b8b..d655035b5bde9dbe2cae2662631133c637f36911 100644 (file)
@@ -329,6 +329,39 @@ class ImmutableDirectoryNode:
         d.addCallback(lambda child: self.delete(current_child_name))
         return d
 
+    def build_manifest(self):
+        # given a dirnode, construct a list refresh-capabilities for all the
+        # nodes it references.
+
+        # this is just a tree-walker, except that following each edge
+        # requires a Deferred.
+
+        manifest = set()
+        manifest.add(self.get_refresh_capability())
+
+        d = self._build_manifest_from_node(self, manifest)
+        d.addCallback(lambda res: manifest)
+        return d
+
+    def _build_manifest_from_node(self, node, manifest):
+        d = node.list()
+        def _got_list(res):
+            dl = []
+            for name, child in res.iteritems():
+                manifest.add(child.get_refresh_capability())
+                if IDirectoryNode.providedBy(child):
+                    dl.append(self._build_manifest_from_node(child, manifest))
+            if dl:
+                return defer.DeferredList(dl)
+        d.addCallback(_got_list)
+        return d
+
+    def get_refresh_capability(self):
+        ro_uri = self.get_immutable_uri()
+        furl, rk = uri.unpack_dirnode_uri(ro_uri)
+        wk, we, rk, index = hashutil.generate_dirnode_keys_from_readkey(rk)
+        return "DIR-REFRESH:%s" % idlib.b2a(index)
+
 class MutableDirectoryNode(ImmutableDirectoryNode):
     implements(IDirectoryNode)
 
@@ -372,6 +405,10 @@ class FileNode:
             return cmp(self.__class__, them.__class__)
         return cmp(self.uri, them.uri)
 
+    def get_refresh_capability(self):
+        d = uri.unpack_uri(self.uri)
+        return "CHK-REFRESH:%s" % idlib.b2a(d['storage_index'])
+
     def download(self, target):
         downloader = self._client.getServiceNamed("downloader")
         return downloader.download(self.uri, target)
index a9ccde8cd59196919eaa9b299d65cb4e17e86e66..db39c68a17509aea28800ac616d3b092c2c04b03 100644 (file)
@@ -206,10 +206,22 @@ class IFileNode(Interface):
     def download_to_data():
         pass
 
+    def get_uri():
+        """Return the URI that can be used by others to get access to this
+        file.
+        """
+
+    def get_refresh_capability():
+        """Return a string that represents the 'refresh capability' for this
+        node. The holder of this capability will be able to renew the lease
+        for this node, protecting it from garbage-collection.
+        """
+
 class IDirectoryNode(Interface):
     def is_mutable():
         """Return True if this directory is mutable, False if it is read-only.
         """
+
     def get_uri():
         """Return the directory URI that can be used by others to get access
         to this directory node. If this node is read-only, the URI will only
@@ -219,10 +231,11 @@ class IDirectoryNode(Interface):
         If you have read-write access to a directory and wish to share merely
         read-only access with others, use get_immutable_uri().
 
-        The dirnode ('1') URI returned by this method can be used in set() on
-        a different directory ('2') to 'mount' a reference to this directory
-        ('1') under the other ('2'). This URI is just a string, so it can be
-        passed around through email or other out-of-band protocol.
+        The dirnode ('1') URI returned by this method can be used in
+        set_uri() on a different directory ('2') to 'mount' a reference to
+        this directory ('1') under the other ('2'). This URI is just a
+        string, so it can be passed around through email or other out-of-band
+        protocol.
         """
 
     def get_immutable_uri():
@@ -234,6 +247,12 @@ class IDirectoryNode(Interface):
         get_immutable_uri() will return the same thing as get_uri().
         """
 
+    def get_refresh_capability():
+        """Return a string that represents the 'refresh capability' for this
+        node. The holder of this capability will be able to renew the lease
+        for this node, protecting it from garbage-collection.
+        """
+
     def list():
         """I return a Deferred that fires with a dictionary mapping child
         name to an IFileNode or IDirectoryNode."""
@@ -280,6 +299,9 @@ class IDirectoryNode(Interface):
         'new_child_name', which defaults to 'current_child_name'. I return a
         Deferred that fires when the operation finishes."""
 
+    def build_manifest():
+        """Return a set of refresh-capabilities for all nodes (directories
+        and files) reachable from this one."""
 
 class ICodecEncoder(Interface):
     def set_params(data_size, required_shares, max_shares):
index c1c734fde40b3d3538f4e9f8cafed862dab3f3ae..43b9b87c3e6d3ddb79b54be3a5b3d9fe87acdb9b 100644 (file)
@@ -173,8 +173,8 @@ class Test(unittest.TestCase):
             self.failUnlessEqual(res, {})
         d.addCallback(_listed)
 
-        file1 = uri.pack_uri("i"*32, "k"*16, "e"*32, 25, 100, 12345)
-        file2 = uri.pack_uri("i"*31 + "2", "k"*16, "e"*32, 25, 100, 12345)
+        file1 = uri.pack_uri("11" + " "*30, "k"*16, "e"*32, 25, 100, 12345)
+        file2 = uri.pack_uri("2i" + " "*30, "k"*16, "e"*32, 25, 100, 12345)
         file2_node = dirnode.FileNode(file2, None)
         d.addCallback(lambda res: rootnode.set_uri("foo", file1))
         # root/
@@ -184,6 +184,7 @@ class Test(unittest.TestCase):
         def _listed2(res):
             self.failUnlessEqual(res.keys(), ["foo"])
             file1_node = res["foo"]
+            self.file1_node = file1_node
             self.failUnless(isinstance(file1_node, dirnode.FileNode))
             self.failUnlessEqual(file1_node.uri, file1)
         d.addCallback(_listed2)
@@ -238,6 +239,10 @@ class Test(unittest.TestCase):
         d.addCallback(self.failUnlessIdentical, file2_node)
         # and a directory
         d.addCallback(lambda res: self.bar_node.create_empty_directory("baz"))
+        def _added_baz(baz_node):
+            self.failUnless(IDirectoryNode.providedBy(baz_node))
+            self.baz_node = baz_node
+        d.addCallback(_added_baz)
         # root/
         # root/foo =file1
         # root/bar/
@@ -257,6 +262,21 @@ class Test(unittest.TestCase):
         d.addCallback(lambda res:
                       self.failIf(res["baz"].is_mutable()))
 
+        # test the manifest
+        d.addCallback(lambda res: self.rootnode.build_manifest())
+        def _check_manifest(manifest):
+            manifest = sorted(list(manifest))
+            self.failUnlessEqual(len(manifest), 5)
+            expected = [self.rootnode.get_refresh_capability(),
+                        self.bar_node.get_refresh_capability(),
+                        self.file1_node.get_refresh_capability(),
+                        file2_node.get_refresh_capability(),
+                        self.baz_node.get_refresh_capability(),
+                        ]
+            expected.sort()
+            self.failUnlessEqual(manifest, expected)
+        d.addCallback(_check_manifest)
+
         # try to add a file to bar-ro, should get exception
         d.addCallback(lambda res:
                       self.bar_node_readonly.set_uri("file3", file2))
@@ -290,7 +310,7 @@ class Test(unittest.TestCase):
                       self.bar_node.move_child_to("file2",
                                                   self.rootnode, "file4"))
         # root/
-        # root/file4 = file4
+        # root/file4 = file2
         # root/bar/
         # root/bar/baz/
         # root/bar-ro/  (read-only)
@@ -327,6 +347,27 @@ class Test(unittest.TestCase):
         d.addCallback(self.failUnlessKeysMatch, ["baz", "file4"])
         d.addCallback(lambda res:self.bar_node_readonly.list())
         d.addCallback(self.failUnlessKeysMatch, ["baz", "file4"])
+        # root/
+        # root/bar/
+        # root/bar/file4 = file2
+        # root/bar/baz/
+        # root/bar-ro/  (read-only)
+        # root/bar-ro/file4 = file2
+        # root/bar-ro/baz/
+
+        # test the manifest
+        d.addCallback(lambda res: self.rootnode.build_manifest())
+        def _check_manifest2(manifest):
+            manifest = sorted(list(manifest))
+            self.failUnlessEqual(len(manifest), 4)
+            expected = [self.rootnode.get_refresh_capability(),
+                        self.bar_node.get_refresh_capability(),
+                        file2_node.get_refresh_capability(),
+                        self.baz_node.get_refresh_capability(),
+                        ]
+            expected.sort()
+            self.failUnlessEqual(manifest, expected)
+        d.addCallback(_check_manifest2)
 
         d.addCallback(self._test_one_3)
         return d