Statistics
| Branch: | Tag: | Revision:

root / autotools / build-bash-completion @ 3add7574

History | View | Annotate | Download (26 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 e0f470ac Iustin Pop
    sw.Write(("local jlist=($( shopt -s nullglob &&"
107 e0f470ac Iustin Pop
              " cd %s 2>/dev/null && echo job-* || : ))"),
108 13718ded Michael Hanselmann
             utils.ShellQuote(pathutils.QUEUE_DIR))
109 e0f470ac Iustin Pop
    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 b954f097 Constantinos Venetsanopoulos
        elif suggest == cli.OPT_COMPL_ONE_EXTSTORAGE:
361 b954f097 Constantinos Venetsanopoulos
          WriteCompReply(sw, "-W \"$(_ganeti_extstorage)\"", cur=cur)
362 63d44c55 Michael Hanselmann
        elif suggest == cli.OPT_COMPL_ONE_IALLOCATOR:
363 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W \"$(_ganeti_iallocator)\"", cur=cur)
364 36e247e1 Guido Trotter
        elif suggest == cli.OPT_COMPL_ONE_NODEGROUP:
365 36e247e1 Guido Trotter
          WriteCompReply(sw, "-W \"$(_ganeti_nodegroup)\"", cur=cur)
366 4e4b6b7a Apollon Oikonomopoulos
        elif suggest == cli.OPT_COMPL_ONE_NETWORK:
367 4e4b6b7a Apollon Oikonomopoulos
          WriteCompReply(sw, "-W \"$(_ganeti_network)\"", cur=cur)
368 2d3ed64b Michael Hanselmann
        elif suggest == cli.OPT_COMPL_INST_ADD_NODES:
369 2d3ed64b Michael Hanselmann
          sw.Write("local tmp= node1= pfx= curvalue=\"${optcur#*:}\"")
370 2d3ed64b Michael Hanselmann
371 2d3ed64b Michael Hanselmann
          sw.Write("if [[ \"$optcur\" == *:* ]]; then")
372 2d3ed64b Michael Hanselmann
          sw.IncIndent()
373 2d3ed64b Michael Hanselmann
          try:
374 2d3ed64b Michael Hanselmann
            sw.Write("node1=\"${optcur%%:*}\"")
375 2d3ed64b Michael Hanselmann
376 2d3ed64b Michael Hanselmann
            sw.Write("if [[ \"$COMP_WORDBREAKS\" != *:* ]]; then")
377 2d3ed64b Michael Hanselmann
            sw.IncIndent()
378 2d3ed64b Michael Hanselmann
            try:
379 2d3ed64b Michael Hanselmann
              sw.Write("pfx=\"$node1:\"")
380 2d3ed64b Michael Hanselmann
            finally:
381 2d3ed64b Michael Hanselmann
              sw.DecIndent()
382 2d3ed64b Michael Hanselmann
            sw.Write("fi")
383 2d3ed64b Michael Hanselmann
          finally:
384 2d3ed64b Michael Hanselmann
            sw.DecIndent()
385 2d3ed64b Michael Hanselmann
          sw.Write("fi")
386 2d3ed64b Michael Hanselmann
387 e80aeb89 Michael Hanselmann
          if self.support_debug:
388 e80aeb89 Michael Hanselmann
            sw.Write("_gnt_log pfx=\"'$pfx'\" curvalue=\"'$curvalue'\""
389 e80aeb89 Michael Hanselmann
                     " node1=\"'$node1'\"")
390 2d3ed64b Michael Hanselmann
391 2d3ed64b Michael Hanselmann
          sw.Write("for i in $(_ganeti_nodes); do")
392 2d3ed64b Michael Hanselmann
          sw.IncIndent()
393 2d3ed64b Michael Hanselmann
          try:
394 2d3ed64b Michael Hanselmann
            sw.Write("if [[ -z \"$node1\" ]]; then")
395 2d3ed64b Michael Hanselmann
            sw.IncIndent()
396 2d3ed64b Michael Hanselmann
            try:
397 2d3ed64b Michael Hanselmann
              sw.Write("tmp=\"$tmp $i $i:\"")
398 2d3ed64b Michael Hanselmann
            finally:
399 2d3ed64b Michael Hanselmann
              sw.DecIndent()
400 2d3ed64b Michael Hanselmann
            sw.Write("elif [[ \"$i\" != \"$node1\" ]]; then")
401 2d3ed64b Michael Hanselmann
            sw.IncIndent()
402 2d3ed64b Michael Hanselmann
            try:
403 2d3ed64b Michael Hanselmann
              sw.Write("tmp=\"$tmp $i\"")
404 2d3ed64b Michael Hanselmann
            finally:
405 2d3ed64b Michael Hanselmann
              sw.DecIndent()
406 2d3ed64b Michael Hanselmann
            sw.Write("fi")
407 2d3ed64b Michael Hanselmann
          finally:
408 2d3ed64b Michael Hanselmann
            sw.DecIndent()
409 2d3ed64b Michael Hanselmann
          sw.Write("done")
