Beispiel #1
0
class RegistrationTest(unittest.TestCase):
    def setUp(self):
        self.register = Registration()

    def test_register_creation(self):
        self.assertIsInstance(self.register, Registration)

    def test_user_bio_length(self):
        self.register.add('*****@*****.**', 'john', 'mike')
        self.assertEqual(self.register.user_bio_length(self.register.user_bio),
                         1)

    def test_add_user(self):
        self.register.add('*****@*****.**', 'John doe', 'mike')
        self.assertEqual(len(self.register.user_bio), 1)

    # def test_check_file_exist(self):
    #     pass

    # def test_add_member(self):
    #     pass

    def test_missing_key(self):
        with self.assertRaises(KeyError):
            self.register.get_email('*****@*****.**')

    @unittest.skip('WIP')
    def test_unknown_method(self):
        pass  #self.assertEqual(self.register.some_method(), 1)
Beispiel #2
0
class ICSDNController(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_4.OFP_VERSION]

    '''
    This file implement the controller.
    Ryu is an event-driven controller that you can handle events separately.

    ==============================
    =========== Events ===========
    Events are defined with a python decorator "@set_ev_cls" and it takes
    two arguments:
        - Event: (EventOFPxxxxx)
            + EventOFPSwitchFeatures: Controller response on switch establishment
            + EventOFPPacketIn: Controller receive message from a switch
        - Event argument:
            + CONFIG_DISPATCHER: Ignore events before handshake

    ==============================
    ========= Parameters =========
    In this section important parameters are described:
        - datapath: Controller-switch negotiation path
        - datapath.ofproto: OpenFlow definitions
        - datapath.ofproto_parser: Wire message for encoder/decoder

    ==============================
    ======== Our protocol ========

    -- get "protocol" field in IP header
    -- case protocol:
    ---- 150 : NDN interest packet
    ---- 151 : NDN data packet
    ---- else: other tcp/ip packets
    '''

    def __init__(self, *args, **kwargs):
        super(ICSDNController, self).__init__(*args, **kwargs)
        
