Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ 2ea65c7d

History | View | Annotate | Download (77.3 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Module dealing with command line parsing"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
25 a8083063 Iustin Pop
import sys
26 a8083063 Iustin Pop
import textwrap
27 a8083063 Iustin Pop
import os.path
28 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 7e49b6ce Michael Hanselmann
from ganeti import ssh
40 cea881e5 Michael Hanselmann
from ganeti import compat
41 a744b676 Manuel Franceschini
from ganeti import netutils
42 a8083063 Iustin Pop
43 c38c44ad Michael Hanselmann
from optparse import (OptionParser, TitledHelpFormatter,
44 38206f3c Iustin Pop
                      Option, OptionValueError)
45 a8083063 Iustin Pop
46 03298ebe Michael Hanselmann
47 4abc4f1e Iustin Pop
__all__ = [
48 4abc4f1e Iustin Pop
  # Command line options
49 fdad8c4d Balazs Lecz
  "ADD_UIDS_OPT",
50 e7e09483 Iustin Pop
  "ALLOCATABLE_OPT",
51 2d5e7ae1 Iustin Pop
  "ALL_OPT",
52 4c61d894 Iustin Pop
  "AUTO_PROMOTE_OPT",
53 e00f7a05 Iustin Pop
  "AUTO_REPLACE_OPT",
54 087ed2ed Iustin Pop
  "BACKEND_OPT",
55 baef337d Iustin Pop
  "CLEANUP_OPT",
56 3db3eb2a Michael Hanselmann
  "CLUSTER_DOMAIN_SECRET_OPT",
57 4abc4f1e Iustin Pop
  "CONFIRM_OPT",
58 e32df528 Iustin Pop
  "CP_SIZE_OPT",
59 4abc4f1e Iustin Pop
  "DEBUG_OPT",
60 a0c9776a Iustin Pop
  "DEBUG_SIMERR_OPT",
61 4b038a1e Iustin Pop
  "DISKIDX_OPT",
62 e3876ccb Iustin Pop
  "DISK_OPT",
63 4b038a1e Iustin Pop
  "DISK_TEMPLATE_OPT",
64 771734c9 Iustin Pop
  "DRAINED_OPT",
65 26591bfd Luca Bigliardi
  "DRBD_HELPER_OPT",
66 7ea7bcf6 Iustin Pop
  "EARLY_RELEASE_OPT",
67 383a3591 Iustin Pop
  "ENABLED_HV_OPT",
68 14e9e7f3 Iustin Pop
  "ERROR_CODES_OPT",
69 4abc4f1e Iustin Pop
  "FIELDS_OPT",
70 4a25828c Iustin Pop
  "FILESTORE_DIR_OPT",
71 0f87c43e Iustin Pop
  "FILESTORE_DRIVER_OPT",
72 06073e85 Guido Trotter
  "FORCE_OPT",
73 06073e85 Guido Trotter
  "FORCE_VARIANT_OPT",
74 29392516 Iustin Pop
  "GLOBAL_FILEDIR_OPT",
75 073271f6 Iustin Pop
  "HVLIST_OPT",
76 48f212d7 Iustin Pop
  "HVOPTS_OPT",
77 236fd9c4 Iustin Pop
  "HYPERVISOR_OPT",
78 4eb62659 Iustin Pop
  "IALLOCATOR_OPT",
79 bf4af505 Apollon Oikonomopoulos
  "DEFAULT_IALLOCATOR_OPT",
80 e588764d Iustin Pop
  "IDENTIFY_DEFAULTS_OPT",
81 82a786d5 Iustin Pop
  "IGNORE_CONSIST_OPT",
82 b6e841a8 Iustin Pop
  "IGNORE_FAILURES_OPT",
83 8d8d650c Michael Hanselmann
  "IGNORE_REMOVE_FAILURES_OPT",
84 ee3f9578 Iustin Pop
  "IGNORE_SECONDARIES_OPT",
85 05586c90 Iustin Pop
  "IGNORE_SIZE_OPT",
86 e3646f22 Iustin Pop
  "MAC_PREFIX_OPT",
87 3953242f Iustin Pop
  "MAINTAIN_NODE_HEALTH_OPT",
88 29392516 Iustin Pop
  "MASTER_NETDEV_OPT",
89 771734c9 Iustin Pop
  "MC_OPT",
90 e71b9ef4 Iustin Pop
  "MIGRATION_TYPE_OPT",
91 7d3a9fab Iustin Pop
  "NET_OPT",
92 6d4a1656 Michael Hanselmann
  "NEW_CLUSTER_CERT_OPT",
93 3db3eb2a Michael Hanselmann
  "NEW_CLUSTER_DOMAIN_SECRET_OPT",
94 6b7d5878 Michael Hanselmann
  "NEW_CONFD_HMAC_KEY_OPT",
95 6d4a1656 Michael Hanselmann
  "NEW_RAPI_CERT_OPT",
96 a14db5ff Iustin Pop
  "NEW_SECONDARY_OPT",
97 4fbc93dd Iustin Pop
  "NIC_PARAMS_OPT",
98 7edc4637 Iustin Pop
  "NODE_LIST_OPT",
99 990b7886 Iustin Pop
  "NODE_PLACEMENT_OPT",
100 26591bfd Luca Bigliardi
  "NODRBD_STORAGE_OPT",
101 4abc4f1e Iustin Pop
  "NOHDR_OPT",
102 91e0748c Iustin Pop
  "NOIPCHECK_OPT",
103 25a8792c Iustin Pop
  "NO_INSTALL_OPT",
104 460d22be Iustin Pop
  "NONAMECHECK_OPT",
105 831040bf Iustin Pop
  "NOLVM_STORAGE_OPT",
106 29392516 Iustin Pop
  "NOMODIFY_ETCHOSTS_OPT",
107 b989b9d9 Ken Wehr
  "NOMODIFY_SSH_SETUP_OPT",
108 26023ecd Iustin Pop
  "NONICS_OPT",
109 f2a0828c Iustin Pop
  "NONLIVE_OPT",
110 14e9e7f3 Iustin Pop
  "NONPLUS1_OPT",
111 44c44832 Iustin Pop
  "NOSHUTDOWN_OPT",
112 edeb878a Iustin Pop
  "NOSTART_OPT",
113 fcdde7f2 Iustin Pop
  "NOSSH_KEYCHECK_OPT",
114 58371861 Iustin Pop
  "NOVOTING_OPT",
115 3f75b4f3 Iustin Pop
  "NWSYNC_OPT",
116 a72d0a79 Iustin Pop
  "ON_PRIMARY_OPT",
117 feb09e6a Iustin Pop
  "ON_SECONDARY_OPT",
118 771734c9 Iustin Pop
  "OFFLINE_OPT",
119 062a7100 Iustin Pop
  "OSPARAMS_OPT",
120 d3ed23ff Iustin Pop
  "OS_OPT",
121 ff00c1a7 Iustin Pop
  "OS_SIZE_OPT",
122 6d4a1656 Michael Hanselmann
  "RAPI_CERT_OPT",
123 b8d0f938 Iustin Pop
  "READD_OPT",
124 12054861 Iustin Pop
  "REBOOT_TYPE_OPT",
125 8d8d650c Michael Hanselmann
  "REMOVE_INSTANCE_OPT",
126 fdad8c4d Balazs Lecz
  "REMOVE_UIDS_OPT",
127 31d97b2a Guido Trotter
  "ROMAN_OPT",
128 8d823629 Iustin Pop
  "SECONDARY_IP_OPT",
129 67840b40 Iustin Pop
  "SELECT_OS_OPT",
130 4abc4f1e Iustin Pop
  "SEP_OPT",
131 fdcf4d84 Iustin Pop
  "SHOWCMD_OPT",
132 7e5eaaa8 Guido Trotter
  "SHUTDOWN_TIMEOUT_OPT",
133 f36d7d81 Iustin Pop
  "SINGLE_NODE_OPT",
134 df62e5db Iustin Pop
  "SRC_DIR_OPT",
135 df62e5db Iustin Pop
  "SRC_NODE_OPT",
136 4abc4f1e Iustin Pop
  "SUBMIT_OPT",
137 99a8c799 Iustin Pop
  "STATIC_OPT",
138 4abc4f1e Iustin Pop
  "SYNC_OPT",
139 4abc4f1e Iustin Pop
  "TAG_SRC_OPT",
140 b5762e2a Guido Trotter
  "TIMEOUT_OPT",
141 1338f2b4 Balazs Lecz
  "UIDPOOL_OPT",
142 4abc4f1e Iustin Pop
  "USEUNITS_OPT",
143 74adc100 Iustin Pop
  "USE_REPL_NET_OPT",
144 9cdb9578 Iustin Pop
  "VERBOSE_OPT",
145 b58726e8 Iustin Pop
  "VG_NAME_OPT",
146 1f587d3d Iustin Pop
  "YES_DOIT_OPT",
147 4abc4f1e Iustin Pop
  # Generic functions for CLI programs
148 4abc4f1e Iustin Pop
  "GenericMain",
149 d77490c5 Iustin Pop
  "GenericInstanceCreate",
150 4abc4f1e Iustin Pop
  "GetClient",
151 4abc4f1e Iustin Pop
  "GetOnlineNodes",
152 4abc4f1e Iustin Pop
  "JobExecutor",
153 4abc4f1e Iustin Pop
  "JobSubmittedException",
154 4abc4f1e Iustin Pop
  "ParseTimespec",
155 7e49b6ce Michael Hanselmann
  "RunWhileClusterStopped",
156 4abc4f1e Iustin Pop
  "SubmitOpCode",
157 4abc4f1e Iustin Pop
  "SubmitOrSend",
158 4abc4f1e Iustin Pop
  "UsesRPC",
159 4abc4f1e Iustin Pop
  # Formatting functions
160 4abc4f1e Iustin Pop
  "ToStderr", "ToStdout",
161 4abc4f1e Iustin Pop
  "FormatError",
162 4abc4f1e Iustin Pop
  "GenerateTable",
163 4abc4f1e Iustin Pop
  "AskUser",
164 4abc4f1e Iustin Pop
  "FormatTimestamp",
165 8a7f1c61 Michael Hanselmann
  "FormatLogMessage",
166 4abc4f1e Iustin Pop
  # Tags functions
167 4abc4f1e Iustin Pop
  "ListTags",
168 4abc4f1e Iustin Pop
  "AddTags",
169 4abc4f1e Iustin Pop
  "RemoveTags",
170 4abc4f1e Iustin Pop
  # command line options support infrastructure
171 4abc4f1e Iustin Pop
  "ARGS_MANY_INSTANCES",
172 4abc4f1e Iustin Pop
  "ARGS_MANY_NODES",
173 4abc4f1e Iustin Pop
  "ARGS_NONE",
174 4abc4f1e Iustin Pop
  "ARGS_ONE_INSTANCE",
175 4abc4f1e Iustin Pop
  "ARGS_ONE_NODE",
176 f9faf9c3 Renรฉ Nussbaumer
  "ARGS_ONE_OS",
177 4abc4f1e Iustin Pop
  "ArgChoice",
178 4abc4f1e Iustin Pop
  "ArgCommand",
179 4abc4f1e Iustin Pop
  "ArgFile",
180 4abc4f1e Iustin Pop
  "ArgHost",
181 4abc4f1e Iustin Pop
  "ArgInstance",
182 4abc4f1e Iustin Pop
  "ArgJobId",
183 4abc4f1e Iustin Pop
  "ArgNode",
184 f9faf9c3 Renรฉ Nussbaumer
  "ArgOs",
185 4abc4f1e Iustin Pop
  "ArgSuggest",
186 4abc4f1e Iustin Pop
  "ArgUnknown",
187 4abc4f1e Iustin Pop
  "OPT_COMPL_INST_ADD_NODES",
188 4abc4f1e Iustin Pop
  "OPT_COMPL_MANY_NODES",
189 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_IALLOCATOR",
190 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_INSTANCE",
191 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_NODE",
192 4abc4f1e Iustin Pop
  "OPT_COMPL_ONE_OS",
193 4abc4f1e Iustin Pop
  "cli_option",
194 4abc4f1e Iustin Pop
  "SplitNodeOption",
195 07150497 Guido Trotter
  "CalculateOSNames",
196 4abc4f1e Iustin Pop
  ]
197 846baef9 Iustin Pop
198 8b46606c Guido Trotter
NO_PREFIX = "no_"
199 8b46606c Guido Trotter
UN_PREFIX = "-"
200 846baef9 Iustin Pop
201 03298ebe Michael Hanselmann
202 863d7f46 Michael Hanselmann
class _Argument:
203 7260cfbe Iustin Pop
  def __init__(self, min=0, max=None): # pylint: disable-msg=W0622
204 863d7f46 Michael Hanselmann
    self.min = min
205 863d7f46 Michael Hanselmann
    self.max = max
206 863d7f46 Michael Hanselmann
207 863d7f46 Michael Hanselmann
  def __repr__(self):
208 863d7f46 Michael Hanselmann
    return ("<%s min=%s max=%s>" %
209 863d7f46 Michael Hanselmann
            (self.__class__.__name__, self.min, self.max))
210 863d7f46 Michael Hanselmann
211 863d7f46 Michael Hanselmann
212 863d7f46 Michael Hanselmann
class ArgSuggest(_Argument):
213 863d7f46 Michael Hanselmann
  """Suggesting argument.
214 863d7f46 Michael Hanselmann

215 863d7f46 Michael Hanselmann
  Value can be any of the ones passed to the constructor.
216 863d7f46 Michael Hanselmann

217 863d7f46 Michael Hanselmann
  """
218 7260cfbe Iustin Pop
  # pylint: disable-msg=W0622
219 863d7f46 Michael Hanselmann
  def __init__(self, min=0, max=None, choices=None):
220 863d7f46 Michael Hanselmann
    _Argument.__init__(self, min=min, max=max)
221 863d7f46 Michael Hanselmann
    self.choices = choices
222 863d7f46 Michael Hanselmann
223 863d7f46 Michael Hanselmann
  def __repr__(self):
224 863d7f46 Michael Hanselmann
    return ("<%s min=%s max=%s choices=%r>" %
225 863d7f46 Michael Hanselmann
            (self.__class__.__name__, self.min, self.max, self.choices))
226 863d7f46 Michael Hanselmann
227 863d7f46 Michael Hanselmann
228 863d7f46 Michael Hanselmann
class ArgChoice(ArgSuggest):
229 863d7f46 Michael Hanselmann
  """Choice argument.
230 863d7f46 Michael Hanselmann

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

234 863d7f46 Michael Hanselmann
  """
235 863d7f46 Michael Hanselmann
236 863d7f46 Michael Hanselmann
237 863d7f46 Michael Hanselmann
class ArgUnknown(_Argument):
238 863d7f46 Michael Hanselmann
  """Unknown argument to program (e.g. determined at runtime).
239 863d7f46 Michael Hanselmann

240 863d7f46 Michael Hanselmann
  """
241 863d7f46 Michael Hanselmann
242 863d7f46 Michael Hanselmann
243 863d7f46 Michael Hanselmann
class ArgInstance(_Argument):
244 863d7f46 Michael Hanselmann
  """Instances argument.
245 863d7f46 Michael Hanselmann

246 863d7f46 Michael Hanselmann
  """
247 863d7f46 Michael Hanselmann
248 863d7f46 Michael Hanselmann
249 863d7f46 Michael Hanselmann
class ArgNode(_Argument):
250 863d7f46 Michael Hanselmann
  """Node argument.
251 863d7f46 Michael Hanselmann

252 863d7f46 Michael Hanselmann
  """
253 863d7f46 Michael Hanselmann
254 863d7f46 Michael Hanselmann
class ArgJobId(_Argument):
255 863d7f46 Michael Hanselmann
  """Job ID argument.
256 863d7f46 Michael Hanselmann

257 863d7f46 Michael Hanselmann
  """
258 863d7f46 Michael Hanselmann
259 863d7f46 Michael Hanselmann
260 863d7f46 Michael Hanselmann
class ArgFile(_Argument):
261 863d7f46 Michael Hanselmann
  """File path argument.
262 863d7f46 Michael Hanselmann

263 863d7f46 Michael Hanselmann
  """
264 863d7f46 Michael Hanselmann
265 863d7f46 Michael Hanselmann
266 863d7f46 Michael Hanselmann
class ArgCommand(_Argument):
267 863d7f46 Michael Hanselmann
  """Command argument.
268 863d7f46 Michael Hanselmann

269 863d7f46 Michael Hanselmann
  """
270 863d7f46 Michael Hanselmann
271 863d7f46 Michael Hanselmann
272 83ec7961 Michael Hanselmann
class ArgHost(_Argument):
273 83ec7961 Michael Hanselmann
  """Host argument.
274 83ec7961 Michael Hanselmann

275 83ec7961 Michael Hanselmann
  """
276 83ec7961 Michael Hanselmann
277 83ec7961 Michael Hanselmann
278 f9faf9c3 Renรฉ Nussbaumer
class ArgOs(_Argument):
279 f9faf9c3 Renรฉ Nussbaumer
  """OS argument.
280 f9faf9c3 Renรฉ Nussbaumer

281 f9faf9c3 Renรฉ Nussbaumer
  """
282 f9faf9c3 Renรฉ Nussbaumer
283 f9faf9c3 Renรฉ Nussbaumer
284 4a265c08 Michael Hanselmann
ARGS_NONE = []
285 4a265c08 Michael Hanselmann
ARGS_MANY_INSTANCES = [ArgInstance()]
286 4a265c08 Michael Hanselmann
ARGS_MANY_NODES = [ArgNode()]
287 4a265c08 Michael Hanselmann
ARGS_ONE_INSTANCE = [ArgInstance(min=1, max=1)]
288 4a265c08 Michael Hanselmann
ARGS_ONE_NODE = [ArgNode(min=1, max=1)]
289 f9faf9c3 Renรฉ Nussbaumer
ARGS_ONE_OS = [ArgOs(min=1, max=1)]
290 4a265c08 Michael Hanselmann
291 4a265c08 Michael Hanselmann
292 846baef9 Iustin Pop
def _ExtractTagsObject(opts, args):
293 846baef9 Iustin Pop
  """Extract the tag type object.
294 846baef9 Iustin Pop

295 846baef9 Iustin Pop
  Note that this function will modify its args parameter.
296 846baef9 Iustin Pop

297 846baef9 Iustin Pop
  """
298 846baef9 Iustin Pop
  if not hasattr(opts, "tag_type"):
299 846baef9 Iustin Pop
    raise errors.ProgrammerError("tag_type not passed to _ExtractTagsObject")
300 846baef9 Iustin Pop
  kind = opts.tag_type
301 846baef9 Iustin Pop
  if kind == constants.TAG_CLUSTER:
302 846baef9 Iustin Pop
    retval = kind, kind
303 846baef9 Iustin Pop
  elif kind == constants.TAG_NODE or kind == constants.TAG_INSTANCE:
304 846baef9 Iustin Pop
    if not args:
305 0c434948 Iustin Pop
      raise errors.OpPrereqError("no arguments passed to the command")
306 846baef9 Iustin Pop
    name = args.pop(0)
307 846baef9 Iustin Pop
    retval = kind, name
308 846baef9 Iustin Pop
  else:
309 846baef9 Iustin Pop
    raise errors.ProgrammerError("Unhandled tag type '%s'" % kind)
310 846baef9 Iustin Pop
  return retval
311 846baef9 Iustin Pop
312 846baef9 Iustin Pop
313 810c50b7 Iustin Pop
def _ExtendTags(opts, args):
314 810c50b7 Iustin Pop
  """Extend the args if a source file has been given.
315 810c50b7 Iustin Pop

316 810c50b7 Iustin Pop
  This function will extend the tags with the contents of the file
317 810c50b7 Iustin Pop
  passed in the 'tags_source' attribute of the opts parameter. A file
318 810c50b7 Iustin Pop
  named '-' will be replaced by stdin.
319 810c50b7 Iustin Pop

320 810c50b7 Iustin Pop
  """
321 810c50b7 Iustin Pop
  fname = opts.tags_source
322 810c50b7 Iustin Pop
  if fname is None:
323 810c50b7 Iustin Pop
    return
324 810c50b7 Iustin Pop
  if fname == "-":
325 810c50b7 Iustin Pop
    new_fh = sys.stdin
326 810c50b7 Iustin Pop
  else:
327 810c50b7 Iustin Pop
    new_fh = open(fname, "r")
328 810c50b7 Iustin Pop
  new_data = []
329 810c50b7 Iustin Pop
  try:
330 810c50b7 Iustin Pop
    # we don't use the nice 'new_data = [line.strip() for line in fh]'
331 810c50b7 Iustin Pop
    # because of python bug 1633941
332 810c50b7 Iustin Pop
    while True:
333 810c50b7 Iustin Pop
      line = new_fh.readline()
334 810c50b7 Iustin Pop
      if not line:
335 810c50b7 Iustin Pop
        break
336 810c50b7 Iustin Pop
      new_data.append(line.strip())
337 810c50b7 Iustin Pop
  finally:
338 810c50b7 Iustin Pop
    new_fh.close()
339 810c50b7 Iustin Pop
  args.extend(new_data)
340 810c50b7 Iustin Pop
341 810c50b7 Iustin Pop
342 846baef9 Iustin Pop
def ListTags(opts, args):
343 846baef9 Iustin Pop
  """List the tags on a given object.
344 846baef9 Iustin Pop

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

350 846baef9 Iustin Pop
  """
351 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
352 7699c3af Iustin Pop
  cl = GetClient()
353 7699c3af Iustin Pop
  result = cl.QueryTags(kind, name)
354 846baef9 Iustin Pop
  result = list(result)
355 846baef9 Iustin Pop
  result.sort()
356 846baef9 Iustin Pop
  for tag in result:
357 03298ebe Michael Hanselmann
    ToStdout(tag)
358 846baef9 Iustin Pop
359 846baef9 Iustin Pop
360 846baef9 Iustin Pop
def AddTags(opts, args):
361 846baef9 Iustin Pop
  """Add tags on a given object.
362 846baef9 Iustin Pop

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

368 846baef9 Iustin Pop
  """
369 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
370 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
371 846baef9 Iustin Pop
  if not args:
372 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be added")
373 846baef9 Iustin Pop
  op = opcodes.OpAddTags(kind=kind, name=name, tags=args)
374 846baef9 Iustin Pop
  SubmitOpCode(op)
375 846baef9 Iustin Pop
376 846baef9 Iustin Pop
377 846baef9 Iustin Pop
def RemoveTags(opts, args):
378 846baef9 Iustin Pop
  """Remove tags from a given object.
379 846baef9 Iustin Pop

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

385 846baef9 Iustin Pop
  """
386 846baef9 Iustin Pop
  kind, name = _ExtractTagsObject(opts, args)
387 810c50b7 Iustin Pop
  _ExtendTags(opts, args)
388 846baef9 Iustin Pop
  if not args:
389 846baef9 Iustin Pop
    raise errors.OpPrereqError("No tags to be removed")
390 846baef9 Iustin Pop
  op = opcodes.OpDelTags(kind=kind, name=name, tags=args)
391 846baef9 Iustin Pop
  SubmitOpCode(op)
392 846baef9 Iustin Pop
393 a8083063 Iustin Pop
394 8929d28c Iustin Pop
def check_unit(option, opt, value): # pylint: disable-msg=W0613
395 65fe4693 Iustin Pop
  """OptParsers custom converter for units.
396 65fe4693 Iustin Pop

397 65fe4693 Iustin Pop
  """
398 a8083063 Iustin Pop
  try:
399 a8083063 Iustin Pop
    return utils.ParseUnit(value)
400 a8083063 Iustin Pop
  except errors.UnitParseError, err:
401 3ecf6786 Iustin Pop
    raise OptionValueError("option %s: %s" % (opt, err))
402 a8083063 Iustin Pop
403 a8083063 Iustin Pop
404 a8469393 Iustin Pop
def _SplitKeyVal(opt, data):
405 a8469393 Iustin Pop
  """Convert a KeyVal string into a dict.
406 a8469393 Iustin Pop

407 a8469393 Iustin Pop
  This function will convert a key=val[,...] string into a dict. Empty
408 a8469393 Iustin Pop
  values will be converted specially: keys which have the prefix 'no_'
409 a8469393 Iustin Pop
  will have the value=False and the prefix stripped, the others will
410 a8469393 Iustin Pop
  have value=True.
411 a8469393 Iustin Pop

412 a8469393 Iustin Pop
  @type opt: string
413 a8469393 Iustin Pop
  @param opt: a string holding the option name for which we process the
414 a8469393 Iustin Pop
      data, used in building error messages
415 a8469393 Iustin Pop
  @type data: string
416 a8469393 Iustin Pop
  @param data: a string of the format key=val,key=val,...
417 a8469393 Iustin Pop
  @rtype: dict
418 a8469393 Iustin Pop
  @return: {key=val, key=val}
419 a8469393 Iustin Pop
  @raises errors.ParameterError: if there are duplicate keys
420 a8469393 Iustin Pop

421 a8469393 Iustin Pop
  """
422 a8469393 Iustin Pop
  kv_dict = {}
423 4f31882e Guido Trotter
  if data:
424 1b3a7656 Iustin Pop
    for elem in utils.UnescapeAndSplit(data, sep=","):
425 4f31882e Guido Trotter
      if "=" in elem:
426 4f31882e Guido Trotter
        key, val = elem.split("=", 1)
427 a8469393 Iustin Pop
      else:
428 4f31882e Guido Trotter
        if elem.startswith(NO_PREFIX):
429 4f31882e Guido Trotter
          key, val = elem[len(NO_PREFIX):], False
430 4f31882e Guido Trotter
        elif elem.startswith(UN_PREFIX):
431 4f31882e Guido Trotter
          key, val = elem[len(UN_PREFIX):], None
432 4f31882e Guido Trotter
        else:
433 4f31882e Guido Trotter
          key, val = elem, True
434 4f31882e Guido Trotter
      if key in kv_dict:
435 4f31882e Guido Trotter
        raise errors.ParameterError("Duplicate key '%s' in option %s" %
436 4f31882e Guido Trotter
                                    (key, opt))
437 4f31882e Guido Trotter
      kv_dict[key] = val
438 a8469393 Iustin Pop
  return kv_dict
439 a8469393 Iustin Pop
440 a8469393 Iustin Pop
441 8929d28c Iustin Pop
def check_ident_key_val(option, opt, value):  # pylint: disable-msg=W0613
442 552c8dff Michael Hanselmann
  """Custom parser for ident:key=val,key=val options.
443 552c8dff Michael Hanselmann

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

447 a8469393 Iustin Pop
  """
448 a8469393 Iustin Pop
  if ":" not in value:
449 8b46606c Guido Trotter
    ident, rest = value, ''
450 a8469393 Iustin Pop
  else:
451 a8469393 Iustin Pop
    ident, rest = value.split(":", 1)
452 8b46606c Guido Trotter
453 8b46606c Guido Trotter
  if ident.startswith(NO_PREFIX):
454 8b46606c Guido Trotter
    if rest:
455 8b46606c Guido Trotter
      msg = "Cannot pass options when removing parameter groups: %s" % value
456 8b46606c Guido Trotter
      raise errors.ParameterError(msg)
457 8b46606c Guido Trotter
    retval = (ident[len(NO_PREFIX):], False)
458 8b46606c Guido Trotter
  elif ident.startswith(UN_PREFIX):
459 8b46606c Guido Trotter
    if rest:
460 8b46606c Guido Trotter
      msg = "Cannot pass options when removing parameter groups: %s" % value
461 8b46606c Guido Trotter
      raise errors.ParameterError(msg)
462 8b46606c Guido Trotter
    retval = (ident[len(UN_PREFIX):], None)
463 8b46606c Guido Trotter
  else:
464 a8469393 Iustin Pop
    kv_dict = _SplitKeyVal(opt, rest)
465 a8469393 Iustin Pop
    retval = (ident, kv_dict)
466 a8469393 Iustin Pop
  return retval
467 a8469393 Iustin Pop
468 a8469393 Iustin Pop
469 8929d28c Iustin Pop
def check_key_val(option, opt, value):  # pylint: disable-msg=W0613
470 552c8dff Michael Hanselmann
  """Custom parser class for key=val,key=val options.
471 552c8dff Michael Hanselmann

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

474 a8469393 Iustin Pop
  """
475 a8469393 Iustin Pop
  return _SplitKeyVal(opt, value)
476 a8469393 Iustin Pop
477 a8469393 Iustin Pop
478 e7b61bb0 Iustin Pop
def check_bool(option, opt, value): # pylint: disable-msg=W0613
479 e7b61bb0 Iustin Pop
  """Custom parser for yes/no options.
480 e7b61bb0 Iustin Pop

481 e7b61bb0 Iustin Pop
  This will store the parsed value as either True or False.
482 e7b61bb0 Iustin Pop

483 e7b61bb0 Iustin Pop
  """
484 e7b61bb0 Iustin Pop
  value = value.lower()
485 e7b61bb0 Iustin Pop
  if value == constants.VALUE_FALSE or value == "no":
486 e7b61bb0 Iustin Pop
    return False
487 e7b61bb0 Iustin Pop
  elif value == constants.VALUE_TRUE or value == "yes":
488 e7b61bb0 Iustin Pop
    return True
489 e7b61bb0 Iustin Pop
  else:
490 e7b61bb0 Iustin Pop
    raise errors.ParameterError("Invalid boolean value '%s'" % value)
491 e7b61bb0 Iustin Pop
492 e7b61bb0 Iustin Pop
493 63d44c55 Michael Hanselmann
# completion_suggestion is normally a list. Using numeric values not evaluating
494 63d44c55 Michael Hanselmann
# to False for dynamic completion.
495 63d44c55 Michael Hanselmann
(OPT_COMPL_MANY_NODES,
496 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_NODE,
497 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_INSTANCE,
498 63d44c55 Michael Hanselmann
 OPT_COMPL_ONE_OS,
499 2d3ed64b Michael Hanselmann
 OPT_COMPL_ONE_IALLOCATOR,
500 2d3ed64b Michael Hanselmann
 OPT_COMPL_INST_ADD_NODES) = range(100, 106)
501 63d44c55 Michael Hanselmann
502 63d44c55 Michael Hanselmann
OPT_COMPL_ALL = frozenset([
503 63d44c55 Michael Hanselmann
  OPT_COMPL_MANY_NODES,
504 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_NODE,
505 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_INSTANCE,
506 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_OS,
507 63d44c55 Michael Hanselmann
  OPT_COMPL_ONE_IALLOCATOR,
508 2d3ed64b Michael Hanselmann
  OPT_COMPL_INST_ADD_NODES,
509 63d44c55 Michael Hanselmann
  ])
510 63d44c55 Michael Hanselmann
511 63d44c55 Michael Hanselmann
512 552c8dff Michael Hanselmann
class CliOption(Option):
513 552c8dff Michael Hanselmann
  """Custom option class for optparse.
514 a8469393 Iustin Pop

515 a8469393 Iustin Pop
  """
516 863d7f46 Michael Hanselmann
  ATTRS = Option.ATTRS + [
517 863d7f46 Michael Hanselmann
    "completion_suggest",
518 863d7f46 Michael Hanselmann
    ]
519 552c8dff Michael Hanselmann
  TYPES = Option.TYPES + (
520 552c8dff Michael Hanselmann
    "identkeyval",
521 552c8dff Michael Hanselmann
    "keyval",
522 552c8dff Michael Hanselmann
    "unit",
523 e7b61bb0 Iustin Pop
    "bool",
524 552c8dff Michael Hanselmann
    )
525 552c8dff Michael Hanselmann
  TYPE_CHECKER = Option.TYPE_CHECKER.copy()
526 552c8dff Michael Hanselmann
  TYPE_CHECKER["identkeyval"] = check_ident_key_val
527 a8469393 Iustin Pop
  TYPE_CHECKER["keyval"] = check_key_val
528 552c8dff Michael Hanselmann
  TYPE_CHECKER["unit"] = check_unit
529 e7b61bb0 Iustin Pop
  TYPE_CHECKER["bool"] = check_bool
530 a8469393 Iustin Pop
531 a8469393 Iustin Pop
532 a8083063 Iustin Pop
# optparse.py sets make_option, so we do it for our own option class, too
533 a8083063 Iustin Pop
cli_option = CliOption
534 a8083063 Iustin Pop
535 a8083063 Iustin Pop
536 771734c9 Iustin Pop
_YORNO = "yes|no"
537 771734c9 Iustin Pop
538 ea34193f Iustin Pop
DEBUG_OPT = cli_option("-d", "--debug", default=0, action="count",
539 ea34193f Iustin Pop
                       help="Increase debugging level")
540 c38c44ad Michael Hanselmann
541 c38c44ad Michael Hanselmann
NOHDR_OPT = cli_option("--no-headers", default=False,
542 c38c44ad Michael Hanselmann
                       action="store_true", dest="no_headers",
543 c38c44ad Michael Hanselmann
                       help="Don't display column headers")
544 c38c44ad Michael Hanselmann
545 c38c44ad Michael Hanselmann
SEP_OPT = cli_option("--separator", default=None,
546 c38c44ad Michael Hanselmann
                     action="store", dest="separator",
547 c38c44ad Michael Hanselmann
                     help=("Separator between output fields"
548 c38c44ad Michael Hanselmann
                           " (defaults to one space)"))
549 c38c44ad Michael Hanselmann
550 c38c44ad Michael Hanselmann
USEUNITS_OPT = cli_option("--units", default=None,
551 c38c44ad Michael Hanselmann
                          dest="units", choices=('h', 'm', 'g', 't'),
552 c38c44ad Michael Hanselmann
                          help="Specify units for output (one of hmgt)")
553 c38c44ad Michael Hanselmann
554 c38c44ad Michael Hanselmann
FIELDS_OPT = cli_option("-o", "--output", dest="output", action="store",
555 c38c44ad Michael Hanselmann
                        type="string", metavar="FIELDS",
556 c38c44ad Michael Hanselmann
                        help="Comma separated list of output fields")
557 c38c44ad Michael Hanselmann
558 c38c44ad Michael Hanselmann
FORCE_OPT = cli_option("-f", "--force", dest="force", action="store_true",
559 c38c44ad Michael Hanselmann
                       default=False, help="Force the operation")
560 c38c44ad Michael Hanselmann
561 c38c44ad Michael Hanselmann
CONFIRM_OPT = cli_option("--yes", dest="confirm", action="store_true",
562 c38c44ad Michael Hanselmann
                         default=False, help="Do not require confirmation")
563 c38c44ad Michael Hanselmann
564 c38c44ad Michael Hanselmann
TAG_SRC_OPT = cli_option("--from", dest="tags_source",
565 c38c44ad Michael Hanselmann
                         default=None, help="File with tag names")
566 c38c44ad Michael Hanselmann
567 c38c44ad Michael Hanselmann
SUBMIT_OPT = cli_option("--submit", dest="submit_only",
568 c38c44ad Michael Hanselmann
                        default=False, action="store_true",
569 c38c44ad Michael Hanselmann
                        help=("Submit the job and return the job ID, but"
570 c38c44ad Michael Hanselmann
                              " don't wait for the job to finish"))
571 c38c44ad Michael Hanselmann
572 c38c44ad Michael Hanselmann
SYNC_OPT = cli_option("--sync", dest="do_locking",
573 c38c44ad Michael Hanselmann
                      default=False, action="store_true",
574 c38c44ad Michael Hanselmann
                      help=("Grab locks while doing the queries"
575 c38c44ad Michael Hanselmann
                            " in order to ensure more consistent results"))
576 c38c44ad Michael Hanselmann
577 c38c44ad Michael Hanselmann
_DRY_RUN_OPT = cli_option("--dry-run", default=False,
578 c38c44ad Michael Hanselmann
                          action="store_true",
579 c38c44ad Michael Hanselmann
                          help=("Do not execute the operation, just run the"
580 c38c44ad Michael Hanselmann
                                " check steps and verify it it could be"
581 c38c44ad Michael Hanselmann
                                " executed"))
582 c38c44ad Michael Hanselmann
583 9cdb9578 Iustin Pop
VERBOSE_OPT = cli_option("-v", "--verbose", default=False,
584 9cdb9578 Iustin Pop
                         action="store_true",
585 9cdb9578 Iustin Pop
                         help="Increase the verbosity of the operation")
586 9cdb9578 Iustin Pop
587 a0c9776a Iustin Pop
DEBUG_SIMERR_OPT = cli_option("--debug-simulate-errors", default=False,
588 a0c9776a Iustin Pop
                              action="store_true", dest="simulate_errors",
589 a0c9776a Iustin Pop
                              help="Debugging option that makes the operation"
590 a0c9776a Iustin Pop
                              " treat most runtime checks as failed")
591 a0c9776a Iustin Pop
592 3f75b4f3 Iustin Pop
NWSYNC_OPT = cli_option("--no-wait-for-sync", dest="wait_for_sync",
593 3f75b4f3 Iustin Pop
                        default=True, action="store_false",
594 3f75b4f3 Iustin Pop
                        help="Don't wait for sync (DANGEROUS!)")
595 3f75b4f3 Iustin Pop
596 4f365444 Iustin Pop
DISK_TEMPLATE_OPT = cli_option("-t", "--disk-template", dest="disk_template",
597 4f365444 Iustin Pop
                               help="Custom disk setup (diskless, file,"
598 4f365444 Iustin Pop
                               " plain or drbd)",
599 4f365444 Iustin Pop
                               default=None, metavar="TEMPL",
600 4f365444 Iustin Pop
                               choices=list(constants.DISK_TEMPLATES))
601 4f365444 Iustin Pop
602 26023ecd Iustin Pop
NONICS_OPT = cli_option("--no-nics", default=False, action="store_true",
603 26023ecd Iustin Pop
                        help="Do not create any network cards for"
604 26023ecd Iustin Pop
                        " the instance")
605 26023ecd Iustin Pop
606 4a25828c Iustin Pop
FILESTORE_DIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
607 4a25828c Iustin Pop
                               help="Relative path under default cluster-wide"
608 4a25828c Iustin Pop
                               " file storage dir to store file-based disks",
609 4a25828c Iustin Pop
                               default=None, metavar="<DIR>")
