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
原文地址:https://www.cnblogs.com/dream397/p/13157412.html