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))
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)

View 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):

View 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