]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
cli: improve formatting of all commands
authorBrian Warner <warner@lothar.com>
Tue, 26 May 2015 18:31:06 +0000 (11:31 -0700)
committerBrian Warner <warner@lothar.com>
Tue, 26 May 2015 18:31:06 +0000 (11:31 -0700)
Also:

* do some light refactoring of create-client/node
* make it clear that these commands' --basedir options do the same as
  the global --node-directory option
* use "global-options" instead of "global-opts"

src/allmydata/scripts/admin.py
src/allmydata/scripts/cli.py
src/allmydata/scripts/common.py
src/allmydata/scripts/create_node.py
src/allmydata/scripts/debug.py
src/allmydata/scripts/runner.py
src/allmydata/scripts/startstop_node.py
src/allmydata/test/test_cli.py

index 092d90a9c143c10c7dd03e971a477098deac3a1d..6dab86ab7b03f3255220a14073bc538dbe63159e 100644 (file)
@@ -3,8 +3,6 @@ from twisted.python import usage
 from allmydata.scripts.common import BaseOptions
 
 class GenerateKeypairOptions(BaseOptions):
 from allmydata.scripts.common import BaseOptions
 
 class GenerateKeypairOptions(BaseOptions):
-    def getSynopsis(self):
-        return "Usage: tahoe [global-opts] admin generate-keypair"
 
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
 
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
@@ -26,7 +24,7 @@ class DerivePubkeyOptions(BaseOptions):
         self.privkey = privkey
 
     def getSynopsis(self):
         self.privkey = privkey
 
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] admin derive-pubkey PRIVKEY"
+        return "Usage: tahoe [global-options] admin derive-pubkey PRIVKEY"
 
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
 
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
@@ -57,7 +55,7 @@ class AdminCommand(BaseOptions):
         if not hasattr(self, 'subOptions'):
             raise usage.UsageError("must specify a subcommand")
     def getSynopsis(self):
         if not hasattr(self, 'subOptions'):
             raise usage.UsageError("must specify a subcommand")
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] admin SUBCOMMAND"
+        return "Usage: tahoe [global-options] admin SUBCOMMAND"
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
         t += """
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
         t += """
index 18ecc6590c573913015b782921cc0faaa1ed88f9..ef4fc71fe9ca2f7713fea2e9afae96f38400473a 100644 (file)
@@ -57,10 +57,8 @@ class MakeDirectoryOptions(FilesystemOptions):
             if self['format'].upper() not in ("SDMF", "MDMF"):
                 raise usage.UsageError("%s is an invalid format" % self['format'])
 
             if self['format'].upper() not in ("SDMF", "MDMF"):
                 raise usage.UsageError("%s is an invalid format" % self['format'])
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] mkdir [options] [REMOTE_DIR]" % (self.command_name,)
-
-    longdesc = """Create a new directory, either unlinked or as a subdirectory."""
+    synopsis = "[options] [REMOTE_DIR]"
+    description = """Create a new directory, either unlinked or as a subdirectory."""
 
 class AddAliasOptions(FilesystemOptions):
     def parseArgs(self, alias, cap):
 
 class AddAliasOptions(FilesystemOptions):
     def parseArgs(self, alias, cap):
@@ -69,10 +67,8 @@ class AddAliasOptions(FilesystemOptions):
             self.alias = self.alias[:-1]
         self.cap = cap
 
             self.alias = self.alias[:-1]
         self.cap = cap
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] add-alias [options] ALIAS[:] DIRCAP" % (self.command_name,)
-
-    longdesc = """Add a new alias for an existing directory."""
+    synopsis = "[options] ALIAS[:] DIRCAP"
+    description = """Add a new alias for an existing directory."""
 
 class CreateAliasOptions(FilesystemOptions):
     def parseArgs(self, alias):
 
 class CreateAliasOptions(FilesystemOptions):
     def parseArgs(self, alias):
@@ -80,16 +76,12 @@ class CreateAliasOptions(FilesystemOptions):
         if self.alias.endswith(u':'):
             self.alias = self.alias[:-1]
 
         if self.alias.endswith(u':'):
             self.alias = self.alias[:-1]
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] create-alias [options] ALIAS[:]" % (self.command_name,)
-
-    longdesc = """Create a new directory and add an alias for it."""
+    synopsis = "[options] ALIAS[:]"
+    description = """Create a new directory and add an alias for it."""
 
 class ListAliasesOptions(FilesystemOptions):
 
 class ListAliasesOptions(FilesystemOptions):
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] list-aliases [options]" % (self.command_name,)
-
-    longdesc = """Display a table of all configured aliases."""
+    synopsis = "[options]"
+    description = """Display a table of all configured aliases."""
 
 class ListOptions(FilesystemOptions):
     optFlags = [
 
 class ListOptions(FilesystemOptions):
     optFlags = [
@@ -102,10 +94,9 @@ class ListOptions(FilesystemOptions):
     def parseArgs(self, where=""):
         self.where = argv_to_unicode(where)
 
     def parseArgs(self, where=""):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] ls [options] [PATH]" % (self.command_name,)
+    synopsis = "[options] [PATH]"
 
 
-    longdesc = """
+    description = """
     List the contents of some portion of the grid.
 
     If PATH is omitted, "tahoe:" is assumed.
     List the contents of some portion of the grid.
 
     If PATH is omitted, "tahoe:" is assumed.
@@ -113,7 +104,7 @@ class ListOptions(FilesystemOptions):
     When the -l or --long option is used, each line is shown in the
     following format:
 
     When the -l or --long option is used, each line is shown in the
     following format:
 
-    drwx <size> <date/time> <name in this directory>
+     drwx <size> <date/time> <name in this directory>
 
     where each of the letters on the left may be replaced by '-'.
     If 'd' is present, it indicates that the object is a directory.
 
     where each of the letters on the left may be replaced by '-'.
     If 'd' is present, it indicates that the object is a directory.
@@ -146,24 +137,20 @@ class GetOptions(FilesystemOptions):
         self.from_file = argv_to_unicode(arg1)
         self.to_file   = None if arg2 is None else argv_to_abspath(arg2)
 
         self.from_file = argv_to_unicode(arg1)
         self.to_file   = None if arg2 is None else argv_to_abspath(arg2)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] get [options] REMOTE_FILE LOCAL_FILE" % (self.command_name,)
