Statistics
| Branch: | Tag: | Revision:

root / autotools / build-bash-completion @ 24476fa0

History | View | Annotate | Download (25.6 kB)

1 4f3d5b76 Michael Hanselmann
#!/usr/bin/python
2 4f3d5b76 Michael Hanselmann
#
3 4f3d5b76 Michael Hanselmann
4 f5ce7613 Iustin Pop
# Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
5 4f3d5b76 Michael Hanselmann
#
6 4f3d5b76 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 4f3d5b76 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 4f3d5b76 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 4f3d5b76 Michael Hanselmann
# (at your option) any later version.
10 4f3d5b76 Michael Hanselmann
#
11 4f3d5b76 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 4f3d5b76 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 4f3d5b76 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 4f3d5b76 Michael Hanselmann
# General Public License for more details.
15 4f3d5b76 Michael Hanselmann
#
16 4f3d5b76 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 4f3d5b76 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 4f3d5b76 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 4f3d5b76 Michael Hanselmann
# 02110-1301, USA.
20 4f3d5b76 Michael Hanselmann
21 4f3d5b76 Michael Hanselmann
22 b4086ce8 Michael Hanselmann
"""Script to generate bash_completion script for Ganeti.
23 b4086ce8 Michael Hanselmann
24 b4086ce8 Michael Hanselmann
"""
25 b4086ce8 Michael Hanselmann
26 b459a848 Andrea Spadaccini
# pylint: disable=C0103
27 b4086ce8 Michael Hanselmann
# [C0103] Invalid name build-bash-completion
28 b4086ce8 Michael Hanselmann
29 4f3d5b76 Michael Hanselmann
import os
30 f8d01158 Iustin Pop
import os.path
31 4f3d5b76 Michael Hanselmann
import re
32 49283373 Michael Hanselmann
import itertools
33 e80aeb89 Michael Hanselmann
import optparse
34 4f3d5b76 Michael Hanselmann
from cStringIO import StringIO
35 4f3d5b76 Michael Hanselmann
36 4f3d5b76 Michael Hanselmann
from ganeti import constants
37 4f3d5b76 Michael Hanselmann
from ganeti import cli
38 4f3d5b76 Michael Hanselmann
from ganeti import utils
39 e948770c Michael Hanselmann
from ganeti import build
40 13718ded Michael Hanselmann
from ganeti import pathutils
41 4f3d5b76 Michael Hanselmann
42 4f3d5b76 Michael Hanselmann
# _autoconf shouldn't be imported from anywhere except constants.py, but we're
43 4f3d5b76 Michael Hanselmann
# making an exception here because this script is only used at build time.
44 4f3d5b76 Michael Hanselmann
from ganeti import _autoconf
45 4f3d5b76 Michael Hanselmann
46 49283373 Michael Hanselmann
#: Regular expression describing desired format of option names. Long names can
47 49283373 Michael Hanselmann
#: contain lowercase characters, numbers and dashes only.
48 49283373 Michael Hanselmann
_OPT_NAME_RE = re.compile(r"^-[a-zA-Z0-9]|--[a-z][-a-z0-9]+$")
49 49283373 Michael Hanselmann
50 4f3d5b76 Michael Hanselmann
51 e80aeb89 Michael Hanselmann
def WritePreamble(sw, support_debug):
52 4f3d5b76 Michael Hanselmann
  """Writes the script preamble.
53 4f3d5b76 Michael Hanselmann
54 4f3d5b76 Michael Hanselmann
  Helper functions should be written here.
55 4f3d5b76 Michael Hanselmann
56 4f3d5b76 Michael Hanselmann
  """
57 4f3d5b76 Michael Hanselmann
  sw.Write("# This script is automatically generated at build time.")
58 4f3d5b76 Michael Hanselmann
  sw.Write("# Do not modify manually.")
59 4f3d5b76 Michael Hanselmann
60 e80aeb89 Michael Hanselmann
  if support_debug:
61 e80aeb89 Michael Hanselmann
    sw.Write("_gnt_log() {")
62 5b0ca9d4 Michael Hanselmann
    sw.IncIndent()
63 5b0ca9d4 Michael Hanselmann
    try:
64 e80aeb89 Michael Hanselmann
      sw.Write("if [[ -n \"$GANETI_COMPL_LOG\" ]]; then")
65 5b0ca9d4 Michael Hanselmann
      sw.IncIndent()
66 5b0ca9d4 Michael Hanselmann
      try:
67 e80aeb89 Michael Hanselmann
        sw.Write("{")
68 e80aeb89 Michael Hanselmann
        sw.IncIndent()
69 e80aeb89 Michael Hanselmann
        try:
70 e80aeb89 Michael Hanselmann
          sw.Write("echo ---")
71 e80aeb89 Michael Hanselmann
          sw.Write("echo \"$@\"")
72 e80aeb89 Michael Hanselmann
          sw.Write("echo")
73 e80aeb89 Michael Hanselmann
        finally:
74 e80aeb89 Michael Hanselmann
          sw.DecIndent()
75 e80aeb89 Michael Hanselmann
        sw.Write("} >> $GANETI_COMPL_LOG")
76 5b0ca9d4 Michael Hanselmann
      finally:
77 5b0ca9d4 Michael Hanselmann
        sw.DecIndent()
78 e80aeb89 Michael Hanselmann
      sw.Write("fi")
79 5b0ca9d4 Michael Hanselmann
    finally:
80 5b0ca9d4 Michael Hanselmann
      sw.DecIndent()
81 e80aeb89 Michael Hanselmann
    sw.Write("}")
82 5b0ca9d4 Michael Hanselmann
83 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_nodes() {")
84 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
85 4f3d5b76 Michael Hanselmann
  try:
86 13718ded Michael Hanselmann
    node_list_path = os.path.join(pathutils.DATA_DIR, "ssconf_node_list")
87 5a78e2e7 Michael Hanselmann
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(node_list_path))
88 4f3d5b76 Michael Hanselmann
  finally:
89 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
90 4f3d5b76 Michael Hanselmann
  sw.Write("}")
91 4f3d5b76 Michael Hanselmann
92 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_instances() {")
93 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
94 4f3d5b76 Michael Hanselmann
  try:
95 13718ded Michael Hanselmann
    instance_list_path = os.path.join(pathutils.DATA_DIR,
96 4f3d5b76 Michael Hanselmann
                                      "ssconf_instance_list")
97 5a78e2e7 Michael Hanselmann
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(instance_list_path))
98 4f3d5b76 Michael Hanselmann
  finally:
99 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
100 4f3d5b76 Michael Hanselmann
  sw.Write("}")
101 4f3d5b76 Michael Hanselmann
102 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_jobs() {")
103 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
104 4f3d5b76 Michael Hanselmann
  try:
105 4f3d5b76 Michael Hanselmann
    # FIXME: this is really going into the internals of the job queue
106 5a78e2e7 Michael Hanselmann
    sw.Write(("local jlist=$( shopt -s nullglob &&"
107 5a78e2e7 Michael Hanselmann
              " cd %s 2>/dev/null && echo job-* || : )"),
108 13718ded Michael Hanselmann
             utils.ShellQuote(pathutils.QUEUE_DIR))
109 5a78e2e7 Michael Hanselmann
    sw.Write('echo "${jlist//job-/}"')
110 4f3d5b76 Michael Hanselmann
  finally:
111 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
112 4f3d5b76 Michael Hanselmann
  sw.Write("}")
113 4f3d5b76 Michael Hanselmann
114 30d44392 Michael Hanselmann
  for (fnname, paths) in [
115 13718ded Michael Hanselmann
    ("os", pathutils.OS_SEARCH_PATH),
116 5ae4945a Iustin Pop
    ("iallocator", constants.IALLOCATOR_SEARCH_PATH),
117 5ae4945a Iustin Pop
    ]:
118 30d44392 Michael Hanselmann
    sw.Write("_ganeti_%s() {", fnname)
119 30d44392 Michael Hanselmann
    sw.IncIndent()
120 30d44392 Michael Hanselmann
    try:
121 30d44392 Michael Hanselmann
      # FIXME: Make querying the master for all OSes cheap
122 30d44392 Michael Hanselmann
      for path in paths:
123 30d44392 Michael Hanselmann
        sw.Write("( shopt -s nullglob && cd %s 2>/dev/null && echo * || : )",
124 30d44392 Michael Hanselmann
                 utils.ShellQuote(path))
125 30d44392 Michael Hanselmann
    finally:
126 30d44392 Michael Hanselmann
      sw.DecIndent()
127 30d44392 Michael Hanselmann
    sw.Write("}")
128 4f3d5b76 Michael Hanselmann
129 36e247e1 Guido Trotter
  sw.Write("_ganeti_nodegroup() {")
130 36e247e1 Guido Trotter
  sw.IncIndent()
131 36e247e1 Guido Trotter
  try:
132 13718ded Michael Hanselmann
    nodegroups_path = os.path.join(pathutils.DATA_DIR, "ssconf_nodegroups")
133 36e247e1 Guido Trotter
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(nodegroups_path))
134 36e247e1 Guido Trotter
  finally:
135 36e247e1 Guido Trotter
    sw.DecIndent()
136 36e247e1 Guido Trotter
  sw.Write("}")
137 36e247e1 Guido Trotter
138 4e4b6b7a Apollon Oikonomopoulos
  sw.Write("_ganeti_network() {")
139 4e4b6b7a Apollon Oikonomopoulos
  sw.IncIndent()
140 4e4b6b7a Apollon Oikonomopoulos
  try:
141 4e4b6b7a Apollon Oikonomopoulos
    networks_path = os.path.join(pathutils.DATA_DIR, "ssconf_networks")
142 4e4b6b7a Apollon Oikonomopoulos
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(networks_path))
143 4e4b6b7a Apollon Oikonomopoulos
  finally:
144 4e4b6b7a Apollon Oikonomopoulos
    sw.DecIndent()
145 4e4b6b7a Apollon Oikonomopoulos
  sw.Write("}")
146 4e4b6b7a Apollon Oikonomopoulos
147 4f3d5b76 Michael Hanselmann
  # Params: <offset> <options with values> <options without values>
148 4f3d5b76 Michael Hanselmann
  # Result variable: $first_arg_idx
149 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_find_first_arg() {")
150 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
151 4f3d5b76 Michael Hanselmann
  try:
152 4f3d5b76 Michael Hanselmann
    sw.Write("local w i")
153 4f3d5b76 Michael Hanselmann
154 4f3d5b76 Michael Hanselmann
    sw.Write("first_arg_idx=")
155 4f3d5b76 Michael Hanselmann
    sw.Write("for (( i=$1; i < COMP_CWORD; ++i )); do")
156 4f3d5b76 Michael Hanselmann
    sw.IncIndent()
157 4f3d5b76 Michael Hanselmann
    try:
158 4f3d5b76 Michael Hanselmann
      sw.Write("w=${COMP_WORDS[$i]}")
159 4f3d5b76 Michael Hanselmann
160 4f3d5b76 Michael Hanselmann
      # Skip option value
161 4f3d5b76 Michael Hanselmann
      sw.Write("""if [[ -n "$2" && "$w" == @($2) ]]; then let ++i""")
162 4f3d5b76 Michael Hanselmann
163 4f3d5b76 Michael Hanselmann
      # Skip
164 4f3d5b76 Michael Hanselmann
      sw.Write("""elif [[ -n "$3" && "$w" == @($3) ]]; then :""")
165 4f3d5b76 Michael Hanselmann
166 4f3d5b76 Michael Hanselmann
      # Ah, we found the first argument
167 4f3d5b76 Michael Hanselmann
      sw.Write("else first_arg_idx=$i; break;")
168 4f3d5b76 Michael Hanselmann
      sw.Write("fi")
169 4f3d5b76 Michael Hanselmann
    finally:
170 4f3d5b76 Michael Hanselmann
      sw.DecIndent()
171 4f3d5b76 Michael Hanselmann
    sw.Write("done")
172 4f3d5b76 Michael Hanselmann
  finally:
173 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
174 4f3d5b76 Michael Hanselmann
  sw.Write("}")
175 4f3d5b76 Michael Hanselmann
176 4f3d5b76 Michael Hanselmann
  # Params: <list of options separated by space>
177 4f3d5b76 Michael Hanselmann
  # Input variable: $first_arg_idx
178 4f3d5b76 Michael Hanselmann
  # Result variables: $arg_idx, $choices
179 4f3d5b76 Michael Hanselmann
  sw.Write("_ganeti_list_options() {")
180 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
181 4f3d5b76 Michael Hanselmann
  try:
182 4f3d5b76 Michael Hanselmann
    sw.Write("""if [[ -z "$first_arg_idx" ]]; then""")
183 4f3d5b76 Michael Hanselmann
    sw.IncIndent()
184 4f3d5b76 Michael Hanselmann
    try:
185 4f3d5b76 Michael Hanselmann
      sw.Write("arg_idx=0")
186 4f3d5b76 Michael Hanselmann
      # Show options only if the current word starts with a dash
187 4f3d5b76 Michael Hanselmann
      sw.Write("""if [[ "$cur" == -* ]]; then""")
188 4f3d5b76 Michael Hanselmann
      sw.IncIndent()
189 4f3d5b76 Michael Hanselmann
      try:
190 4f3d5b76 Michael Hanselmann
        sw.Write("choices=$1")
191 4f3d5b76 Michael Hanselmann
      finally:
192 4f3d5b76 Michael Hanselmann
        sw.DecIndent()
193 4f3d5b76 Michael Hanselmann
      sw.Write("fi")
194 4f3d5b76 Michael Hanselmann
      sw.Write("return")
195 4f3d5b76 Michael Hanselmann
    finally:
196 4f3d5b76 Michael Hanselmann
      sw.DecIndent()
197 4f3d5b76 Michael Hanselmann
    sw.Write("fi")
198 4f3d5b76 Michael Hanselmann
199 4f3d5b76 Michael Hanselmann
    # Calculate position of current argument
200 4f3d5b76 Michael Hanselmann
    sw.Write("arg_idx=$(( COMP_CWORD - first_arg_idx ))")
201 4f3d5b76 Michael Hanselmann
    sw.Write("choices=")
202 4f3d5b76 Michael Hanselmann
  finally:
203 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
204 4f3d5b76 Michael Hanselmann
  sw.Write("}")
205 4f3d5b76 Michael Hanselmann
206 d4b94fe8 Michael Hanselmann
  # Params: <long options with equal sign> <all options>
207 d4b94fe8 Michael Hanselmann
  # Result variable: $optcur
208 3db199ea Michael Hanselmann
  sw.Write("_gnt_checkopt() {")
209 d4b94fe8 Michael Hanselmann
  sw.IncIndent()
210 d4b94fe8 Michael Hanselmann
  try:
211 d4b94fe8 Michael Hanselmann
    sw.Write("""if [[ -n "$1" && "$cur" == @($1) ]]; then""")
212 d4b94fe8 Michael Hanselmann
    sw.IncIndent()
213 d4b94fe8 Michael Hanselmann
    try:
214 d4b94fe8 Michael Hanselmann
      sw.Write("optcur=\"${cur#--*=}\"")
215 d4b94fe8 Michael Hanselmann
      sw.Write("return 0")
216 d4b94fe8 Michael Hanselmann
    finally:
217 d4b94fe8 Michael Hanselmann
      sw.DecIndent()
218 d4b94fe8 Michael Hanselmann
    sw.Write("""elif [[ -n "$2" && "$prev" == @($2) ]]; then""")
219 d4b94fe8 Michael Hanselmann
    sw.IncIndent()
220 d4b94fe8 Michael Hanselmann
    try:
221 d4b94fe8 Michael Hanselmann
      sw.Write("optcur=\"$cur\"")
222 d4b94fe8 Michael Hanselmann
      sw.Write("return 0")
223 d4b94fe8 Michael Hanselmann
    finally:
224 d4b94fe8 Michael Hanselmann
      sw.DecIndent()
225 d4b94fe8 Michael Hanselmann
    sw.Write("fi")
226 d4b94fe8 Michael Hanselmann
227 e80aeb89 Michael Hanselmann
    if support_debug:
228 e80aeb89 Michael Hanselmann
      sw.Write("_gnt_log optcur=\"'$optcur'\"")
229 5b0ca9d4 Michael Hanselmann
230 d4b94fe8 Michael Hanselmann
    sw.Write("return 1")
231 d4b94fe8 Michael Hanselmann
  finally:
232 d4b94fe8 Michael Hanselmann
    sw.DecIndent()
233 d4b94fe8 Michael Hanselmann
  sw.Write("}")
234 d4b94fe8 Michael Hanselmann
235 a10caf87 Michael Hanselmann
  # Params: <compgen options>
236 a10caf87 Michael Hanselmann
  # Result variable: $COMPREPLY
237 3db199ea Michael Hanselmann
  sw.Write("_gnt_compgen() {")
238 a10caf87 Michael Hanselmann
  sw.IncIndent()
239 a10caf87 Michael Hanselmann
  try:
240 a10caf87 Michael Hanselmann
    sw.Write("""COMPREPLY=( $(compgen "$@") )""")
241 e80aeb89 Michael Hanselmann
    if support_debug:
242 e80aeb89 Michael Hanselmann
      sw.Write("_gnt_log COMPREPLY=\"${COMPREPLY[@]}\"")
243 a10caf87 Michael Hanselmann
  finally:
244 a10caf87 Michael Hanselmann
    sw.DecIndent()
245 a10caf87 Michael Hanselmann
  sw.Write("}")
246 a10caf87 Michael Hanselmann
247 4f3d5b76 Michael Hanselmann
248 632d5090 Michael Hanselmann
def WriteCompReply(sw, args, cur="\"$cur\""):
249 3db199ea Michael Hanselmann
  sw.Write("_gnt_compgen %s -- %s", args, cur)
250 4f3d5b76 Michael Hanselmann
  sw.Write("return")
251 4f3d5b76 Michael Hanselmann
252 4f3d5b76 Michael Hanselmann
253 4f3d5b76 Michael Hanselmann
class CompletionWriter:
254 4f3d5b76 Michael Hanselmann
  """Command completion writer class.
255 4f3d5b76 Michael Hanselmann
256 4f3d5b76 Michael Hanselmann
  """
257 e80aeb89 Michael Hanselmann
  def __init__(self, arg_offset, opts, args, support_debug):
258 4f3d5b76 Michael Hanselmann
    self.arg_offset = arg_offset
259 4f3d5b76 Michael Hanselmann
    self.opts = opts
260 4f3d5b76 Michael Hanselmann
    self.args = args
261 e80aeb89 Michael Hanselmann
    self.support_debug = support_debug
262 4f3d5b76 Michael Hanselmann
263 4f3d5b76 Michael Hanselmann
    for opt in opts:
264 f59418db Michael Hanselmann
      # While documented, these variables aren't seen as public attributes by
265 b459a848 Andrea Spadaccini
      # pylint. pylint: disable=W0212
266 4f3d5b76 Michael Hanselmann
      opt.all_names = sorted(opt._short_opts + opt._long_opts)
267 4f3d5b76 Michael Hanselmann
268 49283373 Michael Hanselmann
      invalid = list(itertools.ifilterfalse(_OPT_NAME_RE.match, opt.all_names))
269 49283373 Michael Hanselmann
      if invalid:
270 49283373 Michael Hanselmann
        raise Exception("Option names don't match regular expression '%s': %s" %
271 49283373 Michael Hanselmann
                        (_OPT_NAME_RE.pattern, utils.CommaJoin(invalid)))
272 49283373 Michael Hanselmann
273 4f3d5b76 Michael Hanselmann
  def _FindFirstArgument(self, sw):
274 4f3d5b76 Michael Hanselmann
    ignore = []
275 4f3d5b76 Michael Hanselmann
    skip_one = []
276 4f3d5b76 Michael Hanselmann
277 4f3d5b76 Michael Hanselmann
    for opt in self.opts:
278 4f3d5b76 Michael Hanselmann
      if opt.takes_value():
279 4f3d5b76 Michael Hanselmann
        # Ignore value
280 4f3d5b76 Michael Hanselmann
        for i in opt.all_names:
281 580ef58d Michael Hanselmann
          if i.startswith("--"):
282 580ef58d Michael Hanselmann
            ignore.append("%s=*" % utils.ShellQuote(i))
283 4f3d5b76 Michael Hanselmann
          skip_one.append(utils.ShellQuote(i))
284 4f3d5b76 Michael Hanselmann
      else:
285 4f3d5b76 Michael Hanselmann
        ignore.extend([utils.ShellQuote(i) for i in opt.all_names])
286 4f3d5b76 Michael Hanselmann
287 4f3d5b76 Michael Hanselmann
    ignore = sorted(utils.UniqueSequence(ignore))
288 4f3d5b76 Michael Hanselmann
    skip_one = sorted(utils.UniqueSequence(skip_one))
289 4f3d5b76 Michael Hanselmann
290 4f3d5b76 Michael Hanselmann
    if ignore or skip_one:
291 4f3d5b76 Michael Hanselmann
      # Try to locate first argument
292 4f3d5b76 Michael Hanselmann
      sw.Write("_ganeti_find_first_arg %s %s %s",
293 4f3d5b76 Michael Hanselmann
               self.arg_offset + 1,
294 4f3d5b76 Michael Hanselmann
               utils.ShellQuote("|".join(skip_one)),
295 4f3d5b76 Michael Hanselmann
               utils.ShellQuote("|".join(ignore)))
296 4f3d5b76 Michael Hanselmann
    else:
297 4f3d5b76 Michael Hanselmann
      # When there are no options the first argument is always at position
298 4f3d5b76 Michael Hanselmann
      # offset + 1
299 4f3d5b76 Michael Hanselmann
      sw.Write("first_arg_idx=%s", self.arg_offset + 1)
300 4f3d5b76 Michael Hanselmann
301 4f3d5b76 Michael Hanselmann
  def _CompleteOptionValues(self, sw):
302 4f3d5b76 Michael Hanselmann
    # Group by values
303 4f3d5b76 Michael Hanselmann
    # "values" -> [optname1, optname2, ...]
304 4f3d5b76 Michael Hanselmann
    values = {}
305 4f3d5b76 Michael Hanselmann
306 4f3d5b76 Michael Hanselmann
    for opt in self.opts:
307 4f3d5b76 Michael Hanselmann
      if not opt.takes_value():
308 4f3d5b76 Michael Hanselmann
        continue
309 4f3d5b76 Michael Hanselmann
310 4f3d5b76 Michael Hanselmann
      # Only static choices implemented so far (e.g. no node list)
311 4f3d5b76 Michael Hanselmann
      suggest = getattr(opt, "completion_suggest", None)
312 4f3d5b76 Michael Hanselmann
313 e7b61bb0 Iustin Pop
      # our custom option type
314 e7b61bb0 Iustin Pop
      if opt.type == "bool":
315 e7b61bb0 Iustin Pop
        suggest = ["yes", "no"]
316 e7b61bb0 Iustin Pop
317 4f3d5b76 Michael Hanselmann
      if not suggest:
318 4f3d5b76 Michael Hanselmann
        suggest = opt.choices
319 4f3d5b76 Michael Hanselmann
320 63d44c55 Michael Hanselmann
      if (isinstance(suggest, (int, long)) and
321 63d44c55 Michael Hanselmann
          suggest in cli.OPT_COMPL_ALL):
322 63d44c55 Michael Hanselmann
        key = suggest
323 63d44c55 Michael Hanselmann
      elif suggest:
324 63d44c55 Michael Hanselmann
        key = " ".join(sorted(suggest))
325 4f3d5b76 Michael Hanselmann
      else:
326 63d44c55 Michael Hanselmann
        key = ""
327 4f3d5b76 Michael Hanselmann
328 63d44c55 Michael Hanselmann
      values.setdefault(key, []).extend(opt.all_names)
329 4f3d5b76 Michael Hanselmann
330 4f3d5b76 Michael Hanselmann
    # Don't write any code if there are no option values
331 4f3d5b76 Michael Hanselmann
    if not values:
332 4f3d5b76 Michael Hanselmann
      return
333 4f3d5b76 Michael Hanselmann
334 d4b94fe8 Michael Hanselmann
    cur = "\"$optcur\""
335 d4b94fe8 Michael Hanselmann
336 d4b94fe8 Michael Hanselmann
    wrote_opt = False
337 d4b94fe8 Michael Hanselmann
338 f5ce7613 Iustin Pop
    for (suggest, allnames) in values.items():
339 d4b94fe8 Michael Hanselmann
      longnames = [i for i in allnames if i.startswith("--")]
340 d4b94fe8 Michael Hanselmann
341 d4b94fe8 Michael Hanselmann
      if wrote_opt:
342 d4b94fe8 Michael Hanselmann
        condcmd = "elif"
343 d4b94fe8 Michael Hanselmann
      else:
344 d4b94fe8 Michael Hanselmann
        condcmd = "if"
345 d4b94fe8 Michael Hanselmann
346 3db199ea Michael Hanselmann
      sw.Write("%s _gnt_checkopt %s %s; then", condcmd,
347 d4b94fe8 Michael Hanselmann
               utils.ShellQuote("|".join(["%s=*" % i for i in longnames])),
348 d4b94fe8 Michael Hanselmann
               utils.ShellQuote("|".join(allnames)))
349 d4b94fe8 Michael Hanselmann
      sw.IncIndent()
350 d4b94fe8 Michael Hanselmann
      try:
351 63d44c55 Michael Hanselmann
        if suggest == cli.OPT_COMPL_MANY_NODES:
352 63d44c55 Michael Hanselmann
          # TODO: Implement comma-separated values
353 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W ''", cur=cur)
354 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_NODE:
355 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_nodes)\"", cur=cur)
356 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_INSTANCE:
357 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_instances)\"", cur=cur)
358 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_OS:
359 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_os)\"", cur=cur)
360 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_IALLOCATOR:
361 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_iallocator)\"", cur=cur)
362 36e247e1 Guido Trotter
        elif suggest == cli.OPT_COMPL_ONE_NODEGROUP:
