Merge remote-tracking branch 'upstream/master'
[osm/vim-emu.git] / src / emuvim / dcemulator / net.py
index 27a07d1..15bb6f1 100755 (executable)
@@ -30,15 +30,14 @@ import logging
 import site
 import time
 from subprocess import Popen
-import os
 import re
-import urllib2
-from functools import partial
+import requests
 
 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
+from mininet.clean import cleanup
 import networkx as nx
 from emuvim.dcemulator.monitoring import DCNetworkMonitor
 from emuvim.dcemulator.node import Datacenter, EmulatorCompute
@@ -66,20 +65,19 @@ class DCNetwork(Containernet):
         :param kwargs: path through for Mininet parameters
         :return:
         """
+        # members
         self.dcs = {}
+        self.ryu_process = None
 
-        # make sure any remaining Ryu processes are killed
+        # always cleanup environment before we start the emulator
         self.killRyu()
-        # make sure no containers are left over from a previous emulator run.
-        self.removeLeftoverContainers()
+        cleanup()
 
         # call original Docker.__init__ and setup default controller
         Containernet.__init__(
             self, switch=OVSKernelSwitch, controller=controller, **kwargs)
 
-
         # Ryu management
-        self.ryu_process = None
         if controller == RemoteController:
             # start Ryu controller
             self.startRyu(learning_switch=enable_learning)
@@ -94,9 +92,10 @@ class DCNetwork(Containernet):
         self.vlans = range(4096)[::-1]
 
         # link to Ryu REST_API
-        ryu_ip = '0.0.0.0'
+        ryu_ip = 'localhost'
         ryu_port = '8080'
         self.ryu_REST_api = 'http://{0}:{1}'.format(ryu_ip, ryu_port)
+        self.RyuSession = requests.Session()
 
         # monitoring agent
         if monitor:
@@ -255,14 +254,28 @@ class DCNetwork(Containernet):
         Containernet.stop(self)
 
         # stop Ryu controller
-        self.stopRyu()
+        self.killRyu()
 
 
     def CLI(self):
         CLI(self)
 
-    # to remove chain do setChain( src, dst, cmd='del-flows')
     def setChain(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, **kwargs):
+        """
+        Chain 2 vnf interfaces together by installing the flowrules in the switches along their path.
+        Currently the path is found using the default networkx shortest path function.
+        Each chain gets a unique vlan id , so different chains wil not interfere.
+
+        :param vnf_src_name: vnf name (string)
+        :param vnf_dst_name: vnf name (string)
+        :param vnf_src_interface: source interface name  (string)
+        :param vnf_dst_interface: destination interface name  (string)
+        :param cmd: 'add-flow' (default) to add a chain, 'del-flows' to remove a chain
+        :param cookie: cookie for the installed flowrules (can be used later as identifier for a set of installed chains)
+        :param match: custom match entry to be added to the flowrules (default: only in_port and vlan tag)
+        :param priority: custom flowrule priority
+        :return: output log string
+        """
         cmd = kwargs.get('cmd')
         if cmd == 'add-flow':
             ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs)
@@ -405,6 +418,7 @@ class DCNetwork(Containernet):
         path = kwargs.get('path')
         current_hop = kwargs.get('current_hop')
         vlan = kwargs.get('vlan')
+        priority = kwargs.get('priority')
 
         s = ','
         if match_input:
@@ -415,7 +429,8 @@ class DCNetwork(Containernet):
 
         if cookie:
             flow['cookie'] = int(cookie)
-
+        if priority:
+            flow['priority'] = int(priority)
 
         flow['actions'] = []
 
@@ -518,38 +533,48 @@ class DCNetwork(Containernet):
             self.ryu_process = Popen([ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
         time.sleep(1)
 
-    def stopRyu(self):
+    def killRyu(self):
+        """
+        Stop the Ryu controller that might be started by son-emu.
+        :return:
+        """
+        # try it nicely
         if self.ryu_process is not None:
             self.ryu_process.terminate()
             self.ryu_process.kill()
-        self.killRyu()
-
-    @staticmethod
-    def removeLeftoverContainers():
-        # TODO can be more python-based using eg. docker-py?
-        Popen('docker ps -a -q --filter="name=mn.*" | xargs -r docker rm -f', shell=True)
-
-    @staticmethod
-    def killRyu():
+        # ensure its death ;-)
         Popen(['pkill', '-f', 'ryu-manager'])
 
     def ryu_REST(self, prefix, dpid=None, data=None):
-        try:
-            if dpid:
-                url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid)
-            else:
-                url = self.ryu_REST_api + '/' + str(prefix)
-            if data:
-                #LOG.info('POST: {0}'.format(str(data)))
-                req = urllib2.Request(url, str(data))
-            else:
-                req = urllib2.Request(url)
 
-            ret = urllib2.urlopen(req).read()
+        if dpid:
+            url = self.ryu_REST_api + '/' + str(prefix) + '/' + str(dpid)
+        else:
+            url = self.ryu_REST_api + '/' + str(prefix)
+        if data:
+            req = self.RyuSession.post(url, json=data)
+        else:
+            req = self.RyuSession.get(url)
+
+
+        # do extra logging if status code is not 200 (OK)
+        if req.status_code is not requests.codes.ok:
+            logging.info(
+                'type {0}  encoding: {1} text: {2} headers: {3} history: {4}'.format(req.headers['content-type'],
+                                                                                     req.encoding, req.text,
+                                                                                     req.headers, req.history))
+            LOG.info('url: {0}'.format(str(url)))
+            if data: LOG.info('POST: {0}'.format(str(data)))
+            LOG.info('status: {0} reason: {1}'.format(req.status_code, req.reason))
+
+
+        if 'json' in req.headers['content-type']:
+            ret = req.json()
             return ret
-        except:
-            LOG.info('error url: {0}'.format(str(url)))
-            if data: LOG.info('error POST: {0}'.format(str(data)))
+
+        ret = req.text.rstrip()
+        return ret
+
 
     # 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