add SDN chaining unit test
authorstevenvanrossem <steven.vanrossem@intec.ugent.be>
Wed, 11 May 2016 20:55:15 +0000 (22:55 +0200)
committerstevenvanrossem <steven.vanrossem@intec.ugent.be>
Wed, 11 May 2016 20:55:15 +0000 (22:55 +0200)
src/emuvim/dcemulator/net.py
src/emuvim/examples/monitoring_demo_topology.py
src/emuvim/test/base.py
src/emuvim/test/test_emulator.py

index 248dc34..9ca75f7 100755 (executable)
@@ -30,6 +30,7 @@ class DCNetwork(Dockernet):
     """
 
     def __init__(self, controller=RemoteController, monitor=False,
     """
 
     def __init__(self, controller=RemoteController, monitor=False,
+                 enable_learning = True,   # in case of RemoteController (Ryu), learning switch behavior can be turned off/on
                  dc_emulation_max_cpu=1.0,  # fraction of overall CPU time for emulation
                  dc_emulation_max_mem=512,  # emulation max mem in MB
                  **kwargs):
                  dc_emulation_max_cpu=1.0,  # fraction of overall CPU time for emulation
                  dc_emulation_max_mem=512,  # emulation max mem in MB
                  **kwargs):
@@ -43,13 +44,13 @@ class DCNetwork(Dockernet):
 
         # call original Docker.__init__ and setup default controller
         Dockernet.__init__(
 
         # call original Docker.__init__ and setup default controller
         Dockernet.__init__(
-            self, switch=OVSKernelSwitch, **kwargs)
+            self, switch=OVSKernelSwitch, controller=controller, **kwargs)
 
         # Ryu management
         self.ryu_process = None
         if controller == RemoteController:
             # start Ryu controller
 
         # Ryu management
         self.ryu_process = None
         if controller == RemoteController:
             # start Ryu controller
-            self.startRyu()
+            self.startRyu(learning_switch=enable_learning)
 
         # add the specified controller
         self.addController('c0', controller=controller)
 
         # add the specified controller
         self.addController('c0', controller=controller)
@@ -331,10 +332,15 @@ class DCNetwork(Dockernet):
                 kwargs['vlan'] = vlan
                 kwargs['path'] = path
                 kwargs['current_hop'] = current_hop
                 kwargs['vlan'] = vlan
                 kwargs['path'] = path
                 kwargs['current_hop'] = current_hop
-                ## set flow entry via ovs-ofctl
-                #self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
-                ## set flow entry via ryu rest api
-                self._set_flow_entry_ryu_rest(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
+
+                if self.controller == RemoteController:
+                    ## set flow entry via ryu rest api
+                    self._set_flow_entry_ryu_rest(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
+                else:
+                    ## set flow entry via ovs-ofctl
+                    self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
+
+
 
             # take first link between switches by default
             if isinstance( next_node, OVSSwitch ):
 
             # take first link between switches by default
             if isinstance( next_node, OVSSwitch ):
@@ -359,6 +365,8 @@ class DCNetwork(Dockernet):
 
         flow = {}
         flow['dpid'] = int(node.dpid, 16)
 
         flow = {}
         flow['dpid'] = int(node.dpid, 16)
+        logging.info('node name:{0}'.format(node.name))
+
         if cookie:
             flow['cookie'] = int(cookie)
 
         if cookie:
             flow['cookie'] = int(cookie)
 
@@ -369,15 +377,13 @@ class DCNetwork(Dockernet):
         # http://ryu.readthedocs.io/en/latest/app/ofctl_rest.html#add-a-flow-entry
         if cmd == 'add-flow':
             prefix = 'stats/flowentry/add'
         # http://ryu.readthedocs.io/en/latest/app/ofctl_rest.html#add-a-flow-entry
         if cmd == 'add-flow':
             prefix = 'stats/flowentry/add'
-            action = {}
-            action['type'] = 'OUTPUT'
-            action['port'] = switch_outport_nr
-            flow['actions'].append(action)
             if vlan != None:
                 if path.index(current_hop) == 0:  # first node
                     action = {}
                     action['type'] = 'PUSH_VLAN'  # Push a new VLAN tag if a input frame is non-VLAN-tagged
                     action['ethertype'] = 33024   # Ethertype 0x8100(=33024): IEEE 802.1Q VLAN-tagged frame
             if vlan != None:
                 if path.index(current_hop) == 0:  # first node
                     action = {}
                     action['type'] = 'PUSH_VLAN'  # Push a new VLAN tag if a input frame is non-VLAN-tagged
                     action['ethertype'] = 33024   # Ethertype 0x8100(=33024): IEEE 802.1Q VLAN-tagged frame
+                    flow['actions'].append(action)
+                    action = {}
                     action['type'] = 'SET_FIELD'
                     action['field'] = 'vlan_vid'
                     action['value'] = vlan
                     action['type'] = 'SET_FIELD'
                     action['field'] = 'vlan_vid'
                     action['value'] = vlan
@@ -389,6 +395,11 @@ class DCNetwork(Dockernet):
                     flow['actions'].append(action)
                 else:  # middle nodes
                     match += ',dl_vlan=%s' % vlan
                     flow['actions'].append(action)
                 else:  # middle nodes
                     match += ',dl_vlan=%s' % vlan
+            # output action must come last
+            action = {}
+            action['type'] = 'OUTPUT'
+            action['port'] = switch_outport_nr
+            flow['actions'].append(action)
             #flow['match'] = self._parse_match(match)
         elif cmd == 'del-flows':
             #del(flow['actions'])
             #flow['match'] = self._parse_match(match)
         elif cmd == 'del-flows':
             #del(flow['actions'])
@@ -444,7 +455,7 @@ class DCNetwork(Dockernet):
                                                                                  switch_outport_nr, cmd))
 
     # start Ryu Openflow controller as Remote Controller for the DCNetwork
                                                                                  switch_outport_nr, cmd))
 
     # start Ryu Openflow controller as Remote Controller for the DCNetwork
