Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ f87b405e

History | View | Annotate | Download (28.3 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 4331f6cd Michael Hanselmann
from ganeti import rpc
40 a8083063 Iustin Pop
41 a8083063 Iustin Pop
from optparse import (OptionParser, make_option, TitledHelpFormatter,
42 38206f3c Iustin Pop
                      Option, OptionValueError)
43 a8083063 Iustin Pop
44 ceab32dd Iustin Pop
__all__ = ["DEBUG_OPT", "NOHDR_OPT", "SEP_OPT", "GenericMain",
45 af30b2fd Michael Hanselmann
           "SubmitOpCode", "GetClient",
46 a8469393 Iustin Pop
           "cli_option", "ikv_option", "keyval_option",
47 a8469393 Iustin Pop
           "GenerateTable", "AskUser",
48 a8083063 Iustin Pop
           "ARGS_NONE", "ARGS_FIXED", "ARGS_ATLEAST", "ARGS_ANY", "ARGS_ONE",
49 94428652 Iustin Pop
           "USEUNITS_OPT", "FIELDS_OPT", "FORCE_OPT", "SUBMIT_OPT",
50 810c50b7 Iustin Pop
           "ListTags", "AddTags", "RemoveTags", "TAG_SRC_OPT",
51 94428652 Iustin Pop
           "FormatError", "SplitNodeOption", "SubmitOrSend",
52 07cd723a Iustin Pop
           "JobSubmittedException", "FormatTimestamp", "ParseTimespec",
53 a604f165 Iustin Pop
           "ValidateBeParams",
54 46fbdd04 Iustin Pop
           "ToStderr", "ToStdout",
55 4331f6cd Michael Hanselmann
           "UsesRPC",
56 846baef9 Iustin Pop
           ]
57 846baef9 Iustin Pop
58 846baef9 Iustin Pop
59 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
60 846baef9 Iustin Pop
  """Extract the tag type object.
61 846baef9 Iustin Pop

62 846baef9 Iustin Pop
  Note that this function will modify its args parameter.
63 846baef9 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

319 c41eea6e Iustin Pop
  This function parses the arguements and returns the function which
320 c41eea6e Iustin Pop
  must be executed together with its (modified) arguments.
321 a8083063 Iustin Pop

322 c41eea6e Iustin Pop
  @param argv: the command line
323 c41eea6e Iustin Pop
  @param commands: dictionary with special contents, see the design
324 c41eea6e Iustin Pop
      doc for cmdline handling
325 c41eea6e Iustin Pop
  @param aliases: dictionary with command aliases {'alias': 'target, ...}
326 098c0958 Michael Hanselmann

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

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

411 a604f165 Iustin Pop
  The function will update in-place the given dictionary.
412 a604f165 Iustin Pop

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

418 a604f165 Iustin Pop
  """
419 a604f165 Iustin Pop
  if constants.BE_MEMORY in bep:
420 a604f165 Iustin Pop
    bep[constants.BE_MEMORY] = utils.ParseUnit(bep[constants.BE_MEMORY])
421 a604f165 Iustin Pop
422 a604f165 Iustin Pop
  if constants.BE_VCPUS in bep:
423 a604f165 Iustin Pop
    try:
424 a604f165 Iustin Pop
      bep[constants.BE_VCPUS] = int(bep[constants.BE_VCPUS])
425 a604f165 Iustin Pop
    except ValueError:
426 a604f165 Iustin Pop
      raise errors.ParameterError("Invalid number of VCPUs")
427 a604f165 Iustin Pop
428 a604f165 Iustin Pop
429 4331f6cd Michael Hanselmann
def UsesRPC(fn):
430 4331f6cd Michael Hanselmann
  def wrapper(*args, **kwargs):
431 4331f6cd Michael Hanselmann
    rpc.Init()
432 4331f6cd Michael Hanselmann
    try:
433 4331f6cd Michael Hanselmann
      return fn(*args, **kwargs)
434 4331f6cd Michael Hanselmann
    finally:
435 4331f6cd Michael Hanselmann
      rpc.Shutdown()
436 4331f6cd Michael Hanselmann
  return wrapper
437 4331f6cd Michael Hanselmann
438 4331f6cd Michael Hanselmann
439 47988778 Iustin Pop
def AskUser(text, choices=None):
440 47988778 Iustin Pop
  """Ask the user a question.
441 a8083063 Iustin Pop

442 c41eea6e Iustin Pop
  @param text: the question to ask
443 a8083063 Iustin Pop

444 c41eea6e Iustin Pop
  @param choices: list with elements tuples (input_char, return_value,
445 c41eea6e Iustin Pop
      description); if not given, it will default to: [('y', True,
446 c41eea6e Iustin Pop
      'Perform the operation'), ('n', False, 'Do no do the operation')];
447 c41eea6e Iustin Pop
      note that the '?' char is reserved for help
448 47988778 Iustin Pop

449 c41eea6e Iustin Pop
  @return: one of the return values from the choices list; if input is
450 c41eea6e Iustin Pop
      not possible (i.e. not running with a tty, we return the last
451 c41eea6e Iustin Pop
      entry from the list
452 a8083063 Iustin Pop

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

499 e9d741b6 Iustin Pop
  This exception has one argument, the ID of the job that was
500 e9d741b6 Iustin Pop
  submitted. The handler should print this ID.
501 e9d741b6 Iustin Pop

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

504 e9d741b6 Iustin Pop
  """
505 e9d741b6 Iustin Pop
506 e9d741b6 Iustin Pop
507 0a1e74d9 Iustin Pop
def SendJob(ops, cl=None):
508 0a1e74d9 Iustin Pop
  """Function to submit an opcode without waiting for the results.
509 a8083063 Iustin Pop

510 0a1e74d9 Iustin Pop
  @type ops: list
511 0a1e74d9 Iustin Pop
  @param ops: list of opcodes
512 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
513 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
514 0a1e74d9 Iustin Pop
             if None, a new client will be created
515 a8083063 Iustin Pop

516 a8083063 Iustin Pop
  """
517 e2212007 Iustin Pop
  if cl is None:
518 b33e986b Iustin Pop
    cl = GetClient()
519 685ee993 Iustin Pop
520 0a1e74d9 Iustin Pop
  job_id = cl.SubmitJob(ops)
521 0a1e74d9 Iustin Pop
522 0a1e74d9 Iustin Pop
  return job_id
523 0a1e74d9 Iustin Pop
524 0a1e74d9 Iustin Pop
525 281606c1 Michael Hanselmann
def PollJob(job_id, cl=None, feedback_fn=None):
526 0a1e74d9 Iustin Pop
  """Function to poll for the result of a job.
527 0a1e74d9 Iustin Pop

528 0a1e74d9 Iustin Pop
  @type job_id: job identified
529 0a1e74d9 Iustin Pop
  @param job_id: the job to poll for results
530 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
531 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
532 0a1e74d9 Iustin Pop
             if None, a new client will be created
533 0a1e74d9 Iustin Pop

534 0a1e74d9 Iustin Pop
  """
535 0a1e74d9 Iustin Pop
  if cl is None:
536 0a1e74d9 Iustin Pop
    cl = GetClient()
537 685ee993 Iustin Pop
538 6c5a7090 Michael Hanselmann
  prev_job_info = None
539 6c5a7090 Michael Hanselmann
  prev_logmsg_serial = None
540 6c5a7090 Michael Hanselmann
541 685ee993 Iustin Pop
  while True:
542 6c5a7090 Michael Hanselmann
    result = cl.WaitForJobChange(job_id, ["status"], prev_job_info,
543 6c5a7090 Michael Hanselmann
                                 prev_logmsg_serial)
544 6c5a7090 Michael Hanselmann
    if not result:
545 685ee993 Iustin Pop
      # job not found, go away!
546 0bbe448c Michael Hanselmann
      raise errors.JobLost("Job with id %s lost" % job_id)
547 685ee993 Iustin Pop
548 6c5a7090 Michael Hanselmann
    # Split result, a tuple of (field values, log entries)
549 6c5a7090 Michael Hanselmann
    (job_info, log_entries) = result
550 6c5a7090 Michael Hanselmann
    (status, ) = job_info
551 6c5a7090 Michael Hanselmann
552 6c5a7090 Michael Hanselmann
    if log_entries:
553 6c5a7090 Michael Hanselmann
      for log_entry in log_entries:
554 6c5a7090 Michael Hanselmann
        (serial, timestamp, _, message) = log_entry
555 6c5a7090 Michael Hanselmann
        if callable(feedback_fn):
556 6c5a7090 Michael Hanselmann
          feedback_fn(log_entry[1:])
557 6c5a7090 Michael Hanselmann
        else:
558 6c5a7090 Michael Hanselmann
          print "%s %s" % (time.ctime(utils.MergeTime(timestamp)), message)
559 6c5a7090 Michael Hanselmann
        prev_logmsg_serial = max(prev_logmsg_serial, serial)
560 6c5a7090 Michael Hanselmann
561 0bbe448c Michael Hanselmann
    # TODO: Handle canceled and archived jobs
562 fbf0262f Michael Hanselmann
    elif status in (constants.JOB_STATUS_SUCCESS,
563 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_ERROR,
564 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELING,
565 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELED):
566 685ee993 Iustin Pop
      break
567 6c5a7090 Michael Hanselmann
568 6c5a7090 Michael Hanselmann
    prev_job_info = job_info
569 685ee993 Iustin Pop
570 0e050889 Iustin Pop
  jobs = cl.QueryJobs([job_id], ["status", "opstatus", "opresult"])
571 0bbe448c Michael Hanselmann
  if not jobs:
572 0bbe448c Michael Hanselmann
    raise errors.JobLost("Job with id %s lost" % job_id)
573 685ee993 Iustin Pop
574 0e050889 Iustin Pop
  status, opstatus, result = jobs[0]
575 0bbe448c Michael Hanselmann
  if status == constants.JOB_STATUS_SUCCESS:
576 53c04d04 Iustin Pop
    return result
577 fbf0262f Michael Hanselmann
  elif status in (constants.JOB_STATUS_CANCELING,
578 fbf0262f Michael Hanselmann
                  constants.JOB_STATUS_CANCELED):
579 fbf0262f Michael Hanselmann
    raise errors.OpExecError("Job was canceled")
580 0bbe448c Michael Hanselmann
  else:
581 0e050889 Iustin Pop
    has_ok = False
582 0e050889 Iustin Pop
    for idx, (status, msg) in enumerate(zip(opstatus, result)):
583 0e050889 Iustin Pop
      if status == constants.OP_STATUS_SUCCESS:
584 0e050889 Iustin Pop
        has_ok = True
585 0e050889 Iustin Pop
      elif status == constants.OP_STATUS_ERROR:
586 0e050889 Iustin Pop
        if has_ok:
587 0e050889 Iustin Pop
          raise errors.OpExecError("partial failure (opcode %d): %s" %
588 0e050889 Iustin Pop
                                   (idx, msg))
589 0e050889 Iustin Pop
        else:
590 0e050889 Iustin Pop
          raise errors.OpExecError(str(msg))
591 0e050889 Iustin Pop
    # default failure mode
592 0bbe448c Michael Hanselmann
    raise errors.OpExecError(result)
593 ceab32dd Iustin Pop
594 ceab32dd Iustin Pop
595 0a1e74d9 Iustin Pop
def SubmitOpCode(op, cl=None, feedback_fn=None):
596 0a1e74d9 Iustin Pop
  """Legacy function to submit an opcode.
597 0a1e74d9 Iustin Pop

598 0a1e74d9 Iustin Pop
  This is just a simple wrapper over the construction of the processor
599 0a1e74d9 Iustin Pop
  instance. It should be extended to better handle feedback and
600 0a1e74d9 Iustin Pop
  interaction functions.
601 0a1e74d9 Iustin Pop

602 0a1e74d9 Iustin Pop
  """
603 0a1e74d9 Iustin Pop
  if cl is None:
604 0a1e74d9 Iustin Pop
    cl = GetClient()
605 0a1e74d9 Iustin Pop
606 0a1e74d9 Iustin Pop
  job_id = SendJob([op], cl)
607 0a1e74d9 Iustin Pop
608 53c04d04 Iustin Pop
  op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn)
