Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ cdb08f44

History | View | Annotate | Download (17.2 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 60d49723 Michael Hanselmann
           "FormatError", "SplitNodeOption"
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 d8a4b51d Iustin Pop
                         type="string", help="Comma separated list of"
171 d8a4b51d Iustin Pop
                         " output fields",
172 dcb93971 Michael Hanselmann
                         metavar="FIELDS")
173 dcb93971 Michael Hanselmann
174 fe7b0351 Michael Hanselmann
FORCE_OPT = make_option("-f", "--force", dest="force", action="store_true",
175 fe7b0351 Michael Hanselmann
                        default=False, help="Force the operation")
176 fe7b0351 Michael Hanselmann
177 a8083063 Iustin Pop
_LOCK_OPT = make_option("--lock-retries", default=None,
178 a8083063 Iustin Pop
                        type="int", help=SUPPRESS_HELP)
179 a8083063 Iustin Pop
180 810c50b7 Iustin Pop
TAG_SRC_OPT = make_option("--from", dest="tags_source",
181 810c50b7 Iustin Pop
                          default=None, help="File with tag names")
182 810c50b7 Iustin Pop
183 60d49723 Michael Hanselmann
184 a8083063 Iustin Pop
def ARGS_FIXED(val):
185 a8083063 Iustin Pop
  """Macro-like function denoting a fixed number of arguments"""
186 a8083063 Iustin Pop
  return -val
187 a8083063 Iustin Pop
188 a8083063 Iustin Pop
189 a8083063 Iustin Pop
def ARGS_ATLEAST(val):
190 a8083063 Iustin Pop
  """Macro-like function denoting a minimum number of arguments"""
191 a8083063 Iustin Pop
  return val
192 a8083063 Iustin Pop
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
ARGS_NONE = None
195 a8083063 Iustin Pop
ARGS_ONE = ARGS_FIXED(1)
196 a8083063 Iustin Pop
ARGS_ANY = ARGS_ATLEAST(0)
197 a8083063 Iustin Pop
198 a8083063 Iustin Pop
199 a8083063 Iustin Pop
def check_unit(option, opt, value):
200 65fe4693 Iustin Pop
  """OptParsers custom converter for units.
201 65fe4693 Iustin Pop

202 65fe4693 Iustin Pop
  """
203 a8083063 Iustin Pop
  try:
204 a8083063 Iustin Pop
    return utils.ParseUnit(value)
205 a8083063 Iustin Pop
  except errors.UnitParseError, err:
206 3ecf6786 Iustin Pop
    raise OptionValueError("option %s: %s" % (opt, err))
207 a8083063 Iustin Pop
208 a8083063 Iustin Pop
209 a8083063 Iustin Pop
class CliOption(Option):
210 65fe4693 Iustin Pop
  """Custom option class for optparse.
211 65fe4693 Iustin Pop

212 65fe4693 Iustin Pop
  """
213 a8083063 Iustin Pop
  TYPES = Option.TYPES + ("unit",)
214 a8083063 Iustin Pop
  TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
215 a8083063 Iustin Pop
  TYPE_CHECKER["unit"] = check_unit
216 a8083063 Iustin Pop
217 a8083063 Iustin Pop
218 a8083063 Iustin Pop
# optparse.py sets make_option, so we do it for our own option class, too
219 a8083063 Iustin Pop
cli_option = CliOption
220 a8083063 Iustin Pop
221 a8083063 Iustin Pop
222 de47cf8f Guido Trotter
def _ParseArgs(argv, commands, aliases):
223 a8083063 Iustin Pop
  """Parses the command line and return the function which must be
224 a8083063 Iustin Pop
  executed together with its arguments
225 a8083063 Iustin Pop

226 a8083063 Iustin Pop
  Arguments:
227 a8083063 Iustin Pop
    argv: the command line
228 a8083063 Iustin Pop

229 a8083063 Iustin Pop
    commands: dictionary with special contents, see the design doc for
230 a8083063 Iustin Pop
    cmdline handling
231 de47cf8f Guido Trotter
    aliases: dictionary with command aliases {'alias': 'target, ...}
232 098c0958 Michael Hanselmann

233 a8083063 Iustin Pop
  """
