checkpont more filetree stuff

This commit is contained in:
Brian Warner 2007-01-19 23:22:39 -07:00
parent 8921c1b666
commit 4a0f8bc110
5 changed files with 237 additions and 134 deletions

View File

@ -0,0 +1,16 @@
from zope.interface import implements
from allmydata.filetree.interfaces import INode
class BaseURINode(object):
implements(INode)
prefix = None # must be set by subclass
def is_directory(self):
return False
def serialize_node(self):
return "%s:%s" % (self.prefix, self.uri)
def populate_node(self, data, node_maker):
assert data.startswith(self.prefix + ":")
self.uri = data[len(self.prefix)+1:]

View File

@ -1,11 +1,11 @@
from zope.interface import implements
from allmydata.filetree.interfaces import (INode,
IDirectoryNode,
ISubTree,
ICHKDirectoryNode, ISSKDirectoryNode,
NoSuchChildError,
)
from allmydata.filetree.interfaces import (
INode, IDirectoryNode, ISubTree,
ICHKDirectoryNode, ISSKDirectoryNode,
NoSuchChildError,
)
from allmydata.filetree.basenode import BaseURINode
from allmydata import download
from allmydata.util import bencode
@ -17,42 +17,42 @@ from allmydata.util import bencode
# each time the vdrive changes, update the local drive to match, and
# vice versa.
def to_node(spec):
# TODO
pass
def to_spec(node):
# TODO
pass
# from the itertools 'recipes' page
from itertools import izip, tee
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
try:
b.next()
except StopIteration:
pass
return izip(a, b)
class SubTreeNode:
implements(INode, IDirectoryNode)
def __init__(self, tree):
self.enclosing_tree = tree
# subdirectory_node_children maps child name to another SubTreeNode
# instance. This is only for internal directory nodes. All other
# nodes are listed in child_specifications instead.
self.subdirectory_node_children = {}
# child_specifications maps child name to a specification tuple which
# describes how to obtain the actual child. For example, if "foo.jpg"
# in this node represents a CHK-encoded FILE with a uri of "fooURI",
# then self.child_specifications["foo.jpg"] = ("CHKFILE","fooURI")
self.child_specifications = {}
self.children = {}
# # subdirectory_node_children maps child name to another SubTreeNode
# # instance. This is only for internal directory nodes. All other
# # nodes are listed in child_specifications instead.
# self.subdirectory_node_children = {}
# # child_specifications maps child name to a specification tuple which
# # describes how to obtain the actual child. For example, if "foo.jpg"
# # in this node represents a CHK-encoded FILE with a uri of "fooURI",
# # then self.child_specifications["foo.jpg"] = ("CHKFILE","fooURI")
# self.child_specifications = {}
def is_directory(self):
return True
def list(self):
return sorted(self.subdirectory_node_children.keys() +
self.child_specifications.keys())
return sorted(self.children.keys())
def get(self, childname):
if childname in self.subdirectory_node_children:
return self.subdirectory_node_children[childname]
elif childname in self.child_specifications:
return to_node(self.child_specifications[childname])
if childname in self.children:
return self.children[childname]
else:
raise NoSuchChildError("no child named '%s'" % (childname,))
@ -61,28 +61,24 @@ class SubTreeNode:
def delete(self, childname):
assert self.enclosing_tree.is_mutable()
if childname in self.subdirectory_node_children:
del self.subdirectory_node_children[childname]
elif childname in self.child_specifications:
del self.child_specifications[childname]
if childname in self.children:
del self.children[childname]
else:
raise NoSuchChildError("no child named '%s'" % (childname,))
def add_subdir(self, childname):
assert childname not in self.subdirectory_node_children
assert childname not in self.child_specifications
assert childname not in self.children
newnode = SubTreeNode(self.enclosing_tree)
self.subdirectory_node_children[childname] = newnode
self.children[childname] = newnode
return newnode
def add(self, childname, node):
assert childname not in self.subdirectory_node_children
assert childname not in self.child_specifications
spec = to_spec(node)
self.child_specifications[childname] = spec
assert childname not in self.children
assert INode(node)
self.children[childname] = node
return self
def serialize_to_sexprs(self):
def serialize_node(self):
# note: this is a one-pass recursive serialization that will result
# in the whole file table being held in memory. This is only
# appropriate for directories with fewer than, say, 10k nodes. If we
@ -90,31 +86,26 @@ class SubTreeNode:
# generator instead, and write the serialized data directly to a
# tempfile.
#
# ["DIRECTORY", name1, child1, name2, child2..]
# [name1, child1, name2, child2..]
#
# child1 is either a list for subdirs, or a string for non-subdirs
data = ["DIRECTORY"]
for name in sorted(self.node_children.keys()):
data = []
for name in sorted(self.children.keys()):
data.append(name)
data.append(self.node_children[name].serialize())
for name in sorted(self.child_specifications.keys()):
data.append(name)
data.append(self.child_specifications[name].serialize())
data.append(self.children[name].serialize_node())
return data
def populate_from_sexprs(self, data):
assert data[0] == "DIRECTORY"
assert len(data) % 2 == 1
for i in range(1, len(data), 2):
name = data[i]
child_data = data[i+1]
assert isinstance(child_data, (list, tuple))
child_type = child_data[0]
if child_type == "DIRECTORY":
def populate_node(self, data, node_maker):
assert len(data) % 2 == 0
for (name, child_data) in pairwise(data):
if isinstance(child_data, (list, tuple)):
child = SubTreeNode(self.enclosing_tree)
child.populate_from_sexprs(child_data)
self.node_children[name] = child
child.populate_node(child_data)
else:
self.child_specifications[name] = child_data
assert isinstance(child_data, str)
child = node_maker(child_data)
self.children[name] = child
@ -139,22 +130,22 @@ class _DirectorySubTree(object):
self.root = SubTreeNode(self)
self.mutable = True # sure, why not
def populate_from_specification(self, spec, parent_is_mutable, downloader):
return self.populate_from_node(to_node(spec),
parent_is_mutable, downloader)
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
# self.populate_from_node must be defined by the subclass (CHK or
# SSK), since it controls how the spec is interpreted. It will
# probably use the contents of the node to figure out what to
# download from the mesh, then pass this downloaded serialized data
# to populate_from_data()
raise NotImplementedError
def populate_from_data(self, data):
self.root = SubTreeNode()
self.root.populate_from_sexprs(bencode.bdecode(data))
def populate_from_data(self, data, node_maker):
self.root = SubTreeNode(self)
self.root.populate_node(bencode.bdecode(data), node_maker)
return self
def serialize(self):
"""Return a series of nested lists which describe my structure
in a form that can be bencoded."""
return self.root.serialize_to_sexprs()
def serialize_to_file(self, f):
f.write(bencode.bencode(self.serialize()))
def serialize_subtree_to_file(self, f):
sexprs = self.root.serialize_node()
bencode.bwrite(sexprs, f)
def is_mutable(self):
return self.mutable
@ -167,25 +158,35 @@ class _DirectorySubTree(object):
node = self.root
while remaining_path:
name = remaining_path[0]
if name in node.node_children:
node = node.node_children[name]
assert isinstance(node, SubTreeNode)
try:
childnode = node.get(name)
except NoSuchChildError:
# The node *would* be in this subtree if it existed, but it
# doesn't. Leave found_path and remaining_path alone, and
# node points at the last parent node that was on the path.
break
if IDirectoryNode.providedBy(childnode):
# recurse
node = childnode
found_path.append(name)
remaining_path.pop(0)
continue
if name in node.child_specifications:
else:
# the path takes us out of this subtree and into another
next_subtree_spec = node.child_specifications[name]
node = to_node(next_subtree_spec)
node = childnode # next subtree node
found_path.append(name)
remaining_path.pop(0)
break
# The node *would* be in this subtree if it existed, but it
# doesn't. Leave found_path and remaining_path alone, and node
# points at the last parent node that was on the path.
break
return (found_path, node, remaining_path)
class CHKDirectorySubTreeNode(BaseURINode):
implements(ICHKDirectoryNode)
prefix = "CHKDirectory"
def get_uri(self):
return self.uri
class CHKDirectorySubTree(_DirectorySubTree):
# maybe mutable, maybe not
@ -195,11 +196,11 @@ class CHKDirectorySubTree(_DirectorySubTree):
def set_uri(self, uri):
self.old_uri = uri
def populate_from_node(self, node, parent_is_mutable, downloader):
node = ICHKDirectoryNode(node)
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
assert ICHKDirectoryNode(node)
self.mutable = parent_is_mutable
d = downloader.download(node.get_uri(), download.Data())
d.addCallback(self.populate_from_data)
d.addCallback(self.populate_from_data, node_maker)
return d
def update(self, prepath, work_queue):
@ -219,6 +220,27 @@ class CHKDirectorySubTree(_DirectorySubTree):
# this needs investigation.
return boxname
class SSKDirectorySubTreeNode(object):
implements(INode, ISSKDirectoryNode)
prefix = "SSKDirectory"
def is_directory(self):
return False
def serialize_node(self):
data = (self.read_cap, self.write_cap)
return "%s:%s" % (self.prefix, bencode.bencode(data))
def populate_node(self, data, node_maker):
assert data.startswith(self.prefix + ":")
capdata = data[len(self.prefix)+1:]
self.read_cap, self.write_cap = bencode.bdecode(capdata)
def get_read_capability(self):
return self.read_cap
def get_write_capability(self):
return self.write_cap
class SSKDirectorySubTree(_DirectorySubTree):
def new(self):
@ -229,13 +251,13 @@ class SSKDirectorySubTree(_DirectorySubTree):
def mutation_affects_parent(self):
return False
def populate_from_node(self, node, parent_is_mutable, downloader):
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
node = ISSKDirectoryNode(node)
self.read_capability = node.get_read_capability()
self.write_capability = node.get_write_capability()
self.mutable = bool(self.write_capability)
d = downloader.download_ssk(self.read_capability, download.Data())
d.addCallback(self.populate_from_data)
d.addCallback(self.populate_from_data, node_maker)
return d
def set_version(self, version):

