Commit 28fc9be3 authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

cyclades: Fix reconciliation for network and pools

Fix reconciliation to work with multiple IP pools per network:
* Fix reconcile-networks command to check if a reserved_ip in a Ganeti
  network is reserved in the pool that contains this IP (if any)
* Fix reconcile-pools to reconcile each of the networks IP pools and
  take into account only those IP address that are contained in the
  pool.
parent 6491476e
......@@ -532,29 +532,29 @@ class Network(models.Model):
if not backend_exists:
BackendNetwork.objects.create(backend=backend, network=self)
def get_pool(self, locked=True):
try:
subnet = self.subnets.get(ipversion=4, deleted=False)
except Subnet.DoesNotExist:
raise pools.EmptyPool
return subnet.get_pool(locked=locked)
def allocate_address(self, userid):
try:
subnet = self.subnets.get(ipversion=4, deleted=False)
except Subnet.DoesNotExist:
raise pools.EmptyPool
return subnet.allocate_address(userid)
def get_ip_pools(self, locked=True):
subnets = self.subnets.filter(ipversion=4, deleted=False)\
.prefetch_related("ip_pools")
return [ip_pool for subnet in subnets
for ip_pool in subnet.get_ip_pools(locked=locked)]
def reserve_address(self, address, external=False):
pool = self.get_pool()
pool.reserve(address, external=external)
pool.save()
for ip_pool in self.get_ip_pools():
if ip_pool.contains(address):
ip_pool.reserve(address, external=external)
ip_pool.save()
return
raise pools.InvalidValue("Network %s does not have an IP pool that"
" contains address %s" % (self, address))
def release_address(self, address, external=True):
pool = self.get_pool()
pool.put(address, external=external)
pool.save()
for ip_pool in self.get_ip_pools():
if ip_pool.contains(address):
ip_pool.release(address, external=external)
ip_pool.save()
return
raise pools.InvalidValue("Network %s does not have an IP pool that"
" contains address %s" % (self, address))
@property
def subnet4(self):
......@@ -571,13 +571,11 @@ class Network(models.Model):
def ip_count(self):
"""Return the total and free IPv4 addresses of the network."""
subnets = self.subnets.filter(ipversion=4).prefetch_related("ip_pools")
total, free = 0, 0
for subnet in subnets:
for ip_pool in subnet.ip_pools.all():
pool = ip_pool.pool
total += pool.pool_size
free += pool.count_available()
ip_pools = self.get_ip_pools(locked=False)
for ip_pool in ip_pools:
total += ip_pool.pool_size
free += ip_pool.count_available()
return total, free
class InvalidBackendIdError(Exception):
......@@ -624,20 +622,11 @@ class Subnet(models.Model):
msg = u"<Subnet %s, Network: %s, CIDR: %s>"
return msg % (self.id, self.network_id, self.cidr)
def get_pool(self, locked=True):
if self.ipversion == 6:
raise Exception("IPv6 Subnets have no IP Pool.")
def get_ip_pools(self, locked=True):
ip_pools = self.ip_pools
if locked:
ip_pools = ip_pools.select_for_update()
return ip_pools.all()[0].pool
def allocate_address(self, userid):
pool = self.get_pool(locked=True)
address = pool.get()
pool.save()
return IPAddress.objects.create(network=self.network, subnet=self,
address=address, userid=userid)
return map(lambda ip_pool: ip_pool.pool, ip_pools.all())
class BackendNetwork(models.Model):
......
......@@ -508,7 +508,7 @@ class NetworkReconciler(object):
corresponding Ganeti networks in all Ganeti backends.
"""
network_ip_pool = network.get_pool() # X-Lock on IP Pool
ip_pools = network.get_ip_pools() # X-Lock on IP pools
for bend in self.backends:
bnet = get_backend_network(network, bend)
gnet = self.ganeti_networks[bend].get(network.id)
......@@ -563,14 +563,16 @@ class NetworkReconciler(object):
if externally_reserved:
for ip in externally_reserved.split(","):
ip = ip.strip()
if not network_ip_pool.is_reserved(ip):
msg = ("D: IP '%s' is reserved for network '%s' in"
" backend '%s' but not in DB.")
self.log.info(msg, ip, network, bend)
if self.fix:
network_ip_pool.reserve(ip, external=True)
network_ip_pool.save()
self.log.info("F: Reserved IP '%s'", ip)
for ip_pool in ip_pools:
if ip_pool.contains(ip):
if not ip_pool.is_reserved(ip):
msg = ("D: IP '%s' is reserved for network"
" '%s' in backend '%s' but not in DB.")
self.log.info(msg, ip, network, bend)
if self.fix:
ip_pool.reserve(ip, external=True)
ip_pool.save()
self.log.info("F: Reserved IP '%s'", ip)
def reconcile_parted_network(self, network, backend):
self.log.info("D: Missing DB entry for network %s in backend %s",
......@@ -699,24 +701,17 @@ class PoolReconciler(object):
@transaction.commit_on_success
def reconcile_ip_pool(self, network):
# Check that all NICs have unique IPv4 address
nics = network.nics.filter(ipv4__isnull=False)
check_unique_values(objects=nics, field='ipv4', logger=self.log)
# Check that all Floating IPs have unique IPv4 address
floating_ips = network.floating_ips.filter(deleted=False)
check_unique_values(objects=floating_ips, field='ipv4',
logger=self.log)
# First get(lock) the IP pool of the network to prevent new NICs
# from being created.
network_ip_pool = network.get_pool()
used_ips = set(list(nics.values_list("ipv4", flat=True)) +
list(floating_ips.values_list("ipv4", flat=True)))
check_pool_consistent(pool=network_ip_pool,
pool_class=pools.IPPool,
used_values=used_ips,
fix=self.fix, logger=self.log)
nics = network.ips.all()
check_unique_values(objects=nics, field="address", logger=self.log)
for ip_pool in network.get_ip_pools():
used_ips = ip_pool.pool_table.subnet.ips.values_list("address",
flat=True)
used_ips = filter(lambda x: ip_pool.contains(x), used_ips)
check_pool_consistent(pool=ip_pool,
pool_class=pools.IPPool,
used_values=used_ips,
fix=self.fix, logger=self.log)
def check_unique_values(objects, field, logger):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment