Statistics
| Branch: | Tag: | Revision:

root / autotools / build-bash-completion @ 19930d75

History | View | Annotate | Download (19.7 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Script to generate bash_completion script for Ganeti.
23

    
24
"""
25

    
26
# pylint: disable=C0103
27
# [C0103] Invalid name build-bash-completion
28

    
29
import os
30
import re
31
import itertools
32
import optparse
33
from cStringIO import StringIO
34

    
35
from ganeti import constants
36
from ganeti import cli
37
from ganeti import utils
38
from ganeti import build
39
from ganeti import pathutils
40

    
41
# _autoconf shouldn't be imported from anywhere except constants.py, but we're
42
# making an exception here because this script is only used at build time.
43
from ganeti import _autoconf
44

    
45
#: Regular expression describing desired format of option names. Long names can
46
#: contain lowercase characters, numbers and dashes only.
47
_OPT_NAME_RE = re.compile(r"^-[a-zA-Z0-9]|--[a-z][-a-z0-9]+$")
48

    
49

    
50
def WritePreamble(sw, support_debug):
51
  """Writes the script preamble.
52

    
53
  Helper functions should be written here.
54

    
55
  """
56
  sw.Write("# This script is automatically generated at build time.")
57
  sw.Write("# Do not modify manually.")
58

    
59
  if support_debug:
60
    sw.Write("_gnt_log() {")
61
    sw.IncIndent()
62
    try:
63
      sw.Write("if [[ -n \"$GANETI_COMPL_LOG\" ]]; then")
64
      sw.IncIndent()
65
      try:
66
        sw.Write("{")
67
        sw.IncIndent()
68
        try:
69
          sw.Write("echo ---")
70
          sw.Write("echo \"$@\"")
71
          sw.Write("echo")
72
        finally:
73
          sw.DecIndent()
74
        sw.Write("} >> $GANETI_COMPL_LOG")
75
      finally:
76
        sw.DecIndent()
77
      sw.Write("fi")
78
    finally:
79
      sw.DecIndent()
80
    sw.Write("}")
81

    
82
  sw.Write("_ganeti_nodes() {")
83
  sw.IncIndent()
84
  try:
85
    node_list_path = os.path.join(pathutils.DATA_DIR, "ssconf_node_list")
86
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(node_list_path))
87
  finally:
88
    sw.DecIndent()
89
  sw.Write("}")
90

    
91
  sw.Write("_ganeti_instances() {")
92
  sw.IncIndent()
93
  try:
94
    instance_list_path = os.path.join(pathutils.DATA_DIR,
95
                                      "ssconf_instance_list")
96
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(instance_list_path))
97
  finally:
98
    sw.DecIndent()
99
  sw.Write("}")
100

    
101
  sw.Write("_ganeti_jobs() {")
102
  sw.IncIndent()
103
  try:
104
    # FIXME: this is really going into the internals of the job queue
105
    sw.Write(("local jlist=$( shopt -s nullglob &&"
106
              " cd %s 2>/dev/null && echo job-* || : )"),
107
             utils.ShellQuote(pathutils.QUEUE_DIR))
108
    sw.Write('echo "${jlist//job-/}"')
109
  finally:
110
    sw.DecIndent()
111
  sw.Write("}")
112

    
113
  for (fnname, paths) in [
114
    ("os", pathutils.OS_SEARCH_PATH),
115
    ("iallocator", constants.IALLOCATOR_SEARCH_PATH),
116
    ]:
117
    sw.Write("_ganeti_%s() {", fnname)
118
    sw.IncIndent()
119
    try:
120
      # FIXME: Make querying the master for all OSes cheap
121
      for path in paths:
122
        sw.Write("( shopt -s nullglob && cd %s 2>/dev/null && echo * || : )",
123
                 utils.ShellQuote(path))
124
    finally:
125
      sw.DecIndent()
126
    sw.Write("}")
127

    
128
  sw.Write("_ganeti_nodegroup() {")
129
  sw.IncIndent()
130
  try:
131
    nodegroups_path = os.path.join(pathutils.DATA_DIR, "ssconf_nodegroups")
132
    sw.Write("cat %s 2>/dev/null || :", utils.ShellQuote(nodegroups_path))
133
  finally:
134
    sw.DecIndent()
135
  sw.Write("}")
136

    
137
  # Params: <offset> <options with values> <options without values>
138
  # Result variable: $first_arg_idx
139
  sw.Write("_ganeti_find_first_arg() {")
140
  sw.IncIndent()
141
  try:
142
    sw.Write("local w i")
143

    
144
    sw.Write("first_arg_idx=")
145
    sw.Write("for (( i=$1; i < COMP_CWORD; ++i )); do")
146
    sw.IncIndent()
147
    try:
148
      sw.Write("w=${COMP_WORDS[$i]}")
149

    
150
      # Skip option value
151
      sw.Write("""if [[ -n "$2" && "$w" == @($2) ]]; then let ++i""")
152

    
153
      # Skip
154
      sw.Write("""elif [[ -n "$3" && "$w" == @($3) ]]; then :""")
155

    
156
      # Ah, we found the first argument
157
      sw.Write("else first_arg_idx=$i; break;")
158
      sw.Write("fi")
159
    finally:
160
      sw.DecIndent()
161
    sw.Write("done")
162
  finally:
163
    sw.DecIndent()
164
  sw.Write("}")
165

    
166
  # Params: <list of options separated by space>
167
  # Input variable: $first_arg_idx
168
  # Result variables: $arg_idx, $choices
169
  sw.Write("_ganeti_list_options() {")
170
  sw.IncIndent()
171
  try:
172
    sw.Write("""if [[ -z "$first_arg_idx" ]]; then""")
173
    sw.IncIndent()
174
    try:
175
      sw.Write("arg_idx=0")
176
      # Show options only if the current word starts with a dash
177
      sw.Write("""if [[ "$cur" == -* ]]; then""")
178
      sw.IncIndent()
179
      try:
180
        sw.Write("choices=$1")
181
      finally:
182
        sw.DecIndent()
183
      sw.Write("fi")
184
      sw.Write("return")
185
    finally:
186
      sw.DecIndent()
187
    sw.Write("fi")
188

    
189
    # Calculate position of current argument
190
    sw.Write("arg_idx=$(( COMP_CWORD - first_arg_idx ))")
191
    sw.Write("choices=")
192
  finally:
193
    sw.DecIndent()
194
  sw.Write("}")
195

    
196
  # Params: <long options with equal sign> <all options>
197
  # Result variable: $optcur
198
  sw.Write("_gnt_checkopt() {")
199
  sw.IncIndent()
200
  try:
201
    sw.Write("""if [[ -n "$1" && "$cur" == @($1) ]]; then""")
202
    sw.IncIndent()
203
    try:
204
      sw.Write("optcur=\"${cur#--*=}\"")
205
      sw.Write("return 0")
206
    finally:
207
      sw.DecIndent()
208
    sw.Write("""elif [[ -n "$2" && "$prev" == @($2) ]]; then""")
209
    sw.IncIndent()
210
    try:
211
      sw.Write("optcur=\"$cur\"")
212
      sw.Write("return 0")
213
    finally:
214
      sw.DecIndent()
215
    sw.Write("fi")
216

    
217
    if support_debug:
218
      sw.Write("_gnt_log optcur=\"'$optcur'\"")
219

    
220
    sw.Write("return 1")
221
  finally:
222
    sw.DecIndent()
223
  sw.Write("}")
224

    
225
  # Params: <compgen options>
226
  # Result variable: $COMPREPLY
227
  sw.Write("_gnt_compgen() {")
228
  sw.IncIndent()
229
  try:
230
    sw.Write("""COMPREPLY=( $(compgen "$@") )""")
231
    if support_debug:
232
      sw.Write("_gnt_log COMPREPLY=\"${COMPREPLY[@]}\"")
233
  finally:
234
    sw.DecIndent()
235
  sw.Write("}")
236

    
237

    
238
def WriteCompReply(sw, args, cur="\"$cur\""):
239
  sw.Write("_gnt_compgen %s -- %s", args, cur)
240
  sw.Write("return")
241

    
242

    
243
class CompletionWriter:
244
  """Command completion writer class.
245

    
246
  """
247
  def __init__(self, arg_offset, opts, args, support_debug):
248
    self.arg_offset = arg_offset
249
    self.opts = opts
250
    self.args = args
251
    self.support_debug = support_debug
252

    
253
    for opt in opts:
254
      # While documented, these variables aren't seen as public attributes by
255
      # pylint. pylint: disable=W0212
256
      opt.all_names = sorted(opt._short_opts + opt._long_opts)
257

    
258
      invalid = list(itertools.ifilterfalse(_OPT_NAME_RE.match, opt.all_names))
259
      if invalid:
260
        raise Exception("Option names don't match regular expression '%s': %s" %
261
                        (_OPT_NAME_RE.pattern, utils.CommaJoin(invalid)))
262

    
263
  def _FindFirstArgument(self, sw):
264
    ignore = []
265
    skip_one = []
266

    
267
    for opt in self.opts:
268
      if opt.takes_value():
269
        # Ignore value
270
        for i in opt.all_names:
271
          if i.startswith("--"):
272
            ignore.append("%s=*" % utils.ShellQuote(i))
273
          skip_one.append(utils.ShellQuote(i))
274
      else:
275
        ignore.extend([utils.ShellQuote(i) for i in opt.all_names])
276

    
277
    ignore = sorted(utils.UniqueSequence(ignore))
278
    skip_one = sorted(utils.UniqueSequence(skip_one))
279

    
280
    if ignore or skip_one:
281
      # Try to locate first argument
282
      sw.Write("_ganeti_find_first_arg %s %s %s",
283
               self.arg_offset + 1,
284
               utils.ShellQuote("|".join(skip_one)),
285
               utils.ShellQuote("|".join(ignore)))
286
    else:
287
      # When there are no options the first argument is always at position
288
      # offset + 1
289
      sw.Write("first_arg_idx=%s", self.arg_offset + 1)
290

    
291
  def _CompleteOptionValues(self, sw):
292
    # Group by values
293
    # "values" -> [optname1, optname2, ...]
294
    values = {}
295

    
296
    for opt in self.opts:
297
      if not opt.takes_value():
298
        continue
299

    
300
      # Only static choices implemented so far (e.g. no node list)
301
      suggest = getattr(opt, "completion_suggest", None)
302

    
303
      # our custom option type
304
      if opt.type == "bool":
305
        suggest = ["yes", "no"]
306

    
307
      if not suggest:
308
        suggest = opt.choices
309

    
310
      if (isinstance(suggest, (int, long)) and
311
          suggest in cli.OPT_COMPL_ALL):
312
        key = suggest
313
      elif suggest:
314
        key = " ".join(sorted(suggest))
315
      else:
316
        key = ""
317

    
318
      values.setdefault(key, []).extend(opt.all_names)
319

    
320
    # Don't write any code if there are no option values
321
    if not values:
322
      return
323

    
324
    cur = "\"$optcur\""
325

    
326
    wrote_opt = False
327

    
328
    for (suggest, allnames) in values.items():
329
      longnames = [i for i in allnames if i.startswith("--")]
330

    
331
      if wrote_opt:
332
        condcmd = "elif"
333
      else:
334
        condcmd = "if"
335

    
336
      sw.Write("%s _gnt_checkopt %s %s; then", condcmd,
337
               utils.ShellQuote("|".join(["%s=*" % i for i in longnames])),
338
               utils.ShellQuote("|".join(allnames)))
339
      sw.IncIndent()
340
      try:
341
        if suggest == cli.OPT_COMPL_MANY_NODES:
342
          # TODO: Implement comma-separated values
343
          WriteCompReply(sw, "-W ''", cur=cur)
344
        elif suggest == cli.OPT_COMPL_ONE_NODE:
345
          WriteCompReply(sw, "-W \"$(_ganeti_nodes)\"", cur=cur)
346
        elif suggest == cli.OPT_COMPL_ONE_INSTANCE:
347
          WriteCompReply(sw, "-W \"$(_ganeti_instances)\"", cur=cur)
348
        elif suggest == cli.OPT_COMPL_ONE_OS:
349
          WriteCompReply(sw, "-W \"$(_ganeti_os)\"", cur=cur)
350
        elif suggest == cli.OPT_COMPL_ONE_EXTSTORAGE:
351
          WriteCompReply(sw, "-W \"$(_ganeti_extstorage)\"", cur=cur)
352
        elif suggest == cli.OPT_COMPL_ONE_IALLOCATOR:
353
          WriteCompReply(sw, "-W \"$(_ganeti_iallocator)\"", cur=cur)
354
        elif suggest == cli.OPT_COMPL_ONE_NODEGROUP:
355
          WriteCompReply(sw, "-W \"$(_ganeti_nodegroup)\"", cur=cur)
356
        elif suggest == cli.OPT_COMPL_INST_ADD_NODES:
357
          sw.Write("local tmp= node1= pfx= curvalue=\"${optcur#*:}\"")
358

    
359
          sw.Write("if [[ \"$optcur\" == *:* ]]; then")
360
          sw.IncIndent()
361
          try:
362
            sw.Write("node1=\"${optcur%%:*}\"")
363

    
364
            sw.Write("if [[ \"$COMP_WORDBREAKS\" != *:* ]]; then")
365
            sw.IncIndent()
366
            try:
367
              sw.Write("pfx=\"$node1:\"")
368
            finally:
369
              sw.DecIndent()
370
            sw.Write("fi")
371
          finally:
372
            sw.DecIndent()
373
          sw.Write("fi")
374

    
375
          if self.support_debug:
376
            sw.Write("_gnt_log pfx=\"'$pfx'\" curvalue=\"'$curvalue'\""
377
                     " node1=\"'$node1'\"")
378

    
379
          sw.Write("for i in $(_ganeti_nodes); do")
380
          sw.IncIndent()
381
          try:
382
            sw.Write("if [[ -z \"$node1\" ]]; then")
383
            sw.IncIndent()
384
            try:
385
              sw.Write("tmp=\"$tmp $i $i:\"")
386
            finally:
387
              sw.DecIndent()
388
            sw.Write("elif [[ \"$i\" != \"$node1\" ]]; then")
389
            sw.IncIndent()
390
            try:
391
              sw.Write("tmp=\"$tmp $i\"")
392
            finally:
393
              sw.DecIndent()
394
            sw.Write("fi")
395
          finally:
396
            sw.DecIndent()
397
          sw.Write("done")
398

    
399
          WriteCompReply(sw, "-P \"$pfx\" -W \"$tmp\"", cur="\"$curvalue\"")
400
        else:
401
          WriteCompReply(sw, "-W %s" % utils.ShellQuote(suggest), cur=cur)
402
      finally:
403
        sw.DecIndent()
404

    
405
      wrote_opt = True
406

    
407
    if wrote_opt:
408
      sw.Write("fi")
409

    
410
    return
411

    
412
  def _CompleteArguments(self, sw):
413
    if not (self.opts or self.args):
414
      return
415

    
416
    all_option_names = []
417
    for opt in self.opts:
418
      all_option_names.extend(opt.all_names)
419
    all_option_names.sort()
420

    
421
    # List options if no argument has been specified yet
422
    sw.Write("_ganeti_list_options %s",
423
             utils.ShellQuote(" ".join(all_option_names)))
424

    
425
    if self.args:
426
      last_idx = len(self.args) - 1
427
      last_arg_end = 0
428
      varlen_arg_idx = None
429
      wrote_arg = False
430

    
431
      sw.Write("compgenargs=")
432

    
433
      for idx, arg in enumerate(self.args):
434
        assert arg.min is not None and arg.min >= 0
435
        assert not (idx < last_idx and arg.max is None)
436

    
437
        if arg.min != arg.max or arg.max is None:
438
          if varlen_arg_idx is not None:
439
            raise Exception("Only one argument can have a variable length")
440
          varlen_arg_idx = idx
441

    
442
        compgenargs = []
443

    
444
        if isinstance(arg, cli.ArgUnknown):
445
          choices = ""
446
        elif isinstance(arg, cli.ArgSuggest):
447
          choices = utils.ShellQuote(" ".join(arg.choices))
448
        elif isinstance(arg, cli.ArgInstance):
449
          choices = "$(_ganeti_instances)"
450
        elif isinstance(arg, cli.ArgNode):
451
          choices = "$(_ganeti_nodes)"
452
        elif isinstance(arg, cli.ArgGroup):
453
          choices = "$(_ganeti_nodegroup)"
454
        elif isinstance(arg, cli.ArgJobId):
455
          choices = "$(_ganeti_jobs)"
456
        elif isinstance(arg, cli.ArgOs):
457
          choices = "$(_ganeti_os)"
458
        elif isinstance(arg, cli.ArgExtStorage):
459
          choices = "$(_ganeti_extstorage)"
460
        elif isinstance(arg, cli.ArgFile):
461
          choices = ""
462
          compgenargs.append("-f")
463
        elif isinstance(arg, cli.ArgCommand):
464
          choices = ""
465
          compgenargs.append("-c")
466
        elif isinstance(arg, cli.ArgHost):
467
          choices = ""
468
          compgenargs.append("-A hostname")
469
        else:
470
          raise Exception("Unknown argument type %r" % arg)
471

    
472
        if arg.min == 1 and arg.max == 1:
473
          cmpcode = """"$arg_idx" == %d""" % (last_arg_end)
474
        elif arg.max is None:
475
          cmpcode = """"$arg_idx" -ge %d""" % (last_arg_end)
476
        elif arg.min <= arg.max:
477
          cmpcode = (""""$arg_idx" -ge %d && "$arg_idx" -lt %d""" %
478
                     (last_arg_end, last_arg_end + arg.max))
479
        else:
480
          raise Exception("Unable to generate argument position condition")
481

    
482
        last_arg_end += arg.min
483

    
484
        if choices or compgenargs:
485
          if wrote_arg:
486
            condcmd = "elif"
487
          else:
488
            condcmd = "if"
489

    
490
          sw.Write("""%s [[ %s ]]; then""", condcmd, cmpcode)
491
          sw.IncIndent()
492
          try:
493
            if choices:
494
              sw.Write("""choices="$choices "%s""", choices)
495
            if compgenargs:
496
              sw.Write("compgenargs=%s",
497
                       utils.ShellQuote(" ".join(compgenargs)))
498
          finally:
499
            sw.DecIndent()
500

    
501
          wrote_arg = True
502

    
503
      if wrote_arg:
504
        sw.Write("fi")
505

    
506
    if self.args:
507
      WriteCompReply(sw, """-W "$choices" $compgenargs""")
508
    else:
509
      # $compgenargs exists only if there are arguments
510
      WriteCompReply(sw, '-W "$choices"')
511

    
512
  def WriteTo(self, sw):
513
    self._FindFirstArgument(sw)
514
    self._CompleteOptionValues(sw)
515
    self._CompleteArguments(sw)
516

    
517

    
518
def WriteCompletion(sw, scriptname, funcname, support_debug,
519
                    commands=None,
520
                    opts=None, args=None):
521
  """Writes the completion code for one command.
522

    
523
  @type sw: ShellWriter
524
  @param sw: Script writer
525
  @type scriptname: string
526
  @param scriptname: Name of command line program
527
  @type funcname: string
528
  @param funcname: Shell function name
529
  @type commands: list
530
  @param commands: List of all subcommands in this program
531

    
532
  """
533
  sw.Write("%s_inner() {", funcname)
534
  sw.IncIndent()
535
  try:
536
    sw.Write("local i first_arg_idx choices compgenargs arg_idx optcur"
537
             ' cur="${COMP_WORDS[COMP_CWORD]}"'
538
             ' prev="${COMP_WORDS[COMP_CWORD-1]}"')
539

    
540
    if support_debug:
541
      sw.Write("_gnt_log cur=\"$cur\" prev=\"$prev\"")
542
      sw.Write("[[ -n \"$GANETI_COMPL_LOG\" ]] &&"
543
               " _gnt_log \"$(set | grep ^COMP_)\"")
544

    
545
    sw.Write("COMPREPLY=()")
546

    
547
    if opts is not None and args is not None:
548
      assert not commands
549
      CompletionWriter(0, opts, args, support_debug).WriteTo(sw)
550

    
551
    else:
552
      sw.Write("""if [[ "$COMP_CWORD" == 1 ]]; then""")
553
      sw.IncIndent()
554
      try:
555
        # Complete the command name
556
        WriteCompReply(sw,
557
                       ("-W %s" %
558
                        utils.ShellQuote(" ".join(sorted(commands.keys())))))
559
      finally:
560
        sw.DecIndent()
561
      sw.Write("fi")
562

    
563
      # Group commands by arguments and options
564
      grouped_cmds = {}
565
      for cmd, (_, argdef, optdef, _, _) in commands.items():
566
        if not (argdef or optdef):
567
          continue
568
        grouped_cmds.setdefault((tuple(argdef), tuple(optdef)), set()).add(cmd)
569

    
570
      # We're doing options and arguments to commands
571
      sw.Write("""case "${COMP_WORDS[1]}" in""")
572
      sort_grouped = sorted(grouped_cmds.items(),
573
                            key=lambda (_, y): sorted(y)[0])
574
      for ((argdef, optdef), cmds) in sort_grouped:
575
        assert argdef or optdef
576
        sw.Write("%s)", "|".join(map(utils.ShellQuote, sorted(cmds))))
577
        sw.IncIndent()
578
        try:
579
          CompletionWriter(1, optdef, argdef, support_debug).WriteTo(sw)
580
        finally:
581
          sw.DecIndent()
582
        sw.Write(";;")
583
      sw.Write("esac")
584
  finally:
585
    sw.DecIndent()
586
  sw.Write("}")
587

    
588
  # Wrapper function to always enable extglob (needed for advanced pattern
589
  # matching)
590
  sw.Write("%s() {", funcname)
591
  sw.IncIndent()
592
  try:
593
    # Get current state of extglob
594
    sw.Write("local -r eg=$(shopt -p extglob || :)")
595

    
596
    # Enable extglob
597
    sw.Write("shopt -s extglob")
598

    
599
    sw.Write("%s_inner \"$@\"", funcname)
600

    
601
    # Reset extglob to original value
602
    sw.Write("[[ -n \"$eg\" ]] && $eg")
603
  finally:
604
    sw.DecIndent()
605
  sw.Write("}")
606

    
607
  sw.Write("complete -F %s -o filenames %s",
608
           utils.ShellQuote(funcname),
609
           utils.ShellQuote(scriptname))
610

    
611

    
612
def GetFunctionName(name):
613
  return "_" + re.sub(r"[^a-z0-9]+", "_", name.lower())
614

    
615

    
616
def GetCommands(filename, module):
617
  """Returns the commands defined in a module.
618

    
619
  Aliases are also added as commands.
620

    
621
  """
622
  try:
623
    commands = getattr(module, "commands")
624
  except AttributeError:
625
    raise Exception("Script %s doesn't have 'commands' attribute" %
626
                    filename)
627

    
628
  # Add the implicit "--help" option
629
  help_option = cli.cli_option("-h", "--help", default=False,
630
                               action="store_true")
631

    
632
  for name, (_, _, optdef, _, _) in commands.items():
633
    if help_option not in optdef:
634
      optdef.append(help_option)
635
    for opt in cli.COMMON_OPTS:
636
      if opt in optdef:
637
        raise Exception("Common option '%s' listed for command '%s' in %s" %
638
                        (opt, name, filename))
639
      optdef.append(opt)
640

    
641
  # Use aliases
642
  aliases = getattr(module, "aliases", {})
643
  if aliases:
644
    commands = commands.copy()
645
    for name, target in aliases.items():
646
      commands[name] = commands[target]
647

    
648
  return commands
649

    
650

    
651
def main():
652
  parser = optparse.OptionParser(usage="%prog [--compact]")
653
  parser.add_option("--compact", action="store_true",
654
                    help=("Don't indent output and don't include debugging"
655
                          " facilities"))
656

    
657
  options, args = parser.parse_args()
658
  if args:
659
    parser.error("Wrong number of arguments")
660

    
661
  buf = StringIO()
662
  sw = utils.ShellWriter(buf, indent=not options.compact)
663

    
664
  WritePreamble(sw, not options.compact)
665

    
666
  # gnt-* scripts
667
  for scriptname in _autoconf.GNT_SCRIPTS:
668
    filename = "scripts/%s" % scriptname
669

    
670
    WriteCompletion(sw, scriptname, GetFunctionName(scriptname),
671
                    not options.compact,
672
                    commands=GetCommands(filename,
673
                                         build.LoadModule(filename)))
674

    
675
  # Burnin script
676
  burnin = build.LoadModule("tools/burnin")
677
  WriteCompletion(sw, "%s/burnin" % pathutils.TOOLSDIR, "_ganeti_burnin",
678
                  not options.compact,
679
                  opts=burnin.OPTIONS, args=burnin.ARGUMENTS)
680

    
681
  print buf.getvalue()
682

    
683

    
684
if __name__ == "__main__":
685
  main()