ODL开发者指引:OpenFlow协议库开发者指南

编者按:本文系SDNLAB社区译者计划发布文章,SDNLAB将与国外技术社区、优质媒体和个人进行长期的内容合作,带来更多的优质技术文章。本文是OpenDaylight开发者指南的OpenFlow协议库开发指南部分。

本文译者:toles,虽然是个SDN初学者,但是很看好SDN前景,在不断努力的学习中,希望和大家多多交流。

介绍

OpenFlow协议库是OpenDaylight的一个组件,调解OpenDaylight controller和支持OpenFlow协议的硬件设备之间通信。主要目标是提供用户(或上层OpenDaylight)通信通道,可用于管理网络硬件设备。

功能概览

Openflowjava内部的三个特性:

  • odl-openflowjava-protocol提供全部的openflowjava bundles, 需要与openflow设备通信. 它可以确保消息的转换和处理网络的连接. 它还提供了openflow协议具体模型.
  • odl-openflowjava-all 目前只包含了odl-openflowjava-protocol 特性.
  • odl-openflowjava-stats 提供消息计数和报告机制. 可用于性能分析。

odl-openflowjava-protocol架构

此特性包含基本的bundles有openflow-protocol-api, openflow-protocol-impl, openflow-protocol-spi and 工具.

  • openflow-protocol-api – 包含openflow模型,常量和用于(反)序列化注册的秘钥.
  • openflow-protocol-impl – 包含消息工厂, 转换二进制消息进入DataObjects, 反之亦然. Bundle还包括网络连接处理服务器, netty pipeline处理程序, …
  • openflow-protocol-spi -入口点为openflowjava配置,启动和关闭.基本的起始实现
  • util -二进制JAVA转换和便于实验者秘钥创建的实用工具类

odl-openflowjava-stats 特性

运行在odl-openflowjava-protocol上.它包含了各种的信息类型/事件和报告计数在特定时间周期内. 统计信息收集被配置在openflowjava- config/src/main/resources/45-openflowjava-stats.xml

关键API和接口

基础API/SPI类是ConnectionAdapter (Rpc/通知) 和SwitchConnectionProcider (配置, 启动, 关闭)

安装

检出代码并导入工程到你的IDE.
git clone ssh://@git.opendaylight.org:29418/openflowjava.git

配置

当前实现允许配置:

  • 监听端口(强制)
  • 传送协议(强制)
  • 交换空闲超时 (强制)
  • TLS配置(可选)
  • 线程数(可选)

你可以在如下所示找到典型的Openflow 协议库实例配置:

可能传输协议选项:

  • TCP
  • TLS
  • UDP

交换机空闲超时指定时间检测交换机的空闲状态.当一段时间内没有收到来自交换机的消息,上层被通知交换机闲置.可以使用典型的TLS配置:

  • 取消 标签注释
  • 复制exemplary-switch-privkey.pem, exemplary-switch-cert.pem 和exemplary- cacert.pem文件到你的虚拟机
  • 设置VM加密选项用作复制秘钥 (请访问TLS支持的wiki页关于TLS详细信息)
  • 开始通信

线程模型配置指定需要多少个线程以执行 Netty 的 I/O 操作.

  • boss-threads指定注册传入连接的线程数
  • worker-threads 指定执行读/写(+ 序列化/ 反序列化) 操作线程数.

架构

公共API(openflow-protocol-api)

接口和构建者集合代表Openflow协议结构的不可变数据传输对象.

为了减少冗长的定义和重复性代码,通过代码生成器从YANG模型推出传输对象和服务API.

以下是YANG模型的定义:

  • openflow-types – 定义通用Openflow特定类型
  • openflow-instruction – 定义基本的Openflow 指令
  • openflow-action - 定义基本的Openflow活动
  • openflow-augments – 定义对象扩展
  • openflow-extensible-match – 定义Openflow OXM 匹配
  • openflow-protocol – 定义Openflow 协议消息
  • system-notifications – 定义系统通知对象
  • openflow-configuration – 定义结构用于ConfigSubsystem

这个模块也可以从下面的YANG模型中重复使用类型:

  • ietf-inet-types – IP地址, IP前缀, IP协议相关类型
  • ietf-yang-types - Mac地址, 等等.

