X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/18d750b9d0c7de2c74e7b2ff4cf1e0c0e730e5e9..6c881c52b3f5b040d762a3c0e35b35fab7cfd6a5:/lib/objects.py diff --git a/lib/objects.py b/lib/objects.py index 6f99a81..2f3b4f6 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -26,6 +26,11 @@ pass to and from external parties. """ +# pylint: disable-msg=E0203 + +# E0203: Access to member %r before its definition, since we use +# objects.py which doesn't explicitely initialise its members + import ConfigParser import re @@ -39,20 +44,29 @@ from ganeti import constants __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance", "OS", "Node", "Cluster", "FillDict"] +_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 @@ -91,7 +105,6 @@ class ConfigObject(object): def __init__(self, **kwargs): for k, v in kwargs.iteritems(): setattr(self, k, v) - self.UpgradeConfig() def __getattr__(self, name): if name not in self.__slots__: @@ -99,11 +112,6 @@ class ConfigObject(object): (type(self).__name__, name)) return None - def __setitem__(self, key, value): - if key not in self.__slots__: - raise KeyError(key) - setattr(self, key, value) - def __setstate__(self, state): for name in state: if name in self.__slots__: @@ -187,6 +195,14 @@ class ConfigObject(object): " _ContainerFromDicts" % c_type) return ret + def Copy(self): + """Makes a deep copy of the current object and its children. + + """ + dict_form = self.ToDict() + clone_obj = self.__class__.FromDict(dict_form) + return clone_obj + def __repr__(self): """Implement __repr__ for ConfigObjects.""" return repr(self.ToDict()) @@ -194,8 +210,8 @@ class ConfigObject(object): def UpgradeConfig(self): """Fill defaults for missing configuration values. - This method will be called at object init time, and its implementation will - be object dependent. + This method will be called at configuration load time, and its + implementation will be object dependent. """ pass @@ -206,9 +222,10 @@ class TaggableObject(ConfigObject): """ __slots__ = ConfigObject.__slots__ + ["tags"] + VALID_TAG_RE = re.compile("^[\w.+*/:@-]+$") - @staticmethod - def ValidateTag(tag): + @classmethod + def ValidateTag(cls, tag): """Check if a tag is valid. If the tag is invalid, an errors.TagError will be raised. The @@ -222,7 +239,7 @@ class TaggableObject(ConfigObject): constants.MAX_TAG_LEN) if not tag: raise errors.TagError("Tags cannot be empty") - if not re.match("^[\w.+*/:-]+$", tag): + if not cls.VALID_TAG_RE.match(tag): raise errors.TagError("Tag contains invalid characters") def GetTags(self): @@ -281,7 +298,8 @@ class TaggableObject(ConfigObject): class ConfigData(ConfigObject): """Top-level config object.""" - __slots__ = ["version", "cluster", "nodes", "instances", "serial_no"] + __slots__ = (["version", "cluster", "nodes", "instances", "serial_no"] + + _TIMESTAMPS) def ToDict(self): """Custom function for top-level config data. @@ -308,6 +326,16 @@ class ConfigData(ConfigObject): obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance) return obj + def UpgradeConfig(self): + """Fill defaults for missing configuration values. + + """ + self.cluster.UpgradeConfig() + for node in self.nodes.values(): + node.UpgradeConfig() + for instance in self.instances.values(): + instance.UpgradeConfig() + class NIC(ConfigObject): """Config object representing a network card.""" @@ -463,6 +491,15 @@ class Disk(ConfigObject): raise errors.ProgrammerError("Disk.RecordGrow called for unsupported" " disk type %s" % self.dev_type) + def UnsetSize(self): + """Sets recursively the size to zero for the disk and its children. + + """ + if self.children: + for child in self.children: + child.UnsetSize() + self.size = 0 + def SetPhysicalID(self, target_node, nodes_ip): """Convert the logical ID to the physical ID. @@ -584,6 +621,15 @@ class Disk(ConfigObject): all_errors.append("Disk access mode '%s' is invalid" % (self.mode, )) return all_errors + def UpgradeConfig(self): + """Fill defaults for missing configuration values. + + """ + if self.children: + for child in self.children: + child.UpgradeConfig() + # add here config upgrade for this disk + class Instance(TaggableObject): """Config object representing an instance.""" @@ -600,7 +646,7 @@ class Instance(TaggableObject): "disk_template", "network_port", "serial_no", - ] + ] + _TIMESTAMPS + _UUID def _ComputeSecondaryNodes(self): """Compute the list of secondary nodes. @@ -700,10 +746,12 @@ class Instance(TaggableObject): idx = int(idx) return self.disks[idx] except ValueError, err: - raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err)) + raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err), + errors.ECODE_INVAL) except IndexError: raise errors.OpPrereqError("Invalid disk index: %d (instace has disks" - " 0 to %d" % (idx, len(self.disks))) + " 0 to %d" % (idx, len(self.disks)), + errors.ECODE_INVAL) def ToDict(self): """Instance-specific conversion to standard python types. @@ -733,6 +781,21 @@ class Instance(TaggableObject): obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk) return obj + def UpgradeConfig(self): + """Fill defaults for missing configuration values. + + """ + for nic in self.nics: + 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): """Config object representing an operating system.""" @@ -744,6 +807,7 @@ class OS(ConfigObject): "export_script", "import_script", "rename_script", + "supported_variants", ] @@ -757,7 +821,7 @@ class Node(TaggableObject): "master_candidate", "offline", "drained", - ] + ] + _TIMESTAMPS + _UUID class Cluster(TaggableObject): @@ -782,7 +846,8 @@ class Cluster(TaggableObject): "nicparams", "candidate_pool_size", "modify_etc_hosts", - ] + "modify_ssh_setup", + ] + _TIMESTAMPS + _UUID def UpgradeConfig(self): """Fill defaults for missing configuration values. @@ -807,6 +872,9 @@ class Cluster(TaggableObject): if self.modify_etc_hosts is None: self.modify_etc_hosts = True + if self.modify_ssh_setup is None: + self.modify_ssh_setup = True + # default_bridge is no longer used it 2.1. The slot is left there to # support auto-upgrading, but will be removed in 2.2 if self.default_bridge is not None: @@ -814,12 +882,11 @@ class Cluster(TaggableObject): # default_hypervisor is just the first enabled one in 2.1 if self.default_hypervisor is not None: - self.enabled_hypervisors = [self.default_hypervisor] + \ + self.enabled_hypervisors = ([self.default_hypervisor] + [hvname for hvname in self.enabled_hypervisors - if hvname != self.default_hypervisor] + if hvname != self.default_hypervisor]) self.default_hypervisor = None - def ToDict(self): """Custom function for cluster. @@ -838,18 +905,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. @@ -862,7 +936,7 @@ class Cluster(TaggableObject): """ return FillDict(self.beparams.get(constants.PP_DEFAULT, {}), - instance.beparams) + instance.beparams) class BlockDevStatus(ConfigObject):