X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/79b26a7ab1b978b69525630a5d378a89abb74ce0..cd098c41522469e462efeb1e157a8718ff7b7809:/lib/config.py?ds=sidebyside diff --git a/lib/config.py b/lib/config.py index 1d792d1..7ebcf92 100644 --- a/lib/config.py +++ b/lib/config.py @@ -79,6 +79,7 @@ class ConfigWriter: self._cfg_file = cfg_file self._temporary_ids = set() self._temporary_drbds = {} + self._temporary_macs = set() # Note: in order to prevent errors when resolving our name in # _DistributeConfig, we compute it here once and reuse it; it's # better to raise an error before starting to modify the config @@ -110,11 +111,12 @@ class ConfigWriter: byte2 = random.randrange(0, 256) byte3 = random.randrange(0, 256) mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3) - if mac not in all_macs: + if mac not in all_macs and mac not in self._temporary_macs: break retries -= 1 else: raise errors.ConfigurationError("Can't generate unique MAC") + self._temporary_macs.add(mac) return mac @locking.ssynchronized(_config_lock, shared=1) @@ -126,7 +128,7 @@ class ConfigWriter: """ all_macs = self._AllMACs() - return mac in all_macs + return mac in all_macs or mac in self._temporary_macs @locking.ssynchronized(_config_lock, shared=1) def GenerateDRBDSecret(self): @@ -227,6 +229,36 @@ class ConfigWriter: return result + def _CheckDiskIDs(self, disk, l_ids, p_ids): + """Compute duplicate disk IDs + + @type disk: L{objects.Disk} + @param disk: the disk at which to start searching + @type l_ids: list + @param l_ids: list of current logical ids + @type p_ids: list + @param p_ids: list of current physical ids + @rtype: list + @return: a list of error messages + + """ + result = [] + if disk.logical_id is not None: + if disk.logical_id in l_ids: + result.append("duplicate logical id %s" % str(disk.logical_id)) + else: + l_ids.append(disk.logical_id) + if disk.physical_id is not None: + if disk.physical_id in p_ids: + result.append("duplicate physical id %s" % str(disk.physical_id)) + else: + p_ids.append(disk.physical_id) + + if disk.children: + for child in disk.children: + result.extend(self._CheckDiskIDs(child, l_ids, p_ids)) + return result + def _UnlockedVerifyConfig(self): """Verify function. @@ -239,6 +271,8 @@ class ConfigWriter: seen_macs = [] ports = {} data = self._config_data + seen_lids = [] + seen_pids = [] for instance_name in data.instances: instance = data.instances[instance_name] if instance.primary_node not in data.nodes: @@ -269,6 +303,12 @@ class ConfigWriter: ports[net_port] = [] ports[net_port].append((instance.name, "network port")) + # instance disk verify + for idx, disk in enumerate(instance.disks): + result.extend(["instance '%s' disk %d error: %s" % + (instance.name, idx, msg) for msg in disk.Verify()]) + result.extend(self._CheckDiskIDs(disk, seen_lids, seen_pids)) + # cluster-wide pool of free ports for free_port in data.cluster.tcpudp_port_pool: if free_port not in ports: @@ -299,6 +339,14 @@ class ConfigWriter: result.append("Not enough master candidates: actual %d, target %d" % (mc_now, mc_max)) + # node checks + for node in data.nodes.values(): + if [node.master_candidate, node.drained, node.offline].count(True) > 1: + result.append("Node %s state is invalid: master_candidate=%s," + " drain=%s, offline=%s" % + (node.name, node.master_candidate, node.drain, + node.offline)) + # drbd minors check d_map, duplicates = self._UnlockedComputeDRBDMap() for node, minor, instance_a, instance_b in duplicates: @@ -639,9 +687,18 @@ class ConfigWriter: all_lvs = instance.MapLVsByNode() logging.info("Instance '%s' DISK_LAYOUT: %s", instance.name, all_lvs) + all_macs = self._AllMACs() + for nic in instance.nics: + if nic.mac in all_macs: + raise errors.ConfigurationError("Cannot add instance %s:" + " MAC address '%s' already in use." % (instance.name, nic.mac)) + instance.serial_no = 1 self._config_data.instances[instance.name] = instance + self._config_data.cluster.serial_no += 1 self._UnlockedReleaseDRBDMinors(instance.name) + for nic in instance.nics: + self._temporary_macs.discard(nic.mac) self._WriteConfig() def _SetInstanceStatus(self, instance_name, status): @@ -675,6 +732,7 @@ class ConfigWriter: if instance_name not in self._config_data.instances: raise errors.ConfigurationError("Unknown instance '%s'" % instance_name) del self._config_data.instances[instance_name] + self._config_data.cluster.serial_no += 1 self._WriteConfig() @locking.ssynchronized(_config_lock) @@ -769,7 +827,7 @@ class ConfigWriter: """Get the configuration of all instances. @rtype: dict - @returns: dict of (instance, instance_info), where instance_info is what + @return: dict of (instance, instance_info), where instance_info is what would GetInstanceInfo return for the node """ @@ -896,7 +954,7 @@ class ConfigWriter: """ mc_now = mc_max = 0 for node in self._config_data.nodes.itervalues(): - if not node.offline: + if not (node.offline or node.drained): mc_max += 1 if node.master_candidate: mc_now += 1 @@ -932,7 +990,7 @@ class ConfigWriter: if mc_now >= mc_max: break node = self._config_data.nodes[name] - if node.master_candidate or node.offline: + if node.master_candidate or node.offline or node.drained: continue mod_list.append(node) node.master_candidate = True @@ -1008,10 +1066,12 @@ class ConfigWriter: result = rpc.RpcRunner.call_upload_file(node_list, self._cfg_file, address_list=addr_list) - for node in node_list: - if not result[node]: - logging.error("copy of file %s to node %s failed", - self._cfg_file, node) + for to_node, to_result in result.items(): + msg = to_result.RemoteFailMsg() + if msg: + msg = ("Copy of file %s to node %s failed: %s" % + (self._cfg_file, to_node, msg)) + logging.error(msg) bad = True return not bad @@ -1059,16 +1119,21 @@ class ConfigWriter: """ fn = "\n".join + instance_names = utils.NiceSort(self._UnlockedGetInstanceList()) node_names = utils.NiceSort(self._UnlockedGetNodeList()) node_info = [self._UnlockedGetNodeInfo(name) for name in node_names] + instance_data = fn(instance_names) off_data = fn(node.name for node in node_info if node.offline) + on_data = fn(node.name for node in node_info if not node.offline) mc_data = fn(node.name for node in node_info if node.master_candidate) node_data = fn(node_names) cluster = self._config_data.cluster + cluster_tags = fn(cluster.GetTags()) return { constants.SS_CLUSTER_NAME: cluster.cluster_name, + constants.SS_CLUSTER_TAGS: cluster_tags, constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir, constants.SS_MASTER_CANDIDATES: mc_data, constants.SS_MASTER_IP: cluster.master_ip, @@ -1076,6 +1141,8 @@ class ConfigWriter: constants.SS_MASTER_NODE: cluster.master_node, constants.SS_NODE_LIST: node_data, constants.SS_OFFLINE_NODES: off_data, + constants.SS_ONLINE_NODES: on_data, + constants.SS_INSTANCE_LIST: instance_data, constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION, } @@ -1185,5 +1252,7 @@ class ConfigWriter: if isinstance(target, objects.Instance): self._UnlockedReleaseDRBDMinors(target.name) + for nic in target.nics: + self._temporary_macs.discard(nic.mac) self._WriteConfig()