Finalizing Python3 migration
[osm/vim-emu.git] / src / emuvim / dcemulator / net.py
index 005e272..9e8c9be 100755 (executable)
 # acknowledge the contributions of their colleagues of the SONATA
 # partner consortium (www.sonata-nfv.eu).
 import logging
-
-import site
 import time
-from subprocess import Popen
 import re
 import requests
 import os
 import json
-
+import networkx as nx
+from subprocess import Popen
+# from gevent import monkey
 from mininet.net import Containernet
 from mininet.node import OVSSwitch, OVSKernelSwitch, Docker, RemoteController
 from mininet.cli import CLI
 from mininet.link import TCLink
 from mininet.clean import cleanup
-import networkx as nx
 from emuvim.dcemulator.monitoring import DCNetworkMonitor
 from emuvim.dcemulator.node import Datacenter, EmulatorCompute
 from emuvim.dcemulator.resourcemodel import ResourceModelRegistrar
 
+# ensure correct functionality of all gevent based REST servers
+# monkey.patch_all()
+
+# setup logging
 LOG = logging.getLogger("dcemulator.net")
 LOG.setLevel(logging.DEBUG)
 
@@ -115,7 +117,7 @@ class DCNetwork(Containernet):
         self.DCNetwork_graph = nx.MultiDiGraph()
 
         # initialize pool of vlan tags to setup the SDN paths
-        self.vlans = range(1, 4095)[::-1]
+        self.vlans = list(range(1, 4095))[::-1]
 
         # link to Ryu REST_API
         ryu_ip = 'localhost'
@@ -157,13 +159,13 @@ class DCNetwork(Containernet):
         assert node2 is not None
 
         # ensure type of node1
-        if isinstance(node1, basestring):
+        if isinstance(node1, str):
             if node1 in self.dcs:
                 node1 = self.dcs[node1].switch
         if isinstance(node1, Datacenter):
             node1 = node1.switch
         # ensure type of node2
-        if isinstance(node2, basestring):
+        if isinstance(node2, str):
             if node2 in self.dcs:
                 node2 = self.dcs[node2].switch
         if isinstance(node2, Datacenter):
@@ -224,7 +226,7 @@ class DCNetwork(Containernet):
                       'dst_port_name': node2_port_name}
         attr_dict2.update(attr_dict)
         self.DCNetwork_graph.add_edge(
-            node1.name, node2.name, attr_dict=attr_dict2)
+            node1.name, node2.name, **attr_dict2)
 
         attr_dict2 = {'src_port_id': node2_port_id, 'src_port_nr': node2.ports[link.intf2],
                       'src_port_name': node2_port_name,
@@ -232,7 +234,7 @@ class DCNetwork(Containernet):
                       'dst_port_name': node1_port_name}
         attr_dict2.update(attr_dict)
         self.DCNetwork_graph.add_edge(
-            node2.name, node1.name, attr_dict=attr_dict2)
+            node2.name, node1.name, **attr_dict2)
 
         LOG.debug("addLink: n1={0} intf1={1} -- n2={2} intf2={3}".format(
             str(node1), node1_port_name, str(node2), node2_port_name))
@@ -318,13 +320,13 @@ class DCNetwork(Containernet):
         Returns a list with all containers within all data centers.
         """
         all_containers = []
-        for dc in self.dcs.itervalues():
+        for dc in self.dcs.values():
             all_containers += dc.listCompute()
         return all_containers
 
     def start(self):
         # start
-        for dc in self.dcs.itervalues():
+        for dc in self.dcs.values():
             dc.start()
         Containernet.start(self)
 
@@ -383,6 +385,18 @@ class DCNetwork(Containernet):
             switch_node = self.getNodeByName(src_sw)
             self._set_vlan_tag(switch_node, src_sw_inport_name, vlan)
 
+    def getNodeByName(self, name):
+        """
+        Wraps Containernet's getNodeByName method to avoid
+        key not found exceptions.
+        """
+        try:
+            return super(DCNetwork, self).getNodeByName(name)
+        except BaseException as ex:
+            LOG.warning("Node not found: {}".format(name))
+            LOG.debug("Node not found: {}".format(ex))
+        return None
+
     def _addMonitorFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None,
                         tag=None, **kwargs):
         """
