Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ 1f587d3d

History | View | Annotate | Download (48.7 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 e7e09483 Iustin Pop
  "ALLOCATABLE_OPT",
48 2d5e7ae1 Iustin Pop
  "ALL_OPT",
49 e00f7a05 Iustin Pop
  "AUTO_REPLACE_OPT",
50 087ed2ed Iustin Pop
  "BACKEND_OPT",
51 baef337d Iustin Pop
  "CLEANUP_OPT",
52 4abc4f1e Iustin Pop
  "CONFIRM_OPT",
53 e32df528 Iustin Pop
  "CP_SIZE_OPT",
54 4abc4f1e Iustin Pop
  "DEBUG_OPT",
55 a0c9776a Iustin Pop
  "DEBUG_SIMERR_OPT",
56 4b038a1e Iustin Pop
  "DISKIDX_OPT",
57 e3876ccb Iustin Pop
  "DISK_OPT",
58 4b038a1e Iustin Pop
  "DISK_TEMPLATE_OPT",
59 771734c9 Iustin Pop
  "DRAINED_OPT",
60 383a3591 Iustin Pop
  "ENABLED_HV_OPT",
61 4abc4f1e Iustin Pop
  "FIELDS_OPT",
62 4a25828c Iustin Pop
  "FILESTORE_DIR_OPT",
63 0f87c43e Iustin Pop
  "FILESTORE_DRIVER_OPT",
64 073271f6 Iustin Pop
  "HVLIST_OPT",
65 48f212d7 Iustin Pop
  "HVOPTS_OPT",
66 236fd9c4 Iustin Pop
  "HYPERVISOR_OPT",
67 4eb62659 Iustin Pop
  "IALLOCATOR_OPT",
68 82a786d5 Iustin Pop
  "IGNORE_CONSIST_OPT",
69 b6e841a8 Iustin Pop
  "IGNORE_FAILURES_OPT",
70 05586c90 Iustin Pop
  "IGNORE_SIZE_OPT",
71 4abc4f1e Iustin Pop
  "FORCE_OPT",
72 771734c9 Iustin Pop
  "MC_OPT",
73 7d3a9fab Iustin Pop
  "NET_OPT",
74 a14db5ff Iustin Pop
  "NEW_SECONDARY_OPT",
75 4fbc93dd Iustin Pop
  "NIC_PARAMS_OPT",
76 7edc4637 Iustin Pop
  "NODE_LIST_OPT",
77 990b7886 Iustin Pop
  "NODE_PLACEMENT_OPT",
78 4abc4f1e Iustin Pop
  "NOHDR_OPT",
79 91e0748c Iustin Pop
  "NOIPCHECK_OPT",
80 831040bf Iustin Pop
  "NOLVM_STORAGE_OPT",
81 26023ecd Iustin Pop
  "NONICS_OPT",
82 f2a0828c Iustin Pop
  "NONLIVE_OPT",
83 edeb878a Iustin Pop
  "NOSTART_OPT",
84 fcdde7f2 Iustin Pop
  "NOSSH_KEYCHECK_OPT",
85 3f75b4f3 Iustin Pop
  "NWSYNC_OPT",
86 a72d0a79 Iustin Pop
  "ON_PRIMARY_OPT",
87 feb09e6a Iustin Pop
  "ON_SECONDARY_OPT",
88 771734c9 Iustin Pop
  "OFFLINE_OPT",
89 d3ed23ff Iustin Pop
  "OS_OPT",
90 ff00c1a7 Iustin Pop
  "OS_SIZE_OPT",
91 b8d0f938 Iustin Pop
  "READD_OPT",
92 8d823629 Iustin Pop
  "SECONDARY_IP_OPT",
93 67840b40 Iustin Pop
  "SELECT_OS_OPT",
94 4abc4f1e Iustin Pop
  "SEP_OPT",
95 fdcf4d84 Iustin Pop
  "SHOWCMD_OPT",
96 f36d7d81 Iustin Pop
  "SINGLE_NODE_OPT",
97 df62e5db Iustin Pop
  "SRC_DIR_OPT",
98 df62e5db Iustin Pop
  "SRC_NODE_OPT",
99 4abc4f1e Iustin Pop
  "SUBMIT_OPT",
100 99a8c799 Iustin Pop
  "STATIC_OPT",
101 4abc4f1e Iustin Pop
  "SYNC_OPT",
102 4abc4f1e Iustin Pop
  "TAG_SRC_OPT",
103 4abc4f1e Iustin Pop
  "USEUNITS_OPT",
104 9cdb9578 Iustin Pop
  "VERBOSE_OPT",
105 b58726e8 Iustin Pop
  "VG_NAME_OPT",
106 1f587d3d Iustin Pop
  "YES_DOIT_OPT",
107 4abc4f1e Iustin Pop
  # Generic functions for CLI programs
108 4abc4f1e Iustin Pop
  "GenericMain",
109 4abc4f1e Iustin Pop
  "GetClient",
110 4abc4f1e Iustin Pop
  "GetOnlineNodes",
111 4abc4f1e Iustin Pop
  "JobExecutor",
112 4abc4f1e Iustin Pop
  "JobSubmittedException",
113 4abc4f1e Iustin Pop
  "ParseTimespec",
114 4abc4f1e Iustin Pop
  "SubmitOpCode",
115 4abc4f1e Iustin Pop
  "SubmitOrSend",
116 4abc4f1e Iustin Pop
  "UsesRPC",
117 4abc4f1e Iustin Pop
  # Formatting functions
118 4abc4f1e Iustin Pop
  "ToStderr", "ToStdout",
119 4abc4f1e Iustin Pop
  "FormatError",
120 4abc4f1e Iustin Pop
  "GenerateTable",
121 4abc4f1e Iustin Pop
  "AskUser",
122 4abc4f1e Iustin Pop
  "FormatTimestamp",
123 4abc4f1e Iustin Pop
  # Tags functions
124 4abc4f1e Iustin Pop
  "ListTags",
125 4abc4f1e Iustin Pop
  "AddTags",
126 4abc4f1e Iustin Pop
  "RemoveTags",
127 4abc4f1e Iustin Pop
  # command line options support infrastructure
128 4abc4f1e Iustin Pop
  "ARGS_MANY_INSTANCES",
129 4abc4f1e Iustin Pop
  "ARGS_MANY_NODES",
130 4abc4f1e Iustin Pop
  "ARGS_NONE",
131 4abc4f1e Iustin Pop
  "ARGS_ONE_INSTANCE",
132 4abc4f1e Iustin Pop
  "ARGS_ONE_NODE",
133 4abc4f1e Iustin Pop
  "ArgChoice",
134 4abc4f1e Iustin Pop
  "ArgCommand",
135 4abc4f1e Iustin Pop
  "ArgFile",
136 4abc4f1e Iustin Pop
  "ArgHost",
137 4abc4f1e Iustin Pop
  "ArgInstance",
138 4abc4f1e Iustin Pop
  "ArgJobId",
139 4abc4f1e Iustin Pop
  "ArgNode",
140 4abc4f1e Iustin Pop
  "ArgSuggest",
141 4abc4f1e Iustin Pop
  "ArgUnknown",
142 4abc4f1e Iustin Pop
  "OPT_COMPL_INST_ADD_NODES",
143 4abc4f1e Iustin Pop
  "OPT_COMPL_MANY_NODES",
144 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_IALLOCATOR",
145 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_INSTANCE",
146 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_NODE",
147 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_OS",
148 4abc4f1e Iustin Pop
  "cli_option",
149 4abc4f1e Iustin Pop
  "SplitNodeOption",
150 4abc4f1e Iustin Pop
  ]
151 846baef9 Iustin Pop
152 8b46606c Guido Trotter
NO_PREFIX = "no_"
153 8b46606c Guido Trotter
UN_PREFIX = "-"
154 846baef9 Iustin Pop
155 03298ebe Michael Hanselmann
156 863d7f46 Michael Hanselmann
class _Argument:
157 dff85078 Michael Hanselmann
  def __init__(self, min=0, max=None):
158 863d7f46 Michael Hanselmann
    self.min = min
159 863d7f46 Michael Hanselmann
    self.max = max
160 863d7f46 Michael Hanselmann
161 863d7f46 Michael Hanselmann
  def __repr__(self):
162 863d7f46 Michael Hanselmann
    return ("<%s min=%s max=%s>" %
163 863d7f46 Michael Hanselmann
            (self.__class__.__name__, self.min, self.max))
164 863d7f46 Michael Hanselmann
165 863d7f46 Michael Hanselmann
166 863d7f46 Michael Hanselmann
class ArgSuggest(_Argument):
167 863d7f46 Michael Hanselmann
  """Suggesting argument.
168 863d7f46 Michael Hanselmann

169 863d7f46 Michael Hanselmann
  Value can be any of the ones passed to the constructor.
170 863d7f46 Michael Hanselmann

171 863d7f46 Michael Hanselmann
  """
172 863d7f46 Michael Hanselmann
  def __init__(self, min=0, max=None, choices=None):
173 863d7f46 Michael Hanselmann
    _Argument.__init__(self, min=min, max=max)
174 863d7f46 Michael Hanselmann
    self.choices = choices
175 863d7f46 Michael Hanselmann
176 863d7f46 Michael Hanselmann
  def __repr__(self):
177 863d7f46 Michael Hanselmann
    return ("<%s min=%s max=%s choices=%r>" %
178 863d7f46 Michael Hanselmann
            (self.__class__.__name__, self.min, self.max, self.choices))
179 863d7f46 Michael Hanselmann
180 863d7f46 Michael Hanselmann
181 863d7f46 Michael Hanselmann
class ArgChoice(ArgSuggest):
182 863d7f46 Michael Hanselmann
  """Choice argument.
183 863d7f46 Michael Hanselmann

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

187 863d7f46 Michael Hanselmann
  """
188 863d7f46 Michael Hanselmann
189 863d7f46 Michael Hanselmann
190 863d7f46 Michael Hanselmann
class ArgUnknown(_Argument):
191 863d7f46 Michael Hanselmann
  """Unknown argument to program (e.g. determined at runtime).
192 863d7f46 Michael Hanselmann

193 863d7f46 Michael Hanselmann
  """
194 863d7f46 Michael Hanselmann
195 863d7f46 Michael Hanselmann
196 863d7f46 Michael Hanselmann
class ArgInstance(_Argument):
197 863d7f46 Michael Hanselmann
  """Instances argument.
198 863d7f46 Michael Hanselmann

199 863d7f46 Michael Hanselmann
  """
200 863d7f46 Michael Hanselmann
201 863d7f46 Michael Hanselmann
202 863d7f46 Michael Hanselmann
class ArgNode(_Argument):
203 863d7f46 Michael Hanselmann
  """Node argument.
204 863d7f46 Michael Hanselmann

205 863d7f46 Michael Hanselmann
  """
206 863d7f46 Michael Hanselmann
207 863d7f46 Michael Hanselmann
class ArgJobId(_Argument):
208 863d7f46 Michael Hanselmann
  """Job ID argument.
209 863d7f46 Michael Hanselmann

210 863d7f46 Michael Hanselmann
  """
211 863d7f46 Michael Hanselmann
212 863d7f46 Michael Hanselmann
213 863d7f46 Michael Hanselmann
class ArgFile(_Argument):
214 863d7f46 Michael Hanselmann
  """File path argument.
215 863d7f46 Michael Hanselmann

216 863d7f46 Michael Hanselmann
  """
217 863d7f46 Michael Hanselmann
218 863d7f46 Michael Hanselmann
219 863d7f46 Michael Hanselmann
class ArgCommand(_Argument):
220 863d7f46 Michael Hanselmann
  """Command argument.
221 863d7f46 Michael Hanselmann

222 863d7f46 Michael Hanselmann
  """
223 863d7f46 Michael Hanselmann
224 863d7f46 Michael Hanselmann
225 83ec7961 Michael Hanselmann
class ArgHost(_Argument):
226 83ec7961 Michael Hanselmann
  """Host argument.
227 83ec7961 Michael Hanselmann

228 83ec7961 Michael Hanselmann
  """
