(4)理解 neutron ml2---port創建流程代碼解析


port是neutron的核心資源之一,port的主要目的是承載Mac地址和ip地址。
有了Mac和ip地址虛擬機才能夠實現彼此之間的通信。當然port不一定是
僅僅給虛擬機使用,也可能將port綁定在路由器上。一個port一定是屬於一個
networks的,但是一個port有可能屬於多個subnets,屬於多個subnets意味着
一個網卡可以有多個ip地址,在創建虛擬機綁定網絡的時候可以指定掛載的網卡
和對應的fixed_ip。
 
網卡創建的代碼依然是放在了plugin.py這個文件里
/neutron/plugins/ml2/plugin.py
@utils.transaction_guard
@db_api.retry_if_session_inactive()
def create_port(self, context, port):
    result, mech_context = self._create_port_db(context, port)
    # notify any plugin that is interested in port create events
    kwargs = {'context': context, 'port': result}
    registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)

    try:
        self.mechanism_manager.create_port_postcommit(mech_context)
    except ml2_exc.MechanismDriverError:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("mechanism_manager.create_port_postcommit "
                          "failed, deleting port '%s'"), result['id'])
            self.delete_port(context, result['id'], l3_port_check=False)

    self.notify_security_groups_member_updated(context, result)

    try:
        bound_context = self._bind_port_if_needed(mech_context)
    except os_db_exception.DBDeadlock:
        # bind port can deadlock in normal operation so we just cleanup
        # the port and let the API retry
        with excutils.save_and_reraise_exception():
            LOG.debug("_bind_port_if_needed deadlock, deleting port %s",
                      result['id'])
            self.delete_port(context, result['id'])
    except ml2_exc.MechanismDriverError:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("_bind_port_if_needed "
                          "failed, deleting port '%s'"), result['id'])
            self.delete_port(context, result['id'], l3_port_check=False)

    return bound_context.current

其中最重要的是上面四個步驟,最最重要的是_create_port_db這個函數。

def _create_port_db(self, context, port):
    attrs = port[attributes.PORT]
    if not attrs.get('status'):
        attrs['status'] = const.PORT_STATUS_DOWN

    session = context.session
    with session.begin(subtransactions=True):
        dhcp_opts = attrs.get(edo_ext.EXTRADHCPOPTS, [])
        port_db = self.create_port_db(context, port)
        result = self._make_port_dict(port_db, process_extensions=False)
        self.extension_manager.process_create_port(context, attrs, result)
        self._portsec_ext_port_create_processing(context, result, port)

        # sgids must be got after portsec checked with security group
        sgids = self._get_security_groups_on_port(context, port)
        self._process_port_create_security_group(context, result, sgids)
        network = self.get_network(context, result['network_id'])
        binding = db.add_port_binding(session, result['id'])
        mech_context = driver_context.PortContext(self, context, result,
                                                  network, binding, None)
        self._process_port_binding(mech_context, attrs)

        result[addr_pair.ADDRESS_PAIRS] = (
            self._process_create_allowed_address_pairs(
                context, result,
                attrs.get(addr_pair.ADDRESS_PAIRS)))
        self._process_port_create_extra_dhcp_opts(context, result,
                                                  dhcp_opts)
        self.mechanism_manager.create_port_precommit(mech_context)
        self._setup_dhcp_agent_provisioning_component(context, result)

    self._apply_dict_extend_functions('ports', result, port_db)
    return result, mech_context

如上圖所示_create_port_db做了很多和安全組以及port_binding相關的工作:主要是將port,security_group, port_binding等相關數據存入數據庫,從中可以總結出ml2關於數據庫
操作的邏輯,涉及數據存儲到數據庫的工作都會放在_create_xxx_db(xxx代表subnet, port, network等)函數內部進行操作。比如這里的創建_create_port_db。
db.add_port_binding(session, result['id'])

這句話處理port_binding相關的入庫工作。具體代碼如下所示:
def add_port_binding(session, port_id):
    with session.begin(subtransactions=True):
        record = models.PortBinding(
            port_id=port_id,
            vif_type=portbindings.VIF_TYPE_UNBOUND)
        session.add(record)
        return record
 
create_port_db則處理的是port本身的入庫邏輯。port的創建有一個比較重要的邏輯就是分配ip地址,ip地址和mac地址是port這個資源模型所要承載的最終要的兩個數據。

 下面看一下分配ip地址的代碼:

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:
        ips = 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)

 

 

 

 

 

 

 

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM