一 类ML2Plugin中的处理
Plugin主要把握进行Port Binding的时机主要有下面3个:
1 port创建时,即create_port()执行时,如果已经提供了足够的信息,则需要第一时间进行bind。
2 由于update_port()导致端口信息变化时也需要重新bind端口。
3 Agent通过RPC从Plugin获取port信息时,也会尝试bind端口,这样会避免在获取信息的过程中,由于其他进程更新了port但是并没有bind而导致Agent不能获取最新的bind信息。
下面源码是第1种情况。
#neutron/plugins/ml2/plugin.py
def create_port(self, context, port):
attrs = port['port']
attrs['status'] = const.PORT_STATUS_DOWN
session = context.session
with session.begin(subtransactions=True):
#在数据库创建binding信息,设置port_id,设置vif_type为"unbound"
binding = db.add_port_binding(session, result['id'])
mech_context = driver_context.PortContext(self, context, result,
network, binding)
new_host_port = self._get_host_port_if_changed(mech_context, attrs)
#从传递过来的RESTful请求参数中获取host、vnic等信息,
#并查询数据库是否已经有相应条目存在,如果没有
#或者有但是这里获取的信息不一致,则将vif_type设置为"unbound"
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)
try:
#如果需要且能够绑定,则进行bind
bound_context = self._bind_port_if_needed(mech_context)
return bound_context._port
至于update_port(),Port Binding的处理逻辑与create_port()相同,它们进行端口bind时数据库session已经结束,这么做的原因是,有些Driver可能需要在绑定过程中进行RPC通信,此时应避免持有数据库transaction,但是如此一来就可能会导致bind端口时引起竞争。
上面代码中_bind_port_if_needed()的目的就是处理这种竞争,它的基本思路是:如果将binding信息进行数据库提交的时候,另一个进程已经提交了binding信息则使用另外一个进程的结果,如果另外一个进程仅仅是改变了binding的信息但是还没有提交,则尝试将新的binding信息提交到数据库。
二 Mechanism Manager的处理
Mechanism Manager的处理逻辑很简单,仅仅是实现bind_port()方法:依次尝试调用每一个注册的Mechanism Driver,直到成功bind。
#neutron/plugins/ml2/managers.py
def bind_port(self, context):
binding = context._binding
#依次尝试调用每一个注册的Mechanism Driver
for driver in self.ordered_mech_drivers:
try:
#非常直接的调用每一个注册的Mechanim Driver
driver.obj.bind_port(context)
if binding.segment:
#bind成功
binding.driver = driver.name
return
三 Mechanism Driver的处理
以Linux Bridge Driver为例进行说明。
#neutron/plugins/ml2/drivers/mech_agent.py
class AgentMechanismDriverBase(api.MechanismDriver):
def bind_port(self, context):
vnic_type = context.current.get(portbindings.VNIC_TYPE,
portbindings.VNIC_NORMAL)
if vnic_type not in self.supported_vnic_types:
return
for agent in context.host_agents(self.agent_type):
LOG.debug(_("Checking agent: %s"), agent)
if agent['alive']:
for segment in context.network.network_segments:
if self.try_to_bind_segment_for_agent(context, segment,
agent):
return
AgentMechanismDriverBase实现了公用的bind_port()方法。
它的基本执行逻辑是:
寻找port所在host的所有该Driver的Agent(对于Linux Bridge Driver来说仅有一个),并确保Agent处于live状态,然后通过具体Driver对每个Segment进行逐一检查,如果此Segment能够提供符合条件的port,则返回binding信息给Ml2Plugin。