Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ 2f31098c

History | View | Annotate | Download (15.9 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Module dealing with command line parsing"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
25 a8083063 Iustin Pop
import sys
26 a8083063 Iustin Pop
import textwrap
27 a8083063 Iustin Pop
import os.path
28 a8083063 Iustin Pop
import copy
29 73702ee7 Iustin Pop
from cStringIO import StringIO
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import utils
32 a8083063 Iustin Pop
from ganeti import logger
33 a8083063 Iustin Pop
from ganeti import errors
34 a8083063 Iustin Pop
from ganeti import mcpu
35 a8083063 Iustin Pop
from ganeti import constants
36 846baef9 Iustin Pop
from ganeti import opcodes
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
from optparse import (OptionParser, make_option, TitledHelpFormatter,
39 a8083063 Iustin Pop
                      Option, OptionValueError, SUPPRESS_HELP)
40 a8083063 Iustin Pop
41 a8083063 Iustin Pop
__all__ = ["DEBUG_OPT", "NOHDR_OPT", "SEP_OPT", "GenericMain", "SubmitOpCode",
42 47988778 Iustin Pop
           "cli_option", "GenerateTable", "AskUser",
43 a8083063 Iustin Pop
           "ARGS_NONE", "ARGS_FIXED", "ARGS_ATLEAST", "ARGS_ANY", "ARGS_ONE",
44 846baef9 Iustin Pop
           "USEUNITS_OPT", "FIELDS_OPT", "FORCE_OPT",
45 810c50b7 Iustin Pop
           "ListTags", "AddTags", "RemoveTags", "TAG_SRC_OPT",
46 73702ee7 Iustin Pop
           "FormatError",
47 846baef9 Iustin Pop
           ]
48 846baef9 Iustin Pop
49 846baef9 Iustin Pop
50 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
51 846baef9 Iustin Pop
  """Extract the tag type object.
52 846baef9 Iustin Pop

53 846baef9 Iustin Pop
  Note that this function will modify its args parameter.
54 846baef9 Iustin Pop

55 846baef9 Iustin Pop
  """
56 846baef9 Iustin Pop
  if not hasattr(opts, "tag_type"):
57 846baef9 Iustin Pop
    raise errors.ProgrammerError("tag_type not passed to _ExtractTagsObject")
58 846baef9 Iustin Pop
  kind = opts.tag_type
59 846baef9 Iustin Pop
  if kind == constants.TAG_CLUSTER:
60 846baef9 Iustin Pop
    retval = kind, kind
61 846baef9 Iustin Pop
  elif kind == constants.TAG_NODE or kind == constants.TAG_INSTANCE:
62 846baef9 Iustin Pop
    if not args:
63 0c434948 Iustin Pop
      raise errors.OpPrereqError("no arguments passed to the command")
64 846baef9 Iustin Pop
    name = args.pop(0)
65 846baef9 Iustin Pop
    retval = kind, name
66 846baef9 Iustin Pop
  else:
67 846baef9 Iustin Pop
    raise errors.ProgrammerError("Unhandled tag type '%s'" % kind)
68 846baef9 Iustin Pop
  return retval
69 846baef9 Iustin Pop
70 846baef9 Iustin Pop
71 810c50b7 Iustin Pop
def _ExtendTags(opts, args):
72 810c50b7 Iustin Pop
  """Extend the args if a source file has been given.
73 810c50b7 Iustin Pop

74 810c50b7 Iustin Pop
  This function will extend the tags with the contents of the file
75 810c50b7 Iustin Pop
  passed in the 'tags_source' attribute of the opts parameter. A file
76 810c50b7 Iustin Pop
  named '-' will be replaced by stdin.
77 810c50b7 Iustin Pop

78 810c50b7 Iustin Pop
  """
79 810c50b7 Iustin Pop
  fname = opts.tags_source
80 810c50b7 Iustin Pop
  if fname is None:
81 810c50b7 Iustin Pop
    return
82 810c50b7 Iustin Pop
  if fname == "-":
83 810c50b7 Iustin Pop
    new_fh = sys.stdin
84 810c50b7 Iustin Pop
  else:
85 810c50b7 Iustin Pop
    new_fh = open(fname, "r")
86 810c50b7 Iustin Pop
  new_data = []
87 810c50b7 Iustin Pop
  try:
88 810c50b7 Iustin Pop
    # we don't use the nice 'new_data = [line.strip() for line in fh]'
89 810c50b7 Iustin Pop
    # because of python bug 1633941
90 810c50b7 Iustin Pop
    while True:
91 810c50b7 Iustin Pop
      line = new_fh.readline()
92 810c50b7 Iustin Pop
      if not line:
93 810c50b7 Iustin Pop
        break
94 810c50b7 Iustin Pop
      new_data.append(line.strip())
95 810c50b7 Iustin Pop
  finally:
96 810c50b7 Iustin Pop
    new_fh.close()
97 810c50b7 Iustin Pop
  args.extend(new_data)
98 810c50b7 Iustin Pop
99 810c50b7 Iustin Pop
100 846baef9 Iustin Pop
def ListTags(opts, args):
101 846baef9 Iustin Pop
  """List the tags on a given object.
102 846baef9 Iustin Pop

103 846baef9 Iustin Pop
  This is a generic implementation that knows how to deal with all
104 846baef9 Iustin Pop
  three cases of tag objects (cluster, node, instance). The opts
105 846baef9 Iustin Pop
  argument is expected to contain a tag_type field denoting what
106 846baef9 Iustin Pop
  object type we work on.
107 846baef9 Iustin Pop

108 846baef9 Iustin Pop
  """
109 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
110 846baef9 Iustin Pop
  op = opcodes.OpGetTags(kind=kind, name=name)
111 846baef9 Iustin Pop
  result = SubmitOpCode(op)
112 846baef9 Iustin Pop
  result = list(result)
113 846baef9 Iustin Pop
  result.sort()
114 846baef9 Iustin Pop
  for tag in result:
115 846baef9 Iustin Pop
    print tag
116 846baef9 Iustin Pop
117 846baef9 Iustin Pop
118 846baef9 Iustin Pop
def AddTags(opts, args):
119 846baef9 Iustin Pop
  """Add tags on a given object.
120 846baef9 Iustin Pop

121 846baef9 Iustin Pop
  This is a generic implementation that knows how to deal with all
122 846baef9 Iustin Pop
  three cases of tag objects (cluster, node, instance). The opts
123 846baef9 Iustin Pop
  argument is expected to contain a tag_type field denoting what
124 846baef9 Iustin Pop
  object type we work on.
125 846baef9 Iustin Pop

126 846baef9 Iustin Pop
  """
127 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
128 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
129 846baef9 Iustin Pop
  if not args:
130 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be added")
131 846baef9 Iustin Pop
  op = opcodes.OpAddTags(kind=kind, name=name, tags=args)
132 846baef9 Iustin Pop
  SubmitOpCode(op)
133 846baef9 Iustin Pop
134 846baef9 Iustin Pop
135 846baef9 Iustin Pop
def RemoveTags(opts, args):
136 846baef9 Iustin Pop
  """Remove tags from a given object.
137 846baef9 Iustin Pop

138 846baef9 Iustin Pop
  This is a generic implementation that knows how to deal with all
139 846baef9 Iustin Pop
  three cases of tag objects (cluster, node, instance). The opts
140 846baef9 Iustin Pop
  argument is expected to contain a tag_type field denoting what
141 846baef9 Iustin Pop
  object type we work on.
142 846baef9 Iustin Pop

143 846baef9 Iustin Pop
  """
144 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
145 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
146 846baef9 Iustin Pop
  if not args:
147 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be removed")
148 846baef9 Iustin Pop
  op = opcodes.OpDelTags(kind=kind, name=name, tags=args)
149 846baef9 Iustin Pop
  SubmitOpCode(op)
150 846baef9 Iustin Pop
151 a8083063 Iustin Pop
152 a8083063 Iustin Pop
DEBUG_OPT = make_option("-d", "--debug", default=False,
153 a8083063 Iustin Pop
                        action="store_true",
154 a8083063 Iustin Pop
                        help="Turn debugging on")
155 a8083063 Iustin Pop
156 a8083063 Iustin Pop
NOHDR_OPT = make_option("--no-headers", default=False,
157 a8083063 Iustin Pop
                        action="store_true", dest="no_headers",
158 a8083063 Iustin Pop
                        help="Don't display column headers")
159 a8083063 Iustin Pop
160 137161c9 Michael Hanselmann
SEP_OPT = make_option("--separator", default=None,
161 a8083063 Iustin Pop
                      action="store", dest="separator",
162 a8083063 Iustin Pop
                      help="Separator between output fields"
163 a8083063 Iustin Pop
                      " (defaults to one space)")
164 a8083063 Iustin Pop
165 a8083063 Iustin Pop
USEUNITS_OPT = make_option("--human-readable", default=False,
166 a8083063 Iustin Pop
                           action="store_true", dest="human_readable",
167 a8083063 Iustin Pop
                           help="Print sizes in human readable format")
168 a8083063 Iustin Pop
169 dcb93971 Michael Hanselmann
FIELDS_OPT = make_option("-o", "--output", dest="output", action="store",
170 dcb93971 Michael Hanselmann
                         type="string", help="Select output fields",
171 dcb93971 Michael Hanselmann
                         metavar="FIELDS")
172 dcb93971 Michael Hanselmann
173 fe7b0351 Michael Hanselmann
FORCE_OPT = make_option("-f", "--force", dest="force", action="store_true",
174 fe7b0351 Michael Hanselmann
                        default=False, help="Force the operation")
175 fe7b0351 Michael Hanselmann
176 a8083063 Iustin Pop
_LOCK_OPT = make_option("--lock-retries", default=None,
177 a8083063 Iustin Pop
                        type="int", help=SUPPRESS_HELP)
178 a8083063 Iustin Pop
179 810c50b7 Iustin Pop
TAG_SRC_OPT = make_option("--from", dest="tags_source",
180 810c50b7 Iustin Pop
                          default=None, help="File with tag names")
181 810c50b7 Iustin Pop
182 a8083063 Iustin Pop
def ARGS_FIXED(val):
183 a8083063 Iustin Pop
  """Macro-like function denoting a fixed number of arguments"""
184 a8083063 Iustin Pop
  return -val
185 a8083063 Iustin Pop
186 a8083063 Iustin Pop
187 a8083063 Iustin Pop
def ARGS_ATLEAST(val):
188 a8083063 Iustin Pop
  """Macro-like function denoting a minimum number of arguments"""
189 a8083063 Iustin Pop
  return val
190 a8083063 Iustin Pop
191 a8083063 Iustin Pop
192 a8083063 Iustin Pop
ARGS_NONE = None
193 a8083063 Iustin Pop
ARGS_ONE = ARGS_FIXED(1)
194 a8083063 Iustin Pop
ARGS_ANY = ARGS_ATLEAST(0)
195 a8083063 Iustin Pop
196 a8083063 Iustin Pop
197 a8083063 Iustin Pop
def check_unit(option, opt, value):
198 a8083063 Iustin Pop
  try:
199 a8083063 Iustin Pop
    return utils.ParseUnit(value)
200 a8083063 Iustin Pop
  except errors.UnitParseError, err:
201 3ecf6786 Iustin Pop
    raise OptionValueError("option %s: %s" % (opt, err))
202 a8083063 Iustin Pop
203 a8083063 Iustin Pop
204 a8083063 Iustin Pop
class CliOption(Option):
205 a8083063 Iustin Pop
  TYPES = Option.TYPES + ("unit",)
206 a8083063 Iustin Pop
  TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
207 a8083063 Iustin Pop
  TYPE_CHECKER["unit"] = check_unit
208 a8083063 Iustin Pop
209 a8083063 Iustin Pop
210 a8083063 Iustin Pop
# optparse.py sets make_option, so we do it for our own option class, too
211 a8083063 Iustin Pop
cli_option = CliOption
212 a8083063 Iustin Pop
213 a8083063 Iustin Pop
214 a8083063 Iustin Pop
def _ParseArgs(argv, commands):
215 a8083063 Iustin Pop
  """Parses the command line and return the function which must be
216 a8083063 Iustin Pop
  executed together with its arguments
217 a8083063 Iustin Pop

218 a8083063 Iustin Pop
  Arguments:
219 a8083063 Iustin Pop
    argv: the command line
220 a8083063 Iustin Pop

221 a8083063 Iustin Pop
    commands: dictionary with special contents, see the design doc for
222 a8083063 Iustin Pop
    cmdline handling
223 098c0958 Michael Hanselmann

224 a8083063 Iustin Pop
  """
225 a8083063 Iustin Pop
  if len(argv) == 0:
226 a8083063 Iustin Pop
    binary = "<command>"
227 a8083063 Iustin Pop
  else:
228 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
231 a8083063 Iustin Pop
    print "%s (ganeti) %s" % (binary, constants.RELEASE_VERSION)
232 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
233 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
234 a8083063 Iustin Pop
    sys.exit(0)
235 a8083063 Iustin Pop
236 a8083063 Iustin Pop
  if len(argv) < 2 or argv[1] not in commands.keys():
237 a8083063 Iustin Pop
    # let's do a nice thing
238 a8083063 Iustin Pop
    sortedcmds = commands.keys()
239 a8083063 Iustin Pop
    sortedcmds.sort()
240 a8083063 Iustin Pop
    print ("Usage: %(bin)s {command} [options...] [argument...]"
241 a8083063 Iustin Pop
           "\n%(bin)s <command> --help to see details, or"
242 a8083063 Iustin Pop
           " man %(bin)s\n" % {"bin": binary})
243 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
244 a8083063 Iustin Pop
    mlen = max([len(" %s %s" % (cmd, commands[cmd][3])) for cmd in commands])
245 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
246 a8083063 Iustin Pop
    # and format a nice command list
247 a8083063 Iustin Pop
    print "Commands:"
248 a8083063 Iustin Pop
    for cmd in sortedcmds:
249 a8083063 Iustin Pop
      cmdstr = " %s %s" % (cmd, commands[cmd][3])
250 a8083063 Iustin Pop
      help_text = commands[cmd][4]
251 a8083063 Iustin Pop
      help_lines = textwrap.wrap(help_text, 79-3-mlen)
252 a8083063 Iustin Pop
      print "%-*s - %s" % (mlen, cmdstr,
253 a8083063 Iustin Pop
                                          help_lines.pop(0))
254 a8083063 Iustin Pop
      for line in help_lines:
255 a8083063 Iustin Pop
        print "%-*s   %s" % (mlen, "", line)
256 a8083063 Iustin Pop
    print
257 a8083063 Iustin Pop
    return None, None, None
258 a8083063 Iustin Pop
  cmd = argv.pop(1)
259 a8083063 Iustin Pop
  func, nargs, parser_opts, usage, description = commands[cmd]
260 a8083063 Iustin Pop
  parser_opts.append(_LOCK_OPT)
261 a8083063 Iustin Pop
  parser = OptionParser(option_list=parser_opts,
262 a8083063 Iustin Pop
                        description=description,
263 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
264 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
265 a8083063 Iustin Pop
  parser.disable_interspersed_args()
266 a8083063 Iustin Pop
  options, args = parser.parse_args()
267 a8083063 Iustin Pop
  if nargs is None:
268 a8083063 Iustin Pop
    if len(args) != 0:
269 a8083063 Iustin Pop
      print >> sys.stderr, ("Error: Command %s expects no arguments" % cmd)
270 a8083063 Iustin Pop
      return None, None, None
271 a8083063 Iustin Pop
  elif nargs < 0 and len(args) != -nargs:
272 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects %d argument(s)" %
273 a8083063 Iustin Pop
                         (cmd, -nargs))
274 a8083063 Iustin Pop
    return None, None, None
275 a8083063 Iustin Pop
  elif nargs >= 0 and len(args) < nargs:
276 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects at least %d argument(s)" %
277 a8083063 Iustin Pop
                         (cmd, nargs))
