Statistics
| Branch: | Tag: | Revision:

root / autotools / build-bash-completion @ 178ad717

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