预定义类型的使用使API约定更安全, 有更好的可读性和记录(例如 用 MacAddress代替字节数组…)

TCP 通道pipeline(openflow-protocol-impl)

创建基于配置和支撑的通道处理pipeline.

TCP通道流水线. imageopenflowjava/500px-TCPChannelPipeline.png[width=500]

交换连接提供者.用于其它工程连接点的实现.库通过本类公开其功能.库能够在这里被配置, 启动和关闭.也有方法为客户定制的 (反) 序列化注册.

Tcp连接初始化程序.为了初始化TCP连接到一个设备(交换机),OF插件调用在SwitchConnectionProvider的方法initiateConnection()。该方法依次初始化(Bootstrap)通向设备的服务侧通道。

TCP处理(TCP Handler).表示单服务通过TCP/TLS协议处理进入的连接。TCP处理程序创建一个单实例的TCP通道初始化程序对通道进行初始化。之后监听配置过的InetAddress和端口。当一个新设备连接,TCP处理程序注册他们的通道并把控制传给TCP通道初始化程序。

TCP通道初始化程序.此类用于通道初始化/拒绝和传递参数.之后一个新通道被注册,它调用交换连接处理(OF Plugin)接收方法决定是否库应该保持新的注册通道或者是否通道应该被关闭. 如果通道已经被接受, TCP通道初始化程序创建整个管道所需的处理与ConnectionAdapter 实例.之后通道pipeline准备好了,交换连接处理程序被onConnectionReady通知. OpenFlow 插件可以开始发送下游信息.

空闲处理程序.如果超过指定时间没有收到任何消息,这个处理程序触发空闲状态通知.交换机从ConnectionConfiguration设置收到空闲超时参数.当交换空闲超时内收到消息空闲状态处理程序处于非激活状态.如果超过超时值没有收到指定信息,处理程序创建SwitchIdleEvent消息并发送给下游.

TLS处理程序.通过TLS协议加密和解密消息.约束TLS
处理器进入pipeline 是配置的事情( tag). TLS通信要么不支持要么是必需支持. TLS处理程序作为Netty SslHandler表示.

OF帧解码器. 解析输入流进入正确长度的消息帧为further处理.帧基于Openflow头长度. 如果收到的消息比OpenFlow最短消息(8字节)短, OF帧解码器等待更多的数据.接收至少为8字节后,解码器检查OpenFlow头长度.如果仍然有一些字节丢失,解码器等待它们.其他的帧解码器发送正确长度的消息到下一个处理程序中的通道管道.

OF版本探测器.检测使用OpenFlow协议的版本并丢弃不受支持的版本消息.如果检测版本支持, OF版本探测器创建包含检测的版本和字节消息的VersionMessageWrapper目标,并且向上游发送对象.

OF解码器.选择正确的对象反序列化工厂 (基于消息类型) 并且反序列化消息生成DTO (数据传输对象). OF解码器接收VersionMessageWrapper对象并将其传递到DeserializationFactory返回转换的DTO. DeserializationFactory创建带版本和接收消息类型的MessageCodeKey对象和对象类被接收消息序列化. 在DecoderTable搜索相应解码器时此对象被用作秘钥. DecoderTable实际上是一个map存储解码器.找到解码器翻译成接收消息进入DTO.如果没有找到解码器, 返回null.之后返回转换的DTO回到OF解码器,解码器检查是否为null.当DTO为null时,解码器记录日志并且抛出异常.否则传递DTO further到上游.最后, OF解码器释放ByteBuf包含的接收和解码字节消息.

OF编码器.选择正确的序列化工厂 (基于DTO类型) 并且序列化DTOs为字节消息. OF编码器相对于解码器使用同样的原则. OF Encoder receives DTO OF编码器接收DTO,如果结果不为null把它转化,它发送转化的DTO作为 ByteBuf到下游.通过MessageTypeKey寻找合适的编码器,基于版本和接收的DTO类.

授权入栈处理程序.授权收到的DTO到连接适配器.在channelInactive和channelUnregistered事件中反应.其中一个事件触发, DelegatingInboundHandler 创建DisconnectEvent消息并且发到上游,通知上层交换断链.