278 a8083063 Iustin Pop
    return None, None, None
279 a8083063 Iustin Pop
280 a8083063 Iustin Pop
  return func, options, args
281 a8083063 Iustin Pop
282 a8083063 Iustin Pop
283 47988778 Iustin Pop
def AskUser(text, choices=None):
284 47988778 Iustin Pop
  """Ask the user a question.
285 a8083063 Iustin Pop

286 a8083063 Iustin Pop
  Args:
287 47988778 Iustin Pop
    text - the question to ask.
288 a8083063 Iustin Pop

289 47988778 Iustin Pop
    choices - list with elements tuples (input_char, return_value,
290 47988778 Iustin Pop
    description); if not given, it will default to: [('y', True,
291 47988778 Iustin Pop
    'Perform the operation'), ('n', False, 'Do no do the operation')];
292 47988778 Iustin Pop
    note that the '?' char is reserved for help
293 47988778 Iustin Pop

294 47988778 Iustin Pop
  Returns: one of the return values from the choices list; if input is
295 47988778 Iustin Pop
  not possible (i.e. not running with a tty, we return the last entry
296 47988778 Iustin Pop
  from the list
297 a8083063 Iustin Pop

298 a8083063 Iustin Pop
  """
299 47988778 Iustin Pop
  if choices is None:
300 47988778 Iustin Pop
    choices = [('y', True, 'Perform the operation'),
301 47988778 Iustin Pop
               ('n', False, 'Do not perform the operation')]
302 47988778 Iustin Pop
  if not choices or not isinstance(choices, list):
303 47988778 Iustin Pop
    raise errors.ProgrammerError("Invalid choiches argument to AskUser")
304 47988778 Iustin Pop
  for entry in choices:
305 47988778 Iustin Pop
    if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
306 47988778 Iustin Pop
      raise errors.ProgrammerError("Invalid choiches element to AskUser")
307 47988778 Iustin Pop
308 47988778 Iustin Pop
  answer = choices[-1][1]
309 47988778 Iustin Pop
  new_text = []
310 47988778 Iustin Pop
  for line in text.splitlines():
311 47988778 Iustin Pop
    new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
312 47988778 Iustin Pop
  text = "\n".join(new_text)
313 a8083063 Iustin Pop
  try:
314 a8083063 Iustin Pop
    f = file("/dev/tty", "r+")
315 a8083063 Iustin Pop
  except IOError:
316 47988778 Iustin Pop
    return answer
317 a8083063 Iustin Pop
  try:
318 47988778 Iustin Pop
    chars = [entry[0] for entry in choices]
319 47988778 Iustin Pop
    chars[-1] = "[%s]" % chars[-1]