234 a8083063 Iustin Pop
  if len(argv) == 0:
235 a8083063 Iustin Pop
    binary = "<command>"
236 a8083063 Iustin Pop
  else:
237 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
238 a8083063 Iustin Pop
239 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
240 a8083063 Iustin Pop
    print "%s (ganeti) %s" % (binary, constants.RELEASE_VERSION)
241 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
242 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
243 a8083063 Iustin Pop
    sys.exit(0)
244 a8083063 Iustin Pop
245 de47cf8f Guido Trotter
  if len(argv) < 2 or not (argv[1] in commands or
246 70a35b6f Guido Trotter
                           argv[1] in aliases):
247 a8083063 Iustin Pop
    # let's do a nice thing
248 a8083063 Iustin Pop
    sortedcmds = commands.keys()
249 a8083063 Iustin Pop
    sortedcmds.sort()
250 a8083063 Iustin Pop
    print ("Usage: %(bin)s {command} [options...] [argument...]"
251 a8083063 Iustin Pop
           "\n%(bin)s <command> --help to see details, or"
252 a8083063 Iustin Pop
           " man %(bin)s\n" % {"bin": binary})
253 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
254 a8083063 Iustin Pop
    mlen = max([len(" %s %s" % (cmd, commands[cmd][3])) for cmd in commands])
255 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
256 a8083063 Iustin Pop
    # and format a nice command list
257 a8083063 Iustin Pop
    print "Commands:"
258 a8083063 Iustin Pop
    for cmd in sortedcmds:
259 a8083063 Iustin Pop
      cmdstr = " %s %s" % (cmd, commands[cmd][3])
260 a8083063 Iustin Pop
      help_text = commands[cmd][4]
261 a8083063 Iustin Pop
      help_lines = textwrap.wrap(help_text, 79-3-mlen)
262 a8083063 Iustin Pop
      print "%-*s - %s" % (mlen, cmdstr,
263 a8083063 Iustin Pop
                                          help_lines.pop(0))
264 a8083063 Iustin Pop
      for line in help_lines:
265 a8083063 Iustin Pop
        print "%-*s   %s" % (mlen, "", line)
266 a8083063 Iustin Pop
    print
267 a8083063 Iustin Pop
    return None, None, None
268 de47cf8f Guido Trotter
269 de47cf8f Guido Trotter
  # get command, unalias it, and look it up in commands
270 a8083063 Iustin Pop
  cmd = argv.pop(1)
271 de47cf8f Guido Trotter
  if cmd in aliases:
272 de47cf8f Guido Trotter
    if cmd in commands:
273 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' overrides an existing"
274 de47cf8f Guido Trotter
                                   " command" % cmd)
275 de47cf8f Guido Trotter
276 de47cf8f Guido Trotter
    if aliases[cmd] not in commands:
277 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' maps to non-existing"
278 de47cf8f Guido Trotter
                                   " command '%s'" % (cmd, aliases[cmd]))
279 de47cf8f Guido Trotter
280 de47cf8f Guido Trotter
    cmd = aliases[cmd]
281 de47cf8f Guido Trotter
282 a8083063 Iustin Pop
  func, nargs, parser_opts, usage, description = commands[cmd]
283 a8083063 Iustin Pop
  parser_opts.append(_LOCK_OPT)
284 a8083063 Iustin Pop
  parser = OptionParser(option_list=parser_opts,
285 a8083063 Iustin Pop
                        description=description,
286 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
287 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
288 a8083063 Iustin Pop
  parser.disable_interspersed_args()
289 a8083063 Iustin Pop
  options, args = parser.parse_args()
290 a8083063 Iustin Pop
  if nargs is None:
291 a8083063 Iustin Pop
    if len(args) != 0:
292 a8083063 Iustin Pop
      print >> sys.stderr, ("Error: Command %s expects no arguments" % cmd)
293 a8083063 Iustin Pop
      return None, None, None
294 a8083063 Iustin Pop
  elif nargs < 0 and len(args) != -nargs:
295 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects %d argument(s)" %
296 a8083063 Iustin Pop
                         (cmd, -nargs))
