Fix: Disable dummy GK chaining if no forwarding graph section is present in NSD
[osm/vim-emu.git] / src / emuvim / api / sonata / dummygatekeeper.py
index 85aa5e8..f37d2ff 100755 (executable)
@@ -48,6 +48,7 @@ from subprocess import Popen
 from random import randint
 import ipaddress
 import copy
+import time
 
 logging.basicConfig()
 LOG = logging.getLogger("sonata-dummy-gatekeeper")
@@ -101,6 +102,9 @@ ELINE_SUBNETS = generate_subnets('10.30', 0, subnet_size=50, mask=30)
 # path to the VNFD for the SAP VNF that is deployed as internal SAP point
 SAP_VNFD=None
 
+# Time in seconds to wait for vnf stop scripts to execute fully
+VNF_STOP_WAIT_TIME = 5
+
 class Gatekeeper(object):
 
     def __init__(self):
@@ -149,7 +153,6 @@ class Service(object):
         self.local_docker_files = dict()
         self.remote_docker_image_urls = dict()
         self.instances = dict()
-        #self.vnf_name2docker_name = dict()
         # dict to find the vnf_name for any vnf id
         self.vnf_id2vnf_name = dict()
 
@@ -208,7 +211,9 @@ class Service(object):
             self._start_sap(self.saps[sap], instance_uuid)
 
         # 5. Deploy E-Line and E_LAN links
-        if "virtual_links" in self.nsd:
+        # Attention: Only done if ""forwarding_graphs" section in NSD exists,
+        # even if "forwarding_graphs" are not used directly.
+        if "virtual_links" in self.nsd and "forwarding_graphs" in self.nsd:
             vlinks = self.nsd["virtual_links"]
             # constituent virtual links are not checked
             #fwd_links = self.nsd["forwarding_graphs"][0]["constituent_virtual_links"]
@@ -243,13 +248,17 @@ class Service(object):
         # instance_uuid = str(self.uuid.uuid4())
         vnf_instances = self.instances[instance_uuid]["vnf_instances"]
 
+        # trigger stop skripts in vnf instances and wait a few seconds for completion
+        self._trigger_emulator_stop_scripts_in_vnfis(vnf_instances)
+        time.sleep(VNF_STOP_WAIT_TIME)
+
         for v in vnf_instances:
             self._stop_vnfi(v)
 
         for sap_name in self.saps_ext:
             ext_sap = self.saps[sap_name]
             target_dc = ext_sap.get("dc")
-            target_dc.removeExternalSAP(sap_name, ext_sap['net'])
+            target_dc.removeExternalSAP(sap_name)
             LOG.info("Stopping the SAP instance: %r in DC %r" % (sap_name, target_dc))
 
         if not GK_STANDALONE_MODE:
@@ -260,7 +269,7 @@ class Service(object):
         # last step: remove the instance from the list of all instances
         del self.instances[instance_uuid]
 
-    def _start_vnfd(self, vnfd, vnf_id):
+    def _start_vnfd(self, vnfd, vnf_id, **kwargs):
         """
         Start a single VNFD of this service
         :param vnfd: vnfd descriptor dict
@@ -286,7 +295,9 @@ class Service(object):
             # 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:
+            if cpu_list is None:
+                cpu_list = res_req.get("cpu").get("vcpus")
+            if cpu_list is None:
                 cpu_list="1"
             cpu_bw = res_req.get("cpu").get("cpu_bw")
             if not cpu_bw:
@@ -351,7 +362,8 @@ class Service(object):
                     cpu_period=cpu_period,
                     cpuset=cpu_list,
                     mem_limit=mem_lim,
-                    volumes=volumes)
+                    volumes=volumes,
+                    type=kwargs.get('type','docker'))
 
             # rename the docker0 interfaces (eth0) to the management port name defined in the VNFD
             if USE_DOCKER_MGMT:
@@ -382,8 +394,6 @@ class Service(object):
         :return:
         """
         dn = vnf_id
-        #if vnf_id in self.vnf_name2docker_name:
-        #    dn = self.vnf_name2docker_name[name]
         for vnfi in self.instances[instance_uuid]["vnf_instances"]:
             if vnfi.name == dn:
                 return vnfi
