Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ 4e5a68f8

History | View | Annotate | Download (30.9 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Module dealing with command line parsing"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
25 a8083063 Iustin Pop
import sys
26 a8083063 Iustin Pop
import textwrap
27 a8083063 Iustin Pop
import os.path
28 a8083063 Iustin Pop
import copy
29 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 479636a3 Iustin Pop
           "ValidateBeParams", "ToStderr", "ToStdout", "UsesRPC",
54 479636a3 Iustin Pop
           "GetOnlineNodes", "JobExecutor",
55 846baef9 Iustin Pop
           ]
56 846baef9 Iustin Pop
57 846baef9 Iustin Pop
58 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
59 846baef9 Iustin Pop
  """Extract the tag type object.
60 846baef9 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

894 2241e2b9 Iustin Pop
  """
895 2241e2b9 Iustin Pop
  value = str(value)
896 2241e2b9 Iustin Pop
  if not value:
897 2241e2b9 Iustin Pop
    raise errors.OpPrereqError("Empty time specification passed")
898 2241e2b9 Iustin Pop
  suffix_map = {
899 2241e2b9 Iustin Pop
    's': 1,
900 2241e2b9 Iustin Pop
    'm': 60,
901 2241e2b9 Iustin Pop
    'h': 3600,
902 2241e2b9 Iustin Pop
    'd': 86400,
903 2241e2b9 Iustin Pop
    'w': 604800,
904 2241e2b9 Iustin Pop
    }
905 2241e2b9 Iustin Pop
  if value[-1] not in suffix_map:
906 2241e2b9 Iustin Pop
    try:
907 2241e2b9 Iustin Pop
      value = int(value)
908 2241e2b9 Iustin Pop
    except ValueError:
909 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
910 2241e2b9 Iustin Pop
  else:
911 2241e2b9 Iustin Pop
    multiplier = suffix_map[value[-1]]
912 2241e2b9 Iustin Pop
    value = value[:-1]
913 2241e2b9 Iustin Pop
    if not value: # no data left after stripping the suffix
914 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification (only"
915 2241e2b9 Iustin Pop
                                 " suffix passed)")
916 2241e2b9 Iustin Pop
    try:
917 2241e2b9 Iustin Pop
      value = int(value) * multiplier
918 2241e2b9 Iustin Pop
    except ValueError:
919 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
920 2241e2b9 Iustin Pop
  return value
921 46fbdd04 Iustin Pop
922 46fbdd04 Iustin Pop
923 4040a784 Iustin Pop
def GetOnlineNodes(nodes, cl=None, nowarn=False):
924 4040a784 Iustin Pop
  """Returns the names of online nodes.
925 4040a784 Iustin Pop

926 4040a784 Iustin Pop
  This function will also log a warning on stderr with the names of
927 4040a784 Iustin Pop
  the online nodes.
928 4040a784 Iustin Pop

929 4040a784 Iustin Pop
  @param nodes: if not empty, use only this subset of nodes (minus the
930 4040a784 Iustin Pop
      offline ones)
931 4040a784 Iustin Pop
  @param cl: if not None, luxi client to use
932 4040a784 Iustin Pop
  @type nowarn: boolean
933 4040a784 Iustin Pop
  @param nowarn: by default, this function will output a note with the
934 4040a784 Iustin Pop
      offline nodes that are skipped; if this parameter is True the
935 4040a784 Iustin Pop
      note is not displayed
936 4040a784 Iustin Pop

937 4040a784 Iustin Pop
  """
938 4040a784 Iustin Pop
  if cl is None:
939 4040a784 Iustin Pop
    cl = GetClient()
940 4040a784 Iustin Pop
941 4040a784 Iustin Pop
  op = opcodes.OpQueryNodes(output_fields=["name", "offline"],
942 4040a784 Iustin Pop
                            names=nodes)
943 4040a784 Iustin Pop
  result = SubmitOpCode(op, cl=cl)
944 4040a784 Iustin Pop
  offline = [row[0] for row in result if row[1]]
945 4040a784 Iustin Pop
  if offline and not nowarn:
946 4040a784 Iustin Pop
    ToStderr("Note: skipping offline node(s): %s" % ", ".join(offline))
947 4040a784 Iustin Pop
  return [row[0] for row in result if not row[1]]
948 4040a784 Iustin Pop
949 4040a784 Iustin Pop
950 46fbdd04 Iustin Pop
def _ToStream(stream, txt, *args):
951 46fbdd04 Iustin Pop
  """Write a message to a stream, bypassing the logging system
952 46fbdd04 Iustin Pop

953 46fbdd04 Iustin Pop
  @type stream: file object
954 46fbdd04 Iustin Pop
  @param stream: the file to which we should write
955 46fbdd04 Iustin Pop
  @type txt: str
956 46fbdd04 Iustin Pop
  @param txt: the message
957 46fbdd04 Iustin Pop

958 46fbdd04 Iustin Pop
  """
959 46fbdd04 Iustin Pop
  if args:
960 46fbdd04 Iustin Pop
    args = tuple(args)
961 46fbdd04 Iustin Pop
    stream.write(txt % args)
962 46fbdd04 Iustin Pop
  else:
963 46fbdd04 Iustin Pop
    stream.write(txt)
964 46fbdd04 Iustin Pop
  stream.write('\n')
965 46fbdd04 Iustin Pop
  stream.flush()