229 83ec7961 Michael Hanselmann
230 83ec7961 Michael Hanselmann
231 4a265c08 Michael Hanselmann
ARGS_NONE = []
232 4a265c08 Michael Hanselmann
ARGS_MANY_INSTANCES = [ArgInstance()]
233 4a265c08 Michael Hanselmann
ARGS_MANY_NODES = [ArgNode()]
234 4a265c08 Michael Hanselmann
ARGS_ONE_INSTANCE = [ArgInstance(min=1, max=1)]
235 4a265c08 Michael Hanselmann
ARGS_ONE_NODE = [ArgNode(min=1, max=1)]
236 4a265c08 Michael Hanselmann
237 4a265c08 Michael Hanselmann
238 73b90123 Michael Hanselmann
239 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
240 846baef9 Iustin Pop
  """Extract the tag type object.
241 846baef9 Iustin Pop

242 846baef9 Iustin Pop
  Note that this function will modify its args parameter.
243 846baef9 Iustin Pop

244 846baef9 Iustin Pop
  """
245 846baef9 Iustin Pop
  if not hasattr(opts, "tag_type"):
246 846baef9 Iustin Pop
    raise errors.ProgrammerError("tag_type not passed to _ExtractTagsObject")
247 846baef9 Iustin Pop
  kind = opts.tag_type
248 846baef9 Iustin Pop
  if kind == constants.TAG_CLUSTER:
249 846baef9 Iustin Pop
    retval = kind, kind
250 846baef9 Iustin Pop
  elif kind == constants.TAG_NODE or kind == constants.TAG_INSTANCE:
251 846baef9 Iustin Pop
    if not args:
252 0c434948 Iustin Pop
      raise errors.OpPrereqError("no arguments passed to the command")
253 846baef9 Iustin Pop
    name = args.pop(0)
254 846baef9 Iustin Pop
    retval = kind, name
255 846baef9 Iustin Pop
  else:
256 846baef9 Iustin Pop
    raise errors.ProgrammerError("Unhandled tag type '%s'" % kind)
257 846baef9 Iustin Pop
  return retval
258 846baef9 Iustin Pop
259 846baef9 Iustin Pop
260 810c50b7 Iustin Pop
def _ExtendTags(opts, args):
261 810c50b7 Iustin Pop
  """Extend the args if a source file has been given.
262 810c50b7 Iustin Pop

263 810c50b7 Iustin Pop
  This function will extend the tags with the contents of the file
264 810c50b7 Iustin Pop
  passed in the 'tags_source' attribute of the opts parameter. A file
265 810c50b7 Iustin Pop
  named '-' will be replaced by stdin.
266 810c50b7 Iustin Pop

267 810c50b7 Iustin Pop
  """
268 810c50b7 Iustin Pop
  fname = opts.tags_source
269 810c50b7 Iustin Pop
  if fname is None:
270 810c50b7 Iustin Pop
    return
271 810c50b7 Iustin Pop
  if fname == "-":
272 810c50b7 Iustin Pop
    new_fh = sys.stdin
273 810c50b7 Iustin Pop
  else:
274 810c50b7 Iustin Pop
    new_fh = open(fname, "r")
275 810c50b7 Iustin Pop
  new_data = []
276 810c50b7 Iustin Pop
  try:
277 810c50b7 Iustin Pop
    # we don't use the nice 'new_data = [line.strip() for line in fh]'
278 810c50b7 Iustin Pop
    # because of python bug 1633941
279 810c50b7 Iustin Pop
    while True:
280 810c50b7 Iustin Pop
      line = new_fh.readline()
281 810c50b7 Iustin Pop
      if not line:
282 810c50b7 Iustin Pop
        break
283 810c50b7 Iustin Pop
      new_data.append(line.strip())
284 810c50b7 Iustin Pop
  finally:
285 810c50b7 Iustin Pop
    new_fh.close()
286 810c50b7 Iustin Pop
  args.extend(new_data)
287 810c50b7 Iustin Pop
288 810c50b7 Iustin Pop
289 846baef9 Iustin Pop
def ListTags(opts, args):
290 846baef9 Iustin Pop
  """List the tags on a given object.
291 846baef9 Iustin Pop

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

297 846baef9 Iustin Pop
  """
298 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
299 846baef9 Iustin Pop
  op = opcodes.OpGetTags(kind=kind, name=name)
300 846baef9 Iustin Pop
  result = SubmitOpCode(op)
301 846baef9 Iustin Pop
  result = list(result)
302 846baef9 Iustin Pop
  result.sort()
303 846baef9 Iustin Pop
  for tag in result:
304 03298ebe Michael Hanselmann
    ToStdout(tag)
305 846baef9 Iustin Pop
306 846baef9 Iustin Pop
307 846baef9 Iustin Pop
def AddTags(opts, args):
308 846baef9 Iustin Pop
  """Add tags on a given object.
309 846baef9 Iustin Pop

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

315 846baef9 Iustin Pop
  """
316 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
317 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
318 846baef9 Iustin Pop
  if not args:
319 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be added")
320 846baef9 Iustin Pop
  op = opcodes.OpAddTags(kind=kind, name=name, tags=args)
321 846baef9 Iustin Pop
  SubmitOpCode(op)
322 846baef9 Iustin Pop
323 846baef9 Iustin Pop
324 846baef9 Iustin Pop
def RemoveTags(opts, args):
325 846baef9 Iustin Pop
  """Remove tags from a given object.
326 846baef9 Iustin Pop

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

332 846baef9 Iustin Pop
  """
333 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
334 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
335 846baef9 Iustin Pop
  if not args:
336 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be removed")
337 846baef9 Iustin Pop
  op = opcodes.OpDelTags(kind=kind, name=name, tags=args)
338 846baef9 Iustin Pop
  SubmitOpCode(op)
339 846baef9 Iustin Pop
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
def check_unit(option, opt, value):
342 65fe4693 Iustin Pop
  """OptParsers custom converter for units.
343 65fe4693 Iustin Pop

344 65fe4693 Iustin Pop
  """
345 a8083063 Iustin Pop
  try:
346 a8083063 Iustin Pop
    return utils.ParseUnit(value)
347 a8083063 Iustin Pop
  except errors.UnitParseError, err:
348 3ecf6786 Iustin Pop
    raise OptionValueError("option %s: %s" % (opt, err))
349 a8083063 Iustin Pop
350 a8083063 Iustin Pop
351 a8469393 Iustin Pop
def _SplitKeyVal(opt, data):
352 a8469393 Iustin Pop
  """Convert a KeyVal string into a dict.
353 a8469393 Iustin Pop

354 a8469393 Iustin Pop
  This function will convert a key=val[,...] string into a dict. Empty
355 a8469393 Iustin Pop
  values will be converted specially: keys which have the prefix 'no_'
356 a8469393 Iustin Pop
  will have the value=False and the prefix stripped, the others will
357 a8469393 Iustin Pop
  have value=True.
358 a8469393 Iustin Pop

359 a8469393 Iustin Pop
  @type opt: string
360 a8469393 Iustin Pop
  @param opt: a string holding the option name for which we process the
361 a8469393 Iustin Pop
      data, used in building error messages
362 a8469393 Iustin Pop
  @type data: string
363 a8469393 Iustin Pop
  @param data: a string of the format key=val,key=val,...
364 a8469393 Iustin Pop
  @rtype: dict
365 a8469393 Iustin Pop
  @return: {key=val, key=val}
366 a8469393 Iustin Pop
  @raises errors.ParameterError: if there are duplicate keys
367 a8469393 Iustin Pop

368 a8469393 Iustin Pop
  """
369 a8469393 Iustin Pop
  kv_dict = {}
370 4f31882e Guido Trotter
  if data:
371 4f31882e Guido Trotter
    for elem in data.split(","):
372 4f31882e Guido Trotter
      if "=" in elem:
373 4f31882e Guido Trotter
        key, val = elem.split("=", 1)
374 a8469393 Iustin Pop
      else:
375 4f31882e Guido Trotter
        if elem.startswith(NO_PREFIX):
376 4f31882e Guido Trotter
          key, val = elem[len(NO_PREFIX):], False
377 4f31882e Guido Trotter
        elif elem.startswith(UN_PREFIX):
378 4f31882e Guido Trotter
          key, val = elem[len(UN_PREFIX):], None
379 4f31882e Guido Trotter
        else:
380 4f31882e Guido Trotter
          key, val = elem, True
381 4f31882e Guido Trotter
      if key in kv_dict:
382 4f31882e Guido Trotter
        raise errors.ParameterError("Duplicate key '%s' in option %s" %
383 4f31882e Guido Trotter
                                    (key, opt))
384 4f31882e Guido Trotter
      kv_dict[key] = val
385 a8469393 Iustin Pop
  return kv_dict
386 a8469393 Iustin Pop
387 a8469393 Iustin Pop
388 a8469393 Iustin Pop
def check_ident_key_val(option, opt, value):
389 552c8dff Michael Hanselmann
  """Custom parser for ident:key=val,key=val options.
390 552c8dff Michael Hanselmann

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

394 a8469393 Iustin Pop
  """
395 a8469393 Iustin Pop
  if ":" not in value:
396 8b46606c Guido Trotter
    ident, rest = value, ''
397 a8469393 Iustin Pop
  else:
398 a8469393 Iustin Pop
    ident, rest = value.split(":", 1)
399 8b46606c Guido Trotter
400 8b46606c Guido Trotter
  if ident.startswith(NO_PREFIX):
401 8b46606c Guido Trotter
    if rest:
402 8b46606c Guido Trotter
      msg = "Cannot pass options when removing parameter groups: %s" % value
403 8b46606c Guido Trotter
      raise errors.ParameterError(msg)
404 8b46606c Guido Trotter
    retval = (ident[len(NO_PREFIX):], False)
405 8b46606c Guido Trotter
  elif ident.startswith(UN_PREFIX):
406 8b46606c Guido Trotter
    if rest:
407 8b46606c Guido Trotter
      msg = "Cannot pass options when removing parameter groups: %s" % value
408 8b46606c Guido Trotter
      raise errors.ParameterError(msg)
409 8b46606c Guido Trotter
    retval = (ident[len(UN_PREFIX):], None)
410 8b46606c Guido Trotter
  else:
411 a8469393 Iustin Pop
    kv_dict = _SplitKeyVal(opt, rest)
412 a8469393 Iustin Pop
    retval = (ident, kv_dict)
413 a8469393 Iustin Pop
  return retval
414 a8469393 Iustin Pop
415 a8469393 Iustin Pop
416 a8469393 Iustin Pop
def check_key_val(option, opt, value):
417 552c8dff Michael Hanselmann
  """Custom parser class for key=val,key=val options.
418 552c8dff Michael Hanselmann

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

421 a8469393 Iustin Pop
  """
422 a8469393 Iustin Pop
  return _SplitKeyVal(opt, value)
423 a8469393 Iustin Pop
424 a8469393 Iustin Pop
425 63d44c55 Michael Hanselmann
# completion_suggestion is normally a list. Using numeric values not evaluating
426 63d44c55 Michael Hanselmann
# to False for dynamic completion.
427 63d44c55 Michael Hanselmann
(OPT_COMPL_MANY_NODES,
428 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_NODE,
429 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_INSTANCE,
430 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_OS,
431 2d3ed64b Michael Hanselmann
 OPT_COMPL_ONE_IALLOCATOR,
432 2d3ed64b Michael Hanselmann
 OPT_COMPL_INST_ADD_NODES) = range(100, 106)
433 63d44c55 Michael Hanselmann
434 63d44c55 Michael Hanselmann
OPT_COMPL_ALL = frozenset([
435 63d44c55 Michael Hanselmann
  OPT_COMPL_MANY_NODES,
436 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_NODE,
437 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_INSTANCE,
438 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_OS,
439 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_IALLOCATOR,
440 2d3ed64b Michael Hanselmann
  OPT_COMPL_INST_ADD_NODES,
441 63d44c55 Michael Hanselmann
  ])
442 63d44c55 Michael Hanselmann
443 63d44c55 Michael Hanselmann
444 552c8dff Michael Hanselmann
class CliOption(Option):
445 552c8dff Michael Hanselmann
  """Custom option class for optparse.
446 a8469393 Iustin Pop

447 a8469393 Iustin Pop
  """
448 863d7f46 Michael Hanselmann
  ATTRS = Option.ATTRS + [
449 863d7f46 Michael Hanselmann
    "completion_suggest",
450 863d7f46 Michael Hanselmann
    ]
451 552c8dff Michael Hanselmann
  TYPES = Option.TYPES + (
452 552c8dff Michael Hanselmann
    "identkeyval",
453 552c8dff Michael Hanselmann
    "keyval",
454 552c8dff Michael Hanselmann
    "unit",
455 552c8dff Michael Hanselmann
    )
456 552c8dff Michael Hanselmann
  TYPE_CHECKER = Option.TYPE_CHECKER.copy()
457 552c8dff Michael Hanselmann
  TYPE_CHECKER["identkeyval"] = check_ident_key_val