609 53c04d04 Iustin Pop
610 53c04d04 Iustin Pop
  return op_results[0]
611 0a1e74d9 Iustin Pop
612 0a1e74d9 Iustin Pop
613 94428652 Iustin Pop
def SubmitOrSend(op, opts, cl=None, feedback_fn=None):
614 94428652 Iustin Pop
  """Wrapper around SubmitOpCode or SendJob.
615 94428652 Iustin Pop

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

621 94428652 Iustin Pop
  """
622 94428652 Iustin Pop
  if opts and opts.submit_only:
623 e9d741b6 Iustin Pop
    job_id = SendJob([op], cl=cl)
624 e9d741b6 Iustin Pop
    raise JobSubmittedException(job_id)
625 94428652 Iustin Pop
  else:
626 94428652 Iustin Pop
    return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn)
627 94428652 Iustin Pop
628 94428652 Iustin Pop
629 af30b2fd Michael Hanselmann
def GetClient():
630 af30b2fd Michael Hanselmann
  # TODO: Cache object?
631 b33e986b Iustin Pop
  try:
632 b33e986b Iustin Pop
    client = luxi.Client()
633 b33e986b Iustin Pop
  except luxi.NoMasterError:
634 b33e986b Iustin Pop
    master, myself = ssconf.GetMasterAndMyself()
