From: stevenvanrossem Date: Wed, 11 May 2016 20:55:15 +0000 (+0200) Subject: add SDN chaining unit test X-Git-Tag: v3.1~127^2~3 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2Fvim-emu.git;a=commitdiff_plain;h=7cd3c2529e5b926d0ca11bbaaa106cb8875059a4 add SDN chaining unit test --- diff --git a/src/emuvim/dcemulator/net.py b/src/emuvim/dcemulator/net.py index 248dc34..9ca75f7 100755 --- a/src/emuvim/dcemulator/net.py +++ b/src/emuvim/dcemulator/net.py @@ -30,6 +30,7 @@ class DCNetwork(Dockernet): """ 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): @@ -43,13 +44,13 @@ class DCNetwork(Dockernet): # 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 - self.startRyu() + self.startRyu(learning_switch=enable_learning) # 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 - ## 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 ): @@ -359,6 +365,8 @@ class DCNetwork(Dockernet): flow = {} flow['dpid'] = int(node.dpid, 16) + logging.info('node name:{0}'.format(node.name)) + 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' - 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 + flow['actions'].append(action) + action = {} 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 + # 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']) @@ -444,7 +455,7 @@ class DCNetwork(Dockernet): 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' @@ -455,9 +466,11 @@ class DCNetwork(Dockernet): 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): @@ -466,18 +479,22 @@ class DCNetwork(Dockernet): 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 diff --git a/src/emuvim/examples/monitoring_demo_topology.py b/src/emuvim/examples/monitoring_demo_topology.py index 9737609..4dfd5b7 100755 --- a/src/emuvim/examples/monitoring_demo_topology.py +++ b/src/emuvim/examples/monitoring_demo_topology.py @@ -29,7 +29,7 @@ def create_topology1(): """ 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 @@ -53,15 +53,17 @@ def create_topology1(): 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. """ - net.addLink(dc1, dc2, delay="10ms") + #net.addLink(dc1, dc2, delay="10ms") #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") diff --git a/src/emuvim/test/base.py b/src/emuvim/test/base.py index 4bad515..9efb4ab 100644 --- a/src/emuvim/test/base.py +++ b/src/emuvim/test/base.py @@ -28,7 +28,7 @@ class SimpleTestTopology(unittest.TestCase): 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. @@ -37,7 +37,7 @@ class SimpleTestTopology(unittest.TestCase): 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): diff --git a/src/emuvim/test/test_emulator.py b/src/emuvim/test/test_emulator.py index 2038116..0c387bf 100755 --- a/src/emuvim/test/test_emulator.py +++ b/src/emuvim/test/test_emulator.py @@ -10,6 +10,7 @@ import time 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") @@ -87,6 +88,54 @@ class testEmulatorTopology( SimpleTestTopology ): # 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 ):