Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ 4a25828c

History | View | Annotate | Download (37.4 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 c38c44ad Michael Hanselmann
from optparse import (OptionParser, TitledHelpFormatter,
42 38206f3c Iustin Pop
                      Option, OptionValueError)
43 a8083063 Iustin Pop
44 03298ebe Michael Hanselmann
45 4abc4f1e Iustin Pop
__all__ = [
46 4abc4f1e Iustin Pop
  # Command line options
47 4abc4f1e Iustin Pop
  "CONFIRM_OPT",
48 4abc4f1e Iustin Pop
  "DEBUG_OPT",
49 a0c9776a Iustin Pop
  "DEBUG_SIMERR_OPT",
50 4f365444 Iustin Pop
  "DISK_TEMPLATE_OPT",
51 4abc4f1e Iustin Pop
  "FIELDS_OPT",
52 4a25828c Iustin Pop
  "FILESTORE_DIR_OPT",
53 4abc4f1e Iustin Pop
  "FORCE_OPT",
54 4abc4f1e Iustin Pop
  "NOHDR_OPT",
55 26023ecd Iustin Pop
  "NONICS_OPT",
56 3f75b4f3 Iustin Pop
  "NWSYNC_OPT",
57 4abc4f1e Iustin Pop
  "SEP_OPT",
58 4abc4f1e Iustin Pop
  "SUBMIT_OPT",
59 4abc4f1e Iustin Pop
  "SYNC_OPT",
60 4abc4f1e Iustin Pop
  "TAG_SRC_OPT",
61 4abc4f1e Iustin Pop
  "USEUNITS_OPT",
62 9cdb9578 Iustin Pop
  "VERBOSE_OPT",
63 4abc4f1e Iustin Pop
  # Generic functions for CLI programs
64 4abc4f1e Iustin Pop
  "GenericMain",
65 4abc4f1e Iustin Pop
  "GetClient",
66 4abc4f1e Iustin Pop
  "GetOnlineNodes",
67 4abc4f1e Iustin Pop
  "JobExecutor",
68 4abc4f1e Iustin Pop
  "JobSubmittedException",
69 4abc4f1e Iustin Pop
  "ParseTimespec",
70 4abc4f1e Iustin Pop
  "SubmitOpCode",
71 4abc4f1e Iustin Pop
  "SubmitOrSend",
72 4abc4f1e Iustin Pop
  "UsesRPC",
73 4abc4f1e Iustin Pop
  # Formatting functions
74 4abc4f1e Iustin Pop
  "ToStderr", "ToStdout",
75 4abc4f1e Iustin Pop
  "FormatError",
76 4abc4f1e Iustin Pop
  "GenerateTable",
77 4abc4f1e Iustin Pop
  "AskUser",
78 4abc4f1e Iustin Pop
  "FormatTimestamp",
79 4abc4f1e Iustin Pop
  # Tags functions
80 4abc4f1e Iustin Pop
  "ListTags",
81 4abc4f1e Iustin Pop
  "AddTags",
82 4abc4f1e Iustin Pop
  "RemoveTags",
83 4abc4f1e Iustin Pop
  # command line options support infrastructure
84 4abc4f1e Iustin Pop
  "ARGS_MANY_INSTANCES",
85 4abc4f1e Iustin Pop
  "ARGS_MANY_NODES",
86 4abc4f1e Iustin Pop
  "ARGS_NONE",
87 4abc4f1e Iustin Pop
  "ARGS_ONE_INSTANCE",
88 4abc4f1e Iustin Pop
  "ARGS_ONE_NODE",
89 4abc4f1e Iustin Pop
  "ArgChoice",
90 4abc4f1e Iustin Pop
  "ArgCommand",
91 4abc4f1e Iustin Pop
  "ArgFile",
92 4abc4f1e Iustin Pop
  "ArgHost",
93 4abc4f1e Iustin Pop
  "ArgInstance",
94 4abc4f1e Iustin Pop
  "ArgJobId",
95 4abc4f1e Iustin Pop
  "ArgNode",
96 4abc4f1e Iustin Pop
  "ArgSuggest",
97 4abc4f1e Iustin Pop
  "ArgUnknown",
98 4abc4f1e Iustin Pop
  "OPT_COMPL_INST_ADD_NODES",
99 4abc4f1e Iustin Pop
  "OPT_COMPL_MANY_NODES",
100 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_IALLOCATOR",
101 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_INSTANCE",
102 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_NODE",
103 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_OS",
104 4abc4f1e Iustin Pop
  "cli_option",
105 4abc4f1e Iustin Pop
  "SplitNodeOption",
106 4abc4f1e Iustin Pop
  ]
107 846baef9 Iustin Pop
108 8b46606c Guido Trotter
NO_PREFIX = "no_"
109 8b46606c Guido Trotter
UN_PREFIX = "-"
110 846baef9 Iustin Pop
111 03298ebe Michael Hanselmann
112 863d7f46 Michael Hanselmann
class _Argument:
113 dff85078 Michael Hanselmann
  def __init__(self, min=0, max=None):
114 863d7f46 Michael Hanselmann
    self.min = min
115 863d7f46 Michael Hanselmann
    self.max = max
116 863d7f46 Michael Hanselmann
117 863d7f46 Michael Hanselmann
  def __repr__(self):
118 863d7f46 Michael Hanselmann
    return ("<%s min=%s max=%s>" %
119 863d7f46 Michael Hanselmann
            (self.__class__.__name__, self.min, self.max))
120 863d7f46 Michael Hanselmann
121 863d7f46 Michael Hanselmann
122 863d7f46 Michael Hanselmann
class ArgSuggest(_Argument):
123 863d7f46 Michael Hanselmann
  """Suggesting argument.
124 863d7f46 Michael Hanselmann

125 863d7f46 Michael Hanselmann
  Value can be any of the ones passed to the constructor.
126 863d7f46 Michael Hanselmann

127 863d7f46 Michael Hanselmann
  """
128 863d7f46 Michael Hanselmann
  def __init__(self, min=0, max=None, choices=None):
129 863d7f46 Michael Hanselmann
    _Argument.__init__(self, min=min, max=max)
130 863d7f46 Michael Hanselmann
    self.choices = choices
131 863d7f46 Michael Hanselmann
132 863d7f46 Michael Hanselmann
  def __repr__(self):
133 863d7f46 Michael Hanselmann
    return ("<%s min=%s max=%s choices=%r>" %
134 863d7f46 Michael Hanselmann
            (self.__class__.__name__, self.min, self.max, self.choices))
135 863d7f46 Michael Hanselmann
136 863d7f46 Michael Hanselmann
137 863d7f46 Michael Hanselmann
class ArgChoice(ArgSuggest):
138 863d7f46 Michael Hanselmann
  """Choice argument.
139 863d7f46 Michael Hanselmann

140 863d7f46 Michael Hanselmann
  Value can be any of the ones passed to the constructor. Like L{ArgSuggest},
141 863d7f46 Michael Hanselmann
  but value must be one of the choices.
142 863d7f46 Michael Hanselmann

143 863d7f46 Michael Hanselmann
  """
144 863d7f46 Michael Hanselmann
145 863d7f46 Michael Hanselmann
146 863d7f46 Michael Hanselmann
class ArgUnknown(_Argument):
147 863d7f46 Michael Hanselmann
  """Unknown argument to program (e.g. determined at runtime).
148 863d7f46 Michael Hanselmann

149 863d7f46 Michael Hanselmann
  """
150 863d7f46 Michael Hanselmann
151 863d7f46 Michael Hanselmann
152 863d7f46 Michael Hanselmann
class ArgInstance(_Argument):
153 863d7f46 Michael Hanselmann
  """Instances argument.
154 863d7f46 Michael Hanselmann

155 863d7f46 Michael Hanselmann
  """
156 863d7f46 Michael Hanselmann
157 863d7f46 Michael Hanselmann
158 863d7f46 Michael Hanselmann
class ArgNode(_Argument):
159 863d7f46 Michael Hanselmann
  """Node argument.
160 863d7f46 Michael Hanselmann

161 863d7f46 Michael Hanselmann
  """
162 863d7f46 Michael Hanselmann
163 863d7f46 Michael Hanselmann
class ArgJobId(_Argument):
164 863d7f46 Michael Hanselmann
  """Job ID argument.
165 863d7f46 Michael Hanselmann

166 863d7f46 Michael Hanselmann
  """
167 863d7f46 Michael Hanselmann
168 863d7f46 Michael Hanselmann
169 863d7f46 Michael Hanselmann
class ArgFile(_Argument):
170 863d7f46 Michael Hanselmann
  """File path argument.
171 863d7f46 Michael Hanselmann

172 863d7f46 Michael Hanselmann
  """
173 863d7f46 Michael Hanselmann
174 863d7f46 Michael Hanselmann
175 863d7f46 Michael Hanselmann
class ArgCommand(_Argument):
176 863d7f46 Michael Hanselmann
  """Command argument.
177 863d7f46 Michael Hanselmann

178 863d7f46 Michael Hanselmann
  """
179 863d7f46 Michael Hanselmann
180 863d7f46 Michael Hanselmann
181 83ec7961 Michael Hanselmann
class ArgHost(_Argument):
182 83ec7961 Michael Hanselmann
  """Host argument.
183 83ec7961 Michael Hanselmann

184 83ec7961 Michael Hanselmann
  """
185 83ec7961 Michael Hanselmann
186 83ec7961 Michael Hanselmann
187 4a265c08 Michael Hanselmann
ARGS_NONE = []
188 4a265c08 Michael Hanselmann
ARGS_MANY_INSTANCES = [ArgInstance()]
189 4a265c08 Michael Hanselmann
ARGS_MANY_NODES = [ArgNode()]
190 4a265c08 Michael Hanselmann
ARGS_ONE_INSTANCE = [ArgInstance(min=1, max=1)]
191 4a265c08 Michael Hanselmann
ARGS_ONE_NODE = [ArgNode(min=1, max=1)]
192 4a265c08 Michael Hanselmann
193 4a265c08 Michael Hanselmann
194 73b90123 Michael Hanselmann
195 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
196 846baef9 Iustin Pop
  """Extract the tag type object.
197 846baef9 Iustin Pop

198 846baef9 Iustin Pop
  Note that this function will modify its args parameter.
199 846baef9 Iustin Pop

200 846baef9 Iustin Pop
  """
201 846baef9 Iustin Pop
  if not hasattr(opts, "tag_type"):
202 846baef9 Iustin Pop
    raise errors.ProgrammerError("tag_type not passed to _ExtractTagsObject")
203 846baef9 Iustin Pop
  kind = opts.tag_type
204 846baef9 Iustin Pop
  if kind == constants.TAG_CLUSTER:
205 846baef9 Iustin Pop
    retval = kind, kind
206 846baef9 Iustin Pop
  elif kind == constants.TAG_NODE or kind == constants.TAG_INSTANCE:
207 846baef9 Iustin Pop
    if not args:
208 0c434948 Iustin Pop
      raise errors.OpPrereqError("no arguments passed to the command")
209 846baef9 Iustin Pop
    name = args.pop(0)
210 846baef9 Iustin Pop
    retval = kind, name
211 846baef9 Iustin Pop
  else:
212 846baef9 Iustin Pop
    raise errors.ProgrammerError("Unhandled tag type '%s'" % kind)
213 846baef9 Iustin Pop
  return retval
214 846baef9 Iustin Pop
215 846baef9 Iustin Pop
216 810c50b7 Iustin Pop
def _ExtendTags(opts, args):
217 810c50b7 Iustin Pop
  """Extend the args if a source file has been given.
218 810c50b7 Iustin Pop

219 810c50b7 Iustin Pop
  This function will extend the tags with the contents of the file
220 810c50b7 Iustin Pop
  passed in the 'tags_source' attribute of the opts parameter. A file
221 810c50b7 Iustin Pop
  named '-' will be replaced by stdin.
222 810c50b7 Iustin Pop

223 810c50b7 Iustin Pop
  """
224 810c50b7 Iustin Pop
  fname = opts.tags_source
225 810c50b7 Iustin Pop
  if fname is None:
226 810c50b7 Iustin Pop
    return
227 810c50b7 Iustin Pop
  if fname == "-":
228 810c50b7 Iustin Pop
    new_fh = sys.stdin
229 810c50b7 Iustin Pop
  else:
230 810c50b7 Iustin Pop
    new_fh = open(fname, "r")
231 810c50b7 Iustin Pop
  new_data = []
232 810c50b7 Iustin Pop
  try:
233 810c50b7 Iustin Pop
    # we don't use the nice 'new_data = [line.strip() for line in fh]'
234 810c50b7 Iustin Pop
    # because of python bug 1633941
235 810c50b7 Iustin Pop
    while True:
236 810c50b7 Iustin Pop
      line = new_fh.readline()
237 810c50b7 Iustin Pop
      if not line:
238 810c50b7 Iustin Pop
        break
239 810c50b7 Iustin Pop
      new_data.append(line.strip())
240 810c50b7 Iustin Pop
  finally:
241 810c50b7 Iustin Pop
    new_fh.close()
242 810c50b7 Iustin Pop
  args.extend(new_data)
243 810c50b7 Iustin Pop
244 810c50b7 Iustin Pop
245 846baef9 Iustin Pop
def ListTags(opts, args):
246 846baef9 Iustin Pop
  """List the tags on a given object.
247 846baef9 Iustin Pop

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

253 846baef9 Iustin Pop
  """
254 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
255 846baef9 Iustin Pop
  op = opcodes.OpGetTags(kind=kind, name=name)
256 846baef9 Iustin Pop
  result = SubmitOpCode(op)
257 846baef9 Iustin Pop
  result = list(result)
258 846baef9 Iustin Pop
  result.sort()
259 846baef9 Iustin Pop
  for tag in result:
260 03298ebe Michael Hanselmann
    ToStdout(tag)
261 846baef9 Iustin Pop
262 846baef9 Iustin Pop
263 846baef9 Iustin Pop
def AddTags(opts, args):
264 846baef9 Iustin Pop
  """Add tags on a given object.
265 846baef9 Iustin Pop

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

271 846baef9 Iustin Pop
  """
272 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
273 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
274 846baef9 Iustin Pop
  if not args:
275 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be added")
276 846baef9 Iustin Pop
  op = opcodes.OpAddTags(kind=kind, name=name, tags=args)
277 846baef9 Iustin Pop
  SubmitOpCode(op)
278 846baef9 Iustin Pop
279 846baef9 Iustin Pop
280 846baef9 Iustin Pop
def RemoveTags(opts, args):
281 846baef9 Iustin Pop
  """Remove tags from a given object.
282 846baef9 Iustin Pop

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

288 846baef9 Iustin Pop
  """
289 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
290 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
291 846baef9 Iustin Pop
  if not args:
292 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be removed")
293 846baef9 Iustin Pop
  op = opcodes.OpDelTags(kind=kind, name=name, tags=args)
294 846baef9 Iustin Pop
  SubmitOpCode(op)
295 846baef9 Iustin Pop
296 a8083063 Iustin Pop
297 a8083063 Iustin Pop
def check_unit(option, opt, value):
298 65fe4693 Iustin Pop
  """OptParsers custom converter for units.
299 65fe4693 Iustin Pop

300 65fe4693 Iustin Pop
  """
301 a8083063 Iustin Pop
  try:
302 a8083063 Iustin Pop
    return utils.ParseUnit(value)
303 a8083063 Iustin Pop
  except errors.UnitParseError, err:
304 3ecf6786 Iustin Pop
    raise OptionValueError("option %s: %s" % (opt, err))
305 a8083063 Iustin Pop
306 a8083063 Iustin Pop
307 a8469393 Iustin Pop
def _SplitKeyVal(opt, data):
308 a8469393 Iustin Pop
  """Convert a KeyVal string into a dict.
309 a8469393 Iustin Pop

310 a8469393 Iustin Pop
  This function will convert a key=val[,...] string into a dict. Empty
311 a8469393 Iustin Pop
  values will be converted specially: keys which have the prefix 'no_'
312 a8469393 Iustin Pop
  will have the value=False and the prefix stripped, the others will
313 a8469393 Iustin Pop
  have value=True.
314 a8469393 Iustin Pop

315 a8469393 Iustin Pop
  @type opt: string
316 a8469393 Iustin Pop
  @param opt: a string holding the option name for which we process the
317 a8469393 Iustin Pop
      data, used in building error messages
318 a8469393 Iustin Pop
  @type data: string
319 a8469393 Iustin Pop
  @param data: a string of the format key=val,key=val,...
320 a8469393 Iustin Pop
  @rtype: dict
321 a8469393 Iustin Pop
  @return: {key=val, key=val}
322 a8469393 Iustin Pop
  @raises errors.ParameterError: if there are duplicate keys
323 a8469393 Iustin Pop

324 a8469393 Iustin Pop
  """
325 a8469393 Iustin Pop
  kv_dict = {}
326 4f31882e Guido Trotter
  if data:
327 4f31882e Guido Trotter
    for elem in data.split(","):
328 4f31882e Guido Trotter
      if "=" in elem:
329 4f31882e Guido Trotter
        key, val = elem.split("=", 1)
330 a8469393 Iustin Pop
      else:
331 4f31882e Guido Trotter
        if elem.startswith(NO_PREFIX):
332 4f31882e Guido Trotter
          key, val = elem[len(NO_PREFIX):], False
333 4f31882e Guido Trotter
        elif elem.startswith(UN_PREFIX):
334 4f31882e Guido Trotter
          key, val = elem[len(UN_PREFIX):], None
335 4f31882e Guido Trotter
        else:
336 4f31882e Guido Trotter
          key, val = elem, True
337 4f31882e Guido Trotter
      if key in kv_dict:
338 4f31882e Guido Trotter
        raise errors.ParameterError("Duplicate key '%s' in option %s" %
339 4f31882e Guido Trotter
                                    (key, opt))
340 4f31882e Guido Trotter
      kv_dict[key] = val
341 a8469393 Iustin Pop
  return kv_dict
342 a8469393 Iustin Pop
343 a8469393 Iustin Pop
344 a8469393 Iustin Pop
def check_ident_key_val(option, opt, value):
345 552c8dff Michael Hanselmann
  """Custom parser for ident:key=val,key=val options.
346 552c8dff Michael Hanselmann

347 552c8dff Michael Hanselmann
  This will store the parsed values as a tuple (ident, {key: val}). As such,
348 552c8dff Michael Hanselmann
  multiple uses of this option via action=append is possible.
349 a8469393 Iustin Pop

350 a8469393 Iustin Pop
  """
351 a8469393 Iustin Pop
  if ":" not in value:
352 8b46606c Guido Trotter
    ident, rest = value, ''
353 a8469393 Iustin Pop
  else:
354 a8469393 Iustin Pop
    ident, rest = value.split(":", 1)
355 8b46606c Guido Trotter
356 8b46606c Guido Trotter
  if ident.startswith(NO_PREFIX):
357 8b46606c Guido Trotter
    if rest:
358 8b46606c Guido Trotter
      msg = "Cannot pass options when removing parameter groups: %s" % value
359 8b46606c Guido Trotter
      raise errors.ParameterError(msg)
360 8b46606c Guido Trotter
    retval = (ident[len(NO_PREFIX):], False)
361 8b46606c Guido Trotter
  elif ident.startswith(UN_PREFIX):
362 8b46606c Guido Trotter
    if rest:
363 8b46606c Guido Trotter
      msg = "Cannot pass options when removing parameter groups: %s" % value
364 8b46606c Guido Trotter
      raise errors.ParameterError(msg)
365 8b46606c Guido Trotter
    retval = (ident[len(UN_PREFIX):], None)
366 8b46606c Guido Trotter
  else:
367 a8469393 Iustin Pop
    kv_dict = _SplitKeyVal(opt, rest)
368 a8469393 Iustin Pop
    retval = (ident, kv_dict)
369 a8469393 Iustin Pop
  return retval
370 a8469393 Iustin Pop
371 a8469393 Iustin Pop
372 a8469393 Iustin Pop
def check_key_val(option, opt, value):
373 552c8dff Michael Hanselmann
  """Custom parser class for key=val,key=val options.
374 552c8dff Michael Hanselmann

375 552c8dff Michael Hanselmann
  This will store the parsed values as a dict {key: val}.
376 a8469393 Iustin Pop

377 a8469393 Iustin Pop
  """
378 a8469393 Iustin Pop
  return _SplitKeyVal(opt, value)
379 a8469393 Iustin Pop
380 a8469393 Iustin Pop
381 63d44c55 Michael Hanselmann
# completion_suggestion is normally a list. Using numeric values not evaluating
382 63d44c55 Michael Hanselmann
# to False for dynamic completion.
383 63d44c55 Michael Hanselmann
(OPT_COMPL_MANY_NODES,
384 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_NODE,
385 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_INSTANCE,
386 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_OS,
387 2d3ed64b Michael Hanselmann
 OPT_COMPL_ONE_IALLOCATOR,
388 2d3ed64b Michael Hanselmann
 OPT_COMPL_INST_ADD_NODES) = range(100, 106)
389 63d44c55 Michael Hanselmann
390 63d44c55 Michael Hanselmann
OPT_COMPL_ALL = frozenset([
391 63d44c55 Michael Hanselmann
  OPT_COMPL_MANY_NODES,
392 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_NODE,
393 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_INSTANCE,
394 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_OS,
395 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_IALLOCATOR,
396 2d3ed64b Michael Hanselmann
  OPT_COMPL_INST_ADD_NODES,
397 63d44c55 Michael Hanselmann
  ])
398 63d44c55 Michael Hanselmann
399 63d44c55 Michael Hanselmann
400 552c8dff Michael Hanselmann
class CliOption(Option):
401 552c8dff Michael Hanselmann
  """Custom option class for optparse.
402 a8469393 Iustin Pop

403 a8469393 Iustin Pop
  """
404 863d7f46 Michael Hanselmann
  ATTRS = Option.ATTRS + [
405 863d7f46 Michael Hanselmann
    "completion_suggest",
406 863d7f46 Michael Hanselmann
    ]
407 552c8dff Michael Hanselmann
  TYPES = Option.TYPES + (
408 552c8dff Michael Hanselmann
    "identkeyval",
409 552c8dff Michael Hanselmann
    "keyval",
410 552c8dff Michael Hanselmann
    "unit",
411 552c8dff Michael Hanselmann
    )
412 552c8dff Michael Hanselmann
  TYPE_CHECKER = Option.TYPE_CHECKER.copy()
413 552c8dff Michael Hanselmann
  TYPE_CHECKER["identkeyval"] = check_ident_key_val
414 a8469393 Iustin Pop
  TYPE_CHECKER["keyval"] = check_key_val
415 552c8dff Michael Hanselmann
  TYPE_CHECKER["unit"] = check_unit
416 a8469393 Iustin Pop
417 a8469393 Iustin Pop
418 a8083063 Iustin Pop
# optparse.py sets make_option, so we do it for our own option class, too
419 a8083063 Iustin Pop
cli_option = CliOption
420 a8083063 Iustin Pop
421 a8083063 Iustin Pop
422 c38c44ad Michael Hanselmann
DEBUG_OPT = cli_option("-d", "--debug", default=False,
423 c38c44ad Michael Hanselmann
                       action="store_true",
424 c38c44ad Michael Hanselmann
                       help="Turn debugging on")
425 c38c44ad Michael Hanselmann
426 c38c44ad Michael Hanselmann
NOHDR_OPT = cli_option("--no-headers", default=False,
427 c38c44ad Michael Hanselmann
                       action="store_true", dest="no_headers",
428 c38c44ad Michael Hanselmann
                       help="Don't display column headers")
429 c38c44ad Michael Hanselmann
430 c38c44ad Michael Hanselmann
SEP_OPT = cli_option("--separator", default=None,
431 c38c44ad Michael Hanselmann
                     action="store", dest="separator",
432 c38c44ad Michael Hanselmann
                     help=("Separator between output fields"
433 c38c44ad Michael Hanselmann
                           " (defaults to one space)"))
434 c38c44ad Michael Hanselmann
435 c38c44ad Michael Hanselmann
USEUNITS_OPT = cli_option("--units", default=None,
436 c38c44ad Michael Hanselmann
                          dest="units", choices=('h', 'm', 'g', 't'),
437 c38c44ad Michael Hanselmann
                          help="Specify units for output (one of hmgt)")
438 c38c44ad Michael Hanselmann
439 c38c44ad Michael Hanselmann
FIELDS_OPT = cli_option("-o", "--output", dest="output", action="store",
440 c38c44ad Michael Hanselmann
                        type="string", metavar="FIELDS",
441 c38c44ad Michael Hanselmann
                        help="Comma separated list of output fields")
442 c38c44ad Michael Hanselmann
443 c38c44ad Michael Hanselmann
FORCE_OPT = cli_option("-f", "--force", dest="force", action="store_true",
444 c38c44ad Michael Hanselmann
                       default=False, help="Force the operation")
445 c38c44ad Michael Hanselmann
446 c38c44ad Michael Hanselmann
CONFIRM_OPT = cli_option("--yes", dest="confirm", action="store_true",
447 c38c44ad Michael Hanselmann
                         default=False, help="Do not require confirmation")
448 c38c44ad Michael Hanselmann
449 c38c44ad Michael Hanselmann
TAG_SRC_OPT = cli_option("--from", dest="tags_source",
450 c38c44ad Michael Hanselmann
                         default=None, help="File with tag names")
451 c38c44ad Michael Hanselmann
452 c38c44ad Michael Hanselmann
SUBMIT_OPT = cli_option("--submit", dest="submit_only",
453 c38c44ad Michael Hanselmann
                        default=False, action="store_true",
454 c38c44ad Michael Hanselmann
                        help=("Submit the job and return the job ID, but"
455 c38c44ad Michael Hanselmann
                              " don't wait for the job to finish"))
456 c38c44ad Michael Hanselmann
457 c38c44ad Michael Hanselmann
SYNC_OPT = cli_option("--sync", dest="do_locking",
458 c38c44ad Michael Hanselmann
                      default=False, action="store_true",
459 c38c44ad Michael Hanselmann
                      help=("Grab locks while doing the queries"
460 c38c44ad Michael Hanselmann
                            " in order to ensure more consistent results"))
461 c38c44ad Michael Hanselmann
462 c38c44ad Michael Hanselmann
_DRY_RUN_OPT = cli_option("--dry-run", default=False,
463 c38c44ad Michael Hanselmann
                          action="store_true",
464 c38c44ad Michael Hanselmann
                          help=("Do not execute the operation, just run the"
465 c38c44ad Michael Hanselmann
                                " check steps and verify it it could be"
466 c38c44ad Michael Hanselmann
                                " executed"))
467 c38c44ad Michael Hanselmann
468 9cdb9578 Iustin Pop
VERBOSE_OPT = cli_option("-v", "--verbose", default=False,
469 9cdb9578 Iustin Pop
                         action="store_true",
470 9cdb9578 Iustin Pop
                         help="Increase the verbosity of the operation")
471 9cdb9578 Iustin Pop
472 a0c9776a Iustin Pop
DEBUG_SIMERR_OPT = cli_option("--debug-simulate-errors", default=False,
473 a0c9776a Iustin Pop
                              action="store_true", dest="simulate_errors",
474 a0c9776a Iustin Pop
                              help="Debugging option that makes the operation"
475 a0c9776a Iustin Pop
                              " treat most runtime checks as failed")
476 a0c9776a Iustin Pop
477 3f75b4f3 Iustin Pop
NWSYNC_OPT = cli_option("--no-wait-for-sync", dest="wait_for_sync",
478 3f75b4f3 Iustin Pop
                        default=True, action="store_false",
479 3f75b4f3 Iustin Pop
                        help="Don't wait for sync (DANGEROUS!)")
480 3f75b4f3 Iustin Pop
481 4f365444 Iustin Pop
DISK_TEMPLATE_OPT = cli_option("-t", "--disk-template", dest="disk_template",
482 4f365444 Iustin Pop
                               help="Custom disk setup (diskless, file,"
483 4f365444 Iustin Pop
                               " plain or drbd)",
484 4f365444 Iustin Pop
                               default=None, metavar="TEMPL",
485 4f365444 Iustin Pop
                               choices=list(constants.DISK_TEMPLATES))
486 4f365444 Iustin Pop
487 26023ecd Iustin Pop
NONICS_OPT = cli_option("--no-nics", default=False, action="store_true",
488 26023ecd Iustin Pop
                        help="Do not create any network cards for"
489 26023ecd Iustin Pop
                        " the instance")
490 26023ecd Iustin Pop
491 4a25828c Iustin Pop
FILESTORE_DIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
492 4a25828c Iustin Pop
                               help="Relative path under default cluster-wide"
493 4a25828c Iustin Pop
                               " file storage dir to store file-based disks",
494 4a25828c Iustin Pop
                               default=None, metavar="<DIR>")