610 4a25828c Iustin Pop
611 0f87c43e Iustin Pop
FILESTORE_DRIVER_OPT = cli_option("--file-driver", dest="file_driver",
612 0f87c43e Iustin Pop
                                  help="Driver to use for image files",
613 0f87c43e Iustin Pop
                                  default="loop", metavar="<DRIVER>",
614 0f87c43e Iustin Pop
                                  choices=list(constants.FILE_DRIVER))
615 0f87c43e Iustin Pop
616 4eb62659 Iustin Pop
IALLOCATOR_OPT = cli_option("-I", "--iallocator", metavar="<NAME>",
617 4eb62659 Iustin Pop
                            help="Select nodes for the instance automatically"
618 4eb62659 Iustin Pop
                            " using the <NAME> iallocator plugin",
619 4eb62659 Iustin Pop
                            default=None, type="string",
620 4eb62659 Iustin Pop
                            completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
621 4eb62659 Iustin Pop
622 bf4af505 Apollon Oikonomopoulos
DEFAULT_IALLOCATOR_OPT = cli_option("-I", "--default-iallocator",
623 bf4af505 Apollon Oikonomopoulos
                            metavar="<NAME>",
624 bf4af505 Apollon Oikonomopoulos
                            help="Set the default instance allocator plugin",
625 bf4af505 Apollon Oikonomopoulos
                            default=None, type="string",
626 bf4af505 Apollon Oikonomopoulos
                            completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
627 bf4af505 Apollon Oikonomopoulos
628 d3ed23ff Iustin Pop
OS_OPT = cli_option("-o", "--os-type", dest="os", help="What OS to run",
629 d3ed23ff Iustin Pop
                    metavar="<os>",
630 d3ed23ff Iustin Pop
                    completion_suggest=OPT_COMPL_ONE_OS)
631 d3ed23ff Iustin Pop
632 062a7100 Iustin Pop
OSPARAMS_OPT = cli_option("-O", "--os-parameters", dest="osparams",
633 062a7100 Iustin Pop
                         type="keyval", default={},
634 062a7100 Iustin Pop
                         help="OS parameters")
635 062a7100 Iustin Pop
636 06073e85 Guido Trotter
FORCE_VARIANT_OPT = cli_option("--force-variant", dest="force_variant",
637 06073e85 Guido Trotter
                               action="store_true", default=False,
638 06073e85 Guido Trotter
                               help="Force an unknown variant")
639 06073e85 Guido Trotter
640 25a8792c Iustin Pop
NO_INSTALL_OPT = cli_option("--no-install", dest="no_install",
641 25a8792c Iustin Pop
                            action="store_true", default=False,
642 25a8792c Iustin Pop
                            help="Do not install the OS (will"
643 25a8792c Iustin Pop
                            " enable no-start)")
644 25a8792c Iustin Pop
645 087ed2ed Iustin Pop
BACKEND_OPT = cli_option("-B", "--backend-parameters", dest="beparams",
646 087ed2ed Iustin Pop
                         type="keyval", default={},
647 087ed2ed Iustin Pop
                         help="Backend parameters")
648 48f212d7 Iustin Pop
649 48f212d7 Iustin Pop
HVOPTS_OPT =  cli_option("-H", "--hypervisor-parameters", type="keyval",
650 48f212d7 Iustin Pop
                         default={}, dest="hvparams",
651 48f212d7 Iustin Pop
                         help="Hypervisor parameters")
652 087ed2ed Iustin Pop
653 236fd9c4 Iustin Pop
HYPERVISOR_OPT = cli_option("-H", "--hypervisor-parameters", dest="hypervisor",
654 236fd9c4 Iustin Pop
                            help="Hypervisor and hypervisor options, in the"
655 236fd9c4 Iustin Pop
                            " format hypervisor:option=value,option=value,...",
656 236fd9c4 Iustin Pop
                            default=None, type="identkeyval")
657 073271f6 Iustin Pop
658 073271f6 Iustin Pop
HVLIST_OPT = cli_option("-H", "--hypervisor-parameters", dest="hvparams",
659 073271f6 Iustin Pop
                        help="Hypervisor and hypervisor options, in the"
660 073271f6 Iustin Pop
                        " format hypervisor:option=value,option=value,...",
661 073271f6 Iustin Pop
                        default=[], action="append", type="identkeyval")
662 236fd9c4 Iustin Pop
663 91e0748c Iustin Pop
NOIPCHECK_OPT = cli_option("--no-ip-check", dest="ip_check", default=True,
664 91e0748c Iustin Pop
                           action="store_false",
665 91e0748c Iustin Pop
                           help="Don't check that the instance's IP"
666 91e0748c Iustin Pop
                           " is alive")
667 91e0748c Iustin Pop
668 460d22be Iustin Pop
NONAMECHECK_OPT = cli_option("--no-name-check", dest="name_check",
669 460d22be Iustin Pop
                             default=True, action="store_false",
670 460d22be Iustin Pop
                             help="Don't check that the instance's name"
671 460d22be Iustin Pop
                             " is resolvable")
672 460d22be Iustin Pop
673 7d3a9fab Iustin Pop
NET_OPT = cli_option("--net",
674 7d3a9fab Iustin Pop
                     help="NIC parameters", default=[],
675 7d3a9fab Iustin Pop
                     dest="nics", action="append", type="identkeyval")
676 e3876ccb Iustin Pop
677 e3876ccb Iustin Pop
DISK_OPT = cli_option("--disk", help="Disk parameters", default=[],
678 e3876ccb Iustin Pop
                      dest="disks", action="append", type="identkeyval")
679 91e0748c Iustin Pop
680 4b038a1e Iustin Pop
DISKIDX_OPT = cli_option("--disks", dest="disks", default=None,
681 4b038a1e Iustin Pop
                         help="Comma-separated list of disks"
682 4b038a1e Iustin Pop
                         " indices to act on (e.g. 0,2) (optional,"
683 4b038a1e Iustin Pop
                         " defaults to all disks)")
684 4b038a1e Iustin Pop
685 ff00c1a7 Iustin Pop
OS_SIZE_OPT = cli_option("-s", "--os-size", dest="sd_size",
686 ff00c1a7 Iustin Pop
                         help="Enforces a single-disk configuration using the"
687 ff00c1a7 Iustin Pop
                         " given disk size, in MiB unless a suffix is used",
688 ff00c1a7 Iustin Pop
                         default=None, type="unit", metavar="<size>")
689 ff00c1a7 Iustin Pop
690 82a786d5 Iustin Pop
IGNORE_CONSIST_OPT = cli_option("--ignore-consistency",
691 82a786d5 Iustin Pop
                                dest="ignore_consistency",
692 82a786d5 Iustin Pop
                                action="store_true", default=False,
693 82a786d5 Iustin Pop
                                help="Ignore the consistency of the disks on"
694 82a786d5 Iustin Pop
                                " the secondary")
695 82a786d5 Iustin Pop
696 f2a0828c Iustin Pop
NONLIVE_OPT = cli_option("--non-live", dest="live",
697 f2a0828c Iustin Pop
                         default=True, action="store_false",
698 f2a0828c Iustin Pop
                         help="Do a non-live migration (this usually means"
699 f2a0828c Iustin Pop
                         " freeze the instance, save the state, transfer and"
700 f2a0828c Iustin Pop
                         " only then resume running on the secondary node)")
701 f2a0828c Iustin Pop
702 e71b9ef4 Iustin Pop
MIGRATION_TYPE_OPT = cli_option("--migration-type", dest="migration_type",
703 e71b9ef4 Iustin Pop
                                default=None,
704 e71b9ef4 Iustin Pop
                                choices=list(constants.HT_MIGRATION_TYPES),
705 e71b9ef4 Iustin Pop
                                help="Override default migration type (choose"
706 e71b9ef4 Iustin Pop
                                " either live or non-live")
707 e71b9ef4 Iustin Pop
708 990b7886 Iustin Pop
NODE_PLACEMENT_OPT = cli_option("-n", "--node", dest="node",
709 990b7886 Iustin Pop
                                help="Target node and optional secondary node",
710 990b7886 Iustin Pop
                                metavar="<pnode>[:<snode>]",
711 990b7886 Iustin Pop
                                completion_suggest=OPT_COMPL_INST_ADD_NODES)
712 990b7886 Iustin Pop
713 7edc4637 Iustin Pop
NODE_LIST_OPT = cli_option("-n", "--node", dest="nodes", default=[],
714 7edc4637 Iustin Pop
                           action="append", metavar="<node>",
715 7edc4637 Iustin Pop
                           help="Use only this node (can be used multiple"
716 7edc4637 Iustin Pop
                           " times, if not given defaults to all nodes)",
717 7edc4637 Iustin Pop
                           completion_suggest=OPT_COMPL_ONE_NODE)
718 f36d7d81 Iustin Pop
719 f36d7d81 Iustin Pop
SINGLE_NODE_OPT = cli_option("-n", "--node", dest="node", help="Target node",
720 f36d7d81 Iustin Pop
                             metavar="<node>",
721 f36d7d81 Iustin Pop
                             completion_suggest=OPT_COMPL_ONE_NODE)
722 7edc4637 Iustin Pop
723 edeb878a Iustin Pop
NOSTART_OPT = cli_option("--no-start", dest="start", default=True,
724 edeb878a Iustin Pop
                         action="store_false",
725 edeb878a Iustin Pop
                         help="Don't start the instance after creation")
726 edeb878a Iustin Pop
727 fdcf4d84 Iustin Pop
SHOWCMD_OPT = cli_option("--show-cmd", dest="show_command",
728 fdcf4d84 Iustin Pop
                         action="store_true", default=False,
729 fdcf4d84 Iustin Pop
                         help="Show command instead of executing it")
730 fdcf4d84 Iustin Pop
731 baef337d Iustin Pop
CLEANUP_OPT = cli_option("--cleanup", dest="cleanup",
732 baef337d Iustin Pop
                         default=False, action="store_true",
733 baef337d Iustin Pop
                         help="Instead of performing the migration, try to"
734 baef337d Iustin Pop
                         " recover from a failed cleanup. This is safe"
735 baef337d Iustin Pop
                         " to run even if the instance is healthy, but it"
736 baef337d Iustin Pop
                         " will create extra replication traffic and "
737 baef337d Iustin Pop
                         " disrupt briefly the replication (like during the"
738 baef337d Iustin Pop
                         " migration")
739 baef337d Iustin Pop
740 99a8c799 Iustin Pop
STATIC_OPT = cli_option("-s", "--static", dest="static",
741 99a8c799 Iustin Pop
                        action="store_true", default=False,
742 99a8c799 Iustin Pop
                        help="Only show configuration data, not runtime data")
743 99a8c799 Iustin Pop
744 2d5e7ae1 Iustin Pop
ALL_OPT = cli_option("--all", dest="show_all",
745 2d5e7ae1 Iustin Pop
                     default=False, action="store_true",
746 2d5e7ae1 Iustin Pop
                     help="Show info on all instances on the cluster."
747 2d5e7ae1 Iustin Pop
                     " This can take a long time to run, use wisely")
748 2d5e7ae1 Iustin Pop
749 67840b40 Iustin Pop
SELECT_OS_OPT = cli_option("--select-os", dest="select_os",
750 67840b40 Iustin Pop
                           action="store_true", default=False,
751 67840b40 Iustin Pop
                           help="Interactive OS reinstall, lists available"
752 67840b40 Iustin Pop
                           " OS templates for selection")
753 2d5e7ae1 Iustin Pop
754 b6e841a8 Iustin Pop
IGNORE_FAILURES_OPT = cli_option("--ignore-failures", dest="ignore_failures",
755 b6e841a8 Iustin Pop
                                 action="store_true", default=False,
756 b6e841a8 Iustin Pop
                                 help="Remove the instance from the cluster"
757 b6e841a8 Iustin Pop
                                 " configuration even if there are failures"
758 b6e841a8 Iustin Pop
                                 " during the removal process")
759 b6e841a8 Iustin Pop
760 8d8d650c Michael Hanselmann
IGNORE_REMOVE_FAILURES_OPT = cli_option("--ignore-remove-failures",
761 8d8d650c Michael Hanselmann
                                        dest="ignore_remove_failures",
762 8d8d650c Michael Hanselmann
                                        action="store_true", default=False,
763 8d8d650c Michael Hanselmann
                                        help="Remove the instance from the"
764 8d8d650c Michael Hanselmann
                                        " cluster configuration even if there"
765 8d8d650c Michael Hanselmann
                                        " are failures during the removal"
766 8d8d650c Michael Hanselmann
                                        " process")
767 8d8d650c Michael Hanselmann
768 8d8d650c Michael Hanselmann
REMOVE_INSTANCE_OPT = cli_option("--remove-instance", dest="remove_instance",
769 8d8d650c Michael Hanselmann
                                 action="store_true", default=False,
770 8d8d650c Michael Hanselmann
                                 help="Remove the instance from the cluster")
771 8d8d650c Michael Hanselmann
772 a14db5ff Iustin Pop
NEW_SECONDARY_OPT = cli_option("-n", "--new-secondary", dest="dst_node",
773 a14db5ff Iustin Pop
                               help="Specifies the new secondary node",
774 a14db5ff Iustin Pop
                               metavar="NODE", default=None,
775 a14db5ff Iustin Pop
                               completion_suggest=OPT_COMPL_ONE_NODE)
776 a14db5ff Iustin Pop
777 a72d0a79 Iustin Pop
ON_PRIMARY_OPT = cli_option("-p", "--on-primary", dest="on_primary",
778 a72d0a79 Iustin Pop
                            default=False, action="store_true",
779 a72d0a79 Iustin Pop
                            help="Replace the disk(s) on the primary"
780 a72d0a79 Iustin Pop
                            " node (only for the drbd template)")
781 feb09e6a Iustin Pop
782 feb09e6a Iustin Pop
ON_SECONDARY_OPT = cli_option("-s", "--on-secondary", dest="on_secondary",
783 feb09e6a Iustin Pop
                              default=False, action="store_true",
784 feb09e6a Iustin Pop
                              help="Replace the disk(s) on the secondary"
785 feb09e6a Iustin Pop
                              " node (only for the drbd template)")
786 e00f7a05 Iustin Pop
787 4c61d894 Iustin Pop
AUTO_PROMOTE_OPT = cli_option("--auto-promote", dest="auto_promote",
788 4c61d894 Iustin Pop
                              default=False, action="store_true",
789 4c61d894 Iustin Pop
                              help="Lock all nodes and auto-promote as needed"
790 4c61d894 Iustin Pop
                              " to MC status")
791 4c61d894 Iustin Pop
792 e00f7a05 Iustin Pop
AUTO_REPLACE_OPT = cli_option("-a", "--auto", dest="auto",
793 e00f7a05 Iustin Pop
                              default=False, action="store_true",
794 e00f7a05 Iustin Pop
                              help="Automatically replace faulty disks"
795 e00f7a05 Iustin Pop
                              " (only for the drbd template)")
796 a72d0a79 Iustin Pop
797 05586c90 Iustin Pop
IGNORE_SIZE_OPT = cli_option("--ignore-size", dest="ignore_size",
798 05586c90 Iustin Pop
                             default=False, action="store_true",
799 05586c90 Iustin Pop
                             help="Ignore current recorded size"
800 05586c90 Iustin Pop
                             " (useful for forcing activation when"
801 05586c90 Iustin Pop
                             " the recorded size is wrong)")
802 05586c90 Iustin Pop
803 df62e5db Iustin Pop
SRC_NODE_OPT = cli_option("--src-node", dest="src_node", help="Source node",
804 df62e5db Iustin Pop
                          metavar="<node>",
805 df62e5db Iustin Pop
                          completion_suggest=OPT_COMPL_ONE_NODE)
806 df62e5db Iustin Pop
807 df62e5db Iustin Pop
SRC_DIR_OPT = cli_option("--src-dir", dest="src_dir", help="Source directory",
808 df62e5db Iustin Pop
                         metavar="<dir>")
809 df62e5db Iustin Pop
810 8d823629 Iustin Pop
SECONDARY_IP_OPT = cli_option("-s", "--secondary-ip", dest="secondary_ip",
811 8d823629 Iustin Pop
                              help="Specify the secondary ip for the node",
812 8d823629 Iustin Pop
                              metavar="ADDRESS", default=None)
813 8d823629 Iustin Pop
814 b8d0f938 Iustin Pop
READD_OPT = cli_option("--readd", dest="readd",
815 b8d0f938 Iustin Pop
                       default=False, action="store_true",
816 b8d0f938 Iustin Pop
                       help="Readd old node after replacing it")
817 b8d0f938 Iustin Pop
818 fcdde7f2 Iustin Pop
NOSSH_KEYCHECK_OPT = cli_option("--no-ssh-key-check", dest="ssh_key_check",
819 fcdde7f2 Iustin Pop
                                default=True, action="store_false",
820 fcdde7f2 Iustin Pop
                                help="Disable SSH key fingerprint checking")
821 fcdde7f2 Iustin Pop
822 c38c44ad Michael Hanselmann
823 771734c9 Iustin Pop
MC_OPT = cli_option("-C", "--master-candidate", dest="master_candidate",
824 e7b61bb0 Iustin Pop
                    type="bool", default=None, metavar=_YORNO,
825 771734c9 Iustin Pop
                    help="Set the master_candidate flag on the node")
826 771734c9 Iustin Pop
827 771734c9 Iustin Pop
OFFLINE_OPT = cli_option("-O", "--offline", dest="offline", metavar=_YORNO,
828 e7b61bb0 Iustin Pop
                         type="bool", default=None,
829 771734c9 Iustin Pop
                         help="Set the offline flag on the node")
830 771734c9 Iustin Pop
831 771734c9 Iustin Pop
DRAINED_OPT = cli_option("-D", "--drained", dest="drained", metavar=_YORNO,
832 e7b61bb0 Iustin Pop
                         type="bool", default=None,
833 771734c9 Iustin Pop
                         help="Set the drained flag on the node")
834 771734c9 Iustin Pop
835 e7e09483 Iustin Pop
ALLOCATABLE_OPT = cli_option("--allocatable", dest="allocatable",
836 e7b61bb0 Iustin Pop
                             type="bool", default=None, metavar=_YORNO,
837 e7e09483 Iustin Pop
                             help="Set the allocatable flag on a volume")
838 e7e09483 Iustin Pop
839 831040bf Iustin Pop
NOLVM_STORAGE_OPT = cli_option("--no-lvm-storage", dest="lvm_storage",
840 831040bf Iustin Pop
                               help="Disable support for lvm based instances"
841 831040bf Iustin Pop
                               " (cluster-wide)",
842 831040bf Iustin Pop
                               action="store_false", default=True)
843 831040bf Iustin Pop
844 383a3591 Iustin Pop
ENABLED_HV_OPT = cli_option("--enabled-hypervisors",
845 383a3591 Iustin Pop
                            dest="enabled_hypervisors",
846 383a3591 Iustin Pop
                            help="Comma-separated list of hypervisors",
847 383a3591 Iustin Pop
                            type="string", default=None)
848 383a3591 Iustin Pop
849 4fbc93dd Iustin Pop
NIC_PARAMS_OPT = cli_option("-N", "--nic-parameters", dest="nicparams",
850 4fbc93dd Iustin Pop
                            type="keyval", default={},
851 4fbc93dd Iustin Pop
                            help="NIC parameters")
852 4fbc93dd Iustin Pop
853 e32df528 Iustin Pop
CP_SIZE_OPT = cli_option("-C", "--candidate-pool-size", default=None,
854 e32df528 Iustin Pop
                         dest="candidate_pool_size", type="int",
855 e32df528 Iustin Pop
                         help="Set the candidate pool size")
856 e32df528 Iustin Pop
857 b58726e8 Iustin Pop
VG_NAME_OPT = cli_option("-g", "--vg-name", dest="vg_name",
858 b58726e8 Iustin Pop
                         help="Enables LVM and specifies the volume group"
859 b58726e8 Iustin Pop
                         " name (cluster-wide) for disk allocation [xenvg]",
860 b58726e8 Iustin Pop
                         metavar="VG", default=None)
861 b58726e8 Iustin Pop
862 1f587d3d Iustin Pop
YES_DOIT_OPT = cli_option("--yes-do-it", dest="yes_do_it",
863 1f587d3d Iustin Pop
                          help="Destroy cluster", action="store_true")
864 b58726e8 Iustin Pop
865 58371861 Iustin Pop
NOVOTING_OPT = cli_option("--no-voting", dest="no_voting",
866 58371861 Iustin Pop
                          help="Skip node agreement check (dangerous)",
867 58371861 Iustin Pop
                          action="store_true", default=False)
868 58371861 Iustin Pop
869 e3646f22 Iustin Pop
MAC_PREFIX_OPT = cli_option("-m", "--mac-prefix", dest="mac_prefix",
870 e3646f22 Iustin Pop
                            help="Specify the mac prefix for the instance IP"
871 e3646f22 Iustin Pop
                            " addresses, in the format XX:XX:XX",
872 e3646f22 Iustin Pop
                            metavar="PREFIX",
873 e3646f22 Iustin Pop
                            default=None)
874 e3646f22 Iustin Pop
875 29392516 Iustin Pop
MASTER_NETDEV_OPT = cli_option("--master-netdev", dest="master_netdev",
876 29392516 Iustin Pop
                               help="Specify the node interface (cluster-wide)"
877 29392516 Iustin Pop
                               " on which the master IP address will be added "
878 29392516 Iustin Pop
                               " [%s]" % constants.DEFAULT_BRIDGE,
879 29392516 Iustin Pop
                               metavar="NETDEV",
880 29392516 Iustin Pop
                               default=constants.DEFAULT_BRIDGE)
881 29392516 Iustin Pop
882 29392516 Iustin Pop
GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
883 29392516 Iustin Pop
                                help="Specify the default directory (cluster-"
884 29392516 Iustin Pop
                                "wide) for storing the file-based disks [%s]" %
885 29392516 Iustin Pop
                                constants.DEFAULT_FILE_STORAGE_DIR,
886 29392516 Iustin Pop
                                metavar="DIR",
887 29392516 Iustin Pop
                                default=constants.DEFAULT_FILE_STORAGE_DIR)
888 29392516 Iustin Pop
889 29392516 Iustin Pop
NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts",
890 29392516 Iustin Pop
                                   help="Don't modify /etc/hosts",
891 29392516 Iustin Pop
                                   action="store_false", default=True)
