node.py: the base Node, which handles connection establishment and
application startup
- client.py, introducer_and_vdrive.py: two specialized subclasses of Node, for users
- and the central introducer/vdrive handler, respectively
+ client.py, introducer_and_vdrive.py:
+ these are two specialized subclasses of Node, for users and the central
+ introducer/vdrive handler, respectively. Each works by assembling a
+ collection of services underneath a top-level Node instance.
introducer.py: node introduction handlers, client is used by client.py,
server is used by introducer_and_vdrive.py
download.py: download-side peer selection, share retrieval, decoding
- filetable.py, vdrive.py: implements the current one-global-vdrive layer,
- part runs on client nodes, part runs on the
- central vdrive handler
+ dirnode.py: implements the directory nodes. One part runs on the
+ global vdrive server, the other runs inside a client
+ (starting with vdrive.py)
+
+ vdrive.py: provides a client-side service that accesses the global
+ shared virtual drive and the per-user private drive.
webish.py, web/*.xhtml: provides the web frontend, using a Nevow server
- workqueue.py, filetree/*.py: building blocks for the future filetree work
+ uri.py: URI packing/parsing routines
hashtree.py: Merkle hash tree classes
debugshell.py, manhole.py: SSH-connected python shell, for debug purposes
- uri.py: URI packing/parsing routines
-
util/*.py: misc utility classes
test/*.py: unit tests
Both the client and the central introducer-and-vdrive node runs as a tree of
(twisted.application.service) Service instances. The Foolscap "Tub" is one of
-these. Client nodes have an Uploader service and a Downloader service that turn
-data into URIs and back again. They also have a VDrive service which provides
-access to the single global shared filesystem.
+these. Client nodes have an Uploader service and a Downloader service that
+turn data into URIs and back again. They also have a VirtualDrive service
+which provides access to the single global shared filesystem.
The Uploader is given an "upload source" (which could be an open filehandle,
a filename on local disk, or even a string), and returns a Deferred that
import os, sha, stat, time
from foolscap import Referenceable, SturdyRef
from zope.interface import implements
-from allmydata.interfaces import RIClient, IDirectoryNode
-from allmydata import node, uri
+from allmydata.interfaces import RIClient
+from allmydata import node
from twisted.internet import defer, reactor
from twisted.application.internet import TimerService
from allmydata.webish import WebishServer
from allmydata.control import ControlServer
from allmydata.introducer import IntroducerClient
-from allmydata.dirnode import create_directory_node, create_directory
+from allmydata.vdrive import VirtualDrive
class Client(node.Node, Referenceable):
implements(RIClient)
NODETYPE = "client"
WEBPORTFILE = "webport"
INTRODUCER_FURL_FILE = "introducer.furl"
- GLOBAL_VDRIVE_FURL_FILE = "vdrive.furl"
MY_FURL_FILE = "myself.furl"
SUICIDE_PREVENTION_HOTLINE_FILE = "suicide_prevention_hotline"
- MY_VDRIVE_URI_FILE = "my_vdrive.uri"
# we're pretty narrow-minded right now
OLDEST_SUPPORTED_VERSION = allmydata.__version__
node.Node.__init__(self, basedir)
self.my_furl = None
self.introducer_client = None
- self._connected_to_vdrive = False
self.add_service(StorageServer(os.path.join(basedir, self.STOREDIR)))
self.add_service(Uploader())
self.add_service(Downloader())
+ self.add_service(VirtualDrive())
WEBPORTFILE = os.path.join(self.basedir, self.WEBPORTFILE)
if os.path.exists(WEBPORTFILE):
f = open(WEBPORTFILE, "r")
self.introducer_furl = f.read().strip()
f.close()
- self.global_vdrive_furl = None
- GLOBAL_VDRIVE_FURL_FILE = os.path.join(self.basedir,
- self.GLOBAL_VDRIVE_FURL_FILE)
- if os.path.exists(GLOBAL_VDRIVE_FURL_FILE):
- f = open(GLOBAL_VDRIVE_FURL_FILE, "r")
- self.global_vdrive_furl = f.read().strip()
- f.close()
- #self.add_service(VDrive())
- self._my_vdrive = None
-
hotline_file = os.path.join(self.basedir,
self.SUICIDE_PREVENTION_HOTLINE_FILE)
if os.path.exists(hotline_file):
self.register_control()
- if self.global_vdrive_furl:
- self.vdrive_connector = self.tub.connectTo(self.global_vdrive_furl,
- self._got_vdrive)
-
def register_control(self):
c = ControlServer()
c.setServiceParent(self)
f.close()
os.chmod("control.furl", 0600)
- def _got_vdrive(self, vdrive_server):
- # vdrive_server implements RIVirtualDriveServer
- self.log("connected to vdrive server")
- d = vdrive_server.callRemote("get_public_root_uri")
- d.addCallback(self._got_vdrive_uri)
- d.addCallback(self._got_vdrive_rootnode)
- d.addCallback(self._create_my_vdrive, vdrive_server)
- d.addCallback(self._got_my_vdrive)
-
- def _got_vdrive_uri(self, root_uri):
- furl, wk = uri.unpack_dirnode_uri(root_uri)
- self._vdrive_furl = furl
- return create_directory_node(self, root_uri)
-
- def _got_vdrive_rootnode(self, rootnode):
- self.log("got vdrive root")
- self._vdrive_root = rootnode
- self._connected_to_vdrive = True
-
- #vdrive = self.getServiceNamed("vdrive")
- #vdrive.set_server(vdrive_server)
- #vdrive.set_root(vdrive_root)
-
- if "webish" in self.namedServices:
- webish = self.getServiceNamed("webish")
- webish.set_vdrive_rootnode(rootnode)
-
- def _create_my_vdrive(self, ignored, vdrive_server):
- MY_VDRIVE_URI_FILE = os.path.join(self.basedir,
- self.MY_VDRIVE_URI_FILE)
- try:
- f = open(MY_VDRIVE_URI_FILE, "r")
- my_vdrive_uri = f.read().strip()
- f.close()
- return create_directory_node(self, my_vdrive_uri)
- except EnvironmentError:
- assert self._vdrive_furl
- d = create_directory(self, self._vdrive_furl)
- def _got_directory(dirnode):
- f = open(MY_VDRIVE_URI_FILE, "w")
- f.write(dirnode.get_uri() + "\n")
- f.close()
- return dirnode
- d.addCallback(_got_directory)
- return d
-
- def _got_my_vdrive(self, my_vdrive):
- IDirectoryNode(my_vdrive)
- self._my_vdrive = my_vdrive
-
- if "webish" in self.namedServices:
- webish = self.getServiceNamed("webish")
- webish.set_my_vdrive_rootnode(my_vdrive)
-
def remote_get_versions(self):
return str(allmydata.__version__), str(self.OLDEST_SUPPORTED_VERSION)
def upload_filehandle(filehane):
"""Like upload(), but accepts an open filehandle."""
+class IVirtualDrive(Interface):
+ """I am a service that may be available to a client.
+
+ Within any client program, this service can be retrieved by using
+ client.getService('vdrive').
+ """
+
+ def have_public_root():
+ """Return a Boolean, True if get_public_root() will work."""
+ def get_public_root():
+ """Get the public read-write directory root.
+
+ This returns a Deferred that fires with an IDirectoryNode instance
+ corresponding to the global shared root directory."""
+
+
+ def have_private_root():
+ """Return a Boolean, True if get_public_root() will work."""
+ def get_private_root():
+ """Get the private directory root.
+
+ This returns a Deferred that fires with an IDirectoryNode instance
+ corresponding to this client's private root directory."""
+
+ def get_node(self, uri):
+ """Transform a URI into an IDirectoryNode or IFileNode.
+
+ This returns a Deferred that will fire with an instance that provides
+ either IDirectoryNode or IFileNode, as appropriate."""
class NotCapableError(Exception):
"""You have tried to write to a read-only node."""
log.msg("PUBLISHING")
ut = upload.Data(DATA)
c0 = self.clients[0]
- d1 = c0._vdrive_root.create_empty_directory("subdir1")
+ d1 = c0.getServiceNamed("vdrive").get_public_root()
+ d1.addCallback(lambda root: root.create_empty_directory("subdir1"))
d1.addCallback(lambda subdir1_node:
subdir1_node.add_file("mydata567", ut))
def _stash_uri(filenode):
log.msg("publish finished")
c1 = self.clients[1]
- d1 = c1._vdrive_root.get("subdir1")
+ d1 = c1.getServiceNamed("vdrive").get_public_root()
+ d1.addCallback(lambda root: root.get("subdir1"))
d1.addCallback(lambda subdir1: subdir1.get("mydata567"))
d1.addCallback(lambda filenode: filenode.download_to_data())
return d1
--- /dev/null
+
+import os
+from twisted.application import service
+from zope.interface import implements
+from allmydata.interfaces import IVirtualDrive
+from allmydata import dirnode, uri
+from twisted.internet import defer
+
+class NoGlobalVirtualDriveError(Exception):
+ pass
+class NoPrivateVirtualDriveError(Exception):
+ pass
+
+class VirtualDrive(service.MultiService):
+ implements(IVirtualDrive)
+ name = "vdrive"
+
+ GLOBAL_VDRIVE_FURL_FILE = "vdrive.furl"
+
+ GLOBAL_VDRIVE_URI_FILE = "global_root.uri"
+ MY_VDRIVE_URI_FILE = "my_vdrive.uri"
+
+ def __init__(self):
+ service.MultiService.__init__(self)
+ self._global_uri = None
+ self._private_uri = None
+
+ def startService(self):
+ service.MultiService.startService(self)
+ basedir = self.parent.basedir
+ client = self.parent
+ tub = self.parent.tub
+
+ global_vdrive_furl = None
+ furl_file = os.path.join(basedir, self.GLOBAL_VDRIVE_FURL_FILE)
+ if os.path.exists(furl_file):
+ f = open(furl_file, "r")
+ global_vdrive_furl = f.read().strip()
+ f.close()
+
+ global_uri_file = os.path.join(basedir,
+ self.GLOBAL_VDRIVE_URI_FILE)
+ if os.path.exists(global_uri_file):
+ f = open(global_uri_file)
+ self._global_uri = f.read().strip()
+ f.close()
+ elif global_vdrive_furl:
+ d = tub.getReference(global_vdrive_furl)
+ d.addCallback(lambda vdrive_server:
+ vdrive_server.callRemote("get_public_root_uri"))
+ def _got_global_uri(global_uri):
+ self._global_uri = global_uri
+ d.addCallback(_got_global_uri)
+
+ private_uri_file = os.path.join(basedir,
+ self.MY_VDRIVE_URI_FILE)
+ if os.path.exists(private_uri_file):
+ f = open(private_uri_file)
+ private_vdrive_uri = f.read().strip()
+ f.close()
+ elif global_vdrive_furl:
+ d = dirnode.create_directory(client, global_vdrive_furl)
+ def _got_directory(dirnode):
+ private_uri = dirnode.get_uri()
+ self._private_uri = private_uri
+ f = open(private_uri_file, "w")
+ f.write(private_uri + "\n")
+ f.close()
+ d.addCallback(_got_directory)
+
+
+ def get_node(self, node_uri):
+ if uri.is_dirnode_uri(node_uri):
+ return dirnode.create_directory_node(self.parent, node_uri)
+ else:
+ return defer.succeed(dirnode.FileNode(node_uri, self.parent))
+
+ def have_public_root(self):
+ return bool(self._global_uri)
+ def get_public_root(self):
+ if not self._global_uri:
+ return defer.fail(NoGlobalVirtualDriveError())
+ return self.get_node(self._global_uri)
+
+ def have_private_root(self):
+ return bool(self._private_uri)
+ def get_private_root(self):
+ if not self._private_uri:
+ return defer.fail(NoPrivateVirtualDriveError())
+ return self.get_node(self._private_uri)
+
return "yes"
return "no"
def data_connected_to_vdrive(self, ctx, data):
- if IClient(ctx).connected_to_vdrive():
+ if IClient(ctx).getServiceNamed("vdrive").have_public_root():
return "yes"
return "no"
def data_num_peers(self, ctx, data):
return ctx.tag
def render_global_vdrive(self, ctx, data):
- if self.has_global_vdrive:
+ if IClient(ctx).getServiceNamed("vdrive").have_public_root():
return T.p["To view the global shared filestore, ",
T.a(href="../global_vdrive")["Click Here!"],
]
"responding), no vdrive available."]
def render_my_vdrive(self, ctx, data):
- if self.has_my_vdrive:
+ if IClient(ctx).getServiceNamed("vdrive").have_private_root():
return T.p["To view your personal private non-shared filestore, ",
T.a(href="../my_vdrive")["Click Here!"],
]
child_welcome = Welcome()
+ def child_global_vdrive(self, ctx):
+ client = IClient(ctx)
+ vdrive = client.getServiceNamed("vdrive")
+ if vdrive.have_public_root():
+ d = vdrive.get_public_root()
+ d.addCallback(lambda dirnode: Directory(dirnode, "/"))
+ return d
+ else:
+ return static.Data("sorry, still initializing", "text/plain")
+
+ def child_private_vdrive(self, ctx):
+ client = IClient(ctx)
+ vdrive = client.getServiceNamed("vdrive")
+ if vdrive.have_private_root():
+ d = vdrive.get_private_root()
+ d.addCallback(lambda dirnode: Directory(dirnode, "~"))
+ return d
+ else:
+ return static.Data("sorry, still initializing", "text/plain")
+
class WebishServer(service.MultiService):
name = "webish"
def __init__(self, webport):
service.MultiService.__init__(self)
self.root = Root()
- self.root.child_welcome.has_global_vdrive = False
- self.root.child_welcome.has_my_vdrive = False
- placeholder = static.Data("sorry, still initializing", "text/plain")
- self.root.putChild("vdrive", placeholder)
self.root.putChild("", url.here.child("welcome"))#Welcome())
self.site = site = appserver.NevowSite(self.root)
# I thought you could do the same with an existing interface, but
# apparently 'ISite' does not exist
#self.site._client = self.parent
-
- def set_vdrive_rootnode(self, root):
- self.root.putChild("global_vdrive", Directory(root, "/"))
- self.root.child_welcome.has_global_vdrive = True
- # I tried doing it this way and for some reason it didn't seem to work
- #print "REMEMBERING", self.site, dl, IDownloader
- #self.site.remember(dl, IDownloader)
-
- def set_my_vdrive_rootnode(self, my_vdrive):
- self.root.putChild("my_vdrive", Directory(my_vdrive, "~"))
- self.root.child_welcome.has_my_vdrive = True
-