前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Ryu打造自定义控制器

基于Ryu打造自定义控制器

作者头像
SDNLAB
发布2018-04-03 11:42:52
1.4K0
发布2018-04-03 11:42:52
举报
文章被收录于专栏:SDNLABSDNLAB

控制器是SDN网络中最重要的组成部分。在开发SDN应用时,需要基于某一个控制器开发,而大部分开源控制器都是一个框架或者平台,更多个性化的设置和应用需要开发者自己完成。对于开发者而言,一个自定义的控制器可以让控制器更加适配开发场景,发挥控制器最大的作用,提高开发效率。本篇文章将以Ryu为例,介绍如何修改Ryu源码,打造属于自己风格的自定义控制器。其内容包括自定义参数,事件,启动顺序,报文,协议和底层服务。

自定义参数

很多应用都会涉及相关参数的输入才能运行, 如OpenFlow协议的启动需要配置监听端口。在编写新应用时,需要获取相关参数的值来运行应用,所以需要开发应用时注册参数。目前已有的参数可以通过ryu-manager -h查看。而不同的应用注册的参数很可能名字一样,这就有可能发生冲突。为解决这个问题,Ryu采用了OpenStack的Oslo库,支持全局的命令注册和解析。Oslo支持全局的命令注册和解析,成功解除了命令冲突的难题,也大大降低了参数注册和解析的难度。注册新参数的方法很简单,只需要新建一个文件,然后按照如下案例完成注册,最后再在cmd/manager.py中import即可。

代码语言:javascript
复制
from ryu import cfg
CONF = cfg.CONF
CONF.register<em>cli</em>opts([
cfg.StrOpt(
'milestone-argument', default='', help='my test argument'),
cfg.IntOpt('milestone-server-port', default=6666,
help='milestone server port')])

from ryu import flags
from ryu import version
from ryu.app import wsgi
from ryu.base.app_manager import AppManager
from ryu.controller import controller
from ryu.milestone import milestone
from ryu.topology import switches

通过python setup.py install重新安装Ryu之后即可通过ryu-manager -h来查看到新注册的命令。

自定义事件

Ryu的应用之间通信通过事件机制来完成。比如底层的协议解析模块解析报文之后,生成对应的报文事件,然后分发到各个监听该事件的监听函数。不过,目前为止事件类型还不够丰富,很多需要的信息还无法通过事件传递。比如网络流量监控服务监控到流量发生变化的事件之后,需要生成对应的事件。又比如OFPPacket_in事件包含了所有报文类型的数据,还可以根据触发Packet_in的数据的协议类型来定义细粒度的事件去分流,从而使得处理ARP报文的函数只接收ARP报文的Packet_in, 而不是全部接收到然后再去判断是否是不是ARP报文。以网络流量变化事件EventOXPTrafficStateChange为例,定义事件,生成事件和处理事件的流程如下:

(1) 在controller/ofp_event.py中添加相关类描述:

代码语言:javascript
复制
class EventOXPTrafficStateChange(event.EventBase):
def <strong>init</strong>(self, traffic=None):
super(EventOXPTrafficStateChange, self).<strong>init</strong>()
self.traffic = traffic

(2) 在相关应用中实例化事件,并通过OFPHandler模块的send_event_to_observers函数分发到指定函数。

代码语言:javascript
复制
event = oxp_event.EventOXPTrafficStateChange(traffic=self.free_band_width)
self.oxp_brick = app_manager.lookup_service_brick('oxp_event')
self.oxp_brick.send_event_to_observers(event, MAIN_DISPATCHER)

(3) 注册handler处理事件, 使用@set_ev_cls来注册handler监听该事件。

代码语言:javascript
复制
@set<em>ev</em>cls(oxp<em>event.EventOXPTrafficStateChange, MAIN<em>DISPATCHER)
def reflesh</em>bw</em>best<em>path(self, ev):
self.free</em>band_width = ev.traffic
do<em>some</em>thing(self.free<em>band</em>width)

像Packet_in分流的定义可以参考以上的做法,将事件作为新事件的属性。也可以参考ofp_event模块中的修改类名的方式将类名修改,从而生成新的事件。

自定义启动顺序

Ryu关于Application的启动源码在cmd/manager.py文件中。main函数中完成了app_lists的赋值,即启动应用的顺序。若在Ryu开发了一个很重要的底层应用,需要在启动那个时启动,则可以将其加入app_lists中. 比如当milestone参数为1时,启动多个应用,否则仅启动基础应用的示例如下:

代码语言:javascript
复制
if not app<em>lists:
app</em>lists = ['ryu.controller.ofp_handler']
if CONF.milestone == '1':
my<em>app</em>list = ["muzixing", "milestone"]
app_lists.extend(my<em>app</em>list)

这个自定义启动顺序一般情况下不需要修改,但是当开发的应用是非常重要的底层服务时,可能需要初始化就启动,所以也是非常关键的自定义内容之一。

自定义报文