+    synopsis = "[options] REMOTE_FILE LOCAL_FILE"
 
 
-    longdesc = """
+    description = """
     Retrieve a file from the grid and write it to the local filesystem. If
     LOCAL_FILE is omitted or '-', the contents of the file will be written to
     stdout."""
 
     Retrieve a file from the grid and write it to the local filesystem. If
     LOCAL_FILE is omitted or '-', the contents of the file will be written to
     stdout."""
 
-    def getUsage(self, width=None):
-        t = FilesystemOptions.getUsage(self, width)
-        t += """
-Examples:
- % tahoe get FOO |less            # write to stdout
- % tahoe get tahoe:FOO |less      # same
- % tahoe get FOO bar              # write to local file
- % tahoe get tahoe:FOO bar        # same
-"""
-        return t
+    description_unwrapped = """
+    Examples:
+     % tahoe get FOO |less            # write to stdout
+     % tahoe get tahoe:FOO |less      # same
+     % tahoe get FOO bar              # write to local file
+     % tahoe get tahoe:FOO bar        # same
+    """
 
 class PutOptions(FilesystemOptions):
     optFlags = [
 
 class PutOptions(FilesystemOptions):
     optFlags = [
@@ -186,33 +173,30 @@ class PutOptions(FilesystemOptions):
             if self['format'].upper() not in ("SDMF", "MDMF", "CHK"):
                 raise usage.UsageError("%s is an invalid format" % self['format'])
 
             if self['format'].upper() not in ("SDMF", "MDMF", "CHK"):
                 raise usage.UsageError("%s is an invalid format" % self['format'])
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] put [options] LOCAL_FILE REMOTE_FILE" % (self.command_name,)
+    synopsis = "[options] LOCAL_FILE REMOTE_FILE"
 
 
-    longdesc = """
+    description = """
     Put a file into the grid, copying its contents from the local filesystem.
     If REMOTE_FILE is missing, upload the file but do not link it into a
     directory; also print the new filecap to stdout. If LOCAL_FILE is missing
     or '-', data will be copied from stdin. REMOTE_FILE is assumed to start
     with tahoe: unless otherwise specified.
 
     Put a file into the grid, copying its contents from the local filesystem.
     If REMOTE_FILE is missing, upload the file but do not link it into a
     directory; also print the new filecap to stdout. If LOCAL_FILE is missing
     or '-', data will be copied from stdin. REMOTE_FILE is assumed to start
     with tahoe: unless otherwise specified.
 
-    If the destination file already exists and is mutable, it will be modified
-    in-place, whether or not --mutable is specified. (--mutable only affects
-    creation of new files.)"""
-
-    def getUsage(self, width=None):
-        t = FilesystemOptions.getUsage(self, width)
-        t += """
-Examples:
- % cat FILE | tahoe put                # create unlinked file from stdin
- % cat FILE | tahoe put -              # same
- % tahoe put bar                       # create unlinked file from local 'bar'
- % cat FILE | tahoe put - FOO          # create tahoe:FOO from stdin
- % tahoe put bar FOO                   # copy local 'bar' to tahoe:FOO
- % tahoe put bar tahoe:FOO             # same
- % tahoe put bar MUTABLE-FILE-WRITECAP # modify the mutable file in-place
-"""
-        return t
+    If the destination file already exists and is mutable, it will be
+    modified in-place, whether or not --mutable is specified. (--mutable only
+    affects creation of new files.)
+    """
+
+    description_unwrapped = """
+    Examples:
+     % cat FILE | tahoe put                # create unlinked file from stdin
+     % cat FILE | tahoe put -              # same
+     % tahoe put bar                       # create unlinked file from local 'bar'
+     % cat FILE | tahoe put - FOO          # create tahoe:FOO from stdin
+     % tahoe put bar FOO                   # copy local 'bar' to tahoe:FOO
+     % tahoe put bar tahoe:FOO             # same
+     % tahoe put bar MUTABLE-FILE-WRITECAP # modify the mutable file in-place
+    """
 
 class CpOptions(FilesystemOptions):
     optFlags = [
 
 class CpOptions(FilesystemOptions):
     optFlags = [
@@ -229,10 +213,9 @@ class CpOptions(FilesystemOptions):
         self.sources = map(argv_to_unicode, args[:-1])
         self.destination = argv_to_unicode(args[-1])
 
         self.sources = map(argv_to_unicode, args[:-1])
         self.destination = argv_to_unicode(args[-1])
 
-    def getSynopsis(self):
-        return "Usage: %s [global-opts] cp [options] FROM.. TO" % (self.command_name,)
+    synopsis = "[options] FROM.. TO"
 
 
-    longdesc = """
+    description = """
     Use 'tahoe cp' to copy files between a local filesystem and a Tahoe grid.
     Any FROM/TO arguments that begin with an alias indicate Tahoe-side
     files or non-file arguments. Directories will be copied recursively.
     Use 'tahoe cp' to copy files between a local filesystem and a Tahoe grid.
     Any FROM/TO arguments that begin with an alias indicate Tahoe-side
     files or non-file arguments. Directories will be copied recursively.
@@ -240,15 +223,15 @@ class CpOptions(FilesystemOptions):
     you have previously set up an alias 'home' with 'tahoe create-alias home',
     here are some examples:
 
     you have previously set up an alias 'home' with 'tahoe create-alias home',
     here are some examples:
 
-    tahoe cp ~/foo.txt home:  # creates tahoe-side home:foo.txt
+     tahoe cp ~/foo.txt home:  # creates tahoe-side home:foo.txt
 
 
-    tahoe cp ~/foo.txt /tmp/bar.txt home:  # copies two files to home:
+     tahoe cp ~/foo.txt /tmp/bar.txt home:  # copies two files to home:
 
 
-    tahoe cp ~/Pictures home:stuff/my-pictures  # copies directory recursively
+     tahoe cp ~/Pictures home:stuff/my-pictures  # copies directory recursively
 
     You can also use a dircap as either FROM or TO target:
 
 
     You can also use a dircap as either FROM or TO target:
 
-    tahoe cp URI:DIR2-RO:ixqhc4kdbjxc7o65xjnveoewym:5x6lwoxghrd5rxhwunzavft2qygfkt27oj3fbxlq4c6p45z5uneq/blog.html ./   # copy Zooko's wiki page to a local file
+     tahoe cp URI:DIR2-RO:ixqhc4kdbjxc7o65xjnveoewym:5x6lwoxghrd5rxhwunzavft2qygfkt27oj3fbxlq4c6p45z5uneq/blog.html ./   # copy Zooko's wiki page to a local file
 
     This command still has some limitations: symlinks and special files
     (device nodes, named pipes) are not handled very well. Arguments should
 
     This command still has some limitations: symlinks and special files
     (device nodes, named pipes) are not handled very well. Arguments should
@@ -266,22 +249,21 @@ class UnlinkOptions(FilesystemOptions):
     def parseArgs(self, where):
         self.where = argv_to_unicode(where)
 
     def parseArgs(self, where):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] unlink [options] REMOTE_FILE" % (self.command_name,)
+    synopsis = "[options] REMOTE_FILE"
+    description = "Remove a named file from its parent directory."
 
 class RmOptions(UnlinkOptions):
 
 class RmOptions(UnlinkOptions):
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] rm [options] REMOTE_FILE" % (self.command_name,)
+    synopsis = "[options] REMOTE_FILE"
+    description = "Remove a named file from its parent directory."
 
 class MvOptions(FilesystemOptions):
     def parseArgs(self, frompath, topath):
         self.from_file = argv_to_unicode(frompath)
         self.to_file = argv_to_unicode(topath)
 
 
 class MvOptions(FilesystemOptions):
     def parseArgs(self, frompath, topath):
         self.from_file = argv_to_unicode(frompath)
         self.to_file = argv_to_unicode(topath)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] mv [options] FROM TO" % (self.command_name,)