View File

@ -1,28 +1,32 @@
from zope.interface import implements
from allmydata.filetree.interfaces import INode, IFileNode
from allmydata.filetree.basenode import BaseURINode
from allmydata.util import bencode
class CHKFileNode(BaseURINode):
implements(IFileNode)
prefix = "CHKFile"
class CHKFile(object):
implements(INode, IFileNode)
def __init__(self, uri):
self.uri = uri
def get_uri(self):
return self.uri
class MutableSSKFile(object):
class SSKFileNode(object):
implements(INode, IFileNode)
def __init__(self, read_cap, write_cap):
self.read_cap = read_cap
self.write_cap = write_cap
prefix = "SSKFile"
def is_directory(self):
return False
def serialize_node(self):
data = (self.read_cap, self.write_cap)
return "%s:%s" % (self.prefix, bencode.bencode(data))
def populate_node(self, data, node_maker):
assert data.startswith(self.prefix + ":")
capdata = data[len(self.prefix)+1:]
self.read_cap, self.write_cap = bencode.bdecode(capdata)
def get_read_capability(self):
return self.read_cap
def get_write_capability(self):
return self.write_cap
class ImmutableSSKFile(object):
implements(INode, IFileNode)
def __init__(self, read_cap):
self.read_cap = read_cap
def get_read_capability(self):
return self.read_cap