297 a8083063 Iustin Pop
    return None, None, None
298 a8083063 Iustin Pop
  elif nargs >= 0 and len(args) < nargs:
299 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects at least %d argument(s)" %
300 a8083063 Iustin Pop
                         (cmd, nargs))
301 a8083063 Iustin Pop
    return None, None, None
302 a8083063 Iustin Pop
303 a8083063 Iustin Pop
  return func, options, args
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
306 60d49723 Michael Hanselmann
def SplitNodeOption(value):
307 60d49723 Michael Hanselmann
  """Splits the value of a --node option.
308 60d49723 Michael Hanselmann

309 60d49723 Michael Hanselmann
  """
310 60d49723 Michael Hanselmann
  if value and ':' in value:
311 60d49723 Michael Hanselmann
    return value.split(':', 1)
312 60d49723 Michael Hanselmann
  else:
313 60d49723 Michael Hanselmann
    return (value, None)
314 60d49723 Michael Hanselmann
315 60d49723 Michael Hanselmann
316 47988778 Iustin Pop
def AskUser(text, choices=None):
317 47988778 Iustin Pop
  """Ask the user a question.
318 a8083063 Iustin Pop

319 a8083063 Iustin Pop
  Args:
320 47988778 Iustin Pop
    text - the question to ask.
321 a8083063 Iustin Pop

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

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

331 a8083063 Iustin Pop
  """
332 47988778 Iustin Pop
  if choices is None:
333 47988778 Iustin Pop
    choices = [('y', True, 'Perform the operation'),
334 47988778 Iustin Pop
               ('n', False, 'Do not perform the operation')]
335 47988778 Iustin Pop
  if not choices or not isinstance(choices, list):
336 47988778 Iustin Pop
    raise errors.ProgrammerError("Invalid choiches argument to AskUser")
337 47988778 Iustin Pop
  for entry in choices:
338 47988778 Iustin Pop
    if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
339 47988778 Iustin Pop
      raise errors.ProgrammerError("Invalid choiches element to AskUser")
340 47988778 Iustin Pop
341 47988778 Iustin Pop
  answer = choices[-1][1]
342 47988778 Iustin Pop
  new_text = []
343 47988778 Iustin Pop
  for line in text.splitlines():
344 47988778 Iustin Pop
    new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
345 47988778 Iustin Pop
  text = "\n".join(new_text)
346 a8083063 Iustin Pop
  try:
347 3023170f Iustin Pop
    f = file("/dev/tty", "a+")
348 a8083063 Iustin Pop
  except IOError:
349 47988778 Iustin Pop
    return answer
350 a8083063 Iustin Pop
  try:
351 47988778 Iustin Pop
    chars = [entry[0] for entry in choices]
352 47988778 Iustin Pop
    chars[-1] = "[%s]" % chars[-1]
353 47988778 Iustin Pop
    chars.append('?')
354 47988778 Iustin Pop
    maps = dict([(entry[0], entry[1]) for entry in choices])
355 47988778 Iustin Pop
    while True:
356 47988778 Iustin Pop
      f.write(text)
357 47988778 Iustin Pop
      f.write('\n')
358 47988778 Iustin Pop
      f.write("/".join(chars))
359 47988778 Iustin Pop
      f.write(": ")
360 47988778 Iustin Pop
      line = f.readline(2).strip().lower()
361 47988778 Iustin Pop
      if line in maps:
362 47988778 Iustin Pop
        answer = maps[line]
363 47988778 Iustin Pop
        break
364 47988778 Iustin Pop
      elif line == '?':
365 47988778 Iustin Pop
        for entry in choices:
366 47988778 Iustin Pop
          f.write(" %s - %s\n" % (entry[0], entry[2]))
367 47988778 Iustin Pop
        f.write("\n")
368 47988778 Iustin Pop
        continue
369 a8083063 Iustin Pop
  finally:
370 a8083063 Iustin Pop
    f.close()