458 a8469393 Iustin Pop
  TYPE_CHECKER["keyval"] = check_key_val
459 552c8dff Michael Hanselmann
  TYPE_CHECKER["unit"] = check_unit
460 a8469393 Iustin Pop
461 a8469393 Iustin Pop
462 a8083063 Iustin Pop
# optparse.py sets make_option, so we do it for our own option class, too
463 a8083063 Iustin Pop
cli_option = CliOption
464 a8083063 Iustin Pop
465 a8083063 Iustin Pop
466 771734c9 Iustin Pop
_YESNO = ("yes", "no")
467 771734c9 Iustin Pop
_YORNO = "yes|no"
468 771734c9 Iustin Pop
469 c38c44ad Michael Hanselmann
DEBUG_OPT = cli_option("-d", "--debug", default=False,
470 c38c44ad Michael Hanselmann
                       action="store_true",
471 c38c44ad Michael Hanselmann
                       help="Turn debugging on")
472 c38c44ad Michael Hanselmann
473 c38c44ad Michael Hanselmann
NOHDR_OPT = cli_option("--no-headers", default=False,
474 c38c44ad Michael Hanselmann
                       action="store_true", dest="no_headers",
475 c38c44ad Michael Hanselmann
                       help="Don't display column headers")
476 c38c44ad Michael Hanselmann
477 c38c44ad Michael Hanselmann
SEP_OPT = cli_option("--separator", default=None,
478 c38c44ad Michael Hanselmann
                     action="store", dest="separator",
479 c38c44ad Michael Hanselmann
                     help=("Separator between output fields"
480 c38c44ad Michael Hanselmann
                           " (defaults to one space)"))
481 c38c44ad Michael Hanselmann
482 c38c44ad Michael Hanselmann
USEUNITS_OPT = cli_option("--units", default=None,
483 c38c44ad Michael Hanselmann
                          dest="units", choices=('h', 'm', 'g', 't'),
484 c38c44ad Michael Hanselmann
                          help="Specify units for output (one of hmgt)")
485 c38c44ad Michael Hanselmann
486 c38c44ad Michael Hanselmann
FIELDS_OPT = cli_option("-o", "--output", dest="output", action="store",
487 c38c44ad Michael Hanselmann
                        type="string", metavar="FIELDS",
488 c38c44ad Michael Hanselmann
                        help="Comma separated list of output fields")
489 c38c44ad Michael Hanselmann
490 c38c44ad Michael Hanselmann
FORCE_OPT = cli_option("-f", "--force", dest="force", action="store_true",
491 c38c44ad Michael Hanselmann
                       default=False, help="Force the operation")
492 c38c44ad Michael Hanselmann
493 c38c44ad Michael Hanselmann
CONFIRM_OPT = cli_option("--yes", dest="confirm", action="store_true",
494 c38c44ad Michael Hanselmann
                         default=False, help="Do not require confirmation")
495 c38c44ad Michael Hanselmann
496 c38c44ad Michael Hanselmann
TAG_SRC_OPT = cli_option("--from", dest="tags_source",
497 c38c44ad Michael Hanselmann
                         default=None, help="File with tag names")
498 c38c44ad Michael Hanselmann
499 c38c44ad Michael Hanselmann
SUBMIT_OPT = cli_option("--submit", dest="submit_only",
500 c38c44ad Michael Hanselmann
                        default=False, action="store_true",
501 c38c44ad Michael Hanselmann
                        help=("Submit the job and return the job ID, but"
502 c38c44ad Michael Hanselmann
                              " don't wait for the job to finish"))
503 c38c44ad Michael Hanselmann
504 c38c44ad Michael Hanselmann
SYNC_OPT = cli_option("--sync", dest="do_locking",
505 c38c44ad Michael Hanselmann
                      default=False, action="store_true",
506 c38c44ad Michael Hanselmann
                      help=("Grab locks while doing the queries"
507 c38c44ad Michael Hanselmann
                            " in order to ensure more consistent results"))
508 c38c44ad Michael Hanselmann
509 c38c44ad Michael Hanselmann
_DRY_RUN_OPT = cli_option("--dry-run", default=False,
510 c38c44ad Michael Hanselmann
                          action="store_true",
511 c38c44ad Michael Hanselmann
                          help=("Do not execute the operation, just run the"
512 c38c44ad Michael Hanselmann
                                " check steps and verify it it could be"
513 c38c44ad Michael Hanselmann
                                " executed"))
514 c38c44ad Michael Hanselmann
515 9cdb9578 Iustin Pop
VERBOSE_OPT = cli_option("-v", "--verbose", default=False,
516 9cdb9578 Iustin Pop
                         action="store_true",
517 9cdb9578 Iustin Pop
                         help="Increase the verbosity of the operation")
518 9cdb9578 Iustin Pop
519 a0c9776a Iustin Pop
DEBUG_SIMERR_OPT = cli_option("--debug-simulate-errors", default=False,
520 a0c9776a Iustin Pop
                              action="store_true", dest="simulate_errors",
521 a0c9776a Iustin Pop
                              help="Debugging option that makes the operation"
522 a0c9776a Iustin Pop
                              " treat most runtime checks as failed")
523 a0c9776a Iustin Pop
524 3f75b4f3 Iustin Pop
NWSYNC_OPT = cli_option("--no-wait-for-sync", dest="wait_for_sync",
525 3f75b4f3 Iustin Pop
                        default=True, action="store_false",
526 3f75b4f3 Iustin Pop
                        help="Don't wait for sync (DANGEROUS!)")
527 3f75b4f3 Iustin Pop
528 4f365444 Iustin Pop
DISK_TEMPLATE_OPT = cli_option("-t", "--disk-template", dest="disk_template",
529 4f365444 Iustin Pop
                               help="Custom disk setup (diskless, file,"
530 4f365444 Iustin Pop
                               " plain or drbd)",
531 4f365444 Iustin Pop
                               default=None, metavar="TEMPL",
532 4f365444 Iustin Pop
                               choices=list(constants.DISK_TEMPLATES))
533 4f365444 Iustin Pop
534 26023ecd Iustin Pop
NONICS_OPT = cli_option("--no-nics", default=False, action="store_true",
535 26023ecd Iustin Pop
                        help="Do not create any network cards for"
536 26023ecd Iustin Pop
                        " the instance")
537 26023ecd Iustin Pop
538 4a25828c Iustin Pop
FILESTORE_DIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
539 4a25828c Iustin Pop
                               help="Relative path under default cluster-wide"
540 4a25828c Iustin Pop
                               " file storage dir to store file-based disks",
541 4a25828c Iustin Pop
                               default=None, metavar="<DIR>")
542 4a25828c Iustin Pop
543 0f87c43e Iustin Pop
FILESTORE_DRIVER_OPT = cli_option("--file-driver", dest="file_driver",
544 0f87c43e Iustin Pop
                                  help="Driver to use for image files",
545 0f87c43e Iustin Pop
                                  default="loop", metavar="<DRIVER>",
546 0f87c43e Iustin Pop
                                  choices=list(constants.FILE_DRIVER))