320 47988778 Iustin Pop
    chars.append('?')
321 47988778 Iustin Pop
    maps = dict([(entry[0], entry[1]) for entry in choices])
322 47988778 Iustin Pop
    while True:
323 47988778 Iustin Pop
      f.write(text)
324 47988778 Iustin Pop
      f.write('\n')
325 47988778 Iustin Pop
      f.write("/".join(chars))
326 47988778 Iustin Pop
      f.write(": ")
327 47988778 Iustin Pop
      line = f.readline(2).strip().lower()
328 47988778 Iustin Pop
      if line in maps:
329 47988778 Iustin Pop
        answer = maps[line]
330 47988778 Iustin Pop
        break
331 47988778 Iustin Pop
      elif line == '?':
332 47988778 Iustin Pop
        for entry in choices:
333 47988778 Iustin Pop
          f.write(" %s - %s\n" % (entry[0], entry[2]))
334 47988778 Iustin Pop
        f.write("\n")
335 47988778 Iustin Pop
        continue
336 a8083063 Iustin Pop
  finally:
337 a8083063 Iustin Pop
    f.close()
338 a8083063 Iustin Pop
  return answer
339 a8083063 Iustin Pop
340 a8083063 Iustin Pop
341 9f33ef86 Iustin Pop
def SubmitOpCode(op, proc=None, feedback_fn=None):
342 a8083063 Iustin Pop
  """Function to submit an opcode.
343 a8083063 Iustin Pop

344 a8083063 Iustin Pop
  This is just a simple wrapper over the construction of the processor
345 a8083063 Iustin Pop
  instance. It should be extended to better handle feedback and
346 a8083063 Iustin Pop
  interaction functions.
347 a8083063 Iustin Pop

348 a8083063 Iustin Pop
  """