363 36e247e1 Guido Trotter
          WriteCompReply(sw, "-W \"$(_ganeti_nodegroup)\"", cur=cur)
364 4e4b6b7a Apollon Oikonomopoulos
        elif suggest == cli.OPT_COMPL_ONE_NETWORK:
365 4e4b6b7a Apollon Oikonomopoulos
          WriteCompReply(sw, "-W \"$(_ganeti_network)\"", cur=cur)
366 2d3ed64b Michael Hanselmann
        elif suggest == cli.OPT_COMPL_INST_ADD_NODES:
367 2d3ed64b Michael Hanselmann
          sw.Write("local tmp= node1= pfx= curvalue=\"${optcur#*:}\"")
368 2d3ed64b Michael Hanselmann
369 2d3ed64b Michael Hanselmann
          sw.Write("if [[ \"$optcur\" == *:* ]]; then")
370 2d3ed64b Michael Hanselmann
          sw.IncIndent()
371 2d3ed64b Michael Hanselmann
          try:
372 2d3ed64b Michael Hanselmann
            sw.Write("node1=\"${optcur%%:*}\"")
373 2d3ed64b Michael Hanselmann
374 2d3ed64b Michael Hanselmann
            sw.Write("if [[ \"$COMP_WORDBREAKS\" != *:* ]]; then")