410 2d3ed64b Michael Hanselmann
411 2d3ed64b Michael Hanselmann
          WriteCompReply(sw, "-P \"$pfx\" -W \"$tmp\"", cur="\"$curvalue\"")
412 63d44c55 Michael Hanselmann
        else:
413 63d44c55 Michael Hanselmann
          WriteCompReply(sw, "-W %s" % utils.ShellQuote(suggest), cur=cur)
414 d4b94fe8 Michael Hanselmann
      finally:
415 d4b94fe8 Michael Hanselmann
        sw.DecIndent()
416 d4b94fe8 Michael Hanselmann
417 d4b94fe8 Michael Hanselmann
      wrote_opt = True
418 d4b94fe8 Michael Hanselmann
419 d4b94fe8 Michael Hanselmann
    if wrote_opt:
420 d4b94fe8 Michael Hanselmann
      sw.Write("fi")
421 d4b94fe8 Michael Hanselmann
422 d4b94fe8 Michael Hanselmann
    return
423 632d5090 Michael Hanselmann
424 4f3d5b76 Michael Hanselmann
  def _CompleteArguments(self, sw):
425 4f3d5b76 Michael Hanselmann
    if not (self.opts or self.args):
426 4f3d5b76 Michael Hanselmann
      return
427 4f3d5b76 Michael Hanselmann
428 4f3d5b76 Michael Hanselmann
    all_option_names = []
429 4f3d5b76 Michael Hanselmann
    for opt in self.opts:
430 4f3d5b76 Michael Hanselmann
      all_option_names.extend(opt.all_names)
431 4f3d5b76 Michael Hanselmann
    all_option_names.sort()
432 4f3d5b76 Michael Hanselmann
433 4f3d5b76 Michael Hanselmann
    # List options if no argument has been specified yet
434 4f3d5b76 Michael Hanselmann
    sw.Write("_ganeti_list_options %s",
435 4f3d5b76 Michael Hanselmann
             utils.ShellQuote(" ".join(all_option_names)))
436 4f3d5b76 Michael Hanselmann
437 4f3d5b76 Michael Hanselmann
    if self.args:
438 4f3d5b76 Michael Hanselmann
      last_idx = len(self.args) - 1
439 4f3d5b76 Michael Hanselmann
      last_arg_end = 0
440 4f3d5b76 Michael Hanselmann
      varlen_arg_idx = None
441 4f3d5b76 Michael Hanselmann
      wrote_arg = False
442 4f3d5b76 Michael Hanselmann
443 4f3d5b76 Michael Hanselmann
      sw.Write("compgenargs=")
444 4f3d5b76 Michael Hanselmann
445 4f3d5b76 Michael Hanselmann
      for idx, arg in enumerate(self.args):
446 4f3d5b76 Michael Hanselmann
        assert arg.min is not None and arg.min >= 0
447 4f3d5b76 Michael Hanselmann
        assert not (idx < last_idx and arg.max is None)
448 4f3d5b76 Michael Hanselmann
449 4f3d5b76 Michael Hanselmann
        if arg.min != arg.max or arg.max is None:
450 4f3d5b76 Michael Hanselmann
          if varlen_arg_idx is not None:
451 4f3d5b76 Michael Hanselmann
            raise Exception("Only one argument can have a variable length")
452 4f3d5b76 Michael Hanselmann
          varlen_arg_idx = idx
453 4f3d5b76 Michael Hanselmann
454 4f3d5b76 Michael Hanselmann
        compgenargs = []
455 4f3d5b76 Michael Hanselmann
456 4f3d5b76 Michael Hanselmann
        if isinstance(arg, cli.ArgUnknown):
457 4f3d5b76 Michael Hanselmann
          choices = ""
458 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgSuggest):
459 4f3d5b76 Michael Hanselmann
          choices = utils.ShellQuote(" ".join(arg.choices))
460 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgInstance):
461 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_instances)"
462 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgNode):
463 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_nodes)"
464 667dbd6b Adeodato Simo
        elif isinstance(arg, cli.ArgGroup):
465 667dbd6b Adeodato Simo
          choices = "$(_ganeti_nodegroup)"
466 4e4b6b7a Apollon Oikonomopoulos
        elif isinstance(arg, cli.ArgNetwork):
467 4e4b6b7a Apollon Oikonomopoulos
          choices = "$(_ganeti_network)"
468 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgJobId):
469 4f3d5b76 Michael Hanselmann
          choices = "$(_ganeti_jobs)"
470 f9faf9c3 René Nussbaumer
        elif isinstance(arg, cli.ArgOs):
471 f9faf9c3 René Nussbaumer
          choices = "$(_ganeti_os)"
472 b954f097 Constantinos Venetsanopoulos
        elif isinstance(arg, cli.ArgExtStorage):
473 b954f097 Constantinos Venetsanopoulos
          choices = "$(_ganeti_extstorage)"
474 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgFile):
475 4f3d5b76 Michael Hanselmann
          choices = ""
476 4f3d5b76 Michael Hanselmann
          compgenargs.append("-f")
477 4f3d5b76 Michael Hanselmann
        elif isinstance(arg, cli.ArgCommand):
