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