Merge "Update the processing of assigned addresses when assigning addresses" into stable/rocky

This commit is contained in:
Zuul 2020-07-17 14:23:34 +00:00 committed by Gerrit Code Review
commit 5a5a152984
2 changed files with 67 additions and 16 deletions

View File

@ -159,9 +159,16 @@ class NeutronDbSubnet(ipam_base.Subnet):
def _generate_ips(self, context, prefer_next=False, num_addresses=1): def _generate_ips(self, context, prefer_next=False, num_addresses=1):
"""Generate a set of IPs from the set of available addresses.""" """Generate a set of IPs from the set of available addresses."""
ip_allocations = netaddr.IPSet() allocated_ips = []
for ipallocation in self.subnet_manager.list_allocations(context): requested_num_addresses = num_addresses
ip_allocations.add(ipallocation.ip_address)
allocations = self.subnet_manager.list_allocations(context)
# It is better not to use 'netaddr.IPSet.add',
# because _compact_single_network in 'IPSet.add'
# is quite time consuming.
ip_allocations = netaddr.IPSet(
[netaddr.IPAddress(allocation.ip_address)
for allocation in allocations])
for ip_pool in self.subnet_manager.list_pools(context): for ip_pool in self.subnet_manager.list_pools(context):
ip_set = netaddr.IPSet() ip_set = netaddr.IPSet()
@ -170,34 +177,53 @@ class NeutronDbSubnet(ipam_base.Subnet):
if av_set.size == 0: if av_set.size == 0:
continue continue
if av_set.size < num_addresses: if av_set.size < requested_num_addresses:
# Not enough addresses in pool to perform validation # All addresses of the address pool are allocated
# TODO(njohnston): How to handle when there are enough IPs but # for the first time and the remaining addresses
# not enough in a single pool to satisfy the request? # will be allocated in the next address pools.
continue allocated_num_addresses = av_set.size
else:
# All expected addresses can be assigned in this loop.
allocated_num_addresses = requested_num_addresses
if prefer_next: if prefer_next:
allocated_ip_pool = list(itertools.islice(av_set, allocated_ip_pool = list(itertools.islice(av_set,
num_addresses)) allocated_num_addresses))
return [str(allocated_ip) allocated_ips.extend([str(allocated_ip)
for allocated_ip in allocated_ip_pool] for allocated_ip in allocated_ip_pool])
requested_num_addresses -= allocated_num_addresses
if requested_num_addresses:
# More addresses need to be allocated in the next loop.
continue
return allocated_ips
window = min(av_set.size, MAX_WIN) window = min(av_set.size, MAX_WIN)
# NOTE(gryf): If there is more than one address, make the window # NOTE(gryf): If there is more than one address, make the window
# bigger, so that are chances to fulfill demanded amount of IPs. # bigger, so that are chances to fulfill demanded amount of IPs.
if num_addresses > 1: if allocated_num_addresses > 1:
window = min(av_set.size, num_addresses * MULTIPLIER, window = min(av_set.size,
allocated_num_addresses * MULTIPLIER,
MAX_WIN_MULTI) MAX_WIN_MULTI)
if window < num_addresses: if window < allocated_num_addresses:
continue continue
else: else:
# Maximize randomness by using the random module's built in # Maximize randomness by using the random module's built in
# sampling function # sampling function
av_ips = list(itertools.islice(av_set, 0, window)) av_ips = list(itertools.islice(av_set, 0, window))
allocated_ip_pool = random.sample(av_ips, num_addresses) allocated_ip_pool = random.sample(av_ips,
return [str(allocated_ip) for allocated_ip in allocated_ip_pool] allocated_num_addresses)
allocated_ips.extend([str(allocated_ip)
for allocated_ip in allocated_ip_pool])
requested_num_addresses -= allocated_num_addresses
if requested_num_addresses:
# More addresses need to be allocated in the next loop.
continue
return allocated_ips
raise ipam_exc.IpAddressGenerationFailure( raise ipam_exc.IpAddressGenerationFailure(
subnet_id=self.subnet_manager.neutron_id) subnet_id=self.subnet_manager.neutron_id)

View File

@ -381,6 +381,31 @@ class TestNeutronDbIpamSubnet(testlib_api.SqlTestCase,
ipam_subnet.bulk_allocate, ipam_subnet.bulk_allocate,
ipam_req.BulkAddressRequest(target_ip_count)) ipam_req.BulkAddressRequest(target_ip_count))
def test_bulk_allocate_multiple_address_pools(self):
target_ip_count = 10
# 11 addresses available
allocation_pools = [{'start': '192.168.0.5', 'end': '192.168.0.9'},
{'start': '192.168.0.15', 'end': '192.168.0.20'}]
ipam_subnet = self._create_and_allocate_ipam_subnet(
'192.168.0.0/24', allocation_pools=allocation_pools,
ip_version=constants.IP_VERSION_4)[0]
ip_addresses = ipam_subnet.bulk_allocate(
ipam_req.BulkAddressRequest(target_ip_count))
self.assertEqual(target_ip_count, len(ip_addresses))
self.assertRaises(ipam_exc.IpAddressGenerationFailure,
ipam_subnet.bulk_allocate,
ipam_req.BulkAddressRequest(2))
def test_prefernext_allocate_multiple_address_pools(self):
ipam_subnet = self._create_and_allocate_ipam_subnet(
'192.168.0.0/30', ip_version=constants.IP_VERSION_4)[0]
ipam_subnet.allocate(ipam_req.PreferNextAddressRequest())
# The second address generation request on a /30 for v4 net must fail
self.assertRaises(ipam_exc.IpAddressGenerationFailure,
ipam_subnet.allocate,
ipam_req.PreferNextAddressRequest)
def _test_deallocate_address(self, cidr, ip_version): def _test_deallocate_address(self, cidr, ip_version):
ipam_subnet = self._create_and_allocate_ipam_subnet( ipam_subnet = self._create_and_allocate_ipam_subnet(
cidr, ip_version=ip_version)[0] cidr, ip_version=ip_version)[0]