-    def startRyu(self):
+    def startRyu(self, learning_switch=True):
         # start Ryu controller with rest-API
         python_install_path = site.getsitepackages()[0]
         ryu_path = python_install_path + '/ryu/app/simple_switch_13.py'
         # start Ryu controller with rest-API
         python_install_path = site.getsitepackages()[0]
         ryu_path = python_install_path + '/ryu/app/simple_switch_13.py'
@@ -455,9 +466,11 @@ class DCNetwork(Dockernet):
         ryu_of_port = '6653'
         ryu_cmd = 'ryu-manager'
         FNULL = open("/tmp/ryu.log", 'w')
         ryu_of_port = '6653'
         ryu_cmd = 'ryu-manager'
         FNULL = open("/tmp/ryu.log", 'w')
-        self.ryu_process = Popen([ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
-        # no learning switch
-        #self.ryu_process = Popen([ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
+        if learning_switch:
+            self.ryu_process = Popen([ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
+        else:
+            # no learning switch
+            self.ryu_process = Popen([ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
         time.sleep(1)
 
     def stopRyu(self):
         time.sleep(1)
 
     def stopRyu(self):
@@ -466,18 +479,22 @@ class DCNetwork(Dockernet):
             self.ryu_process.kill()
 
     def ryu_REST(self, prefix, dpid=None, data=None):
             self.ryu_process.kill()
 
     def ryu_REST(self, prefix, dpid=None, data=None):
-        if dpid:
-            url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid)
-        else:
-            url = self.ryu_REST_api + '/' + str(prefix)
-        if data:
-            #logging.info('POST: {0}'.format(str(data)))
-            req = urllib2.Request(url, str(data))
-        else:
-            req = urllib2.Request(url)
+        try:
+            if dpid:
+                url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid)
+            else:
+                url = self.ryu_REST_api + '/' + str(prefix)
+            if data:
+                #logging.info('POST: {0}'.format(str(data)))
+                req = urllib2.Request(url, str(data))
+            else:
+                req = urllib2.Request(url)
 
 
-        ret = urllib2.urlopen(req).read()
-        return ret
+            ret = urllib2.urlopen(req).read()
+            return ret
+        except:
+            logging.info('error url: {0}'.format(str(url)))
+            if data: logging.info('error POST: {0}'.format(str(data)))
 
     # need to respect that some match fields must be integers
     # http://ryu.readthedocs.io/en/latest/app/ofctl_rest.html#description-of-match-and-actions
 
     # need to respect that some match fields must be integers
     # http://ryu.readthedocs.io/en/latest/app/ofctl_rest.html#description-of-match-and-actions
index 9737609..4dfd5b7 100755 (executable)
@@ -29,7 +29,7 @@ def create_topology1():
     """
     1. Create a data center network object (DCNetwork) with monitoring enabled
     """
     """
     1. Create a data center network object (DCNetwork) with monitoring enabled
     """
-    net = DCNetwork(monitor=True)
+    net = DCNetwork(monitor=True, enable_learning=False)
 
     """
     1b. add a monitoring agent to the DCNetwork
 
     """
     1b. add a monitoring agent to the DCNetwork
@@ -53,15 +53,17 @@ def create_topology1():
     3. You can add additional SDN switches for data center
        interconnections to the network.
     """
     3. You can add additional SDN switches for data center
        interconnections to the network.
     """
-    #s1 = net.addSwitch("s1")
+    s1 = net.addSwitch("s1")
 
     """
     4. Add links between your data centers and additional switches
        to define you topology.
        These links can use Mininet's features to limit bw, add delay or jitter.
     """
 
     """
     4. Add links between your data centers and additional switches
        to define you topology.
        These links can use Mininet's features to limit bw, add delay or jitter.
     """
-    net.addLink(dc1, dc2, delay="10ms")
+    #net.addLink(dc1, dc2, delay="10ms")
     #net.addLink(dc1, dc2)
     #net.addLink(dc1, dc2)
+    net.addLink(dc1, s1)
+    net.addLink(s1, dc2)
     #net.addLink("datacenter1", s1, delay="20ms")
     #net.addLink(s1, dc3)
     #net.addLink(s1, "datacenter4")
     #net.addLink("datacenter1", s1, delay="20ms")
     #net.addLink(s1, dc3)
     #net.addLink(s1, "datacenter4")
index 4bad515..9efb4ab 100644 (file)
@@ -28,7 +28,7 @@ class SimpleTestTopology(unittest.TestCase):
     def createNet(
             self,
             nswitches=0, ndatacenter=0, nhosts=0, ndockers=0,
     def createNet(
             self,
             nswitches=0, ndatacenter=0, nhosts=0, ndockers=0,
-            autolinkswitches=False, controller=Controller):
+            autolinkswitches=False, controller=Controller, **kwargs):
         """
         Creates a Mininet instance and automatically adds some
         nodes to it.
         """
         Creates a Mininet instance and automatically adds some
         nodes to it.
@@ -37,7 +37,7 @@ class SimpleTestTopology(unittest.TestCase):
         for our tests. Only use other controllers if you want to test
         specific controller functionality.
         """
         for our tests. Only use other controllers if you want to test
         specific controller functionality.
         """
-        self.net = DCNetwork(controller=controller)
+        self.net = DCNetwork(controller=controller, **kwargs)
 
         # add some switches
         for i in range(0, nswitches):
 
         # add some switches
         for i in range(0, nswitches):
index 2038116..0c387bf 100755 (executable)
@@ -10,6 +10,7 @@ import time
 import unittest
 from emuvim.dcemulator.node import EmulatorCompute
 from emuvim.test.base import SimpleTestTopology
 import unittest
 from emuvim.dcemulator.node import EmulatorCompute
 from emuvim.test.base import SimpleTestTopology
+from mininet.node import RemoteController
 
 
 #@unittest.skip("disabled topology tests for development")
 
 
 #@unittest.skip("disabled topology tests for development")
@@ -87,6 +88,54 @@ class testEmulatorTopology( SimpleTestTopology ):
         # stop Mininet network
         self.stopNet()
 
         # stop Mininet network
         self.stopNet()
 
+class testEmulatorNetworking( SimpleTestTopology ):
+
+    def testSDNChaining(self):
+        """
+        Create a two data centers and interconnect them with additional
+        switches between them.
+        Uses Ryu SDN controller.
+        Connect the Docker hosts to different datacenters and setup the links between.
+        """
+        # create network
+        self.createNet(
+            nswitches=3, ndatacenter=2, nhosts=0, ndockers=0,
+            autolinkswitches=True,
+            controller=RemoteController,
+            enable_learning=False)
+        # setup links
+        self.net.addLink(self.dc[0], self.s[0])
+        self.net.addLink(self.s[2], self.dc[1])
+        # start Mininet network
+        self.startNet()
+
+        # add compute resources
+        vnf1 = self.dc[0].startCompute("vnf1", network=[{'id':'intf1', 'ip':'10.0.10.1/24'}])
+        vnf2 = self.dc[1].startCompute("vnf2", network=[{'id':'intf2', 'ip':'10.0.10.2/24'}])
+        # check number of running nodes
+        self.assertTrue(len(self.getDockernetContainers()) == 2)
+        self.assertTrue(len(self.net.hosts) == 2)
+        self.assertTrue(len(self.net.switches) == 5)
+        # check status
+        # check get status
+        s1 = self.dc[0].containers.get("vnf1").getStatus()
+        self.assertTrue(s1["name"] == "vnf1")
+        self.assertTrue(s1["state"]["Running"])
+        self.assertTrue(s1["network"][0]['intf_name'] == 'intf1')
+        self.assertTrue(s1["network"][0]['ip'] == '10.0.10.1')
+
+        s2 = self.dc[1].containers.get("vnf2").getStatus()
+        self.assertTrue(s2["name"] == "vnf2")
+        self.assertTrue(s2["state"]["Running"])
+        self.assertTrue(s2["network"][0]['intf_name'] == 'intf2')
+        self.assertTrue(s2["network"][0]['ip'] == '10.0.10.2')
+
+        # setup links
+        self.net.setChain('vnf1', 'vnf2', 'intf1', 'intf2', bidirectional=True, cmd='add-flow')
+        # check connectivity by using ping
+        self.assertTrue(self.net.ping([vnf1, vnf2]) <= 0.0)
+        # stop Mininet network
+        self.stopNet()
 
 #@unittest.skip("disabled compute tests for development")
 class testEmulatorCompute( SimpleTestTopology ):
 
 #@unittest.skip("disabled compute tests for development")
 class testEmulatorCompute( SimpleTestTopology ):