实验目的
- 在互联网越来越发达的时代,网络上承载着越来越多的业务,所以维护网络的稳定非常重要。
- 当网络发生异常时,需要快速定位故障点,从而快速修复网络,保障业务的畅通。
- 流量监控可以实时获取网络的流量状况,实现网络流量状态可视化。
- 在故障发生时,通过流量监控应用可以快速定位故障点,缩短故障恢复时长。
此外,流量监控的数据也可以为流量工程提供数据支撑,从而提升网络整体的带宽利用率。
示例应用完成了基于OpenFlow1.3协议的流量统计信息的获取
- 包括基于端口的流量统计信息和基于流表项的统计信息
- 示例仅介绍如何获取统计信息,统计信息的处理和呈现将不做介绍
- 更多信息可以参考完整版流量监控应用
实验程序
```pythoncoding:utf-8
from operator import attrgetter from ryu.app import simple_switch_13 from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER,DEAD_DISPATCHER from ryu.controller.handler import set_ev_cls from ryu.lib import hub
class SimpleMonitor(simpleswitch13.SimpleSwitch13): def init(self, args, *kwargs): super(SimpleMonitor, self).__init(args,*kwargs) self.datapaths = {}
# 在初始化SimpleMonitor对象时,会启动一个协程 去周期运行_monitor方法。
# _monitor方法中调用了_request_stats方法
# 该方法向datapath发送了流表统计信息请求OFPFlowStatsRequest和端口统计信息请求OFPPortStatsRequest。
# 这两个请求报文发送到datapath之后,交换机需要回复控制器对应的统计信息回复报文。
self.monitor_thread = hub.spawn(self._monitor)
@set_ev_cls(ofp_event.EventOFPStateChange,
[MAIN_DISPATCHER,DEAD_DISPATCHER])
def _state_change_handler(self, ev):
datapath = ev.datapath
if ev.state == MAIN_DISPATCHER:
if not datapath.id in self.datapaths:
self.logger.debug('register datapath: %016x', datapath.id)
self.datapaths[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]
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)
def _flow_stats_reply_handler(self, ev):
# _flow_stats_reply_handler 方法和_port_stats_reply_handler
# 分别注册了EventOFPFlowStatsReply事件和EventOFPPortStatsReply事件。
# 当交换机回复统计信息报文时,会触发对应的处理方法。
# 比如交换机通过OFPPortStatsReply报文回复控制器其端口统计信息,在Ryu内部就会产生EventOFPPortStatsReply事件
# 该事件会分发到所有注册了该事件的handler,其中就包括_port_stats_reply_handler。
# _port_stats_reply_handler方法被调用之后,会将报文信息解析并打印相关信息,从而完成统计报文的解析处理工作。
# 同理,流表项统计信息的回复信息的处理也是如此。
body = ev.msg.body
self.logger.info('datapath'
'in-port eth-dst'
'out-port packets bytes')
self.logger.info('---------------- '
'-------- ----------------- '
'-------- -------- --------')
for stat in sorted([flow for flow in body if flow.priority == 1],
key=lambda flow: (flow.match['in_port'],
flow.match['eth_dst'])):
self.logger.info('%016x %8x %17s %8x %8d %8d',
ev.msg.datapath.id,
stat.match['in_port'], stat.match['eth_dst'],
stat.instructions[0].actions[0].port,
stat.packet_count, stat.byte_count)
self.logger.info('datapath '
'tcpsrc in-port eth-dst '
'outport packets bytes')
self.logger.info('---------------- '
'------ -- -------- ----------------- '
'------')
# 在成功解析到流量统计信息之后,可以将其保存到对应的数据结构中。
# 然后使用当前数据减去前一周期获取的统计数据,再通过两个统计信息的时间信息,相减得到时间间隔。
# 最后将统计信息数值的差除以时间间隔,从而获得统计流量速度信息,完成流量监控程序。
# 由于代码较长,且此部分内容不属于本示例展示重点
for stat in sorted([flow for flow in body if flow.priority == 2],
key=lambda flow: (flow.match['tcp_src'],
flow.match['in_port'],
flow.match['eth_dst'])):
self.logger.info('%016x %8d %8x %17s %8x %8d %8d',
ev.msg.datapath.id,
stat.match['tcp_src'], stat.match['in_port'], stat.match['eth_dst'],
stat.instructions[0].actions[0].port,
stat.packet_count, stat.byte_count)
@set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
def _port_stats_reply_handler(self, ev):
body = ev.msg.body
self.logger.info('datapath port '
'rx-pkts rxbytes rx-error '
'tx-pkts txbytes tx-error')
self.logger.info('---------------- -------- '
'-------- -------- -------- '
'-------- -------- --------')
for stat in sorted(body, key=attrgetter('port_no')):
self.logger.info('%016x %8x %8d %8d %8d %8d %8d %8d',
ev.msg.datapath.id, stat.port_no,
stat.rx_packets, stat.rx_bytes, stat.rx_errors,
stat.tx_packets, stat.tx_bytes, stat.tx_errors)
<a name="teLDV"></a>
# 运行实验
- 写完以上代码之后将其保存为Python 文件, 如`simple_monitor_13_test.py`,然后保存到ryu/app目录下
- 由于这个文件是新加的,Ryu编译文件中并没有,所以还需要将Ryu重新编译安装
- 重新安装之后,通过ryu-manager命令启动Ryu控制器并加载新写的应用即可
- 具体命令如下
```shell
cd ryu
python setup.py install
ryu-manager ryu/app/simple_monitor_13_test.py
启动Ryu之后,通过Mininet启动任意无环拓扑并连接到控制器。
sudo mn --controller remote
连接成功之后,运行结果如图所示