Add the 'vdrive' service, for clients to access the public/private root dirs.

These allow client-side code to conveniently retrieve the IDirectoryNode
instances for both the global shared public root directory, and the per-user
private root directory.
This commit is contained in:
Brian Warner 2007-06-27 17:11:06 -07:00
parent 1d86cd161b
commit b06c74c2a1
6 changed files with 165 additions and 106 deletions

View File

@ -23,8 +23,10 @@ Within src/allmydata/ :
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
@ -38,20 +40,21 @@ Within src/allmydata/ :
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
@ -59,9 +62,9 @@ Within src/allmydata/ :
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

View File

@ -2,8 +2,8 @@
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
@ -16,7 +16,7 @@ from allmydata.download import Downloader
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)
@ -25,10 +25,8 @@ class Client(node.Node, Referenceable):
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__
@ -37,10 +35,10 @@ class Client(node.Node, Referenceable):
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")
@ -54,16 +52,6 @@ class Client(node.Node, Referenceable):
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):
@ -99,10 +87,6 @@ class Client(node.Node, Referenceable):
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)
@ -112,60 +96,6 @@ class Client(node.Node, Referenceable):
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)

View File

@ -655,6 +655,35 @@ class IUploader(Interface):
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."""

View File

@ -238,7 +238,8 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase):
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):
@ -251,7 +252,8 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase):
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

91
src/allmydata/vdrive.py Normal file
View File

@ -0,0 +1,91 @@
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)

View File

@ -42,7 +42,7 @@ class Welcome(rend.Page):
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):
@ -64,7 +64,7 @@ class Welcome(rend.Page):
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!"],
]
@ -72,7 +72,7 @@ class Welcome(rend.Page):
"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!"],
]
@ -397,6 +397,26 @@ class Root(rend.Page):
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"
@ -404,10 +424,6 @@ class WebishServer(service.MultiService):
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)
@ -425,15 +441,3 @@ class WebishServer(service.MultiService):
# 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