X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/ada0e6803b2811edf87d32b7ac6335766785ed3e..dde3b0d5c06378213c580002472b8887605e7e3c:/autotools/build-bash-completion diff --git a/autotools/build-bash-completion b/autotools/build-bash-completion index c0295cc..63def12 100755 --- a/autotools/build-bash-completion +++ b/autotools/build-bash-completion @@ -27,6 +27,7 @@ # [C0103] Invalid name build-bash-completion import os +import os.path import re import itertools import optparse @@ -38,6 +39,8 @@ from ganeti import utils from ganeti import build from ganeti import pathutils +from ganeti.tools import burnin + # _autoconf shouldn't be imported from anywhere except constants.py, but we're # making an exception here because this script is only used at build time. from ganeti import _autoconf @@ -102,10 +105,10 @@ def WritePreamble(sw, support_debug): sw.IncIndent() try: # FIXME: this is really going into the internals of the job queue - sw.Write(("local jlist=$( shopt -s nullglob &&" - " cd %s 2>/dev/null && echo job-* || : )"), + sw.Write(("local jlist=($( shopt -s nullglob &&" + " cd %s 2>/dev/null && echo job-* || : ))"), utils.ShellQuote(pathutils.QUEUE_DIR)) - sw.Write('echo "${jlist//job-/}"') + sw.Write('echo "${jlist[@]/job-/}"') finally: sw.DecIndent() sw.Write("}") @@ -134,6 +137,15 @@ def WritePreamble(sw, support_debug): sw.DecIndent() sw.Write("}") + sw.Write("_ganeti_network() {") + sw.IncIndent() + try: + networks_path = os.path.join(pathutils.DATA_DIR, "ssconf_networks") + sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(networks_path)) + finally: + sw.DecIndent() + sw.Write("}") + # Params: # Result variable: $first_arg_idx sw.Write("_ganeti_find_first_arg() {") @@ -347,10 +359,14 @@ class CompletionWriter: WriteCompReply(sw, "-W \"$(_ganeti_instances)\"", cur=cur) elif suggest == cli.OPT_COMPL_ONE_OS: WriteCompReply(sw, "-W \"$(_ganeti_os)\"", cur=cur) + elif suggest == cli.OPT_COMPL_ONE_EXTSTORAGE: + WriteCompReply(sw, "-W \"$(_ganeti_extstorage)\"", 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_ONE_NETWORK: + WriteCompReply(sw, "-W \"$(_ganeti_network)\"", cur=cur) elif suggest == cli.OPT_COMPL_INST_ADD_NODES: sw.Write("local tmp= node1= pfx= curvalue=\"${optcur#*:}\"") @@ -449,10 +465,14 @@ class CompletionWriter: choices = "$(_ganeti_nodes)" elif isinstance(arg, cli.ArgGroup): choices = "$(_ganeti_nodegroup)" + elif isinstance(arg, cli.ArgNetwork): + choices = "$(_ganeti_network)" elif isinstance(arg, cli.ArgJobId): choices = "$(_ganeti_jobs)" elif isinstance(arg, cli.ArgOs): choices = "$(_ganeti_os)" + elif isinstance(arg, cli.ArgExtStorage): + choices = "$(_ganeti_extstorage)" elif isinstance(arg, cli.ArgFile): choices = "" compgenargs.append("-f") @@ -641,11 +661,12 @@ def HaskellOptToOptParse(opts, kind): opts = opts.split(",") if kind == "none": return cli.cli_option(*opts, action="store_true") - elif kind in ["file", "string", "host", "dir"]: - return cli.cli_option(*opts, type="string") - elif kind == "numeric": - # FIXME: float values here + elif kind in ["file", "string", "host", "dir", "inetaddr"]: return cli.cli_option(*opts, type="string") + elif kind == "integer": + return cli.cli_option(*opts, type="int") + elif kind == "float": + return cli.cli_option(*opts, type="float") elif kind == "onegroup": return cli.cli_option(*opts, type="string", completion_suggest=cli.OPT_COMPL_ONE_NODEGROUP) @@ -655,43 +676,136 @@ def HaskellOptToOptParse(opts, kind): elif kind == "manyinstances": # FIXME: no support for many instances return cli.cli_option(*opts, type="string") - elif kind.startswith("choices "): - choices = kind[len("choices "):].split(",") + elif kind.startswith("choices="): + choices = kind[len("choices="):].split(",") return cli.cli_option(*opts, type="choice", choices=choices) else: # FIXME: there are many other currently unused completion types, # should be added on an as-needed basis - raise Exception("Unhnadled option kind '%s'" % kind) + raise Exception("Unhandled option kind '%s'" % kind) + + +#: serialised kind to arg type +_ARG_MAP = { + "choices": cli.ArgChoice, + "command": cli.ArgCommand, + "file": cli.ArgFile, + "host": cli.ArgHost, + "jobid": cli.ArgJobId, + "onegroup": cli.ArgGroup, + "oneinstance": cli.ArgInstance, + "onenode": cli.ArgNode, + "oneos": cli.ArgOs, + "string": cli.ArgUnknown, + "suggests": cli.ArgSuggest, + } + + +def HaskellArgToCliArg(kind, min_cnt, max_cnt): + """Converts a Haskell options to Python _Argument. + + @type kind: string + @param kind: type generated by Common.hs/argComplToText; needs to be + kept in sync + + """ + min_cnt = int(min_cnt) + if max_cnt == "none": + max_cnt = None + else: + max_cnt = int(max_cnt) + # pylint: disable=W0142 + # since we pass **kwargs + kwargs = {"min": min_cnt, "max": max_cnt} + + if kind.startswith("choices=") or kind.startswith("suggest="): + (kind, choices) = kind.split("=", 1) + kwargs["choices"] = choices.split(",") + + if kind not in _ARG_MAP: + raise Exception("Unhandled argument kind '%s'" % kind) + else: + return _ARG_MAP[kind](**kwargs) + + +def ParseHaskellOptsArgs(script, output): + """Computes list of options/arguments from help-completion output. + + """ + cli_opts = [] + cli_args = [] + for line in output.splitlines(): + v = line.split(None) + exc = lambda msg: Exception("Invalid %s output from %s: %s" % + (msg, script, v)) + if len(v) < 2: + raise exc("help completion") + if v[0].startswith("-"): + if len(v) != 2: + raise exc("option format") + (opts, kind) = v + cli_opts.append(HaskellOptToOptParse(opts, kind)) + else: + if len(v) != 3: + raise exc("argument format") + (kind, min_cnt, max_cnt) = v + cli_args.append(HaskellArgToCliArg(kind, min_cnt, max_cnt)) + return (cli_opts, cli_args) def WriteHaskellCompletion(sw, script, htools=True, debug=True): """Generates completion information for a Haskell program. - This Converts completion info from a Haskell program into 'fake' + This converts completion info from a Haskell program into 'fake' cli_opts and then builds completion for them. """ if htools: - cmd = "./htools/htools" + cmd = "./src/htools" env = {"HTOOLS": script} script_name = script func_name = "htools_%s" % script else: - # note: this is not yet used (daemons) - cmd = script + cmd = "./" + script env = {} - script_name = script - func_name = script + script_name = os.path.basename(script) + func_name = script_name + func_name = GetFunctionName(func_name) output = utils.RunCmd([cmd, "--help-completion"], env=env, cwd=".").output - cli_opts = [] - for line in output.splitlines(): - v = line.split(None, 1) - if len(v) != 2: - raise Exception("Invalid help completion output from %s: %s" % - (script, v)) - (opts, kind) = v - cli_opts.append(HaskellOptToOptParse(opts, kind)) - WriteCompletion(sw, script_name, func_name, debug, opts=cli_opts, args=[]) + (opts, args) = ParseHaskellOptsArgs(script_name, output) + WriteCompletion(sw, script_name, func_name, debug, opts=opts, args=args) + + +def WriteHaskellCmdCompletion(sw, script, debug=True): + """Generates completion information for a Haskell multi-command program. + + This gathers the list of commands from a Haskell program and + computes the list of commands available, then builds the sub-command + list of options/arguments for each command, using that for building + a unified help output. + + """ + cmd = "./" + script + script_name = os.path.basename(script) + func_name = script_name + func_name = GetFunctionName(func_name) + output = utils.RunCmd([cmd, "--help-completion"], cwd=".").output + commands = {} + lines = output.splitlines() + if len(lines) != 1: + raise Exception("Invalid lines in multi-command mode: %s" % str(lines)) + v = lines[0].split(None) + exc = lambda msg: Exception("Invalid %s output from %s: %s" % + (msg, script, v)) + if len(v) != 3: + raise exc("help completion in multi-command mode") + if not v[0].startswith("choices="): + raise exc("invalid format in multi-command mode '%s'" % v[0]) + for subcmd in v[0][len("choices="):].split(","): + output = utils.RunCmd([cmd, subcmd, "--help-completion"], cwd=".").output + (opts, args) = ParseHaskellOptsArgs(script, output) + commands[subcmd] = (None, args, opts, None, None) + WriteCompletion(sw, script_name, func_name, debug, commands=commands) def main(): @@ -704,36 +818,49 @@ def main(): if args: parser.error("Wrong number of arguments") + # Whether to build debug version of completion script + debug = not options.compact + buf = StringIO() - sw = utils.ShellWriter(buf, indent=not options.compact) + sw = utils.ShellWriter(buf, indent=debug) # Remember original state of extglob and enable it (required for pattern # matching; must be enabled while parsing script) sw.Write("gnt_shopt_extglob=$(shopt -p extglob || :)") sw.Write("shopt -s extglob") - WritePreamble(sw, not options.compact) + WritePreamble(sw, debug) # gnt-* scripts for scriptname in _autoconf.GNT_SCRIPTS: filename = "scripts/%s" % scriptname - WriteCompletion(sw, scriptname, GetFunctionName(scriptname), - not options.compact, + WriteCompletion(sw, scriptname, GetFunctionName(scriptname), debug, commands=GetCommands(filename, build.LoadModule(filename))) # Burnin script - burnin = build.LoadModule("tools/burnin") WriteCompletion(sw, "%s/burnin" % pathutils.TOOLSDIR, "_ganeti_burnin", - not options.compact, + debug, opts=burnin.OPTIONS, args=burnin.ARGUMENTS) + # ganeti-cleaner + WriteHaskellCompletion(sw, "daemons/ganeti-cleaner", htools=False, + debug=not options.compact) + # htools, if enabled if _autoconf.HTOOLS: for script in _autoconf.HTOOLS_PROGS: - WriteHaskellCompletion(sw, script, htools=True, - debug=not options.compact) + WriteHaskellCompletion(sw, script, htools=True, debug=debug) + + # ganeti-confd, if enabled + if _autoconf.ENABLE_CONFD: + WriteHaskellCompletion(sw, "src/ganeti-confd", htools=False, + debug=debug) + + # mon-collector, if monitoring is enabled + if _autoconf.ENABLE_MOND: + WriteHaskellCmdCompletion(sw, "src/mon-collector", debug=debug) # Reset extglob to original value sw.Write("[[ -n \"$gnt_shopt_extglob\" ]] && $gnt_shopt_extglob")