- 
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)
 
- 
流程图梳理
- 
![]()