635 b33e986b Iustin Pop
    if master != myself:
636 b33e986b Iustin Pop
      raise errors.OpPrereqError("This is not the master node, please connect"
637 b33e986b Iustin Pop
                                 " to node '%s' and rerun the command" %
638 b33e986b Iustin Pop
                                 master)
639 b33e986b Iustin Pop
    else:
640 b33e986b Iustin Pop
      raise
641 b33e986b Iustin Pop
  return client
642 af30b2fd Michael Hanselmann
643 af30b2fd Michael Hanselmann
644 73702ee7 Iustin Pop
def FormatError(err):
645 73702ee7 Iustin Pop
  """Return a formatted error message for a given error.
646 73702ee7 Iustin Pop

647 73702ee7 Iustin Pop
  This function takes an exception instance and returns a tuple
648 73702ee7 Iustin Pop
  consisting of two values: first, the recommended exit code, and
649 73702ee7 Iustin Pop
  second, a string describing the error message (not
650 73702ee7 Iustin Pop
  newline-terminated).
651 73702ee7 Iustin Pop

652 73702ee7 Iustin Pop
  """
653 73702ee7 Iustin Pop
  retcode = 1
654 73702ee7 Iustin Pop
  obuf = StringIO()
655 e2e521d0 Iustin Pop
  msg = str(err)
656 73702ee7 Iustin Pop
  if isinstance(err, errors.ConfigurationError):
657 e2e521d0 Iustin Pop
    txt = "Corrupt configuration file: %s" % msg
658 46fbdd04 Iustin Pop
    logging.error(txt)
659 e2e521d0 Iustin Pop
    obuf.write(txt + "\n")
660 73702ee7 Iustin Pop
    obuf.write("Aborting.")
661 73702ee7 Iustin Pop
    retcode = 2
662 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksAbort):
663 73702ee7 Iustin Pop
    obuf.write("Failure: hooks execution failed:\n")
664 73702ee7 Iustin Pop
    for node, script, out in err.args[0]:
665 73702ee7 Iustin Pop
      if out:
666 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s, output: %s\n" %
667 73702ee7 Iustin Pop
                   (node, script, out))
668 73702ee7 Iustin Pop
      else:
669 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s (no output)\n" %
670 73702ee7 Iustin Pop
                   (node, script))
671 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksFailure):
672 e2e521d0 Iustin Pop
    obuf.write("Failure: hooks general failure: %s" % msg)
673 73702ee7 Iustin Pop
  elif isinstance(err, errors.ResolverError):
674 73702ee7 Iustin Pop
    this_host = utils.HostInfo.SysName()
675 73702ee7 Iustin Pop
    if err.args[0] == this_host:
676 73702ee7 Iustin Pop
      msg = "Failure: can't resolve my own hostname ('%s')"
677 73702ee7 Iustin Pop
    else:
678 73702ee7 Iustin Pop
      msg = "Failure: can't resolve hostname '%s'"
679 73702ee7 Iustin Pop
    obuf.write(msg % err.args[0])
680 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpPrereqError):
681 73702ee7 Iustin Pop
    obuf.write("Failure: prerequisites not met for this"
682 e2e521d0 Iustin Pop
               " operation:\n%s" % msg)
683 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpExecError):
684 e2e521d0 Iustin Pop
    obuf.write("Failure: command execution error:\n%s" % msg)
685 73702ee7 Iustin Pop
  elif isinstance(err, errors.TagError):
686 e2e521d0 Iustin Pop
    obuf.write("Failure: invalid tag(s) given:\n%s" % msg)