966 46fbdd04 Iustin Pop
967 46fbdd04 Iustin Pop
968 46fbdd04 Iustin Pop
def ToStdout(txt, *args):
969 46fbdd04 Iustin Pop
  """Write a message to stdout only, bypassing the logging system
970 46fbdd04 Iustin Pop

971 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
972 46fbdd04 Iustin Pop

973 46fbdd04 Iustin Pop
  @type txt: str
974 46fbdd04 Iustin Pop
  @param txt: the message
975 46fbdd04 Iustin Pop

976 46fbdd04 Iustin Pop
  """
977 46fbdd04 Iustin Pop
  _ToStream(sys.stdout, txt, *args)
978 46fbdd04 Iustin Pop
979 46fbdd04 Iustin Pop
980 46fbdd04 Iustin Pop
def ToStderr(txt, *args):
981 46fbdd04 Iustin Pop
  """Write a message to stderr only, bypassing the logging system
982 46fbdd04 Iustin Pop

983 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
984 46fbdd04 Iustin Pop

985 46fbdd04 Iustin Pop
  @type txt: str
986 46fbdd04 Iustin Pop
  @param txt: the message
987 46fbdd04 Iustin Pop

988 46fbdd04 Iustin Pop
  """
989 46fbdd04 Iustin Pop
  _ToStream(sys.stderr, txt, *args)
990 479636a3 Iustin Pop
991 479636a3 Iustin Pop
992 479636a3 Iustin Pop
class JobExecutor(object):
993 479636a3 Iustin Pop
  """Class which manages the submission and execution of multiple jobs.
994 479636a3 Iustin Pop

995 479636a3 Iustin Pop
  Note that instances of this class should not be reused between
996 479636a3 Iustin Pop
  GetResults() calls.
997 479636a3 Iustin Pop

998 479636a3 Iustin Pop
  """
999 479636a3 Iustin Pop
  def __init__(self, cl=None, verbose=True):
1000 479636a3 Iustin Pop
    self.queue = []
1001 479636a3 Iustin Pop
    if cl is None:
1002 479636a3 Iustin Pop
      cl = GetClient()
1003 479636a3 Iustin Pop
    self.cl = cl
1004 479636a3 Iustin Pop
    self.verbose = verbose
1005 479636a3 Iustin Pop
1006 479636a3 Iustin Pop
  def QueueJob(self, name, *ops):
1007 479636a3 Iustin Pop
    """Submit a job for execution.
1008 479636a3 Iustin Pop

1009 479636a3 Iustin Pop
    @type name: string
1010 479636a3 Iustin Pop
    @param name: a description of the job, will be used in WaitJobSet
1011 479636a3 Iustin Pop
    """
1012 479636a3 Iustin Pop
    job_id = SendJob(ops, cl=self.cl)
1013 479636a3 Iustin Pop
    self.queue.append((job_id, name))
1014 479636a3 Iustin Pop
1015 479636a3 Iustin Pop
  def GetResults(self):
1016 479636a3 Iustin Pop
    """Wait for and return the results of all jobs.
1017 479636a3 Iustin Pop

1018 479636a3 Iustin Pop
    @rtype: list
1019 479636a3 Iustin Pop
    @return: list of tuples (success, job results), in the same order
1020 479636a3 Iustin Pop
        as the submitted jobs; if a job has failed, instead of the result
1021 479636a3 Iustin Pop
        there will be the error message
1022 479636a3 Iustin Pop

1023 479636a3 Iustin Pop
    """
1024 479636a3 Iustin Pop
    results = []
1025 479636a3 Iustin Pop
    if self.verbose:
1026 479636a3 Iustin Pop
      ToStdout("Submitted jobs %s", ", ".join(row[0] for row in self.queue))
1027 479636a3 Iustin Pop
    for jid, name in self.queue:
1028 479636a3 Iustin Pop
      if self.verbose:
1029 479636a3 Iustin Pop
        ToStdout("Waiting for job %s for %s...", jid, name)
1030 479636a3 Iustin Pop
      try:
1031 479636a3 Iustin Pop
        job_result = PollJob(jid, cl=self.cl)
1032 479636a3 Iustin Pop
        success = True
1033 479636a3 Iustin Pop
      except (errors.GenericError, luxi.ProtocolError), err:
1034 479636a3 Iustin Pop
        _, job_result = FormatError(err)
1035 479636a3 Iustin Pop
        success = False
1036 479636a3 Iustin Pop
        # the error message will always be shown, verbose or not
1037 479636a3 Iustin Pop
        ToStderr("Job %s for %s has failed: %s", jid, name, job_result)
1038 479636a3 Iustin Pop
1039 479636a3 Iustin Pop
      results.append((success, job_result))
1040 479636a3 Iustin Pop
    return results
1041 479636a3 Iustin Pop
1042 479636a3 Iustin Pop
  def WaitOrShow(self, wait):
1043 479636a3 Iustin Pop
    """Wait for job results or only print the job IDs.
1044 479636a3 Iustin Pop

1045 479636a3 Iustin Pop
    @type wait: boolean
1046 479636a3 Iustin Pop
    @param wait: whether to wait or not
1047 479636a3 Iustin Pop

1048 479636a3 Iustin Pop
    """
1049 479636a3 Iustin Pop
    if wait:
1050 479636a3 Iustin Pop
      return self.GetResults()
1051 479636a3 Iustin Pop
    else:
1052 479636a3 Iustin Pop
      for jid, name in self.queue:
1053 479636a3 Iustin Pop
        ToStdout("%s: %s", jid, name)