371 a8083063 Iustin Pop
  return answer
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
374 9f33ef86 Iustin Pop
def SubmitOpCode(op, proc=None, feedback_fn=None):
375 a8083063 Iustin Pop
  """Function to submit an opcode.
376 a8083063 Iustin Pop

377 a8083063 Iustin Pop
  This is just a simple wrapper over the construction of the processor
378 a8083063 Iustin Pop
  instance. It should be extended to better handle feedback and
379 a8083063 Iustin Pop
  interaction functions.
380 a8083063 Iustin Pop

381 a8083063 Iustin Pop
  """
382 9f33ef86 Iustin Pop
  if feedback_fn is None:
383 9f33ef86 Iustin Pop
    feedback_fn = logger.ToStdout
384 1a8c0ce1 Iustin Pop
  if proc is None:
385 1a8c0ce1 Iustin Pop
    proc = mcpu.Processor(feedback=feedback_fn)
386 1a8c0ce1 Iustin Pop
  return proc.ExecOpCode(op)
387 a8083063 Iustin Pop
388 a8083063 Iustin Pop
389 73702ee7 Iustin Pop
def FormatError(err):
390 73702ee7 Iustin Pop
  """Return a formatted error message for a given error.
391 73702ee7 Iustin Pop

392 73702ee7 Iustin Pop
  This function takes an exception instance and returns a tuple
393 73702ee7 Iustin Pop
  consisting of two values: first, the recommended exit code, and
394 73702ee7 Iustin Pop
  second, a string describing the error message (not
395 73702ee7 Iustin Pop
  newline-terminated).
396 73702ee7 Iustin Pop

397 73702ee7 Iustin Pop
  """
398 73702ee7 Iustin Pop
  retcode = 1
399 73702ee7 Iustin Pop
  obuf = StringIO()
400 e2e521d0 Iustin Pop
  msg = str(err)
401 73702ee7 Iustin Pop
  if isinstance(err, errors.ConfigurationError):
402 e2e521d0 Iustin Pop
    txt = "Corrupt configuration file: %s" % msg
403 e2e521d0 Iustin Pop
    logger.Error(txt)
404 e2e521d0 Iustin Pop
    obuf.write(txt + "\n")
405 73702ee7 Iustin Pop
    obuf.write("Aborting.")
406 73702ee7 Iustin Pop
    retcode = 2
407 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksAbort):
408 73702ee7 Iustin Pop
    obuf.write("Failure: hooks execution failed:\n")
409 73702ee7 Iustin Pop
    for node, script, out in err.args[0]:
410 73702ee7 Iustin Pop
      if out:
411 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s, output: %s\n" %
412 73702ee7 Iustin Pop
                   (node, script, out))
413 73702ee7 Iustin Pop
      else:
414 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s (no output)\n" %
415 73702ee7 Iustin Pop
                   (node, script))
416 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksFailure):
417 e2e521d0 Iustin Pop
    obuf.write("Failure: hooks general failure: %s" % msg)
418 73702ee7 Iustin Pop
  elif isinstance(err, errors.ResolverError):
419 73702ee7 Iustin Pop
    this_host = utils.HostInfo.SysName()
420 73702ee7 Iustin Pop
    if err.args[0] == this_host:
421 73702ee7 Iustin Pop
      msg = "Failure: can't resolve my own hostname ('%s')"
422 73702ee7 Iustin Pop
    else:
423 73702ee7 Iustin Pop
      msg = "Failure: can't resolve hostname '%s'"
424 73702ee7 Iustin Pop
    obuf.write(msg % err.args[0])
425 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpPrereqError):
426 73702ee7 Iustin Pop
    obuf.write("Failure: prerequisites not met for this"
427 e2e521d0 Iustin Pop
               " operation:\n%s" % msg)
428 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpExecError):
429 e2e521d0 Iustin Pop
    obuf.write("Failure: command execution error:\n%s" % msg)
430 73702ee7 Iustin Pop
  elif isinstance(err, errors.TagError):
431 e2e521d0 Iustin Pop
    obuf.write("Failure: invalid tag(s) given:\n%s" % msg)
432 73702ee7 Iustin Pop
  elif isinstance(err, errors.GenericError):
433 e2e521d0 Iustin Pop
    obuf.write("Unhandled Ganeti error: %s" % msg)
