From 62a7762b7371984a4851bc0d9021fbced5a9406e Mon Sep 17 00:00:00 2001 From: Apollon Oikonomopoulos <apollon@noc.grnet.gr> Date: Mon, 22 Nov 2010 17:27:54 +0200 Subject: [PATCH] Add config objects and methods for IP pools This patch introduces the following changes to lib.config and lib.objects to facilitate IP pool management. - Add a "networks" key to objects.cluster to hold link -> subnet relations. Each link is assumed to be associated with at most one IPv4 and at most one IPv6 subnet. Currently only IPv4 subnets are used. - Add a TemporaryReservationManager for IP address reservations. - Add config.{_UnlockedCommitIp,CommitIp,_UnlockedReleaseIp,ReleaseIp} to write IP pool changes to the config file. - Add config.{ReserveIp,GenerateIp} for use with the _temporary_ips TemporaryReservationManager. - Commit succesful reservations/releases to the IP pools in config.AddInstance and config.RemoveInstance Signed-off-by: Apollon Oikonomopoulos <apollon@noc.grnet.gr> --- lib/config.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++- lib/objects.py | 5 +++ 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/lib/config.py b/lib/config.py index 68e414cb5..499fa5494 100644 --- a/lib/config.py +++ b/lib/config.py @@ -49,6 +49,7 @@ from ganeti import serializer from ganeti import uidpool from ganeti import netutils from ganeti import runtime +from ganeti import ippool _config_lock = locking.SharedLock("ConfigWriter") @@ -148,8 +149,10 @@ class ConfigWriter: self._temporary_macs = TemporaryReservationManager() self._temporary_secrets = TemporaryReservationManager() self._temporary_lvs = TemporaryReservationManager() + self._temporary_ips = TemporaryReservationManager() self._all_rms = [self._temporary_ids, self._temporary_macs, - self._temporary_secrets, self._temporary_lvs] + self._temporary_secrets, self._temporary_lvs, + self._temporary_ips] # 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 @@ -214,6 +217,104 @@ class ConfigWriter: else: self._temporary_macs.Reserve(ec_id, mac) + def _UnlockedCommitIp(self, link, address): + """Commit a reserved IP address to an IP pool. + + The IP address is taken from the IP pool designated by link and marked + as reserved. + + """ + if link is None: + nicparams = self._config_data.cluster.nicparams[constants.VALUE_DEFAULT] + link = nicparams[constants.NIC_LINK] + + if link not in self._config_data.cluster.networks or\ + "4" not in self._config_data.cluster.networks[link]: + return False + + netdict = self._config_data.cluster.networks[link]["4"] + network = ippool.IPv4Network.fromdict(netdict) + try: + addr = network.reserve(address) + except ippool.IPv4PoolError, err: + raise errors.ConfigurationError("Unable to reserve IP: %s", str(err)) + + self._config_data.cluster.networks[link]["4"] = network.todict() + + @locking.ssynchronized(_config_lock) + def CommitIp(self, link, address): + """Commit a reserved IP to an IP pool + + This is just a wrapper around _UnlockedCommitIp. + + """ + self._UnlockedCommitIp(self, link) + self._WriteConfig() + + def _UnlockedReleaseIp(self, link, address): + """Give a specific IP address back to an IP pool. + + The IP address is returned to the IP pool designated by pool_id and marked + as reserved. + + """ + if link is None: + nicparams = self._config_data.cluster.nicparams[constants.VALUE_DEFAULT] + link = nicparams[constants.NIC_LINK] + + if link not in self._config_data.cluster.networks or\ + "4" not in self._config_data.cluster.networks[link]: + return + + netdict = self._config_data.cluster.networks[link]["4"] + network = ippool.IPv4Network.fromdict(netdict) + network.release(address) + self._config_data.cluster.networks[link]["4"] = network.todict() + + @locking.ssynchronized(_config_lock) + def ReleaseIp(self, link, address): + """Give a specified IP address back to an IP pool. + + This is just a wrapper around _UnlockedReleaseIp. + + """ + self._UnlockedReleaseIp(link, address) + self._WriteConfig() + + @locking.ssynchronized(_config_lock, shared=1) + def GenerateIp(self, link, ec_id): + """Find a free IPv4 address for an instance. + + """ + if link is None: + nicparams = self._config_data.cluster.nicparams[constants.VALUE_DEFAULT] + link = nicparams[constants.NIC_LINK] + + if link not in self._config_data.cluster.networks or\ + "4" not in self._config_data.cluster.networks[link]: + raise errors.OpPrereqError("No network defined on link %s exists" % link, + errors.ECODE_INVAL) + netdict = self._config_data.cluster.networks[link]["4"] + network = ippool.IPv4Network.fromdict(netdict) + return self._temporary_ips.Generate([], network.generate_free(), ec_id) + + @locking.ssynchronized(_config_lock, shared=1) + def ReserveIp(self, link, address, ec_id): + """Reserve a given IPv4 address for use by an instance. + + """ + if link is None: + nicparams = self._config_data.cluster.nicparams[constants.VALUE_DEFAULT] + link = nicparams[constants.NIC_LINK] + + if link not in self._config_data.cluster.networks or\ + "4" not in self._config_data.cluster.networks[link]: + return + netdict = self._config_data.cluster.networks[link]["4"] + network = ippool.IPv4Network.fromdict(netdict) + network.reserve(address) + return self._temporary_ips.Reserve(address, ec_id) + @locking.ssynchronized(_config_lock, shared=1) def ReserveLV(self, lv_name, ec_id): """Reserve an VG/LV pair for an instance. @@ -1087,6 +1188,9 @@ 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) self._EnsureUUID(instance, ec_id) @@ -1141,6 +1245,10 @@ 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) del self._config_data.instances[instance_name] self._config_data.cluster.serial_no += 1 self._WriteConfig() diff --git a/lib/objects.py b/lib/objects.py index dc25a4e95..b3f425673 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -1087,6 +1087,7 @@ class Cluster(TaggableObject): "blacklisted_os", "primary_ip_family", "prealloc_wipe_disks", + "networks", ] + _TIMESTAMPS + _UUID def UpgradeConfig(self): @@ -1173,6 +1174,10 @@ class Cluster(TaggableObject): if self.shared_file_storage_dir is None: self.shared_file_storage_dir = "" + # Network management + if self.networks is None: + self.networks = {} + def ToDict(self): """Custom function for cluster. -- GitLab