Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ ec39d63c

History | View | Annotate | Download (58 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 685ee993 Iustin Pop
import time
29 46fbdd04 Iustin Pop
import logging
30 73702ee7 Iustin Pop
from cStringIO import StringIO
31 a8083063 Iustin Pop
32 a8083063 Iustin Pop
from ganeti import utils
33 a8083063 Iustin Pop
from ganeti import errors
34 a8083063 Iustin Pop
from ganeti import constants
35 846baef9 Iustin Pop
from ganeti import opcodes
36 ceab32dd Iustin Pop
from ganeti import luxi
37 b33e986b Iustin Pop
from ganeti import ssconf
38 4331f6cd Michael Hanselmann
from ganeti import rpc
39 a8083063 Iustin Pop
40 c38c44ad Michael Hanselmann
from optparse import (OptionParser, TitledHelpFormatter,
41 38206f3c Iustin Pop
                      Option, OptionValueError)
42 a8083063 Iustin Pop
43 03298ebe Michael Hanselmann
44 4abc4f1e Iustin Pop
__all__ = [
45 4abc4f1e Iustin Pop
  # Command line options
46 e7e09483 Iustin Pop
  "ALLOCATABLE_OPT",
47 2d5e7ae1 Iustin Pop
  "ALL_OPT",
48 e00f7a05 Iustin Pop
  "AUTO_REPLACE_OPT",
49 087ed2ed Iustin Pop
  "BACKEND_OPT",
50 baef337d Iustin Pop
  "CLEANUP_OPT",
51 4abc4f1e Iustin Pop
  "CONFIRM_OPT",
52 e32df528 Iustin Pop
  "CP_SIZE_OPT",
53 4abc4f1e Iustin Pop
  "DEBUG_OPT",
54 a0c9776a Iustin Pop
  "DEBUG_SIMERR_OPT",
55 4b038a1e Iustin Pop
  "DISKIDX_OPT",
56 e3876ccb Iustin Pop
  "DISK_OPT",
57 4b038a1e Iustin Pop
  "DISK_TEMPLATE_OPT",
58 771734c9 Iustin Pop
  "DRAINED_OPT",
59 383a3591 Iustin Pop
  "ENABLED_HV_OPT",
60 14e9e7f3 Iustin Pop
  "ERROR_CODES_OPT",
61 4abc4f1e Iustin Pop
  "FIELDS_OPT",
62 4a25828c Iustin Pop
  "FILESTORE_DIR_OPT",
63 0f87c43e Iustin Pop
  "FILESTORE_DRIVER_OPT",
64 06073e85 Guido Trotter
  "FORCE_OPT",
65 06073e85 Guido Trotter
  "FORCE_VARIANT_OPT",
66 29392516 Iustin Pop
  "GLOBAL_FILEDIR_OPT",
67 073271f6 Iustin Pop
  "HVLIST_OPT",
68 48f212d7 Iustin Pop
  "HVOPTS_OPT",
69 236fd9c4 Iustin Pop
  "HYPERVISOR_OPT",
70 4eb62659 Iustin Pop
  "IALLOCATOR_OPT",
71 82a786d5 Iustin Pop
  "IGNORE_CONSIST_OPT",
72 b6e841a8 Iustin Pop
  "IGNORE_FAILURES_OPT",
73 ee3f9578 Iustin Pop
  "IGNORE_SECONDARIES_OPT",
74 05586c90 Iustin Pop
  "IGNORE_SIZE_OPT",
75 e3646f22 Iustin Pop
  "MAC_PREFIX_OPT",
76 29392516 Iustin Pop
  "MASTER_NETDEV_OPT",
77 771734c9 Iustin Pop
  "MC_OPT",
78 7d3a9fab Iustin Pop
  "NET_OPT",
79 a14db5ff Iustin Pop
  "NEW_SECONDARY_OPT",
80 4fbc93dd Iustin Pop
  "NIC_PARAMS_OPT",
81 7edc4637 Iustin Pop
  "NODE_LIST_OPT",
82 990b7886 Iustin Pop
  "NODE_PLACEMENT_OPT",
83 4abc4f1e Iustin Pop
  "NOHDR_OPT",
84 91e0748c Iustin Pop
  "NOIPCHECK_OPT",
85 460d22be Iustin Pop
  "NONAMECHECK_OPT",
86 831040bf Iustin Pop
  "NOLVM_STORAGE_OPT",
87 29392516 Iustin Pop
  "NOMODIFY_ETCHOSTS_OPT",
88 b989b9d9 Ken Wehr
  "NOMODIFY_SSH_SETUP_OPT",
89 26023ecd Iustin Pop
  "NONICS_OPT",
90 f2a0828c Iustin Pop
  "NONLIVE_OPT",
91 14e9e7f3 Iustin Pop
  "NONPLUS1_OPT",
92 44c44832 Iustin Pop
  "NOSHUTDOWN_OPT",
93 edeb878a Iustin Pop
  "NOSTART_OPT",
94 fcdde7f2 Iustin Pop
  "NOSSH_KEYCHECK_OPT",
95 58371861 Iustin Pop
  "NOVOTING_OPT",
96 3f75b4f3 Iustin Pop
  "NWSYNC_OPT",
97 a72d0a79 Iustin Pop
  "ON_PRIMARY_OPT",
98 feb09e6a Iustin Pop
  "ON_SECONDARY_OPT",
99 771734c9 Iustin Pop
  "OFFLINE_OPT",
100 d3ed23ff Iustin Pop
  "OS_OPT",
101 ff00c1a7 Iustin Pop
  "OS_SIZE_OPT",
102 b8d0f938 Iustin Pop
  "READD_OPT",
103 12054861 Iustin Pop
  "REBOOT_TYPE_OPT",
104 8d823629 Iustin Pop
  "SECONDARY_IP_OPT",
105 67840b40 Iustin Pop
  "SELECT_OS_OPT",
106 4abc4f1e Iustin Pop
  "SEP_OPT",
107 fdcf4d84 Iustin Pop
  "SHOWCMD_OPT",
108 7e5eaaa8 Guido Trotter
  "SHUTDOWN_TIMEOUT_OPT",
109 f36d7d81 Iustin Pop
  "SINGLE_NODE_OPT",
110 df62e5db Iustin Pop
  "SRC_DIR_OPT",
111 df62e5db Iustin Pop
  "SRC_NODE_OPT",
112 4abc4f1e Iustin Pop
  "SUBMIT_OPT",
113 99a8c799 Iustin Pop
  "STATIC_OPT",
114 4abc4f1e Iustin Pop
  "SYNC_OPT",
115 4abc4f1e Iustin Pop
  "TAG_SRC_OPT",
116 b5762e2a Guido Trotter
  "TIMEOUT_OPT",
117 4abc4f1e Iustin Pop
  "USEUNITS_OPT",
118 9cdb9578 Iustin Pop
  "VERBOSE_OPT",
119 b58726e8 Iustin Pop
  "VG_NAME_OPT",
120 1f587d3d Iustin Pop
  "YES_DOIT_OPT",
121 4abc4f1e Iustin Pop
  # Generic functions for CLI programs
122 4abc4f1e Iustin Pop
  "GenericMain",
123 d77490c5 Iustin Pop
  "GenericInstanceCreate",
124 4abc4f1e Iustin Pop
  "GetClient",
125 4abc4f1e Iustin Pop
  "GetOnlineNodes",
126 4abc4f1e Iustin Pop
  "JobExecutor",
127 4abc4f1e Iustin Pop
  "JobSubmittedException",
128 4abc4f1e Iustin Pop
  "ParseTimespec",
129 4abc4f1e Iustin Pop
  "SubmitOpCode",
130 4abc4f1e Iustin Pop
  "SubmitOrSend",
131 4abc4f1e Iustin Pop
  "UsesRPC",
132 4abc4f1e Iustin Pop
  # Formatting functions
133 4abc4f1e Iustin Pop
  "ToStderr", "ToStdout",
134 4abc4f1e Iustin Pop
  "FormatError",
135 4abc4f1e Iustin Pop
  "GenerateTable",
136 4abc4f1e Iustin Pop
  "AskUser",
137 4abc4f1e Iustin Pop
  "FormatTimestamp",
138 4abc4f1e Iustin Pop
  # Tags functions
139 4abc4f1e Iustin Pop
  "ListTags",
140 4abc4f1e Iustin Pop
  "AddTags",
141 4abc4f1e Iustin Pop
  "RemoveTags",
142 4abc4f1e Iustin Pop
  # command line options support infrastructure
143 4abc4f1e Iustin Pop
  "ARGS_MANY_INSTANCES",
144 4abc4f1e Iustin Pop
  "ARGS_MANY_NODES",
145 4abc4f1e Iustin Pop
  "ARGS_NONE",
146 4abc4f1e Iustin Pop
  "ARGS_ONE_INSTANCE",
147 4abc4f1e Iustin Pop
  "ARGS_ONE_NODE",
148 4abc4f1e Iustin Pop
  "ArgChoice",
149 4abc4f1e Iustin Pop
  "ArgCommand",
150 4abc4f1e Iustin Pop
  "ArgFile",
151 4abc4f1e Iustin Pop
  "ArgHost",
152 4abc4f1e Iustin Pop
  "ArgInstance",
153 4abc4f1e Iustin Pop
  "ArgJobId",
154 4abc4f1e Iustin Pop
  "ArgNode",
155 4abc4f1e Iustin Pop
  "ArgSuggest",
156 4abc4f1e Iustin Pop
  "ArgUnknown",
157 4abc4f1e Iustin Pop
  "OPT_COMPL_INST_ADD_NODES",
158 4abc4f1e Iustin Pop
  "OPT_COMPL_MANY_NODES",
159 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_IALLOCATOR",
160 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_INSTANCE",
161 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_NODE",
162 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_OS",
163 4abc4f1e Iustin Pop
  "cli_option",
164 4abc4f1e Iustin Pop
  "SplitNodeOption",
165 07150497 Guido Trotter
  "CalculateOSNames",
166 4abc4f1e Iustin Pop
  ]
167 846baef9 Iustin Pop
168 8b46606c Guido Trotter
NO_PREFIX = "no_"
169 8b46606c Guido Trotter
UN_PREFIX = "-"
170 846baef9 Iustin Pop
171 03298ebe Michael Hanselmann
172 863d7f46 Michael Hanselmann
class _Argument:
173 7260cfbe Iustin Pop
  def __init__(self, min=0, max=None): # pylint: disable-msg=W0622
174 863d7f46 Michael Hanselmann
    self.min = min
175 863d7f46 Michael Hanselmann
    self.max = max
176 863d7f46 Michael Hanselmann
177 863d7f46 Michael Hanselmann
  def __repr__(self):
178 863d7f46 Michael Hanselmann
    return ("<%s min=%s max=%s>" %
179 863d7f46 Michael Hanselmann
            (self.__class__.__name__, self.min, self.max))
180 863d7f46 Michael Hanselmann
181 863d7f46 Michael Hanselmann
182 863d7f46 Michael Hanselmann
class ArgSuggest(_Argument):
183 863d7f46 Michael Hanselmann
  """Suggesting argument.
184 863d7f46 Michael Hanselmann

185 863d7f46 Michael Hanselmann
  Value can be any of the ones passed to the constructor.
186 863d7f46 Michael Hanselmann

187 863d7f46 Michael Hanselmann
  """
188 7260cfbe Iustin Pop
  # pylint: disable-msg=W0622
189 863d7f46 Michael Hanselmann
  def __init__(self, min=0, max=None, choices=None):
190 863d7f46 Michael Hanselmann
    _Argument.__init__(self, min=min, max=max)
191 863d7f46 Michael Hanselmann
    self.choices = choices
192 863d7f46 Michael Hanselmann
193 863d7f46 Michael Hanselmann
  def __repr__(self):
194 863d7f46 Michael Hanselmann
    return ("<%s min=%s max=%s choices=%r>" %
195 863d7f46 Michael Hanselmann
            (self.__class__.__name__, self.min, self.max, self.choices))
196 863d7f46 Michael Hanselmann
197 863d7f46 Michael Hanselmann
198 863d7f46 Michael Hanselmann
class ArgChoice(ArgSuggest):
199 863d7f46 Michael Hanselmann
  """Choice argument.
200 863d7f46 Michael Hanselmann

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

204 863d7f46 Michael Hanselmann
  """
205 863d7f46 Michael Hanselmann
206 863d7f46 Michael Hanselmann
207 863d7f46 Michael Hanselmann
class ArgUnknown(_Argument):
208 863d7f46 Michael Hanselmann
  """Unknown argument to program (e.g. determined at runtime).
209 863d7f46 Michael Hanselmann

210 863d7f46 Michael Hanselmann
  """
211 863d7f46 Michael Hanselmann
212 863d7f46 Michael Hanselmann
213 863d7f46 Michael Hanselmann
class ArgInstance(_Argument):
214 863d7f46 Michael Hanselmann
  """Instances argument.
215 863d7f46 Michael Hanselmann

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

222 863d7f46 Michael Hanselmann
  """
223 863d7f46 Michael Hanselmann
224 863d7f46 Michael Hanselmann
class ArgJobId(_Argument):
225 863d7f46 Michael Hanselmann
  """Job ID argument.
226 863d7f46 Michael Hanselmann

227 863d7f46 Michael Hanselmann
  """
228 863d7f46 Michael Hanselmann
229 863d7f46 Michael Hanselmann
230 863d7f46 Michael Hanselmann
class ArgFile(_Argument):
231 863d7f46 Michael Hanselmann
  """File path argument.
232 863d7f46 Michael Hanselmann

233 863d7f46 Michael Hanselmann
  """
234 863d7f46 Michael Hanselmann
235 863d7f46 Michael Hanselmann
236 863d7f46 Michael Hanselmann
class ArgCommand(_Argument):
237 863d7f46 Michael Hanselmann
  """Command argument.
238 863d7f46 Michael Hanselmann

239 863d7f46 Michael Hanselmann
  """
240 863d7f46 Michael Hanselmann
241 863d7f46 Michael Hanselmann
242 83ec7961 Michael Hanselmann
class ArgHost(_Argument):
243 83ec7961 Michael Hanselmann
  """Host argument.
244 83ec7961 Michael Hanselmann

245 83ec7961 Michael Hanselmann
  """
246 83ec7961 Michael Hanselmann
247 83ec7961 Michael Hanselmann
248 4a265c08 Michael Hanselmann
ARGS_NONE = []
249 4a265c08 Michael Hanselmann
ARGS_MANY_INSTANCES = [ArgInstance()]
250 4a265c08 Michael Hanselmann
ARGS_MANY_NODES = [ArgNode()]
251 4a265c08 Michael Hanselmann
ARGS_ONE_INSTANCE = [ArgInstance(min=1, max=1)]
252 4a265c08 Michael Hanselmann
ARGS_ONE_NODE = [ArgNode(min=1, max=1)]
253 4a265c08 Michael Hanselmann
254 4a265c08 Michael Hanselmann
255 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
256 846baef9 Iustin Pop
  """Extract the tag type object.
257 846baef9 Iustin Pop

258 846baef9 Iustin Pop
  Note that this function will modify its args parameter.
259 846baef9 Iustin Pop

260 846baef9 Iustin Pop
  """
261 846baef9 Iustin Pop
  if not hasattr(opts, "tag_type"):
262 846baef9 Iustin Pop
    raise errors.ProgrammerError("tag_type not passed to _ExtractTagsObject")
263 846baef9 Iustin Pop
  kind = opts.tag_type
264 846baef9 Iustin Pop
  if kind == constants.TAG_CLUSTER:
265 846baef9 Iustin Pop
    retval = kind, kind
266 846baef9 Iustin Pop
  elif kind == constants.TAG_NODE or kind == constants.TAG_INSTANCE:
267 846baef9 Iustin Pop
    if not args:
268 0c434948 Iustin Pop
      raise errors.OpPrereqError("no arguments passed to the command")
269 846baef9 Iustin Pop
    name = args.pop(0)
270 846baef9 Iustin Pop
    retval = kind, name
271 846baef9 Iustin Pop
  else:
272 846baef9 Iustin Pop
    raise errors.ProgrammerError("Unhandled tag type '%s'" % kind)
273 846baef9 Iustin Pop
  return retval
274 846baef9 Iustin Pop
275 846baef9 Iustin Pop
276 810c50b7 Iustin Pop
def _ExtendTags(opts, args):
277 810c50b7 Iustin Pop
  """Extend the args if a source file has been given.
278 810c50b7 Iustin Pop

279 810c50b7 Iustin Pop
  This function will extend the tags with the contents of the file
280 810c50b7 Iustin Pop
  passed in the 'tags_source' attribute of the opts parameter. A file
281 810c50b7 Iustin Pop
  named '-' will be replaced by stdin.
282 810c50b7 Iustin Pop

283 810c50b7 Iustin Pop
  """
284 810c50b7 Iustin Pop
  fname = opts.tags_source
285 810c50b7 Iustin Pop
  if fname is None:
286 810c50b7 Iustin Pop
    return
287 810c50b7 Iustin Pop
  if fname == "-":
288 810c50b7 Iustin Pop
    new_fh = sys.stdin
289 810c50b7 Iustin Pop
  else:
290 810c50b7 Iustin Pop
    new_fh = open(fname, "r")
291 810c50b7 Iustin Pop
  new_data = []
292 810c50b7 Iustin Pop
  try:
293 810c50b7 Iustin Pop
    # we don't use the nice 'new_data = [line.strip() for line in fh]'
294 810c50b7 Iustin Pop
    # because of python bug 1633941
295 810c50b7 Iustin Pop
    while True:
296 810c50b7 Iustin Pop
      line = new_fh.readline()
297 810c50b7 Iustin Pop
      if not line:
298 810c50b7 Iustin Pop
        break
299 810c50b7 Iustin Pop
      new_data.append(line.strip())
300 810c50b7 Iustin Pop
  finally:
301 810c50b7 Iustin Pop
    new_fh.close()
302 810c50b7 Iustin Pop
  args.extend(new_data)
303 810c50b7 Iustin Pop
304 810c50b7 Iustin Pop
305 846baef9 Iustin Pop
def ListTags(opts, args):
306 846baef9 Iustin Pop
  """List the tags on a given object.
307 846baef9 Iustin Pop

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

313 846baef9 Iustin Pop
  """
314 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
315 7699c3af Iustin Pop
  cl = GetClient()
316 7699c3af Iustin Pop
  result = cl.QueryTags(kind, name)
317 846baef9 Iustin Pop
  result = list(result)
318 846baef9 Iustin Pop
  result.sort()
319 846baef9 Iustin Pop
  for tag in result:
320 03298ebe Michael Hanselmann
    ToStdout(tag)
321 846baef9 Iustin Pop
322 846baef9 Iustin Pop
323 846baef9 Iustin Pop
def AddTags(opts, args):
324 846baef9 Iustin Pop
  """Add tags on a given object.
325 846baef9 Iustin Pop

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

331 846baef9 Iustin Pop
  """
332 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
333 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
334 846baef9 Iustin Pop
  if not args:
335 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be added")
336 846baef9 Iustin Pop
  op = opcodes.OpAddTags(kind=kind, name=name, tags=args)
337 846baef9 Iustin Pop
  SubmitOpCode(op)
338 846baef9 Iustin Pop
339 846baef9 Iustin Pop
340 846baef9 Iustin Pop
def RemoveTags(opts, args):
341 846baef9 Iustin Pop
  """Remove tags from a given object.
342 846baef9 Iustin Pop

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

348 846baef9 Iustin Pop
  """
349 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
350 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
351 846baef9 Iustin Pop
  if not args:
352 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be removed")
353 846baef9 Iustin Pop
  op = opcodes.OpDelTags(kind=kind, name=name, tags=args)
354 846baef9 Iustin Pop
  SubmitOpCode(op)
355 846baef9 Iustin Pop
356 a8083063 Iustin Pop
357 8929d28c Iustin Pop
def check_unit(option, opt, value): # pylint: disable-msg=W0613
358 65fe4693 Iustin Pop
  """OptParsers custom converter for units.
359 65fe4693 Iustin Pop

360 65fe4693 Iustin Pop
  """
361 a8083063 Iustin Pop
  try:
362 a8083063 Iustin Pop
    return utils.ParseUnit(value)
363 a8083063 Iustin Pop
  except errors.UnitParseError, err:
364 3ecf6786 Iustin Pop
    raise OptionValueError("option %s: %s" % (opt, err))
365 a8083063 Iustin Pop
366 a8083063 Iustin Pop
367 a8469393 Iustin Pop
def _SplitKeyVal(opt, data):
368 a8469393 Iustin Pop
  """Convert a KeyVal string into a dict.
369 a8469393 Iustin Pop

370 a8469393 Iustin Pop
  This function will convert a key=val[,...] string into a dict. Empty
371 a8469393 Iustin Pop
  values will be converted specially: keys which have the prefix 'no_'
372 a8469393 Iustin Pop
  will have the value=False and the prefix stripped, the others will
373 a8469393 Iustin Pop
  have value=True.
374 a8469393 Iustin Pop

375 a8469393 Iustin Pop
  @type opt: string
376 a8469393 Iustin Pop
  @param opt: a string holding the option name for which we process the
377 a8469393 Iustin Pop
      data, used in building error messages
378 a8469393 Iustin Pop
  @type data: string
379 a8469393 Iustin Pop
  @param data: a string of the format key=val,key=val,...
380 a8469393 Iustin Pop
  @rtype: dict
381 a8469393 Iustin Pop
  @return: {key=val, key=val}
382 a8469393 Iustin Pop
  @raises errors.ParameterError: if there are duplicate keys
383 a8469393 Iustin Pop

384 a8469393 Iustin Pop
  """
385 a8469393 Iustin Pop
  kv_dict = {}
386 4f31882e Guido Trotter
  if data:
387 4f31882e Guido Trotter
    for elem in data.split(","):
388 4f31882e Guido Trotter
      if "=" in elem:
389 4f31882e Guido Trotter
        key, val = elem.split("=", 1)
390 a8469393 Iustin Pop
      else:
391 4f31882e Guido Trotter
        if elem.startswith(NO_PREFIX):
392 4f31882e Guido Trotter
          key, val = elem[len(NO_PREFIX):], False
393 4f31882e Guido Trotter
        elif elem.startswith(UN_PREFIX):
394 4f31882e Guido Trotter
          key, val = elem[len(UN_PREFIX):], None
395 4f31882e Guido Trotter
        else:
396 4f31882e Guido Trotter
          key, val = elem, True
397 4f31882e Guido Trotter
      if key in kv_dict:
398 4f31882e Guido Trotter
        raise errors.ParameterError("Duplicate key '%s' in option %s" %
399 4f31882e Guido Trotter
                                    (key, opt))
400 4f31882e Guido Trotter
      kv_dict[key] = val
401 a8469393 Iustin Pop
  return kv_dict
402 a8469393 Iustin Pop
403 a8469393 Iustin Pop
404 8929d28c Iustin Pop
def check_ident_key_val(option, opt, value):  # pylint: disable-msg=W0613
405 552c8dff Michael Hanselmann
  """Custom parser for ident:key=val,key=val options.
406 552c8dff Michael Hanselmann

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

410 a8469393 Iustin Pop
  """
411 a8469393 Iustin Pop
  if ":" not in value:
412 8b46606c Guido Trotter
    ident, rest = value, ''
413 a8469393 Iustin Pop
  else:
414 a8469393 Iustin Pop
    ident, rest = value.split(":", 1)
415 8b46606c Guido Trotter
416 8b46606c Guido Trotter
  if ident.startswith(NO_PREFIX):
417 8b46606c Guido Trotter
    if rest:
418 8b46606c Guido Trotter
      msg = "Cannot pass options when removing parameter groups: %s" % value
419 8b46606c Guido Trotter
      raise errors.ParameterError(msg)
420 8b46606c Guido Trotter
    retval = (ident[len(NO_PREFIX):], False)
421 8b46606c Guido Trotter
  elif ident.startswith(UN_PREFIX):
422 8b46606c Guido Trotter
    if rest:
423 8b46606c Guido Trotter
      msg = "Cannot pass options when removing parameter groups: %s" % value
424 8b46606c Guido Trotter
      raise errors.ParameterError(msg)
425 8b46606c Guido Trotter
    retval = (ident[len(UN_PREFIX):], None)
426 8b46606c Guido Trotter
  else:
427 a8469393 Iustin Pop
    kv_dict = _SplitKeyVal(opt, rest)
428 a8469393 Iustin Pop
    retval = (ident, kv_dict)
429 a8469393 Iustin Pop
  return retval
430 a8469393 Iustin Pop
431 a8469393 Iustin Pop
432 8929d28c Iustin Pop
def check_key_val(option, opt, value):  # pylint: disable-msg=W0613
433 552c8dff Michael Hanselmann
  """Custom parser class for key=val,key=val options.
434 552c8dff Michael Hanselmann

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

437 a8469393 Iustin Pop
  """
438 a8469393 Iustin Pop
  return _SplitKeyVal(opt, value)
439 a8469393 Iustin Pop
440 a8469393 Iustin Pop
441 63d44c55 Michael Hanselmann
# completion_suggestion is normally a list. Using numeric values not evaluating
442 63d44c55 Michael Hanselmann
# to False for dynamic completion.
443 63d44c55 Michael Hanselmann
(OPT_COMPL_MANY_NODES,
444 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_NODE,
445 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_INSTANCE,
446 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_OS,
447 2d3ed64b Michael Hanselmann
 OPT_COMPL_ONE_IALLOCATOR,
448 2d3ed64b Michael Hanselmann
 OPT_COMPL_INST_ADD_NODES) = range(100, 106)
449 63d44c55 Michael Hanselmann
450 63d44c55 Michael Hanselmann
OPT_COMPL_ALL = frozenset([
451 63d44c55 Michael Hanselmann
  OPT_COMPL_MANY_NODES,
452 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_NODE,
453 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_INSTANCE,
454 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_OS,
455 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_IALLOCATOR,
456 2d3ed64b Michael Hanselmann
  OPT_COMPL_INST_ADD_NODES,
457 63d44c55 Michael Hanselmann
  ])
458 63d44c55 Michael Hanselmann
459 63d44c55 Michael Hanselmann
460 552c8dff Michael Hanselmann
class CliOption(Option):
461 552c8dff Michael Hanselmann
  """Custom option class for optparse.
462 a8469393 Iustin Pop

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

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

847 c41eea6e Iustin Pop
  @param argv: the command line
848 c41eea6e Iustin Pop
  @param commands: dictionary with special contents, see the design
849 c41eea6e Iustin Pop
      doc for cmdline handling
850 c41eea6e Iustin Pop
  @param aliases: dictionary with command aliases {'alias': 'target, ...}
851 098c0958 Michael Hanselmann

852 a8083063 Iustin Pop
  """
853 a8083063 Iustin Pop
  if len(argv) == 0:
854 a8083063 Iustin Pop
    binary = "<command>"
855 a8083063 Iustin Pop
  else:
856 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
857 a8083063 Iustin Pop
858 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
859 03298ebe Michael Hanselmann
    ToStdout("%s (ganeti) %s", binary, constants.RELEASE_VERSION)
860 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
861 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
862 a8083063 Iustin Pop
    sys.exit(0)
863 a8083063 Iustin Pop
864 de47cf8f Guido Trotter
  if len(argv) < 2 or not (argv[1] in commands or
865 70a35b6f Guido Trotter
                           argv[1] in aliases):
866 a8083063 Iustin Pop
    # let's do a nice thing
867 a8083063 Iustin Pop
    sortedcmds = commands.keys()
868 a8083063 Iustin Pop
    sortedcmds.sort()
869 03298ebe Michael Hanselmann
870 03298ebe Michael Hanselmann
    ToStdout("Usage: %s {command} [options...] [argument...]", binary)
871 03298ebe Michael Hanselmann
    ToStdout("%s <command> --help to see details, or man %s", binary, binary)
872 03298ebe Michael Hanselmann
    ToStdout("")
873 03298ebe Michael Hanselmann
874 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
875 4e713df6 Iustin Pop
    mlen = max([len(" %s" % cmd) for cmd in commands])
876 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
877 03298ebe Michael Hanselmann
878 a8083063 Iustin Pop
    # and format a nice command list
879 03298ebe Michael Hanselmann
    ToStdout("Commands:")
880 a8083063 Iustin Pop
    for cmd in sortedcmds:
881 4e713df6 Iustin Pop
      cmdstr = " %s" % (cmd,)
882 9a033156 Iustin Pop
      help_text = commands[cmd][4]
883 03298ebe Michael Hanselmann
      help_lines = textwrap.wrap(help_text, 79 - 3 - mlen)
884 03298ebe Michael Hanselmann
      ToStdout("%-*s - %s", mlen, cmdstr, help_lines.pop(0))
885 a8083063 Iustin Pop
      for line in help_lines:
886 03298ebe Michael Hanselmann
        ToStdout("%-*s   %s", mlen, "", line)
887 03298ebe Michael Hanselmann
888 03298ebe Michael Hanselmann
    ToStdout("")
889 03298ebe Michael Hanselmann
890 a8083063 Iustin Pop
    return None, None, None
891 de47cf8f Guido Trotter
892 de47cf8f Guido Trotter
  # get command, unalias it, and look it up in commands
893 a8083063 Iustin Pop
  cmd = argv.pop(1)
894 de47cf8f Guido Trotter
  if cmd in aliases:
895 de47cf8f Guido Trotter
    if cmd in commands:
896 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' overrides an existing"
897 de47cf8f Guido Trotter
                                   " command" % cmd)
