Create port ip分配流程梳理
-
self.ipam.allocate_ips_for_port_and_store( context, port, port_id)
-
#/usr/lib/python2.7/site-packages/neutron/db/ipam_pluggable_backend.py def allocate_ips_for_port_and_store(self, context, port, port_id): # Make a copy of port dict to prevent changing # incoming dict by adding 'id' to it. # Deepcopy doesn't work correctly in this case, because copy of # ATTR_NOT_SPECIFIED object happens. Address of copied object doesn't # match original object, so 'is' check fails port_copy = {'port': port['port'].copy()} port_copy['port']['id'] = port_id network_id = port_copy['port']['network_id'] ips = [] try: #tip 1 ips = self._allocate_ips_for_port(context, port_copy) # ips:[{'subnet_id': u'3dc7c748-99ac-471f-bfb6-fcebbee4d223', 'ip_address': '192.168.193.2'}] # ips has been allocated by self._allocate_ips_for_port(context, port_copy) for ip in ips: ip_address = ip['ip_address'] subnet_id = ip['subnet_id'] IpamPluggableBackend._store_ip_allocation( context, ip_address, network_id, subnet_id, port_id) return ips except Exception: with excutils.save_and_reraise_exception(): if ips: ipam_driver = driver.Pool.get_instance(None, context) if not ipam_driver.needs_rollback(): return LOG.debug("An exception occurred during port creation. " "Reverting IP allocation") self._safe_rollback(self._ipam_deallocate_ips, context, ipam_driver, port_copy['port'], ips, revert_on_fail=False)
-
_allocate_ips_for_port()
-
def _allocate_ips_for_port(self, context, port): """Allocate IP addresses for the port. IPAM version. If port['fixed_ips'] is set to 'ATTR_NOT_SPECIFIED', allocate IP addresses for the port. If port['fixed_ips'] contains an IP address or a subnet_id then allocate an IP address accordingly. """ p = port['port'] fixed_configured = p['fixed_ips'] is not constants.ATTR_NOT_SPECIFIED #NOTE(zcc):get all eligible subnets subnets = self._ipam_get_subnets(context, network_id=p['network_id'], host=p.get(portbindings.HOST_ID), service_type=p.get('device_owner'), fixed_configured=fixed_configured) #NOTE(zcc):classify subnets by ip-version v4, v6_stateful, v6_stateless = self._classify_subnets( context, subnets) if fixed_configured: #TODO(zcc):if the fixed_ips: ips = self._test_fixed_ips_for_port(context, p["network_id"], p['fixed_ips'], p['device_owner'], subnets) else: ips = [] version_subnets = [v4, v6_stateful] for subnets in version_subnets: if subnets: ips.append([{'subnet_id': s['id']} for s in subnets]) # ips: [[{'subnet_id': u'3dc7c748-99ac-471f-bfb6-fcebbee4d223'}, {'subnet_id': u'00316985-d755-4df0-a301-90b925764ec9'}]] ips.extend(self._get_auto_address_ips(v6_stateless, p)) ipam_driver = driver.Pool.get_instance(None, context) return self._ipam_allocate_ips(context, ipam_driver, p, ips)
-
._ipam_allocate_ips(context, ipam_driver, p, ips)
-
def _ipam_allocate_ips(self, context, ipam_driver, port, ips, revert_on_fail=True): """Allocate set of ips over IPAM. If any single ip allocation fails, tries to deallocate all allocated ip addresses. """ allocated = [] # we need to start with entries that asked for a specific IP in case # those IPs happen to be next in the line for allocation for ones that # didn't ask for a specific IP #NOTE(zcc):sorted by subnet-id ips.sort(key=lambda x: 'ip_address' not in x) try: for ip in ips: # By default IP info is dict, used to allocate single ip # from single subnet. # IP info can be list, used to allocate single ip from # multiple subnets ip_list = [ip] if isinstance(ip, dict) else ip #'ip_list:', [{'subnet_id': u'3dc7c748-99ac-471f-bfb6-fcebbee4d223'}, {'subnet_id': u'00316985-d755-4df0-a301-90b925764ec9'}] subnets = [ip_dict['subnet_id'] for ip_dict in ip_list] try: factory = ipam_driver.get_address_request_factory() #NOTE(zcc):judage ip type with port and ip_list[0] ip_request = factory.get_request(context, port, ip_list[0]) #NOTE(zcc): /usr/lib/python2.7/site-packages/neutron/ipam/subnet_alloc.py def get_allocator(self, subnet_ids): return IpamSubnetGroup(self, subnet_ids) ipam_allocator = ipam_driver.get_allocator(subnets) ip_address, subnet_id = ipam_allocator.allocate(ip_request) #(ip_address:'192.168.191.3', subnet_id: '00316985-d755-4df0-a301-90b925764ec9') except ipam_exc.IpAddressGenerationFailureAllSubnets: raise n_exc.IpAddressGenerationFailure( net_id=port['network_id']) allocated.append({'ip_address': ip_address, 'subnet_id': subnet_id}) except Exception: with excutils.save_and_reraise_exception(): if not ipam_driver.needs_rollback(): return LOG.debug("An exception occurred during IP allocation.") if revert_on_fail and allocated: LOG.debug("Reverting allocation") # In case of deadlock deallocation fails with db error # and rewrites original exception preventing db_retry # wrappers from restarting entire api request. self._safe_rollback(self._ipam_deallocate_ips, context, ipam_driver, port, allocated, revert_on_fail=False) elif not revert_on_fail and ips: addresses = ', '.join(self._get_failed_ips(ips, allocated)) LOG.error("IP allocation failed on " "external system for %s", addresses) return allocated
-
IpamsubnetGroup(driver.SubnetGroup)
-
#/usr/lib/python2.7/site-packages/neutron/ipam/subnet_alloc.py class IpamSubnetGroup(driver.SubnetGroup): def __init__(self, driver, subnet_ids): self._driver = driver self._subnet_ids = subnet_ids def allocate(self, address_request): '''Originally, the Neutron pluggable IPAM backend would ask the driver to try to allocate an IP from each subnet in turn, one by one. This implementation preserves that behavior so that existing drivers work as they did before while giving them the opportunity to optimize it by overridding the implementation. ''' for subnet_id in self._subnet_ids: try: #NOTE(zcc):judage this subnet has avaliable ips, if has ,return ipam_subnet = self._driver.get_subnet(subnet_id) return ipam_subnet.allocate(address_request), subnet_id except ipam_exc.IpAddressGenerationFailure: continue raise ipam_exc.IpAddressGenerationFailureAllSubnets()
-
ipam_subnet.allocate(address_request)
-
#/usr/lib/python2.7/site-packages/neutron/ipam/drivers/neutrondb_ipam/driver.py def allocate(self, address_request): # NOTE(pbondar): Ipam driver is always called in context of already # 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 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) else: prefer_next = isinstance(address_request, ipam_req.PreferNextAddressRequest) # prefer_next: False # NOTE(jetlee): Now, we use tooz to resolve the collision # of allocate ip address # do else if cfg.CONF.tooz.enabled and cfg.CONF.tooz.ip_lock and \ self._context.external_net_flag: LOG.debug("before generate ip by tooz lock.") self._context.lock_dict = {} ip_address, all_pool_id, used_ip = \ self._generate_ip_by_tooz_lock(self._context, prefer_next) LOG.debug("Those ip %s was ocupied by other process.", used_ip) else: #generate ip_address ip_address, all_pool_id = self._generate_ip(self._context, prefer_next) # Create IP allocation request object # The only defined status at this stage is 'ALLOCATED'. # More states will be available in the future - e.g.: RECYCLABLE try: # TODO(ataraday): revisit this after objects switched to # new enginefacade with self._context.session.begin(subtransactions=True): # NOTE(kevinbenton): we use a subtransaction to force # a flush here so we can capture DBReferenceErrors due # to concurrent subnet deletions. (galera would deadlock # later on final commit) 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 ip_address
-
_generate_ip()
-
#/usr/lib/python2.7/site-packages/neutron/ipam/drivers/neutrondb_ipam/driver.py def _generate_ip(self, context, prefer_next=False): """Generate an IP address from the set of available addresses.""" ip_allocations = netaddr.IPSet() #get all used ip under this subnet for ipallocation in self.subnet_manager.list_allocations(context): ip_allocations.add(ipallocation.ip_address) #get all ips between first_ip and last_ip 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)) #get avaliable ips 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, 30) ip_index = random.randint(1, window) #get candidate_ips candidate_ips = list(itertools.islice(av_set, ip_index)) #get random ip from candidate_ips allocated_ip = candidate_ips[ random.randint(0, len(candidate_ips) - 1)] return str(allocated_ip), ip_pool.id raise ipam_exc.IpAddressGenerationFailure( subnet_id=self.subnet_manager.neutron_id)
-
流程图梳理