#        CONF = cfg.CONF
#        CONF.register_opts([
#            cfg.StrOpt('experimnet', default='ds100ms__dsh_1ms', help = ('The experiment by the parameters'))], group='NETPARAM')
        print (CONF.netparam.exp)
        self.log_file='./Out/' + CONF.netparam.protocol +  '/' + CONF.netparam.exp + '/' + CONF.netparam.smpl 
        if os.path.exists(self.log_file) is not True:
            os.makedirs(self.log_file)
        self.switches = []
        self.links = []
        self.mac_to_port = {}
        self.topology_api_app = self
        self.net = nx.DiGraph()  # Directional graph to set output_port
        self.INTEREST_PROTOCOL = 150
        self.DATA_PROTOCOL = 151
        self.REGISTER_PROTOCOL = 152
        self.HELLO_LABEL = 932620
        f = os.system('ifconfig > ifconfig')
        f = open('ifconfig', 'r+').read()
        # f = open('/proc/net/arp', 'r').read()sc
        mac_regex = re.compile("(?:[0-9a-fA-F]:?){12}")
        self.CONTROLLER_ETH = mac_regex.findall(f)[0]
        
        self.INTEREST_PORT = 9997
        self.DATA_PORT = 9998
        self.RIB = Registration()
        self.switches_dp=dict()
        self.icn_switches = []
        self.packet_in_num = 0
        threading.Thread(target=self.packet_in_rate).start()
        
        # self.nodes = {}
        # self.links = {}
        # self.no_of_nodes = 0
        # self.no_of_links = 0
        # self.i = 0

    def packet_in_rate(self):
        T = float(10)
        f = open(self.log_file+'/controller_log','w')
        f.close()
        pre_num = 0
        while (True):
            time.sleep(T)
            rate = (self.packet_in_num - pre_num)/T
            pre_num = self.packet_in_num
            f = open(self.log_file+'/controller_log','a')
            print(rate,file=f)
            f.close()
            
            
    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    def switch_features_handler(self, ev):
        """
        Handle switch features reply to install table miss flow entries.
        TIP: You can keep this function without any change and reuse it.
        :param ev: Event
        :return:
        """
        
        datapath = ev.msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        self.switches_dp.update({datapath.id:datapath})
        if datapath.id in self.icn_switches:
            print("insert initial rules in ICN switch "+str( datapath.id))
            
            # install table-miss flow entry
            #
            # We specify NO BUFFER to max_len of the output action due to
            # OVS bug. At this moment, if we specify a lesser number, e.g.,
            # 128, OVS will send Packet-In with invalid buffer_id and
            # truncated packet data. In that case, we cannot output packets
            # correctly.  The bug has been fixed in OVS v2.1.0.
    
            match = parser.OFPMatch()   # Match everything
            actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                              ofproto.OFPCML_NO_BUFFER)]
    
            '''
            Add a flow(role) with priority 0 (lowest priority) in order to
            send mismatched requests to the controller
            '''
            self.add_flow(datapath, 0, 0, match, actions)
            self.add_flow(datapath, 1, 0, match, actions)
            self.add_flow(datapath, 2, 0, match, actions)
            self.add_flow(datapath, 3, 0, match, actions)
            
    
            # Add a role to goto table 1 for first interest packets from clients
            # and goto table 2 for other INT and Data packets
            ins = [parser.OFPInstructionGotoTable(table_id=1)]
            match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_MPLS)
            self.add_flow(datapath, 0, 2, match, None, instruction=ins)
    
            ins = [parser.OFPInstructionGotoTable(table_id=2)]
            match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP,ip_proto=self.INTEREST_PROTOCOL)
            self.add_flow(datapath, 0, 2, match, None, instruction=ins)
    
            match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP,ip_proto=self.DATA_PROTOCOL)
            self.add_flow(datapath, 0, 2, match, None, instruction=ins)
            
            ins = [parser.OFPInstructionGotoTable(table_id=3)]
            match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP,ip_proto=self.INTEREST_PROTOCOL)
            self.add_flow(datapath, 2, 1, match, None, instruction=ins)
    
            match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP,ip_proto=self.DATA_PROTOCOL)
            self.add_flow(datapath, 2, 1, match, None, instruction=ins)
    #        
    #        ins = [parser.OFPInstructionGotoTable(table_id=1)]
    #        match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_MPLS,
    #                                ip_proto=self.DATA_PROTOCOL)
    #        self.add_flow(datapath, 0, 3, match, None, instruction=ins)
    
            ## Send to controller if incoming packet is a registration one
            match = parser.OFPMatch(eth_dst = self.CONTROLLER_ETH, eth_type=ether_types.ETH_TYPE_MPLS)
            actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                              ofproto.OFPCML_NO_BUFFER)]
            self.add_flow(datapath, 0, 2, match, actions)
        else:
            print("insert initial rules in legacy switch "+str( datapath.id))
            match = parser.OFPMatch()   # Match everything
            actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                              ofproto.OFPCML_NO_BUFFER)]
    
            '''
            Add a flow(role) with priority 0 (lowest priority) in order to
            send mismatched requests to the controller
            '''
            self.add_flow(datapath, 0, 0, match, actions)
            self.add_flow(datapath, 1, 0, match, actions)
            ins = [parser.OFPInstructionGotoTable(table_id=1)]
            match = parser.OFPMatch()
            self.add_flow(datapath, 0, 1, match, None, instruction=ins)
      
        
        
    def add_flow(self, datapath, table_id, priority, match, actions, instruction=None):
        """
        Add entry to switch table
        :param datapath: target switch
        :param table_id: id of table!
        :param priority: higher number means higher priority
        :param match: which flows match this entry
        :param actions: what to do for matched flow
        :param instruction: set for instruction commands such as GotoTable
        :return: null
        """
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser

        if not instruction:
            inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                                 actions)]
            mod = parser.OFPFlowMod(datapath=datapath, table_id=table_id,
                                    priority=priority,
                                    match=match, instructions=inst)

        elif not actions:
            mod = parser.OFPFlowMod(datapath=datapath, table_id=table_id,
                                    priority=priority, command=ofproto.OFPFC_ADD,
                                    match=match,  instructions=instruction)
        else:
            action_list = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                                 actions)]
            inst = action_list + instruction
                                                 
            mod = parser.OFPFlowMod(datapath=datapath, table_id=table_id,
                                    priority=priority, command=ofproto.OFPFC_ADD,
                                    match=match,  instructions=inst)
        datapath.send_msg(mod)
        
        
    def packet_decode(self,pkt):
        """
        Decode a packet and create a dictionary of its fields
        :param pkt: target packet
        :return: if packet is a LLDP it returns None, 
                 otherwise it returns the dictionary of its fields
        """
        fields = dict()
        
        ########################################
        # Layer2 information of packet(Ethernet)
        eth = pkt.get_protocol(ethernet.ethernet)

        fields['eth_type'] = eth.ethertype
        # Ignore lldp(Link Layer Discovery Protocol) packet
        if fields['eth_type'] == ether_types.ETH_TYPE_LLDP:
            return None
          
        # ethernet src and dst of packet
        fields['eth_dst'] = eth.dst
        fields['eth_src'] = eth.src


        #############
        # MPLS header
        mpls_header = pkt.get_protocol(mpls.mpls)
        if mpls_header:
            print ('mpls header')
            fields['mpls_label'] = mpls_header.label
            print (fields['mpls_label'])
        else: 
            fields['mpls_label'] = -1            
        ip = pkt.get_protocol(ipv4.ipv4)
        if ip:
            print('ip payload:')
            fields['ip_src'] = ip.src
            fields['ip_dst'] = ip.dst
            fields['ip_protocol'] = ip.proto
        else:
            fields['ip_protocol'] = -1
                              
        return fields
                   

    def lookup_out_port(self,msg_data_path, ip_src, content_label=None, ip_dst=None):
        if content_label is not None:
            print ('searching ', content_label)
            publisher = self.RIB.lookup(content_label)
        else:
            publisher = dict({'ip':ip_dst})
        if publisher is not None and publisher['ip'] in self.net:
            print('lookup path for ' + str(publisher['ip']) + ' from ' + str(ip_src))
                                    
            path = nx.shortest_path(self.net, ip_src, publisher['ip'])

            # for switch in path[1:]:
            #dpid = msg_data_path.id
            return publisher['ip'], path