375 2d3ed64b Michael Hanselmann
            sw.IncIndent()
376 2d3ed64b Michael Hanselmann
            try:
377 2d3ed64b Michael Hanselmann
              sw.Write("pfx=\"$node1:\"")
378 2d3ed64b Michael Hanselmann
            finally:
379 2d3ed64b Michael Hanselmann
              sw.DecIndent()
380 2d3ed64b Michael Hanselmann
            sw.Write("fi")
381 2d3ed64b Michael Hanselmann
          finally:
382 2d3ed64b Michael Hanselmann
            sw.DecIndent()
383 2d3ed64b Michael Hanselmann
          sw.Write("fi")
384 2d3ed64b Michael Hanselmann
385 e80aeb89 Michael Hanselmann
          if self.support_debug:
386 e80aeb89 Michael Hanselmann
            sw.Write("_gnt_log pfx=\"'$pfx'\" curvalue=\"'$curvalue'\""
387 e80aeb89 Michael Hanselmann
                     " node1=\"'$node1'\"")
388 2d3ed64b Michael Hanselmann
389 2d3ed64b Michael Hanselmann
          sw.Write("for i in $(_ganeti_nodes); do")
390 2d3ed64b Michael Hanselmann
          sw.IncIndent()
391 2d3ed64b Michael Hanselmann
          try:
392 2d3ed64b Michael Hanselmann
            sw.Write("if [[ -z \"$node1\" ]]; then")
393 2d3ed64b Michael Hanselmann
            sw.IncIndent()
394 2d3ed64b Michael Hanselmann
            try:
395 2d3ed64b Michael Hanselmann
              sw.Write("tmp=\"$tmp $i $i:\"")
396 2d3ed64b Michael Hanselmann
            finally:
397 2d3ed64b Michael Hanselmann
              sw.DecIndent()
398 2d3ed64b Michael Hanselmann
            sw.Write("elif [[ \"$i\" != \"$node1\" ]]; then")
399 2d3ed64b Michael Hanselmann
            sw.IncIndent()
400 2d3ed64b Michael Hanselmann
            try:
401 2d3ed64b Michael Hanselmann
              sw.Write("tmp=\"$tmp $i\"")
402 2d3ed64b Michael Hanselmann
            finally:
403 2d3ed64b Michael Hanselmann
              sw.DecIndent()
404 2d3ed64b Michael Hanselmann
            sw.Write("fi")
405 2d3ed64b Michael Hanselmann
          finally:
406 2d3ed64b Michael Hanselmann
            sw.DecIndent()
407 2d3ed64b Michael Hanselmann
          sw.Write("done")
408 2d3ed64b Michael Hanselmann
409 2d3ed64b Michael Hanselmann
          WriteCompReply(sw, "-P \"$pfx\" -W \"$tmp\"", cur="\"$curvalue\"")
410 63d44c55 Michael Hanselmann
        else:
411 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W %s" % utils.ShellQuote(suggest), cur=cur)
412 d4b94fe8 Michael Hanselmann
      finally:
413 d4b94fe8 Michael Hanselmann
        sw.DecIndent()
414 d4b94fe8 Michael Hanselmann
415 d4b94fe8 Michael Hanselmann
      wrote_opt = True
416 d4b94fe8 Michael Hanselmann
417 d4b94fe8 Michael Hanselmann
    if wrote_opt:
418 d4b94fe8 Michael Hanselmann
      sw.Write("fi")
419 d4b94fe8 Michael Hanselmann
420 d4b94fe8 Michael Hanselmann
    return
421 632d5090 Michael Hanselmann
422 4f3d5b76 Michael Hanselmann
  def _CompleteArguments(self, sw):
423 4f3d5b76 Michael Hanselmann
    if not (self.opts or self.args):
424 4f3d5b76 Michael Hanselmann
      return
425 4f3d5b76 Michael Hanselmann
426 4f3d5b76 Michael Hanselmann
    all_option_names = []
427 4f3d5b76 Michael Hanselmann
    for opt in self.opts:
428 4f3d5b76 Michael Hanselmann
      all_option_names.extend(opt.all_names)
429 4f3d5b76 Michael Hanselmann
    all_option_names.sort()
430 4f3d5b76 Michael Hanselmann
431 4f3d5b76 Michael Hanselmann
    # List options if no argument has been specified yet
432 4f3d5b76 Michael Hanselmann
    sw.Write("_ganeti_list_options %s",
433 4f3d5b76 Michael Hanselmann
             utils.ShellQuote(" ".join(all_option_names)))
434 4f3d5b76 Michael Hanselmann
435 4f3d5b76 Michael Hanselmann
    if self.args:
