Statistics
| Branch: | Tag: | Revision:

root / autotools / build-bash-completion @ e80aeb89

History | View | Annotate | Download (19.5 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_IALLOCATOR:
351
          WriteCompReply(sw, "-W \"$(_ganeti_iallocator)\"", cur=cur)
352
        elif suggest == cli.OPT_COMPL_ONE_NODEGROUP:
353
          WriteCompReply(sw, "-W \"$(_ganeti_nodegroup)\"", cur=cur)
354
        elif suggest == cli.OPT_COMPL_INST_ADD_NODES:
355
          sw.Write("local tmp= node1= pfx= curvalue=\"${optcur#*:}\"")
356

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

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

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

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

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

    
403
      wrote_opt = True
404

    
405
    if wrote_opt:
406
      sw.Write("fi")
407

    
408
    return
409

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

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

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

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

    
429
      sw.Write("compgenargs=")
430

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

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

    
440
        compgenargs = []
441

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

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

    
478
        last_arg_end += arg.min
479

    
480
        if choices or compgenargs:
481
          if wrote_arg:
482
            condcmd = "elif"
483
          else:
484
            condcmd = "if"
485

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

    
497
          wrote_arg = True
498

    
499
      if wrote_arg:
500
        sw.Write("fi")
501

    
502
    if self.args:
503
      WriteCompReply(sw, """-W "$choices" $compgenargs""")
504
    else:
505
      # $compgenargs exists only if there are arguments
506
      WriteCompReply(sw, '-W "$choices"')
507

    
508
  def WriteTo(self, sw):
509
    self._FindFirstArgument(sw)
510
    self._CompleteOptionValues(sw)
511
    self._CompleteArguments(sw)
512

    
513

    
514
def WriteCompletion(sw, scriptname, funcname, support_debug,
515
                    commands=None,
516
                    opts=None, args=None):
517
  """Writes the completion code for one command.
518

    
519
  @type sw: ShellWriter
520
  @param sw: Script writer
521
  @type scriptname: string
522
  @param scriptname: Name of command line program
523
  @type funcname: string
524
  @param funcname: Shell function name
525
  @type commands: list
526
  @param commands: List of all subcommands in this program
527

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

    
536
    if support_debug:
537
      sw.Write("_gnt_log cur=\"$cur\" prev=\"$prev\"")
538
      sw.Write("[[ -n \"$GANETI_COMPL_LOG\" ]] &&"
539
               " _gnt_log \"$(set | grep ^COMP_)\"")
540

    
541
    sw.Write("COMPREPLY=()")
542

    
543
    if opts is not None and args is not None:
544
      assert not commands
545
      CompletionWriter(0, opts, args, support_debug).WriteTo(sw)
546

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

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

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

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

    
592
    # Enable extglob
593
    sw.Write("shopt -s extglob")
594

    
595
    sw.Write("%s_inner \"$@\"", funcname)
596

    
597
    # Reset extglob to original value
598
    sw.Write("[[ -n \"$eg\" ]] && $eg")
599
  finally:
600
    sw.DecIndent()
601
  sw.Write("}")
602

    
603
  sw.Write("complete -F %s -o filenames %s",
604
           utils.ShellQuote(funcname),
605
           utils.ShellQuote(scriptname))
606

    
607

    
608
def GetFunctionName(name):
609
  return "_" + re.sub(r"[^a-z0-9]+", "_", name.lower())
610

    
611

    
612
def GetCommands(filename, module):
613
  """Returns the commands defined in a module.
614

    
615
  Aliases are also added as commands.
616

    
617
  """
618
  try:
619
    commands = getattr(module, "commands")
620
  except AttributeError:
621
    raise Exception("Script %s doesn't have 'commands' attribute" %
622
                    filename)
623

    
624
  # Add the implicit "--help" option
625
  help_option = cli.cli_option("-h", "--help", default=False,
626
                               action="store_true")
627

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

    
637
  # Use aliases
638
  aliases = getattr(module, "aliases", {})
639
  if aliases:
640
    commands = commands.copy()
641
    for name, target in aliases.items():
642
      commands[name] = commands[target]
643

    
644
  return commands
645

    
646

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

    
653
  options, args = parser.parse_args()
654
  if args:
655
    parser.error("Wrong number of arguments")
656

    
657
  buf = StringIO()
658
  sw = utils.ShellWriter(buf, indent=not options.compact)
659

    
660
  WritePreamble(sw, not options.compact)
661

    
662
  # gnt-* scripts
663
  for scriptname in _autoconf.GNT_SCRIPTS:
664
    filename = "scripts/%s" % scriptname
665

    
666
    WriteCompletion(sw, scriptname, GetFunctionName(scriptname),
667
                    not options.compact,
668
                    commands=GetCommands(filename,
669
                                         build.LoadModule(filename)))
670

    
671
  # Burnin script
672
  burnin = build.LoadModule("tools/burnin")
673
  WriteCompletion(sw, "%s/burnin" % pathutils.TOOLSDIR, "_ganeti_burnin",
674
                  not options.compact,
675
                  opts=burnin.OPTIONS, args=burnin.ARGUMENTS)
676

    
677
  print buf.getvalue()
678

    
679

    
680
if __name__ == "__main__":
681
  main()