无法将Kruskal算法部分与ryu控制器代码整合,并实现下发流表的操作,Kruskal算法部分已经做好了,就是不知得如何加入其中
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import ether_types
from ryu.topology import event
from ryu.topology.api import get_switch,get_link
import json
import time
import sdn_info_request
class Kruskal(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(Kruskal, self).__init__(*args, **kwargs)
# 初始化一些数据结构,比如mac_to_port等
self.mac_to_port = {}
self.path={}
self.mac={}
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
msg=ev.msg
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# 安装table-miss流表项
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions,buffer_id=None):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# 构建数据包并发送
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)]
if buffer_id:
mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
priority=priority, match=match,
instructions=inst)
else:
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)
# 发现拓扑的变化
@set_ev_cls(event.EventSwitchEnter, [CONFIG_DISPATCHER, MAIN_DISPATCHER])
def switch_enter_handler(self, ev):
time.sleep(self.sleep_interval)
print("A switch entered. Topology rediscovery...\n------------------------------")
# TODO:调用网络算法
self.kruskal(self)
# 编写网络算法
def kruskal():
# 1、完成算法逻辑,2、保存应有的数据结构,3、打印指定信息,4、......
# 需要用到sdn_info_request存储必要信息
from operator import truediv
from pdb import lasti2lineno
from pprint import pprint
with open("project1_test.json", "r") as f:
topo_dict = json.load(f)
#以上为读取json文件为字典形式,已type查看
list1 = []
list2 = []
for name1 in topo_dict['networks']['hosts']:
list1.append(name1)
#把h放在一个列表
for name2 in topo_dict['networks']['switches']:
list2.append(name2)
#把s放在一个列表
list_nodes = list1 + list2
#列表合并
print("Nodes:")
print(list_nodes)
#输出点列表
list_edges = []
print("Edges:")
for name in topo_dict['networks']['links']:
list_edges.append(
(topo_dict['networks']['links'][name]['source'],
topo_dict['networks']['links'][name]['target'],
topo_dict['networks']['links'][name]['config']['target']['delay_us']))
for name in topo_dict['networks']['links']:
list_edges.append(
(topo_dict['networks']['links'][name]['target'],
topo_dict['networks']['links'][name]['source'],
topo_dict['networks']['links'][name]['config']['target']['delay_us']))
pprint(list_edges)
#输出路径列表
rev_list = []
for num in list_edges:
rev_list.append(num[::-1]) #倒序,方便排序
new_data = sorted(rev_list)
#按delay_us排序
Nodelist = list_nodes
Linelist = new_data
#为了不把原来的数据改掉了,新赋值一份以防万一
nlist = [] #就是一个储存下面所示信息的列表
A = {} #定义一个字典A,开始时候每个节点是一个树
for nodes in Nodelist:
nlist = [nodes,0] #nodes是键对应的点相连的点,如果点作为连接的末端后面的数字就加1
A[nodes] =nlist #for循环在字典中储存列表
#整了一个字典嵌套列表存储信息{'A':['A',0] ......}
def Find_Set(node):
if A[node][0] != node:
A[node][0] = Find_Set(A[node][0])
return A[node][0]
#find节点是否在一个树里
def Union(n1, n2):
N1 = Find_Set(n1)
N2 = Find_Set(n2)
if N1 != N2:
if A[N1][1] > A[N2][1]:
A[N2][0] = N1
else:
A[N1][0] = N2
if A[N1][1] == A[N2][1]:
A[N2][1] += 1
#连接两个节点,修改初始时定义的字典嵌套列表信息
def Mst_Kruskal(Llist):
minutree = []
for line in Llist:
delay, n1, n2 = line
if Find_Set(n1) != Find_Set(n2):
Union(n1, n2)
minutree.append(line)
return minutree
#以下为转变一下输出形式,上面输出的形式还是列表嵌套元组的形式
Minutree = Mst_Kruskal(Linelist)
L = []
Sum_delay = 0
for info in Minutree:
delay,n1,n2 = info
L.append(n1+n2) #字符串合并
num_delay = float(delay) #字符串转换为浮点型
Sum_delay = num_delay + Sum_delay #delay求和
Sum_delay = Sum_delay/1000
print('mini tree:')
space = ''
print(L,space*5,Sum_delay,end='')
print('ms')
print('head node: ')
a=input()
print('trail node :')
c=input()
l=[]#创造一个空list
def new_func1(n):#实现n++
n=n+1
return n
def new_func():#将n置0
n=0
return n
isGo=1#判断是否跳出递归
def tree2(str1,str2,l):#开始递归
imn=[]#用来存储l的最后一个元素
tem=[]#用于调换info元素前后关系
n = new_func()
global isGo#声明全局变量
if isGo==0:
return l
while(isGo):#该循环可以实现对l的弹出和加入操作
for inf in Minutree:
n = new_func1(n)#开始计数,以免删除错误的边
if isGo==0:
return l
if str1==str2:
isGo=0
return l
if inf[1]==str1:#如果是inf【1】为str1则将该边加入l
if isGo==0:
return l
str1=inf[2]
tem=[inf[0],inf[1],inf[2]]
l.append(tem)
del Minutree[n-1]#删除该边在最小树上
tree2(str1,str2,l)#进入递归
if inf[2]==str1:#同理
if isGo==0:
return l
str1=inf[1]
tem=[inf[0],inf[2],inf[1]]#将inf的顺序调换,实现操作方便
l.append(tem)
del Minutree[n-1]
tree2(str1,str2,l)
if isGo==0:
return l
if Minutree==[]:
isGo=0
return l
if isGo==1:#若遍历了所以边都没有可以递归的,则开始弹出操作
if len(l)==1:
n = new_func()#将n归0,避免删错边
inm=l[-1]
str1=inm[1]
l.pop(-1)
if len(l)>1:
l.pop(-1)
n = new_func()#将n归0,避免删错边
inm=l[-1]
str1=inm[2]#str1返回上一条边的后一个元素,再次开始循环找边
tree2(a,c,l)
#将最小路径输出
m=[]
Sum_delay2 = 0
for info in l:
delay,n1,n2 = info
m.append(n1) #字符串合并
num_delay = float(delay) #字符串转换为浮点型
Sum_delay2 = num_delay + Sum_delay2 #delay求和
Sum_delay2 = Sum_delay2/1000
delay,n1,n2=l[-1]
m.append(n2)
print('trace:')
space = ''
print(m,space*5,Sum_delay2,end='')
print('ms')
pass
# 处理packet in
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# 获取dpid
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
# 分析数据包
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
eth_pkt=pkt.get_protocols(ethernet.ethernet)
# LLDP报文不需要处理,我这里直接帮大家写了
if eth.ethertype == ether_types.ETH_TYPE_LLDP:
return
# 获取数据包的源目mac地址、in_port
dst = eth_pkt.dst
src = eth_pkt.src
in_port = msg.match['in_port']
# TODO:根据网络算法结果,完成转发策略
# 参考思路(可以不这么做,完成即可):
# 如果目的IP地址对ryu控制而言已知,则根据已知信息从特定端口转发,并添加流表
# 否则根据STP路径进行泛洪
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
#进行自学习,尽可能避免在下一次洪泛
# learn a mac address to avoid FLOOD next time.
#dpid是交换机的id,src是数据包的源mac地址,in_port是交换机接受到包的端口
self.mac_to_port[dpid][src] = in_port
#检验目的地址是否已经学习
if dst in self.mac_to_port[dpid]:
#如果已经学习到,则向交换机下发流表,并让交换机向相应端口转发包
out_port = self.mac_to_port[dpid][dst]
else:
#如果还没有学习到,则无法下发流表,让交换机洪泛转发包。
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
# verify if we have a valid buffer_id, if yes avoid to send both
# flow_mod & packet_out
if msg.buffer_id != ofproto.OFP_NO_BUFFER:
#buffer_id不为None,控制器只需下发流表的命令,交换机增加了流表项后,位于缓冲区的数据包会自动转发出去。
self.add_flow(datapath, 1, match, actions, msg.buffer_id)
return
else:
#buffer_id为None,那么控制器不仅要更改交换机的流表项,
self.add_flow(datapath, 1, match, actions)
#还要把数据包的信息传给交换机,让交换机把数据包转发出去。
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
# packet out
out = parser.OFPPacketOut(datapath=datapath,
buffer_id=ofproto.OFP_NO_BUFFER,
in_port=in_port, actions=actions,
data=msg.data)
datapath.send_msg(out)
sdn_info_request代码
import requests
MASTER_IP = "192.168.1.16"
MASTER_PORT = "8000"
def get_switch_dpid(user, topo):
data = {
"user": user,
"topo": topo
}
resp = requests.post(url=f"http://{MASTER_IP}:{MASTER_PORT}/switch_dpid/",
json=data)
print(resp.json())
return resp.json()
def get_host_mac(user, topo):
data = {
"user": user,
"topo": topo
}
resp = requests.post(url=f"http://{MASTER_IP}:{MASTER_PORT}/host_mac/",
json=data)
print(resp.json())
return resp.json()
def get_switch_port(user, topo):
data = {
"user": user,
"topo": topo
}
resp = requests.post(url=f"http://{MASTER_IP}:{MASTER_PORT}/link_port/",
json=data)
print(resp.json())
return resp.json()
if __name__ == "__main__":
sw2dpid = get_switch_dpid("sw", "kruskal_test")
host2mac = get_host_mac("sw", "kruskal_test")
sw2port = get_switch_port("sw", "kruskal_test")
无法运行,会产生报错
将Kruskal算法插入ryu控制器中,并实现读取mininet上网络拓扑,一个节点在ping另外一个节点的时候打印交换机的dpid及对应的路径
你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,技术专家团超时未为您做出解答
本次提问扣除的有问必答次数,已经为您补发到账户,我们后续会持续优化,扩大我们的服务范围,为您带来更好地服务。