"MASTER_NETMASK_OPT",
"MC_OPT",
"MIGRATION_MODE_OPT",
+ "MODIFY_ETCHOSTS_OPT",
"NET_OPT",
"NETWORK_OPT",
"NETWORK6_OPT",
"PREALLOC_WIPE_DISKS_OPT",
"PRIMARY_IP_VERSION_OPT",
"PRIMARY_ONLY_OPT",
+ "PRINT_JOBID_OPT",
"PRIORITY_OPT",
"RAPI_CERT_OPT",
"READD_OPT",
"SRC_DIR_OPT",
"SRC_NODE_OPT",
"SUBMIT_OPT",
+ "SUBMIT_OPTS",
"STARTUP_PAUSED_OPT",
"STATIC_OPT",
"SYNC_OPT",
return retval
-def check_list_ident_key_val(_, opt, value):
- """Custom parser for "ident:key=val,key=val/ident:key=val" options.
+def check_multilist_ident_key_val(_, opt, value):
+ """Custom parser for "ident:key=val,key=val/ident:key=val//ident:.." options.
@rtype: list of dictionary
- @return: {ident: {key: val, key: val}, ident: {key: val}}
+ @return: [{ident: {key: val, key: val}, ident: {key: val}}, {ident:..}]
"""
- return _SplitListKeyVal(opt, value)
+ retval = []
+ for line in value.split("//"):
+ retval.append(_SplitListKeyVal(opt, line))
+ return retval
def check_bool(option, opt, value): # pylint: disable=W0613
"completion_suggest",
]
TYPES = Option.TYPES + (
- "listidentkeyval",
+ "multilistidentkeyval",
"identkeyval",
"keyval",
"unit",
"maybefloat",
)
TYPE_CHECKER = Option.TYPE_CHECKER.copy()
- TYPE_CHECKER["listidentkeyval"] = check_list_ident_key_val
+ TYPE_CHECKER["multilistidentkeyval"] = check_multilist_ident_key_val
TYPE_CHECKER["identkeyval"] = check_ident_key_val
TYPE_CHECKER["keyval"] = check_key_val
TYPE_CHECKER["unit"] = check_unit
help=("Submit the job and return the job ID, but"
" don't wait for the job to finish"))
+PRINT_JOBID_OPT = cli_option("--print-jobid", dest="print_jobid",
+ default=False, action="store_true",
+ help=("Additionally print the job as first line"
+ " on stdout (for scripting)."))
+
SYNC_OPT = cli_option("--sync", dest="do_locking",
default=False, action="store_true",
help=("Grab locks while doing the queries"
FILESTORE_DRIVER_OPT = cli_option("--file-driver", dest="file_driver",
help="Driver to use for image files",
- default="loop", metavar="<DRIVER>",
+ default=None, metavar="<DRIVER>",
choices=list(constants.FILE_DRIVER))
IALLOCATOR_OPT = cli_option("-I", "--iallocator", metavar="<NAME>",
IPOLICY_BOUNDS_SPECS_STR = "--ipolicy-bounds-specs"
IPOLICY_BOUNDS_SPECS_OPT = cli_option(IPOLICY_BOUNDS_SPECS_STR,
dest="ipolicy_bounds_specs",
- type="listidentkeyval", default=None,
+ type="multilistidentkeyval", default=None,
help="Complete instance specs limits")
IPOLICY_STD_SPECS_STR = "--ipolicy-std-specs"
CLEANUP_OPT = cli_option("--cleanup", dest="cleanup",
default=False, action="store_true",
- help="Instead of performing the migration, try to"
- " recover from a failed cleanup. This is safe"
+ help="Instead of performing the migration/failover,"
+ " try to recover from a failed cleanup. This is safe"
" to run even if the instance is healthy, but it"
" will create extra replication traffic and "
" disrupt briefly the replication (like during the"
- " migration")
+ " migration/failover")
STATIC_OPT = cli_option("-s", "--static", dest="static",
action="store_true", default=False,
"wide) for storing the file-based disks [%s]" %
pathutils.DEFAULT_FILE_STORAGE_DIR,
metavar="DIR",
- default=pathutils.DEFAULT_FILE_STORAGE_DIR)
+ default=None)
GLOBAL_SHARED_FILEDIR_OPT = cli_option(
"--shared-file-storage-dir",
help="Specify the default directory (cluster-wide) for storing the"
" shared file-based disks [%s]" %
pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
- metavar="SHAREDDIR", default=pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR)
+ metavar="SHAREDDIR", default=None)
NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts",
help="Don't modify %s" % pathutils.ETC_HOSTS,
action="store_false", default=True)
+MODIFY_ETCHOSTS_OPT = \
+ cli_option("--modify-etc-hosts", dest="modify_etc_hosts", metavar=_YORNO,
+ default=None, type="bool",
+ help="Defines whether the cluster should autonomously modify"
+ " and keep in sync the /etc/hosts file of the nodes")
+
NOMODIFY_SSH_SETUP_OPT = cli_option("--no-ssh-init", dest="modify_ssh_setup",
help="Don't initialize SSH keys",
action="store_false", default=True)
#: Options provided by all commands
COMMON_OPTS = [DEBUG_OPT, REASON_OPT]
+# options related to asynchronous job handling
+
+SUBMIT_OPTS = [
+ SUBMIT_OPT,
+ PRINT_JOBID_OPT,
+ ]
+
# common options for creating instances. add and import then add their own
# specific ones.
COMMON_CREATE_OPTS = [
OSPARAMS_OPT,
OS_SIZE_OPT,
SUBMIT_OPT,
+ PRINT_JOBID_OPT,
TAG_ADD_OPT,
DRY_RUN_OPT,
PRIORITY_OPT,
SetGenericOpcodeOpts([op], opts)
job_id = SendJob([op], cl=cl)
+ if hasattr(opts, "print_jobid") and opts.print_jobid:
+ ToStdout("%d" % job_id)
op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn,
reporter=reporter)
job = [op]
SetGenericOpcodeOpts(job, opts)
job_id = SendJob(job, cl=cl)
+ if opts.print_jobid:
+ ToStdout("%d" % job_id)
raise JobSubmittedException(job_id)
else:
return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn, opts=opts)
raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
(didx, err), errors.ECODE_INVAL)
elif constants.IDISK_ADOPT in ddict:
+ if constants.IDISK_SPINDLES in ddict:
+ raise errors.OpPrereqError("spindles is not a valid option when"
+ " adopting a disk", errors.ECODE_INVAL)
if mode == constants.INSTANCE_IMPORT:
raise errors.OpPrereqError("Disk adoption not allowed for instance"
" import", errors.ECODE_INVAL)
)
ret.append(
- ("enabled disk templates",
+ ("allowed disk templates",
_FormatListInfoDefault(custom_ipolicy.get(constants.IPOLICY_DTS),
eff_ipolicy[constants.IPOLICY_DTS]))
)
if stdspecs:
buf.write(" %s " % IPOLICY_STD_SPECS_STR)
_PrintSpecsParameters(buf, stdspecs)
- minmax = ipolicy.get("minmax")
- if minmax:
- minspecs = minmax[0].get("min")
- maxspecs = minmax[0].get("max")
+ minmaxes = ipolicy.get("minmax", [])
+ first = True
+ for minmax in minmaxes:
+ minspecs = minmax.get("min")
+ maxspecs = minmax.get("max")
if minspecs and maxspecs:
- buf.write(" %s " % IPOLICY_BOUNDS_SPECS_STR)
+ if first:
+ buf.write(" %s " % IPOLICY_BOUNDS_SPECS_STR)
+ first = False
+ else:
+ buf.write("//")
buf.write("min:")
_PrintSpecsParameters(buf, minspecs)
buf.write("/max:")
def _GetISpecsInAllowedValues(minmax_ispecs, allowed_values):
ret = None
- if minmax_ispecs and allowed_values and len(minmax_ispecs) == 1:
- for (key, spec) in minmax_ispecs.items():
+ if (minmax_ispecs and allowed_values and len(minmax_ispecs) == 1 and
+ len(minmax_ispecs[0]) == 1):
+ for (key, spec) in minmax_ispecs[0].items():
# This loop is executed exactly once
if key in allowed_values and not spec:
ret = key
if found_allowed is not None:
ipolicy_out[constants.ISPECS_MINMAX] = found_allowed
elif minmax_ispecs is not None:
- minmax_out = {}
- for (key, spec) in minmax_ispecs.items():
- if key not in constants.ISPECS_MINMAX_KEYS:
- msg = "Invalid key in bounds instance specifications: %s" % key
- raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
- minmax_out[key] = _ParseISpec(spec, key, True)
- ipolicy_out[constants.ISPECS_MINMAX] = [minmax_out]
+ minmax_out = []
+ for mmpair in minmax_ispecs:
+ mmpair_out = {}
+ for (key, spec) in mmpair.items():
+ if key not in constants.ISPECS_MINMAX_KEYS:
+ msg = "Invalid key in bounds instance specifications: %s" % key
+ raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
+ mmpair_out[key] = _ParseISpec(spec, key, True)
+ minmax_out.append(mmpair_out)
+ ipolicy_out[constants.ISPECS_MINMAX] = minmax_out
if std_ispecs is not None:
assert not group_ipolicy # This is not an option for gnt-group
ipolicy_out[constants.ISPECS_STD] = _ParseISpec(std_ispecs, "std", False)