+    synopsis = "[options] FROM TO"
 
 
-    longdesc = """
+    description = """
     Use 'tahoe mv' to move files that are already on the grid elsewhere on
     the grid, e.g., 'tahoe mv alias:some_file alias:new_file'.
 
     Use 'tahoe mv' to move files that are already on the grid elsewhere on
     the grid, e.g., 'tahoe mv alias:some_file alias:new_file'.
 
@@ -298,10 +280,9 @@ class LnOptions(FilesystemOptions):
         self.from_file = argv_to_unicode(frompath)
         self.to_file = argv_to_unicode(topath)
 
         self.from_file = argv_to_unicode(frompath)
         self.to_file = argv_to_unicode(topath)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] ln [options] FROM_LINK TO_LINK" % (self.command_name,)
+    synopsis = "[options] FROM_LINK TO_LINK"
 
 
-    longdesc = """
+    description = """
     Use 'tahoe ln' to duplicate a link (directory entry) already on the grid
     to elsewhere on the grid. For example 'tahoe ln alias:some_file
     alias:new_file'. causes 'alias:new_file' to point to the same object that
     Use 'tahoe ln' to duplicate a link (directory entry) already on the grid
     to elsewhere on the grid. For example 'tahoe ln alias:some_file
     alias:new_file'. causes 'alias:new_file' to point to the same object that
@@ -345,8 +326,7 @@ class BackupOptions(FilesystemOptions):
         self.from_dir = argv_to_abspath(localdir)
         self.to_dir = argv_to_unicode(topath)
 
         self.from_dir = argv_to_abspath(localdir)
         self.to_dir = argv_to_unicode(topath)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] backup [options] FROM ALIAS:TO" % (self.command_name,)
+    synopsis = "[options] FROM ALIAS:TO"
 
     def opt_exclude(self, pattern):
         """Ignore files matching a glob pattern. You may give multiple
 
     def opt_exclude(self, pattern):
         """Ignore files matching a glob pattern. You may give multiple
@@ -388,7 +368,7 @@ class BackupOptions(FilesystemOptions):
             else:
                 yield filename
 
             else:
                 yield filename
 
-    longdesc = """
+    description = """
     Add a versioned backup of the local FROM directory to a timestamped
     subdirectory of the TO/Archives directory on the grid, sharing as many
     files and directories as possible with earlier backups. Create TO/Latest
     Add a versioned backup of the local FROM directory to a timestamped
     subdirectory of the TO/Archives directory on the grid, sharing as many
     files and directories as possible with earlier backups. Create TO/Latest
@@ -403,10 +383,10 @@ class WebopenOptions(FilesystemOptions):
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] webopen [options] [ALIAS:PATH]" % (self.command_name,)
+    synopsis = "[options] [ALIAS:PATH]"
 
 
-    longdesc = """Open a web browser to the contents of some file or
+    description = """
+    Open a web browser to the contents of some file or
     directory on the grid. When run without arguments, open the Welcome
     page."""
 
     directory on the grid. When run without arguments, open the Welcome
     page."""
 
