Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ 6605411d

History | View | Annotate | Download (25.1 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 685ee993 Iustin Pop
import time
30 73702ee7 Iustin Pop
from cStringIO import StringIO
31 a8083063 Iustin Pop
32 a8083063 Iustin Pop
from ganeti import utils
33 a8083063 Iustin Pop
from ganeti import logger
34 a8083063 Iustin Pop
from ganeti import errors
35 a8083063 Iustin Pop
from ganeti import constants
36 846baef9 Iustin Pop
from ganeti import opcodes
37 ceab32dd Iustin Pop
from ganeti import luxi
38 b33e986b Iustin Pop
from ganeti import ssconf
39 a8083063 Iustin Pop
40 a8083063 Iustin Pop
from optparse import (OptionParser, make_option, TitledHelpFormatter,
41 38206f3c Iustin Pop
                      Option, OptionValueError)
42 a8083063 Iustin Pop
43 ceab32dd Iustin Pop
__all__ = ["DEBUG_OPT", "NOHDR_OPT", "SEP_OPT", "GenericMain",
44 af30b2fd Michael Hanselmann
           "SubmitOpCode", "GetClient",
45 a8469393 Iustin Pop
           "cli_option", "ikv_option", "keyval_option",
46 a8469393 Iustin Pop
           "GenerateTable", "AskUser",
47 a8083063 Iustin Pop
           "ARGS_NONE", "ARGS_FIXED", "ARGS_ATLEAST", "ARGS_ANY", "ARGS_ONE",
48 94428652 Iustin Pop
           "USEUNITS_OPT", "FIELDS_OPT", "FORCE_OPT", "SUBMIT_OPT",
49 810c50b7 Iustin Pop
           "ListTags", "AddTags", "RemoveTags", "TAG_SRC_OPT",
50 94428652 Iustin Pop
           "FormatError", "SplitNodeOption", "SubmitOrSend",
51 07cd723a Iustin Pop
           "JobSubmittedException", "FormatTimestamp", "ParseTimespec",
52 a604f165 Iustin Pop
           "ValidateBeParams",
53 846baef9 Iustin Pop
           ]
54 846baef9 Iustin Pop
55 846baef9 Iustin Pop
56 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
57 846baef9 Iustin Pop
  """Extract the tag type object.
58 846baef9 Iustin Pop

59 846baef9 Iustin Pop
  Note that this function will modify its args parameter.
60 846baef9 Iustin Pop

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

80 810c50b7 Iustin Pop
  This function will extend the tags with the contents of the file
81 810c50b7 Iustin Pop
  passed in the 'tags_source' attribute of the opts parameter. A file
82 810c50b7 Iustin Pop
  named '-' will be replaced by stdin.
83 810c50b7 Iustin Pop

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

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

114 846baef9 Iustin Pop
  """
115 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
116 846baef9 Iustin Pop
  op = opcodes.OpGetTags(kind=kind, name=name)
117 846baef9 Iustin Pop
  result = SubmitOpCode(op)
118 846baef9 Iustin Pop
  result = list(result)
119 846baef9 Iustin Pop
  result.sort()
120 846baef9 Iustin Pop
  for tag in result:
121 846baef9 Iustin Pop
    print tag
122 846baef9 Iustin Pop
123 846baef9 Iustin Pop
124 846baef9 Iustin Pop
def AddTags(opts, args):
125 846baef9 Iustin Pop
  """Add tags on a given object.
126 846baef9 Iustin Pop

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

132 846baef9 Iustin Pop
  """
133 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
134 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
135 846baef9 Iustin Pop
  if not args:
136 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be added")
137 846baef9 Iustin Pop
  op = opcodes.OpAddTags(kind=kind, name=name, tags=args)
138 846baef9 Iustin Pop
  SubmitOpCode(op)
139 846baef9 Iustin Pop
140 846baef9 Iustin Pop
141 846baef9 Iustin Pop
def RemoveTags(opts, args):
142 846baef9 Iustin Pop
  """Remove tags from a given object.
143 846baef9 Iustin Pop

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

149 846baef9 Iustin Pop
  """
150 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
151 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
152 846baef9 Iustin Pop
  if not args:
153 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be removed")
154 846baef9 Iustin Pop
  op = opcodes.OpDelTags(kind=kind, name=name, tags=args)
155 846baef9 Iustin Pop
  SubmitOpCode(op)
156 846baef9 Iustin Pop
157 a8083063 Iustin Pop
158 a8083063 Iustin Pop
DEBUG_OPT = make_option("-d", "--debug", default=False,
159 a8083063 Iustin Pop
                        action="store_true",
160 a8083063 Iustin Pop
                        help="Turn debugging on")
161 a8083063 Iustin Pop
162 a8083063 Iustin Pop
NOHDR_OPT = make_option("--no-headers", default=False,
163 a8083063 Iustin Pop
                        action="store_true", dest="no_headers",
164 a8083063 Iustin Pop
                        help="Don't display column headers")
165 a8083063 Iustin Pop
166 137161c9 Michael Hanselmann
SEP_OPT = make_option("--separator", default=None,
167 a8083063 Iustin Pop
                      action="store", dest="separator",
168 a8083063 Iustin Pop
                      help="Separator between output fields"
169 a8083063 Iustin Pop
                      " (defaults to one space)")
170 a8083063 Iustin Pop
171 a8083063 Iustin Pop
USEUNITS_OPT = make_option("--human-readable", default=False,
172 a8083063 Iustin Pop
                           action="store_true", dest="human_readable",
173 a8083063 Iustin Pop
                           help="Print sizes in human readable format")
174 a8083063 Iustin Pop
175 dcb93971 Michael Hanselmann
FIELDS_OPT = make_option("-o", "--output", dest="output", action="store",
176 d8a4b51d Iustin Pop
                         type="string", help="Comma separated list of"
177 d8a4b51d Iustin Pop
                         " output fields",
178 dcb93971 Michael Hanselmann
                         metavar="FIELDS")
179 dcb93971 Michael Hanselmann
180 fe7b0351 Michael Hanselmann
FORCE_OPT = make_option("-f", "--force", dest="force", action="store_true",
181 fe7b0351 Michael Hanselmann
                        default=False, help="Force the operation")
182 fe7b0351 Michael Hanselmann
183 810c50b7 Iustin Pop
TAG_SRC_OPT = make_option("--from", dest="tags_source",
184 810c50b7 Iustin Pop
                          default=None, help="File with tag names")
185 810c50b7 Iustin Pop
186 94428652 Iustin Pop
SUBMIT_OPT = make_option("--submit", dest="submit_only",
187 94428652 Iustin Pop
                         default=False, action="store_true",
188 94428652 Iustin Pop
                         help="Submit the job and return the job ID, but"
189 94428652 Iustin Pop
                         " don't wait for the job to finish")
190 94428652 Iustin Pop
191 60d49723 Michael Hanselmann
192 a8083063 Iustin Pop
def ARGS_FIXED(val):
193 a8083063 Iustin Pop
  """Macro-like function denoting a fixed number of arguments"""
194 a8083063 Iustin Pop
  return -val
195 a8083063 Iustin Pop
196 a8083063 Iustin Pop
197 a8083063 Iustin Pop
def ARGS_ATLEAST(val):
198 a8083063 Iustin Pop
  """Macro-like function denoting a minimum number of arguments"""
199 a8083063 Iustin Pop
  return val
200 a8083063 Iustin Pop
201 a8083063 Iustin Pop
202 a8083063 Iustin Pop
ARGS_NONE = None
203 a8083063 Iustin Pop
ARGS_ONE = ARGS_FIXED(1)
204 a8083063 Iustin Pop
ARGS_ANY = ARGS_ATLEAST(0)
205 a8083063 Iustin Pop
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
def check_unit(option, opt, value):
208 65fe4693 Iustin Pop
  """OptParsers custom converter for units.
209 65fe4693 Iustin Pop

210 65fe4693 Iustin Pop
  """
211 a8083063 Iustin Pop
  try:
212 a8083063 Iustin Pop
    return utils.ParseUnit(value)
213 a8083063 Iustin Pop
  except errors.UnitParseError, err:
214 3ecf6786 Iustin Pop
    raise OptionValueError("option %s: %s" % (opt, err))
215 a8083063 Iustin Pop
216 a8083063 Iustin Pop
217 a8083063 Iustin Pop
class CliOption(Option):
218 65fe4693 Iustin Pop
  """Custom option class for optparse.
219 65fe4693 Iustin Pop

220 65fe4693 Iustin Pop
  """
221 a8083063 Iustin Pop
  TYPES = Option.TYPES + ("unit",)
222 a8083063 Iustin Pop
  TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
223 a8083063 Iustin Pop
  TYPE_CHECKER["unit"] = check_unit
224 a8083063 Iustin Pop
225 a8083063 Iustin Pop
226 a8469393 Iustin Pop
def _SplitKeyVal(opt, data):
227 a8469393 Iustin Pop
  """Convert a KeyVal string into a dict.
228 a8469393 Iustin Pop

229 a8469393 Iustin Pop
  This function will convert a key=val[,...] string into a dict. Empty
230 a8469393 Iustin Pop
  values will be converted specially: keys which have the prefix 'no_'
231 a8469393 Iustin Pop
  will have the value=False and the prefix stripped, the others will
232 a8469393 Iustin Pop
  have value=True.
233 a8469393 Iustin Pop

234 a8469393 Iustin Pop
  @type opt: string
235 a8469393 Iustin Pop
  @param opt: a string holding the option name for which we process the
236 a8469393 Iustin Pop
      data, used in building error messages
237 a8469393 Iustin Pop
  @type data: string
238 a8469393 Iustin Pop
  @param data: a string of the format key=val,key=val,...
239 a8469393 Iustin Pop
  @rtype: dict
240 a8469393 Iustin Pop
  @return: {key=val, key=val}
241 a8469393 Iustin Pop
  @raises errors.ParameterError: if there are duplicate keys
242 a8469393 Iustin Pop

243 a8469393 Iustin Pop
  """
244 a8469393 Iustin Pop
  NO_PREFIX = "no_"
245 fcd62d84 Iustin Pop
  UN_PREFIX = "-"
246 a8469393 Iustin Pop
  kv_dict = {}
247 a8469393 Iustin Pop
  for elem in data.split(","):
248 a8469393 Iustin Pop
    if "=" in elem:
249 a8469393 Iustin Pop
      key, val = elem.split("=", 1)
250 a8469393 Iustin Pop
    else:
251 a8469393 Iustin Pop
      if elem.startswith(NO_PREFIX):
252 a8469393 Iustin Pop
        key, val = elem[len(NO_PREFIX):], False
253 fcd62d84 Iustin Pop
      elif elem.startswith(UN_PREFIX):
254 fcd62d84 Iustin Pop
        key, val = elem[len(UN_PREFIX):], None
255 a8469393 Iustin Pop
      else:
256 a8469393 Iustin Pop
        key, val = elem, True
257 a8469393 Iustin Pop
    if key in kv_dict:
258 a8469393 Iustin Pop
      raise errors.ParameterError("Duplicate key '%s' in option %s" %
259 a8469393 Iustin Pop
                                  (key, opt))
260 a8469393 Iustin Pop
    kv_dict[key] = val
261 a8469393 Iustin Pop
  return kv_dict
262 a8469393 Iustin Pop
263 a8469393 Iustin Pop
264 a8469393 Iustin Pop
def check_ident_key_val(option, opt, value):
265 a8469393 Iustin Pop
  """Custom parser for the IdentKeyVal option type.
266 a8469393 Iustin Pop

267 a8469393 Iustin Pop
  """
268 a8469393 Iustin Pop
  if ":" not in value:
269 a8469393 Iustin Pop
    retval =  (value, {})
270 a8469393 Iustin Pop
  else:
271 a8469393 Iustin Pop
    ident, rest = value.split(":", 1)
272 a8469393 Iustin Pop
    kv_dict = _SplitKeyVal(opt, rest)
273 a8469393 Iustin Pop
    retval = (ident, kv_dict)
274 a8469393 Iustin Pop
  return retval
275 a8469393 Iustin Pop
276 a8469393 Iustin Pop
277 a8469393 Iustin Pop
class IdentKeyValOption(Option):
278 a8469393 Iustin Pop
  """Custom option class for ident:key=val,key=val options.
279 a8469393 Iustin Pop

280 a8469393 Iustin Pop
  This will store the parsed values as a tuple (ident, {key: val}). As
281 a8469393 Iustin Pop
  such, multiple uses of this option via action=append is possible.
282 a8469393 Iustin Pop

283 a8469393 Iustin Pop
  """
284 a8469393 Iustin Pop
  TYPES = Option.TYPES + ("identkeyval",)
285 a8469393 Iustin Pop
  TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
286 a8469393 Iustin Pop
  TYPE_CHECKER["identkeyval"] = check_ident_key_val
287 a8469393 Iustin Pop
288 a8469393 Iustin Pop
289 a8469393 Iustin Pop
def check_key_val(option, opt, value):
290 a8469393 Iustin Pop
  """Custom parser for the KeyVal option type.
291 a8469393 Iustin Pop

292 a8469393 Iustin Pop
  """
293 a8469393 Iustin Pop
  return _SplitKeyVal(opt, value)
294 a8469393 Iustin Pop
295 a8469393 Iustin Pop
296 a8469393 Iustin Pop
class KeyValOption(Option):
297 a8469393 Iustin Pop
  """Custom option class for key=val,key=val options.
298 a8469393 Iustin Pop

299 a8469393 Iustin Pop
  This will store the parsed values as a dict {key: val}.
300 a8469393 Iustin Pop

301 a8469393 Iustin Pop
  """
302 a8469393 Iustin Pop
  TYPES = Option.TYPES + ("keyval",)
303 a8469393 Iustin Pop
  TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
304 a8469393 Iustin Pop
  TYPE_CHECKER["keyval"] = check_key_val
305 a8469393 Iustin Pop
306 a8469393 Iustin Pop
307 a8083063 Iustin Pop
# optparse.py sets make_option, so we do it for our own option class, too
308 a8083063 Iustin Pop
cli_option = CliOption
309 a8469393 Iustin Pop
ikv_option = IdentKeyValOption
310 a8469393 Iustin Pop
keyval_option = KeyValOption
311 a8083063 Iustin Pop
312 a8083063 Iustin Pop
313 de47cf8f Guido Trotter
def _ParseArgs(argv, commands, aliases):
314 a8083063 Iustin Pop
  """Parses the command line and return the function which must be
315 a8083063 Iustin Pop
  executed together with its arguments
316 a8083063 Iustin Pop

317 a8083063 Iustin Pop
  Arguments:
318 a8083063 Iustin Pop
    argv: the command line
319 a8083063 Iustin Pop

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

324 a8083063 Iustin Pop
  """
325 a8083063 Iustin Pop
  if len(argv) == 0:
326 a8083063 Iustin Pop
    binary = "<command>"
327 a8083063 Iustin Pop
  else:
328 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
329 a8083063 Iustin Pop
330 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
331 a8083063 Iustin Pop
    print "%s (ganeti) %s" % (binary, constants.RELEASE_VERSION)
332 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
333 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
334 a8083063 Iustin Pop
    sys.exit(0)
335 a8083063 Iustin Pop
336 de47cf8f Guido Trotter
  if len(argv) < 2 or not (argv[1] in commands or
337 70a35b6f Guido Trotter
                           argv[1] in aliases):
338 a8083063 Iustin Pop
    # let's do a nice thing
339 a8083063 Iustin Pop
    sortedcmds = commands.keys()
340 a8083063 Iustin Pop
    sortedcmds.sort()
341 a8083063 Iustin Pop
    print ("Usage: %(bin)s {command} [options...] [argument...]"
342 a8083063 Iustin Pop
           "\n%(bin)s <command> --help to see details, or"
343 a8083063 Iustin Pop
           " man %(bin)s\n" % {"bin": binary})
344 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
345 4e713df6 Iustin Pop
    mlen = max([len(" %s" % cmd) for cmd in commands])
346 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
347 a8083063 Iustin Pop
    # and format a nice command list
348 a8083063 Iustin Pop
    print "Commands:"
349 a8083063 Iustin Pop
    for cmd in sortedcmds:
350 4e713df6 Iustin Pop
      cmdstr = " %s" % (cmd,)
351 9a033156 Iustin Pop
      help_text = commands[cmd][4]
352 a8083063 Iustin Pop
      help_lines = textwrap.wrap(help_text, 79-3-mlen)
353 4e713df6 Iustin Pop
      print "%-*s - %s" % (mlen, cmdstr, help_lines.pop(0))
354 a8083063 Iustin Pop
      for line in help_lines:
355 a8083063 Iustin Pop
        print "%-*s   %s" % (mlen, "", line)
356 a8083063 Iustin Pop
    print
357 a8083063 Iustin Pop
    return None, None, None
358 de47cf8f Guido Trotter
359 de47cf8f Guido Trotter
  # get command, unalias it, and look it up in commands
360 a8083063 Iustin Pop
  cmd = argv.pop(1)
361 de47cf8f Guido Trotter
  if cmd in aliases:
362 de47cf8f Guido Trotter
    if cmd in commands:
363 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' overrides an existing"
364 de47cf8f Guido Trotter
                                   " command" % cmd)
