X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/082a7f91e9bc15c3b94fe8ebd99fe3c0f7c21ae9..55224070299fd267979208a247c2f4288bbd3dbb:/lib/objects.py diff --git a/lib/objects.py b/lib/objects.py index 72ba43c..1fc2db3 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -29,6 +29,7 @@ pass to and from external parties. import ConfigParser import re +import copy from cStringIO import StringIO from ganeti import errors @@ -49,7 +50,7 @@ class ConfigObject(object): as None instead of raising an error Classes derived from this must always declare __slots__ (we use many - config objects and the memory reduction is useful. + config objects and the memory reduction is useful) """ __slots__ = [] @@ -273,7 +274,7 @@ class NIC(ConfigObject): class Disk(ConfigObject): """Config object representing a block device.""" __slots__ = ["dev_type", "logical_id", "physical_id", - "children", "iv_name", "size"] + "children", "iv_name", "size", "mode"] def CreateOnSecondary(self): """Test if this device needs to be created on a secondary node.""" @@ -504,68 +505,68 @@ class Instance(TaggableObject): "primary_node", "os", "hypervisor", + "hvparams", + "beparams", "status", - "memory", - "vcpus", "nics", "disks", "disk_template", "network_port", - "kernel_path", - "initrd_path", - "hvm_boot_order", - "hvm_acpi", - "hvm_pae", - "hvm_cdrom_image_path", - "hvm_nic_type", - "hvm_disk_type", - "vnc_bind_address", "serial_no", ] def _ComputeSecondaryNodes(self): """Compute the list of secondary nodes. + This is a simple wrapper over _ComputeAllNodes. + + """ + all_nodes = set(self._ComputeAllNodes()) + all_nodes.discard(self.primary_node) + return tuple(all_nodes) + + secondary_nodes = property(_ComputeSecondaryNodes, None, None, + "List of secondary nodes") + + def _ComputeAllNodes(self): + """Compute the list of all nodes. + Since the data is already there (in the drbd disks), keeping it as a separate normal attribute is redundant and if not properly synchronised can cause problems. Thus it's better to compute it dynamically. """ - def _Helper(primary, sec_nodes, device): - """Recursively computes secondary nodes given a top device.""" + def _Helper(nodes, device): + """Recursively computes nodes given a top device.""" if device.dev_type in constants.LDS_DRBD: - nodea, nodeb, dummy = device.logical_id[:3] - if nodea == primary: - candidate = nodeb - else: - candidate = nodea - if candidate not in sec_nodes: - sec_nodes.append(candidate) + nodea, nodeb = device.logical_id[:2] + nodes.add(nodea) + nodes.add(nodeb) if device.children: for child in device.children: - _Helper(primary, sec_nodes, child) + _Helper(nodes, child) - secondary_nodes = [] + all_nodes = set() + all_nodes.add(self.primary_node) for device in self.disks: - _Helper(self.primary_node, secondary_nodes, device) - return tuple(secondary_nodes) + _Helper(all_nodes, device) + return tuple(all_nodes) - secondary_nodes = property(_ComputeSecondaryNodes, None, None, - "List of secondary nodes") + all_nodes = property(_ComputeAllNodes, None, None, + "List of all nodes of the instance") def MapLVsByNode(self, lvmap=None, devs=None, node=None): """Provide a mapping of nodes to LVs this instance owns. - This function figures out what logical volumes should belong on which - nodes, recursing through a device tree. + This function figures out what logical volumes should belong on + which nodes, recursing through a device tree. - Args: - lvmap: (optional) a dictionary to receive the 'node' : ['lv', ...] data. + @param lvmap: optional dictionary to receive the + 'node' : ['lv', ...] data. - Returns: - None if lvmap arg is given. - Otherwise, { 'nodename' : ['volume1', 'volume2', ...], ... } + @return: None if lvmap arg is given, otherwise, a dictionary + of the form { 'nodename' : ['volume1', 'volume2', ...], ... } """ if node == None: @@ -587,12 +588,6 @@ class Instance(TaggableObject): lvmap[node].append(dev.logical_id[1]) elif dev.dev_type in constants.LDS_DRBD: - if dev.logical_id[0] not in lvmap: - lvmap[dev.logical_id[0]] = [] - - if dev.logical_id[1] not in lvmap: - lvmap[dev.logical_id[1]] = [] - if dev.children: self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0]) self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1]) @@ -602,17 +597,26 @@ class Instance(TaggableObject): return ret - def FindDisk(self, name): - """Find a disk given having a specified name. + def FindDisk(self, idx): + """Find a disk given having a specified index. - This will return the disk which has the given iv_name. + This is just a wrapper that does validation of the index. - """ - for disk in self.disks: - if disk.iv_name == name: - return disk + @type idx: int + @param idx: the disk index + @rtype: L{Disk} + @return: the corresponding disk + @raise errors.OpPrereqError: when the given index is not valid - return None + """ + try: + idx = int(idx) + return self.disks[idx] + except ValueError, err: + raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err)) + except IndexError: + raise errors.OpPrereqError("Invalid disk index: %d (instace has disks" + " 0 to %d" % (idx, len(self.disks))) def ToDict(self): """Instance-specific conversion to standard python types. @@ -683,6 +687,8 @@ class Node(TaggableObject): "primary_ip", "secondary_ip", "serial_no", + "master_candidate", + "offline", ] @@ -696,13 +702,16 @@ class Cluster(TaggableObject): "mac_prefix", "volume_group_name", "default_bridge", - "hypervisor", + "default_hypervisor", "master_node", "master_ip", "master_netdev", "cluster_name", "file_storage_dir", "enabled_hypervisors", + "hvparams", + "beparams", + "candidate_pool_size", ] def ToDict(self): @@ -723,6 +732,48 @@ class Cluster(TaggableObject): obj.tcpudp_port_pool = set(obj.tcpudp_port_pool) return obj + @staticmethod + def FillDict(defaults_dict, custom_dict): + """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 + @rtype: dict + @return: dict with the 'full' values + + """ + ret_dict = copy.deepcopy(defaults_dict) + ret_dict.update(custom_dict) + return ret_dict + + def FillHV(self, instance): + """Fill an instance's hvparams dict. + + @type instance: object + @param instance: the instance parameter to fill + @rtype: dict + @return: a copy of the instance's hvparams with missing keys filled from + the cluster defaults + + """ + return self.FillDict(self.hvparams.get(instance.hypervisor, {}), + instance.hvparams) + + def FillBE(self, instance): + """Fill an instance's beparams dict. + + @type instance: object + @param instance: the instance parameter to fill + @rtype: dict + @return: a copy of the instance's beparams with missing keys filled from + the cluster defaults + + """ + return self.FillDict(self.beparams.get(constants.BEGR_DEFAULT, {}), + instance.beparams) + class SerializableConfigParser(ConfigParser.SafeConfigParser): """Simple wrapper over ConfigParse that allows serialization.