The Tahoe-LAFS storage server can be configured to store its shares on a
cloud storage service, rather than on the local filesystem.
+All cloud storage services store the data in a particular container (also
+called a "bucket" in some storage services). You can create this container
+using the "tahoe admin create-container" command, once you have a correctly
+configured Tahoe-LAFS node. That is, configure the node with the container
+name you decided to use (e.g. "tahoedata"), then run the command.
+
Amazon Simple Storage Service (S3)
==================================
is most often used by developers who have just modified the code and want to
start using their changes.
+Some less frequently used administration commands, for key generation/derivation
+and for creating a cloud backend container, are grouped as subcommands of
+"``tahoe admin``". For a list of these use "``tahoe admin --help``", or for
+more detailed help on a particular command, use "``tahoe admin COMMAND --help``".
+
Filesystem Manipulation
=======================
permutation-seed.
"""
+ def create_container():
+ """
+ Create a container for the configured backend, if necessary. Return a
+ Deferred that fires with False if no container is needed for this backend
+ type, or something other than False if a container has been successfully
+ created. It is an error to attempt to create a container that already exists.
+ """
+
class IShareSet(Interface):
def get_storage_index():
+import os
+
from twisted.python import usage
-from allmydata.scripts.common import BaseOptions
+from allmydata.scripts.common import BaseOptions, BasedirOptions
class GenerateKeypairOptions(BaseOptions):
print >>out, "public:", pubkey_vs
return 0
+
+class CreateContainerOptions(BasedirOptions):
+ def getSynopsis(self):
+ return "Usage: tahoe [global-options] admin create-container [NODEDIR]"
+
+ def getUsage(self, width=None):
+ t = BasedirOptions.getUsage(self, width)
+ t += """
+Create a storage container, using the name and credentials configured in
+tahoe.cfg. This is needed only for the cloud backend, and only if the
+container has not already been created. See <docs/backends/cloud.rst>
+for more details.
+"""
+ return t
+
+def create_container(options):
+ from twisted.internet import reactor, defer
+
+ err = options.stderr
+
+ d = defer.maybeDeferred(do_create_container, options)
+ def _failed(f):
+ print >>err, "Container creation failed."
+ print >>err, "%s: %s" % (f.value.__class__.__name__, f.value)
+ print >>err
+ return f
+ d.addErrback(_failed)
+ d.addCallbacks(lambda ign: os._exit(0), lambda ign: os._exit(1))
+ reactor.run()
+
+def do_create_container(options):
+ from allmydata.node import ConfigOnly
+ from allmydata.client import Client
+
+ out = options.stderr
+ config = ConfigOnly(options['basedir'])
+ (backend, _) = Client.configure_backend(config)
+
+ d = backend.create_container()
+ def _done(res):
+ if res is False:
+ print >>out, ("It is not necessary to create a container for this backend type (%s)."
+ % (backend.__class__.__name__,))
+ else:
+ print >>out, "The container was successfully created."
+ print >>out
+ d.addCallback(_done)
+ return d
+
+
class AdminCommand(BaseOptions):
subCommands = [
("generate-keypair", None, GenerateKeypairOptions,
"Generate a public/private keypair, write to stdout."),
("derive-pubkey", None, DerivePubkeyOptions,
"Derive a public key from a private key."),
+ ("create-container", None, CreateContainerOptions,
+ "Create a container for the configured cloud backend."),
]
def postOptions(self):
if not hasattr(self, 'subOptions'):
subDispatch = {
"generate-keypair": print_keypair,
"derive-pubkey": derive_pubkey,
+ "create-container": create_container,
}
def do_admin(options):
]
def parseArgs(self, basedir=None):
- if self.parent['node-directory'] and self['basedir']:
+ # This finds the node-directory option correctly even if we are in a subcommand.
+ root = self.parent
+ while root.parent is not None:
+ root = root.parent
+
+ if root['node-directory'] and self['basedir']:
raise usage.UsageError("The --node-directory (or -d) and --basedir (or -C) options cannot both be used.")
- if self.parent['node-directory'] and basedir:
+ if root['node-directory'] and basedir:
raise usage.UsageError("The --node-directory (or -d) option and a basedir argument cannot both be used.")
if self['basedir'] and basedir:
raise usage.UsageError("The --basedir (or -C) option and a basedir argument cannot both be used.")
b = argv_to_abspath(basedir)
elif self['basedir']:
b = argv_to_abspath(self['basedir'])
- elif self.parent['node-directory']:
- b = argv_to_abspath(self.parent['node-directory'])
+ elif root['node-directory']:
+ b = argv_to_abspath(root['node-directory'])
elif self.default_nodedir:
b = self.default_nodedir
else:
# compatibility requirements for permutation seeds. The disk backend overrides this.
return False
+ def create_container(self):
+ # Backends for which it is necessary to create a container, should override this
+ # and return a Deferred that fires with something other than False when the
+ # container has been created.
+ return defer.succeed(False)
+
class ShareSet(object):
"""
# TODO: query space usage of container if supported.
return 2**64
+ def create_container(self):
+ return self._container.create()
+
class CloudShareSet(ShareSet):
implements(IShareSet)
def test_create_admin(self):
help = str(admin.AdminCommand())
- self.failUnlessIn(" [global-opts] admin SUBCOMMAND", help)
+ self.failUnlessIn(" [global-options] admin SUBCOMMAND", help)
def test_create_admin_generate_keypair(self):
help = str(admin.GenerateKeypairOptions())
- self.failUnlessIn(" [global-opts] admin generate-keypair", help)
+ self.failUnlessIn(" [global-options] admin generate-keypair", help)
def test_create_admin_derive_pubkey(self):
help = str(admin.DerivePubkeyOptions())
- self.failUnlessIn(" [global-opts] admin derive-pubkey", help)
+ self.failUnlessIn(" [global-options] admin derive-pubkey", help)
+
+ def test_create_admin_create_container(self):
+ help = str(admin.CreateContainerOptions())
+ self.failUnlessIn(" [global-options] admin create-container [NODEDIR]", help)
class Ln(GridTestMixin, CLITestMixin, unittest.TestCase):