495 4a25828c Iustin Pop
496 c38c44ad Michael Hanselmann
497 de47cf8f Guido Trotter
def _ParseArgs(argv, commands, aliases):
498 c41eea6e Iustin Pop
  """Parser for the command line arguments.
499 a8083063 Iustin Pop

500 5bbd3f7f Michael Hanselmann
  This function parses the arguments and returns the function which
501 c41eea6e Iustin Pop
  must be executed together with its (modified) arguments.
502 a8083063 Iustin Pop

503 c41eea6e Iustin Pop
  @param argv: the command line
504 c41eea6e Iustin Pop
  @param commands: dictionary with special contents, see the design
505 c41eea6e Iustin Pop
      doc for cmdline handling
506 c41eea6e Iustin Pop
  @param aliases: dictionary with command aliases {'alias': 'target, ...}
507 098c0958 Michael Hanselmann

508 a8083063 Iustin Pop
  """
509 a8083063 Iustin Pop
  if len(argv) == 0:
510 a8083063 Iustin Pop
    binary = "<command>"
511 a8083063 Iustin Pop
  else:
512 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
513 a8083063 Iustin Pop
514 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
515 03298ebe Michael Hanselmann
    ToStdout("%s (ganeti) %s", binary, constants.RELEASE_VERSION)