478 4f3d5b76 Michael Hanselmann
          choices = ""
479 4f3d5b76 Michael Hanselmann
          compgenargs.append("-c")
480 83ec7961 Michael Hanselmann
        elif isinstance(arg, cli.ArgHost):
481 83ec7961 Michael Hanselmann
          choices = ""
482 83ec7961 Michael Hanselmann
          compgenargs.append("-A hostname")
483 4f3d5b76 Michael Hanselmann
        else:
484 4f3d5b76 Michael Hanselmann
          raise Exception("Unknown argument type %r" % arg)
485 4f3d5b76 Michael Hanselmann
486 4f3d5b76 Michael Hanselmann
        if arg.min == 1 and arg.max == 1:
487 4f3d5b76 Michael Hanselmann
          cmpcode = """"$arg_idx" == %d""" % (last_arg_end)
488 8adfb141 Michael Hanselmann
        elif arg.max is None:
489 8adfb141 Michael Hanselmann
          cmpcode = """"$arg_idx" -ge %d""" % (last_arg_end)
490 5431eff1 Michael Hanselmann
        elif arg.min <= arg.max:
491 4f3d5b76 Michael Hanselmann
          cmpcode = (""""$arg_idx" -ge %d && "$arg_idx" -lt %d""" %
492 4f3d5b76 Michael Hanselmann
                     (last_arg_end, last_arg_end + arg.max))
493 4f3d5b76 Michael Hanselmann
        else:
494 4f3d5b76 Michael Hanselmann
          raise Exception("Unable to generate argument position condition")
495 4f3d5b76 Michael Hanselmann
496 4f3d5b76 Michael Hanselmann
        last_arg_end += arg.min
497 4f3d5b76 Michael Hanselmann
498 4f3d5b76 Michael Hanselmann
        if choices or compgenargs:
499 4f3d5b76 Michael Hanselmann
          if wrote_arg:
500 4f3d5b76 Michael Hanselmann
            condcmd = "elif"
501 4f3d5b76 Michael Hanselmann
          else:
502 4f3d5b76 Michael Hanselmann
            condcmd = "if"
503 4f3d5b76 Michael Hanselmann
504 4f3d5b76 Michael Hanselmann
          sw.Write("""%s [[ %s ]]; then""", condcmd, cmpcode)
505 4f3d5b76 Michael Hanselmann
          sw.IncIndent()
506 4f3d5b76 Michael Hanselmann
          try:
507 4f3d5b76 Michael Hanselmann
            if choices:
508 4f3d5b76 Michael Hanselmann
              sw.Write("""choices="$choices "%s""", choices)
509 4f3d5b76 Michael Hanselmann
            if compgenargs:
510 8adfb141 Michael Hanselmann
              sw.Write("compgenargs=%s",
511 8adfb141 Michael Hanselmann
                       utils.ShellQuote(" ".join(compgenargs)))
512 4f3d5b76 Michael Hanselmann
          finally:
513 4f3d5b76 Michael Hanselmann
            sw.DecIndent()
514 4f3d5b76 Michael Hanselmann
515 4f3d5b76 Michael Hanselmann
          wrote_arg = True
516 4f3d5b76 Michael Hanselmann
517 4f3d5b76 Michael Hanselmann
      if wrote_arg:
518 4f3d5b76 Michael Hanselmann
        sw.Write("fi")
519 4f3d5b76 Michael Hanselmann
520 4f3d5b76 Michael Hanselmann
    if self.args:
521 4f3d5b76 Michael Hanselmann
      WriteCompReply(sw, """-W "$choices" $compgenargs""")
522 4f3d5b76 Michael Hanselmann
    else:
523 4f3d5b76 Michael Hanselmann
      # $compgenargs exists only if there are arguments
524 4f3d5b76 Michael Hanselmann
      WriteCompReply(sw, '-W "$choices"')
525 4f3d5b76 Michael Hanselmann
526 4f3d5b76 Michael Hanselmann
  def WriteTo(self, sw):
527 4f3d5b76 Michael Hanselmann
    self._FindFirstArgument(sw)
528 4f3d5b76 Michael Hanselmann
    self._CompleteOptionValues(sw)
529 4f3d5b76 Michael Hanselmann
    self._CompleteArguments(sw)
530 4f3d5b76 Michael Hanselmann
531 4f3d5b76 Michael Hanselmann
532 e80aeb89 Michael Hanselmann
def WriteCompletion(sw, scriptname, funcname, support_debug,
533 4f3d5b76 Michael Hanselmann
                    commands=None,
534 4f3d5b76 Michael Hanselmann
                    opts=None, args=None):
535 4f3d5b76 Michael Hanselmann
  """Writes the completion code for one command.
536 4f3d5b76 Michael Hanselmann
537 4f3d5b76 Michael Hanselmann
  @type sw: ShellWriter
538 4f3d5b76 Michael Hanselmann
  @param sw: Script writer
539 4f3d5b76 Michael Hanselmann
  @type scriptname: string
540 4f3d5b76 Michael Hanselmann
  @param scriptname: Name of command line program
541 4f3d5b76 Michael Hanselmann
  @type funcname: string
542 4f3d5b76 Michael Hanselmann
  @param funcname: Shell function name
543 4f3d5b76 Michael Hanselmann
  @type commands: list
544 4f3d5b76 Michael Hanselmann
  @param commands: List of all subcommands in this program
545 4f3d5b76 Michael Hanselmann
546 4f3d5b76 Michael Hanselmann
  """