通道出站队列.消息刷新处理程序.
存储输出消息(DTOs)并刷新.刷新的执行是基于时间过期和消息队列数.

连接适配器.提供了pipeline顶部的外观,隐藏了netty.io特性.提供了一种方法来注册传入的消息并将消息发送到特定的通道/会话. ConnectionAdapterImpl主要实现了3个接口(统一在一个超接口ConnectionFacade中):

  • ConnectionAdapter
  • MessageConsumer
  • OpenflowProtocolService

ConnectionAdapter接口有用于设置监听器的方法(消息, 系统和连接准备监听器),该方法检查是否所有监听器被设置,检查是否通道存活并断开连接方法. 断链方法清除responseCache并禁用新消息.

MessageConsumer接口只有一个方法: consume(). Consume()方法被DelegatingInboundHandler方法调用.此方法基于其类型处理接收的DTO.有三种接收对象类型:

  • 系统通知 – 调用OpenFlow插件中的系统通知(systemListener设置).至于DisconnectEvent消息,连接适配器清除响应缓存并禁止consume()方法处理,
  • OpenFlow异步消息 (来自交换机) -调用Openflow插件中响应的通知,
  • OpenFlow 对称消息 (响应请求) - create RpcResponseKey with XID and DTO’s class set. RpcResponseKey用于在responseCache在responseCache找到future对象. Future 对象收到的消息和错误 (如果任何发生)被设置成功标志.假设在responseCache没有发现future对象,连接适配器记录告警和丢弃的消息到日志.连接适配器也记录接收到一个未知的DTO警告到日志.

OpenflowProtocolService接口包含了全部rpc-methods为发送消息从上层到下游并响应。请求消息返回将来填充的期望回复消息,否则这个期望的将来是Void类型。

注意: MultipartRequest消息是唯一例外.实际上它是请求-应答消息类型, 如果是作为rpc实现它不能处理更多的MultipartReply 消息(only one Future).这是为什么MultipartReply作为通知的实现. OpenFlow插件负责纠正消息处理.

UDP通道pipeline (openflow-protocol-impl)

创建以配置和支撑为基础的UDP通道处理pipeline.交换机连接提供者, 通道出站队列和连接适配器与TCP连接/通道pipeline的情况下实现的作用相同 (请看上面).

Figure 16.1. UDP Channel pipeline

UDP处理程序.代表一个单独的服务器正在处理UDP (DTLS)协议之上的传入连接. UDP处理程序创建一个UDP通道初始化的单例实例,这个实例将出示通道.之后监听绑定配置的地址和端口.当一个新设备连接, UDP处理程序注册通道并传递控制权给UDP通道初始化程序.

UDP通道初始化程序.这个类被用于通道初始化和传递参数.之后一个新通道被注册(UDP也永远只有一个通道) UDP通道初始化程序创建整个流水线与所需要的处理程序.

DTLS处理程序.还没有实现.将处理安全DTLS连接.

OF数据报文处理程序. 结合OF帧解码器和OF版本检测器功能.从接收数据报文提取消息并检查消息版本是否支持.如果收到的消息来自未知发送机, OF报文处理程序为此发送机创建连接适配器并将其存储在UdpConnectionMap发送机的地址.此map也被用于发送消息和正确连接适配器查找,委托消息从一个通道到多个会话.

OF数据报文解码器.选择正确的反序列化工厂 (基于消息类型)并且反序列化消息进入生成DTOs.OF解码器接收VersionMessageUdpWrapper对象将其传递到DeserializationFactory,返回转化的DTO. DeserializationFactory创建带版本和接收消息类型的MessageCodeKey对象并将接收到的消息反序列化为对象的类.此对象被用作在DecoderTable搜索相应解码器的关键字. DecoderTable是基于映射存储解码器. 发现解码器转换接收的消息进入DTO (DataTransferObject).如果没有发现解码器, 返回null.之后返回转换的DTO到OF报文解码器,此解码器检查是否为null.当DTO为null,解码器把此状态记录日志中.否则在UdpConnectionMap内通过DTO找到相应的连接适配器.最后, OF解码器释放包含的ByteBuf和解码字节消息.

