实验目的

  • 在互联网越来越发达的时代,网络上承载着越来越多的业务,所以维护网络的稳定非常重要。
  • 当网络发生异常时,需要快速定位故障点,从而快速修复网络,保障业务的畅通。
  • 流量监控可以实时获取网络的流量状况,实现网络流量状态可视化。
  • 在故障发生时,通过流量监控应用可以快速定位故障点,缩短故障恢复时长。
  • 此外,流量监控的数据也可以为流量工程提供数据支撑,从而提升网络整体的带宽利用率。

  • 示例应用完成了基于OpenFlow1.3协议的流量统计信息的获取

  • 包括基于端口的流量统计信息和基于流表项的统计信息
  • 示例仅介绍如何获取统计信息,统计信息的处理和呈现将不做介绍
  • 更多信息可以参考完整版流量监控应用

    实验程序

    ```python

    coding: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 = {}

  1. # 在初始化SimpleMonitor对象时,会启动一个协程 去周期运行_monitor方法。
  2. # _monitor方法中调用了_request_stats方法
  3. # 该方法向datapath发送了流表统计信息请求OFPFlowStatsRequest和端口统计信息请求OFPPortStatsRequest。
  4. # 这两个请求报文发送到datapath之后,交换机需要回复控制器对应的统计信息回复报文。
  5. self.monitor_thread = hub.spawn(self._monitor)
  6. @set_ev_cls(ofp_event.EventOFPStateChange,
  7. [MAIN_DISPATCHER,DEAD_DISPATCHER])
  8. def _state_change_handler(self, ev):
  9. datapath = ev.datapath
  10. if ev.state == MAIN_DISPATCHER:
  11. if not datapath.id in self.datapaths:
  12. self.logger.debug('register datapath: %016x', datapath.id)
  13. self.datapaths[datapath.id] = datapath
  14. elif ev.state == DEAD_DISPATCHER:
  15. if datapath.id in self.datapaths:
  16. self.logger.debug('unregister datapath: %016x', datapath.id)
  17. del self.datapaths[datapath.id]
  18. def _monitor(self):
  19. while True:
  20. for dp in self.datapaths.values():
  21. self._request_stats(dp)
  22. hub.sleep(10)
  23. def _request_stats(self, datapath):
  24. self.logger.debug('send stats request: %016x', datapath.id)
  25. ofproto = datapath.ofproto
  26. parser = datapath.ofproto_parser
  27. req = parser.OFPFlowStatsRequest(datapath)
  28. datapath.send_msg(req)
  29. req = parser.OFPPortStatsRequest(datapath, 0, ofproto.OFPP_ANY)
  30. datapath.send_msg(req)
  31. @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
  32. def _flow_stats_reply_handler(self, ev):
  33. # _flow_stats_reply_handler 方法和_port_stats_reply_handler
  34. # 分别注册了EventOFPFlowStatsReply事件和EventOFPPortStatsReply事件。
  35. # 当交换机回复统计信息报文时,会触发对应的处理方法。
  36. # 比如交换机通过OFPPortStatsReply报文回复控制器其端口统计信息,在Ryu内部就会产生EventOFPPortStatsReply事件
  37. # 该事件会分发到所有注册了该事件的handler,其中就包括_port_stats_reply_handler。
  38. # _port_stats_reply_handler方法被调用之后,会将报文信息解析并打印相关信息,从而完成统计报文的解析处理工作。
  39. # 同理,流表项统计信息的回复信息的处理也是如此。
  40. body = ev.msg.body
  41. self.logger.info('datapath'
  42. 'in-port eth-dst'
  43. 'out-port packets bytes')
  44. self.logger.info('---------------- '
  45. '-------- ----------------- '
  46. '-------- -------- --------')
  47. for stat in sorted([flow for flow in body if flow.priority == 1],
  48. key=lambda flow: (flow.match['in_port'],
  49. flow.match['eth_dst'])):
  50. self.logger.info('%016x %8x %17s %8x %8d %8d',
  51. ev.msg.datapath.id,
  52. stat.match['in_port'], stat.match['eth_dst'],
  53. stat.instructions[0].actions[0].port,
  54. stat.packet_count, stat.byte_count)
  55. self.logger.info('datapath '
  56. 'tcpsrc in-port eth-dst '
  57. 'outport packets bytes')
  58. self.logger.info('---------------- '
  59. '------ -- -------- ----------------- '
  60. '------')
  61. # 在成功解析到流量统计信息之后,可以将其保存到对应的数据结构中。
  62. # 然后使用当前数据减去前一周期获取的统计数据,再通过两个统计信息的时间信息,相减得到时间间隔。
  63. # 最后将统计信息数值的差除以时间间隔,从而获得统计流量速度信息,完成流量监控程序。
  64. # 由于代码较长,且此部分内容不属于本示例展示重点
  65. for stat in sorted([flow for flow in body if flow.priority == 2],
  66. key=lambda flow: (flow.match['tcp_src'],
  67. flow.match['in_port'],
  68. flow.match['eth_dst'])):
  69. self.logger.info('%016x %8d %8x %17s %8x %8d %8d',
  70. ev.msg.datapath.id,
  71. stat.match['tcp_src'], stat.match['in_port'], stat.match['eth_dst'],
  72. stat.instructions[0].actions[0].port,
  73. stat.packet_count, stat.byte_count)
  74. @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
  75. def _port_stats_reply_handler(self, ev):
  76. body = ev.msg.body
  77. self.logger.info('datapath port '
  78. 'rx-pkts rxbytes rx-error '
  79. 'tx-pkts txbytes tx-error')
  80. self.logger.info('---------------- -------- '
  81. '-------- -------- -------- '
  82. '-------- -------- --------')
  83. for stat in sorted(body, key=attrgetter('port_no')):
  84. self.logger.info('%016x %8x %8d %8d %8d %8d %8d %8d',
  85. ev.msg.datapath.id, stat.port_no,
  86. stat.rx_packets, stat.rx_bytes, stat.rx_errors,
  87. stat.tx_packets, stat.tx_bytes, stat.tx_errors)
  1. <a name="teLDV"></a>
  2. # 运行实验
  3. - 写完以上代码之后将其保存为Python 文件, 如`simple_monitor_13_test.py`,然后保存到ryu/app目录下
  4. - 由于这个文件是新加的,Ryu编译文件中并没有,所以还需要将Ryu重新编译安装
  5. - 重新安装之后,通过ryu-manager命令启动Ryu控制器并加载新写的应用即可
  6. - 具体命令如下
  7. ```shell
  8. cd ryu
  9. python setup.py install
  10. ryu-manager ryu/app/simple_monitor_13_test.py
  • 启动Ryu之后,通过Mininet启动任意无环拓扑并连接到控制器。

    1. sudo mn --controller remote
  • 连接成功之后,运行结果如图所示

image.png