547 0f87c43e Iustin Pop
548 4eb62659 Iustin Pop
IALLOCATOR_OPT = cli_option("-I", "--iallocator", metavar="<NAME>",
549 4eb62659 Iustin Pop
                            help="Select nodes for the instance automatically"
550 4eb62659 Iustin Pop
                            " using the <NAME> iallocator plugin",
551 4eb62659 Iustin Pop
                            default=None, type="string",
552 4eb62659 Iustin Pop
                            completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
553 4eb62659 Iustin Pop
554 d3ed23ff Iustin Pop
OS_OPT = cli_option("-o", "--os-type", dest="os", help="What OS to run",
555 d3ed23ff Iustin Pop
                    metavar="<os>",
556 d3ed23ff Iustin Pop
                    completion_suggest=OPT_COMPL_ONE_OS)
557 d3ed23ff Iustin Pop
558 087ed2ed Iustin Pop
BACKEND_OPT = cli_option("-B", "--backend-parameters", dest="beparams",
559 087ed2ed Iustin Pop
                         type="keyval", default={},
560 087ed2ed Iustin Pop
                         help="Backend parameters")
561 48f212d7 Iustin Pop
562 48f212d7 Iustin Pop
HVOPTS_OPT =  cli_option("-H", "--hypervisor-parameters", type="keyval",
563 48f212d7 Iustin Pop
                         default={}, dest="hvparams",
564 48f212d7 Iustin Pop
                         help="Hypervisor parameters")
565 087ed2ed Iustin Pop
566 236fd9c4 Iustin Pop
HYPERVISOR_OPT = cli_option("-H", "--hypervisor-parameters", dest="hypervisor",
567 236fd9c4 Iustin Pop
                            help="Hypervisor and hypervisor options, in the"
568 236fd9c4 Iustin Pop
                            " format hypervisor:option=value,option=value,...",
569 236fd9c4 Iustin Pop
                            default=None, type="identkeyval")
570 073271f6 Iustin Pop
571 073271f6 Iustin Pop
HVLIST_OPT = cli_option("-H", "--hypervisor-parameters", dest="hvparams",
572 073271f6 Iustin Pop
                        help="Hypervisor and hypervisor options, in the"
573 073271f6 Iustin Pop
                        " format hypervisor:option=value,option=value,...",
574 073271f6 Iustin Pop
                        default=[], action="append", type="identkeyval")
575 236fd9c4 Iustin Pop
576 91e0748c Iustin Pop
NOIPCHECK_OPT = cli_option("--no-ip-check", dest="ip_check", default=True,
577 91e0748c Iustin Pop
                           action="store_false",
578 91e0748c Iustin Pop
                           help="Don't check that the instance's IP"
579 91e0748c Iustin Pop
                           " is alive")
580 91e0748c Iustin Pop
581 7d3a9fab Iustin Pop
NET_OPT = cli_option("--net",
582 7d3a9fab Iustin Pop
                     help="NIC parameters", default=[],
583 7d3a9fab Iustin Pop
                     dest="nics", action="append", type="identkeyval")
584 e3876ccb Iustin Pop
585 e3876ccb Iustin Pop
DISK_OPT = cli_option("--disk", help="Disk parameters", default=[],
586 e3876ccb Iustin Pop
                      dest="disks", action="append", type="identkeyval")
587 91e0748c Iustin Pop
588 4b038a1e Iustin Pop
DISKIDX_OPT = cli_option("--disks", dest="disks", default=None,
589 4b038a1e Iustin Pop
                         help="Comma-separated list of disks"
590 4b038a1e Iustin Pop
                         " indices to act on (e.g. 0,2) (optional,"
591 4b038a1e Iustin Pop
                         " defaults to all disks)")
592 4b038a1e Iustin Pop
593 ff00c1a7 Iustin Pop
OS_SIZE_OPT = cli_option("-s", "--os-size", dest="sd_size",
594 ff00c1a7 Iustin Pop
                         help="Enforces a single-disk configuration using the"
595 ff00c1a7 Iustin Pop
                         " given disk size, in MiB unless a suffix is used",
596 ff00c1a7 Iustin Pop
                         default=None, type="unit", metavar="<size>")
597 ff00c1a7 Iustin Pop
598 82a786d5 Iustin Pop
IGNORE_CONSIST_OPT = cli_option("--ignore-consistency",
599 82a786d5 Iustin Pop
                                dest="ignore_consistency",
600 82a786d5 Iustin Pop
                                action="store_true", default=False,
601 82a786d5 Iustin Pop
                                help="Ignore the consistency of the disks on"
602 82a786d5 Iustin Pop
                                " the secondary")
603 82a786d5 Iustin Pop
604 f2a0828c Iustin Pop
NONLIVE_OPT = cli_option("--non-live", dest="live",
605 f2a0828c Iustin Pop
                         default=True, action="store_false",
606 f2a0828c Iustin Pop
                         help="Do a non-live migration (this usually means"
607 f2a0828c Iustin Pop
                         " freeze the instance, save the state, transfer and"
608 f2a0828c Iustin Pop
                         " only then resume running on the secondary node)")
609 f2a0828c Iustin Pop
610 990b7886 Iustin Pop
NODE_PLACEMENT_OPT = cli_option("-n", "--node", dest="node",
611 990b7886 Iustin Pop
                                help="Target node and optional secondary node",
612 990b7886 Iustin Pop
                                metavar="<pnode>[:<snode>]",
613 990b7886 Iustin Pop
                                completion_suggest=OPT_COMPL_INST_ADD_NODES)
614 990b7886 Iustin Pop
615 7edc4637 Iustin Pop
NODE_LIST_OPT = cli_option("-n", "--node", dest="nodes", default=[],
616 7edc4637 Iustin Pop
                           action="append", metavar="<node>",
617 7edc4637 Iustin Pop
                           help="Use only this node (can be used multiple"
618 7edc4637 Iustin Pop
                           " times, if not given defaults to all nodes)",
619 7edc4637 Iustin Pop
                           completion_suggest=OPT_COMPL_ONE_NODE)
620 f36d7d81 Iustin Pop
621 f36d7d81 Iustin Pop
SINGLE_NODE_OPT = cli_option("-n", "--node", dest="node", help="Target node",
622 f36d7d81 Iustin Pop
                             metavar="<node>",
623 f36d7d81 Iustin Pop
                             completion_suggest=OPT_COMPL_ONE_NODE)
624 7edc4637 Iustin Pop
625 edeb878a Iustin Pop
NOSTART_OPT = cli_option("--no-start", dest="start", default=True,
626 edeb878a Iustin Pop
                         action="store_false",
627 edeb878a Iustin Pop
                         help="Don't start the instance after creation")
628 edeb878a Iustin Pop
629 fdcf4d84 Iustin Pop
SHOWCMD_OPT = cli_option("--show-cmd", dest="show_command",
630 fdcf4d84 Iustin Pop
                         action="store_true", default=False,
631 fdcf4d84 Iustin Pop
                         help="Show command instead of executing it")
632 fdcf4d84 Iustin Pop
633 baef337d Iustin Pop
CLEANUP_OPT = cli_option("--cleanup", dest="cleanup",
634 baef337d Iustin Pop
                         default=False, action="store_true",
635 baef337d Iustin Pop
                         help="Instead of performing the migration, try to"
636 baef337d Iustin Pop
                         " recover from a failed cleanup. This is safe"
637 baef337d Iustin Pop
                         " to run even if the instance is healthy, but it"
638 baef337d Iustin Pop
                         " will create extra replication traffic and "
639 baef337d Iustin Pop
                         " disrupt briefly the replication (like during the"
640 baef337d Iustin Pop
                         " migration")
641 baef337d Iustin Pop
642 99a8c799 Iustin Pop
STATIC_OPT = cli_option("-s", "--static", dest="static",
643 99a8c799 Iustin Pop
                        action="store_true", default=False,
644 99a8c799 Iustin Pop
                        help="Only show configuration data, not runtime data")
645 99a8c799 Iustin Pop
646 2d5e7ae1 Iustin Pop
ALL_OPT = cli_option("--all", dest="show_all",
647 2d5e7ae1 Iustin Pop
                     default=False, action="store_true",
648 2d5e7ae1 Iustin Pop
                     help="Show info on all instances on the cluster."
649 2d5e7ae1 Iustin Pop
                     " This can take a long time to run, use wisely")
650 2d5e7ae1 Iustin Pop
651 67840b40 Iustin Pop
SELECT_OS_OPT = cli_option("--select-os", dest="select_os",
652 67840b40 Iustin Pop
                           action="store_true", default=False,
653 67840b40 Iustin Pop
                           help="Interactive OS reinstall, lists available"
654 67840b40 Iustin Pop
                           " OS templates for selection")
655 2d5e7ae1 Iustin Pop
656 b6e841a8 Iustin Pop
IGNORE_FAILURES_OPT = cli_option("--ignore-failures", dest="ignore_failures",
657 b6e841a8 Iustin Pop
                                 action="store_true", default=False,
658 b6e841a8 Iustin Pop
                                 help="Remove the instance from the cluster"
659 b6e841a8 Iustin Pop
                                 " configuration even if there are failures"
660 b6e841a8 Iustin Pop
                                 " during the removal process")
661 b6e841a8 Iustin Pop
662 a14db5ff Iustin Pop
NEW_SECONDARY_OPT = cli_option("-n", "--new-secondary", dest="dst_node",
663 a14db5ff Iustin Pop
                               help="Specifies the new secondary node",
664 a14db5ff Iustin Pop
                               metavar="NODE", default=None,
665 a14db5ff Iustin Pop
                               completion_suggest=OPT_COMPL_ONE_NODE)
666 a14db5ff Iustin Pop
667 a72d0a79 Iustin Pop
ON_PRIMARY_OPT = cli_option("-p", "--on-primary", dest="on_primary",
668 a72d0a79 Iustin Pop
                            default=False, action="store_true",
669 a72d0a79 Iustin Pop
                            help="Replace the disk(s) on the primary"
670 a72d0a79 Iustin Pop
                            " node (only for the drbd template)")
671 feb09e6a Iustin Pop
672 feb09e6a Iustin Pop
ON_SECONDARY_OPT = cli_option("-s", "--on-secondary", dest="on_secondary",
673 feb09e6a Iustin Pop
                              default=False, action="store_true",
674 feb09e6a Iustin Pop
                              help="Replace the disk(s) on the secondary"
675 feb09e6a Iustin Pop
                              " node (only for the drbd template)")
676 e00f7a05 Iustin Pop
677 e00f7a05 Iustin Pop
AUTO_REPLACE_OPT = cli_option("-a", "--auto", dest="auto",
678 e00f7a05 Iustin Pop
                              default=False, action="store_true",
679 e00f7a05 Iustin Pop
                              help="Automatically replace faulty disks"
680 e00f7a05 Iustin Pop
                              " (only for the drbd template)")
681 a72d0a79 Iustin Pop
682 05586c90 Iustin Pop
IGNORE_SIZE_OPT = cli_option("--ignore-size", dest="ignore_size",
683 05586c90 Iustin Pop
                             default=False, action="store_true",
684 05586c90 Iustin Pop
                             help="Ignore current recorded size"
685 05586c90 Iustin Pop
                             " (useful for forcing activation when"
686 05586c90 Iustin Pop
                             " the recorded size is wrong)")
687 05586c90 Iustin Pop
688 df62e5db Iustin Pop
SRC_NODE_OPT = cli_option("--src-node", dest="src_node", help="Source node",
689 df62e5db Iustin Pop
                          metavar="<node>",
690 df62e5db Iustin Pop
                          completion_suggest=OPT_COMPL_ONE_NODE)
691 df62e5db Iustin Pop
692 df62e5db Iustin Pop
SRC_DIR_OPT = cli_option("--src-dir", dest="src_dir", help="Source directory",
693 df62e5db Iustin Pop
                         metavar="<dir>")
694 df62e5db Iustin Pop
695 8d823629 Iustin Pop
SECONDARY_IP_OPT = cli_option("-s", "--secondary-ip", dest="secondary_ip",
696 8d823629 Iustin Pop
                              help="Specify the secondary ip for the node",
697 8d823629 Iustin Pop
                              metavar="ADDRESS", default=None)
698 8d823629 Iustin Pop
699 b8d0f938 Iustin Pop
READD_OPT = cli_option("--readd", dest="readd",
700 b8d0f938 Iustin Pop
                       default=False, action="store_true",
701 b8d0f938 Iustin Pop
                       help="Readd old node after replacing it")
702 b8d0f938 Iustin Pop
703 fcdde7f2 Iustin Pop
NOSSH_KEYCHECK_OPT = cli_option("--no-ssh-key-check", dest="ssh_key_check",
704 fcdde7f2 Iustin Pop
                                default=True, action="store_false",
705 fcdde7f2 Iustin Pop
                                help="Disable SSH key fingerprint checking")
706 fcdde7f2 Iustin Pop
707 c38c44ad Michael Hanselmann
708 771734c9 Iustin Pop
MC_OPT = cli_option("-C", "--master-candidate", dest="master_candidate",
709 771734c9 Iustin Pop
                    choices=_YESNO, default=None, metavar=_YORNO,
710 771734c9 Iustin Pop
                    help="Set the master_candidate flag on the node")
711 771734c9 Iustin Pop
712 771734c9 Iustin Pop
OFFLINE_OPT = cli_option("-O", "--offline", dest="offline", metavar=_YORNO,
713 771734c9 Iustin Pop
                         choices=_YESNO, default=None,
714 771734c9 Iustin Pop
                         help="Set the offline flag on the node")
715 771734c9 Iustin Pop
716 771734c9 Iustin Pop
DRAINED_OPT = cli_option("-D", "--drained", dest="drained", metavar=_YORNO,
717 771734c9 Iustin Pop
                         choices=_YESNO, default=None,
718 771734c9 Iustin Pop
                         help="Set the drained flag on the node")
719 771734c9 Iustin Pop
720 e7e09483 Iustin Pop
ALLOCATABLE_OPT = cli_option("--allocatable", dest="allocatable",
721 e7e09483 Iustin Pop
                             choices=_YESNO, default=None, metavar=_YORNO,
722 e7e09483 Iustin Pop
                             help="Set the allocatable flag on a volume")
723 e7e09483 Iustin Pop
724 831040bf Iustin Pop
NOLVM_STORAGE_OPT = cli_option("--no-lvm-storage", dest="lvm_storage",
725 831040bf Iustin Pop
                               help="Disable support for lvm based instances"
726 831040bf Iustin Pop
                               " (cluster-wide)",
727 831040bf Iustin Pop
                               action="store_false", default=True)
728 831040bf Iustin Pop
729 383a3591 Iustin Pop
ENABLED_HV_OPT = cli_option("--enabled-hypervisors",
730 383a3591 Iustin Pop
                            dest="enabled_hypervisors",
731 383a3591 Iustin Pop
                            help="Comma-separated list of hypervisors",
732 383a3591 Iustin Pop
                            type="string", default=None)
733 383a3591 Iustin Pop
734 4fbc93dd Iustin Pop
NIC_PARAMS_OPT = cli_option("-N", "--nic-parameters", dest="nicparams",
735 4fbc93dd Iustin Pop
                            type="keyval", default={},
736 4fbc93dd Iustin Pop
                            help="NIC parameters")
737 4fbc93dd Iustin Pop
738 e32df528 Iustin Pop
CP_SIZE_OPT = cli_option("-C", "--candidate-pool-size", default=None,
739 e32df528 Iustin Pop
                         dest="candidate_pool_size", type="int",
740 e32df528 Iustin Pop
                         help="Set the candidate pool size")
741 e32df528 Iustin Pop
742 b58726e8 Iustin Pop
VG_NAME_OPT = cli_option("-g", "--vg-name", dest="vg_name",
743 b58726e8 Iustin Pop
                         help="Enables LVM and specifies the volume group"
744 b58726e8 Iustin Pop
                         " name (cluster-wide) for disk allocation [xenvg]",
745 b58726e8 Iustin Pop
                         metavar="VG", default=None)
746 b58726e8 Iustin Pop
747 1f587d3d Iustin Pop
YES_DOIT_OPT = cli_option("--yes-do-it", dest="yes_do_it",
748 1f587d3d Iustin Pop
                          help="Destroy cluster", action="store_true")
749 b58726e8 Iustin Pop
750 771734c9 Iustin Pop
751 de47cf8f Guido Trotter
def _ParseArgs(argv, commands, aliases):
752 c41eea6e Iustin Pop
  """Parser for the command line arguments.
753 a8083063 Iustin Pop

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

757 c41eea6e Iustin Pop
  @param argv: the command line
758 c41eea6e Iustin Pop
  @param commands: dictionary with special contents, see the design
759 c41eea6e Iustin Pop
      doc for cmdline handling
760 c41eea6e Iustin Pop
  @param aliases: dictionary with command aliases {'alias': 'target, ...}
761 098c0958 Michael Hanselmann

762 a8083063 Iustin Pop
  """
763 a8083063 Iustin Pop
  if len(argv) == 0:
764 a8083063 Iustin Pop
    binary = "<command>"
765 a8083063 Iustin Pop
  else:
766 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
767 a8083063 Iustin Pop
768 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
769 03298ebe Michael Hanselmann
    ToStdout("%s (ganeti) %s", binary, constants.RELEASE_VERSION)
770 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
771 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
772 a8083063 Iustin Pop
    sys.exit(0)
773 a8083063 Iustin Pop
774 de47cf8f Guido Trotter
  if len(argv) < 2 or not (argv[1] in commands or
775 70a35b6f Guido Trotter
                           argv[1] in aliases):
776 a8083063 Iustin Pop
    # let's do a nice thing
777 a8083063 Iustin Pop
    sortedcmds = commands.keys()
778 a8083063 Iustin Pop
    sortedcmds.sort()
779 03298ebe Michael Hanselmann
780 03298ebe Michael Hanselmann
    ToStdout("Usage: %s {command} [options...] [argument...]", binary)
781 03298ebe Michael Hanselmann
    ToStdout("%s <command> --help to see details, or man %s", binary, binary)
782 03298ebe Michael Hanselmann
    ToStdout("")
783 03298ebe Michael Hanselmann
784 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
785 4e713df6 Iustin Pop
    mlen = max([len(" %s" % cmd) for cmd in commands])
786 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
787 03298ebe Michael Hanselmann
788 a8083063 Iustin Pop
    # and format a nice command list
789 03298ebe Michael Hanselmann
    ToStdout("Commands:")
790 a8083063 Iustin Pop
    for cmd in sortedcmds:
791 4e713df6 Iustin Pop
      cmdstr = " %s" % (cmd,)
792 9a033156 Iustin Pop
      help_text = commands[cmd][4]
793 03298ebe Michael Hanselmann
      help_lines = textwrap.wrap(help_text, 79 - 3 - mlen)
794 03298ebe Michael Hanselmann
      ToStdout("%-*s - %s", mlen, cmdstr, help_lines.pop(0))
795 a8083063 Iustin Pop
      for line in help_lines:
796 03298ebe Michael Hanselmann
        ToStdout("%-*s   %s", mlen, "", line)
797 03298ebe Michael Hanselmann
798 03298ebe Michael Hanselmann
    ToStdout("")
799 03298ebe Michael Hanselmann
800 a8083063 Iustin Pop
    return None, None, None
801 de47cf8f Guido Trotter
802 de47cf8f Guido Trotter
  # get command, unalias it, and look it up in commands
803 a8083063 Iustin Pop
  cmd = argv.pop(1)
804 de47cf8f Guido Trotter
  if cmd in aliases:
805 de47cf8f Guido Trotter
    if cmd in commands:
806 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' overrides an existing"
807 de47cf8f Guido Trotter
                                   " command" % cmd)