547 34dfced1 Michael Hanselmann
  sw.Write("%s() {", funcname)
548 4f3d5b76 Michael Hanselmann
  sw.IncIndent()
549 4f3d5b76 Michael Hanselmann
  try:
550 34dfced1 Michael Hanselmann
    sw.Write("local "
551 632d5090 Michael Hanselmann
             ' cur="${COMP_WORDS[COMP_CWORD]}"'
552 34dfced1 Michael Hanselmann
             ' prev="${COMP_WORDS[COMP_CWORD-1]}"'
553 34dfced1 Michael Hanselmann
             ' i first_arg_idx choices compgenargs arg_idx optcur')
554 580ef58d Michael Hanselmann
555 e80aeb89 Michael Hanselmann
    if support_debug:
556 e80aeb89 Michael Hanselmann
      sw.Write("_gnt_log cur=\"$cur\" prev=\"$prev\"")
557 e80aeb89 Michael Hanselmann
      sw.Write("[[ -n \"$GANETI_COMPL_LOG\" ]] &&"
558 e80aeb89 Michael Hanselmann
               " _gnt_log \"$(set | grep ^COMP_)\"")
559 4f3d5b76 Michael Hanselmann
560 4f3d5b76 Michael Hanselmann
    sw.Write("COMPREPLY=()")
561 4f3d5b76 Michael Hanselmann
562 4f3d5b76 Michael Hanselmann
    if opts is not None and args is not None:
563 4f3d5b76 Michael Hanselmann
      assert not commands
564 e80aeb89 Michael Hanselmann
      CompletionWriter(0, opts, args, support_debug).WriteTo(sw)
565 4f3d5b76 Michael Hanselmann
566 4f3d5b76 Michael Hanselmann
    else:
567 4f3d5b76 Michael Hanselmann
      sw.Write("""if [[ "$COMP_CWORD" == 1 ]]; then""")
568 4f3d5b76 Michael Hanselmann
      sw.IncIndent()
569 4f3d5b76 Michael Hanselmann
      try:
570 4f3d5b76 Michael Hanselmann
        # Complete the command name
571 4f3d5b76 Michael Hanselmann
        WriteCompReply(sw,
572 4f3d5b76 Michael Hanselmann
                       ("-W %s" %
573 4f3d5b76 Michael Hanselmann
                        utils.ShellQuote(" ".join(sorted(commands.keys())))))
574 4f3d5b76 Michael Hanselmann
      finally:
575 4f3d5b76 Michael Hanselmann
        sw.DecIndent()
576 4f3d5b76 Michael Hanselmann
      sw.Write("fi")
577 4f3d5b76 Michael Hanselmann
578 b33cad4a Michael Hanselmann
      # Group commands by arguments and options
579 b33cad4a Michael Hanselmann
      grouped_cmds = {}
580 f5ce7613 Iustin Pop
      for cmd, (_, argdef, optdef, _, _) in commands.items():
581 4f3d5b76 Michael Hanselmann
        if not (argdef or optdef):
582 4f3d5b76 Michael Hanselmann
          continue
583 b33cad4a Michael Hanselmann
        grouped_cmds.setdefault((tuple(argdef), tuple(optdef)), set()).add(cmd)
584 4f3d5b76 Michael Hanselmann
585 b33cad4a Michael Hanselmann
      # We're doing options and arguments to commands
586 b33cad4a Michael Hanselmann
      sw.Write("""case "${COMP_WORDS[1]}" in""")
587 f5ce7613 Iustin Pop
      sort_grouped = sorted(grouped_cmds.items(),
588 f5ce7613 Iustin Pop
                            key=lambda (_, y): sorted(y)[0])
589 f5ce7613 Iustin Pop
      for ((argdef, optdef), cmds) in sort_grouped:
590 b33cad4a Michael Hanselmann
        assert argdef or optdef
591 b33cad4a Michael Hanselmann
        sw.Write("%s)", "|".join(map(utils.ShellQuote, sorted(cmds))))
592 4f3d5b76 Michael Hanselmann
        sw.IncIndent()
593 4f3d5b76 Michael Hanselmann
        try:
594 e80aeb89 Michael Hanselmann
          CompletionWriter(1, optdef, argdef, support_debug).WriteTo(sw)
595 4f3d5b76 Michael Hanselmann
        finally:
596 4f3d5b76 Michael Hanselmann
          sw.DecIndent()
597 4f3d5b76 Michael Hanselmann
        sw.Write(";;")
598 4f3d5b76 Michael Hanselmann
      sw.Write("esac")
599 4f3d5b76 Michael Hanselmann
  finally:
600 4f3d5b76 Michael Hanselmann
    sw.DecIndent()
