improve vlan tagging in emulator chaining
authorSteven Van Rossem <steven.vanrossem@intec.ugent.be>
Tue, 31 Oct 2017 12:28:37 +0000 (13:28 +0100)
committerSteven Van Rossem <steven.vanrossem@intec.ugent.be>
Tue, 31 Oct 2017 12:28:37 +0000 (13:28 +0100)
src/emuvim/dashboard/js/main.js
src/emuvim/dcemulator/net.py
src/emuvim/dcemulator/node.py

index 3fdd488..b30a40c 100755 (executable)
@@ -83,7 +83,7 @@ function update_table_container(data)
            build_network_table(item[1].network, item[0]);
     });
     $("#lbl_container_count").text(data.length);
-    $("#table_network").append('<table class="interface_table"><tr class="interface_row"><td class="interface_port">datacenter port</td><td class="interface_name">interface</td><td class="interface_ip">ip</td><td class="interface_mac">mac</td></tr></table>')
+    $("#table_network").append('<table class="interface_table"><tr class="interface_row"><td class="interface_port">datacenter port</td><td class="interface_name">interface</td><td class="interface_ip">ip</td><td class="interface_mac">mac</td><td class="vlan_tag">vlan</td></tr></table>')
     // update lateness counter
     LAST_UPDATE_TIMESTAMP_CONTAINER = Date.now();
 }
@@ -99,6 +99,7 @@ function build_network_table(network_list, id)
         row_str += '<td class="interface_name">' + interface.intf_name + '</td>';
         row_str += '<td class="interface_ip">' + interface.ip + '</td>';
         row_str += '<td class="interface_mac">' + interface.mac + '</td>';
+        row_str += '<td class="vlan_tag">' + interface.vlan + '</td>';
         row_str += '</tr>';
     });
     $("#network_list_" + id).append(row_str)
index ea9fd1c..2cc005e 100755 (executable)
@@ -34,6 +34,7 @@ import re
 import requests
 import os
 import json
+import copy
 
 from mininet.net import Containernet
 from mininet.node import Controller, DefaultController, OVSSwitch, OVSKernelSwitch, Docker, RemoteController
@@ -82,7 +83,7 @@ class DCNetwork(Containernet):
         self.deployed_nsds = []
         self.deployed_elines = []
         self.deployed_elans = []
-        self.installed_chains = []
+        self.vlan_dict = {}
 
 
         # always cleanup environment before we start the emulator
@@ -335,20 +336,25 @@ class DCNetwork(Containernet):
     def CLI(self):
         CLI(self)
 
-    def setLAN(self, vnf_list):
+    def setLAN(self, vnf_list, vlan=None, action='add'):
         """
         setup an E-LAN network by assigning the same VLAN tag to each DC interface of the VNFs in the E-LAN
 
         :param vnf_list: names of the VNFs in this E-LAN  [{name:,interface:},...]
+        :param vlan: vlan tag to be used
+        :param action: 'add' or 'delete' the vlan tags for the intefaces
+
         :return:
         """
         src_sw = None
         src_sw_inport_nr = 0
         src_sw_inport_name = None
 
-        # get a vlan tag for this E-LAN
-        vlan = self.vlans.pop()
+        if vlan is None:
+            # get a vlan tag for this E-LAN
+            vlan = self.vlans.pop()
 
+        ret = ''
         for vnf in vnf_list:
             vnf_src_name = vnf['name']
             vnf_src_interface = vnf['interface']
@@ -374,7 +380,17 @@ class DCNetwork(Containernet):
             # set the tag on the dc switch interface
             LOG.debug('set E-LAN: vnf name: {0} interface: {1} tag: {2}'.format(vnf_src_name, vnf_src_interface,vlan))
             switch_node = self.getNodeByName(src_sw)
-            self._set_vlan_tag(switch_node, src_sw_inport_name, vlan)
+            if action == 'add':
+                self._set_vlan_tag(switch_node, src_sw_inport_name, vlan, vnf_src_name, vnf_src_interface)
+            elif action == 'delete':
+                self._remove_vlan_tag(switch_node, src_sw_inport_name, vlan, vnf_src_name, vnf_src_interface)
+            else:
+                ret += "\nERROR: undefined action: {0}".format(action)
+                continue
+
+            ret+= "\n{0} vlan tag: {1} on {2}:{3}".format(action, vlan, vnf_src_name, vnf_src_interface)
+
+        return ret
 
     def _addMonitorFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None,
                        tag=None, **kwargs):