808 de47cf8f Guido Trotter
809 de47cf8f Guido Trotter
    if aliases[cmd] not in commands:
810 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' maps to non-existing"
811 de47cf8f Guido Trotter
                                   " command '%s'" % (cmd, aliases[cmd]))
812 de47cf8f Guido Trotter
813 de47cf8f Guido Trotter
    cmd = aliases[cmd]
814 de47cf8f Guido Trotter
815 a8005e17 Michael Hanselmann
  func, args_def, parser_opts, usage, description = commands[cmd]
816 64c65a2a Iustin Pop
  parser = OptionParser(option_list=parser_opts + [_DRY_RUN_OPT],
817 a8083063 Iustin Pop
                        description=description,
818 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
819 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
820 a8083063 Iustin Pop
  parser.disable_interspersed_args()
821 a8083063 Iustin Pop
  options, args = parser.parse_args()
822 a8005e17 Michael Hanselmann
823 a8005e17 Michael Hanselmann
  if not _CheckArguments(cmd, args_def, args):
824 a8083063 Iustin Pop
    return None, None, None
825 a8083063 Iustin Pop
826 a8083063 Iustin Pop
  return func, options, args
827 a8083063 Iustin Pop
828 a8083063 Iustin Pop
829 a8005e17 Michael Hanselmann
def _CheckArguments(cmd, args_def, args):
830 a8005e17 Michael Hanselmann
  """Verifies the arguments using the argument definition.
831 a8005e17 Michael Hanselmann

832 a8005e17 Michael Hanselmann
  Algorithm:
833 a8005e17 Michael Hanselmann

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

836 a8005e17 Michael Hanselmann
    1. For each argument in definition
837 a8005e17 Michael Hanselmann

838 a8005e17 Michael Hanselmann
      1. Keep running count of minimum number of values (min_count)
839 a8005e17 Michael Hanselmann
      1. Keep running count of maximum number of values (max_count)
840 a8005e17 Michael Hanselmann
      1. If it has an unlimited number of values
841 a8005e17 Michael Hanselmann

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

844 a8005e17 Michael Hanselmann
    1. If last argument has limited number of values
845 a8005e17 Michael Hanselmann

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

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

850 a8005e17 Michael Hanselmann
  """
851 a8005e17 Michael Hanselmann
  if args and not args_def:
852 a8005e17 Michael Hanselmann
    ToStderr("Error: Command %s expects no arguments", cmd)
853 a8005e17 Michael Hanselmann
    return False
854 a8005e17 Michael Hanselmann
855 a8005e17 Michael Hanselmann
  min_count = None
856 a8005e17 Michael Hanselmann
  max_count = None
857 a8005e17 Michael Hanselmann
  check_max = None
858 a8005e17 Michael Hanselmann
859 a8005e17 Michael Hanselmann
  last_idx = len(args_def) - 1
860 a8005e17 Michael Hanselmann
861 a8005e17 Michael Hanselmann
  for idx, arg in enumerate(args_def):
862 a8005e17 Michael Hanselmann
    if min_count is None:
863 a8005e17 Michael Hanselmann
      min_count = arg.min
864 a8005e17 Michael Hanselmann
    elif arg.min is not None:
865 a8005e17 Michael Hanselmann
      min_count += arg.min
866 a8005e17 Michael Hanselmann
867 a8005e17 Michael Hanselmann
    if max_count is None:
868 a8005e17 Michael Hanselmann
      max_count = arg.max
869 a8005e17 Michael Hanselmann
    elif arg.max is not None:
870 a8005e17 Michael Hanselmann
      max_count += arg.max
871 a8005e17 Michael Hanselmann
872 a8005e17 Michael Hanselmann
    if idx == last_idx:
873 a8005e17 Michael Hanselmann
      check_max = (arg.max is not None)
874 a8005e17 Michael Hanselmann
875 a8005e17 Michael Hanselmann
    elif arg.max is None:
876 a8005e17 Michael Hanselmann
      raise errors.ProgrammerError("Only the last argument can have max=None")
877 a8005e17 Michael Hanselmann
878 a8005e17 Michael Hanselmann
  if check_max:
879 a8005e17 Michael Hanselmann
    # Command with exact number of arguments
880 a8005e17 Michael Hanselmann
    if (min_count is not None and max_count is not None and
881 a8005e17 Michael Hanselmann
        min_count == max_count and len(args) != min_count):
882 a8005e17 Michael Hanselmann
      ToStderr("Error: Command %s expects %d argument(s)", cmd, min_count)
883 a8005e17 Michael Hanselmann
      return False
884 a8005e17 Michael Hanselmann
885 a8005e17 Michael Hanselmann
    # Command with limited number of arguments
886 a8005e17 Michael Hanselmann
    if max_count is not None and len(args) > max_count:
887 a8005e17 Michael Hanselmann
      ToStderr("Error: Command %s expects only %d argument(s)",
888 a8005e17 Michael Hanselmann
               cmd, max_count)
889 a8005e17 Michael Hanselmann
      return False
890 a8005e17 Michael Hanselmann
891 a8005e17 Michael Hanselmann
  # Command with some required arguments
892 a8005e17 Michael Hanselmann
  if min_count is not None and len(args) < min_count:
893 a8005e17 Michael Hanselmann
    ToStderr("Error: Command %s expects at least %d argument(s)",
894 a8005e17 Michael Hanselmann
             cmd, min_count)
895 a8005e17 Michael Hanselmann
    return False
896 a8005e17 Michael Hanselmann
897 a8005e17 Michael Hanselmann
  return True
898 a8005e17 Michael Hanselmann
899 a8005e17 Michael Hanselmann
900 60d49723 Michael Hanselmann
def SplitNodeOption(value):
901 60d49723 Michael Hanselmann
  """Splits the value of a --node option.
902 60d49723 Michael Hanselmann

903 60d49723 Michael Hanselmann
  """
904 60d49723 Michael Hanselmann
  if value and ':' in value:
905 60d49723 Michael Hanselmann
    return value.split(':', 1)
906 60d49723 Michael Hanselmann
  else:
907 60d49723 Michael Hanselmann
    return (value, None)
908 60d49723 Michael Hanselmann
909 60d49723 Michael Hanselmann
910 4331f6cd Michael Hanselmann
def UsesRPC(fn):
911 4331f6cd Michael Hanselmann
  def wrapper(*args, **kwargs):
912 4331f6cd Michael Hanselmann
    rpc.Init()
913 4331f6cd Michael Hanselmann
    try:
914 4331f6cd Michael Hanselmann
      return fn(*args, **kwargs)
915 4331f6cd Michael Hanselmann
    finally:
916 4331f6cd Michael Hanselmann
      rpc.Shutdown()
917 4331f6cd Michael Hanselmann
  return wrapper
918 4331f6cd Michael Hanselmann
919 4331f6cd Michael Hanselmann
920 47988778 Iustin Pop
def AskUser(text, choices=None):
921 47988778 Iustin Pop
  """Ask the user a question.
922 a8083063 Iustin Pop

923 c41eea6e Iustin Pop
  @param text: the question to ask
924 a8083063 Iustin Pop

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

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

934 a8083063 Iustin Pop
  """
935 47988778 Iustin Pop
  if choices is None:
936 47988778 Iustin Pop
    choices = [('y', True, 'Perform the operation'),
937 47988778 Iustin Pop
               ('n', False, 'Do not perform the operation')]
938 47988778 Iustin Pop
  if not choices or not isinstance(choices, list):
939 5bbd3f7f Michael Hanselmann
    raise errors.ProgrammerError("Invalid choices argument to AskUser")
940 47988778 Iustin Pop
  for entry in choices:
941 47988778 Iustin Pop
    if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
942 5bbd3f7f Michael Hanselmann
      raise errors.ProgrammerError("Invalid choices element to AskUser")
943 47988778 Iustin Pop
944 47988778 Iustin Pop
  answer = choices[-1][1]
945 47988778 Iustin Pop
  new_text = []
946 47988778 Iustin Pop
  for line in text.splitlines():
947 47988778 Iustin Pop
    new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
948 47988778 Iustin Pop
  text = "\n".join(new_text)
949 a8083063 Iustin Pop
  try:
950 3023170f Iustin Pop
    f = file("/dev/tty", "a+")
951 a8083063 Iustin Pop
  except IOError:
952 47988778 Iustin Pop
    return answer
953 a8083063 Iustin Pop
  try:
954 47988778 Iustin Pop
    chars = [entry[0] for entry in choices]
955 47988778 Iustin Pop
    chars[-1] = "[%s]" % chars[-1]
956 47988778 Iustin Pop
    chars.append('?')
957 47988778 Iustin Pop
    maps = dict([(entry[0], entry[1]) for entry in choices])
958 47988778 Iustin Pop
    while True:
959 47988778 Iustin Pop
      f.write(text)
960 47988778 Iustin Pop
      f.write('\n')
961 47988778 Iustin Pop
      f.write("/".join(chars))
962 47988778 Iustin Pop
      f.write(": ")
963 47988778 Iustin Pop
      line = f.readline(2).strip().lower()
964 47988778 Iustin Pop
      if line in maps:
965 47988778 Iustin Pop
        answer = maps[line]
966 47988778 Iustin Pop
        break
967 47988778 Iustin Pop
      elif line == '?':
968 47988778 Iustin Pop
        for entry in choices:
969 47988778 Iustin Pop
          f.write(" %s - %s\n" % (entry[0], entry[2]))
970 47988778 Iustin Pop
        f.write("\n")
971 47988778 Iustin Pop
        continue
972 a8083063 Iustin Pop
  finally:
973 a8083063 Iustin Pop
    f.close()
974 a8083063 Iustin Pop
  return answer
975 a8083063 Iustin Pop
976 a8083063 Iustin Pop
977 e9d741b6 Iustin Pop
class JobSubmittedException(Exception):
978 e9d741b6 Iustin Pop
  """Job was submitted, client should exit.
979 e9d741b6 Iustin Pop

980 e9d741b6 Iustin Pop
  This exception has one argument, the ID of the job that was
981 e9d741b6 Iustin Pop
  submitted. The handler should print this ID.
982 e9d741b6 Iustin Pop

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

985 e9d741b6 Iustin Pop
  """
986 e9d741b6 Iustin Pop
987 e9d741b6 Iustin Pop
988 0a1e74d9 Iustin Pop
def SendJob(ops, cl=None):
989 0a1e74d9 Iustin Pop
  """Function to submit an opcode without waiting for the results.
990 a8083063 Iustin Pop

991 0a1e74d9 Iustin Pop
  @type ops: list
992 0a1e74d9 Iustin Pop
  @param ops: list of opcodes
993 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
994 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
995 0a1e74d9 Iustin Pop
             if None, a new client will be created
996 a8083063 Iustin Pop

997 a8083063 Iustin Pop
  """
998 e2212007 Iustin Pop
  if cl is None:
999 b33e986b Iustin Pop
    cl = GetClient()
1000 685ee993 Iustin Pop
1001 0a1e74d9 Iustin Pop
  job_id = cl.SubmitJob(ops)
1002 0a1e74d9 Iustin Pop
1003 0a1e74d9 Iustin Pop
  return job_id
1004 0a1e74d9 Iustin Pop
1005 0a1e74d9 Iustin Pop
1006 281606c1 Michael Hanselmann
def PollJob(job_id, cl=None, feedback_fn=None):
1007 0a1e74d9 Iustin Pop
  """Function to poll for the result of a job.
1008 0a1e74d9 Iustin Pop

1009 0a1e74d9 Iustin Pop
  @type job_id: job identified
1010 0a1e74d9 Iustin Pop
  @param job_id: the job to poll for results
1011 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
1012 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
1013 0a1e74d9 Iustin Pop
             if None, a new client will be created
1014 0a1e74d9 Iustin Pop

1015 0a1e74d9 Iustin Pop
  """
1016 0a1e74d9 Iustin Pop
  if cl is None:
1017 0a1e74d9 Iustin Pop
    cl = GetClient()
1018 685ee993 Iustin Pop
1019 6c5a7090 Michael Hanselmann
  prev_job_info = None
1020 6c5a7090 Michael Hanselmann
  prev_logmsg_serial = None
1021 6c5a7090 Michael Hanselmann
1022 685ee993 Iustin Pop
  while True:
1023 6c5a7090 Michael Hanselmann
    result = cl.WaitForJobChange(job_id, ["status"], prev_job_info,
1024 6c5a7090 Michael Hanselmann
                                 prev_logmsg_serial)
1025 6c5a7090 Michael Hanselmann
    if not result:
1026 685ee993 Iustin Pop
      # job not found, go away!
1027 0bbe448c Michael Hanselmann
      raise errors.JobLost("Job with id %s lost" % job_id)
1028 685ee993 Iustin Pop
1029 6c5a7090 Michael Hanselmann
    # Split result, a tuple of (field values, log entries)
1030 6c5a7090 Michael Hanselmann
    (job_info, log_entries) = result
1031 6c5a7090 Michael Hanselmann
    (status, ) = job_info
1032 6c5a7090 Michael Hanselmann
1033 6c5a7090 Michael Hanselmann
    if log_entries:
1034 6c5a7090 Michael Hanselmann
      for log_entry in log_entries:
1035 6c5a7090 Michael Hanselmann
        (serial, timestamp, _, message) = log_entry
1036 6c5a7090 Michael Hanselmann
        if callable(feedback_fn):
1037 6c5a7090 Michael Hanselmann
          feedback_fn(log_entry[1:])
1038 6c5a7090 Michael Hanselmann
        else:
1039 26f15862 Iustin Pop
          encoded = utils.SafeEncode(message)
1040 03298ebe Michael Hanselmann
          ToStdout("%s %s", time.ctime(utils.MergeTime(timestamp)), encoded)
1041 6c5a7090 Michael Hanselmann
        prev_logmsg_serial = max(prev_logmsg_serial, serial)
1042 6c5a7090 Michael Hanselmann
1043 0bbe448c Michael Hanselmann
    # TODO: Handle canceled and archived jobs
1044 fbf0262f Michael Hanselmann
    elif status in (constants.JOB_STATUS_SUCCESS,
1045 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_ERROR,
1046 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELING,
1047 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELED):
1048 685ee993 Iustin Pop
      break
1049 6c5a7090 Michael Hanselmann
1050 6c5a7090 Michael Hanselmann
    prev_job_info = job_info
1051 685ee993 Iustin Pop
1052 0e050889 Iustin Pop
  jobs = cl.QueryJobs([job_id], ["status", "opstatus", "opresult"])
1053 0bbe448c Michael Hanselmann
  if not jobs:
1054 0bbe448c Michael Hanselmann
    raise errors.JobLost("Job with id %s lost" % job_id)
1055 685ee993 Iustin Pop
1056 0e050889 Iustin Pop
  status, opstatus, result = jobs[0]
1057 0bbe448c Michael Hanselmann
  if status == constants.JOB_STATUS_SUCCESS:
1058 53c04d04 Iustin Pop
    return result
1059 fbf0262f Michael Hanselmann
  elif status in (constants.JOB_STATUS_CANCELING,
1060 fbf0262f Michael Hanselmann
                  constants.JOB_STATUS_CANCELED):
1061 fbf0262f Michael Hanselmann
    raise errors.OpExecError("Job was canceled")
1062 0bbe448c Michael Hanselmann
  else:
1063 0e050889 Iustin Pop
    has_ok = False
1064 0e050889 Iustin Pop
    for idx, (status, msg) in enumerate(zip(opstatus, result)):
1065 0e050889 Iustin Pop
      if status == constants.OP_STATUS_SUCCESS:
1066 0e050889 Iustin Pop
        has_ok = True
1067 0e050889 Iustin Pop
      elif status == constants.OP_STATUS_ERROR:
1068 bcb66fca Iustin Pop
        errors.MaybeRaise(msg)
1069 0e050889 Iustin Pop
        if has_ok:
1070 0e050889 Iustin Pop
          raise errors.OpExecError("partial failure (opcode %d): %s" %
1071 0e050889 Iustin Pop
                                   (idx, msg))
1072 0e050889 Iustin Pop
        else:
1073 0e050889 Iustin Pop
          raise errors.OpExecError(str(msg))
1074 0e050889 Iustin Pop
    # default failure mode
1075 0bbe448c Michael Hanselmann
    raise errors.OpExecError(result)
1076 ceab32dd Iustin Pop
1077 ceab32dd Iustin Pop
1078 0a1e74d9 Iustin Pop
def SubmitOpCode(op, cl=None, feedback_fn=None):
1079 0a1e74d9 Iustin Pop
  """Legacy function to submit an opcode.
1080 0a1e74d9 Iustin Pop

1081 0a1e74d9 Iustin Pop
  This is just a simple wrapper over the construction of the processor
1082 0a1e74d9 Iustin Pop
  instance. It should be extended to better handle feedback and
1083 0a1e74d9 Iustin Pop
  interaction functions.
1084 0a1e74d9 Iustin Pop

1085 0a1e74d9 Iustin Pop
  """
1086 0a1e74d9 Iustin Pop
  if cl is None:
1087 0a1e74d9 Iustin Pop
    cl = GetClient()
1088 0a1e74d9 Iustin Pop
1089 0a1e74d9 Iustin Pop
  job_id = SendJob([op], cl)
1090 0a1e74d9 Iustin Pop
1091 53c04d04 Iustin Pop
  op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn)