601 4f3d5b76 Michael Hanselmann
  sw.Write("}")
602 4f3d5b76 Michael Hanselmann
603 4f3d5b76 Michael Hanselmann
  sw.Write("complete -F %s -o filenames %s",
604 4f3d5b76 Michael Hanselmann
           utils.ShellQuote(funcname),
605 4f3d5b76 Michael Hanselmann
           utils.ShellQuote(scriptname))
606 4f3d5b76 Michael Hanselmann
607 4f3d5b76 Michael Hanselmann
608 4f3d5b76 Michael Hanselmann
def GetFunctionName(name):
609 4f3d5b76 Michael Hanselmann
  return "_" + re.sub(r"[^a-z0-9]+", "_", name.lower())
610 4f3d5b76 Michael Hanselmann
611 4f3d5b76 Michael Hanselmann
612 4f3d5b76 Michael Hanselmann
def GetCommands(filename, module):
613 4f3d5b76 Michael Hanselmann
  """Returns the commands defined in a module.
614 4f3d5b76 Michael Hanselmann
615 4f3d5b76 Michael Hanselmann
  Aliases are also added as commands.
616 4f3d5b76 Michael Hanselmann
617 4f3d5b76 Michael Hanselmann
  """
618 4f3d5b76 Michael Hanselmann
  try:
619 4f3d5b76 Michael Hanselmann
    commands = getattr(module, "commands")
620 f59418db Michael Hanselmann
  except AttributeError:
621 4f3d5b76 Michael Hanselmann
    raise Exception("Script %s doesn't have 'commands' attribute" %
622 4f3d5b76 Michael Hanselmann
                    filename)
623 4f3d5b76 Michael Hanselmann
624 1f4e391b Michael Hanselmann
  # Add the implicit "--help" option
625 1f4e391b Michael Hanselmann
  help_option = cli.cli_option("-h", "--help", default=False,
626 1f4e391b Michael Hanselmann
                               action="store_true")
627 1f4e391b Michael Hanselmann
628 5786c087 Michael Hanselmann
  for name, (_, _, optdef, _, _) in commands.items():
629 1f4e391b Michael Hanselmann
    if help_option not in optdef:
630 1f4e391b Michael Hanselmann
      optdef.append(help_option)
631 5786c087 Michael Hanselmann
    for opt in cli.COMMON_OPTS:
632 5786c087 Michael Hanselmann
      if opt in optdef:
633 5786c087 Michael Hanselmann
        raise Exception("Common option '%s' listed for command '%s' in %s" %
634 5786c087 Michael Hanselmann
                        (opt, name, filename))
635 5786c087 Michael Hanselmann
      optdef.append(opt)
636 1f4e391b Michael Hanselmann
637 4f3d5b76 Michael Hanselmann
  # Use aliases
638 4f3d5b76 Michael Hanselmann
  aliases = getattr(module, "aliases", {})
639 4f3d5b76 Michael Hanselmann
  if aliases:
640 4f3d5b76 Michael Hanselmann
    commands = commands.copy()
641 f5ce7613 Iustin Pop
    for name, target in aliases.items():
642 4f3d5b76 Michael Hanselmann
      commands[name] = commands[target]
643 4f3d5b76 Michael Hanselmann
644 4f3d5b76 Michael Hanselmann
  return commands
645 4f3d5b76 Michael Hanselmann
646 4f3d5b76 Michael Hanselmann
647 ada0e680 Iustin Pop
def HaskellOptToOptParse(opts, kind):
648 ada0e680 Iustin Pop
  """Converts a Haskell options to Python cli_options.
649 ada0e680 Iustin Pop
650 ada0e680 Iustin Pop
  @type opts: string
651 ada0e680 Iustin Pop
  @param opts: comma-separated string with short and long options
652 ada0e680 Iustin Pop
  @type kind: string
653 ada0e680 Iustin Pop
  @param kind: type generated by Common.hs/complToText; needs to be
654 ada0e680 Iustin Pop
      kept in sync
655 ada0e680 Iustin Pop
656 ada0e680 Iustin Pop
  """
657 ada0e680 Iustin Pop
  # pylint: disable=W0142
658 ada0e680 Iustin Pop
  # since we pass *opts in a number of places
659 ada0e680 Iustin Pop
  opts = opts.split(",")
660 ada0e680 Iustin Pop
  if kind == "none":
661 ada0e680 Iustin Pop
    return cli.cli_option(*opts, action="store_true")
662 f8d01158 Iustin Pop
  elif kind in ["file", "string", "host", "dir", "inetaddr"]:
663 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="string")
664 ecebe9f6 Iustin Pop
  elif kind == "integer":
665 ecebe9f6 Iustin Pop
    return cli.cli_option(*opts, type="int")
666 ecebe9f6 Iustin Pop
  elif kind == "float":
667 ecebe9f6 Iustin Pop
    return cli.cli_option(*opts, type="float")
668 ada0e680 Iustin Pop
  elif kind == "onegroup":
669 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="string",
670 ada0e680 Iustin Pop
                           completion_suggest=cli.OPT_COMPL_ONE_NODEGROUP)
