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)