436 4f3d5b76 Michael Hanselmann
      last_idx = len(self.args) - 1
437 4f3d5b76 Michael Hanselmann
      last_arg_end = 0
438 4f3d5b76 Michael Hanselmann
      varlen_arg_idx = None
439 4f3d5b76 Michael Hanselmann
      wrote_arg = False
440 4f3d5b76 Michael Hanselmann
441 4f3d5b76 Michael Hanselmann
      sw.Write("compgenargs=")
442 4f3d5b76 Michael Hanselmann
443 4f3d5b76 Michael Hanselmann
      for idx, arg in enumerate(self.args):
444 4f3d5b76 Michael Hanselmann
        assert arg.min is not None and arg.min >= 0
445 4f3d5b76 Michael Hanselmann
        assert not (idx < last_idx and arg.max is None)
446 4f3d5b76 Michael Hanselmann
447 4f3d5b76 Michael Hanselmann
        if arg.min != arg.max or arg.max is None:
448 4f3d5b76 Michael Hanselmann
          if varlen_arg_idx is not None:
449 4f3d5b76 Michael Hanselmann
            raise Exception("Only one argument can have a variable length")
450 4f3d5b76 Michael Hanselmann
          varlen_arg_idx = idx
451 4f3d5b76 Michael Hanselmann
452 4f3d5b76 Michael Hanselmann
        compgenargs = []
453 4f3d5b76 Michael Hanselmann
454 4f3d5b76 Michael Hanselmann
        if isinstance(arg, cli.ArgUnknown):
455 4f3d5b76 Michael Hanselmann
          choices = ""
456 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgSuggest):
457 4f3d5b76 Michael Hanselmann
          choices = utils.ShellQuote(" ".join(arg.choices))
458 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgInstance):
459 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_instances)"
460 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgNode):
461 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_nodes)"
462 667dbd6b Adeodato Simo
        elif isinstance(arg, cli.ArgGroup):
463 667dbd6b Adeodato Simo
          choices = "$(_ganeti_nodegroup)"
464 4e4b6b7a Apollon Oikonomopoulos
        elif isinstance(arg, cli.ArgNetwork):
465 4e4b6b7a Apollon Oikonomopoulos
          choices = "$(_ganeti_network)"
466 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgJobId):
467 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_jobs)"
468 f9faf9c3 René Nussbaumer
        elif isinstance(arg, cli.ArgOs):
469 f9faf9c3 René Nussbaumer
          choices = "$(_ganeti_os)"
470 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgFile):
471 4f3d5b76 Michael Hanselmann
          choices = ""
472 4f3d5b76 Michael Hanselmann
          compgenargs.append("-f")
473 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgCommand):
474 4f3d5b76 Michael Hanselmann
          choices = ""
475 4f3d5b76 Michael Hanselmann
          compgenargs.append("-c")
476 83ec7961 Michael Hanselmann
        elif isinstance(arg, cli.ArgHost):
477 83ec7961 Michael Hanselmann
          choices = ""
478 83ec7961 Michael Hanselmann
          compgenargs.append("-A hostname")
479 4f3d5b76 Michael Hanselmann
        else:
480 4f3d5b76 Michael Hanselmann
          raise Exception("Unknown argument type %r" % arg)
481 4f3d5b76 Michael Hanselmann
482 4f3d5b76 Michael Hanselmann
        if arg.min == 1 and arg.max == 1:
483 4f3d5b76 Michael Hanselmann
          cmpcode = """"$arg_idx" == %d""" % (last_arg_end)
484 8adfb141 Michael Hanselmann
        elif arg.max is None:
485 8adfb141 Michael Hanselmann
          cmpcode = """"$arg_idx" -ge %d""" % (last_arg_end)
486 5431eff1 Michael Hanselmann
        elif arg.min <= arg.max:
487 4f3d5b76 Michael Hanselmann
          cmpcode = (""""$arg_idx" -ge %d && "$arg_idx" -lt %d""" %
488 4f3d5b76 Michael Hanselmann
                     (last_arg_end, last_arg_end + arg.max))
489 4f3d5b76 Michael Hanselmann
        else:
490 4f3d5b76 Michael Hanselmann
          raise Exception("Unable to generate argument position condition")
491 4f3d5b76 Michael Hanselmann
492 4f3d5b76 Michael Hanselmann
        last_arg_end += arg.min
493 4f3d5b76 Michael Hanselmann
494 4f3d5b76 Michael Hanselmann
        if choices or compgenargs:
495 4f3d5b76 Michael Hanselmann
          if wrote_arg:
496 4f3d5b76 Michael Hanselmann
            condcmd = "elif"
497 4f3d5b76 Michael Hanselmann
          else:
498 4f3d5b76 Michael Hanselmann
            condcmd = "if"
499 4f3d5b76 Michael Hanselmann
500 4f3d5b76 Michael Hanselmann
          sw.Write("""%s [[ %s ]]; then""", condcmd, cmpcode)
501 4f3d5b76 Michael Hanselmann
          sw.IncIndent()
502 4f3d5b76 Michael Hanselmann
          try:
503 4f3d5b76 Michael Hanselmann
            if choices:
504 4f3d5b76 Michael Hanselmann
              sw.Write("""choices="$choices "%s""", choices)
505 4f3d5b76 Michael Hanselmann
            if compgenargs:
506 8adfb141 Michael Hanselmann
              sw.Write("compgenargs=%s",
507 8adfb141 Michael Hanselmann
                       utils.ShellQuote(" ".join(compgenargs)))
508 4f3d5b76 Michael Hanselmann
          finally:
509 4f3d5b76 Michael Hanselmann
            sw.DecIndent()
510 4f3d5b76 Michael Hanselmann
511 4f3d5b76 Michael Hanselmann
          wrote_arg = True
512 4f3d5b76 Michael Hanselmann
513 4f3d5b76 Michael Hanselmann
      if wrote_arg:
514 4f3d5b76 Michael Hanselmann
        sw.Write("fi")
515 4f3d5b76 Michael Hanselmann
516 4f3d5b76 Michael Hanselmann
    if self.args:
517 4f3d5b76 Michael Hanselmann
      WriteCompReply(sw, """-W "$choices" $compgenargs""")
518 4f3d5b76 Michael Hanselmann
    else:
519 4f3d5b76 Michael Hanselmann
      # $compgenargs exists only if there are arguments
520 4f3d5b76 Michael Hanselmann
      WriteCompReply(sw, '-W "$choices"')
521 4f3d5b76 Michael Hanselmann
522 4f3d5b76 Michael Hanselmann
  def WriteTo(self, sw):
523 4f3d5b76 Michael Hanselmann
    self._FindFirstArgument(sw)
524 4f3d5b76 Michael Hanselmann
    self._CompleteOptionValues(sw)
525 4f3d5b76 Michael Hanselmann
    self._CompleteArguments(sw)
526 4f3d5b76 Michael Hanselmann
527 4f3d5b76 Michael Hanselmann
528 e80aeb89 Michael Hanselmann
def WriteCompletion(sw, scriptname, funcname, support_debug,
529 4f3d5b76 Michael Hanselmann
                    commands=None,
530 4f3d5b76 Michael Hanselmann
                    opts=None, args=None):
531 4f3d5b76 Michael Hanselmann
  """Writes the completion code for one command.
532 4f3d5b76 Michael Hanselmann
533 4f3d5b76 Michael Hanselmann
  @type sw: ShellWriter
534 4f3d5b76 Michael Hanselmann
  @param sw: Script writer
535 4f3d5b76 Michael Hanselmann
  @type scriptname: string
536 4f3d5b76 Michael Hanselmann
  @param scriptname: Name of command line program
537 4f3d5b76 Michael Hanselmann
  @type funcname: string
538 4f3d5b76 Michael Hanselmann
  @param funcname: Shell function name
539 4f3d5b76 Michael Hanselmann
  @type commands: list
540 4f3d5b76 Michael Hanselmann
  @param commands: List of all subcommands in this program
541 4f3d5b76 Michael Hanselmann
542 4f3d5b76 Michael Hanselmann
  """