365 de47cf8f Guido Trotter
366 de47cf8f Guido Trotter
    if aliases[cmd] not in commands:
367 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' maps to non-existing"
368 de47cf8f Guido Trotter
                                   " command '%s'" % (cmd, aliases[cmd]))
369 de47cf8f Guido Trotter
370 de47cf8f Guido Trotter
    cmd = aliases[cmd]
371 de47cf8f Guido Trotter
372 a8083063 Iustin Pop
  func, nargs, parser_opts, usage, description = commands[cmd]
373 a8083063 Iustin Pop
  parser = OptionParser(option_list=parser_opts,
374 a8083063 Iustin Pop
                        description=description,
375 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
376 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
377 a8083063 Iustin Pop
  parser.disable_interspersed_args()
378 a8083063 Iustin Pop
  options, args = parser.parse_args()
379 a8083063 Iustin Pop
  if nargs is None:
380 a8083063 Iustin Pop
    if len(args) != 0:
381 a8083063 Iustin Pop
      print >> sys.stderr, ("Error: Command %s expects no arguments" % cmd)
382 a8083063 Iustin Pop
      return None, None, None
383 a8083063 Iustin Pop
  elif nargs < 0 and len(args) != -nargs:
384 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects %d argument(s)" %
385 a8083063 Iustin Pop
                         (cmd, -nargs))
