码迷,mamicode.com
首页 > 其他好文 > 详细

SDN实验---Ryu的应用开发(五)网络拓扑发现

时间:2020-12-30 11:11:41      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:color   load   lazy   path   建立   wait   packet   sel   ase   

一:实验简介

(一)网络拓扑信息:

技术图片

其中1,2,3表示该交换机对应的端口号!!!

(二)用邻接矩阵展示

技术图片

其中左侧列S1,S2,S3,S4表示出节点,----->,上面S1,S2,S3,S4表示入节点。

(m,1),m表示出节点的端口--->入节点,1表示两个节点有连接!

(三)主机信息展示

技术图片 

二:代码实现 

(一)导入模块

from ryu.base import app_manager

from ryu.ofproto import ofproto_v1_3

from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER,DEAD_DISPATCHER #只是表示datapath数据路径的状态
from ryu.controller.handler import set_ev_cls

from ryu.lib import hub
from ryu.lib.packet import packet,ethernet

from ryu.topology import event,switches
from ryu.topology.api import get_switch,get_link,get_host

(二)数据结构

class 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 = self  #用于保持对象本身,后面get_switch等方法需要(我们也可以直接传入self)
        self.link_list = None         #保存所有的link信息,由get_link获得
        self.switch_list = None       #保存所有的switch信息,由get_switch获得
        self.host_list = None         #保存所有的host信息,由get_host获得

        self.dpid2id = {}             #获取交换机dpid,以及自定义id--->{dpid:id}
        self.id2dpid = {}             #对应上面的self.dpid2id,翻转即可,因为我们使用id进行建立邻接矩阵,这两个结构方便查找
        self.dpid2switch = {}         #保存dpid和对应的交换机全部信息---->通过矩阵获得id,然后获得dpid,最后获得交换机对象信息

        self.ip2host = {}             #根据ip,保存主机对象信息--->{ip:host}
        self.ip2switch = {}           #根据ip,获取当前主机是连接到哪个交换机--->{ip:dpid}

        self.net_size = 0             #记录交换机个数(网络拓扑大小)
        self.net_topo = []            #用于保存邻接矩阵
        

        self.monitor_thread = hub.spawn(self._monitor)  #协程实现定时检测网络拓扑

(三)实现基本openflow消息处理

    @set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER)
    def switch_feature_handle(self,ev):
        """
        datapath中有配置消息到达
        """
        #print("------------------switch_feature_handle")

        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        match = 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.ofproto
        ofp_parser = datapath.ofproto_parser

        inst = [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.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        dpid = datapath.id
        in_port = msg.match[in_port]

        pkt = packet.Packet(msg.data)
        eth_pkt = pkt.get_protocol(ethernet.ethernet)
        dst = eth_pkt.dst
        src = 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)

注意:对于packet_in消息,我们没有处理,所以整个网络(交换机之间的链路)是无法工作通信的,

但是各个交换机可以与控制器通信(switch_feature_handle实现),主机也可以和边缘交换机通信,

所以控制器可以获取网络拓扑信息!!! 

(四)实现拓扑发现功能

    def _monitor(self):
        """
        协程实现伪并发,探测拓扑状态
        """
        while True:
            #print("------------------_monitor")
            self.get_topology(None)
            try:
                self.show_topology()
            except Exception as err:
                print("Please use cmd: pingall to detect topology and wait a moment")
            hub.sleep(5) #5秒一次


    events = [event.EventSwitchEnter, event.EventSwitchLeave,
               event.EventSwitchReconnected,
               event.EventPortAdd, event.EventPortDelete,
               event.EventPortModify,
               event.EventLinkAdd, event.EventLinkDelete,
               event.EventHostAdd]
    @set_ev_cls(events)
    def get_topology(self,ev):
        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即可用于拓扑发现
        self.host_list = get_host(self.topology_api_app)     #3.需要使用pingall,主机通过与边缘交换机连接,才能告诉控制器
        
        #获取交换机字典id2dpid{id:dpid} dpid2switch{dpid:switch object}
        for i,switch in enumerate(self.switch_list):
            self.id2dpid[i] = switch.dp.id
            self.dpid2id[switch.dp.id] = i
            self.dpid2switch[switch.dp.id] = switch

        #获取主机信息字典ip2host{ipv4:host object}  ip2switch{ipv4:dpid}
        for i,host in enumerate(self.host_list):
            self.ip2switch["%s"%host.ipv4] = host.port.dpid
            self.ip2host["%s"%host.ipv4] = host

        #根据链路信息,开始获取拓扑信息
        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.dpid
            src_port = link.src.port_no

            dst_dpid = link.dst.dpid
            dst_port = link.dst.port_no

            sid = self.dpid2id[src_dpid]
            did = self.dpid2id[dst_dpid]

            self.net_topo[sid][did] = (src_port,1) #注意:这里1表示存在链路,后面可以修改为时延
            self.net_topo[did][sid] = (dst_port,1)

    def show_topology(self):
        print("-----------------show_topology")
        print("----------switch network----------")
        line_info = "         "
        for i in range(self.net_size):
            line_info+="s%d      "%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+="0      "
                else:
                    line_info+="(%d,%d)  "%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.ofproto import ofproto_v1_3

from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER,DEAD_DISPATCHER #只是表示datapath数据路径的状态
from ryu.controller.handler import set_ev_cls

from ryu.lib import hub
from ryu.lib.packet import packet,ethernet

from ryu.topology import event,switches
from ryu.topology.api import get_switch,get_link,get_host

class 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 = self
        self.link_list = None
        self.switch_list = None
        self.host_list = None

        self.dpid2id = {}
        self.id2dpid = {}
        self.dpid2switch = {}

        self.ip2host = {}
        self.ip2switch = {}

        self.net_size = 0
        self.net_topo = []
        

        self.monitor_thread = hub.spawn(self._monitor)

    def _monitor(self):
        """
        协程实现伪并发,探测拓扑状态
        """
        while True:
            #print("------------------_monitor")
            self.get_topology(None)
            try:
                self.show_topology()
            except Exception as err:
                print("Please use cmd: pingall to detect topology and wait a moment")
            hub.sleep(5) #5秒一次


    @set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER)
    def switch_feature_handle(self,ev):
        """
        datapath中有配置消息到达
        """
        #print("------------------switch_feature_handle")

        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        match = 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.ofproto
        ofp_parser = datapath.ofproto_parser

        inst = [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.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        dpid = datapath.id
        in_port = msg.match[in_port]

        pkt = packet.Packet(msg.data)
        eth_pkt = pkt.get_protocol(ethernet.ethernet)
        dst = eth_pkt.dst
        src = 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)


    events = [event.EventSwitchEnter, event.EventSwitchLeave,
               event.EventSwitchReconnected,
               event.EventPortAdd, event.EventPortDelete,
               event.EventPortModify,
               event.EventLinkAdd, event.EventLinkDelete,
               event.EventHostAdd]
    @set_ev_cls(events)
    def get_topology(self,ev):
        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即可用于拓扑发现
        self.host_list = get_host(self.topology_api_app) #3.需要使用pingall,主机通过与边缘交换机连接,才能告诉控制器
        
        #获取交换机字典id2dpid{id:dpid} dpid2switch{dpid:switch object}
        for i,switch in enumerate(self.switch_list):
            self.id2dpid[i] = switch.dp.id
            self.dpid2id[switch.dp.id] = i
            self.dpid2switch[switch.dp.id] = switch

        #获取主机信息字典ip2host{ipv4:host object}  ip2switch{ipv4:dpid}
        for i,host in enumerate(self.host_list):
            self.ip2switch["%s"%host.ipv4] = host.port.dpid
            self.ip2host["%s"%host.ipv4] = host

        #根据链路信息,开始获取拓扑信息
        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.dpid
            src_port = link.src.port_no

            dst_dpid = link.dst.dpid
            dst_port = link.dst.port_no

            sid = self.dpid2id[src_dpid]
            did = self.dpid2id[dst_dpid]

            self.net_topo[sid][did] = (src_port,1) #注意:这里1表示存在链路,后面可以修改为时延
            self.net_topo[did][sid] = (dst_port,1)




        #print("+++++++++++allSwitch:")
        #for i,switch in enumerate(allSwitch): #switch中含有datapath和port对象
        #    print("===%d datapath:"%i)
        #    print(switch.dp,switch.dp.id)
        #    print("===%d ports: dpid  portno  name hwaddr:"%i)
        #    print(switch.ports)
        #    for port in switch.ports:
        #        print(port.dpid,port.port_no,port.name,port.hw_addr)

        #===0 datapath:
        #<ryu.controller.controller.Datapath object at 0x7f8275748940> 1
        #===0 ports: dpid  portno  name hwaddr:
        #[<ryu.topology.switches.Port object at 0x7f8275862c50>, <ryu.topology.switches.Port object at 0x7f8275862da0>]
        #1 1 bs1-eth1 ae:22:48:41:18:1d
        #1 2 bs1-eth2 8a:71:db:bd:a1:86
        
        #print("+++++++++++allLink:")
        #for link in self.link_list:
        #    print(link.src,link.dst)

        #+++++++++++allLink:
        #Port<dpid=2, port_no=2, LIVE> Port<dpid=1, port_no=2, LIVE>
        #Port<dpid=1, port_no=2, LIVE> Port<dpid=2, port_no=2, LIVE>

        #print("+++++++++++allHost:")
        #for host in self.host_list:
        #    print("%s %s %s"%(host.ipv4,host.mac,host.port))

        #+++++++++++allHost:
        #[10.0.0.2] 5e:20:b8:90:dd:0e Port<dpid=2, port_no=1, LIVE>
        #[10.0.0.1] 6e:a0:c2:a0:f0:2a Port<dpid=1, port_no=1, LIVE>


    def show_topology(self):
        print("-----------------show_topology")
        print("----------switch network----------")
        line_info = "         "
        for i in range(self.net_size):
            line_info+="s%d      "%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+="0      "
                else:
                    line_info+="(%d,%d)  "%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))
View Code

三:实验验证

(一)启动Ryu控制器

ryu-manager TopoDetect.py --verbose --observe-links

其中--observe-links用于拓扑发现,添加之后用于链路的信息获取!!

技术图片

(二)启动mininet

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

技术图片

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

技术图片

(三)结果显示

技术图片

 

SDN实验---Ryu的应用开发(五)网络拓扑发现

标签:color   load   lazy   path   建立   wait   packet   sel   ase   

原文地址:https://www.cnblogs.com/ssyfj/p/14187621.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!