X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/96acbc09b70c82051e2156744bf1ae6752004cb7..7260cfbe90e7bf0a30b296a8196618c8558d08b9:/lib/objects.py diff --git a/lib/objects.py b/lib/objects.py index 1cdc98f..f8d94da 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -26,6 +26,12 @@ pass to and from external parties. """ +# pylint: disable-msg=E0203,W0201 + +# E0203: Access to member %r before its definition, since we use +# objects.py which doesn't explicitely initialise its members + +# W0201: Attribute '%s' defined outside __init__ import ConfigParser import re @@ -39,20 +45,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 +106,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,18 +113,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 __getstate__(self): - state = {} - for name in self.__slots__: - if hasattr(self, name): - state[name] = getattr(self, name) - return state - def __setstate__(self, state): for name in state: if name in self.__slots__: @@ -126,7 +128,14 @@ class ConfigObject(object): make sure all objects returned are only standard python types. """ - return dict([(k, getattr(self, k, None)) for k in self.__slots__]) + result = {} + for name in self.__slots__: + value = getattr(self, name, None) + if value is not None: + result[name] = value + return result + + __getstate__ = ToDict @classmethod def FromDict(cls, val): @@ -145,7 +154,7 @@ class ConfigObject(object): raise errors.ConfigurationError("Invalid object passed to FromDict:" " expected dict, got %s" % type(val)) val_str = dict([(str(k), v) for k, v in val.iteritems()]) - obj = cls(**val_str) + obj = cls(**val_str) # pylint: disable-msg=W0142 return obj @staticmethod @@ -187,6 +196,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 +211,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 +223,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 +240,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 +299,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 +327,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.""" @@ -326,7 +355,7 @@ class NIC(ConfigObject): err = "Invalid nic mode: %s" % nicparams[constants.NIC_MODE] raise errors.ConfigurationError(err) - if (nicparams[constants.NIC_MODE] is constants.NIC_MODE_BRIDGED and + if (nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED and not nicparams[constants.NIC_LINK]): err = "Missing bridged nic link" raise errors.ConfigurationError(err) @@ -463,6 +492,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 +622,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 +647,7 @@ class Instance(TaggableObject): "disk_template", "network_port", "serial_no", - ] + ] + _TIMESTAMPS + _UUID def _ComputeSecondaryNodes(self): """Compute the list of secondary nodes. @@ -700,10 +747,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 +782,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 +808,7 @@ class OS(ConfigObject): "export_script", "import_script", "rename_script", + "supported_variants", ] @@ -757,7 +822,7 @@ class Node(TaggableObject): "master_candidate", "offline", "drained", - ] + ] + _TIMESTAMPS + _UUID class Cluster(TaggableObject): @@ -782,12 +847,15 @@ class Cluster(TaggableObject): "nicparams", "candidate_pool_size", "modify_etc_hosts", - ] + "modify_ssh_setup", + ] + _TIMESTAMPS + _UUID def UpgradeConfig(self): """Fill defaults for missing configuration values. """ + # pylint: disable-msg=E0203 + # because these are "defined" via slots, not manually if self.hvparams is None: self.hvparams = constants.HVC_DEFAULTS else: @@ -807,6 +875,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 +885,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 +908,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 +939,7 @@ class Cluster(TaggableObject): """ return FillDict(self.beparams.get(constants.PP_DEFAULT, {}), - instance.beparams) + instance.beparams) class BlockDevStatus(ConfigObject): @@ -874,16 +951,42 @@ class BlockDevStatus(ConfigObject): "sync_percent", "estimated_time", "is_degraded", - "ldisk_degraded", + "ldisk_status", ] - def ToLegacyStatus(self): - """Converts the device status to a legacy tuple. - """ - return (self.dev_path, self.major, self.minor, - self.sync_percent, self.estimated_time, - self.is_degraded, self.ldisk_degraded) +class ConfdRequest(ConfigObject): + """Object holding a confd request. + + @ivar protocol: confd protocol version + @ivar type: confd query type + @ivar query: query request + @ivar rsalt: requested reply salt + + """ + __slots__ = [ + "protocol", + "type", + "query", + "rsalt", + ] + + +class ConfdReply(ConfigObject): + """Object holding a confd reply. + + @ivar protocol: confd protocol version + @ivar status: reply status code (ok, error) + @ivar answer: confd query reply + @ivar serial: configuration serial number + + """ + __slots__ = [ + "protocol", + "status", + "answer", + "serial", + ] class SerializableConfigParser(ConfigParser.SafeConfigParser):