OF数据报文编码器.选择正确的序列化工厂(基于DTO的类型)并且串行DTOs成为字节消息. OF报文编码器使用了相同原则做相反的处理. OF编码器接收DTO并转换,如果结果不为null,转换为数据包发送给下游,通过MessageTypeKey寻找相应的编码器, 基于版本和接收DTO的类.

SPI (openflow-protocol-spi)

定义接口用于其他工程库的连接点. 库通过这个接口来公开其功能.

集成测试(openflow-protocol-it)

测试与简单的客户端通信.

简单客户端(simple-client)

轻量级交换机仿真器 –期望场景可编程.

实用工具 (util)

包含实用工具类,主要用于和ByteBuf一起工作.

库的生命周期

步骤 (之后库的bundle启动):

  • [1] 库由ConfigSubsystem配置 (地址, 端口, 加密, …)
  • [2] Plugin injects its SwitchConnectionHandler into the Library
  • [3] 插件开启库
  • [4]库创建配置协议处理程序(e.g. TCP Handler)
  • [5] 协议处理程序创建通道初始化
  • [6] 通道初始化程序通知插件是否接受传入每个新交换连接
  • [7] 插件响应:
  • true - 继续构建管道
  • false - 拒绝连接/断开连接通道
  • [8] Library notifies Plugin with onSwitchConnected(ConnectionAdapter) notification, passing reference to ConnectionAdapter, that will handle the connection[9]插件注册系统和消息监听器[10] FireConnectionReadyNotification() 被触发,宣布用于通信的流水线处理程序已经被创建并且插件可以开始通信[11]需要的时候插件关闭这个库

图 16.2. 库的生命周期

统计信息收集器

介绍

统计信息采集器采集消息统计. Current collected statistics (DS - downstream,
US - upstream):

  • DS_ENTERED_OFJAVA – 所有信息在openflowjava 登记(从openflowplugin拾取)
  • DS_ENCODE_SUCCESS - 编码消息成功
  • DS_ENCODE_FAIL -在编码(序列化)过程中失败的消息
  • DS_FLOW_MODS_ENTERED -全部flow-mod消息进入openflowjava
  • DS_FLOW_MODS_SENT -全部的flow-mod消息成功发送
  • US_RECEIVED_IN_OFJAVA -从交换机接收的消息
  • US_DECODE_SUCCESS -消息解码成功
  • US_DECODE_FAIL -消息解码(反序列化)过程中失败
  • US_MESSAGE_PASS -消息转交给openflowplugin

Karaf

为了开启统计, 需要特性:install odl-openflowjava-stats. To see the logs one should use log:set DEBUG org.opendaylight.openflowjava.statistics and than
probably log:display (you can log:list to see if the logging has been set).调整集合设置修改modify 45-openflowjava-stats.xml足以.

JConsole

Jconsole为统计信息收集器提供两个命令:

  • 打印当前统计
  • 重置统计计数

之后JConsole 附属到正确的处理程序上, 只需进入MBeans tab # org.opendaylight.controller # RuntimeBean # statistics-collection- service-impl # statistics-collection-service-impl # Operations 能使用这些命令.

TLS Support

注意

请看OpenFlow Plugin Developper Guide

可扩展性

介绍

扩展性入口点是SwitchConnectionProvider. SwitchConnectionProvider 为(解)序列化注册包含了方法c.注册解序列化需要使用.register*Deserializer(key, impl). 注册序列化必须使用.register*Serializer(key, impl).注册可以发生在配置过程中或者运行时.

注意:假设当接收到实验者信息,没有(反)序列化器被注册,此库将抛出IllegalArgumentException.

基本原理

为了使用扩展需要增加现有模型和注册新(反)序列化器.

增加模型: 1. 创建新增加

Register (de)serializers: 1.创建你的(反)序列化器 2. 实现OFDeserializer<> / OFSerializer<> -以防你(反)序列化接口需要使用多TableFeatures消息, 让它实现HeaderDeserializer<> / HeaderSerializer 3.实现规定的方法 4.在相应的秘钥下注册你的反序列化器 (案例 ExperimenterActionDeserializerKey) 5.相应的秘钥下注册你的序列化器 (in our case ExperimenterActionSerializerKey) 6. 完成, 测试你的实现

