fix 1238: Allow individual cloud-init user-data at each VM 75/9875/1
authortierno <alfonso.tiernosepulveda@telefonica.com>
Tue, 6 Oct 2020 08:22:07 +0000 (08:22 +0000)
committertierno <alfonso.tiernosepulveda@telefonica.com>
Mon, 19 Oct 2020 10:45:43 +0000 (10:45 +0000)
create individual user-data at each vm, parsing jinja2 with a
new OSM variable that includes known ip-address, mac-address,
vnf and ns id, vdu id and index, ...
Added OSM params at proxy charms

Change-Id: I3cb37d9c0d2816d89c55c7b2da2693992ed5dfb1
Signed-off-by: tierno <alfonso.tiernosepulveda@telefonica.com>
osm_lcm/ns.py

index 10d07ef..61ad281 100644 (file)
@@ -22,7 +22,7 @@ import logging
 import logging.handlers
 import traceback
 import json
-from jinja2 import Environment, Template, meta, TemplateError, TemplateNotFound, TemplateSyntaxError
+from jinja2 import Environment, TemplateError, TemplateNotFound, StrictUndefined, UndefinedError
 
 from osm_lcm import ROclient
 from osm_lcm.ng_ro import NgRoClient, NgRoException
@@ -278,6 +278,69 @@ class NsLcm(LcmBase):
         except Exception as e:
             self.logger.warn('Error updating NS state for ns={}: {}'.format(nsr_id, e))
 
+    @staticmethod
+    def _parse_cloud_init(cloud_init_text, additional_params, vnfd_id, vdu_id):
+        try:
+            env = Environment(undefined=StrictUndefined)
+            template = env.from_string(cloud_init_text)
+            return template.render(additional_params or {})
+        except UndefinedError as e:
+            raise LcmException("Variable {} at vnfd[id={}]:vdu[id={}]:cloud-init/cloud-init-"
+                               "file, must be provided in the instantiation parameters inside the "
+                               "'additionalParamsForVnf/Vdu' block".format(e, vnfd_id, vdu_id))
+        except (TemplateError, TemplateNotFound) as e:
+            raise LcmException("Error parsing Jinja2 to cloud-init content at vnfd[id={}]:vdu[id={}]: {}".
+                               format(vnfd_id, vdu_id, e))
+
+    def _get_cloud_init(self, vdu, vnfd):
+        try:
+            cloud_init_content = cloud_init_file = None
+            if vdu.get("cloud-init-file"):
+                base_folder = vnfd["_admin"]["storage"]
+                cloud_init_file = "{}/{}/cloud_init/{}".format(base_folder["folder"], base_folder["pkg-dir"],
+                                                               vdu["cloud-init-file"])
+                with self.fs.file_open(cloud_init_file, "r") as ci_file:
+                    cloud_init_content = ci_file.read()
+            elif vdu.get("cloud-init"):
+                cloud_init_content = vdu["cloud-init"]
+
+            return cloud_init_content
+        except FsException as e:
+            raise LcmException("Error reading vnfd[id={}]:vdu[id={}]:cloud-init-file={}: {}".
+                               format(vnfd["id"], vdu["id"], cloud_init_file, e))
+
+    def _get_osm_params(self, db_vnfr, vdu_id=None, vdu_count_index=0):
+        osm_params = {x.replace("-", "_"): db_vnfr[x] for x in ("ip-address", "vim-account-id", "vnfd-id", "vnfd-ref")
+                      if db_vnfr.get(x) is not None}
+        osm_params["ns_id"] = db_vnfr["nsr-id-ref"]
+        osm_params["vnf_id"] = db_vnfr["_id"]
+        osm_params["member_vnf_index"] = db_vnfr["member-vnf-index-ref"]
+        if db_vnfr.get("vdur"):
+            osm_params["vdu"] = {}
+            for vdur in db_vnfr["vdur"]:
+                vdu = {
+                    "count_index": vdur["count-index"],
+                    "vdu_id": vdur["vdu-id-ref"],
+                    "interfaces": {}
+                }
+                if vdur.get("ip-address"):
+                    vdu["ip_address"] = vdur["ip-address"]
+                for iface in vdur["interfaces"]:
+                    vdu["interfaces"][iface["name"]] = \
+                        {x.replace("-", "_"): iface[x] for x in ("mac-address", "ip-address", "vnf-vld-id", "name")
+                         if iface.get(x) is not None}
+                vdu_id_index = "{}-{}".format(vdur["vdu-id-ref"], vdur["count-index"])
+                osm_params["vdu"][vdu_id_index] = vdu
+            if vdu_id:
+                osm_params["vdu_id"] = vdu_id
+                osm_params["count_index"] = vdu_count_index
+        return osm_params
+
+    def _get_vdu_additional_params(self, db_vnfr, vdu_id):
+        vdur = next(vdur for vdur in db_vnfr.get("vdur") if vdu_id == vdur["vdu-id-ref"])
+        additional_params = vdur.get("additionalParams")
+        return self._format_additional_params(additional_params)
+
     def vnfd2RO(self, vnfd, new_id=None, additionalParams=None, nsrId=None):
         """
         Converts creates a new vnfd descriptor for RO base on input OSM IM vnfd
@@ -287,54 +350,23 @@ class NsLcm(LcmBase):
         :param nsrId: Id of the NSR
         :return: copy of vnfd
         """