434 73702ee7 Iustin Pop
  else:
435 e2e521d0 Iustin Pop
    obuf.write("Unhandled exception: %s" % msg)
436 73702ee7 Iustin Pop
  return retcode, obuf.getvalue().rstrip('\n')
437 73702ee7 Iustin Pop
438 73702ee7 Iustin Pop
439 de47cf8f Guido Trotter
def GenericMain(commands, override=None, aliases=None):
440 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
441 a8083063 Iustin Pop

442 334d1483 Iustin Pop
  Arguments:
443 334d1483 Iustin Pop
    - commands: a dictionary with a special structure, see the design doc
444 334d1483 Iustin Pop
                for command line handling.
445 334d1483 Iustin Pop
    - override: if not None, we expect a dictionary with keys that will
446 334d1483 Iustin Pop
                override command line options; this can be used to pass
447 334d1483 Iustin Pop
                options from the scripts to generic functions
448 de47cf8f Guido Trotter
    - aliases: dictionary with command aliases {'alias': 'target, ...}
449 a8083063 Iustin Pop

450 a8083063 Iustin Pop
  """
451 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
452 a8083063 Iustin Pop
  if sys.argv:
453 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
454 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
455 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
456 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
457 a8083063 Iustin Pop
    else:
458 a8083063 Iustin Pop
      old_cmdline = ""
459 a8083063 Iustin Pop
  else:
460 a8083063 Iustin Pop
    binary = "<unknown program>"
461 a8083063 Iustin Pop
    old_cmdline = ""
462 a8083063 Iustin Pop
463 de47cf8f Guido Trotter
  if aliases is None:
464 de47cf8f Guido Trotter
    aliases = {}
465 de47cf8f Guido Trotter
466 de47cf8f Guido Trotter
  func, options, args = _ParseArgs(sys.argv, commands, aliases)
467 a8083063 Iustin Pop
  if func is None: # parse error
468 a8083063 Iustin Pop
    return 1
469 a8083063 Iustin Pop
470 334d1483 Iustin Pop
  if override is not None:
471 334d1483 Iustin Pop
    for key, val in override.iteritems():
472 334d1483 Iustin Pop
      setattr(options, key, val)
473 334d1483 Iustin Pop
474 a8083063 Iustin Pop
  logger.SetupLogging(debug=options.debug, program=binary)
475 a8083063 Iustin Pop
476 f362096f Iustin Pop
  utils.debug = options.debug
477 a8083063 Iustin Pop
  try:
478 a8083063 Iustin Pop
    utils.Lock('cmd', max_retries=options.lock_retries, debug=options.debug)
479 a8083063 Iustin Pop
  except errors.LockError, err:
480 a8083063 Iustin Pop
    logger.ToStderr(str(err))
481 a8083063 Iustin Pop
    return 1
482 36c68ff1 Iustin Pop
  except KeyboardInterrupt:
483 36c68ff1 Iustin Pop
    logger.ToStderr("Aborting.")
484 36c68ff1 Iustin Pop
    return 1
485 a8083063 Iustin Pop
486 a8083063 Iustin Pop
  if old_cmdline:
487 a8083063 Iustin Pop
    logger.Info("run with arguments '%s'" % old_cmdline)
488 a8083063 Iustin Pop
  else:
489 a8083063 Iustin Pop
    logger.Info("run with no arguments")
490 a8083063 Iustin Pop
491 a8083063 Iustin Pop
  try:
492 a8083063 Iustin Pop
    try:
493 a8083063 Iustin Pop
      result = func(options, args)
494 73702ee7 Iustin Pop
    except errors.GenericError, err:
495 73702ee7 Iustin Pop
      result, err_msg = FormatError(err)
496 73702ee7 Iustin Pop
      logger.ToStderr(err_msg)
497 a8083063 Iustin Pop
  finally:
498 a8083063 Iustin Pop
    utils.Unlock('cmd')
499 a8083063 Iustin Pop
    utils.LockCleanup()
500 a8083063 Iustin Pop
501 a8083063 Iustin Pop
  return result
502 137161c9 Michael Hanselmann
503 137161c9 Michael Hanselmann
504 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
505 16be8703 Iustin Pop
                  numfields=None, unitfields=None):
506 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
507 137161c9 Michael Hanselmann

508 137161c9 Michael Hanselmann
  Args:
509 137161c9 Michael Hanselmann
    headers: Dict of header titles or None if no headers should be shown
510 137161c9 Michael Hanselmann
    fields: List of fields to show
511 137161c9 Michael Hanselmann
    separator: String used to separate fields or None for spaces
512 137161c9 Michael Hanselmann
    data: Data to be printed
513 137161c9 Michael Hanselmann
    numfields: List of fields to be aligned to right
514 137161c9 Michael Hanselmann
    unitfields: List of fields to be formatted as units
515 137161c9 Michael Hanselmann

516 137161c9 Michael Hanselmann
  """
517 137161c9 Michael Hanselmann
  if numfields is None:
518 137161c9 Michael Hanselmann
    numfields = []
519 137161c9 Michael Hanselmann
  if unitfields is None:
520 137161c9 Michael Hanselmann
    unitfields = []
521 137161c9 Michael Hanselmann
522 137161c9 Michael Hanselmann
  format_fields = []
523 137161c9 Michael Hanselmann
  for field in fields:
524 01ca31ae Iustin Pop
    if headers and field not in headers:
525 01ca31ae Iustin Pop
      raise errors.ProgrammerError("Missing header description for field '%s'"
526 01ca31ae Iustin Pop
                                   % field)
527 137161c9 Michael Hanselmann
    if separator is not None:
528 137161c9 Michael Hanselmann
      format_fields.append("%s")
529 137161c9 Michael Hanselmann
    elif field in numfields:
530 137161c9 Michael Hanselmann
      format_fields.append("%*s")
531 137161c9 Michael Hanselmann
    else:
532 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
533 137161c9 Michael Hanselmann
534 137161c9 Michael Hanselmann
  if separator is None:
535 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
536 137161c9 Michael Hanselmann
    format = ' '.join(format_fields)
537 137161c9 Michael Hanselmann
  else:
538 137161c9 Michael Hanselmann
    format = separator.replace("%", "%%").join(format_fields)
539 137161c9 Michael Hanselmann
540 137161c9 Michael Hanselmann
  for row in data:
541 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
542 137161c9 Michael Hanselmann
      if fields[idx] in unitfields:
543 137161c9 Michael Hanselmann
        try:
544 137161c9 Michael Hanselmann
          val = int(val)
545 137161c9 Michael Hanselmann
        except ValueError:
546 137161c9 Michael Hanselmann
          pass
547 137161c9 Michael Hanselmann
        else:
548 137161c9 Michael Hanselmann
          val = row[idx] = utils.FormatUnit(val)
549 01ca31ae Iustin Pop
      val = row[idx] = str(val)
550 137161c9 Michael Hanselmann
      if separator is None:
551 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
552 137161c9 Michael Hanselmann
553 16be8703 Iustin Pop
  result = []
554 137161c9 Michael Hanselmann
  if headers:
555 137161c9 Michael Hanselmann
    args = []
556 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
557 137161c9 Michael Hanselmann
      hdr = headers[name]
558 137161c9 Michael Hanselmann
      if separator is None:
559 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
560 137161c9 Michael Hanselmann
        args.append(mlens[idx])
561 137161c9 Michael Hanselmann
      args.append(hdr)
562 16be8703 Iustin Pop
    result.append(format % tuple(args))
563 137161c9 Michael Hanselmann
564 137161c9 Michael Hanselmann
  for line in data:
565 137161c9 Michael Hanselmann
    args = []
566 137161c9 Michael Hanselmann
    for idx in xrange(len(fields)):
567 137161c9 Michael Hanselmann
      if separator is None:
568 137161c9 Michael Hanselmann
        args.append(mlens[idx])
569 137161c9 Michael Hanselmann
      args.append(line[idx])
570 16be8703 Iustin Pop
    result.append(format % tuple(args))
571 16be8703 Iustin Pop
572 16be8703 Iustin Pop
  return result