349 9f33ef86 Iustin Pop
  if proc is None:
350 9f33ef86 Iustin Pop
    proc = mcpu.Processor()
351 9f33ef86 Iustin Pop
  if feedback_fn is None:
352 9f33ef86 Iustin Pop
    feedback_fn = logger.ToStdout
353 9f33ef86 Iustin Pop
  return proc.ExecOpCode(op, feedback_fn)
354 a8083063 Iustin Pop
355 a8083063 Iustin Pop
356 73702ee7 Iustin Pop
def FormatError(err):
357 73702ee7 Iustin Pop
  """Return a formatted error message for a given error.
358 73702ee7 Iustin Pop

359 73702ee7 Iustin Pop
  This function takes an exception instance and returns a tuple
360 73702ee7 Iustin Pop
  consisting of two values: first, the recommended exit code, and
361 73702ee7 Iustin Pop
  second, a string describing the error message (not
362 73702ee7 Iustin Pop
  newline-terminated).
363 73702ee7 Iustin Pop

364 73702ee7 Iustin Pop
  """
365 73702ee7 Iustin Pop
  retcode = 1
366 73702ee7 Iustin Pop
  obuf = StringIO()
367 73702ee7 Iustin Pop
  if isinstance(err, errors.ConfigurationError):
368 73702ee7 Iustin Pop
    msg = "Corrupt configuration file: %s" % err
