]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/filetree/interfaces.py
a40712b3b386f83b2f9f01927d586db6727343d8
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / filetree / interfaces.py
1
2 from zope.interface import Interface
3
4 class INode(Interface):
5     """This is some sort of retrievable node. All objects which implement
6     other I*Node interfaces also implement this one."""
7
8     # the INode-implementing class must have an attribute named .prefix which
9     # contains a string.
10
11     def serialize_node():
12         """Return a data structure which contains enough information to build
13         this node again in the future (by calling
14         INodeMaker.make_node_from_serialized(). For IDirectoryNodes, this
15         will be a list. For all other nodes this will be a string of the form
16         'prefix:body', where 'prefix' must be the same as the class attribute
17         .prefix ."""
18     def populate_node(body, node_maker):
19         """INodeMaker.make_node_from_serialized() will first use the prefix
20         from the .prefix attribute to decide what kind of Node to create.
21         They will then call this populate_node() method with the body to
22         populate the new Node. 'node_maker' provides INodeMaker, which
23         provides that same make_node_from_serialized function to create any
24         internal child nodes that might be necessary."""
25
26     def is_leaf_subtree():
27         """Return True if this node does not refer to a traversable
28         subtree. When searching for the node that describes a path, the
29         search will stop at the first leaf node found. IFileNodes should
30         return True here.
31         """
32 # TODO: there is a slightly confusing mixture of IDirectoryNodes and all
33 # other kinds of nodes. It is convenient to mix them because that way list()
34 # can point at nodes of all sorts, but IDirectoryNodes are very different
35 # than the rest (because they to not represent distinct subtrees). There
36 # might be a better way to factor this.
37
38 # TODO: 'node' is a problematic term here. It refers to nodes in the graph of
39 # connected subtrees. It also refers to nodes in the graph of directories and
40 # links within a single subtree. And the interface named INode is
41 # unfortunately a homonym with "inode", the data structure we previously used
42 # to represent information about an uploaded file which was too large to keep
43 # locally (the list of blockids), which meant the inode itself was uploaded.
44 # We no longer use inodes, but using a word that sounds just like it may
45 # cause confusion.
46
47 class IFileNode(Interface):
48     """This is a file which can be retrieved."""
49     # TODO: not sure which of these to provide.. should URIs contain "CHK" or
50     # "SSK" in them? Or should that be a detail of IDownloader?
51     def get_uri():
52         """Return the URI of the target file. This URI can be passed
53         to an IDownloader to retrieve the data."""
54     def download(downloader, target):
55         """Download the file to the given target (using the provided
56         downloader). Return a deferred that fires (with 'target') when the
57         download is complete."""
58
59 class IDirectoryNode(Interface):
60     """This is a directory which can be listed."""
61     # these calls do not modify the subtree
62     def list():
63         """Return a dictionary mapping each childname to a node. These nodes
64         implement various I*Node interfaces depending upon what they can do."""
65     def get(childname):
66         """Return a child node. Raises NoSuchChildError if there is no
67         child of that name."""
68     def get_subtree():
69         """Return the ISubTree which contains this node."""
70
71     # the following calls modify the subtree. After calling them, you must
72     # tell the enclosing subtree to serialize and upload itself. They can
73     # only be called if this directory node is associated with a mutable
74     # subtree.
75     def delete(childname):
76         """Delete any child referenced by this name."""
77     def add_subdir(childname):
78         """Create a new directory node, and return it."""
79     def add(childname, node):
80         """Add a new node to this path. Returns self."""
81
82 class ISubTree(Interface):
83     """A subtree is a collection of Nodes: files, directories, other trees.
84
85     A subtree represents a set of connected directories and files that all
86     share the same access control: any given person can read or write
87     anything in this tree as a group, and it is not possible to give access
88     to some pieces of this tree and not to others. Read-only access to
89     individual files can be granted independently, of course, but through an
90     unnamed URI, not as a subdirectory.
91
92     Each internal directory is represented by a separate Node. This might be
93     a DirectoryNode, or it might be a FileNode.
94     """
95
96     # All ISubTree-providing instances must have a class-level attribute
97     # named .node_class which references the matching INode-providing class.
98     # This is used by the ISubTreeMaker to turn nodes into subtrees.
99
100     def populate_from_node(node, parent_is_mutable, node_maker, downloader):
101         """Subtrees are created by ISubTreeMaker.open() being called with an
102         INode which describes both the kind of subtree to be created and a
103         way to obtain its contents. open() uses the node to create a new
104         instance of the appropriate subtree type, then calls this
105         populate_from_node() method.
106
107         Each subtree's populate_from_node() method is expected to use the
108         downloader to obtain a file with the subtree's serialized contents
109         (probably by pulling data from some source, like the mesh, the vdrive 
110         server, an HTTP server, or somewhere on the local filesystem), then
111         unserialize them and populate the subtree's state.
112
113         Return a Deferred that will fire (with self) when this subtree is
114         ready for use (specifically when it is ready for get() and add()
115         calls).
116         """
117
118     def is_mutable():
119         """This returns True if we have the ability to modify this subtree.
120         If this returns True, this reference may be adapted to
121         IMutableSubTree to actually exercise these mutation rights.
122         """
123
124     def mutation_modifies_parent():
125         """This returns True if any modification to this subtree will result
126         in it getting a new identity, and thus requiring its parent be
127         notified. This is True for CHKDirectorySubTree, but False for
128         SSKDirectorySubTree and all redirections.
129         """
130
131     def get_node_for_path(path):
132         """Ask this subtree to follow the path through its internal nodes.
133
134         Returns a tuple of (found_path, node, remaining_path). This method
135         operations synchronously, and does not return a Deferred.
136
137         (found_path=path, found_node, [])
138         If the path terminates within this subtree, found_path=path and
139         remaining_path=[], and the node will be an internal IDirectoryNode.
140
141         (found_path, last_node, remaining_path)
142         If the path does not terminate within this subtree but neither does
143         it exit this subtree, the last internal IDirectoryNode that *was* on
144         the path will be returned in 'node'. The path components that led to
145         this node will be in found_path, and the remaining components will be
146         in remaining_path. If you want to create the target node, loop over
147         remaining_path as follows::
148
149          while remaining_path:
150            node = node.add_subdir(remaining_path.pop(0))
151
152         (found_path, exit_node, remaining_path)
153         If the path leaves this subtree, 'node' will be a different kind of
154         INode (probably one that points at a child directory of some sort),
155         found_path will be the components that led to this point, and
156         remaining_path will be the remaining components. If you still wish to
157         locate the target, use 'node' to open a new subtree, then provide
158         'remaining_path' to the new subtree's get_node_for_path() method.
159
160         """
161
162     def put_node_at_path(path, node):
163         """Add the given node to this subtree, at 'path'.
164
165         This may create internal directory subnodes as necessary. This must
166         run synchronously, and returns None.
167         """
168
169     def delete_node_at_path(path):
170         """Delete the node at the the given path.
171
172         This must run synchronously, and returns None.
173         """
174
175     def serialize_subtree_to_file(f):
176         """Create a string which describes my structure and write it to the
177         given filehandle (using only .write()). This string should be
178         suitable for uploading to the mesh or storing in a local file."""
179
180     def update_now(uploader):
181         """Perform whatever work is necessary to record this subtree to
182         persistent storage.
183
184         This returns an INode, or a Deferred that fires (with an INode) when
185         the subtree has been persisted.
186
187         For directory subtrees, this will cause the subtree to serialize
188         itself to a file, then upload this file to the mesh, then create an
189         INode-providing instance which describes where the file wound up. For
190         redirections, this will cause the subtree to modify the redirection's
191         persistent storage, then return the (unmodified) INode that describes
192         the redirection.
193
194         This form does not use the workqueue. If the node is shut down before
195         the Deferred fires, a redirection or SSK subtree might be left in its
196         previous state, or it might have been updated.
197         """
198
199     def update(workqueue):
200         """Perform and schedule whatever work is necessary to record this
201         subtree to persistent storage.
202
203         Returns a boxname or None, synchronously. This function does not
204         return a Deferred.
205
206         If the parent subtree needs to be modified with the new identity of
207         this subtree (i.e. for CHKDirectorySubTree instances), this will
208         return a boxname in which the serialized INode will be placed once
209         the added workqueue steps have completed. The caller should add
210         'addpath' steps to the workqueue using this boxname (which will
211         eventually cause recursion on other subtrees, until some subtree is
212         updated which does not require notifying the parent). update() will
213         add steps to delete the box at the end of the workqueue.
214
215         If the parent subtree does not need to be modified (i.e. for
216         SSKDirectorySubTree instances, or redirections), this will return
217         None.
218
219         This is like update_now(), but uses the workqueue to insure
220         consistency in the face of node shutdowns. Once our intentions have
221         been recorded in the workqueue, if the node is shut down before the
222         upload steps have completed, the update will eventually complete the
223         next time the node is started.
224         """
225
226     def create_node_now():
227         # TODO: this is no longer just for testing.. vdrive.addpath needs it
228         """FOR TESTING ONLY. Immediately create and return an INode which
229         describes the current state of this subtree. This does not perform
230         any upload or persistence work, and thus depends upon any internal
231         state having been previously set correctly. In general this will
232         return the correct value for subtrees which have just been created
233         (and not yet mutated). It will also return the correct value for
234         subtrees which do not change their identity when they are mutated
235         (SSKDirectorySubTrees and redirections).
236         """
237
238 class INodeMaker(Interface):
239     def make_node_from_serialized(serialized):
240         """Turn a string into an INode, which contains information about the
241         file or directory (like a URI), but does not contain the actual
242         contents. An ISubTreeMaker can be used later to retrieve the contents
243         (which means downloading the file if this is an IFileNode, or perhaps
244         creating a new subtree from the contents)."""
245
246 class ISubTreeMaker(Interface):
247     def make_subtree_from_node(node, parent_is_mutable):
248         """Turn an INode into an ISubTree.
249
250         I accept an INode-providing specification of a subtree, and return a
251         Deferred that fires with an ISubTree-providing instance. I will
252         perform network IO and download the serialized data that the INode
253         references, if necessary, or ask the vdrive server (or other provider) 
254         for a pointer, or read it from local disk.
255         """
256
257
258 class IVirtualDrive(Interface):
259
260     def __init__(workqueue, downloader, root_node):
261         pass
262
263     # commands to manipulate files
264
265     def list(path):
266         """List the contents of the directory at the given path.
267
268         'path' is a list of strings (empty to refer to the root directory)
269         and must refer to a DIRECTORY node. This method returns a Deferred
270         that fires with a dictionary that maps strings to filetypes. The
271         strings are useful as path name components. The filetypes are
272         Interfaces: either IDirectoryNode if path+[childname] can be used in
273         a 'list' method, or IFileNode if path+[childname] can be used in a
274         'download' method.
275
276         The Deferred will errback (with NoSuchDirectoryError) if the path
277         does not point to an actual directory.
278         """
279
280     def download(path, target):
281         """Download the file at the given path to 'target'.
282
283         'path' must refer to a FILE. 'target' must implement IDownloadTarget.
284         This returns a Deferred that fires (with 'target') when the download
285         is complete.
286         """
287
288     def upload_data(path, data):
289         """Upload a string to the given path. The path must not already exist.
290
291         path[:-1] must refer to a writable DIRECTORY node.
292
293         This uses the workqueue, and returns None.
294         """
295
296     def upload(path, filename):
297         """Upload a file from disk to the given path.
298
299         This uses the workqueue, and returns None.
300         """
301
302     def delete(path):
303         """Delete the file or directory at the given path.
304
305         Returns a Deferred that fires (with self) when the delete is
306         complete.
307         """
308
309     def add_node(path, node):
310         """Add a node to the given path. Use the workqueue.
311         """
312
313     # commands to manipulate subtrees
314
315     # ... detach subtree, merge subtree, etc
316
317
318 # TODO
319
320 class ICHKDirectoryNode(Interface):
321     def get_uri():
322         pass
323 class ISSKDirectoryNode(Interface):
324     def get_read_capability():
325         pass
326     def get_write_capability():
327         pass
328
329
330
331 class NoSuchChildError(Exception):
332     pass
333 class NoSuchDirectoryError(Exception):
334     pass
335 class PathAlreadyExistsError(Exception):
336     pass
337 class PathDoesNotExistError(Exception):
338     pass