386 a8083063 Iustin Pop
    return None, None, None
387 a8083063 Iustin Pop
  elif nargs >= 0 and len(args) < nargs:
388 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects at least %d argument(s)" %
389 a8083063 Iustin Pop
                         (cmd, nargs))
390 a8083063 Iustin Pop
    return None, None, None
391 a8083063 Iustin Pop
392 a8083063 Iustin Pop
  return func, options, args
393 a8083063 Iustin Pop
394 a8083063 Iustin Pop
395 60d49723 Michael Hanselmann
def SplitNodeOption(value):
396 60d49723 Michael Hanselmann
  """Splits the value of a --node option.
397 60d49723 Michael Hanselmann

398 60d49723 Michael Hanselmann
  """
399 60d49723 Michael Hanselmann
  if value and ':' in value:
400 60d49723 Michael Hanselmann
    return value.split(':', 1)
401 60d49723 Michael Hanselmann
  else:
402 60d49723 Michael Hanselmann
    return (value, None)
403 60d49723 Michael Hanselmann
404 60d49723 Michael Hanselmann
405 a604f165 Iustin Pop
def ValidateBeParams(bep):
406 a604f165 Iustin Pop
  """Parse and check the given beparams.
407 a604f165 Iustin Pop

408 a604f165 Iustin Pop
  The function will update in-place the given dictionary.
409 a604f165 Iustin Pop

410 a604f165 Iustin Pop
  @type bep: dict
411 a604f165 Iustin Pop
  @param bep: input beparams
412 a604f165 Iustin Pop
  @raise errors.ParameterError: if the input values are not OK
413 a604f165 Iustin Pop
  @raise errors.UnitParseError: if the input values are not OK
414 a604f165 Iustin Pop

415 a604f165 Iustin Pop
  """
