Neutron集成ONOS源码分析

时间:2022-05-06
本文章向大家介绍Neutron集成ONOS源码分析,主要内容包括一.networking_onos/common、二.networking_onos/plugins、三.networking_onos/tests、小结、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

OpenStack Neutron集成ONOS的项目,名为“networking-onos”。目前,在Neutron项目中现已开始从源码中移除诸如SDN Plugin这些项目的代码,使之成为独立的项目,其Release的代码托管在Github上。

截止目前,ONOS已经在ML2、L3层支持集成Neutron。具体详情,可参考neutron项目代码中的doc/source/stadium/sub_projects.rst文件。

networking-onos项目Github地址:https://github.com/openstack/networking-onos。 networking-onos项目Review地址:https://review.openstack.org/#/q/onos,n,z。

在这里,我将networking-onos安装在了CentOS平台上,源码目录为/usr/lib/python2.7/site-packages/networking_onos。该目录下,主要有common、plugins、tests这三个文件夹。下面我们将以此分别展开分析。

一.networking_onos/common

该目录下主要包括了config.py、utils.py这2个模块,用于定义一些公共的库。 1)common/config.py

from oslo_config import cfg
 
  ONOS_DRIVER_OPTS = [
      cfg.StrOpt('url_path',
                 default='',
                 help=_('ONOS ReST interface URL')),
      cfg.StrOpt('username',
                 default='',
                 help=_('Username for authentication.')),
      cfg.StrOpt('password',
                 default='',
       secret=True,  # do not expose value in the logs
        help=_('Password for authentication.'))
]
 
  cfg.CONF.register_opts(ONOS_DRIVER_OPTS, "onos")

分析: 该文件中的代码,无外乎是使用openstack的config这个公共库,用于配置文件的管理和命令行的解析;cfg模块的主要作用有两个,一个是对配置文件进行解析,一个是对命令行的参数进行解析。其数据类型有:StrOpt、ListOpt、DictOpt、IntOpt、FloatOpt。

在这里,它用于解析ml2_conf.ini等文件中的内容,具体用于解析列表中的ONOS和Neutron集成的username、password、url_path这三种信息。

最后,一行代码使用oslo.cfg 模块中的cfg.CONF全局变量中的register_opts()方法注册我们需要解析的配置项。

2)common/utils.py

def send_msg(onos_path, onos_auth, msg_type, entity_path, entity=None):
      """Send message to the ONOS controller."""
      path = '/'.join([onos_path, entity_path])
      hdr = {'Content-Type': 'application/json'}
    body = jsonutils.dumps(entity, indent=2) if entity else None
      LOG.debug("Sending MSG_TYPE (%(msg)s) URL (%(path)s) "
                "OBJECT (%(entity)s) BODY (%(body)s)",
                {'msg': msg_type, 'path': path,
                 'entity': entity, 'body': body})
      req = requests.request(method=msg_type, url=path,
      headers=hdr, data=body,
      auth=onos_auth)
      # Let's raise voice for an error
      req.raise_for_status()
  def safe_delete_from_dict(dict, keys):
      """Ignore key errors when deleting from a dictionary."""
      for key in keys:
          dict.pop(key, None)

分析: 该模块中,主要定义了send_msg和safe_delete_from_dict这2个函数。前者主要用于将onos_path, onos_auth, msg_type, entity_path这些信息发送给onos controller。而后者,则用于当从字典中删除key-value键值时,忽略关键错误。

二.networking_onos/plugins

这里,我们需要注意的是,networking-onos集成neutron的plugin,从架构上分为了ML2、L3这二层。如下图所示:

1)ml2/driver.py

from oslo_config import cfg
  from oslo_log import helpers as log_helpers
  from oslo_log import log as logging
  from neutron.common import constants as n_const
  from neutron.extensions import portbindings
  from neutron.plugins.common import constants
  from neutron.plugins.ml2 import driver_api as api
  from networking_onos.common import config  # noqa
  from networking_onos.common import utils as onos_utils
  LOG = logging.getLogger(__name__
  class ONOSMechanismDriver(api.MechanismDriver):
  …………..

分析: Networking-onos的ML2 driver,总共分别从oslo公共库、neutron、networking_onos这三个地方,导入了诸如config、log这些模块来处理诸如配置文件、命令行参数、日志管理这些事儿。以及使用neutron和networking_onos库中的extensions、plugins、ml2、config这些模块。

在该模块中,使用了一个继承自api.MechanismDriver父类的ONOSMechanismDriver的类,用于实现Neutron中的ML2 Driver。除此之外,使用了一个@log_helpers.log_method_call装饰器,主要而言,该模块通过扩展core plugin api来实现自己定义的方法或资源操作。

其中定义的方法,主要实现了Neutron ML2中的network、subnet、port这几类核心资源。其功能如下所示:

  • Network

create_network update_network delete_network

  • Subnet

create_subnet update_subnet delete_subnet

  • Port

create_port update_port delete_port bind_port

最后的,check_segment函数用于检查ONOS MechanismDriver中的segment是否是有效的,其定义的NETWORK_TYPE有:local、gre、vxlan、vlan。 当然,ml2目录下的README文件,也是很有必要一读的。

2)l3/driver.py

