"GLOBAL_FILEDIR_OPT",
"HID_OS_OPT",
"GLOBAL_SHARED_FILEDIR_OPT",
+ "HOTPLUG_OPT",
"HVLIST_OPT",
"HVOPTS_OPT",
"HYPERVISOR_OPT",
"MASTER_NETMASK_OPT",
"MC_OPT",
"MIGRATION_MODE_OPT",
+ "MODIFY_ETCHOSTS_OPT",
"NET_OPT",
"NETWORK_OPT",
"NETWORK6_OPT",
"SPECS_DISK_SIZE_OPT",
"SPECS_MEM_SIZE_OPT",
"SPECS_NIC_COUNT_OPT",
+ "SPLIT_ISPECS_OPTS",
"IPOLICY_STD_SPECS_OPT",
"IPOLICY_DISK_TEMPLATES",
"IPOLICY_VCPU_RATIO",
"OPT_COMPL_ONE_OS",
"OPT_COMPL_ONE_EXTSTORAGE",
"cli_option",
+ "FixHvParams",
"SplitNodeOption",
"CalculateOSNames",
"ParseFields",
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
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,
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)
default=False, action="store_true",
help="Include default values")
+HOTPLUG_OPT = cli_option("--hotplug", dest="hotplug",
+ action="store_true", default=False,
+ help="Hotplug supported devices (NICs and Disks)")
+
#: Options provided by all commands
COMMON_OPTS = [DEBUG_OPT, REASON_OPT]
# common instance policy options
INSTANCE_POLICY_OPTS = [
+ IPOLICY_BOUNDS_SPECS_OPT,
+ IPOLICY_DISK_TEMPLATES,
+ IPOLICY_VCPU_RATIO,
+ IPOLICY_SPINDLE_RATIO,
+ ]
+
+# instance policy split specs options
+SPLIT_ISPECS_OPTS = [
SPECS_CPU_COUNT_OPT,
SPECS_DISK_COUNT_OPT,
SPECS_DISK_SIZE_OPT,
SPECS_MEM_SIZE_OPT,
SPECS_NIC_COUNT_OPT,
- IPOLICY_BOUNDS_SPECS_OPT,
- IPOLICY_DISK_TEMPLATES,
- IPOLICY_VCPU_RATIO,
- IPOLICY_SPINDLE_RATIO,
]
return nics
+def FixHvParams(hvparams):
+ # In Ganeti 2.8.4 the separator for the usb_devices hvparam was changed from
+ # comma to space because commas cannot be accepted on the command line
+ # (they already act as the separator between different hvparams). Still,
+ # RAPI should be able to accept commas for backwards compatibility.
+ # Therefore, we convert spaces into commas here, and we keep the old
+ # parsing logic everywhere else.
+ try:
+ new_usb_devices = hvparams[constants.HV_USB_DEVICES].replace(" ", ",")
+ hvparams[constants.HV_USB_DEVICES] = new_usb_devices
+ except KeyError:
+ #No usb_devices, no modification required
+ pass
+
+
def GenericInstanceCreate(mode, opts, args):
"""Add an instance to the cluster via either creation or import.
utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_COMPAT)
utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
+ FixHvParams(hvparams)
if mode == constants.INSTANCE_CREATE:
start = opts.start
if iscluster:
eff_ipolicy = custom_ipolicy
+ minmax_out = []
custom_minmax = custom_ipolicy.get(constants.ISPECS_MINMAX)
- ret = [
- (key,
- FormatParamsDictInfo(custom_minmax.get(key, {}),
- eff_ipolicy[constants.ISPECS_MINMAX][key]))
- for key in constants.ISPECS_MINMAX_KEYS
- ]
+ if custom_minmax:
+ for (k, minmax) in enumerate(custom_minmax):
+ minmax_out.append([
+ ("%s/%s" % (key, k),
+ FormatParamsDictInfo(minmax[key], minmax[key]))
+ for key in constants.ISPECS_MINMAX_KEYS
+ ])
+ else:
+ for (k, minmax) in enumerate(eff_ipolicy[constants.ISPECS_MINMAX]):
+ minmax_out.append([
+ ("%s/%s" % (key, k),
+ FormatParamsDictInfo({}, minmax[key]))
+ for key in constants.ISPECS_MINMAX_KEYS
+ ])
+ ret = [("bounds specs", minmax_out)]
+
if iscluster:
stdspecs = custom_ipolicy[constants.ISPECS_STD]
ret.append(
)
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:
+ 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 _InitISpecsFromSplitOpts(ipolicy, ispecs_mem_size, ispecs_cpu_count,
ispecs_disk_count, ispecs_disk_size,
- ispecs_nic_count, group_ipolicy, allowed_values):
+ ispecs_nic_count, group_ipolicy, fill_all):
try:
if ispecs_mem_size:
ispecs_mem_size = _MaybeParseUnit(ispecs_mem_size)
forced_type = TISPECS_CLUSTER_TYPES
for specs in ispecs_transposed.values():
assert type(specs) is dict
- utils.ForceDictType(specs, forced_type, allowed_values=allowed_values)
+ utils.ForceDictType(specs, forced_type)
# then transpose
ispecs = {
for key, val in specs.items(): # {min: .. ,max: .., std: ..}
assert key in ispecs
ispecs[key][name] = val
+ minmax_out = {}
for key in constants.ISPECS_MINMAX_KEYS:
- ipolicy[constants.ISPECS_MINMAX][key] = ispecs[key]
- ipolicy[constants.ISPECS_STD] = ispecs[constants.ISPECS_STD]
+ if fill_all:
+ minmax_out[key] = \
+ objects.FillDict(constants.ISPECS_MINMAX_DEFAULTS[key], ispecs[key])
+ else:
+ minmax_out[key] = ispecs[key]
+ ipolicy[constants.ISPECS_MINMAX] = [minmax_out]
+ if fill_all:
+ ipolicy[constants.ISPECS_STD] = \
+ objects.FillDict(constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
+ ispecs[constants.ISPECS_STD])
+ else:
+ ipolicy[constants.ISPECS_STD] = ispecs[constants.ISPECS_STD]
def _ParseSpecUnit(spec, keyname):
ret = spec.copy()
for k in [constants.ISPEC_DISK_SIZE, constants.ISPEC_MEM_SIZE]:
- if k in ret and ret[k] != constants.VALUE_DEFAULT:
+ if k in ret:
try:
ret[k] = utils.ParseUnit(ret[k])
except (TypeError, ValueError, errors.UnitParseError), err:
return ret
-def _ParseISpec(spec, keyname, allowed_values):
+def _ParseISpec(spec, keyname, required):
ret = _ParseSpecUnit(spec, keyname)
- utils.ForceDictType(ret, constants.ISPECS_PARAMETER_TYPES,
- allowed_values=allowed_values)
+ utils.ForceDictType(ret, constants.ISPECS_PARAMETER_TYPES)
+ missing = constants.ISPECS_PARAMETERS - frozenset(ret.keys())
+ if required and missing:
+ raise errors.OpPrereqError("Missing parameters in ipolicy spec %s: %s" %
+ (keyname, utils.CommaJoin(missing)),
+ errors.ECODE_INVAL)
+ return ret
+
+
+def _GetISpecsInAllowedValues(minmax_ispecs, allowed_values):
+ ret = None
+ 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
return ret
def _InitISpecsFromFullOpts(ipolicy_out, minmax_ispecs, std_ispecs,
group_ipolicy, allowed_values):
- if 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, allowed_values)
+ found_allowed = _GetISpecsInAllowedValues(minmax_ispecs, allowed_values)
+ if found_allowed is not None:
+ ipolicy_out[constants.ISPECS_MINMAX] = found_allowed
+ elif minmax_ispecs is not None:
+ 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",
- allowed_values)
+ ipolicy_out[constants.ISPECS_STD] = _ParseISpec(std_ispecs, "std", False)
def CreateIPolicyFromOpts(ispecs_mem_size=None,
@param fill_all: whether for cluster policies we should ensure that
all values are filled
-
"""
- if ((ispecs_mem_size or ispecs_cpu_count or ispecs_disk_count or
- ispecs_disk_size or ispecs_nic_count) and
- (minmax_ispecs is not None or std_ispecs is not None)):
+ assert not (fill_all and allowed_values)
+
+ split_specs = (ispecs_mem_size or ispecs_cpu_count or ispecs_disk_count or
+ ispecs_disk_size or ispecs_nic_count)
+ if (split_specs and (minmax_ispecs is not None or std_ispecs is not None)):
raise errors.OpPrereqError("A --specs-xxx option cannot be specified"
" together with any --ipolicy-xxx-specs option",
errors.ECODE_INVAL)
ipolicy_out = objects.MakeEmptyIPolicy()
- if minmax_ispecs is None and std_ispecs is None:
+ if split_specs:
+ assert fill_all
_InitISpecsFromSplitOpts(ipolicy_out, ispecs_mem_size, ispecs_cpu_count,
ispecs_disk_count, ispecs_disk_size,
- ispecs_nic_count, group_ipolicy, allowed_values)
- else:
+ ispecs_nic_count, group_ipolicy, fill_all)
+ elif (minmax_ispecs is not None or std_ispecs is not None):
_InitISpecsFromFullOpts(ipolicy_out, minmax_ispecs, std_ispecs,
group_ipolicy, allowed_values)