Remove old "reason" implementation
[ganeti-local] / lib / cli.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Module dealing with command line parsing"""
23
24
25 import sys
26 import textwrap
27 import os.path
28 import time
29 import logging
30 import errno
31 import itertools
32 import shlex
33 from cStringIO import StringIO
34
35 from ganeti import utils
36 from ganeti import errors
37 from ganeti import constants
38 from ganeti import opcodes
39 from ganeti import luxi
40 from ganeti import ssconf
41 from ganeti import rpc
42 from ganeti import ssh
43 from ganeti import compat
44 from ganeti import netutils
45 from ganeti import qlang
46 from ganeti import objects
47 from ganeti import pathutils
48
49 from optparse import (OptionParser, TitledHelpFormatter,
50                       Option, OptionValueError)
51
52
53 __all__ = [
54   # Command line options
55   "ABSOLUTE_OPT",
56   "ADD_UIDS_OPT",
57   "ADD_RESERVED_IPS_OPT",
58   "ALLOCATABLE_OPT",
59   "ALLOC_POLICY_OPT",
60   "ALL_OPT",
61   "ALLOW_FAILOVER_OPT",
62   "AUTO_PROMOTE_OPT",
63   "AUTO_REPLACE_OPT",
64   "BACKEND_OPT",
65   "BLK_OS_OPT",
66   "CAPAB_MASTER_OPT",
67   "CAPAB_VM_OPT",
68   "CLEANUP_OPT",
69   "CLUSTER_DOMAIN_SECRET_OPT",
70   "CONFIRM_OPT",
71   "CP_SIZE_OPT",
72   "DEBUG_OPT",
73   "DEBUG_SIMERR_OPT",
74   "DISKIDX_OPT",
75   "DISK_OPT",
76   "DISK_PARAMS_OPT",
77   "DISK_TEMPLATE_OPT",
78   "DRAINED_OPT",
79   "DRY_RUN_OPT",
80   "DRBD_HELPER_OPT",
81   "DST_NODE_OPT",
82   "EARLY_RELEASE_OPT",
83   "ENABLED_HV_OPT",
84   "ENABLED_DISK_TEMPLATES_OPT",
85   "ERROR_CODES_OPT",
86   "FAILURE_ONLY_OPT",
87   "FIELDS_OPT",
88   "FILESTORE_DIR_OPT",
89   "FILESTORE_DRIVER_OPT",
90   "FORCE_FILTER_OPT",
91   "FORCE_OPT",
92   "FORCE_VARIANT_OPT",
93   "GATEWAY_OPT",
94   "GATEWAY6_OPT",
95   "GLOBAL_FILEDIR_OPT",
96   "HID_OS_OPT",
97   "GLOBAL_SHARED_FILEDIR_OPT",
98   "HVLIST_OPT",
99   "HVOPTS_OPT",
100   "HYPERVISOR_OPT",
101   "IALLOCATOR_OPT",
102   "DEFAULT_IALLOCATOR_OPT",
103   "IDENTIFY_DEFAULTS_OPT",
104   "IGNORE_CONSIST_OPT",
105   "IGNORE_ERRORS_OPT",
106   "IGNORE_FAILURES_OPT",
107   "IGNORE_OFFLINE_OPT",
108   "IGNORE_REMOVE_FAILURES_OPT",
109   "IGNORE_SECONDARIES_OPT",
110   "IGNORE_SIZE_OPT",
111   "INTERVAL_OPT",
112   "MAC_PREFIX_OPT",
113   "MAINTAIN_NODE_HEALTH_OPT",
114   "MASTER_NETDEV_OPT",
115   "MASTER_NETMASK_OPT",
116   "MC_OPT",
117   "MIGRATION_MODE_OPT",
118   "NET_OPT",
119   "NETWORK_OPT",
120   "NETWORK6_OPT",
121   "NEW_CLUSTER_CERT_OPT",
122   "NEW_CLUSTER_DOMAIN_SECRET_OPT",
123   "NEW_CONFD_HMAC_KEY_OPT",
124   "NEW_RAPI_CERT_OPT",
125   "NEW_PRIMARY_OPT",
126   "NEW_SECONDARY_OPT",
127   "NEW_SPICE_CERT_OPT",
128   "NIC_PARAMS_OPT",
129   "NOCONFLICTSCHECK_OPT",
130   "NODE_FORCE_JOIN_OPT",
131   "NODE_LIST_OPT",
132   "NODE_PLACEMENT_OPT",
133   "NODEGROUP_OPT",
134   "NODE_PARAMS_OPT",
135   "NODE_POWERED_OPT",
136   "NODRBD_STORAGE_OPT",
137   "NOHDR_OPT",
138   "NOIPCHECK_OPT",
139   "NO_INSTALL_OPT",
140   "NONAMECHECK_OPT",
141   "NOLVM_STORAGE_OPT",
142   "NOMODIFY_ETCHOSTS_OPT",
143   "NOMODIFY_SSH_SETUP_OPT",
144   "NONICS_OPT",
145   "NONLIVE_OPT",
146   "NONPLUS1_OPT",
147   "NORUNTIME_CHGS_OPT",
148   "NOSHUTDOWN_OPT",
149   "NOSTART_OPT",
150   "NOSSH_KEYCHECK_OPT",
151   "NOVOTING_OPT",
152   "NO_REMEMBER_OPT",
153   "NWSYNC_OPT",
154   "OFFLINE_INST_OPT",
155   "ONLINE_INST_OPT",
156   "ON_PRIMARY_OPT",
157   "ON_SECONDARY_OPT",
158   "OFFLINE_OPT",
159   "OSPARAMS_OPT",
160   "OS_OPT",
161   "OS_SIZE_OPT",
162   "OOB_TIMEOUT_OPT",
163   "POWER_DELAY_OPT",
164   "PREALLOC_WIPE_DISKS_OPT",
165   "PRIMARY_IP_VERSION_OPT",
166   "PRIMARY_ONLY_OPT",
167   "PRIORITY_OPT",
168   "RAPI_CERT_OPT",
169   "READD_OPT",
170   "REASON_OPT",
171   "REBOOT_TYPE_OPT",
172   "REMOVE_INSTANCE_OPT",
173   "REMOVE_RESERVED_IPS_OPT",
174   "REMOVE_UIDS_OPT",
175   "RESERVED_LVS_OPT",
176   "RUNTIME_MEM_OPT",
177   "ROMAN_OPT",
178   "SECONDARY_IP_OPT",
179   "SECONDARY_ONLY_OPT",
180   "SELECT_OS_OPT",
181   "SEP_OPT",
182   "SHOWCMD_OPT",
183   "SHOW_MACHINE_OPT",
184   "SHUTDOWN_TIMEOUT_OPT",
185   "SINGLE_NODE_OPT",
186   "SPECS_CPU_COUNT_OPT",
187   "SPECS_DISK_COUNT_OPT",
188   "SPECS_DISK_SIZE_OPT",
189   "SPECS_MEM_SIZE_OPT",
190   "SPECS_NIC_COUNT_OPT",
191   "IPOLICY_DISK_TEMPLATES",
192   "IPOLICY_VCPU_RATIO",
193   "SPICE_CACERT_OPT",
194   "SPICE_CERT_OPT",
195   "SRC_DIR_OPT",
196   "SRC_NODE_OPT",
197   "SUBMIT_OPT",
198   "STARTUP_PAUSED_OPT",
199   "STATIC_OPT",
200   "SYNC_OPT",
201   "TAG_ADD_OPT",
202   "TAG_SRC_OPT",
203   "TIMEOUT_OPT",
204   "TO_GROUP_OPT",
205   "UIDPOOL_OPT",
206   "USEUNITS_OPT",
207   "USE_EXTERNAL_MIP_SCRIPT",
208   "USE_REPL_NET_OPT",
209   "VERBOSE_OPT",
210   "VG_NAME_OPT",
211   "WFSYNC_OPT",
212   "YES_DOIT_OPT",
213   "DISK_STATE_OPT",
214   "HV_STATE_OPT",
215   "IGNORE_IPOLICY_OPT",
216   "INSTANCE_POLICY_OPTS",
217   # Generic functions for CLI programs
218   "ConfirmOperation",
219   "CreateIPolicyFromOpts",
220   "GenericMain",
221   "GenericInstanceCreate",
222   "GenericList",
223   "GenericListFields",
224   "GetClient",
225   "GetOnlineNodes",
226   "JobExecutor",
227   "JobSubmittedException",
228   "ParseTimespec",
229   "RunWhileClusterStopped",
230   "SubmitOpCode",
231   "SubmitOrSend",
232   "UsesRPC",
233   # Formatting functions
234   "ToStderr", "ToStdout",
235   "FormatError",
236   "FormatQueryResult",
237   "FormatParamsDictInfo",
238   "FormatPolicyInfo",
239   "PrintGenericInfo",
240   "GenerateTable",
241   "AskUser",
242   "FormatTimestamp",
243   "FormatLogMessage",
244   # Tags functions
245   "ListTags",
246   "AddTags",
247   "RemoveTags",
248   # command line options support infrastructure
249   "ARGS_MANY_INSTANCES",
250   "ARGS_MANY_NODES",
251   "ARGS_MANY_GROUPS",
252   "ARGS_MANY_NETWORKS",
253   "ARGS_NONE",
254   "ARGS_ONE_INSTANCE",
255   "ARGS_ONE_NODE",
256   "ARGS_ONE_GROUP",
257   "ARGS_ONE_OS",
258   "ARGS_ONE_NETWORK",
259   "ArgChoice",
260   "ArgCommand",
261   "ArgFile",
262   "ArgGroup",
263   "ArgHost",
264   "ArgInstance",
265   "ArgJobId",
266   "ArgNetwork",
267   "ArgNode",
268   "ArgOs",
269   "ArgExtStorage",
270   "ArgSuggest",
271   "ArgUnknown",
272   "OPT_COMPL_INST_ADD_NODES",
273   "OPT_COMPL_MANY_NODES",
274   "OPT_COMPL_ONE_IALLOCATOR",
275   "OPT_COMPL_ONE_INSTANCE",
276   "OPT_COMPL_ONE_NODE",
277   "OPT_COMPL_ONE_NODEGROUP",
278   "OPT_COMPL_ONE_NETWORK",
279   "OPT_COMPL_ONE_OS",
280   "OPT_COMPL_ONE_EXTSTORAGE",
281   "cli_option",
282   "SplitNodeOption",
283   "CalculateOSNames",
284   "ParseFields",
285   "COMMON_CREATE_OPTS",
286   ]
287
288 NO_PREFIX = "no_"
289 UN_PREFIX = "-"
290
291 #: Priorities (sorted)
292 _PRIORITY_NAMES = [
293   ("low", constants.OP_PRIO_LOW),
294   ("normal", constants.OP_PRIO_NORMAL),
295   ("high", constants.OP_PRIO_HIGH),
296   ]
297
298 #: Priority dictionary for easier lookup
299 # TODO: Replace this and _PRIORITY_NAMES with a single sorted dictionary once
300 # we migrate to Python 2.6
301 _PRIONAME_TO_VALUE = dict(_PRIORITY_NAMES)
302
303 # Query result status for clients
304 (QR_NORMAL,
305  QR_UNKNOWN,
306  QR_INCOMPLETE) = range(3)
307
308 #: Maximum batch size for ChooseJob
309 _CHOOSE_BATCH = 25
310
311
312 # constants used to create InstancePolicy dictionary
313 TISPECS_GROUP_TYPES = {
314   constants.ISPECS_MIN: constants.VTYPE_INT,
315   constants.ISPECS_MAX: constants.VTYPE_INT,
316   }
317
318 TISPECS_CLUSTER_TYPES = {
319   constants.ISPECS_MIN: constants.VTYPE_INT,
320   constants.ISPECS_MAX: constants.VTYPE_INT,
321   constants.ISPECS_STD: constants.VTYPE_INT,
322   }
323
324 #: User-friendly names for query2 field types
325 _QFT_NAMES = {
326   constants.QFT_UNKNOWN: "Unknown",
327   constants.QFT_TEXT: "Text",
328   constants.QFT_BOOL: "Boolean",
329   constants.QFT_NUMBER: "Number",
330   constants.QFT_UNIT: "Storage size",
331   constants.QFT_TIMESTAMP: "Timestamp",
332   constants.QFT_OTHER: "Custom",
333   }
334
335
336 class _Argument:
337   def __init__(self, min=0, max=None): # pylint: disable=W0622
338     self.min = min
339     self.max = max
340
341   def __repr__(self):
342     return ("<%s min=%s max=%s>" %
343             (self.__class__.__name__, self.min, self.max))
344
345
346 class ArgSuggest(_Argument):
347   """Suggesting argument.
348
349   Value can be any of the ones passed to the constructor.
350
351   """
352   # pylint: disable=W0622
353   def __init__(self, min=0, max=None, choices=None):
354     _Argument.__init__(self, min=min, max=max)
355     self.choices = choices
356
357   def __repr__(self):
358     return ("<%s min=%s max=%s choices=%r>" %
359             (self.__class__.__name__, self.min, self.max, self.choices))
360
361
362 class ArgChoice(ArgSuggest):
363   """Choice argument.
364
365   Value can be any of the ones passed to the constructor. Like L{ArgSuggest},
366   but value must be one of the choices.
367
368   """
369
370
371 class ArgUnknown(_Argument):
372   """Unknown argument to program (e.g. determined at runtime).
373
374   """
375
376
377 class ArgInstance(_Argument):
378   """Instances argument.
379
380   """
381
382
383 class ArgNode(_Argument):
384   """Node argument.
385
386   """
387
388
389 class ArgNetwork(_Argument):
390   """Network argument.
391
392   """
393
394
395 class ArgGroup(_Argument):
396   """Node group argument.
397
398   """
399
400
401 class ArgJobId(_Argument):
402   """Job ID argument.
403
404   """
405
406
407 class ArgFile(_Argument):
408   """File path argument.
409
410   """
411
412
413 class ArgCommand(_Argument):
414   """Command argument.
415
416   """
417
418
419 class ArgHost(_Argument):
420   """Host argument.
421
422   """
423
424
425 class ArgOs(_Argument):
426   """OS argument.
427
428   """
429
430
431 class ArgExtStorage(_Argument):
432   """ExtStorage argument.
433
434   """
435
436
437 ARGS_NONE = []
438 ARGS_MANY_INSTANCES = [ArgInstance()]
439 ARGS_MANY_NETWORKS = [ArgNetwork()]
440 ARGS_MANY_NODES = [ArgNode()]
441 ARGS_MANY_GROUPS = [ArgGroup()]
442 ARGS_ONE_INSTANCE = [ArgInstance(min=1, max=1)]
443 ARGS_ONE_NETWORK = [ArgNetwork(min=1, max=1)]
444 ARGS_ONE_NODE = [ArgNode(min=1, max=1)]
445 # TODO
446 ARGS_ONE_GROUP = [ArgGroup(min=1, max=1)]
447 ARGS_ONE_OS = [ArgOs(min=1, max=1)]
448
449
450 def _ExtractTagsObject(opts, args):
451   """Extract the tag type object.
452
453   Note that this function will modify its args parameter.
454
455   """
456   if not hasattr(opts, "tag_type"):
457     raise errors.ProgrammerError("tag_type not passed to _ExtractTagsObject")
458   kind = opts.tag_type
459   if kind == constants.TAG_CLUSTER:
460     retval = kind, None
461   elif kind in (constants.TAG_NODEGROUP,
462                 constants.TAG_NODE,
463                 constants.TAG_NETWORK,
464                 constants.TAG_INSTANCE):
465     if not args:
466       raise errors.OpPrereqError("no arguments passed to the command",
467                                  errors.ECODE_INVAL)
468     name = args.pop(0)
469     retval = kind, name
470   else:
471     raise errors.ProgrammerError("Unhandled tag type '%s'" % kind)
472   return retval
473
474
475 def _ExtendTags(opts, args):
476   """Extend the args if a source file has been given.
477
478   This function will extend the tags with the contents of the file
479   passed in the 'tags_source' attribute of the opts parameter. A file
480   named '-' will be replaced by stdin.
481
482   """
483   fname = opts.tags_source
484   if fname is None:
485     return
486   if fname == "-":
487     new_fh = sys.stdin
488   else:
489     new_fh = open(fname, "r")
490   new_data = []
491   try:
492     # we don't use the nice 'new_data = [line.strip() for line in fh]'
493     # because of python bug 1633941
494     while True:
495       line = new_fh.readline()
496       if not line:
497         break
498       new_data.append(line.strip())
499   finally:
500     new_fh.close()
501   args.extend(new_data)
502
503
504 def ListTags(opts, args):
505   """List the tags on a given object.
506
507   This is a generic implementation that knows how to deal with all
508   three cases of tag objects (cluster, node, instance). The opts
509   argument is expected to contain a tag_type field denoting what
510   object type we work on.
511
512   """
513   kind, name = _ExtractTagsObject(opts, args)
514   cl = GetClient(query=True)
515   result = cl.QueryTags(kind, name)
516   result = list(result)
517   result.sort()
518   for tag in result:
519     ToStdout(tag)
520
521
522 def AddTags(opts, args):
523   """Add tags on a given object.
524
525   This is a generic implementation that knows how to deal with all
526   three cases of tag objects (cluster, node, instance). The opts
527   argument is expected to contain a tag_type field denoting what
528   object type we work on.
529
530   """
531   kind, name = _ExtractTagsObject(opts, args)
532   _ExtendTags(opts, args)
533   if not args:
534     raise errors.OpPrereqError("No tags to be added", errors.ECODE_INVAL)
535   op = opcodes.OpTagsSet(kind=kind, name=name, tags=args)
536   SubmitOrSend(op, opts)
537
538
539 def RemoveTags(opts, args):
540   """Remove tags from a given object.
541
542   This is a generic implementation that knows how to deal with all
543   three cases of tag objects (cluster, node, instance). The opts
544   argument is expected to contain a tag_type field denoting what
545   object type we work on.
546
547   """
548   kind, name = _ExtractTagsObject(opts, args)
549   _ExtendTags(opts, args)
550   if not args:
551     raise errors.OpPrereqError("No tags to be removed", errors.ECODE_INVAL)
552   op = opcodes.OpTagsDel(kind=kind, name=name, tags=args)
553   SubmitOrSend(op, opts)
554
555
556 def check_unit(option, opt, value): # pylint: disable=W0613
557   """OptParsers custom converter for units.
558
559   """
560   try:
561     return utils.ParseUnit(value)
562   except errors.UnitParseError, err:
563     raise OptionValueError("option %s: %s" % (opt, err))
564
565
566 def _SplitKeyVal(opt, data):
567   """Convert a KeyVal string into a dict.
568
569   This function will convert a key=val[,...] string into a dict. Empty
570   values will be converted specially: keys which have the prefix 'no_'
571   will have the value=False and the prefix stripped, the others will
572   have value=True.
573
574   @type opt: string
575   @param opt: a string holding the option name for which we process the
576       data, used in building error messages
577   @type data: string
578   @param data: a string of the format key=val,key=val,...
579   @rtype: dict
580   @return: {key=val, key=val}
581   @raises errors.ParameterError: if there are duplicate keys
582
583   """
584   kv_dict = {}
585   if data:
586     for elem in utils.UnescapeAndSplit(data, sep=","):
587       if "=" in elem:
588         key, val = elem.split("=", 1)
589       else:
590         if elem.startswith(NO_PREFIX):
591           key, val = elem[len(NO_PREFIX):], False
592         elif elem.startswith(UN_PREFIX):
593           key, val = elem[len(UN_PREFIX):], None
594         else:
595           key, val = elem, True
596       if key in kv_dict:
597         raise errors.ParameterError("Duplicate key '%s' in option %s" %
598                                     (key, opt))
599       kv_dict[key] = val
600   return kv_dict
601
602
603 def check_ident_key_val(option, opt, value):  # pylint: disable=W0613
604   """Custom parser for ident:key=val,key=val options.
605
606   This will store the parsed values as a tuple (ident, {key: val}). As such,
607   multiple uses of this option via action=append is possible.
608
609   """
610   if ":" not in value:
611     ident, rest = value, ""
612   else:
613     ident, rest = value.split(":", 1)
614
615   if ident.startswith(NO_PREFIX):
616     if rest:
617       msg = "Cannot pass options when removing parameter groups: %s" % value
618       raise errors.ParameterError(msg)
619     retval = (ident[len(NO_PREFIX):], False)
620   elif (ident.startswith(UN_PREFIX) and
621         (len(ident) <= len(UN_PREFIX) or
622          not ident[len(UN_PREFIX)][0].isdigit())):
623     if rest:
624       msg = "Cannot pass options when removing parameter groups: %s" % value
625       raise errors.ParameterError(msg)
626     retval = (ident[len(UN_PREFIX):], None)
627   else:
628     kv_dict = _SplitKeyVal(opt, rest)
629     retval = (ident, kv_dict)
630   return retval
631
632
633 def check_key_val(option, opt, value):  # pylint: disable=W0613
634   """Custom parser class for key=val,key=val options.
635
636   This will store the parsed values as a dict {key: val}.
637
638   """
639   return _SplitKeyVal(opt, value)
640
641
642 def check_bool(option, opt, value): # pylint: disable=W0613
643   """Custom parser for yes/no options.
644
645   This will store the parsed value as either True or False.
646
647   """
648   value = value.lower()
649   if value == constants.VALUE_FALSE or value == "no":
650     return False
651   elif value == constants.VALUE_TRUE or value == "yes":
652     return True
653   else:
654     raise errors.ParameterError("Invalid boolean value '%s'" % value)
655
656
657 def check_list(option, opt, value): # pylint: disable=W0613
658   """Custom parser for comma-separated lists.
659
660   """
661   # we have to make this explicit check since "".split(",") is [""],
662   # not an empty list :(
663   if not value:
664     return []
665   else:
666     return utils.UnescapeAndSplit(value)
667
668
669 def check_maybefloat(option, opt, value): # pylint: disable=W0613
670   """Custom parser for float numbers which might be also defaults.
671
672   """
673   value = value.lower()
674
675   if value == constants.VALUE_DEFAULT:
676     return value
677   else:
678     return float(value)
679
680
681 # completion_suggestion is normally a list. Using numeric values not evaluating
682 # to False for dynamic completion.
683 (OPT_COMPL_MANY_NODES,
684  OPT_COMPL_ONE_NODE,
685  OPT_COMPL_ONE_INSTANCE,
686  OPT_COMPL_ONE_OS,
687  OPT_COMPL_ONE_EXTSTORAGE,
688  OPT_COMPL_ONE_IALLOCATOR,
689  OPT_COMPL_ONE_NETWORK,
690  OPT_COMPL_INST_ADD_NODES,
691  OPT_COMPL_ONE_NODEGROUP) = range(100, 109)
692
693 OPT_COMPL_ALL = compat.UniqueFrozenset([
694   OPT_COMPL_MANY_NODES,
695   OPT_COMPL_ONE_NODE,
696   OPT_COMPL_ONE_INSTANCE,
697   OPT_COMPL_ONE_OS,
698   OPT_COMPL_ONE_EXTSTORAGE,
699   OPT_COMPL_ONE_IALLOCATOR,
700   OPT_COMPL_ONE_NETWORK,
701   OPT_COMPL_INST_ADD_NODES,
702   OPT_COMPL_ONE_NODEGROUP,
703   ])
704
705
706 class CliOption(Option):
707   """Custom option class for optparse.
708
709   """
710   ATTRS = Option.ATTRS + [
711     "completion_suggest",
712     ]
713   TYPES = Option.TYPES + (
714     "identkeyval",
715     "keyval",
716     "unit",
717     "bool",
718     "list",
719     "maybefloat",
720     )
721   TYPE_CHECKER = Option.TYPE_CHECKER.copy()
722   TYPE_CHECKER["identkeyval"] = check_ident_key_val
723   TYPE_CHECKER["keyval"] = check_key_val
724   TYPE_CHECKER["unit"] = check_unit
725   TYPE_CHECKER["bool"] = check_bool
726   TYPE_CHECKER["list"] = check_list
727   TYPE_CHECKER["maybefloat"] = check_maybefloat
728
729
730 # optparse.py sets make_option, so we do it for our own option class, too
731 cli_option = CliOption
732
733
734 _YORNO = "yes|no"
735
736 DEBUG_OPT = cli_option("-d", "--debug", default=0, action="count",
737                        help="Increase debugging level")
738
739 NOHDR_OPT = cli_option("--no-headers", default=False,
740                        action="store_true", dest="no_headers",
741                        help="Don't display column headers")
742
743 SEP_OPT = cli_option("--separator", default=None,
744                      action="store", dest="separator",
745                      help=("Separator between output fields"
746                            " (defaults to one space)"))
747
748 USEUNITS_OPT = cli_option("--units", default=None,
749                           dest="units", choices=("h", "m", "g", "t"),
750                           help="Specify units for output (one of h/m/g/t)")
751
752 FIELDS_OPT = cli_option("-o", "--output", dest="output", action="store",
753                         type="string", metavar="FIELDS",
754                         help="Comma separated list of output fields")
755
756 FORCE_OPT = cli_option("-f", "--force", dest="force", action="store_true",
757                        default=False, help="Force the operation")
758
759 CONFIRM_OPT = cli_option("--yes", dest="confirm", action="store_true",
760                          default=False, help="Do not require confirmation")
761
762 IGNORE_OFFLINE_OPT = cli_option("--ignore-offline", dest="ignore_offline",
763                                   action="store_true", default=False,
764                                   help=("Ignore offline nodes and do as much"
765                                         " as possible"))
766
767 TAG_ADD_OPT = cli_option("--tags", dest="tags",
768                          default=None, help="Comma-separated list of instance"
769                                             " tags")
770
771 TAG_SRC_OPT = cli_option("--from", dest="tags_source",
772                          default=None, help="File with tag names")
773
774 SUBMIT_OPT = cli_option("--submit", dest="submit_only",
775                         default=False, action="store_true",
776                         help=("Submit the job and return the job ID, but"
777                               " don't wait for the job to finish"))
778
779 SYNC_OPT = cli_option("--sync", dest="do_locking",
780                       default=False, action="store_true",
781                       help=("Grab locks while doing the queries"
782                             " in order to ensure more consistent results"))
783
784 DRY_RUN_OPT = cli_option("--dry-run", default=False,
785                          action="store_true",
786                          help=("Do not execute the operation, just run the"
787                                " check steps and verify if it could be"
788                                " executed"))
789
790 VERBOSE_OPT = cli_option("-v", "--verbose", default=False,
791                          action="store_true",
792                          help="Increase the verbosity of the operation")
793
794 DEBUG_SIMERR_OPT = cli_option("--debug-simulate-errors", default=False,
795                               action="store_true", dest="simulate_errors",
796                               help="Debugging option that makes the operation"
797                               " treat most runtime checks as failed")
798
799 NWSYNC_OPT = cli_option("--no-wait-for-sync", dest="wait_for_sync",
800                         default=True, action="store_false",
801                         help="Don't wait for sync (DANGEROUS!)")
802
803 WFSYNC_OPT = cli_option("--wait-for-sync", dest="wait_for_sync",
804                         default=False, action="store_true",
805                         help="Wait for disks to sync")
806
807 ONLINE_INST_OPT = cli_option("--online", dest="online_inst",
808                              action="store_true", default=False,
809                              help="Enable offline instance")
810
811 OFFLINE_INST_OPT = cli_option("--offline", dest="offline_inst",
812                               action="store_true", default=False,
813                               help="Disable down instance")
814
815 DISK_TEMPLATE_OPT = cli_option("-t", "--disk-template", dest="disk_template",
816                                help=("Custom disk setup (%s)" %
817                                      utils.CommaJoin(constants.DISK_TEMPLATES)),
818                                default=None, metavar="TEMPL",
819                                choices=list(constants.DISK_TEMPLATES))
820
821 NONICS_OPT = cli_option("--no-nics", default=False, action="store_true",
822                         help="Do not create any network cards for"
823                         " the instance")
824
825 FILESTORE_DIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
826                                help="Relative path under default cluster-wide"
827                                " file storage dir to store file-based disks",
828                                default=None, metavar="<DIR>")
829
830 FILESTORE_DRIVER_OPT = cli_option("--file-driver", dest="file_driver",
831                                   help="Driver to use for image files",
832                                   default="loop", metavar="<DRIVER>",
833                                   choices=list(constants.FILE_DRIVER))
834
835 IALLOCATOR_OPT = cli_option("-I", "--iallocator", metavar="<NAME>",
836                             help="Select nodes for the instance automatically"
837                             " using the <NAME> iallocator plugin",
838                             default=None, type="string",
839                             completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
840
841 DEFAULT_IALLOCATOR_OPT = cli_option("-I", "--default-iallocator",
842                                     metavar="<NAME>",
843                                     help="Set the default instance"
844                                     " allocator plugin",
845                                     default=None, type="string",
846                                     completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
847
848 OS_OPT = cli_option("-o", "--os-type", dest="os", help="What OS to run",
849                     metavar="<os>",
850                     completion_suggest=OPT_COMPL_ONE_OS)
851
852 OSPARAMS_OPT = cli_option("-O", "--os-parameters", dest="osparams",
853                           type="keyval", default={},
854                           help="OS parameters")
855
856 FORCE_VARIANT_OPT = cli_option("--force-variant", dest="force_variant",
857                                action="store_true", default=False,
858                                help="Force an unknown variant")
859
860 NO_INSTALL_OPT = cli_option("--no-install", dest="no_install",
861                             action="store_true", default=False,
862                             help="Do not install the OS (will"
863                             " enable no-start)")
864
865 NORUNTIME_CHGS_OPT = cli_option("--no-runtime-changes",
866                                 dest="allow_runtime_chgs",
867                                 default=True, action="store_false",
868                                 help="Don't allow runtime changes")
869
870 BACKEND_OPT = cli_option("-B", "--backend-parameters", dest="beparams",
871                          type="keyval", default={},
872                          help="Backend parameters")
873
874 HVOPTS_OPT = cli_option("-H", "--hypervisor-parameters", type="keyval",
875                         default={}, dest="hvparams",
876                         help="Hypervisor parameters")
877
878 DISK_PARAMS_OPT = cli_option("-D", "--disk-parameters", dest="diskparams",
879                              help="Disk template parameters, in the format"
880                              " template:option=value,option=value,...",
881                              type="identkeyval", action="append", default=[])
882
883 SPECS_MEM_SIZE_OPT = cli_option("--specs-mem-size", dest="ispecs_mem_size",
884                                  type="keyval", default={},
885                                  help="Memory size specs: list of key=value,"
886                                 " where key is one of min, max, std"
887                                  " (in MB or using a unit)")
888
889 SPECS_CPU_COUNT_OPT = cli_option("--specs-cpu-count", dest="ispecs_cpu_count",
890                                  type="keyval", default={},
891                                  help="CPU count specs: list of key=value,"
892                                  " where key is one of min, max, std")
893
894 SPECS_DISK_COUNT_OPT = cli_option("--specs-disk-count",
895                                   dest="ispecs_disk_count",
896                                   type="keyval", default={},
897                                   help="Disk count specs: list of key=value,"
898                                   " where key is one of min, max, std")
899
900 SPECS_DISK_SIZE_OPT = cli_option("--specs-disk-size", dest="ispecs_disk_size",
901                                  type="keyval", default={},
902                                  help="Disk size specs: list of key=value,"
903                                  " where key is one of min, max, std"
904                                  " (in MB or using a unit)")
905
906 SPECS_NIC_COUNT_OPT = cli_option("--specs-nic-count", dest="ispecs_nic_count",
907                                  type="keyval", default={},
908                                  help="NIC count specs: list of key=value,"
909                                  " where key is one of min, max, std")
910
911 IPOLICY_DISK_TEMPLATES = cli_option("--ipolicy-disk-templates",
912                                     dest="ipolicy_disk_templates",
913                                     type="list", default=None,
914                                     help="Comma-separated list of"
915                                     " enabled disk templates")
916
917 IPOLICY_VCPU_RATIO = cli_option("--ipolicy-vcpu-ratio",
918                                  dest="ipolicy_vcpu_ratio",
919                                  type="maybefloat", default=None,
920                                  help="The maximum allowed vcpu-to-cpu ratio")
921
922 IPOLICY_SPINDLE_RATIO = cli_option("--ipolicy-spindle-ratio",
923                                    dest="ipolicy_spindle_ratio",
924                                    type="maybefloat", default=None,
925                                    help=("The maximum allowed instances to"
926                                          " spindle ratio"))
927
928 HYPERVISOR_OPT = cli_option("-H", "--hypervisor-parameters", dest="hypervisor",
929                             help="Hypervisor and hypervisor options, in the"
930                             " format hypervisor:option=value,option=value,...",
931                             default=None, type="identkeyval")
932
933 HVLIST_OPT = cli_option("-H", "--hypervisor-parameters", dest="hvparams",
934                         help="Hypervisor and hypervisor options, in the"
935                         " format hypervisor:option=value,option=value,...",
936                         default=[], action="append", type="identkeyval")
937
938 NOIPCHECK_OPT = cli_option("--no-ip-check", dest="ip_check", default=True,
939                            action="store_false",
940                            help="Don't check that the instance's IP"
941                            " is alive")
942
943 NONAMECHECK_OPT = cli_option("--no-name-check", dest="name_check",
944                              default=True, action="store_false",
945                              help="Don't check that the instance's name"
946                              " is resolvable")
947
948 NET_OPT = cli_option("--net",
949                      help="NIC parameters", default=[],
950                      dest="nics", action="append", type="identkeyval")
951
952 DISK_OPT = cli_option("--disk", help="Disk parameters", default=[],
953                       dest="disks", action="append", type="identkeyval")
954
955 DISKIDX_OPT = cli_option("--disks", dest="disks", default=None,
956                          help="Comma-separated list of disks"
957                          " indices to act on (e.g. 0,2) (optional,"
958                          " defaults to all disks)")
959
960 OS_SIZE_OPT = cli_option("-s", "--os-size", dest="sd_size",
961                          help="Enforces a single-disk configuration using the"
962                          " given disk size, in MiB unless a suffix is used",
963                          default=None, type="unit", metavar="<size>")
964
965 IGNORE_CONSIST_OPT = cli_option("--ignore-consistency",
966                                 dest="ignore_consistency",
967                                 action="store_true", default=False,
968                                 help="Ignore the consistency of the disks on"
969                                 " the secondary")
970
971 ALLOW_FAILOVER_OPT = cli_option("--allow-failover",
972                                 dest="allow_failover",
973                                 action="store_true", default=False,
974                                 help="If migration is not possible fallback to"
975                                      " failover")
976
977 NONLIVE_OPT = cli_option("--non-live", dest="live",
978                          default=True, action="store_false",
979                          help="Do a non-live migration (this usually means"
980                          " freeze the instance, save the state, transfer and"
981                          " only then resume running on the secondary node)")
982
983 MIGRATION_MODE_OPT = cli_option("--migration-mode", dest="migration_mode",
984                                 default=None,
985                                 choices=list(constants.HT_MIGRATION_MODES),
986                                 help="Override default migration mode (choose"
987                                 " either live or non-live")
988
989 NODE_PLACEMENT_OPT = cli_option("-n", "--node", dest="node",
990                                 help="Target node and optional secondary node",
991                                 metavar="<pnode>[:<snode>]",
992                                 completion_suggest=OPT_COMPL_INST_ADD_NODES)
993
994 NODE_LIST_OPT = cli_option("-n", "--node", dest="nodes", default=[],
995                            action="append", metavar="<node>",
996                            help="Use only this node (can be used multiple"
997                            " times, if not given defaults to all nodes)",
998                            completion_suggest=OPT_COMPL_ONE_NODE)
999
1000 NODEGROUP_OPT_NAME = "--node-group"
1001 NODEGROUP_OPT = cli_option("-g", NODEGROUP_OPT_NAME,
1002                            dest="nodegroup",
1003                            help="Node group (name or uuid)",
1004                            metavar="<nodegroup>",
1005                            default=None, type="string",
1006                            completion_suggest=OPT_COMPL_ONE_NODEGROUP)
1007
1008 SINGLE_NODE_OPT = cli_option("-n", "--node", dest="node", help="Target node",
1009                              metavar="<node>",
1010                              completion_suggest=OPT_COMPL_ONE_NODE)
1011
1012 NOSTART_OPT = cli_option("--no-start", dest="start", default=True,
1013                          action="store_false",
1014                          help="Don't start the instance after creation")
1015
1016 SHOWCMD_OPT = cli_option("--show-cmd", dest="show_command",
1017                          action="store_true", default=False,
1018                          help="Show command instead of executing it")
1019
1020 CLEANUP_OPT = cli_option("--cleanup", dest="cleanup",
1021                          default=False, action="store_true",
1022                          help="Instead of performing the migration, try to"
1023                          " recover from a failed cleanup. This is safe"
1024                          " to run even if the instance is healthy, but it"
1025                          " will create extra replication traffic and "
1026                          " disrupt briefly the replication (like during the"
1027                          " migration")
1028
1029 STATIC_OPT = cli_option("-s", "--static", dest="static",
1030                         action="store_true", default=False,
1031                         help="Only show configuration data, not runtime data")
1032
1033 ALL_OPT = cli_option("--all", dest="show_all",
1034                      default=False, action="store_true",
1035                      help="Show info on all instances on the cluster."
1036                      " This can take a long time to run, use wisely")
1037
1038 SELECT_OS_OPT = cli_option("--select-os", dest="select_os",
1039                            action="store_true", default=False,
1040                            help="Interactive OS reinstall, lists available"
1041                            " OS templates for selection")
1042
1043 IGNORE_FAILURES_OPT = cli_option("--ignore-failures", dest="ignore_failures",
1044                                  action="store_true", default=False,
1045                                  help="Remove the instance from the cluster"
1046                                  " configuration even if there are failures"
1047                                  " during the removal process")
1048
1049 IGNORE_REMOVE_FAILURES_OPT = cli_option("--ignore-remove-failures",
1050                                         dest="ignore_remove_failures",
1051                                         action="store_true", default=False,
1052                                         help="Remove the instance from the"
1053                                         " cluster configuration even if there"
1054                                         " are failures during the removal"
1055                                         " process")
1056
1057 REMOVE_INSTANCE_OPT = cli_option("--remove-instance", dest="remove_instance",
1058                                  action="store_true", default=False,
1059                                  help="Remove the instance from the cluster")
1060
1061 DST_NODE_OPT = cli_option("-n", "--target-node", dest="dst_node",
1062                                help="Specifies the new node for the instance",
1063                                metavar="NODE", default=None,
1064                                completion_suggest=OPT_COMPL_ONE_NODE)
1065
1066 NEW_SECONDARY_OPT = cli_option("-n", "--new-secondary", dest="dst_node",
1067                                help="Specifies the new secondary node",
1068                                metavar="NODE", default=None,
1069                                completion_suggest=OPT_COMPL_ONE_NODE)
1070
1071 NEW_PRIMARY_OPT = cli_option("--new-primary", dest="new_primary_node",
1072                              help="Specifies the new primary node",
1073                              metavar="<node>", default=None,
1074                              completion_suggest=OPT_COMPL_ONE_NODE)
1075
1076 ON_PRIMARY_OPT = cli_option("-p", "--on-primary", dest="on_primary",
1077                             default=False, action="store_true",
1078                             help="Replace the disk(s) on the primary"
1079                                  " node (applies only to internally mirrored"
1080                                  " disk templates, e.g. %s)" %
1081                                  utils.CommaJoin(constants.DTS_INT_MIRROR))
1082
1083 ON_SECONDARY_OPT = cli_option("-s", "--on-secondary", dest="on_secondary",
1084                               default=False, action="store_true",
1085                               help="Replace the disk(s) on the secondary"
1086                                    " node (applies only to internally mirrored"
1087                                    " disk templates, e.g. %s)" %
1088                                    utils.CommaJoin(constants.DTS_INT_MIRROR))
1089
1090 AUTO_PROMOTE_OPT = cli_option("--auto-promote", dest="auto_promote",
1091                               default=False, action="store_true",
1092                               help="Lock all nodes and auto-promote as needed"
1093                               " to MC status")
1094
1095 AUTO_REPLACE_OPT = cli_option("-a", "--auto", dest="auto",
1096                               default=False, action="store_true",
1097                               help="Automatically replace faulty disks"
1098                                    " (applies only to internally mirrored"
1099                                    " disk templates, e.g. %s)" %
1100                                    utils.CommaJoin(constants.DTS_INT_MIRROR))
1101
1102 IGNORE_SIZE_OPT = cli_option("--ignore-size", dest="ignore_size",
1103                              default=False, action="store_true",
1104                              help="Ignore current recorded size"
1105                              " (useful for forcing activation when"
1106                              " the recorded size is wrong)")
1107
1108 SRC_NODE_OPT = cli_option("--src-node", dest="src_node", help="Source node",
1109                           metavar="<node>",
1110                           completion_suggest=OPT_COMPL_ONE_NODE)
1111
1112 SRC_DIR_OPT = cli_option("--src-dir", dest="src_dir", help="Source directory",
1113                          metavar="<dir>")
1114
1115 SECONDARY_IP_OPT = cli_option("-s", "--secondary-ip", dest="secondary_ip",
1116                               help="Specify the secondary ip for the node",
1117                               metavar="ADDRESS", default=None)
1118
1119 READD_OPT = cli_option("--readd", dest="readd",
1120                        default=False, action="store_true",
1121                        help="Readd old node after replacing it")
1122
1123 NOSSH_KEYCHECK_OPT = cli_option("--no-ssh-key-check", dest="ssh_key_check",
1124                                 default=True, action="store_false",
1125                                 help="Disable SSH key fingerprint checking")
1126
1127 NODE_FORCE_JOIN_OPT = cli_option("--force-join", dest="force_join",
1128                                  default=False, action="store_true",
1129                                  help="Force the joining of a node")
1130
1131 MC_OPT = cli_option("-C", "--master-candidate", dest="master_candidate",
1132                     type="bool", default=None, metavar=_YORNO,
1133                     help="Set the master_candidate flag on the node")
1134
1135 OFFLINE_OPT = cli_option("-O", "--offline", dest="offline", metavar=_YORNO,
1136                          type="bool", default=None,
1137                          help=("Set the offline flag on the node"
1138                                " (cluster does not communicate with offline"
1139                                " nodes)"))
1140
1141 DRAINED_OPT = cli_option("-D", "--drained", dest="drained", metavar=_YORNO,
1142                          type="bool", default=None,
1143                          help=("Set the drained flag on the node"
1144                                " (excluded from allocation operations)"))
1145
1146 CAPAB_MASTER_OPT = cli_option("--master-capable", dest="master_capable",
1147                               type="bool", default=None, metavar=_YORNO,
1148                               help="Set the master_capable flag on the node")
1149
1150 CAPAB_VM_OPT = cli_option("--vm-capable", dest="vm_capable",
1151                           type="bool", default=None, metavar=_YORNO,
1152                           help="Set the vm_capable flag on the node")
1153
1154 ALLOCATABLE_OPT = cli_option("--allocatable", dest="allocatable",
1155                              type="bool", default=None, metavar=_YORNO,
1156                              help="Set the allocatable flag on a volume")
1157
1158 NOLVM_STORAGE_OPT = cli_option("--no-lvm-storage", dest="lvm_storage",
1159                                help="Disable support for lvm based instances"
1160                                " (cluster-wide)",
1161                                action="store_false", default=True)
1162
1163 ENABLED_HV_OPT = cli_option("--enabled-hypervisors",
1164                             dest="enabled_hypervisors",
1165                             help="Comma-separated list of hypervisors",
1166                             type="string", default=None)
1167
1168 ENABLED_DISK_TEMPLATES_OPT = cli_option("--enabled-disk-templates",
1169                                         dest="enabled_disk_templates",
1170                                         help="Comma-separated list of "
1171                                              "disk templates",
1172                                         type="string", default=None)
1173
1174 NIC_PARAMS_OPT = cli_option("-N", "--nic-parameters", dest="nicparams",
1175                             type="keyval", default={},
1176                             help="NIC parameters")
1177
1178 CP_SIZE_OPT = cli_option("-C", "--candidate-pool-size", default=None,
1179                          dest="candidate_pool_size", type="int",
1180                          help="Set the candidate pool size")
1181
1182 VG_NAME_OPT = cli_option("--vg-name", dest="vg_name",
1183                          help=("Enables LVM and specifies the volume group"
1184                                " name (cluster-wide) for disk allocation"
1185                                " [%s]" % constants.DEFAULT_VG),
1186                          metavar="VG", default=None)
1187
1188 YES_DOIT_OPT = cli_option("--yes-do-it", "--ya-rly", dest="yes_do_it",
1189                           help="Destroy cluster", action="store_true")
1190
1191 NOVOTING_OPT = cli_option("--no-voting", dest="no_voting",
1192                           help="Skip node agreement check (dangerous)",
1193                           action="store_true", default=False)
1194
1195 MAC_PREFIX_OPT = cli_option("-m", "--mac-prefix", dest="mac_prefix",
1196                             help="Specify the mac prefix for the instance IP"
1197                             " addresses, in the format XX:XX:XX",
1198                             metavar="PREFIX",
1199                             default=None)
1200
1201 MASTER_NETDEV_OPT = cli_option("--master-netdev", dest="master_netdev",
1202                                help="Specify the node interface (cluster-wide)"
1203                                " on which the master IP address will be added"
1204                                " (cluster init default: %s)" %
1205                                constants.DEFAULT_BRIDGE,
1206                                metavar="NETDEV",
1207                                default=None)
1208
1209 MASTER_NETMASK_OPT = cli_option("--master-netmask", dest="master_netmask",
1210                                 help="Specify the netmask of the master IP",
1211                                 metavar="NETMASK",
1212                                 default=None)
1213
1214 USE_EXTERNAL_MIP_SCRIPT = cli_option("--use-external-mip-script",
1215                                      dest="use_external_mip_script",
1216                                      help="Specify whether to run a"
1217                                      " user-provided script for the master"
1218                                      " IP address turnup and"
1219                                      " turndown operations",
1220                                      type="bool", metavar=_YORNO, default=None)
1221
1222 GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
1223                                 help="Specify the default directory (cluster-"
1224                                 "wide) for storing the file-based disks [%s]" %
1225                                 pathutils.DEFAULT_FILE_STORAGE_DIR,
1226                                 metavar="DIR",
1227                                 default=pathutils.DEFAULT_FILE_STORAGE_DIR)
1228
1229 GLOBAL_SHARED_FILEDIR_OPT = cli_option(
1230   "--shared-file-storage-dir",
1231   dest="shared_file_storage_dir",
1232   help="Specify the default directory (cluster-wide) for storing the"
1233   " shared file-based disks [%s]" %
1234   pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
1235   metavar="SHAREDDIR", default=pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR)
1236
1237 NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts",
1238                                    help="Don't modify %s" % pathutils.ETC_HOSTS,
1239                                    action="store_false", default=True)
1240
1241 NOMODIFY_SSH_SETUP_OPT = cli_option("--no-ssh-init", dest="modify_ssh_setup",
1242                                     help="Don't initialize SSH keys",
1243                                     action="store_false", default=True)
1244
1245 ERROR_CODES_OPT = cli_option("--error-codes", dest="error_codes",
1246                              help="Enable parseable error messages",
1247                              action="store_true", default=False)
1248
1249 NONPLUS1_OPT = cli_option("--no-nplus1-mem", dest="skip_nplusone_mem",
1250                           help="Skip N+1 memory redundancy tests",
1251                           action="store_true", default=False)
1252
1253 REBOOT_TYPE_OPT = cli_option("-t", "--type", dest="reboot_type",
1254                              help="Type of reboot: soft/hard/full",
1255                              default=constants.INSTANCE_REBOOT_HARD,
1256                              metavar="<REBOOT>",
1257                              choices=list(constants.REBOOT_TYPES))
1258
1259 IGNORE_SECONDARIES_OPT = cli_option("--ignore-secondaries",
1260                                     dest="ignore_secondaries",
1261                                     default=False, action="store_true",
1262                                     help="Ignore errors from secondaries")
1263
1264 NOSHUTDOWN_OPT = cli_option("--noshutdown", dest="shutdown",
1265                             action="store_false", default=True,
1266                             help="Don't shutdown the instance (unsafe)")
1267
1268 TIMEOUT_OPT = cli_option("--timeout", dest="timeout", type="int",
1269                          default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
1270                          help="Maximum time to wait")
1271
1272 SHUTDOWN_TIMEOUT_OPT = cli_option("--shutdown-timeout",
1273                                   dest="shutdown_timeout", type="int",
1274                                   default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
1275                                   help="Maximum time to wait for instance"
1276                                   " shutdown")
1277
1278 INTERVAL_OPT = cli_option("--interval", dest="interval", type="int",
1279                           default=None,
1280                           help=("Number of seconds between repetions of the"
1281                                 " command"))
1282
1283 EARLY_RELEASE_OPT = cli_option("--early-release",
1284                                dest="early_release", default=False,
1285                                action="store_true",
1286                                help="Release the locks on the secondary"
1287                                " node(s) early")
1288
1289 NEW_CLUSTER_CERT_OPT = cli_option("--new-cluster-certificate",
1290                                   dest="new_cluster_cert",
1291                                   default=False, action="store_true",
1292                                   help="Generate a new cluster certificate")
1293
1294 RAPI_CERT_OPT = cli_option("--rapi-certificate", dest="rapi_cert",
1295                            default=None,
1296                            help="File containing new RAPI certificate")
1297
1298 NEW_RAPI_CERT_OPT = cli_option("--new-rapi-certificate", dest="new_rapi_cert",
1299                                default=None, action="store_true",
1300                                help=("Generate a new self-signed RAPI"
1301                                      " certificate"))
1302
1303 SPICE_CERT_OPT = cli_option("--spice-certificate", dest="spice_cert",
1304                             default=None,
1305                             help="File containing new SPICE certificate")
1306
1307 SPICE_CACERT_OPT = cli_option("--spice-ca-certificate", dest="spice_cacert",
1308                               default=None,
1309                               help="File containing the certificate of the CA"
1310                               " which signed the SPICE certificate")
1311
1312 NEW_SPICE_CERT_OPT = cli_option("--new-spice-certificate",
1313                                 dest="new_spice_cert", default=None,
1314                                 action="store_true",
1315                                 help=("Generate a new self-signed SPICE"
1316                                       " certificate"))
1317
1318 NEW_CONFD_HMAC_KEY_OPT = cli_option("--new-confd-hmac-key",
1319                                     dest="new_confd_hmac_key",
1320                                     default=False, action="store_true",
1321                                     help=("Create a new HMAC key for %s" %
1322                                           constants.CONFD))
1323
1324 CLUSTER_DOMAIN_SECRET_OPT = cli_option("--cluster-domain-secret",
1325                                        dest="cluster_domain_secret",
1326                                        default=None,
1327                                        help=("Load new new cluster domain"
1328                                              " secret from file"))
1329
1330 NEW_CLUSTER_DOMAIN_SECRET_OPT = cli_option("--new-cluster-domain-secret",
1331                                            dest="new_cluster_domain_secret",
1332                                            default=False, action="store_true",
1333                                            help=("Create a new cluster domain"
1334                                                  " secret"))
1335
1336 USE_REPL_NET_OPT = cli_option("--use-replication-network",
1337                               dest="use_replication_network",
1338                               help="Whether to use the replication network"
1339                               " for talking to the nodes",
1340                               action="store_true", default=False)
1341
1342 MAINTAIN_NODE_HEALTH_OPT = \
1343     cli_option("--maintain-node-health", dest="maintain_node_health",
1344                metavar=_YORNO, default=None, type="bool",
1345                help="Configure the cluster to automatically maintain node"
1346                " health, by shutting down unknown instances, shutting down"
1347                " unknown DRBD devices, etc.")
1348
1349 IDENTIFY_DEFAULTS_OPT = \
1350     cli_option("--identify-defaults", dest="identify_defaults",
1351                default=False, action="store_true",
1352                help="Identify which saved instance parameters are equal to"
1353                " the current cluster defaults and set them as such, instead"
1354                " of marking them as overridden")
1355
1356 UIDPOOL_OPT = cli_option("--uid-pool", default=None,
1357                          action="store", dest="uid_pool",
1358                          help=("A list of user-ids or user-id"
1359                                " ranges separated by commas"))
1360
1361 ADD_UIDS_OPT = cli_option("--add-uids", default=None,
1362                           action="store", dest="add_uids",
1363                           help=("A list of user-ids or user-id"
1364                                 " ranges separated by commas, to be"
1365                                 " added to the user-id pool"))
1366
1367 REMOVE_UIDS_OPT = cli_option("--remove-uids", default=None,
1368                              action="store", dest="remove_uids",
1369                              help=("A list of user-ids or user-id"
1370                                    " ranges separated by commas, to be"
1371                                    " removed from the user-id pool"))
1372
1373 RESERVED_LVS_OPT = cli_option("--reserved-lvs", default=None,
1374                               action="store", dest="reserved_lvs",
1375                               help=("A comma-separated list of reserved"
1376                                     " logical volumes names, that will be"
1377                                     " ignored by cluster verify"))
1378
1379 ROMAN_OPT = cli_option("--roman",
1380                        dest="roman_integers", default=False,
1381                        action="store_true",
1382                        help="Use roman numbers for positive integers")
1383
1384 DRBD_HELPER_OPT = cli_option("--drbd-usermode-helper", dest="drbd_helper",
1385                              action="store", default=None,
1386                              help="Specifies usermode helper for DRBD")
1387
1388 NODRBD_STORAGE_OPT = cli_option("--no-drbd-storage", dest="drbd_storage",
1389                                 action="store_false", default=True,
1390                                 help="Disable support for DRBD")
1391
1392 PRIMARY_IP_VERSION_OPT = \
1393     cli_option("--primary-ip-version", default=constants.IP4_VERSION,
1394                action="store", dest="primary_ip_version",
1395                metavar="%d|%d" % (constants.IP4_VERSION,
1396                                   constants.IP6_VERSION),
1397                help="Cluster-wide IP version for primary IP")
1398
1399 SHOW_MACHINE_OPT = cli_option("-M", "--show-machine-names", default=False,
1400                               action="store_true",
1401                               help="Show machine name for every line in output")
1402
1403 FAILURE_ONLY_OPT = cli_option("--failure-only", default=False,
1404                               action="store_true",
1405                               help=("Hide successful results and show failures"
1406                                     " only (determined by the exit code)"))
1407
1408 REASON_OPT = cli_option("--reason", default=None,
1409                         help="The reason for executing the command")
1410
1411
1412 def _PriorityOptionCb(option, _, value, parser):
1413   """Callback for processing C{--priority} option.
1414
1415   """
1416   value = _PRIONAME_TO_VALUE[value]
1417
1418   setattr(parser.values, option.dest, value)
1419
1420
1421 PRIORITY_OPT = cli_option("--priority", default=None, dest="priority",
1422                           metavar="|".join(name for name, _ in _PRIORITY_NAMES),
1423                           choices=_PRIONAME_TO_VALUE.keys(),
1424                           action="callback", type="choice",
1425                           callback=_PriorityOptionCb,
1426                           help="Priority for opcode processing")
1427
1428 HID_OS_OPT = cli_option("--hidden", dest="hidden",
1429                         type="bool", default=None, metavar=_YORNO,
1430                         help="Sets the hidden flag on the OS")
1431
1432 BLK_OS_OPT = cli_option("--blacklisted", dest="blacklisted",
1433                         type="bool", default=None, metavar=_YORNO,
1434                         help="Sets the blacklisted flag on the OS")
1435
1436 PREALLOC_WIPE_DISKS_OPT = cli_option("--prealloc-wipe-disks", default=None,
1437                                      type="bool", metavar=_YORNO,
1438                                      dest="prealloc_wipe_disks",
1439                                      help=("Wipe disks prior to instance"
1440                                            " creation"))
1441
1442 NODE_PARAMS_OPT = cli_option("--node-parameters", dest="ndparams",
1443                              type="keyval", default=None,
1444                              help="Node parameters")
1445
1446 ALLOC_POLICY_OPT = cli_option("--alloc-policy", dest="alloc_policy",
1447                               action="store", metavar="POLICY", default=None,
1448                               help="Allocation policy for the node group")
1449
1450 NODE_POWERED_OPT = cli_option("--node-powered", default=None,
1451                               type="bool", metavar=_YORNO,
1452                               dest="node_powered",
1453                               help="Specify if the SoR for node is powered")
1454
1455 OOB_TIMEOUT_OPT = cli_option("--oob-timeout", dest="oob_timeout", type="int",
1456                              default=constants.OOB_TIMEOUT,
1457                              help="Maximum time to wait for out-of-band helper")
1458
1459 POWER_DELAY_OPT = cli_option("--power-delay", dest="power_delay", type="float",
1460                              default=constants.OOB_POWER_DELAY,
1461                              help="Time in seconds to wait between power-ons")
1462
1463 FORCE_FILTER_OPT = cli_option("-F", "--filter", dest="force_filter",
1464                               action="store_true", default=False,
1465                               help=("Whether command argument should be treated"
1466                                     " as filter"))
1467
1468 NO_REMEMBER_OPT = cli_option("--no-remember",
1469                              dest="no_remember",
1470                              action="store_true", default=False,
1471                              help="Perform but do not record the change"
1472                              " in the configuration")
1473
1474 PRIMARY_ONLY_OPT = cli_option("-p", "--primary-only",
1475                               default=False, action="store_true",
1476                               help="Evacuate primary instances only")
1477
1478 SECONDARY_ONLY_OPT = cli_option("-s", "--secondary-only",
1479                                 default=False, action="store_true",
1480                                 help="Evacuate secondary instances only"
1481                                      " (applies only to internally mirrored"
1482                                      " disk templates, e.g. %s)" %
1483                                      utils.CommaJoin(constants.DTS_INT_MIRROR))
1484
1485 STARTUP_PAUSED_OPT = cli_option("--paused", dest="startup_paused",
1486                                 action="store_true", default=False,
1487                                 help="Pause instance at startup")
1488
1489 TO_GROUP_OPT = cli_option("--to", dest="to", metavar="<group>",
1490                           help="Destination node group (name or uuid)",
1491                           default=None, action="append",
1492                           completion_suggest=OPT_COMPL_ONE_NODEGROUP)
1493
1494 IGNORE_ERRORS_OPT = cli_option("-I", "--ignore-errors", default=[],
1495                                action="append", dest="ignore_errors",
1496                                choices=list(constants.CV_ALL_ECODES_STRINGS),
1497                                help="Error code to be ignored")
1498
1499 DISK_STATE_OPT = cli_option("--disk-state", default=[], dest="disk_state",
1500                             action="append",
1501                             help=("Specify disk state information in the"
1502                                   " format"
1503                                   " storage_type/identifier:option=value,...;"
1504                                   " note this is unused for now"),
1505                             type="identkeyval")
1506
1507 HV_STATE_OPT = cli_option("--hypervisor-state", default=[], dest="hv_state",
1508                           action="append",
1509                           help=("Specify hypervisor state information in the"
1510                                 " format hypervisor:option=value,...;"
1511                                 " note this is unused for now"),
1512                           type="identkeyval")
1513
1514 IGNORE_IPOLICY_OPT = cli_option("--ignore-ipolicy", dest="ignore_ipolicy",
1515                                 action="store_true", default=False,
1516                                 help="Ignore instance policy violations")
1517
1518 RUNTIME_MEM_OPT = cli_option("-m", "--runtime-memory", dest="runtime_mem",
1519                              help="Sets the instance's runtime memory,"
1520                              " ballooning it up or down to the new value",
1521                              default=None, type="unit", metavar="<size>")
1522
1523 ABSOLUTE_OPT = cli_option("--absolute", dest="absolute",
1524                           action="store_true", default=False,
1525                           help="Marks the grow as absolute instead of the"
1526                           " (default) relative mode")
1527
1528 NETWORK_OPT = cli_option("--network",
1529                          action="store", default=None, dest="network",
1530                          help="IP network in CIDR notation")
1531
1532 GATEWAY_OPT = cli_option("--gateway",
1533                          action="store", default=None, dest="gateway",
1534                          help="IP address of the router (gateway)")
1535
1536 ADD_RESERVED_IPS_OPT = cli_option("--add-reserved-ips",
1537                                   action="store", default=None,
1538                                   dest="add_reserved_ips",
1539                                   help="Comma-separated list of"
1540                                   " reserved IPs to add")
1541
1542 REMOVE_RESERVED_IPS_OPT = cli_option("--remove-reserved-ips",
1543                                      action="store", default=None,
1544                                      dest="remove_reserved_ips",
1545                                      help="Comma-delimited list of"
1546                                      " reserved IPs to remove")
1547
1548 NETWORK6_OPT = cli_option("--network6",
1549                           action="store", default=None, dest="network6",
1550                           help="IP network in CIDR notation")
1551
1552 GATEWAY6_OPT = cli_option("--gateway6",
1553                           action="store", default=None, dest="gateway6",
1554                           help="IP6 address of the router (gateway)")
1555
1556 NOCONFLICTSCHECK_OPT = cli_option("--no-conflicts-check",
1557                                   dest="conflicts_check",
1558                                   default=True,
1559                                   action="store_false",
1560                                   help="Don't check for conflicting IPs")
1561
1562 #: Options provided by all commands
1563 COMMON_OPTS = [DEBUG_OPT]
1564
1565 # common options for creating instances. add and import then add their own
1566 # specific ones.
1567 COMMON_CREATE_OPTS = [
1568   BACKEND_OPT,
1569   DISK_OPT,
1570   DISK_TEMPLATE_OPT,
1571   FILESTORE_DIR_OPT,
1572   FILESTORE_DRIVER_OPT,
1573   HYPERVISOR_OPT,
1574   IALLOCATOR_OPT,
1575   NET_OPT,
1576   NODE_PLACEMENT_OPT,
1577   NOIPCHECK_OPT,
1578   NOCONFLICTSCHECK_OPT,
1579   NONAMECHECK_OPT,
1580   NONICS_OPT,
1581   NWSYNC_OPT,
1582   OSPARAMS_OPT,
1583   OS_SIZE_OPT,
1584   SUBMIT_OPT,
1585   TAG_ADD_OPT,
1586   DRY_RUN_OPT,
1587   PRIORITY_OPT,
1588   ]
1589
1590 # common instance policy options
1591 INSTANCE_POLICY_OPTS = [
1592   SPECS_CPU_COUNT_OPT,
1593   SPECS_DISK_COUNT_OPT,
1594   SPECS_DISK_SIZE_OPT,
1595   SPECS_MEM_SIZE_OPT,
1596   SPECS_NIC_COUNT_OPT,
1597   IPOLICY_DISK_TEMPLATES,
1598   IPOLICY_VCPU_RATIO,
1599   IPOLICY_SPINDLE_RATIO,
1600   ]
1601
1602
1603 class _ShowUsage(Exception):
1604   """Exception class for L{_ParseArgs}.
1605
1606   """
1607   def __init__(self, exit_error):
1608     """Initializes instances of this class.
1609
1610     @type exit_error: bool
1611     @param exit_error: Whether to report failure on exit
1612
1613     """
1614     Exception.__init__(self)
1615     self.exit_error = exit_error
1616
1617
1618 class _ShowVersion(Exception):
1619   """Exception class for L{_ParseArgs}.
1620
1621   """
1622
1623
1624 def _ParseArgs(binary, argv, commands, aliases, env_override):
1625   """Parser for the command line arguments.
1626
1627   This function parses the arguments and returns the function which
1628   must be executed together with its (modified) arguments.
1629
1630   @param binary: Script name
1631   @param argv: Command line arguments
1632   @param commands: Dictionary containing command definitions
1633   @param aliases: dictionary with command aliases {"alias": "target", ...}
1634   @param env_override: list of env variables allowed for default args
1635   @raise _ShowUsage: If usage description should be shown
1636   @raise _ShowVersion: If version should be shown
1637
1638   """
1639   assert not (env_override - set(commands))
1640   assert not (set(aliases.keys()) & set(commands.keys()))
1641
1642   if len(argv) > 1:
1643     cmd = argv[1]
1644   else:
1645     # No option or command given
1646     raise _ShowUsage(exit_error=True)
1647
1648   if cmd == "--version":
1649     raise _ShowVersion()
1650   elif cmd == "--help":
1651     raise _ShowUsage(exit_error=False)
1652   elif not (cmd in commands or cmd in aliases):
1653     raise _ShowUsage(exit_error=True)
1654
1655   # get command, unalias it, and look it up in commands
1656   if cmd in aliases:
1657     if aliases[cmd] not in commands:
1658       raise errors.ProgrammerError("Alias '%s' maps to non-existing"
1659                                    " command '%s'" % (cmd, aliases[cmd]))
1660
1661     cmd = aliases[cmd]
1662
1663   if cmd in env_override:
1664     args_env_name = ("%s_%s" % (binary.replace("-", "_"), cmd)).upper()
1665     env_args = os.environ.get(args_env_name)
1666     if env_args:
1667       argv = utils.InsertAtPos(argv, 2, shlex.split(env_args))
1668
1669   func, args_def, parser_opts, usage, description = commands[cmd]
1670   parser = OptionParser(option_list=parser_opts + COMMON_OPTS,
1671                         description=description,
1672                         formatter=TitledHelpFormatter(),
1673                         usage="%%prog %s %s" % (cmd, usage))
1674   parser.disable_interspersed_args()
1675   options, args = parser.parse_args(args=argv[2:])
1676
1677   if not _CheckArguments(cmd, args_def, args):
1678     return None, None, None
1679
1680   return func, options, args
1681
1682
1683 def _FormatUsage(binary, commands):
1684   """Generates a nice description of all commands.
1685
1686   @param binary: Script name
1687   @param commands: Dictionary containing command definitions
1688
1689   """
1690   # compute the max line length for cmd + usage
1691   mlen = min(60, max(map(len, commands)))
1692
1693   yield "Usage: %s {command} [options...] [argument...]" % binary
1694   yield "%s <command> --help to see details, or man %s" % (binary, binary)
1695   yield ""
1696   yield "Commands:"
1697
1698   # and format a nice command list
1699   for (cmd, (_, _, _, _, help_text)) in sorted(commands.items()):
1700     help_lines = textwrap.wrap(help_text, 79 - 3 - mlen)
1701     yield " %-*s - %s" % (mlen, cmd, help_lines.pop(0))
1702     for line in help_lines:
1703       yield " %-*s   %s" % (mlen, "", line)
1704
1705   yield ""
1706
1707
1708 def _CheckArguments(cmd, args_def, args):
1709   """Verifies the arguments using the argument definition.
1710
1711   Algorithm:
1712
1713     1. Abort with error if values specified by user but none expected.
1714
1715     1. For each argument in definition
1716
1717       1. Keep running count of minimum number of values (min_count)
1718       1. Keep running count of maximum number of values (max_count)
1719       1. If it has an unlimited number of values
1720
1721         1. Abort with error if it's not the last argument in the definition
1722
1723     1. If last argument has limited number of values
1724
1725       1. Abort with error if number of values doesn't match or is too large
1726
1727     1. Abort with error if user didn't pass enough values (min_count)
1728
1729   """
1730   if args and not args_def:
1731     ToStderr("Error: Command %s expects no arguments", cmd)
1732     return False
1733
1734   min_count = None
1735   max_count = None
1736   check_max = None
1737
1738   last_idx = len(args_def) - 1
1739
1740   for idx, arg in enumerate(args_def):
1741     if min_count is None:
1742       min_count = arg.min
1743     elif arg.min is not None:
1744       min_count += arg.min
1745
1746     if max_count is None:
1747       max_count = arg.max
1748     elif arg.max is not None:
1749       max_count += arg.max
1750
1751     if idx == last_idx:
1752       check_max = (arg.max is not None)
1753
1754     elif arg.max is None:
1755       raise errors.ProgrammerError("Only the last argument can have max=None")
1756
1757   if check_max:
1758     # Command with exact number of arguments
1759     if (min_count is not None and max_count is not None and
1760         min_count == max_count and len(args) != min_count):
1761       ToStderr("Error: Command %s expects %d argument(s)", cmd, min_count)
1762       return False
1763
1764     # Command with limited number of arguments
1765     if max_count is not None and len(args) > max_count:
1766       ToStderr("Error: Command %s expects only %d argument(s)",
1767                cmd, max_count)
1768       return False
1769
1770   # Command with some required arguments
1771   if min_count is not None and len(args) < min_count:
1772     ToStderr("Error: Command %s expects at least %d argument(s)",
1773              cmd, min_count)
1774     return False
1775
1776   return True
1777
1778
1779 def SplitNodeOption(value):
1780   """Splits the value of a --node option.
1781
1782   """
1783   if value and ":" in value:
1784     return value.split(":", 1)
1785   else:
1786     return (value, None)
1787
1788
1789 def CalculateOSNames(os_name, os_variants):
1790   """Calculates all the names an OS can be called, according to its variants.
1791
1792   @type os_name: string
1793   @param os_name: base name of the os
1794   @type os_variants: list or None
1795   @param os_variants: list of supported variants
1796   @rtype: list
1797   @return: list of valid names
1798
1799   """
1800   if os_variants:
1801     return ["%s+%s" % (os_name, v) for v in os_variants]
1802   else:
1803     return [os_name]
1804
1805
1806 def ParseFields(selected, default):
1807   """Parses the values of "--field"-like options.
1808
1809   @type selected: string or None
1810   @param selected: User-selected options
1811   @type default: list
1812   @param default: Default fields
1813
1814   """
1815   if selected is None:
1816     return default
1817
1818   if selected.startswith("+"):
1819     return default + selected[1:].split(",")
1820
1821   return selected.split(",")
1822
1823
1824 UsesRPC = rpc.RunWithRPC
1825
1826
1827 def AskUser(text, choices=None):
1828   """Ask the user a question.
1829
1830   @param text: the question to ask
1831
1832   @param choices: list with elements tuples (input_char, return_value,
1833       description); if not given, it will default to: [('y', True,
1834       'Perform the operation'), ('n', False, 'Do no do the operation')];
1835       note that the '?' char is reserved for help
1836
1837   @return: one of the return values from the choices list; if input is
1838       not possible (i.e. not running with a tty, we return the last
1839       entry from the list
1840
1841   """
1842   if choices is None:
1843     choices = [("y", True, "Perform the operation"),
1844                ("n", False, "Do not perform the operation")]
1845   if not choices or not isinstance(choices, list):
1846     raise errors.ProgrammerError("Invalid choices argument to AskUser")
1847   for entry in choices:
1848     if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == "?":
1849       raise errors.ProgrammerError("Invalid choices element to AskUser")
1850
1851   answer = choices[-1][1]
1852   new_text = []
1853   for line in text.splitlines():
1854     new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
1855   text = "\n".join(new_text)
1856   try:
1857     f = file("/dev/tty", "a+")
1858   except IOError:
1859     return answer
1860   try:
1861     chars = [entry[0] for entry in choices]
1862     chars[-1] = "[%s]" % chars[-1]
1863     chars.append("?")
1864     maps = dict([(entry[0], entry[1]) for entry in choices])
1865     while True:
1866       f.write(text)
1867       f.write("\n")
1868       f.write("/".join(chars))
1869       f.write(": ")
1870       line = f.readline(2).strip().lower()
1871       if line in maps:
1872         answer = maps[line]
1873         break
1874       elif line == "?":
1875         for entry in choices:
1876           f.write(" %s - %s\n" % (entry[0], entry[2]))
1877         f.write("\n")
1878         continue
1879   finally:
1880     f.close()
1881   return answer
1882
1883
1884 class JobSubmittedException(Exception):
1885   """Job was submitted, client should exit.
1886
1887   This exception has one argument, the ID of the job that was
1888   submitted. The handler should print this ID.
1889
1890   This is not an error, just a structured way to exit from clients.
1891
1892   """
1893
1894
1895 def SendJob(ops, cl=None):
1896   """Function to submit an opcode without waiting for the results.
1897
1898   @type ops: list
1899   @param ops: list of opcodes
1900   @type cl: luxi.Client
1901   @param cl: the luxi client to use for communicating with the master;
1902              if None, a new client will be created
1903
1904   """
1905   if cl is None:
1906     cl = GetClient()
1907
1908   job_id = cl.SubmitJob(ops)
1909
1910   return job_id
1911
1912
1913 def GenericPollJob(job_id, cbs, report_cbs):
1914   """Generic job-polling function.
1915
1916   @type job_id: number
1917   @param job_id: Job ID
1918   @type cbs: Instance of L{JobPollCbBase}
1919   @param cbs: Data callbacks
1920   @type report_cbs: Instance of L{JobPollReportCbBase}
1921   @param report_cbs: Reporting callbacks
1922
1923   """
1924   prev_job_info = None
1925   prev_logmsg_serial = None
1926
1927   status = None
1928
1929   while True:
1930     result = cbs.WaitForJobChangeOnce(job_id, ["status"], prev_job_info,
1931                                       prev_logmsg_serial)
1932     if not result:
1933       # job not found, go away!
1934       raise errors.JobLost("Job with id %s lost" % job_id)
1935
1936     if result == constants.JOB_NOTCHANGED:
1937       report_cbs.ReportNotChanged(job_id, status)
1938
1939       # Wait again
1940       continue
1941
1942     # Split result, a tuple of (field values, log entries)
1943     (job_info, log_entries) = result
1944     (status, ) = job_info
1945
1946     if log_entries:
1947       for log_entry in log_entries:
1948         (serial, timestamp, log_type, message) = log_entry
1949         report_cbs.ReportLogMessage(job_id, serial, timestamp,
1950                                     log_type, message)
1951         prev_logmsg_serial = max(prev_logmsg_serial, serial)
1952
1953     # TODO: Handle canceled and archived jobs
1954     elif status in (constants.JOB_STATUS_SUCCESS,
1955                     constants.JOB_STATUS_ERROR,
1956                     constants.JOB_STATUS_CANCELING,
1957                     constants.JOB_STATUS_CANCELED):
1958       break
1959
1960     prev_job_info = job_info
1961
1962   jobs = cbs.QueryJobs([job_id], ["status", "opstatus", "opresult"])
1963   if not jobs:
1964     raise errors.JobLost("Job with id %s lost" % job_id)
1965
1966   status, opstatus, result = jobs[0]
1967
1968   if status == constants.JOB_STATUS_SUCCESS:
1969     return result
1970
1971   if status in (constants.JOB_STATUS_CANCELING, constants.JOB_STATUS_CANCELED):
1972     raise errors.OpExecError("Job was canceled")
1973
1974   has_ok = False
1975   for idx, (status, msg) in enumerate(zip(opstatus, result)):
1976     if status == constants.OP_STATUS_SUCCESS:
1977       has_ok = True
1978     elif status == constants.OP_STATUS_ERROR:
1979       errors.MaybeRaise(msg)
1980
1981       if has_ok:
1982         raise errors.OpExecError("partial failure (opcode %d): %s" %
1983                                  (idx, msg))
1984
1985       raise errors.OpExecError(str(msg))
1986
1987   # default failure mode
1988   raise errors.OpExecError(result)
1989
1990
1991 class JobPollCbBase:
1992   """Base class for L{GenericPollJob} callbacks.
1993
1994   """
1995   def __init__(self):
1996     """Initializes this class.
1997
1998     """
1999
2000   def WaitForJobChangeOnce(self, job_id, fields,
2001                            prev_job_info, prev_log_serial):
2002     """Waits for changes on a job.
2003
2004     """
2005     raise NotImplementedError()
2006
2007   def QueryJobs(self, job_ids, fields):
2008     """Returns the selected fields for the selected job IDs.
2009
2010     @type job_ids: list of numbers
2011     @param job_ids: Job IDs
2012     @type fields: list of strings
2013     @param fields: Fields
2014
2015     """
2016     raise NotImplementedError()
2017
2018
2019 class JobPollReportCbBase:
2020   """Base class for L{GenericPollJob} reporting callbacks.
2021
2022   """
2023   def __init__(self):
2024     """Initializes this class.
2025
2026     """
2027
2028   def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
2029     """Handles a log message.
2030
2031     """
2032     raise NotImplementedError()
2033
2034   def ReportNotChanged(self, job_id, status):
2035     """Called for if a job hasn't changed in a while.
2036
2037     @type job_id: number
2038     @param job_id: Job ID
2039     @type status: string or None
2040     @param status: Job status if available
2041
2042     """
2043     raise NotImplementedError()
2044
2045
2046 class _LuxiJobPollCb(JobPollCbBase):
2047   def __init__(self, cl):
2048     """Initializes this class.
2049
2050     """
2051     JobPollCbBase.__init__(self)
2052     self.cl = cl
2053
2054   def WaitForJobChangeOnce(self, job_id, fields,
2055                            prev_job_info, prev_log_serial):
2056     """Waits for changes on a job.
2057
2058     """
2059     return self.cl.WaitForJobChangeOnce(job_id, fields,
2060                                         prev_job_info, prev_log_serial)
2061
2062   def QueryJobs(self, job_ids, fields):
2063     """Returns the selected fields for the selected job IDs.
2064
2065     """
2066     return self.cl.QueryJobs(job_ids, fields)
2067
2068
2069 class FeedbackFnJobPollReportCb(JobPollReportCbBase):
2070   def __init__(self, feedback_fn):
2071     """Initializes this class.
2072
2073     """
2074     JobPollReportCbBase.__init__(self)
2075
2076     self.feedback_fn = feedback_fn
2077
2078     assert callable(feedback_fn)
2079
2080   def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
2081     """Handles a log message.
2082
2083     """
2084     self.feedback_fn((timestamp, log_type, log_msg))
2085
2086   def ReportNotChanged(self, job_id, status):
2087     """Called if a job hasn't changed in a while.
2088
2089     """
2090     # Ignore
2091
2092
2093 class StdioJobPollReportCb(JobPollReportCbBase):
2094   def __init__(self):
2095     """Initializes this class.
2096
2097     """
2098     JobPollReportCbBase.__init__(self)
2099
2100     self.notified_queued = False
2101     self.notified_waitlock = False
2102
2103   def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
2104     """Handles a log message.
2105
2106     """
2107     ToStdout("%s %s", time.ctime(utils.MergeTime(timestamp)),
2108              FormatLogMessage(log_type, log_msg))
2109
2110   def ReportNotChanged(self, job_id, status):
2111     """Called if a job hasn't changed in a while.
2112
2113     """
2114     if status is None:
2115       return
2116
2117     if status == constants.JOB_STATUS_QUEUED and not self.notified_queued:
2118       ToStderr("Job %s is waiting in queue", job_id)
2119       self.notified_queued = True
2120
2121     elif status == constants.JOB_STATUS_WAITING and not self.notified_waitlock:
2122       ToStderr("Job %s is trying to acquire all necessary locks", job_id)
2123       self.notified_waitlock = True
2124
2125
2126 def FormatLogMessage(log_type, log_msg):
2127   """Formats a job message according to its type.
2128
2129   """
2130   if log_type != constants.ELOG_MESSAGE:
2131     log_msg = str(log_msg)
2132
2133   return utils.SafeEncode(log_msg)
2134
2135
2136 def PollJob(job_id, cl=None, feedback_fn=None, reporter=None):
2137   """Function to poll for the result of a job.
2138
2139   @type job_id: job identified
2140   @param job_id: the job to poll for results
2141   @type cl: luxi.Client
2142   @param cl: the luxi client to use for communicating with the master;
2143              if None, a new client will be created
2144
2145   """
2146   if cl is None:
2147     cl = GetClient()
2148
2149   if reporter is None:
2150     if feedback_fn:
2151       reporter = FeedbackFnJobPollReportCb(feedback_fn)
2152     else:
2153       reporter = StdioJobPollReportCb()
2154   elif feedback_fn:
2155     raise errors.ProgrammerError("Can't specify reporter and feedback function")
2156
2157   return GenericPollJob(job_id, _LuxiJobPollCb(cl), reporter)
2158
2159
2160 def SubmitOpCode(op, cl=None, feedback_fn=None, opts=None, reporter=None):
2161   """Legacy function to submit an opcode.
2162
2163   This is just a simple wrapper over the construction of the processor
2164   instance. It should be extended to better handle feedback and
2165   interaction functions.
2166
2167   """
2168   if cl is None:
2169     cl = GetClient()
2170
2171   SetGenericOpcodeOpts([op], opts)
2172
2173   job_id = SendJob([op], cl=cl)
2174
2175   op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn,
2176                        reporter=reporter)
2177
2178   return op_results[0]
2179
2180
2181 def SubmitOrSend(op, opts, cl=None, feedback_fn=None):
2182   """Wrapper around SubmitOpCode or SendJob.
2183
2184   This function will decide, based on the 'opts' parameter, whether to
2185   submit and wait for the result of the opcode (and return it), or
2186   whether to just send the job and print its identifier. It is used in
2187   order to simplify the implementation of the '--submit' option.
2188
2189   It will also process the opcodes if we're sending the via SendJob
2190   (otherwise SubmitOpCode does it).
2191
2192   """
2193   if opts and opts.submit_only:
2194     job = [op]
2195     SetGenericOpcodeOpts(job, opts)
2196     job_id = SendJob(job, cl=cl)
2197     raise JobSubmittedException(job_id)
2198   else:
2199     return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn, opts=opts)
2200
2201
2202 def SetGenericOpcodeOpts(opcode_list, options):
2203   """Processor for generic options.
2204
2205   This function updates the given opcodes based on generic command
2206   line options (like debug, dry-run, etc.).
2207
2208   @param opcode_list: list of opcodes
2209   @param options: command line options or None
2210   @return: None (in-place modification)
2211
2212   """
2213   if not options:
2214     return
2215   for op in opcode_list:
2216     op.debug_level = options.debug
2217     if hasattr(options, "dry_run"):
2218       op.dry_run = options.dry_run
2219     if getattr(options, "priority", None) is not None:
2220       op.priority = options.priority
2221
2222
2223 def GetClient(query=False):
2224   """Connects to the a luxi socket and returns a client.
2225
2226   @type query: boolean
2227   @param query: this signifies that the client will only be
2228       used for queries; if the build-time parameter
2229       enable-split-queries is enabled, then the client will be
2230       connected to the query socket instead of the masterd socket
2231
2232   """
2233   override_socket = os.getenv(constants.LUXI_OVERRIDE, "")
2234   if override_socket:
2235     if override_socket == constants.LUXI_OVERRIDE_MASTER:
2236       address = pathutils.MASTER_SOCKET
2237     elif override_socket == constants.LUXI_OVERRIDE_QUERY:
2238       address = pathutils.QUERY_SOCKET
2239     else:
2240       address = override_socket
2241   elif query and constants.ENABLE_SPLIT_QUERY:
2242     address = pathutils.QUERY_SOCKET
2243   else:
2244     address = None
2245   # TODO: Cache object?
2246   try:
2247     client = luxi.Client(address=address)
2248   except luxi.NoMasterError:
2249     ss = ssconf.SimpleStore()
2250
2251     # Try to read ssconf file
2252     try:
2253       ss.GetMasterNode()
2254     except errors.ConfigurationError:
2255       raise errors.OpPrereqError("Cluster not initialized or this machine is"
2256                                  " not part of a cluster",
2257                                  errors.ECODE_INVAL)
2258
2259     master, myself = ssconf.GetMasterAndMyself(ss=ss)
2260     if master != myself:
2261       raise errors.OpPrereqError("This is not the master node, please connect"
2262                                  " to node '%s' and rerun the command" %
2263                                  master, errors.ECODE_INVAL)
2264     raise
2265   return client
2266
2267
2268 def FormatError(err):
2269   """Return a formatted error message for a given error.
2270
2271   This function takes an exception instance and returns a tuple
2272   consisting of two values: first, the recommended exit code, and
2273   second, a string describing the error message (not
2274   newline-terminated).
2275
2276   """
2277   retcode = 1
2278   obuf = StringIO()
2279   msg = str(err)
2280   if isinstance(err, errors.ConfigurationError):
2281     txt = "Corrupt configuration file: %s" % msg
2282     logging.error(txt)
2283     obuf.write(txt + "\n")
2284     obuf.write("Aborting.")
2285     retcode = 2
2286   elif isinstance(err, errors.HooksAbort):
2287     obuf.write("Failure: hooks execution failed:\n")
2288     for node, script, out in err.args[0]:
2289       if out:
2290         obuf.write("  node: %s, script: %s, output: %s\n" %
2291                    (node, script, out))
2292       else:
2293         obuf.write("  node: %s, script: %s (no output)\n" %
2294                    (node, script))
2295   elif isinstance(err, errors.HooksFailure):
2296     obuf.write("Failure: hooks general failure: %s" % msg)
2297   elif isinstance(err, errors.ResolverError):
2298     this_host = netutils.Hostname.GetSysName()
2299     if err.args[0] == this_host:
2300       msg = "Failure: can't resolve my own hostname ('%s')"
2301     else:
2302       msg = "Failure: can't resolve hostname '%s'"
2303     obuf.write(msg % err.args[0])
2304   elif isinstance(err, errors.OpPrereqError):
2305     if len(err.args) == 2:
2306       obuf.write("Failure: prerequisites not met for this"
2307                  " operation:\nerror type: %s, error details:\n%s" %
2308                  (err.args[1], err.args[0]))
2309     else:
2310       obuf.write("Failure: prerequisites not met for this"
2311                  " operation:\n%s" % msg)
2312   elif isinstance(err, errors.OpExecError):
2313     obuf.write("Failure: command execution error:\n%s" % msg)
2314   elif isinstance(err, errors.TagError):
2315     obuf.write("Failure: invalid tag(s) given:\n%s" % msg)
2316   elif isinstance(err, errors.JobQueueDrainError):
2317     obuf.write("Failure: the job queue is marked for drain and doesn't"
2318                " accept new requests\n")
2319   elif isinstance(err, errors.JobQueueFull):
2320     obuf.write("Failure: the job queue is full and doesn't accept new"
2321                " job submissions until old jobs are archived\n")
2322   elif isinstance(err, errors.TypeEnforcementError):
2323     obuf.write("Parameter Error: %s" % msg)
2324   elif isinstance(err, errors.ParameterError):
2325     obuf.write("Failure: unknown/wrong parameter name '%s'" % msg)
2326   elif isinstance(err, luxi.NoMasterError):
2327     if err.args[0] == pathutils.MASTER_SOCKET:
2328       daemon = "the master daemon"
2329     elif err.args[0] == pathutils.QUERY_SOCKET:
2330       daemon = "the config daemon"
2331     else:
2332       daemon = "socket '%s'" % str(err.args[0])
2333     obuf.write("Cannot communicate with %s.\nIs the process running"
2334                " and listening for connections?" % daemon)
2335   elif isinstance(err, luxi.TimeoutError):
2336     obuf.write("Timeout while talking to the master daemon. Jobs might have"
2337                " been submitted and will continue to run even if the call"
2338                " timed out. Useful commands in this situation are \"gnt-job"
2339                " list\", \"gnt-job cancel\" and \"gnt-job watch\". Error:\n")
2340     obuf.write(msg)
2341   elif isinstance(err, luxi.PermissionError):
2342     obuf.write("It seems you don't have permissions to connect to the"
2343                " master daemon.\nPlease retry as a different user.")
2344   elif isinstance(err, luxi.ProtocolError):
2345     obuf.write("Unhandled protocol error while talking to the master daemon:\n"
2346                "%s" % msg)
2347   elif isinstance(err, errors.JobLost):
2348     obuf.write("Error checking job status: %s" % msg)
2349   elif isinstance(err, errors.QueryFilterParseError):
2350     obuf.write("Error while parsing query filter: %s\n" % err.args[0])
2351     obuf.write("\n".join(err.GetDetails()))
2352   elif isinstance(err, errors.GenericError):
2353     obuf.write("Unhandled Ganeti error: %s" % msg)
2354   elif isinstance(err, JobSubmittedException):
2355     obuf.write("JobID: %s\n" % err.args[0])
2356     retcode = 0
2357   else:
2358     obuf.write("Unhandled exception: %s" % msg)
2359   return retcode, obuf.getvalue().rstrip("\n")
2360
2361
2362 def GenericMain(commands, override=None, aliases=None,
2363                 env_override=frozenset()):
2364   """Generic main function for all the gnt-* commands.
2365
2366   @param commands: a dictionary with a special structure, see the design doc
2367                    for command line handling.
2368   @param override: if not None, we expect a dictionary with keys that will
2369                    override command line options; this can be used to pass
2370                    options from the scripts to generic functions
2371   @param aliases: dictionary with command aliases {'alias': 'target, ...}
2372   @param env_override: list of environment names which are allowed to submit
2373                        default args for commands
2374
2375   """
2376   # save the program name and the entire command line for later logging
2377   if sys.argv:
2378     binary = os.path.basename(sys.argv[0])
2379     if not binary:
2380       binary = sys.argv[0]
2381
2382     if len(sys.argv) >= 2:
2383       logname = utils.ShellQuoteArgs([binary, sys.argv[1]])
2384     else:
2385       logname = binary
2386
2387     cmdline = utils.ShellQuoteArgs([binary] + sys.argv[1:])
2388   else:
2389     binary = "<unknown program>"
2390     cmdline = "<unknown>"
2391
2392   if aliases is None:
2393     aliases = {}
2394
2395   try:
2396     (func, options, args) = _ParseArgs(binary, sys.argv, commands, aliases,
2397                                        env_override)
2398   except _ShowVersion:
2399     ToStdout("%s (ganeti %s) %s", binary, constants.VCS_VERSION,
2400              constants.RELEASE_VERSION)
2401     return constants.EXIT_SUCCESS
2402   except _ShowUsage, err:
2403     for line in _FormatUsage(binary, commands):
2404       ToStdout(line)
2405
2406     if err.exit_error:
2407       return constants.EXIT_FAILURE
2408     else:
2409       return constants.EXIT_SUCCESS
2410   except errors.ParameterError, err:
2411     result, err_msg = FormatError(err)
2412     ToStderr(err_msg)
2413     return 1
2414
2415   if func is None: # parse error
2416     return 1
2417
2418   if override is not None:
2419     for key, val in override.iteritems():
2420       setattr(options, key, val)
2421
2422   utils.SetupLogging(pathutils.LOG_COMMANDS, logname, debug=options.debug,
2423                      stderr_logging=True)
2424
2425   logging.info("Command line: %s", cmdline)
2426
2427   try:
2428     result = func(options, args)
2429   except (errors.GenericError, luxi.ProtocolError,
2430           JobSubmittedException), err:
2431     result, err_msg = FormatError(err)
2432     logging.exception("Error during command processing")
2433     ToStderr(err_msg)
2434   except KeyboardInterrupt:
2435     result = constants.EXIT_FAILURE
2436     ToStderr("Aborted. Note that if the operation created any jobs, they"
2437              " might have been submitted and"
2438              " will continue to run in the background.")
2439   except IOError, err:
2440     if err.errno == errno.EPIPE:
2441       # our terminal went away, we'll exit
2442       sys.exit(constants.EXIT_FAILURE)
2443     else:
2444       raise
2445
2446   return result
2447
2448
2449 def ParseNicOption(optvalue):
2450   """Parses the value of the --net option(s).
2451
2452   """
2453   try:
2454     nic_max = max(int(nidx[0]) + 1 for nidx in optvalue)
2455   except (TypeError, ValueError), err:
2456     raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err),
2457                                errors.ECODE_INVAL)
2458
2459   nics = [{}] * nic_max
2460   for nidx, ndict in optvalue:
2461     nidx = int(nidx)
2462
2463     if not isinstance(ndict, dict):
2464       raise errors.OpPrereqError("Invalid nic/%d value: expected dict,"
2465                                  " got %s" % (nidx, ndict), errors.ECODE_INVAL)
2466
2467     utils.ForceDictType(ndict, constants.INIC_PARAMS_TYPES)
2468
2469     nics[nidx] = ndict
2470
2471   return nics
2472
2473
2474 def GenericInstanceCreate(mode, opts, args):
2475   """Add an instance to the cluster via either creation or import.
2476
2477   @param mode: constants.INSTANCE_CREATE or constants.INSTANCE_IMPORT
2478   @param opts: the command line options selected by the user
2479   @type args: list
2480   @param args: should contain only one element, the new instance name
2481   @rtype: int
2482   @return: the desired exit code
2483
2484   """
2485   instance = args[0]
2486
2487   (pnode, snode) = SplitNodeOption(opts.node)
2488
2489   hypervisor = None
2490   hvparams = {}
2491   if opts.hypervisor:
2492     hypervisor, hvparams = opts.hypervisor
2493
2494   if opts.nics:
2495     nics = ParseNicOption(opts.nics)
2496   elif opts.no_nics:
2497     # no nics
2498     nics = []
2499   elif mode == constants.INSTANCE_CREATE:
2500     # default of one nic, all auto
2501     nics = [{}]
2502   else:
2503     # mode == import
2504     nics = []
2505
2506   if opts.disk_template == constants.DT_DISKLESS:
2507     if opts.disks or opts.sd_size is not None:
2508       raise errors.OpPrereqError("Diskless instance but disk"
2509                                  " information passed", errors.ECODE_INVAL)
2510     disks = []
2511   else:
2512     if (not opts.disks and not opts.sd_size
2513         and mode == constants.INSTANCE_CREATE):
2514       raise errors.OpPrereqError("No disk information specified",
2515                                  errors.ECODE_INVAL)
2516     if opts.disks and opts.sd_size is not None:
2517       raise errors.OpPrereqError("Please use either the '--disk' or"
2518                                  " '-s' option", errors.ECODE_INVAL)
2519     if opts.sd_size is not None:
2520       opts.disks = [(0, {constants.IDISK_SIZE: opts.sd_size})]
2521
2522     if opts.disks:
2523       try:
2524         disk_max = max(int(didx[0]) + 1 for didx in opts.disks)
2525       except ValueError, err:
2526         raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err),
2527                                    errors.ECODE_INVAL)
2528       disks = [{}] * disk_max
2529     else:
2530       disks = []
2531     for didx, ddict in opts.disks:
2532       didx = int(didx)
2533       if not isinstance(ddict, dict):
2534         msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict)
2535         raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
2536       elif constants.IDISK_SIZE in ddict:
2537         if constants.IDISK_ADOPT in ddict:
2538           raise errors.OpPrereqError("Only one of 'size' and 'adopt' allowed"
2539                                      " (disk %d)" % didx, errors.ECODE_INVAL)
2540         try:
2541           ddict[constants.IDISK_SIZE] = \
2542             utils.ParseUnit(ddict[constants.IDISK_SIZE])
2543         except ValueError, err:
2544           raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
2545                                      (didx, err), errors.ECODE_INVAL)
2546       elif constants.IDISK_ADOPT in ddict:
2547         if mode == constants.INSTANCE_IMPORT:
2548           raise errors.OpPrereqError("Disk adoption not allowed for instance"
2549                                      " import", errors.ECODE_INVAL)
2550         ddict[constants.IDISK_SIZE] = 0
2551       else:
2552         raise errors.OpPrereqError("Missing size or adoption source for"
2553                                    " disk %d" % didx, errors.ECODE_INVAL)
2554       disks[didx] = ddict
2555
2556   if opts.tags is not None:
2557     tags = opts.tags.split(",")
2558   else:
2559     tags = []
2560
2561   utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_COMPAT)
2562   utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
2563
2564   if mode == constants.INSTANCE_CREATE:
2565     start = opts.start
2566     os_type = opts.os
2567     force_variant = opts.force_variant
2568     src_node = None
2569     src_path = None
2570     no_install = opts.no_install
2571     identify_defaults = False
2572   elif mode == constants.INSTANCE_IMPORT:
2573     start = False
2574     os_type = None
2575     force_variant = False
2576     src_node = opts.src_node
2577     src_path = opts.src_dir
2578     no_install = None
2579     identify_defaults = opts.identify_defaults
2580   else:
2581     raise errors.ProgrammerError("Invalid creation mode %s" % mode)
2582
2583   op = opcodes.OpInstanceCreate(instance_name=instance,
2584                                 disks=disks,
2585                                 disk_template=opts.disk_template,
2586                                 nics=nics,
2587                                 conflicts_check=opts.conflicts_check,
2588                                 pnode=pnode, snode=snode,
2589                                 ip_check=opts.ip_check,
2590                                 name_check=opts.name_check,
2591                                 wait_for_sync=opts.wait_for_sync,
2592                                 file_storage_dir=opts.file_storage_dir,
2593                                 file_driver=opts.file_driver,
2594                                 iallocator=opts.iallocator,
2595                                 hypervisor=hypervisor,
2596                                 hvparams=hvparams,
2597                                 beparams=opts.beparams,
2598                                 osparams=opts.osparams,
2599                                 mode=mode,
2600                                 start=start,
2601                                 os_type=os_type,
2602                                 force_variant=force_variant,
2603                                 src_node=src_node,
2604                                 src_path=src_path,
2605                                 tags=tags,
2606                                 no_install=no_install,
2607                                 identify_defaults=identify_defaults,
2608                                 ignore_ipolicy=opts.ignore_ipolicy)
2609
2610   SubmitOrSend(op, opts)
2611   return 0
2612
2613
2614 class _RunWhileClusterStoppedHelper:
2615   """Helper class for L{RunWhileClusterStopped} to simplify state management
2616
2617   """
2618   def __init__(self, feedback_fn, cluster_name, master_node, online_nodes):
2619     """Initializes this class.
2620
2621     @type feedback_fn: callable
2622     @param feedback_fn: Feedback function
2623     @type cluster_name: string
2624     @param cluster_name: Cluster name
2625     @type master_node: string
2626     @param master_node Master node name
2627     @type online_nodes: list
2628     @param online_nodes: List of names of online nodes
2629
2630     """
2631     self.feedback_fn = feedback_fn
2632     self.cluster_name = cluster_name
2633     self.master_node = master_node
2634     self.online_nodes = online_nodes
2635
2636     self.ssh = ssh.SshRunner(self.cluster_name)
2637
2638     self.nonmaster_nodes = [name for name in online_nodes
2639                             if name != master_node]
2640
2641     assert self.master_node not in self.nonmaster_nodes
2642
2643   def _RunCmd(self, node_name, cmd):
2644     """Runs a command on the local or a remote machine.
2645
2646     @type node_name: string
2647     @param node_name: Machine name
2648     @type cmd: list
2649     @param cmd: Command
2650
2651     """
2652     if node_name is None or node_name == self.master_node:
2653       # No need to use SSH
2654       result = utils.RunCmd(cmd)
2655     else:
2656       result = self.ssh.Run(node_name, constants.SSH_LOGIN_USER,
2657                             utils.ShellQuoteArgs(cmd))
2658
2659     if result.failed:
2660       errmsg = ["Failed to run command %s" % result.cmd]
2661       if node_name:
2662         errmsg.append("on node %s" % node_name)
2663       errmsg.append(": exitcode %s and error %s" %
2664                     (result.exit_code, result.output))
2665       raise errors.OpExecError(" ".join(errmsg))
2666
2667   def Call(self, fn, *args):
2668     """Call function while all daemons are stopped.
2669
2670     @type fn: callable
2671     @param fn: Function to be called
2672
2673     """
2674     # Pause watcher by acquiring an exclusive lock on watcher state file
2675     self.feedback_fn("Blocking watcher")
2676     watcher_block = utils.FileLock.Open(pathutils.WATCHER_LOCK_FILE)
2677     try:
2678       # TODO: Currently, this just blocks. There's no timeout.
2679       # TODO: Should it be a shared lock?
2680       watcher_block.Exclusive(blocking=True)
2681
2682       # Stop master daemons, so that no new jobs can come in and all running
2683       # ones are finished
2684       self.feedback_fn("Stopping master daemons")
2685       self._RunCmd(None, [pathutils.DAEMON_UTIL, "stop-master"])
2686       try:
2687         # Stop daemons on all nodes
2688         for node_name in self.online_nodes:
2689           self.feedback_fn("Stopping daemons on %s" % node_name)
2690           self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "stop-all"])
2691
2692         # All daemons are shut down now
2693         try:
2694           return fn(self, *args)
2695         except Exception, err:
2696           _, errmsg = FormatError(err)
2697           logging.exception("Caught exception")
2698           self.feedback_fn(errmsg)
2699           raise
2700       finally:
2701         # Start cluster again, master node last
2702         for node_name in self.nonmaster_nodes + [self.master_node]:
2703           self.feedback_fn("Starting daemons on %s" % node_name)
2704           self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "start-all"])
2705     finally:
2706       # Resume watcher
2707       watcher_block.Close()
2708
2709
2710 def RunWhileClusterStopped(feedback_fn, fn, *args):
2711   """Calls a function while all cluster daemons are stopped.
2712
2713   @type feedback_fn: callable
2714   @param feedback_fn: Feedback function
2715   @type fn: callable
2716   @param fn: Function to be called when daemons are stopped
2717
2718   """
2719   feedback_fn("Gathering cluster information")
2720
2721   # This ensures we're running on the master daemon
2722   cl = GetClient()
2723
2724   (cluster_name, master_node) = \
2725     cl.QueryConfigValues(["cluster_name", "master_node"])
2726
2727   online_nodes = GetOnlineNodes([], cl=cl)
2728
2729   # Don't keep a reference to the client. The master daemon will go away.
2730   del cl
2731
2732   assert master_node in online_nodes
2733
2734   return _RunWhileClusterStoppedHelper(feedback_fn, cluster_name, master_node,
2735                                        online_nodes).Call(fn, *args)
2736
2737
2738 def GenerateTable(headers, fields, separator, data,
2739                   numfields=None, unitfields=None,
2740                   units=None):
2741   """Prints a table with headers and different fields.
2742
2743   @type headers: dict
2744   @param headers: dictionary mapping field names to headers for
2745       the table
2746   @type fields: list
2747   @param fields: the field names corresponding to each row in
2748       the data field
2749   @param separator: the separator to be used; if this is None,
2750       the default 'smart' algorithm is used which computes optimal
2751       field width, otherwise just the separator is used between
2752       each field
2753   @type data: list
2754   @param data: a list of lists, each sublist being one row to be output
2755   @type numfields: list
2756   @param numfields: a list with the fields that hold numeric
2757       values and thus should be right-aligned
2758   @type unitfields: list
2759   @param unitfields: a list with the fields that hold numeric
2760       values that should be formatted with the units field
2761   @type units: string or None
2762   @param units: the units we should use for formatting, or None for
2763       automatic choice (human-readable for non-separator usage, otherwise
2764       megabytes); this is a one-letter string
2765
2766   """
2767   if units is None:
2768     if separator:
2769       units = "m"
2770     else:
2771       units = "h"
2772
2773   if numfields is None:
2774     numfields = []
2775   if unitfields is None:
2776     unitfields = []
2777
2778   numfields = utils.FieldSet(*numfields)   # pylint: disable=W0142
2779   unitfields = utils.FieldSet(*unitfields) # pylint: disable=W0142
2780
2781   format_fields = []
2782   for field in fields:
2783     if headers and field not in headers:
2784       # TODO: handle better unknown fields (either revert to old
2785       # style of raising exception, or deal more intelligently with
2786       # variable fields)
2787       headers[field] = field
2788     if separator is not None:
2789       format_fields.append("%s")
2790     elif numfields.Matches(field):
2791       format_fields.append("%*s")
2792     else:
2793       format_fields.append("%-*s")
2794
2795   if separator is None:
2796     mlens = [0 for name in fields]
2797     format_str = " ".join(format_fields)
2798   else:
2799     format_str = separator.replace("%", "%%").join(format_fields)
2800
2801   for row in data:
2802     if row is None:
2803       continue
2804     for idx, val in enumerate(row):
2805       if unitfields.Matches(fields[idx]):
2806         try:
2807           val = int(val)
2808         except (TypeError, ValueError):
2809           pass
2810         else:
2811           val = row[idx] = utils.FormatUnit(val, units)
2812       val = row[idx] = str(val)
2813       if separator is None:
2814         mlens[idx] = max(mlens[idx], len(val))
2815
2816   result = []
2817   if headers:
2818     args = []
2819     for idx, name in enumerate(fields):
2820       hdr = headers[name]
2821       if separator is None:
2822         mlens[idx] = max(mlens[idx], len(hdr))
2823         args.append(mlens[idx])
2824       args.append(hdr)
2825     result.append(format_str % tuple(args))
2826
2827   if separator is None:
2828     assert len(mlens) == len(fields)
2829
2830     if fields and not numfields.Matches(fields[-1]):
2831       mlens[-1] = 0
2832
2833   for line in data:
2834     args = []
2835     if line is None:
2836       line = ["-" for _ in fields]
2837     for idx in range(len(fields)):
2838       if separator is None:
2839         args.append(mlens[idx])
2840       args.append(line[idx])
2841     result.append(format_str % tuple(args))
2842
2843   return result
2844
2845
2846 def _FormatBool(value):
2847   """Formats a boolean value as a string.
2848
2849   """
2850   if value:
2851     return "Y"
2852   return "N"
2853
2854
2855 #: Default formatting for query results; (callback, align right)
2856 _DEFAULT_FORMAT_QUERY = {
2857   constants.QFT_TEXT: (str, False),
2858   constants.QFT_BOOL: (_FormatBool, False),
2859   constants.QFT_NUMBER: (str, True),
2860   constants.QFT_TIMESTAMP: (utils.FormatTime, False),
2861   constants.QFT_OTHER: (str, False),
2862   constants.QFT_UNKNOWN: (str, False),
2863   }
2864
2865
2866 def _GetColumnFormatter(fdef, override, unit):
2867   """Returns formatting function for a field.
2868
2869   @type fdef: L{objects.QueryFieldDefinition}
2870   @type override: dict
2871   @param override: Dictionary for overriding field formatting functions,
2872     indexed by field name, contents like L{_DEFAULT_FORMAT_QUERY}
2873   @type unit: string
2874   @param unit: Unit used for formatting fields of type L{constants.QFT_UNIT}
2875   @rtype: tuple; (callable, bool)
2876   @return: Returns the function to format a value (takes one parameter) and a
2877     boolean for aligning the value on the right-hand side
2878
2879   """
2880   fmt = override.get(fdef.name, None)
2881   if fmt is not None:
2882     return fmt
2883
2884   assert constants.QFT_UNIT not in _DEFAULT_FORMAT_QUERY
2885
2886   if fdef.kind == constants.QFT_UNIT:
2887     # Can't keep this information in the static dictionary
2888     return (lambda value: utils.FormatUnit(value, unit), True)
2889
2890   fmt = _DEFAULT_FORMAT_QUERY.get(fdef.kind, None)
2891   if fmt is not None:
2892     return fmt
2893
2894   raise NotImplementedError("Can't format column type '%s'" % fdef.kind)
2895
2896
2897 class _QueryColumnFormatter:
2898   """Callable class for formatting fields of a query.
2899
2900   """
2901   def __init__(self, fn, status_fn, verbose):
2902     """Initializes this class.
2903
2904     @type fn: callable
2905     @param fn: Formatting function
2906     @type status_fn: callable
2907     @param status_fn: Function to report fields' status
2908     @type verbose: boolean
2909     @param verbose: whether to use verbose field descriptions or not
2910
2911     """
2912     self._fn = fn
2913     self._status_fn = status_fn
2914     self._verbose = verbose
2915
2916   def __call__(self, data):
2917     """Returns a field's string representation.
2918
2919     """
2920     (status, value) = data
2921
2922     # Report status
2923     self._status_fn(status)
2924
2925     if status == constants.RS_NORMAL:
2926       return self._fn(value)
2927
2928     assert value is None, \
2929            "Found value %r for abnormal status %s" % (value, status)
2930
2931     return FormatResultError(status, self._verbose)
2932
2933
2934 def FormatResultError(status, verbose):
2935   """Formats result status other than L{constants.RS_NORMAL}.
2936
2937   @param status: The result status
2938   @type verbose: boolean
2939   @param verbose: Whether to return the verbose text
2940   @return: Text of result status
2941
2942   """
2943   assert status != constants.RS_NORMAL, \
2944          "FormatResultError called with status equal to constants.RS_NORMAL"
2945   try:
2946     (verbose_text, normal_text) = constants.RSS_DESCRIPTION[status]
2947   except KeyError:
2948     raise NotImplementedError("Unknown status %s" % status)
2949   else:
2950     if verbose:
2951       return verbose_text
2952     return normal_text
2953
2954
2955 def FormatQueryResult(result, unit=None, format_override=None, separator=None,
2956                       header=False, verbose=False):
2957   """Formats data in L{objects.QueryResponse}.
2958
2959   @type result: L{objects.QueryResponse}
2960   @param result: result of query operation
2961   @type unit: string
2962   @param unit: Unit used for formatting fields of type L{constants.QFT_UNIT},
2963     see L{utils.text.FormatUnit}
2964   @type format_override: dict
2965   @param format_override: Dictionary for overriding field formatting functions,
2966     indexed by field name, contents like L{_DEFAULT_FORMAT_QUERY}
2967   @type separator: string or None
2968   @param separator: String used to separate fields
2969   @type header: bool
2970   @param header: Whether to output header row
2971   @type verbose: boolean
2972   @param verbose: whether to use verbose field descriptions or not
2973
2974   """
2975   if unit is None:
2976     if separator:
2977       unit = "m"
2978     else:
2979       unit = "h"
2980
2981   if format_override is None:
2982     format_override = {}
2983
2984   stats = dict.fromkeys(constants.RS_ALL, 0)
2985
2986   def _RecordStatus(status):
2987     if status in stats:
2988       stats[status] += 1
2989
2990   columns = []
2991   for fdef in result.fields:
2992     assert fdef.title and fdef.name
2993     (fn, align_right) = _GetColumnFormatter(fdef, format_override, unit)
2994     columns.append(TableColumn(fdef.title,
2995                                _QueryColumnFormatter(fn, _RecordStatus,
2996                                                      verbose),
2997                                align_right))
2998
2999   table = FormatTable(result.data, columns, header, separator)
3000
3001   # Collect statistics
3002   assert len(stats) == len(constants.RS_ALL)
3003   assert compat.all(count >= 0 for count in stats.values())
3004
3005   # Determine overall status. If there was no data, unknown fields must be
3006   # detected via the field definitions.
3007   if (stats[constants.RS_UNKNOWN] or
3008       (not result.data and _GetUnknownFields(result.fields))):
3009     status = QR_UNKNOWN
3010   elif compat.any(count > 0 for key, count in stats.items()
3011                   if key != constants.RS_NORMAL):
3012     status = QR_INCOMPLETE
3013   else:
3014     status = QR_NORMAL
3015
3016   return (status, table)
3017
3018
3019 def _GetUnknownFields(fdefs):
3020   """Returns list of unknown fields included in C{fdefs}.
3021
3022   @type fdefs: list of L{objects.QueryFieldDefinition}
3023
3024   """
3025   return [fdef for fdef in fdefs
3026           if fdef.kind == constants.QFT_UNKNOWN]
3027
3028
3029 def _WarnUnknownFields(fdefs):
3030   """Prints a warning to stderr if a query included unknown fields.
3031
3032   @type fdefs: list of L{objects.QueryFieldDefinition}
3033
3034   """
3035   unknown = _GetUnknownFields(fdefs)
3036   if unknown:
3037     ToStderr("Warning: Queried for unknown fields %s",
3038              utils.CommaJoin(fdef.name for fdef in unknown))
3039     return True
3040
3041   return False
3042
3043
3044 def GenericList(resource, fields, names, unit, separator, header, cl=None,
3045                 format_override=None, verbose=False, force_filter=False,
3046                 namefield=None, qfilter=None, isnumeric=False):
3047   """Generic implementation for listing all items of a resource.
3048
3049   @param resource: One of L{constants.QR_VIA_LUXI}
3050   @type fields: list of strings
3051   @param fields: List of fields to query for
3052   @type names: list of strings
3053   @param names: Names of items to query for
3054   @type unit: string or None
3055   @param unit: Unit used for formatting fields of type L{constants.QFT_UNIT} or
3056     None for automatic choice (human-readable for non-separator usage,
3057     otherwise megabytes); this is a one-letter string
3058   @type separator: string or None
3059   @param separator: String used to separate fields
3060   @type header: bool
3061   @param header: Whether to show header row
3062   @type force_filter: bool
3063   @param force_filter: Whether to always treat names as filter
3064   @type format_override: dict
3065   @param format_override: Dictionary for overriding field formatting functions,
3066     indexed by field name, contents like L{_DEFAULT_FORMAT_QUERY}
3067   @type verbose: boolean
3068   @param verbose: whether to use verbose field descriptions or not
3069   @type namefield: string
3070   @param namefield: Name of field to use for simple filters (see
3071     L{qlang.MakeFilter} for details)
3072   @type qfilter: list or None
3073   @param qfilter: Query filter (in addition to names)
3074   @param isnumeric: bool
3075   @param isnumeric: Whether the namefield's type is numeric, and therefore
3076     any simple filters built by namefield should use integer values to
3077     reflect that
3078
3079   """
3080   if not names:
3081     names = None
3082
3083   namefilter = qlang.MakeFilter(names, force_filter, namefield=namefield,
3084                                 isnumeric=isnumeric)
3085
3086   if qfilter is None:
3087     qfilter = namefilter
3088   elif namefilter is not None:
3089     qfilter = [qlang.OP_AND, namefilter, qfilter]
3090
3091   if cl is None:
3092     cl = GetClient()
3093
3094   response = cl.Query(resource, fields, qfilter)
3095
3096   found_unknown = _WarnUnknownFields(response.fields)
3097
3098   (status, data) = FormatQueryResult(response, unit=unit, separator=separator,
3099                                      header=header,
3100                                      format_override=format_override,
3101                                      verbose=verbose)
3102
3103   for line in data:
3104     ToStdout(line)
3105
3106   assert ((found_unknown and status == QR_UNKNOWN) or
3107           (not found_unknown and status != QR_UNKNOWN))
3108
3109   if status == QR_UNKNOWN:
3110     return constants.EXIT_UNKNOWN_FIELD
3111
3112   # TODO: Should the list command fail if not all data could be collected?
3113   return constants.EXIT_SUCCESS
3114
3115
3116 def _FieldDescValues(fdef):
3117   """Helper function for L{GenericListFields} to get query field description.
3118
3119   @type fdef: L{objects.QueryFieldDefinition}
3120   @rtype: list
3121
3122   """
3123   return [
3124     fdef.name,
3125     _QFT_NAMES.get(fdef.kind, fdef.kind),
3126     fdef.title,
3127     fdef.doc,
3128     ]
3129
3130
3131 def GenericListFields(resource, fields, separator, header, cl=None):
3132   """Generic implementation for listing fields for a resource.
3133
3134   @param resource: One of L{constants.QR_VIA_LUXI}
3135   @type fields: list of strings
3136   @param fields: List of fields to query for
3137   @type separator: string or None
3138   @param separator: String used to separate fields
3139   @type header: bool
3140   @param header: Whether to show header row
3141
3142   """
3143   if cl is None:
3144     cl = GetClient()
3145
3146   if not fields:
3147     fields = None
3148
3149   response = cl.QueryFields(resource, fields)
3150
3151   found_unknown = _WarnUnknownFields(response.fields)
3152
3153   columns = [
3154     TableColumn("Name", str, False),
3155     TableColumn("Type", str, False),
3156     TableColumn("Title", str, False),
3157     TableColumn("Description", str, False),
3158     ]
3159
3160   rows = map(_FieldDescValues, response.fields)
3161
3162   for line in FormatTable(rows, columns, header, separator):
3163     ToStdout(line)
3164
3165   if found_unknown:
3166     return constants.EXIT_UNKNOWN_FIELD
3167
3168   return constants.EXIT_SUCCESS
3169
3170
3171 class TableColumn:
3172   """Describes a column for L{FormatTable}.
3173
3174   """
3175   def __init__(self, title, fn, align_right):
3176     """Initializes this class.
3177
3178     @type title: string
3179     @param title: Column title
3180     @type fn: callable
3181     @param fn: Formatting function
3182     @type align_right: bool
3183     @param align_right: Whether to align values on the right-hand side
3184
3185     """
3186     self.title = title
3187     self.format = fn
3188     self.align_right = align_right
3189
3190
3191 def _GetColFormatString(width, align_right):
3192   """Returns the format string for a field.
3193
3194   """
3195   if align_right:
3196     sign = ""
3197   else:
3198     sign = "-"
3199
3200   return "%%%s%ss" % (sign, width)
3201
3202
3203 def FormatTable(rows, columns, header, separator):
3204   """Formats data as a table.
3205
3206   @type rows: list of lists
3207   @param rows: Row data, one list per row
3208   @type columns: list of L{TableColumn}
3209   @param columns: Column descriptions
3210   @type header: bool
3211   @param header: Whether to show header row
3212   @type separator: string or None
3213   @param separator: String used to separate columns
3214
3215   """
3216   if header:
3217     data = [[col.title for col in columns]]
3218     colwidth = [len(col.title) for col in columns]
3219   else:
3220     data = []
3221     colwidth = [0 for _ in columns]
3222
3223   # Format row data
3224   for row in rows:
3225     assert len(row) == len(columns)
3226
3227     formatted = [col.format(value) for value, col in zip(row, columns)]
3228
3229     if separator is None:
3230       # Update column widths
3231       for idx, (oldwidth, value) in enumerate(zip(colwidth, formatted)):
3232         # Modifying a list's items while iterating is fine
3233         colwidth[idx] = max(oldwidth, len(value))
3234
3235     data.append(formatted)
3236
3237   if separator is not None:
3238     # Return early if a separator is used
3239     return [separator.join(row) for row in data]
3240
3241   if columns and not columns[-1].align_right:
3242     # Avoid unnecessary spaces at end of line
3243     colwidth[-1] = 0
3244
3245   # Build format string
3246   fmt = " ".join([_GetColFormatString(width, col.align_right)
3247                   for col, width in zip(columns, colwidth)])
3248
3249   return [fmt % tuple(row) for row in data]
3250
3251
3252 def FormatTimestamp(ts):
3253   """Formats a given timestamp.
3254
3255   @type ts: timestamp
3256   @param ts: a timeval-type timestamp, a tuple of seconds and microseconds
3257
3258   @rtype: string
3259   @return: a string with the formatted timestamp
3260
3261   """
3262   if not isinstance(ts, (tuple, list)) or len(ts) != 2:
3263     return "?"
3264
3265   (sec, usecs) = ts
3266   return utils.FormatTime(sec, usecs=usecs)
3267
3268
3269 def ParseTimespec(value):
3270   """Parse a time specification.
3271
3272   The following suffixed will be recognized:
3273
3274     - s: seconds
3275     - m: minutes
3276     - h: hours
3277     - d: day
3278     - w: weeks
3279
3280   Without any suffix, the value will be taken to be in seconds.
3281
3282   """
3283   value = str(value)
3284   if not value:
3285     raise errors.OpPrereqError("Empty time specification passed",
3286                                errors.ECODE_INVAL)
3287   suffix_map = {
3288     "s": 1,
3289     "m": 60,
3290     "h": 3600,
3291     "d": 86400,
3292     "w": 604800,
3293     }
3294   if value[-1] not in suffix_map:
3295     try:
3296       value = int(value)
3297     except (TypeError, ValueError):
3298       raise errors.OpPrereqError("Invalid time specification '%s'" % value,
3299                                  errors.ECODE_INVAL)
3300   else:
3301     multiplier = suffix_map[value[-1]]
3302     value = value[:-1]
3303     if not value: # no data left after stripping the suffix
3304       raise errors.OpPrereqError("Invalid time specification (only"
3305                                  " suffix passed)", errors.ECODE_INVAL)
3306     try:
3307       value = int(value) * multiplier
3308     except (TypeError, ValueError):
3309       raise errors.OpPrereqError("Invalid time specification '%s'" % value,
3310                                  errors.ECODE_INVAL)
3311   return value
3312
3313
3314 def GetOnlineNodes(nodes, cl=None, nowarn=False, secondary_ips=False,
3315                    filter_master=False, nodegroup=None):
3316   """Returns the names of online nodes.
3317
3318   This function will also log a warning on stderr with the names of
3319   the online nodes.
3320
3321   @param nodes: if not empty, use only this subset of nodes (minus the
3322       offline ones)
3323   @param cl: if not None, luxi client to use
3324   @type nowarn: boolean
3325   @param nowarn: by default, this function will output a note with the
3326       offline nodes that are skipped; if this parameter is True the
3327       note is not displayed
3328   @type secondary_ips: boolean
3329   @param secondary_ips: if True, return the secondary IPs instead of the
3330       names, useful for doing network traffic over the replication interface
3331       (if any)
3332   @type filter_master: boolean
3333   @param filter_master: if True, do not return the master node in the list
3334       (useful in coordination with secondary_ips where we cannot check our
3335       node name against the list)
3336   @type nodegroup: string
3337   @param nodegroup: If set, only return nodes in this node group
3338
3339   """
3340   if cl is None:
3341     cl = GetClient()
3342
3343   qfilter = []
3344
3345   if nodes:
3346     qfilter.append(qlang.MakeSimpleFilter("name", nodes))
3347
3348   if nodegroup is not None:
3349     qfilter.append([qlang.OP_OR, [qlang.OP_EQUAL, "group", nodegroup],
3350                                  [qlang.OP_EQUAL, "group.uuid", nodegroup]])
3351
3352   if filter_master:
3353     qfilter.append([qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
3354
3355   if qfilter:
3356     if len(qfilter) > 1:
3357       final_filter = [qlang.OP_AND] + qfilter
3358     else:
3359       assert len(qfilter) == 1
3360       final_filter = qfilter[0]
3361   else:
3362     final_filter = None
3363
3364   result = cl.Query(constants.QR_NODE, ["name", "offline", "sip"], final_filter)
3365
3366   def _IsOffline(row):
3367     (_, (_, offline), _) = row
3368     return offline
3369
3370   def _GetName(row):
3371     ((_, name), _, _) = row
3372     return name
3373
3374   def _GetSip(row):
3375     (_, _, (_, sip)) = row
3376     return sip
3377
3378   (offline, online) = compat.partition(result.data, _IsOffline)
3379
3380   if offline and not nowarn:
3381     ToStderr("Note: skipping offline node(s): %s" %
3382              utils.CommaJoin(map(_GetName, offline)))
3383
3384   if secondary_ips:
3385     fn = _GetSip
3386   else:
3387     fn = _GetName
3388
3389   return map(fn, online)
3390
3391
3392 def _ToStream(stream, txt, *args):
3393   """Write a message to a stream, bypassing the logging system
3394
3395   @type stream: file object
3396   @param stream: the file to which we should write
3397   @type txt: str
3398   @param txt: the message
3399
3400   """
3401   try:
3402     if args:
3403       args = tuple(args)
3404       stream.write(txt % args)
3405     else:
3406       stream.write(txt)
3407     stream.write("\n")
3408     stream.flush()
3409   except IOError, err:
3410     if err.errno == errno.EPIPE:
3411       # our terminal went away, we'll exit
3412       sys.exit(constants.EXIT_FAILURE)
3413     else:
3414       raise
3415
3416
3417 def ToStdout(txt, *args):
3418   """Write a message to stdout only, bypassing the logging system
3419
3420   This is just a wrapper over _ToStream.
3421
3422   @type txt: str
3423   @param txt: the message
3424
3425   """
3426   _ToStream(sys.stdout, txt, *args)
3427
3428
3429 def ToStderr(txt, *args):
3430   """Write a message to stderr only, bypassing the logging system
3431
3432   This is just a wrapper over _ToStream.
3433
3434   @type txt: str
3435   @param txt: the message
3436
3437   """
3438   _ToStream(sys.stderr, txt, *args)
3439
3440
3441 class JobExecutor(object):
3442   """Class which manages the submission and execution of multiple jobs.
3443
3444   Note that instances of this class should not be reused between
3445   GetResults() calls.
3446
3447   """
3448   def __init__(self, cl=None, verbose=True, opts=None, feedback_fn=None):
3449     self.queue = []
3450     if cl is None:
3451       cl = GetClient()
3452     self.cl = cl
3453     self.verbose = verbose
3454     self.jobs = []
3455     self.opts = opts
3456     self.feedback_fn = feedback_fn
3457     self._counter = itertools.count()
3458
3459   @staticmethod
3460   def _IfName(name, fmt):
3461     """Helper function for formatting name.
3462
3463     """
3464     if name:
3465       return fmt % name
3466
3467     return ""
3468
3469   def QueueJob(self, name, *ops):
3470     """Record a job for later submit.
3471
3472     @type name: string
3473     @param name: a description of the job, will be used in WaitJobSet
3474
3475     """
3476     SetGenericOpcodeOpts(ops, self.opts)
3477     self.queue.append((self._counter.next(), name, ops))
3478
3479   def AddJobId(self, name, status, job_id):
3480     """Adds a job ID to the internal queue.
3481
3482     """
3483     self.jobs.append((self._counter.next(), status, job_id, name))
3484
3485   def SubmitPending(self, each=False):
3486     """Submit all pending jobs.
3487
3488     """
3489     if each:
3490       results = []
3491       for (_, _, ops) in self.queue:
3492         # SubmitJob will remove the success status, but raise an exception if
3493         # the submission fails, so we'll notice that anyway.
3494         results.append([True, self.cl.SubmitJob(ops)[0]])
3495     else:
3496       results = self.cl.SubmitManyJobs([ops for (_, _, ops) in self.queue])
3497     for ((status, data), (idx, name, _)) in zip(results, self.queue):
3498       self.jobs.append((idx, status, data, name))
3499
3500   def _ChooseJob(self):
3501     """Choose a non-waiting/queued job to poll next.
3502
3503     """
3504     assert self.jobs, "_ChooseJob called with empty job list"
3505
3506     result = self.cl.QueryJobs([i[2] for i in self.jobs[:_CHOOSE_BATCH]],
3507                                ["status"])
3508     assert result
3509
3510     for job_data, status in zip(self.jobs, result):
3511       if (isinstance(status, list) and status and
3512           status[0] in (constants.JOB_STATUS_QUEUED,
3513                         constants.JOB_STATUS_WAITING,
3514                         constants.JOB_STATUS_CANCELING)):
3515         # job is still present and waiting
3516         continue
3517       # good candidate found (either running job or lost job)
3518       self.jobs.remove(job_data)
3519       return job_data
3520
3521     # no job found
3522     return self.jobs.pop(0)
3523
3524   def GetResults(self):
3525     """Wait for and return the results of all jobs.
3526
3527     @rtype: list
3528     @return: list of tuples (success, job results), in the same order
3529         as the submitted jobs; if a job has failed, instead of the result
3530         there will be the error message
3531
3532     """
3533     if not self.jobs:
3534       self.SubmitPending()
3535     results = []
3536     if self.verbose:
3537       ok_jobs = [row[2] for row in self.jobs if row[1]]
3538       if ok_jobs:
3539         ToStdout("Submitted jobs %s", utils.CommaJoin(ok_jobs))
3540
3541     # first, remove any non-submitted jobs
3542     self.jobs, failures = compat.partition(self.jobs, lambda x: x[1])
3543     for idx, _, jid, name in failures:
3544       ToStderr("Failed to submit job%s: %s", self._IfName(name, " for %s"), jid)
3545       results.append((idx, False, jid))
3546
3547     while self.jobs:
3548       (idx, _, jid, name) = self._ChooseJob()
3549       ToStdout("Waiting for job %s%s ...", jid, self._IfName(name, " for %s"))
3550       try:
3551         job_result = PollJob(jid, cl=self.cl, feedback_fn=self.feedback_fn)
3552         success = True
3553       except errors.JobLost, err:
3554         _, job_result = FormatError(err)
3555         ToStderr("Job %s%s has been archived, cannot check its result",
3556                  jid, self._IfName(name, " for %s"))
3557         success = False
3558       except (errors.GenericError, luxi.ProtocolError), err:
3559         _, job_result = FormatError(err)
3560         success = False
3561         # the error message will always be shown, verbose or not
3562         ToStderr("Job %s%s has failed: %s",
3563                  jid, self._IfName(name, " for %s"), job_result)
3564
3565       results.append((idx, success, job_result))
3566
3567     # sort based on the index, then drop it
3568     results.sort()
3569     results = [i[1:] for i in results]
3570
3571     return results
3572
3573   def WaitOrShow(self, wait):
3574     """Wait for job results or only print the job IDs.
3575
3576     @type wait: boolean
3577     @param wait: whether to wait or not
3578
3579     """
3580     if wait:
3581       return self.GetResults()
3582     else:
3583       if not self.jobs:
3584         self.SubmitPending()
3585       for _, status, result, name in self.jobs:
3586         if status:
3587           ToStdout("%s: %s", result, name)
3588         else:
3589           ToStderr("Failure for %s: %s", name, result)
3590       return [row[1:3] for row in self.jobs]
3591
3592
3593 def FormatParamsDictInfo(param_dict, actual):
3594   """Formats a parameter dictionary.
3595
3596   @type param_dict: dict
3597   @param param_dict: the own parameters
3598   @type actual: dict
3599   @param actual: the current parameter set (including defaults)
3600   @rtype: dict
3601   @return: dictionary where the value of each parameter is either a fully
3602       formatted string or a dictionary containing formatted strings
3603
3604   """
3605   ret = {}
3606   for (key, data) in actual.items():
3607     if isinstance(data, dict) and data:
3608       ret[key] = FormatParamsDictInfo(param_dict.get(key, {}), data)
3609     else:
3610       ret[key] = str(param_dict.get(key, "default (%s)" % data))
3611   return ret
3612
3613
3614 def _FormatListInfoDefault(data, def_data):
3615   if data is not None:
3616     ret = utils.CommaJoin(data)
3617   else:
3618     ret = "default (%s)" % utils.CommaJoin(def_data)
3619   return ret
3620
3621
3622 def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster):
3623   """Formats an instance policy.
3624
3625   @type custom_ipolicy: dict
3626   @param custom_ipolicy: own policy
3627   @type eff_ipolicy: dict
3628   @param eff_ipolicy: effective policy (including defaults); ignored for
3629       cluster
3630   @type iscluster: bool
3631   @param iscluster: the policy is at cluster level
3632   @rtype: list of pairs
3633   @return: formatted data, suitable for L{PrintGenericInfo}
3634
3635   """
3636   if iscluster:
3637     eff_ipolicy = custom_ipolicy
3638
3639   custom_minmax = custom_ipolicy.get(constants.ISPECS_MINMAX)
3640   ret = [
3641     (key,
3642      FormatParamsDictInfo(custom_minmax.get(key, {}),
3643                           eff_ipolicy[constants.ISPECS_MINMAX][key]))
3644     for key in constants.ISPECS_MINMAX_KEYS
3645     ]
3646   if iscluster:
3647     stdspecs = custom_ipolicy[constants.ISPECS_STD]
3648     ret.append(
3649       (constants.ISPECS_STD,
3650        FormatParamsDictInfo(stdspecs, stdspecs))
3651       )
3652
3653   ret.append(
3654     ("enabled disk templates",
3655      _FormatListInfoDefault(custom_ipolicy.get(constants.IPOLICY_DTS),
3656                             eff_ipolicy[constants.IPOLICY_DTS]))
3657     )
3658   ret.extend([
3659     (key, str(custom_ipolicy.get(key, "default (%s)" % eff_ipolicy[key])))
3660     for key in constants.IPOLICY_PARAMETERS
3661     ])
3662   return ret
3663
3664
3665 def ConfirmOperation(names, list_type, text, extra=""):
3666   """Ask the user to confirm an operation on a list of list_type.
3667
3668   This function is used to request confirmation for doing an operation
3669   on a given list of list_type.
3670
3671   @type names: list
3672   @param names: the list of names that we display when
3673       we ask for confirmation
3674   @type list_type: str
3675   @param list_type: Human readable name for elements in the list (e.g. nodes)
3676   @type text: str
3677   @param text: the operation that the user should confirm
3678   @rtype: boolean
3679   @return: True or False depending on user's confirmation.
3680
3681   """
3682   count = len(names)
3683   msg = ("The %s will operate on %d %s.\n%s"
3684          "Do you want to continue?" % (text, count, list_type, extra))
3685   affected = (("\nAffected %s:\n" % list_type) +
3686               "\n".join(["  %s" % name for name in names]))
3687
3688   choices = [("y", True, "Yes, execute the %s" % text),
3689              ("n", False, "No, abort the %s" % text)]
3690
3691   if count > 20:
3692     choices.insert(1, ("v", "v", "View the list of affected %s" % list_type))
3693     question = msg
3694   else:
3695     question = msg + affected
3696
3697   choice = AskUser(question, choices)
3698   if choice == "v":
3699     choices.pop(1)
3700     choice = AskUser(msg + affected, choices)
3701   return choice
3702
3703
3704 def _MaybeParseUnit(elements):
3705   """Parses and returns an array of potential values with units.
3706
3707   """
3708   parsed = {}
3709   for k, v in elements.items():
3710     if v == constants.VALUE_DEFAULT:
3711       parsed[k] = v
3712     else:
3713       parsed[k] = utils.ParseUnit(v)
3714   return parsed
3715
3716
3717 def _InitIspecsFromOpts(ipolicy, ispecs_mem_size, ispecs_cpu_count,
3718                         ispecs_disk_count, ispecs_disk_size, ispecs_nic_count,
3719                         group_ipolicy, allowed_values):
3720   try:
3721     if ispecs_mem_size:
3722       ispecs_mem_size = _MaybeParseUnit(ispecs_mem_size)
3723     if ispecs_disk_size:
3724       ispecs_disk_size = _MaybeParseUnit(ispecs_disk_size)
3725   except (TypeError, ValueError, errors.UnitParseError), err:
3726     raise errors.OpPrereqError("Invalid disk (%s) or memory (%s) size"
3727                                " in policy: %s" %
3728                                (ispecs_disk_size, ispecs_mem_size, err),
3729                                errors.ECODE_INVAL)
3730
3731   # prepare ipolicy dict
3732   ispecs_transposed = {
3733     constants.ISPEC_MEM_SIZE: ispecs_mem_size,
3734     constants.ISPEC_CPU_COUNT: ispecs_cpu_count,
3735     constants.ISPEC_DISK_COUNT: ispecs_disk_count,
3736     constants.ISPEC_DISK_SIZE: ispecs_disk_size,
3737     constants.ISPEC_NIC_COUNT: ispecs_nic_count,
3738     }
3739
3740   # first, check that the values given are correct
3741   if group_ipolicy:
3742     forced_type = TISPECS_GROUP_TYPES
3743   else:
3744     forced_type = TISPECS_CLUSTER_TYPES
3745   for specs in ispecs_transposed.values():
3746     utils.ForceDictType(specs, forced_type, allowed_values=allowed_values)
3747
3748   # then transpose
3749   ispecs = {
3750     constants.ISPECS_MIN: {},
3751     constants.ISPECS_MAX: {},
3752     constants.ISPECS_STD: {},
3753     }
3754   for (name, specs) in ispecs_transposed.iteritems():
3755     assert name in constants.ISPECS_PARAMETERS
3756     for key, val in specs.items(): # {min: .. ,max: .., std: ..}
3757       assert key in ispecs
3758       ispecs[key][name] = val
3759   for key in constants.ISPECS_MINMAX_KEYS:
3760     ipolicy[constants.ISPECS_MINMAX][key] = ispecs[key]
3761   ipolicy[constants.ISPECS_STD] = ispecs[constants.ISPECS_STD]
3762
3763
3764 def CreateIPolicyFromOpts(ispecs_mem_size=None,
3765                           ispecs_cpu_count=None,
3766                           ispecs_disk_count=None,
3767                           ispecs_disk_size=None,
3768                           ispecs_nic_count=None,
3769                           ipolicy_disk_templates=None,
3770                           ipolicy_vcpu_ratio=None,
3771                           ipolicy_spindle_ratio=None,
3772                           group_ipolicy=False,
3773                           allowed_values=None,
3774                           fill_all=False):
3775   """Creation of instance policy based on command line options.
3776
3777   @param fill_all: whether for cluster policies we should ensure that
3778     all values are filled
3779
3780
3781   """
3782
3783   ipolicy_out = objects.MakeEmptyIPolicy()
3784   _InitIspecsFromOpts(ipolicy_out, ispecs_mem_size, ispecs_cpu_count,
3785                       ispecs_disk_count, ispecs_disk_size, ispecs_nic_count,
3786                       group_ipolicy, allowed_values)
3787
3788   if ipolicy_disk_templates is not None:
3789     ipolicy_out[constants.IPOLICY_DTS] = list(ipolicy_disk_templates)
3790   if ipolicy_vcpu_ratio is not None:
3791     ipolicy_out[constants.IPOLICY_VCPU_RATIO] = ipolicy_vcpu_ratio
3792   if ipolicy_spindle_ratio is not None:
3793     ipolicy_out[constants.IPOLICY_SPINDLE_RATIO] = ipolicy_spindle_ratio
3794
3795   assert not (frozenset(ipolicy_out.keys()) - constants.IPOLICY_ALL_KEYS)
3796
3797   if not group_ipolicy and fill_all:
3798     ipolicy_out = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, ipolicy_out)
3799
3800   return ipolicy_out
3801
3802
3803 def _SerializeGenericInfo(buf, data, level, afterkey=False):
3804   """Formatting core of L{PrintGenericInfo}.
3805
3806   @param buf: (string) stream to accumulate the result into
3807   @param data: data to format
3808   @type level: int
3809   @param level: depth in the data hierarchy, used for indenting
3810   @type afterkey: bool
3811   @param afterkey: True when we are in the middle of a line after a key (used
3812       to properly add newlines or indentation)
3813
3814   """
3815   baseind = "  "
3816   if isinstance(data, dict):
3817     if not data:
3818       buf.write("\n")
3819     else:
3820       if afterkey:
3821         buf.write("\n")
3822         doindent = True
3823       else:
3824         doindent = False
3825       for key in sorted(data):
3826         if doindent:
3827           buf.write(baseind * level)
3828         else:
3829           doindent = True
3830         buf.write(key)
3831         buf.write(": ")
3832         _SerializeGenericInfo(buf, data[key], level + 1, afterkey=True)
3833   elif isinstance(data, list) and len(data) > 0 and isinstance(data[0], tuple):
3834     # list of tuples (an ordered dictionary)
3835     if afterkey:
3836       buf.write("\n")
3837       doindent = True
3838     else:
3839       doindent = False
3840     for (key, val) in data:
3841       if doindent:
3842         buf.write(baseind * level)
3843       else:
3844         doindent = True
3845       buf.write(key)
3846       buf.write(": ")
3847       _SerializeGenericInfo(buf, val, level + 1, afterkey=True)
3848   elif isinstance(data, list):
3849     if not data:
3850       buf.write("\n")
3851     else:
3852       if afterkey:
3853         buf.write("\n")
3854         doindent = True
3855       else:
3856         doindent = False
3857       for item in data:
3858         if doindent:
3859           buf.write(baseind * level)
3860         else:
3861           doindent = True
3862         buf.write("-")
3863         buf.write(baseind[1:])
3864         _SerializeGenericInfo(buf, item, level + 1)
3865   else:
3866     # This branch should be only taken for strings, but it's practically
3867     # impossible to guarantee that no other types are produced somewhere
3868     buf.write(str(data))
3869     buf.write("\n")
3870
3871
3872 def PrintGenericInfo(data):
3873   """Print information formatted according to the hierarchy.
3874
3875   The output is a valid YAML string.
3876
3877   @param data: the data to print. It's a hierarchical structure whose elements
3878       can be:
3879         - dictionaries, where keys are strings and values are of any of the
3880           types listed here
3881         - lists of pairs (key, value), where key is a string and value is of
3882           any of the types listed here; it's a way to encode ordered
3883           dictionaries
3884         - lists of any of the types listed here
3885         - strings
3886
3887   """
3888   buf = StringIO()
3889   _SerializeGenericInfo(buf, data, 0)
3890   ToStdout(buf.getvalue().rstrip("\n"))