687 686d7433 Iustin Pop
  elif isinstance(err, errors.JobQueueDrainError):
688 686d7433 Iustin Pop
    obuf.write("Failure: the job queue is marked for drain and doesn't"
689 686d7433 Iustin Pop
               " accept new requests\n")
690 f87b405e Michael Hanselmann
  elif isinstance(err, errors.JobQueueFull):
691 f87b405e Michael Hanselmann
    obuf.write("Failure: the job queue is full and doesn't accept new"
692 f87b405e Michael Hanselmann
               " job submissions until old jobs are archived\n")
693 73702ee7 Iustin Pop
  elif isinstance(err, errors.GenericError):
694 e2e521d0 Iustin Pop
    obuf.write("Unhandled Ganeti error: %s" % msg)
695 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.NoMasterError):
696 03a8dbdc Iustin Pop
    obuf.write("Cannot communicate with the master daemon.\nIs it running"
697 082c5adb Michael Hanselmann
               " and listening for connections?")
698 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.TimeoutError):
699 03a8dbdc Iustin Pop
    obuf.write("Timeout while talking to the master daemon. Error:\n"
700 03a8dbdc Iustin Pop
               "%s" % msg)
701 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.ProtocolError):
702 03a8dbdc Iustin Pop
    obuf.write("Unhandled protocol error while talking to the master daemon:\n"
703 03a8dbdc Iustin Pop
               "%s" % msg)
704 e9d741b6 Iustin Pop
  elif isinstance(err, JobSubmittedException):
705 e9d741b6 Iustin Pop
    obuf.write("JobID: %s\n" % err.args[0])
706 e9d741b6 Iustin Pop
    retcode = 0
707 73702ee7 Iustin Pop
  else:
708 e2e521d0 Iustin Pop
    obuf.write("Unhandled exception: %s" % msg)
709 73702ee7 Iustin Pop
  return retcode, obuf.getvalue().rstrip('\n')
710 73702ee7 Iustin Pop
711 73702ee7 Iustin Pop
712 de47cf8f Guido Trotter
def GenericMain(commands, override=None, aliases=None):
713 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
714 a8083063 Iustin Pop

715 334d1483 Iustin Pop
  Arguments:
716 334d1483 Iustin Pop
    - commands: a dictionary with a special structure, see the design doc
717 334d1483 Iustin Pop
                for command line handling.
718 334d1483 Iustin Pop
    - override: if not None, we expect a dictionary with keys that will
719 334d1483 Iustin Pop
                override command line options; this can be used to pass
720 334d1483 Iustin Pop
                options from the scripts to generic functions