369 73702ee7 Iustin Pop
    logger.Error(msg)
370 73702ee7 Iustin Pop
    obuf.write(msg + "\n")
371 73702ee7 Iustin Pop
    obuf.write("Aborting.")
372 73702ee7 Iustin Pop
    retcode = 2
373 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksAbort):
374 73702ee7 Iustin Pop
    obuf.write("Failure: hooks execution failed:\n")
375 73702ee7 Iustin Pop
    for node, script, out in err.args[0]:
376 73702ee7 Iustin Pop
      if out:
377 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s, output: %s\n" %
378 73702ee7 Iustin Pop
                   (node, script, out))
379 73702ee7 Iustin Pop
      else:
380 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s (no output)\n" %
381 73702ee7 Iustin Pop
                   (node, script))
382 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksFailure):
383 73702ee7 Iustin Pop
    obuf.write("Failure: hooks general failure: %s" % str(err))
384 73702ee7 Iustin Pop
  elif isinstance(err, errors.ResolverError):
385 73702ee7 Iustin Pop
    this_host = utils.HostInfo.SysName()
386 73702ee7 Iustin Pop
    if err.args[0] == this_host:
387 73702ee7 Iustin Pop
      msg = "Failure: can't resolve my own hostname ('%s')"
388 73702ee7 Iustin Pop
    else:
389 73702ee7 Iustin Pop
      msg = "Failure: can't resolve hostname '%s'"
390 73702ee7 Iustin Pop
    obuf.write(msg % err.args[0])
