mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-19 21:17:54 +00:00
more filetree hacking, still too early to test
This commit is contained in:
parent
4a0f8bc110
commit
e843abe542
@ -2,15 +2,17 @@
|
||||
from zope.interface import implements
|
||||
from allmydata.filetree.interfaces import INode
|
||||
|
||||
class BaseURINode(object):
|
||||
class BaseDataNode(object):
|
||||
implements(INode)
|
||||
prefix = None # must be set by subclass
|
||||
|
||||
def is_directory(self):
|
||||
return False
|
||||
def get_base_data(self):
|
||||
raise NotImplementedError # must be provided by subclass
|
||||
def set_base_data(self, data):
|
||||
raise NotImplementedError # must be provided by subclass
|
||||
def serialize_node(self):
|
||||
return "%s:%s" % (self.prefix, self.uri)
|
||||
return "%s:%s" % (self.prefix, self.get_base_data())
|
||||
def populate_node(self, data, node_maker):
|
||||
assert data.startswith(self.prefix + ":")
|
||||
self.uri = data[len(self.prefix)+1:]
|
||||
self.set_base_data(data[len(self.prefix)+1:])
|
||||
|
||||
|
@ -5,7 +5,7 @@ from allmydata.filetree.interfaces import (
|
||||
ICHKDirectoryNode, ISSKDirectoryNode,
|
||||
NoSuchChildError,
|
||||
)
|
||||
from allmydata.filetree.basenode import BaseURINode
|
||||
from allmydata.filetree.basenode import BaseDataNode
|
||||
from allmydata import download
|
||||
from allmydata.util import bencode
|
||||
|
||||
@ -44,9 +44,6 @@ class SubTreeNode:
|
||||
# # then self.child_specifications["foo.jpg"] = ("CHKFILE","fooURI")
|
||||
# self.child_specifications = {}
|
||||
|
||||
def is_directory(self):
|
||||
return True
|
||||
|
||||
def list(self):
|
||||
return sorted(self.children.keys())
|
||||
|
||||
@ -179,10 +176,15 @@ class _DirectorySubTree(object):
|
||||
break
|
||||
return (found_path, node, remaining_path)
|
||||
|
||||
class CHKDirectorySubTreeNode(BaseURINode):
|
||||
class CHKDirectorySubTreeNode(BaseDataNode):
|
||||
implements(ICHKDirectoryNode)
|
||||
prefix = "CHKDirectory"
|
||||
|
||||
def get_base_data(self):
|
||||
return self.uri
|
||||
def set_base_data(self, data):
|
||||
self.uri = data
|
||||
|
||||
def get_uri(self):
|
||||
return self.uri
|
||||
|
||||
@ -190,9 +192,6 @@ class CHKDirectorySubTreeNode(BaseURINode):
|
||||
class CHKDirectorySubTree(_DirectorySubTree):
|
||||
# maybe mutable, maybe not
|
||||
|
||||
def mutation_affects_parent(self):
|
||||
return True
|
||||
|
||||
def set_uri(self, uri):
|
||||
self.old_uri = uri
|
||||
|
||||
@ -209,6 +208,7 @@ class CHKDirectorySubTree(_DirectorySubTree):
|
||||
self.serialize_to_file(f)
|
||||
f.close()
|
||||
boxname = work_queue.create_boxname()
|
||||
# mutation affects our parent
|
||||
work_queue.add_upload_chk(filename, boxname)
|
||||
work_queue.add_delete_tempfile(filename)
|
||||
work_queue.add_retain_uri_from_box(boxname)
|
||||
@ -225,8 +225,6 @@ 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))
|
||||
@ -248,9 +246,6 @@ class SSKDirectorySubTree(_DirectorySubTree):
|
||||
self.version = 0
|
||||
# TODO: populate
|
||||
|
||||
def mutation_affects_parent(self):
|
||||
return False
|
||||
|
||||
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
|
||||
node = ISSKDirectoryNode(node)
|
||||
self.read_capability = node.get_read_capability()
|
||||
@ -268,6 +263,7 @@ class SSKDirectorySubTree(_DirectorySubTree):
|
||||
f, filename = work_queue.create_tempfile(".sskdir")
|
||||
self.serialize_to_file(f)
|
||||
f.close()
|
||||
# mutation does not affect our parent
|
||||
work_queue.add_upload_ssk(filename, self.write_capability,
|
||||
self.version)
|
||||
self.version = self.version + 1
|
||||
|
@ -1,13 +1,18 @@
|
||||
|
||||
from zope.interface import implements
|
||||
from allmydata.filetree.interfaces import INode, IFileNode
|
||||
from allmydata.filetree.basenode import BaseURINode
|
||||
from allmydata.filetree.basenode import BaseDataNode
|
||||
from allmydata.util import bencode
|
||||
|
||||
class CHKFileNode(BaseURINode):
|
||||
class CHKFileNode(BaseDataNode):
|
||||
implements(IFileNode)
|
||||
prefix = "CHKFile"
|
||||
|
||||
def get_base_data(self):
|
||||
return self.uri
|
||||
def set_base_data(self, data):
|
||||
self.uri = data
|
||||
|
||||
def get_uri(self):
|
||||
return self.uri
|
||||
|
||||
@ -15,8 +20,6 @@ class SSKFileNode(object):
|
||||
implements(INode, IFileNode)
|
||||
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))
|
||||
|
@ -4,17 +4,16 @@ from zope.interface import Interface
|
||||
class INode(Interface):
|
||||
"""This is some sort of retrievable node. All objects which implement
|
||||
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."""
|
||||
this node again in the future (by calling
|
||||
vdrive.make_node_from_serialized(). 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."""
|
||||
"""vdrive.make_node_from_serialized() 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."""
|
||||
@ -83,12 +82,6 @@ class ISubTree(Interface):
|
||||
calls).
|
||||
"""
|
||||
|
||||
def populate_from_data(data):
|
||||
"""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 is_mutable():
|
||||
"""This returns True if we have the ability to modify this subtree.
|
||||
If this returns True, this reference may be adapted to
|
||||
@ -194,7 +187,7 @@ class IVirtualDrive(Interface):
|
||||
|
||||
# internal methods
|
||||
|
||||
def make_node(serialized):
|
||||
def make_node_from_serialized(serialized):
|
||||
"""Given a string produced by original_node.serialize_node(), produce
|
||||
an equivalent node.
|
||||
"""
|
||||
|
@ -1,98 +1,183 @@
|
||||
|
||||
from cStringIO import StringIO
|
||||
from zope.interface import implements
|
||||
from twisted.internet import defer
|
||||
|
||||
from allmydata.filetree.interfaces import ISubTree
|
||||
from allmydata.filetree.basenode import BaseDataNode
|
||||
from allmydata.util import bencode
|
||||
|
||||
class LocalFileRedirection(object):
|
||||
class LocalFileRedirectionNode(BaseDataNode):
|
||||
prefix = "LocalFileRedirection"
|
||||
|
||||
def new(self, handle):
|
||||
self.handle = handle
|
||||
|
||||
def get_base_data(self):
|
||||
return self.handle
|
||||
def set_base_data(self, data):
|
||||
self.handle = data
|
||||
|
||||
class _BaseRedirection(object):
|
||||
implements(ISubTree)
|
||||
|
||||
def new(self, child_node):
|
||||
self.child_node = child_node
|
||||
|
||||
def get_node_for_path(self, path):
|
||||
return ([], self.child_node, path)
|
||||
|
||||
def serialize_subtree_to_file(self, f):
|
||||
return self.child_node.serialize_node()
|
||||
|
||||
def _populate_from_data(self, data, node_maker):
|
||||
self.child_node = node_maker(data)
|
||||
return self
|
||||
|
||||
class LocalFileRedirection(_BaseRedirection):
|
||||
stype = "LocalFileRedirection"
|
||||
|
||||
def populate_from_specification(self, spec, parent_is_mutable, downloader):
|
||||
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
|
||||
# return a Deferred that fires (with self) when this node is ready
|
||||
# for use
|
||||
|
||||
(stype, filename) = spec
|
||||
assert stype == self.stype
|
||||
#filename = spec.get_filename()
|
||||
# there is a local file which contains a bencoded serialized
|
||||
# subtree specification.
|
||||
assert isinstance(node, LocalFileRedirectionNode)
|
||||
self.filename = node.handle
|
||||
# there is a local file which contains a bencoded serialized subtree
|
||||
# specification.
|
||||
|
||||
# TODO: will this enable outsiders to cause us to read from
|
||||
# arbitrary files? Think about this.
|
||||
f = open(filename, "rb")
|
||||
# TODO: will this enable outsiders to cause us to read from arbitrary
|
||||
# files? Think about this. It is probably a good idea to restrict the
|
||||
# filename to be a single component, and to always put them in a
|
||||
# well-known directory that contains nothing else, and maybe make
|
||||
# them unguessable.
|
||||
f = open(self.filename, "rb")
|
||||
data = f.read()
|
||||
f.close()
|
||||
# note: we don't cache the contents of the file. TODO: consider
|
||||
# doing this based upon mtime. It is important that we be able to
|
||||
# notice if the file has been changed.
|
||||
return defer.succeed(self._populate_from_data(data, node_maker))
|
||||
|
||||
return self.populate_from_data(data)
|
||||
def is_mutable(self):
|
||||
return True
|
||||
|
||||
def populate_from_data(self, data):
|
||||
# data is a subtree specification for our one child
|
||||
self.child_spec = bencode.bdecode(data)
|
||||
return self
|
||||
def update(self, prepath, workqueue):
|
||||
f = open(self.filename, "wb")
|
||||
self.serialize_subtree_to_file(f)
|
||||
f.close()
|
||||
|
||||
class QueenRedirection(object):
|
||||
stype = "QueenRedirection"
|
||||
|
||||
def populate_from_specification(self, spec, parent_is_mutable, downloader):
|
||||
# this specifies a handle for which the Queen maintains a
|
||||
# serialized subtree specification.
|
||||
(stype, handle) = spec
|
||||
class QueenRedirectionNode(LocalFileRedirectionNode):
|
||||
prefix = "QueenRedirection"
|
||||
|
||||
class QueenRedirection(_BaseRedirection):
|
||||
style = "QueenRedirection"
|
||||
|
||||
def new(self, handle):
|
||||
self.handle = handle
|
||||
|
||||
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
|
||||
# this specifies a handle for which the Queen maintains a serialized
|
||||
# subtree specification.
|
||||
assert isinstance(node, QueenRedirectionNode)
|
||||
self.handle = node.handle
|
||||
|
||||
# TODO: queen?
|
||||
d = self._queen.callRemote("lookup_handle", handle)
|
||||
d.addCallback(self.populate_from_data)
|
||||
d = self._queen.callRemote("lookup_handle", self.handle)
|
||||
d.addCallback(self._populate_from_data, node_maker)
|
||||
return d
|
||||
|
||||
def populate_from_data(self, data):
|
||||
self.child_spec = bencode.bdecode(data)
|
||||
return self
|
||||
def is_mutable(self):
|
||||
return True # TODO: maybe, maybe not
|
||||
|
||||
class QueenOrLocalFileRedirection(object):
|
||||
def update(self, prepath, workqueue):
|
||||
f = StringIO()
|
||||
self.serialize_subtree_to_file(f)
|
||||
d = self._queen.callRemote("set_handle", self.handle, f.getvalue())
|
||||
return d
|
||||
|
||||
class QueenOrLocalFileRedirectionNode(LocalFileRedirectionNode):
|
||||
prefix = "QueenOrLocalFileRedirection"
|
||||
|
||||
class QueenOrLocalFileRedirection(_BaseRedirection):
|
||||
stype = "QueenOrLocalFileRedirection"
|
||||
|
||||
def populate_from_specification(self, spec, parent_is_mutable, downloader):
|
||||
def new(self, handle, child_node):
|
||||
self.handle = handle
|
||||
self.version = 0
|
||||
self.child_node = child_node
|
||||
# TODO
|
||||
|
||||
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
|
||||
# there is a local file which contains a bencoded serialized
|
||||
# subtree specification. The queen also has a copy. Whomever has
|
||||
# the higher version number wins.
|
||||
(stype, filename, handle) = spec
|
||||
assert isinstance(node, QueenOrLocalFileRedirectionNode)
|
||||
self.filename = self.handle = node.handle
|
||||
|
||||
f = open(filename, "rb")
|
||||
f = open(self.filename, "rb")
|
||||
#local_version, local_data = bencode.bdecode(f.read())
|
||||
local_version_and_data = f.read()
|
||||
f.close()
|
||||
|
||||
# TODO: queen?
|
||||
# TODO: pubsub so we can cache the queen's results
|
||||
d = self._queen.callRemote("lookup_handle", handle)
|
||||
d.addCallback(self._choose_winner, local_version_and_data)
|
||||
d = self._queen.callRemote("lookup_handle", self.handle)
|
||||
d.addCallback(self._choose_winner, local_version_and_data, node_maker)
|
||||
return d
|
||||
|
||||
def _choose_winner(self, queen_version_and_data, local_version_and_data):
|
||||
def _choose_winner(self, queen_version_and_data, local_version_and_data, node_maker):
|
||||
queen_version, queen_data = bencode.bdecode(queen_version_and_data)
|
||||
local_version, local_data = bencode.bdecode(local_version_and_data)
|
||||
if queen_version > local_version:
|
||||
data = queen_data
|
||||
self.version = queen_version
|
||||
else:
|
||||
data = local_data
|
||||
return self.populate_from_data(data)
|
||||
|
||||
def populate_from_data(self, data):
|
||||
self.version = local_version
|
||||
# NOTE: two layers of bencoding here, TODO
|
||||
self.child_spec = bencode.bdecode(data)
|
||||
return self
|
||||
return self._populate_from_data(data, node_maker)
|
||||
|
||||
class HTTPRedirection(object):
|
||||
stype = "HTTPRedirection"
|
||||
def is_mutable(self):
|
||||
return True
|
||||
|
||||
def populate_from_specification(self, spec, parent_is_mutable, downloader):
|
||||
# this specifies a URL at which there is a bencoded serialized
|
||||
# subtree specification.
|
||||
(stype, url) = spec
|
||||
from twisted.web import client
|
||||
d = client.getPage(url)
|
||||
d.addCallback(self.populate_from_data)
|
||||
def update(self, prepath, workqueue):
|
||||
self.version += 1
|
||||
f = StringIO()
|
||||
self.serialize_subtree_to_file(f)
|
||||
version_and_data = bencode.bencode((self.version, f.getvalue()))
|
||||
f = open(self.filename, "wb")
|
||||
f.write(version_and_data)
|
||||
f.close()
|
||||
d = self._queen.callRemote("set_handle", self.handle, version_and_data)
|
||||
return d
|
||||
|
||||
def populate_from_data(self, data):
|
||||
self.child_spec = bencode.bdecode(data)
|
||||
return self
|
||||
class HTTPRedirectionNode(BaseDataNode):
|
||||
prefix = "HTTPRedirection"
|
||||
|
||||
def new(self, url):
|
||||
self.url = url
|
||||
|
||||
def get_base_data(self):
|
||||
return self.url
|
||||
def set_base_data(self, data):
|
||||
self.url = data
|
||||
|
||||
class HTTPRedirection(_BaseRedirection):
|
||||
stype = "HTTPRedirection"
|
||||
|
||||
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
|
||||
# this specifies a URL at which there is a bencoded serialized
|
||||
# subtree specification.
|
||||
assert isinstance(node, HTTPRedirectionNode)
|
||||
from twisted.web import client
|
||||
d = client.getPage(node.url)
|
||||
d.addCallback(self._populate_from_data, node_maker)
|
||||
return d
|
||||
|
||||
def is_mutable(self):
|
||||
return False
|
||||
|
||||
def update(self, prepath, workqueue):
|
||||
raise RuntimeError("HTTPRedirection is not mutable")
|
||||
|
@ -8,6 +8,9 @@ from allmydata.filetree.interfaces import (
|
||||
)
|
||||
from allmydata.upload import IUploadable
|
||||
|
||||
# this list is used by VirtualDrive.make_node_from_serialized() to convert
|
||||
# node specification strings (found inside the serialized form of subtrees)
|
||||
# into Nodes (which live in the in-RAM form of subtrees).
|
||||
all_node_types = [
|
||||
directory.CHKDirectorySubTreeNode,
|
||||
directory.SSKDirectorySubTreeNode,
|
||||
@ -57,7 +60,7 @@ class VirtualDrive(object):
|
||||
|
||||
def _get_closest_node_2(self, res, parent_is_mutable):
|
||||
(found_path, node, remaining_path) = res
|
||||
if node.is_directory():
|
||||
if IDirectoryNode.providedBy(node):
|
||||
# traversal done
|
||||
return (node, remaining_path)
|
||||
# otherwise, we must open and recurse into a new subtree
|
||||
@ -125,7 +128,7 @@ class VirtualDrive(object):
|
||||
return d
|
||||
|
||||
# these are called when loading and creating nodes
|
||||
def make_node(self, serialized):
|
||||
def make_node_from_serialized(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
|
||||
@ -139,7 +142,7 @@ class VirtualDrive(object):
|
||||
for node_class in all_node_types:
|
||||
if prefix == node_class.prefix:
|
||||
node = node_class()
|
||||
node.populate_node(serialized, self.make_node)
|
||||
node.populate_node(serialized, self.make_node_from_serialized)
|
||||
return node
|
||||
raise RuntimeError("unable to handle subtree type '%s'" % prefix)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user