516 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
517 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
518 a8083063 Iustin Pop
    sys.exit(0)
519 a8083063 Iustin Pop
520 de47cf8f Guido Trotter
  if len(argv) < 2 or not (argv[1] in commands or
521 70a35b6f Guido Trotter
                           argv[1] in aliases):
522 a8083063 Iustin Pop
    # let's do a nice thing
523 a8083063 Iustin Pop
    sortedcmds = commands.keys()
524 a8083063 Iustin Pop
    sortedcmds.sort()
525 03298ebe Michael Hanselmann
526 03298ebe Michael Hanselmann
    ToStdout("Usage: %s {command} [options...] [argument...]", binary)
527 03298ebe Michael Hanselmann
    ToStdout("%s <command> --help to see details, or man %s", binary, binary)
528 03298ebe Michael Hanselmann
    ToStdout("")
529 03298ebe Michael Hanselmann
530 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
531 4e713df6 Iustin Pop
    mlen = max([len(" %s" % cmd) for cmd in commands])
532 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
533 03298ebe Michael Hanselmann
534 a8083063 Iustin Pop
    # and format a nice command list
535 03298ebe Michael Hanselmann
    ToStdout("Commands:")
536 a8083063 Iustin Pop
    for cmd in sortedcmds:
537 4e713df6 Iustin Pop
      cmdstr = " %s" % (cmd,)