892 29392516 Iustin Pop
893 b989b9d9 Ken Wehr
NOMODIFY_SSH_SETUP_OPT = cli_option("--no-ssh-init", dest="modify_ssh_setup",
894 b989b9d9 Ken Wehr
                                    help="Don't initialize SSH keys",
895 b989b9d9 Ken Wehr
                                    action="store_false", default=True)
896 b989b9d9 Ken Wehr
897 14e9e7f3 Iustin Pop
ERROR_CODES_OPT = cli_option("--error-codes", dest="error_codes",
898 14e9e7f3 Iustin Pop
                             help="Enable parseable error messages",
899 14e9e7f3 Iustin Pop
                             action="store_true", default=False)
900 14e9e7f3 Iustin Pop
901 14e9e7f3 Iustin Pop
NONPLUS1_OPT = cli_option("--no-nplus1-mem", dest="skip_nplusone_mem",
902 14e9e7f3 Iustin Pop
                          help="Skip N+1 memory redundancy tests",
903 14e9e7f3 Iustin Pop
                          action="store_true", default=False)
904 14e9e7f3 Iustin Pop
905 12054861 Iustin Pop
REBOOT_TYPE_OPT = cli_option("-t", "--type", dest="reboot_type",
906 12054861 Iustin Pop
                             help="Type of reboot: soft/hard/full",
907 12054861 Iustin Pop
                             default=constants.INSTANCE_REBOOT_HARD,
908 12054861 Iustin Pop
                             metavar="<REBOOT>",
909 12054861 Iustin Pop
                             choices=list(constants.REBOOT_TYPES))
910 12054861 Iustin Pop
911 ee3f9578 Iustin Pop
IGNORE_SECONDARIES_OPT = cli_option("--ignore-secondaries",
912 ee3f9578 Iustin Pop
                                    dest="ignore_secondaries",
913 ee3f9578 Iustin Pop
                                    default=False, action="store_true",
914 ee3f9578 Iustin Pop
                                    help="Ignore errors from secondaries")
915 ee3f9578 Iustin Pop
916 69b99987 Michael Hanselmann
NOSHUTDOWN_OPT = cli_option("--noshutdown", dest="shutdown",
917 44c44832 Iustin Pop
                            action="store_false", default=True,
918 44c44832 Iustin Pop
                            help="Don't shutdown the instance (unsafe)")
919 44c44832 Iustin Pop
920 b5762e2a Guido Trotter
TIMEOUT_OPT = cli_option("--timeout", dest="timeout", type="int",
921 b5762e2a Guido Trotter
                         default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
922 b5762e2a Guido Trotter
                         help="Maximum time to wait")
923 44c44832 Iustin Pop
924 4d98c565 Guido Trotter
SHUTDOWN_TIMEOUT_OPT = cli_option("--shutdown-timeout",
925 4d98c565 Guido Trotter
                         dest="shutdown_timeout", type="int",
926 7e5eaaa8 Guido Trotter
                         default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
927 7e5eaaa8 Guido Trotter
                         help="Maximum time to wait for instance shutdown")
928 7e5eaaa8 Guido Trotter
929 7ea7bcf6 Iustin Pop
EARLY_RELEASE_OPT = cli_option("--early-release",
930 7ea7bcf6 Iustin Pop
                               dest="early_release", default=False,
931 7ea7bcf6 Iustin Pop
                               action="store_true",
932 7ea7bcf6 Iustin Pop
                               help="Release the locks on the secondary"
933 7ea7bcf6 Iustin Pop
                               " node(s) early")
934 7ea7bcf6 Iustin Pop
935 6d4a1656 Michael Hanselmann
NEW_CLUSTER_CERT_OPT = cli_option("--new-cluster-certificate",
936 6d4a1656 Michael Hanselmann
                                  dest="new_cluster_cert",
937 6d4a1656 Michael Hanselmann
                                  default=False, action="store_true",
938 6d4a1656 Michael Hanselmann
                                  help="Generate a new cluster certificate")
939 6d4a1656 Michael Hanselmann
940 6d4a1656 Michael Hanselmann
RAPI_CERT_OPT = cli_option("--rapi-certificate", dest="rapi_cert",
941 6d4a1656 Michael Hanselmann
                           default=None,
942 6d4a1656 Michael Hanselmann
                           help="File containing new RAPI certificate")
943 6d4a1656 Michael Hanselmann
944 6d4a1656 Michael Hanselmann
NEW_RAPI_CERT_OPT = cli_option("--new-rapi-certificate", dest="new_rapi_cert",
945 6d4a1656 Michael Hanselmann
                               default=None, action="store_true",
946 6d4a1656 Michael Hanselmann
                               help=("Generate a new self-signed RAPI"
947 6d4a1656 Michael Hanselmann
                                     " certificate"))
948 6d4a1656 Michael Hanselmann
949 6b7d5878 Michael Hanselmann
NEW_CONFD_HMAC_KEY_OPT = cli_option("--new-confd-hmac-key",
950 6b7d5878 Michael Hanselmann
                                    dest="new_confd_hmac_key",
951 6b7d5878 Michael Hanselmann
                                    default=False, action="store_true",
952 6b7d5878 Michael Hanselmann
                                    help=("Create a new HMAC key for %s" %
953 6b7d5878 Michael Hanselmann
                                          constants.CONFD))
954 6d4a1656 Michael Hanselmann
955 3db3eb2a Michael Hanselmann
CLUSTER_DOMAIN_SECRET_OPT = cli_option("--cluster-domain-secret",
956 3db3eb2a Michael Hanselmann
                                       dest="cluster_domain_secret",
957 3db3eb2a Michael Hanselmann
                                       default=None,
958 3db3eb2a Michael Hanselmann
                                       help=("Load new new cluster domain"
959 3db3eb2a Michael Hanselmann
                                             " secret from file"))
960 3db3eb2a Michael Hanselmann
961 3db3eb2a Michael Hanselmann
NEW_CLUSTER_DOMAIN_SECRET_OPT = cli_option("--new-cluster-domain-secret",
962 3db3eb2a Michael Hanselmann
                                           dest="new_cluster_domain_secret",
963 3db3eb2a Michael Hanselmann
                                           default=False, action="store_true",
964 3db3eb2a Michael Hanselmann
                                           help=("Create a new cluster domain"
965 3db3eb2a Michael Hanselmann
                                                 " secret"))
966 3db3eb2a Michael Hanselmann
967 74adc100 Iustin Pop
USE_REPL_NET_OPT = cli_option("--use-replication-network",
968 74adc100 Iustin Pop
                              dest="use_replication_network",
969 74adc100 Iustin Pop
                              help="Whether to use the replication network"
970 74adc100 Iustin Pop
                              " for talking to the nodes",
971 74adc100 Iustin Pop
                              action="store_true", default=False)
972 74adc100 Iustin Pop
973 3953242f Iustin Pop
MAINTAIN_NODE_HEALTH_OPT = \
974 3953242f Iustin Pop
    cli_option("--maintain-node-health", dest="maintain_node_health",
975 3953242f Iustin Pop
               metavar=_YORNO, default=None, type="bool",
976 3953242f Iustin Pop
               help="Configure the cluster to automatically maintain node"
977 3953242f Iustin Pop
               " health, by shutting down unknown instances, shutting down"
978 3953242f Iustin Pop
               " unknown DRBD devices, etc.")
979 3953242f Iustin Pop
980 e588764d Iustin Pop
IDENTIFY_DEFAULTS_OPT = \
981 e588764d Iustin Pop
    cli_option("--identify-defaults", dest="identify_defaults",
982 e588764d Iustin Pop
               default=False, action="store_true",
983 e588764d Iustin Pop
               help="Identify which saved instance parameters are equal to"
984 e588764d Iustin Pop
               " the current cluster defaults and set them as such, instead"
985 e588764d Iustin Pop
               " of marking them as overridden")
986 e588764d Iustin Pop
987 1338f2b4 Balazs Lecz
UIDPOOL_OPT = cli_option("--uid-pool", default=None,
988 1338f2b4 Balazs Lecz
                         action="store", dest="uid_pool",
989 1338f2b4 Balazs Lecz
                         help=("A list of user-ids or user-id"
990 1338f2b4 Balazs Lecz
                               " ranges separated by commas"))
991 1338f2b4 Balazs Lecz
992 fdad8c4d Balazs Lecz
ADD_UIDS_OPT = cli_option("--add-uids", default=None,
993 fdad8c4d Balazs Lecz
                          action="store", dest="add_uids",
994 fdad8c4d Balazs Lecz
                          help=("A list of user-ids or user-id"
995 fdad8c4d Balazs Lecz
                                " ranges separated by commas, to be"
996 fdad8c4d Balazs Lecz
                                " added to the user-id pool"))
997 fdad8c4d Balazs Lecz
998 fdad8c4d Balazs Lecz
REMOVE_UIDS_OPT = cli_option("--remove-uids", default=None,
999 fdad8c4d Balazs Lecz
                             action="store", dest="remove_uids",
1000 fdad8c4d Balazs Lecz
                             help=("A list of user-ids or user-id"
1001 fdad8c4d Balazs Lecz
                                   " ranges separated by commas, to be"
1002 fdad8c4d Balazs Lecz
                                   " removed from the user-id pool"))
1003 fdad8c4d Balazs Lecz
1004 31d97b2a Guido Trotter
ROMAN_OPT = cli_option("--roman",
1005 31d97b2a Guido Trotter
                       dest="roman_integers", default=False,
1006 31d97b2a Guido Trotter
                       action="store_true",
1007 31d97b2a Guido Trotter
                       help="Use roman numbers for positive integers")
1008 31d97b2a Guido Trotter
1009 26591bfd Luca Bigliardi
DRBD_HELPER_OPT = cli_option("--drbd-usermode-helper", dest="drbd_helper",
1010 26591bfd Luca Bigliardi
                             action="store", default=None,
1011 26591bfd Luca Bigliardi
                             help="Specifies usermode helper for DRBD")
1012 26591bfd Luca Bigliardi
1013 26591bfd Luca Bigliardi
NODRBD_STORAGE_OPT = cli_option("--no-drbd-storage", dest="drbd_storage",
1014 26591bfd Luca Bigliardi
                                action="store_false", default=True,
1015 26591bfd Luca Bigliardi
                                help="Disable support for DRBD")
1016 31d97b2a Guido Trotter
1017 771734c9 Iustin Pop
1018 de47cf8f Guido Trotter
def _ParseArgs(argv, commands, aliases):
1019 c41eea6e Iustin Pop
  """Parser for the command line arguments.
1020 a8083063 Iustin Pop

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

1024 c41eea6e Iustin Pop
  @param argv: the command line
1025 c41eea6e Iustin Pop
  @param commands: dictionary with special contents, see the design
1026 c41eea6e Iustin Pop
      doc for cmdline handling
1027 c41eea6e Iustin Pop
  @param aliases: dictionary with command aliases {'alias': 'target, ...}
1028 098c0958 Michael Hanselmann

1029 a8083063 Iustin Pop
  """
1030 a8083063 Iustin Pop
  if len(argv) == 0:
1031 a8083063 Iustin Pop
    binary = "<command>"
1032 a8083063 Iustin Pop
  else:
1033 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
1034 a8083063 Iustin Pop
1035 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
1036 84a12e40 Iustin Pop
    ToStdout("%s (ganeti %s) %s", binary, constants.VCS_VERSION,
1037 84a12e40 Iustin Pop
             constants.RELEASE_VERSION)
1038 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
1039 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
1040 a8083063 Iustin Pop
    sys.exit(0)
1041 a8083063 Iustin Pop
1042 de47cf8f Guido Trotter
  if len(argv) < 2 or not (argv[1] in commands or
1043 70a35b6f Guido Trotter
                           argv[1] in aliases):
1044 a8083063 Iustin Pop
    # let's do a nice thing
1045 a8083063 Iustin Pop
    sortedcmds = commands.keys()
1046 a8083063 Iustin Pop
    sortedcmds.sort()
1047 03298ebe Michael Hanselmann
1048 03298ebe Michael Hanselmann
    ToStdout("Usage: %s {command} [options...] [argument...]", binary)
1049 03298ebe Michael Hanselmann
    ToStdout("%s <command> --help to see details, or man %s", binary, binary)
1050 03298ebe Michael Hanselmann
    ToStdout("")
1051 03298ebe Michael Hanselmann
1052 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
1053 4e713df6 Iustin Pop
    mlen = max([len(" %s" % cmd) for cmd in commands])
1054 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
1055 03298ebe Michael Hanselmann
1056 a8083063 Iustin Pop
    # and format a nice command list
1057 03298ebe Michael Hanselmann
    ToStdout("Commands:")
1058 a8083063 Iustin Pop
    for cmd in sortedcmds:
1059 4e713df6 Iustin Pop
      cmdstr = " %s" % (cmd,)
1060 9a033156 Iustin Pop
      help_text = commands[cmd][4]
1061 03298ebe Michael Hanselmann
      help_lines = textwrap.wrap(help_text, 79 - 3 - mlen)
1062 03298ebe Michael Hanselmann
      ToStdout("%-*s - %s", mlen, cmdstr, help_lines.pop(0))
1063 a8083063 Iustin Pop
      for line in help_lines:
1064 03298ebe Michael Hanselmann
        ToStdout("%-*s   %s", mlen, "", line)
1065 03298ebe Michael Hanselmann
1066 03298ebe Michael Hanselmann
    ToStdout("")
1067 03298ebe Michael Hanselmann
1068 a8083063 Iustin Pop
    return None, None, None
1069 de47cf8f Guido Trotter
1070 de47cf8f Guido Trotter
  # get command, unalias it, and look it up in commands
1071 a8083063 Iustin Pop
  cmd = argv.pop(1)
1072 de47cf8f Guido Trotter
  if cmd in aliases:
1073 de47cf8f Guido Trotter
    if cmd in commands:
1074 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' overrides an existing"
1075 de47cf8f Guido Trotter
                                   " command" % cmd)
1076 de47cf8f Guido Trotter
1077 de47cf8f Guido Trotter
    if aliases[cmd] not in commands:
1078 de47cf8f Guido Trotter
      raise errors.ProgrammerError("Alias '%s' maps to non-existing"
1079 de47cf8f Guido Trotter
                                   " command '%s'" % (cmd, aliases[cmd]))
1080 de47cf8f Guido Trotter
1081 de47cf8f Guido Trotter
    cmd = aliases[cmd]
1082 de47cf8f Guido Trotter
1083 a8005e17 Michael Hanselmann
  func, args_def, parser_opts, usage, description = commands[cmd]