在某些特定的场景中,需要对OpenFlow协议等协议进行报文拓展,从而完成新报文,新字段的测试工作。在Ryu中自定义OpenFlow报文的操作步骤如下:

(1) 在对应版本的ofproto_v1_x.py中定义所有需要使用到的字段值,如最重要的报文类型,以定义一个名为milestone的报文为例,报文类型为25, 字段只有一个字符串类型的data, 可以用来放任何信息,长度为64bits。所以在文件中定义/声明 报文名字和类型的对应,报文的格式和长度,以便序列化和解析。Ryu使用struct对数据进行序列化和解析。

代码语言:javascript
复制
OFPT<em>MILESTONE = 25
OFP</em>MILESTONE<em>PACK<em>STR = '!8s' #定义报文格式
OFP</em>MILESTONE</em>SIZE = 16 # 定义报文长度
assert (calcsize(OFP<em>MILESTONE</em>PACK_STR) + OFP<em>HEADER</em>SIZE ==OFP<em>MILESTONE</em>SIZE) # 检查长度是否正确

(2) 在对应版本的ofproto_v1_x_parser.py中添加对应报文类的定义,包括其解析方法和序列化方法。其中解析方法是一个类方法,在MsgBase中定义,派生类调用时需要使用类型来区分。序列化方法分为self._serialize_pre(), self._serialize_body()和self._serialize_header()三部分。派生类仅需完成self._serialize_body()的内容即可。值得注意的是,如果需要对报文进行除报头以外的解析,则必须在完成parser函数之后使用@_register_parser装饰符将对应函数和类名的映射关系加入到关系字典中,以便使用时查找,否则会报错。

代码语言:javascript
复制
@_register_parser
@_set_msg_type(ofproto.OFPT_MILESTONE)
class OFPMILESTONE(MsgBase):
"""
MILESTONE message
 
It is a test msg:www.muzixing.com
========== ===============================
Attribute  Description
========== ===============================
data       just some data.
========== ===============================
"""
def __init__(self, datapath, data=None):
    super(OFPMILESTONE, self).__init__(datapath)
    self.data = data
@classmethod
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
    msg = super(OFPMILESTONE, cls).parser(datapath, version, msg_type,
     msg_len, xid, buf)
    offset = ofproto.OFP_HELLO_HEADER_SIZE
    data = struct.unpack_from(ofproto.OFP_MILESTONE_PACK_STR, msg.buf, offset)
    msg.data = data
    return msg
def _serialize_body(self):
    msg_pack_into(ofproto.OFP_MILESTONE_PACK_STR, self.buf,
                  ofproto.OFP_HEADER_SIZE, self.data)

至此,自定义报文完成。关于struct模块的使用,以及OpenFlow协议代码的介绍可以参考另一篇文章《Ryu:OpenFlow协议源码分析》。重新安装Ryu即可将该报文写入到Ryu运行代码中,Ryu的事件机制会自动将这个报文生成对应的事件,进一步测试需要读者自行开发。

自定义协议

既然讲到自定义报文,那么继续提一下自定义协议。读者可以根据《Ryu:OpenFlow协议源码分析》提到的思路去模仿编写一个新的协议。底层的数据收发可以学习controller/controller.py, 协议定义可以学习ofproto目录下的一系列内容。各种需要自定义的细节,如自定义参数,自定义事件等都已经在上文提到。后续将会专门书写如何在Ryu中开发新协议,本文不再展开。

自定义服务

为了更好的开发应用,开发者应该开发一套底层的服务,为自己的进一步开发提供帮助。如ARP代理,DHCP服务,基础的网络资源感知服务等等。SDN集中式的优点很大在于拥有全局的视角,可以掌握全局的资源,从而进行全局最优的业务部署。所有业务的基础都基于对网络资源的感知,所以此处以网络资源感知为例。首先需要完成网络拓扑的最优路径的计算,此外处于某些场景的需求,需要收集网络流量状况的数据,从而完成基于流量的最优化决策。在此基础之上,完成基础的 最短路径转发应用,实现最基础的网络应用,为其他更高层次的应用开发和算法验证提供基本的服务支撑。由于篇幅限制,本文不加展开,详情可查看《SDN网络感知服务与最短路径应用》。如果读者希望自己搭建一套底层服务,或者在笔者的基础之上加工,推荐使用networkx进行拓扑信息的存储。networkx提供了大量高效有用的函数,可以最大程度降低开发者在算法问题上的工作量。

完成以上应用之后,可将其作为启动服务的一种,并通过参数来确定是否启动Ryu时启动这些业务。这些业务中涉及到的自定义事件,可用于与上层应用之间的通信,实现定制化的SDN控制器。

总结

本篇文章介绍了基于Ryu打造自定义控制器的内容,包括自定义参数,事件,启动顺序,报文,协议和服务。相信读者如果能根据应用场景进行深度自定义,可以很大程度上提升开发效率。关于自定义协议部分,后续会有更多文章介绍,敬请期待。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2015-11-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 SDNLAB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档