From: tierno Date: Tue, 6 Oct 2020 08:22:07 +0000 (+0000) Subject: fix 1238: Allow individual cloud-init user-data at each VM X-Git-Tag: release-v9.0-start~21 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FLCM.git;a=commitdiff_plain;h=72ef84f360d308b979e7adaaca1cdb5e9e6678a9 fix 1238: Allow individual cloud-init user-data at each VM 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 --- diff --git a/osm_lcm/ns.py b/osm_lcm/ns.py index 10d07ef..61ad281 100644 --- a/osm_lcm/ns.py +++ b/osm_lcm/ns.py @@ -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":