1084 064c21f8 Iustin Pop
  parser = OptionParser(option_list=parser_opts + [_DRY_RUN_OPT, DEBUG_OPT],
1085 a8083063 Iustin Pop
                        description=description,
1086 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
1087 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
1088 a8083063 Iustin Pop
  parser.disable_interspersed_args()
1089 a8083063 Iustin Pop
  options, args = parser.parse_args()
1090 a8005e17 Michael Hanselmann
1091 a8005e17 Michael Hanselmann
  if not _CheckArguments(cmd, args_def, args):
1092 a8083063 Iustin Pop
    return None, None, None
1093 a8083063 Iustin Pop
1094 a8083063 Iustin Pop
  return func, options, args
1095 a8083063 Iustin Pop
1096 a8083063 Iustin Pop
1097 a8005e17 Michael Hanselmann
def _CheckArguments(cmd, args_def, args):
1098 a8005e17 Michael Hanselmann
  """Verifies the arguments using the argument definition.
1099 a8005e17 Michael Hanselmann

1100 a8005e17 Michael Hanselmann
  Algorithm:
1101 a8005e17 Michael Hanselmann

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

1104 a8005e17 Michael Hanselmann
    1. For each argument in definition
1105 a8005e17 Michael Hanselmann

1106 a8005e17 Michael Hanselmann
      1. Keep running count of minimum number of values (min_count)
1107 a8005e17 Michael Hanselmann
      1. Keep running count of maximum number of values (max_count)
1108 a8005e17 Michael Hanselmann
      1. If it has an unlimited number of values
1109 a8005e17 Michael Hanselmann

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

1112 a8005e17 Michael Hanselmann
    1. If last argument has limited number of values
1113 a8005e17 Michael Hanselmann

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

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

1118 a8005e17 Michael Hanselmann
  """
1119 a8005e17 Michael Hanselmann
  if args and not args_def:
1120 a8005e17 Michael Hanselmann
    ToStderr("Error: Command %s expects no arguments", cmd)
1121 a8005e17 Michael Hanselmann
    return False
1122 a8005e17 Michael Hanselmann
1123 a8005e17 Michael Hanselmann
  min_count = None
1124 a8005e17 Michael Hanselmann
  max_count = None
1125 a8005e17 Michael Hanselmann
  check_max = None
1126 a8005e17 Michael Hanselmann
1127 a8005e17 Michael Hanselmann
  last_idx = len(args_def) - 1
1128 a8005e17 Michael Hanselmann
1129 a8005e17 Michael Hanselmann
  for idx, arg in enumerate(args_def):
1130 a8005e17 Michael Hanselmann
    if min_count is None:
1131 a8005e17 Michael Hanselmann
      min_count = arg.min
1132 a8005e17 Michael Hanselmann
    elif arg.min is not None:
1133 a8005e17 Michael Hanselmann
      min_count += arg.min
1134 a8005e17 Michael Hanselmann
1135 a8005e17 Michael Hanselmann
    if max_count is None:
1136 a8005e17 Michael Hanselmann
      max_count = arg.max
1137 a8005e17 Michael Hanselmann
    elif arg.max is not None:
1138 a8005e17 Michael Hanselmann
      max_count += arg.max
1139 a8005e17 Michael Hanselmann
1140 a8005e17 Michael Hanselmann
    if idx == last_idx:
1141 a8005e17 Michael Hanselmann
      check_max = (arg.max is not None)
1142 a8005e17 Michael Hanselmann
1143 a8005e17 Michael Hanselmann
    elif arg.max is None:
1144 a8005e17 Michael Hanselmann
      raise errors.ProgrammerError("Only the last argument can have max=None")
1145 a8005e17 Michael Hanselmann
1146 a8005e17 Michael Hanselmann
  if check_max:
1147 a8005e17 Michael Hanselmann
    # Command with exact number of arguments
1148 a8005e17 Michael Hanselmann
    if (min_count is not None and max_count is not None and
1149 a8005e17 Michael Hanselmann
        min_count == max_count and len(args) != min_count):
1150 a8005e17 Michael Hanselmann
      ToStderr("Error: Command %s expects %d argument(s)", cmd, min_count)
1151 a8005e17 Michael Hanselmann
      return False
1152 a8005e17 Michael Hanselmann
1153 a8005e17 Michael Hanselmann
    # Command with limited number of arguments
1154 a8005e17 Michael Hanselmann
    if max_count is not None and len(args) > max_count:
1155 a8005e17 Michael Hanselmann
      ToStderr("Error: Command %s expects only %d argument(s)",
1156 a8005e17 Michael Hanselmann
               cmd, max_count)
1157 a8005e17 Michael Hanselmann
      return False
1158 a8005e17 Michael Hanselmann
1159 a8005e17 Michael Hanselmann
  # Command with some required arguments
1160 a8005e17 Michael Hanselmann
  if min_count is not None and len(args) < min_count:
1161 a8005e17 Michael Hanselmann
    ToStderr("Error: Command %s expects at least %d argument(s)",
1162 a8005e17 Michael Hanselmann
             cmd, min_count)
1163 a8005e17 Michael Hanselmann
    return False
1164 a8005e17 Michael Hanselmann
1165 a8005e17 Michael Hanselmann
  return True
1166 a8005e17 Michael Hanselmann
1167 a8005e17 Michael Hanselmann
1168 60d49723 Michael Hanselmann
def SplitNodeOption(value):
1169 60d49723 Michael Hanselmann
  """Splits the value of a --node option.
1170 60d49723 Michael Hanselmann

1171 60d49723 Michael Hanselmann
  """
1172 60d49723 Michael Hanselmann
  if value and ':' in value:
1173 60d49723 Michael Hanselmann
    return value.split(':', 1)
1174 60d49723 Michael Hanselmann
  else:
1175 60d49723 Michael Hanselmann
    return (value, None)
1176 60d49723 Michael Hanselmann
1177 60d49723 Michael Hanselmann
1178 07150497 Guido Trotter
def CalculateOSNames(os_name, os_variants):
1179 07150497 Guido Trotter
  """Calculates all the names an OS can be called, according to its variants.
1180 07150497 Guido Trotter

1181 07150497 Guido Trotter
  @type os_name: string
1182 07150497 Guido Trotter
  @param os_name: base name of the os
1183 07150497 Guido Trotter
  @type os_variants: list or None
1184 07150497 Guido Trotter
  @param os_variants: list of supported variants
1185 07150497 Guido Trotter
  @rtype: list
1186 07150497 Guido Trotter
  @return: list of valid names
1187 07150497 Guido Trotter

1188 07150497 Guido Trotter
  """
1189 07150497 Guido Trotter
  if os_variants:
1190 07150497 Guido Trotter
    return ['%s+%s' % (os_name, v) for v in os_variants]
1191 07150497 Guido Trotter
  else:
1192 07150497 Guido Trotter
    return [os_name]
1193 07150497 Guido Trotter
1194 07150497 Guido Trotter
1195 4331f6cd Michael Hanselmann
def UsesRPC(fn):
1196 4331f6cd Michael Hanselmann
  def wrapper(*args, **kwargs):
1197 4331f6cd Michael Hanselmann
    rpc.Init()
1198 4331f6cd Michael Hanselmann
    try:
1199 4331f6cd Michael Hanselmann
      return fn(*args, **kwargs)
1200 4331f6cd Michael Hanselmann
    finally:
1201 4331f6cd Michael Hanselmann
      rpc.Shutdown()
1202 4331f6cd Michael Hanselmann
  return wrapper
1203 4331f6cd Michael Hanselmann
1204 4331f6cd Michael Hanselmann
1205 47988778 Iustin Pop
def AskUser(text, choices=None):
1206 47988778 Iustin Pop
  """Ask the user a question.
1207 a8083063 Iustin Pop

1208 c41eea6e Iustin Pop
  @param text: the question to ask
1209 a8083063 Iustin Pop

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

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

1219 a8083063 Iustin Pop
  """
1220 47988778 Iustin Pop
  if choices is None:
1221 47988778 Iustin Pop
    choices = [('y', True, 'Perform the operation'),
1222 47988778 Iustin Pop
               ('n', False, 'Do not perform the operation')]
1223 47988778 Iustin Pop
  if not choices or not isinstance(choices, list):
1224 5bbd3f7f Michael Hanselmann
    raise errors.ProgrammerError("Invalid choices argument to AskUser")
1225 47988778 Iustin Pop
  for entry in choices:
1226 47988778 Iustin Pop
    if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
1227 5bbd3f7f Michael Hanselmann
      raise errors.ProgrammerError("Invalid choices element to AskUser")
1228 47988778 Iustin Pop
1229 47988778 Iustin Pop
  answer = choices[-1][1]
1230 47988778 Iustin Pop
  new_text = []
1231 47988778 Iustin Pop
  for line in text.splitlines():
1232 47988778 Iustin Pop
    new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
1233 47988778 Iustin Pop
  text = "\n".join(new_text)
1234 a8083063 Iustin Pop
  try:
1235 3023170f Iustin Pop
    f = file("/dev/tty", "a+")
1236 a8083063 Iustin Pop
  except IOError:
1237 47988778 Iustin Pop
    return answer
1238 a8083063 Iustin Pop
  try:
1239 47988778 Iustin Pop
    chars = [entry[0] for entry in choices]
1240 47988778 Iustin Pop
    chars[-1] = "[%s]" % chars[-1]
1241 47988778 Iustin Pop
    chars.append('?')
1242 47988778 Iustin Pop
    maps = dict([(entry[0], entry[1]) for entry in choices])
1243 47988778 Iustin Pop
    while True:
1244 47988778 Iustin Pop
      f.write(text)
1245 47988778 Iustin Pop
      f.write('\n')
1246 47988778 Iustin Pop
      f.write("/".join(chars))
1247 47988778 Iustin Pop
      f.write(": ")
1248 47988778 Iustin Pop
      line = f.readline(2).strip().lower()
1249 47988778 Iustin Pop
      if line in maps:
1250 47988778 Iustin Pop
        answer = maps[line]
1251 47988778 Iustin Pop
        break
1252 47988778 Iustin Pop
      elif line == '?':
1253 47988778 Iustin Pop
        for entry in choices:
1254 47988778 Iustin Pop
          f.write(" %s - %s\n" % (entry[0], entry[2]))
1255 47988778 Iustin Pop
        f.write("\n")
1256 47988778 Iustin Pop
        continue
1257 a8083063 Iustin Pop
  finally:
1258 a8083063 Iustin Pop
    f.close()
1259 a8083063 Iustin Pop
  return answer
1260 a8083063 Iustin Pop
1261 a8083063 Iustin Pop
1262 e9d741b6 Iustin Pop
class JobSubmittedException(Exception):
1263 e9d741b6 Iustin Pop
  """Job was submitted, client should exit.
1264 e9d741b6 Iustin Pop

1265 e9d741b6 Iustin Pop
  This exception has one argument, the ID of the job that was
1266 e9d741b6 Iustin Pop
  submitted. The handler should print this ID.
1267 e9d741b6 Iustin Pop

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

1270 e9d741b6 Iustin Pop
  """
1271 e9d741b6 Iustin Pop
1272 e9d741b6 Iustin Pop
1273 0a1e74d9 Iustin Pop
def SendJob(ops, cl=None):
1274 0a1e74d9 Iustin Pop
  """Function to submit an opcode without waiting for the results.
1275 a8083063 Iustin Pop

1276 0a1e74d9 Iustin Pop
  @type ops: list
1277 0a1e74d9 Iustin Pop
  @param ops: list of opcodes
1278 0a1e74d9 Iustin Pop
  @type cl: luxi.Client
1279 0a1e74d9 Iustin Pop
  @param cl: the luxi client to use for communicating with the master;
1280 0a1e74d9 Iustin Pop
             if None, a new client will be created
1281 a8083063 Iustin Pop

1282 a8083063 Iustin Pop
  """
1283 e2212007 Iustin Pop
  if cl is None:
1284 b33e986b Iustin Pop
    cl = GetClient()
1285 685ee993 Iustin Pop
1286 0a1e74d9 Iustin Pop
  job_id = cl.SubmitJob(ops)
1287 0a1e74d9 Iustin Pop
1288 0a1e74d9 Iustin Pop
  return job_id
1289 0a1e74d9 Iustin Pop
1290 0a1e74d9 Iustin Pop
1291 4e338533 Michael Hanselmann
def GenericPollJob(job_id, cbs, report_cbs):
1292 4e338533 Michael Hanselmann
  """Generic job-polling function.
1293 0a1e74d9 Iustin Pop

1294 4e338533 Michael Hanselmann
  @type job_id: number
1295 4e338533 Michael Hanselmann
  @param job_id: Job ID
1296 4e338533 Michael Hanselmann
  @type cbs: Instance of L{JobPollCbBase}
1297 4e338533 Michael Hanselmann
  @param cbs: Data callbacks
1298 4e338533 Michael Hanselmann
  @type report_cbs: Instance of L{JobPollReportCbBase}
1299 4e338533 Michael Hanselmann
  @param report_cbs: Reporting callbacks
1300 0a1e74d9 Iustin Pop

1301 0a1e74d9 Iustin Pop
  """
1302 6c5a7090 Michael Hanselmann
  prev_job_info = None
1303 6c5a7090 Michael Hanselmann
  prev_logmsg_serial = None
1304 6c5a7090 Michael Hanselmann
1305 f4484122 Michael Hanselmann
  status = None
1306 f4484122 Michael Hanselmann
1307 685ee993 Iustin Pop
  while True:
1308 4e338533 Michael Hanselmann
    result = cbs.WaitForJobChangeOnce(job_id, ["status"], prev_job_info,
1309 4e338533 Michael Hanselmann
                                      prev_logmsg_serial)
1310 6c5a7090 Michael Hanselmann
    if not result:
1311 685ee993 Iustin Pop
      # job not found, go away!
1312 0bbe448c Michael Hanselmann
      raise errors.JobLost("Job with id %s lost" % job_id)
1313 4e338533 Michael Hanselmann
1314 4e338533 Michael Hanselmann
    if result == constants.JOB_NOTCHANGED:
1315 4e338533 Michael Hanselmann
      report_cbs.ReportNotChanged(job_id, status)
1316 f4484122 Michael Hanselmann
1317 f4484122 Michael Hanselmann
      # Wait again
1318 f4484122 Michael Hanselmann
      continue
1319 685ee993 Iustin Pop
1320 6c5a7090 Michael Hanselmann
    # Split result, a tuple of (field values, log entries)
1321 6c5a7090 Michael Hanselmann
    (job_info, log_entries) = result
1322 6c5a7090 Michael Hanselmann
    (status, ) = job_info
1323 6c5a7090 Michael Hanselmann
1324 6c5a7090 Michael Hanselmann
    if log_entries:
1325 6c5a7090 Michael Hanselmann
      for log_entry in log_entries:
1326 4e338533 Michael Hanselmann
        (serial, timestamp, log_type, message) = log_entry
1327 4e338533 Michael Hanselmann
        report_cbs.ReportLogMessage(job_id, serial, timestamp,
1328 4e338533 Michael Hanselmann
                                    log_type, message)
1329 6c5a7090 Michael Hanselmann
        prev_logmsg_serial = max(prev_logmsg_serial, serial)
1330 6c5a7090 Michael Hanselmann
1331 0bbe448c Michael Hanselmann
    # TODO: Handle canceled and archived jobs
1332 fbf0262f Michael Hanselmann
    elif status in (constants.JOB_STATUS_SUCCESS,
1333 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_ERROR,
1334 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELING,
1335 fbf0262f Michael Hanselmann
                    constants.JOB_STATUS_CANCELED):
1336 685ee993 Iustin Pop
      break
1337 6c5a7090 Michael Hanselmann
1338 6c5a7090 Michael Hanselmann
    prev_job_info = job_info
1339 685ee993 Iustin Pop
1340 4e338533 Michael Hanselmann
  jobs = cbs.QueryJobs([job_id], ["status", "opstatus", "opresult"])
1341 0bbe448c Michael Hanselmann
  if not jobs:
1342 0bbe448c Michael Hanselmann
    raise errors.JobLost("Job with id %s lost" % job_id)
1343 685ee993 Iustin Pop
1344 0e050889 Iustin Pop
  status, opstatus, result = jobs[0]
1345 4e338533 Michael Hanselmann
1346 0bbe448c Michael Hanselmann
  if status == constants.JOB_STATUS_SUCCESS:
1347 53c04d04 Iustin Pop
    return result
1348 4e338533 Michael Hanselmann
1349 4e338533 Michael Hanselmann
  if status in (constants.JOB_STATUS_CANCELING, constants.JOB_STATUS_CANCELED):
1350 fbf0262f Michael Hanselmann
    raise errors.OpExecError("Job was canceled")
1351 4e338533 Michael Hanselmann
1352 4e338533 Michael Hanselmann
  has_ok = False
1353 4e338533 Michael Hanselmann
  for idx, (status, msg) in enumerate(zip(opstatus, result)):
1354 4e338533 Michael Hanselmann
    if status == constants.OP_STATUS_SUCCESS:
1355 4e338533 Michael Hanselmann
      has_ok = True
1356 4e338533 Michael Hanselmann
    elif status == constants.OP_STATUS_ERROR:
1357 4e338533 Michael Hanselmann
      errors.MaybeRaise(msg)
1358 4e338533 Michael Hanselmann
1359 4e338533 Michael Hanselmann
      if has_ok:
1360 4e338533 Michael Hanselmann
        raise errors.OpExecError("partial failure (opcode %d): %s" %
1361 4e338533 Michael Hanselmann
                                 (idx, msg))
1362 4e338533 Michael Hanselmann
1363 4e338533 Michael Hanselmann
      raise errors.OpExecError(str(msg))
1364 4e338533 Michael Hanselmann
1365 4e338533 Michael Hanselmann
  # default failure mode
1366 4e338533 Michael Hanselmann
  raise errors.OpExecError(result)
1367 4e338533 Michael Hanselmann
1368 4e338533 Michael Hanselmann
1369 4e338533 Michael Hanselmann
class JobPollCbBase:
1370 4e338533 Michael Hanselmann
  """Base class for L{GenericPollJob} callbacks.
1371 4e338533 Michael Hanselmann

1372 4e338533 Michael Hanselmann
  """
1373 4e338533 Michael Hanselmann
  def __init__(self):
1374 4e338533 Michael Hanselmann
    """Initializes this class.
1375 4e338533 Michael Hanselmann

1376 4e338533 Michael Hanselmann
    """
1377 4e338533 Michael Hanselmann
1378 4e338533 Michael Hanselmann
  def WaitForJobChangeOnce(self, job_id, fields,
1379 4e338533 Michael Hanselmann
                           prev_job_info, prev_log_serial):
1380 4e338533 Michael Hanselmann
    """Waits for changes on a job.
1381 4e338533 Michael Hanselmann

1382 4e338533 Michael Hanselmann
    """
1383 4e338533 Michael Hanselmann
    raise NotImplementedError()
1384 4e338533 Michael Hanselmann
1385 4e338533 Michael Hanselmann
  def QueryJobs(self, job_ids, fields):
1386 4e338533 Michael Hanselmann
    """Returns the selected fields for the selected job IDs.
1387 4e338533 Michael Hanselmann

1388 4e338533 Michael Hanselmann
    @type job_ids: list of numbers
1389 4e338533 Michael Hanselmann
    @param job_ids: Job IDs
1390 4e338533 Michael Hanselmann
    @type fields: list of strings
1391 4e338533 Michael Hanselmann
    @param fields: Fields
1392 4e338533 Michael Hanselmann

1393 4e338533 Michael Hanselmann
    """
1394 4e338533 Michael Hanselmann
    raise NotImplementedError()
1395 4e338533 Michael Hanselmann
1396 4e338533 Michael Hanselmann
1397 4e338533 Michael Hanselmann
class JobPollReportCbBase:
1398 4e338533 Michael Hanselmann
  """Base class for L{GenericPollJob} reporting callbacks.
1399 4e338533 Michael Hanselmann

1400 4e338533 Michael Hanselmann
  """
1401 4e338533 Michael Hanselmann
  def __init__(self):
1402 4e338533 Michael Hanselmann
    """Initializes this class.
1403 4e338533 Michael Hanselmann

1404 4e338533 Michael Hanselmann
    """
1405 4e338533 Michael Hanselmann
1406 4e338533 Michael Hanselmann
  def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
1407 4e338533 Michael Hanselmann
    """Handles a log message.
1408 4e338533 Michael Hanselmann

1409 4e338533 Michael Hanselmann
    """
1410 4e338533 Michael Hanselmann
    raise NotImplementedError()
1411 4e338533 Michael Hanselmann
1412 4e338533 Michael Hanselmann
  def ReportNotChanged(self, job_id, status):
1413 4e338533 Michael Hanselmann
    """Called for if a job hasn't changed in a while.
1414 4e338533 Michael Hanselmann

1415 4e338533 Michael Hanselmann
    @type job_id: number
1416 4e338533 Michael Hanselmann
    @param job_id: Job ID
1417 4e338533 Michael Hanselmann
    @type status: string or None
1418 4e338533 Michael Hanselmann
    @param status: Job status if available
1419 4e338533 Michael Hanselmann

1420 4e338533 Michael Hanselmann
    """
1421 4e338533 Michael Hanselmann
    raise NotImplementedError()
1422 4e338533 Michael Hanselmann
1423 4e338533 Michael Hanselmann
1424 4e338533 Michael Hanselmann
class _LuxiJobPollCb(JobPollCbBase):
1425 4e338533 Michael Hanselmann
  def __init__(self, cl):
1426 4e338533 Michael Hanselmann
    """Initializes this class.
1427 4e338533 Michael Hanselmann

1428 4e338533 Michael Hanselmann
    """
1429 4e338533 Michael Hanselmann
    JobPollCbBase.__init__(self)
1430 4e338533 Michael Hanselmann
    self.cl = cl
1431 4e338533 Michael Hanselmann
1432 4e338533 Michael Hanselmann
  def WaitForJobChangeOnce(self, job_id, fields,
1433 4e338533 Michael Hanselmann
                           prev_job_info, prev_log_serial):
1434 4e338533 Michael Hanselmann
    """Waits for changes on a job.
1435 4e338533 Michael Hanselmann

1436 4e338533 Michael Hanselmann
    """
1437 4e338533 Michael Hanselmann
    return self.cl.WaitForJobChangeOnce(job_id, fields,
1438 4e338533 Michael Hanselmann
                                        prev_job_info, prev_log_serial)
1439 4e338533 Michael Hanselmann
1440 4e338533 Michael Hanselmann
  def QueryJobs(self, job_ids, fields):
1441 4e338533 Michael Hanselmann
    """Returns the selected fields for the selected job IDs.
1442 4e338533 Michael Hanselmann

1443 4e338533 Michael Hanselmann
    """
1444 4e338533 Michael Hanselmann
    return self.cl.QueryJobs(job_ids, fields)
1445 4e338533 Michael Hanselmann
1446 4e338533 Michael Hanselmann
1447 4e338533 Michael Hanselmann
class FeedbackFnJobPollReportCb(JobPollReportCbBase):
1448 4e338533 Michael Hanselmann
  def __init__(self, feedback_fn):
1449 4e338533 Michael Hanselmann
    """Initializes this class.
1450 4e338533 Michael Hanselmann

1451 4e338533 Michael Hanselmann
    """
1452 4e338533 Michael Hanselmann
    JobPollReportCbBase.__init__(self)
1453 4e338533 Michael Hanselmann
1454 4e338533 Michael Hanselmann
    self.feedback_fn = feedback_fn
1455 4e338533 Michael Hanselmann
1456 4e338533 Michael Hanselmann
    assert callable(feedback_fn)
1457 4e338533 Michael Hanselmann
1458 4e338533 Michael Hanselmann
  def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
1459 4e338533 Michael Hanselmann
    """Handles a log message.
1460 4e338533 Michael Hanselmann

1461 4e338533 Michael Hanselmann
    """
1462 4e338533 Michael Hanselmann
    self.feedback_fn((timestamp, log_type, log_msg))
1463 4e338533 Michael Hanselmann
1464 4e338533 Michael Hanselmann
  def ReportNotChanged(self, job_id, status):
1465 4e338533 Michael Hanselmann
    """Called if a job hasn't changed in a while.
1466 4e338533 Michael Hanselmann

1467 4e338533 Michael Hanselmann
    """
1468 4e338533 Michael Hanselmann
    # Ignore
1469 4e338533 Michael Hanselmann
1470 4e338533 Michael Hanselmann
1471 4e338533 Michael Hanselmann
class StdioJobPollReportCb(JobPollReportCbBase):
1472 4e338533 Michael Hanselmann
  def __init__(self):
1473 4e338533 Michael Hanselmann
    """Initializes this class.
1474 4e338533 Michael Hanselmann

1475 4e338533 Michael Hanselmann
    """
1476 4e338533 Michael Hanselmann
    JobPollReportCbBase.__init__(self)
1477 4e338533 Michael Hanselmann
1478 4e338533 Michael Hanselmann
    self.notified_queued = False
1479 4e338533 Michael Hanselmann
    self.notified_waitlock = False
1480 4e338533 Michael Hanselmann
1481 4e338533 Michael Hanselmann
  def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
1482 4e338533 Michael Hanselmann
    """Handles a log message.
1483 4e338533 Michael Hanselmann

1484 4e338533 Michael Hanselmann
    """
1485 4e338533 Michael Hanselmann
    ToStdout("%s %s", time.ctime(utils.MergeTime(timestamp)),
1486 8a7f1c61 Michael Hanselmann
             FormatLogMessage(log_type, log_msg))
1487 4e338533 Michael Hanselmann
1488 4e338533 Michael Hanselmann
  def ReportNotChanged(self, job_id, status):
1489 4e338533 Michael Hanselmann
    """Called if a job hasn't changed in a while.
1490 4e338533 Michael Hanselmann

1491 4e338533 Michael Hanselmann
    """
1492 4e338533 Michael Hanselmann
    if status is None:
1493 4e338533 Michael Hanselmann
      return
1494 4e338533 Michael Hanselmann
1495 4e338533 Michael Hanselmann
    if status == constants.JOB_STATUS_QUEUED and not self.notified_queued:
1496 4e338533 Michael Hanselmann
      ToStderr("Job %s is waiting in queue", job_id)
1497 4e338533 Michael Hanselmann
      self.notified_queued = True
1498 4e338533 Michael Hanselmann
1499 4e338533 Michael Hanselmann
    elif status == constants.JOB_STATUS_WAITLOCK and not self.notified_waitlock:
1500 4e338533 Michael Hanselmann
      ToStderr("Job %s is trying to acquire all necessary locks", job_id)
1501 4e338533 Michael Hanselmann
      self.notified_waitlock = True
1502 4e338533 Michael Hanselmann
1503 4e338533 Michael Hanselmann
1504 8a7f1c61 Michael Hanselmann
def FormatLogMessage(log_type, log_msg):
1505 8a7f1c61 Michael Hanselmann
  """Formats a job message according to its type.
1506 8a7f1c61 Michael Hanselmann

1507 8a7f1c61 Michael Hanselmann
  """
1508 8a7f1c61 Michael Hanselmann
  if log_type != constants.ELOG_MESSAGE:
1509 8a7f1c61 Michael Hanselmann
    log_msg = str(log_msg)
1510 8a7f1c61 Michael Hanselmann
1511 8a7f1c61 Michael Hanselmann
  return utils.SafeEncode(log_msg)
1512 8a7f1c61 Michael Hanselmann
1513 8a7f1c61 Michael Hanselmann
1514 583163a6 Michael Hanselmann
def PollJob(job_id, cl=None, feedback_fn=None, reporter=None):
1515 4e338533 Michael Hanselmann
  """Function to poll for the result of a job.
1516 4e338533 Michael Hanselmann

1517 4e338533 Michael Hanselmann
  @type job_id: job identified
1518 4e338533 Michael Hanselmann
  @param job_id: the job to poll for results
1519 4e338533 Michael Hanselmann
  @type cl: luxi.Client
1520 4e338533 Michael Hanselmann
  @param cl: the luxi client to use for communicating with the master;
1521 4e338533 Michael Hanselmann
             if None, a new client will be created
1522 4e338533 Michael Hanselmann

1523 4e338533 Michael Hanselmann
  """
1524 4e338533 Michael Hanselmann
  if cl is None:
1525 4e338533 Michael Hanselmann
    cl = GetClient()
1526 4e338533 Michael Hanselmann
1527 583163a6 Michael Hanselmann
  if reporter is None:
1528 583163a6 Michael Hanselmann
    if feedback_fn:
1529 583163a6 Michael Hanselmann
      reporter = FeedbackFnJobPollReportCb(feedback_fn)
1530 583163a6 Michael Hanselmann
    else:
1531 583163a6 Michael Hanselmann
      reporter = StdioJobPollReportCb()
1532 583163a6 Michael Hanselmann
  elif feedback_fn:
1533 583163a6 Michael Hanselmann
    raise errors.ProgrammerError("Can't specify reporter and feedback function")
1534 4e338533 Michael Hanselmann
1535 4e338533 Michael Hanselmann
  return GenericPollJob(job_id, _LuxiJobPollCb(cl), reporter)
1536 ceab32dd Iustin Pop
1537 ceab32dd Iustin Pop
1538 583163a6 Michael Hanselmann
def SubmitOpCode(op, cl=None, feedback_fn=None, opts=None, reporter=None):
1539 0a1e74d9 Iustin Pop
  """Legacy function to submit an opcode.
1540 0a1e74d9 Iustin Pop

1541 0a1e74d9 Iustin Pop
  This is just a simple wrapper over the construction of the processor
1542 0a1e74d9 Iustin Pop
  instance. It should be extended to better handle feedback and
1543 0a1e74d9 Iustin Pop
  interaction functions.
1544 0a1e74d9 Iustin Pop

1545 0a1e74d9 Iustin Pop
  """
1546 0a1e74d9 Iustin Pop
  if cl is None:
1547 0a1e74d9 Iustin Pop
    cl = GetClient()
1548 0a1e74d9 Iustin Pop
1549 293ba2d8 Iustin Pop
  SetGenericOpcodeOpts([op], opts)
1550 293ba2d8 Iustin Pop
1551 0a1e74d9 Iustin Pop
  job_id = SendJob([op], cl)
1552 0a1e74d9 Iustin Pop
1553 583163a6 Michael Hanselmann
  op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn,
1554 583163a6 Michael Hanselmann
                       reporter=reporter)
