openStack Neutron 作为一种 SDN(Software Defined Network),在其内部使用 ML2 模块来管理Layer2。ML2 全称是 Modular Layer 2。它是一个可以同时管理多种 Layer 2 技术的框架。在 OpenStack Neutron 的项目代码中,ML2 目前支持 Open vSwitch,linux bridge,SR-IOV 等虚拟化 Layer 2 技术。在 Neutron 的各个子项目中,有更多的 Layer 2 技术被支持。
需要注意的是,ML2 与运行在各个 OpenStack 节点上的 L2 agents 是有区别的。ML2 是 Neutron server 上的模块,而运行在各个 OpenStack 节点上的 L2 agents 是实际与虚拟化 Layer 2 技术交互的服务。ML2 与运行在各个 OpenStack 节点上的 L2 agent 通过 AMQP(Advanced Message Queuing Protocol)进行交互,下发命令并获取信息。
ML2 并非是随着 OpenStack Neutron 一同诞生的,直到 Havana 版本,OpenStack Neutron 才支持 ML2。OpenStack Neutron 最开始只支持 1-2 种 Layer 2 技术,随着发展,越来越多的 Layer 2 技术被支持。而在 ML2 之前,每支持一种 Layer 2 技术,都需要对 OpenStack Neutron 中的 L2 resource,例如 Network/Subnet/Port 的逻辑进行一次重写,这大大增加了相应的工作量。并且,在 ML2 之前,OpenStack Neutron 最多只支持一种 Layer 2 技术,也就是说如果配置使用了 Open vSwitch,那么整个 OpenStack 环境都只能使用 neutron-openvswitch-agent 作为 Layer 2 的管理服务与 Open vSwitch 交互。
ML2 的提出解决了上面两个问题。ML2 之前的 Layer 2 plugin 代码相同的部分被提取到了 ML2 plugin 中。这样,当一个新的 Layer 2 需要被 Neutron 支持时,只需要实现其特殊部分的代码,需要的代码工作大大减少,开发人员甚至不需要详细了解 Neutron 的具体实现机制,只需要实现对应的接口。并且,ML2 通过其中的 mechanism drivers 可以同时管理多种 Layer 2 技术,如下图 所示。
ML2对二层网络进行抽象和建模,引入了type driver和mechanism driver。在H版本中,ML2 Plugin被添加意图取代所有的Core Plugin,它采用了更加灵活的结构进行实现。
ML2的核心就是可以加载多个mechanism drivers,在一个openstack环境中支持多种虚拟网络实现技术。ML2解耦了网络拓扑类型与底层的虚拟网络实现机制,并且分别通过Driver的形式进行扩展,其中,不同的网络拓扑类型对应着TypeDriver,由TypeManager管理,不同的网络实现机制对应着MechanismDriver,由MechanismManger管理。
这是所有对 Neutron 中 L2 resource 操作的入口,实现文件是 neutron/plugins/ml2/plugin.py。修改配置文件neutron.conf使配置生效方式代码如下:
core_plugin = ml2
在配置文件setup.cfg已经配置了默认的core_plugins,如下所示:
neutron.core_plugins =
# 用于提供二层虚拟网络,实现了network/subnet/port资源的操作,
#这些操作最终由Plugin通过RPC调用OpenvSwitch Agent来完成。
# 根据setup.cfg文件可以看出代码路径是 neutron\plugins\ml2\plugin\Ml2Plugin
ml2 = neutron.plugins.ml2.plugin:Ml2Plugin
class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
dvr_mac_db.DVRDbMixin,
external_net_db.External_net_db_mixin,
sg_db_rpc.SecurityGroupServerRpcMixin,
agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
addr_pair_db.AllowedAddressPairsMixin,
vlantransparent_db.Vlantransparent_db_mixin,
extradhcpopt_db.ExtraDhcpOptMixin,
address_scope_db.AddressScopeDbMixin,
service_type_db.SubnetServiceTypeMixin):
"""Implement the Neutron L2 abstractions using modules.
Ml2Plugin is a Neutron plugin based on separately extensible sets
of network types and mechanisms for connecting to networks of
those types. The network types and mechanisms are implemented as
drivers loaded via Python entry points. Networks can be made up of
multiple segments (not yet fully implemented).
"""
通过ML2Plugin类的定义看,它通过继承众多的Mixin,支持许多的功能。由于具体设备的操作由Agent来完成,ML2 Plugin本身大部分是完成基于数据库的一些操作,致力于正确有效的管理network/subnet/port这些资源及其相关关系,同时正确地与Agent交互从而完成虚拟网络部署。
除了三个核心的资源外,ML2Plugin还支持许多扩展资源,ML2Plugin类需要实现这些资源的操作接口,以供受到用户请求时资源对应的Controller调用。这些扩展资源并不是都由ML2Plugin实现,许多接口是由其父类实现。
# List of supported extensions
_supported_extension_aliases = ["provider", "external-net", "binding",
"quotas", "security-group", "agent",
"dhcp_agent_scheduler",
"multi-provider", "allowed-address-pairs",
"extra_dhcp_opt", "subnet_allocation",
"net-mtu", "net-mtu-writable",
"vlan-transparent",
"address-scope",
"availability_zone",
"network_availability_zone",
"default-subnetpools",
"subnet-service-types"]
def __init__(self):
# First load drivers, then initialize DB, then initialize drivers
self.type_manager = managers.TypeManager()
self.extension_manager = managers.ExtensionManager()
self.mechanism_manager = managers.MechanismManager()
super(Ml2Plugin, self).__init__()
self.type_manager.initialize()
self.extension_manager.initialize()
self.mechanism_manager.initialize()
self._setup_dhcp()
self._start_rpc_notifiers()
self.add_agent_status_check_worker(self.agent_health_check)
self.add_workers(self.mechanism_manager.get_workers())
self._verify_service_plugins_requirements()
LOG.info("Modular L2 Plugin initialization complete")
Segment可以简单的理解为对物理网络一部分的描述,比如它可以是物理网络中很多vlan中的一个vlan。ML2仅仅使用下面的数据结构来定义一个Segment。
{
NETWORK_TYPE, PHYSICAL_NETWORK, and SEGMENTATION_ID}
如果Segment对应了物理网络中的一个vlan,则segmentation_id就是这个vlan的vlan_id;如果Segment对应的是GRE网络中的一个Tunnel,则segmentation_id就是这个Tunnel的Tunnel ID。ML2就是使用这样简单的方式将Segment与物理网络对应起来。
TypeManager和MechanismManager负责加载对应的TypeDriver和MechanismDriver,并将具体的操作分发到具体的Driver中。此外一些Driver通用的代码也由Manager完成。
TypeManager在初始化的时候,会根据配置文件加载对应的TypeDriver。TypeManager与其管理的TypeDriver一起提供了对Segment的各种操作,包括存储、验证、分配和回收。具体的步骤在Type drivers章节中详细介绍。
在ML2Plugin中的初始化过程中会实例化TypeManager,并且在TypeManager的初始化过程中完成配置文件中的type_drivers、tenant_network_types、external_network_type,具体代码如下:
# ML2Plugin中的初始化:
self.type_manager = managers.TypeManager()
self.type_manager.initialize()
# TypeManager初始化:
class TypeManager(stevedore.named.NamedExtensionManager):
"""Manage network segment types using drivers."""
def __init__(self):
# Mapping from type name to DriverManager
self.drivers = {}
LOG.info("Configured type driver names: %s",
cfg.CONF.ml2.type_drivers)
super(TypeManager, self).__init__('neutron.ml2.type_drivers',
cfg.CONF.ml2.type_drivers,
invoke_on_load=True)
LOG.info("Loaded type driver names: %s", self.names())
# 注册 type driver
self._register_types()
# 校验并注册tenant_network_types
self._check_tenant_network_types(cfg.CONF.ml2.tenant_network_types)
# 校验external_network_type
self._check_external_network_type(cfg.CONF.ml2.external_network_type)
def _register_types(self):
for ext in self:
network_type = ext.obj.get_type()
if network_type in self.drivers:
LOG.error("Type driver '%(new_driver)s' ignored because"
" type driver '%(old_driver)s' is already"
" registered for type '%(type)s'",
{
'new_driver': ext.name,
'old_driver': self.drivers[network_type].name,
'type': network_type})
else:
self.drivers[network_type] = ext
LOG.info("Registered types: %s", self.drivers.keys())
def _check_tenant_network_types(self, types):
self.tenant_network_types = []
for network_type in types:
# tenant_network_types配置的type需要在type_drives配置中存在
if network_type in self.drivers:
self.tenant_network_types.append(network_type)
else:
LOG.error("No type driver for tenant network_type: %s. "
"Service terminated!", network_type)
raise SystemExit(1)
LOG.info("Tenant network_types: %s", self.tenant_network_types)
def _check_external_network_type(self, ext_network_type):
# 如果配置external_network_type,则必须要在type_drivers中配置过
if ext_network_type and ext_network_type not in self.drivers:
LOG.error("No type driver for external network_type: %s. "
"Service terminated!", ext_network_type)
raise SystemExit(1)
def initialize(self):
for network_type, driver in self.drivers.items():
LOG.info("Initializing driver for type '%s'", network_type)
driver.obj.initialize()
在实现过程如何调用具体的type driver,参考代码如下:
def reserve_provider_segment(self, context, segment):
network_type = segment.get(ml2_api.NETWORK_TYPE)
# 根据segment中的network_type调用具体的type driver
driver = self.drivers.get(network_type)
if isinstance(driver.obj, api.TypeDriver):
return driver.obj.reserve_provider_segment(context.session,
segment)
else:
return driver.obj.reserve_provider_segment(context,
segment)
MechanismManager的初始化参考TypeManager的初始化。
MechanismManager分发操作并具体传递操作到具体的MechanismDriver中,一个需要Mechanism Driver处理的操作会按照配置的顺序依次调用每一个Driver的对应函数来完成,比如对于需要配置交换机的操作,可能ovs虚拟交换机和外部真实的物理交换机比如Cisco交换机都需要进行配置,这个时候就需要ovs MechanismDriver和Cisco MechanismDriver都被调用进行处理。
def _call_on_drivers(self, method_name, context,
continue_on_failure=False, raise_db_retriable=False):
"""Helper method for calling a method across all mechanism drivers.
:param method_name: name of the method to call
:param context: context parameter to pass to each method call
:param continue_on_failure: whether or not to continue to call
all mechanism drivers once one has raised an exception
:param raise_db_retriable: whether or not to treat retriable db
exception by mechanism drivers to propagate up to upper layer so
that upper layer can handle it or error in ML2 player
:raises: neutron.plugins.ml2.common.MechanismDriverError
if any mechanism driver call fails. or DB retriable error when
raise_db_retriable=False. See neutron.db.api.is_retriable for
what db exception is retriable
"""
errors = []
for driver in self.ordered_mech_drivers:
try:
getattr(driver.obj, method_name)(context)
except Exception as e:
if raise_db_retriable and db_api.is_retriable(e):
with excutils.save_and_reraise_exception():
LOG.debug("DB exception raised by Mechanism driver "
"'%(name)s' in %(method)s",
{
'name': driver.name, 'method': method_name},
exc_info=e)
LOG.exception(
"Mechanism driver '%(name)s' failed in %(method)s",
{
'name': driver.name, 'method': method_name}
)
errors.append(e)
if not continue_on_failure:
break
if errors:
raise ml2_exc.MechanismDriverError(
method=method_name,
errors=errors
)
Each available network type is managed by an ml2 TypeDriver. TypeDrivers maintain any needed type-specific network state, and perform provider network validation and tenant network allocation. The ml2 plugin currently includes drivers for the local, flat, vlan, gre and vxlan network types.
物理环境中的L2网络存在多种类型,在Openstack Neutron中也支持多种网络类型。这些网络类型是由ML2Plugin中的type drivers完成。Openstack虚拟环境可以支持一种或多种网络类型(如果使用没有配置的网络类型,Neutron 会报网络类型不支持的错误),可以通过修改ml2_conf.ini配置文件完成配置,配置信息参考如下:
[ml2]
tenant_network_types = vxlan
extension_drivers = port_security
type_drivers = local,flat,vlan,gre,vxlan
mechanism_drivers = openvswitch,linuxbridge
tenant_netw |