1092 53c04d04 Iustin Pop
1093 53c04d04 Iustin Pop
  return op_results[0]
1094 0a1e74d9 Iustin Pop
1095 0a1e74d9 Iustin Pop
1096 94428652 Iustin Pop
def SubmitOrSend(op, opts, cl=None, feedback_fn=None):
1097 94428652 Iustin Pop
  """Wrapper around SubmitOpCode or SendJob.
1098 94428652 Iustin Pop

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

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

1106 94428652 Iustin Pop
  """
1107 64c65a2a Iustin Pop
  if opts and opts.dry_run:
1108 64c65a2a Iustin Pop
    op.dry_run = opts.dry_run
1109 94428652 Iustin Pop
  if opts and opts.submit_only:
1110 e9d741b6 Iustin Pop
    job_id = SendJob([op], cl=cl)
1111 e9d741b6 Iustin Pop
    raise JobSubmittedException(job_id)
1112 94428652 Iustin Pop
  else:
1113 94428652 Iustin Pop
    return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn)
1114 94428652 Iustin Pop
1115 94428652 Iustin Pop
1116 af30b2fd Michael Hanselmann
def GetClient():
1117 af30b2fd Michael Hanselmann
  # TODO: Cache object?
1118 b33e986b Iustin Pop
  try:
1119 b33e986b Iustin Pop
    client = luxi.Client()
1120 b33e986b Iustin Pop
  except luxi.NoMasterError:
1121 b33e986b Iustin Pop
    master, myself = ssconf.GetMasterAndMyself()
1122 b33e986b Iustin Pop
    if master != myself:
1123 b33e986b Iustin Pop
      raise errors.OpPrereqError("This is not the master node, please connect"
1124 b33e986b Iustin Pop
                                 " to node '%s' and rerun the command" %
1125 b33e986b Iustin Pop
                                 master)
1126 b33e986b Iustin Pop
    else:
1127 b33e986b Iustin Pop
      raise
1128 b33e986b Iustin Pop
  return client
1129 af30b2fd Michael Hanselmann
1130 af30b2fd Michael Hanselmann
1131 73702ee7 Iustin Pop
def FormatError(err):
1132 73702ee7 Iustin Pop
  """Return a formatted error message for a given error.
1133 73702ee7 Iustin Pop

1134 73702ee7 Iustin Pop
  This function takes an exception instance and returns a tuple
1135 73702ee7 Iustin Pop
  consisting of two values: first, the recommended exit code, and
1136 73702ee7 Iustin Pop
  second, a string describing the error message (not
1137 73702ee7 Iustin Pop
  newline-terminated).
1138 73702ee7 Iustin Pop

1139 73702ee7 Iustin Pop
  """
1140 73702ee7 Iustin Pop
  retcode = 1
1141 73702ee7 Iustin Pop
  obuf = StringIO()
1142 e2e521d0 Iustin Pop
  msg = str(err)
1143 73702ee7 Iustin Pop
  if isinstance(err, errors.ConfigurationError):
1144 e2e521d0 Iustin Pop
    txt = "Corrupt configuration file: %s" % msg
1145 46fbdd04 Iustin Pop
    logging.error(txt)
1146 e2e521d0 Iustin Pop
    obuf.write(txt + "\n")
1147 73702ee7 Iustin Pop
    obuf.write("Aborting.")
1148 73702ee7 Iustin Pop
    retcode = 2
1149 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksAbort):
1150 73702ee7 Iustin Pop
    obuf.write("Failure: hooks execution failed:\n")
1151 73702ee7 Iustin Pop
    for node, script, out in err.args[0]:
1152 73702ee7 Iustin Pop
      if out:
1153 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s, output: %s\n" %
1154 73702ee7 Iustin Pop
                   (node, script, out))
1155 73702ee7 Iustin Pop
      else:
1156 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s (no output)\n" %
1157 73702ee7 Iustin Pop
                   (node, script))
1158 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksFailure):
1159 e2e521d0 Iustin Pop
    obuf.write("Failure: hooks general failure: %s" % msg)
1160 73702ee7 Iustin Pop
  elif isinstance(err, errors.ResolverError):
1161 73702ee7 Iustin Pop
    this_host = utils.HostInfo.SysName()
1162 73702ee7 Iustin Pop
    if err.args[0] == this_host:
1163 73702ee7 Iustin Pop
      msg = "Failure: can't resolve my own hostname ('%s')"
1164 73702ee7 Iustin Pop
    else:
1165 73702ee7 Iustin Pop
      msg = "Failure: can't resolve hostname '%s'"
1166 73702ee7 Iustin Pop
    obuf.write(msg % err.args[0])
1167 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpPrereqError):
1168 73702ee7 Iustin Pop
    obuf.write("Failure: prerequisites not met for this"
1169 e2e521d0 Iustin Pop
               " operation:\n%s" % msg)
1170 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpExecError):
1171 e2e521d0 Iustin Pop
    obuf.write("Failure: command execution error:\n%s" % msg)
1172 73702ee7 Iustin Pop
  elif isinstance(err, errors.TagError):
1173 e2e521d0 Iustin Pop
    obuf.write("Failure: invalid tag(s) given:\n%s" % msg)
1174 686d7433 Iustin Pop
  elif isinstance(err, errors.JobQueueDrainError):
1175 686d7433 Iustin Pop
    obuf.write("Failure: the job queue is marked for drain and doesn't"
1176 686d7433 Iustin Pop
               " accept new requests\n")
1177 f87b405e Michael Hanselmann
  elif isinstance(err, errors.JobQueueFull):
1178 f87b405e Michael Hanselmann
    obuf.write("Failure: the job queue is full and doesn't accept new"
1179 f87b405e Michael Hanselmann
               " job submissions until old jobs are archived\n")
1180 a5728081 Guido Trotter
  elif isinstance(err, errors.TypeEnforcementError):
1181 a5728081 Guido Trotter
    obuf.write("Parameter Error: %s" % msg)
1182 c1ce76bb Iustin Pop
  elif isinstance(err, errors.ParameterError):
