1 目前
1.1 数据中心拓扑模拟
数据层网络拓扑的搭建是通过Mininet来完成的,使用miniedit 可视化方法或者调用 Topo 模块的 API 进行构建拓扑。已经能通过Topo模块的 addSwitch,addHost 以及 addLink 等方法来分别添加交换机,主机以及相应的链路来自定义拓扑。
构建了fattree拓扑:当设置k=4时,使用mininam打开如下:

(但是ping出错,错误和 https://edu.sdnlab.com/community/question/sdn/502 相同)
(发现L1和L2的交换机连接错误)
更改L1和L2的连接方式之后:
再看看这个https://www.cnblogs.com/pullself/p/10295480.html
改了之后的代码(看了k=6 8的情况应该是对的):
from mininet.topo import Topofrom mininet.net import Mininetfrom mininet.node import RemoteControllerfrom mininet.link import TCLinkfrom mininet.util import dumpNodeConnectionsfrom mininet.log import setLogLevelclass MyTopo(Topo):def __init__(self):super(MyTopo,self).__init__()#Marking the number of switch for per levelk=20L1 = (k/2)*(k/2);L2 = (k/2)*kL3 = L2#Starting create the switchc = [] #core switcha = [] #aggregate switche = [] #edge switchlocations={}#notice: switch label is a special data structurefor i in range(L1):c_sw = self.addSwitch('c{}'.format(i+1)) #label from 1 to n,not start with 0c.append(c_sw)for i in range(L2):a_sw = self.addSwitch('a{}'.format(L1+i+1))a.append(a_sw)for i in range(L3):e_sw = self.addSwitch('e{}'.format(L1+L2+i+1))e.append(e_sw)#Starting create the link between switchs#first the first level and second level link !!!!!for i in range(L1):c_sw = c[i]locations[c[i]]=(100,100*(i+1));start=i/(k/2)for j in range(start,L2,k/2):self.addLink(c_sw,a[j])#second the second level and third level linkfor i in range(L2):locations[a[i]]=(200,100*(i+1));num=i/(k/2)for nn in range(num*3,num*3+3):self.addLink(a[i],e[nn])#Starting create the host and create link between switchs and hostsfor i in range(L3):locations[e[i]]=(300,100*(i+1));for j in range(2):hs = self.addHost('h{}'.format(i*2+j+1))self.addLink(e[i],hs)topos = {"mytopo":(lambda:MyTopo())}
1.2 流表下发
见文档:https://www.yuque.com/eoy9tu/um859a/xa7kq3#87NAu
1.3构建流量模型(通信模式)
参考链接:https://www.cnblogs.com/ownhp/p/9094908.html
https://www.sdnlab.com/11079.html
流量传输主要是通过 Mininet 自带的 iperf 工具进行模拟,iperf 作为 Mininet 的性能测试工具,有多个参数进行选择。
1.3.1 iperf

脚本中调用命令: net.iperf((h1, h2))
h1.cmdPrint(‘iperf -c ‘ + h2.IP() + ‘ -u -t 10 -i 1 -b 100m’)
xterm 命令可以对网络拓扑中的终端分别进行操作, 分别打开服务器与客户机终端执行命令。
h1(ip地址为10.0.0.1)执行:iperf -s - i -u 1
h3执行:iperf -c 10.0.0.1 -i 1 -t 20 -b 5
表示客户端在 20 秒内会持续向服务器以 5M bit/s 的速率进行传输:
对比别人的测试结果(区别是没有丢包):
测试结果包含了当前传输带宽,时延抖动以及丢包率等信息。
1.3.2 自定义命令扩展/构建流量模型
为了能够更好的模仿数据中心的真实流量,需要在 Mininet 源码的基础上添加自定义功能函数,用来测试算法对网络性能的影响。
在 Mininet 中进行自定义命令功能拓展主要分为 4 步:
(1)在 mininet/net.py 中实现拓展功能函数:function();
(2)将新命令加入可执行组件 bin/mn 中,即注册新命令 cmd;
(3)在 mininet/cli.py 中实现对命令进行解析的功能,从中解析出相应的相关参数,并运行自定义的功能函数;
(4)重新安装 Mininet 核心组件。在mininet/util目录,重新编译安装mininet:
随机模型
实现终端之间随机选取客户端和服务器,并同时进行传输。
①net.py添加iperf_single()和iperfMulti()
②修改cli.py将iperfmulti命令在CLI类中注册中,添加do_iperfmulti()。
③在mininet/bin/mn文件中加入iperfmulti可执行命令。
在iperf_single()函数中组装要执行的命令,并设置结果的输出目录为/home/zg/log。iperfMulti()中按照主机数目进行循环,每次选择一台主机,作为客户端。如果客户端和服务端是同一台主机,随机选一台新的主机作为服务端,直到找到一台与客户端不同的主机,用来做服务端,再调用iperf_single()。
命令iperfmulti成功添加:
测试输出:

以下为1.out,client.out:

2 计划
修改拓扑
概率模型
为mininet添加自定义命令iperfpb,依次为每一台主机(编号为m)分别以概率Pt 、Pa 、Pc 向主机编号为(m+i)、(m+j)、(m+k)的主机发送数据包。可用以算法测试。
信息收集
对网络不同链路之间的剩余带宽以及相应的时延信息等进行收集。
在收集链路剩余带宽时,采用主动下发策略,由控制器周期性下发Request。交换机接收到请求后,将交换机的端口状态反馈给控制器,其中包含端口发送的字节数tx_bytes ,接收的字节数rx_bytes ,以及相应的监测时间t ;PortDescStatsReply 中包含了端口的最大带宽capability 。通过这些信息计算带宽。参考公式:

先在控制器实现最短路径算法,再根据收集到的信息对流表项进行修改并对相应交换机的流表进行更新。
先实现最短路径
一些库的安装(python版本的切换见https://blog.csdn.net/weixin_44495738/article/details/112585772)
执行
sudo ryu-manager sp.py —observe-links
—observe-links必须要加:自动下发LLDP,用于拓扑发现,否则看不到链路信息sudo python MiniNAM.py —custom create_topo.py —topo mytopo,3 —controller=remote
可以把python MiniNAM.py替换成mn,因为MiniNAM.py只是mininet加个界面
启动控制器,获取交换机基本信息(6个):

然后是拓扑信息(交换机list,链路list)(6个):
然后执行命令h1 ping h2 -c 1:
(之后控制器不会再显示其他信息了,因为有流表了,不会询问控制器了)
权重写死,所以走的s1-s4-s5-s3的路径,而不是更少跳数。
权重上为10,下为100:
查看交换机S1的信息:(s1-5的dpid依次为1-5)
而查看s0的信息:

查看流表如下:
分析
之后根据链路状态来更新链路权重,下发新的流表。
本ryu应用计算的是单源最短路,使用的是networkx提供的nx.shortest_path函数(sp.py的183行)。
在h1使用arp协议获知h2的mac地址之后,h1发出icmp ping包,s1不知道怎么处理,让控制器c0做决定,然后c0得出最短路径是1-4-5-3,让s1发给s4,s4也不知道怎么处理,还发给c0,c0还要算一次最短路,让s4发给s5,如此下去,因此要调用多次最短路径。不过也就最开始这样,等以后多发几次包,交换机流表项都配置好后,以后就不用问控制器了,交换机就知道该送到哪个交换机了。
如果链路的权重变化了,那就要通知控制器,重新计算最短路,删除之前的流表项,控制器配置新的流表项。
下一步考虑如何测得链路带宽/时延来改变链路权值并通知控制器,计算新的路径,下发流表。
将权重设置为跳数/时延或者带宽
时延的测量实现
报错:portdata中没有delay参数。这里测量时延的原理详细见https://account.fnedu.com/16859.html (为计算时延,还可以通过switches模块中的PortData类的发送时间戳来实现,无需修改LLDP数据包格式。) 如何在LLDP中添加自定义LLDPDU见:https://www.sdnlab.com/16820.html
设置weight=delay:
ryu-manager shortest_forwarding.py —observe-links —k-paths=2 —weight=delay
设置weight为delay时:(以时延作为权重)
拓扑为sudo mn —controller=remote —topo=tree,2,2 —mac:
带宽
(先计算基于跳数的K条最短路径,再从中选取可用带宽最大的那条路径最为最优解。)
ryu-manager shortest_forwarding.py —observe-links —k-paths=2 —weight=bw

以端口的统计信息为例,控制器通过周期下发Port statistics消息可以获得交换机端口的统计信息,其返回的统计消息格式如下:
struct ofp_port_stats {uint32_t port_no;uint8_t pad[4];/* Align to 64-bits. */uint64_t rx_packets;/* Number of received packets. */uint64_t tx_packets;/* Number of transmitted packets. */uint64_t rx_bytes;/* Number of received bytes. */uint64_t tx_bytes;/* Number of transmitted bytes. */uint64_t rx_dropped;/* Number of packets dropped by RX. */uint64_t tx_dropped;/* Number of packets dropped by TX. */uint64_t rx_errors;/* Number of receive errors. This is a super-setof more specific receive errors and should beuint64_t tx_errors; /*uint64_t rx_frame_err;uint64_t rx_over_err;uint64_t rx_crc_err;uint64_t collisions;uint32_t duration_sec;uint32_t duration_nsec;};
跳数
ryu-manager shortest_forwarding.py —observe-links —k-paths=2 —weight=hop
拓扑依然是:sudo mn —controller=remote —topo=tree,2,2 —mac

以上代码(graph的存储,时延的计算,带宽的计算)看了,已经能获得拓扑中每条链路的时延和带宽,时延和带宽都是控制器主动间隔一段时间主动询问的。
修改后,能同时测量链路的时延和带宽:
都能显示:

目标:同时测量时延和带宽并储存
修改始终测量delay后,拓扑连接控制器后能显示以上测量结果,但是ping的话会报错。
只有ryu-manager shortest_forwarding.py —observe-links —k-paths=2 —weight=delay没有报错。
ryu-manager shortest_forwarding.py —observe-links —k-paths=2 —weight=hop/bw都报错
再次修改:
要使得无论权值设置为什么,都要测量每条链路的时延.带宽,并存进图中,显示到控制器。
只是路径的选择上,会根据weight的设置,从graph中取出相应的数据来路由。
修改delay的测量函数如下:
def _detector(self):"""Delay detecting functon.Send echo request and calculate link delay periodically"""##while CONF.weight == 'delay':while True :self._send_echo_request()self.create_link_delay()if CONF.weight == 'delay':try:self.awareness.shortest_paths = {}self.logger.debug("Refresh the shortest_paths")except:self.awareness = lookup_service_brick('awareness')self.show_delay_statis()hub.sleep(setting.DELAY_DETECTING_PERIOD)
修改bw的测量函数:
def _monitor(self):"""Main entry method of monitoring traffic."""#while CONF.weight == 'bw':while True:self.stats['flow'] = {}self.stats['port'] = {}for dp in self.datapaths.values():self.port_features.setdefault(dp.id, {})self._request_stats(dp)# refresh data.self.capabilities = Noneself.best_paths = Nonehub.sleep(setting.MONITOR_PERIOD)if self.stats['flow'] or self.stats['port']:self.show_stat('flow')self.show_stat('port')hub.sleep(1)

- 上图为设置运行时权重为hop ,能同时测量时延和bw并显示(也存进了graph),pingall也能成功。
- 运行时设置weight=delay/weight,同时测量且显示,pingall未报错。