671 ada0e680 Iustin Pop
  elif kind == "onenode":
672 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="string",
673 ada0e680 Iustin Pop
                          completion_suggest=cli.OPT_COMPL_ONE_NODE)
674 ada0e680 Iustin Pop
  elif kind == "manyinstances":
675 ada0e680 Iustin Pop
    # FIXME: no support for many instances
676 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="string")
677 fad06963 Iustin Pop
  elif kind.startswith("choices="):
678 fad06963 Iustin Pop
    choices = kind[len("choices="):].split(",")
679 ada0e680 Iustin Pop
    return cli.cli_option(*opts, type="choice", choices=choices)
680 ada0e680 Iustin Pop
  else:
681 ada0e680 Iustin Pop
    # FIXME: there are many other currently unused completion types,
682 ada0e680 Iustin Pop
    # should be added on an as-needed basis
683 fad06963 Iustin Pop
    raise Exception("Unhandled option kind '%s'" % kind)
684 fad06963 Iustin Pop
685 fad06963 Iustin Pop
686 fad06963 Iustin Pop
#: serialised kind to arg type
687 fad06963 Iustin Pop
_ARG_MAP = {
688 46118ed2 Iustin Pop
  "choices": cli.ArgChoice,
689 fad06963 Iustin Pop
  "command": cli.ArgCommand,
690 fad06963 Iustin Pop
  "file": cli.ArgFile,
691 fad06963 Iustin Pop
  "host": cli.ArgHost,
692 fad06963 Iustin Pop
  "jobid": cli.ArgJobId,
693 fad06963 Iustin Pop
  "onegroup": cli.ArgGroup,
694 fad06963 Iustin Pop
  "oneinstance": cli.ArgInstance,
695 fad06963 Iustin Pop
  "onenode": cli.ArgNode,
696 fad06963 Iustin Pop
  "oneos": cli.ArgOs,
697 fad06963 Iustin Pop
  "string": cli.ArgUnknown,
698 46118ed2 Iustin Pop
  "suggests": cli.ArgSuggest,
699 fad06963 Iustin Pop
  }
700 fad06963 Iustin Pop
701 fad06963 Iustin Pop
702 fad06963 Iustin Pop
def HaskellArgToCliArg(kind, min_cnt, max_cnt):
703 fad06963 Iustin Pop
  """Converts a Haskell options to Python _Argument.
704 fad06963 Iustin Pop
705 fad06963 Iustin Pop
  @type kind: string
706 fad06963 Iustin Pop
  @param kind: type generated by Common.hs/argComplToText; needs to be
707 fad06963 Iustin Pop
      kept in sync
708 fad06963 Iustin Pop
709 fad06963 Iustin Pop
  """
710 fad06963 Iustin Pop
  min_cnt = int(min_cnt)
711 fad06963 Iustin Pop
  if max_cnt == "none":
712 fad06963 Iustin Pop
    max_cnt = None
713 fad06963 Iustin Pop
  else:
714 fad06963 Iustin Pop
    max_cnt = int(max_cnt)
715 fad06963 Iustin Pop
  # pylint: disable=W0142
716 fad06963 Iustin Pop
  # since we pass **kwargs
717 fad06963 Iustin Pop
  kwargs = {"min": min_cnt, "max": max_cnt}
718 fad06963 Iustin Pop
719 fad06963 Iustin Pop
  if kind.startswith("choices=") or kind.startswith("suggest="):
720 fad06963 Iustin Pop
    (kind, choices) = kind.split("=", 1)
721 fad06963 Iustin Pop
    kwargs["choices"] = choices.split(",")
722 fad06963 Iustin Pop
723 fad06963 Iustin Pop
  if kind not in _ARG_MAP:
724 fad06963 Iustin Pop
    raise Exception("Unhandled argument kind '%s'" % kind)
725 fad06963 Iustin Pop
  else:
726 fad06963 Iustin Pop
    return _ARG_MAP[kind](**kwargs)
727 ada0e680 Iustin Pop
728 ada0e680 Iustin Pop
729 24476fa0 Iustin Pop
def ParseHaskellOptsArgs(script, output):
730 24476fa0 Iustin Pop
  """Computes list of options/arguments from help-completion output.
731 ada0e680 Iustin Pop
732 ada0e680 Iustin Pop
  """
733 ada0e680 Iustin Pop
  cli_opts = []
734 24476fa0 Iustin Pop
  cli_args = []
735 ada0e680 Iustin Pop
  for line in output.splitlines():
736 fad06963 Iustin Pop
    v = line.split(None)
737 fad06963 Iustin Pop
    exc = lambda msg: Exception("Invalid %s output from %s: %s" %
738 fad06963 Iustin Pop
                                (msg, script, v))
739 fad06963 Iustin Pop
    if len(v) < 2:
740 fad06963 Iustin Pop
      raise exc("help completion")
741 fad06963 Iustin Pop
    if v[0].startswith("-"):
742 fad06963 Iustin Pop
      if len(v) != 2:
743 fad06963 Iustin Pop
        raise exc("option format")
