diff --git a/lib/bootstrap.py b/lib/bootstrap.py index 3e42eaee44a8599854a5f78751261ac0f616de23..66c166c9f11718e3168f945536ec770d88237639 100644 --- a/lib/bootstrap.py +++ b/lib/bootstrap.py @@ -421,8 +421,7 @@ def InitCluster(cluster_name, mac_prefix, # pylint: disable=R0913, R0914 utils.ForceDictType(val, constants.ISPECS_PARAMETER_TYPES) objects.NIC.CheckParameterSyntax(nicparams) - full_ipolicy = objects.FillDictOfDicts(constants.IPOLICY_DEFAULTS, - ipolicy) + full_ipolicy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, ipolicy) objects.InstancePolicy.CheckParameterSyntax(full_ipolicy) if ndparams is not None: diff --git a/lib/client/gnt_cluster.py b/lib/client/gnt_cluster.py index d1ecbd538adeb0b8e9776ead59555c8f50b36e6d..6cbd4da86cbeab00cf25cb12714bf908dbcfc647 100644 --- a/lib/client/gnt_cluster.py +++ b/lib/client/gnt_cluster.py @@ -139,13 +139,16 @@ def InitCluster(opts, args): utils.ForceDictType(diskparams[templ], constants.DISK_DT_TYPES) # prepare ipolicy dict + ispecs_dts = opts.ispecs_disk_templates # hate long var names ipolicy_raw = \ objects.CreateIPolicyFromOpts(ispecs_mem_size=opts.ispecs_mem_size, ispecs_cpu_count=opts.ispecs_cpu_count, ispecs_disk_count=opts.ispecs_disk_count, ispecs_disk_size=opts.ispecs_disk_size, - ispecs_nic_count=opts.ispecs_nic_count) - ipolicy = objects.FillDictOfDicts(constants.IPOLICY_DEFAULTS, ipolicy_raw) + ispecs_nic_count=opts.ispecs_nic_count, + ispecs_disk_templates=ispecs_dts, + fill_all=True) + ipolicy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, ipolicy_raw) for value in ipolicy.values(): utils.ForceDictType(value, constants.ISPECS_PARAMETER_TYPES) @@ -461,6 +464,8 @@ def ShowClusterConfig(opts, args): for key in constants.IPOLICY_PARAMETERS: ToStdout(" - %s", key) _PrintGroupedParams(result["ipolicy"][key], roman=opts.roman_integers) + ToStdout(" - enabled disk templates: %s", + utils.CommaJoin(result["ipolicy"][constants.ISPECS_DTS])) return 0 @@ -984,12 +989,14 @@ def SetClusterParams(opts, args): if ndparams is not None: utils.ForceDictType(ndparams, constants.NDS_PARAMETER_TYPES) + ispecs_dts = opts.ispecs_disk_templates ipolicy = \ objects.CreateIPolicyFromOpts(ispecs_mem_size=opts.ispecs_mem_size, ispecs_cpu_count=opts.ispecs_cpu_count, ispecs_disk_count=opts.ispecs_disk_count, ispecs_disk_size=opts.ispecs_disk_size, - ispecs_nic_count=opts.ispecs_nic_count) + ispecs_nic_count=opts.ispecs_nic_count, + ispecs_disk_templates=ispecs_dts) mnh = opts.maintain_node_health diff --git a/lib/client/gnt_group.py b/lib/client/gnt_group.py index f39c6e5ef07e0a94a17a266b0bfb35aa0a0f8232..b8d823646a151dc27e3b1afbd27ae1039986d5f9 100644 --- a/lib/client/gnt_group.py +++ b/lib/client/gnt_group.py @@ -192,6 +192,7 @@ def SetGroupParams(opts, args): ispecs_disk_count=opts.ispecs_disk_count, ispecs_disk_size=opts.ispecs_disk_size, ispecs_nic_count=opts.ispecs_nic_count, + ispecs_disk_templates=opts.ispecs_disk_templates, group_ipolicy=True, allowed_values=[constants.VALUE_DEFAULT]) diff --git a/lib/cmdlib.py b/lib/cmdlib.py index b66bfbabe74b9dcd8b472e905d2e4e3ec5988fee..1b612f733c007a6fa19879d592bb9f829a29150e 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -721,6 +721,42 @@ def _GetUpdatedParams(old_params, update_dict, return params_copy +def _GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False): + """Return the new version of a instance policy. + + @param group_policy: whether this policy applies to a group and thus + we should support removal of policy entries + + """ + use_none = use_default = group_policy + ipolicy = copy.deepcopy(old_ipolicy) + for key, value in new_ipolicy.items(): + if key in constants.IPOLICY_PARAMETERS: + utils.ForceDictType(value, constants.ISPECS_PARAMETER_TYPES) + ipolicy[key] = _GetUpdatedParams(old_ipolicy.get(key, {}), value, + use_none=use_none, + use_default=use_default) + else: + # FIXME: we assume all others are lists; this should be redone + # in a nicer way + if not value or value == [constants.VALUE_DEFAULT]: + if group_policy: + del ipolicy[key] + else: + raise errors.OpPrereqError("Can't unset ipolicy attribute '%s'" + " on the cluster'" % key, + errors.ECODE_INVAL) + else: + logging.info("Setting %s to %s", key, value) + ipolicy[key] = list(value) + try: + objects.InstancePolicy.CheckParameterSyntax(ipolicy) + except errors.ConfigurationError, err: + raise errors.OpPrereqError("Invalid instance policy: %s" % err, + errors.ECODE_INVAL) + return ipolicy + + def _UpdateAndVerifySubDict(base, updates, type_check): """Updates and verifies a dict with sub dicts of the same type. @@ -3830,17 +3866,8 @@ class LUClusterSetParams(LogicalUnit): for storage, svalues in new_disk_state.items()) if self.op.ipolicy: - ipolicy = {} - for key, value in self.op.ipolicy.items(): - utils.ForceDictType(value, constants.ISPECS_PARAMETER_TYPES) - ipolicy[key] = _GetUpdatedParams(cluster.ipolicy.get(key, {}), - value) - try: - objects.InstancePolicy.CheckParameterSyntax(ipolicy) - except errors.ConfigurationError, err: - raise errors.OpPrereqError("Invalid instance policy: %s" % err, - errors.ECODE_INVAL) - self.new_ipolicy = ipolicy + self.new_ipolicy = _GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy, + group_policy=False) if self.op.nicparams: utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES) @@ -13306,18 +13333,9 @@ class LUGroupSetParams(LogicalUnit): self.group.disk_state_static) if self.op.ipolicy: - g_ipolicy = {} - for key, value in self.op.ipolicy.iteritems(): - g_ipolicy[key] = _GetUpdatedParams(self.group.ipolicy.get(key, {}), - value, - use_none=True) - utils.ForceDictType(g_ipolicy[key], constants.ISPECS_PARAMETER_TYPES) - self.new_ipolicy = g_ipolicy - try: - objects.InstancePolicy.CheckParameterSyntax(self.new_ipolicy) - except errors.ConfigurationError, err: - raise errors.OpPrereqError("Invalid instance policy: %s" % err, - errors.ECODE_INVAL) + self.new_ipolicy = _GetUpdatedIPolicy(self.group.ipolicy, + self.op.ipolicy, + group_policy=True) def BuildHooksEnv(self): """Build hooks env. diff --git a/lib/config.py b/lib/config.py index 03a3d5aeb0fbf59db40582ad6c464163270c3a99..7654ba1a70adfd43f12cb6b97ebe9efc9523f240 100644 --- a/lib/config.py +++ b/lib/config.py @@ -433,9 +433,16 @@ class ConfigWriter: result.append("%s has invalid instance policy: %s" % (owner, err)) def _helper_ispecs(owner, params): - for key, value in params.iteritems(): - fullkey = "ipolicy/" + key - _helper(owner, fullkey, value, constants.ISPECS_PARAMETER_TYPES) + for key, value in params.items(): + if key in constants.IPOLICY_PARAMETERS: + fullkey = "ipolicy/" + key + _helper(owner, fullkey, value, constants.ISPECS_PARAMETER_TYPES) + else: + # FIXME: assuming list type + if not isinstance(value, list): + result.append("%s has invalid instance policy: for %s," + " expecting list, got %s" % + (owner, key, type(value))) # check cluster parameters _helper("cluster", "beparams", cluster.SimpleFillBE({}), diff --git a/lib/constants.py b/lib/constants.py index 16931ac6c5e34d10965fa3c1865c033fa73ed56f..9216e0ab9c5ab2a7a60e9521c52c568306debf8f 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -941,12 +941,14 @@ ISPECS_PARAMETERS = frozenset(ISPECS_PARAMETER_TYPES.keys()) ISPECS_MIN = "min" ISPECS_MAX = "max" ISPECS_STD = "std" +ISPECS_DTS = "disk_templates" IPOLICY_PARAMETERS = frozenset([ ISPECS_MIN, ISPECS_MAX, ISPECS_STD, ]) +IPOLICY_ALL_KEYS = IPOLICY_PARAMETERS.union([ISPECS_DTS]) # Node parameter names ND_OOB_PROGRAM = "oob_program" @@ -1885,7 +1887,8 @@ IPOLICY_DEFAULTS = { ISPEC_DISK_COUNT: 1, ISPEC_DISK_SIZE: 1024, ISPEC_NIC_COUNT: 1, - } + }, + ISPECS_DTS: DISK_TEMPLATES, } MASTER_POOL_SIZE_DEFAULT = 10 diff --git a/lib/objects.py b/lib/objects.py index 47fe527dfbc47c86772109394b0b0a78d261af0b..3da52edd8791c3d203a20bc210671f1179e05e13 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc. +# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -59,7 +59,7 @@ _UUID = ["uuid"] TISPECS_GROUP_TYPES = { constants.ISPECS_MIN: constants.VTYPE_INT, constants.ISPECS_MAX: constants.VTYPE_INT, -} + } TISPECS_CLUSTER_TYPES = { constants.ISPECS_MIN: constants.VTYPE_INT, @@ -92,15 +92,20 @@ def FillDict(defaults_dict, custom_dict, skip_keys=None): return ret_dict -def FillDictOfDicts(defaults_dict, custom_dict, skip_keys=None): - """Run FillDict for each key in dictionary. +def FillIPolicy(default_ipolicy, custom_ipolicy, skip_keys=None): + """Fills an instance policy with defaults. """ + assert frozenset(default_ipolicy.keys()) == constants.IPOLICY_ALL_KEYS ret_dict = {} - for key in defaults_dict: - ret_dict[key] = FillDict(defaults_dict[key], - custom_dict.get(key, {}), + for key in constants.IPOLICY_PARAMETERS: + ret_dict[key] = FillDict(default_ipolicy[key], + custom_ipolicy.get(key, {}), skip_keys=skip_keys) + # list items + for key in [constants.ISPECS_DTS]: + ret_dict[key] = list(custom_ipolicy.get(key, default_ipolicy[key])) + return ret_dict @@ -166,9 +171,9 @@ def MakeEmptyIPolicy(): """ return dict([ - (constants.ISPECS_MIN, dict()), - (constants.ISPECS_MAX, dict()), - (constants.ISPECS_STD, dict()), + (constants.ISPECS_MIN, {}), + (constants.ISPECS_MAX, {}), + (constants.ISPECS_STD, {}), ]) @@ -177,9 +182,14 @@ def CreateIPolicyFromOpts(ispecs_mem_size=None, ispecs_disk_count=None, ispecs_disk_size=None, ispecs_nic_count=None, + ispecs_disk_templates=None, group_ipolicy=False, - allowed_values=None): - """Creation of instane policy based on command line options. + allowed_values=None, + fill_all=False): + """Creation of instance policy based on command line options. + + @param fill_all: whether for cluster policies we should ensure that + all values are filled """ @@ -208,6 +218,12 @@ def CreateIPolicyFromOpts(ispecs_mem_size=None, for key, val in specs.items(): # {min: .. ,max: .., std: ..} ipolicy_out[key][name] = val + # no filldict for lists + if not group_ipolicy and fill_all and ispecs_disk_templates is None: + ispecs_disk_templates = constants.DISK_TEMPLATES + if ispecs_disk_templates is not None: + ipolicy_out[constants.ISPECS_DTS] = list(ispecs_disk_templates) + return ipolicy_out @@ -857,7 +873,7 @@ class Disk(ConfigObject): class InstancePolicy(ConfigObject): """Config object representing instance policy limits dictionary.""" - __slots__ = ["min", "max", "std"] + __slots__ = ["min", "max", "std", "disk_templates"] @classmethod def CheckParameterSyntax(cls, ipolicy): @@ -866,6 +882,8 @@ class InstancePolicy(ConfigObject): """ for param in constants.ISPECS_PARAMETERS: InstancePolicy.CheckISpecSyntax(ipolicy, param) + if constants.ISPECS_DTS in ipolicy: + InstancePolicy.CheckDiskTemplates(ipolicy[constants.ISPECS_DTS]) @classmethod def CheckISpecSyntax(cls, ipolicy, name): @@ -892,6 +910,16 @@ class InstancePolicy(ConfigObject): if min_v > std_v or std_v > max_v: raise errors.ConfigurationError(err) + @classmethod + def CheckDiskTemplates(cls, disk_templates): + """Checks the disk templates for validity. + + """ + wrong = frozenset(disk_templates).difference(constants.DISK_TEMPLATES) + if wrong: + raise errors.ConfigurationError("Invalid disk template(s) %s" % + utils.CommaJoin(wrong)) + class Instance(TaggableObject): """Config object representing an instance.""" @@ -1473,7 +1501,7 @@ class Cluster(TaggableObject): # instance policy added before 2.6 if self.ipolicy is None: - self.ipolicy = FillDictOfDicts(constants.IPOLICY_DEFAULTS, {}) + self.ipolicy = FillIPolicy(constants.IPOLICY_DEFAULTS, {}) @property def primary_hypervisor(self): @@ -1668,7 +1696,7 @@ class Cluster(TaggableObject): the cluster defaults """ - return FillDictOfDicts(self.ipolicy, ipolicy) + return FillIPolicy(self.ipolicy, ipolicy) class BlockDevStatus(ConfigObject):