mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-23 14:52:26 +00:00
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:
parent
1d86cd161b
commit
b06c74c2a1
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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."""
|
||||
|
@ -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
91
src/allmydata/vdrive.py
Normal 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)
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user