使用ryu控制器通过Kruskal最小生成树算法下发流表出错

问题遇到的现象和发生背景

无法将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及对应的路径

你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,技术专家团超时未为您做出解答


本次提问扣除的有问必答次数,已经为您补发到账户,我们后续会持续优化,扩大我们的服务范围,为您带来更好地服务。