538 9a033156 Iustin Pop
      help_text = commands[cmd][4]
539 03298ebe Michael Hanselmann
      help_lines = textwrap.wrap(help_text, 79 - 3 - mlen)
540 03298ebe Michael Hanselmann
      ToStdout("%-*s - %s", mlen, cmdstr, help_lines.pop(0))
541 a8083063 Iustin Pop
      for line in help_lines:
542 03298ebe Michael Hanselmann
        ToStdout("%-*s   %s", mlen, "", line)
543 03298ebe Michael Hanselmann
544 03298ebe Michael Hanselmann
    ToStdout("")
545 03298ebe Michael Hanselmann
546 a8083063 Iustin Pop
    return None, None, None
547 de47cf8f Guido Trotter
548 de47cf8f Guido Trotter
  # get command, unalias it, and look it up in commands
549 a8083063 Iustin Pop
  cmd = argv.pop(1)
550 de47cf8f Guido Trotter
  if cmd in aliases:
551 de47cf8f Guido Trotter
    if cmd in commands:
552 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' overrides an existing"
553 de47cf8f Guido Trotter
                                   " command" % cmd)
554 de47cf8f Guido Trotter
555 de47cf8f Guido Trotter
    if aliases[cmd] not in commands:
556 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' maps to non-existing"
557 de47cf8f Guido Trotter
                                   " command '%s'" % (cmd, aliases[cmd]))
558 de47cf8f Guido Trotter
559 de47cf8f Guido Trotter
    cmd = aliases[cmd]
560 de47cf8f Guido Trotter
561 a8005e17 Michael Hanselmann
  func, args_def, parser_opts, usage, description = commands[cmd]