View File

@ -6,12 +6,27 @@ class INode(Interface):
other I*Node interfaces also implement this one."""
def is_directory():
"""Return True if this node is an internal directory node."""
def serialize_node():
"""Return a data structure which contains enough information to build
this node again in the future (by calling vdrive.make_node(). For
IDirectoryNodes, this will be a list. For all other nodes this will
be a string."""
def populate_node(data, node_maker):
"""vdrive.make_node() will first use the prefix inside 'data' to
decide what kind of Node to create. It will then call this function
to populate the new Node from the data returned by serialize_node."""
class IFileNode(Interface):
"""This is a file which can be retrieved."""
# TODO: not sure which of these to provide.. should URIs contain "CHK" or
# "SSK" in them? Or should that be a detail of IDownloader?
def get_uri():
"""Return the URI of the target file. This URI can be passed
to an IDownloader to retrieve the data."""
def download(downloader, target):
"""Download the file to the given target (using the provided
downloader). Return a deferred that fires (with 'target') when the
download is complete."""
class IDirectoryNode(Interface):
"""This is a directory which can be listed."""
@ -50,28 +65,30 @@ class ISubTree(Interface):
a DirectoryNode, or it might be a FileNode.
"""
def populate_from_specification(spec, parent_is_mutable, downloader):
"""Given a specification tuple, arrange to populate this subtree by
pulling data from some source (possibly the mesh, or the queen, or an
HTTP server, or the local filesystem). Return a Deferred that will
fire (with self) when this subtree is ready for use (specifically
when it is ready for get() and add() calls).
"""
def populate_from_node(node, parent_is_mutable, node_maker, downloader):
"""Subtrees are created by opener.open() being called with an INode
which describes both the kind of subtree to be created and a way to
obtain its contents. open() uses the node to create a new instance of
the appropriate subtree type, then calls this populate_from_node()
method.
def populate_from_node(node, parent_is_mutable, downloader):
"""Like populate_from_specification."""
Each subtree's populate_from_node() method is expected to use the
downloader to obtain a file with the subtree's serialized contents
(probably by pulling data from some source, like the mesh, the queen,
an HTTP server, or somewhere on the local filesystem), then
unserialize them and populate the subtree's state.
Return a Deferred that will fire (with self) when this subtree is
ready for use (specifically when it is ready for get() and add()
calls).
"""
def populate_from_data(data):
"""Used internally by populate_from_specification. This is called
with a sequence of bytes that describes the contents of the subtree,
"""Used internally by populate_from_node. This is called with a
sequence of bytes that describes the contents of the subtree,
probably a bencoded tuple or s-expression. Returns self.
"""
def unserialize(serialized_data):
"""Populate all nodes from serialized_data, previously created by
calling my serialize() method. 'serialized_data' is a series of
nested lists (s-expressions), probably recorded in bencoded form."""
def is_mutable():
"""This returns True if we have the ability to modify this subtree.
If this returns True, this reference may be adapted to
@ -109,6 +126,11 @@ class ISubTree(Interface):
"""
def serialize_subtree_to_file(f):
"""Create a string which describes my structure and write it to the
given filehandle (using only .write()). This string should be
suitable for uploading to the mesh or storing in a local file."""
def update(prepath, workqueue):
"""Perform and schedule whatever work is necessary to record this
subtree to persistent storage and update the parent at 'prepath'
@ -121,10 +143,6 @@ class ISubTree(Interface):
some subtree is updated which does not require notifying the parent.
"""
def serialize():
"""Return a series of nested lists which describe my structure
in a form that can be bencoded."""
#class IMutableSubTree(Interface):
# def mutation_affects_parent():
@ -171,6 +189,16 @@ class IOpener(Interface):
class IVirtualDrive(Interface):
def __init__(workqueue, downloader, root_node):
pass
# internal methods
def make_node(serialized):
"""Given a string produced by original_node.serialize_node(), produce
an equivalent node.
"""
# commands to manipulate files
def list(path):
@ -220,9 +248,13 @@ class IVirtualDrive(Interface):
# TODO
class ICHKDirectoryNode(Interface):
pass
def get_uri():
pass
class ISSKDirectoryNode(Interface):
pass
def get_read_capability():
pass
def get_write_capability():
pass

View File

@ -1,27 +1,37 @@
from zope.interface import implements
from allmydata.filetree import opener
from allmydata.filetree.interfaces import (IVirtualDrive, ISubTree, IFileNode,
IDirectoryNode, NoSuchDirectoryError,
NoSuchChildError, PathAlreadyExistsError,
PathDoesNotExistError,
)
from allmydata.filetree import opener, directory, redirect
from allmydata.filetree.interfaces import (
IVirtualDrive, INode, ISubTree, IFileNode, IDirectoryNode,
NoSuchDirectoryError, NoSuchChildError, PathAlreadyExistsError,
PathDoesNotExistError,
)
from allmydata.upload import IUploadable
all_node_types = [
directory.CHKDirectorySubTreeNode,
directory.SSKDirectorySubTreeNode,
redirect.LocalFileRedirectionNode,
redirect.QueenRedirectionNode,
redirect.HTTPRedirectionNode,
redirect.QueenOrLocalFileRedirectionNode,
]
class VirtualDrive(object):
implements(IVirtualDrive)
def __init__(self, workqueue, downloader, root_specification):
def __init__(self, workqueue, downloader, root_node):
assert INode(root_node)
self.workqueue = workqueue
workqueue.set_vdrive(self)
# TODO: queen?
self.opener = opener.Opener(self.queen, downloader)
self.root_specification = root_specification
self.root_node = root_node
# these methods are used to walk through our subtrees
def _get_root(self):
return self.opener.open(self.root_specification, False)
return self.opener.open(self.root_node, False)
def _get_node(self, path):
d = self._get_closest_node(path)
@ -114,6 +124,25 @@ class VirtualDrive(object):
d.addCallback(_got_closest)
return d
# these are called when loading and creating nodes
def make_node(self, serialized):
# this turns a string into an INode, which contains information about
# the file or directory (like a URI), but does not contain the actual
# contents. An IOpener can be used later to retrieve the contents
# (which means downloading the file if this is an IFileNode, or
# perhaps creating a new subtree from the contents)
# maybe include parent_is_mutable?
assert isinstance(serialized, str)
colon = serialized.index(":")
prefix = serialized[:colon]
for node_class in all_node_types:
if prefix == node_class.prefix:
node = node_class()
node.populate_node(serialized, self.make_node)
return node
raise RuntimeError("unable to handle subtree type '%s'" % prefix)
# these are called by the workqueue
def add(self, path, new_node):