SDN实验---Ryu的应用开发(六)网络拓扑时延探测

2024-02-10 09:10

本文主要是介绍SDN实验---Ryu的应用开发(六)网络拓扑时延探测,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一:实验原理

网络时延探测应用利用了Ryu自带的Switches模块的数据,获取到了LLDP数据发送时的时间戳,然后和收到的时间戳进行相减,得到了LLDP数据包从控制器下发到交换机A,然后从交换机A到交换机B,再上报给控制器的时延T1,示例见图1的蓝色箭头。同理反向的时延T2由绿色的箭头组成。

此外,控制器到交换机的往返时延由一个蓝色箭头和一个绿色箭头组成,此部分时延由echo报文测试,分别为Ta,Tb。最后链路的前向后向平均时延T=(T1+T2-Ta-Tb)/2。

二:时延探测代码实现

        (一)拓扑发现模块

from ryu.base import app_managerfrom ryu.ofproto import ofproto_v1_3from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER,DEAD_DISPATCHER #只是表示datapath数据路径的状态
from ryu.controller.handler import set_ev_clsfrom ryu.lib import hub
from ryu.lib.packet import packet,ethernetfrom ryu.topology import event,switches
from ryu.topology.api import get_switch,get_link,get_hostimport threading,time,randomDELAY_MONITOR_PERIOD = 5class TopoDetect(app_manager.RyuApp):OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]def __init__(self,*args,**kwargs):super(TopoDetect,self).__init__(*args,**kwargs)self.topology_api_app = selfself.name = "topology"self.link_list = Noneself.switch_list = Noneself.host_list = Noneself.dpid2id = {}self.id2dpid = {}self.dpid2switch = {}self.ip2host = {}self.ip2switch = {}self.net_size = 0self.net_topo = []self.net_flag = Falseself.net_arrived = 0self.monitor_thread = hub.spawn(self._monitor)def _monitor(self):  #修改,只获取拓扑,不主动显示!!!"""协程实现伪并发,探测拓扑状态"""while True:#print("------------------_monitor")self._host_add_handler(None) #主机单独提取处理self.get_topology(None)hub.sleep(DELAY_MONITOR_PERIOD) #5秒一次@set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER)def switch_feature_handle(self,ev):"""datapath中有配置消息到达"""#print("------XXXXXXXXXXX------%d------XXXXXXXXXXX------------switch_feature_handle"%self.net_arrived)#print("----%s----------",ev.msg)msg = ev.msgdatapath = msg.datapathofproto = datapath.ofprotoofp_parser = datapath.ofproto_parsermatch = ofp_parser.OFPMatch()actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)]self.add_flow(datapath=datapath,priority=0,match=match,actions=actions,extra_info="config infomation arrived!!")def add_flow(self,datapath,priority,match,actions,idle_timeout=0,hard_timeout=0,extra_info=None):#print("------------------add_flow:")if extra_info != None:print(extra_info)ofproto = datapath.ofprotoofp_parser = datapath.ofproto_parserinst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)]mod = ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,idle_timeout=idle_timeout,hard_timeout=hard_timeout,match=match,instructions=inst)datapath.send_msg(mod);@set_ev_cls(ofp_event.EventOFPPacketIn,MAIN_DISPATCHER)def packet_in_handler(self,ev):#print("------------------packet_in_handler")msg = ev.msgdatapath = msg.datapathofproto = datapath.ofprotoofp_parser = datapath.ofproto_parserdpid = datapath.idin_port = msg.match['in_port']pkt = packet.Packet(msg.data)eth_pkt = pkt.get_protocol(ethernet.ethernet)dst = eth_pkt.dstsrc = eth_pkt.src#self.logger.info("------------------Controller %s get packet, Mac address from: %s send to: %s , send from datapath: %s,in port is: %s"#                    ,dpid,src,dst,dpid,in_port)self.get_topology(None)@set_ev_cls([event.EventHostAdd])def _host_add_handler(self,ev):    #主机信息单独处理,不属于网络拓扑self.host_list = get_host(self.topology_api_app) #3.需要使用pingall,主机通过与边缘交换机连接,才能告诉控制器#获取主机信息字典ip2host{ipv4:host object}  ip2switch{ipv4:dpid}for i,host in enumerate(self.host_list):self.ip2switch["%s"%host.ipv4] = host.port.dpidself.ip2host["%s"%host.ipv4] = hostevents = [event.EventSwitchEnter, event.EventSwitchLeave,event.EventSwitchReconnected,event.EventPortAdd, event.EventPortDelete,event.EventPortModify,event.EventLinkAdd, event.EventLinkDelete]@set_ev_cls(events)def get_topology(self,ev):#print("------+++++++++++------%d------+++++++++++------------get_topology"%self.net_arrived)self.net_flag = Falseself.net_topo = []#print("-----------------get_topology")#获取所有的交换机、链路self.switch_list = get_switch(self.topology_api_app) #1.只要交换机与控制器联通,就可以获取self.link_list = get_link(self.topology_api_app) #2.在ryu启动时,加上--observe-links即可用于拓扑发现#获取交换机字典id2dpid{id:dpid} dpid2switch{dpid:switch object}for i,switch in enumerate(self.switch_list):self.id2dpid[i] = switch.dp.idself.dpid2id[switch.dp.id] = iself.dpid2switch[switch.dp.id] = switch#根据链路信息,开始获取拓扑信息self.net_size = len(self.id2dpid) #表示网络中交换机个数for i in range(self.net_size):self.net_topo.append([0]*self.net_size)for link in self.link_list:src_dpid = link.src.dpidsrc_port = link.src.port_nodst_dpid = link.dst.dpiddst_port = link.dst.port_notry:sid = self.dpid2id[src_dpid]did = self.dpid2id[dst_dpid]except KeyError as e:#print("--------------Error:get KeyError with link infomation(%s)"%e)returnself.net_topo[sid][did] = [src_port,0] #注意:这里0表示存在链路,后面可以修改为时延self.net_topo[did][sid] = [dst_port,0] #注意:修改为列表,不要用元组,元组无法修改,我们后面要修改时延self.net_flag = True #表示网络拓扑创建成功def show_topology(self):print("-----------------show_topology")print("----------switch network----------")line_info = "         "for i in range(self.net_size):line_info+="        s%-5d        "%self.id2dpid[i]print(line_info)for i in range(self.net_size):line_info = "s%d      "%self.id2dpid[i]for j in range(self.net_size):if self.net_topo[i][j] == 0:line_info+="%-22d"%0else:line_info+="(%d,%.12f)    "%tuple(self.net_topo[i][j])print(line_info)print("----------host 2 switch----------")for key,val in self.ip2switch.items():print("%s---s%d"%(key,val))

        (二)模块导入 

