merge master
[osm/vim-emu.git] / src / emuvim / dcemulator / net.py
index 248dc34..115b9e5 100755 (executable)
@@ -12,7 +12,7 @@ import re
 import urllib2
 from functools import partial
 
-from mininet.net import Dockernet
+from mininet.net import Containernet
 from mininet.node import Controller, DefaultController, OVSSwitch, OVSKernelSwitch, Docker, RemoteController
 from mininet.cli import CLI
 from mininet.link import TCLink
@@ -21,20 +21,21 @@ from emuvim.dcemulator.monitoring import DCNetworkMonitor
 from emuvim.dcemulator.node import Datacenter, EmulatorCompute
 from emuvim.dcemulator.resourcemodel import ResourceModelRegistrar
 
-class DCNetwork(Dockernet):
+class DCNetwork(Containernet):
     """
-    Wraps the original Mininet/Dockernet class and provides
+    Wraps the original Mininet/Containernet class and provides
     methods to add data centers, switches, etc.
 
     This class is used by topology definition scripts.
     """
 
     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):
         """
-        Create an extended version of a Dockernet network
+        Create an extended version of a Containernet network
         :param dc_emulation_max_cpu: max. CPU time used by containers in data centers
         :param kwargs: path through for Mininet parameters
         :return:
@@ -42,14 +43,15 @@ class DCNetwork(Dockernet):
         self.dcs = {}
 
         # call original Docker.__init__ and setup default controller
-        Dockernet.__init__(
-            self, switch=OVSKernelSwitch, **kwargs)
+        Containernet.__init__(
+            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)
@@ -121,11 +123,11 @@ class DCNetwork(Dockernet):
                 params["params2"]["ip"] = self.getNextIp()
         # ensure that we allow TCLinks between data centers
         # TODO this is not optimal, we use cls=Link for containers and TCLink for data centers
-        # see Dockernet issue: https://github.com/mpeuster/dockernet/issues/3
+        # see Containernet issue: https://github.com/mpeuster/containernet/issues/3
         if "cls" not in params:
             params["cls"] = TCLink
 
-        link = Dockernet.addLink(self, node1, node2, **params)
+        link = Containernet.addLink(self, node1, node2, **params)
 
         # try to give container interfaces a default id
         node1_port_id = node1.ports[link.intf1]
@@ -143,7 +145,7 @@ class DCNetwork(Dockernet):
 
         # add edge and assigned port number to graph in both directions between node1 and node2
         # port_id: id given in descriptor (if available, otherwise same as port)
-        # port: portnumber assigned by Dockernet
+        # port: portnumber assigned by Containernet
 
         attr_dict = {}
         # possible weight metrics allowed by TClink class:
@@ -180,14 +182,14 @@ class DCNetwork(Dockernet):
         Wrapper for addDocker method to use custom container class.
         """
         self.DCNetwork_graph.add_node(label)
-        return Dockernet.addDocker(self, label, cls=EmulatorCompute, **params)
+        return Containernet.addDocker(self, label, cls=EmulatorCompute, **params)
 
     def removeDocker( self, label, **params ):
         """
         Wrapper for removeDocker method to update graph.
         """
         self.DCNetwork_graph.remove_node(label)
-        return Dockernet.removeDocker(self, label, **params)
+        return Containernet.removeDocker(self, label, **params)
 
     def addSwitch( self, name, add_to_graph=True, **params ):
         """
@@ -195,7 +197,7 @@ class DCNetwork(Dockernet):
         """
         if add_to_graph:
             self.DCNetwork_graph.add_node(name)
-        return Dockernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', **params)
+        return Containernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', **params)
 
     def getAllContainers(self):
         """
@@ -210,7 +212,7 @@ class DCNetwork(Dockernet):
         # start
         for dc in self.dcs.itervalues():
             dc.start()
-        Dockernet.start(self)
+        Containernet.start(self)
 
     def stop(self):
 
@@ -219,7 +221,7 @@ class DCNetwork(Dockernet):
             self.monitor_agent.stop()
 
         # stop emulator net
-        Dockernet.stop(self)
+        Containernet.stop(self)
 
         # stop Ryu controller
         self.stopRyu()
@@ -331,10 +333,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 +366,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 +378,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 +396,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 +456,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 +467,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 +480,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