注意:如果你不知道用什么秘钥实现你的(反)序列化,请看Registration keys一页.

例子

假设我们有供应商/实验者动作,由这种结构表示:

首先, 我们必须增强现有的模型. 我们创建一个新模型, 导入"openflow-types.yang" (不要忘记更新你的pom.xml和api依赖).现在我们创建了foo操作标识:

这将作为我们结构中的类型. 现在我们必须增强现有的action结构,以致我们将有所需的第一和第二字段.为了创建新扩展, 我们的模块不得不导入"openflow-action.yang". 增加如下:

我们完成了模型的改变. 运行mvn clean编译生成源代码.生成后,我们需要实现我们的(反)序列化.

反序列化:

序列化:

序列化和反序列化注册:

我们已经准备好测试我们的实现.

NOTE:供应商/实验者结构只定义供应商/实验者ID标识(除操作类型).对全部供应商消息,供应商/实验者ID是唯一的-这就是为什么供应商能只注册在一个ExperimenterAction(De)SerializerKey类下.这就是为什么供应商不得不在他们拥有的子类/子类型交换/选择.

详细演练:反序列化可扩展性

外部接口& 类描述. OFGeneralDeserializer:

  • OFDeserializer
  1. deserialize(ByteBuf) – 反序列化ByteBuf
  • HeaderDeserializer
  1. deserializeHeaders(ByteBuf) – 序列化只有 E 头(在 TableFeatures消息使用)

DeserializerRegistryInjector

  • injectDeserializerRegistry(DeserializerRegistry) -注入注册进反序列化器.客户反序列化器需要访问其他反序列化器时有用.

NOTE: DeserializerRegistryInjector 不是OFGeneralDeserializer派生的.它是一个独立的接口.

MessageCodeKey及他们的后代被用作在DeserializerRegistry反序列化查找. MessageCodeKey 应该在一般情况下使用,然而它的派生类用在更特殊的情况下.例如ActionDeserializerKey被用作行动解序列化器查找和(解)注册.供应商提供仅包含最必要字段特殊关键字. 这些关键字通常开始带Experimenter前缀(MatchEntryDeserializerKey 是一个异常).

MessageCodeKey有这些域:

  • short version - Openflow wire版本号
  • int value – 读取字节消息的值
  • Class<?> clazz – 创建对象类

场景介绍

  • [1]在自定义bundle场景开始要扩展库的功能.自定义bundle公开实现创建反序列化器OFDeserializer/ HeaderDeserializer 接口 (封装在OFGeneralDeserializer统一超接口).
  • [2]创建的反序列化器与相应的ExperimenterKeys是配对的,用于反序列化器查找.如果你不知道什么关键字应被用于你的(反)序列化器实现,请浏览Registration keys.
  • [3]一对反序列化器通过SwitchConnectionProvider传递到OF库.registerCustomDeserializer(key, impl).库注册返序列化器.
  • 注册时, 库检查是否反序列化器是一个 DeserializerRegistryInjector接口的实例. 如果是, DeserializerRegistry (它存储了所有的反序列化参考)注入进反序列化程序.

当返序列化程序需要访问其他反序列化程序特别有用. 举例 IntructionsDeserializer 需要访问 ActionsDeserializer 为了能够处理OFPIT_WRITE_ACTIONS/OFPIT_APPLY_ACTIONS指令.

Detailed walkthrough: 序列化可扩展性

外部接口& 类描述.OFGeneralSerializer:

  • OFSerializer
  • serialize(E,ByteBuf) – 序列化 E 进入给出ByteBuf
  • HeaderSerializer
  • serializeHeaders(E,ByteBuf) – 序列化 E 头(用于多TableFeatures消息)

SerializerRegistryInjector * injectSerializerRegistry(SerializerRegistry) – 注入serializer registry into serializer.当序列化程序需要访问其他序列化程序时有用.

NOTE: SerializerRegistryInjector不是OFGeneralSerializer后代.

MessageTypeKey和它们的后代 这些关键字被用于序列化器在SerializerRegistry内查找. MessageTypeKey当它的后代被用在特殊的情况使用.例如ActionSerializerKey被用于Action反序列化器查找注销. 供应商提供仅包含最必要字段特殊关键字.这些关键字通常开始带Experimenter前缀 (MatchEntrySerializerKey是一个异常).