416 a604f165 Iustin Pop
  if constants.BE_MEMORY in bep:
417 a604f165 Iustin Pop
    bep[constants.BE_MEMORY] = utils.ParseUnit(bep[constants.BE_MEMORY])
418 a604f165 Iustin Pop
419 a604f165 Iustin Pop
  if constants.BE_VCPUS in bep:
420 a604f165 Iustin Pop
    try:
421 a604f165 Iustin Pop
      bep[constants.BE_VCPUS] = int(bep[constants.BE_VCPUS])
422 a604f165 Iustin Pop
    except ValueError:
423 a604f165 Iustin Pop
      raise errors.ParameterError("Invalid number of VCPUs")
424 a604f165 Iustin Pop
425 a604f165 Iustin Pop
426 47988778 Iustin Pop
def AskUser(text, choices=None):
427 47988778 Iustin Pop
  """Ask the user a question.
428 a8083063 Iustin Pop

429 a8083063 Iustin Pop
  Args:
430 47988778 Iustin Pop
    text - the question to ask.
431 a8083063 Iustin Pop

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

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

441 a8083063 Iustin Pop
  """
442 47988778 Iustin Pop
  if choices is None:
443 47988778 Iustin Pop
    choices = [('y', True, 'Perform the operation'),
444 47988778 Iustin Pop
               ('n', False, 'Do not perform the operation')]
445 47988778 Iustin Pop
  if not choices or not isinstance(choices, list):
446 47988778 Iustin Pop
    raise errors.ProgrammerError("Invalid choiches argument to AskUser")
447 47988778 Iustin Pop
  for entry in choices:
448 47988778 Iustin Pop
    if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
449 47988778 Iustin Pop
      raise errors.ProgrammerError("Invalid choiches element to AskUser")
450 47988778 Iustin Pop
451 47988778 Iustin Pop
  answer = choices[-1][1]
452 47988778 Iustin Pop
  new_text = []
453 47988778 Iustin Pop
  for line in text.splitlines():
454 47988778 Iustin Pop
    new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
455 47988778 Iustin Pop
  text = "\n".join(new_text)
456 a8083063 Iustin Pop
  try:
457 3023170f Iustin Pop
    f = file("/dev/tty", "a+")
458 a8083063 Iustin Pop
  except IOError:
459 47988778 Iustin Pop
    return answer
460 a8083063 Iustin Pop
  try:
461 47988778 Iustin Pop
    chars = [entry[0] for entry in choices]
462 47988778 Iustin Pop
    chars[-1] = "[%s]" % chars[-1]
463 47988778 Iustin Pop
    chars.append('?')
464 47988778 Iustin Pop
    maps = dict([(entry[0], entry[1]) for entry in choices])
465 47988778 Iustin Pop
    while True:
466 47988778 Iustin Pop
      f.write(text)
467 47988778 Iustin Pop
      f.write('\n')
468 47988778 Iustin Pop
      f.write("/".join(chars))
469 47988778 Iustin Pop
      f.write(": ")
470 47988778 Iustin Pop
      line = f.readline(2).strip().lower()
471 47988778 Iustin Pop
      if line in maps:
472 47988778 Iustin Pop
        answer = maps[line]
473 47988778 Iustin Pop
        break
474 47988778 Iustin Pop
      elif line == '?':
475 47988778 Iustin Pop
        for entry in choices:
476 47988778 Iustin Pop
          f.write(" %s - %s\n" % (entry[0], entry[2]))