721 de47cf8f Guido Trotter
    - aliases: dictionary with command aliases {'alias': 'target, ...}
722 a8083063 Iustin Pop

723 a8083063 Iustin Pop
  """
724 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
725 a8083063 Iustin Pop
  if sys.argv:
726 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
727 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
728 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
729 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
730 a8083063 Iustin Pop
    else:
731 a8083063 Iustin Pop
      old_cmdline = ""
732 a8083063 Iustin Pop
  else:
733 a8083063 Iustin Pop
    binary = "<unknown program>"
734 a8083063 Iustin Pop
    old_cmdline = ""
735 a8083063 Iustin Pop
736 de47cf8f Guido Trotter
  if aliases is None:
737 de47cf8f Guido Trotter
    aliases = {}
738 de47cf8f Guido Trotter
739 de47cf8f Guido Trotter
  func, options, args = _ParseArgs(sys.argv, commands, aliases)
740 a8083063 Iustin Pop
  if func is None: # parse error
741 a8083063 Iustin Pop
    return 1
742 a8083063 Iustin Pop
743 334d1483 Iustin Pop
  if override is not None:
744 334d1483 Iustin Pop
    for key, val in override.iteritems():
745 334d1483 Iustin Pop
      setattr(options, key, val)
746 334d1483 Iustin Pop
747 82d9caef Iustin Pop
  utils.SetupLogging(constants.LOG_COMMANDS, debug=options.debug,
748 82d9caef Iustin Pop
                     stderr_logging=True, program=binary)
749 a8083063 Iustin Pop
750 f362096f Iustin Pop
  utils.debug = options.debug
751 a8083063 Iustin Pop
752 a8083063 Iustin Pop
  if old_cmdline:
753 46fbdd04 Iustin Pop
    logging.info("run with arguments '%s'", old_cmdline)
754 a8083063 Iustin Pop
  else:
755 46fbdd04 Iustin Pop
    logging.info("run with no arguments")
756 a8083063 Iustin Pop
757 a8083063 Iustin Pop
  try:
758 a4af651e Iustin Pop
    result = func(options, args)
759 d8353c3a Iustin Pop
  except (errors.GenericError, luxi.ProtocolError,
760 d8353c3a Iustin Pop
          JobSubmittedException), err:
761 a4af651e Iustin Pop
    result, err_msg = FormatError(err)
762 46fbdd04 Iustin Pop
    logging.exception("Error durring command processing")
763 46fbdd04 Iustin Pop
    ToStderr(err_msg)
764 a8083063 Iustin Pop
765 a8083063 Iustin Pop
  return result
766 137161c9 Michael Hanselmann
767 137161c9 Michael Hanselmann
768 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
769 9fbfbb7b Iustin Pop
                  numfields=None, unitfields=None,
770 9fbfbb7b Iustin Pop
                  units=None):
771 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
772 137161c9 Michael Hanselmann

773 9fbfbb7b Iustin Pop
  @type headers: dict
774 9fbfbb7b Iustin Pop
  @param headers: dictionary mapping field names to headers for
775 9fbfbb7b Iustin Pop
      the table
776 9fbfbb7b Iustin Pop
  @type fields: list
777 9fbfbb7b Iustin Pop
  @param fields: the field names corresponding to each row in
778 9fbfbb7b Iustin Pop
      the data field
779 9fbfbb7b Iustin Pop
  @param separator: the separator to be used; if this is None,
780 9fbfbb7b Iustin Pop
      the default 'smart' algorithm is used which computes optimal
781 9fbfbb7b Iustin Pop
      field width, otherwise just the separator is used between
782 9fbfbb7b Iustin Pop
      each field
783 9fbfbb7b Iustin Pop
  @type data: list
784 9fbfbb7b Iustin Pop
  @param data: a list of lists, each sublist being one row to be output
785 9fbfbb7b Iustin Pop
  @type numfields: list
786 9fbfbb7b Iustin Pop
  @param numfields: a list with the fields that hold numeric
787 9fbfbb7b Iustin Pop
      values and thus should be right-aligned
788 9fbfbb7b Iustin Pop
  @type unitfields: list
789 9fbfbb7b Iustin Pop
  @param unitfields: a list with the fields that hold numeric
790 9fbfbb7b Iustin Pop
      values that should be formatted with the units field
791 9fbfbb7b Iustin Pop
  @type units: string or None
792 9fbfbb7b Iustin Pop
  @param units: the units we should use for formatting, or None for
793 9fbfbb7b Iustin Pop
      automatic choice (human-readable for non-separator usage, otherwise
794 9fbfbb7b Iustin Pop
      megabytes); this is a one-letter string
795 137161c9 Michael Hanselmann

796 137161c9 Michael Hanselmann
  """
797 9fbfbb7b Iustin Pop
  if units is None:
798 9fbfbb7b Iustin Pop
    if separator:
799 9fbfbb7b Iustin Pop
      units = "m"
800 9fbfbb7b Iustin Pop
    else:
801 9fbfbb7b Iustin Pop
      units = "h"
802 9fbfbb7b Iustin Pop
803 137161c9 Michael Hanselmann
  if numfields is None:
804 137161c9 Michael Hanselmann
    numfields = []
805 137161c9 Michael Hanselmann
  if unitfields is None:
806 137161c9 Michael Hanselmann
    unitfields = []
807 137161c9 Michael Hanselmann
808 00430f8e Iustin Pop
  numfields = utils.FieldSet(*numfields)
809 00430f8e Iustin Pop
  unitfields = utils.FieldSet(*unitfields)
810 00430f8e Iustin Pop
811 137161c9 Michael Hanselmann
  format_fields = []
812 137161c9 Michael Hanselmann
  for field in fields:
813 01ca31ae Iustin Pop
    if headers and field not in headers:
814 71c1af58 Iustin Pop
      # FIXME: handle better unknown fields (either revert to old
815 71c1af58 Iustin Pop
      # style of raising exception, or deal more intelligently with
816 71c1af58 Iustin Pop
      # variable fields)
817 71c1af58 Iustin Pop
      headers[field] = field
818 137161c9 Michael Hanselmann
    if separator is not None:
819 137161c9 Michael Hanselmann
      format_fields.append("%s")
820 00430f8e Iustin Pop
    elif numfields.Matches(field):
821 137161c9 Michael Hanselmann
      format_fields.append("%*s")
822 137161c9 Michael Hanselmann
    else:
823 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
824 137161c9 Michael Hanselmann
825 137161c9 Michael Hanselmann
  if separator is None:
826 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
827 137161c9 Michael Hanselmann
    format = ' '.join(format_fields)
828 137161c9 Michael Hanselmann
  else:
829 137161c9 Michael Hanselmann
    format = separator.replace("%", "%%").join(format_fields)
830 137161c9 Michael Hanselmann
831 137161c9 Michael Hanselmann
  for row in data:
832 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
833 00430f8e Iustin Pop
      if unitfields.Matches(fields[idx]):
834 137161c9 Michael Hanselmann
        try:
835 137161c9 Michael Hanselmann
          val = int(val)
836 137161c9 Michael Hanselmann
        except ValueError:
837 137161c9 Michael Hanselmann
          pass
838 137161c9 Michael Hanselmann
        else:
839 9fbfbb7b Iustin Pop
          val = row[idx] = utils.FormatUnit(val, units)
840 01ca31ae Iustin Pop
      val = row[idx] = str(val)
841 137161c9 Michael Hanselmann
      if separator is None:
842 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
843 137161c9 Michael Hanselmann
844 16be8703 Iustin Pop
  result = []
845 137161c9 Michael Hanselmann
  if headers:
846 137161c9 Michael Hanselmann
    args = []
847 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
848 137161c9 Michael Hanselmann
      hdr = headers[name]
849 137161c9 Michael Hanselmann
      if separator is None:
850 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
851 137161c9 Michael Hanselmann
        args.append(mlens[idx])
852 137161c9 Michael Hanselmann
      args.append(hdr)
853 16be8703 Iustin Pop
    result.append(format % tuple(args))
854 137161c9 Michael Hanselmann
855 137161c9 Michael Hanselmann
  for line in data:
856 137161c9 Michael Hanselmann
    args = []
857 137161c9 Michael Hanselmann
    for idx in xrange(len(fields)):
858 137161c9 Michael Hanselmann
      if separator is None:
859 137161c9 Michael Hanselmann
        args.append(mlens[idx])
860 137161c9 Michael Hanselmann
      args.append(line[idx])
861 16be8703 Iustin Pop
    result.append(format % tuple(args))
862 16be8703 Iustin Pop
863 16be8703 Iustin Pop
  return result
864 3386e7a9 Iustin Pop
865 3386e7a9 Iustin Pop
866 3386e7a9 Iustin Pop
def FormatTimestamp(ts):
867 3386e7a9 Iustin Pop
  """Formats a given timestamp.
868 3386e7a9 Iustin Pop

869 3386e7a9 Iustin Pop
  @type ts: timestamp
870 3386e7a9 Iustin Pop
  @param ts: a timeval-type timestamp, a tuple of seconds and microseconds
871 3386e7a9 Iustin Pop

872 3386e7a9 Iustin Pop
  @rtype: string
873 3386e7a9 Iustin Pop
  @returns: a string with the formatted timestamp
874 3386e7a9 Iustin Pop

875 3386e7a9 Iustin Pop
  """
876 e0ec0ff6 Iustin Pop
  if not isinstance (ts, (tuple, list)) or len(ts) != 2:
877 e0ec0ff6 Iustin Pop
    return '?'
878 3386e7a9 Iustin Pop
  sec, usec = ts
879 3386e7a9 Iustin Pop
  return time.strftime("%F %T", time.localtime(sec)) + ".%06d" % usec
880 2241e2b9 Iustin Pop
881 2241e2b9 Iustin Pop
882 2241e2b9 Iustin Pop
def ParseTimespec(value):
883 2241e2b9 Iustin Pop
  """Parse a time specification.
884 2241e2b9 Iustin Pop

885 2241e2b9 Iustin Pop
  The following suffixed will be recognized:
886 2241e2b9 Iustin Pop

887 2241e2b9 Iustin Pop
    - s: seconds
888 2241e2b9 Iustin Pop
    - m: minutes
889 2241e2b9 Iustin Pop
    - h: hours
890 2241e2b9 Iustin Pop
    - d: day
891 2241e2b9 Iustin Pop
    - w: weeks
892 2241e2b9 Iustin Pop

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

895 2241e2b9 Iustin Pop
  """
896 2241e2b9 Iustin Pop
  value = str(value)
897 2241e2b9 Iustin Pop
  if not value:
898 2241e2b9 Iustin Pop
    raise errors.OpPrereqError("Empty time specification passed")
899 2241e2b9 Iustin Pop
  suffix_map = {
900 2241e2b9 Iustin Pop
    's': 1,
901 2241e2b9 Iustin Pop
    'm': 60,
902 2241e2b9 Iustin Pop
    'h': 3600,
903 2241e2b9 Iustin Pop
    'd': 86400,
904 2241e2b9 Iustin Pop
    'w': 604800,
905 2241e2b9 Iustin Pop
    }
906 2241e2b9 Iustin Pop
  if value[-1] not in suffix_map:
907 2241e2b9 Iustin Pop
    try:
908 2241e2b9 Iustin Pop
      value = int(value)
909 2241e2b9 Iustin Pop
    except ValueError:
910 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
911 2241e2b9 Iustin Pop
  else:
912 2241e2b9 Iustin Pop
    multiplier = suffix_map[value[-1]]
913 2241e2b9 Iustin Pop
    value = value[:-1]
914 2241e2b9 Iustin Pop
    if not value: # no data left after stripping the suffix
915 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification (only"
916 2241e2b9 Iustin Pop
                                 " suffix passed)")
917 2241e2b9 Iustin Pop
    try:
918 2241e2b9 Iustin Pop
      value = int(value) * multiplier
919 2241e2b9 Iustin Pop
    except ValueError:
920 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
921 2241e2b9 Iustin Pop
  return value
922 46fbdd04 Iustin Pop
923 46fbdd04 Iustin Pop
924 46fbdd04 Iustin Pop
def _ToStream(stream, txt, *args):
925 46fbdd04 Iustin Pop
  """Write a message to a stream, bypassing the logging system
926 46fbdd04 Iustin Pop

927 46fbdd04 Iustin Pop
  @type stream: file object
928 46fbdd04 Iustin Pop
  @param stream: the file to which we should write
929 46fbdd04 Iustin Pop
  @type txt: str
930 46fbdd04 Iustin Pop
  @param txt: the message
931 46fbdd04 Iustin Pop

932 46fbdd04 Iustin Pop
  """
933 46fbdd04 Iustin Pop
  if args:
934 46fbdd04 Iustin Pop
    args = tuple(args)
935 46fbdd04 Iustin Pop
    stream.write(txt % args)
936 46fbdd04 Iustin Pop
  else:
937 46fbdd04 Iustin Pop
    stream.write(txt)
938 46fbdd04 Iustin Pop
  stream.write('\n')
939 46fbdd04 Iustin Pop
  stream.flush()
940 46fbdd04 Iustin Pop
941 46fbdd04 Iustin Pop
942 46fbdd04 Iustin Pop
def ToStdout(txt, *args):
943 46fbdd04 Iustin Pop
  """Write a message to stdout only, bypassing the logging system
944 46fbdd04 Iustin Pop

945 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
946 46fbdd04 Iustin Pop

947 46fbdd04 Iustin Pop
  @type txt: str
948 46fbdd04 Iustin Pop
  @param txt: the message
949 46fbdd04 Iustin Pop

950 46fbdd04 Iustin Pop
  """
951 46fbdd04 Iustin Pop
  _ToStream(sys.stdout, txt, *args)
952 46fbdd04 Iustin Pop
953 46fbdd04 Iustin Pop
954 46fbdd04 Iustin Pop
def ToStderr(txt, *args):
955 46fbdd04 Iustin Pop
  """Write a message to stderr only, bypassing the logging system
956 46fbdd04 Iustin Pop

957 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
958 46fbdd04 Iustin Pop

959 46fbdd04 Iustin Pop
  @type txt: str
960 46fbdd04 Iustin Pop
  @param txt: the message
961 46fbdd04 Iustin Pop

962 46fbdd04 Iustin Pop
  """
963 46fbdd04 Iustin Pop
  _ToStream(sys.stderr, txt, *args)