From d7b2774c554290855d0bb17b87168f095c1f3431 Mon Sep 17 00:00:00 2001 From: Steven Van Rossem Date: Tue, 31 Oct 2017 13:28:37 +0100 Subject: [PATCH] improve vlan tagging in emulator chaining --- src/emuvim/dashboard/js/main.js | 3 +- src/emuvim/dcemulator/net.py | 89 +++++++++++++++++++++------------ src/emuvim/dcemulator/node.py | 19 ++++++- 3 files changed, 77 insertions(+), 34 deletions(-) diff --git a/src/emuvim/dashboard/js/main.js b/src/emuvim/dashboard/js/main.js index 3fdd488..b30a40c 100755 --- a/src/emuvim/dashboard/js/main.js +++ b/src/emuvim/dashboard/js/main.js @@ -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('
datacenter portinterfaceipmac
') + $("#table_network").append('
datacenter portinterfaceipmacvlan
') // update lateness counter LAST_UPDATE_TIMESTAMP_CONTAINER = Date.now(); } @@ -99,6 +99,7 @@ function build_network_table(network_list, id) row_str += '' + interface.intf_name + ''; row_str += '' + interface.ip + ''; row_str += '' + interface.mac + ''; + row_str += '' + interface.vlan + ''; row_str += ''; }); $("#network_list_" + id).append(row_str) diff --git a/src/emuvim/dcemulator/net.py b/src/emuvim/dcemulator/net.py index ea9fd1c..2cc005e 100755 --- a/src/emuvim/dcemulator/net.py +++ b/src/emuvim/dcemulator/net.py @@ -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 diff --git a/src/emuvim/dcemulator/node.py b/src/emuvim/dcemulator/node.py index 77a71a0..9bc0b3e 100755 --- a/src/emuvim/dcemulator/node.py +++ b/src/emuvim/dcemulator/node.py @@ -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): -- 2.25.1