1555 53c04d04 Iustin Pop
1556 53c04d04 Iustin Pop
  return op_results[0]
1557 0a1e74d9 Iustin Pop
1558 0a1e74d9 Iustin Pop
1559 94428652 Iustin Pop
def SubmitOrSend(op, opts, cl=None, feedback_fn=None):
1560 94428652 Iustin Pop
  """Wrapper around SubmitOpCode or SendJob.
1561 94428652 Iustin Pop

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

1567 293ba2d8 Iustin Pop
  It will also process the opcodes if we're sending the via SendJob
1568 293ba2d8 Iustin Pop
  (otherwise SubmitOpCode does it).
1569 64c65a2a Iustin Pop

1570 94428652 Iustin Pop
  """
1571 94428652 Iustin Pop
  if opts and opts.submit_only:
1572 293ba2d8 Iustin Pop
    job = [op]
1573 293ba2d8 Iustin Pop
    SetGenericOpcodeOpts(job, opts)
1574 293ba2d8 Iustin Pop
    job_id = SendJob(job, cl=cl)
1575 e9d741b6 Iustin Pop
    raise JobSubmittedException(job_id)
1576 94428652 Iustin Pop
  else:
1577 293ba2d8 Iustin Pop
    return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn, opts=opts)
1578 293ba2d8 Iustin Pop
1579 293ba2d8 Iustin Pop
1580 293ba2d8 Iustin Pop
def SetGenericOpcodeOpts(opcode_list, options):
1581 293ba2d8 Iustin Pop
  """Processor for generic options.
1582 293ba2d8 Iustin Pop

1583 293ba2d8 Iustin Pop
  This function updates the given opcodes based on generic command
1584 293ba2d8 Iustin Pop
  line options (like debug, dry-run, etc.).
1585 293ba2d8 Iustin Pop

1586 293ba2d8 Iustin Pop
  @param opcode_list: list of opcodes
1587 293ba2d8 Iustin Pop
  @param options: command line options or None
1588 293ba2d8 Iustin Pop
  @return: None (in-place modification)
1589 293ba2d8 Iustin Pop

1590 293ba2d8 Iustin Pop
  """
1591 293ba2d8 Iustin Pop
  if not options:
1592 293ba2d8 Iustin Pop
    return
1593 293ba2d8 Iustin Pop
  for op in opcode_list:
1594 293ba2d8 Iustin Pop
    op.dry_run = options.dry_run
1595 293ba2d8 Iustin Pop
    op.debug_level = options.debug
1596 94428652 Iustin Pop
1597 94428652 Iustin Pop
1598 af30b2fd Michael Hanselmann
def GetClient():
1599 af30b2fd Michael Hanselmann
  # TODO: Cache object?
1600 b33e986b Iustin Pop
  try:
1601 b33e986b Iustin Pop
    client = luxi.Client()
1602 b33e986b Iustin Pop
  except luxi.NoMasterError:
1603 d9a51679 Michael Hanselmann
    ss = ssconf.SimpleStore()
1604 d9a51679 Michael Hanselmann
1605 d9a51679 Michael Hanselmann
    # Try to read ssconf file
1606 d9a51679 Michael Hanselmann
    try:
1607 d9a51679 Michael Hanselmann
      ss.GetMasterNode()
1608 d9a51679 Michael Hanselmann
    except errors.ConfigurationError:
1609 d9a51679 Michael Hanselmann
      raise errors.OpPrereqError("Cluster not initialized or this machine is"
1610 d9a51679 Michael Hanselmann
                                 " not part of a cluster")
1611 d9a51679 Michael Hanselmann
1612 d9a51679 Michael Hanselmann
    master, myself = ssconf.GetMasterAndMyself(ss=ss)
1613 b33e986b Iustin Pop
    if master != myself:
1614 b33e986b Iustin Pop
      raise errors.OpPrereqError("This is not the master node, please connect"
1615 b33e986b Iustin Pop
                                 " to node '%s' and rerun the command" %
1616 b33e986b Iustin Pop
                                 master)
1617 d9a51679 Michael Hanselmann
    raise
1618 b33e986b Iustin Pop
  return client
1619 af30b2fd Michael Hanselmann
1620 af30b2fd Michael Hanselmann
1621 73702ee7 Iustin Pop
def FormatError(err):
1622 73702ee7 Iustin Pop
  """Return a formatted error message for a given error.
1623 73702ee7 Iustin Pop

1624 73702ee7 Iustin Pop
  This function takes an exception instance and returns a tuple
1625 73702ee7 Iustin Pop
  consisting of two values: first, the recommended exit code, and
1626 73702ee7 Iustin Pop
  second, a string describing the error message (not
1627 73702ee7 Iustin Pop
  newline-terminated).
1628 73702ee7 Iustin Pop

1629 73702ee7 Iustin Pop
  """
1630 73702ee7 Iustin Pop
  retcode = 1
1631 73702ee7 Iustin Pop
  obuf = StringIO()
1632 e2e521d0 Iustin Pop
  msg = str(err)
1633 73702ee7 Iustin Pop
  if isinstance(err, errors.ConfigurationError):
1634 e2e521d0 Iustin Pop
    txt = "Corrupt configuration file: %s" % msg
1635 46fbdd04 Iustin Pop
    logging.error(txt)
1636 e2e521d0 Iustin Pop
    obuf.write(txt + "\n")
1637 73702ee7 Iustin Pop
    obuf.write("Aborting.")
1638 73702ee7 Iustin Pop
    retcode = 2
1639 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksAbort):
1640 73702ee7 Iustin Pop
    obuf.write("Failure: hooks execution failed:\n")
1641 73702ee7 Iustin Pop
    for node, script, out in err.args[0]:
1642 73702ee7 Iustin Pop
      if out:
1643 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s, output: %s\n" %
1644 73702ee7 Iustin Pop
                   (node, script, out))
1645 73702ee7 Iustin Pop
      else:
1646 73702ee7 Iustin Pop
        obuf.write("  node: %s, script: %s (no output)\n" %
1647 73702ee7 Iustin Pop
                   (node, script))
1648 73702ee7 Iustin Pop
  elif isinstance(err, errors.HooksFailure):
1649 e2e521d0 Iustin Pop
    obuf.write("Failure: hooks general failure: %s" % msg)
1650 73702ee7 Iustin Pop
  elif isinstance(err, errors.ResolverError):
1651 a744b676 Manuel Franceschini
    this_host = netutils.HostInfo.SysName()
1652 73702ee7 Iustin Pop
    if err.args[0] == this_host:
1653 73702ee7 Iustin Pop
      msg = "Failure: can't resolve my own hostname ('%s')"
1654 73702ee7 Iustin Pop
    else:
1655 73702ee7 Iustin Pop
      msg = "Failure: can't resolve hostname '%s'"
1656 73702ee7 Iustin Pop
    obuf.write(msg % err.args[0])
1657 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpPrereqError):
1658 5c983ee5 Iustin Pop
    if len(err.args) == 2:
1659 5c983ee5 Iustin Pop
      obuf.write("Failure: prerequisites not met for this"
1660 5c983ee5 Iustin Pop
               " operation:\nerror type: %s, error details:\n%s" %
1661 5c983ee5 Iustin Pop
                 (err.args[1], err.args[0]))
1662 5c983ee5 Iustin Pop
    else:
1663 5c983ee5 Iustin Pop
      obuf.write("Failure: prerequisites not met for this"
1664 5c983ee5 Iustin Pop
                 " operation:\n%s" % msg)
1665 73702ee7 Iustin Pop
  elif isinstance(err, errors.OpExecError):
1666 e2e521d0 Iustin Pop
    obuf.write("Failure: command execution error:\n%s" % msg)
1667 73702ee7 Iustin Pop
  elif isinstance(err, errors.TagError):
1668 e2e521d0 Iustin Pop
    obuf.write("Failure: invalid tag(s) given:\n%s" % msg)
1669 686d7433 Iustin Pop
  elif isinstance(err, errors.JobQueueDrainError):
1670 686d7433 Iustin Pop
    obuf.write("Failure: the job queue is marked for drain and doesn't"
1671 686d7433 Iustin Pop
               " accept new requests\n")
1672 f87b405e Michael Hanselmann
  elif isinstance(err, errors.JobQueueFull):
1673 f87b405e Michael Hanselmann
    obuf.write("Failure: the job queue is full and doesn't accept new"
1674 f87b405e Michael Hanselmann
               " job submissions until old jobs are archived\n")
1675 a5728081 Guido Trotter
  elif isinstance(err, errors.TypeEnforcementError):
1676 a5728081 Guido Trotter
    obuf.write("Parameter Error: %s" % msg)
1677 c1ce76bb Iustin Pop
  elif isinstance(err, errors.ParameterError):
1678 c1ce76bb Iustin Pop
    obuf.write("Failure: unknown/wrong parameter name '%s'" % msg)
1679 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.NoMasterError):
1680 03a8dbdc Iustin Pop
    obuf.write("Cannot communicate with the master daemon.\nIs it running"
1681 082c5adb Michael Hanselmann
               " and listening for connections?")
1682 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.TimeoutError):
1683 03a8dbdc Iustin Pop
    obuf.write("Timeout while talking to the master daemon. Error:\n"
1684 03a8dbdc Iustin Pop
               "%s" % msg)
1685 03a8dbdc Iustin Pop
  elif isinstance(err, luxi.ProtocolError):
1686 03a8dbdc Iustin Pop
    obuf.write("Unhandled protocol error while talking to the master daemon:\n"
1687 03a8dbdc Iustin Pop
               "%s" % msg)
1688 797506fc Michael Hanselmann
  elif isinstance(err, errors.GenericError):
1689 797506fc Michael Hanselmann
    obuf.write("Unhandled Ganeti error: %s" % msg)
1690 e9d741b6 Iustin Pop
  elif isinstance(err, JobSubmittedException):
1691 e9d741b6 Iustin Pop
    obuf.write("JobID: %s\n" % err.args[0])
1692 e9d741b6 Iustin Pop
    retcode = 0
1693 73702ee7 Iustin Pop
  else:
1694 e2e521d0 Iustin Pop
    obuf.write("Unhandled exception: %s" % msg)
1695 73702ee7 Iustin Pop
  return retcode, obuf.getvalue().rstrip('\n')
1696 73702ee7 Iustin Pop
1697 73702ee7 Iustin Pop
1698 de47cf8f Guido Trotter
def GenericMain(commands, override=None, aliases=None):
1699 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
1700 a8083063 Iustin Pop

1701 334d1483 Iustin Pop
  Arguments:
1702 334d1483 Iustin Pop
    - commands: a dictionary with a special structure, see the design doc
1703 334d1483 Iustin Pop
                for command line handling.
1704 334d1483 Iustin Pop
    - override: if not None, we expect a dictionary with keys that will
1705 334d1483 Iustin Pop
                override command line options; this can be used to pass
1706 334d1483 Iustin Pop
                options from the scripts to generic functions