898 de47cf8f Guido Trotter
899 de47cf8f Guido Trotter
    if aliases[cmd] not in commands:
900 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' maps to non-existing"
901 de47cf8f Guido Trotter
                                   " command '%s'" % (cmd, aliases[cmd]))
902 de47cf8f Guido Trotter
903 de47cf8f Guido Trotter
    cmd = aliases[cmd]
904 de47cf8f Guido Trotter
905 a8005e17 Michael Hanselmann
  func, args_def, parser_opts, usage, description = commands[cmd]
906 064c21f8 Iustin Pop
  parser = OptionParser(option_list=parser_opts + [_DRY_RUN_OPT, DEBUG_OPT],
907 a8083063 Iustin Pop
                        description=description,
908 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
909 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
910 a8083063 Iustin Pop
  parser.disable_interspersed_args()
911 a8083063 Iustin Pop
  options, args = parser.parse_args()
912 a8005e17 Michael Hanselmann
913 a8005e17 Michael Hanselmann
  if not _CheckArguments(cmd, args_def, args):
914 a8083063 Iustin Pop
    return None, None, None
915 a8083063 Iustin Pop
916 a8083063 Iustin Pop
  return func, options, args
917 a8083063 Iustin Pop
918 a8083063 Iustin Pop
919 a8005e17 Michael Hanselmann
def _CheckArguments(cmd, args_def, args):
920 a8005e17 Michael Hanselmann
  """Verifies the arguments using the argument definition.
921 a8005e17 Michael Hanselmann

922 a8005e17 Michael Hanselmann
  Algorithm:
923 a8005e17 Michael Hanselmann

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

926 a8005e17 Michael Hanselmann
    1. For each argument in definition
927 a8005e17 Michael Hanselmann

928 a8005e17 Michael Hanselmann
      1. Keep running count of minimum number of values (min_count)
929 a8005e17 Michael Hanselmann
      1. Keep running count of maximum number of values (max_count)
930 a8005e17 Michael Hanselmann
      1. If it has an unlimited number of values
931 a8005e17 Michael Hanselmann

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

934 a8005e17 Michael Hanselmann
    1. If last argument has limited number of values
935 a8005e17 Michael Hanselmann

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

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

940 a8005e17 Michael Hanselmann
  """
941 a8005e17 Michael Hanselmann
  if args and not args_def:
942 a8005e17 Michael Hanselmann
    ToStderr("Error: Command %s expects no arguments", cmd)
943 a8005e17 Michael Hanselmann
    return False
944 a8005e17 Michael Hanselmann
945 a8005e17 Michael Hanselmann
  min_count = None
946 a8005e17 Michael Hanselmann
  max_count = None
947 a8005e17 Michael Hanselmann
  check_max = None
948 a8005e17 Michael Hanselmann
949 a8005e17 Michael Hanselmann
  last_idx = len(args_def) - 1
950 a8005e17 Michael Hanselmann
951 a8005e17 Michael Hanselmann
  for idx, arg in enumerate(args_def):
952 a8005e17 Michael Hanselmann
    if min_count is None:
953 a8005e17 Michael Hanselmann
      min_count = arg.min
954 a8005e17 Michael Hanselmann
    elif arg.min is not None:
955 a8005e17 Michael Hanselmann
      min_count += arg.min
956 a8005e17 Michael Hanselmann
957 a8005e17 Michael Hanselmann
    if max_count is None:
958 a8005e17 Michael Hanselmann
      max_count = arg.max
959 a8005e17 Michael Hanselmann
    elif arg.max is not None:
960 a8005e17 Michael Hanselmann
      max_count += arg.max
961 a8005e17 Michael Hanselmann
962 a8005e17 Michael Hanselmann
    if idx == last_idx:
963 a8005e17 Michael Hanselmann
      check_max = (arg.max is not None)
964 a8005e17 Michael Hanselmann
965 a8005e17 Michael Hanselmann
    elif arg.max is None:
966 a8005e17 Michael Hanselmann
      raise errors.ProgrammerError("Only the last argument can have max=None")
967 a8005e17 Michael Hanselmann
968 a8005e17 Michael Hanselmann
  if check_max:
969 a8005e17 Michael Hanselmann
    # Command with exact number of arguments
970 a8005e17 Michael Hanselmann
    if (min_count is not None and max_count is not None and
971 a8005e17 Michael Hanselmann
        min_count == max_count and len(args) != min_count):
972 a8005e17 Michael Hanselmann
      ToStderr("Error: Command %s expects %d argument(s)", cmd, min_count)
973 a8005e17 Michael Hanselmann
      return False
974 a8005e17 Michael Hanselmann
975 a8005e17 Michael Hanselmann
    # Command with limited number of arguments
976 a8005e17 Michael Hanselmann
    if max_count is not None and len(args) > max_count:
977 a8005e17 Michael Hanselmann
      ToStderr("Error: Command %s expects only %d argument(s)",
978 a8005e17 Michael Hanselmann
               cmd, max_count)
979 a8005e17 Michael Hanselmann
      return False
980 a8005e17 Michael Hanselmann
981 a8005e17 Michael Hanselmann
  # Command with some required arguments
982 a8005e17 Michael Hanselmann
  if min_count is not None and len(args) < min_count:
983 a8005e17 Michael Hanselmann
    ToStderr("Error: Command %s expects at least %d argument(s)",
984 a8005e17 Michael Hanselmann
             cmd, min_count)
985 a8005e17 Michael Hanselmann
    return False
986 a8005e17 Michael Hanselmann
987 a8005e17 Michael Hanselmann
  return True
988 a8005e17 Michael Hanselmann
989 a8005e17 Michael Hanselmann
990 60d49723 Michael Hanselmann
def SplitNodeOption(value):
991 60d49723 Michael Hanselmann
  """Splits the value of a --node option.
992 60d49723 Michael Hanselmann

993 60d49723 Michael Hanselmann
  """
994 60d49723 Michael Hanselmann
  if value and ':' in value:
995 60d49723 Michael Hanselmann
    return value.split(':', 1)
996 60d49723 Michael Hanselmann
  else:
997 60d49723 Michael Hanselmann
    return (value, None)
998 60d49723 Michael Hanselmann
999 60d49723 Michael Hanselmann
1000 07150497 Guido Trotter
def CalculateOSNames(os_name, os_variants):
1001 07150497 Guido Trotter
  """Calculates all the names an OS can be called, according to its variants.
1002 07150497 Guido Trotter

1003 07150497 Guido Trotter
  @type os_name: string
1004 07150497 Guido Trotter
  @param os_name: base name of the os
1005 07150497 Guido Trotter
  @type os_variants: list or None
1006 07150497 Guido Trotter
  @param os_variants: list of supported variants
1007 07150497 Guido Trotter
  @rtype: list
1008 07150497 Guido Trotter
  @return: list of valid names
1009 07150497 Guido Trotter

1010 07150497 Guido Trotter
  """
1011 07150497 Guido Trotter
  if os_variants:
1012 07150497 Guido Trotter
    return ['%s+%s' % (os_name, v) for v in os_variants]
1013 07150497 Guido Trotter
  else:
1014 07150497 Guido Trotter
    return [os_name]
1015 07150497 Guido Trotter
1016 07150497 Guido Trotter
1017 4331f6cd Michael Hanselmann
def UsesRPC(fn):
1018 4331f6cd Michael Hanselmann
  def wrapper(*args, **kwargs):
1019 4331f6cd Michael Hanselmann
    rpc.Init()
1020 4331f6cd Michael Hanselmann
    try:
1021 4331f6cd Michael Hanselmann
      return fn(*args, **kwargs)
1022 4331f6cd Michael Hanselmann
    finally:
1023 4331f6cd Michael Hanselmann
      rpc.Shutdown()
1024 4331f6cd Michael Hanselmann
  return wrapper
1025 4331f6cd Michael Hanselmann
1026 4331f6cd Michael Hanselmann
1027 47988778 Iustin Pop
def AskUser(text, choices=None):
1028 47988778 Iustin Pop
  """Ask the user a question.
1029 a8083063 Iustin Pop

1030 c41eea6e Iustin Pop
  @param text: the question to ask
1031 a8083063 Iustin Pop

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

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

1041 a8083063 Iustin Pop
  """
1042 47988778 Iustin Pop
  if choices is None:
1043 47988778 Iustin Pop
    choices = [('y', True, 'Perform the operation'),
1044 47988778 Iustin Pop
               ('n', False, 'Do not perform the operation')]
1045 47988778 Iustin Pop
  if not choices or not isinstance(choices, list):
1046 5bbd3f7f Michael Hanselmann
    raise errors.ProgrammerError("Invalid choices argument to AskUser")
1047 47988778 Iustin Pop
  for entry in choices:
1048 47988778 Iustin Pop
    if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
1049 5bbd3f7f Michael Hanselmann
      raise errors.ProgrammerError("Invalid choices element to AskUser")
1050 47988778 Iustin Pop
1051 47988778 Iustin Pop
  answer = choices[-1][1]
1052 47988778 Iustin Pop
  new_text = []
1053 47988778 Iustin Pop
  for line in text.splitlines():
1054 47988778 Iustin Pop
    new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
1055 47988778 Iustin Pop
  text = "\n".join(new_text)
1056 a8083063 Iustin Pop
  try:
1057 3023170f Iustin Pop
    f = file("/dev/tty", "a+")
1058 a8083063 Iustin Pop
  except IOError:
1059 47988778 Iustin Pop
    return answer
1060 a8083063 Iustin Pop
  try:
1061 47988778 Iustin Pop
    chars = [entry[0] for entry in choices]
1062 47988778 Iustin Pop
    chars[-1] = "[%s]" % chars[-1]
1063 47988778 Iustin Pop
    chars.append('?')
1064 47988778 Iustin Pop
    maps = dict([(entry[0], entry[1]) for entry in choices])
1065 47988778 Iustin Pop
    while True:
1066 47988778 Iustin Pop
      f.write(text)
1067 47988778 Iustin Pop
      f.write('\n')
1068 47988778 Iustin Pop
      f.write("/".join(chars))
1069 47988778 Iustin Pop
      f.write(": ")
1070 47988778 Iustin Pop
      line = f.readline(2).strip().lower()
1071 47988778 Iustin Pop
      if line in maps:
1072 47988778 Iustin Pop
        answer = maps[line]
1073 47988778 Iustin Pop
        break
1074 47988778 Iustin Pop
      elif line == '?':
1075 47988778 Iustin Pop
        for entry in choices:
1076 47988778 Iustin Pop
          f.write(" %s - %s\n" % (entry[0], entry[2]))
1077 47988778 Iustin Pop
        f.write("\n")
1078 47988778 Iustin Pop
        continue
1079 a8083063 Iustin Pop
  finally:
1080 a8083063 Iustin Pop
    f.close()
1081 a8083063 Iustin Pop
  return answer
1082 a8083063 Iustin Pop
1083 a8083063 Iustin Pop
1084 e9d741b6 Iustin Pop
class JobSubmittedException(Exception):
1085 e9d741b6 Iustin Pop
  """Job was submitted, client should exit.
1086 e9d741b6 Iustin Pop

1087 e9d741b6 Iustin Pop
  This exception has one argument, the ID of the job that was
1088 e9d741b6 Iustin Pop
  submitted. The handler should print this ID.
1089 e9d741b6 Iustin Pop

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

1092 e9d741b6 Iustin Pop
  """
1093 e9d741b6 Iustin Pop
1094 e9d741b6 Iustin Pop
1095 0a1e74d9 Iustin Pop
def SendJob(ops, cl=None):
1096 0a1e74d9 Iustin Pop
  """Function to submit an opcode without waiting for the results.
1097 a8083063 Iustin Pop

1098 0a1e74d9 Iustin Pop
  @type ops: list
1099 0a1e74d9 Iustin Pop
  @param ops: list of opcodes
1100 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
1101 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
1102 0a1e74d9 Iustin Pop
             if None, a new client will be created
1103 a8083063 Iustin Pop

1104 a8083063 Iustin Pop
  """
1105 e2212007 Iustin Pop
  if cl is None:
1106 b33e986b Iustin Pop
    cl = GetClient()
1107 685ee993 Iustin Pop
1108 0a1e74d9 Iustin Pop
  job_id = cl.SubmitJob(ops)
1109 0a1e74d9 Iustin Pop
1110 0a1e74d9 Iustin Pop
  return job_id
1111 0a1e74d9 Iustin Pop
1112 0a1e74d9 Iustin Pop
1113 281606c1 Michael Hanselmann
def PollJob(job_id, cl=None, feedback_fn=None):
1114 0a1e74d9 Iustin Pop
  """Function to poll for the result of a job.
1115 0a1e74d9 Iustin Pop

1116 0a1e74d9 Iustin Pop
  @type job_id: job identified
1117 0a1e74d9 Iustin Pop
  @param job_id: the job to poll for results
1118 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
1119 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
1120 0a1e74d9 Iustin Pop
             if None, a new client will be created
1121 0a1e74d9 Iustin Pop

1122 0a1e74d9 Iustin Pop
  """
1123 0a1e74d9 Iustin Pop
  if cl is None:
1124 0a1e74d9 Iustin Pop
    cl = GetClient()
1125 685ee993 Iustin Pop
1126 6c5a7090 Michael Hanselmann
  prev_job_info = None
1127 6c5a7090 Michael Hanselmann
  prev_logmsg_serial = None
1128 6c5a7090 Michael Hanselmann
1129 685ee993 Iustin Pop
  while True:
1130 6c5a7090 Michael Hanselmann
    result = cl.WaitForJobChange(job_id, ["status"], prev_job_info,
1131 6c5a7090 Michael Hanselmann
                                 prev_logmsg_serial)
1132 6c5a7090 Michael Hanselmann
    if not result:
1133 685ee993 Iustin Pop
      # job not found, go away!
1134 0bbe448c Michael Hanselmann
      raise errors.JobLost("Job with id %s lost" % job_id)
1135 685ee993 Iustin Pop
1136 6c5a7090 Michael Hanselmann
    # Split result, a tuple of (field values, log entries)
1137 6c5a7090 Michael Hanselmann
    (job_info, log_entries) = result
1138 6c5a7090 Michael Hanselmann
    (status, ) = job_info
1139 6c5a7090 Michael Hanselmann
1140 6c5a7090 Michael Hanselmann
    if log_entries:
1141 6c5a7090 Michael Hanselmann
      for log_entry in log_entries:
1142 6c5a7090 Michael Hanselmann
        (serial, timestamp, _, message) = log_entry
1143 6c5a7090 Michael Hanselmann
        if callable(feedback_fn):
1144 6c5a7090 Michael Hanselmann
          feedback_fn(log_entry[1:])
1145 6c5a7090 Michael Hanselmann
        else:
1146 26f15862 Iustin Pop
          encoded = utils.SafeEncode(message)
1147 03298ebe Michael Hanselmann
          ToStdout("%s %s", time.ctime(utils.MergeTime(timestamp)), encoded)
1148 6c5a7090 Michael Hanselmann
        prev_logmsg_serial = max(prev_logmsg_serial, serial)
1149 6c5a7090 Michael Hanselmann
1150 0bbe448c Michael Hanselmann
    # TODO: Handle canceled and archived jobs
1151 fbf0262f Michael Hanselmann
    elif status in (constants.JOB_STATUS_SUCCESS,
1152 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_ERROR,
1153 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELING,
1154 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELED):
1155 685ee993 Iustin Pop
      break
1156 6c5a7090 Michael Hanselmann
1157 6c5a7090 Michael Hanselmann
    prev_job_info = job_info
1158 685ee993 Iustin Pop
1159 0e050889 Iustin Pop
  jobs = cl.QueryJobs([job_id], ["status", "opstatus", "opresult"])
1160 0bbe448c Michael Hanselmann
  if not jobs:
1161 0bbe448c Michael Hanselmann
    raise errors.JobLost("Job with id %s lost" % job_id)
1162 685ee993 Iustin Pop
1163 0e050889 Iustin Pop
  status, opstatus, result = jobs[0]
1164 0bbe448c Michael Hanselmann
  if status == constants.JOB_STATUS_SUCCESS:
1165 53c04d04 Iustin Pop
    return result
1166 fbf0262f Michael Hanselmann
  elif status in (constants.JOB_STATUS_CANCELING,
1167 fbf0262f Michael Hanselmann
                  constants.JOB_STATUS_CANCELED):
1168 fbf0262f Michael Hanselmann
    raise errors.OpExecError("Job was canceled")
1169 0bbe448c Michael Hanselmann
  else:
1170 0e050889 Iustin Pop
    has_ok = False
1171 0e050889 Iustin Pop
    for idx, (status, msg) in enumerate(zip(opstatus, result)):
1172 0e050889 Iustin Pop
      if status == constants.OP_STATUS_SUCCESS:
1173 0e050889 Iustin Pop
        has_ok = True
1174 0e050889 Iustin Pop
      elif status == constants.OP_STATUS_ERROR:
1175 bcb66fca Iustin Pop
        errors.MaybeRaise(msg)
1176 0e050889 Iustin Pop
        if has_ok:
1177 0e050889 Iustin Pop
          raise errors.OpExecError("partial failure (opcode %d): %s" %
1178 0e050889 Iustin Pop
                                   (idx, msg))
1179 0e050889 Iustin Pop
        else:
1180 0e050889 Iustin Pop
          raise errors.OpExecError(str(msg))
1181 0e050889 Iustin Pop
    # default failure mode
1182 0bbe448c Michael Hanselmann
    raise errors.OpExecError(result)
1183 ceab32dd Iustin Pop
1184 ceab32dd Iustin Pop
1185 0a1e74d9 Iustin Pop
def SubmitOpCode(op, cl=None, feedback_fn=None):
1186 0a1e74d9 Iustin Pop
  """Legacy function to submit an opcode.
1187 0a1e74d9 Iustin Pop

1188 0a1e74d9 Iustin Pop
  This is just a simple wrapper over the construction of the processor
1189 0a1e74d9 Iustin Pop
  instance. It should be extended to better handle feedback and
1190 0a1e74d9 Iustin Pop
  interaction functions.
1191 0a1e74d9 Iustin Pop

1192 0a1e74d9 Iustin Pop
  """
1193 0a1e74d9 Iustin Pop
  if cl is None:
1194 0a1e74d9 Iustin Pop
    cl = GetClient()
1195 0a1e74d9 Iustin Pop
1196 0a1e74d9 Iustin Pop
  job_id = SendJob([op], cl)
1197 0a1e74d9 Iustin Pop
1198 53c04d04 Iustin Pop
  op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn)
