use fixed cookie value: 1
[osm/vim-emu.git] / src / emuvim / api / sonata / dummygatekeeper.py
index 20150d0..916e168 100755 (executable)
@@ -105,7 +105,10 @@ class Service(object):
         self.local_docker_files = dict()
         self.remote_docker_image_urls = dict()
         self.instances = dict()
-        self.vnfname2num = dict()
+        self.vnf_name2docker_name = dict()
+        # lets generate a set of subnet configurations used for e-line chaining setup
+        self.eline_subnets_src = generate_subnet_strings(50, start=200, subnet_size=24, ip=1)
+        self.eline_subnets_dst = generate_subnet_strings(50, start=200, subnet_size=24, ip=2)
 
     def onboard(self):
         """
@@ -154,30 +157,46 @@ class Service(object):
             self.instances[instance_uuid]["vnf_instances"].append(vnfi)
 
         # 3. Configure the chaining of the network functions (currently only E-Line links supported)
-        nfid2name = defaultdict(lambda :"NotExistingNode", 
-                                reduce(lambda x,y: dict(x, **y),
-                                       map(lambda d:{d["vnf_id"]:d["vnf_name"]},
+        vnf_id2vnf_name = defaultdict(lambda: "NotExistingNode",
+                                reduce(lambda x, y: dict(x, **y),
+                                       map(lambda d: {d["vnf_id"]: d["vnf_name"]},
                                            self.nsd["network_functions"])))
         
         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")]
 
-        cookie = 1 # not clear why this is needed - to check with Steven
+        # 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_node, src_port = link["connection_points_reference"][0].split(":")
-            dst_node, dst_port = link["connection_points_reference"][1].split(":")
-
-            srcname = nfid2name[src_node]
-            dstname = nfid2name[dst_node]
-            LOG.debug("src name: "+srcname+" dst name: "+dstname)
-
-            if (srcname in self.vnfds) and (dstname in self.vnfds) :
-                network = self.vnfds[srcname].get("dc").net  # there should be a cleaner way to find the DCNetwork
-                src_vnf = self.vnfname2num[srcname]
-                dst_vnf = self.vnfname2num[dstname]
-                ret = network.setChain(src_vnf, dst_vnf, vnf_src_interface=src_port, vnf_dst_interface=dst_port, bidirectional = True, cmd="add-flow", cookie = cookie)
-                cookie += 1
+            src_id, src_if_name = link["connection_points_reference"][0].split(":")
+            dst_id, dst_if_name = link["connection_points_reference"][1].split(":")
+
+            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
+                src_docker_name = self.vnf_name2docker_name[src_name]
+                dst_docker_name = self.vnf_name2docker_name[dst_name]
+                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=True, cmd="add-flow", cookie=cookie)
+
+                # 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))
 
         # 4. 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"])
@@ -207,11 +226,55 @@ class Service(object):
             # 3. do the dc.startCompute(name="foobar") call to run the container
             # TODO consider flavors, and other annotations
             intfs = vnfd.get("connection_points")
-            self.vnfname2num[vnf_name] = GK.get_next_vnf_name()
-            LOG.info("VNF "+vnf_name+" mapped to "+self.vnfname2num[vnf_name]+" on dc "+str(vnfd.get("dc")))
-            vnfi = target_dc.startCompute(self.vnfname2num[vnf_name], network=intfs, image=docker_name, flavor_name="small")
+
+            # use the vnf_id in the nsd as docker name
+            # so deployed containers can be easily mapped back to the nsd
+            vnf_name2id = defaultdict(lambda: "NotExistingNode",
+                                          reduce(lambda x, y: dict(x, **y),
+                                                 map(lambda d: {d["vnf_name"]: d["vnf_id"]},
+                                                     self.nsd["network_functions"])))
+            self.vnf_name2docker_name[vnf_name] = vnf_name2id[vnf_name]
+            # self.vnf_name2docker_name[vnf_name] = GK.get_next_vnf_name()
+
+            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")
             return vnfi
 
+    def _get_vnf_instance(self, instance_uuid, name):
+        """
+        Returns the Docker object for the given VNF name (or Docker name).
+        :param instance_uuid: UUID of the service instance to search in.
+        :param name: VNF name or Docker name. We are fuzzy here.
+        :return:
+        """
+        dn = name
+        if name 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
+        LOG.warning("No container with name: %r found.")
+        return None
+
+    @staticmethod
+    def _vnf_reconfigure_network(vnfi, if_name, net_str):
+        """
+        Reconfigure the network configuration of a specific interface
+        of a running container.
+        :param vnfi: container instacne
+        :param if_name: interface name
+        :param net_str: network configuration string, e.g., 1.2.3.4/24
+        :return:
+        """
+        intf = vnfi.intf(intf=if_name)
+        if intf is not None:
+            intf.setIP(net_str)
+            LOG.debug("Reconfigured network of %s:%s to %r" % (vnfi.name, if_name, net_str))
+        else:
+            LOG.warning("Interface not found: %s:%s. Network reconfiguration skipped." % (vnfi.name, if_name))
+
+
     def _trigger_emulator_start_scripts_in_vnfis(self, vnfi_list):
         for vnfi in vnfi_list:
             config = vnfi.dcinfo.get("Config", dict())
@@ -409,6 +472,7 @@ class Packages(fr.Resource):
         Return a list of UUID's of uploaded service packages.
         :return: dict/list
         """
+        LOG.info("GET /packages")
         return {"service_uuid_list": list(GK.services.iterkeys())}
 
 
@@ -440,7 +504,8 @@ class Instantiations(fr.Resource):
         Returns a list of UUIDs containing all running services.
         :return: dict / list
         """
-        return {"service_instance_list": [
+        LOG.info("GET /instantiations")
+        return {"service_instantiations_list": [
             list(s.instances.iterkeys()) for s in GK.services.itervalues()]}
 
 
@@ -488,6 +553,16 @@ def make_relative_path(path):
     return path
 
 
+def generate_subnet_strings(n, start=1, subnet_size=24, ip=0):
+    """
+    Helper to generate different network configuration strings.
+    """
+    r = list()
+    for i in range(start, start + n):
+        r.append("%d.0.0.%d/%d" % (i, ip, subnet_size))
+    return r
+
+
 if __name__ == '__main__':
     """
     Lets allow to run the API in standalone mode.