1183 c1ce76bb Iustin Pop
    obuf.write("Failure: unknown/wrong parameter name '%s'" % msg)
1184 73702ee7 Iustin Pop
  elif isinstance(err, errors.GenericError):
1185 e2e521d0 Iustin Pop
    obuf.write("Unhandled Ganeti error: %s" % msg)
1186 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.NoMasterError):
1187 03a8dbdc Iustin Pop
    obuf.write("Cannot communicate with the master daemon.\nIs it running"
1188 082c5adb Michael Hanselmann
               " and listening for connections?")
1189 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.TimeoutError):
1190 03a8dbdc Iustin Pop
    obuf.write("Timeout while talking to the master daemon. Error:\n"
1191 03a8dbdc Iustin Pop
               "%s" % msg)
1192 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.ProtocolError):
1193 03a8dbdc Iustin Pop
    obuf.write("Unhandled protocol error while talking to the master daemon:\n"
1194 03a8dbdc Iustin Pop
               "%s" % msg)
1195 e9d741b6 Iustin Pop
  elif isinstance(err, JobSubmittedException):
1196 e9d741b6 Iustin Pop
    obuf.write("JobID: %s\n" % err.args[0])
1197 e9d741b6 Iustin Pop
    retcode = 0
1198 73702ee7 Iustin Pop
  else:
1199 e2e521d0 Iustin Pop
    obuf.write("Unhandled exception: %s" % msg)
1200 73702ee7 Iustin Pop
  return retcode, obuf.getvalue().rstrip('\n')
1201 73702ee7 Iustin Pop
1202 73702ee7 Iustin Pop
1203 de47cf8f Guido Trotter
def GenericMain(commands, override=None, aliases=None):
1204 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
1205 a8083063 Iustin Pop

1206 334d1483 Iustin Pop
  Arguments:
1207 334d1483 Iustin Pop
    - commands: a dictionary with a special structure, see the design doc
1208 334d1483 Iustin Pop
                for command line handling.
1209 334d1483 Iustin Pop
    - override: if not None, we expect a dictionary with keys that will
1210 334d1483 Iustin Pop
                override command line options; this can be used to pass
1211 334d1483 Iustin Pop
                options from the scripts to generic functions
