dirnode: add build_manifest() and introduce 'refresh capabilities'

This commit is contained in:
Brian Warner 2007-06-26 19:41:20 -07:00
parent 2766b7988b
commit 18ab5ce837
3 changed files with 107 additions and 7 deletions

View File

@ -329,6 +329,39 @@ class ImmutableDirectoryNode:
d.addCallback(lambda child: self.delete(current_child_name)) d.addCallback(lambda child: self.delete(current_child_name))
return d 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): class MutableDirectoryNode(ImmutableDirectoryNode):
implements(IDirectoryNode) implements(IDirectoryNode)
@ -372,6 +405,10 @@ class FileNode:
return cmp(self.__class__, them.__class__) return cmp(self.__class__, them.__class__)
return cmp(self.uri, them.uri) 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): def download(self, target):
downloader = self._client.getServiceNamed("downloader") downloader = self._client.getServiceNamed("downloader")
return downloader.download(self.uri, target) return downloader.download(self.uri, target)

View File

@ -206,10 +206,22 @@ class IFileNode(Interface):
def download_to_data(): def download_to_data():
pass 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): class IDirectoryNode(Interface):
def is_mutable(): def is_mutable():
"""Return True if this directory is mutable, False if it is read-only. """Return True if this directory is mutable, False if it is read-only.
""" """
def get_uri(): def get_uri():
"""Return the directory URI that can be used by others to get access """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 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 If you have read-write access to a directory and wish to share merely
read-only access with others, use get_immutable_uri(). read-only access with others, use get_immutable_uri().
The dirnode ('1') URI returned by this method can be used in set() on The dirnode ('1') URI returned by this method can be used in
a different directory ('2') to 'mount' a reference to this directory set_uri() on a different directory ('2') to 'mount' a reference to
('1') under the other ('2'). This URI is just a string, so it can be this directory ('1') under the other ('2'). This URI is just a
passed around through email or other out-of-band protocol. string, so it can be passed around through email or other out-of-band
protocol.
""" """
def get_immutable_uri(): def get_immutable_uri():
@ -234,6 +247,12 @@ class IDirectoryNode(Interface):
get_immutable_uri() will return the same thing as get_uri(). 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(): def list():
"""I return a Deferred that fires with a dictionary mapping child """I return a Deferred that fires with a dictionary mapping child
name to an IFileNode or IDirectoryNode.""" 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 'new_child_name', which defaults to 'current_child_name'. I return a
Deferred that fires when the operation finishes.""" 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): class ICodecEncoder(Interface):
def set_params(data_size, required_shares, max_shares): def set_params(data_size, required_shares, max_shares):

View File

@ -173,8 +173,8 @@ class Test(unittest.TestCase):
self.failUnlessEqual(res, {}) self.failUnlessEqual(res, {})
d.addCallback(_listed) d.addCallback(_listed)
file1 = uri.pack_uri("i"*32, "k"*16, "e"*32, 25, 100, 12345) file1 = uri.pack_uri("11" + " "*30, "k"*16, "e"*32, 25, 100, 12345)
file2 = uri.pack_uri("i"*31 + "2", "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) file2_node = dirnode.FileNode(file2, None)
d.addCallback(lambda res: rootnode.set_uri("foo", file1)) d.addCallback(lambda res: rootnode.set_uri("foo", file1))
# root/ # root/
@ -184,6 +184,7 @@ class Test(unittest.TestCase):
def _listed2(res): def _listed2(res):
self.failUnlessEqual(res.keys(), ["foo"]) self.failUnlessEqual(res.keys(), ["foo"])
file1_node = res["foo"] file1_node = res["foo"]
self.file1_node = file1_node
self.failUnless(isinstance(file1_node, dirnode.FileNode)) self.failUnless(isinstance(file1_node, dirnode.FileNode))
self.failUnlessEqual(file1_node.uri, file1) self.failUnlessEqual(file1_node.uri, file1)
d.addCallback(_listed2) d.addCallback(_listed2)
@ -238,6 +239,10 @@ class Test(unittest.TestCase):
d.addCallback(self.failUnlessIdentical, file2_node) d.addCallback(self.failUnlessIdentical, file2_node)
# and a directory # and a directory
d.addCallback(lambda res: self.bar_node.create_empty_directory("baz")) 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/
# root/foo =file1 # root/foo =file1
# root/bar/ # root/bar/
@ -257,6 +262,21 @@ class Test(unittest.TestCase):
d.addCallback(lambda res: d.addCallback(lambda res:
self.failIf(res["baz"].is_mutable())) 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 # try to add a file to bar-ro, should get exception
d.addCallback(lambda res: d.addCallback(lambda res:
self.bar_node_readonly.set_uri("file3", file2)) self.bar_node_readonly.set_uri("file3", file2))
@ -290,7 +310,7 @@ class Test(unittest.TestCase):
self.bar_node.move_child_to("file2", self.bar_node.move_child_to("file2",
self.rootnode, "file4")) self.rootnode, "file4"))
# root/ # root/
# root/file4 = file4 # root/file4 = file2
# root/bar/ # root/bar/
# root/bar/baz/ # root/bar/baz/
# root/bar-ro/ (read-only) # root/bar-ro/ (read-only)
@ -327,6 +347,27 @@ class Test(unittest.TestCase):
d.addCallback(self.failUnlessKeysMatch, ["baz", "file4"]) d.addCallback(self.failUnlessKeysMatch, ["baz", "file4"])
d.addCallback(lambda res:self.bar_node_readonly.list()) d.addCallback(lambda res:self.bar_node_readonly.list())
d.addCallback(self.failUnlessKeysMatch, ["baz", "file4"]) 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) d.addCallback(self._test_one_3)
return d return d