562 64c65a2a Iustin Pop
  parser = OptionParser(option_list=parser_opts + [_DRY_RUN_OPT],
563 a8083063 Iustin Pop
                        description=description,
564 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
565 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
566 a8083063 Iustin Pop
  parser.disable_interspersed_args()
567 a8083063 Iustin Pop
  options, args = parser.parse_args()
568 a8005e17 Michael Hanselmann
569 a8005e17 Michael Hanselmann
  if not _CheckArguments(cmd, args_def, args):
570 a8083063 Iustin Pop
    return None, None, None
571 a8083063 Iustin Pop
572 a8083063 Iustin Pop
  return func, options, args
573 a8083063 Iustin Pop
574 a8083063 Iustin Pop
575 a8005e17 Michael Hanselmann
def _CheckArguments(cmd, args_def, args):
576 a8005e17 Michael Hanselmann
  """Verifies the arguments using the argument definition.
577 a8005e17 Michael Hanselmann

578 a8005e17 Michael Hanselmann
  Algorithm:
579 a8005e17 Michael Hanselmann

580 a8005e17 Michael Hanselmann
    1. Abort with error if values specified by user but none expected.
581 a8005e17 Michael Hanselmann

582 a8005e17 Michael Hanselmann
    1. For each argument in definition
583 a8005e17 Michael Hanselmann

584 a8005e17 Michael Hanselmann
      1. Keep running count of minimum number of values (min_count)
585 a8005e17 Michael Hanselmann
      1. Keep running count of maximum number of values (max_count)
586 a8005e17 Michael Hanselmann
      1. If it has an unlimited number of values
587 a8005e17 Michael Hanselmann

588 a8005e17 Michael Hanselmann
        1. Abort with error if it's not the last argument in the definition
589 a8005e17 Michael Hanselmann

590 a8005e17 Michael Hanselmann
    1. If last argument has limited number of values
591 a8005e17 Michael Hanselmann

592 a8005e17 Michael Hanselmann
      1. Abort with error if number of values doesn't match or is too large
593 a8005e17 Michael Hanselmann

594 a8005e17 Michael Hanselmann
    1. Abort with error if user didn't pass enough values (min_count)
595 a8005e17 Michael Hanselmann

596 a8005e17 Michael Hanselmann
  """
597 a8005e17 Michael Hanselmann
  if args and not args_def:
598 a8005e17 Michael Hanselmann
    ToStderr("Error: Command %s expects no arguments", cmd)
599 a8005e17 Michael Hanselmann
    return False
600 a8005e17 Michael Hanselmann
601 a8005e17 Michael Hanselmann
  min_count = None
602 a8005e17 Michael Hanselmann
  max_count = None
603 a8005e17 Michael Hanselmann
  check_max = None
604 a8005e17 Michael Hanselmann
605 a8005e17 Michael Hanselmann
  last_idx = len(args_def) - 1
606 a8005e17 Michael Hanselmann
607 a8005e17 Michael Hanselmann
  for idx, arg in enumerate(args_def):
608 a8005e17 Michael Hanselmann
    if min_count is None:
609 a8005e17 Michael Hanselmann
      min_count = arg.min
610 a8005e17 Michael Hanselmann
    elif arg.min is not None:
611 a8005e17 Michael Hanselmann
      min_count += arg.min
612 a8005e17 Michael Hanselmann
613 a8005e17 Michael Hanselmann
    if max_count is None:
614 a8005e17 Michael Hanselmann
      max_count = arg.max
615 a8005e17 Michael Hanselmann
    elif arg.max is not None:
616 a8005e17 Michael Hanselmann
      max_count += arg.max
617 a8005e17 Michael Hanselmann
618 a8005e17 Michael Hanselmann
    if idx == last_idx:
619 a8005e17 Michael Hanselmann
      check_max = (arg.max is not None)
620 a8005e17 Michael Hanselmann
621 a8005e17 Michael Hanselmann
    elif arg.max is None:
622 a8005e17 Michael Hanselmann
      raise errors.ProgrammerError("Only the last argument can have max=None")
623 a8005e17 Michael Hanselmann
624 a8005e17 Michael Hanselmann
  if check_max:
625 a8005e17 Michael Hanselmann
    # Command with exact number of arguments
626 a8005e17 Michael Hanselmann
    if (min_count is not None and max_count is not None and
627 a8005e17 Michael Hanselmann
        min_count == max_count and len(args) != min_count):
628 a8005e17 Michael Hanselmann
      ToStderr("Error: Command %s expects %d argument(s)", cmd, min_count)
629 a8005e17 Michael Hanselmann
      return False
630 a8005e17 Michael Hanselmann
631 a8005e17 Michael Hanselmann
    # Command with limited number of arguments
632 a8005e17 Michael Hanselmann
    if max_count is not None and len(args) > max_count:
633 a8005e17 Michael Hanselmann
      ToStderr("Error: Command %s expects only %d argument(s)",
634 a8005e17 Michael Hanselmann
               cmd, max_count)
635 a8005e17 Michael Hanselmann
      return False
636 a8005e17 Michael Hanselmann
637 a8005e17 Michael Hanselmann
  # Command with some required arguments
638 a8005e17 Michael Hanselmann
  if min_count is not None and len(args) < min_count:
639 a8005e17 Michael Hanselmann
    ToStderr("Error: Command %s expects at least %d argument(s)",
640 a8005e17 Michael Hanselmann
             cmd, min_count)
641 a8005e17 Michael Hanselmann
    return False
642 a8005e17 Michael Hanselmann
643 a8005e17 Michael Hanselmann
  return True
644 a8005e17 Michael Hanselmann
645 a8005e17 Michael Hanselmann
646 60d49723 Michael Hanselmann
def SplitNodeOption(value):
647 60d49723 Michael Hanselmann
  """Splits the value of a --node option.
648 60d49723 Michael Hanselmann

649 60d49723 Michael Hanselmann
  """
650 60d49723 Michael Hanselmann
  if value and ':' in value:
651 60d49723 Michael Hanselmann
    return value.split(':', 1)
652 60d49723 Michael Hanselmann
  else:
653 60d49723 Michael Hanselmann
    return (value, None)
654 60d49723 Michael Hanselmann
655 60d49723 Michael Hanselmann
656 4331f6cd Michael Hanselmann
def UsesRPC(fn):
657 4331f6cd Michael Hanselmann
  def wrapper(*args, **kwargs):
658 4331f6cd Michael Hanselmann
    rpc.Init()
659 4331f6cd Michael Hanselmann
    try:
660 4331f6cd Michael Hanselmann
      return fn(*args, **kwargs)
661 4331f6cd Michael Hanselmann
    finally:
662 4331f6cd Michael Hanselmann
      rpc.Shutdown()
663 4331f6cd Michael Hanselmann
  return wrapper
664 4331f6cd Michael Hanselmann
665 4331f6cd Michael Hanselmann
666 47988778 Iustin Pop
def AskUser(text, choices=None):
667 47988778 Iustin Pop
  """Ask the user a question.
668 a8083063 Iustin Pop

669 c41eea6e Iustin Pop
  @param text: the question to ask
670 a8083063 Iustin Pop

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

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

680 a8083063 Iustin Pop
  """
681 47988778 Iustin Pop
  if choices is None:
682 47988778 Iustin Pop
    choices = [('y', True, 'Perform the operation'),
683 47988778 Iustin Pop
               ('n', False, 'Do not perform the operation')]
684 47988778 Iustin Pop
  if not choices or not isinstance(choices, list):
685 5bbd3f7f Michael Hanselmann
    raise errors.ProgrammerError("Invalid choices argument to AskUser")
686 47988778 Iustin Pop
  for entry in choices:
687 47988778 Iustin Pop
    if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
688 5bbd3f7f Michael Hanselmann
      raise errors.ProgrammerError("Invalid choices element to AskUser")
689 47988778 Iustin Pop
690 47988778 Iustin Pop
  answer = choices[-1][1]
691 47988778 Iustin Pop
  new_text = []
692 47988778 Iustin Pop
  for line in text.splitlines():
693 47988778 Iustin Pop
    new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
694 47988778 Iustin Pop
  text = "\n".join(new_text)
695 a8083063 Iustin Pop
  try:
696 3023170f Iustin Pop
    f = file("/dev/tty", "a+")
697 a8083063 Iustin Pop
  except IOError:
698 47988778 Iustin Pop
    return answer
699 a8083063 Iustin Pop
  try:
700 47988778 Iustin Pop
    chars = [entry[0] for entry in choices]
701 47988778 Iustin Pop
    chars[-1] = "[%s]" % chars[-1]
702 47988778 Iustin Pop
    chars.append('?')
703 47988778 Iustin Pop
    maps = dict([(entry[0], entry[1]) for entry in choices])
704 47988778 Iustin Pop
    while True:
705 47988778 Iustin Pop
      f.write(text)
706 47988778 Iustin Pop
      f.write('\n')
707 47988778 Iustin Pop
      f.write("/".join(chars))
708 47988778 Iustin Pop
      f.write(": ")
709 47988778 Iustin Pop
      line = f.readline(2).strip().lower()
710 47988778 Iustin Pop
      if line in maps:
711 47988778 Iustin Pop
        answer = maps[line]
712 47988778 Iustin Pop
        break
713 47988778 Iustin Pop
      elif line == '?':
714 47988778 Iustin Pop
        for entry in choices:
715 47988778 Iustin Pop
          f.write(" %s - %s\n" % (entry[0], entry[2]))
716 47988778 Iustin Pop
        f.write("\n")
717 47988778 Iustin Pop
        continue
718 a8083063 Iustin Pop
  finally:
719 a8083063 Iustin Pop
    f.close()
720 a8083063 Iustin Pop
  return answer
721 a8083063 Iustin Pop
722 a8083063 Iustin Pop
723 e9d741b6 Iustin Pop
class JobSubmittedException(Exception):
724 e9d741b6 Iustin Pop
  """Job was submitted, client should exit.
725 e9d741b6 Iustin Pop

726 e9d741b6 Iustin Pop
  This exception has one argument, the ID of the job that was
727 e9d741b6 Iustin Pop
  submitted. The handler should print this ID.
728 e9d741b6 Iustin Pop

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

731 e9d741b6 Iustin Pop
  """
732 e9d741b6 Iustin Pop
733 e9d741b6 Iustin Pop
734 0a1e74d9 Iustin Pop
def SendJob(ops, cl=None):
735 0a1e74d9 Iustin Pop
  """Function to submit an opcode without waiting for the results.
736 a8083063 Iustin Pop

737 0a1e74d9 Iustin Pop
  @type ops: list
738 0a1e74d9 Iustin Pop
  @param ops: list of opcodes
739 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
740 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
741 0a1e74d9 Iustin Pop
             if None, a new client will be created
742 a8083063 Iustin Pop

743 a8083063 Iustin Pop
  """
744 e2212007 Iustin Pop
  if cl is None:
745 b33e986b Iustin Pop
    cl = GetClient()
746 685ee993 Iustin Pop
747 0a1e74d9 Iustin Pop
  job_id = cl.SubmitJob(ops)
748 0a1e74d9 Iustin Pop
749 0a1e74d9 Iustin Pop
  return job_id
750 0a1e74d9 Iustin Pop
751 0a1e74d9 Iustin Pop
752 281606c1 Michael Hanselmann
def PollJob(job_id, cl=None, feedback_fn=None):
753 0a1e74d9 Iustin Pop
  """Function to poll for the result of a job.
754 0a1e74d9 Iustin Pop

755 0a1e74d9 Iustin Pop
  @type job_id: job identified
756 0a1e74d9 Iustin Pop
  @param job_id: the job to poll for results
757 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
758 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
759 0a1e74d9 Iustin Pop
             if None, a new client will be created
760 0a1e74d9 Iustin Pop

761 0a1e74d9 Iustin Pop
  """
762 0a1e74d9 Iustin Pop
  if cl is None:
763 0a1e74d9 Iustin Pop
    cl = GetClient()
764 685ee993 Iustin Pop
765 6c5a7090 Michael Hanselmann
  prev_job_info = None
766 6c5a7090 Michael Hanselmann
  prev_logmsg_serial = None
767 6c5a7090 Michael Hanselmann
768 685ee993 Iustin Pop
  while True:
769 6c5a7090 Michael Hanselmann
    result = cl.WaitForJobChange(job_id, ["status"], prev_job_info,
770 6c5a7090 Michael Hanselmann
                                 prev_logmsg_serial)
771 6c5a7090 Michael Hanselmann
    if not result:
772 685ee993 Iustin Pop
      # job not found, go away!
773 0bbe448c Michael Hanselmann
      raise errors.JobLost("Job with id %s lost" % job_id)
774 685ee993 Iustin Pop
775 6c5a7090 Michael Hanselmann
    # Split result, a tuple of (field values, log entries)
776 6c5a7090 Michael Hanselmann
    (job_info, log_entries) = result
777 6c5a7090 Michael Hanselmann
    (status, ) = job_info
778 6c5a7090 Michael Hanselmann
779 6c5a7090 Michael Hanselmann
    if log_entries:
780 6c5a7090 Michael Hanselmann
      for log_entry in log_entries:
781 6c5a7090 Michael Hanselmann
        (serial, timestamp, _, message) = log_entry
782 6c5a7090 Michael Hanselmann
        if callable(feedback_fn):
783 6c5a7090 Michael Hanselmann
          feedback_fn(log_entry[1:])
784 6c5a7090 Michael Hanselmann
        else:
785 26f15862 Iustin Pop
          encoded = utils.SafeEncode(message)
786 03298ebe Michael Hanselmann
          ToStdout("%s %s", time.ctime(utils.MergeTime(timestamp)), encoded)
787 6c5a7090 Michael Hanselmann
        prev_logmsg_serial = max(prev_logmsg_serial, serial)
788 6c5a7090 Michael Hanselmann
789 0bbe448c Michael Hanselmann
    # TODO: Handle canceled and archived jobs
790 fbf0262f Michael Hanselmann
    elif status in (constants.JOB_STATUS_SUCCESS,
791 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_ERROR,
792 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELING,
793 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELED):
794 685ee993 Iustin Pop
      break
795 6c5a7090 Michael Hanselmann
796 6c5a7090 Michael Hanselmann
    prev_job_info = job_info
797 685ee993 Iustin Pop
798 0e050889 Iustin Pop
  jobs = cl.QueryJobs([job_id], ["status", "opstatus", "opresult"])
799 0bbe448c Michael Hanselmann
  if not jobs:
800 0bbe448c Michael Hanselmann
    raise errors.JobLost("Job with id %s lost" % job_id)
801 685ee993 Iustin Pop
802 0e050889 Iustin Pop
  status, opstatus, result = jobs[0]
803 0bbe448c Michael Hanselmann
  if status == constants.JOB_STATUS_SUCCESS:
804 53c04d04 Iustin Pop
    return result
805 fbf0262f Michael Hanselmann
  elif status in (constants.JOB_STATUS_CANCELING,
806 fbf0262f Michael Hanselmann
                  constants.JOB_STATUS_CANCELED):
807 fbf0262f Michael Hanselmann
    raise errors.OpExecError("Job was canceled")
808 0bbe448c Michael Hanselmann
  else:
809 0e050889 Iustin Pop
    has_ok = False
810 0e050889 Iustin Pop
    for idx, (status, msg) in enumerate(zip(opstatus, result)):
811 0e050889 Iustin Pop
      if status == constants.OP_STATUS_SUCCESS:
812 0e050889 Iustin Pop
        has_ok = True
813 0e050889 Iustin Pop
      elif status == constants.OP_STATUS_ERROR:
814 bcb66fca Iustin Pop
        errors.MaybeRaise(msg)
815 0e050889 Iustin Pop
        if has_ok:
816 0e050889 Iustin Pop
          raise errors.OpExecError("partial failure (opcode %d): %s" %
817 0e050889 Iustin Pop
                                   (idx, msg))
818 0e050889 Iustin Pop
        else:
819 0e050889 Iustin Pop
          raise errors.OpExecError(str(msg))
820 0e050889 Iustin Pop
    # default failure mode
821 0bbe448c Michael Hanselmann
    raise errors.OpExecError(result)
822 ceab32dd Iustin Pop
823 ceab32dd Iustin Pop
824 0a1e74d9 Iustin Pop
def SubmitOpCode(op, cl=None, feedback_fn=None):
825 0a1e74d9 Iustin Pop
  """Legacy function to submit an opcode.
826 0a1e74d9 Iustin Pop

827 0a1e74d9 Iustin Pop
  This is just a simple wrapper over the construction of the processor
828 0a1e74d9 Iustin Pop
  instance. It should be extended to better handle feedback and
829 0a1e74d9 Iustin Pop
  interaction functions.
830 0a1e74d9 Iustin Pop

831 0a1e74d9 Iustin Pop
  """
832 0a1e74d9 Iustin Pop
  if cl is None:
833 0a1e74d9 Iustin Pop
    cl = GetClient()
834 0a1e74d9 Iustin Pop
835 0a1e74d9 Iustin Pop
  job_id = SendJob([op], cl)
836 0a1e74d9 Iustin Pop
837 53c04d04 Iustin Pop
  op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn)
838 53c04d04 Iustin Pop
839 53c04d04 Iustin Pop
  return op_results[0]