477 47988778 Iustin Pop
        f.write("\n")
478 47988778 Iustin Pop
        continue
479 a8083063 Iustin Pop
  finally:
480 a8083063 Iustin Pop
    f.close()
481 a8083063 Iustin Pop
  return answer
482 a8083063 Iustin Pop
483 a8083063 Iustin Pop
484 e9d741b6 Iustin Pop
class JobSubmittedException(Exception):
485 e9d741b6 Iustin Pop
  """Job was submitted, client should exit.
486 e9d741b6 Iustin Pop

487 e9d741b6 Iustin Pop
  This exception has one argument, the ID of the job that was
488 e9d741b6 Iustin Pop
  submitted. The handler should print this ID.
489 e9d741b6 Iustin Pop

490 e9d741b6 Iustin Pop
  This is not an error, just a structured way to exit from clients.
491 e9d741b6 Iustin Pop

492 e9d741b6 Iustin Pop
  """
493 e9d741b6 Iustin Pop
494 e9d741b6 Iustin Pop
495 0a1e74d9 Iustin Pop
def SendJob(ops, cl=None):
496 0a1e74d9 Iustin Pop
  """Function to submit an opcode without waiting for the results.
497 a8083063 Iustin Pop

498 0a1e74d9 Iustin Pop
  @type ops: list
499 0a1e74d9 Iustin Pop
  @param ops: list of opcodes
500 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
501 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
502 0a1e74d9 Iustin Pop
             if None, a new client will be created
503 a8083063 Iustin Pop

504 a8083063 Iustin Pop
  """
505 e2212007 Iustin Pop
  if cl is None:
506 b33e986b Iustin Pop
    cl = GetClient()
507 685ee993 Iustin Pop
508 0a1e74d9 Iustin Pop
  job_id = cl.SubmitJob(ops)
509 0a1e74d9 Iustin Pop
510 0a1e74d9 Iustin Pop
  return job_id
511 0a1e74d9 Iustin Pop
512 0a1e74d9 Iustin Pop
513 281606c1 Michael Hanselmann
def PollJob(job_id, cl=None, feedback_fn=None):
514 0a1e74d9 Iustin Pop
  """Function to poll for the result of a job.
515 0a1e74d9 Iustin Pop

516 0a1e74d9 Iustin Pop
  @type job_id: job identified
517 0a1e74d9 Iustin Pop
  @param job_id: the job to poll for results
518 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
519 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
520 0a1e74d9 Iustin Pop
             if None, a new client will be created
521 0a1e74d9 Iustin Pop

522 0a1e74d9 Iustin Pop
  """
523 0a1e74d9 Iustin Pop
  if cl is None:
524 0a1e74d9 Iustin Pop
    cl = GetClient()
525 685ee993 Iustin Pop
526 6c5a7090 Michael Hanselmann
  prev_job_info = None
527 6c5a7090 Michael Hanselmann
  prev_logmsg_serial = None
528 6c5a7090 Michael Hanselmann
529 685ee993 Iustin Pop
  while True:
530 6c5a7090 Michael Hanselmann
    result = cl.WaitForJobChange(job_id, ["status"], prev_job_info,
531 6c5a7090 Michael Hanselmann
                                 prev_logmsg_serial)
532 6c5a7090 Michael Hanselmann
    if not result:
533 685ee993 Iustin Pop
      # job not found, go away!
534 0bbe448c Michael Hanselmann
      raise errors.JobLost("Job with id %s lost" % job_id)
535 685ee993 Iustin Pop
536 6c5a7090 Michael Hanselmann
    # Split result, a tuple of (field values, log entries)
537 6c5a7090 Michael Hanselmann
    (job_info, log_entries) = result
538 6c5a7090 Michael Hanselmann
    (status, ) = job_info
539 6c5a7090 Michael Hanselmann
540 6c5a7090 Michael Hanselmann
    if log_entries:
541 6c5a7090 Michael Hanselmann
      for log_entry in log_entries:
542 6c5a7090 Michael Hanselmann
        (serial, timestamp, _, message) = log_entry
543 6c5a7090 Michael Hanselmann
        if callable(feedback_fn):
544 6c5a7090 Michael Hanselmann
          feedback_fn(log_entry[1:])
545 6c5a7090 Michael Hanselmann
        else:
546 6c5a7090 Michael Hanselmann
          print "%s %s" % (time.ctime(utils.MergeTime(timestamp)), message)
547 6c5a7090 Michael Hanselmann
        prev_logmsg_serial = max(prev_logmsg_serial, serial)
548 6c5a7090 Michael Hanselmann
549 0bbe448c Michael Hanselmann
    # TODO: Handle canceled and archived jobs
550 6c5a7090 Michael Hanselmann
    elif status in (constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR):
551 685ee993 Iustin Pop
      break
552 6c5a7090 Michael Hanselmann
553 6c5a7090 Michael Hanselmann
    prev_job_info = job_info
554 685ee993 Iustin Pop
555 af30b2fd Michael Hanselmann
  jobs = cl.QueryJobs([job_id], ["status", "opresult"])
556 0bbe448c Michael Hanselmann
  if not jobs:
557 0bbe448c Michael Hanselmann
    raise errors.JobLost("Job with id %s lost" % job_id)
558 685ee993 Iustin Pop
559 0bbe448c Michael Hanselmann
  status, result = jobs[0]
560 0bbe448c Michael Hanselmann
  if status == constants.JOB_STATUS_SUCCESS:
561 53c04d04 Iustin Pop
    return result
562 0bbe448c Michael Hanselmann
  else:
563 0bbe448c Michael Hanselmann
    raise errors.OpExecError(result)
564 ceab32dd Iustin Pop
565 ceab32dd Iustin Pop
566 0a1e74d9 Iustin Pop
def SubmitOpCode(op, cl=None, feedback_fn=None):
567 0a1e74d9 Iustin Pop
  """Legacy function to submit an opcode.
568 0a1e74d9 Iustin Pop

569 0a1e74d9 Iustin Pop
  This is just a simple wrapper over the construction of the processor
570 0a1e74d9 Iustin Pop
  instance. It should be extended to better handle feedback and
571 0a1e74d9 Iustin Pop
  interaction functions.
572 0a1e74d9 Iustin Pop

573 0a1e74d9 Iustin Pop
  """