391 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpPrereqError):
392 73702ee7 Iustin Pop
    obuf.write("Failure: prerequisites not met for this"
393 73702ee7 Iustin Pop
               " operation:\n%s" % str(err))
394 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpExecError):
395 73702ee7 Iustin Pop
    obuf.write("Failure: command execution error:\n%s" % str(err))
396 73702ee7 Iustin Pop
  elif isinstance(err, errors.TagError):
397 73702ee7 Iustin Pop
    obuf.write("Failure: invalid tag(s) given:\n%s" % str(err))
398 73702ee7 Iustin Pop
  elif isinstance(err, errors.GenericError):
399 73702ee7 Iustin Pop
    obuf.write("Unhandled Ganeti error: %s" % str(err))
400 73702ee7 Iustin Pop
  else:
401 73702ee7 Iustin Pop
    obuf.write("Unhandled exception: %s" % str(err))
402 73702ee7 Iustin Pop
  return retcode, obuf.getvalue().rstrip('\n')
403 73702ee7 Iustin Pop
404 73702ee7 Iustin Pop
405 334d1483 Iustin Pop
def GenericMain(commands, override=None):
406 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
407 a8083063 Iustin Pop

408 334d1483 Iustin Pop
  Arguments:
409 334d1483 Iustin Pop
    - commands: a dictionary with a special structure, see the design doc
410 334d1483 Iustin Pop
                for command line handling.
411 334d1483 Iustin Pop
    - override: if not None, we expect a dictionary with keys that will
412 334d1483 Iustin Pop
                override command line options; this can be used to pass
413 334d1483 Iustin Pop
                options from the scripts to generic functions
414 a8083063 Iustin Pop

415 a8083063 Iustin Pop
  """
416 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
417 a8083063 Iustin Pop
  if sys.argv:
418 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
419 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
420 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
421 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
422 a8083063 Iustin Pop
    else:
423 a8083063 Iustin Pop
      old_cmdline = ""
424 a8083063 Iustin Pop
  else:
425 a8083063 Iustin Pop
    binary = "<unknown program>"
426 a8083063 Iustin Pop
    old_cmdline = ""
427 a8083063 Iustin Pop
428 a8083063 Iustin Pop
  func, options, args = _ParseArgs(sys.argv, commands)
429 a8083063 Iustin Pop
  if func is None: # parse error
430 a8083063 Iustin Pop
    return 1
431 a8083063 Iustin Pop
432 334d1483 Iustin Pop
  if override is not None:
433 334d1483 Iustin Pop
    for key, val in override.iteritems():
434 334d1483 Iustin Pop
      setattr(options, key, val)
435 334d1483 Iustin Pop
436 a8083063 Iustin Pop
  logger.SetupLogging(debug=options.debug, program=binary)
437 a8083063 Iustin Pop
438 a8083063 Iustin Pop
  try:
439 a8083063 Iustin Pop
    utils.Lock('cmd', max_retries=options.lock_retries, debug=options.debug)
440 a8083063 Iustin Pop
  except errors.LockError, err:
441 a8083063 Iustin Pop
    logger.ToStderr(str(err))
442 a8083063 Iustin Pop
    return 1
443 a8083063 Iustin Pop
444 a8083063 Iustin Pop
  if old_cmdline:
445 a8083063 Iustin Pop
    logger.Info("run with arguments '%s'" % old_cmdline)
446 a8083063 Iustin Pop
  else:
447 a8083063 Iustin Pop
    logger.Info("run with no arguments")
448 a8083063 Iustin Pop
449 a8083063 Iustin Pop
  try:
450 a8083063 Iustin Pop
    try:
451 a8083063 Iustin Pop
      result = func(options, args)
452 73702ee7 Iustin Pop
    except errors.GenericError, err:
453 73702ee7 Iustin Pop
      result, err_msg = FormatError(err)
454 73702ee7 Iustin Pop
      logger.ToStderr(err_msg)
455 a8083063 Iustin Pop
  finally:
456 a8083063 Iustin Pop
    utils.Unlock('cmd')
457 a8083063 Iustin Pop
    utils.LockCleanup()
458 a8083063 Iustin Pop
459 a8083063 Iustin Pop
  return result
460 137161c9 Michael Hanselmann
461 137161c9 Michael Hanselmann
462 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
463 16be8703 Iustin Pop
                  numfields=None, unitfields=None):
464 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
465 137161c9 Michael Hanselmann

466 137161c9 Michael Hanselmann
  Args:
467 137161c9 Michael Hanselmann
    headers: Dict of header titles or None if no headers should be shown
468 137161c9 Michael Hanselmann
    fields: List of fields to show
469 137161c9 Michael Hanselmann
    separator: String used to separate fields or None for spaces
470 137161c9 Michael Hanselmann
    data: Data to be printed
471 137161c9 Michael Hanselmann
    numfields: List of fields to be aligned to right
472 137161c9 Michael Hanselmann
    unitfields: List of fields to be formatted as units
473 137161c9 Michael Hanselmann

474 137161c9 Michael Hanselmann
  """