@@ -420,11 +400,10 @@ class ManifestOptions(FilesystemOptions):
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] manifest [options] [ALIAS:PATH]" % (self.command_name,)
-
-    longdesc = """Print a list of all files and directories reachable from
-    the given starting point."""
+    synopsis = "[options] [ALIAS:PATH]"
+    description = """
+    Print a list of all files and directories reachable from the given
+    starting point."""
 
 class StatsOptions(FilesystemOptions):
     optFlags = [
 
 class StatsOptions(FilesystemOptions):
     optFlags = [
@@ -433,11 +412,10 @@ class StatsOptions(FilesystemOptions):
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] stats [options] [ALIAS:PATH]" % (self.command_name,)
-
-    longdesc = """Print statistics about of all files and directories
-    reachable from the given starting point."""
+    synopsis = "[options] [ALIAS:PATH]"
+    description = """
+    Print statistics about of all files and directories reachable from the
+    given starting point."""
 
 class CheckOptions(FilesystemOptions):
     optFlags = [
 
 class CheckOptions(FilesystemOptions):
     optFlags = [
@@ -449,10 +427,8 @@ class CheckOptions(FilesystemOptions):
     def parseArgs(self, *locations):
         self.locations = map(argv_to_unicode, locations)
 
     def parseArgs(self, *locations):
         self.locations = map(argv_to_unicode, locations)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] check [options] [ALIAS:PATH]" % (self.command_name,)
-
-    longdesc = """
+    synopsis = "[options] [ALIAS:PATH]"
+    description = """
     Check a single file or directory: count how many shares are available and
     verify their hashes. Optionally repair the file if any problems were
     found."""
     Check a single file or directory: count how many shares are available and
     verify their hashes. Optionally repair the file if any problems were
     found."""
@@ -468,10 +444,8 @@ class DeepCheckOptions(FilesystemOptions):
     def parseArgs(self, *locations):
         self.locations = map(argv_to_unicode, locations)
 
     def parseArgs(self, *locations):
         self.locations = map(argv_to_unicode, locations)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] deep-check [options] [ALIAS:PATH]" % (self.command_name,)
-
-    longdesc = """
+    synopsis = "[options] [ALIAS:PATH]"
+    description = """
     Check all files and directories reachable from the given starting point
     (which must be a directory), like 'tahoe check' but for multiple files.
     Optionally repair any problems found."""
     Check all files and directories reachable from the given starting point
     (which must be a directory), like 'tahoe check' but for multiple files.
     Optionally repair any problems found."""
index 3ebb3e8ca807650478ca56e2ad38bbba7a30c58a..d6246fc05f6198621080fd1d0e4389a03c26ff74 100644 (file)
@@ -93,7 +93,7 @@ class NoDefaultBasedirOptions(BasedirOptions):
         BasedirOptions.parseArgs(self, basedir)
 
     def getSynopsis(self):
         BasedirOptions.parseArgs(self, basedir)
 
     def getSynopsis(self):
-        return "Usage:  %s [global-opts] %s [options] NODEDIR" % (self.command_name, self.subcommand_name)
+        return "Usage:  %s [global-options] %s [options] NODEDIR" % (self.command_name, self.subcommand_name)
 
 
 DEFAULT_ALIAS = u"tahoe"
 
 
 DEFAULT_ALIAS = u"tahoe"
index 6663d125d2d5278dd6240c82c77d1a85b845f34e..65cbb63ae740d1e8630dffde99ee820e59a08914 100644 (file)
@@ -1,11 +1,12 @@
 
 import os, sys
 from allmydata.scripts.common import BasedirOptions, NoDefaultBasedirOptions
 
 import os, sys
 from allmydata.scripts.common import BasedirOptions, NoDefaultBasedirOptions
+from allmydata.scripts.default_nodedir import _default_nodedir
 from allmydata.util.assertutil import precondition
 from allmydata.util.assertutil import precondition
-from allmydata.util.encodingutil import listdir_unicode, argv_to_unicode, quote_output
+from allmydata.util.encodingutil import listdir_unicode, argv_to_unicode, quote_output, quote_local_unicode_path
 import allmydata
 
 import allmydata
 
-class CreateClientOptions(BasedirOptions):
+class _CreateBaseOptions(BasedirOptions):
     optParameters = [
         # we provide 'create-node'-time options for the most common
         # configuration knobs. The rest can be controlled by editing
     optParameters = [
         # we provide 'create-node'-time options for the most common
         # configuration knobs. The rest can be controlled by editing
@@ -14,28 +15,30 @@ class CreateClientOptions(BasedirOptions):
         ("introducer", "i", None, "Specify the introducer FURL to use."),
         ("webport", "p", "tcp:3456:interface=127.0.0.1",
          "Specify which TCP port to run the HTTP interface on. Use 'none' to disable."),
         ("introducer", "i", None, "Specify the introducer FURL to use."),
         ("webport", "p", "tcp:3456:interface=127.0.0.1",
          "Specify which TCP port to run the HTTP interface on. Use 'none' to disable."),
-        ]
+        ("basedir", "C", None, "Specify which Tahoe base directory should be used. This has the same effect as the global --node-directory option. [default: %s]"
+         % quote_local_unicode_path(_default_nodedir)),
 
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] create-client [options] [NODEDIR]" % (self.command_name,)
+        ]
 
 
-    # This is overridden in order to ensure we get a "Wrong number of arguments."
-    # error when more than one argument is given.
+    # This is overridden in order to ensure we get a "Wrong number of
+    # arguments." error when more than one argument is given.
     def parseArgs(self, basedir=None):
         BasedirOptions.parseArgs(self, basedir)
 
     def parseArgs(self, basedir=None):
         BasedirOptions.parseArgs(self, basedir)
 
+class CreateClientOptions(_CreateBaseOptions):
+    synopsis = "[options] [NODEDIR]"
+    description = "Create a client-only Tahoe-LAFS node (no storage server)."
 
 class CreateNodeOptions(CreateClientOptions):
     optFlags = [
         ("no-storage", None, "Do not offer storage service to other nodes."),
         ]
 
 class CreateNodeOptions(CreateClientOptions):
     optFlags = [
         ("no-storage", None, "Do not offer storage service to other nodes."),
         ]
-
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] create-node [options] [NODEDIR]" % (self.command_name,)
-
+    synopsis = "[options] [NODEDIR]"
+    description = "Create a full Tahoe-LAFS node (client+server)."
 
 class CreateIntroducerOptions(NoDefaultBasedirOptions):
     subcommand_name = "create-introducer"
 
 class CreateIntroducerOptions(NoDefaultBasedirOptions):
     subcommand_name = "create-introducer"
+    description = "Create a Tahoe-LAFS introducer."
 
 
 client_tac = """
 
 
 client_tac = """
index fedd69851e312c28ca6b871d74fce84a4a39c198..738f911e39307b60a35b6ae07b92bfe307de5c79 100644 (file)
@@ -11,25 +11,21 @@ from allmydata.scripts.common import BaseOptions
 
 class DumpOptions(BaseOptions):
     def getSynopsis(self):
 
 class DumpOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug dump-share SHARE_FILENAME"
+        return "Usage: tahoe [global-options] debug dump-share SHARE_FILENAME"
 
     optFlags = [
         ["offsets", None, "Display a table of section offsets."],
         ["leases-only", None, "Dump leases but not CHK contents."],
         ]
 
 
     optFlags = [
         ["offsets", None, "Display a table of section offsets."],
         ["leases-only", None, "Dump leases but not CHK contents."],
         ]
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Print lots of information about the given share, by parsing the share's
 contents. This includes share type, lease information, encoding parameters,
 hash-tree roots, public keys, and segment sizes. This command also emits a
 verify-cap for the file that uses the share.
 
  tahoe debug dump-share testgrid/node-3/storage/shares/4v/4vozh77tsrw7mdhnj7qvp5ky74/0
 Print lots of information about the given share, by parsing the share's
 contents. This includes share type, lease information, encoding parameters,
 hash-tree roots, public keys, and segment sizes. This command also emits a
 verify-cap for the file that uses the share.
 
  tahoe debug dump-share testgrid/node-3/storage/shares/4v/4vozh77tsrw7mdhnj7qvp5ky74/0
-
 """
 """
-        return t
 
     def parseArgs(self, filename):
         from allmydata.util.encodingutil import argv_to_abspath
 
     def parseArgs(self, filename):
         from allmydata.util.encodingutil import argv_to_abspath