840 0a1e74d9 Iustin Pop
841 0a1e74d9 Iustin Pop
842 94428652 Iustin Pop
def SubmitOrSend(op, opts, cl=None, feedback_fn=None):
843 94428652 Iustin Pop
  """Wrapper around SubmitOpCode or SendJob.
844 94428652 Iustin Pop

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

850 64c65a2a Iustin Pop
  It will also add the dry-run parameter from the options passed, if true.
851 64c65a2a Iustin Pop

852 94428652 Iustin Pop
  """
853 64c65a2a Iustin Pop
  if opts and opts.dry_run:
854 64c65a2a Iustin Pop
    op.dry_run = opts.dry_run
855 94428652 Iustin Pop
  if opts and opts.submit_only:
856 e9d741b6 Iustin Pop
    job_id = SendJob([op], cl=cl)
857 e9d741b6 Iustin Pop
    raise JobSubmittedException(job_id)
858 94428652 Iustin Pop
  else:
859 94428652 Iustin Pop
    return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn)
860 94428652 Iustin Pop
861 94428652 Iustin Pop
862 af30b2fd Michael Hanselmann
def GetClient():
863 af30b2fd Michael Hanselmann
  # TODO: Cache object?
864 b33e986b Iustin Pop
  try:
865 b33e986b Iustin Pop
    client = luxi.Client()
866 b33e986b Iustin Pop
  except luxi.NoMasterError:
867 b33e986b Iustin Pop
    master, myself = ssconf.GetMasterAndMyself()
868 b33e986b Iustin Pop
    if master != myself:
869 b33e986b Iustin Pop
      raise errors.OpPrereqError("This is not the master node, please connect"
870 b33e986b Iustin Pop
                                 " to node '%s' and rerun the command" %
871 b33e986b Iustin Pop
                                 master)
872 b33e986b Iustin Pop
    else:
873 b33e986b Iustin Pop
      raise
874 b33e986b Iustin Pop
  return client
875 af30b2fd Michael Hanselmann
876 af30b2fd Michael Hanselmann
877 73702ee7 Iustin Pop
def FormatError(err):
878 73702ee7 Iustin Pop
  """Return a formatted error message for a given error.
879 73702ee7 Iustin Pop

880 73702ee7 Iustin Pop
  This function takes an exception instance and returns a tuple
881 73702ee7 Iustin Pop
  consisting of two values: first, the recommended exit code, and
882 73702ee7 Iustin Pop
  second, a string describing the error message (not
883 73702ee7 Iustin Pop
  newline-terminated).
884 73702ee7 Iustin Pop

885 73702ee7 Iustin Pop
  """
886 73702ee7 Iustin Pop
  retcode = 1
887 73702ee7 Iustin Pop
  obuf = StringIO()
888 e2e521d0 Iustin Pop
  msg = str(err)
889 73702ee7 Iustin Pop
  if isinstance(err, errors.ConfigurationError):
890 e2e521d0 Iustin Pop
    txt = "Corrupt configuration file: %s" % msg
891 46fbdd04 Iustin Pop
    logging.error(txt)
892 e2e521d0 Iustin Pop
    obuf.write(txt + "\n")
893 73702ee7 Iustin Pop
    obuf.write("Aborting.")
894 73702ee7 Iustin Pop
    retcode = 2
895 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksAbort):
896 73702ee7 Iustin Pop
    obuf.write("Failure: hooks execution failed:\n")
897 73702ee7 Iustin Pop
    for node, script, out in err.args[0]:
898 73702ee7 Iustin Pop
      if out:
899 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s, output: %s\n" %
900 73702ee7 Iustin Pop
                   (node, script, out))
901 73702ee7 Iustin Pop
      else:
902 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s (no output)\n" %
903 73702ee7 Iustin Pop
                   (node, script))
904 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksFailure):
905 e2e521d0 Iustin Pop
    obuf.write("Failure: hooks general failure: %s" % msg)
906 73702ee7 Iustin Pop
  elif isinstance(err, errors.ResolverError):
907 73702ee7 Iustin Pop
    this_host = utils.HostInfo.SysName()
908 73702ee7 Iustin Pop
    if err.args[0] == this_host:
909 73702ee7 Iustin Pop
      msg = "Failure: can't resolve my own hostname ('%s')"
910 73702ee7 Iustin Pop
    else:
911 73702ee7 Iustin Pop
      msg = "Failure: can't resolve hostname '%s'"
912 73702ee7 Iustin Pop
    obuf.write(msg % err.args[0])
913 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpPrereqError):
914 73702ee7 Iustin Pop
    obuf.write("Failure: prerequisites not met for this"
915 e2e521d0 Iustin Pop
               " operation:\n%s" % msg)
916 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpExecError):
917 e2e521d0 Iustin Pop
    obuf.write("Failure: command execution error:\n%s" % msg)
918 73702ee7 Iustin Pop
  elif isinstance(err, errors.TagError):
919 e2e521d0 Iustin Pop
    obuf.write("Failure: invalid tag(s) given:\n%s" % msg)
920 686d7433 Iustin Pop
  elif isinstance(err, errors.JobQueueDrainError):
921 686d7433 Iustin Pop
    obuf.write("Failure: the job queue is marked for drain and doesn't"
922 686d7433 Iustin Pop
               " accept new requests\n")
923 f87b405e Michael Hanselmann
  elif isinstance(err, errors.JobQueueFull):
924 f87b405e Michael Hanselmann
    obuf.write("Failure: the job queue is full and doesn't accept new"
925 f87b405e Michael Hanselmann
               " job submissions until old jobs are archived\n")
926 a5728081 Guido Trotter
  elif isinstance(err, errors.TypeEnforcementError):
927 a5728081 Guido Trotter
    obuf.write("Parameter Error: %s" % msg)
928 c1ce76bb Iustin Pop
  elif isinstance(err, errors.ParameterError):
929 c1ce76bb Iustin Pop
    obuf.write("Failure: unknown/wrong parameter name '%s'" % msg)
930 73702ee7 Iustin Pop
  elif isinstance(err, errors.GenericError):
931 e2e521d0 Iustin Pop
    obuf.write("Unhandled Ganeti error: %s" % msg)
932 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.NoMasterError):
933 03a8dbdc Iustin Pop
    obuf.write("Cannot communicate with the master daemon.\nIs it running"
934 082c5adb Michael Hanselmann
               " and listening for connections?")
935 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.TimeoutError):
936 03a8dbdc Iustin Pop
    obuf.write("Timeout while talking to the master daemon. Error:\n"
937 03a8dbdc Iustin Pop
               "%s" % msg)
938 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.ProtocolError):
939 03a8dbdc Iustin Pop
    obuf.write("Unhandled protocol error while talking to the master daemon:\n"
940 03a8dbdc Iustin Pop
               "%s" % msg)
941 e9d741b6 Iustin Pop
  elif isinstance(err, JobSubmittedException):
942 e9d741b6 Iustin Pop
    obuf.write("JobID: %s\n" % err.args[0])
943 e9d741b6 Iustin Pop
    retcode = 0
944 73702ee7 Iustin Pop
  else:
945 e2e521d0 Iustin Pop
    obuf.write("Unhandled exception: %s" % msg)
946 73702ee7 Iustin Pop
  return retcode, obuf.getvalue().rstrip('\n')
947 73702ee7 Iustin Pop
948 73702ee7 Iustin Pop
949 de47cf8f Guido Trotter
def GenericMain(commands, override=None, aliases=None):
950 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
951 a8083063 Iustin Pop

952 334d1483 Iustin Pop
  Arguments:
953 334d1483 Iustin Pop
    - commands: a dictionary with a special structure, see the design doc
954 334d1483 Iustin Pop
                for command line handling.
955 334d1483 Iustin Pop
    - override: if not None, we expect a dictionary with keys that will
956 334d1483 Iustin Pop
                override command line options; this can be used to pass
957 334d1483 Iustin Pop
                options from the scripts to generic functions