@@ -544,26 +560,29 @@ class DCNetwork(Containernet):
         :return: output log string
         """
 
+        # check if chain already exists (by checking if a vlan tag has already been assigned for this interface)
+        id_src = "{0}:{1}".format(vnf_src_name, vnf_src_interface)
+        tag_src = self.vlan_dict.get(id_src)
+        id_dst = "{0}:{1}".format(vnf_dst_name, vnf_dst_interface)
+        tag_dst = self.vlan_dict.get(id_dst)
+        if (tag_src == tag_dst) and (tag_src is not None) and (tag_dst is not None):
+            kwargs['tag'] = tag_src
+
         # special procedure for monitoring flows
         if kwargs.get('monitor'):
-
-            # check if chain already exists
-            found_chains = [chain_dict for chain_dict in self.installed_chains if
-             (chain_dict['vnf_src_name'] == vnf_src_name and chain_dict['vnf_src_interface'] == vnf_src_interface
-             and chain_dict['vnf_dst_name'] == vnf_dst_name and chain_dict['vnf_dst_interface'] == vnf_dst_interface)]
-
-            if len(found_chains) > 0:
-                # this chain exists, so need an extra monitoring flow
+            tag = kwargs.get['tag']
+            if tag is not None:
+                # this chain exists (part of E-LAN or E-Line), so need an extra monitoring flow, reusing the existing vlan
                 # assume only 1 chain per vnf/interface pair
                 LOG.debug('*** installing monitoring chain on top of pre-defined chain from {0}:{1} -> {2}:{3}'.
                             format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface))
-                tag = found_chains[0]['tag']
                 ret = self._addMonitorFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface,
                                      tag=tag, table_id=0, **kwargs)
                 return ret
             else:
-                # no chain existing (or E-LAN) -> install normal chain
-                LOG.warning('*** installing monitoring chain without pre-defined NSD chain from {0}:{1} -> {2}:{3}'.
+                # no chain existing (no common vlan tag) -> install normal chain
+                LOG.warning('*** installing monitoring chain without pre-defined NSD chain from {0}:{1} -> {2}:{3} '
+                            '(no matching vlan tags found, installing new chain)'.
                             format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface))
                 pass
 
@@ -661,16 +680,6 @@ class DCNetwork(Containernet):
             else:
                 vlan = self.vlans.pop()
 
-        # store the used vlan tag to identify this chain
-        if not kwargs.get('monitor'):
-            chain_dict = {}
-            chain_dict['vnf_src_name'] = vnf_src_name
-            chain_dict['vnf_dst_name'] = vnf_dst_name
-            chain_dict['vnf_src_interface'] = vnf_src_interface
-            chain_dict['vnf_dst_interface'] = vnf_dst_interface
-            chain_dict['tag'] = vlan
-            self.installed_chains.append(chain_dict)
-
         #iterate through the path to install the flow-entries
         for i in range(0,len(path)):
             current_node = self.getNodeByName(current_hop)
@@ -703,6 +712,10 @@ class DCNetwork(Containernet):
                 kwargs['switch_inport_name'] = src_sw_inport_name
                 kwargs['switch_outport_name'] = dst_sw_outport_name
                 kwargs['pathindex'] = i
+                kwargs['vnf_src_name'] = vnf_src_name
+                kwargs['vnf_src_interface'] = vnf_src_interface
+                kwargs['vnf_dst_name'] = vnf_dst_name
+                kwargs['vnf_dst_interface'] = vnf_dst_interface
 
                 if self.controller == RemoteController:
                     ## set flow entry via ryu rest api
@@ -766,10 +779,12 @@ class DCNetwork(Containernet):
             prefix = 'stats/flowentry/add'
             if vlan != None:
                 if index == 0:  # first node
-                    # set vlan tag in ovs instance (to isolate E-LANs)
+                    # set vlan tag in ovs instance (to isolate from E-LANs)
                     if not skip_vlan_tag:
                         in_port_name = kwargs.get('switch_inport_name')
-                        self._set_vlan_tag(node, in_port_name, vlan)
+                        vnf_src_name = kwargs.get('vnf_src_name')
+                        vnf_src_interface = kwargs.get('vnf_src_interface')
+                        self._set_vlan_tag(node, in_port_name, vlan, vnf_src_name, vnf_src_interface)
                     # set vlan push action if more than 1 switch in the path
                     if len(path) > 1:
                         action = {}
@@ -783,11 +798,13 @@ class DCNetwork(Containernet):
                         action['value'] = vlan | 0x1000
                         flow['actions'].append(action)
 
-                elif index == len(path) - 1:  # last node
-                    # set vlan tag in ovs instance (to isolate E-LANs)
+                if index == len(path) - 1:  # last node
+                    # set vlan tag in ovs instance (to isolate from E-LANs)
                     if not skip_vlan_tag:
                         out_port_name = kwargs.get('switch_outport_name')
-                        self._set_vlan_tag(node, out_port_name, vlan)
+                        vnf_dst_name = kwargs.get('vnf_dst_name')
+                        vnf_dst_interface = kwargs.get('vnf_dst_interface')
+                        self._set_vlan_tag(node, out_port_name, vlan, vnf_dst_name, vnf_dst_interface)
                     # set vlan pop action if more than 1 switch in the path
                     if len(path) > 1:
                         match += ',dl_vlan=%s' % vlan
@@ -819,10 +836,18 @@ class DCNetwork(Containernet):
         flow['match'] = self._parse_match(match)
         self.ryu_REST(prefix, data=flow)
 
-    def _set_vlan_tag(self, node, switch_port, tag):
+    def _set_vlan_tag(self, node, switch_port, tag, vnf_name, vnf_interface):
+        id = "{0}:{1}".format(vnf_name, vnf_interface)
+        self.vlan_dict[id]=tag
         node.vsctl('set', 'port {0} tag={1}'.format(switch_port,tag))
         LOG.debug("set vlan in switch: {0} in_port: {1} vlan tag: {2}".format(node.name, switch_port, tag))
 
+    def _remove_vlan_tag(self, node, switch_port, tag, vnf_name, vnf_interface):
+        id = "{0}:{1}".format(vnf_name, vnf_interface)
+        self.vlan_dict.pop(id)
+        node.vsctl('remove', 'port {0} tag {1}'.format(switch_port,tag))
+        LOG.debug("unset vlan in switch: {0} in_port: {1} vlan tag: {2}".format(node.name, switch_port, tag))
+
     def _set_flow_entry_dpctl(self, node, switch_inport_nr, switch_outport_nr, **kwargs):
 
         match = 'in_port=%s' % switch_inport_nr
index 77a71a0..9bc0b3e 100755 (executable)
@@ -28,6 +28,7 @@ partner consortium (www.sonata-nfv.eu).
 from mininet.node import Docker, OVSBridge
 from mininet.link import Link
 from emuvim.dcemulator.resourcemodel import NotEnoughResourcesAvailable
+import threading
 import logging
 
 
@@ -66,8 +67,11 @@ class EmulatorCompute(Docker):
             vnf_name = self.name
             vnf_interface = str(i)
             dc_port_name = self.datacenter.net.find_connected_dc_interface(vnf_name, vnf_interface)
+            id = "{0}:{1}".format(vnf_name, vnf_interface)
+            vlan_tag = self.datacenter.net.vlan_dict.get(id)
             # format list of tuples (name, Ip, MAC, isUp, status, dc_portname)
-            intf_dict = {'intf_name': str(i), 'ip': "{0}/{1}".format(i.IP(), i.prefixLen), 'netmask': i.prefixLen, 'mac': i.MAC(), 'up': i.isUp(), 'status': i.status(), 'dc_portname': dc_port_name}
+            intf_dict = {'intf_name': str(i), 'ip': "{0}/{1}".format(i.IP(), i.prefixLen), 'netmask': i.prefixLen,
+                         'mac': i.MAC(), 'up': i.isUp(), 'status': i.status(), 'dc_portname': dc_port_name, 'vlan': vlan_tag}
             networkStatusList.append(intf_dict)
 
         return networkStatusList
@@ -280,6 +284,19 @@ class Datacenter(object):
         # do bookkeeping
         self.containers[name] = d
 
+        # Execute SON_EMU_CMD
+        config = d.dcinfo.get("Config", dict())
+        env = config.get("Env", list())
+        for env_var in env:
+            var, cmd = map(str.strip, map(str, env_var.split('=', 1)))
+            LOG.debug("%r = %r" % (var, cmd))
+            if var == "SON_EMU_CMD":
+                LOG.info("Executing entry point script in %r: %r" % (d.name, cmd))
+                # execute command in new thread to ensure that GK is not blocked by VNF
+                t = threading.Thread(target=d.cmdPrint, args=(cmd,))
+                t.daemon = True
+                t.start()
+
         return d  # we might use UUIDs for naming later on
 
     def stopCompute(self, name):