1199 53c04d04 Iustin Pop
1200 53c04d04 Iustin Pop
  return op_results[0]
1201 0a1e74d9 Iustin Pop
1202 0a1e74d9 Iustin Pop
1203 94428652 Iustin Pop
def SubmitOrSend(op, opts, cl=None, feedback_fn=None):
1204 94428652 Iustin Pop
  """Wrapper around SubmitOpCode or SendJob.
1205 94428652 Iustin Pop

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

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

1213 94428652 Iustin Pop
  """
1214 64c65a2a Iustin Pop
  if opts and opts.dry_run:
1215 64c65a2a Iustin Pop
    op.dry_run = opts.dry_run
1216 94428652 Iustin Pop
  if opts and opts.submit_only:
1217 e9d741b6 Iustin Pop
    job_id = SendJob([op], cl=cl)
1218 e9d741b6 Iustin Pop
    raise JobSubmittedException(job_id)
1219 94428652 Iustin Pop
  else:
1220 94428652 Iustin Pop
    return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn)
1221 94428652 Iustin Pop
1222 94428652 Iustin Pop
1223 af30b2fd Michael Hanselmann
def GetClient():
1224 af30b2fd Michael Hanselmann
  # TODO: Cache object?
1225 b33e986b Iustin Pop
  try:
