RyuBook1.0案例二:Traffic Monitor项目源码分析
转载:https://www.cnblogs.com/NinWoo/p/9398351.html
Traffic Monitor源码分析 从simple_switch_13.SimpleSwitch13控制器继承并开发 class SimpleMonitor13(simple_switch_13.SimpleSwitch13): 增添datapaths列表,存储交换机id def __init__(self, *args, **kwargs): super(SimpleMonitor13, self).__init__(*args, **kwargs) self.datapaths = {} 引入hub.spawn()函数启动一个新线程,输入为一个新的方法_monitor。 创建一个EventOFPStateChange监听事件,监听MAIN_DISPATCHER,DEAD_DISPATCHER两种情况。 @set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER, DEAD_DISPATCHER]) 监听如果为MAIN_DISPATCHER,并且datapath.id不在datapath列表中,则证明是新加入的交换机 if ev.state == MAIN_DISPATCHER: if datapath.id not in self.datapaths: self.logger.debug('register datapath: %016x', datapath.id) self.datapaths[datapath.id] = datapath 如果为DEAD_DISPATCHER,并且datapath.id在datapath列表中,则证明是掉线的交换机 elif ev.state == DEAD_DISPATCHER: if datapath.id in self.datapaths: self.logger.debug('unregister datapath: %016x', datapath.id) del self.datapaths[datapath.id] _monitor方法,循环不断向datapath列表中的交换机发送Flow状态请求,和Port状态请求 def _monitor(self): while True: for dp in self.datapaths.values(): self._request_stats(dp) hub.sleep(10) def _request_stats(self, datapath): self.logger.debug('send stats request: %016x', datapath.id) ofproto = datapath.ofproto parser = datapath.ofproto_parser req = parser.OFPFlowStatsRequest(datapath) datapath.send_msg(req) req = parser.OFPPortStatsRequest(datapath, 0, ofproto.OFPP_ANY) datapath.send_msg(req) 刚刚发送了请求,现在监听其回复 @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) ... @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) ...
处理一个事件的标准模板 首先,我们来看一个标准的控制器处理事件的模板 @set_ev_cls(ofp_event.Event, DISPATCHER(s)) def your_function(self, ev): ... 简单说,@set_ev_cls(ofp_event.Event, DISPATCHER(s))的含义就是,当接收到DISPATCHER(s)情况的Event事件进行your_function处理。 DISPATCHER(s)可以为单独一个,也可以为由多个DISPATCHER组成的列表,DISPATCHER描述的情况包括: Defination Explanation HANDSHAKE_DISPATCHER 交换HELLO消息 CONFIG_DISPATCHER 等待接收SwitchFeatures消息 MAIN_DISPATCHER 正常状态 DEAD_DISPATCHER 连接断开 其中your_function是由你自己定义的函数处理过程,命名可以任意指定;ofp_event.Event是由ofp_event.py提供的一系列事件,在学习了几个Ryu的程序之后,深感,其核心就在于对这些事件的理解。所以,接下来,在分析学习官方案例的同时,也会整合Ryu源码与OpenFlow1.3.3协议的内容,对这些事件进行深入的理解与分析。 ofp_event ofp_event类位于ryu/controller/ofp_event.py,主要定义了OpenFlow中的各种事件,配合set_cls_ev可以对指定事件进行处理。 ofp_event.EventOFPSwitchFeatures ofp_event.EventOFPPacketIn ofp_event.EventOFPStateChange 在源码中,对EventOFPStateChange这样进行介绍: An event class for negotiation phase change notification. An instance of this class is sent to observer after changing the negotiation phase. An instance has at least the following attributes. ========= ================================================================= Attribute Description ========= ================================================================= datapath ryu.controller.controller.Datapath instance of the switch ========= ================================================================= 意思说,该class是处理协商阶段变更通知的事件,在协商更改后发生此消息给观察者。 当我们使用一下的命令,我们就成为了观察者,发生此类消息时,便可以进行接收和处理 @set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER, DEAD_DISPATCHER]) 详细案例见Traffic Monitor 在协商阶段,MAIN_DISPATCHER意味着有新的交换机接入,DEAD_DISPATCHER意味着有交换机脱离连接。 总结: 发起事件 处理事件 交换机状态变化 EventOFPStateChange ofp_event.EventOFPFlowStatsReply 在源码中,对EventOFPFlowStatsReply这样介绍: Individual flow statistics reply message The switch responds with this message to an individual flow statistics request. 意思说,该事件用于处理个体流量统计回复消息,即统计某一交换机上的流量信息。而流量统计信息存储在body(ev.msg.body)结构体中。具体包括: table_id duration_sec duration_nsec priority idle_timeout hard_timeout flags cookie packet_count byte_count match instructions 使用范例: @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) def flow_stats_reply_handler(self, ev): flows = [] for stat in ev.msg.body: flows.append('table_id=%s ' 'duration_sec=%d duration_nsec=%d ' 'priority=%d ' 'idle_timeout=%d hard_timeout=%d flags=0x%04x ' 'cookie=%d packet_count=%d byte_count=%d ' 'match=%s instructions=%s' % (stat.table_id, stat.duration_sec, stat.duration_nsec, stat.priority, stat.idle_timeout, stat.hard_timeout, stat.flags, stat.cookie, stat.packet_count, stat.byte_count, stat.match, stat.instructions)) self.logger.debug('FlowStats: %s', flows) 来源:源码ryu\ofproto\ofproto_v1_3_parser.py 与上一个事件的产生来源不同,该事件并不是由交换机状态改变而产生,而是由控制器主动发出,触发该事件并接收处理的。控制器主动发出的命令为OFPFlowStatsRequest函数。 同样,查看源码中该函数的文档 """ Individual flow statistics request message The controller uses this message to query individual flow statistics. ================ ====================================================== Attribute Description ================ ====================================================== flags Zero or ``OFPMPF_REQ_MORE`` table_id ID of table to read out_port Require matching entries to include this as an output port out_group Require matching entries to include this as an output group cookie Require matching entries to contain this cookie value cookie_mask Mask used to restrict the cookie bits that must match match Instance of ``OFPMatch`` ================ ====================================================== 看见,该函数作用就是发出流量统计请求的。文档同样给出范例程序: Example:: def send_flow_stats_request(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser cookie = cookie_mask = 0 match = ofp_parser.OFPMatch(in_port=1) req = ofp_parser.OFPFlowStatsRequest(datapath, 0, ofp.OFPTT_ALL, ofp.OFPP_ANY, ofp.OFPG_ANY, cookie, cookie_mask, match) datapath.send_msg(req) 查看构造函数: def __init__(self, datapath, flags=0, table_id=ofproto.OFPTT_ALL, out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, cookie=0, cookie_mask=0, match=None, type_=None): 实际使用过程中,如果没有特定需要,这里我们可以只指定一个datapath参数,其他为缺省的默认值。 小结: 发起事件 处理事件 OFPFlowStatsRequest EventOFPFlowStatsReply ofp_event.EventOFPPortStatsReply 在源码中对该函数的说明如下: """ Port statistics reply message The switch responds with this message to a port statistics request. ================ ====================================================== Attribute Description ================ ====================================================== body List of ``OFPPortStats`` instance ================ ====================================================== 该函数为端口统计应答消息,文档中给的范例程序如下: Example:: @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) def port_stats_reply_handler(self, ev): ports = [] for stat in ev.msg.body: ports.append('port_no=%d ' 'rx_packets=%d tx_packets=%d ' 'rx_bytes=%d tx_bytes=%d ' 'rx_dropped=%d tx_dropped=%d ' 'rx_errors=%d tx_errors=%d ' 'rx_frame_err=%d rx_over_err=%d rx_crc_err=%d ' 'collisions=%d duration_sec=%d duration_nsec=%d' % (stat.port_no, stat.rx_packets, stat.tx_packets, stat.rx_bytes, stat.tx_bytes, stat.rx_dropped, stat.tx_dropped, stat.rx_errors, stat.tx_errors, stat.rx_frame_err, stat.rx_over_err, stat.rx_crc_err, stat.collisions, stat.duration_sec, stat.duration_nsec)) self.logger.debug('PortStats: %s', ports) 通过案例程序,我们可以看到,从该消息中,我们可以获取到: rx_packets tx_packets rx_bytes tx_bytes rx_dropped tx_dropped rx_errors tx_errors rx_frame_err tx_overerr rx_crc_err collisions duration_sec duration_nsec 同样该事件同样对应存在一个端口统计请求事件OFPPortStatsRequest,源码中对该函数的说明如下: """ Port statistics request message The controller uses this message to query information about ports statistics. ================ ====================================================== Attribute Description ================ ====================================================== flags Zero or ``OFPMPF_REQ_MORE`` port_no Port number to read (OFPP_ANY to all ports) ================ ====================================================== 使用范例如下: Example:: def send_port_stats_request(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPPortStatsRequest(datapath, 0, ofp.OFPP_ANY) datapath.send_msg(req) 小结: 发起事件 处理事件 OFPPortStatsRequest EventOFPPortStatsReply