@@ -432,6 +442,21 @@ class Service(object):
                     t.daemon = True
                     t.start()
 
+    def _trigger_emulator_stop_scripts_in_vnfis(self, vnfi_list):
+        for vnfi in vnfi_list:
+            config = vnfi.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)))
+                if var=="SON_EMU_CMD_STOP":
+                    LOG.info("Executing stop 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,))
+                    t.daemon = True
+                    t.start()
+
+
+
     def _unpack_service_package(self):
         """
         unzip *.son file and store contents in CATALOG_FOLDER/services/<service_uuid>/
@@ -547,7 +572,7 @@ class Service(object):
         if sap["type"] == "internal":
             vnfi = None
             if not GK_STANDALONE_MODE:
-                vnfi = self._start_vnfd(sap, sap['name'])
+                vnfi = self._start_vnfd(sap, sap['name'], type='sap_int')
             self.instances[instance_uuid]["vnf_instances"].append(vnfi)
 
         elif sap["type"] == "external":
@@ -584,8 +609,6 @@ class Service(object):
                 src_id = src_sap_id
                 # set intf name to None so the chaining function will choose the first one
                 src_if_name = None
-                #src_name = self.vnf_id2vnf_name[src_id]
-                #dst_name = self.vnf_id2vnf_name[dst_id]
                 dst_vnfi = self._get_vnf_instance(instance_uuid, dst_id)
                 if dst_vnfi is not None:
                     # choose first ip address in sap subnet
@@ -598,8 +621,6 @@ class Service(object):
                 dst_id = dst_sap_id
                 # set intf name to None so the chaining function will choose the first one
                 dst_if_name = None
-                #src_name = self.vnf_id2vnf_name[src_id]
-                #dst_name = self.vnf_id2vnf_name[dst_id]
                 src_vnfi = self._get_vnf_instance(instance_uuid, src_id)
                 if src_vnfi is not None:
                     sap_net = self.saps[dst_sap_id]['net']
@@ -614,8 +635,6 @@ class Service(object):
                     src_id = src_sap_id
                 if dst_sap_id in self.saps_int:
                     dst_id = dst_sap_id
-                #src_name = self.vnf_id2vnf_name[src_id]
-                #dst_name = self.vnf_id2vnf_name[dst_id]
                 # 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_id)
                 dst_vnfi = self._get_vnf_instance(instance_uuid, dst_id)
@@ -681,20 +700,18 @@ class Service(object):
                     src_docker_name = vnf_sap_id
                     vnf_id = vnf_sap_id
 
-                #vnf_name = self.vnf_id2vnf_name[vnf_id]
                 LOG.debug(
-                    "Setting up E-LAN interface. %s(%s:%s) -> %s" % (
+                    "Setting up E-LAN interface. (%s:%s) -> %s" % (
                         vnf_id, intf_name, ip_address))
 
-                if vnf_id 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_id)
-                    if vnfi is not None:
-                        self._vnf_reconfigure_network(vnfi, intf_name, ip_address)
-                        # add this vnf and interface to the E-LAN for tagging
-                        elan_vnf_list.append({'name': src_docker_name, 'interface': intf_name})
+                # 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_id)
+                if vnfi is not None:
+                    self._vnf_reconfigure_network(vnfi, intf_name, ip_address)
+                    # add this vnf and interface to the E-LAN for tagging
+                    elan_vnf_list.append({'name': src_docker_name, 'interface': intf_name})
 
             # install the VLAN tags for this E-LAN
             GK.net.setLAN(elan_vnf_list)
@@ -886,13 +903,11 @@ class RoundRobinDcPlacementWithSAPs(object):
 
             # check if there is a SAP in the link
             if src_sap_id in saps:
-                #dst_vnf_name = vnf_id2vnf_name[dst_id]
                 # get dc where connected vnf is mapped to
                 dc = vnfds[dst_id]['dc']
                 saps[src_sap_id]['dc'] = dc
 
             if dst_sap_id in saps:
-                #src_vnf_name = vnf_id2vnf_name[src_id]
                 # get dc where connected vnf is mapped to
                 dc = vnfds[src_id]['dc']
                 saps[dst_sap_id]['dc'] = dc