From ac75fad9bb3651501b6d54c29c451ca2ffca0723 Mon Sep 17 00:00:00 2001 From: Apollon Oikonomopoulos <apollon@noc.grnet.gr> Date: Mon, 18 Apr 2011 18:41:30 +0300 Subject: [PATCH] Instance addition and removal: manage IP addresses Prototype implementation of IP address management during instance addition/removal. Signed-off-by: Apollon Oikonomopoulos <apollon@noc.grnet.gr> --- lib/cmdlib.py | 115 ++++++++++++++++++++++++++++++-------------------- lib/config.py | 24 ++++++++--- 2 files changed, 86 insertions(+), 53 deletions(-) diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 82f9a297a..1528c848b 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -7713,6 +7713,52 @@ class LUInstanceCreate(LogicalUnit): if self.op.identify_defaults: self._RevertToDefaults(cluster) + # disk checks/pre-build + self.disks = [] + for disk in self.op.disks: + mode = disk.get("mode", constants.DISK_RDWR) + if mode not in constants.DISK_ACCESS_SET: + raise errors.OpPrereqError("Invalid disk access mode '%s'" % + mode, errors.ECODE_INVAL) + size = disk.get("size", None) + if size is None: + raise errors.OpPrereqError("Missing disk size", errors.ECODE_INVAL) + try: + size = int(size) + except (TypeError, ValueError): + raise errors.OpPrereqError("Invalid disk size '%s'" % size, + errors.ECODE_INVAL) + vg = disk.get("vg", self.cfg.GetVGName()) + new_disk = {"size": size, "mode": mode, "vg": vg} + if "adopt" in disk: + new_disk["adopt"] = disk["adopt"] + self.disks.append(new_disk) + + #### allocator run + # We run the iallocator here, so that we can use the primary node + # together with the network link for IP address reservation + + if self.op.iallocator is not None: + self._RunAllocator() + + #### node related checks + + # check primary node + self.pnode = pnode = self.cfg.GetNodeInfo(self.op.pnode) + assert self.pnode is not None, \ + "Cannot retrieve locked node %s" % self.op.pnode + if pnode.offline: + raise errors.OpPrereqError("Cannot use offline primary node '%s'" % + pnode.name, errors.ECODE_STATE) + if pnode.drained: + raise errors.OpPrereqError("Cannot use drained primary node '%s'" % + pnode.name, errors.ECODE_STATE) + if not pnode.vm_capable: + raise errors.OpPrereqError("Cannot use non-vm_capable primary node" + " '%s'" % pnode.name, errors.ECODE_STATE) + + self.secondaries = [] + # NIC buildup self.nics = [] for idx, nic in enumerate(self.op.nics): @@ -7756,21 +7802,21 @@ class LUInstanceCreate(LogicalUnit): errors.ECODE_INVAL) nic_ip = self.hostname1.ip try: - self.cfg.ReserveIp(link, nic_ip, self.proc.GetECId()) + self.cfg.ReserveIp(self.pnode.name, link, nic_ip, self.proc.GetECId()) except errors.ReservationError: raise errors.OpPrereqError("IP address %s already in use" " in cluster" % nic_ip, errors.ECODE_NOTUNIQUE) elif ip.lower() == constants.NIC_IP_POOL: - nic_ip = self.cfg.GenerateIp(link, self.proc.GetECId()) - logging.info("Chose ip %s from pool %s" % (nic_ip, link)) + nic_ip = self.cfg.GenerateIp(self.pnode.name, link, self.proc.GetECId()) + self.LogInfo("Chose IP %s from pool %s", nic_ip, link) else: if not netutils.IPAddress.IsValid(ip): raise errors.OpPrereqError("Invalid IP address '%s'" % ip, errors.ECODE_INVAL) nic_ip = ip try: - self.cfg.ReserveIp(link, nic_ip, self.proc.GetECId()) + self.cfg.ReserveIp(self.pnode.name, link, nic_ip, self.proc.GetECId()) except errors.ReservationError: raise errors.OpPrereqError("IP address %s already in use" " in cluster" % nic_ip, @@ -7819,6 +7865,25 @@ class LUInstanceCreate(LogicalUnit): new_disk["adopt"] = disk["adopt"] self.disks.append(new_disk) + # ip ping checks (we use the same ip that was resolved in ExpandNames) + if self.op.ip_check: + if netutils.TcpPing(self.check_ip, constants.DEFAULT_NODED_PORT): + raise errors.OpPrereqError("IP %s of instance %s already in use" % + (self.check_ip, self.op.instance_name), + errors.ECODE_NOTUNIQUE) + + #### mac address generation + # By generating here the mac address both the allocator and the hooks get + # the real final mac address rather than the 'auto' or 'generate' value. + # There is a race condition between the generation and the instance object + # creation, which means that we know the mac is valid now, but we're not + # sure it will be when we actually add the instance. If things go bad + # adding the instance will abort because of a duplicate mac, and the + # creation job will fail. + for nic in self.nics: + if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE): + nic.mac = self.cfg.GenerateMAC(self.proc.GetECId()) + if self.op.mode == constants.INSTANCE_IMPORT: # Check that the new instance doesn't have less disks than the export @@ -7858,48 +7923,6 @@ class LUInstanceCreate(LogicalUnit): # ENDIF: self.op.mode == constants.INSTANCE_IMPORT - # ip ping checks (we use the same ip that was resolved in ExpandNames) - if self.op.ip_check: - if netutils.TcpPing(self.check_ip, constants.DEFAULT_NODED_PORT): - raise errors.OpPrereqError("IP %s of instance %s already in use" % - (self.check_ip, self.op.instance_name), - errors.ECODE_NOTUNIQUE) - - #### mac address generation - # By generating here the mac address both the allocator and the hooks get - # the real final mac address rather than the 'auto' or 'generate' value. - # There is a race condition between the generation and the instance object - # creation, which means that we know the mac is valid now, but we're not - # sure it will be when we actually add the instance. If things go bad - # adding the instance will abort because of a duplicate mac, and the - # creation job will fail. - for nic in self.nics: - if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE): - nic.mac = self.cfg.GenerateMAC(self.proc.GetECId()) - - #### allocator run - - if self.op.iallocator is not None: - self._RunAllocator() - - #### node related checks - - # check primary node - self.pnode = pnode = self.cfg.GetNodeInfo(self.op.pnode) - assert self.pnode is not None, \ - "Cannot retrieve locked node %s" % self.op.pnode - if pnode.offline: - raise errors.OpPrereqError("Cannot use offline primary node '%s'" % - pnode.name, errors.ECODE_STATE) - if pnode.drained: - raise errors.OpPrereqError("Cannot use drained primary node '%s'" % - pnode.name, errors.ECODE_STATE) - if not pnode.vm_capable: - raise errors.OpPrereqError("Cannot use non-vm_capable primary node" - " '%s'" % pnode.name, errors.ECODE_STATE) - - self.secondaries = [] - # mirror node verification if self.op.disk_template in constants.DTS_INT_MIRROR: if self.op.snode == pnode.name: diff --git a/lib/config.py b/lib/config.py index b58d4eec1..d5e981c42 100644 --- a/lib/config.py +++ b/lib/config.py @@ -1234,9 +1234,12 @@ class ConfigWriter: raise errors.ConfigurationError("Cannot add instance %s:" " MAC address '%s' already in use." % (instance.name, nic.mac)) - # Commit all IP addresses to the respective address pools - self._UnlockedCommitIp(nic.nicparams.get(constants.NIC_LINK, None), - nic.ip) + link = nic.nicparams.get(constants.NIC_LINK, None) + net_uuid = self._UnlockedGetNetworkFromNodeLink(instance.primary_node, + link) + if net_uuid: + # Commit all IP addresses to the respective address pools + self._UnlockedCommitIp(net_uuid, nic.ip) self._EnsureUUID(instance, ec_id) @@ -1291,10 +1294,17 @@ class ConfigWriter: """ if instance_name not in self._config_data.instances: raise errors.ConfigurationError("Unknown instance '%s'" % instance_name) - for nic in self._config_data.instances[instance_name].nics: - # Return all IP addresses to the respective address pools - self._UnlockedReleaseIp(nic.nicparams.get(constants.NIC_LINK, None), - nic.ip) + + instance = self._UnlockedGetInstanceInfo(instance_name) + + for nic in instance.nics: + link = nic.nicparams.get(constants.NIC_LINK, None) + net_uuid = self._UnlockedGetNetworkFromNodeLink(instance.primary_node, + link) + if net_uuid: + # Return all IP addresses to the respective address pools + self._UnlockedReleaseIp(net_uuid, nic.ip) + del self._config_data.instances[instance_name] self._config_data.cluster.serial_no += 1 self._WriteConfig() -- GitLab