@@ -462,7 +476,7 @@ class DCNetwork(Containernet):
             return "No path could be found between {0} and {1}".format(
                 vnf_src_name, vnf_dst_name)
 
-        LOG.info("Path between {0} and {1}: {2}".format(
+        LOG.debug("Creating path between {0} and {1}: {2}".format(
             vnf_src_name, vnf_dst_name, path))
 
         current_hop = src_sw
@@ -484,7 +498,7 @@ class DCNetwork(Containernet):
 
             if next_hop == vnf_dst_name:
                 switch_outport_nr = dst_sw_outport_nr
-                LOG.info("end node reached: {0}".format(vnf_dst_name))
+                LOG.debug("end node reached: {0}".format(vnf_dst_name))
             elif not isinstance(next_node, OVSSwitch):
                 LOG.info("Next node: {0} is not a switch".format(next_hop))
                 return "Next node: {0} is not a switch".format(next_hop)
@@ -615,7 +629,7 @@ class DCNetwork(Containernet):
         # check if port is specified (vnf:port)
         if vnf_src_interface is None:
             # take first interface by default
-            connected_sw = self.DCNetwork_graph.neighbors(vnf_src_name)[0]
+            connected_sw = list(self.DCNetwork_graph.neighbors(vnf_src_name))[0]
             link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
             vnf_src_interface = link_dict[0]['src_port_id']
 
@@ -632,7 +646,7 @@ class DCNetwork(Containernet):
 
         if vnf_dst_interface is None:
             # take first interface by default
-            connected_sw = self.DCNetwork_graph.neighbors(vnf_dst_name)[0]
+            connected_sw = list(self.DCNetwork_graph.neighbors(vnf_dst_name))[0]
             link_dict = self.DCNetwork_graph[connected_sw][vnf_dst_name]
             vnf_dst_interface = link_dict[0]['dst_port_id']
 
@@ -666,7 +680,7 @@ class DCNetwork(Containernet):
                 return "No path could be found between {0} and {1}".format(
                     vnf_src_name, vnf_dst_name)
 
-        LOG.info("Path between {0} and {1}: {2}".format(
+        LOG.debug("Creating path between {0} and {1}: {2}".format(
             vnf_src_name, vnf_dst_name, path))
 
         current_hop = src_sw
@@ -706,7 +720,7 @@ class DCNetwork(Containernet):
 
             if next_hop == vnf_dst_name:
                 switch_outport_nr = dst_sw_outport_nr
-                LOG.info("end node reached: {0}".format(vnf_dst_name))
+                LOG.debug("end node reached: {0}".format(vnf_dst_name))
             elif not isinstance(next_node, OVSSwitch):
                 LOG.info("Next node: {0} is not a switch".format(next_hop))
                 return "Next node: {0} is not a switch".format(next_hop)
@@ -746,6 +760,8 @@ class DCNetwork(Containernet):
             'match_input': kwargs.get('match')
         }
         flow_options_str = json.dumps(flow_options, indent=1)
+        LOG.info("Installed flow rule: ({}:{}) -> ({}:{}) with options: {}"
+                 .format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface, flow_options))
         return "success: {2} between {0} and {1} with options: {3}".format(
             vnf_src_name, vnf_dst_name, cmd, flow_options_str)
 
@@ -758,6 +774,7 @@ class DCNetwork(Containernet):
         cmd = kwargs.get('cmd')
         path = kwargs.get('path')
         index = kwargs.get('pathindex')
+        mod_dl_dst = kwargs.get('mod_dl_dst')
 
         vlan = kwargs.get('vlan')
         priority = kwargs.get('priority', DEFAULT_PRIORITY)
@@ -825,6 +842,12 @@ class DCNetwork(Containernet):
 
                 else:  # middle nodes
                     match += ',dl_vlan=%s' % vlan
+            if mod_dl_dst:
+                action = {}
+                action['type'] = 'SET_FIELD'
+                action['field'] = 'eth_dst'
+                action['value'] = mod_dl_dst
+                flow['actions'].append(action)
 
             # output action must come last
             action = {}
@@ -896,14 +919,14 @@ class DCNetwork(Containernet):
     # start Ryu Openflow controller as Remote Controller for the DCNetwork
     def startRyu(self, learning_switch=True):
         # start Ryu controller with rest-API
-        python_install_path = site.getsitepackages()[0]
+
         # ryu default learning switch
-        # ryu_path = python_install_path + '/ryu/app/simple_switch_13.py'
+        # ryu_learning_app = python_install_path + '/ryu/app/simple_switch_13.py'
         # custom learning switch that installs a default NORMAL action in the
         # ovs switches
         dir_path = os.path.dirname(os.path.realpath(__file__))
-        ryu_path = dir_path + '/son_emu_simple_switch_13.py'
-        ryu_path2 = python_install_path + '/ryu/app/ofctl_rest.py'
+        ryu_learning_app = dir_path + '/son_emu_simple_switch_13.py'
+        ryu_rest_app = 'ryu.app.ofctl_rest'
         # change the default Openflow controller port to 6653 (official IANA-assigned port number), as used by Mininet
         # Ryu still uses 6633 as default
         ryu_option = '--ofp-tcp-listen-port'
@@ -911,15 +934,13 @@ class DCNetwork(Containernet):
         ryu_cmd = 'ryu-manager'
         FNULL = open("/tmp/ryu.log", 'w')
         if learning_switch:
-            self.ryu_process = Popen(
-                [ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
-            LOG.debug('starting ryu-controller with {0}'.format(ryu_path))
-            LOG.debug('starting ryu-controller with {0}'.format(ryu_path2))
+            # learning and rest api
+            args = [ryu_cmd, ryu_learning_app, ryu_rest_app, ryu_option, ryu_of_port]
         else:
             # no learning switch, but with rest api
-            self.ryu_process = Popen(
-                [ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
-            LOG.debug('starting ryu-controller with {0}'.format(ryu_path2))
+            args = [ryu_cmd, ryu_rest_app, ryu_option, ryu_of_port]
+        self.ryu_process = Popen(args, stdout=FNULL, stderr=FNULL)
+        LOG.debug('starting ryu-controller with %s' % args)
         time.sleep(1)
 
     def killRyu(self):
@@ -928,9 +949,12 @@ class DCNetwork(Containernet):
         :return:
         """
         # try it nicely
-        if self.ryu_process is not None:
-            self.ryu_process.terminate()
-            self.ryu_process.kill()
+        try:
+            if self.ryu_process is not None:
+                self.ryu_process.terminate()
+                self.ryu_process.kill()
+        except BaseException as ex:
+            LOG.warning("Error during Ryu stop: {}".format(ex))
         # ensure its death ;-)
         Popen(['pkill', '-f', 'ryu-manager'])
 
@@ -940,6 +964,8 @@ class DCNetwork(Containernet):
             url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid)
         else:
             url = self.ryu_REST_api + '/' + str(prefix)
+
+        LOG.debug('sending RYU command: %s, payload: %s', url, data)
         if data:
             req = self.RyuSession.post(url, json=data)
         else:
@@ -947,7 +973,7 @@ class DCNetwork(Containernet):
 
         # do extra logging if status code is not 200 (OK)
         if req.status_code is not requests.codes.ok:
-            logging.info(
+            LOG.info(
                 'type {0}  encoding: {1} text: {2} headers: {3} history: {4}'.format(req.headers['content-type'],
                                                                                      req.encoding, req.text,
                                                                                      req.headers, req.history))