MessageTypeKey 存在这些域:

  • short version - Openflow wire 版本号
  • Class msgType - DTO

class方案演练

  • [1]串行扩展原理类似于反序列化原理.方案开始于一个自定义的包中.自定义bundle创建序列化器实现外露的OFSerializer / HeaderSerializer接口 (覆盖OFGeneralSerializer超级接口下).
  • [2] 创建序列化器搭配他们的ExperimenterKeys,这用于序列化器的查找.如果你不知道什么秘钥用作你的序列化器的实现,请浏览注册秘钥一页.
  • [3]成对的序列化器通过SwitchConnectionProvider传递给OF库.registerCustomSerializer(key, impl). 库注册序列化器.
  • While registering,库检查是否序列化器是SerializerRegistryInjector接口的一个实例. If yes如果是, SerializerRegistry (存储所有序列化器引用)被注入进序列化器.

当序列化器需要访问其他反序列化器时特别有用.例如IntructionsSerializer为了能够处理OFPIT_WRITE_ACTIONS/OFPIT_APPLY_ACTIONS指令需要访问ActionsSerializer.

Figure 16.4. Serialization scenario walkthrough

内部描述

SwitchConnectionProvider SwitchConnectionProvider构造和初始化序列化器和反序列化器注册默认的(反)序列化器. 拒绝DeserializerRegistry 进入DeserializationFactory, SerializerRegistry 进入 SerializationFactory.当调用自定义的(反)序列化, SwitchConnectionProvider在适当的注册表调用注册方法.

DeserializerRegistry / SerializerRegistry为了初始化默认的(反)序列化器DeserializerRegistry / SerializerRegistry,注册表都包含init()方法.注册表检查是否关键字或(反)序列化器实现不为null.如果至少有一个是null, 抛出NullPointerException.否则如果他是(De)SerializerRegistryInjector实例,(反)序列化器被检查.如果它是这个接口的实例,注册表被注入进(反)序列化实现.

GetSerializer(key) 或 GetDeserializer(key)执行注册查找. 因为有两个独立接口可能放入注册表,此注册表用作它们的统一super接口. 获得(De)Serializer(key) 方法 强制转换super接口为所需的类型.从注册表接收有一个null检查为(反)序列化器.如果反序列化器没有找到, NullPointerException 与秘钥描述被抛出.

注册秘钥

反序列化. openflow扩展和秘钥

这有三个供应商特性的扩展在Openflow v1.0和八个在Openflow v1.3.这些扩展被注册在注册密钥下:

Table 16.1. Deserialization反序列化

序列化. openflow扩展和秘钥

在Openflow v1.0有三个供应商特定扩展Openflow v1.3有七个.这些扩展被注册在秘钥下,如下表所示:

Table 16.2. 序列化

SDNLAB社区译者 正在火热招募中

 
成为译者的好处:

  • 优质的英文原材料,最直接的提升英语能力
  • 提高社区影响力,国内极具影响力的SDN交流平台
  • 最优的内容传播途径,认可才是硬道理
  • 社区福利免费拿,一手的学习资料
  • 分享推动SDN发展,提供国内新鲜的技术资料

什么样的人才能成为译者?

  • 热爱分享、热爱社区;喜爱SDN等网络创新技术;

怎样成为译者?
1、添加微信:353176266 或点击识别二维码

2、进行自我介绍
3、阅读社区提供的翻译资料
4、翻译测试

 
编译类仅出于传递更多信息之目的,系SDNLAB对海外相关站点最新信息的翻译稿,仅供参考,不代表证实其描述或赞同其观点,投资者据此操作,风险自担;翻译质量问题请指正。


  • 本站原创文章仅代表作者观点,不代表SDNLAB立场。所有原创内容版权均属SDNLAB,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用,转载须注明来自 SDNLAB并附上本文链接。
  • 本文链接http://www.sdnlab.com/16581.html
分享到:
相关文章
1条评论

登录后才可以评论

  1. comment reply 几米憧憬 2017/03/22 19:29
    基于什么库实现,能不能讲清晰下
        1楼
SDNLAB君 发表于16-04-19
0