1226 b33e986b Iustin Pop
    client = luxi.Client()
1227 b33e986b Iustin Pop
  except luxi.NoMasterError:
1228 d9a51679 Michael Hanselmann
    ss = ssconf.SimpleStore()
1229 d9a51679 Michael Hanselmann
1230 d9a51679 Michael Hanselmann
    # Try to read ssconf file
1231 d9a51679 Michael Hanselmann
    try:
1232 d9a51679 Michael Hanselmann
      ss.GetMasterNode()
1233 d9a51679 Michael Hanselmann
    except errors.ConfigurationError:
1234 d9a51679 Michael Hanselmann
      raise errors.OpPrereqError("Cluster not initialized or this machine is"
1235 d9a51679 Michael Hanselmann
                                 " not part of a cluster")
1236 d9a51679 Michael Hanselmann
1237 d9a51679 Michael Hanselmann
    master, myself = ssconf.GetMasterAndMyself(ss=ss)
1238 b33e986b Iustin Pop
    if master != myself:
1239 b33e986b Iustin Pop
      raise errors.OpPrereqError("This is not the master node, please connect"
1240 b33e986b Iustin Pop
                                 " to node '%s' and rerun the command" %
1241 b33e986b Iustin Pop
                                 master)
1242 d9a51679 Michael Hanselmann
    raise
