Statistics
| Branch: | Tag: | Revision:

root / autotools / build-bash-completion @ 7ca67731

History | View | Annotate | Download (23.7 kB)

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