diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 7f046ec1e1ba9a9b8e07628d6ff13d27225070b6..c1a52ffeb06f0f1cbbd16703317dc219d007059f 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -500,6 +500,21 @@ def _CheckBooleanOpField(op, name): setattr(op, name, val) +def _CheckGlobalHvParams(params): + """Validates that given hypervisor params are not global ones. + + This will ensure that instances don't get customised versions of + global params. + + """ + used_globals = constants.HVC_GLOBALS.intersection(params) + if used_globals: + msg = ("The following hypervisor parameters are global and cannot" + " be customized at instance level, please modify them at" + " cluster level: %s" % ", ".join(used_globals)) + raise errors.OpPrereqError(msg, errors.ECODE_INVAL) + + def _CheckNodeOnline(lu, node): """Ensure that a given node is online. @@ -4200,7 +4215,8 @@ class LUQueryInstances(NoHooksLU): "hvparams", ] + _SIMPLE_FIELDS + ["hv/%s" % name - for name in constants.HVS_PARAMETERS] + + for name in constants.HVS_PARAMETERS + if name not in constants.HVC_GLOBALS] + ["be/%s" % name for name in constants.BES_PARAMETERS]) _FIELDS_DYNAMIC = utils.FieldSet("oper_state", "oper_ram", "status") @@ -4295,7 +4311,7 @@ class LUQueryInstances(NoHooksLU): cluster = self.cfg.GetClusterInfo() for instance in instance_list: iout = [] - i_hv = cluster.FillHV(instance) + i_hv = cluster.FillHV(instance, skip_globals=True) i_be = cluster.FillBE(instance) i_nicp = [objects.FillDict(cluster.nicparams[constants.PP_DEFAULT], nic.nicparams) for nic in instance.nics] @@ -4382,7 +4398,8 @@ class LUQueryInstances(NoHooksLU): elif field == "hvparams": val = i_hv elif (field.startswith(HVPREFIX) and - field[len(HVPREFIX):] in constants.HVS_PARAMETERS): + field[len(HVPREFIX):] in constants.HVS_PARAMETERS and + field[len(HVPREFIX):] not in constants.HVC_GLOBALS): val = i_hv.get(field[len(HVPREFIX):], None) elif field == "beparams": val = i_be @@ -5599,6 +5616,8 @@ class LUCreateInstance(LogicalUnit): hv_type = hypervisor.GetHypervisor(self.op.hypervisor) hv_type.CheckParameterSyntax(filled_hvp) self.hv_full = filled_hvp + # check that we don't specify global parameters on an instance + _CheckGlobalHvParams(self.op.hvparams) # fill and remember the beparams dict utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES) @@ -7292,7 +7311,7 @@ class LUQueryInstanceData(NoHooksLU): "hypervisor": instance.hypervisor, "network_port": instance.network_port, "hv_instance": instance.hvparams, - "hv_actual": cluster.FillHV(instance), + "hv_actual": cluster.FillHV(instance, skip_globals=True), "be_instance": instance.beparams, "be_actual": cluster.FillBE(instance), "serial_no": instance.serial_no, @@ -7329,6 +7348,9 @@ class LUSetInstanceParams(LogicalUnit): self.op.hvparams or self.op.beparams): raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL) + if self.op.hvparams: + _CheckGlobalHvParams(self.op.hvparams) + # Disk validation disk_addremove = 0 for disk_op, disk_dict in self.op.disks: diff --git a/lib/constants.py b/lib/constants.py index bab6d8d11a59d4ab7628befa58f07ba398d393f4..8a47a9ddae840b94e20351e0857d5e52a702d2ca 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -650,6 +650,10 @@ HVC_DEFAULTS = { }, } +HVC_GLOBALS = frozenset([ + HV_MIGRATION_PORT, + ]) + BEC_DEFAULTS = { BE_MEMORY: 128, BE_VCPUS: 1, diff --git a/lib/objects.py b/lib/objects.py index e87faddafca8fe82e75b8d5b253735152205d17a..06bc7e0298e41451b7682eb43cfd952afb6ff76b 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -42,19 +42,26 @@ __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance", _TIMESTAMPS = ["ctime", "mtime"] _UUID = ["uuid"] -def FillDict(defaults_dict, custom_dict): +def FillDict(defaults_dict, custom_dict, skip_keys=[]): """Basic function to apply settings on top a default dict. @type defaults_dict: dict @param defaults_dict: dictionary holding the default values @type custom_dict: dict @param custom_dict: dictionary holding customized value + @type skip_keys: list + @param skip_keys: which keys not to fill @rtype: dict @return: dict with the 'full' values """ ret_dict = copy.deepcopy(defaults_dict) ret_dict.update(custom_dict) + for k in skip_keys: + try: + del ret_dict[k] + except KeyError: + pass return ret_dict @@ -777,6 +784,12 @@ class Instance(TaggableObject): nic.UpgradeConfig() for disk in self.disks: disk.UpgradeConfig() + if self.hvparams: + for key in constants.HVC_GLOBALS: + try: + del self.hvparams[key] + except KeyError: + pass class OS(ConfigObject): @@ -887,18 +900,25 @@ class Cluster(TaggableObject): obj.tcpudp_port_pool = set(obj.tcpudp_port_pool) return obj - def FillHV(self, instance): + def FillHV(self, instance, skip_globals=False): """Fill an instance's hvparams dict. @type instance: L{objects.Instance} @param instance: the instance parameter to fill + @type skip_globals: boolean + @param skip_globals: if True, the global hypervisor parameters will + not be filled @rtype: dict @return: a copy of the instance's hvparams with missing keys filled from the cluster defaults """ + if skip_globals: + skip_keys = constants.HVC_GLOBALS + else: + skip_keys = [] return FillDict(self.hvparams.get(instance.hypervisor, {}), - instance.hvparams) + instance.hvparams, skip_keys=skip_keys) def FillBE(self, instance): """Fill an instance's beparams dict. @@ -911,7 +931,7 @@ class Cluster(TaggableObject): """ return FillDict(self.beparams.get(constants.PP_DEFAULT, {}), - instance.beparams) + instance.beparams) class BlockDevStatus(ConfigObject):