1243 b33e986b Iustin Pop
  return client
1244 af30b2fd Michael Hanselmann
1245 af30b2fd Michael Hanselmann
1246 73702ee7 Iustin Pop
def FormatError(err):
1247 73702ee7 Iustin Pop
  """Return a formatted error message for a given error.
1248 73702ee7 Iustin Pop

1249 73702ee7 Iustin Pop
  This function takes an exception instance and returns a tuple
1250 73702ee7 Iustin Pop
  consisting of two values: first, the recommended exit code, and
1251 73702ee7 Iustin Pop
  second, a string describing the error message (not
1252 73702ee7 Iustin Pop
  newline-terminated).
1253 73702ee7 Iustin Pop

1254 73702ee7 Iustin Pop
  """
1255 73702ee7 Iustin Pop
  retcode = 1
1256 73702ee7 Iustin Pop
  obuf = StringIO()
1257 e2e521d0 Iustin Pop
  msg = str(err)
1258 73702ee7 Iustin Pop
  if isinstance(err, errors.ConfigurationError):
1259 e2e521d0 Iustin Pop
    txt = "Corrupt configuration file: %s" % msg
1260 46fbdd04 Iustin Pop
    logging.error(txt)
1261 e2e521d0 Iustin Pop
    obuf.write(txt + "\n")
1262 73702ee7 Iustin Pop
    obuf.write("Aborting.")
1263 73702ee7 Iustin Pop
    retcode = 2
1264 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksAbort):
1265 73702ee7 Iustin Pop
    obuf.write("Failure: hooks execution failed:\n")
1266 73702ee7 Iustin Pop
    for node, script, out in err.args[0]:
1267 73702ee7 Iustin Pop
      if out:
1268 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s, output: %s\n" %
1269 73702ee7 Iustin Pop
                   (node, script, out))
1270 73702ee7 Iustin Pop
      else:
1271 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s (no output)\n" %
1272 73702ee7 Iustin Pop
                   (node, script))
1273 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksFailure):
1274 e2e521d0 Iustin Pop
    obuf.write("Failure: hooks general failure: %s" % msg)
1275 73702ee7 Iustin Pop
  elif isinstance(err, errors.ResolverError):
1276 73702ee7 Iustin Pop
    this_host = utils.HostInfo.SysName()
1277 73702ee7 Iustin Pop
    if err.args[0] == this_host:
1278 73702ee7 Iustin Pop
      msg = "Failure: can't resolve my own hostname ('%s')"
1279 73702ee7 Iustin Pop
    else:
1280 73702ee7 Iustin Pop
      msg = "Failure: can't resolve hostname '%s'"
1281 73702ee7 Iustin Pop
    obuf.write(msg % err.args[0])
1282 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpPrereqError):
1283 5c983ee5 Iustin Pop
    if len(err.args) == 2:
1284 5c983ee5 Iustin Pop
      obuf.write("Failure: prerequisites not met for this"
1285 5c983ee5 Iustin Pop
               " operation:\nerror type: %s, error details:\n%s" %
1286 5c983ee5 Iustin Pop
                 (err.args[1], err.args[0]))
1287 5c983ee5 Iustin Pop
    else:
1288 5c983ee5 Iustin Pop
      obuf.write("Failure: prerequisites not met for this"
1289 5c983ee5 Iustin Pop
                 " operation:\n%s" % msg)
1290 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpExecError):
1291 e2e521d0 Iustin Pop
    obuf.write("Failure: command execution error:\n%s" % msg)
1292 73702ee7 Iustin Pop
  elif isinstance(err, errors.TagError):
1293 e2e521d0 Iustin Pop
    obuf.write("Failure: invalid tag(s) given:\n%s" % msg)
1294 686d7433 Iustin Pop
  elif isinstance(err, errors.JobQueueDrainError):
1295 686d7433 Iustin Pop
    obuf.write("Failure: the job queue is marked for drain and doesn't"
1296 686d7433 Iustin Pop
               " accept new requests\n")
1297 f87b405e Michael Hanselmann
  elif isinstance(err, errors.JobQueueFull):
1298 f87b405e Michael Hanselmann
    obuf.write("Failure: the job queue is full and doesn't accept new"
1299 f87b405e Michael Hanselmann
               " job submissions until old jobs are archived\n")
1300 a5728081 Guido Trotter
  elif isinstance(err, errors.TypeEnforcementError):
1301 a5728081 Guido Trotter
    obuf.write("Parameter Error: %s" % msg)
1302 c1ce76bb Iustin Pop
  elif isinstance(err, errors.ParameterError):
1303 c1ce76bb Iustin Pop
    obuf.write("Failure: unknown/wrong parameter name '%s'" % msg)
1304 73702ee7 Iustin Pop
  elif isinstance(err, errors.GenericError):
