Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ 99aabbed

History | View | Annotate | Download (26.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 46fbdd04 Iustin Pop
import logging
31 73702ee7 Iustin Pop
from cStringIO import StringIO
32 a8083063 Iustin Pop
33 a8083063 Iustin Pop
from ganeti import utils
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 46fbdd04 Iustin Pop
           "ToStderr", "ToStdout",
54 846baef9 Iustin Pop
           ]
55 846baef9 Iustin Pop
56 846baef9 Iustin Pop
57 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
58 846baef9 Iustin Pop
  """Extract the tag type object.
59 846baef9 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

810 3386e7a9 Iustin Pop
  @type ts: timestamp
811 3386e7a9 Iustin Pop
  @param ts: a timeval-type timestamp, a tuple of seconds and microseconds
812 3386e7a9 Iustin Pop

813 3386e7a9 Iustin Pop
  @rtype: string
814 3386e7a9 Iustin Pop
  @returns: a string with the formatted timestamp
815 3386e7a9 Iustin Pop

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

826 2241e2b9 Iustin Pop
  The following suffixed will be recognized:
827 2241e2b9 Iustin Pop

828 2241e2b9 Iustin Pop
    - s: seconds
829 2241e2b9 Iustin Pop
    - m: minutes
830 2241e2b9 Iustin Pop
    - h: hours
831 2241e2b9 Iustin Pop
    - d: day
832 2241e2b9 Iustin Pop
    - w: weeks
833 2241e2b9 Iustin Pop

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

836 2241e2b9 Iustin Pop
  """
837 2241e2b9 Iustin Pop
  value = str(value)
838 2241e2b9 Iustin Pop
  if not value:
839 2241e2b9 Iustin Pop
    raise errors.OpPrereqError("Empty time specification passed")
840 2241e2b9 Iustin Pop
  suffix_map = {
841 2241e2b9 Iustin Pop
    's': 1,
842 2241e2b9 Iustin Pop
    'm': 60,
843 2241e2b9 Iustin Pop
    'h': 3600,
844 2241e2b9 Iustin Pop
    'd': 86400,
845 2241e2b9 Iustin Pop
    'w': 604800,
846 2241e2b9 Iustin Pop
    }
847 2241e2b9 Iustin Pop
  if value[-1] not in suffix_map:
848 2241e2b9 Iustin Pop
    try:
849 2241e2b9 Iustin Pop
      value = int(value)
850 2241e2b9 Iustin Pop
    except ValueError:
851 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
852 2241e2b9 Iustin Pop
  else:
853 2241e2b9 Iustin Pop
    multiplier = suffix_map[value[-1]]
854 2241e2b9 Iustin Pop
    value = value[:-1]
855 2241e2b9 Iustin Pop
    if not value: # no data left after stripping the suffix
856 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification (only"
857 2241e2b9 Iustin Pop
                                 " suffix passed)")
858 2241e2b9 Iustin Pop
    try:
859 2241e2b9 Iustin Pop
      value = int(value) * multiplier
860 2241e2b9 Iustin Pop
    except ValueError:
861 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
862 2241e2b9 Iustin Pop
  return value
863 46fbdd04 Iustin Pop
864 46fbdd04 Iustin Pop
865 46fbdd04 Iustin Pop
def _ToStream(stream, txt, *args):
866 46fbdd04 Iustin Pop
  """Write a message to a stream, bypassing the logging system
867 46fbdd04 Iustin Pop

868 46fbdd04 Iustin Pop
  @type stream: file object
869 46fbdd04 Iustin Pop
  @param stream: the file to which we should write
870 46fbdd04 Iustin Pop
  @type txt: str
871 46fbdd04 Iustin Pop
  @param txt: the message
872 46fbdd04 Iustin Pop

873 46fbdd04 Iustin Pop
  """
874 46fbdd04 Iustin Pop
  if args:
875 46fbdd04 Iustin Pop
    args = tuple(args)
876 46fbdd04 Iustin Pop
    stream.write(txt % args)
877 46fbdd04 Iustin Pop
  else:
878 46fbdd04 Iustin Pop
    stream.write(txt)
879 46fbdd04 Iustin Pop
  stream.write('\n')
880 46fbdd04 Iustin Pop
  stream.flush()
881 46fbdd04 Iustin Pop
882 46fbdd04 Iustin Pop
883 46fbdd04 Iustin Pop
def ToStdout(txt, *args):
884 46fbdd04 Iustin Pop
  """Write a message to stdout only, bypassing the logging system
885 46fbdd04 Iustin Pop

886 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
887 46fbdd04 Iustin Pop

888 46fbdd04 Iustin Pop
  @type txt: str
889 46fbdd04 Iustin Pop
  @param txt: the message
890 46fbdd04 Iustin Pop

891 46fbdd04 Iustin Pop
  """
892 46fbdd04 Iustin Pop
  _ToStream(sys.stdout, txt, *args)
893 46fbdd04 Iustin Pop
894 46fbdd04 Iustin Pop
895 46fbdd04 Iustin Pop
def ToStderr(txt, *args):
896 46fbdd04 Iustin Pop
  """Write a message to stderr only, bypassing the logging system
897 46fbdd04 Iustin Pop

898 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
899 46fbdd04 Iustin Pop

900 46fbdd04 Iustin Pop
  @type txt: str
901 46fbdd04 Iustin Pop
  @param txt: the message
902 46fbdd04 Iustin Pop

903 46fbdd04 Iustin Pop
  """
904 46fbdd04 Iustin Pop
  _ToStream(sys.stderr, txt, *args)