475 137161c9 Michael Hanselmann
  if numfields is None:
476 137161c9 Michael Hanselmann
    numfields = []
477 137161c9 Michael Hanselmann
  if unitfields is None:
478 137161c9 Michael Hanselmann
    unitfields = []
479 137161c9 Michael Hanselmann
480 137161c9 Michael Hanselmann
  format_fields = []
481 137161c9 Michael Hanselmann
  for field in fields:
482 137161c9 Michael Hanselmann
    if separator is not None:
483 137161c9 Michael Hanselmann
      format_fields.append("%s")
484 137161c9 Michael Hanselmann
    elif field in numfields:
485 137161c9 Michael Hanselmann
      format_fields.append("%*s")
486 137161c9 Michael Hanselmann
    else:
487 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
488 137161c9 Michael Hanselmann
489 137161c9 Michael Hanselmann
  if separator is None:
490 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
491 137161c9 Michael Hanselmann
    format = ' '.join(format_fields)
492 137161c9 Michael Hanselmann
  else:
493 137161c9 Michael Hanselmann
    format = separator.replace("%", "%%").join(format_fields)
494 137161c9 Michael Hanselmann
495 137161c9 Michael Hanselmann
  for row in data:
496 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
497 137161c9 Michael Hanselmann
      if fields[idx] in unitfields:
498 137161c9 Michael Hanselmann
        try:
499 137161c9 Michael Hanselmann
          val = int(val)
500 137161c9 Michael Hanselmann
        except ValueError:
501 137161c9 Michael Hanselmann
          pass
502 137161c9 Michael Hanselmann
        else:
503 137161c9 Michael Hanselmann
          val = row[idx] = utils.FormatUnit(val)
504 137161c9 Michael Hanselmann
      if separator is None:
505 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
506 137161c9 Michael Hanselmann
507 16be8703 Iustin Pop
  result = []
508 137161c9 Michael Hanselmann
  if headers:
509 137161c9 Michael Hanselmann
    args = []
510 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
511 137161c9 Michael Hanselmann
      hdr = headers[name]
512 137161c9 Michael Hanselmann
      if separator is None:
513 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
514 137161c9 Michael Hanselmann
        args.append(mlens[idx])
515 137161c9 Michael Hanselmann
      args.append(hdr)
516 16be8703 Iustin Pop
    result.append(format % tuple(args))
517 137161c9 Michael Hanselmann
518 137161c9 Michael Hanselmann
  for line in data:
519 137161c9 Michael Hanselmann
    args = []
520 137161c9 Michael Hanselmann
    for idx in xrange(len(fields)):
521 137161c9 Michael Hanselmann
      if separator is None:
522 137161c9 Michael Hanselmann
        args.append(mlens[idx])
523 137161c9 Michael Hanselmann
      args.append(line[idx])
524 16be8703 Iustin Pop
    result.append(format % tuple(args))
525 16be8703 Iustin Pop
526 16be8703 Iustin Pop
  return result