1305 e2e521d0 Iustin Pop
    obuf.write("Unhandled Ganeti error: %s" % msg)
1306 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.NoMasterError):
1307 03a8dbdc Iustin Pop
    obuf.write("Cannot communicate with the master daemon.\nIs it running"
1308 082c5adb Michael Hanselmann
               " and listening for connections?")
1309 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.TimeoutError):
1310 03a8dbdc Iustin Pop
    obuf.write("Timeout while talking to the master daemon. Error:\n"
1311 03a8dbdc Iustin Pop
               "%s" % msg)
1312 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.ProtocolError):
1313 03a8dbdc Iustin Pop
    obuf.write("Unhandled protocol error while talking to the master daemon:\n"
1314 03a8dbdc Iustin Pop
               "%s" % msg)
1315 e9d741b6 Iustin Pop
  elif isinstance(err, JobSubmittedException):
1316 e9d741b6 Iustin Pop
    obuf.write("JobID: %s\n" % err.args[0])
1317 e9d741b6 Iustin Pop
    retcode = 0
1318 73702ee7 Iustin Pop
  else:
1319 e2e521d0 Iustin Pop
    obuf.write("Unhandled exception: %s" % msg)
1320 73702ee7 Iustin Pop
  return retcode, obuf.getvalue().rstrip('\n')
1321 73702ee7 Iustin Pop
1322 73702ee7 Iustin Pop
1323 de47cf8f Guido Trotter
def GenericMain(commands, override=None, aliases=None):
1324 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
1325 a8083063 Iustin Pop

1326 334d1483 Iustin Pop
  Arguments:
1327 334d1483 Iustin Pop
    - commands: a dictionary with a special structure, see the design doc
1328 334d1483 Iustin Pop
                for command line handling.
1329 334d1483 Iustin Pop
    - override: if not None, we expect a dictionary with keys that will
1330 334d1483 Iustin Pop
                override command line options; this can be used to pass
1331 334d1483 Iustin Pop
                options from the scripts to generic functions