574 0a1e74d9 Iustin Pop
  if cl is None:
575 0a1e74d9 Iustin Pop
    cl = GetClient()
576 0a1e74d9 Iustin Pop
577 0a1e74d9 Iustin Pop
  job_id = SendJob([op], cl)
578 0a1e74d9 Iustin Pop
579 53c04d04 Iustin Pop
  op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn)
580 53c04d04 Iustin Pop
581 53c04d04 Iustin Pop
  return op_results[0]
582 0a1e74d9 Iustin Pop
583 0a1e74d9 Iustin Pop
584 94428652 Iustin Pop
def SubmitOrSend(op, opts, cl=None, feedback_fn=None):
585 94428652 Iustin Pop
  """Wrapper around SubmitOpCode or SendJob.
586 94428652 Iustin Pop

587 94428652 Iustin Pop
  This function will decide, based on the 'opts' parameter, whether to
588 94428652 Iustin Pop
  submit and wait for the result of the opcode (and return it), or
589 94428652 Iustin Pop
  whether to just send the job and print its identifier. It is used in
590 94428652 Iustin Pop
  order to simplify the implementation of the '--submit' option.
591 94428652 Iustin Pop

592 94428652 Iustin Pop
  """
593 94428652 Iustin Pop
  if opts and opts.submit_only:
594 e9d741b6 Iustin Pop
    job_id = SendJob([op], cl=cl)
595 e9d741b6 Iustin Pop
    raise JobSubmittedException(job_id)
596 94428652 Iustin Pop
  else:
597 94428652 Iustin Pop
    return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn)
598 94428652 Iustin Pop
599 94428652 Iustin Pop
600 af30b2fd Michael Hanselmann
def GetClient():
601 af30b2fd Michael Hanselmann
  # TODO: Cache object?
602 b33e986b Iustin Pop
  try:
603 b33e986b Iustin Pop
    client = luxi.Client()
604 b33e986b Iustin Pop
  except luxi.NoMasterError:
605 b33e986b Iustin Pop
    master, myself = ssconf.GetMasterAndMyself()
606 b33e986b Iustin Pop
    if master != myself:
607 b33e986b Iustin Pop
      raise errors.OpPrereqError("This is not the master node, please connect"
608 b33e986b Iustin Pop
                                 " to node '%s' and rerun the command" %
609 b33e986b Iustin Pop
                                 master)
610 b33e986b Iustin Pop
    else:
611 b33e986b Iustin Pop
      raise
612 b33e986b Iustin Pop
  return client
613 af30b2fd Michael Hanselmann
614 af30b2fd Michael Hanselmann
615 73702ee7 Iustin Pop
def FormatError(err):
616 73702ee7 Iustin Pop
  """Return a formatted error message for a given error.
617 73702ee7 Iustin Pop

618 73702ee7 Iustin Pop
  This function takes an exception instance and returns a tuple
619 73702ee7 Iustin Pop
  consisting of two values: first, the recommended exit code, and
620 73702ee7 Iustin Pop
  second, a string describing the error message (not
621 73702ee7 Iustin Pop
  newline-terminated).
622 73702ee7 Iustin Pop

623 73702ee7 Iustin Pop
  """
624 73702ee7 Iustin Pop
  retcode = 1
625 73702ee7 Iustin Pop
  obuf = StringIO()
626 e2e521d0 Iustin Pop
  msg = str(err)
627 73702ee7 Iustin Pop
  if isinstance(err, errors.ConfigurationError):
628 e2e521d0 Iustin Pop
    txt = "Corrupt configuration file: %s" % msg
629 e2e521d0 Iustin Pop
    logger.Error(txt)
630 e2e521d0 Iustin Pop
    obuf.write(txt + "\n")
631 73702ee7 Iustin Pop
    obuf.write("Aborting.")
632 73702ee7 Iustin Pop
    retcode = 2
633 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksAbort):
634 73702ee7 Iustin Pop
    obuf.write("Failure: hooks execution failed:\n")
635 73702ee7 Iustin Pop
    for node, script, out in err.args[0]:
636 73702ee7 Iustin Pop
      if out:
637 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s, output: %s\n" %
638 73702ee7 Iustin Pop
                   (node, script, out))
639 73702ee7 Iustin Pop
      else:
640 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s (no output)\n" %
641 73702ee7 Iustin Pop
                   (node, script))
642 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksFailure):
643 e2e521d0 Iustin Pop
    obuf.write("Failure: hooks general failure: %s" % msg)
644 73702ee7 Iustin Pop
  elif isinstance(err, errors.ResolverError):
645 73702ee7 Iustin Pop
    this_host = utils.HostInfo.SysName()
646 73702ee7 Iustin Pop
    if err.args[0] == this_host:
647 73702ee7 Iustin Pop
      msg = "Failure: can't resolve my own hostname ('%s')"
648 73702ee7 Iustin Pop
    else:
649 73702ee7 Iustin Pop
      msg = "Failure: can't resolve hostname '%s'"
650 73702ee7 Iustin Pop
    obuf.write(msg % err.args[0])
651 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpPrereqError):
652 73702ee7 Iustin Pop
    obuf.write("Failure: prerequisites not met for this"
653 e2e521d0 Iustin Pop
               " operation:\n%s" % msg)
654 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpExecError):
655 e2e521d0 Iustin Pop
    obuf.write("Failure: command execution error:\n%s" % msg)
656 73702ee7 Iustin Pop
  elif isinstance(err, errors.TagError):
657 e2e521d0 Iustin Pop
    obuf.write("Failure: invalid tag(s) given:\n%s" % msg)
658 73702ee7 Iustin Pop
  elif isinstance(err, errors.GenericError):
