I have topo with 4 switches and 4 hosts. The switches construct a loop. My goal is to learn the topology of the network when switches are connected to the controller.
The problem is that the function get_all_links()
returns all of the possible links or at least something that doesn't make sense. I call that function when a port_modify
event is fired.
Here is the code I use to construct the topo:
<Removed Imports to save space>
class Simple3PktSwitch(Topo):
"""Simple topology example."""
def __init__(self):
"""Create custom topo."""
# Initialize topology
Topo.__init__(self)
# Add hosts and switches
h1 = self.addHost('h1')
h2 = self.addHost('h2')
h3 = self.addHost('h3')
h4 = self.addHost('h4')
# Adding switches
p1 = self.addSwitch('p1', dpid="0000000000000001")
p2 = self.addSwitch('p2', dpid="0000000000000002")
p3 = self.addSwitch('p3', dpid="0000000000000003")
p4 = self.addSwitch('p4', dpid="0000000000000004")
# Add links
self.addLink(h1, p1)
self.addLink(h2, p2)
self.addLink(h3, p3)
self.addLink(h4, p4)
self.addLink(p2, p4)
self.addLink(p1, p2)
self.addLink(p3, p4)
self.addLink(p1, p3)
def run():
c = RemoteController('c', '0.0.0.0', 6633)
net = Mininet(topo=Simple3PktSwitch(), controller=None, autoSetMacs=True)
net.addController(c)
net.start()
# installStaticFlows( net )
CLI(net)
net.stop()
# if the script is run directly (sudo custom/optical.py):
if __name__ == '__main__':
setLogLevel('info')
run()
Here is my code for Ryu:
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER, DEAD_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.topology import event
from ryu.topology.api import get_all_switch, get_all_link
from ryu.lib import dpid as dpid_lib
from ryu.controller import dpset
import networkx as nx
UP = 1
DOWN = 0
class SimpleSwitch13(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(SimpleSwitch13, self).__init__(*args, **kwargs)
# USed for learning switch functioning
self.mac_to_port = {}
# Holds the topology data and structure
self.topo_shape = TopoStructure()
"""
This function determines the links and switches currently in the topology
"""
def get_topology_data(self):
# Call get_switch() to get the list of objects Switch.
self.topo_shape.topo_raw_switches = get_all_switch(self)
# Call get_link() to get the list of objects Link.
self.topo_shape.topo_raw_links = get_all_link(self)
self.topo_shape.print_links("get_topology_data")
self.topo_shape.print_switches("get_topology_data")
###################################################################################
"""
EventOFPPortStatus: An event class for switch port status notification.
The bellow handles the event.
"""
@set_ev_cls(dpset.EventPortModify, MAIN_DISPATCHER)
def port_modify_handler(self, ev):
dp = ev.dp
port_attr = ev.port
dp_str = dpid_lib.dpid_to_str(dp.id)
self.logger.info("\t ***switch dpid=%s"
"\n \t port_no=%d hw_addr=%s name=%s config=0x%08x "
"\n \t state=0x%08x curr=0x%08x advertised=0x%08x "
"\n \t supported=0x%08x peer=0x%08x curr_speed=%d max_speed=%d" %
(dp_str, port_attr.port_no, port_attr.hw_addr,
port_attr.name, port_attr.config,
port_attr.state, port_attr.curr, port_attr.advertised,
port_attr.supported, port_attr.peer, port_attr.curr_speed,
port_attr.max_speed))
if port_attr.state == 1:
self.topo_shape.print_links("Link Down")
out = self.topo_shape.link_with_src_port(port_attr.port_no, dp.id)
print "out"+str(out)
if out is not None:
print(self.topo_shape.find_shortest_path(out.src.dpid))
elif port_attr.state == 0:
self.topo_shape.topo_raw_links = get_all_link(self) ### HERE ###
print ("Link count: "+str(len(self.topo_shape.topo_raw_links)))
self.topo_shape.print_links("Link Up")
#self.topo_shape.topo_raw_links = get_all_link(self)
#self.topo_shape.print_links("Link Up")
###################################################################################
"""
This class holds the list of links and switches in the topology and it provides some useful functions
"""
class TopoStructure():
def __init__(self, *args, **kwargs):
self.topo_raw_switches = []
self.topo_raw_links = []
self.topo_links = []
self.net = nx.DiGraph()
def print_links(self, func_str=None):
# Convert the raw link to list so that it is printed easily
print(" \t"+str(func_str)+": Current Links:")
for l in self.topo_raw_links:
print (" \t\t"+str(l))
def print_switches(self, func_str=None):
print(" \t"+str(func_str)+": Current Switches:")
for s in self.topo_raw_switches:
print (" \t\t"+str(s))
def switches_count(self):
return len(self.topo_raw_switches)
def convert_raw_links_to_list(self):
# Build a list with all the links [((srcNode,port), (dstNode, port))].
# The list is easier for printing.
self.lock.acquire()
self.topo_links = [((link.src.dpid, link.src.port_no),
(link.dst.dpid, link.dst.port_no))
for link in self.topo_raw_links]
self.lock.release()
def convert_raw_switch_to_list(self):
# Build a list with all the switches ([switches])
self.lock.acquire()
self.topo_switches = [(switch.dp.id, UP) for switch in self.topo_raw_switches]
self.lock.release()
"""
Adds the link to list of raw links
"""
def bring_up_link(self, link):
self.topo_raw_links.append(link)
"""
Check if a link with specific nodes exists.
"""
def check_link(self,sdpid, sport, ddpid, dport):
for i, link in self.topo_raw_links:
if ((sdpid, sport), (ddpid, dport)) == ((link.src.dpid, link.src.port_no), (link.dst.dpid, link.dst.port_no)):
return True
return False
"""
Finds the shortest path from source s to destination d.
Both s and d are switches.
"""
def find_shortest_path(self, s):
s_count = self.switches_count()
s_temp = s
visited = []
shortest_path = {}
while s_count != len(visited):
print visited
visited.append(s_temp)
print visited
print ("s_temp 1: " + str(s_temp))
for l in self.find_links_with_src(s_temp):
print "\t"+str(l)
if l.dst.dpid not in visited:
print ("\t\tDPID dst: "+ str(l.dst.dpid))
if l.src.dpid in shortest_path:
shortest_path[l.dst.dpid] += 1
print("\t\t\tdpid found. Count: "+str(shortest_path[l.dst.dpid]))
else:
print("\t\t\tdpid not found.")
shortest_path[l.dst.dpid] = 0
print ("shortest_path: "+str(shortest_path))
min_val = min(shortest_path.itervalues())
t = [k for k,v in shortest_path.iteritems() if v == min_val]
s_temp = t[0]
print "s_temp 2: " + str(s_temp)+"\n"
return shortest_path
"""
Finds the dpids of destinations where the links' source is s_dpid
"""
def find_dst_with_src(self, s_dpid):
d = []
for l in self.topo_raw_links:
if l.src.dpid == s_dpid:
d.append(l.dst.dpid)
return d
"""
Finds the list of link objects where links' src dpid is s_dpid
"""
def find_links_with_src(self, s_dpid):
d_links = []
for l in self.topo_raw_links:
if l.src.dpid == s_dpid:
d_links.append(l)
return d_links
"""
Returns a link object that has in_dpid and in_port as either source or destination dpid and port.
"""
def link_with_src_dst_port(self, in_port, in_dpid):
for l in self.topo_raw_links:
if (l.src.dpid == in_dpid and l.src.port_no == in_port) or (l.dst.dpid == in_dpid and l.src.port_no == in_port):
return l
return None
"""
Returns a link object that has in_dpid and in_port as either source dpid and port.
"""
def link_with_src_port(self, in_port, in_dpid):
for l in self.topo_raw_links:
if (l.src.dpid == in_dpid and l.src.port_no == in_port) or (l.dst.dpid == in_dpid and l.src.port_no == in_port):
return l
return None
So when I check for links it gives me 24 links while there is only 4.
The code is partially on SDNLab. I removed some of the events to save some space. For full code please visit: https://github.com/Ehsan70/RyuApps/blob/master/topo_learner.py