class ONOSL3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
                   extraroute_db.ExtraRoute_db_mixin,
                   l3_gwmode_db.L3_NAT_db_mixin,
                   l3_agentschedulers_db.L3AgentSchedulerDbMixin,
                   onos_router.ONOSRouter,
                   onos_fip.ONOSFloatingIP):

该模块中,定义了一个继承自许多父类的名为ONOSL3Plugin的类,用于实现L3层网络服务中的,诸如neutron db、路由器db、NAT db、l3 db等数据模型的操作。

该类的核心是实现L3层的 Router Service Plugin。用于提供router和floatingip资源,以及管理相关的request/response。

无外乎,这里实现了L3层网络服务中的如下一些资源:

  • Router

create_router update_router delete_router

  • floatingip

create_floatingip update_floatingip delete_floatingip add_router_interface remove_router_interface

该模块中,还定义了一个setup_rpc的函数,用于远程过程调用和其他的api、service进行消息通信。我们知道,Neutron中的Router,有网关臂和接口臂两种,网关臂用于设置网关和Public_network关联;而接口臂则用于添加接口,关联租户/用户创建的租户网络(Private_network),对应于模块中的add_router_interface方法。

需要注意的是,router和floating_ip这两类L3资源的代码,均在同级目录下有具体实现。

3)l3/floating_ip.py 该模块,用于实现ONOS的L3 Floating IP Service。其继承自Object基类的ONOSFloatingIP类,用于将包括msg_type、entity_path、entity 在内的Neutron的L3 Floating IP消息发送给ONOS Controller。

4)l3/router.py 该模块实现、原理同上类似,用于实现ONOS L3 Router Service。返回一个包含subnet_id、port_id、router_id、tenant_id在内的router_dict字典数据结构。

5)l3/ README 如果要了解相关的L3实现信息,该文件同样值得一读。

三.networking_onos/tests

该目录下,分别定义的是ML2、L3层的Mock单元测试模块。如下。 1)ml2/ test_driver.py 无疑,该模块中使用了python中的单元测试模块——mock。Mock用于在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便于测试的测试方法。

代码测试只针对指定函数的内部代码。如果测试代码需要依赖于其他的代码片段,在某种不幸的情形下,即使被测试的函数没有变化,这部分内嵌代码的修改仍然可能破坏原有的测试。Mock 方法就是用于把测试与测试边界以外的对象隔离开。

该模块中,fake了ML2网络服务中的如下一些资源: Network: network_uuid、network_object

Subnet: subnet_uuid、subnet_object

Port: port_uuid、port_object

其中ONOSMechanismDriverTestCase类的set_test_config方法,fake了连接onos controller所必需的一些信息。同时,分别使用网络、子网、端口的delete、update、delete方法,进行单元测试。此外,还定义了验证segment(network_types)、端口绑定这些方法。

2)l3/test_driver.py L3层的单元测试,同ML2层的单元测试模块在实现和原理上是一样的。首先会fake一些必要的数据,如tenant_id、router_uuid、router_object(即创建router的必要数据)、network_id、floating_ip、port_id等之类的数据。

class ONOSL3PluginTestCase(base.BaseTestCase,
                             test_neutron_extensions.ExtensionTestCase,
                             onos_driver.ONOSL3Plugin):
 
      def setUp(self):
          super(ONOSL3PluginTestCase, self).setUp()
          self._setUpExtension(
              'neutron.extensions.l3.RouterPluginBase', None,
              l3.RESOURCE_ATTRIBUTE_MAP, l3.L3, None,
              allow_pagination=True, allow_sorting=True,
              supported_extension_aliases=['router'],
              use_quota=True)
          self.instance = self.plugin.return_value

在继承自BaseTestCase、ExtensionTestCase和ONOSL3Plugin这三个父类的ONOSL3PluginTestCase类中,_test_send_msg方法分别使用了REST API框架中的POST、PUT、DELETE这三类资源操作。

Router、floating_ip的两类L3层资源的创建、更新、删除操作;以及Router和floating_ip相互之间的操作,譬如,路由器管理浮动网络、租户网络等。

小结

本次分析只是粗粒度的做了个讲解,不能算精细。但我们相信这是一个不错的开始,通过此番梳理,我们懂得了如下一些知识点:一是如何开发一个全新的服务Plugin及其Driver(与为一个已有的Plugin,添加Driver有所不同);二是如何去写一个neutron环境中的单元测试。