@ -33,6 +33,9 @@ from neutron.ipam import utils as ipam_utils
LOG = log . getLogger ( __name__ )
MAX_WIN = 1000
MULTIPLIER = 100
MAX_WIN_MULTI = MAX_WIN * MULTIPLIER
class NeutronDbSubnet ( ipam_base . Subnet ) :
@ -152,6 +155,10 @@ class NeutronDbSubnet(ipam_base.Subnet):
def _generate_ip ( self , context , prefer_next = False ) :
""" Generate an IP address from the set of available addresses. """
return self . _generate_ips ( context , prefer_next ) [ 0 ]
def _generate_ips ( self , context , prefer_next = False , num_addresses = 1 ) :
""" Generate a set of IPs from the set of available addresses. """
ip_allocations = netaddr . IPSet ( )
for ipallocation in self . subnet_manager . list_allocations ( context ) :
ip_allocations . add ( ipallocation . ip_address )
@ -163,16 +170,34 @@ class NeutronDbSubnet(ipam_base.Subnet):
if av_set . size == 0 :
continue
if av_set . size < num_addresses :
# Not enough addresses in pool to perform validation
# TODO(njohnston): How to handle when there are enough IPs but
# not enough in a single pool to satisfy the request?
continue
if prefer_next :
window = 1
allocated_ip_pool = list ( itertools . islice ( av_set ,
num_addresses ) )
return [ str ( allocated_ip )
for allocated_ip in allocated_ip_pool ]
window = min ( av_set . size , MAX_WIN )
# NOTE(gryf): If there is more than one address, make the window
# bigger, so that are chances to fulfill demanded amount of IPs.
if num_addresses > 1 :
window = min ( av_set . size , num_addresses * MULTIPLIER ,
MAX_WIN_MULTI )
if window < num_addresses :
continue
else :
# Compute a value for the selection window
window = min ( av_set . size , 30 )
ip_index = random . randint ( 1 , window )
candidate_ips = list ( itertools . islice ( av_set , ip_index ) )
allocated_ip = candidate_ips [
random . randint ( 0 , len ( candidate_ips ) - 1 ) ]
return str ( allocated_ip ) , ip_pool . id
# Maximize randomness by using the random module's built in
# sampling function
av_ips = list ( itertools . islice ( av_set , 0 , window ) )
allocated_ip_pool = random . sample ( av_ips , num_addresses )
return [ str ( allocated_ip ) for allocated_ip in allocated_ip_pool ]
raise ipam_exc . IpAddressGenerationFailure (
subnet_id = self . subnet_manager . neutron_id )
@ -182,7 +207,6 @@ class NeutronDbSubnet(ipam_base.Subnet):
# running transaction, which is started on create_port or upper level.
# To be able to do rollback/retry actions correctly ipam driver
# should not create new nested transaction blocks.
all_pool_id = None
# NOTE(salv-orlando): It would probably better to have a simpler
# model for address requests and just check whether there is a
# specific IP address specified in address_request
@ -194,8 +218,7 @@ class NeutronDbSubnet(ipam_base.Subnet):
else :
prefer_next = isinstance ( address_request ,
ipam_req . PreferNextAddressRequest )
ip_address , all_pool_id = self . _generate_ip ( self . _context ,
prefer_next )
ip_address = self . _generate_ip ( self . _context , prefer_next )
# Create IP allocation request object
# The only defined status at this stage is 'ALLOCATED'.
@ -215,6 +238,26 @@ class NeutronDbSubnet(ipam_base.Subnet):
subnet_id = self . subnet_manager . neutron_id )
return ip_address
def bulk_allocate ( self , address_request ) :
# The signature of this function differs from allocate only in that it
# returns a list of addresses, as opposed to a single address.
if not isinstance ( address_request , ipam_req . BulkAddressRequest ) :
return [ self . allocate ( address_request ) ]
num_addrs = address_request . num_addresses
allocated_ip_pool = self . _generate_ips ( self . _context ,
False ,
num_addrs )
# Create IP allocation request objects
try :
with self . _context . session . begin ( subtransactions = True ) :
for ip_address in allocated_ip_pool :
self . subnet_manager . create_allocation ( self . _context ,
ip_address )
except db_exc . DBReferenceError :
raise n_exc . SubnetNotFound (
subnet_id = self . subnet_manager . neutron_id )
return allocated_ip_pool
def deallocate ( self , address ) :
# This is almost a no-op because the Neutron DB IPAM driver does not
# delete IPAllocation objects at every deallocation. The only