543 34dfced1 Michael Hanselmann
  sw.Write("%s() {", funcname)
544 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
545 4f3d5b76 Michael Hanselmann
  try:
546 34dfced1 Michael Hanselmann
    sw.Write("local "
547 632d5090 Michael Hanselmann
             ' cur="${COMP_WORDS[COMP_CWORD]}"'
548 34dfced1 Michael Hanselmann
             ' prev="${COMP_WORDS[COMP_CWORD-1]}"'
549 34dfced1 Michael Hanselmann
             ' i first_arg_idx choices compgenargs arg_idx optcur')
550 580ef58d Michael Hanselmann
551 e80aeb89 Michael Hanselmann
    if support_debug:
552 e80aeb89 Michael Hanselmann
      sw.Write("_gnt_log cur=\"$cur\" prev=\"$prev\"")
553 e80aeb89 Michael Hanselmann
      sw.Write("[[ -n \"$GANETI_COMPL_LOG\" ]] &&"
554 e80aeb89 Michael Hanselmann
               " _gnt_log \"$(set | grep ^COMP_)\"")
555 4f3d5b76 Michael Hanselmann
556 4f3d5b76 Michael Hanselmann
    sw.Write("COMPREPLY=()")
557 4f3d5b76 Michael Hanselmann
558 4f3d5b76 Michael Hanselmann
    if opts is not None and args is not None:
559 4f3d5b76 Michael Hanselmann
      assert not commands
560 e80aeb89 Michael Hanselmann
      CompletionWriter(0, opts, args, support_debug).WriteTo(sw)
561 4f3d5b76 Michael Hanselmann
562 4f3d5b76 Michael Hanselmann
    else:
563 4f3d5b76 Michael Hanselmann
      sw.Write("""if [[ "$COMP_CWORD" == 1 ]]; then""")
564 4f3d5b76 Michael Hanselmann
      sw.IncIndent()
565 4f3d5b76 Michael Hanselmann
      try:
566 4f3d5b76 Michael Hanselmann
        # Complete the command name
567 4f3d5b76 Michael Hanselmann
        WriteCompReply(sw,
568 4f3d5b76 Michael Hanselmann
                       ("-W %s" %
569 4f3d5b76 Michael Hanselmann
                        utils.ShellQuote(" ".join(sorted(commands.keys())))))
570 4f3d5b76 Michael Hanselmann
      finally:
571 4f3d5b76 Michael Hanselmann
        sw.DecIndent()
572 4f3d5b76 Michael Hanselmann
      sw.Write("fi")
573 4f3d5b76 Michael Hanselmann
574 b33cad4a Michael Hanselmann
      # Group commands by arguments and options
575 b33cad4a Michael Hanselmann
      grouped_cmds = {}
576 f5ce7613 Iustin Pop
      for cmd, (_, argdef, optdef, _, _) in commands.items():
577 4f3d5b76 Michael Hanselmann
        if not (argdef or optdef):
578 4f3d5b76 Michael Hanselmann
          continue
579 b33cad4a Michael Hanselmann
        grouped_cmds.setdefault((tuple(argdef), tuple(optdef)), set()).add(cmd)
580 4f3d5b76 Michael Hanselmann
581 b33cad4a Michael Hanselmann
      # We're doing options and arguments to commands
582 b33cad4a Michael Hanselmann
      sw.Write("""case "${COMP_WORDS[1]}" in""")
583 f5ce7613 Iustin Pop
      sort_grouped = sorted(grouped_cmds.items(),
584 f5ce7613 Iustin Pop
                            key=lambda (_, y): sorted(y)[0])
585 f5ce7613 Iustin Pop
      for ((argdef, optdef), cmds) in sort_grouped:
586 b33cad4a Michael Hanselmann
        assert argdef or optdef
587 b33cad4a Michael Hanselmann
        sw.Write("%s)", "|".join(map(utils.ShellQuote, sorted(cmds))))
588 4f3d5b76 Michael Hanselmann
        sw.IncIndent()
589 4f3d5b76 Michael Hanselmann
        try:
590 e80aeb89 Michael Hanselmann
          CompletionWriter(1, optdef, argdef, support_debug).WriteTo(sw)
591 4f3d5b76 Michael Hanselmann
        finally:
592 4f3d5b76 Michael Hanselmann
          sw.DecIndent()
593 4f3d5b76 Michael Hanselmann
        sw.Write(";;")
594 4f3d5b76 Michael Hanselmann
      sw.Write("esac")
595 4f3d5b76 Michael Hanselmann
  finally:
596 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
597 4f3d5b76 Michael Hanselmann
  sw.Write("}")
598 4f3d5b76 Michael Hanselmann
599 4f3d5b76 Michael Hanselmann
  sw.Write("complete -F %s -o filenames %s",
600 4f3d5b76 Michael Hanselmann
           utils.ShellQuote(funcname),
601 4f3d5b76 Michael Hanselmann
           utils.ShellQuote(scriptname))
602 4f3d5b76 Michael Hanselmann
603 4f3d5b76 Michael Hanselmann
604 4f3d5b76 Michael Hanselmann
def GetFunctionName(name):
605 4f3d5b76 Michael Hanselmann
  return "_" + re.sub(r"[^a-z0-9]+", "_", name.lower())
606 4f3d5b76 Michael Hanselmann
607 4f3d5b76 Michael Hanselmann
608 4f3d5b76 Michael Hanselmann
def GetCommands(filename, module):
609 4f3d5b76 Michael Hanselmann
  """Returns the commands defined in a module.
610 4f3d5b76 Michael Hanselmann
611 4f3d5b76 Michael Hanselmann
  Aliases are also added as commands.
612 4f3d5b76 Michael Hanselmann
613 4f3d5b76 Michael Hanselmann
  """
614 4f3d5b76 Michael Hanselmann
  try:
615 4f3d5b76 Michael Hanselmann
    commands = getattr(module, "commands")
616 f59418db Michael Hanselmann
  except AttributeError:
617 4f3d5b76 Michael Hanselmann
    raise Exception("Script %s doesn't have 'commands' attribute" %
618 4f3d5b76 Michael Hanselmann
                    filename)
619 4f3d5b76 Michael Hanselmann
620 1f4e391b Michael Hanselmann
  # Add the implicit "--help" option
621 1f4e391b Michael Hanselmann
  help_option = cli.cli_option("-h", "--help", default=False,
622 1f4e391b Michael Hanselmann
                               action="store_true")
623 1f4e391b Michael Hanselmann
624 5786c087 Michael Hanselmann
  for name, (_, _, optdef, _, _) in commands.items():
625 1f4e391b Michael Hanselmann
    if help_option not in optdef:
626 1f4e391b Michael Hanselmann
      optdef.append(help_option)
627 5786c087 Michael Hanselmann
    for opt in cli.COMMON_OPTS:
628 5786c087 Michael Hanselmann
      if opt in optdef:
629 5786c087 Michael Hanselmann
        raise Exception("Common option '%s' listed for command '%s' in %s" %
630 5786c087 Michael Hanselmann
                        (opt, name, filename))
631 5786c087 Michael Hanselmann
      optdef.append(opt)
632 1f4e391b Michael Hanselmann
633 4f3d5b76 Michael Hanselmann
  # Use aliases
634 4f3d5b76 Michael Hanselmann
  aliases = getattr(module, "aliases", {})
635 4f3d5b76 Michael Hanselmann
  if aliases:
636 4f3d5b76 Michael Hanselmann
    commands = commands.copy()
637 f5ce7613 Iustin Pop
    for name, target in aliases.items():
638 4f3d5b76 Michael Hanselmann
      commands[name] = commands[target]
639 4f3d5b76 Michael Hanselmann
640 4f3d5b76 Michael Hanselmann
  return commands