#            next_hop = path[path.index(dpid)+1]
#            #dpid = next_hop
#            #datapath = api.get_datapath(self, dpid)
#            print ('forward to: ',out_port)
#            return [out_port,publisher['ip']]
        else:
            print ('flood')
            #out_port = msg_data_path.ofproto.OFPP_FLOOD
            #if pkt_fields['eth_dst'] != "ff:ff:ff:ff:ff:ff":
            #return  [out_port,'']  
            return '',msg_data_path.ofproto.OFPP_FLOOD
            
    def encode_flow_id(self, ip_src,ip_dst):
        fields_src=ip_src.split(".")
        fields_dst=ip_dst.split(".")
        return fields_src[3]+'.'+fields_src[2]+'.'+ fields_dst[3]+'.' + fields_dst[2]
    
    def lookup_flow_id_path(self, flow_id):
        return self.RIB.get_flow_path(flow_id)
        
    def create_mpls_datapath(self, msg, pkt_fields):
            
        """
        Add a flow in table 1 for pop mpls header and set appropriate 
        IP destination and then goto table2
        In table 2 for interest packets, tos field is set to be the in_port
        and then interest and data packets goto table 3 
        In table 3 forwarding rules for interest or data packets are added
        :param msg: target incoming msg
        :param pkt_fields: the fields of incoming interest packet
        :param oport: the port to forward packet on if necessary
        :return: created packet_out
        """
        msg_datapath = msg.datapath
        parser = msg_datapath.ofproto_parser
        iport = msg.match['in_port']
        
        
        ip_dst, next_ports = self.lookup_out_port(msg_datapath, pkt_fields['ip_src'], 
                                        content_label=pkt_fields['mpls_label'])
        
        if ip_dst=='':
            return    
        flow_id = self.encode_flow_id(pkt_fields['ip_src'],ip_dst)   
        print('path: ', flow_id, ': ', next_ports)          
        #add a rule to remove mpls and set ip_desAddr to publisher ip
        if msg_datapath.id in self.icn_switches:              
            ins = [parser.OFPInstructionGotoTable(table_id=2)]
            actions = [parser.OFPActionPopMpls(ethertype=2048),
                       parser.OFPActionSetField(ipv4_dst=flow_id)]
            
            match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_MPLS,
                                    mpls_label=pkt_fields['mpls_label'])      
            self.add_flow(msg_datapath, 1, 1, match, actions, instruction=ins)
            
            #send back the packet to the switch for matching operations
            #data = None
    #        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
    #            print("data_mpls=msg.data")
    #            data = msg.data
            actions.append(parser.OFPActionSetField(ip_dscp=iport))
            actions.append(parser.OFPActionOutput(SH_IN_PORT))
            #actions.append(parser.OFPActionOutput(oport))
            out = parser.OFPPacketOut(datapath=msg_datapath, buffer_id=msg.buffer_id,
                                      in_port=iport, actions=actions)   
                                      
        else:
            next_hop = next_ports[2]
            oport = self.net[msg_datapath.id][next_hop]['port']
            ins = [parser.OFPInstructionGotoTable(table_id=1)]
            actions = [parser.OFPActionPopMpls(ethertype=2048),
                       parser.OFPActionSetField(ipv4_dst=flow_id)]
            
            match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_MPLS,
                                    mpls_label=pkt_fields['mpls_label'])      
            self.add_flow(msg_datapath, 0, 2, match, actions, instruction=ins)
            
            actions.append(parser.OFPActionOutput(oport))
            out = parser.OFPPacketOut(datapath=msg_datapath, buffer_id=msg.buffer_id,
                                      in_port=iport, actions=actions)  
        #config table 2 flows and defaut flows of table 3 to goto SH
        if self.RIB.add_flow(flow_id,iport, next_ports)==0:
            self.create_ndn_datapath(msg,pkt_fields,flow_id,next_ports)
                      
        return out    
        
    
    def create_ndn_datapath(self, msg, pkt_fields,flow_id, next_ports):
        msg_datapath = msg.datapath
        parser = msg_datapath.ofproto_parser
        iport = msg.match['in_port']
        #add a rule to set ip_tos to in_port
        ss = next_ports.index(msg_datapath.id)
        for switch in next_ports[ss:len(next_ports)-1]:
            msg_datapath = self.switches_dp[switch]
            next_hop = next_ports[next_ports.index(switch)+1]
            oport = self.net[switch][next_hop]['port']
            if switch in self.icn_switches:
                #print('add flow for switch ', switch)
                ins = [parser.OFPInstructionGotoTable(table_id=3)]
                actions = [parser.OFPActionSetField(ip_dscp=iport)]
                
                match = parser.OFPMatch(in_port=iport, eth_type=ether_types.ETH_TYPE_IP,
                                        ip_proto=self.INTEREST_PROTOCOL) 
                self.add_flow(msg_datapath, 2, 2, match, actions, instruction=ins)
                
                    
                match = parser.OFPMatch(in_port=oport, eth_type=ether_types.ETH_TYPE_IP,
                                        ip_proto=self.DATA_PROTOCOL) 
                self.add_flow(msg_datapath, 2, 2, match, None, instruction=ins)                                
                                         
                # send interest packets to SH        
                actions = [parser.OFPActionOutput(SH_IN_PORT)]
                #actions = [parser.OFPActionOutput(oport)]
                #print('add flow for table 3 in switch ', switch)
                match = parser.OFPMatch(in_port=iport,eth_type=ether_types.ETH_TYPE_IP,
                                        ip_proto=self.INTEREST_PROTOCOL)        
                self.add_flow(msg_datapath, 3, 1, match, actions)
                
                # send data packets on backward path to the service host      
                actions = [parser.OFPActionOutput(SH_IN_PORT)]
                #actions = [parser.OFPActionOutput(iport)]
                
                match = parser.OFPMatch(in_port=oport,eth_type=ether_types.ETH_TYPE_IP,
                                        ip_proto=self.DATA_PROTOCOL)        
                self.add_flow(msg_datapath, 3, 2, match, actions)
                
                # Add Forward interest path to the destination
                actions = [parser.OFPActionOutput(oport)]
                match = parser.OFPMatch(in_port=SH_OUT_PORT, eth_type=ether_types.ETH_TYPE_IP, 
                                        ip_proto=self.INTEREST_PROTOCOL, ipv4_dst=flow_id)
                #match = parser.OFPMatch(mpls_label=mpls_label, in_port=SH_PORT)
                self.add_flow(msg_datapath, 3, 3, match, actions)     
             
                # Add backward data path to the source
                actions = [parser.OFPActionDecNwTtl(),parser.OFPActionOutput(iport)]
                match = parser.OFPMatch(in_port=SH_OUT_PORT, eth_type=ether_types.ETH_TYPE_IP, 
                                        ip_proto=self.DATA_PROTOCOL, ip_dscp=iport)
                                        
                self.add_flow(msg_datapath, 3, 4, match, actions)
                
            else:
                print('Create channel for forwarding on virtual link in order to forward in non ICN switch')
                print('flow_id:', flow_id, ',switch:', switch, ',next_switch:',next_hop ,',in port: ', iport, 'oport:', oport)
                #for interests
                actions = [parser.OFPActionOutput(oport)]
                match = parser.OFPMatch(in_port=iport, eth_type=ether_types.ETH_TYPE_IP, ip_proto=self.INTEREST_PROTOCOL,
                                        ipv4_dst=flow_id)
                self.add_flow(msg_datapath, 1, 1, match, actions)
                #for data
                actions = [parser.OFPActionDecNwTtl(), parser.OFPActionOutput(iport)]
                match = parser.OFPMatch(in_port=oport, eth_type=ether_types.ETH_TYPE_IP, ip_proto=self.DATA_PROTOCOL,
                                        ipv4_dst=flow_id)
                self.add_flow(msg_datapath, 1, 1, match, actions)
            print('finding iport from next_hop '+str(next_hop)+ ' to switch ' + str(switch))
            print(self.net[next_hop])
            if 'port' in self.net[next_hop][switch]:
                iport = self.net[next_hop][switch]['port']
            
        #send back the packet to the switch for matching operations
        oport = self.net[msg.datapath.id][next_ports[next_ports.index(msg.datapath.id)+1]]['port']