@@ -408,7 +404,7 @@ def dump_MDMF_share(m, length, options):
 
 class DumpCapOptions(BaseOptions):
     def getSynopsis(self):
 
 class DumpCapOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug dump-cap [options] FILECAP"
+        return "Usage: tahoe [global-options] debug dump-cap [options] FILECAP"
     optParameters = [
         ["nodeid", "n",
          None, "Specify the storage server nodeid (ASCII), to construct WE and secrets."],
     optParameters = [
         ["nodeid", "n",
          None, "Specify the storage server nodeid (ASCII), to construct WE and secrets."],
@@ -420,9 +416,7 @@ class DumpCapOptions(BaseOptions):
     def parseArgs(self, cap):
         self.cap = cap
 
     def parseArgs(self, cap):
         self.cap = cap
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Print information about the given cap-string (aka: URI, file-cap, dir-cap,
 read-cap, write-cap). The URI string is parsed and unpacked. This prints the
 type of the cap, its storage index, and any derived keys.
 Print information about the given cap-string (aka: URI, file-cap, dir-cap,
 read-cap, write-cap). The URI string is parsed and unpacked. This prints the
 type of the cap, its storage index, and any derived keys.
@@ -437,7 +431,6 @@ If additional information is provided (storage server nodeid and/or client
 base secret), this command will compute the shared secrets used for the
 write-enabler and for lease-renewal.
 """
 base secret), this command will compute the shared secrets used for the
 write-enabler and for lease-renewal.
 """
-        return t
 
 
 def dump_cap(options):
 
 
 def dump_cap(options):
@@ -610,16 +603,14 @@ def dump_uri_instance(u, nodeid, secret, out, show_header=True):
 
 class FindSharesOptions(BaseOptions):
     def getSynopsis(self):
 
 class FindSharesOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug find-shares STORAGE_INDEX NODEDIRS.."
+        return "Usage: tahoe [global-options] debug find-shares STORAGE_INDEX NODEDIRS.."
 
     def parseArgs(self, storage_index_s, *nodedirs):
         from allmydata.util.encodingutil import argv_to_abspath
         self.si_s = storage_index_s
         self.nodedirs = map(argv_to_abspath, nodedirs)
 
 
     def parseArgs(self, storage_index_s, *nodedirs):
         from allmydata.util.encodingutil import argv_to_abspath
         self.si_s = storage_index_s
         self.nodedirs = map(argv_to_abspath, nodedirs)
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Locate all shares for the given storage index. This command looks through one
 or more node directories to find the shares. It returns a list of filenames,
 one per line, for each share file found.
 Locate all shares for the given storage index. This command looks through one
 or more node directories to find the shares. It returns a list of filenames,
 one per line, for each share file found.
@@ -630,7 +621,6 @@ It may be useful during testing, when running a test grid in which all the
 nodes are on a local disk. The share files thus located can be counted,
 examined (with dump-share), or corrupted/deleted to test checker/repairer.
 """
 nodes are on a local disk. The share files thus located can be counted,
 examined (with dump-share), or corrupted/deleted to test checker/repairer.
 """
-        return t
 
 def find_shares(options):
     """Given a storage index and a list of node directories, emit a list of
 
 def find_shares(options):
     """Given a storage index and a list of node directories, emit a list of
@@ -659,9 +649,6 @@ def find_shares(options):
 
 
 class CatalogSharesOptions(BaseOptions):
 
 
 class CatalogSharesOptions(BaseOptions):
-    """
-
-    """
     def parseArgs(self, *nodedirs):
         from allmydata.util.encodingutil import argv_to_abspath
         self.nodedirs = map(argv_to_abspath, nodedirs)
     def parseArgs(self, *nodedirs):
         from allmydata.util.encodingutil import argv_to_abspath
         self.nodedirs = map(argv_to_abspath, nodedirs)
@@ -669,11 +656,9 @@ class CatalogSharesOptions(BaseOptions):
             raise usage.UsageError("must specify at least one node directory")
 
     def getSynopsis(self):
             raise usage.UsageError("must specify at least one node directory")
 
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug catalog-shares NODEDIRS.."
+        return "Usage: tahoe [global-options] debug catalog-shares NODEDIRS.."
 
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Locate all shares in the given node directories, and emit a one-line summary
 of each share. Run it like this:
 
 Locate all shares in the given node directories, and emit a one-line summary
 of each share. Run it like this:
 
@@ -691,7 +676,6 @@ you see shares with the same SI but different parameters/filesize/UEB_hash,
 then something is wrong. The misc/find-share/anomalies.py script may be
 useful for purpose.
 """
 then something is wrong. The misc/find-share/anomalies.py script may be
 useful for purpose.
 """
-        return t
 
 def call(c, *args, **kwargs):
     # take advantage of the fact that ImmediateReadBucketProxy returns
 
 def call(c, *args, **kwargs):
     # take advantage of the fact that ImmediateReadBucketProxy returns
@@ -882,15 +866,13 @@ def catalog_shares_one_abbrevdir(si_s, si_dir, now, out, err):
 
 class CorruptShareOptions(BaseOptions):
     def getSynopsis(self):
 
 class CorruptShareOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug corrupt-share SHARE_FILENAME"
+        return "Usage: tahoe [global-options] debug corrupt-share SHARE_FILENAME"
 
     optParameters = [
         ["offset", "o", "block-random", "Specify which bit to flip."],
         ]
 
 
     optParameters = [
         ["offset", "o", "block-random", "Specify which bit to flip."],
         ]
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Corrupt the given share by flipping a bit. This will cause a
 verifying/downloading client to log an integrity-check failure incident, and
 downloads will proceed with a different share.
 Corrupt the given share by flipping a bit. This will cause a
 verifying/downloading client to log an integrity-check failure incident, and
 downloads will proceed with a different share.
@@ -902,7 +884,6 @@ to flip a single random bit of the block data.
 
 Obviously, this command should not be used in normal operation.
 """
 
 Obviously, this command should not be used in normal operation.
 """
-        return t
     def parseArgs(self, filename):
         self['filename'] = filename
 
     def parseArgs(self, filename):
         self['filename'] = filename
 
@@ -962,7 +943,7 @@ def corrupt_share(options):
 
 class ReplOptions(BaseOptions):
     def getSynopsis(self):
 
 class ReplOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug repl"
+        return "Usage: tahoe [global-options] debug repl"
 
 def repl(options):
     import code
 
 def repl(options):
     import code
@@ -973,7 +954,7 @@ DEFAULT_TESTSUITE = 'allmydata'
 
 class TrialOptions(twisted_trial.Options):
     def getSynopsis(self):
 
 class TrialOptions(twisted_trial.Options):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug trial [options] [[file|package|module|TestCase|testmethod]...]"
+        return "Usage: tahoe [global-options] debug trial [options] [[file|package|module|TestCase|testmethod]...]"
 
     def parseOptions(self, all_subargs, *a, **kw):
         self.trial_args = list(all_subargs)
 
     def parseOptions(self, all_subargs, *a, **kw):
         self.trial_args = list(all_subargs)
@@ -985,13 +966,10 @@ class TrialOptions(twisted_trial.Options):
         if not nonoption_args:
             self.trial_args.append(DEFAULT_TESTSUITE)
 
         if not nonoption_args:
             self.trial_args.append(DEFAULT_TESTSUITE)
 
-    def getUsage(self, width=None):
-        t = twisted_trial.Options.getUsage(self, width)
-        t += """
-The 'tahoe debug trial' command uses the correct imports for this instance of
-Tahoe-LAFS. The default test suite is '%s'.
-""" % (DEFAULT_TESTSUITE,)
-        return t
+    longdesc = twisted_trial.Options.longdesc + "\n\n" + (
+        "The 'tahoe debug trial' command uses the correct imports for this "
+        "instance of Tahoe-LAFS. The default test suite is '%s'."
+        % DEFAULT_TESTSUITE)
 
 def trial(config):
     sys.argv = ['trial'] + config.trial_args
 
 def trial(config):
     sys.argv = ['trial'] + config.trial_args
@@ -1014,9 +992,9 @@ def fixOptionsClass( (subcmd, shortcut, OptionsClass, desc) ):
             t = OptionsClass.getSynopsis(self)
             i = t.find("Usage: flogtool ")
             if i >= 0:
             t = OptionsClass.getSynopsis(self)
             i = t.find("Usage: flogtool ")
             if i >= 0:
-                return "Usage: tahoe [global-opts] debug flogtool " + t[i+len("Usage: flogtool "):]
+                return "Usage: tahoe [global-options] debug flogtool " + t[i+len("Usage: flogtool "):]
             else:
             else:
-                return "Usage: tahoe [global-opts] debug flogtool %s [options]" % (subcmd,)
+                return "Usage: tahoe [global-options] debug flogtool %s [options]" % (subcmd,)
     return (subcmd, shortcut, FixedOptionsClass, desc)
 
 class FlogtoolOptions(foolscap_cli.Options):
     return (subcmd, shortcut, FixedOptionsClass, desc)
 
 class FlogtoolOptions(foolscap_cli.Options):
@@ -1025,7 +1003,7 @@ class FlogtoolOptions(foolscap_cli.Options):
         self.subCommands = map(fixOptionsClass, self.subCommands)
 
     def getSynopsis(self):
         self.subCommands = map(fixOptionsClass, self.subCommands)
 
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug flogtool (%s) [command options]" % ("|".join([x[0] for x in self.subCommands]))
+        return "Usage: tahoe [global-options] debug flogtool COMMAND [flogtool-options]"
 
     def parseOptions(self, all_subargs, *a, **kw):
         self.flogtool_args = list(all_subargs)
 
     def parseOptions(self, all_subargs, *a, **kw):
         self.flogtool_args = list(all_subargs)
@@ -1037,7 +1015,7 @@ class FlogtoolOptions(foolscap_cli.Options):
 The 'tahoe debug flogtool' command uses the correct imports for this instance
 of Tahoe-LAFS.
 
 The 'tahoe debug flogtool' command uses the correct imports for this instance
 of Tahoe-LAFS.
 
-Please run 'tahoe debug flogtool SUBCOMMAND --help' for more details on each
+Please run 'tahoe debug flogtool COMMAND --help' for more details on each
 subcommand.
 """
         return t
 subcommand.
 """
         return t
@@ -1066,20 +1044,11 @@ class DebugCommand(BaseOptions):
     def postOptions(self):
         if not hasattr(self, 'subOptions'):
             raise usage.UsageError("must specify a subcommand")
     def postOptions(self):
         if not hasattr(self, 'subOptions'):
             raise usage.UsageError("must specify a subcommand")
-    def getSynopsis(self):
-        return ""
+    synopsis = "COMMAND"
+
     def getUsage(self, width=None):
     def getUsage(self, width=None):
-        #t = BaseOptions.getUsage(self, width)
-        t = """Usage: tahoe debug SUBCOMMAND
-Subcommands:
-    tahoe debug dump-share      Unpack and display the contents of a share.
-    tahoe debug dump-cap        Unpack a read-cap or write-cap.
-    tahoe debug find-shares     Locate sharefiles in node directories.
-    tahoe debug catalog-shares  Describe all shares in node dirs.
-    tahoe debug corrupt-share   Corrupt a share by flipping a bit.
-    tahoe debug repl            Open a Python interpreter.
-    tahoe debug trial           Run tests using Twisted Trial with the right imports.
-    tahoe debug flogtool        Utilities to access log files.
+        t = BaseOptions.getUsage(self, width)
+        t += """\
 
 Please run e.g. 'tahoe debug dump-share --help' for more details on each
 subcommand.
 
 Please run e.g. 'tahoe debug dump-share --help' for more details on each
 subcommand.
index 4767e59e067138d1ae6436249c980ae8c452b3f1..c331eee72684ec1ac4a8c8e01da7ed0ad2557e0d 100644 (file)
@@ -70,7 +70,7 @@ class Options(usage.Options):
         return ("\nUsage: tahoe [global-options] <command> [command-options]\n"
                 + self.getUsage())
 
         return ("\nUsage: tahoe [global-options] <command> [command-options]\n"
                 + self.getUsage())
 
-    synopsis = "\nUsage: tahoe [global-opts]" # used only for subcommands
+    synopsis = "\nUsage: tahoe [global-options]" # used only for subcommands
 
     def getUsage(self, **kwargs):
         t = usage.Options.getUsage(self, **kwargs)
 
     def getUsage(self, **kwargs):
         t = usage.Options.getUsage(self, **kwargs)
index 45b7fd11233753ebca334342f9c2ae1011625d64..b1aad6acdc35cb4d252ce69152348bafd22e2547 100644 (file)
@@ -3,12 +3,17 @@ import os, sys, signal, time
 from allmydata.scripts.common import BasedirOptions
 from twisted.scripts import twistd
 from twisted.python import usage
 from allmydata.scripts.common import BasedirOptions
 from twisted.scripts import twistd
 from twisted.python import usage
+from allmydata.scripts.default_nodedir import _default_nodedir
 from allmydata.util import fileutil
 from allmydata.util.encodingutil import listdir_unicode, quote_local_unicode_path
 
 
 class StartOptions(BasedirOptions):
     subcommand_name = "start"
 from allmydata.util import fileutil
 from allmydata.util.encodingutil import listdir_unicode, quote_local_unicode_path
 
 
 class StartOptions(BasedirOptions):
     subcommand_name = "start"
+    optParameters = [
+        ("basedir", "C", None, "Specify which Tahoe base directory should be used. This has the same effect as the global --node-directory option. [default: %s]"
+         % quote_local_unicode_path(_default_nodedir)),
+        ]
 
     def parseArgs(self, basedir=None, *twistd_args):
         # This can't handle e.g. 'tahoe start --nodaemon', since '--nodaemon'
 
     def parseArgs(self, basedir=None, *twistd_args):
         # This can't handle e.g. 'tahoe start --nodaemon', since '--nodaemon'
@@ -21,7 +26,7 @@ class StartOptions(BasedirOptions):
         self.twistd_args = twistd_args
 
     def getSynopsis(self):
         self.twistd_args = twistd_args
 
     def getSynopsis(self):
-        return "Usage:  %s [global-opts] %s [options] [NODEDIR [twistd-options]]" % (self.command_name, self.subcommand_name)
+        return "Usage:  %s [global-options] %s [options] [NODEDIR [twistd-options]]" % (self.command_name, self.subcommand_name)
 
     def getUsage(self, width=None):
         t = BasedirOptions.getUsage(self, width) + "\n"
 
     def getUsage(self, width=None):
         t = BasedirOptions.getUsage(self, width) + "\n"
@@ -40,7 +45,7 @@ class StopOptions(BasedirOptions):
         BasedirOptions.parseArgs(self, basedir)
 
     def getSynopsis(self):
         BasedirOptions.parseArgs(self, basedir)
 
     def getSynopsis(self):
-        return "Usage:  %s [global-opts] stop [options] [NODEDIR]" % (self.command_name,)
+        return "Usage:  %s [global-options] stop [options] [NODEDIR]" % (self.command_name,)
 
 class RestartOptions(StartOptions):
     subcommand_name = "restart"
 
 class RestartOptions(StartOptions):
     subcommand_name = "restart"
index 3c73d7069f66d268c08773e01f4b766a23767730..3caf7687ff9fb33a20a3fa1267e0d5568ed92be6 100644 (file)
@@ -563,124 +563,124 @@ class CLI(CLITestMixin, unittest.TestCase):
 class Help(unittest.TestCase):
     def test_get(self):
         help = str(cli.GetOptions())
 class Help(unittest.TestCase):
     def test_get(self):
         help = str(cli.GetOptions())
-        self.failUnlessIn(" [global-opts] get [options] REMOTE_FILE LOCAL_FILE", help)
+        self.failUnlessIn("[options] REMOTE_FILE LOCAL_FILE", help)
         self.failUnlessIn("% tahoe get FOO |less", help)
 
     def test_put(self):
         help = str(cli.PutOptions())
         self.failUnlessIn("% tahoe get FOO |less", help)
 
     def test_put(self):
         help = str(cli.PutOptions())
-        self.failUnlessIn(" [global-opts] put [options] LOCAL_FILE REMOTE_FILE", help)
+        self.failUnlessIn("[options] LOCAL_FILE REMOTE_FILE", help)
         self.failUnlessIn("% cat FILE | tahoe put", help)
 
     def test_ls(self):
         help = str(cli.ListOptions())
         self.failUnlessIn("% cat FILE | tahoe put", help)
 
     def test_ls(self):
         help = str(cli.ListOptions())
-        self.failUnlessIn(" [global-opts] ls [options] [PATH]", help)
+        self.failUnlessIn("[options] [PATH]", help)
 
     def test_unlink(self):
         help = str(cli.UnlinkOptions())
 
     def test_unlink(self):
         help = str(cli.UnlinkOptions())
-        self.failUnlessIn(" [global-opts] unlink [options] REMOTE_FILE", help)
+        self.failUnlessIn("[options] REMOTE_FILE", help)
 
     def test_rm(self):
         help = str(cli.RmOptions())
 
     def test_rm(self):
         help = str(cli.RmOptions())
-        self.failUnlessIn(" [global-opts] rm [options] REMOTE_FILE", help)
+        self.failUnlessIn("[options] REMOTE_FILE", help)
 
     def test_mv(self):
         help = str(cli.MvOptions())
 
     def test_mv(self):
         help = str(cli.MvOptions())
-        self.failUnlessIn(" [global-opts] mv [options] FROM TO", help)
+        self.failUnlessIn("[options] FROM TO", help)
         self.failUnlessIn("Use 'tahoe mv' to move files", help)
 
     def test_cp(self):
         help = str(cli.CpOptions())
         self.failUnlessIn("Use 'tahoe mv' to move files", help)
 
     def test_cp(self):
         help = str(cli.CpOptions())
-        self.failUnlessIn(" [global-opts] cp [options] FROM.. TO", help)
+        self.failUnlessIn("[options] FROM.. TO", help)
         self.failUnlessIn("Use 'tahoe cp' to copy files", help)
 
     def test_ln(self):
         help = str(cli.LnOptions())
         self.failUnlessIn("Use 'tahoe cp' to copy files", help)
 
     def test_ln(self):
         help = str(cli.LnOptions())
-        self.failUnlessIn(" [global-opts] ln [options] FROM_LINK TO_LINK", help)
+        self.failUnlessIn("[options] FROM_LINK TO_LINK", help)
         self.failUnlessIn("Use 'tahoe ln' to duplicate a link", help)
 
     def test_mkdir(self):
         help = str(cli.MakeDirectoryOptions())
         self.failUnlessIn("Use 'tahoe ln' to duplicate a link", help)
 
     def test_mkdir(self):
         help = str(cli.MakeDirectoryOptions())
-        self.failUnlessIn(" [global-opts] mkdir [options] [REMOTE_DIR]", help)
+        self.failUnlessIn("[options] [REMOTE_DIR]", help)
         self.failUnlessIn("Create a new directory", help)
 
     def test_backup(self):
         help = str(cli.BackupOptions())
         self.failUnlessIn("Create a new directory", help)
 
     def test_backup(self):
         help = str(cli.BackupOptions())
-        self.failUnlessIn(" [global-opts] backup [options] FROM ALIAS:TO", help)
+        self.failUnlessIn("[options] FROM ALIAS:TO", help)
 
     def test_webopen(self):
         help = str(cli.WebopenOptions())
 
     def test_webopen(self):
         help = str(cli.WebopenOptions())
-        self.failUnlessIn(" [global-opts] webopen [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_manifest(self):
         help = str(cli.ManifestOptions())
 
     def test_manifest(self):
         help = str(cli.ManifestOptions())
-        self.failUnlessIn(" [global-opts] manifest [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_stats(self):
         help = str(cli.StatsOptions())
 
     def test_stats(self):
         help = str(cli.StatsOptions())
-        self.failUnlessIn(" [global-opts] stats [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_check(self):
         help = str(cli.CheckOptions())
 
     def test_check(self):
         help = str(cli.CheckOptions())
-        self.failUnlessIn(" [global-opts] check [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_deep_check(self):
         help = str(cli.DeepCheckOptions())
 
     def test_deep_check(self):
         help = str(cli.DeepCheckOptions())
-        self.failUnlessIn(" [global-opts] deep-check [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_create_alias(self):
         help = str(cli.CreateAliasOptions())
 
     def test_create_alias(self):
         help = str(cli.CreateAliasOptions())
-        self.failUnlessIn(" [global-opts] create-alias [options] ALIAS[:]", help)
+        self.failUnlessIn("[options] ALIAS[:]", help)
 
     def test_add_alias(self):
         help = str(cli.AddAliasOptions())
 
     def test_add_alias(self):
         help = str(cli.AddAliasOptions())
-        self.failUnlessIn(" [global-opts] add-alias [options] ALIAS[:] DIRCAP", help)
+        self.failUnlessIn("[options] ALIAS[:] DIRCAP", help)
 
     def test_list_aliases(self):
         help = str(cli.ListAliasesOptions())
 
     def test_list_aliases(self):
         help = str(cli.ListAliasesOptions())
-        self.failUnlessIn(" [global-opts] list-aliases [options]", help)
+        self.failUnlessIn("[options]", help)
 
     def test_start(self):
         help = str(startstop_node.StartOptions())
 
     def test_start(self):
         help = str(startstop_node.StartOptions())
-        self.failUnlessIn(" [global-opts] start [options] [NODEDIR [twistd-options]]", help)
+        self.failUnlessIn("[options] [NODEDIR [twistd-options]]", help)
 
     def test_stop(self):
         help = str(startstop_node.StopOptions())
 
     def test_stop(self):
         help = str(startstop_node.StopOptions())
-        self.failUnlessIn(" [global-opts] stop [options] [NODEDIR]", help)
+        self.failUnlessIn("[options] [NODEDIR]", help)
 
     def test_restart(self):
         help = str(startstop_node.RestartOptions())
 
     def test_restart(self):
         help = str(startstop_node.RestartOptions())
-        self.failUnlessIn(" [global-opts] restart [options] [NODEDIR [twistd-options]]", help)
+        self.failUnlessIn("[options] [NODEDIR [twistd-options]]", help)
 
     def test_run(self):
         help = str(startstop_node.RunOptions())
 
     def test_run(self):
         help = str(startstop_node.RunOptions())
-        self.failUnlessIn(" [global-opts] run [options] [NODEDIR [twistd-options]]", help)
+        self.failUnlessIn("[options] [NODEDIR [twistd-options]]", help)
 
     def test_create_client(self):
         help = str(create_node.CreateClientOptions())
 
     def test_create_client(self):
         help = str(create_node.CreateClientOptions())
-        self.failUnlessIn(" [global-opts] create-client [options] [NODEDIR]", help)
+        self.failUnlessIn("[options] [NODEDIR]", help)
 
     def test_create_node(self):
         help = str(create_node.CreateNodeOptions())
 
     def test_create_node(self):
         help = str(create_node.CreateNodeOptions())
-        self.failUnlessIn(" [global-opts] create-node [options] [NODEDIR]", help)
+        self.failUnlessIn("[options] [NODEDIR]", help)
 
     def test_create_introducer(self):
         help = str(create_node.CreateIntroducerOptions())
 
     def test_create_introducer(self):
         help = str(create_node.CreateIntroducerOptions())
-        self.failUnlessIn(" [global-opts] create-introducer [options] NODEDIR", help)
+        self.failUnlessIn("[options] NODEDIR", help)
 
     def test_debug_trial(self):
         help = str(debug.TrialOptions())
 
     def test_debug_trial(self):
         help = str(debug.TrialOptions())
-        self.failUnlessIn(" [global-opts] debug trial [options] [[file|package|module|TestCase|testmethod]...]", help)
+        self.failUnlessIn(" [global-options] debug trial [options] [[file|package|module|TestCase|testmethod]...]", help)
         self.failUnlessIn("The 'tahoe debug trial' command uses the correct imports", help)
 
     def test_debug_flogtool(self):
         options = debug.FlogtoolOptions()
         help = str(options)
         self.failUnlessIn("The 'tahoe debug trial' command uses the correct imports", help)
 
     def test_debug_flogtool(self):
         options = debug.FlogtoolOptions()
         help = str(options)
-        self.failUnlessIn(" [global-opts] debug flogtool ", help)
+        self.failUnlessIn(" [global-options] debug flogtool ", help)
         self.failUnlessIn("The 'tahoe debug flogtool' command uses the correct imports", help)
 
         for (option, shortcut, oClass, desc) in options.subCommands:
             subhelp = str(oClass())
         self.failUnlessIn("The 'tahoe debug flogtool' command uses the correct imports", help)
 
         for (option, shortcut, oClass, desc) in options.subCommands:
             subhelp = str(oClass())
-            self.failUnlessIn(" [global-opts] debug flogtool %s " % (option,), subhelp)
+            self.failUnlessIn(" [global-options] debug flogtool %s " % (option,), subhelp)
 
 
 class Ln(GridTestMixin, CLITestMixin, unittest.TestCase):
 
 
 class Ln(GridTestMixin, CLITestMixin, unittest.TestCase):