1707 de47cf8f Guido Trotter
    - aliases: dictionary with command aliases {'alias': 'target, ...}
1708 a8083063 Iustin Pop

1709 a8083063 Iustin Pop
  """
1710 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
1711 a8083063 Iustin Pop
  if sys.argv:
1712 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
1713 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
1714 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
1715 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
1716 a8083063 Iustin Pop
    else:
1717 a8083063 Iustin Pop
      old_cmdline = ""
1718 a8083063 Iustin Pop
  else:
1719 a8083063 Iustin Pop
    binary = "<unknown program>"
1720 a8083063 Iustin Pop
    old_cmdline = ""
1721 a8083063 Iustin Pop
1722 de47cf8f Guido Trotter
  if aliases is None:
1723 de47cf8f Guido Trotter
    aliases = {}
1724 de47cf8f Guido Trotter
1725 3126878d Guido Trotter
  try:
1726 3126878d Guido Trotter
    func, options, args = _ParseArgs(sys.argv, commands, aliases)
1727 3126878d Guido Trotter
  except errors.ParameterError, err:
1728 3126878d Guido Trotter
    result, err_msg = FormatError(err)
1729 3126878d Guido Trotter
    ToStderr(err_msg)
1730 3126878d Guido Trotter
    return 1
1731 3126878d Guido Trotter
1732 a8083063 Iustin Pop
  if func is None: # parse error
1733 a8083063 Iustin Pop
    return 1
1734 a8083063 Iustin Pop
1735 334d1483 Iustin Pop
  if override is not None:
1736 334d1483 Iustin Pop
    for key, val in override.iteritems():
1737 334d1483 Iustin Pop
      setattr(options, key, val)
1738 334d1483 Iustin Pop
1739 82d9caef Iustin Pop
  utils.SetupLogging(constants.LOG_COMMANDS, debug=options.debug,
1740 82d9caef Iustin Pop
                     stderr_logging=True, program=binary)
1741 a8083063 Iustin Pop
1742 a8083063 Iustin Pop
  if old_cmdline:
1743 46fbdd04 Iustin Pop
    logging.info("run with arguments '%s'", old_cmdline)
1744 a8083063 Iustin Pop
  else:
1745 46fbdd04 Iustin Pop
    logging.info("run with no arguments")
1746 a8083063 Iustin Pop
1747 a8083063 Iustin Pop
  try:
1748 a4af651e Iustin Pop
    result = func(options, args)
1749 d8353c3a Iustin Pop
  except (errors.GenericError, luxi.ProtocolError,
1750 d8353c3a Iustin Pop
          JobSubmittedException), err:
1751 a4af651e Iustin Pop
    result, err_msg = FormatError(err)
1752 5bbd3f7f Michael Hanselmann
    logging.exception("Error during command processing")
1753 46fbdd04 Iustin Pop
    ToStderr(err_msg)
1754 a8083063 Iustin Pop
1755 a8083063 Iustin Pop
  return result
1756 137161c9 Michael Hanselmann
1757 137161c9 Michael Hanselmann
1758 d77490c5 Iustin Pop
def GenericInstanceCreate(mode, opts, args):
1759 d77490c5 Iustin Pop
  """Add an instance to the cluster via either creation or import.
1760 d77490c5 Iustin Pop

1761 d77490c5 Iustin Pop
  @param mode: constants.INSTANCE_CREATE or constants.INSTANCE_IMPORT
1762 d77490c5 Iustin Pop
  @param opts: the command line options selected by the user
1763 d77490c5 Iustin Pop
  @type args: list
1764 d77490c5 Iustin Pop
  @param args: should contain only one element, the new instance name
1765 d77490c5 Iustin Pop
  @rtype: int
1766 d77490c5 Iustin Pop
  @return: the desired exit code
1767 d77490c5 Iustin Pop

1768 d77490c5 Iustin Pop
  """
1769 d77490c5 Iustin Pop
  instance = args[0]
1770 d77490c5 Iustin Pop
1771 d77490c5 Iustin Pop
  (pnode, snode) = SplitNodeOption(opts.node)
1772 d77490c5 Iustin Pop
1773 d77490c5 Iustin Pop
  hypervisor = None
1774 d77490c5 Iustin Pop
  hvparams = {}
1775 d77490c5 Iustin Pop
  if opts.hypervisor:
1776 d77490c5 Iustin Pop
    hypervisor, hvparams = opts.hypervisor
1777 d77490c5 Iustin Pop
1778 d77490c5 Iustin Pop
  if opts.nics:
1779 d77490c5 Iustin Pop
    try:
1780 21bcb9aa Michael Hanselmann
      nic_max = max(int(nidx[0]) + 1 for nidx in opts.nics)
1781 d77490c5 Iustin Pop
    except ValueError, err:
1782 d77490c5 Iustin Pop
      raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
1783 d77490c5 Iustin Pop
    nics = [{}] * nic_max
1784 d77490c5 Iustin Pop
    for nidx, ndict in opts.nics:
1785 d77490c5 Iustin Pop
      nidx = int(nidx)
1786 d77490c5 Iustin Pop
      if not isinstance(ndict, dict):
1787 d77490c5 Iustin Pop
        msg = "Invalid nic/%d value: expected dict, got %s" % (nidx, ndict)
1788 d77490c5 Iustin Pop
        raise errors.OpPrereqError(msg)
1789 d77490c5 Iustin Pop
      nics[nidx] = ndict
1790 d77490c5 Iustin Pop
  elif opts.no_nics:
1791 d77490c5 Iustin Pop
    # no nics
1792 d77490c5 Iustin Pop
    nics = []
1793 0af0f641 Iustin Pop
  elif mode == constants.INSTANCE_CREATE:
1794 d77490c5 Iustin Pop
    # default of one nic, all auto
1795 d77490c5 Iustin Pop
    nics = [{}]
1796 0af0f641 Iustin Pop
  else:
1797 0af0f641 Iustin Pop
    # mode == import
1798 0af0f641 Iustin Pop
    nics = []
1799 d77490c5 Iustin Pop
1800 d77490c5 Iustin Pop
  if opts.disk_template == constants.DT_DISKLESS:
1801 d77490c5 Iustin Pop
    if opts.disks or opts.sd_size is not None:
1802 d77490c5 Iustin Pop
      raise errors.OpPrereqError("Diskless instance but disk"
1803 d77490c5 Iustin Pop
                                 " information passed")
1804 d77490c5 Iustin Pop
    disks = []
1805 d77490c5 Iustin Pop
  else:
1806 9b12ed0f Iustin Pop
    if (not opts.disks and not opts.sd_size
1807 9b12ed0f Iustin Pop
        and mode == constants.INSTANCE_CREATE):
1808 d77490c5 Iustin Pop
      raise errors.OpPrereqError("No disk information specified")
1809 d77490c5 Iustin Pop
    if opts.disks and opts.sd_size is not None:
1810 d77490c5 Iustin Pop
      raise errors.OpPrereqError("Please use either the '--disk' or"
1811 d77490c5 Iustin Pop
                                 " '-s' option")
1812 d77490c5 Iustin Pop
    if opts.sd_size is not None:
1813 d77490c5 Iustin Pop
      opts.disks = [(0, {"size": opts.sd_size})]
1814 9b12ed0f Iustin Pop
1815 9b12ed0f Iustin Pop
    if opts.disks:
1816 9b12ed0f Iustin Pop
      try:
1817 9b12ed0f Iustin Pop
        disk_max = max(int(didx[0]) + 1 for didx in opts.disks)
1818 9b12ed0f Iustin Pop
      except ValueError, err:
1819 9b12ed0f Iustin Pop
        raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
1820 9b12ed0f Iustin Pop
      disks = [{}] * disk_max
1821 9b12ed0f Iustin Pop
    else:
1822 9b12ed0f Iustin Pop
      disks = []
1823 d77490c5 Iustin Pop
    for didx, ddict in opts.disks:
1824 d77490c5 Iustin Pop
      didx = int(didx)
1825 d77490c5 Iustin Pop
      if not isinstance(ddict, dict):
1826 d77490c5 Iustin Pop
        msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict)
1827 d77490c5 Iustin Pop
        raise errors.OpPrereqError(msg)
1828 5029db65 Iustin Pop
      elif "size" in ddict:
1829 5029db65 Iustin Pop
        if "adopt" in ddict:
1830 5029db65 Iustin Pop
          raise errors.OpPrereqError("Only one of 'size' and 'adopt' allowed"
1831 5029db65 Iustin Pop
                                     " (disk %d)" % didx)
1832 5029db65 Iustin Pop
        try:
1833 5029db65 Iustin Pop
          ddict["size"] = utils.ParseUnit(ddict["size"])
1834 5029db65 Iustin Pop
        except ValueError, err:
1835 5029db65 Iustin Pop
          raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
1836 5029db65 Iustin Pop
                                     (didx, err))
1837 5029db65 Iustin Pop
      elif "adopt" in ddict:
1838 5029db65 Iustin Pop
        if mode == constants.INSTANCE_IMPORT:
1839 5029db65 Iustin Pop
          raise errors.OpPrereqError("Disk adoption not allowed for instance"
1840 5029db65 Iustin Pop
                                     " import")
1841 5029db65 Iustin Pop
        ddict["size"] = 0
1842 5029db65 Iustin Pop
      else:
1843 5029db65 Iustin Pop
        raise errors.OpPrereqError("Missing size or adoption source for"
1844 5029db65 Iustin Pop
                                   " disk %d" % didx)
1845 d77490c5 Iustin Pop
      disks[didx] = ddict
1846 d77490c5 Iustin Pop
1847 d77490c5 Iustin Pop
  utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_TYPES)
1848 d77490c5 Iustin Pop
  utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
1849 d77490c5 Iustin Pop
1850 d77490c5 Iustin Pop
  if mode == constants.INSTANCE_CREATE:
1851 d77490c5 Iustin Pop
    start = opts.start
1852 d77490c5 Iustin Pop
    os_type = opts.os
1853 1ee8e01a Guido Trotter
    force_variant = opts.force_variant
1854 d77490c5 Iustin Pop
    src_node = None
1855 d77490c5 Iustin Pop
    src_path = None
1856 25a8792c Iustin Pop
    no_install = opts.no_install
1857 e588764d Iustin Pop
    identify_defaults = False
1858 d77490c5 Iustin Pop
  elif mode == constants.INSTANCE_IMPORT:
1859 d77490c5 Iustin Pop
    start = False
1860 d77490c5 Iustin Pop
    os_type = None
1861 1ee8e01a Guido Trotter
    force_variant = False
1862 d77490c5 Iustin Pop
    src_node = opts.src_node
1863 d77490c5 Iustin Pop
    src_path = opts.src_dir
1864 25a8792c Iustin Pop
    no_install = None
1865 e588764d Iustin Pop
    identify_defaults = opts.identify_defaults
1866 d77490c5 Iustin Pop
  else:
1867 d77490c5 Iustin Pop
    raise errors.ProgrammerError("Invalid creation mode %s" % mode)
1868 d77490c5 Iustin Pop
1869 d77490c5 Iustin Pop
  op = opcodes.OpCreateInstance(instance_name=instance,
1870 d77490c5 Iustin Pop
                                disks=disks,
1871 d77490c5 Iustin Pop
                                disk_template=opts.disk_template,
1872 d77490c5 Iustin Pop
                                nics=nics,
1873 d77490c5 Iustin Pop
                                pnode=pnode, snode=snode,
1874 d77490c5 Iustin Pop
                                ip_check=opts.ip_check,
1875 460d22be Iustin Pop
                                name_check=opts.name_check,
1876 d77490c5 Iustin Pop
                                wait_for_sync=opts.wait_for_sync,
1877 d77490c5 Iustin Pop
                                file_storage_dir=opts.file_storage_dir,
1878 d77490c5 Iustin Pop
                                file_driver=opts.file_driver,
1879 d77490c5 Iustin Pop
                                iallocator=opts.iallocator,
1880 d77490c5 Iustin Pop
                                hypervisor=hypervisor,
1881 d77490c5 Iustin Pop
                                hvparams=hvparams,
1882 d77490c5 Iustin Pop
                                beparams=opts.beparams,
1883 062a7100 Iustin Pop
                                osparams=opts.osparams,
1884 d77490c5 Iustin Pop
                                mode=mode,
1885 d77490c5 Iustin Pop
                                start=start,
1886 d77490c5 Iustin Pop
                                os_type=os_type,
1887 1ee8e01a Guido Trotter
                                force_variant=force_variant,
1888 d77490c5 Iustin Pop
                                src_node=src_node,
1889 25a8792c Iustin Pop
                                src_path=src_path,
1890 e588764d Iustin Pop
                                no_install=no_install,
1891 e588764d Iustin Pop
                                identify_defaults=identify_defaults)
1892 d77490c5 Iustin Pop
1893 d77490c5 Iustin Pop
  SubmitOrSend(op, opts)
1894 d77490c5 Iustin Pop
  return 0
1895 d77490c5 Iustin Pop
1896 d77490c5 Iustin Pop
1897 7e49b6ce Michael Hanselmann
class _RunWhileClusterStoppedHelper:
1898 7e49b6ce Michael Hanselmann
  """Helper class for L{RunWhileClusterStopped} to simplify state management
1899 7e49b6ce Michael Hanselmann

1900 7e49b6ce Michael Hanselmann
  """
1901 7e49b6ce Michael Hanselmann
  def __init__(self, feedback_fn, cluster_name, master_node, online_nodes):
1902 7e49b6ce Michael Hanselmann
    """Initializes this class.
1903 7e49b6ce Michael Hanselmann

1904 7e49b6ce Michael Hanselmann
    @type feedback_fn: callable
1905 7e49b6ce Michael Hanselmann
    @param feedback_fn: Feedback function
1906 7e49b6ce Michael Hanselmann
    @type cluster_name: string
1907 7e49b6ce Michael Hanselmann
    @param cluster_name: Cluster name
1908 7e49b6ce Michael Hanselmann
    @type master_node: string
1909 7e49b6ce Michael Hanselmann
    @param master_node Master node name
1910 7e49b6ce Michael Hanselmann
    @type online_nodes: list
1911 7e49b6ce Michael Hanselmann
    @param online_nodes: List of names of online nodes
1912 7e49b6ce Michael Hanselmann

1913 7e49b6ce Michael Hanselmann
    """
1914 7e49b6ce Michael Hanselmann
    self.feedback_fn = feedback_fn
1915 7e49b6ce Michael Hanselmann
    self.cluster_name = cluster_name
1916 7e49b6ce Michael Hanselmann
    self.master_node = master_node
1917 7e49b6ce Michael Hanselmann
    self.online_nodes = online_nodes
1918 7e49b6ce Michael Hanselmann
1919 7e49b6ce Michael Hanselmann
    self.ssh = ssh.SshRunner(self.cluster_name)
1920 7e49b6ce Michael Hanselmann
1921 7e49b6ce Michael Hanselmann
    self.nonmaster_nodes = [name for name in online_nodes
1922 7e49b6ce Michael Hanselmann
                            if name != master_node]
1923 7e49b6ce Michael Hanselmann
1924 7e49b6ce Michael Hanselmann
    assert self.master_node not in self.nonmaster_nodes
1925 7e49b6ce Michael Hanselmann
1926 7e49b6ce Michael Hanselmann
  def _RunCmd(self, node_name, cmd):
1927 7e49b6ce Michael Hanselmann
    """Runs a command on the local or a remote machine.
1928 7e49b6ce Michael Hanselmann

1929 7e49b6ce Michael Hanselmann
    @type node_name: string
1930 7e49b6ce Michael Hanselmann
    @param node_name: Machine name
1931 7e49b6ce Michael Hanselmann
    @type cmd: list
1932 7e49b6ce Michael Hanselmann
    @param cmd: Command
1933 7e49b6ce Michael Hanselmann

1934 7e49b6ce Michael Hanselmann
    """
1935 7e49b6ce Michael Hanselmann
    if node_name is None or node_name == self.master_node:
1936 7e49b6ce Michael Hanselmann
      # No need to use SSH
1937 7e49b6ce Michael Hanselmann
      result = utils.RunCmd(cmd)
1938 7e49b6ce Michael Hanselmann
    else:
1939 7e49b6ce Michael Hanselmann
      result = self.ssh.Run(node_name, "root", utils.ShellQuoteArgs(cmd))
1940 7e49b6ce Michael Hanselmann
1941 7e49b6ce Michael Hanselmann
    if result.failed:
1942 7e49b6ce Michael Hanselmann
      errmsg = ["Failed to run command %s" % result.cmd]
1943 7e49b6ce Michael Hanselmann
      if node_name:
1944 7e49b6ce Michael Hanselmann
        errmsg.append("on node %s" % node_name)
1945 7e49b6ce Michael Hanselmann
      errmsg.append(": exitcode %s and error %s" %
1946 7e49b6ce Michael Hanselmann
                    (result.exit_code, result.output))
1947 7e49b6ce Michael Hanselmann
      raise errors.OpExecError(" ".join(errmsg))
1948 7e49b6ce Michael Hanselmann
1949 7e49b6ce Michael Hanselmann
  def Call(self, fn, *args):
1950 7e49b6ce Michael Hanselmann
    """Call function while all daemons are stopped.
1951 7e49b6ce Michael Hanselmann

1952 7e49b6ce Michael Hanselmann
    @type fn: callable
1953 7e49b6ce Michael Hanselmann
    @param fn: Function to be called
1954 7e49b6ce Michael Hanselmann

1955 7e49b6ce Michael Hanselmann
    """
1956 7e49b6ce Michael Hanselmann
    # Pause watcher by acquiring an exclusive lock on watcher state file
1957 7e49b6ce Michael Hanselmann
    self.feedback_fn("Blocking watcher")
1958 7e49b6ce Michael Hanselmann
    watcher_block = utils.FileLock.Open(constants.WATCHER_STATEFILE)
1959 7e49b6ce Michael Hanselmann
    try:
1960 7e49b6ce Michael Hanselmann
      # TODO: Currently, this just blocks. There's no timeout.
1961 7e49b6ce Michael Hanselmann
      # TODO: Should it be a shared lock?
1962 7e49b6ce Michael Hanselmann
      watcher_block.Exclusive(blocking=True)
1963 7e49b6ce Michael Hanselmann
1964 7e49b6ce Michael Hanselmann
      # Stop master daemons, so that no new jobs can come in and all running
1965 7e49b6ce Michael Hanselmann
      # ones are finished
1966 7e49b6ce Michael Hanselmann
      self.feedback_fn("Stopping master daemons")
1967 7e49b6ce Michael Hanselmann
      self._RunCmd(None, [constants.DAEMON_UTIL, "stop-master"])
1968 7e49b6ce Michael Hanselmann
      try:
1969 7e49b6ce Michael Hanselmann
        # Stop daemons on all nodes
1970 7e49b6ce Michael Hanselmann
        for node_name in self.online_nodes:
1971 7e49b6ce Michael Hanselmann
          self.feedback_fn("Stopping daemons on %s" % node_name)
1972 7e49b6ce Michael Hanselmann
          self._RunCmd(node_name, [constants.DAEMON_UTIL, "stop-all"])
1973 7e49b6ce Michael Hanselmann
1974 7e49b6ce Michael Hanselmann
        # All daemons are shut down now
1975 7e49b6ce Michael Hanselmann
        try:
1976 7e49b6ce Michael Hanselmann
          return fn(self, *args)
1977 d512e84b Michael Hanselmann
        except Exception, err:
1978 d512e84b Michael Hanselmann
          _, errmsg = FormatError(err)
1979 7e49b6ce Michael Hanselmann
          logging.exception("Caught exception")
1980 d512e84b Michael Hanselmann
          self.feedback_fn(errmsg)
1981 7e49b6ce Michael Hanselmann
          raise
1982 7e49b6ce Michael Hanselmann
      finally:
1983 7e49b6ce Michael Hanselmann
        # Start cluster again, master node last
1984 7e49b6ce Michael Hanselmann
        for node_name in self.nonmaster_nodes + [self.master_node]:
1985 7e49b6ce Michael Hanselmann
          self.feedback_fn("Starting daemons on %s" % node_name)
1986 7e49b6ce Michael Hanselmann
          self._RunCmd(node_name, [constants.DAEMON_UTIL, "start-all"])
1987 7e49b6ce Michael Hanselmann
    finally:
1988 7e49b6ce Michael Hanselmann
      # Resume watcher
1989 7e49b6ce Michael Hanselmann
      watcher_block.Close()
1990 7e49b6ce Michael Hanselmann
1991 7e49b6ce Michael Hanselmann
1992 7e49b6ce Michael Hanselmann
def RunWhileClusterStopped(feedback_fn, fn, *args):
1993 7e49b6ce Michael Hanselmann
  """Calls a function while all cluster daemons are stopped.
1994 7e49b6ce Michael Hanselmann

1995 7e49b6ce Michael Hanselmann
  @type feedback_fn: callable
1996 7e49b6ce Michael Hanselmann
  @param feedback_fn: Feedback function
1997 7e49b6ce Michael Hanselmann
  @type fn: callable
1998 7e49b6ce Michael Hanselmann
  @param fn: Function to be called when daemons are stopped
1999 7e49b6ce Michael Hanselmann

2000 7e49b6ce Michael Hanselmann
  """
2001 7e49b6ce Michael Hanselmann
  feedback_fn("Gathering cluster information")
2002 7e49b6ce Michael Hanselmann
2003 7e49b6ce Michael Hanselmann
  # This ensures we're running on the master daemon
2004 7e49b6ce Michael Hanselmann
  cl = GetClient()
2005 7e49b6ce Michael Hanselmann
2006 7e49b6ce Michael Hanselmann
  (cluster_name, master_node) = \
2007 7e49b6ce Michael Hanselmann
    cl.QueryConfigValues(["cluster_name", "master_node"])
2008 7e49b6ce Michael Hanselmann
2009 7e49b6ce Michael Hanselmann
  online_nodes = GetOnlineNodes([], cl=cl)
2010 7e49b6ce Michael Hanselmann
2011 7e49b6ce Michael Hanselmann
  # Don't keep a reference to the client. The master daemon will go away.
2012 7e49b6ce Michael Hanselmann
  del cl
2013 7e49b6ce Michael Hanselmann
2014 7e49b6ce Michael Hanselmann
  assert master_node in online_nodes
2015 7e49b6ce Michael Hanselmann
2016 7e49b6ce Michael Hanselmann
  return _RunWhileClusterStoppedHelper(feedback_fn, cluster_name, master_node,
2017 7e49b6ce Michael Hanselmann
                                       online_nodes).Call(fn, *args)
2018 7e49b6ce Michael Hanselmann
2019 7e49b6ce Michael Hanselmann
2020 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
2021 9fbfbb7b Iustin Pop
                  numfields=None, unitfields=None,
2022 9fbfbb7b Iustin Pop
                  units=None):
2023 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
2024 137161c9 Michael Hanselmann

2025 9fbfbb7b Iustin Pop
  @type headers: dict
2026 9fbfbb7b Iustin Pop
  @param headers: dictionary mapping field names to headers for
2027 9fbfbb7b Iustin Pop
      the table
2028 9fbfbb7b Iustin Pop
  @type fields: list
2029 9fbfbb7b Iustin Pop
  @param fields: the field names corresponding to each row in
2030 9fbfbb7b Iustin Pop
      the data field
2031 9fbfbb7b Iustin Pop
  @param separator: the separator to be used; if this is None,
2032 9fbfbb7b Iustin Pop
      the default 'smart' algorithm is used which computes optimal
2033 9fbfbb7b Iustin Pop
      field width, otherwise just the separator is used between
2034 9fbfbb7b Iustin Pop
      each field
2035 9fbfbb7b Iustin Pop
  @type data: list
2036 9fbfbb7b Iustin Pop
  @param data: a list of lists, each sublist being one row to be output
2037 9fbfbb7b Iustin Pop
  @type numfields: list
2038 9fbfbb7b Iustin Pop
  @param numfields: a list with the fields that hold numeric
2039 9fbfbb7b Iustin Pop
      values and thus should be right-aligned
2040 9fbfbb7b Iustin Pop
  @type unitfields: list
2041 9fbfbb7b Iustin Pop
  @param unitfields: a list with the fields that hold numeric
2042 9fbfbb7b Iustin Pop
      values that should be formatted with the units field
2043 9fbfbb7b Iustin Pop
  @type units: string or None
2044 9fbfbb7b Iustin Pop
  @param units: the units we should use for formatting, or None for
2045 9fbfbb7b Iustin Pop
      automatic choice (human-readable for non-separator usage, otherwise
2046 9fbfbb7b Iustin Pop
      megabytes); this is a one-letter string
2047 137161c9 Michael Hanselmann

2048 137161c9 Michael Hanselmann
  """
2049 9fbfbb7b Iustin Pop
  if units is None:
2050 9fbfbb7b Iustin Pop
    if separator:
2051 9fbfbb7b Iustin Pop
      units = "m"
2052 9fbfbb7b Iustin Pop
    else:
2053 9fbfbb7b Iustin Pop
      units = "h"
2054 9fbfbb7b Iustin Pop
2055 137161c9 Michael Hanselmann
  if numfields is None:
2056 137161c9 Michael Hanselmann
    numfields = []
2057 137161c9 Michael Hanselmann
  if unitfields is None:
2058 137161c9 Michael Hanselmann
    unitfields = []
2059 137161c9 Michael Hanselmann
2060 fe267188 Iustin Pop
  numfields = utils.FieldSet(*numfields)   # pylint: disable-msg=W0142
2061 fe267188 Iustin Pop
  unitfields = utils.FieldSet(*unitfields) # pylint: disable-msg=W0142
2062 00430f8e Iustin Pop
2063 137161c9 Michael Hanselmann
  format_fields = []
2064 137161c9 Michael Hanselmann
  for field in fields:
2065 01ca31ae Iustin Pop
    if headers and field not in headers:
2066 ea5a5b74 Guido Trotter
      # TODO: handle better unknown fields (either revert to old
2067 71c1af58 Iustin Pop
      # style of raising exception, or deal more intelligently with
2068 71c1af58 Iustin Pop
      # variable fields)
2069 71c1af58 Iustin Pop
      headers[field] = field
2070 137161c9 Michael Hanselmann
    if separator is not None:
2071 137161c9 Michael Hanselmann
      format_fields.append("%s")
2072 00430f8e Iustin Pop
    elif numfields.Matches(field):
2073 137161c9 Michael Hanselmann
      format_fields.append("%*s")
2074 137161c9 Michael Hanselmann
    else:
2075 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
2076 137161c9 Michael Hanselmann
2077 137161c9 Michael Hanselmann
  if separator is None:
2078 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
2079 c04bc777 Iustin Pop
    format_str = ' '.join(format_fields)
2080 137161c9 Michael Hanselmann
  else:
2081 c04bc777 Iustin Pop
    format_str = separator.replace("%", "%%").join(format_fields)
2082 137161c9 Michael Hanselmann
2083 137161c9 Michael Hanselmann
  for row in data:
2084 dcbd6288 Guido Trotter
    if row is None:
2085 dcbd6288 Guido Trotter
      continue
2086 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
2087 00430f8e Iustin Pop
      if unitfields.Matches(fields[idx]):
2088 137161c9 Michael Hanselmann
        try:
2089 137161c9 Michael Hanselmann
          val = int(val)
2090 691744c4 Iustin Pop
        except (TypeError, ValueError):
2091 137161c9 Michael Hanselmann
          pass
2092 137161c9 Michael Hanselmann
        else:
2093 9fbfbb7b Iustin Pop
          val = row[idx] = utils.FormatUnit(val, units)
2094 01ca31ae Iustin Pop
      val = row[idx] = str(val)
2095 137161c9 Michael Hanselmann
      if separator is None:
2096 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
2097 137161c9 Michael Hanselmann
2098 16be8703 Iustin Pop
  result = []
2099 137161c9 Michael Hanselmann
  if headers:
2100 137161c9 Michael Hanselmann
    args = []
2101 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
2102 137161c9 Michael Hanselmann
      hdr = headers[name]
2103 137161c9 Michael Hanselmann
      if separator is None:
2104 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
2105 137161c9 Michael Hanselmann
        args.append(mlens[idx])
2106 137161c9 Michael Hanselmann
      args.append(hdr)
2107 c04bc777 Iustin Pop
    result.append(format_str % tuple(args))
2108 137161c9 Michael Hanselmann
2109 ec39d63c Michael Hanselmann
  if separator is None:
2110 ec39d63c Michael Hanselmann
    assert len(mlens) == len(fields)
2111 ec39d63c Michael Hanselmann
2112 ec39d63c Michael Hanselmann
    if fields and not numfields.Matches(fields[-1]):
2113 ec39d63c Michael Hanselmann
      mlens[-1] = 0
2114 ec39d63c Michael Hanselmann
2115 137161c9 Michael Hanselmann
  for line in data:
2116 137161c9 Michael Hanselmann
    args = []
2117 dcbd6288 Guido Trotter
    if line is None:
2118 dcbd6288 Guido Trotter
      line = ['-' for _ in fields]
2119 f1501b3f Michael Hanselmann
    for idx in range(len(fields)):
2120 137161c9 Michael Hanselmann
      if separator is None:
2121 137161c9 Michael Hanselmann
        args.append(mlens[idx])
2122 137161c9 Michael Hanselmann
      args.append(line[idx])
2123 c04bc777 Iustin Pop
    result.append(format_str % tuple(args))
2124 16be8703 Iustin Pop
2125 16be8703 Iustin Pop
  return result
2126 3386e7a9 Iustin Pop
2127 3386e7a9 Iustin Pop
2128 3386e7a9 Iustin Pop
def FormatTimestamp(ts):
2129 3386e7a9 Iustin Pop
  """Formats a given timestamp.
2130 3386e7a9 Iustin Pop

2131 3386e7a9 Iustin Pop
  @type ts: timestamp
2132 3386e7a9 Iustin Pop
  @param ts: a timeval-type timestamp, a tuple of seconds and microseconds
2133 3386e7a9 Iustin Pop

2134 3386e7a9 Iustin Pop
  @rtype: string
2135 5fcc718f Iustin Pop
  @return: a string with the formatted timestamp
2136 3386e7a9 Iustin Pop

2137 3386e7a9 Iustin Pop
  """
2138 e0ec0ff6 Iustin Pop
  if not isinstance (ts, (tuple, list)) or len(ts) != 2:
2139 e0ec0ff6 Iustin Pop
    return '?'
2140 3386e7a9 Iustin Pop
  sec, usec = ts
2141 3386e7a9 Iustin Pop
  return time.strftime("%F %T", time.localtime(sec)) + ".%06d" % usec
2142 2241e2b9 Iustin Pop
2143 2241e2b9 Iustin Pop
2144 2241e2b9 Iustin Pop
def ParseTimespec(value):
2145 2241e2b9 Iustin Pop
  """Parse a time specification.
2146 2241e2b9 Iustin Pop

2147 2241e2b9 Iustin Pop
  The following suffixed will be recognized:
2148 2241e2b9 Iustin Pop

2149 2241e2b9 Iustin Pop
    - s: seconds
2150 2241e2b9 Iustin Pop
    - m: minutes
2151 2241e2b9 Iustin Pop
    - h: hours
2152 2241e2b9 Iustin Pop
    - d: day
2153 2241e2b9 Iustin Pop
    - w: weeks
2154 2241e2b9 Iustin Pop

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

2157 2241e2b9 Iustin Pop
  """
2158 2241e2b9 Iustin Pop
  value = str(value)
2159 2241e2b9 Iustin Pop
  if not value:
2160 2241e2b9 Iustin Pop
    raise errors.OpPrereqError("Empty time specification passed")
2161 2241e2b9 Iustin Pop
  suffix_map = {
2162 2241e2b9 Iustin Pop
    's': 1,
2163 2241e2b9 Iustin Pop
    'm': 60,
2164 2241e2b9 Iustin Pop
    'h': 3600,
2165 2241e2b9 Iustin Pop
    'd': 86400,
2166 2241e2b9 Iustin Pop
    'w': 604800,
2167 2241e2b9 Iustin Pop
    }
2168 2241e2b9 Iustin Pop
  if value[-1] not in suffix_map:
2169 2241e2b9 Iustin Pop
    try:
2170 2241e2b9 Iustin Pop
      value = int(value)
2171 691744c4 Iustin Pop
    except (TypeError, ValueError):
2172 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
2173 2241e2b9 Iustin Pop
  else:
2174 2241e2b9 Iustin Pop
    multiplier = suffix_map[value[-1]]
2175 2241e2b9 Iustin Pop
    value = value[:-1]
2176 2241e2b9 Iustin Pop
    if not value: # no data left after stripping the suffix
2177 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification (only"
2178 2241e2b9 Iustin Pop
                                 " suffix passed)")
2179 2241e2b9 Iustin Pop
    try:
2180 2241e2b9 Iustin Pop
      value = int(value) * multiplier
2181 691744c4 Iustin Pop
    except (TypeError, ValueError):
2182 2241e2b9 Iustin Pop
      raise errors.OpPrereqError("Invalid time specification '%s'" % value)
2183 2241e2b9 Iustin Pop
  return value
2184 46fbdd04 Iustin Pop
2185 46fbdd04 Iustin Pop
2186 e9e26bb3 Iustin Pop
def GetOnlineNodes(nodes, cl=None, nowarn=False, secondary_ips=False,
2187 e9e26bb3 Iustin Pop
                   filter_master=False):
2188 4040a784 Iustin Pop
  """Returns the names of online nodes.
2189 4040a784 Iustin Pop

2190 4040a784 Iustin Pop
  This function will also log a warning on stderr with the names of
2191 4040a784 Iustin Pop
  the online nodes.
2192 4040a784 Iustin Pop

2193 4040a784 Iustin Pop
  @param nodes: if not empty, use only this subset of nodes (minus the
2194 4040a784 Iustin Pop
      offline ones)
2195 4040a784 Iustin Pop
  @param cl: if not None, luxi client to use
2196 4040a784 Iustin Pop
  @type nowarn: boolean
2197 4040a784 Iustin Pop
  @param nowarn: by default, this function will output a note with the
2198 4040a784 Iustin Pop
      offline nodes that are skipped; if this parameter is True the
2199 4040a784 Iustin Pop
      note is not displayed
2200 e9e26bb3 Iustin Pop
  @type secondary_ips: boolean
2201 e9e26bb3 Iustin Pop
  @param secondary_ips: if True, return the secondary IPs instead of the
2202 e9e26bb3 Iustin Pop
      names, useful for doing network traffic over the replication interface
2203 e9e26bb3 Iustin Pop
      (if any)
2204 e9e26bb3 Iustin Pop
  @type filter_master: boolean
2205 e9e26bb3 Iustin Pop
  @param filter_master: if True, do not return the master node in the list
2206 e9e26bb3 Iustin Pop
      (useful in coordination with secondary_ips where we cannot check our
2207 e9e26bb3 Iustin Pop
      node name against the list)
2208 4040a784 Iustin Pop

2209 4040a784 Iustin Pop
  """
2210 4040a784 Iustin Pop
  if cl is None:
2211 4040a784 Iustin Pop
    cl = GetClient()
2212 4040a784 Iustin Pop
2213 e9e26bb3 Iustin Pop
  if secondary_ips:
2214 e9e26bb3 Iustin Pop
    name_idx = 2
2215 e9e26bb3 Iustin Pop
  else:
2216 e9e26bb3 Iustin Pop
    name_idx = 0
2217 e9e26bb3 Iustin Pop
2218 e9e26bb3 Iustin Pop
  if filter_master:
2219 e9e26bb3 Iustin Pop
    master_node = cl.QueryConfigValues(["master_node"])[0]
2220 e9e26bb3 Iustin Pop
    filter_fn = lambda x: x != master_node
2221 e9e26bb3 Iustin Pop
  else:
2222 e9e26bb3 Iustin Pop
    filter_fn = lambda _: True
2223 e9e26bb3 Iustin Pop
2224 e9e26bb3 Iustin Pop
  result = cl.QueryNodes(names=nodes, fields=["name", "offline", "sip"],
2225 2e7b8369 Iustin Pop
                         use_locking=False)
2226 4040a784 Iustin Pop
  offline = [row[0] for row in result if row[1]]
2227 4040a784 Iustin Pop
  if offline and not nowarn:
2228 1f864b60 Iustin Pop
    ToStderr("Note: skipping offline node(s): %s" % utils.CommaJoin(offline))
2229 e9e26bb3 Iustin Pop
  return [row[name_idx] for row in result if not row[1] and filter_fn(row[0])]
2230 4040a784 Iustin Pop
2231 4040a784 Iustin Pop
2232 46fbdd04 Iustin Pop
def _ToStream(stream, txt, *args):
2233 46fbdd04 Iustin Pop
  """Write a message to a stream, bypassing the logging system
2234 46fbdd04 Iustin Pop

2235 46fbdd04 Iustin Pop
  @type stream: file object
2236 46fbdd04 Iustin Pop
  @param stream: the file to which we should write
2237 46fbdd04 Iustin Pop
  @type txt: str
2238 46fbdd04 Iustin Pop
  @param txt: the message
2239 46fbdd04 Iustin Pop

2240 46fbdd04 Iustin Pop
  """
2241 46fbdd04 Iustin Pop
  if args:
2242 46fbdd04 Iustin Pop
    args = tuple(args)
2243 46fbdd04 Iustin Pop
    stream.write(txt % args)
2244 46fbdd04 Iustin Pop
  else:
2245 46fbdd04 Iustin Pop
    stream.write(txt)
2246 46fbdd04 Iustin Pop
  stream.write('\n')
2247 46fbdd04 Iustin Pop
  stream.flush()
2248 46fbdd04 Iustin Pop
2249 46fbdd04 Iustin Pop
2250 46fbdd04 Iustin Pop
def ToStdout(txt, *args):
2251 46fbdd04 Iustin Pop
  """Write a message to stdout only, bypassing the logging system
2252 46fbdd04 Iustin Pop

2253 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
2254 46fbdd04 Iustin Pop

2255 46fbdd04 Iustin Pop
  @type txt: str
2256 46fbdd04 Iustin Pop
  @param txt: the message
2257 46fbdd04 Iustin Pop

2258 46fbdd04 Iustin Pop
  """
2259 46fbdd04 Iustin Pop
  _ToStream(sys.stdout, txt, *args)
2260 46fbdd04 Iustin Pop
2261 46fbdd04 Iustin Pop
2262 46fbdd04 Iustin Pop
def ToStderr(txt, *args):
2263 46fbdd04 Iustin Pop
  """Write a message to stderr only, bypassing the logging system
2264 46fbdd04 Iustin Pop

2265 46fbdd04 Iustin Pop
  This is just a wrapper over _ToStream.
2266 46fbdd04 Iustin Pop

2267 46fbdd04 Iustin Pop
  @type txt: str
2268 46fbdd04 Iustin Pop
  @param txt: the message
2269 46fbdd04 Iustin Pop

2270 46fbdd04 Iustin Pop
  """
2271 46fbdd04 Iustin Pop
  _ToStream(sys.stderr, txt, *args)
2272 479636a3 Iustin Pop
2273 479636a3 Iustin Pop
2274 479636a3 Iustin Pop
class JobExecutor(object):
2275 479636a3 Iustin Pop
  """Class which manages the submission and execution of multiple jobs.
2276 479636a3 Iustin Pop

2277 479636a3 Iustin Pop
  Note that instances of this class should not be reused between
2278 479636a3 Iustin Pop
  GetResults() calls.
2279 479636a3 Iustin Pop

2280 479636a3 Iustin Pop
  """
2281 919ca415 Iustin Pop
  def __init__(self, cl=None, verbose=True, opts=None, feedback_fn=None):
2282 479636a3 Iustin Pop
    self.queue = []
2283 479636a3 Iustin Pop
    if cl is None:
2284 479636a3 Iustin Pop
      cl = GetClient()
2285 479636a3 Iustin Pop
    self.cl = cl
2286 479636a3 Iustin Pop
    self.verbose = verbose
2287 23b4b983 Iustin Pop
    self.jobs = []
2288 cff5fa7f Iustin Pop
    self.opts = opts
2289 919ca415 Iustin Pop
    self.feedback_fn = feedback_fn
2290 479636a3 Iustin Pop
2291 479636a3 Iustin Pop
  def QueueJob(self, name, *ops):
2292 23b4b983 Iustin Pop
    """Record a job for later submit.
2293 479636a3 Iustin Pop

2294 479636a3 Iustin Pop
    @type name: string
2295 479636a3 Iustin Pop
    @param name: a description of the job, will be used in WaitJobSet
2296 479636a3 Iustin Pop
    """
2297 cff5fa7f Iustin Pop
    SetGenericOpcodeOpts(ops, self.opts)
2298 23b4b983 Iustin Pop
    self.queue.append((name, ops))
2299 23b4b983 Iustin Pop
2300 66ecc479 Guido Trotter
  def SubmitPending(self, each=False):
2301 23b4b983 Iustin Pop
    """Submit all pending jobs.
2302 23b4b983 Iustin Pop

2303 23b4b983 Iustin Pop
    """
2304 66ecc479 Guido Trotter
    if each:
2305 66ecc479 Guido Trotter
      results = []
2306 66ecc479 Guido Trotter
      for row in self.queue:
2307 66ecc479 Guido Trotter
        # SubmitJob will remove the success status, but raise an exception if
2308 66ecc479 Guido Trotter
        # the submission fails, so we'll notice that anyway.
2309 66ecc479 Guido Trotter
        results.append([True, self.cl.SubmitJob(row[1])])
2310 66ecc479 Guido Trotter
    else:
2311 66ecc479 Guido Trotter
      results = self.cl.SubmitManyJobs([row[1] for row in self.queue])
2312 5299e61f Iustin Pop
    for (idx, ((status, data), (name, _))) in enumerate(zip(results,
2313 5299e61f Iustin Pop
                                                            self.queue)):
2314 5299e61f Iustin Pop
      self.jobs.append((idx, status, data, name))
2315 5299e61f Iustin Pop
2316 5299e61f Iustin Pop
  def _ChooseJob(self):
2317 5299e61f Iustin Pop
    """Choose a non-waiting/queued job to poll next.
2318 5299e61f Iustin Pop

2319 5299e61f Iustin Pop
    """
2320 5299e61f Iustin Pop
    assert self.jobs, "_ChooseJob called with empty job list"
2321 5299e61f Iustin Pop
2322 5299e61f Iustin Pop
    result = self.cl.QueryJobs([i[2] for i in self.jobs], ["status"])
2323 5299e61f Iustin Pop
    assert result
2324 5299e61f Iustin Pop
2325 5299e61f Iustin Pop
    for job_data, status in zip(self.jobs, result):
2326 5299e61f Iustin Pop
      if status[0] in (constants.JOB_STATUS_QUEUED,
2327 5299e61f Iustin Pop
                    constants.JOB_STATUS_WAITLOCK,
2328 5299e61f Iustin Pop
                    constants.JOB_STATUS_CANCELING):
2329 5299e61f Iustin Pop
        # job is still waiting
2330 5299e61f Iustin Pop
        continue
2331 5299e61f Iustin Pop
      # good candidate found
2332 5299e61f Iustin Pop
      self.jobs.remove(job_data)
2333 5299e61f Iustin Pop
      return job_data
2334 5299e61f Iustin Pop
2335 5299e61f Iustin Pop
    # no job found
2336 5299e61f Iustin Pop
    return self.jobs.pop(0)
2337 479636a3 Iustin Pop
2338 479636a3 Iustin Pop
  def GetResults(self):
2339 479636a3 Iustin Pop
    """Wait for and return the results of all jobs.
2340 479636a3 Iustin Pop

2341 479636a3 Iustin Pop
    @rtype: list
2342 479636a3 Iustin Pop
    @return: list of tuples (success, job results), in the same order
2343 479636a3 Iustin Pop
        as the submitted jobs; if a job has failed, instead of the result
2344 479636a3 Iustin Pop
        there will be the error message
2345 479636a3 Iustin Pop

2346 479636a3 Iustin Pop
    """
2347 23b4b983 Iustin Pop
    if not self.jobs:
2348 23b4b983 Iustin Pop
      self.SubmitPending()
2349 479636a3 Iustin Pop
    results = []
2350 479636a3 Iustin Pop
    if self.verbose:
2351 5299e61f Iustin Pop
      ok_jobs = [row[2] for row in self.jobs if row[1]]
2352 23b4b983 Iustin Pop
      if ok_jobs:
2353 1f864b60 Iustin Pop
        ToStdout("Submitted jobs %s", utils.CommaJoin(ok_jobs))
2354 5299e61f Iustin Pop
2355 5299e61f Iustin Pop
    # first, remove any non-submitted jobs
2356 cea881e5 Michael Hanselmann
    self.jobs, failures = compat.partition(self.jobs, lambda x: x[1])
2357 5299e61f Iustin Pop
    for idx, _, jid, name in failures:
2358 c63355f2 Iustin Pop
      ToStderr("Failed to submit job for %s: %s", name, jid)
2359 c63355f2 Iustin Pop
      results.append((idx, False, jid))
2360 5299e61f Iustin Pop
2361 5299e61f Iustin Pop
    while self.jobs:
2362 5299e61f Iustin Pop
      (idx, _, jid, name) = self._ChooseJob()
2363 5299e61f Iustin Pop
      ToStdout("Waiting for job %s for %s...", jid, name)
2364 479636a3 Iustin Pop
      try:
2365 919ca415 Iustin Pop
        job_result = PollJob(jid, cl=self.cl, feedback_fn=self.feedback_fn)
2366 479636a3 Iustin Pop
        success = True
2367 479636a3 Iustin Pop
      except (errors.GenericError, luxi.ProtocolError), err:
2368 479636a3 Iustin Pop
        _, job_result = FormatError(err)
2369 479636a3 Iustin Pop
        success = False
2370 479636a3 Iustin Pop
        # the error message will always be shown, verbose or not
2371 479636a3 Iustin Pop
        ToStderr("Job %s for %s has failed: %s", jid, name, job_result)
2372 479636a3 Iustin Pop
2373 5299e61f Iustin Pop
      results.append((idx, success, job_result))
2374 5299e61f Iustin Pop
2375 5299e61f Iustin Pop
    # sort based on the index, then drop it
2376 5299e61f Iustin Pop
    results.sort()
2377 5299e61f Iustin Pop
    results = [i[1:] for i in results]
2378 5299e61f Iustin Pop
2379 479636a3 Iustin Pop
    return results
2380 479636a3 Iustin Pop
2381 479636a3 Iustin Pop
  def WaitOrShow(self, wait):
2382 479636a3 Iustin Pop
    """Wait for job results or only print the job IDs.
2383 479636a3 Iustin Pop

2384 479636a3 Iustin Pop
    @type wait: boolean
2385 479636a3 Iustin Pop
    @param wait: whether to wait or not
2386 479636a3 Iustin Pop

2387 479636a3 Iustin Pop
    """
2388 479636a3 Iustin Pop
    if wait:
2389 479636a3 Iustin Pop
      return self.GetResults()
2390 479636a3 Iustin Pop
    else:
2391 23b4b983 Iustin Pop
      if not self.jobs:
2392 23b4b983 Iustin Pop
        self.SubmitPending()
2393 71834b2a Guido Trotter
      for _, status, result, name in self.jobs:
2394 23b4b983 Iustin Pop
        if status:
2395 23b4b983 Iustin Pop
          ToStdout("%s: %s", result, name)
2396 23b4b983 Iustin Pop
        else:
2397 23b4b983 Iustin Pop
          ToStderr("Failure for %s: %s", name, result)
2398 53a8a54d Iustin Pop
      return [row[1:3] for row in self.jobs]