659 e2e521d0 Iustin Pop
    obuf.write("Unhandled Ganeti error: %s" % msg)
660 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.NoMasterError):
661 03a8dbdc Iustin Pop
    obuf.write("Cannot communicate with the master daemon.\nIs it running"
662 082c5adb Michael Hanselmann
               " and listening for connections?")
663 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.TimeoutError):
664 03a8dbdc Iustin Pop
    obuf.write("Timeout while talking to the master daemon. Error:\n"
665 03a8dbdc Iustin Pop
               "%s" % msg)
666 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.ProtocolError):
667 03a8dbdc Iustin Pop
    obuf.write("Unhandled protocol error while talking to the master daemon:\n"
668 03a8dbdc Iustin Pop
               "%s" % msg)
669 e9d741b6 Iustin Pop
  elif isinstance(err, JobSubmittedException):
670 e9d741b6 Iustin Pop
    obuf.write("JobID: %s\n" % err.args[0])
671 e9d741b6 Iustin Pop
    retcode = 0
672 73702ee7 Iustin Pop
  else:
673 e2e521d0 Iustin Pop
    obuf.write("Unhandled exception: %s" % msg)
674 73702ee7 Iustin Pop
  return retcode, obuf.getvalue().rstrip('\n')
675 73702ee7 Iustin Pop
676 73702ee7 Iustin Pop
677 de47cf8f Guido Trotter
def GenericMain(commands, override=None, aliases=None):
678 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
679 a8083063 Iustin Pop

680 334d1483 Iustin Pop
  Arguments:
681 334d1483 Iustin Pop
    - commands: a dictionary with a special structure, see the design doc
682 334d1483 Iustin Pop
                for command line handling.
683 334d1483 Iustin Pop
    - override: if not None, we expect a dictionary with keys that will
684 334d1483 Iustin Pop
                override command line options; this can be used to pass
685 334d1483 Iustin Pop
                options from the scripts to generic functions