1332 de47cf8f Guido Trotter
    - aliases: dictionary with command aliases {'alias': 'target, ...}
1333 a8083063 Iustin Pop

1334 a8083063 Iustin Pop
  """
1335 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
1336 a8083063 Iustin Pop
  if sys.argv:
1337 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
1338 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
1339 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
1340 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
1341 a8083063 Iustin Pop
    else:
1342 a8083063 Iustin Pop
      old_cmdline = ""
1343 a8083063 Iustin Pop
  else:
1344 a8083063 Iustin Pop
    binary = "<unknown program>"
1345 a8083063 Iustin Pop
    old_cmdline = ""
1346 a8083063 Iustin Pop
1347 de47cf8f Guido Trotter
  if aliases is None:
1348 de47cf8f Guido Trotter
    aliases = {}
1349 de47cf8f Guido Trotter
1350 3126878d Guido Trotter
  try:
1351 3126878d Guido Trotter
    func, options, args = _ParseArgs(sys.argv, commands, aliases)
1352 3126878d Guido Trotter
  except errors.ParameterError, err:
1353 3126878d Guido Trotter
    result, err_msg = FormatError(err)
1354 3126878d Guido Trotter
    ToStderr(err_msg)
1355 3126878d Guido Trotter
    return 1
1356 3126878d Guido Trotter
1357 a8083063 Iustin Pop
  if func is None: # parse error
1358 a8083063 Iustin Pop
    return 1
1359 a8083063 Iustin Pop
1360 334d1483 Iustin Pop
  if override is not None:
1361 334d1483 Iustin Pop
    for key, val in override.iteritems():
1362 334d1483 Iustin Pop
      setattr(options, key, val)
1363 334d1483 Iustin Pop
1364 82d9caef Iustin Pop
  utils.SetupLogging(constants.LOG_COMMANDS, debug=options.debug,
1365 82d9caef Iustin Pop
                     stderr_logging=True, program=binary)
1366 a8083063 Iustin Pop
1367 a8083063 Iustin Pop
  if old_cmdline:
1368 46fbdd04 Iustin Pop
    logging.info("run with arguments '%s'", old_cmdline)
1369 a8083063 Iustin Pop
  else:
1370 46fbdd04 Iustin Pop
    logging.info("run with no arguments")
1371 a8083063 Iustin Pop
1372 a8083063 Iustin Pop
  try:
1373 a4af651e Iustin Pop
    result = func(options, args)
1374 d8353c3a Iustin Pop
  except (errors.GenericError, luxi.ProtocolError,
1375 d8353c3a Iustin Pop
          JobSubmittedException), err:
1376 a4af651e Iustin Pop
    result, err_msg = FormatError(err)
1377 5bbd3f7f Michael Hanselmann
    logging.exception("Error during command processing")
1378 46fbdd04 Iustin Pop
    ToStderr(err_msg)
1379 a8083063 Iustin Pop
1380 a8083063 Iustin Pop
  return result
1381 137161c9 Michael Hanselmann
1382 137161c9 Michael Hanselmann
1383 d77490c5 Iustin Pop
def GenericInstanceCreate(mode, opts, args):
1384 d77490c5 Iustin Pop
  """Add an instance to the cluster via either creation or import.
1385 d77490c5 Iustin Pop

1386 d77490c5 Iustin Pop
  @param mode: constants.INSTANCE_CREATE or constants.INSTANCE_IMPORT
1387 d77490c5 Iustin Pop
  @param opts: the command line options selected by the user
1388 d77490c5 Iustin Pop
  @type args: list
1389 d77490c5 Iustin Pop
  @param args: should contain only one element, the new instance name
1390 d77490c5 Iustin Pop
  @rtype: int
1391 d77490c5 Iustin Pop
  @return: the desired exit code
1392 d77490c5 Iustin Pop

1393 d77490c5 Iustin Pop
  """
1394 d77490c5 Iustin Pop
  instance = args[0]
1395 d77490c5 Iustin Pop
1396 d77490c5 Iustin Pop
  (pnode, snode) = SplitNodeOption(opts.node)
1397 d77490c5 Iustin Pop
1398 d77490c5 Iustin Pop
  hypervisor = None
1399 d77490c5 Iustin Pop
  hvparams = {}
1400 d77490c5 Iustin Pop
  if opts.hypervisor:
1401 d77490c5 Iustin Pop
    hypervisor, hvparams = opts.hypervisor
1402 d77490c5 Iustin Pop
1403 d77490c5 Iustin Pop
  if opts.nics:
1404 d77490c5 Iustin Pop
    try:
1405 21bcb9aa Michael Hanselmann
      nic_max = max(int(nidx[0]) + 1 for nidx in opts.nics)
1406 d77490c5 Iustin Pop
    except ValueError, err:
1407 d77490c5 Iustin Pop
      raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
1408 d77490c5 Iustin Pop
    nics = [{}] * nic_max
1409 d77490c5 Iustin Pop
    for nidx, ndict in opts.nics:
1410 d77490c5 Iustin Pop
      nidx = int(nidx)
1411 d77490c5 Iustin Pop
      if not isinstance(ndict, dict):
1412 d77490c5 Iustin Pop
        msg = "Invalid nic/%d value: expected dict, got %s" % (nidx, ndict)
1413 d77490c5 Iustin Pop
        raise errors.OpPrereqError(msg)
1414 d77490c5 Iustin Pop
      nics[nidx] = ndict
1415 d77490c5 Iustin Pop
  elif opts.no_nics:
1416 d77490c5 Iustin Pop
    # no nics
1417 d77490c5 Iustin Pop
    nics = []
1418 d77490c5 Iustin Pop
  else:
1419 d77490c5 Iustin Pop
    # default of one nic, all auto
1420 d77490c5 Iustin Pop
    nics = [{}]
1421 d77490c5 Iustin Pop
1422 d77490c5 Iustin Pop
  if opts.disk_template == constants.DT_DISKLESS:
1423 d77490c5 Iustin Pop
    if opts.disks or opts.sd_size is not None:
1424 d77490c5 Iustin Pop
      raise errors.OpPrereqError("Diskless instance but disk"
1425 d77490c5 Iustin Pop
                                 " information passed")
1426 d77490c5 Iustin Pop
    disks = []
1427 d77490c5 Iustin Pop
  else:
1428 d77490c5 Iustin Pop
    if not opts.disks and not opts.sd_size:
1429 d77490c5 Iustin Pop
      raise errors.OpPrereqError("No disk information specified")
1430 d77490c5 Iustin Pop
    if opts.disks and opts.sd_size is not None:
1431 d77490c5 Iustin Pop
      raise errors.OpPrereqError("Please use either the '--disk' or"
1432 d77490c5 Iustin Pop
                                 " '-s' option")
1433 d77490c5 Iustin Pop
    if opts.sd_size is not None:
1434 d77490c5 Iustin Pop
      opts.disks = [(0, {"size": opts.sd_size})]
1435 d77490c5 Iustin Pop
    try:
1436 21bcb9aa Michael Hanselmann
      disk_max = max(int(didx[0]) + 1 for didx in opts.disks)
1437 d77490c5 Iustin Pop
    except ValueError, err:
1438 d77490c5 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
1439 d77490c5 Iustin Pop
    disks = [{}] * disk_max
1440 d77490c5 Iustin Pop
    for didx, ddict in opts.disks:
1441 d77490c5 Iustin Pop
      didx = int(didx)
1442 d77490c5 Iustin Pop
      if not isinstance(ddict, dict):
1443 d77490c5 Iustin Pop
        msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict)
1444 d77490c5 Iustin Pop
        raise errors.OpPrereqError(msg)
1445 d77490c5 Iustin Pop
      elif "size" not in ddict:
1446 d77490c5 Iustin Pop
        raise errors.OpPrereqError("Missing size for disk %d" % didx)
1447 d77490c5 Iustin Pop
      try:
1448 d77490c5 Iustin Pop
        ddict["size"] = utils.ParseUnit(ddict["size"])
1449 d77490c5 Iustin Pop
      except ValueError, err:
1450 d77490c5 Iustin Pop
        raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
1451 d77490c5 Iustin Pop
                                   (didx, err))
1452 d77490c5 Iustin Pop
      disks[didx] = ddict
1453 d77490c5 Iustin Pop
1454 d77490c5 Iustin Pop
  utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_TYPES)
1455 d77490c5 Iustin Pop
  utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
1456 d77490c5 Iustin Pop
1457 d77490c5 Iustin Pop
  if mode == constants.INSTANCE_CREATE:
1458 d77490c5 Iustin Pop
    start = opts.start
1459 d77490c5 Iustin Pop
    os_type = opts.os
1460 d77490c5 Iustin Pop
    src_node = None
1461 d77490c5 Iustin Pop
    src_path = None
1462 d77490c5 Iustin Pop
  elif mode == constants.INSTANCE_IMPORT:
1463 d77490c5 Iustin Pop
    start = False
1464 d77490c5 Iustin Pop
    os_type = None
1465 d77490c5 Iustin Pop
    src_node = opts.src_node
1466 d77490c5 Iustin Pop
    src_path = opts.src_dir
1467 d77490c5 Iustin Pop
  else:
1468 d77490c5 Iustin Pop
    raise errors.ProgrammerError("Invalid creation mode %s" % mode)
1469 d77490c5 Iustin Pop
1470 d77490c5 Iustin Pop
  op = opcodes.OpCreateInstance(instance_name=instance,
1471 d77490c5 Iustin Pop
                                disks=disks,
1472 d77490c5 Iustin Pop
                                disk_template=opts.disk_template,
1473 d77490c5 Iustin Pop
                                nics=nics,
1474 d77490c5 Iustin Pop
                                pnode=pnode, snode=snode,
1475 d77490c5 Iustin Pop
                                ip_check=opts.ip_check,
1476 460d22be Iustin Pop
                                name_check=opts.name_check,
1477 d77490c5 Iustin Pop
                                wait_for_sync=opts.wait_for_sync,
1478 d77490c5 Iustin Pop
                                file_storage_dir=opts.file_storage_dir,
1479 d77490c5 Iustin Pop
                                file_driver=opts.file_driver,
1480 d77490c5 Iustin Pop
                                iallocator=opts.iallocator,
1481 d77490c5 Iustin Pop
                                hypervisor=hypervisor,
1482 d77490c5 Iustin Pop
                                hvparams=hvparams,
1483 d77490c5 Iustin Pop
                                beparams=opts.beparams,
1484 d77490c5 Iustin Pop
                                mode=mode,
1485 d77490c5 Iustin Pop
                                start=start,
1486 d77490c5 Iustin Pop
                                os_type=os_type,
1487 d77490c5 Iustin Pop
                                src_node=src_node,
1488 d77490c5 Iustin Pop
                                src_path=src_path)
1489 d77490c5 Iustin Pop
1490 d77490c5 Iustin Pop
  SubmitOrSend(op, opts)
1491 d77490c5 Iustin Pop
  return 0
1492 d77490c5 Iustin Pop
1493 d77490c5 Iustin Pop
1494 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
1495 9fbfbb7b Iustin Pop
                  numfields=None, unitfields=None,
1496 9fbfbb7b Iustin Pop
                  units=None):
1497 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
1498 137161c9 Michael Hanselmann

1499 9fbfbb7b Iustin Pop
  @type headers: dict
1500 9fbfbb7b Iustin Pop
  @param headers: dictionary mapping field names to headers for
1501 9fbfbb7b Iustin Pop
      the table
1502 9fbfbb7b Iustin Pop
  @type fields: list
1503 9fbfbb7b Iustin Pop
  @param fields: the field names corresponding to each row in
1504 9fbfbb7b Iustin Pop
      the data field
1505 9fbfbb7b Iustin Pop
  @param separator: the separator to be used; if this is None,
1506 9fbfbb7b Iustin Pop
      the default 'smart' algorithm is used which computes optimal
1507 9fbfbb7b Iustin Pop
      field width, otherwise just the separator is used between
1508 9fbfbb7b Iustin Pop
      each field
1509 9fbfbb7b Iustin Pop
  @type data: list
1510 9fbfbb7b Iustin Pop
  @param data: a list of lists, each sublist being one row to be output
1511 9fbfbb7b Iustin Pop
  @type numfields: list
1512 9fbfbb7b Iustin Pop
  @param numfields: a list with the fields that hold numeric
1513 9fbfbb7b Iustin Pop
      values and thus should be right-aligned
1514 9fbfbb7b Iustin Pop
  @type unitfields: list
1515 9fbfbb7b Iustin Pop
  @param unitfields: a list with the fields that hold numeric
1516 9fbfbb7b Iustin Pop
      values that should be formatted with the units field
1517 9fbfbb7b Iustin Pop
  @type units: string or None
1518 9fbfbb7b Iustin Pop
  @param units: the units we should use for formatting, or None for
1519 9fbfbb7b Iustin Pop
      automatic choice (human-readable for non-separator usage, otherwise
1520 9fbfbb7b Iustin Pop
      megabytes); this is a one-letter string
1521 137161c9 Michael Hanselmann

1522 137161c9 Michael Hanselmann
  """
1523 9fbfbb7b Iustin Pop
  if units is None:
1524 9fbfbb7b Iustin Pop
    if separator:
1525 9fbfbb7b Iustin Pop
      units = "m"
1526 9fbfbb7b Iustin Pop
    else:
1527 9fbfbb7b Iustin Pop
      units = "h"
1528 9fbfbb7b Iustin Pop
1529 137161c9 Michael Hanselmann
  if numfields is None:
1530 137161c9 Michael Hanselmann
    numfields = []
1531 137161c9 Michael Hanselmann
  if unitfields is None:
1532 137161c9 Michael Hanselmann
    unitfields = []
1533 137161c9 Michael Hanselmann
1534 fe267188 Iustin Pop
  numfields = utils.FieldSet(*numfields)   # pylint: disable-msg=W0142
1535 fe267188 Iustin Pop
  unitfields = utils.FieldSet(*unitfields) # pylint: disable-msg=W0142
1536 00430f8e Iustin Pop
1537 137161c9 Michael Hanselmann
  format_fields = []
1538 137161c9 Michael Hanselmann
  for field in fields:
1539 01ca31ae Iustin Pop
    if headers and field not in headers:
1540 ea5a5b74 Guido Trotter
      # TODO: handle better unknown fields (either revert to old
1541 71c1af58 Iustin Pop
      # style of raising exception, or deal more intelligently with
1542 71c1af58 Iustin Pop
      # variable fields)
1543 71c1af58 Iustin Pop
      headers[field] = field
1544 137161c9 Michael Hanselmann
    if separator is not None:
1545 137161c9 Michael Hanselmann
      format_fields.append("%s")
1546 00430f8e Iustin Pop
    elif numfields.Matches(field):
1547 137161c9 Michael Hanselmann
      format_fields.append("%*s")
1548 137161c9 Michael Hanselmann
    else:
1549 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
1550 137161c9 Michael Hanselmann
1551 137161c9 Michael Hanselmann
  if separator is None:
1552 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
1553 137161c9 Michael Hanselmann
    format = ' '.join(format_fields)
1554 137161c9 Michael Hanselmann
  else:
1555 137161c9 Michael Hanselmann
    format = separator.replace("%", "%%").join(format_fields)
1556 137161c9 Michael Hanselmann
1557 137161c9 Michael Hanselmann
  for row in data:
1558 dcbd6288 Guido Trotter
    if row is None:
1559 dcbd6288 Guido Trotter
      continue
1560 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
1561 00430f8e Iustin Pop
      if unitfields.Matches(fields[idx]):
1562 137161c9 Michael Hanselmann
        try:
1563 137161c9 Michael Hanselmann
          val = int(val)
1564 137161c9 Michael Hanselmann
        except ValueError:
1565 137161c9 Michael Hanselmann
          pass
1566 137161c9 Michael Hanselmann
        else:
1567 9fbfbb7b Iustin Pop
          val = row[idx] = utils.FormatUnit(val, units)
1568 01ca31ae Iustin Pop
      val = row[idx] = str(val)
1569 137161c9 Michael Hanselmann
      if separator is None:
1570 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
1571 137161c9 Michael Hanselmann
1572 16be8703 Iustin Pop
  result = []
1573 137161c9 Michael Hanselmann
  if headers:
1574 137161c9 Michael Hanselmann
    args = []
1575 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
1576 137161c9 Michael Hanselmann
      hdr = headers[name]
1577 137161c9 Michael Hanselmann
      if separator is None:
1578 ec39d63c Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
1579 137161c9 Michael Hanselmann
        args.append(mlens[idx])
1580 137161c9 Michael Hanselmann
      args.append(hdr)
1581 16be8703 Iustin Pop
    result.append(format % tuple(args))
1582 137161c9 Michael Hanselmann
1583 ec39d63c Michael Hanselmann
  if separator is None:
1584 ec39d63c Michael Hanselmann
    assert len(mlens) == len(fields)
1585 ec39d63c Michael Hanselmann
1586 ec39d63c Michael Hanselmann
    if fields and not numfields.Matches(fields[-1]):
1587 ec39d63c Michael Hanselmann
      mlens[-1] = 0
1588 ec39d63c Michael Hanselmann
1589 137161c9 Michael Hanselmann
  for line in data:
1590 137161c9 Michael Hanselmann
    args = []
1591 dcbd6288 Guido Trotter
    if line is None:
1592 dcbd6288 Guido Trotter
      line = ['-' for _ in fields]
1593 f1501b3f Michael Hanselmann
    for idx in range(len(fields)):
1594 137161c9 Michael Hanselmann
      if separator is None:
1595 137161c9 Michael Hanselmann
        args.append(mlens[idx])
1596 137161c9 Michael Hanselmann
      args.append(line[idx])
1597 16be8703 Iustin Pop
    result.append(format % tuple(args))
1598 16be8703 Iustin Pop
1599 16be8703 Iustin Pop
  return result
1600 3386e7a9 Iustin Pop
1601 3386e7a9 Iustin Pop
1602 3386e7a9 Iustin Pop
def FormatTimestamp(ts):
1603 3386e7a9 Iustin Pop
  """Formats a given timestamp.
1604 3386e7a9 Iustin Pop

1605 3386e7a9 Iustin Pop
  @type ts: timestamp
1606 3386e7a9 Iustin Pop
  @param ts: a timeval-type timestamp, a tuple of seconds and microseconds
1607 3386e7a9 Iustin Pop

1608 3386e7a9 Iustin Pop
  @rtype: string
1609 5fcc718f Iustin Pop
  @return: a string with the formatted timestamp
1610 3386e7a9 Iustin Pop

1611 3386e7a9 Iustin Pop
  """
1612 e0ec0ff6 Iustin Pop
  if not isinstance (ts, (tuple, list)) or len(ts) != 2:
1613 e0ec0ff6 Iustin Pop
    return '?'
1614 3386e7a9 Iustin Pop
  sec, usec = ts
1615 3386e7a9 Iustin Pop
  return time.strftime("%F %T", time.localtime(sec)) + ".%06d" % usec
1616 2241e2b9 Iustin Pop
1617 2241e2b9 Iustin Pop
1618 2241e2b9 Iustin Pop
def ParseTimespec(value):
1619 2241e2b9 Iustin Pop
  """Parse a time specification.
1620 2241e2b9 Iustin Pop

1621 2241e2b9 Iustin Pop
  The following suffixed will be recognized:
1622 2241e2b9 Iustin Pop

1623 2241e2b9 Iustin Pop
    - s: seconds
1624 2241e2b9 Iustin Pop
    - m: minutes
1625 2241e2b9 Iustin Pop
    - h: hours
1626 2241e2b9 Iustin Pop
    - d: day
1627 2241e2b9 Iustin Pop
    - w: weeks
1628 2241e2b9 Iustin Pop

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

1631 2241e2b9 Iustin Pop
  """
1632 2241e2b9 Iustin Pop
  value = str(value)
1633 2241e2b9 Iustin Pop
  if not value:
1634 2241e2b9 Iustin Pop
    raise errors.OpPrereqError("Empty time specification passed")
1635 2241e2b9 Iustin Pop
  suffix_map = {
1636 2241e2b9 Iustin Pop
    's': 1,
1637 2241e2b9 Iustin Pop
    'm': 60,
1638 2241e2b9 Iustin Pop
    'h': 3600,
1639 2241e2b9 Iustin Pop
    'd': 86400,
1640 2241e2b9 Iustin Pop
    'w': 604800,
1641 2241e2b9 Iustin Pop
    }
1642 2241e2b9 Iustin Pop
  if value[-1] not in suffix_map:
1643 2241e2b9 Iustin Pop
    try:
1644 2241e2b9 Iustin Pop
      value = int(value)
1645 2241e2b9 Iustin Pop
    except ValueError:
1646 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
1647 2241e2b9 Iustin Pop
  else:
1648 2241e2b9 Iustin Pop
    multiplier = suffix_map[value[-1]]
1649 2241e2b9 Iustin Pop
    value = value[:-1]
1650 2241e2b9 Iustin Pop
    if not value: # no data left after stripping the suffix
1651 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification (only"
1652 2241e2b9 Iustin Pop
                                 " suffix passed)")
1653 2241e2b9 Iustin Pop
    try:
1654 2241e2b9 Iustin Pop
      value = int(value) * multiplier
1655 2241e2b9 Iustin Pop
    except ValueError:
1656 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
1657 2241e2b9 Iustin Pop
  return value
1658 46fbdd04 Iustin Pop
1659 46fbdd04 Iustin Pop
1660 4040a784 Iustin Pop
def GetOnlineNodes(nodes, cl=None, nowarn=False):
1661 4040a784 Iustin Pop
  """Returns the names of online nodes.
1662 4040a784 Iustin Pop

1663 4040a784 Iustin Pop
  This function will also log a warning on stderr with the names of
1664 4040a784 Iustin Pop
  the online nodes.
1665 4040a784 Iustin Pop

1666 4040a784 Iustin Pop
  @param nodes: if not empty, use only this subset of nodes (minus the
1667 4040a784 Iustin Pop
      offline ones)
1668 4040a784 Iustin Pop
  @param cl: if not None, luxi client to use
1669 4040a784 Iustin Pop
  @type nowarn: boolean
1670 4040a784 Iustin Pop
  @param nowarn: by default, this function will output a note with the
1671 4040a784 Iustin Pop
      offline nodes that are skipped; if this parameter is True the
1672 4040a784 Iustin Pop
      note is not displayed
1673 4040a784 Iustin Pop

1674 4040a784 Iustin Pop
  """
1675 4040a784 Iustin Pop
  if cl is None:
1676 4040a784 Iustin Pop
    cl = GetClient()
1677 4040a784 Iustin Pop
1678 2e7b8369 Iustin Pop
  result = cl.QueryNodes(names=nodes, fields=["name", "offline"],
1679 2e7b8369 Iustin Pop
                         use_locking=False)
1680 4040a784 Iustin Pop
  offline = [row[0] for row in result if row[1]]
1681 4040a784 Iustin Pop
  if offline and not nowarn:
1682 1f864b60 Iustin Pop
    ToStderr("Note: skipping offline node(s): %s" % utils.CommaJoin(offline))
1683 4040a784 Iustin Pop
  return [row[0] for row in result if not row[1]]
1684 4040a784 Iustin Pop
1685 4040a784 Iustin Pop
1686 46fbdd04 Iustin Pop
def _ToStream(stream, txt, *args):
1687 46fbdd04 Iustin Pop
  """Write a message to a stream, bypassing the logging system
1688 46fbdd04 Iustin Pop

1689 46fbdd04 Iustin Pop
  @type stream: file object
1690 46fbdd04 Iustin Pop
  @param stream: the file to which we should write
1691 46fbdd04 Iustin Pop
  @type txt: str
1692 46fbdd04 Iustin Pop
  @param txt: the message
1693 46fbdd04 Iustin Pop

1694 46fbdd04 Iustin Pop
  """
1695 46fbdd04 Iustin Pop
  if args:
1696 46fbdd04 Iustin Pop
    args = tuple(args)
1697 46fbdd04 Iustin Pop
    stream.write(txt % args)
1698 46fbdd04 Iustin Pop
  else:
1699 46fbdd04 Iustin Pop
    stream.write(txt)
1700 46fbdd04 Iustin Pop
  stream.write('\n')
1701 46fbdd04 Iustin Pop
  stream.flush()
1702 46fbdd04 Iustin Pop
1703 46fbdd04 Iustin Pop
1704 46fbdd04 Iustin Pop
def ToStdout(txt, *args):
1705 46fbdd04 Iustin Pop
  """Write a message to stdout only, bypassing the logging system
1706 46fbdd04 Iustin Pop

1707 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
1708 46fbdd04 Iustin Pop

1709 46fbdd04 Iustin Pop
  @type txt: str
1710 46fbdd04 Iustin Pop
  @param txt: the message
1711 46fbdd04 Iustin Pop

1712 46fbdd04 Iustin Pop
  """
1713 46fbdd04 Iustin Pop
  _ToStream(sys.stdout, txt, *args)
1714 46fbdd04 Iustin Pop
1715 46fbdd04 Iustin Pop
1716 46fbdd04 Iustin Pop
def ToStderr(txt, *args):
1717 46fbdd04 Iustin Pop
  """Write a message to stderr only, bypassing the logging system
1718 46fbdd04 Iustin Pop

1719 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
1720 46fbdd04 Iustin Pop

1721 46fbdd04 Iustin Pop
  @type txt: str
1722 46fbdd04 Iustin Pop
  @param txt: the message
1723 46fbdd04 Iustin Pop

1724 46fbdd04 Iustin Pop
  """
1725 46fbdd04 Iustin Pop
  _ToStream(sys.stderr, txt, *args)
1726 479636a3 Iustin Pop
1727 479636a3 Iustin Pop
1728 479636a3 Iustin Pop
class JobExecutor(object):
1729 479636a3 Iustin Pop
  """Class which manages the submission and execution of multiple jobs.
1730 479636a3 Iustin Pop

1731 479636a3 Iustin Pop
  Note that instances of this class should not be reused between
1732 479636a3 Iustin Pop
  GetResults() calls.
1733 479636a3 Iustin Pop

1734 479636a3 Iustin Pop
  """
1735 479636a3 Iustin Pop
  def __init__(self, cl=None, verbose=True):
1736 479636a3 Iustin Pop
    self.queue = []
1737 479636a3 Iustin Pop
    if cl is None:
1738 479636a3 Iustin Pop
      cl = GetClient()
1739 479636a3 Iustin Pop
    self.cl = cl
1740 479636a3 Iustin Pop
    self.verbose = verbose
1741 23b4b983 Iustin Pop
    self.jobs = []
1742 479636a3 Iustin Pop
1743 479636a3 Iustin Pop
  def QueueJob(self, name, *ops):
1744 23b4b983 Iustin Pop
    """Record a job for later submit.
1745 479636a3 Iustin Pop

1746 479636a3 Iustin Pop
    @type name: string
1747 479636a3 Iustin Pop
    @param name: a description of the job, will be used in WaitJobSet
1748 479636a3 Iustin Pop
    """
1749 23b4b983 Iustin Pop
    self.queue.append((name, ops))
1750 23b4b983 Iustin Pop
1751 23b4b983 Iustin Pop
  def SubmitPending(self):
1752 23b4b983 Iustin Pop
    """Submit all pending jobs.
1753 23b4b983 Iustin Pop

1754 23b4b983 Iustin Pop
    """
1755 23b4b983 Iustin Pop
    results = self.cl.SubmitManyJobs([row[1] for row in self.queue])
1756 23b4b983 Iustin Pop
    for ((status, data), (name, _)) in zip(results, self.queue):
1757 23b4b983 Iustin Pop
      self.jobs.append((status, data, name))
1758 479636a3 Iustin Pop
1759 479636a3 Iustin Pop
  def GetResults(self):
1760 479636a3 Iustin Pop
    """Wait for and return the results of all jobs.
1761 479636a3 Iustin Pop

1762 479636a3 Iustin Pop
    @rtype: list
1763 479636a3 Iustin Pop
    @return: list of tuples (success, job results), in the same order
1764 479636a3 Iustin Pop
        as the submitted jobs; if a job has failed, instead of the result
1765 479636a3 Iustin Pop
        there will be the error message
1766 479636a3 Iustin Pop

1767 479636a3 Iustin Pop
    """
1768 23b4b983 Iustin Pop
    if not self.jobs:
1769 23b4b983 Iustin Pop
      self.SubmitPending()
1770 479636a3 Iustin Pop
    results = []
1771 479636a3 Iustin Pop
    if self.verbose:
1772 23b4b983 Iustin Pop
      ok_jobs = [row[1] for row in self.jobs if row[0]]
1773 23b4b983 Iustin Pop
      if ok_jobs:
1774 1f864b60 Iustin Pop
        ToStdout("Submitted jobs %s", utils.CommaJoin(ok_jobs))
1775 23b4b983 Iustin Pop
    for submit_status, jid, name in self.jobs:
1776 23b4b983 Iustin Pop
      if not submit_status:
1777 23b4b983 Iustin Pop
        ToStderr("Failed to submit job for %s: %s", name, jid)
1778 23b4b983 Iustin Pop
        results.append((False, jid))
1779 23b4b983 Iustin Pop
        continue
1780 479636a3 Iustin Pop
      if self.verbose:
1781 479636a3 Iustin Pop
        ToStdout("Waiting for job %s for %s...", jid, name)
1782 479636a3 Iustin Pop
      try:
1783 479636a3 Iustin Pop
        job_result = PollJob(jid, cl=self.cl)
1784 479636a3 Iustin Pop
        success = True
1785 479636a3 Iustin Pop
      except (errors.GenericError, luxi.ProtocolError), err:
1786 479636a3 Iustin Pop
        _, job_result = FormatError(err)
1787 479636a3 Iustin Pop
        success = False
1788 479636a3 Iustin Pop
        # the error message will always be shown, verbose or not
1789 479636a3 Iustin Pop
        ToStderr("Job %s for %s has failed: %s", jid, name, job_result)
1790 479636a3 Iustin Pop
1791 479636a3 Iustin Pop
      results.append((success, job_result))
1792 479636a3 Iustin Pop
    return results
1793 479636a3 Iustin Pop
1794 479636a3 Iustin Pop
  def WaitOrShow(self, wait):
1795 479636a3 Iustin Pop
    """Wait for job results or only print the job IDs.
1796 479636a3 Iustin Pop

1797 479636a3 Iustin Pop
    @type wait: boolean
1798 479636a3 Iustin Pop
    @param wait: whether to wait or not
1799 479636a3 Iustin Pop

1800 479636a3 Iustin Pop
    """
1801 479636a3 Iustin Pop
    if wait:
1802 479636a3 Iustin Pop
      return self.GetResults()
1803 479636a3 Iustin Pop
    else:
1804 23b4b983 Iustin Pop
      if not self.jobs:
1805 23b4b983 Iustin Pop
        self.SubmitPending()
1806 23b4b983 Iustin Pop
      for status, result, name in self.jobs:
1807 23b4b983 Iustin Pop
        if status:
1808 23b4b983 Iustin Pop
          ToStdout("%s: %s", result, name)
1809 23b4b983 Iustin Pop
        else:
1810 23b4b983 Iustin Pop
          ToStderr("Failure for %s: %s", name, result)