def _allocate_specific_ip(context, subnet_id, ip_address): """Allocate a specific IP address on the subnet.""" ip = int(netaddr.IPAddress(ip_address)) range_qry = context.session.query(models_v2.IPAvailabilityRange).join( models_v2.IPAllocationPool).with_lockmode('update') results = range_qry.filter_by(subnet_id=subnet_id) for ip_range in results: first = int(netaddr.IPAddress(ip_range['first_ip'])) last = int(netaddr.IPAddress(ip_range['last_ip'])) if first <= ip <= last: if first == last: context.session.delete(ip_range) return elif first == ip: new_first_ip = str(netaddr.IPAddress(ip_address) + 1) ip_range['first_ip'] = new_first_ip return elif last == ip: new_last_ip = str(netaddr.IPAddress(ip_address) - 1) ip_range['last_ip'] = new_last_ip return else: # Adjust the original range to end before ip_address old_last_ip = ip_range['last_ip'] new_last_ip = str(netaddr.IPAddress(ip_address) - 1) ip_range['last_ip'] = new_last_ip # Create a new second range for after ip_address new_first_ip = str(netaddr.IPAddress(ip_address) + 1) new_ip_range = models_v2.IPAvailabilityRange( allocation_pool_id=ip_range['allocation_pool_id'], first_ip=new_first_ip, last_ip=old_last_ip) context.session.add(new_ip_range) return
def _make_subnet(self, n, cidr_prefix, network_id): session = self.context.session subnet = models_v2.Subnet(tenant_id='fake-tenant-id', name='test-subnet-{0}'.format(n), network_id=network_id, ip_version=4, cidr='{0}.0/24'.format(cidr_prefix), gateway_ip='{0}.1'.format(cidr_prefix), enable_dhcp=True, shared=False) session.add(subnet) session.flush() ippool = models_v2.IPAllocationPool( subnet_id=subnet['id'], first_ip='{0}.1'.format(cidr_prefix), last_ip='{0}.254'.format(cidr_prefix)) session.add(ippool) session.flush() iprange = models_v2.IPAvailabilityRange( allocation_pool_id=ippool['id'], first_ip='{0}.1'.format(cidr_prefix), last_ip='{0}.254'.format(cidr_prefix)) session.add(iprange) session.flush() return subnet
def _save_allocation_pools(self, context, subnet, allocation_pools): for pool in allocation_pools: ip_pool = models_v2.IPAllocationPool(subnet=subnet, first_ip=pool['start'], last_ip=pool['end']) context.session.add(ip_pool) ip_range = models_v2.IPAvailabilityRange(ipallocationpool=ip_pool, first_ip=pool['start'], last_ip=pool['end']) context.session.add(ip_range)
def save_allocation_pools(self, context, subnet, allocation_pools): for pool in allocation_pools: first_ip = str(netaddr.IPAddress(pool.first, pool.version)) last_ip = str(netaddr.IPAddress(pool.last, pool.version)) ip_pool = models_v2.IPAllocationPool(subnet=subnet, first_ip=first_ip, last_ip=last_ip) context.session.add(ip_pool) ip_range = models_v2.IPAvailabilityRange(ipallocationpool=ip_pool, first_ip=first_ip, last_ip=last_ip) context.session.add(ip_range)
def _rebuild_availability_ranges(context, subnets): """Rebuild availability ranges. This method is called only when there's no more IP available or by _update_subnet_allocation_pools. Calling _update_subnet_allocation_pools before calling this function deletes the IPAllocationPools associated with the subnet that is updating, which will result in deleting the IPAvailabilityRange too. """ ip_qry = context.session.query( models_v2.IPAllocation).with_lockmode('update') # PostgreSQL does not support select...for update with an outer join. # No join is needed here. pool_qry = context.session.query( models_v2.IPAllocationPool).options( orm.noload('available_ranges')).with_lockmode('update') for subnet in sorted(subnets): LOG.debug("Rebuilding availability ranges for subnet %s", subnet) # Create a set of all currently allocated addresses ip_qry_results = ip_qry.filter_by(subnet_id=subnet['id']) allocations = netaddr.IPSet([netaddr.IPAddress(i['ip_address']) for i in ip_qry_results]) for pool in pool_qry.filter_by(subnet_id=subnet['id']): # Create a set of all addresses in the pool poolset = netaddr.IPSet(netaddr.IPRange(pool['first_ip'], pool['last_ip'])) # Use set difference to find free addresses in the pool available = poolset - allocations # Generator compacts an ip set into contiguous ranges def ipset_to_ranges(ipset): first, last = None, None for cidr in ipset.iter_cidrs(): if last and last + 1 != cidr.first: yield netaddr.IPRange(first, last) first = None first, last = first if first else cidr.first, cidr.last if first: yield netaddr.IPRange(first, last) # Write the ranges to the db for ip_range in ipset_to_ranges(available): available_range = models_v2.IPAvailabilityRange( allocation_pool_id=pool['id'], first_ip=str(netaddr.IPAddress(ip_range.first)), last_ip=str(netaddr.IPAddress(ip_range.last))) context.session.add(available_range)