merge with latest upstream status
authorstevenvanrossem <steven.vanrossem@intec.ugent.be>
Fri, 27 Jan 2017 22:37:29 +0000 (23:37 +0100)
committerstevenvanrossem <steven.vanrossem@intec.ugent.be>
Fri, 27 Jan 2017 22:37:29 +0000 (23:37 +0100)
1  2 
Vagrantfile
ansible/install.yml
misc/sonata-demo-service.son
setup.py
src/emuvim/api/sonata/dummygatekeeper.py
src/emuvim/dcemulator/net.py
src/emuvim/dcemulator/node.py
src/emuvim/test/unittests/test_sonata_dummy_gatekeeper.py

diff --combined Vagrantfile
index b87a9f5,7c9c68f..7c9c68f
mode 100755,100644..100755
@@@ -111,7 -111,7 +111,7 @@@ Vagrant.configure(2) do |config
       sudo apt-get install -y git ansible aptitude
       sudo echo "localhost ansible_connection=local" >> /etc/ansible/hosts
       # install containernet
-      git clone https://github.com/mpeuster/containernet.git
+      git clone https://github.com/containernet/containernet.git
       echo "Installing containernet (will take some time ~30 minutes) ..."
       cd /home/vagrant/containernet/ansible
       sudo ansible-playbook install.yml
diff --combined ansible/install.yml
@@@ -57,8 -57,8 +57,8 @@@
     - name: install requests
       pip: name=requests state=latest
  
--   - name: install docker-py
--     pip: name=docker-py version=1.7.1
++   - name: install docker
++     pip: name=docker version=2.0.2
  
     - name: install prometheus_client
       pip: name=prometheus_client state=latest
index c4e58e2,c4e58e2..c4e58e2
mode 100644,100644..100755
Binary files differ
diff --combined setup.py
+++ b/setup.py
@@@ -52,7 -52,7 +52,7 @@@ setup(name='emuvim'
            'pytest',
            'Flask',
            'flask_restful',
--          'docker-py==1.7.1',
++          'docker==2.0.2',
            'requests',
            'prometheus_client',
            'urllib3'
@@@ -39,7 -39,7 +39,7 @@@ import hashli
  import zipfile
  import yaml
  import threading
 -from docker import Client as DockerClient
 +from docker import DockerClient
  from flask import Flask, request
  import flask_restful as fr
  from collections import defaultdict
@@@ -75,7 -75,6 +75,7 @@@ class Gatekeeper(object)
      def __init__(self):
          self.services = dict()
          self.dcs = dict()
 +        self.net = None
          self.vnf_counter = 0  # used to generate short names for VNFs (Mininet limitation)
          LOG.info("Create SONATA dummy gatekeeper.")
  
@@@ -176,98 -175,83 +176,99 @@@ class Service(object)
                  vnfi = self._start_vnfd(vnfd)
              self.instances[instance_uuid]["vnf_instances"].append(vnfi)
  
-         vlinks = self.nsd["virtual_links"]
-         fwd_links = self.nsd["forwarding_graphs"][0]["constituent_virtual_links"]
-         eline_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-Line")]
-         elan_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-LAN")]
-         GK.net.deployed_elines.extend(eline_fwd_links)
-         GK.net.deployed_elans.extend(elan_fwd_links)
-         # 4a. deploy E-Line links
-         # cookie is used as identifier for the flowrules installed by the dummygatekeeper
-         # eg. different services get a unique cookie for their flowrules
-         cookie = 1
-         for link in eline_fwd_links:
-             src_id, src_if_name = link["connection_points_reference"][0].split(":")
-             dst_id, dst_if_name = link["connection_points_reference"][1].split(":")
-             # check if there is a SAP in the link
-             if src_id in self.sap_identifiers:
-                 src_docker_name = "{0}_{1}".format(src_id, src_if_name)
-                 src_id = src_docker_name
-             else:
-                 src_docker_name = src_id
-             if dst_id in self.sap_identifiers:
-                 dst_docker_name = "{0}_{1}".format(dst_id, dst_if_name)
-                 dst_id = dst_docker_name
-             else:
-                 dst_docker_name = dst_id
-             src_name = vnf_id2vnf_name[src_id]
-             dst_name = vnf_id2vnf_name[dst_id]
-             LOG.debug(
-                 "Setting up E-Line link. %s(%s:%s) -> %s(%s:%s)" % (
-                     src_name, src_id, src_if_name, dst_name, dst_id, dst_if_name))
-             if (src_name in self.vnfds) and (dst_name in self.vnfds):
-                 network = self.vnfds[src_name].get("dc").net  # there should be a cleaner way to find the DCNetwork
-                 LOG.debug(src_docker_name)
-                 ret = network.setChain(
-                     src_docker_name, dst_docker_name,
-                     vnf_src_interface=src_if_name, vnf_dst_interface=dst_if_name,
-                     bidirectional=BIDIRECTIONAL_CHAIN, cmd="add-flow", cookie=cookie, priority=10)
-                 # re-configure the VNFs IP assignment and ensure that a new subnet is used for each E-Link
-                 src_vnfi = self._get_vnf_instance(instance_uuid, src_name)
-                 if src_vnfi is not None:
-                     self._vnf_reconfigure_network(src_vnfi, src_if_name, self.eline_subnets_src.pop(0))
-                 dst_vnfi = self._get_vnf_instance(instance_uuid, dst_name)
-                 if dst_vnfi is not None:
-                     self._vnf_reconfigure_network(dst_vnfi, dst_if_name, self.eline_subnets_dst.pop(0))
-         # 4b. deploy E-LAN links
-         base = 10
-         for link in elan_fwd_links:
-             elan_vnf_list=[]
-             # generate lan ip address
-             ip = 1
-             for intf in link["connection_points_reference"]:
-                 ip_address = generate_lan_string("10.0", base, subnet_size=24, ip=ip)
-                 vnf_id, intf_name = intf.split(":")
-                 if vnf_id in self.sap_identifiers:
-                     src_docker_name = "{0}_{1}".format(vnf_id, intf_name)
-                     vnf_id = src_docker_name
+         if "virtual_links" in self.nsd:
+             vlinks = self.nsd["virtual_links"]
+             fwd_links = self.nsd["forwarding_graphs"][0]["constituent_virtual_links"]
+             eline_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-Line")]
+             elan_fwd_links = [l for l in vlinks if (l["id"] in fwd_links) and (l["connectivity_type"] == "E-LAN")]
++            GK.net.deployed_elines.extend(eline_fwd_links)
++            GK.net.deployed_elans.extend(elan_fwd_links)
++
+             # 4a. deploy E-Line links
+             # cookie is used as identifier for the flowrules installed by the dummygatekeeper
+             # eg. different services get a unique cookie for their flowrules
+             cookie = 1
+             for link in eline_fwd_links:
+                 src_id, src_if_name = link["connection_points_reference"][0].split(":")
+                 dst_id, dst_if_name = link["connection_points_reference"][1].split(":")
+                 # check if there is a SAP in the link
+                 if src_id in self.sap_identifiers:
+                     src_docker_name = "{0}_{1}".format(src_id, src_if_name)
+                     src_id = src_docker_name
                  else:
-                     src_docker_name = vnf_id
-                 vnf_name = vnf_id2vnf_name[vnf_id]
-                 LOG.debug(
-                     "Setting up E-LAN link. %s(%s:%s) -> %s" % (
-                         vnf_name, vnf_id, intf_name, ip_address))
-                 if vnf_name in self.vnfds:
-                     # re-configure the VNFs IP assignment and ensure that a new subnet is used for each E-LAN
-                     # E-LAN relies on the learning switch capability of Ryu which has to be turned on in the topology
-                     # (DCNetwork(controller=RemoteController, enable_learning=True)), so no explicit chaining is necessary.
-                     vnfi = self._get_vnf_instance(instance_uuid, vnf_name)
-                     if vnfi is not None:
-                         self._vnf_reconfigure_network(vnfi, intf_name, ip_address)
-                         # increase for the next ip address on this E-LAN
-                         ip += 1
+                     src_docker_name = src_id
  
-                         # add this vnf and interface to the E-LAN for tagging
-                         network = self.vnfds[vnf_name].get("dc").net  # there should be a cleaner way to find the DCNetwork
-                         elan_vnf_list.append({'name':src_docker_name,'interface':intf_name})
+                 if dst_id in self.sap_identifiers:
+                     dst_docker_name = "{0}_{1}".format(dst_id, dst_if_name)
+                     dst_id = dst_docker_name
+                 else:
+                     dst_docker_name = dst_id
  
+                 src_name = vnf_id2vnf_name[src_id]
+                 dst_name = vnf_id2vnf_name[dst_id]
  
-             # install the VLAN tags for this E-LAN
-             network.setLAN(elan_vnf_list)
-             # increase the base ip address for the next E-LAN
-             base += 1
+                 LOG.debug(
+                     "Setting up E-Line link. %s(%s:%s) -> %s(%s:%s)" % (
+                         src_name, src_id, src_if_name, dst_name, dst_id, dst_if_name))
+                 if (src_name in self.vnfds) and (dst_name in self.vnfds):
+                     network = self.vnfds[src_name].get("dc").net  # there should be a cleaner way to find the DCNetwork
+                     LOG.debug(src_docker_name)
+                     ret = network.setChain(
+                         src_docker_name, dst_docker_name,
+                         vnf_src_interface=src_if_name, vnf_dst_interface=dst_if_name,
+                         bidirectional=BIDIRECTIONAL_CHAIN, cmd="add-flow", cookie=cookie, priority=10)
+                     # re-configure the VNFs IP assignment and ensure that a new subnet is used for each E-Link
+                     src_vnfi = self._get_vnf_instance(instance_uuid, src_name)
+                     if src_vnfi is not None:
+                         self._vnf_reconfigure_network(src_vnfi, src_if_name, self.eline_subnets_src.pop(0))
+                     dst_vnfi = self._get_vnf_instance(instance_uuid, dst_name)
+                     if dst_vnfi is not None:
+                         self._vnf_reconfigure_network(dst_vnfi, dst_if_name, self.eline_subnets_dst.pop(0))
+             # 4b. deploy E-LAN links
+             base = 10
+             for link in elan_fwd_links:
++
++                elan_vnf_list=[]
++
+                 # generate lan ip address
+                 ip = 1
+                 for intf in link["connection_points_reference"]:
+                     ip_address = generate_lan_string("10.0", base, subnet_size=24, ip=ip)
+                     vnf_id, intf_name = intf.split(":")
+                     if vnf_id in self.sap_identifiers:
+                         src_docker_name = "{0}_{1}".format(vnf_id, intf_name)
+                         vnf_id = src_docker_name
++                    else:
++                        src_docker_name = vnf_id
+                     vnf_name = vnf_id2vnf_name[vnf_id]
+                     LOG.debug(
+                         "Setting up E-LAN link. %s(%s:%s) -> %s" % (
+                             vnf_name, vnf_id, intf_name, ip_address))
+                     if vnf_name in self.vnfds:
+                         # re-configure the VNFs IP assignment and ensure that a new subnet is used for each E-LAN
+                         # E-LAN relies on the learning switch capability of Ryu which has to be turned on in the topology
+                         # (DCNetwork(controller=RemoteController, enable_learning=True)), so no explicit chaining is necessary.
+                         vnfi = self._get_vnf_instance(instance_uuid, vnf_name)
+                         if vnfi is not None:
+                             self._vnf_reconfigure_network(vnfi, intf_name, ip_address)
+                             # increase for the next ip address on this E-LAN
+                             ip += 1
++
++                            # add this vnf and interface to the E-LAN for tagging
++                            network = self.vnfds[vnf_name].get("dc").net  # there should be a cleaner way to find the DCNetwork
++                            elan_vnf_list.append({'name':src_docker_name,'interface':intf_name})
++
++
++                # install the VLAN tags for this E-LAN
++                network.setLAN(elan_vnf_list)
+                 # increase the base ip address for the next E-LAN
+                 base += 1
  
          # 5. run the emulator specific entrypoint scripts in the VNFIs of this service instance
          self._trigger_emulator_start_scripts_in_vnfis(self.instances[instance_uuid]["vnf_instances"])
              assert(target_dc is not None)
              if not self._check_docker_image_exists(docker_name):
                  raise Exception("Docker image %r not found. Abort." % docker_name)
-             # 3. do the dc.startCompute(name="foobar") call to run the container
+             # 3. get the resource limits
+             res_req = u.get("resource_requirements")
+             cpu_list = res_req.get("cpu").get("cores")
+             if not cpu_list or len(cpu_list)==0:
+                 cpu_list="1"
+             cpu_bw = res_req.get("cpu").get("cpu_bw")
+             if not cpu_bw:
+                 cpu_bw=1
+             mem_num = str(res_req.get("memory").get("size"))
+             if len(mem_num)==0:
+                 mem_num="2"
+             mem_unit = str(res_req.get("memory").get("size_unit"))
+             if str(mem_unit)==0:
+                 mem_unit="GB"
+             mem_limit = float(mem_num)
+             if mem_unit=="GB":
+                 mem_limit=mem_limit*1024*1024*1024
+             elif mem_unit=="MB":
+                 mem_limit=mem_limit*1024*1024
+             elif mem_unit=="KB":
+                 mem_limit=mem_limit*1024
+             mem_lim = int(mem_limit)
+             cpu_period, cpu_quota = self._calculate_cpu_cfs_values(float(cpu_bw))
+             # 4. do the dc.startCompute(name="foobar") call to run the container
              # TODO consider flavors, and other annotations
              intfs = vnfd.get("connection_points")
  
  
              LOG.info("Starting %r as %r in DC %r" % (vnf_name, self.vnf_name2docker_name[vnf_name], vnfd.get("dc")))
              LOG.debug("Interfaces for %r: %r" % (vnf_name, intfs))
-             vnfi = target_dc.startCompute(self.vnf_name2docker_name[vnf_name], network=intfs, image=docker_name, flavor_name="small")
+             vnfi = target_dc.startCompute(self.vnf_name2docker_name[vnf_name], network=intfs, image=docker_name, flavor_name="small",
+                     cpu_quota=cpu_quota, cpu_period=cpu_period, cpuset=cpu_list, mem_limit=mem_lim)
              return vnfi
  
      def _stop_vnfi(self, vnfi):
              config = vnfi.dcinfo.get("Config", dict())
              env = config.get("Env", list())
              for env_var in env:
-                 if "SON_EMU_CMD=" in env_var:
-                     cmd = str(env_var.split("=")[1])
+                 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" % (vnfi.name, cmd))
                      # execute command in new thread to ensure that GK is not blocked by VNF
                      t = threading.Thread(target=vnfi.cmdPrint, args=(cmd,))
                  self.package_content_path,
                  make_relative_path(self.manifest.get("entry_service_template")))
              self.nsd = load_yaml(nsd_path)
 +            GK.net.deployed_nsds.append(self.nsd)
              LOG.debug("Loaded NSD: %r" % self.nsd.get("name"))
  
      def _load_vnfd(self):
          dc = DockerClient()
          for url in self.remote_docker_image_urls.itervalues():
              if not FORCE_PULL:  # only pull if not present (speedup for development)
 -                if len(dc.images(name=url)) > 0:
 +                if len(dc.images.list(name=url)) > 0:
                      LOG.debug("Image %r present. Skipping pull." % url)
                      continue
              LOG.info("Pulling image: %r" % url)
          :param image_name: name of the docker image
          :return:
          """
 -        return len(DockerClient().images(image_name)) > 0
 +        return len(DockerClient().images.list(name=image_name)) > 0
  
      def _calculate_placement(self, algorithm):
          """
          for name, vnfd in self.vnfds.iteritems():
              LOG.info("Placed VNF %r on DC %r" % (name, str(vnfd.get("dc"))))
  
+     def _calculate_cpu_cfs_values(self, cpu_time_percentage):
+         """
+         Calculate cpu period and quota for CFS
+         :param cpu_time_percentage: percentage of overall CPU to be used
+         :return: cpu_period, cpu_quota
+         """
+         if cpu_time_percentage is None:
+             return -1, -1
+         if cpu_time_percentage < 0:
+             return -1, -1
+         # (see: https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt)
+         # Attention minimum cpu_quota is 1ms (micro)
+         cpu_period = 1000000  # lets consider a fixed period of 1000000 microseconds for now
+         LOG.debug("cpu_period is %r, cpu_percentage is %r" % (cpu_period, cpu_time_percentage))
+         cpu_quota = cpu_period * cpu_time_percentage  # calculate the fraction of cpu time for this container
+         # ATTENTION >= 1000 to avoid a invalid argument system error ... no idea why
+         if cpu_quota < 1000:
+             LOG.debug("cpu_quota before correcting: %r" % cpu_quota)
+             cpu_quota = 1000
+             LOG.warning("Increased CPU quota to avoid system error.")
+         LOG.debug("Calculated: cpu_period=%f / cpu_quota=%f" % (cpu_period, cpu_quota))
+         return int(cpu_period), int(cpu_quota)
  
  """
  Some (simple) placement algorithms
@@@ -676,8 -709,7 +727,7 @@@ class Instantiations(fr.Resource)
          if service_uuid in GK.services and instance_uuid in GK.services[service_uuid].instances:
              # valid service and instance UUID, stop service
              GK.services.get(service_uuid).stop_service(instance_uuid)
-             del GK.services.get(service_uuid).instances[instance_uuid]
-             return
+             return "service instance with uuid %r stopped." % instance_uuid,200
          return "Service not found", 404
  
  class Exit(fr.Resource):
          """
          Stop the running Containernet instance regardless of data transmitted
          """
-         GK.net.stop()
+         list(GK.dcs.values())[0].net.stop()
  
  
  def initialize_GK():
@@@ -715,7 -747,6 +765,7 @@@ api.add_resource(Exit, '/emulator/exit'
  
  def start_rest_api(host, port, datacenters=dict()):
      GK.dcs = datacenters
 +    GK.net = get_dc_network()
      # start the Flask server (not the best performance but ok for our use case)
      app.run(host=host,
              port=port,
@@@ -764,14 -795,6 +814,14 @@@ def generate_subnet_strings(n, start=1
          r.append("%d.0.0.%d/%d" % (i, ip, subnet_size))
      return r
  
 +def get_dc_network():
 +    """
 +    retrieve the DCnetwork where this dummygatekeeper (GK) connects to.
 +    Assume at least 1 datacenter is connected to this GK, and that all datacenters belong to the same DCNetwork
 +    :return:
 +    """
 +    assert (len(GK.dcs) > 0)
 +    return GK.dcs.values()[0].net
  
  if __name__ == '__main__':
      """
@@@ -32,7 -32,6 +32,7 @@@ import tim
  from subprocess import Popen
  import re
  import requests
 +import os
  
  from mininet.net import Containernet
  from mininet.node import Controller, DefaultController, OVSSwitch, OVSKernelSwitch, Docker, RemoteController
@@@ -47,9 -46,6 +47,9 @@@ from emuvim.dcemulator.resourcemodel im
  LOG = logging.getLogger("dcemulator.net")
  LOG.setLevel(logging.DEBUG)
  
 +# default CPU period used for cpu percentage-based cfs values (microseconds)
 +CPU_PERIOD = 1000000
 +
  class DCNetwork(Containernet):
      """
      Wraps the original Mininet/Containernet class and provides
@@@ -59,7 -55,7 +59,7 @@@
      """
  
      def __init__(self, controller=RemoteController, monitor=False,
 -                 enable_learning = True,   # in case of RemoteController (Ryu), learning switch behavior can be turned off/on
 +                 enable_learning=False, # learning switch behavior of the default ovs switches icw Ryu controller can be turned off/on, needed for E-LAN functionality
                   dc_emulation_max_cpu=1.0,  # fraction of overall CPU time for emulation
                   dc_emulation_max_mem=512,  # emulation max mem in MB
                   **kwargs):
          # members
          self.dcs = {}
          self.ryu_process = None
 +        #list of deployed nsds.E_Lines and E_LANs (uploaded from the dummy gatekeeper)
 +        self.deployed_nsds = []
 +        self.deployed_elines = []
 +        self.deployed_elans = []
 +        self.installed_chains = []
 +
  
          # always cleanup environment before we start the emulator
          self.killRyu()
          Containernet.__init__(
              self, switch=OVSKernelSwitch, controller=controller, **kwargs)
  
 +        # default switch configuration
 +        enable_ryu_learning = False
 +        if enable_learning :
 +            self.failMode = 'standalone'
 +            enable_ryu_learning = True
 +        else:
 +            self.failMode = 'secure'
 +
          # Ryu management
          if controller == RemoteController:
              # start Ryu controller
 -            self.startRyu(learning_switch=enable_learning)
 +            self.startRyu(learning_switch=enable_ryu_learning)
  
          # add the specified controller
          self.addController('c0', controller=controller)
          # initialize resource model registrar
          self.rm_registrar = ResourceModelRegistrar(
              dc_emulation_max_cpu, dc_emulation_max_mem)
 +        self.cpu_period = CPU_PERIOD
  
      def addDatacenter(self, label, metadata={}, resource_log_path=None):
          """
          """
          assert node1 is not None
          assert node2 is not None
 -        LOG.debug("addLink: n1=%s n2=%s" % (str(node1), str(node2)))
 +
          # ensure type of node1
          if isinstance( node1, basestring ):
              if node1 in self.dcs:
          attr_dict2.update(attr_dict)
          self.DCNetwork_graph.add_edge(node2.name, node1.name, attr_dict=attr_dict2)
  
 +        LOG.debug("addLink: n1={0} intf1={1} -- n2={2} intf2={3}".format(
 +            str(node1),node1_port_name, str(node2), node2_port_name))
 +
          return link
  
      def addDocker( self, label, **params ):
          """
          Wrapper for addSwitch method to store switch also in graph.
          """
 +
 +        # add this switch to the global topology overview
          if add_to_graph:
              self.DCNetwork_graph.add_node(name)
 -        return Containernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', **params)
 +
 +        # set the learning switch behavior
 +        if 'failMode' in params :
 +            failMode = params['failMode']
 +        else :
 +            failMode = self.failMode
 +
 +        s = Containernet.addSwitch(self, name, protocols='OpenFlow10,OpenFlow12,OpenFlow13', failMode=failMode, **params)
 +
 +        # set flow entry that enables learning switch behavior (needed to enable E-LAN functionality)
 +        #LOG.info('failmode {0}'.format(failMode))
 +        #if failMode == 'standalone' :
 +        #    LOG.info('add NORMAL')
 +        #    s.dpctl('add-flow', 'actions=NORMAL')
 +
 +        return s
  
      def getAllContainers(self):
          """
      def CLI(self):
          CLI(self)
  
 +    def setLAN(self, vnf_list):
 +        """
 +        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:},...]
 +        :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()
 +
 +        for vnf in vnf_list:
 +            vnf_src_name = vnf['name']
 +            vnf_src_interface = vnf['interface']
 +
 +            # 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]
 +                link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
 +                vnf_src_interface = link_dict[0]['src_port_id']
 +
 +            for connected_sw in self.DCNetwork_graph.neighbors(vnf_src_name):
 +                link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
 +                for link in link_dict:
 +                    if (link_dict[link]['src_port_id'] == vnf_src_interface or
 +                                link_dict[link]['src_port_name'] == vnf_src_interface):  # Fix: we might also get interface names, e.g, from a son-emu-cli call
 +                        # found the right link and connected switch
 +                        src_sw = connected_sw
 +                        src_sw_inport_nr = link_dict[link]['dst_port_nr']
 +                        src_sw_inport_name = link_dict[link]['dst_port_name']
 +                        break
 +
 +            # 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)
 +
 +    def _addMonitorFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None,
 +                       tag=None, **kwargs):
 +        """
 +        Add a monitoring flow entry that adds a special flowentry/counter at the begin or end of a chain.
 +        So this monitoring flowrule exists on top of a previously defined chain rule and uses the same vlan tag/routing.
 +        :param vnf_src_name:
 +        :param vnf_dst_name:
 +        :param vnf_src_interface:
 +        :param vnf_dst_interface:
 +        :param tag: vlan tag to be used for this chain (same tag as existing chain)
 +        :param monitor_placement: 'tx' or 'rx' indicating to place the extra flowentry resp. at the beginning or end of the chain
 +        :return:
 +        """
 +
 +        src_sw = None
 +        src_sw_inport_nr = 0
 +        src_sw_inport_name = None
 +        dst_sw = None
 +        dst_sw_outport_nr = 0
 +        dst_sw_outport_name = None
 +
 +        LOG.debug("call AddMonitorFlow vnf_src_name=%r, vnf_src_interface=%r, vnf_dst_name=%r, vnf_dst_interface=%r",
 +                  vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface)
 +
 +        #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]
 +            link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
 +            vnf_src_interface = link_dict[0]['src_port_id']
 +
 +        for connected_sw in self.DCNetwork_graph.neighbors(vnf_src_name):
 +            link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
 +            for link in link_dict:
 +                if (link_dict[link]['src_port_id'] == vnf_src_interface or
 +                        link_dict[link]['src_port_name'] == vnf_src_interface):  # Fix: we might also get interface names, e.g, from a son-emu-cli call
 +                    # found the right link and connected switch
 +                    src_sw = connected_sw
 +                    src_sw_inport_nr = link_dict[link]['dst_port_nr']
 +                    src_sw_inport_name = link_dict[link]['dst_port_name']
 +                    break
 +
 +        if vnf_dst_interface is None:
 +            # take first interface by default
 +            connected_sw = 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']
 +
 +        vnf_dst_name = vnf_dst_name.split(':')[0]
 +        for connected_sw in self.DCNetwork_graph.neighbors(vnf_dst_name):
 +            link_dict = self.DCNetwork_graph[connected_sw][vnf_dst_name]
 +            for link in link_dict:
 +                if link_dict[link]['dst_port_id'] == vnf_dst_interface or \
 +                        link_dict[link]['dst_port_name'] == vnf_dst_interface:  # Fix: we might also get interface names, e.g, from a son-emu-cli call
 +                    # found the right link and connected switch
 +                    dst_sw = connected_sw
 +                    dst_sw_outport_nr = link_dict[link]['src_port_nr']
 +                    dst_sw_outport_name = link_dict[link]['src_port_name']
 +                    break
 +
 +        if not tag >= 0:
 +            LOG.exception('tag not valid: {0}'.format(tag))
 +
 +        # get shortest path
 +        try:
 +            # returns the first found shortest path
 +            # if all shortest paths are wanted, use: all_shortest_paths
 +            path = nx.shortest_path(self.DCNetwork_graph, src_sw, dst_sw, weight=kwargs.get('weight'))
 +        except:
 +            LOG.exception("No path could be found between {0} and {1} using src_sw={2} and dst_sw={3}".format(
 +                vnf_src_name, vnf_dst_name, src_sw, dst_sw))
 +            LOG.debug("Graph nodes: %r" % self.DCNetwork_graph.nodes())
 +            LOG.debug("Graph edges: %r" % self.DCNetwork_graph.edges())
 +            for e, v in self.DCNetwork_graph.edges():
 +                LOG.debug("%r" % self.DCNetwork_graph[e][v])
 +            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(vnf_src_name, vnf_dst_name, path))
 +
 +        current_hop = src_sw
 +        switch_inport_nr = src_sw_inport_nr
 +
 +        cmd = kwargs.get('cmd')
 +
 +        #iterate through the path to install the flow-entries
 +        for i in range(0,len(path)):
 +            current_node = self.getNodeByName(current_hop)
 +
 +            if path.index(current_hop) < len(path)-1:
 +                next_hop = path[path.index(current_hop)+1]
 +            else:
 +                #last switch reached
 +                next_hop = vnf_dst_name
 +
 +            next_node = self.getNodeByName(next_hop)
 +
 +            if next_hop == vnf_dst_name:
 +                switch_outport_nr = dst_sw_outport_nr
 +                LOG.info("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)
 +            else:
 +                # take first link between switches by default
 +                index_edge_out = 0
 +                switch_outport_nr = self.DCNetwork_graph[current_hop][next_hop][index_edge_out]['src_port_nr']
 +
 +
 +           # set of entry via ovs-ofctl
 +            if isinstance( current_node, OVSSwitch ):
 +                kwargs['vlan'] = tag
 +                kwargs['path'] = path
 +                kwargs['current_hop'] = current_hop
 +                kwargs['switch_inport_name'] = src_sw_inport_name
 +                kwargs['switch_outport_name'] = dst_sw_outport_name
 +                kwargs['skip_vlan_tag'] = True
 +
 +                monitor_placement = kwargs.get('monitor_placement').strip()
 +                # put monitor flow at the dst switch
 +                insert_flow = False
 +                if monitor_placement == 'tx' and path.index(current_hop) == 0:  # first node:
 +                    insert_flow = True
 +                # put monitoring flow at the src switch
 +                elif monitor_placement == 'rx' and path.index(current_hop) == len(path) - 1:  # last node:
 +                    insert_flow = True
 +                elif monitor_placement not in ['rx', 'tx']:
 +                    LOG.exception('invalid monitor command: {0}'.format(monitor_placement))
 +
 +
 +                if self.controller == RemoteController and insert_flow:
 +                    ## set flow entry via ryu rest api
 +                    self._set_flow_entry_ryu_rest(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
 +                    break
 +                elif insert_flow:
 +                    ## set flow entry via ovs-ofctl
 +                    self._set_flow_entry_dpctl(current_node, switch_inport_nr, switch_outport_nr, **kwargs)
 +                    break
 +
 +            # take first link between switches by default
 +            if isinstance( next_node, OVSSwitch ):
 +                switch_inport_nr = self.DCNetwork_graph[current_hop][next_hop][0]['dst_port_nr']
 +                current_hop = next_hop
 +
 +        return "path {2} between {0} and {1}".format(vnf_src_name, vnf_dst_name, cmd)
 +
 +
      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.
          :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
++<<<<<<< HEAD
 +        :param monitor: boolean to indicate whether this chain is a monitoring chain
 +        :param tag: vlan tag to be used for this chain (pre-defined or new one if none is specified)
++=======
+         :param path: custom path between the two VNFs (list of switches)
++>>>>>>> upstream/master
          :return: output log string
          """
 +
 +        # 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
 +                # 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}'.
 +                            format(vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface))
 +                pass
 +
 +
          cmd = kwargs.get('cmd')
 -        if cmd == 'add-flow':
 +        if cmd == 'add-flow' or cmd == 'del-flows':
              ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs)
              if kwargs.get('bidirectional'):
+                 if kwargs.get('path') is not None:
+                     kwargs['path'] = list(reversed(kwargs.get('path')))
                  ret = ret +'\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs)
  
 -        elif cmd == 'del-flows':
 -            ret = self._chainAddFlow(vnf_src_name, vnf_dst_name, vnf_src_interface, vnf_dst_interface, **kwargs)
 -            if kwargs.get('bidirectional'):
 -                if kwargs.get('path') is not None:
 -                    kwargs['path'] = list(reversed(kwargs.get('path')))
 -                ret = ret + '\n' + self._chainAddFlow(vnf_dst_name, vnf_src_name, vnf_dst_interface, vnf_src_interface, **kwargs)
 -
          else:
              ret = "Command unknown"
  
      def _chainAddFlow(self, vnf_src_name, vnf_dst_name, vnf_src_interface=None, vnf_dst_interface=None, **kwargs):
  
          src_sw = None
 -        dst_sw = None
          src_sw_inport_nr = 0
 +        src_sw_inport_name = None
 +        dst_sw = None
          dst_sw_outport_nr = 0
 +        dst_sw_outport_name = None
  
          LOG.debug("call chainAddFlow vnf_src_name=%r, vnf_src_interface=%r, vnf_dst_name=%r, vnf_dst_interface=%r",
                    vnf_src_name, vnf_src_interface, vnf_dst_name, vnf_dst_interface)
                      # found the right link and connected switch
                      src_sw = connected_sw
                      src_sw_inport_nr = link_dict[link]['dst_port_nr']
 +                    src_sw_inport_name = link_dict[link]['dst_port_name']
                      break
  
          if vnf_dst_interface is None:
                      # found the right link and connected switch
                      dst_sw = connected_sw
                      dst_sw_outport_nr = link_dict[link]['src_port_nr']
 +                    dst_sw_outport_name = link_dict[link]['src_port_name']
                      break
  
-         # get shortest path
-         try:
-             # returns the first found shortest path
-             # if all shortest paths are wanted, use: all_shortest_paths
-             path = nx.shortest_path(self.DCNetwork_graph, src_sw, dst_sw, weight=kwargs.get('weight'))
-         except:
-             LOG.exception("No path could be found between {0} and {1} using src_sw={2} and dst_sw={3}".format(
-                 vnf_src_name, vnf_dst_name, src_sw, dst_sw))
-             LOG.debug("Graph nodes: %r" % self.DCNetwork_graph.nodes())
-             LOG.debug("Graph edges: %r" % self.DCNetwork_graph.edges())
-             for e, v in self.DCNetwork_graph.edges():
-                 LOG.debug("%r" % self.DCNetwork_graph[e][v])
-             return "No path could be found between {0} and {1}".format(vnf_src_name, vnf_dst_name)
+         path = kwargs.get('path')
+         if path is None:
+             # get shortest path
+             try:
+                 # returns the first found shortest path
+                 # if all shortest paths are wanted, use: all_shortest_paths
+                 path = nx.shortest_path(self.DCNetwork_graph, src_sw, dst_sw, weight=kwargs.get('weight'))
+             except:
+                 LOG.exception("No path could be found between {0} and {1} using src_sw={2} and dst_sw={3}".format(
+                     vnf_src_name, vnf_dst_name, src_sw, dst_sw))
+                 LOG.debug("Graph nodes: %r" % self.DCNetwork_graph.nodes())
+                 LOG.debug("Graph edges: %r" % self.DCNetwork_graph.edges())
+                 for e, v in self.DCNetwork_graph.edges():
+                     LOG.debug("%r" % self.DCNetwork_graph[e][v])
+                 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(vnf_src_name, vnf_dst_name, path))
  
          current_hop = src_sw
          switch_inport_nr = src_sw_inport_nr
  
 -        # choose free vlan if path contains more than 1 switch
 +        # choose free vlan
 +        ## if path contains more than 1 switch
          cmd = kwargs.get('cmd')
          vlan = None
          if cmd == 'add-flow':
 -            if len(path) > 1:
 +            if kwargs.get('tag'):
 +                # use pre-defined tag
 +                vlan = kwargs.get('tag')
 +            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)
  
-             if path.index(current_hop) < len(path)-1:
-                 next_hop = path[path.index(current_hop)+1]
+             if i < len(path) - 1:
+                 next_hop = path[i + 1]
              else:
-                 #last switch reached
+                 # last switch reached
                  next_hop = vnf_dst_name
  
              next_node = self.getNodeByName(next_hop)
              if isinstance( current_node, OVSSwitch ):
                  kwargs['vlan'] = vlan
                  kwargs['path'] = path
 +                kwargs['current_hop'] = current_hop
 +                kwargs['switch_inport_name'] = src_sw_inport_name
 +                kwargs['switch_outport_name'] = dst_sw_outport_name
+                 kwargs['pathindex'] = i
  
                  if self.controller == RemoteController:
                      ## set flow entry via ryu rest api
                      ## 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 ):
                  switch_inport_nr = self.DCNetwork_graph[current_hop][next_hop][0]['dst_port_nr']
          match_input = kwargs.get('match')
          cmd = kwargs.get('cmd')
          path = kwargs.get('path')
-         current_hop = kwargs.get('current_hop')
+         index = kwargs.get('pathindex')
          vlan = kwargs.get('vlan')
          priority = kwargs.get('priority')
 +        # flag to not set the ovs port vlan tag
 +        skip_vlan_tag = kwargs.get('skip_vlan_tag')
 +        # table id to put this flowentry
 +        table_id = kwargs.get('table_id')
 +        if not table_id:
 +            table_id = 0
  
          s = ','
          if match_input:
          if priority:
              flow['priority'] = int(priority)
  
 +        flow['table_id'] = table_id
 +
          flow['actions'] = []
  
          # possible Ryu actions, match fields:
          if cmd == 'add-flow':
              prefix = 'stats/flowentry/add'
              if vlan != None:
-                 if path.index(current_hop) == 0:  # first node
+                 if index == 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'
 -                    # ryu expects the field to be masked
 -                    action['value'] = vlan | 0x1000
 -                    flow['actions'].append(action)
 -                elif index == len(path) -1:  # last node
 -                    match += ',dl_vlan=%s' % vlan
 -                    action = {}
 -                    action['type'] = 'POP_VLAN'
 -                    flow['actions'].append(action)
 +                    # set vlan tag in ovs instance (to isolate E-LANs)
 +                    if not skip_vlan_tag:
 +                        in_port_name = kwargs.get('switch_inport_name')
 +                        self._set_vlan_tag(node, in_port_name, vlan)
 +                    # set vlan push action if more than 1 switch in the path
 +                    if len(path) > 1:
 +                        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
++                        # ryu expects the field to be masked
++                        action['value'] = vlan | 0x1000
 +                        flow['actions'].append(action)
 +
-                 if path.index(current_hop) == len(path) - 1:  # last node
++                elif index == len(path) - 1:  # last node
 +                    # set vlan tag in ovs instance (to isolate E-LANs)
 +                    if not skip_vlan_tag:
 +                        out_port_name = kwargs.get('switch_outport_name')
 +                        self._set_vlan_tag(node, out_port_name, vlan)
 +                    # set vlan pop action if more than 1 switch in the path
 +                    if len(path) > 1:
 +                        match += ',dl_vlan=%s' % vlan
 +                        action = {}
 +                        action['type'] = 'POP_VLAN'
 +                        flow['actions'].append(action)
 +
-                 if 0 < path.index(current_hop) < (len(path) - 1):  # middle nodes
+                 else:  # middle nodes
                      match += ',dl_vlan=%s' % vlan
 +
              # output action must come last
              action = {}
              action['type'] = 'OUTPUT'
          flow['match'] = self._parse_match(match)
          self.ryu_REST(prefix, data=flow)
  
 +    def _set_vlan_tag(self, node, switch_port, 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 _set_flow_entry_dpctl(self, node, switch_inport_nr, switch_outport_nr, **kwargs):
 +
          match = 'in_port=%s' % switch_inport_nr
  
          cookie = kwargs.get('cookie')
          match_input = kwargs.get('match')
          cmd = kwargs.get('cmd')
          path = kwargs.get('path')
-         current_hop = kwargs.get('current_hop')
+         index = kwargs.get('pathindex')
          vlan = kwargs.get('vlan')
  
          s = ','
          if cmd == 'add-flow':
              action = 'action=%s' % switch_outport_nr
              if vlan != None:
-                 if path.index(current_hop) == 0:  # first node
+                 if index == 0: # first node
                      action = ('action=mod_vlan_vid:%s' % vlan) + (',output=%s' % switch_outport_nr)
                      match = '-O OpenFlow13 ' + match
-                 elif path.index(current_hop) == len(path) - 1:  # last node
+                 elif index == len(path) - 1:  # last node
                      match += ',dl_vlan=%s' % vlan
                      action = 'action=strip_vlan,output=%s' % switch_outport_nr
                  else:  # middle nodes
      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'
 +        # ryu default learning switch
 +        #ryu_path = 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'
          # change the default Openflow controller port to 6653 (official IANA-assigned port number), as used by Mininet
          # Ryu still uses 6633 as default
          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))
          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))
          time.sleep(1)
  
      def killRyu(self):
                  dict.update({match[0]:m2})
          return dict
  
 +    def find_connected_dc_interface(self, vnf_src_name, vnf_src_interface):
 +        for connected_sw in self.DCNetwork_graph.neighbors(vnf_src_name):
 +            link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
 +            for link in link_dict:
 +                if (link_dict[link]['src_port_id'] == vnf_src_interface or
 +                        link_dict[link]['src_port_name'] == vnf_src_interface):  # Fix: we might also get interface names, e.g, from a son-emu-cli call
 +                    # found the right link and connected switch
 +                    src_sw = connected_sw
 +                    src_sw_inport_nr = link_dict[link]['dst_port_nr']
 +                    src_sw_inport_name = link_dict[link]['dst_port_name']
 +                    return src_sw_inport_name
@@@ -38,6 -38,7 +38,6 @@@ LOG.setLevel(logging.DEBUG
  
  DCDPID_BASE = 1000  # start of switch dpid's used for data center switches
  
 -
  class EmulatorCompute(Docker):
      """
      Emulator specific compute node class.
          Helper method to receive information about the virtual networks
          this compute instance is connected to.
          """
 -        # format list of tuples (name, Ip, MAC, isUp, status)
 -        return [{'intf_name':str(i), 'ip':i.IP(), 'mac':i.MAC(), 'up':i.isUp(), 'status':i.status()}
 -                for i in self.intfList()]
 +        # get all links and find dc switch interface
 +        networkStatusList = []
 +        for i in self.intfList():
 +            vnf_name = self.name
 +            vnf_interface = str(i)
 +            dc_port_name = self.datacenter.net.find_connected_dc_interface(vnf_name, vnf_interface)
 +            # format list of tuples (name, Ip, MAC, isUp, status, dc_portname)
 +            intf_dict = {'intf_name': str(i), 'ip': i.IP(), 'mac': i.MAC(), 'up': i.isUp(), 'status': i.status(), 'dc_portname': dc_port_name}
 +            networkStatusList.append(intf_dict)
 +
 +        return networkStatusList
  
      def getStatus(self):
          """
@@@ -90,7 -83,6 +90,7 @@@
          status["memswap_limit"] = self.memswap_limit
          status["state"] = self.dcli.inspect_container(self.dc)["State"]
          status["id"] = self.dcli.inspect_container(self.dc)["Id"]
 +        status["short_id"] = self.dcli.inspect_container(self.dc)["Id"][:12]
          status["datacenter"] = (None if self.datacenter is None
                                  else self.datacenter.label)
          return status
@@@ -113,7 -105,7 +113,7 @@@ class Datacenter(object)
          self.name = "dc%d" % Datacenter.DC_COUNTER
          Datacenter.DC_COUNTER += 1
          # use this for user defined names that can be longer than self.name
-         self.label = label  
+         self.label = label
          # dict to store arbitrary metadata (e.g. latitude and longitude)
          self.metadata = metadata
          # path to which resource information should be logged (e.g. for experiments). None = no logging
      def start(self):
          pass
  
-     def startCompute(self, name, image=None, command=None, network=None, flavor_name="tiny", **kwargs):
+     def startCompute(self, name, image=None, command=None, network=None, flavor_name="tiny", **params):
          """
          Create a new container as compute resource and connect it to this
          data center.
              if len(network) < 1:
                  network.append({})
  
 +        # apply hard-set resource limits=0
 +        cpu_percentage = kwargs.get('cpu_percent')
 +        if cpu_percentage:
 +            cpu_period = self.net.cpu_period
 +            cpu_quota = self.net.cpu_period * float(cpu_percentage)
 +        else:
 +            cpu_quota = None
 +            cpu_period = None
 +
          # create the container
          d = self.net.addDocker(
              "%s" % (name),
              dcmd=command,
              datacenter=self,
              flavor_name=flavor_name,
 +            cpu_period = cpu_period,
 +            cpu_quota = cpu_quota,
 +            environment = {'VNF_NAME':name}
+             **params
          )
  
 +
 +
          # apply resource limits to container if a resource model is defined
          if self._resource_model is not None:
              try:
@@@ -44,7 -44,7 +44,7 @@@ class testSonataDummyGatekeeper(SimpleT
  #    @unittest.skip("disabled")
      def test_GK_Api_start_service(self):
          # create network
 -        self.createNet(nswitches=0, ndatacenter=2, nhosts=2, ndockers=0)
 +        self.createNet(nswitches=0, ndatacenter=2, nhosts=2, ndockers=0, enable_learning=True)
          # setup links
          self.net.addLink(self.dc[0], self.h[0])
          self.net.addLink(self.dc[0], self.dc[1])
          # check compute list result
          self.assertEqual(len(self.dc[0].listCompute()), 2)
          # check connectivity by using ping
 +        ELAN_list=[]
 +        for i in [0]:
 +            for vnf in self.dc[i].listCompute():
 +                # check connection
 +                p = self.net.ping([self.h[i], vnf])
 +                print p
 +                self.assertTrue(p <= 0.0)
 +
 +                # check E LAN connection
 +                network_list = vnf.getNetworkStatus()
 +                mgmt_ip = [intf['ip'] for intf in network_list if intf['intf_name'] == 'mgmt']
 +                self.assertTrue(len(mgmt_ip) > 0)
 +                ip_address = mgmt_ip[0]
 +                ELAN_list.append(ip_address)
 +                print ip_address
 +
 +        # check ELAN connection by ping over the mgmt network (needs to be configured as ELAN in the test service)
          for vnf in self.dc[0].listCompute():
 -            p = self.net.ping([self.h[0], vnf])
 -            self.assertTrue(p <= 50.0)
 +            network_list = vnf.getNetworkStatus()
 +            mgmt_ip = [intf['ip'] for intf in network_list if intf['intf_name'] == 'mgmt']
 +            self.assertTrue(len(mgmt_ip) > 0)
 +            ip_address = mgmt_ip[0]
 +            print ELAN_list
 +            print ip_address
 +            test_ip_list = list(ELAN_list)
 +            test_ip_list.remove(ip_address)
 +            for ip in test_ip_list:
 +                p = self.net.ping([vnf],manualdestip=ip)
 +                print p
 +                self.assertTrue(p <= 0.0)
 +
          # stop Mininet network
          self.stopNet()
          initialize_GK()
  
+ #    @unittest.skip("disabled")
      def test_GK_Api_stop_service(self):
          # create network
          self.createNet(ndatacenter=2, nhosts=2)
          # stop Mininet network
          self.stopNet()
          initialize_GK()
+     def test_GK_stress_service(self):
+         # create network
+         self.createNet(ndatacenter=2, nhosts=2)
+         # connect dummy GK to data centers
+         sdkg1 = SonataDummyGatekeeperEndpoint("0.0.0.0", 5000)
+         sdkg1.connectDatacenter(self.dc[0])
+         sdkg1.connectDatacenter(self.dc[1])
+         # run the dummy gatekeeper (in another thread, don't block)
+         sdkg1.start()
+         # start Mininet network
+         self.startNet()
+         time.sleep(1)
+         print "starting tests"
+         # board package
+         files = {"package": open("misc/sonata-stress-service.son", "rb")}
+         r = requests.post("http://127.0.0.1:5000/packages", files=files)
+         self.assertEqual(r.status_code, 201)
+         self.assertTrue(json.loads(r.text).get("service_uuid") is not None)
+         # instantiate service
+         self.service_uuid = json.loads(r.text).get("service_uuid")
+         r2 = requests.post("http://127.0.0.1:5000/instantiations", data=json.dumps({"service_uuid": self.service_uuid}))
+         self.assertEqual(r2.status_code, 201)
+         # give the emulator some time to instantiate everything
+         time.sleep(2)
+         # check get request APIs
+         r3 = requests.get("http://127.0.0.1:5000/packages")
+         self.assertEqual(len(json.loads(r3.text).get("service_uuid_list")), 1)
+         r4 = requests.get("http://127.0.0.1:5000/instantiations")
+         self.assertEqual(len(json.loads(r4.text).get("service_instantiations_list")), 1)
+         # stop the service
+         service_instance_uuid = json.loads(r2.text).get("service_instance_uuid")
+         self.assertTrue(service_instance_uuid is not None)
+         requests.delete("http://127.0.0.1:5000/instantiations", data=json.dumps({"service_uuid": self.service_uuid, "service_instance_uuid":service_instance_uuid}))
+         r5 = requests.get("http://127.0.0.1:5000/instantiations")
+         self.assertTrue(len(json.loads(r5.text).get("service_instantiations_list")), 0)     # note that there was 1 instance before
+         # stop Mininet network
+         self.stopNet()
+         initialize_GK()