641 4f3d5b76 Michael Hanselmann
642 4f3d5b76 Michael Hanselmann
643 ada0e680 Iustin Pop
def HaskellOptToOptParse(opts, kind):
644 ada0e680 Iustin Pop
  """Converts a Haskell options to Python cli_options.
645 ada0e680 Iustin Pop
646 ada0e680 Iustin Pop
  @type opts: string
647 ada0e680 Iustin Pop
  @param opts: comma-separated string with short and long options
648 ada0e680 Iustin Pop
  @type kind: string
649 ada0e680 Iustin Pop
  @param kind: type generated by Common.hs/complToText; needs to be
650 ada0e680 Iustin Pop
      kept in sync
651 ada0e680 Iustin Pop
652 ada0e680 Iustin Pop
  """
653 ada0e680 Iustin Pop
  # pylint: disable=W0142
654 ada0e680 Iustin Pop
  # since we pass *opts in a number of places
655 ada0e680 Iustin Pop
  opts = opts.split(",")
656 ada0e680 Iustin Pop
  if kind == "none":
657 ada0e680 Iustin Pop
    return cli.cli_option(*opts, action="store_true")
658 f8d01158 Iustin Pop
  elif kind in ["file", "string", "host", "dir", "inetaddr"]:
659 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="string")
660 ecebe9f6 Iustin Pop
  elif kind == "integer":
661 ecebe9f6 Iustin Pop
    return cli.cli_option(*opts, type="int")
662 ecebe9f6 Iustin Pop
  elif kind == "float":
663 ecebe9f6 Iustin Pop
    return cli.cli_option(*opts, type="float")
664 ada0e680 Iustin Pop
  elif kind == "onegroup":
665 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="string",
666 ada0e680 Iustin Pop
                           completion_suggest=cli.OPT_COMPL_ONE_NODEGROUP)
667 ada0e680 Iustin Pop
  elif kind == "onenode":
668 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="string",
669 ada0e680 Iustin Pop
                          completion_suggest=cli.OPT_COMPL_ONE_NODE)
670 ada0e680 Iustin Pop
  elif kind == "manyinstances":
671 ada0e680 Iustin Pop
    # FIXME: no support for many instances
672 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="string")
673 fad06963 Iustin Pop
  elif kind.startswith("choices="):
674 fad06963 Iustin Pop
    choices = kind[len("choices="):].split(",")
675 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="choice", choices=choices)
676 ada0e680 Iustin Pop
  else:
677 ada0e680 Iustin Pop
    # FIXME: there are many other currently unused completion types,
678 ada0e680 Iustin Pop
    # should be added on an as-needed basis
679 fad06963 Iustin Pop
    raise Exception("Unhandled option kind '%s'" % kind)
680 fad06963 Iustin Pop
681 fad06963 Iustin Pop
682 fad06963 Iustin Pop
#: serialised kind to arg type
683 fad06963 Iustin Pop
_ARG_MAP = {
684 46118ed2 Iustin Pop
  "choices": cli.ArgChoice,
685 fad06963 Iustin Pop
  "command": cli.ArgCommand,
686 fad06963 Iustin Pop
  "file": cli.ArgFile,
687 fad06963 Iustin Pop
  "host": cli.ArgHost,
688 fad06963 Iustin Pop
  "jobid": cli.ArgJobId,
689 fad06963 Iustin Pop
  "onegroup": cli.ArgGroup,
690 fad06963 Iustin Pop
  "oneinstance": cli.ArgInstance,
691 fad06963 Iustin Pop
  "onenode": cli.ArgNode,
692 fad06963 Iustin Pop
  "oneos": cli.ArgOs,
693 fad06963 Iustin Pop
  "string": cli.ArgUnknown,
694 46118ed2 Iustin Pop
  "suggests": cli.ArgSuggest,
695 fad06963 Iustin Pop
  }
696 fad06963 Iustin Pop
697 fad06963 Iustin Pop
698 fad06963 Iustin Pop
def HaskellArgToCliArg(kind, min_cnt, max_cnt):
699 fad06963 Iustin Pop
  """Converts a Haskell options to Python _Argument.
700 fad06963 Iustin Pop
701 fad06963 Iustin Pop
  @type kind: string
702 fad06963 Iustin Pop
  @param kind: type generated by Common.hs/argComplToText; needs to be
703 fad06963 Iustin Pop
      kept in sync
704 fad06963 Iustin Pop
705 fad06963 Iustin Pop
  """
706 fad06963 Iustin Pop
  min_cnt = int(min_cnt)
707 fad06963 Iustin Pop
  if max_cnt == "none":
708 fad06963 Iustin Pop
    max_cnt = None
709 fad06963 Iustin Pop
  else:
710 fad06963 Iustin Pop
    max_cnt = int(max_cnt)
711 fad06963 Iustin Pop
  # pylint: disable=W0142
712 fad06963 Iustin Pop
  # since we pass **kwargs
713 fad06963 Iustin Pop
  kwargs = {"min": min_cnt, "max": max_cnt}
714 fad06963 Iustin Pop
715 fad06963 Iustin Pop
  if kind.startswith("choices=") or kind.startswith("suggest="):
716 fad06963 Iustin Pop
    (kind, choices) = kind.split("=", 1)
717 fad06963 Iustin Pop
    kwargs["choices"] = choices.split(",")
718 fad06963 Iustin Pop
719 fad06963 Iustin Pop
  if kind not in _ARG_MAP:
720 fad06963 Iustin Pop
    raise Exception("Unhandled argument kind '%s'" % kind)
721 fad06963 Iustin Pop
  else:
722 fad06963 Iustin Pop
    return _ARG_MAP[kind](**kwargs)
723 ada0e680 Iustin Pop
724 ada0e680 Iustin Pop
725 24476fa0 Iustin Pop
def ParseHaskellOptsArgs(script, output):
726 24476fa0 Iustin Pop
  """Computes list of options/arguments from help-completion output.
727 ada0e680 Iustin Pop
728 ada0e680 Iustin Pop
  """
729 ada0e680 Iustin Pop
  cli_opts = []
730 24476fa0 Iustin Pop
  cli_args = []
731 ada0e680 Iustin Pop
  for line in output.splitlines():
732 fad06963 Iustin Pop
    v = line.split(None)
733 fad06963 Iustin Pop
    exc = lambda msg: Exception("Invalid %s output from %s: %s" %
734 fad06963 Iustin Pop
                                (msg, script, v))
735 fad06963 Iustin Pop
    if len(v) < 2:
736 fad06963 Iustin Pop
      raise exc("help completion")
737 fad06963 Iustin Pop
    if v[0].startswith("-"):
738 fad06963 Iustin Pop
      if len(v) != 2:
739 fad06963 Iustin Pop
        raise exc("option format")
740 fad06963 Iustin Pop
      (opts, kind) = v
741 fad06963 Iustin Pop
      cli_opts.append(HaskellOptToOptParse(opts, kind))
742 fad06963 Iustin Pop
    else:
743 fad06963 Iustin Pop
      if len(v) != 3:
744 fad06963 Iustin Pop
        raise exc("argument format")
745 fad06963 Iustin Pop
      (kind, min_cnt, max_cnt) = v
746 24476fa0 Iustin Pop
      cli_args.append(HaskellArgToCliArg(kind, min_cnt, max_cnt))
747 24476fa0 Iustin Pop
  return (cli_opts, cli_args)
748 24476fa0 Iustin Pop
749 24476fa0 Iustin Pop
750 24476fa0 Iustin Pop
def WriteHaskellCompletion(sw, script, htools=True, debug=True):
751 24476fa0 Iustin Pop
  """Generates completion information for a Haskell program.
752 24476fa0 Iustin Pop
753 24476fa0 Iustin Pop
  This converts completion info from a Haskell program into 'fake'
754 24476fa0 Iustin Pop
  cli_opts and then builds completion for them.
755 24476fa0 Iustin Pop
756 24476fa0 Iustin Pop
  """
757 24476fa0 Iustin Pop
  if htools:
758 24476fa0 Iustin Pop
    cmd = "./htools/htools"
759 24476fa0 Iustin Pop
    env = {"HTOOLS": script}
760 24476fa0 Iustin Pop
    script_name = script
761 24476fa0 Iustin Pop
    func_name = "htools_%s" % script
762 24476fa0 Iustin Pop
  else:
763 24476fa0 Iustin Pop
    cmd = "./" + script
764 24476fa0 Iustin Pop
    env = {}
765 24476fa0 Iustin Pop
    script_name = os.path.basename(script)
766 24476fa0 Iustin Pop
    func_name = script_name
767 24476fa0 Iustin Pop
  func_name = GetFunctionName(func_name)
768 24476fa0 Iustin Pop
  output = utils.RunCmd([cmd, "--help-completion"], env=env, cwd=".").output
769 24476fa0 Iustin Pop
  (opts, args) = ParseHaskellOptsArgs(script_name, output)
770 24476fa0 Iustin Pop
  WriteCompletion(sw, script_name, func_name, debug, opts=opts, args=args)
771 24476fa0 Iustin Pop
772 24476fa0 Iustin Pop
773 24476fa0 Iustin Pop
def WriteHaskellCmdCompletion(sw, script, debug=True):
774 24476fa0 Iustin Pop
  """Generates completion information for a Haskell multi-command program.
775 24476fa0 Iustin Pop
776 24476fa0 Iustin Pop
  This gathers the list of commands from a Haskell program and
777 24476fa0 Iustin Pop
  computes the list of commands available, then builds the sub-command
778 24476fa0 Iustin Pop
  list of options/arguments for each command, using that for building
779 24476fa0 Iustin Pop
  a unified help output.
780 24476fa0 Iustin Pop
781 24476fa0 Iustin Pop
  """
782 24476fa0 Iustin Pop
  cmd = "./" + script
783 24476fa0 Iustin Pop
  script_name = os.path.basename(script)
784 24476fa0 Iustin Pop
  func_name = script_name
785 24476fa0 Iustin Pop
  func_name = GetFunctionName(func_name)
786 24476fa0 Iustin Pop
  output = utils.RunCmd([cmd, "--help-completion"], cwd=".").output
787 24476fa0 Iustin Pop
  commands = {}
788 24476fa0 Iustin Pop
  lines = output.splitlines()
789 24476fa0 Iustin Pop
  if len(lines) != 1:
790 24476fa0 Iustin Pop
    raise Exception("Invalid lines in multi-command mode: %s" % str(lines))
791 24476fa0 Iustin Pop
  v = lines[0].split(None)
792 24476fa0 Iustin Pop
  exc = lambda msg: Exception("Invalid %s output from %s: %s" %
793 24476fa0 Iustin Pop
                              (msg, script, v))
794 24476fa0 Iustin Pop
  if len(v) != 3:
795 24476fa0 Iustin Pop
    raise exc("help completion in multi-command mode")
796 24476fa0 Iustin Pop
  if not v[0].startswith("choices="):
797 24476fa0 Iustin Pop
    raise exc("invalid format in multi-command mode '%s'" % v[0])
798 24476fa0 Iustin Pop
  for subcmd in v[0][len("choices="):].split(","):
799 24476fa0 Iustin Pop
    output = utils.RunCmd([cmd, subcmd, "--help-completion"], cwd=".").output
800 24476fa0 Iustin Pop
    (opts, args) = ParseHaskellOptsArgs(script, output)
801 24476fa0 Iustin Pop
    commands[subcmd] = (None, args, opts, None, None)
802 24476fa0 Iustin Pop
  WriteCompletion(sw, script_name, func_name, debug, commands=commands)
803 ada0e680 Iustin Pop
804 ada0e680 Iustin Pop
805 4f3d5b76 Michael Hanselmann
def main():
806 e80aeb89 Michael Hanselmann
  parser = optparse.OptionParser(usage="%prog [--compact]")
807 e80aeb89 Michael Hanselmann
  parser.add_option("--compact", action="store_true",
808 e80aeb89 Michael Hanselmann
                    help=("Don't indent output and don't include debugging"
809 e80aeb89 Michael Hanselmann
                          " facilities"))
810 e80aeb89 Michael Hanselmann
811 e80aeb89 Michael Hanselmann
  options, args = parser.parse_args()
812 e80aeb89 Michael Hanselmann
  if args:
813 e80aeb89 Michael Hanselmann
    parser.error("Wrong number of arguments")
814 e80aeb89 Michael Hanselmann
815 24476fa0 Iustin Pop
  # Whether to build debug version of completion script
816 24476fa0 Iustin Pop
  debug = not options.compact
817 24476fa0 Iustin Pop
818 4f3d5b76 Michael Hanselmann
  buf = StringIO()
819 24476fa0 Iustin Pop
  sw = utils.ShellWriter(buf, indent=debug)
820 4f3d5b76 Michael Hanselmann
821 893ad76d Michael Hanselmann
  # Remember original state of extglob and enable it (required for pattern
822 893ad76d Michael Hanselmann
  # matching; must be enabled while parsing script)
823 893ad76d Michael Hanselmann
  sw.Write("gnt_shopt_extglob=$(shopt -p extglob || :)")
824 893ad76d Michael Hanselmann
  sw.Write("shopt -s extglob")
825 893ad76d Michael Hanselmann
826 24476fa0 Iustin Pop
  WritePreamble(sw, debug)
827 4f3d5b76 Michael Hanselmann
828 4f3d5b76 Michael Hanselmann
  # gnt-* scripts
829 4f3d5b76 Michael Hanselmann
  for scriptname in _autoconf.GNT_SCRIPTS:
830 4f3d5b76 Michael Hanselmann
    filename = "scripts/%s" % scriptname
831 4f3d5b76 Michael Hanselmann
832 24476fa0 Iustin Pop
    WriteCompletion(sw, scriptname, GetFunctionName(scriptname), debug,
833 d6f5892b Michael Hanselmann
                    commands=GetCommands(filename,
834 e948770c Michael Hanselmann
                                         build.LoadModule(filename)))
835 4f3d5b76 Michael Hanselmann
836 4f3d5b76 Michael Hanselmann
  # Burnin script
837 e948770c Michael Hanselmann
  burnin = build.LoadModule("tools/burnin")
838 13718ded Michael Hanselmann
  WriteCompletion(sw, "%s/burnin" % pathutils.TOOLSDIR, "_ganeti_burnin",
839 24476fa0 Iustin Pop
                  debug,
840 4f3d5b76 Michael Hanselmann
                  opts=burnin.OPTIONS, args=burnin.ARGUMENTS)
841 4f3d5b76 Michael Hanselmann
842 46118ed2 Iustin Pop
  # ganeti-cleaner
843 46118ed2 Iustin Pop
  WriteHaskellCompletion(sw, "daemons/ganeti-cleaner", htools=False,
844 46118ed2 Iustin Pop
                         debug=not options.compact)
845 46118ed2 Iustin Pop
846 ada0e680 Iustin Pop
  # htools, if enabled
847 ada0e680 Iustin Pop
  if _autoconf.HTOOLS:
848 ada0e680 Iustin Pop
    for script in _autoconf.HTOOLS_PROGS:
849 24476fa0 Iustin Pop
      WriteHaskellCompletion(sw, script, htools=True, debug=debug)
850 ada0e680 Iustin Pop
851 f8d01158 Iustin Pop
  # ganeti-confd, if enabled
852 f8d01158 Iustin Pop
  if _autoconf.ENABLE_CONFD:
853 f8d01158 Iustin Pop
    WriteHaskellCompletion(sw, "htools/ganeti-confd", htools=False,
854 24476fa0 Iustin Pop
                           debug=debug)
855 f8d01158 Iustin Pop
856 893ad76d Michael Hanselmann
  # Reset extglob to original value
857 893ad76d Michael Hanselmann
  sw.Write("[[ -n \"$gnt_shopt_extglob\" ]] && $gnt_shopt_extglob")
858 893ad76d Michael Hanselmann
  sw.Write("unset gnt_shopt_extglob")
859 893ad76d Michael Hanselmann
860 4f3d5b76 Michael Hanselmann
  print buf.getvalue()
861 4f3d5b76 Michael Hanselmann
862 4f3d5b76 Michael Hanselmann
863 4f3d5b76 Michael Hanselmann
if __name__ == "__main__":
864 4f3d5b76 Michael Hanselmann
  main()