from ryu.base import app_manager
from ryu.base.app_manager import lookup_service_brickfrom ryu.ofproto import ofproto_v1_3from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER,DEAD_DISPATCHER,HANDSHAKE_DISPATCHER #只是表示datapath数据路径的状态
from ryu.controller.handler import set_ev_clsfrom ryu.lib import hub
from ryu.lib.packet import packet,ethernetfrom ryu.topology.switches import Switches
from ryu.topology.switches import LLDPPacketimport time

        (三)数据结构 

ECHO_REQUEST_INTERVAL = 0.05
DELAY_DETECTING_PERIOD = 5class DelayDetect(app_manager.RyuApp):OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]def __init__(self,*args,**kwargs):super(DelayDetect,self).__init__(*args,**kwargs)self.name = "delay"self.topology = lookup_service_brick("topology") #注意:我们使用lookup_service_brick加载模块实例时,对于我们自己定义的app,我们需要在类中定义self.name。self.switches = lookup_service_brick("switches") #此外,最重要的是:我们启动本模块DelayDetect时,必须同时启动自定义的模块!!! 比如:ryu-manager ./TopoDetect.py ./DelayDetect.py --verbose --observe-linksself.dpid2switch = {} #或者直接为{},也可以。下面_state_change_handler也会添加进去self.dpid2echoDelay = {} #记录echo时延self.src_sport_dst2Delay = {} #记录LLDP报文测量的时延。实际上可以直接更新,这里单独记录,为了单独展示 {”src_dpid-srt_port-dst_dpid“:delay}self.detector_thread = hub.spawn(self._detector)

        (四)协程获取链路时延 

def _detector(self):"""协程实现伪并发,探测链路时延"""while True:if self.topology == None:self.topology = lookup_service_brick("topology")if self.topology.net_flag:#print("------------------_detector------------------")self._send_echo_request()self.get_link_delay()if self.topology.net_flag:try:self.show_delay()self.topology.show_topology()  #拓扑显示except Exception as err:print("------------------Detect delay failure!!!------------------")hub.sleep(DELAY_DETECTING_PERIOD) #5秒一次

        (五)获取Echo时延 

