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) #协程实现定时检测网络拓扑
@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)
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:
#
#===0 ports: dpid portno name hwaddr:
#[
#1 1 b‘s1-eth1‘ ae:22:48:41:18:1d
#1 2 b‘s1-eth2‘ 8a:71:db:bd:a1:86
#print("+++++++++++allLink:")
#for link in self.link_list:
# print(link.src,link.dst)
#+++++++++++allLink:
#Port
#Port
#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
#[‘10.0.0.1‘] 6e:a0:c2:a0:f0:2a Port
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))
ryu-manager TopoDetect.py --verbose --observe-links
sudo mn --topo=linear,4 --switch=ovsk --cOntroller=remote --link=tc
SDN实验---Ryu的应用开发(五)网络拓扑发现