目录
Extension以及Port binding简述
Port binding 的属性
Port binding 的属性在Neutron的mechanism driver中作用
SimpleAgentMechanismDriverBase
SriovNicSwitchMechanismDriver
LinuxbridgeMechanismDriver
OpenvswitchMechanismDriver
Port binding 的属性在Nova的虚拟化virt vif driver中作用
Port binding 的属性在Ironic的
Extension 是 Neutron 项目中对基本资源实现属性扩展的手段,而 Portbinding 则是 Neutron 在早期即引入的一个扩展模块,可以实现对 Port 资源的属性扩展。该扩展模块在 neutron 及 Agent 将 port 和实际网卡资源绑定时发挥重要作用,使得管理员可以人工指定或者获取 port 的物理绑定信息,其属性的正确与否会影响到 server(虚拟机、baremeta)的能否成功启动。
Port binding 扩展定义的属性主要包括 vnic_type、vif_type、vif_details、host_id、profile ,这些属性都可以在创建 port 的 REST API接口中可以直接指定。
一个创建 port 的 REST API body 实例如下:
POST /v2.0/ports
{
"port": {
"binding:host_id": "4df8d9ff-6f6f-438f-90a1-ef660d4586ad",
"binding:profile": {
"local_link_information": [
{
"port_id": "Ethernet3/1",
"switch_id": "0a:1b:2c:3d:4e:5f",
"switch_info": "switch1"
}
]
},
"binding:vnic_type": "baremetal",
"device_id": "d90a13da-be41-461f-9f99-1dbcf438fdf2",
"device_owner": "baremetal:none",
"dns_domain": "my-domain.org.",
"dns_name": "myport",
"qos_policy_id": "29d5e02e-d5ab-4929-bee4-4a9fc12e22ae"
}
}
关于每个属性的具体含义、类型与取值范围如下表:
属性名称 | 类型 | 含义与取值 |
---|---|---|
binding:host_id | string | 该端口位于的实际计算节点的 uuid。 |
binding:profile | object | 这是一个字典,该字典赋予特定计算节点上应用能够传递或者接受vif port信息的能力,该信息专用于该计算节点上的网络后端实现,该字段的格式由对应的后端驱动决定,API 不作规定。 |
binding:vif_details | object | 这是一个字典,记载关于该端口的一些附加信息 。至今为止这个字段包含以下key: port_filter 和 ovs_hybrid_plug 。 port_filter 是一个布尔值用来表示当前系统的网络服务是否提供端口过滤的功能,比如安全组和/或防IP/MAC地址仿冒。 ovs_hybrid_plug 是一个布尔值的标志用来通知一个网络 API 消费者(比如 nova )是否对OVS使用混合插入(hybrid plugging)策略。 |
binding:vif_type | string | 用来处理该端口的机制类型。一个网络 API 消费者例如 nova 根据此属性可以选择一个合适的设备(比如一台虚拟机的网络接口)关联到该端口上 。当前可用的值包括: ovs , bridge , macvtap , hw_veb , hostdev_physical , vhostuser , distributed 以及 other 。还有一些特殊的值: unbound 和binding_failed 。 unbound 没有被任何网络后端所实现绑定。 binding_failed 则代表当一个网络后端在试图对该端口进行绑定时发生了错误。 |
binding:vnic_type | string | 该端口应该隶属的vNIC的类型 。 该选项被Neutron用来决定使用哪种 mechanism driver 来处理该端口的绑定工作。可用的值包括: normal , macvtap , direct , baremetal , direct-physical 和 virtio-forwarder .。至于哪一种vNIC才是实际可用,这取决于当前部署的实际环境。 |
Neutron.plugin.ml2.plugin.Ml2plugin类方法 _bind_port()中调用注册到mechanism manager的driver尝试对端口进行绑定。
def _bind_port(self, orig_context):
# 构建一个新的port上下文结构
port = orig_context.current
orig_binding = orig_context._binding
new_binding = models.PortBinding(
host=orig_binding.host,
vnic_type=orig_binding.vnic_type,
profile=orig_binding.profile,
vif_type=portbindings.VIF_TYPE_UNBOUND,
vif_details=''
)
self._update_port_dict_binding(port, new_binding)
new_context = driver_context.PortContext(
self, orig_context._plugin_context, port,
orig_context.network.current, new_binding, None,
original_port=orig_context.original)
# 以下开始使用注册的mechanism driver进行端口绑定
self.mechanism_manager.bind_port(new_context)
return new_context
在每个 mechanism driver 的 __init__() 方法中会指明该 driver 是否支持端口包过滤特性,接下来会指定该 driver 支持的 vNIC 类型,ML2 的 mechanism driver 只会调用支持该端口指定vNIC 类型的driver来尝试执行绑定操作。
首先看一下 ml2 的基本实现,基本实现指定了支持 normal 类型的 vNIC 。
@six.add_metaclass(abc.ABCMeta)
class SimpleAgentMechanismDriverBase(AgentMechanismDriverBase):
"""Base class for simple drivers using an L2 agent.
"""
def __init__(self, agent_type, vif_type, vif_details,
supported_vnic_types=[portbindings.VNIC_NORMAL]):
"""支持normal类型的vNIC
"""
super(SimpleAgentMechanismDriverBase, self).__init__(
agent_type, supported_vnic_types)
self.vif_type = vif_type
self.vif_details = vif_details
看一下 该 driver 对 bind_port() 方法的具体实现:
def bind_port(self, context):
LOG.debug("Attempting to bind port %(port)s on "
"network %(network)s",
{'port': context.current['id'],
'network': context.network.current['id']})
vnic_type = context.current.get(portbindings.VNIC_TYPE,
portbindings.VNIC_NORMAL)
if vnic_type not in self.supported_vnic_types:
LOG.debug("Refusing to bind due to unsupported vnic_type: %s",
vnic_type)
return
agents = context.host_agents(self.agent_type)
if not agents:
LOG.debug("Port %(pid)s on network %(network)s not bound, "
"no agent of type %(at)s registered on host %(host)s",
{'pid': context.current['id'],
'at': self.agent_type,
'network': context.network.current['id'],
'host': context.host})
for agent in agents:
LOG.debug("Checking agent: %s", agent)
if agent['alive']:
for segment in context.segments_to_bind:
if self.try_to_bind_segment_for_agent(context, segment,
agent):
LOG.debug("Bound using segment: %s", segment)
return
else:
LOG.warning(_LW("Refusing to bind port %(pid)s to dead agent: "
"%(agent)s"),
{'pid': context.current['id'], 'agent': agent})
以 SriovNicSwitchMechanismDriver 来举例,可以看到 SR-IOV 类型的 Mechanism Driver 不支持端口包过滤特性,同时支持的vNIC 类型包括 macvtap
, direct
, direct-physical
:
class SriovNicSwitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
"""SR-IOV 类型的Mechanism Driver
"""
def __init__(self,
agent_type=constants.AGENT_TYPE_NIC_SWITCH,
vif_details={portbindings.CAP_PORT_FILTER: False},
supported_vnic_types=[portbindings.VNIC_DIRECT,
portbindings.VNIC_MACVTAP,
portbindings.VNIC_DIRECT_PHYSICAL]):
"""
:param agent_type: Constant identifying agent type in agents_db
:param vif_details: 指定该类型不支持端口包过滤特性
:param supported_vnic_types: 该driver支持的vnic_type类型
"""
self.agent_type = agent_type
self.supported_vnic_types = supported_vnic_types
看看该 driver 对 bind_port() 方法的具体实现:
def bind_port(self, context):
LOG.debug("Attempting to bind port %(port)s on "
"network %(network)s",
{'port': context.current['id'],
'network': context.network.current['id']})
vnic_type = context.current.get(portbindings.VNIC_TYPE,
portbindings.VNIC_NORMAL)
if vnic_type not in self.supported_vnic_types:
LOG.debug("Refusing to bind due to unsupported vnic_type: %s",
vnic_type)
return
if vnic_type == portbindings.VNIC_DIRECT_PHYSICAL:
# Physical functions don't support things like QoS properties,
# spoof checking, etc. so we might as well side-step the agent
# for now. The agent also doesn't currently recognize non-VF
# PCI devices so we won't get port status change updates
# either. This should be changed in the future so physical
# functions can use device mapping checks and the plugin can
# get port status updates.
for segment in context.segments_to_bind:
if self.try_to_bind_segment_for_agent(context, segment,
agent=None):
break
return
for agent in context.host_agents(self.agent_type):
LOG.debug("Checking agent: %s", agent)
if agent['alive']:
for segment in context.segments_to_bind:
if self.try_to_bind_segment_for_agent(context, segment,
agent):
return
else:
LOG.warning(_LW("Attempting to bind with dead agent: %s"),
agent)
再来看 LinuxbridgeMechanismDriver ,可以看到该 driver 支持 端口包过滤特性的安全组特性,以及该 driver 支持的vNIC 类型为normal
,支持的
vif
类型为
bridge 。
class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
"""Attach to networks using linuxbridge L2 agent.
"""
def __init__(self):
sg_enabled = securitygroups_rpc.is_firewall_enabled()
super(LinuxbridgeMechanismDriver, self).__init__(
constants.AGENT_TYPE_LINUXBRIDGE,
portbindings.VIF_TYPE_BRIDGE,
{portbindings.CAP_PORT_FILTER: sg_enabled})
lb_qos_driver.register()
看看该 driver 对 bind_port() 方法的具体实现:
def bind_port(self, context):
LOG.debug("Attempting to bind port %(port)s on "
"network %(network)s",
{'port': context.current['id'],
'network': context.network.current['id']})
vnic_type = context.current.get(portbindings.VNIC_TYPE,
portbindings.VNIC_NORMAL)
if vnic_type not in self.supported_vnic_types:
LOG.debug("Refusing to bind due to unsupported vnic_type: %s",
vnic_type)
return
agents = context.host_agents(self.agent_type)
if not agents:
LOG.debug("Port %(pid)s on network %(network)s not bound, "
"no agent of type %(at)s registered on host %(host)s",
{'pid': context.current['id'],
'at': self.agent_type,
'network': context.network.current['id'],
'host': context.host})
for agent in agents:
LOG.debug("Checking agent: %s", agent)
if agent['alive']:
for segment in context.segments_to_bind:
if self.try_to_bind_segment_for_agent(context, segment,
agent):
LOG.debug("Bound using segment: %s", segment)
return
else:
LOG.warning(_LW("Refusing to bind port %(pid)s to dead agent: "
"%(agent)s"),
{'pid': context.current['id'], 'agent': agent})
最后,再来看用得最多的 OpenvswitchMechanismDriver ,可以看出该 driver 支持支持安全组特性和混合 plug 策略,且仅支持normal 类型的 vNIC
,同时支持的
vif
类型为
ovs
。
class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
"""
The OpenvswitchMechanismDriver
"""
def __init__(self):
sg_enabled = securitygroups_rpc.is_firewall_enabled()
hybrid_plug_required = (not cfg.CONF.SECURITYGROUP.firewall_driver or
cfg.CONF.SECURITYGROUP.firewall_driver in (
IPTABLES_FW_DRIVER_FULL, 'iptables_hybrid')) and sg_enabled
#支持安全组特性和混合plug
vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled,
portbindings.OVS_HYBRID_PLUG: hybrid_plug_required}
super(OpenvswitchMechanismDriver, self).__init__(
constants.AGENT_TYPE_OVS,
#支持ovs类型的vnic
portbindings.VIF_TYPE_OVS,
vif_details)
ovs_qos_driver.register()
看看该 driver 对 bind_port() 方法的具体实现继承了 ML2 的基本实现,但是重写了内部的 try_to_bind_segment_for_agent() 方法的实现
def try_to_bind_segment_for_agent(self, context, segment, agent):
if self.check_segment_for_agent(segment, agent):
context.set_binding(segment[api.ID],
self.get_vif_type(agent, context),
self.get_vif_details(agent, context))
return True
else:
return False