0
votes

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

1

1 Answers

0
votes

Problem solved.

I had to use copy.copy() to update self.topo_shape.topo_raw_switches. i.e: self.topo_shape.topo_raw_switches = copy.copy(get_all_switch(self))