744 fad06963 Iustin Pop
      (opts, kind) = v
745 fad06963 Iustin Pop
      cli_opts.append(HaskellOptToOptParse(opts, kind))
746 fad06963 Iustin Pop
    else:
747 fad06963 Iustin Pop
      if len(v) != 3:
748 fad06963 Iustin Pop
        raise exc("argument format")
749 fad06963 Iustin Pop
      (kind, min_cnt, max_cnt) = v
750 24476fa0 Iustin Pop
      cli_args.append(HaskellArgToCliArg(kind, min_cnt, max_cnt))
751 24476fa0 Iustin Pop
  return (cli_opts, cli_args)
752 24476fa0 Iustin Pop
753 24476fa0 Iustin Pop
754 24476fa0 Iustin Pop
def WriteHaskellCompletion(sw, script, htools=True, debug=True):
755 24476fa0 Iustin Pop
  """Generates completion information for a Haskell program.
756 24476fa0 Iustin Pop
757 24476fa0 Iustin Pop
  This converts completion info from a Haskell program into 'fake'
758 24476fa0 Iustin Pop
  cli_opts and then builds completion for them.
759 24476fa0 Iustin Pop
760 24476fa0 Iustin Pop
  """
761 24476fa0 Iustin Pop
  if htools:
762 3add7574 Iustin Pop
    cmd = "./src/htools"
763 24476fa0 Iustin Pop
    env = {"HTOOLS": script}
764 24476fa0 Iustin Pop
    script_name = script
765 24476fa0 Iustin Pop
    func_name = "htools_%s" % script
766 24476fa0 Iustin Pop
  else:
767 24476fa0 Iustin Pop
    cmd = "./" + script
768 24476fa0 Iustin Pop
    env = {}
769 24476fa0 Iustin Pop
    script_name = os.path.basename(script)
770 24476fa0 Iustin Pop
    func_name = script_name
771 24476fa0 Iustin Pop
  func_name = GetFunctionName(func_name)
772 24476fa0 Iustin Pop
  output = utils.RunCmd([cmd, "--help-completion"], env=env, cwd=".").output
773 24476fa0 Iustin Pop
  (opts, args) = ParseHaskellOptsArgs(script_name, output)
774 24476fa0 Iustin Pop
  WriteCompletion(sw, script_name, func_name, debug, opts=opts, args=args)
775 24476fa0 Iustin Pop
776 24476fa0 Iustin Pop
777 24476fa0 Iustin Pop
def WriteHaskellCmdCompletion(sw, script, debug=True):
778 24476fa0 Iustin Pop
  """Generates completion information for a Haskell multi-command program.
779 24476fa0 Iustin Pop
780 24476fa0 Iustin Pop
  This gathers the list of commands from a Haskell program and
781 24476fa0 Iustin Pop
  computes the list of commands available, then builds the sub-command
782 24476fa0 Iustin Pop
  list of options/arguments for each command, using that for building
783 24476fa0 Iustin Pop
  a unified help output.
784 24476fa0 Iustin Pop
785 24476fa0 Iustin Pop
  """
786 24476fa0 Iustin Pop
  cmd = "./" + script
787 24476fa0 Iustin Pop
  script_name = os.path.basename(script)
788 24476fa0 Iustin Pop
  func_name = script_name
789 24476fa0 Iustin Pop
  func_name = GetFunctionName(func_name)
790 24476fa0 Iustin Pop
  output = utils.RunCmd([cmd, "--help-completion"], cwd=".").output
791 24476fa0 Iustin Pop
  commands = {}
792 24476fa0 Iustin Pop
  lines = output.splitlines()
793 24476fa0 Iustin Pop
  if len(lines) != 1:
794 24476fa0 Iustin Pop
    raise Exception("Invalid lines in multi-command mode: %s" % str(lines))
795 24476fa0 Iustin Pop
  v = lines[0].split(None)
796 24476fa0 Iustin Pop
  exc = lambda msg: Exception("Invalid %s output from %s: %s" %
797 24476fa0 Iustin Pop
                              (msg, script, v))
798 24476fa0 Iustin Pop
  if len(v) != 3:
799 24476fa0 Iustin Pop
    raise exc("help completion in multi-command mode")
800 24476fa0 Iustin Pop
  if not v[0].startswith("choices="):
801 24476fa0 Iustin Pop
    raise exc("invalid format in multi-command mode '%s'" % v[0])
802 24476fa0 Iustin Pop
  for subcmd in v[0][len("choices="):].split(","):
803 24476fa0 Iustin Pop
    output = utils.RunCmd([cmd, subcmd, "--help-completion"], cwd=".").output
804 24476fa0 Iustin Pop
    (opts, args) = ParseHaskellOptsArgs(script, output)
805 24476fa0 Iustin Pop
    commands[subcmd] = (None, args, opts, None, None)
806 24476fa0 Iustin Pop
  WriteCompletion(sw, script_name, func_name, debug, commands=commands)