958 de47cf8f Guido Trotter
    - aliases: dictionary with command aliases {'alias': 'target, ...}
959 a8083063 Iustin Pop

960 a8083063 Iustin Pop
  """
961 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
962 a8083063 Iustin Pop
  if sys.argv:
963 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
964 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
965 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
966 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
967 a8083063 Iustin Pop
    else:
968 a8083063 Iustin Pop
      old_cmdline = ""
969 a8083063 Iustin Pop
  else:
970 a8083063 Iustin Pop
    binary = "<unknown program>"
971 a8083063 Iustin Pop
    old_cmdline = ""
972 a8083063 Iustin Pop
973 de47cf8f Guido Trotter
  if aliases is None:
974 de47cf8f Guido Trotter
    aliases = {}
975 de47cf8f Guido Trotter
976 3126878d Guido Trotter
  try:
977 3126878d Guido Trotter
    func, options, args = _ParseArgs(sys.argv, commands, aliases)
978 3126878d Guido Trotter
  except errors.ParameterError, err:
979 3126878d Guido Trotter
    result, err_msg = FormatError(err)
980 3126878d Guido Trotter
    ToStderr(err_msg)
981 3126878d Guido Trotter
    return 1
982 3126878d Guido Trotter
983 a8083063 Iustin Pop
  if func is None: # parse error
984 a8083063 Iustin Pop
    return 1
985 a8083063 Iustin Pop
986 334d1483 Iustin Pop
  if override is not None:
987 334d1483 Iustin Pop
    for key, val in override.iteritems():
988 334d1483 Iustin Pop
      setattr(options, key, val)
989 334d1483 Iustin Pop
990 82d9caef Iustin Pop
  utils.SetupLogging(constants.LOG_COMMANDS, debug=options.debug,
991 82d9caef Iustin Pop
                     stderr_logging=True, program=binary)
992 a8083063 Iustin Pop
993 a8083063 Iustin Pop
  if old_cmdline:
994 46fbdd04 Iustin Pop
    logging.info("run with arguments '%s'", old_cmdline)
995 a8083063 Iustin Pop
  else:
996 46fbdd04 Iustin Pop
    logging.info("run with no arguments")
997 a8083063 Iustin Pop
998 a8083063 Iustin Pop
  try:
999 a4af651e Iustin Pop
    result = func(options, args)
1000 d8353c3a Iustin Pop
  except (errors.GenericError, luxi.ProtocolError,
1001 d8353c3a Iustin Pop
          JobSubmittedException), err:
1002 a4af651e Iustin Pop
    result, err_msg = FormatError(err)
1003 5bbd3f7f Michael Hanselmann
    logging.exception("Error during command processing")
1004 46fbdd04 Iustin Pop
    ToStderr(err_msg)
1005 a8083063 Iustin Pop
1006 a8083063 Iustin Pop
  return result
1007 137161c9 Michael Hanselmann
1008 137161c9 Michael Hanselmann
1009 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
1010 9fbfbb7b Iustin Pop
                  numfields=None, unitfields=None,
1011 9fbfbb7b Iustin Pop
                  units=None):
1012 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
1013 137161c9 Michael Hanselmann

1014 9fbfbb7b Iustin Pop
  @type headers: dict
1015 9fbfbb7b Iustin Pop
  @param headers: dictionary mapping field names to headers for
1016 9fbfbb7b Iustin Pop
      the table
1017 9fbfbb7b Iustin Pop
  @type fields: list
1018 9fbfbb7b Iustin Pop
  @param fields: the field names corresponding to each row in
1019 9fbfbb7b Iustin Pop
      the data field
1020 9fbfbb7b Iustin Pop
  @param separator: the separator to be used; if this is None,
1021 9fbfbb7b Iustin Pop
      the default 'smart' algorithm is used which computes optimal
1022 9fbfbb7b Iustin Pop
      field width, otherwise just the separator is used between
1023 9fbfbb7b Iustin Pop
      each field
1024 9fbfbb7b Iustin Pop
  @type data: list
1025 9fbfbb7b Iustin Pop
  @param data: a list of lists, each sublist being one row to be output
1026 9fbfbb7b Iustin Pop
  @type numfields: list
1027 9fbfbb7b Iustin Pop
  @param numfields: a list with the fields that hold numeric
1028 9fbfbb7b Iustin Pop
      values and thus should be right-aligned
1029 9fbfbb7b Iustin Pop
  @type unitfields: list
1030 9fbfbb7b Iustin Pop
  @param unitfields: a list with the fields that hold numeric
1031 9fbfbb7b Iustin Pop
      values that should be formatted with the units field
1032 9fbfbb7b Iustin Pop
  @type units: string or None
1033 9fbfbb7b Iustin Pop
  @param units: the units we should use for formatting, or None for
1034 9fbfbb7b Iustin Pop
      automatic choice (human-readable for non-separator usage, otherwise
1035 9fbfbb7b Iustin Pop
      megabytes); this is a one-letter string
1036 137161c9 Michael Hanselmann

1037 137161c9 Michael Hanselmann
  """
1038 9fbfbb7b Iustin Pop
  if units is None:
1039 9fbfbb7b Iustin Pop
    if separator:
1040 9fbfbb7b Iustin Pop
      units = "m"
1041 9fbfbb7b Iustin Pop
    else:
1042 9fbfbb7b Iustin Pop
      units = "h"
1043 9fbfbb7b Iustin Pop
1044 137161c9 Michael Hanselmann
  if numfields is None:
1045 137161c9 Michael Hanselmann
    numfields = []
1046 137161c9 Michael Hanselmann
  if unitfields is None:
1047 137161c9 Michael Hanselmann
    unitfields = []
1048 137161c9 Michael Hanselmann
1049 00430f8e Iustin Pop
  numfields = utils.FieldSet(*numfields)
1050 00430f8e Iustin Pop
  unitfields = utils.FieldSet(*unitfields)
1051 00430f8e Iustin Pop
1052 137161c9 Michael Hanselmann
  format_fields = []
1053 137161c9 Michael Hanselmann
  for field in fields:
1054 01ca31ae Iustin Pop
    if headers and field not in headers:
1055 ea5a5b74 Guido Trotter
      # TODO: handle better unknown fields (either revert to old
1056 71c1af58 Iustin Pop
      # style of raising exception, or deal more intelligently with
1057 71c1af58 Iustin Pop
      # variable fields)
1058 71c1af58 Iustin Pop
      headers[field] = field
1059 137161c9 Michael Hanselmann
    if separator is not None:
1060 137161c9 Michael Hanselmann
      format_fields.append("%s")
1061 00430f8e Iustin Pop
    elif numfields.Matches(field):
1062 137161c9 Michael Hanselmann
      format_fields.append("%*s")
1063 137161c9 Michael Hanselmann
    else:
1064 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
1065 137161c9 Michael Hanselmann
1066 137161c9 Michael Hanselmann
  if separator is None:
1067 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
1068 137161c9 Michael Hanselmann
    format = ' '.join(format_fields)
1069 137161c9 Michael Hanselmann
  else:
1070 137161c9 Michael Hanselmann
    format = separator.replace("%", "%%").join(format_fields)
1071 137161c9 Michael Hanselmann
1072 137161c9 Michael Hanselmann
  for row in data:
1073 dcbd6288 Guido Trotter
    if row is None:
1074 dcbd6288 Guido Trotter
      continue
1075 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
1076 00430f8e Iustin Pop
      if unitfields.Matches(fields[idx]):
1077 137161c9 Michael Hanselmann
        try:
1078 137161c9 Michael Hanselmann
          val = int(val)
1079 137161c9 Michael Hanselmann
        except ValueError:
1080 137161c9 Michael Hanselmann
          pass
1081 137161c9 Michael Hanselmann
        else:
1082 9fbfbb7b Iustin Pop
          val = row[idx] = utils.FormatUnit(val, units)
1083 01ca31ae Iustin Pop
      val = row[idx] = str(val)
1084 137161c9 Michael Hanselmann
      if separator is None:
1085 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
1086 137161c9 Michael Hanselmann
1087 16be8703 Iustin Pop
  result = []
1088 137161c9 Michael Hanselmann
  if headers:
1089 137161c9 Michael Hanselmann
    args = []
1090 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
1091 137161c9 Michael Hanselmann
      hdr = headers[name]
1092 137161c9 Michael Hanselmann
      if separator is None:
1093 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
1094 137161c9 Michael Hanselmann
        args.append(mlens[idx])
1095 137161c9 Michael Hanselmann
      args.append(hdr)
1096 16be8703 Iustin Pop
    result.append(format % tuple(args))
1097 137161c9 Michael Hanselmann
1098 137161c9 Michael Hanselmann
  for line in data:
1099 137161c9 Michael Hanselmann
    args = []
1100 dcbd6288 Guido Trotter
    if line is None:
1101 dcbd6288 Guido Trotter
      line = ['-' for _ in fields]
1102 137161c9 Michael Hanselmann
    for idx in xrange(len(fields)):
1103 137161c9 Michael Hanselmann
      if separator is None:
1104 137161c9 Michael Hanselmann
        args.append(mlens[idx])
1105 137161c9 Michael Hanselmann
      args.append(line[idx])
1106 16be8703 Iustin Pop
    result.append(format % tuple(args))
1107 16be8703 Iustin Pop
1108 16be8703 Iustin Pop
  return result
1109 3386e7a9 Iustin Pop
1110 3386e7a9 Iustin Pop
1111 3386e7a9 Iustin Pop
def FormatTimestamp(ts):
1112 3386e7a9 Iustin Pop
  """Formats a given timestamp.
1113 3386e7a9 Iustin Pop

1114 3386e7a9 Iustin Pop
  @type ts: timestamp
1115 3386e7a9 Iustin Pop
  @param ts: a timeval-type timestamp, a tuple of seconds and microseconds
1116 3386e7a9 Iustin Pop

1117 3386e7a9 Iustin Pop
  @rtype: string
1118 5fcc718f Iustin Pop
  @return: a string with the formatted timestamp
1119 3386e7a9 Iustin Pop

1120 3386e7a9 Iustin Pop
  """
1121 e0ec0ff6 Iustin Pop
  if not isinstance (ts, (tuple, list)) or len(ts) != 2:
1122 e0ec0ff6 Iustin Pop
    return '?'
1123 3386e7a9 Iustin Pop
  sec, usec = ts
1124 3386e7a9 Iustin Pop
  return time.strftime("%F %T", time.localtime(sec)) + ".%06d" % usec
1125 2241e2b9 Iustin Pop
1126 2241e2b9 Iustin Pop
1127 2241e2b9 Iustin Pop
def ParseTimespec(value):
1128 2241e2b9 Iustin Pop
  """Parse a time specification.
1129 2241e2b9 Iustin Pop

1130 2241e2b9 Iustin Pop
  The following suffixed will be recognized:
1131 2241e2b9 Iustin Pop

1132 2241e2b9 Iustin Pop
    - s: seconds
1133 2241e2b9 Iustin Pop
    - m: minutes
1134 2241e2b9 Iustin Pop
    - h: hours
1135 2241e2b9 Iustin Pop
    - d: day
1136 2241e2b9 Iustin Pop
    - w: weeks
1137 2241e2b9 Iustin Pop

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

1140 2241e2b9 Iustin Pop
  """
1141 2241e2b9 Iustin Pop
  value = str(value)
1142 2241e2b9 Iustin Pop
  if not value:
1143 2241e2b9 Iustin Pop
    raise errors.OpPrereqError("Empty time specification passed")
1144 2241e2b9 Iustin Pop
  suffix_map = {
1145 2241e2b9 Iustin Pop
    's': 1,
1146 2241e2b9 Iustin Pop
    'm': 60,
1147 2241e2b9 Iustin Pop
    'h': 3600,
1148 2241e2b9 Iustin Pop
    'd': 86400,
1149 2241e2b9 Iustin Pop
    'w': 604800,
1150 2241e2b9 Iustin Pop
    }
1151 2241e2b9 Iustin Pop
  if value[-1] not in suffix_map:
1152 2241e2b9 Iustin Pop
    try:
1153 2241e2b9 Iustin Pop
      value = int(value)
1154 2241e2b9 Iustin Pop
    except ValueError:
1155 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
1156 2241e2b9 Iustin Pop
  else:
1157 2241e2b9 Iustin Pop
    multiplier = suffix_map[value[-1]]
1158 2241e2b9 Iustin Pop
    value = value[:-1]
1159 2241e2b9 Iustin Pop
    if not value: # no data left after stripping the suffix
1160 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification (only"
1161 2241e2b9 Iustin Pop
                                 " suffix passed)")
1162 2241e2b9 Iustin Pop
    try:
1163 2241e2b9 Iustin Pop
      value = int(value) * multiplier
1164 2241e2b9 Iustin Pop
    except ValueError:
1165 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
1166 2241e2b9 Iustin Pop
  return value
1167 46fbdd04 Iustin Pop
1168 46fbdd04 Iustin Pop
1169 4040a784 Iustin Pop
def GetOnlineNodes(nodes, cl=None, nowarn=False):
1170 4040a784 Iustin Pop
  """Returns the names of online nodes.
1171 4040a784 Iustin Pop

1172 4040a784 Iustin Pop
  This function will also log a warning on stderr with the names of
1173 4040a784 Iustin Pop
  the online nodes.
1174 4040a784 Iustin Pop

1175 4040a784 Iustin Pop
  @param nodes: if not empty, use only this subset of nodes (minus the
1176 4040a784 Iustin Pop
      offline ones)
1177 4040a784 Iustin Pop
  @param cl: if not None, luxi client to use
1178 4040a784 Iustin Pop
  @type nowarn: boolean
1179 4040a784 Iustin Pop
  @param nowarn: by default, this function will output a note with the
1180 4040a784 Iustin Pop
      offline nodes that are skipped; if this parameter is True the
1181 4040a784 Iustin Pop
      note is not displayed
1182 4040a784 Iustin Pop

1183 4040a784 Iustin Pop
  """
1184 4040a784 Iustin Pop
  if cl is None:
1185 4040a784 Iustin Pop
    cl = GetClient()
1186 4040a784 Iustin Pop
1187 2e7b8369 Iustin Pop
  result = cl.QueryNodes(names=nodes, fields=["name", "offline"],
1188 2e7b8369 Iustin Pop
                         use_locking=False)
1189 4040a784 Iustin Pop
  offline = [row[0] for row in result if row[1]]
1190 4040a784 Iustin Pop
  if offline and not nowarn:
1191 4040a784 Iustin Pop
    ToStderr("Note: skipping offline node(s): %s" % ", ".join(offline))
1192 4040a784 Iustin Pop
  return [row[0] for row in result if not row[1]]
1193 4040a784 Iustin Pop
1194 4040a784 Iustin Pop
1195 46fbdd04 Iustin Pop
def _ToStream(stream, txt, *args):
1196 46fbdd04 Iustin Pop
  """Write a message to a stream, bypassing the logging system
1197 46fbdd04 Iustin Pop

1198 46fbdd04 Iustin Pop
  @type stream: file object
1199 46fbdd04 Iustin Pop
  @param stream: the file to which we should write
1200 46fbdd04 Iustin Pop
  @type txt: str
1201 46fbdd04 Iustin Pop
  @param txt: the message
1202 46fbdd04 Iustin Pop

1203 46fbdd04 Iustin Pop
  """
1204 46fbdd04 Iustin Pop
  if args:
1205 46fbdd04 Iustin Pop
    args = tuple(args)
1206 46fbdd04 Iustin Pop
    stream.write(txt % args)
1207 46fbdd04 Iustin Pop
  else:
1208 46fbdd04 Iustin Pop
    stream.write(txt)
1209 46fbdd04 Iustin Pop
  stream.write('\n')
1210 46fbdd04 Iustin Pop
  stream.flush()
1211 46fbdd04 Iustin Pop
1212 46fbdd04 Iustin Pop
1213 46fbdd04 Iustin Pop
def ToStdout(txt, *args):
1214 46fbdd04 Iustin Pop
  """Write a message to stdout only, bypassing the logging system
1215 46fbdd04 Iustin Pop

1216 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
1217 46fbdd04 Iustin Pop

1218 46fbdd04 Iustin Pop
  @type txt: str
1219 46fbdd04 Iustin Pop
  @param txt: the message
1220 46fbdd04 Iustin Pop

1221 46fbdd04 Iustin Pop
  """
1222 46fbdd04 Iustin Pop
  _ToStream(sys.stdout, txt, *args)
1223 46fbdd04 Iustin Pop
1224 46fbdd04 Iustin Pop
1225 46fbdd04 Iustin Pop
def ToStderr(txt, *args):
1226 46fbdd04 Iustin Pop
  """Write a message to stderr only, bypassing the logging system
1227 46fbdd04 Iustin Pop

1228 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
1229 46fbdd04 Iustin Pop

1230 46fbdd04 Iustin Pop
  @type txt: str
1231 46fbdd04 Iustin Pop
  @param txt: the message
1232 46fbdd04 Iustin Pop

1233 46fbdd04 Iustin Pop
  """
1234 46fbdd04 Iustin Pop
  _ToStream(sys.stderr, txt, *args)
1235 479636a3 Iustin Pop
1236 479636a3 Iustin Pop
1237 479636a3 Iustin Pop
class JobExecutor(object):
1238 479636a3 Iustin Pop
  """Class which manages the submission and execution of multiple jobs.
1239 479636a3 Iustin Pop

1240 479636a3 Iustin Pop
  Note that instances of this class should not be reused between
1241 479636a3 Iustin Pop
  GetResults() calls.
1242 479636a3 Iustin Pop

1243 479636a3 Iustin Pop
  """
1244 479636a3 Iustin Pop
  def __init__(self, cl=None, verbose=True):
1245 479636a3 Iustin Pop
    self.queue = []
1246 479636a3 Iustin Pop
    if cl is None:
1247 479636a3 Iustin Pop
      cl = GetClient()
1248 479636a3 Iustin Pop
    self.cl = cl
1249 479636a3 Iustin Pop
    self.verbose = verbose
1250 23b4b983 Iustin Pop
    self.jobs = []
1251 479636a3 Iustin Pop
1252 479636a3 Iustin Pop
  def QueueJob(self, name, *ops):
1253 23b4b983 Iustin Pop
    """Record a job for later submit.
1254 479636a3 Iustin Pop

1255 479636a3 Iustin Pop
    @type name: string
1256 479636a3 Iustin Pop
    @param name: a description of the job, will be used in WaitJobSet
1257 479636a3 Iustin Pop
    """
1258 23b4b983 Iustin Pop
    self.queue.append((name, ops))
1259 23b4b983 Iustin Pop
1260 23b4b983 Iustin Pop
  def SubmitPending(self):
1261 23b4b983 Iustin Pop
    """Submit all pending jobs.
1262 23b4b983 Iustin Pop

1263 23b4b983 Iustin Pop
    """
1264 23b4b983 Iustin Pop
    results = self.cl.SubmitManyJobs([row[1] for row in self.queue])
1265 23b4b983 Iustin Pop
    for ((status, data), (name, _)) in zip(results, self.queue):
1266 23b4b983 Iustin Pop
      self.jobs.append((status, data, name))
1267 479636a3 Iustin Pop
1268 479636a3 Iustin Pop
  def GetResults(self):
1269 479636a3 Iustin Pop
    """Wait for and return the results of all jobs.
1270 479636a3 Iustin Pop

1271 479636a3 Iustin Pop
    @rtype: list
1272 479636a3 Iustin Pop
    @return: list of tuples (success, job results), in the same order
1273 479636a3 Iustin Pop
        as the submitted jobs; if a job has failed, instead of the result
1274 479636a3 Iustin Pop
        there will be the error message
1275 479636a3 Iustin Pop

1276 479636a3 Iustin Pop
    """
1277 23b4b983 Iustin Pop
    if not self.jobs:
1278 23b4b983 Iustin Pop
      self.SubmitPending()
1279 479636a3 Iustin Pop
    results = []
1280 479636a3 Iustin Pop
    if self.verbose:
1281 23b4b983 Iustin Pop
      ok_jobs = [row[1] for row in self.jobs if row[0]]
1282 23b4b983 Iustin Pop
      if ok_jobs:
1283 23b4b983 Iustin Pop
        ToStdout("Submitted jobs %s", ", ".join(ok_jobs))
1284 23b4b983 Iustin Pop
    for submit_status, jid, name in self.jobs:
1285 23b4b983 Iustin Pop
      if not submit_status:
1286 23b4b983 Iustin Pop
        ToStderr("Failed to submit job for %s: %s", name, jid)
1287 23b4b983 Iustin Pop
        results.append((False, jid))
1288 23b4b983 Iustin Pop
        continue
1289 479636a3 Iustin Pop
      if self.verbose:
1290 479636a3 Iustin Pop
        ToStdout("Waiting for job %s for %s...", jid, name)
1291 479636a3 Iustin Pop
      try:
1292 479636a3 Iustin Pop
        job_result = PollJob(jid, cl=self.cl)
1293 479636a3 Iustin Pop
        success = True
1294 479636a3 Iustin Pop
      except (errors.GenericError, luxi.ProtocolError), err:
1295 479636a3 Iustin Pop
        _, job_result = FormatError(err)
1296 479636a3 Iustin Pop
        success = False
1297 479636a3 Iustin Pop
        # the error message will always be shown, verbose or not
1298 479636a3 Iustin Pop
        ToStderr("Job %s for %s has failed: %s", jid, name, job_result)
1299 479636a3 Iustin Pop
1300 479636a3 Iustin Pop
      results.append((success, job_result))
1301 479636a3 Iustin Pop
    return results
1302 479636a3 Iustin Pop
1303 479636a3 Iustin Pop
  def WaitOrShow(self, wait):
1304 479636a3 Iustin Pop
    """Wait for job results or only print the job IDs.
1305 479636a3 Iustin Pop

1306 479636a3 Iustin Pop
    @type wait: boolean
1307 479636a3 Iustin Pop
    @param wait: whether to wait or not
1308 479636a3 Iustin Pop

1309 479636a3 Iustin Pop
    """
1310 479636a3 Iustin Pop
    if wait:
1311 479636a3 Iustin Pop
      return self.GetResults()
1312 479636a3 Iustin Pop
    else:
1313 23b4b983 Iustin Pop
      if not self.jobs:
1314 23b4b983 Iustin Pop
        self.SubmitPending()
1315 23b4b983 Iustin Pop
      for status, result, name in self.jobs:
1316 23b4b983 Iustin Pop
        if status:
1317 23b4b983 Iustin Pop
          ToStdout("%s: %s", result, name)
1318 23b4b983 Iustin Pop
        else:
1319 23b4b983 Iustin Pop
          ToStderr("Failure for %s: %s", name, result)