1212 de47cf8f Guido Trotter
    - aliases: dictionary with command aliases {'alias': 'target, ...}
1213 a8083063 Iustin Pop

1214 a8083063 Iustin Pop
  """
1215 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
1216 a8083063 Iustin Pop
  if sys.argv:
1217 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
1218 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
1219 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
1220 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
1221 a8083063 Iustin Pop
    else:
1222 a8083063 Iustin Pop
      old_cmdline = ""
1223 a8083063 Iustin Pop
  else:
1224 a8083063 Iustin Pop
    binary = "<unknown program>"
1225 a8083063 Iustin Pop
    old_cmdline = ""
1226 a8083063 Iustin Pop
1227 de47cf8f Guido Trotter
  if aliases is None:
1228 de47cf8f Guido Trotter
    aliases = {}
1229 de47cf8f Guido Trotter
1230 3126878d Guido Trotter
  try:
1231 3126878d Guido Trotter
    func, options, args = _ParseArgs(sys.argv, commands, aliases)
1232 3126878d Guido Trotter
  except errors.ParameterError, err:
1233 3126878d Guido Trotter
    result, err_msg = FormatError(err)
1234 3126878d Guido Trotter
    ToStderr(err_msg)
1235 3126878d Guido Trotter
    return 1
1236 3126878d Guido Trotter
1237 a8083063 Iustin Pop
  if func is None: # parse error
1238 a8083063 Iustin Pop
    return 1
1239 a8083063 Iustin Pop
1240 334d1483 Iustin Pop
  if override is not None:
1241 334d1483 Iustin Pop
    for key, val in override.iteritems():
1242 334d1483 Iustin Pop
      setattr(options, key, val)
1243 334d1483 Iustin Pop
1244 82d9caef Iustin Pop
  utils.SetupLogging(constants.LOG_COMMANDS, debug=options.debug,
1245 82d9caef Iustin Pop
                     stderr_logging=True, program=binary)
1246 a8083063 Iustin Pop
1247 a8083063 Iustin Pop
  if old_cmdline:
1248 46fbdd04 Iustin Pop
    logging.info("run with arguments '%s'", old_cmdline)
1249 a8083063 Iustin Pop
  else:
1250 46fbdd04 Iustin Pop
    logging.info("run with no arguments")
1251 a8083063 Iustin Pop
1252 a8083063 Iustin Pop
  try:
1253 a4af651e Iustin Pop
    result = func(options, args)
1254 d8353c3a Iustin Pop
  except (errors.GenericError, luxi.ProtocolError,
1255 d8353c3a Iustin Pop
          JobSubmittedException), err:
1256 a4af651e Iustin Pop
    result, err_msg = FormatError(err)
1257 5bbd3f7f Michael Hanselmann
    logging.exception("Error during command processing")
1258 46fbdd04 Iustin Pop
    ToStderr(err_msg)
1259 a8083063 Iustin Pop
1260 a8083063 Iustin Pop
  return result
1261 137161c9 Michael Hanselmann
1262 137161c9 Michael Hanselmann
1263 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
1264 9fbfbb7b Iustin Pop
                  numfields=None, unitfields=None,
1265 9fbfbb7b Iustin Pop
                  units=None):
1266 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
1267 137161c9 Michael Hanselmann

1268 9fbfbb7b Iustin Pop
  @type headers: dict
1269 9fbfbb7b Iustin Pop
  @param headers: dictionary mapping field names to headers for
1270 9fbfbb7b Iustin Pop
      the table
1271 9fbfbb7b Iustin Pop
  @type fields: list
1272 9fbfbb7b Iustin Pop
  @param fields: the field names corresponding to each row in
1273 9fbfbb7b Iustin Pop
      the data field
1274 9fbfbb7b Iustin Pop
  @param separator: the separator to be used; if this is None,
1275 9fbfbb7b Iustin Pop
      the default 'smart' algorithm is used which computes optimal
1276 9fbfbb7b Iustin Pop
      field width, otherwise just the separator is used between
1277 9fbfbb7b Iustin Pop
      each field
1278 9fbfbb7b Iustin Pop
  @type data: list
1279 9fbfbb7b Iustin Pop
  @param data: a list of lists, each sublist being one row to be output
1280 9fbfbb7b Iustin Pop
  @type numfields: list
1281 9fbfbb7b Iustin Pop
  @param numfields: a list with the fields that hold numeric
1282 9fbfbb7b Iustin Pop
      values and thus should be right-aligned
1283 9fbfbb7b Iustin Pop
  @type unitfields: list
1284 9fbfbb7b Iustin Pop
  @param unitfields: a list with the fields that hold numeric
1285 9fbfbb7b Iustin Pop
      values that should be formatted with the units field
1286 9fbfbb7b Iustin Pop
  @type units: string or None
1287 9fbfbb7b Iustin Pop
  @param units: the units we should use for formatting, or None for
1288 9fbfbb7b Iustin Pop
      automatic choice (human-readable for non-separator usage, otherwise
1289 9fbfbb7b Iustin Pop
      megabytes); this is a one-letter string
1290 137161c9 Michael Hanselmann

1291 137161c9 Michael Hanselmann
  """
1292 9fbfbb7b Iustin Pop
  if units is None:
1293 9fbfbb7b Iustin Pop
    if separator:
1294 9fbfbb7b Iustin Pop
      units = "m"
1295 9fbfbb7b Iustin Pop
    else:
1296 9fbfbb7b Iustin Pop
      units = "h"
1297 9fbfbb7b Iustin Pop
1298 137161c9 Michael Hanselmann
  if numfields is None:
1299 137161c9 Michael Hanselmann
    numfields = []
1300 137161c9 Michael Hanselmann
  if unitfields is None:
1301 137161c9 Michael Hanselmann
    unitfields = []
1302 137161c9 Michael Hanselmann
1303 00430f8e Iustin Pop
  numfields = utils.FieldSet(*numfields)
1304 00430f8e Iustin Pop
  unitfields = utils.FieldSet(*unitfields)
1305 00430f8e Iustin Pop
1306 137161c9 Michael Hanselmann
  format_fields = []
1307 137161c9 Michael Hanselmann
  for field in fields:
1308 01ca31ae Iustin Pop
    if headers and field not in headers:
1309 ea5a5b74 Guido Trotter
      # TODO: handle better unknown fields (either revert to old
1310 71c1af58 Iustin Pop
      # style of raising exception, or deal more intelligently with
1311 71c1af58 Iustin Pop
      # variable fields)
1312 71c1af58 Iustin Pop
      headers[field] = field
1313 137161c9 Michael Hanselmann
    if separator is not None:
1314 137161c9 Michael Hanselmann
      format_fields.append("%s")
1315 00430f8e Iustin Pop
    elif numfields.Matches(field):
1316 137161c9 Michael Hanselmann
      format_fields.append("%*s")
1317 137161c9 Michael Hanselmann
    else:
1318 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
1319 137161c9 Michael Hanselmann
1320 137161c9 Michael Hanselmann
  if separator is None:
1321 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
1322 137161c9 Michael Hanselmann
    format = ' '.join(format_fields)
1323 137161c9 Michael Hanselmann
  else:
1324 137161c9 Michael Hanselmann
    format = separator.replace("%", "%%").join(format_fields)
1325 137161c9 Michael Hanselmann
1326 137161c9 Michael Hanselmann
  for row in data:
1327 dcbd6288 Guido Trotter
    if row is None:
1328 dcbd6288 Guido Trotter
      continue
1329 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
1330 00430f8e Iustin Pop
      if unitfields.Matches(fields[idx]):
1331 137161c9 Michael Hanselmann
        try:
1332 137161c9 Michael Hanselmann
          val = int(val)
1333 137161c9 Michael Hanselmann
        except ValueError:
1334 137161c9 Michael Hanselmann
          pass
1335 137161c9 Michael Hanselmann
        else:
1336 9fbfbb7b Iustin Pop
          val = row[idx] = utils.FormatUnit(val, units)
1337 01ca31ae Iustin Pop
      val = row[idx] = str(val)
1338 137161c9 Michael Hanselmann
      if separator is None:
1339 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
1340 137161c9 Michael Hanselmann
1341 16be8703 Iustin Pop
  result = []
1342 137161c9 Michael Hanselmann
  if headers:
1343 137161c9 Michael Hanselmann
    args = []
1344 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
1345 137161c9 Michael Hanselmann
      hdr = headers[name]
1346 137161c9 Michael Hanselmann
      if separator is None:
1347 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
1348 137161c9 Michael Hanselmann
        args.append(mlens[idx])
1349 137161c9 Michael Hanselmann
      args.append(hdr)
1350 16be8703 Iustin Pop
    result.append(format % tuple(args))
1351 137161c9 Michael Hanselmann
1352 137161c9 Michael Hanselmann
  for line in data:
1353 137161c9 Michael Hanselmann
    args = []
1354 dcbd6288 Guido Trotter
    if line is None:
1355 dcbd6288 Guido Trotter
      line = ['-' for _ in fields]
1356 137161c9 Michael Hanselmann
    for idx in xrange(len(fields)):
1357 137161c9 Michael Hanselmann
      if separator is None:
1358 137161c9 Michael Hanselmann
        args.append(mlens[idx])
1359 137161c9 Michael Hanselmann
      args.append(line[idx])
1360 16be8703 Iustin Pop
    result.append(format % tuple(args))
1361 16be8703 Iustin Pop
1362 16be8703 Iustin Pop
  return result
1363 3386e7a9 Iustin Pop
1364 3386e7a9 Iustin Pop
1365 3386e7a9 Iustin Pop
def FormatTimestamp(ts):
1366 3386e7a9 Iustin Pop
  """Formats a given timestamp.
1367 3386e7a9 Iustin Pop

1368 3386e7a9 Iustin Pop
  @type ts: timestamp
1369 3386e7a9 Iustin Pop
  @param ts: a timeval-type timestamp, a tuple of seconds and microseconds
1370 3386e7a9 Iustin Pop

1371 3386e7a9 Iustin Pop
  @rtype: string
1372 5fcc718f Iustin Pop
  @return: a string with the formatted timestamp
1373 3386e7a9 Iustin Pop

1374 3386e7a9 Iustin Pop
  """
1375 e0ec0ff6 Iustin Pop
  if not isinstance (ts, (tuple, list)) or len(ts) != 2:
1376 e0ec0ff6 Iustin Pop
    return '?'
1377 3386e7a9 Iustin Pop
  sec, usec = ts
1378 3386e7a9 Iustin Pop
  return time.strftime("%F %T", time.localtime(sec)) + ".%06d" % usec
1379 2241e2b9 Iustin Pop
1380 2241e2b9 Iustin Pop
1381 2241e2b9 Iustin Pop
def ParseTimespec(value):
1382 2241e2b9 Iustin Pop
  """Parse a time specification.
1383 2241e2b9 Iustin Pop

1384 2241e2b9 Iustin Pop
  The following suffixed will be recognized:
1385 2241e2b9 Iustin Pop

1386 2241e2b9 Iustin Pop
    - s: seconds
1387 2241e2b9 Iustin Pop
    - m: minutes
1388 2241e2b9 Iustin Pop
    - h: hours
1389 2241e2b9 Iustin Pop
    - d: day
1390 2241e2b9 Iustin Pop
    - w: weeks
1391 2241e2b9 Iustin Pop

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

1394 2241e2b9 Iustin Pop
  """
1395 2241e2b9 Iustin Pop
  value = str(value)
1396 2241e2b9 Iustin Pop
  if not value:
1397 2241e2b9 Iustin Pop
    raise errors.OpPrereqError("Empty time specification passed")
1398 2241e2b9 Iustin Pop
  suffix_map = {
1399 2241e2b9 Iustin Pop
    's': 1,
1400 2241e2b9 Iustin Pop
    'm': 60,
1401 2241e2b9 Iustin Pop
    'h': 3600,
1402 2241e2b9 Iustin Pop
    'd': 86400,
1403 2241e2b9 Iustin Pop
    'w': 604800,
1404 2241e2b9 Iustin Pop
    }
1405 2241e2b9 Iustin Pop
  if value[-1] not in suffix_map:
1406 2241e2b9 Iustin Pop
    try:
1407 2241e2b9 Iustin Pop
      value = int(value)
1408 2241e2b9 Iustin Pop
    except ValueError:
1409 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
1410 2241e2b9 Iustin Pop
  else:
1411 2241e2b9 Iustin Pop
    multiplier = suffix_map[value[-1]]
1412 2241e2b9 Iustin Pop
    value = value[:-1]
1413 2241e2b9 Iustin Pop
    if not value: # no data left after stripping the suffix
1414 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification (only"
1415 2241e2b9 Iustin Pop
                                 " suffix passed)")
1416 2241e2b9 Iustin Pop
    try:
1417 2241e2b9 Iustin Pop
      value = int(value) * multiplier
1418 2241e2b9 Iustin Pop
    except ValueError:
1419 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
1420 2241e2b9 Iustin Pop
  return value
1421 46fbdd04 Iustin Pop
1422 46fbdd04 Iustin Pop
1423 4040a784 Iustin Pop
def GetOnlineNodes(nodes, cl=None, nowarn=False):
1424 4040a784 Iustin Pop
  """Returns the names of online nodes.
1425 4040a784 Iustin Pop

1426 4040a784 Iustin Pop
  This function will also log a warning on stderr with the names of
1427 4040a784 Iustin Pop
  the online nodes.
1428 4040a784 Iustin Pop

1429 4040a784 Iustin Pop
  @param nodes: if not empty, use only this subset of nodes (minus the
1430 4040a784 Iustin Pop
      offline ones)
1431 4040a784 Iustin Pop
  @param cl: if not None, luxi client to use
1432 4040a784 Iustin Pop
  @type nowarn: boolean
1433 4040a784 Iustin Pop
  @param nowarn: by default, this function will output a note with the
1434 4040a784 Iustin Pop
      offline nodes that are skipped; if this parameter is True the
1435 4040a784 Iustin Pop
      note is not displayed
1436 4040a784 Iustin Pop

1437 4040a784 Iustin Pop
  """
1438 4040a784 Iustin Pop
  if cl is None:
1439 4040a784 Iustin Pop
    cl = GetClient()
1440 4040a784 Iustin Pop
1441 2e7b8369 Iustin Pop
  result = cl.QueryNodes(names=nodes, fields=["name", "offline"],
1442 2e7b8369 Iustin Pop
                         use_locking=False)
1443 4040a784 Iustin Pop
  offline = [row[0] for row in result if row[1]]
1444 4040a784 Iustin Pop
  if offline and not nowarn:
1445 4040a784 Iustin Pop
    ToStderr("Note: skipping offline node(s): %s" % ", ".join(offline))
1446 4040a784 Iustin Pop
  return [row[0] for row in result if not row[1]]
1447 4040a784 Iustin Pop
1448 4040a784 Iustin Pop
1449 46fbdd04 Iustin Pop
def _ToStream(stream, txt, *args):
1450 46fbdd04 Iustin Pop
  """Write a message to a stream, bypassing the logging system
1451 46fbdd04 Iustin Pop

1452 46fbdd04 Iustin Pop
  @type stream: file object
1453 46fbdd04 Iustin Pop
  @param stream: the file to which we should write
1454 46fbdd04 Iustin Pop
  @type txt: str
1455 46fbdd04 Iustin Pop
  @param txt: the message
1456 46fbdd04 Iustin Pop

1457 46fbdd04 Iustin Pop
  """
1458 46fbdd04 Iustin Pop
  if args:
1459 46fbdd04 Iustin Pop
    args = tuple(args)
1460 46fbdd04 Iustin Pop
    stream.write(txt % args)
1461 46fbdd04 Iustin Pop
  else:
1462 46fbdd04 Iustin Pop
    stream.write(txt)
1463 46fbdd04 Iustin Pop
  stream.write('\n')
1464 46fbdd04 Iustin Pop
  stream.flush()
1465 46fbdd04 Iustin Pop
1466 46fbdd04 Iustin Pop
1467 46fbdd04 Iustin Pop
def ToStdout(txt, *args):
1468 46fbdd04 Iustin Pop
  """Write a message to stdout only, bypassing the logging system
1469 46fbdd04 Iustin Pop

1470 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
1471 46fbdd04 Iustin Pop

1472 46fbdd04 Iustin Pop
  @type txt: str
1473 46fbdd04 Iustin Pop
  @param txt: the message
1474 46fbdd04 Iustin Pop

1475 46fbdd04 Iustin Pop
  """
1476 46fbdd04 Iustin Pop
  _ToStream(sys.stdout, txt, *args)
1477 46fbdd04 Iustin Pop
1478 46fbdd04 Iustin Pop
1479 46fbdd04 Iustin Pop
def ToStderr(txt, *args):
1480 46fbdd04 Iustin Pop
  """Write a message to stderr only, bypassing the logging system
1481 46fbdd04 Iustin Pop

1482 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
1483 46fbdd04 Iustin Pop

1484 46fbdd04 Iustin Pop
  @type txt: str
1485 46fbdd04 Iustin Pop
  @param txt: the message
1486 46fbdd04 Iustin Pop

1487 46fbdd04 Iustin Pop
  """
1488 46fbdd04 Iustin Pop
  _ToStream(sys.stderr, txt, *args)
1489 479636a3 Iustin Pop
1490 479636a3 Iustin Pop
1491 479636a3 Iustin Pop
class JobExecutor(object):
1492 479636a3 Iustin Pop
  """Class which manages the submission and execution of multiple jobs.
1493 479636a3 Iustin Pop

1494 479636a3 Iustin Pop
  Note that instances of this class should not be reused between
1495 479636a3 Iustin Pop
  GetResults() calls.
1496 479636a3 Iustin Pop

1497 479636a3 Iustin Pop
  """
1498 479636a3 Iustin Pop
  def __init__(self, cl=None, verbose=True):
1499 479636a3 Iustin Pop
    self.queue = []
1500 479636a3 Iustin Pop
    if cl is None:
1501 479636a3 Iustin Pop
      cl = GetClient()
1502 479636a3 Iustin Pop
    self.cl = cl
1503 479636a3 Iustin Pop
    self.verbose = verbose
1504 23b4b983 Iustin Pop
    self.jobs = []
1505 479636a3 Iustin Pop
1506 479636a3 Iustin Pop
  def QueueJob(self, name, *ops):
1507 23b4b983 Iustin Pop
    """Record a job for later submit.
1508 479636a3 Iustin Pop

1509 479636a3 Iustin Pop
    @type name: string
1510 479636a3 Iustin Pop
    @param name: a description of the job, will be used in WaitJobSet
1511 479636a3 Iustin Pop
    """
1512 23b4b983 Iustin Pop
    self.queue.append((name, ops))
1513 23b4b983 Iustin Pop
1514 23b4b983 Iustin Pop
  def SubmitPending(self):
1515 23b4b983 Iustin Pop
    """Submit all pending jobs.
1516 23b4b983 Iustin Pop

1517 23b4b983 Iustin Pop
    """
1518 23b4b983 Iustin Pop
    results = self.cl.SubmitManyJobs([row[1] for row in self.queue])
1519 23b4b983 Iustin Pop
    for ((status, data), (name, _)) in zip(results, self.queue):
1520 23b4b983 Iustin Pop
      self.jobs.append((status, data, name))
1521 479636a3 Iustin Pop
1522 479636a3 Iustin Pop
  def GetResults(self):
1523 479636a3 Iustin Pop
    """Wait for and return the results of all jobs.
1524 479636a3 Iustin Pop

1525 479636a3 Iustin Pop
    @rtype: list
1526 479636a3 Iustin Pop
    @return: list of tuples (success, job results), in the same order
1527 479636a3 Iustin Pop
        as the submitted jobs; if a job has failed, instead of the result
1528 479636a3 Iustin Pop
        there will be the error message
1529 479636a3 Iustin Pop

1530 479636a3 Iustin Pop
    """
1531 23b4b983 Iustin Pop
    if not self.jobs:
1532 23b4b983 Iustin Pop
      self.SubmitPending()
1533 479636a3 Iustin Pop
    results = []
1534 479636a3 Iustin Pop
    if self.verbose:
1535 23b4b983 Iustin Pop
      ok_jobs = [row[1] for row in self.jobs if row[0]]
1536 23b4b983 Iustin Pop
      if ok_jobs:
1537 23b4b983 Iustin Pop
        ToStdout("Submitted jobs %s", ", ".join(ok_jobs))
1538 23b4b983 Iustin Pop
    for submit_status, jid, name in self.jobs:
1539 23b4b983 Iustin Pop
      if not submit_status:
1540 23b4b983 Iustin Pop
        ToStderr("Failed to submit job for %s: %s", name, jid)
1541 23b4b983 Iustin Pop
        results.append((False, jid))
1542 23b4b983 Iustin Pop
        continue
1543 479636a3 Iustin Pop
      if self.verbose:
1544 479636a3 Iustin Pop
        ToStdout("Waiting for job %s for %s...", jid, name)
1545 479636a3 Iustin Pop
      try:
1546 479636a3 Iustin Pop
        job_result = PollJob(jid, cl=self.cl)
1547 479636a3 Iustin Pop
        success = True
1548 479636a3 Iustin Pop
      except (errors.GenericError, luxi.ProtocolError), err:
1549 479636a3 Iustin Pop
        _, job_result = FormatError(err)
1550 479636a3 Iustin Pop
        success = False
1551 479636a3 Iustin Pop
        # the error message will always be shown, verbose or not
1552 479636a3 Iustin Pop
        ToStderr("Job %s for %s has failed: %s", jid, name, job_result)
1553 479636a3 Iustin Pop
1554 479636a3 Iustin Pop
      results.append((success, job_result))
1555 479636a3 Iustin Pop
    return results
1556 479636a3 Iustin Pop
1557 479636a3 Iustin Pop
  def WaitOrShow(self, wait):
1558 479636a3 Iustin Pop
    """Wait for job results or only print the job IDs.
1559 479636a3 Iustin Pop

1560 479636a3 Iustin Pop
    @type wait: boolean
1561 479636a3 Iustin Pop
    @param wait: whether to wait or not
1562 479636a3 Iustin Pop

1563 479636a3 Iustin Pop
    """
1564 479636a3 Iustin Pop
    if wait:
1565 479636a3 Iustin Pop
      return self.GetResults()
1566 479636a3 Iustin Pop
    else:
1567 23b4b983 Iustin Pop
      if not self.jobs:
1568 23b4b983 Iustin Pop
        self.SubmitPending()
1569 23b4b983 Iustin Pop
      for status, result, name in self.jobs:
1570 23b4b983 Iustin Pop
        if status:
1571 23b4b983 Iustin Pop
          ToStdout("%s: %s", result, name)
1572 23b4b983 Iustin Pop
        else:
1573 23b4b983 Iustin Pop
          ToStderr("Failure for %s: %s", name, result)