686 de47cf8f Guido Trotter
    - aliases: dictionary with command aliases {'alias': 'target, ...}
687 a8083063 Iustin Pop

688 a8083063 Iustin Pop
  """
689 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
690 a8083063 Iustin Pop
  if sys.argv:
691 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
692 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
693 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
694 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
695 a8083063 Iustin Pop
    else:
696 a8083063 Iustin Pop
      old_cmdline = ""
697 a8083063 Iustin Pop
  else:
698 a8083063 Iustin Pop
    binary = "<unknown program>"
699 a8083063 Iustin Pop
    old_cmdline = ""
700 a8083063 Iustin Pop
701 de47cf8f Guido Trotter
  if aliases is None:
702 de47cf8f Guido Trotter
    aliases = {}
703 de47cf8f Guido Trotter
704 de47cf8f Guido Trotter
  func, options, args = _ParseArgs(sys.argv, commands, aliases)
705 a8083063 Iustin Pop
  if func is None: # parse error
706 a8083063 Iustin Pop
    return 1
707 a8083063 Iustin Pop
708 334d1483 Iustin Pop
  if override is not None:
709 334d1483 Iustin Pop
    for key, val in override.iteritems():
710 334d1483 Iustin Pop
      setattr(options, key, val)
711 334d1483 Iustin Pop
712 59f187eb Iustin Pop
  logger.SetupLogging(constants.LOG_COMMANDS, debug=options.debug,
713 59f187eb Iustin Pop
                      stderr_logging=True, program=binary)
714 a8083063 Iustin Pop
715 f362096f Iustin Pop
  utils.debug = options.debug
716 a8083063 Iustin Pop
717 a8083063 Iustin Pop
  if old_cmdline:
718 a8083063 Iustin Pop
    logger.Info("run with arguments '%s'" % old_cmdline)
719 a8083063 Iustin Pop
  else:
720 a8083063 Iustin Pop
    logger.Info("run with no arguments")
721 a8083063 Iustin Pop
722 a8083063 Iustin Pop
  try:
723 a4af651e Iustin Pop
    result = func(options, args)
724 03a8dbdc Iustin Pop
  except (errors.GenericError, luxi.ProtocolError), err:
725 a4af651e Iustin Pop
    result, err_msg = FormatError(err)
726 a4af651e Iustin Pop
    logger.ToStderr(err_msg)
727 a8083063 Iustin Pop
728 a8083063 Iustin Pop
  return result
729 137161c9 Michael Hanselmann
730 137161c9 Michael Hanselmann
731 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
732 16be8703 Iustin Pop
                  numfields=None, unitfields=None):
733 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
734 137161c9 Michael Hanselmann

735 137161c9 Michael Hanselmann
  Args:
736 137161c9 Michael Hanselmann
    headers: Dict of header titles or None if no headers should be shown
737 137161c9 Michael Hanselmann
    fields: List of fields to show
738 137161c9 Michael Hanselmann
    separator: String used to separate fields or None for spaces
739 137161c9 Michael Hanselmann
    data: Data to be printed
740 137161c9 Michael Hanselmann
    numfields: List of fields to be aligned to right
741 137161c9 Michael Hanselmann
    unitfields: List of fields to be formatted as units
742 137161c9 Michael Hanselmann

743 137161c9 Michael Hanselmann
  """
744 137161c9 Michael Hanselmann
  if numfields is None:
745 137161c9 Michael Hanselmann
    numfields = []
746 137161c9 Michael Hanselmann
  if unitfields is None:
747 137161c9 Michael Hanselmann
    unitfields = []
748 137161c9 Michael Hanselmann
749 137161c9 Michael Hanselmann
  format_fields = []
750 137161c9 Michael Hanselmann
  for field in fields:
751 01ca31ae Iustin Pop
    if headers and field not in headers:
752 01ca31ae Iustin Pop
      raise errors.ProgrammerError("Missing header description for field '%s'"
753 01ca31ae Iustin Pop
                                   % field)
754 137161c9 Michael Hanselmann
    if separator is not None:
755 137161c9 Michael Hanselmann
      format_fields.append("%s")
756 137161c9 Michael Hanselmann
    elif field in numfields:
757 137161c9 Michael Hanselmann
      format_fields.append("%*s")
758 137161c9 Michael Hanselmann
    else:
759 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
760 137161c9 Michael Hanselmann
761 137161c9 Michael Hanselmann
  if separator is None:
762 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
763 137161c9 Michael Hanselmann
    format = ' '.join(format_fields)
764 137161c9 Michael Hanselmann
  else:
765 137161c9 Michael Hanselmann
    format = separator.replace("%", "%%").join(format_fields)
766 137161c9 Michael Hanselmann
767 137161c9 Michael Hanselmann
  for row in data:
768 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
769 137161c9 Michael Hanselmann
      if fields[idx] in unitfields:
770 137161c9 Michael Hanselmann
        try:
771 137161c9 Michael Hanselmann
          val = int(val)
772 137161c9 Michael Hanselmann
        except ValueError:
773 137161c9 Michael Hanselmann
          pass
774 137161c9 Michael Hanselmann
        else:
775 137161c9 Michael Hanselmann
          val = row[idx] = utils.FormatUnit(val)
776 01ca31ae Iustin Pop
      val = row[idx] = str(val)
777 137161c9 Michael Hanselmann
      if separator is None:
778 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
779 137161c9 Michael Hanselmann
780 16be8703 Iustin Pop
  result = []
781 137161c9 Michael Hanselmann
  if headers:
782 137161c9 Michael Hanselmann
    args = []
783 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
784 137161c9 Michael Hanselmann
      hdr = headers[name]
785 137161c9 Michael Hanselmann
      if separator is None:
786 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
787 137161c9 Michael Hanselmann
        args.append(mlens[idx])
788 137161c9 Michael Hanselmann
      args.append(hdr)
789 16be8703 Iustin Pop
    result.append(format % tuple(args))
790 137161c9 Michael Hanselmann
791 137161c9 Michael Hanselmann
  for line in data:
792 137161c9 Michael Hanselmann
    args = []
793 137161c9 Michael Hanselmann
    for idx in xrange(len(fields)):
794 137161c9 Michael Hanselmann
      if separator is None:
795 137161c9 Michael Hanselmann
        args.append(mlens[idx])
796 137161c9 Michael Hanselmann
      args.append(line[idx])
797 16be8703 Iustin Pop
    result.append(format % tuple(args))
798 16be8703 Iustin Pop
799 16be8703 Iustin Pop
  return result
800 3386e7a9 Iustin Pop
801 3386e7a9 Iustin Pop
802 3386e7a9 Iustin Pop
def FormatTimestamp(ts):
803 3386e7a9 Iustin Pop
  """Formats a given timestamp.
804 3386e7a9 Iustin Pop

805 3386e7a9 Iustin Pop
  @type ts: timestamp
806 3386e7a9 Iustin Pop
  @param ts: a timeval-type timestamp, a tuple of seconds and microseconds
807 3386e7a9 Iustin Pop

808 3386e7a9 Iustin Pop
  @rtype: string
809 3386e7a9 Iustin Pop
  @returns: a string with the formatted timestamp
810 3386e7a9 Iustin Pop

811 3386e7a9 Iustin Pop
  """
812 e0ec0ff6 Iustin Pop
  if not isinstance (ts, (tuple, list)) or len(ts) != 2:
813 e0ec0ff6 Iustin Pop
    return '?'
814 3386e7a9 Iustin Pop
  sec, usec = ts
815 3386e7a9 Iustin Pop
  return time.strftime("%F %T", time.localtime(sec)) + ".%06d" % usec
816 2241e2b9 Iustin Pop
817 2241e2b9 Iustin Pop
818 2241e2b9 Iustin Pop
def ParseTimespec(value):
819 2241e2b9 Iustin Pop
  """Parse a time specification.
820 2241e2b9 Iustin Pop

821 2241e2b9 Iustin Pop
  The following suffixed will be recognized:
822 2241e2b9 Iustin Pop

823 2241e2b9 Iustin Pop
    - s: seconds
824 2241e2b9 Iustin Pop
    - m: minutes
825 2241e2b9 Iustin Pop
    - h: hours
826 2241e2b9 Iustin Pop
    - d: day
827 2241e2b9 Iustin Pop
    - w: weeks
828 2241e2b9 Iustin Pop

829 2241e2b9 Iustin Pop
  Without any suffix, the value will be taken to be in seconds.
830 2241e2b9 Iustin Pop

831 2241e2b9 Iustin Pop
  """
832 2241e2b9 Iustin Pop
  value = str(value)
833 2241e2b9 Iustin Pop
  if not value:
834 2241e2b9 Iustin Pop
    raise errors.OpPrereqError("Empty time specification passed")
835 2241e2b9 Iustin Pop
  suffix_map = {
836 2241e2b9 Iustin Pop
    's': 1,
837 2241e2b9 Iustin Pop
    'm': 60,
838 2241e2b9 Iustin Pop
    'h': 3600,
839 2241e2b9 Iustin Pop
    'd': 86400,
840 2241e2b9 Iustin Pop
    'w': 604800,
841 2241e2b9 Iustin Pop
    }
842 2241e2b9 Iustin Pop
  if value[-1] not in suffix_map:
843 2241e2b9 Iustin Pop
    try:
844 2241e2b9 Iustin Pop
      value = int(value)
845 2241e2b9 Iustin Pop
    except ValueError:
846 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
847 2241e2b9 Iustin Pop
  else:
848 2241e2b9 Iustin Pop
    multiplier = suffix_map[value[-1]]
849 2241e2b9 Iustin Pop
    value = value[:-1]
850 2241e2b9 Iustin Pop
    if not value: # no data left after stripping the suffix
851 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification (only"
852 2241e2b9 Iustin Pop
                                 " suffix passed)")
853 2241e2b9 Iustin Pop
    try:
854 2241e2b9 Iustin Pop
      value = int(value) * multiplier
855 2241e2b9 Iustin Pop
    except ValueError:
856 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
857 2241e2b9 Iustin Pop
  return value