807 ada0e680 Iustin Pop
808 ada0e680 Iustin Pop
809 4f3d5b76 Michael Hanselmann
def main():
810 e80aeb89 Michael Hanselmann
  parser = optparse.OptionParser(usage="%prog [--compact]")
811 e80aeb89 Michael Hanselmann
  parser.add_option("--compact", action="store_true",
812 e80aeb89 Michael Hanselmann
                    help=("Don't indent output and don't include debugging"
813 e80aeb89 Michael Hanselmann
                          " facilities"))
814 e80aeb89 Michael Hanselmann
815 e80aeb89 Michael Hanselmann
  options, args = parser.parse_args()
816 e80aeb89 Michael Hanselmann
  if args:
817 e80aeb89 Michael Hanselmann
    parser.error("Wrong number of arguments")
818 e80aeb89 Michael Hanselmann
819 24476fa0 Iustin Pop
  # Whether to build debug version of completion script
820 24476fa0 Iustin Pop
  debug = not options.compact
821 24476fa0 Iustin Pop
822 4f3d5b76 Michael Hanselmann
  buf = StringIO()
823 24476fa0 Iustin Pop
  sw = utils.ShellWriter(buf, indent=debug)
824 4f3d5b76 Michael Hanselmann
825 893ad76d Michael Hanselmann
  # Remember original state of extglob and enable it (required for pattern
826 893ad76d Michael Hanselmann
  # matching; must be enabled while parsing script)
827 893ad76d Michael Hanselmann
  sw.Write("gnt_shopt_extglob=$(shopt -p extglob || :)")
828 893ad76d Michael Hanselmann
  sw.Write("shopt -s extglob")
829 893ad76d Michael Hanselmann
830 24476fa0 Iustin Pop
  WritePreamble(sw, debug)
831 4f3d5b76 Michael Hanselmann
832 4f3d5b76 Michael Hanselmann
  # gnt-* scripts
833 4f3d5b76 Michael Hanselmann
  for scriptname in _autoconf.GNT_SCRIPTS:
834 4f3d5b76 Michael Hanselmann
    filename = "scripts/%s" % scriptname
835 4f3d5b76 Michael Hanselmann
836 24476fa0 Iustin Pop
    WriteCompletion(sw, scriptname, GetFunctionName(scriptname), debug,
837 d6f5892b Michael Hanselmann
                    commands=GetCommands(filename,
838 e948770c Michael Hanselmann
                                         build.LoadModule(filename)))
839 4f3d5b76 Michael Hanselmann
840 4f3d5b76 Michael Hanselmann
  # Burnin script
841 e948770c Michael Hanselmann
  burnin = build.LoadModule("tools/burnin")
842 13718ded Michael Hanselmann
  WriteCompletion(sw, "%s/burnin" % pathutils.TOOLSDIR, "_ganeti_burnin",
843 24476fa0 Iustin Pop
                  debug,
844 4f3d5b76 Michael Hanselmann
                  opts=burnin.OPTIONS, args=burnin.ARGUMENTS)
845 4f3d5b76 Michael Hanselmann
846 46118ed2 Iustin Pop
  # ganeti-cleaner
847 46118ed2 Iustin Pop
  WriteHaskellCompletion(sw, "daemons/ganeti-cleaner", htools=False,
848 46118ed2 Iustin Pop
                         debug=not options.compact)
849 46118ed2 Iustin Pop
850 ada0e680 Iustin Pop
  # htools, if enabled
851 ada0e680 Iustin Pop
  if _autoconf.HTOOLS:
852 ada0e680 Iustin Pop
    for script in _autoconf.HTOOLS_PROGS:
853 24476fa0 Iustin Pop
      WriteHaskellCompletion(sw, script, htools=True, debug=debug)
854 ada0e680 Iustin Pop
855 f8d01158 Iustin Pop
  # ganeti-confd, if enabled
856 f8d01158 Iustin Pop
  if _autoconf.ENABLE_CONFD:
857 3add7574 Iustin Pop
    WriteHaskellCompletion(sw, "src/ganeti-confd", htools=False,
858 24476fa0 Iustin Pop
                           debug=debug)
859 f8d01158 Iustin Pop
860 988dc5ca Iustin Pop
  # mon-collector, if monitoring is enabled
861 988dc5ca Iustin Pop
  if _autoconf.ENABLE_MONITORING:
862 3add7574 Iustin Pop
    WriteHaskellCmdCompletion(sw, "src/mon-collector", debug=debug)
863 988dc5ca Iustin Pop
864 893ad76d Michael Hanselmann
  # Reset extglob to original value
865 893ad76d Michael Hanselmann
  sw.Write("[[ -n \"$gnt_shopt_extglob\" ]] && $gnt_shopt_extglob")
866 893ad76d Michael Hanselmann
  sw.Write("unset gnt_shopt_extglob")
867 893ad76d Michael Hanselmann
868 4f3d5b76 Michael Hanselmann
  print buf.getvalue()
869 4f3d5b76 Michael Hanselmann
870 4f3d5b76 Michael Hanselmann
871 4f3d5b76 Michael Hanselmann
if __name__ == "__main__":
872 4f3d5b76 Michael Hanselmann
  main()