#        data = None
#        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
#            print("data_ndn=msg.data")
#            data = msg.data
        #actions = actions.append(parser.OFPActionOutput(SH_IN_PORT))
        actions = [parser.OFPActionSetField(ip_dscp=iport), parser.OFPActionOutput(SH_IN_PORT)]
        out = parser.OFPPacketOut(datapath=msg.datapath, buffer_id=msg.buffer_id,
                                  in_port=iport, actions=actions)                         
        return out
      
      
    def create_legacy_datapath(self,msg, pkt_fields):
        """
        forward any other packets except NDN packets
        :param msg: target incoming msg
        :param out_port: the port to forward packet on if necessary
        :return: created packet_out
        """
        datapath = msg.datapath
        dpid = datapath.id
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        in_port = msg.match['in_port']  # Input port of switch (to the source)
        
        if pkt_fields['ip_dst'] in self.net:
            # print 'it is'
            path = nx.shortest_path(self.net, pkt_fields['ip_src'], pkt_fields['ip_dst'])

            # for switch in path[1:]:
            next_hop = path[path.index(dpid)+1]
            out_port = self.net[dpid][next_hop]['port']
        else:
            out_port = ofproto.OFPP_FLOOD
            if pkt_fields['eth_dst'] != "ff:ff:ff:ff:ff:ff":
                return
        
        actions = [parser.OFPActionOutput(out_port, ofproto.OFPCML_NO_BUFFER)]
        match = parser.OFPMatch(eth_type=pkt_fields['eth_type'], ipv4_dst=pkt_fields['ip_dst'])
        self.add_flow(datapath, 0, 1, match, actions)
        data = None
        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
            data = msg.data
        out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                               in_port=in_port, actions=actions, data=data)   
        return out
            
    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        """
        Packet_In event; Mismatched in switch and it asks controller
        :param ev: Event
        :return:
        """
        msg = ev.msg
        datapath = msg.datapath
        dpid = datapath.id  # Switch id
        in_port = msg.match['in_port']
        pkt = packet.Packet(msg.data)
        pkt_fields = self.packet_decode(pkt)
        
        if pkt_fields is None or pkt_fields['ip_protocol']==-1:
            return
            
        self.packet_in_num += 1
        # Add hosts to the net graph
        if pkt_fields['ip_src'] not in self.net:
            print ('add '+ pkt_fields['ip_src'])
            self.net.add_node(pkt_fields['ip_src'])
            self.net.add_edge(dpid, pkt_fields['ip_src'], port=in_port)
            self.net.add_edge(pkt_fields['ip_src'], dpid)
            
        # 
        if pkt_fields['ip_protocol'] == self.REGISTER_PROTOCOL:
            if pkt_fields['mpls_label']==self.HELLO_LABEL:
                print('add switch %i as an ICN switch' %dpid)
                self.icn_switches.append(dpid)
                self.switch_features_handler(ev)
            else:
                print ('New data registration in %i' % dpid)
                self.RIB.add(pkt_fields['mpls_label'],pkt_fields['ip_src'],pkt_fields['eth_src'])

            return

        # install a flow(role) to avoid packet_in next time
        # Interest packets
        if pkt_fields['ip_protocol'] == self.INTEREST_PROTOCOL:
        # if dst_port == self.INTEREST_PORT:
            """
            Interest packets has special ip_protocol number (150)
            If ip_protocol == 150 -> Interest packet
            """
            print ('New interest in %i ' % dpid )
            if (pkt_fields['mpls_label']>0):
                out = self.create_mpls_datapath(msg, pkt_fields)
            else:
                print('packet flow_id: ', pkt_fields['ip_dst'])
                path = self.lookup_flow_id_path(pkt_fields['ip_dst'])
                out = self.create_ndn_datapath(msg, pkt_fields,pkt_fields['ip_dst'],path)
                
            datapath.send_msg(out)

        elif pkt_fields['ip_protocol'] == self.DATA_PROTOCOL:
            """
            Data packets has special ip_protocol number (151)
            If ip_protocol == 151 -> Data packet
            """
            print ('New data in %i' % dpid)
            print ('in_port:',in_port)
            
        
        elif pkt_fields['ip_protocol'] != -1:
            """
            Other packets forward normally based on SDN
            """
            print ('Not NDN packet %i' % dpid)
            # self.logger.info("s%s from:%s to:%s sport:%s dport:%s",
            #                  dpid, et_src, et_dst, in_port, out_port)
            out = self.create_legacy_datapath(msg,pkt_fields)
            if out is not None:
                datapath.send_msg(out)

    @set_ev_cls(event.EventSwitchEnter)
    def get_topology_data(self, ev):
        s_id = ev.switch.dp.id        
        print('-----------------------------------------------')     
        print('new switch:',s_id)
        links_list = get_link(self,s_id)
        while(len(links_list)==0):
            links_list = get_link(self,s_id)
        links = [(link.src.dpid, link.dst.dpid, {'port': link.src.port_no})
                 for link in links_list]
        self.links.extend(links)
        
        links = [(link.dst.dpid, link.src.dpid, {'port': link.dst.port_no})
                 for link in links_list]
        self.links.extend(links)
        
        self.switches.append(s_id)

        print(self.links)
        self.net.add_nodes_from(self.switches)
        self.net.add_edges_from(self.links)