-        try:
-            vnfd_RO = deepcopy(vnfd)
-            # remove unused by RO configuration, monitoring, scaling and internal keys
-            vnfd_RO.pop("_id", None)
-            vnfd_RO.pop("_admin", None)
-            vnfd_RO.pop("vnf-configuration", None)
-            vnfd_RO.pop("monitoring-param", None)
-            vnfd_RO.pop("scaling-group-descriptor", None)
-            vnfd_RO.pop("kdu", None)
-            vnfd_RO.pop("k8s-cluster", None)
-            if new_id:
-                vnfd_RO["id"] = new_id
-
-            # parse cloud-init or cloud-init-file with the provided variables using Jinja2
-            for vdu in get_iterable(vnfd_RO, "vdu"):
-                cloud_init_file = None
-                if vdu.get("cloud-init-file"):
-                    base_folder = vnfd["_admin"]["storage"]
-                    cloud_init_file = "{}/{}/cloud_init/{}".format(base_folder["folder"], base_folder["pkg-dir"],
-                                                                   vdu["cloud-init-file"])
-                    with self.fs.file_open(cloud_init_file, "r") as ci_file:
-                        cloud_init_content = ci_file.read()
-                    vdu.pop("cloud-init-file", None)
-                elif vdu.get("cloud-init"):
-                    cloud_init_content = vdu["cloud-init"]
-                else:
-                    continue
-
-                env = Environment()
-                ast = env.parse(cloud_init_content)
-                mandatory_vars = meta.find_undeclared_variables(ast)
-                if mandatory_vars:
-                    for var in mandatory_vars:
-                        if not additionalParams or var not in additionalParams.keys():
-                            raise LcmException("Variable '{}' defined at vnfd[id={}]:vdu[id={}]:cloud-init/cloud-init-"
-                                               "file, must be provided in the instantiation parameters inside the "
-                                               "'additionalParamsForVnf' block".format(var, vnfd["id"], vdu["id"]))
-                template = Template(cloud_init_content)
-                cloud_init_content = template.render(additionalParams or {})
-                vdu["cloud-init"] = cloud_init_content
-
-            return vnfd_RO
-        except FsException as e:
-            raise LcmException("Error reading vnfd[id={}]:vdu[id={}]:cloud-init-file={}: {}".
-                               format(vnfd["id"], vdu["id"], cloud_init_file, e))
-        except (TemplateError, TemplateNotFound, TemplateSyntaxError) as e:
-            raise LcmException("Error parsing Jinja2 to cloud-init content at vnfd[id={}]:vdu[id={}]: {}".
-                               format(vnfd["id"], vdu["id"], e))
+        vnfd_RO = deepcopy(vnfd)
+        # remove unused by RO configuration, monitoring, scaling and internal keys
+        vnfd_RO.pop("_id", None)
+        vnfd_RO.pop("_admin", None)
+        vnfd_RO.pop("vnf-configuration", None)
+        vnfd_RO.pop("monitoring-param", None)
+        vnfd_RO.pop("scaling-group-descriptor", None)
+        vnfd_RO.pop("kdu", None)
+        vnfd_RO.pop("k8s-cluster", None)
+        if new_id:
+            vnfd_RO["id"] = new_id
+
+        # parse cloud-init or cloud-init-file with the provided variables using Jinja2
+        for vdu in get_iterable(vnfd_RO, "vdu"):
+            vdu.pop("cloud-init-file", None)
+            vdu.pop("cloud-init", None)
+        return vnfd_RO
 
     def _ns_params_2_RO(self, ns_params, nsd, vnfd_dict, db_vnfrs, n2vc_key_list):
         """
@@ -444,6 +476,25 @@ class NsLcm(LcmBase):
                         populate_dict(RO_ns_params,
                                       ("vnfs", vnf_member["member-vnf-index"], "vdus", vdu, "mgmt_keys"),
                                       n2vc_key_list)
+            # cloud init
+            for vdu in get_iterable(vnfd, "vdu"):
+                cloud_init_text = self._get_cloud_init(vdu, vnfd)
+                if not cloud_init_text:
+                    continue
+                for vnf_member in nsd.get("constituent-vnfd"):
+                    if vnf_member["vnfd-id-ref"] != vnfd_ref:
+                        continue
+                    db_vnfr = db_vnfrs[vnf_member["member-vnf-index"]]
+                    additional_params = self._get_vdu_additional_params(db_vnfr, vdu["id"]) or {}
+
+                    cloud_init_list = []
+                    for vdu_index in range(0, int(vdu.get("count", 1))):
+                        additional_params["OSM"] = self._get_osm_params(db_vnfr, vdu["id"], vdu_index)
+                        cloud_init_list.append(self._parse_cloud_init(cloud_init_text, additional_params, vnfd["id"],
+                                                                      vdu["id"]))
+                    populate_dict(RO_ns_params,
+                                  ("vnfs", vnf_member["member-vnf-index"], "vdus", vdu["id"], "cloud_init"),
+                                  cloud_init_list)
 
         if ns_params.get("vduImage"):
             RO_ns_params["vduImage"] = ns_params["vduImage"]
@@ -2045,9 +2096,9 @@ class NsLcm(LcmBase):
                 kdu_name = None
 
                 # Get additional parameters
-                deploy_params = {}
+                deploy_params = {"OSM": self._get_osm_params(db_vnfr)}
                 if db_vnfr.get("additionalParamsForVnf"):
-                    deploy_params = self._format_additional_params(db_vnfr["additionalParamsForVnf"].copy())
+                    deploy_params.update(self._format_additional_params(db_vnfr["additionalParamsForVnf"].copy()))
 
                 descriptor_config = vnfd.get("vnf-configuration")
                 if descriptor_config:
@@ -2080,15 +2131,8 @@ class NsLcm(LcmBase):
                         deploy_params_vdu = self._format_additional_params(vdur["additionalParams"])
                     else:
                         deploy_params_vdu = deploy_params
+                    deploy_params_vdu["OSM"] = self._get_osm_params(db_vnfr, vdu_id, vdu_count_index=0)
                     if descriptor_config:
-                        # look for vdu index in the db_vnfr["vdu"] section
-                        # for vdur_index, vdur in enumerate(db_vnfr["vdur"]):
-                        #     if vdur["vdu-id-ref"] == vdu_id:
-                        #         break
-                        # else:
-                        #     raise LcmException("Mismatch vdu_id={} not found in the vnfr['vdur'] list for "
-                        #                        "member_vnf_index={}".format(vdu_id, member_vnf_index))
-                        # vdu_name = vdur.get("name")
                         vdu_name = None
                         kdu_name = None
                         for vdu_index in range(int(vdud.get("count", 1))):
@@ -2120,15 +2164,10 @@ class NsLcm(LcmBase):
                         vdu_id = None
                         vdu_index = 0
                         vdu_name = None
-                        # look for vdu index in the db_vnfr["vdu"] section
-                        # for vdur_index, vdur in enumerate(db_vnfr["vdur"]):
-                        #     if vdur["vdu-id-ref"] == vdu_id:
-                        #         break
-                        # else:
-                        #     raise LcmException("Mismatch vdu_id={} not found in the vnfr['vdur'] list for "
-                        #                        "member_vnf_index={}".format(vdu_id, member_vnf_index))
-                        # vdu_name = vdur.get("name")
-                        # vdu_name = None
+                        kdur = next(x for x in db_vnfr["kdur"] if x["kdu-name"] == kdu_name)
+                        deploy_params_kdu = {"OSM": self._get_osm_params(db_vnfr)}
+                        if kdur.get("additionalParams"):
+                            deploy_params_kdu = self._format_additional_params(kdur["additionalParams"])
 
                         self._deploy_n2vc(
                             logging_text=logging_text,
@@ -2143,7 +2182,7 @@ class NsLcm(LcmBase):
                             member_vnf_index=member_vnf_index,
                             vdu_index=vdu_index,
                             vdu_name=vdu_name,
-                            deploy_params=deploy_params,
+                            deploy_params=deploy_params_kdu,
                             descriptor_config=descriptor_config,
                             base_folder=base_folder,
                             task_instantiation_info=tasks_dict_info,
@@ -2162,9 +2201,9 @@ class NsLcm(LcmBase):
                 vdu_name = None
 
                 # Get additional parameters
-                deploy_params = {}
+                deploy_params = {"OSM": self._get_osm_params(db_vnfr)}
                 if db_nsr.get("additionalParamsForNs"):
-                    deploy_params = self._format_additional_params(db_nsr["additionalParamsForNs"].copy())
+                    deploy_params.update(self._format_additional_params(db_nsr["additionalParamsForNs"].copy()))
                 base_folder = nsd["_admin"]["storage"]
                 self._deploy_n2vc(
                     logging_text=logging_text,
@@ -3957,8 +3996,25 @@ class NsLcm(LcmBase):
                 vdu_scaling_info["scaling_direction"] = "OUT"
                 vdu_scaling_info["vdu-create"] = {}
                 for vdu_scale_info in scaling_descriptor["vdu"]:
+                    vdud = next(vdu for vdu in db_vnfd.get("vdu") if vdu["id"] == vdu_scale_info["vdu-id-ref"])
+                    vdu_index = len([x for x in db_vnfr.get("vdur", ())
+                                     if x.get("vdu-id-ref") == vdu_scale_info["vdu-id-ref"] and
+                                     x.get("member-vnf-index-ref") == vnf_index])
+                    cloud_init_text = self._get_cloud_init(vdud, db_vnfd)
+                    if cloud_init_text:
+                        additional_params = self._get_vdu_additional_params(db_vnfr, vdud["id"]) or {}
+                    cloud_init_list = []
+                    for x in range(vdu_scale_info.get("count", 1)):
+                        if cloud_init_text:
+                            # TODO Information of its own ip is not available because db_vnfr is not updated.
+                            additional_params["OSM"] = self._get_osm_params(db_vnfr, vdu_scale_info["vdu-id-ref"],
+                                                                            vdu_index + x)
+                            cloud_init_list.append(self._parse_cloud_init(cloud_init_text, additional_params,
+                                                                          db_vnfd["id"], vdud["id"]))
                     RO_scaling_info.append({"osm_vdu_id": vdu_scale_info["vdu-id-ref"], "member-vnf-index": vnf_index,
                                             "type": "create", "count": vdu_scale_info.get("count", 1)})
+                    if cloud_init_list:
+                        RO_scaling_info[-1]["cloud_init"] = cloud_init_list
                     vdu_scaling_info["vdu-create"][vdu_scale_info["vdu-id-ref"]] = vdu_scale_info.get("count", 1)
 
             elif scaling_type == "SCALE_IN":