作为一个业余研究Ryu的软件工程师,一直惊叹于Ryu设计的优雅与简洁。一年多坚持下来,也有自己的一些收获,写出来和大家分享一下。
我们的故事从@set_ev_cls这个被大量使用的装饰器开始。
1 2 3 4 5 6 7 |
def set_ev_cls(ev_cls, dispatchers=None): def _set_ev_cls_dec(handler): handler.ev_cls = ev_cls handler.dispatchers = _listify(dispatchers) handler.observer = ev_cls.__module__ return handler return _set_ev_cls_dec |
装饰器的内容已经有小伙伴写到了,这里不再重复。我关心的重点在第一个参数ev_cls上,
比如simple_switch_13.py中@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
EventOFPPacketIn,类究竟在哪定义,我在\ryu\controller\ofp_event文件中没有找到,甚至搜索完了整个ryu工程都没有搜到。与此类似的ofp_event.EventOFPPortStatus, ofp_event.EventOFPFlowRemoved,ofp_event.EventOFPDescStatsReply等等,他们这些类的定义也没有找到。
某个烈日炎炎的下午,不经意的我在\ryu\controller\ofp_event文件中发现了一点蛛丝马迹:
1 2 3 4 |
_OFP_MSG_EVENTS = {} def _ofp_msg_name_to_ev_name(msg_name): return 'Event' + msg_name |
将ofp的msg名字加上“Event的前缀”,这个有点像。
1 2 3 4 5 6 7 8 9 10 11 12 |
def _create_ofp_msg_ev_class(msg_cls): name = _ofp_msg_name_to_ev_name(msg_cls.__name__) # print 'creating ofp_event %s' % name if name in _OFP_MSG_EVENTS: return cls = type(name, (EventOFPMsgBase,), dict(__init__=lambda self, msg: super(self.__class__, self).__init__(msg))) globals()[name] = cls _OFP_MSG_EVENTS[name] = cls |
喔,这个地方creat了一个类,好像就是ev_class(type的用法:创建类的类,元类,以name为类型,以EventOFPMsgBase为父类,并定义了初始化方法,并放到全局字典globals和_OFP_MSG_EVENTS),动态生成需要的类,小样,真有你的。
1 2 3 4 5 6 |
def _create_ofp_msg_ev_from_module(ofp_praser): # print mod for _k, cls in inspect.getmembers(ofp_parser, inspect.isclass): if not hasattr(cls, 'cls_msg_type'): continue _create_ofp_msg_ev_class(cls) |
更进一步从模块名创建ev_class,明确标明了,有'cls_msg_type'属性的才有资格创建类。
1 2 3 4 |
for ofp_mods in ofproto.get_ofp_modules().values(): ofp_parser = ofp_mods[1] # print 'loading module %s' % ofp_parser _create_ofp_msg_ev_from_module(ofp_parser) |
终于看到调用的地方了,在同一个文件中的这段代码可不是定义函数,而是直接执行语句哦。其中get_ofp_modules就是获取ofp的版本文件的parser,然后根据这些文件创建对应了msg_ev_class.我们去ryu\ofproto\ofproto_v1_X_parser(X可以为0,1,2,3,你懂的)文件中确认一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@_register_parser @_set_msg_type(ofproto_v1_3.OFPT_PACKET_IN) #自动给类加上cls_msg_type属性 class OFPPacketIn(MsgBase): """ Packet-In message The switch sends the packet that received to the controller by this message. ....... Example:: @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev): msg = ev.msg ofp = dp.ofproto if msg.reason == ofp.OFPR_NO_MATCH: reason = 'NO MATCH' elif msg.reason == ofp.OFPR_ACTION: reason = 'ACTION' elif msg.reason == ofp.OFPR_INVALID_TTL: reason = 'INVALID TTL' else: reason = 'unknown' self.logger.debug('OFPPacketIn received: ' 'buffer_id=%x total_len=%d reason=%s ' 'table_id=%d cookie=%d match=%s data=%s', msg.buffer_id, msg.total_len, reason, msg.table_id, msg.cookie, msg.match, utils.hex_array(msg.data)) """ |
看,还真是这样。连注释上都写得是一模一样的Example,其他诸如ofp_event.EventOFPPortStatus, ofp_event.EventOFPFlowRemoved,ofp_event.EventOFPDescStatsReply也能找到对应的地方。
这下终于真相大白,我们脑海中有了这样一幅画面,自动去搜索ofp不同版本的模块,找到有cls_msg_type属性的类,然后把它们的名字变换一下,生成对应的类。Ryu的优美与简洁,果然名不虚传。
还有我们的ofp_msg_to_ev,看它的定义是生成类的实例。在Datapath类的_recv_loop中,Ryu从数据流中解析出msg,然后根据msg生成上面msg_ev_class的实例,并通过ofp_brick发送给观察者。send_event_to_observers(ev, self.state)是通过SERVICE_BRICKS(服务实体链表,其实就是所有app的字典)来进行不同模块中的通信的。
在RyuApp的register_handler,register_observer可以看到,观察者和handler的注册都是通过类名。而RyuApp的get_handlers,get_observers方法传入得参数是实例。
这样我们就知道了@set_ev_cls如何用ofp的event类去修饰handler方法,以及如何由具体的报文,得到对应的实例,最后调用到具体的handler方法上。良好的命名规范还真有助于增加代码的可读性。
我们的李呈大神在《RYU源码解读》(https://www.sdnlab.com/6395.html)已经把ryu的启动流程给我们大致讲解了。
不过我本人觉得还不过瘾,有几点在这里补充一下。
1.hanglder.reuster_service是在import的时候被执行的,一般都在app文件的最开头,而不是在使用@set_ev_cls装饰器的时候。
2._Event的使用在\ryu\topology\switches中有如下代码
1 2 3 4 5 |
class Switches(app_manager.RyuApp): _EVENTS = [event.EventSwitchEnter, event.EventSwitchLeave, event.EventPortAdd, event.EventPortDelete, event.EventPortModify, event.EventLinkAdd, event.EventLinkDelete] |
而在\ryu\base\app_manager文件中有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class AppManager(object): def instantiate_apps(self, *args, **kwargs): .......... # name is module name of ev_cls name = m.observer.split('.')[-1] #如果是使用@set_ev_handler装饰,name为None if name in SERVICE_BRICKS: brick = SERVICE_BRICKS[name] brick.register_observer(m.ev_cls, i.name, m.dispatchers) # allow RyuApp and Event class are in different module for brick in SERVICE_BRICKS.itervalues(): if m.ev_cls in brick._EVENTS: brick.register_observer(m.ev_cls, i.name, m.dispatchers) |
从这些线索中我们可以看到_EVENTS其实就是上文中动态生成的msg_ev_class的补充,因为不属于OFP模块的内容,自然需要other模块提供。当然Event类的来源还通过import得到,比如\ryu\lib\lacplib.py就定义了大量的Event类,simple_switch_lacp_13.py就是通过from ryu.lib import lacplib来使用的。
3.@set_ev_cls还有个兄弟@set_ev_handler,在\ryu\app\gre_tunnel和\ryu\controller\ofp_handler有使用。两者的区别在set_ev_handler收到对应的事件后自己处理就行了,不需要再通知给observer,而set_ev_cls先通知给observer,再自己处理。原因如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# should be named something like 'observe_event' def set_ev_cls(ev_cls, dispatchers=None): def _set_ev_cls_dec(handler): handler.ev_cls = ev_cls handler.dispatchers = _listify(dispatchers) handler.observer = ev_cls.__module__ #我有这句话,我的兄弟可没有喔 return handler return _set_ev_cls_dec def set_ev_handler(ev_cls, dispatchers=None): def _set_ev_cls_dec(handler): handler.ev_cls = ev_cls handler.dispatchers = _listify(dispatchers) return handler return _set_ev_cls_dec |
读完了这些,不得不感叹,RYU实在是太美妙了。它的内部机制的实现,很值得我们学习和借鉴。
作者简介:童渊 汉柏科技软件工程师,业余研究Ryu,博客地址是http://blog.sina.com.cn/u/2399557665