return ret_dict
-def FillIPolicy(default_ipolicy, custom_ipolicy, skip_keys=None):
+def FillIPolicy(default_ipolicy, custom_ipolicy):
"""Fills an instance policy with defaults.
"""
assert frozenset(default_ipolicy.keys()) == constants.IPOLICY_ALL_KEYS
- ret_dict = {}
- for key in constants.IPOLICY_ISPECS:
- ret_dict[key] = FillDict(default_ipolicy[key],
- custom_ipolicy.get(key, {}),
- skip_keys=skip_keys)
- # list items
- for key in [constants.IPOLICY_DTS]:
- ret_dict[key] = list(custom_ipolicy.get(key, default_ipolicy[key]))
- # other items which we know we can directly copy (immutables)
- for key in constants.IPOLICY_PARAMETERS:
- ret_dict[key] = custom_ipolicy.get(key, default_ipolicy[key])
-
+ ret_dict = copy.deepcopy(custom_ipolicy)
+ for key in default_ipolicy:
+ if key not in ret_dict:
+ ret_dict[key] = copy.deepcopy(default_ipolicy[key])
+ elif key == constants.ISPECS_STD:
+ ret_dict[key] = FillDict(default_ipolicy[key], ret_dict[key])
return ret_dict
"""Create empty IPolicy dictionary.
"""
- return dict([
- (constants.ISPECS_MIN, {}),
- (constants.ISPECS_MAX, {}),
- (constants.ISPECS_STD, {}),
- ])
+ return {}
class ConfigObject(outils.ValidatedSlots):
self.networks = {}
for network in self.networks.values():
network.UpgradeConfig()
+ self._UpgradeEnabledDiskTemplates()
+
+ def _UpgradeEnabledDiskTemplates(self):
+ """Upgrade the cluster's enabled disk templates by inspecting the currently
+ enabled and/or used disk templates.
+
+ """
+ # enabled_disk_templates in the cluster config were introduced in 2.8.
+ # Remove this code once upgrading from earlier versions is deprecated.
+ if not self.cluster.enabled_disk_templates:
+ template_set = \
+ set([inst.disk_template for inst in self.instances.values()])
+ # Add drbd and plain, if lvm is enabled (by specifying a volume group)
+ if self.cluster.volume_group_name:
+ template_set.add(constants.DT_DRBD8)
+ template_set.add(constants.DT_PLAIN)
+ # FIXME: Adapt this when dis/enabling at configure time is removed.
+ # Enable 'file' and 'sharedfile', if they are enabled, even though they
+ # might currently not be used.
+ if constants.ENABLE_FILE_STORAGE:
+ template_set.add(constants.DT_FILE)
+ if constants.ENABLE_SHARED_FILE_STORAGE:
+ template_set.add(constants.DT_SHARED_FILE)
+ # Set enabled_disk_templates to the inferred disk templates. Order them
+ # according to a preference list that is based on Ganeti's history of
+ # supported disk templates.
+ self.cluster.enabled_disk_templates = []
+ for preferred_template in constants.DISK_TEMPLATE_PREFERENCE:
+ if preferred_template in template_set:
+ self.cluster.enabled_disk_templates.append(preferred_template)
+ template_set.remove(preferred_template)
+ self.cluster.enabled_disk_templates.extend(list(template_set))
class NIC(ConfigObject):
"""Config object representing a network card."""
- __slots__ = ["mac", "ip", "network", "nicparams", "netinfo"]
+ __slots__ = ["name", "mac", "ip", "network", "nicparams", "netinfo"] + _UUID
@classmethod
def CheckParameterSyntax(cls, nicparams):
class Disk(ConfigObject):
"""Config object representing a block device."""
- __slots__ = ["dev_type", "logical_id", "physical_id",
- "children", "iv_name", "size", "mode", "params"]
+ __slots__ = (["name", "dev_type", "logical_id", "physical_id",
+ "children", "iv_name", "size", "mode", "params", "spindles"] +
+ _UUID)
def CreateOnSecondary(self):
"""Test if this device needs to be created on a secondary node."""
raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
" disk type %s" % self.dev_type)
- def Update(self, size=None, mode=None):
- """Apply changes to size and mode.
+ def Update(self, size=None, mode=None, spindles=None):
+ """Apply changes to size, spindles and mode.
"""
if self.dev_type == constants.LD_DRBD8:
self.size = size
if mode is not None:
self.mode = mode
+ if spindles is not None:
+ self.spindles = spindles
def UnsetSize(self):
"""Sets recursively the size to zero for the disk and its children.
val += ", not visible"
else:
val += ", visible as /dev/%s" % self.iv_name
+ if self.spindles is not None:
+ val += ", spindles=%s" % self.spindles
if isinstance(self.size, int):
val += ", size=%dm)>" % self.size
else:
class InstancePolicy(ConfigObject):
"""Config object representing instance policy limits dictionary.
-
Note that this object is not actually used in the config, it's just
used as a placeholder for a few functions.
def CheckParameterSyntax(cls, ipolicy, check_std):
""" Check the instance policy for validity.
+ @type ipolicy: dict
+ @param ipolicy: dictionary with min/max/std specs and policies
+ @type check_std: bool
+ @param check_std: Whether to check std value or just assume compliance
+ @raise errors.ConfigurationError: when the policy is not legal
+
"""
- for param in constants.ISPECS_PARAMETERS:
- InstancePolicy.CheckISpecSyntax(ipolicy, param, check_std)
+ InstancePolicy.CheckISpecSyntax(ipolicy, check_std)
if constants.IPOLICY_DTS in ipolicy:
InstancePolicy.CheckDiskTemplates(ipolicy[constants.IPOLICY_DTS])
for key in constants.IPOLICY_PARAMETERS:
utils.CommaJoin(wrong_keys))
@classmethod
- def CheckISpecSyntax(cls, ipolicy, name, check_std):
- """Check the instance policy for validity on a given key.
+ def _CheckIncompleteSpec(cls, spec, keyname):
+ missing_params = constants.ISPECS_PARAMETERS - frozenset(spec.keys())
+ if missing_params:
+ msg = ("Missing instance specs parameters for %s: %s" %
+ (keyname, utils.CommaJoin(missing_params)))
+ raise errors.ConfigurationError(msg)
- We check if the instance policy makes sense for a given key, that is
- if ipolicy[min][name] <= ipolicy[std][name] <= ipolicy[max][name].
+ @classmethod
+ def CheckISpecSyntax(cls, ipolicy, check_std):
+ """Check the instance policy specs for validity.
@type ipolicy: dict
- @param ipolicy: dictionary with min, max, std specs
+ @param ipolicy: dictionary with min/max/std specs
+ @type check_std: bool
+ @param check_std: Whether to check std value or just assume compliance
+ @raise errors.ConfigurationError: when specs are not valid
+
+ """
+ if constants.ISPECS_MINMAX not in ipolicy:
+ # Nothing to check
+ return
+
+ if check_std and constants.ISPECS_STD not in ipolicy:
+ msg = "Missing key in ipolicy: %s" % constants.ISPECS_STD
+ raise errors.ConfigurationError(msg)
+ stdspec = ipolicy.get(constants.ISPECS_STD)
+ if check_std:
+ InstancePolicy._CheckIncompleteSpec(stdspec, constants.ISPECS_STD)
+
+ if not ipolicy[constants.ISPECS_MINMAX]:
+ raise errors.ConfigurationError("Empty minmax specifications")
+ std_is_good = False
+ for minmaxspecs in ipolicy[constants.ISPECS_MINMAX]:
+ missing = constants.ISPECS_MINMAX_KEYS - frozenset(minmaxspecs.keys())
+ if missing:
+ msg = "Missing instance specification: %s" % utils.CommaJoin(missing)
+ raise errors.ConfigurationError(msg)
+ for (key, spec) in minmaxspecs.items():
+ InstancePolicy._CheckIncompleteSpec(spec, key)
+
+ spec_std_ok = True
+ for param in constants.ISPECS_PARAMETERS:
+ par_std_ok = InstancePolicy._CheckISpecParamSyntax(minmaxspecs, stdspec,
+ param, check_std)
+ spec_std_ok = spec_std_ok and par_std_ok
+ std_is_good = std_is_good or spec_std_ok
+ if not std_is_good:
+ raise errors.ConfigurationError("Invalid std specifications")
+
+ @classmethod
+ def _CheckISpecParamSyntax(cls, minmaxspecs, stdspec, name, check_std):
+ """Check the instance policy specs for validity on a given key.
+
+ We check if the instance specs makes sense for a given key, that is
+ if minmaxspecs[min][name] <= stdspec[name] <= minmaxspec[max][name].
+
+ @type minmaxspecs: dict
+ @param minmaxspecs: dictionary with min and max instance spec
+ @type stdspec: dict
+ @param stdspec: dictionary with standard instance spec
@type name: string
@param name: what are the limits for
@type check_std: bool
@param check_std: Whether to check std value or just assume compliance
- @raise errors.ConfigureError: when specs for given name are not valid
+ @rtype: bool
+ @return: C{True} when specs are valid, C{False} when standard spec for the
+ given name is not valid
+ @raise errors.ConfigurationError: when min/max specs for the given name
+ are not valid
"""
- min_v = ipolicy[constants.ISPECS_MIN].get(name, 0)
+ minspec = minmaxspecs[constants.ISPECS_MIN]
+ maxspec = minmaxspecs[constants.ISPECS_MAX]
+ min_v = minspec[name]
+ max_v = maxspec[name]
- if check_std:
- std_v = ipolicy[constants.ISPECS_STD].get(name, min_v)
- std_msg = std_v
- else:
- std_v = min_v
- std_msg = "-"
-
- max_v = ipolicy[constants.ISPECS_MAX].get(name, std_v)
- err = ("Invalid specification of min/max/std values for %s: %s/%s/%s" %
- (name,
- ipolicy[constants.ISPECS_MIN].get(name, "-"),
- ipolicy[constants.ISPECS_MAX].get(name, "-"),
- std_msg))
- if min_v > std_v or std_v > max_v:
+ if min_v > max_v:
+ err = ("Invalid specification of min/max values for %s: %s/%s" %
+ (name, min_v, max_v))
raise errors.ConfigurationError(err)
+ elif check_std:
+ std_v = stdspec.get(name, min_v)
+ return std_v >= min_v and std_v <= max_v
+ else:
+ return True
@classmethod
def CheckDiskTemplates(cls, disk_templates):
"nics",
"disks",
"disk_template",
+ "disks_active",
"network_port",
"serial_no",
] + _TIMESTAMPS + _UUID
if self.osparams is None:
self.osparams = {}
UpgradeBeParams(self.beparams)
+ if self.disks_active is None:
+ self.disks_active = self.admin_state == constants.ADMINST_UP
class OS(ConfigObject):
"prealloc_wipe_disks",
"hv_state_static",
"disk_state_static",
+ "enabled_disk_templates",
] + _TIMESTAMPS + _UUID
def UpgradeConfig(self):
wrongkeys = frozenset(self.ipolicy.keys()) - constants.IPOLICY_ALL_KEYS
if wrongkeys:
# These keys would be silently removed by FillIPolicy()
- msg = ("Cluster instance policy contains spourious keys: %s" %
+ msg = ("Cluster instance policy contains spurious keys: %s" %
utils.CommaJoin(wrongkeys))
raise errors.ConfigurationError(msg)
self.ipolicy = FillIPolicy(constants.IPOLICY_DEFAULTS, self.ipolicy)