# 02110-1301, USA.
-import optparse
+"""Script to generate bash_completion script for Ganeti.
+
+"""
+
+# pylint: disable-msg=C0103
+# [C0103] Invalid name build-bash-completion
+
import os
-import sys
import re
from cStringIO import StringIO
from ganeti import _autoconf
-class ShellWriter:
- """Helper class to write scripts with indentation.
-
- """
- INDENT_STR = " "
-
- def __init__(self, fh):
- self._fh = fh
- self._indent = 0
-
- def IncIndent(self):
- """Increase indentation level by 1.
-
- """
- self._indent += 1
-
- def DecIndent(self):
- """Decrease indentation level by 1.
-
- """
- assert self._indent > 0
- self._indent -= 1
-
- def Write(self, txt, *args):
- """Write line to output file.
-
- """
- self._fh.write(self._indent * self.INDENT_STR)
-
- if args:
- self._fh.write(txt % args)
- else:
- self._fh.write(txt)
-
- self._fh.write("\n")
-
-
def WritePreamble(sw):
"""Writes the script preamble.
sw.DecIndent()
sw.Write("}")
+ sw.Write("_ganeti_nodegroup() {")
+ sw.IncIndent()
+ try:
+ nodegroups_path = os.path.join(constants.DATA_DIR, "ssconf_nodegroups")
+ sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(nodegroups_path))
+ finally:
+ sw.DecIndent()
+ sw.Write("}")
+
# Params: <offset> <options with values> <options without values>
# Result variable: $first_arg_idx
sw.Write("_ganeti_find_first_arg() {")
sw.DecIndent()
sw.Write("}")
+ # Params: <compgen options>
+ # Result variable: $COMPREPLY
+ sw.Write("_ganeti_compgen() {")
+ sw.IncIndent()
+ try:
+ sw.Write("""COMPREPLY=( $(compgen "$@") )""")
+ sw.Write("_ganeti_dbglog COMPREPLY=\"${COMPREPLY[@]}\"")
+ finally:
+ sw.DecIndent()
+ sw.Write("}")
+
def WriteCompReply(sw, args, cur="\"$cur\""):
- sw.Write("""COMPREPLY=( $(compgen %s -- %s) )""", args, cur)
- sw.Write("_ganeti_dbglog COMPREPLY=\"${COMPREPLY[@]}\"")
+ sw.Write("_ganeti_compgen %s -- %s", args, cur)
sw.Write("return")
self.args = args
for opt in opts:
+ # While documented, these variables aren't seen as public attributes by
+ # pylint. pylint: disable-msg=W0212
opt.all_names = sorted(opt._short_opts + opt._long_opts)
def _FindFirstArgument(self, sw):
# Only static choices implemented so far (e.g. no node list)
suggest = getattr(opt, "completion_suggest", None)
+ # our custom option type
+ if opt.type == "bool":
+ suggest = ["yes", "no"]
+
if not suggest:
suggest = opt.choices
WriteCompReply(sw, "-W \"$(_ganeti_os)\"", cur=cur)
elif suggest == cli.OPT_COMPL_ONE_IALLOCATOR:
WriteCompReply(sw, "-W \"$(_ganeti_iallocator)\"", cur=cur)
+ elif suggest == cli.OPT_COMPL_ONE_NODEGROUP:
+ WriteCompReply(sw, "-W \"$(_ganeti_nodegroup)\"", cur=cur)
elif suggest == cli.OPT_COMPL_INST_ADD_NODES:
sw.Write("local tmp= node1= pfx= curvalue=\"${optcur#*:}\"")
choices = "$(_ganeti_nodes)"
elif isinstance(arg, cli.ArgJobId):
choices = "$(_ganeti_jobs)"
+ elif isinstance(arg, cli.ArgOs):
+ choices = "$(_ganeti_os)"
elif isinstance(arg, cli.ArgFile):
choices = ""
compgenargs.append("-f")
if arg.min == 1 and arg.max == 1:
cmpcode = """"$arg_idx" == %d""" % (last_arg_end)
+ elif arg.max is None:
+ cmpcode = """"$arg_idx" -ge %d""" % (last_arg_end)
elif arg.min <= arg.max:
cmpcode = (""""$arg_idx" -ge %d && "$arg_idx" -lt %d""" %
(last_arg_end, last_arg_end + arg.max))
- elif arg.max is None:
- cmpcode = """"$arg_idx" -ge %d""" % (last_arg_end)
else:
raise Exception("Unable to generate argument position condition")
if choices:
sw.Write("""choices="$choices "%s""", choices)
if compgenargs:
- sw.Write("compgenargs=%s", utils.ShellQuote(" ".join(compgenargs)))
+ sw.Write("compgenargs=%s",
+ utils.ShellQuote(" ".join(compgenargs)))
finally:
sw.DecIndent()
"""
try:
commands = getattr(module, "commands")
- except AttributeError, err:
+ except AttributeError:
raise Exception("Script %s doesn't have 'commands' attribute" %
filename)
+ # Add the implicit "--help" option
+ help_option = cli.cli_option("-h", "--help", default=False,
+ action="store_true")
+
+ for name, (_, _, optdef, _, _) in commands.items():
+ if help_option not in optdef:
+ optdef.append(help_option)
+ for opt in cli.COMMON_OPTS:
+ if opt in optdef:
+ raise Exception("Common option '%s' listed for command '%s' in %s" %
+ (opt, name, filename))
+ optdef.append(opt)
+
# Use aliases
aliases = getattr(module, "aliases", {})
if aliases:
def main():
buf = StringIO()
- sw = ShellWriter(buf)
+ sw = utils.ShellWriter(buf)
WritePreamble(sw)