def _generate_ip(self, session, prefer_next=False): """Generate an IP address from the set of available addresses.""" ip_allocations = netaddr.IPSet() for ipallocation in self.subnet_manager.list_allocations(session): ip_allocations.add(netaddr.IPAddress(ipallocation.ip_address)) for ip_pool in self.subnet_manager.list_pools(session): ip_set = netaddr.IPSet() ip_set.add(netaddr.IPRange(ip_pool.first_ip, ip_pool.last_ip)) av_set = ip_set.difference(ip_allocations) if av_set.size == 0: continue if prefer_next: window = 1 else: # Compute a value for the selection window window = min(av_set.size, 10) ip_index = random.randint(1, window) candidate_ips = list(itertools.islice(av_set, ip_index)) allocated_ip = candidate_ips[-1] return str(allocated_ip), ip_pool.id raise ipam_exc.IpAddressGenerationFailure( subnet_id=self.subnet_manager.neutron_id)
def backend_allocate(self, address_request): try: # allocate a specific IP if isinstance(address_request, ipam_req.SpecificAddressRequest): # This handles both specific and automatic address requests ip_address = str(address_request.address) self._vcns.allocate_ipam_ip_from_pool(self._nsx_pool_id, ip_addr=ip_address) else: # Allocate any free IP response = self._vcns.allocate_ipam_ip_from_pool( self._nsx_pool_id)[1] # get the ip from the response root = et.fromstring(response) ip_address = root.find('ipAddress').text except vc_exc.VcnsApiException as e: # handle backend failures error_code = self._get_vcns_error_code(e) if error_code == constants.NSX_ERROR_IPAM_ALLOCATE_IP_USED: # This IP is already in use raise ipam_exc.IpAddressAlreadyAllocated( ip=ip_address, subnet_id=self._subnet_id) if error_code == constants.NSX_ERROR_IPAM_ALLOCATE_ALL_USED: # No more IP addresses available on the pool raise ipam_exc.IpAddressGenerationFailure( subnet_id=self._subnet_id) else: raise ipam_exc.IPAllocationFailed() return ip_address
def backend_allocate(self, address_request): try: # allocate a specific IP if isinstance(address_request, ipam_req.SpecificAddressRequest): # This handles both specific and automatic address requests ip_address = str(address_request.address) # If this is the subnet gateway IP - no need to allocate it subnet = self.get_details() if str(subnet.gateway_ip) == ip_address: LOG.info("Skip allocation of gateway-ip for pool %s", self._nsx_pool_id) return ip_address else: # Allocate any free IP ip_address = None response = self.nsxlib_ipam.allocate(self._nsx_pool_id, ip_addr=ip_address) ip_address = response['allocation_id'] except nsx_lib_exc.ManagerError as e: LOG.error( "NSX IPAM failed to allocate ip %(ip)s of subnet " "%(id)s: %(e)s; code %(code)s", { 'e': e, 'ip': ip_address, 'id': self._subnet_id, 'code': e.error_code }) if e.error_code == error.ERR_CODE_IPAM_POOL_EXHAUSTED: # No more IP addresses available on the pool raise ipam_exc.IpAddressGenerationFailure( subnet_id=self._subnet_id) if e.error_code == error.ERR_CODE_IPAM_SPECIFIC_IP: # The NSX backend does not support allocation of specific IPs # prior to version 2.0. msg = (_("NSX-V3 IPAM driver does not support allocation of a " "specific ip %s for port") % ip_address) raise NotImplementedError(msg) if e.error_code == error.ERR_CODE_IPAM_IP_ALLOCATED: # This IP is already in use raise ipam_exc.IpAddressAlreadyAllocated( ip=ip_address, subnet_id=self._subnet_id) if e.error_code == error.ERR_CODE_OBJECT_NOT_FOUND: msg = (_("NSX-V3 IPAM failed to allocate: pool %s was not " "found") % self._nsx_pool_id) raise ipam_exc.IpamValueInvalid(message=msg) else: # another backend error raise ipam_exc.IPAllocationFailed() except Exception as e: LOG.error( "NSX IPAM failed to allocate ip %(ip)s of subnet " "%(id)s: %(e)s", { 'e': e, 'ip': ip_address, 'id': self._subnet_id }) # handle unexpected failures raise ipam_exc.IPAllocationFailed() return ip_address
def allocate_ip_from_pool(self, subnet_id, allocation_pools, mac, port_id=None, port_tenant_id=None, device_id=None, device_owner=None): hostname = uuidutils.generate_uuid() port_tenant_name = self.ib_cxt.get_tenant_name(port_tenant_id) ea_ip_address = eam.get_ea_for_ip(self.ib_cxt.user_id, port_tenant_id, port_tenant_name, self.ib_cxt.network, port_id, device_id, device_owner) dns_view = self.ib_cxt.mapping.dns_view zone_auth = self.pattern_builder.get_zone_name( is_external=self.ib_cxt.network_is_external) allocated_ip = None ip_alloc = (self.ib_cxt.dhcp_port_ip_alloc if device_owner == n_const.DEVICE_OWNER_DHCP else self.ib_cxt.ip_alloc) for pool in allocation_pools: first_ip = pool['start'] last_ip = pool['end'] try: allocated_ip = ip_alloc.allocate_ip_from_range( self.ib_cxt.mapping.network_view, dns_view, zone_auth, hostname, mac, first_ip, last_ip, ea_ip_address) if allocated_ip: break except ib_exc.InfobloxCannotAllocateIp as err: LOG.info("Failed to allocate IP from range (%s-%s)" "with error: %r." % (first_ip, last_ip, err)) continue except ib_exc.InfobloxCannotCreateObject as err: LOG.info("Failed to create IP from range (%s-%s)" "with error: %r." % (first_ip, last_ip, err)) continue if allocated_ip: LOG.info('IP address allocated on Infoblox NIOS: %s', allocated_ip) if device_owner == n_const.DEVICE_OWNER_DHCP: self.ib_cxt.update_nameservers(allocated_ip) else: LOG.info("All IPs from subnet %(subnet_id)s allocated", {'subnet_id': subnet_id}) raise ipam_exc.IpAddressGenerationFailure(subnet_id=subnet_id) return allocated_ip
def create_vmipreservation(self, address_request, vsdclient, is_l2, nuage_id): ip_type = self._get_ip_type() try: ipv4_address = ipv6_address = None if isinstance(address_request, ipam_req.SpecificAddressRequest): # This handles both specific and automatic address requests # Check availability of requested IP ip_address = str(address_request.address) self._verify_ip(self._context, ip_address) if ip_type == 'IPV4': ipv4_address = ip_address else: ipv6_address = ip_address vsdclient.create_vm_ip_reservation(is_l2, nuage_id, ip_type=ip_type, ipv4_address=ipv4_address, ipv6_address=ipv6_address) else: # Calculate allocation pools allocation_pools = self.subnet_manager.list_pools( self._context) ipreservation = vsdclient.create_vm_ip_reservation( is_l2, nuage_id, ip_type=ip_type, allocation_pools=allocation_pools) if ip_type == 'IPV4': ipv4_address = ip_address = ipreservation['ipv4_address'] else: ipv4_address = ip_address = ipreservation['ipv6_address'] except restproxy.RESTProxyError as e: if e.vsd_code == vsd_constants.VSD_DUPLICATE_VMIPRESERVATION: reason = ('The requested ip address is already reserved ' 'on VSD by another entity.') raise ipam_exc.InvalidAddressRequest(reason=reason) if e.vsd_code == vsd_constants.VSD_SUBNET_FULL: raise ipam_exc.IpAddressGenerationFailure( subnet_id=self._subnet_id) elif e.vsd_code == vsd_constants.VSD_IP_IN_USE_ERR_CODE: raise ipam_exc.InvalidAddressRequest(reason=str(e)) raise def rollback(db_api_conn): vsdclient.delete_vm_ip_reservation(is_l2, nuage_id, ipv4_address, ipv6_address) event.listen(self._context.session, "after_rollback", rollback) return ip_address
def _try_generate_ip(self, session): """Generate an IP address from availability ranges.""" ip_range = self.subnet_manager.get_first_range(session) if not ip_range: LOG.debug("All IPs from subnet %(subnet_id)s allocated", {'subnet_id': self.subnet_manager.neutron_id}) raise ipam_exc.IpAddressGenerationFailure( subnet_id=self.subnet_manager.neutron_id) # A suitable range was found. Return IP address. ip_address = ip_range['first_ip'] LOG.debug("Allocated IP - %(ip_address)s from range " "[%(first_ip)s; %(last_ip)s]", {'ip_address': ip_address, 'first_ip': ip_address, 'last_ip': ip_range['last_ip']}) return ip_address, ip_range['allocation_pool_id']
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) for ip_pool in self.subnet_manager.list_pools(context): ip_set = netaddr.IPSet() ip_set.add(netaddr.IPRange(ip_pool.first_ip, ip_pool.last_ip)) av_set = ip_set.difference(ip_allocations) 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: 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: # 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)
def _generate_ips(self, context, prefer_next=False, num_addresses=1): """Generate a set of IPs from the set of available addresses.""" allocated_ips = [] requested_num_addresses = num_addresses 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): ip_set = netaddr.IPSet() ip_set.add(netaddr.IPRange(ip_pool.first_ip, ip_pool.last_ip)) av_set = ip_set.difference(ip_allocations) if av_set.size == 0: continue if av_set.size < requested_num_addresses: # All addresses of the address pool are allocated # for the first time and the remaining addresses # will be allocated in the next address pools. 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: allocated_ip_pool = list( itertools.islice(av_set, 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 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 allocated_num_addresses > 1: window = min(av_set.size, allocated_num_addresses * MULTIPLIER, MAX_WIN_MULTI) if window < allocated_num_addresses: continue # 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, 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( subnet_id=self.subnet_manager.neutron_id)