def _send_echo_request(self):"""发生echo报文到datapath"""for datapath in self.dpid2switch.values():parser = datapath.ofproto_parserecho_req = parser.OFPEchoRequest(datapath,data=bytes("%.12f"%time.time(),encoding="utf8")) #获取当前时间datapath.send_msg(echo_req)#重要!不要同时发送echo请求,因为它几乎同时会生成大量echo回复。#在echo_reply_处理程序中处理echo reply时,会产生大量队列等待延迟。hub.sleep(ECHO_REQUEST_INTERVAL)@set_ev_cls(ofp_event.EventOFPEchoReply,[MAIN_DISPATCHER,CONFIG_DISPATCHER,HANDSHAKE_DISPATCHER])def echo_reply_handler(self,ev):"""处理echo响应报文,获取控制器到交换机的链路往返时延Controller|    echo latency |  `|‘ Switch        """now_timestamp = time.time()try:echo_delay = now_timestamp - eval(ev.msg.data)self.dpid2echoDelay[ev.msg.datapath.id] = echo_delayexcept:return

         (六)获取LLDP时延

 @set_ev_cls(ofp_event.EventOFPPacketIn,MAIN_DISPATCHER)def packet_in_handler(self,ev): #处理到达的LLDP报文,从而获得LLDP时延"""Controller|        /|\    \|/         |Switch----->Switch"""msg = ev.msgtry:src_dpid,src_outport = LLDPPacket.lldp_parse(msg.data) #获取两个相邻交换机的源交换机dpid和port_no(与目的交换机相连的端口)dst_dpid = msg.datapath.id #获取目的交换机(第二个),因为来到控制器的消息是由第二个(目的)交换机上传过来的dst_inport = msg.match['in_port']if self.switches is None:self.switches = lookup_service_brick("switches") #获取交换机模块实例#获得key(Port类实例)和data(PortData类实例)for port in self.switches.ports.keys(): #开始获取对应交换机端口的发送时间戳if src_dpid == port.dpid and src_outport == port.port_no: #匹配keyport_data = self.switches.ports[port] #获取满足key条件的values值PortData实例,内部保存了发送LLDP报文时的timestamp信息timestamp = port_data.timestampif timestamp:delay = time.time() - timestampself._save_delay_data(src=src_dpid,dst=dst_dpid,src_port=src_outport,lldpdealy=delay)except:returndef _save_delay_data(self,src,dst,src_port,lldpdealy):key = "%s-%s-%s"%(src,src_port,dst)self.src_sport_dst2Delay[key] = lldpdealy

        (七)根据LLDP和Echo时延,更新网络拓扑图中的权值信息 

def get_link_delay(self):"""更新图中的权值信息"""print("--------------get_link_delay-----------")for src_sport_dst in self.src_sport_dst2Delay.keys():src,sport,dst = tuple(map(eval,src_sport_dst.split("-")))if src in self.dpid2echoDelay.keys() and dst in self.dpid2echoDelay.keys():sid,did = self.topology.dpid2id[src],self.topology.dpid2id[dst]if self.topology.net_topo[sid][did] != 0:if self.topology.net_topo[sid][did][0] == sport:s_d_delay = self.src_sport_dst2Delay[src_sport_dst]-(self.dpid2echoDelay[src]+self.dpid2echoDelay[dst])/2;if s_d_delay < 0: #注意:可能出现单向计算时延导致最后小于0,这是不允许的。则不进行更新,使用上一次原始值continueself.topology.net_topo[sid][did][1] = self.src_sport_dst2Delay[src_sport_dst]-(self.dpid2echoDelay[src]+self.dpid2echoDelay[dst])/2

        (八)显示网络拓扑图和Echo、LLDP时延信息 

@set_ev_cls(ofp_event.EventOFPStateChange,[MAIN_DISPATCHER, DEAD_DISPATCHER])def _state_change_handler(self, ev):datapath = ev.datapathif ev.state == MAIN_DISPATCHER:if not datapath.id in self.dpid2switch:self.logger.debug('Register datapath: %016x', datapath.id)self.dpid2switch[datapath.id] = datapathelif ev.state == DEAD_DISPATCHER:if datapath.id in self.dpid2switch:self.logger.debug('Unregister datapath: %016x', datapath.id)del self.dpid2switch[datapath.id]if self.topology == None:self.topology = lookup_service_brick("topology")print("-----------------------_state_change_handler-----------------------")print(self.topology.show_topology())print(self.switches)def show_delay(self):print("-----------------------show echo delay-----------------------")for key,val in self.dpid2echoDelay.items():print("s%d----%.12f"%(key,val))print("-----------------------show LLDP delay-----------------------")for key,val in self.src_sport_dst2Delay.items():print("%s----%.12f"%(key,val))

三:实验测试

        (一)启动Ryu

ryu-manager findtopo.py DelayDetect.py --verbose --observe-links

        (二)启动mininet

sudo mn --topo=linear,4 --switch=ovsk --controller=remote --link=tc

         注意:需要在mininet中使用pingall,才能使得交换机获得host存在,从而使得控制器获取host消息。

        (三)结果显示 

这篇关于SDN实验---Ryu的应用开发(六)网络拓扑时延探测的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/696709

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来