Feature 9952: Distributed Proxy Charms
[osm/LCM.git] / osm_lcm / ns.py
index dc53b98..198def1 100644 (file)
@@ -22,11 +22,13 @@ import logging
 import logging.handlers
 import traceback
 import json
 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 import ROclient
+from osm_lcm.ng_ro import NgRoClient, NgRoException
 from osm_lcm.lcm_utils import LcmException, LcmExceptionNoMgmtIP, LcmBase, deep_get, get_iterable, populate_dict
 from n2vc.k8s_helm_conn import K8sHelmConnector
 from osm_lcm.lcm_utils import LcmException, LcmExceptionNoMgmtIP, LcmBase, deep_get, get_iterable, populate_dict
 from n2vc.k8s_helm_conn import K8sHelmConnector
+from n2vc.k8s_helm3_conn import K8sHelm3Connector
 from n2vc.k8s_juju_conn import K8sJujuConnector
 
 from osm_common.dbbase import DbException
 from n2vc.k8s_juju_conn import K8sJujuConnector
 
 from osm_common.dbbase import DbException
@@ -35,13 +37,16 @@ from osm_common.fsbase import FsException
 from n2vc.n2vc_juju_conn import N2VCJujuConnector
 from n2vc.exceptions import N2VCException, N2VCNotFound, K8sException
 
 from n2vc.n2vc_juju_conn import N2VCJujuConnector
 from n2vc.exceptions import N2VCException, N2VCNotFound, K8sException
 
+from osm_lcm.lcm_helm_conn import LCMHelmConn
+
 from copy import copy, deepcopy
 from http import HTTPStatus
 from time import time
 from uuid import uuid4
 from copy import copy, deepcopy
 from http import HTTPStatus
 from time import time
 from uuid import uuid4
-from functools import partial
 
 
-__author__ = "Alfonso Tierno"
+from random import randint
+
+__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
 
 
 class NsLcm(LcmBase):
 
 
 class NsLcm(LcmBase):
@@ -49,15 +54,15 @@ class NsLcm(LcmBase):
     timeout_ns_deploy = 2 * 3600   # default global timeout for deployment a ns
     timeout_ns_terminate = 1800   # default global timeout for un deployment a ns
     timeout_charm_delete = 10 * 60
     timeout_ns_deploy = 2 * 3600   # default global timeout for deployment a ns
     timeout_ns_terminate = 1800   # default global timeout for un deployment a ns
     timeout_charm_delete = 10 * 60
-    timeout_primitive = 10 * 60  # timeout for primitive execution
-    timeout_progress_primitive = 2 * 60  # timeout for some progress in a primitive execution
+    timeout_primitive = 30 * 60  # timeout for primitive execution
+    timeout_progress_primitive = 10 * 60  # timeout for some progress in a primitive execution
 
     SUBOPERATION_STATUS_NOT_FOUND = -1
     SUBOPERATION_STATUS_NEW = -2
     SUBOPERATION_STATUS_SKIP = -3
     task_name_deploy_vca = "Deploying VCA"
 
 
     SUBOPERATION_STATUS_NOT_FOUND = -1
     SUBOPERATION_STATUS_NEW = -2
     SUBOPERATION_STATUS_SKIP = -3
     task_name_deploy_vca = "Deploying VCA"
 
-    def __init__(self, db, msg, fs, lcm_tasks, config, loop):
+    def __init__(self, db, msg, fs, lcm_tasks, config, loop, prometheus=None):
         """
         Init, Connect to database, filesystem storage, and messaging
         :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
         """
         Init, Connect to database, filesystem storage, and messaging
         :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
@@ -74,6 +79,7 @@ class NsLcm(LcmBase):
         self.lcm_tasks = lcm_tasks
         self.timeout = config["timeout"]
         self.ro_config = config["ro_config"]
         self.lcm_tasks = lcm_tasks
         self.timeout = config["timeout"]
         self.ro_config = config["ro_config"]
+        self.ng_ro = config["ro_config"].get("ng")
         self.vca_config = config["VCA"].copy()
 
         # create N2VC connector
         self.vca_config = config["VCA"].copy()
 
         # create N2VC connector
@@ -88,7 +94,18 @@ class NsLcm(LcmBase):
             on_update_db=self._on_update_n2vc_db
         )
 
             on_update_db=self._on_update_n2vc_db
         )
 
-        self.k8sclusterhelm = K8sHelmConnector(
+        self.conn_helm_ee = LCMHelmConn(
+            db=self.db,
+            fs=self.fs,
+            log=self.logger,
+            loop=self.loop,
+            url=None,
+            username=None,
+            vca_config=self.vca_config,
+            on_update_db=self._on_update_n2vc_db
+        )
+
+        self.k8sclusterhelm2 = K8sHelmConnector(
             kubectl_command=self.vca_config.get("kubectlpath"),
             helm_command=self.vca_config.get("helmpath"),
             fs=self.fs,
             kubectl_command=self.vca_config.get("kubectlpath"),
             helm_command=self.vca_config.get("helmpath"),
             fs=self.fs,
@@ -97,23 +114,49 @@ class NsLcm(LcmBase):
             on_update_db=None,
         )
 
             on_update_db=None,
         )
 
+        self.k8sclusterhelm3 = K8sHelm3Connector(
+            kubectl_command=self.vca_config.get("kubectlpath"),
+            helm_command=self.vca_config.get("helm3path"),
+            fs=self.fs,
+            log=self.logger,
+            db=self.db,
+            on_update_db=None,
+        )
+
         self.k8sclusterjuju = K8sJujuConnector(
             kubectl_command=self.vca_config.get("kubectlpath"),
             juju_command=self.vca_config.get("jujupath"),
             fs=self.fs,
             log=self.logger,
             db=self.db,
         self.k8sclusterjuju = K8sJujuConnector(
             kubectl_command=self.vca_config.get("kubectlpath"),
             juju_command=self.vca_config.get("jujupath"),
             fs=self.fs,
             log=self.logger,
             db=self.db,
+            loop=self.loop,
             on_update_db=None,
             on_update_db=None,
+            vca_config=self.vca_config,
         )
 
         self.k8scluster_map = {
         )
 
         self.k8scluster_map = {
-            "helm-chart": self.k8sclusterhelm,
-            "chart": self.k8sclusterhelm,
+            "helm-chart": self.k8sclusterhelm2,
+            "helm-chart-v3": self.k8sclusterhelm3,
+            "chart": self.k8sclusterhelm3,
             "juju-bundle": self.k8sclusterjuju,
             "juju": self.k8sclusterjuju,
         }
             "juju-bundle": self.k8sclusterjuju,
             "juju": self.k8sclusterjuju,
         }
+
+        self.vca_map = {
+            "lxc_proxy_charm": self.n2vc,
+            "native_charm": self.n2vc,
+            "k8s_proxy_charm": self.n2vc,
+            "helm": self.conn_helm_ee,
+            "helm-v3": self.conn_helm_ee
+        }
+
+        self.prometheus = prometheus
+
         # create RO client
         # create RO client
-        self.RO = ROclient.ROClient(self.loop, **self.ro_config)
+        if self.ng_ro:
+            self.RO = NgRoClient(self.loop, **self.ro_config)
+        else:
+            self.RO = ROclient.ROClient(self.loop, **self.ro_config)
 
     def _on_update_ro_db(self, nsrs_id, ro_descriptor):
 
 
     def _on_update_ro_db(self, nsrs_id, ro_descriptor):
 
@@ -220,6 +263,69 @@ class NsLcm(LcmBase):
         except Exception as e:
             self.logger.warn('Error updating NS state for ns={}: {}'.format(nsr_id, e))
 
         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
     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
@@ -229,54 +335,23 @@ class NsLcm(LcmBase):
         :param nsrId: Id of the NSR
         :return: copy of vnfd
         """
         :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):
         """
 
     def _ns_params_2_RO(self, ns_params, nsd, vnfd_dict, db_vnfrs, n2vc_key_list):
         """
@@ -386,6 +461,25 @@ class NsLcm(LcmBase):
                         populate_dict(RO_ns_params,
                                       ("vnfs", vnf_member["member-vnf-index"], "vdus", vdu, "mgmt_keys"),
                                       n2vc_key_list)
                         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"]
 
         if ns_params.get("vduImage"):
             RO_ns_params["vduImage"] = ns_params["vduImage"]
@@ -571,11 +665,21 @@ class NsLcm(LcmBase):
                 continue
             vdu_id_ref = vdur["vdu-id-ref"]
             if vdu_create and vdu_create.get(vdu_id_ref):
                 continue
             vdu_id_ref = vdur["vdu-id-ref"]
             if vdu_create and vdu_create.get(vdu_id_ref):
+                vdur_copy = deepcopy(vdur)
+                vdur_copy["status"] = "BUILD"
+                vdur_copy["status-detailed"] = None
+                vdur_copy["ip_address"]: None
+                for iface in vdur_copy["interfaces"]:
+                    iface["ip-address"] = None
+                    iface["mac-address"] = None
+                    iface.pop("mgmt_vnf", None)  # only first vdu can be managment of vnf   # TODO ALF
                 for index in range(0, vdu_create[vdu_id_ref]):
                 for index in range(0, vdu_create[vdu_id_ref]):
-                    vdur = deepcopy(vdur)
-                    vdur["_id"] = str(uuid4())
-                    vdur["count-index"] += 1
-                    vdurs.insert(vdu_index+1+index, vdur)
+                    vdur_copy["_id"] = str(uuid4())
+                    vdur_copy["count-index"] += 1
+                    vdurs.insert(vdu_index+1+index, vdur_copy)
+                    self.logger.debug("scale out, adding vdu={}".format(vdur_copy))
+                    vdur_copy = deepcopy(vdur_copy)
+
                 del vdu_create[vdu_id_ref]
             if vdu_delete and vdu_delete.get(vdu_id_ref):
                 del vdurs[vdu_index]
                 del vdu_create[vdu_id_ref]
             if vdu_delete and vdu_delete.get(vdu_id_ref):
                 del vdurs[vdu_index]
@@ -727,19 +831,27 @@ class NsLcm(LcmBase):
         return ns_config_info
 
     @staticmethod
         return ns_config_info
 
     @staticmethod
-    def _get_initial_config_primitive_list(desc_primitive_list, vca_deployed):
+    def _get_initial_config_primitive_list(desc_primitive_list, vca_deployed, ee_descriptor_id):
         """
         Generates a list of initial-config-primitive based on the list provided by the descriptor. It includes internal
         primitives as verify-ssh-credentials, or config when needed
         :param desc_primitive_list: information of the descriptor
         :param vca_deployed: information of the deployed, needed for known if it is related to an NS, VNF, VDU and if
             this element contains a ssh public key
         """
         Generates a list of initial-config-primitive based on the list provided by the descriptor. It includes internal
         primitives as verify-ssh-credentials, or config when needed
         :param desc_primitive_list: information of the descriptor
         :param vca_deployed: information of the deployed, needed for known if it is related to an NS, VNF, VDU and if
             this element contains a ssh public key
+        :param ee_descriptor_id: execution environment descriptor id. It is the value of
+            XXX_configuration.execution-environment-list.INDEX.id; it can be None
         :return: The modified list. Can ba an empty list, but always a list
         """
         :return: The modified list. Can ba an empty list, but always a list
         """
-        if desc_primitive_list:
-            primitive_list = desc_primitive_list.copy()
-        else:
-            primitive_list = []
+
+        primitive_list = desc_primitive_list or []
+
+        # filter primitives by ee_id
+        primitive_list = [p for p in primitive_list if p.get("execution-environment-ref") == ee_descriptor_id]
+
+        # sort by 'seq'
+        if primitive_list:
+            primitive_list.sort(key=lambda val: int(val['seq']))
+
         # look for primitive config, and get the position. None if not present
         config_position = None
         for index, primitive in enumerate(primitive_list):
         # look for primitive config, and get the position. None if not present
         config_position = None
         for index, primitive in enumerate(primitive_list):
@@ -751,11 +863,184 @@ class NsLcm(LcmBase):
         if not vca_deployed["member-vnf-index"] and config_position is None:
             primitive_list.insert(0, {"name": "config", "parameter": []})
             config_position = 0
         if not vca_deployed["member-vnf-index"] and config_position is None:
             primitive_list.insert(0, {"name": "config", "parameter": []})
             config_position = 0
-        # for VNF/VDU add verify-ssh-credentials after config
+        # TODO revise if needed: for VNF/VDU add verify-ssh-credentials after config
         if vca_deployed["member-vnf-index"] and config_position is not None and vca_deployed.get("ssh-public-key"):
             primitive_list.insert(config_position + 1, {"name": "verify-ssh-credentials", "parameter": []})
         return primitive_list
 
         if vca_deployed["member-vnf-index"] and config_position is not None and vca_deployed.get("ssh-public-key"):
             primitive_list.insert(config_position + 1, {"name": "verify-ssh-credentials", "parameter": []})
         return primitive_list
 
+    async def _instantiate_ng_ro(self, logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs, db_vnfds_ref,
+                                 n2vc_key_list, stage, start_deploy, timeout_ns_deploy):
+        nslcmop_id = db_nslcmop["_id"]
+        target = {
+            "name": db_nsr["name"],
+            "ns": {"vld": []},
+            "vnf": [],
+            "image": deepcopy(db_nsr["image"]),
+            "flavor": deepcopy(db_nsr["flavor"]),
+            "action_id": nslcmop_id,
+        }
+        for image in target["image"]:
+            image["vim_info"] = []
+        for flavor in target["flavor"]:
+            flavor["vim_info"] = []
+
+        ns_params = db_nslcmop.get("operationParams")
+        ssh_keys = []
+        if ns_params.get("ssh_keys"):
+            ssh_keys += ns_params.get("ssh_keys")
+        if n2vc_key_list:
+            ssh_keys += n2vc_key_list
+
+        cp2target = {}
+        for vld_index, vld in enumerate(nsd.get("vld")):
+            target_vld = {"id": vld["id"],
+                          "name": vld["name"],
+                          "mgmt-network": vld.get("mgmt-network", False),
+                          "type": vld.get("type"),
+                          "vim_info": [{"vim-network-name": vld.get("vim-network-name"),
+                                        "vim_account_id": ns_params["vimAccountId"]}],
+                          }
+            for cp in vld["vnfd-connection-point-ref"]:
+                cp2target["member_vnf:{}.{}".format(cp["member-vnf-index-ref"], cp["vnfd-connection-point-ref"])] = \
+                    "nsrs:{}:vld.{}".format(nsr_id, vld_index)
+            target["ns"]["vld"].append(target_vld)
+        for vnfr in db_vnfrs.values():
+            vnfd = db_vnfds_ref[vnfr["vnfd-ref"]]
+            target_vnf = deepcopy(vnfr)
+            for vld in target_vnf.get("vld", ()):
+                # check if connected to a ns.vld
+                vnf_cp = next((cp for cp in vnfd.get("connection-point", ()) if
+                               cp.get("internal-vld-ref") == vld["id"]), None)
+                if vnf_cp:
+                    ns_cp = "member_vnf:{}.{}".format(vnfr["member-vnf-index-ref"], vnf_cp["id"])
+                    if cp2target.get(ns_cp):
+                        vld["target"] = cp2target[ns_cp]
+                vld["vim_info"] = [{"vim-network-name": vld.get("vim-network-name"),
+                                    "vim_account_id": vnfr["vim-account-id"]}]
+
+            for vdur in target_vnf.get("vdur", ()):
+                vdur["vim_info"] = [{"vim_account_id": vnfr["vim-account-id"]}]
+                vdud_index, vdud = next(k for k in enumerate(vnfd["vdu"]) if k[1]["id"] == vdur["vdu-id-ref"])
+                # vdur["additionalParams"] = vnfr.get("additionalParamsForVnf")  # TODO additional params for VDU
+
+                if ssh_keys:
+                    if deep_get(vdud, ("vdu-configuration", "config-access", "ssh-access", "required")):
+                        vdur["ssh-keys"] = ssh_keys
+                        vdur["ssh-access-required"] = True
+                    elif deep_get(vnfd, ("vnf-configuration", "config-access", "ssh-access", "required")) and \
+                            any(iface.get("mgmt-vnf") for iface in vdur["interfaces"]):
+                        vdur["ssh-keys"] = ssh_keys
+                        vdur["ssh-access-required"] = True
+
+                # cloud-init
+                if vdud.get("cloud-init-file"):
+                    vdur["cloud-init"] = "{}:file:{}".format(vnfd["_id"], vdud.get("cloud-init-file"))
+                elif vdud.get("cloud-init"):
+                    vdur["cloud-init"] = "{}:vdu:{}".format(vnfd["_id"], vdud_index)
+
+                # flavor
+                ns_flavor = target["flavor"][int(vdur["ns-flavor-id"])]
+                if not next((vi for vi in ns_flavor["vim_info"] if
+                             vi and vi.get("vim_account_id") == vnfr["vim-account-id"]), None):
+                    ns_flavor["vim_info"].append({"vim_account_id": vnfr["vim-account-id"]})
+                # image
+                ns_image = target["image"][int(vdur["ns-image-id"])]
+                if not next((vi for vi in ns_image["vim_info"] if
+                             vi and vi.get("vim_account_id") == vnfr["vim-account-id"]), None):
+                    ns_image["vim_info"].append({"vim_account_id": vnfr["vim-account-id"]})
+
+                vdur["vim_info"] = [{"vim_account_id": vnfr["vim-account-id"]}]
+            target["vnf"].append(target_vnf)
+
+        desc = await self.RO.deploy(nsr_id, target)
+        action_id = desc["action_id"]
+        await self._wait_ng_ro(self, nsr_id, action_id, nslcmop_id, start_deploy, timeout_ns_deploy, stage)
+
+        # Updating NSR
+        db_nsr_update = {
+            "_admin.deployed.RO.operational-status": "running",
+            "detailed-status": " ".join(stage)
+        }
+        # db_nsr["_admin.deployed.RO.detailed-status"] = "Deployed at VIM"
+        self.update_db_2("nsrs", nsr_id, db_nsr_update)
+        self._write_op_status(nslcmop_id, stage)
+        self.logger.debug(logging_text + "ns deployed at RO. RO_id={}".format(action_id))
+        return
+
+    async def _wait_ng_ro(self, nsr_id, action_id, nslcmop_id, start_time, timeout, stage):
+        detailed_status_old = None
+        db_nsr_update = {}
+        while time() <= start_time + timeout:
+            desc_status = await self.RO.status(nsr_id, action_id)
+            if desc_status["status"] == "FAILED":
+                raise NgRoException(desc_status["details"])
+            elif desc_status["status"] == "BUILD":
+                stage[2] = "VIM: ({})".format(desc_status["details"])
+            elif desc_status["status"] == "DONE":
+                stage[2] = "Deployed at VIM"
+                break
+            else:
+                assert False, "ROclient.check_ns_status returns unknown {}".format(desc_status["status"])
+            if stage[2] != detailed_status_old:
+                detailed_status_old = stage[2]
+                db_nsr_update["detailed-status"] = " ".join(stage)
+                self.update_db_2("nsrs", nsr_id, db_nsr_update)
+                self._write_op_status(nslcmop_id, stage)
+            await asyncio.sleep(5, loop=self.loop)
+        else:  # timeout_ns_deploy
+            raise NgRoException("Timeout waiting ns to deploy")
+
+    async def _terminate_ng_ro(self, logging_text, nsr_deployed, nsr_id, nslcmop_id, stage):
+        db_nsr_update = {}
+        failed_detail = []
+        action_id = None
+        start_deploy = time()
+        try:
+            target = {
+                "ns": {"vld": []},
+                "vnf": [],
+                "image": [],
+                "flavor": [],
+            }
+            desc = await self.RO.deploy(nsr_id, target)
+            action_id = desc["action_id"]
+            db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = action_id
+            db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETING"
+            self.logger.debug(logging_text + "ns terminate action at RO. action_id={}".format(action_id))
+
+            # wait until done
+            delete_timeout = 20 * 60  # 20 minutes
+            await self._wait_ng_ro(self, nsr_id, action_id, nslcmop_id, start_deploy, delete_timeout, stage)
+
+            db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
+            db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
+            # delete all nsr
+            await self.RO.delete(nsr_id)
+        except Exception as e:
+            if isinstance(e, NgRoException) and e.http_code == 404:  # not found
+                db_nsr_update["_admin.deployed.RO.nsr_id"] = None
+                db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
+                db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
+                self.logger.debug(logging_text + "RO_action_id={} already deleted".format(action_id))
+            elif isinstance(e, NgRoException) and e.http_code == 409:  # conflict
+                failed_detail.append("delete conflict: {}".format(e))
+                self.logger.debug(logging_text + "RO_action_id={} delete conflict: {}".format(action_id, e))
+            else:
+                failed_detail.append("delete error: {}".format(e))
+                self.logger.error(logging_text + "RO_action_id={} delete error: {}".format(action_id, e))
+
+        if failed_detail:
+            stage[2] = "Error deleting from VIM"
+        else:
+            stage[2] = "Deleted from VIM"
+        db_nsr_update["detailed-status"] = " ".join(stage)
+        self.update_db_2("nsrs", nsr_id, db_nsr_update)
+        self._write_op_status(nslcmop_id, stage)
+
+        if failed_detail:
+            raise LcmException("; ".join(failed_detail))
+        return
+
     async def instantiate_RO(self, logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs, db_vnfds_ref,
                              n2vc_key_list, stage):
         """
     async def instantiate_RO(self, logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs, db_vnfds_ref,
                              n2vc_key_list, stage):
         """
@@ -793,6 +1078,10 @@ class NsLcm(LcmBase):
                 else:
                     ns_params["vimAccountId"] == vnfr["vim-account-id"]
 
                 else:
                     ns_params["vimAccountId"] == vnfr["vim-account-id"]
 
+            if self.ng_ro:
+                return await self._instantiate_ng_ro(logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs,
+                                                     db_vnfds_ref, n2vc_key_list, stage, start_deploy,
+                                                     timeout_ns_deploy)
             # deploy RO
             # get vnfds, instantiate at RO
             for c_vnf in nsd.get("constituent-vnfd", ()):
             # deploy RO
             # get vnfds, instantiate at RO
             for c_vnf in nsd.get("constituent-vnfd", ()):
@@ -988,11 +1277,39 @@ class NsLcm(LcmBase):
             self._write_op_status(nslcmop_id, stage)
             # await self._on_update_n2vc_db("nsrs", {"_id": nsr_id}, "_admin.deployed", db_nsr_update)
             # self.logger.debug(logging_text + "Deployed at VIM")
             self._write_op_status(nslcmop_id, stage)
             # await self._on_update_n2vc_db("nsrs", {"_id": nsr_id}, "_admin.deployed", db_nsr_update)
             # self.logger.debug(logging_text + "Deployed at VIM")
-        except (ROclient.ROClientException, LcmException, DbException) as e:
+        except (ROclient.ROClientException, LcmException, DbException, NgRoException) as e:
             stage[2] = "ERROR deploying at VIM"
             self.set_vnfr_at_error(db_vnfrs, str(e))
             raise
 
             stage[2] = "ERROR deploying at VIM"
             self.set_vnfr_at_error(db_vnfrs, str(e))
             raise
 
+    async def wait_kdu_up(self, logging_text, nsr_id, vnfr_id, kdu_name):
+        """
+        Wait for kdu to be up, get ip address
+        :param logging_text: prefix use for logging
+        :param nsr_id:
+        :param vnfr_id:
+        :param kdu_name:
+        :return: IP address
+        """
+
+        # self.logger.debug(logging_text + "Starting wait_kdu_up")
+        nb_tries = 0
+
+        while nb_tries < 360:
+            db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
+            kdur = next((x for x in get_iterable(db_vnfr, "kdur") if x.get("kdu-name") == kdu_name), None)
+            if not kdur:
+                raise LcmException("Not found vnfr_id={}, kdu_name={}".format(vnfr_id, kdu_name))
+            if kdur.get("status"):
+                if kdur["status"] in ("READY", "ENABLED"):
+                    return kdur.get("ip-address")
+                else:
+                    raise LcmException("target KDU={} is in error state".format(kdu_name))
+
+            await asyncio.sleep(10, loop=self.loop)
+            nb_tries += 1
+        raise LcmException("Timeout waiting KDU={} instantiated".format(kdu_name))
+
     async def wait_vm_up_insert_key_ro(self, logging_text, nsr_id, vnfr_id, vdu_id, vdu_index, pub_key=None, user=None):
         """
         Wait for ip addres at RO, and optionally, insert public key in virtual machine
     async def wait_vm_up_insert_key_ro(self, logging_text, nsr_id, vnfr_id, vdu_id, vdu_index, pub_key=None, user=None):
         """
         Wait for ip addres at RO, and optionally, insert public key in virtual machine
@@ -1068,21 +1385,29 @@ class NsLcm(LcmBase):
                     return ip_address
                 try:
                     ro_vm_id = "{}-{}".format(db_vnfr["member-vnf-index-ref"], target_vdu_id)  # TODO add vdu_index
                     return ip_address
                 try:
                     ro_vm_id = "{}-{}".format(db_vnfr["member-vnf-index-ref"], target_vdu_id)  # TODO add vdu_index
-                    result_dict = await self.RO.create_action(
-                        item="ns",
-                        item_id_name=ro_nsr_id,
-                        descriptor={"add_public_key": pub_key, "vms": [ro_vm_id], "user": user}
-                    )
-                    # result_dict contains the format {VM-id: {vim_result: 200, description: text}}
-                    if not result_dict or not isinstance(result_dict, dict):
-                        raise LcmException("Unknown response from RO when injecting key")
-                    for result in result_dict.values():
-                        if result.get("vim_result") == 200:
-                            break
-                        else:
-                            raise ROclient.ROClientException("error injecting key: {}".format(
-                                result.get("description")))
-                    break
+                    if self.ng_ro:
+                        target = {"action": "inject_ssh_key", "key": pub_key, "user": user,
+                                  "vnf": [{"_id": vnfr_id, "vdur": [{"id": vdu_id}]}],
+                                  }
+                        await self.RO.deploy(nsr_id, target)
+                    else:
+                        result_dict = await self.RO.create_action(
+                            item="ns",
+                            item_id_name=ro_nsr_id,
+                            descriptor={"add_public_key": pub_key, "vms": [ro_vm_id], "user": user}
+                        )
+                        # result_dict contains the format {VM-id: {vim_result: 200, description: text}}
+                        if not result_dict or not isinstance(result_dict, dict):
+                            raise LcmException("Unknown response from RO when injecting key")
+                        for result in result_dict.values():
+                            if result.get("vim_result") == 200:
+                                break
+                            else:
+                                raise ROclient.ROClientException("error injecting key: {}".format(
+                                    result.get("description")))
+                        break
+                except NgRoException as e:
+                    raise LcmException("Reaching max tries injecting key. Error: {}".format(e))
                 except ROclient.ROClientException as e:
                     if not nb_tries:
                         self.logger.debug(logging_text + "error injecting key: {}. Retrying until {} seconds".
                 except ROclient.ROClientException as e:
                     if not nb_tries:
                         self.logger.debug(logging_text + "error injecting key: {}. Retrying until {} seconds".
@@ -1130,11 +1455,13 @@ class NsLcm(LcmBase):
         raise LcmException("Configuration aborted because dependent charm/s timeout")
 
     async def instantiate_N2VC(self, logging_text, vca_index, nsi_id, db_nsr, db_vnfr, vdu_id, kdu_name, vdu_index,
         raise LcmException("Configuration aborted because dependent charm/s timeout")
 
     async def instantiate_N2VC(self, logging_text, vca_index, nsi_id, db_nsr, db_vnfr, vdu_id, kdu_name, vdu_index,
-                               config_descriptor, deploy_params, base_folder, nslcmop_id, stage):
+                               config_descriptor, deploy_params, base_folder, nslcmop_id, stage, vca_type, vca_name,
+                               ee_config_descriptor):
         nsr_id = db_nsr["_id"]
         db_update_entry = "_admin.deployed.VCA.{}.".format(vca_index)
         vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
         vca_deployed = db_nsr["_admin"]["deployed"]["VCA"][vca_index]
         nsr_id = db_nsr["_id"]
         db_update_entry = "_admin.deployed.VCA.{}.".format(vca_index)
         vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
         vca_deployed = db_nsr["_admin"]["deployed"]["VCA"][vca_index]
+        osm_config = {"osm": {"ns_id": db_nsr["_id"]}}
         db_dict = {
             'collection': 'nsrs',
             'filter': {'_id': nsr_id},
         db_dict = {
             'collection': 'nsrs',
             'filter': {'_id': nsr_id},
@@ -1149,6 +1476,7 @@ class NsLcm(LcmBase):
             vnfr_id = None
             if db_vnfr:
                 vnfr_id = db_vnfr["_id"]
             vnfr_id = None
             if db_vnfr:
                 vnfr_id = db_vnfr["_id"]
+                osm_config["osm"]["vnf_id"] = vnfr_id
 
             namespace = "{nsi}.{ns}".format(
                 nsi=nsi_id if nsi_id else "",
 
             namespace = "{nsi}.{ns}".format(
                 nsi=nsi_id if nsi_id else "",
@@ -1162,30 +1490,40 @@ class NsLcm(LcmBase):
                     namespace += ".{}-{}".format(vdu_id, vdu_index or 0)
                     element_type = 'VDU'
                     element_under_configuration = "{}-{}".format(vdu_id, vdu_index or 0)
                     namespace += ".{}-{}".format(vdu_id, vdu_index or 0)
                     element_type = 'VDU'
                     element_under_configuration = "{}-{}".format(vdu_id, vdu_index or 0)
+                    osm_config["osm"]["vdu_id"] = vdu_id
                 elif kdu_name:
                     namespace += ".{}".format(kdu_name)
                     element_type = 'KDU'
                     element_under_configuration = kdu_name
                 elif kdu_name:
                     namespace += ".{}".format(kdu_name)
                     element_type = 'KDU'
                     element_under_configuration = kdu_name
+                    osm_config["osm"]["kdu_name"] = kdu_name
 
             # Get artifact path
 
             # Get artifact path
-            self.fs.sync()  # Sync from FSMongo
-            artifact_path = "{}/{}/charms/{}".format(
+            artifact_path = "{}/{}/{}/{}".format(
                 base_folder["folder"],
                 base_folder["pkg-dir"],
                 base_folder["folder"],
                 base_folder["pkg-dir"],
-                config_descriptor["juju"]["charm"]
+                "charms" if vca_type in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm") else "helm-charts",
+                vca_name
             )
             )
+            # get initial_config_primitive_list that applies to this element
+            initial_config_primitive_list = config_descriptor.get('initial-config-primitive')
 
 
-            is_proxy_charm = deep_get(config_descriptor, ('juju', 'charm')) is not None
-            if deep_get(config_descriptor, ('juju', 'proxy')) is False:
-                is_proxy_charm = False
+            # add config if not present for NS charm
+            ee_descriptor_id = ee_config_descriptor.get("id")
+            initial_config_primitive_list = self._get_initial_config_primitive_list(initial_config_primitive_list,
+                                                                                    vca_deployed, ee_descriptor_id)
 
             # n2vc_redesign STEP 3.1
 
             # n2vc_redesign STEP 3.1
-
             # find old ee_id if exists
             ee_id = vca_deployed.get("ee_id")
 
             # find old ee_id if exists
             ee_id = vca_deployed.get("ee_id")
 
+            vim_account_id = (
+                deep_get(db_vnfr, ("vim-account-id",)) or
+                deep_get(deploy_params, ("OSM", "vim_account_id"))
+            )
+            vca_cloud, vca_cloud_credential = self.get_vca_cloud_and_credentials(vim_account_id)
+            vca_k8s_cloud, vca_k8s_cloud_credential = self.get_vca_k8s_cloud_and_credentials(vim_account_id)
             # create or register execution environment in VCA
             # create or register execution environment in VCA
-            if is_proxy_charm:
+            if vca_type in ("lxc_proxy_charm", "k8s_proxy_charm", "helm", "helm-v3"):
 
                 self._write_configuration_status(
                     nsr_id=nsr_id,
 
                 self._write_configuration_status(
                     nsr_id=nsr_id,
@@ -1197,11 +1535,29 @@ class NsLcm(LcmBase):
 
                 step = "create execution environment"
                 self.logger.debug(logging_text + step)
 
                 step = "create execution environment"
                 self.logger.debug(logging_text + step)
-                ee_id, credentials = await self.n2vc.create_execution_environment(namespace=namespace,
-                                                                                  reuse_ee_id=ee_id,
-                                                                                  db_dict=db_dict)
 
 
-            else:
+                ee_id = None
+                credentials = None
+                if vca_type == "k8s_proxy_charm":
+                    ee_id = await self.vca_map[vca_type].install_k8s_proxy_charm(
+                        charm_name=artifact_path[artifact_path.rfind("/") + 1:],
+                        namespace=namespace,
+                        artifact_path=artifact_path,
+                        db_dict=db_dict,
+                        cloud_name=vca_k8s_cloud,
+                        credential_name=vca_k8s_cloud_credential,
+                    )
+                else:
+                    ee_id, credentials = await self.vca_map[vca_type].create_execution_environment(
+                        namespace=namespace,
+                        reuse_ee_id=ee_id,
+                        db_dict=db_dict,
+                        config=osm_config,
+                        cloud_name=vca_cloud,
+                        credential_name=vca_cloud_credential,
+                    )
+
+            elif vca_type == "native_charm":
                 step = "Waiting to VM being up and getting IP address"
                 self.logger.debug(logging_text + step)
                 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(logging_text, nsr_id, vnfr_id, vdu_id, vdu_index,
                 step = "Waiting to VM being up and getting IP address"
                 self.logger.debug(logging_text + step)
                 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(logging_text, nsr_id, vnfr_id, vdu_id, vdu_index,
@@ -1211,14 +1567,14 @@ class NsLcm(LcmBase):
                 username = deep_get(config_descriptor, ("config-access", "ssh-access", "default-user"))
                 # TODO remove this when changes on IM regarding config-access:ssh-access:default-user were
                 #  merged. Meanwhile let's get username from initial-config-primitive
                 username = deep_get(config_descriptor, ("config-access", "ssh-access", "default-user"))
                 # TODO remove this when changes on IM regarding config-access:ssh-access:default-user were
                 #  merged. Meanwhile let's get username from initial-config-primitive
-                if not username and config_descriptor.get("initial-config-primitive"):
-                    for config_primitive in config_descriptor["initial-config-primitive"]:
+                if not username and initial_config_primitive_list:
+                    for config_primitive in initial_config_primitive_list:
                         for param in config_primitive.get("parameter", ()):
                             if param["name"] == "ssh-username":
                                 username = param["value"]
                                 break
                 if not username:
                         for param in config_primitive.get("parameter", ()):
                             if param["name"] == "ssh-username":
                                 username = param["value"]
                                 break
                 if not username:
-                    raise LcmException("Cannot determine the username neither with 'initial-config-promitive' nor with "
+                    raise LcmException("Cannot determine the username neither with 'initial-config-primitive' nor with "
                                        "'config-access.ssh-access.default-user'")
                 credentials["username"] = username
                 # n2vc_redesign STEP 3.2
                                        "'config-access.ssh-access.default-user'")
                 credentials["username"] = username
                 # n2vc_redesign STEP 3.2
@@ -1233,20 +1589,25 @@ class NsLcm(LcmBase):
 
                 step = "register execution environment {}".format(credentials)
                 self.logger.debug(logging_text + step)
 
                 step = "register execution environment {}".format(credentials)
                 self.logger.debug(logging_text + step)
-                ee_id = await self.n2vc.register_execution_environment(credentials=credentials, namespace=namespace,
-                                                                       db_dict=db_dict)
+                ee_id = await self.vca_map[vca_type].register_execution_environment(
+                    credentials=credentials,
+                    namespace=namespace,
+                    db_dict=db_dict,
+                    cloud_name=vca_cloud,
+                    credential_name=vca_cloud_credential,
+                )
 
             # for compatibility with MON/POL modules, the need model and application name at database
 
             # for compatibility with MON/POL modules, the need model and application name at database
-            # TODO ask to N2VC instead of assuming the format "model_name.application_name"
+            # TODO ask MON/POL if needed to not assuming anymore the format "model_name.application_name"
             ee_id_parts = ee_id.split('.')
             ee_id_parts = ee_id.split('.')
-            model_name = ee_id_parts[0]
-            application_name = ee_id_parts[1]
-            db_nsr_update = {db_update_entry + "model": model_name,
-                             db_update_entry + "application": application_name,
-                             db_update_entry + "ee_id": ee_id}
+            db_nsr_update = {db_update_entry + "ee_id": ee_id}
+            if len(ee_id_parts) >= 2:
+                model_name = ee_id_parts[0]
+                application_name = ee_id_parts[1]
+                db_nsr_update[db_update_entry + "model"] = model_name
+                db_nsr_update[db_update_entry + "application"] = application_name
 
             # n2vc_redesign STEP 3.3
 
             # n2vc_redesign STEP 3.3
-
             step = "Install configuration Software"
 
             self._write_configuration_status(
             step = "Install configuration Software"
 
             self._write_configuration_status(
@@ -1261,50 +1622,68 @@ class NsLcm(LcmBase):
             # TODO check if already done
             self.logger.debug(logging_text + step)
             config = None
             # TODO check if already done
             self.logger.debug(logging_text + step)
             config = None
-            if not is_proxy_charm:
-                initial_config_primitive_list = config_descriptor.get('initial-config-primitive')
-                if initial_config_primitive_list:
-                    for primitive in initial_config_primitive_list:
-                        if primitive["name"] == "config":
-                            config = self._map_primitive_params(
-                                primitive,
-                                {},
-                                deploy_params
-                            )
+            if vca_type == "native_charm":
+                config_primitive = next((p for p in initial_config_primitive_list if p["name"] == "config"), None)
+                if config_primitive:
+                    config = self._map_primitive_params(
+                        config_primitive,
+                        {},
+                        deploy_params
+                    )
+            num_units = 1
+            if vca_type == "lxc_proxy_charm":
+                if element_type == "NS":
+                    num_units = db_nsr.get("config-units") or 1
+                elif element_type == "VNF":
+                    num_units = db_vnfr.get("config-units") or 1
+                elif element_type == "VDU":
+                    for v in db_vnfr["vdur"]:
+                        if vdu_id == v["vdu-id-ref"]:
+                            num_units = v.get("config-units") or 1
                             break
                             break
-            await self.n2vc.install_configuration_sw(
-                ee_id=ee_id,
-                artifact_path=artifact_path,
-                db_dict=db_dict,
-                config=config
-            )
+            if vca_type != "k8s_proxy_charm":
+                await self.vca_map[vca_type].install_configuration_sw(
+                    ee_id=ee_id,
+                    artifact_path=artifact_path,
+                    db_dict=db_dict,
+                    config=config,
+                    num_units=num_units,
+                )
 
             # write in db flag of configuration_sw already installed
             self.update_db_2("nsrs", nsr_id, {db_update_entry + "config_sw_installed": True})
 
             # add relations for this VCA (wait for other peers related with this VCA)
 
             # write in db flag of configuration_sw already installed
             self.update_db_2("nsrs", nsr_id, {db_update_entry + "config_sw_installed": True})
 
             # add relations for this VCA (wait for other peers related with this VCA)
-            await self._add_vca_relations(logging_text=logging_text, nsr_id=nsr_id, vca_index=vca_index)
+            await self._add_vca_relations(logging_text=logging_text, nsr_id=nsr_id,
+                                          vca_index=vca_index, vca_type=vca_type)
 
             # if SSH access is required, then get execution environment SSH public
 
             # if SSH access is required, then get execution environment SSH public
-            if is_proxy_charm:  # if native charm we have waited already to VM be UP
+            # if native charm we have waited already to VM be UP
+            if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"):
                 pub_key = None
                 user = None
                 pub_key = None
                 user = None
+                # self.logger.debug("get ssh key block")
                 if deep_get(config_descriptor, ("config-access", "ssh-access", "required")):
                 if deep_get(config_descriptor, ("config-access", "ssh-access", "required")):
+                    # self.logger.debug("ssh key needed")
                     # Needed to inject a ssh key
                     user = deep_get(config_descriptor, ("config-access", "ssh-access", "default-user"))
                     step = "Install configuration Software, getting public ssh key"
                     # Needed to inject a ssh key
                     user = deep_get(config_descriptor, ("config-access", "ssh-access", "default-user"))
                     step = "Install configuration Software, getting public ssh key"
-                    pub_key = await self.n2vc.get_ee_ssh_public__key(ee_id=ee_id, db_dict=db_dict)
+                    pub_key = await self.vca_map[vca_type].get_ee_ssh_public__key(ee_id=ee_id, db_dict=db_dict)
 
                     step = "Insert public key into VM user={} ssh_key={}".format(user, pub_key)
                 else:
 
                     step = "Insert public key into VM user={} ssh_key={}".format(user, pub_key)
                 else:
+                    # self.logger.debug("no need to get ssh key")
                     step = "Waiting to VM being up and getting IP address"
                 self.logger.debug(logging_text + step)
 
                 # n2vc_redesign STEP 5.1
                 # wait for RO (ip-address) Insert pub_key into VM
                 if vnfr_id:
                     step = "Waiting to VM being up and getting IP address"
                 self.logger.debug(logging_text + step)
 
                 # n2vc_redesign STEP 5.1
                 # wait for RO (ip-address) Insert pub_key into VM
                 if vnfr_id:
-                    rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(logging_text, nsr_id, vnfr_id, vdu_id, vdu_index,
-                                                                     user=user, pub_key=pub_key)
+                    if kdu_name:
+                        rw_mgmt_ip = await self.wait_kdu_up(logging_text, nsr_id, vnfr_id, kdu_name)
+                    else:
+                        rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(logging_text, nsr_id, vnfr_id, vdu_id,
+                                                                         vdu_index, user=user, pub_key=pub_key)
                 else:
                     rw_mgmt_ip = None   # This is for a NS configuration
 
                 else:
                     rw_mgmt_ip = None   # This is for a NS configuration
 
@@ -1315,20 +1694,6 @@ class NsLcm(LcmBase):
 
             # n2vc_redesign STEP 6  Execute initial config primitive
             step = 'execute initial config primitive'
 
             # n2vc_redesign STEP 6  Execute initial config primitive
             step = 'execute initial config primitive'
-            initial_config_primitive_list = config_descriptor.get('initial-config-primitive')
-
-            # sort initial config primitives by 'seq'
-            if initial_config_primitive_list:
-                try:
-                    initial_config_primitive_list.sort(key=lambda val: int(val['seq']))
-                except Exception as e:
-                    self.logger.error(logging_text + step + ": " + str(e))
-            else:
-                self.logger.debug(logging_text + step + ": No initial-config-primitive")
-
-            # add config if not present for NS charm
-            initial_config_primitive_list = self._get_initial_config_primitive_list(initial_config_primitive_list,
-                                                                                    vca_deployed)
 
             # wait for dependent primitives execution (NS -> VNF -> VDU)
             if initial_config_primitive_list:
 
             # wait for dependent primitives execution (NS -> VNF -> VDU)
             if initial_config_primitive_list:
@@ -1367,7 +1732,7 @@ class NsLcm(LcmBase):
 
                 step = "execute primitive '{}' params '{}'".format(initial_config_primitive["name"], primitive_params_)
                 self.logger.debug(logging_text + step)
 
                 step = "execute primitive '{}' params '{}'".format(initial_config_primitive["name"], primitive_params_)
                 self.logger.debug(logging_text + step)
-                await self.n2vc.exec_primitive(
+                await self.vca_map[vca_type].exec_primitive(
                     ee_id=ee_id,
                     primitive_name=initial_config_primitive["name"],
                     params_dict=primitive_params_,
                     ee_id=ee_id,
                     primitive_name=initial_config_primitive["name"],
                     params_dict=primitive_params_,
@@ -1381,6 +1746,19 @@ class NsLcm(LcmBase):
 
                 # TODO register in database that primitive is done
 
 
                 # TODO register in database that primitive is done
 
+            # STEP 7 Configure metrics
+            if vca_type == "helm" or vca_type == "helm-v3":
+                prometheus_jobs = await self.add_prometheus_metrics(
+                    ee_id=ee_id,
+                    artifact_path=artifact_path,
+                    ee_config_descriptor=ee_config_descriptor,
+                    vnfr_id=vnfr_id,
+                    nsr_id=nsr_id,
+                    target_ip=rw_mgmt_ip,
+                )
+                if prometheus_jobs:
+                    self.update_db_2("nsrs", nsr_id, {db_update_entry + "prometheus_jobs": prometheus_jobs})
+
             step = "instantiated at VCA"
             self.logger.debug(logging_text + step)
 
             step = "instantiated at VCA"
             self.logger.debug(logging_text + step)
 
@@ -1573,8 +1951,11 @@ class NsLcm(LcmBase):
             # wait for any previous tasks in process
             await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
 
             # wait for any previous tasks in process
             await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
 
+            stage[1] = "Sync filesystem from database."
+            self.fs.sync()  # TODO, make use of partial sync, only for the needed packages
+
             # STEP 0: Reading database (nslcmops, nsrs, nsds, vnfrs, vnfds)
             # STEP 0: Reading database (nslcmops, nsrs, nsds, vnfrs, vnfds)
-            stage[1] = "Reading from database,"
+            stage[1] = "Reading from database."
             # nsState="BUILDING", currentOperation="INSTANTIATING", currentOperationID=nslcmop_id
             db_nsr_update["detailed-status"] = "creating"
             db_nsr_update["operational-status"] = "init"
             # nsState="BUILDING", currentOperation="INSTANTIATING", currentOperationID=nslcmop_id
             db_nsr_update["detailed-status"] = "creating"
             db_nsr_update["operational-status"] = "init"
@@ -1592,7 +1973,7 @@ class NsLcm(LcmBase):
             )
 
             # read from db: operation
             )
 
             # read from db: operation
-            stage[1] = "Getting nslcmop={} from db".format(nslcmop_id)
+            stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id)
             db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
             ns_params = db_nslcmop.get("operationParams")
             if ns_params and ns_params.get("timeout_ns_deploy"):
             db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
             ns_params = db_nslcmop.get("operationParams")
             if ns_params and ns_params.get("timeout_ns_deploy"):
@@ -1601,14 +1982,15 @@ class NsLcm(LcmBase):
                 timeout_ns_deploy = self.timeout.get("ns_deploy", self.timeout_ns_deploy)
 
             # read from db: ns
                 timeout_ns_deploy = self.timeout.get("ns_deploy", self.timeout_ns_deploy)
 
             # read from db: ns
-            stage[1] = "Getting nsr={} from db".format(nsr_id)
+            stage[1] = "Getting nsr={} from db.".format(nsr_id)
             db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
             db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
-            # nsd is replicated into ns (no db read)
-            nsd = db_nsr["nsd"]
+            stage[1] = "Getting nsd={} from db.".format(db_nsr["nsd-id"])
+            nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
+            db_nsr["nsd"] = nsd
             # nsr_name = db_nsr["name"]   # TODO short-name??
 
             # read from db: vnf's of this ns
             # nsr_name = db_nsr["name"]   # TODO short-name??
 
             # read from db: vnf's of this ns
-            stage[1] = "Getting vnfrs from db"
+            stage[1] = "Getting vnfrs from db."
             self.logger.debug(logging_text + stage[1])
             db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
 
             self.logger.debug(logging_text + stage[1])
             db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
 
@@ -1622,10 +2004,11 @@ class NsLcm(LcmBase):
                 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr   # vnf's dict indexed by member-index: '1', '2', etc
                 vnfd_id = vnfr["vnfd-id"]                       # vnfd uuid for this vnf
                 vnfd_ref = vnfr["vnfd-ref"]                     # vnfd name for this vnf
                 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr   # vnf's dict indexed by member-index: '1', '2', etc
                 vnfd_id = vnfr["vnfd-id"]                       # vnfd uuid for this vnf
                 vnfd_ref = vnfr["vnfd-ref"]                     # vnfd name for this vnf
+
                 # if we haven't this vnfd, read it from db
                 if vnfd_id not in db_vnfds:
                     # read from db
                 # if we haven't this vnfd, read it from db
                 if vnfd_id not in db_vnfds:
                     # read from db
-                    stage[1] = "Getting vnfd={} id='{}' from db".format(vnfd_id, vnfd_ref)
+                    stage[1] = "Getting vnfd={} id='{}' from db.".format(vnfd_id, vnfd_ref)
                     self.logger.debug(logging_text + stage[1])
                     vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
 
                     self.logger.debug(logging_text + stage[1])
                     vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
 
@@ -1658,6 +2041,7 @@ class NsLcm(LcmBase):
             # set state to INSTANTIATED. When instantiated NBI will not delete directly
             db_nsr_update["_admin.nsState"] = "INSTANTIATED"
             self.update_db_2("nsrs", nsr_id, db_nsr_update)
             # set state to INSTANTIATED. When instantiated NBI will not delete directly
             db_nsr_update["_admin.nsState"] = "INSTANTIATED"
             self.update_db_2("nsrs", nsr_id, db_nsr_update)
+            self.db.set_list("vnfrs", {"nsr-id-ref": nsr_id}, {"_admin.nsState": "INSTANTIATED"})
 
             # n2vc_redesign STEP 2 Deploy Network Scenario
             stage[0] = 'Stage 2/5: deployment of KDUs, VMs and execution environments.'
 
             # n2vc_redesign STEP 2 Deploy Network Scenario
             stage[0] = 'Stage 2/5: deployment of KDUs, VMs and execution environments.'
@@ -1666,7 +2050,7 @@ class NsLcm(LcmBase):
                 stage=stage
             )
 
                 stage=stage
             )
 
-            stage[1] = "Deploying KDUs,"
+            stage[1] = "Deploying KDUs."
             # self.logger.debug(logging_text + "Before deploy_kdus")
             # Call to deploy_kdus in case exists the "vdu:kdu" param
             await self.deploy_kdus(
             # self.logger.debug(logging_text + "Before deploy_kdus")
             # Call to deploy_kdus in case exists the "vdu:kdu" param
             await self.deploy_kdus(
@@ -1721,12 +2105,12 @@ class NsLcm(LcmBase):
                 kdu_name = None
 
                 # Get additional parameters
                 kdu_name = None
 
                 # Get additional parameters
-                deploy_params = {}
+                deploy_params = {"OSM": self._get_osm_params(db_vnfr)}
                 if db_vnfr.get("additionalParamsForVnf"):
                 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")
 
                 descriptor_config = vnfd.get("vnf-configuration")
-                if descriptor_config and descriptor_config.get("juju"):
+                if descriptor_config:
                     self._deploy_n2vc(
                         logging_text=logging_text + "member_vnf_index={} ".format(member_vnf_index),
                         db_nsr=db_nsr,
                     self._deploy_n2vc(
                         logging_text=logging_text + "member_vnf_index={} ".format(member_vnf_index),
                         db_nsr=db_nsr,
@@ -1756,15 +2140,8 @@ class NsLcm(LcmBase):
                         deploy_params_vdu = self._format_additional_params(vdur["additionalParams"])
                     else:
                         deploy_params_vdu = deploy_params
                         deploy_params_vdu = self._format_additional_params(vdur["additionalParams"])
                     else:
                         deploy_params_vdu = deploy_params
-                    if descriptor_config and descriptor_config.get("juju"):
-                        # 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")
+                    deploy_params_vdu["OSM"] = self._get_osm_params(db_vnfr, vdu_id, vdu_count_index=0)
+                    if descriptor_config:
                         vdu_name = None
                         kdu_name = None
                         for vdu_index in range(int(vdud.get("count", 1))):
                         vdu_name = None
                         kdu_name = None
                         for vdu_index in range(int(vdud.get("count", 1))):
@@ -1792,19 +2169,14 @@ class NsLcm(LcmBase):
                 for kdud in get_iterable(vnfd, 'kdu'):
                     kdu_name = kdud["name"]
                     descriptor_config = kdud.get('kdu-configuration')
                 for kdud in get_iterable(vnfd, 'kdu'):
                     kdu_name = kdud["name"]
                     descriptor_config = kdud.get('kdu-configuration')
-                    if descriptor_config and descriptor_config.get("juju"):
+                    if descriptor_config:
                         vdu_id = None
                         vdu_index = 0
                         vdu_name = None
                         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,
 
                         self._deploy_n2vc(
                             logging_text=logging_text,
@@ -1819,7 +2191,7 @@ class NsLcm(LcmBase):
                             member_vnf_index=member_vnf_index,
                             vdu_index=vdu_index,
                             vdu_name=vdu_name,
                             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,
                             descriptor_config=descriptor_config,
                             base_folder=base_folder,
                             task_instantiation_info=tasks_dict_info,
@@ -1838,9 +2210,9 @@ class NsLcm(LcmBase):
                 vdu_name = None
 
                 # Get additional parameters
                 vdu_name = None
 
                 # Get additional parameters
-                deploy_params = {}
+                deploy_params = {"OSM": self._get_osm_params(db_vnfr)}
                 if db_nsr.get("additionalParamsForNs"):
                 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,
                 base_folder = nsd["_admin"]["storage"]
                 self._deploy_n2vc(
                     logging_text=logging_text,
@@ -1907,8 +2279,8 @@ class NsLcm(LcmBase):
             if error_list:
                 error_detail = ". ".join(error_list)
                 self.logger.error(logging_text + error_detail)
             if error_list:
                 error_detail = ". ".join(error_list)
                 self.logger.error(logging_text + error_detail)
-                error_description_nslcmop = 'Stage: {}. Detail: {}'.format(stage[0], error_detail)
-                error_description_nsr = 'Operation: INSTANTIATING.{}, Stage {}'.format(nslcmop_id, stage[0])
+                error_description_nslcmop = '{} Detail: {}'.format(stage[0], error_detail)
+                error_description_nsr = 'Operation: INSTANTIATING.{}, {}'.format(nslcmop_id, stage[0])
 
                 db_nsr_update["detailed-status"] = error_description_nsr + " Detail: " + error_detail
                 db_nslcmop_update["detailed-status"] = error_detail
 
                 db_nsr_update["detailed-status"] = error_description_nsr + " Detail: " + error_detail
                 db_nslcmop_update["detailed-status"] = error_detail
@@ -1951,7 +2323,8 @@ class NsLcm(LcmBase):
             self.logger.debug(logging_text + "Exit")
             self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_instantiate")
 
             self.logger.debug(logging_text + "Exit")
             self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_instantiate")
 
-    async def _add_vca_relations(self, logging_text, nsr_id, vca_index: int, timeout: int = 3600) -> bool:
+    async def _add_vca_relations(self, logging_text, nsr_id, vca_index: int,
+                                 timeout: int = 3600, vca_type: str = None) -> bool:
 
         # steps:
         # 1. find all relations for this VCA
 
         # steps:
         # 1. find all relations for this VCA
@@ -1959,18 +2332,20 @@ class NsLcm(LcmBase):
         # 3. add relations
 
         try:
         # 3. add relations
 
         try:
+            vca_type = vca_type or "lxc_proxy_charm"
 
             # STEP 1: find all relations for this VCA
 
             # read nsr record
             db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
 
             # STEP 1: find all relations for this VCA
 
             # read nsr record
             db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
+            nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
 
             # this VCA data
             my_vca = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))[vca_index]
 
             # read all ns-configuration relations
             ns_relations = list()
 
             # this VCA data
             my_vca = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))[vca_index]
 
             # read all ns-configuration relations
             ns_relations = list()
-            db_ns_relations = deep_get(db_nsr, ('nsd', 'ns-configuration', 'relation'))
+            db_ns_relations = deep_get(nsd, ('ns-configuration', 'relation'))
             if db_ns_relations:
                 for r in db_ns_relations:
                     # check if this VCA is in the relation
             if db_ns_relations:
                 for r in db_ns_relations:
                     # check if this VCA is in the relation
@@ -2011,7 +2386,7 @@ class NsLcm(LcmBase):
                 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
 
                 # for each defined NS relation, find the VCA's related
                 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
 
                 # for each defined NS relation, find the VCA's related
-                for r in ns_relations:
+                for r in ns_relations.copy():
                     from_vca_ee_id = None
                     to_vca_ee_id = None
                     from_vca_endpoint = None
                     from_vca_ee_id = None
                     to_vca_ee_id = None
                     from_vca_endpoint = None
@@ -2028,7 +2403,7 @@ class NsLcm(LcmBase):
                             to_vca_endpoint = r.get('entities')[1].get('endpoint')
                     if from_vca_ee_id and to_vca_ee_id:
                         # add relation
                             to_vca_endpoint = r.get('entities')[1].get('endpoint')
                     if from_vca_ee_id and to_vca_ee_id:
                         # add relation
-                        await self.n2vc.add_relation(
+                        await self.vca_map[vca_type].add_relation(
                             ee_id_1=from_vca_ee_id,
                             ee_id_2=to_vca_ee_id,
                             endpoint_1=from_vca_endpoint,
                             ee_id_1=from_vca_ee_id,
                             ee_id_2=to_vca_ee_id,
                             endpoint_1=from_vca_endpoint,
@@ -2056,22 +2431,25 @@ class NsLcm(LcmBase):
                             pass
 
                 # for each defined VNF relation, find the VCA's related
                             pass
 
                 # for each defined VNF relation, find the VCA's related
-                for r in vnf_relations:
+                for r in vnf_relations.copy():
                     from_vca_ee_id = None
                     to_vca_ee_id = None
                     from_vca_endpoint = None
                     to_vca_endpoint = None
                     vca_list = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))
                     for vca in vca_list:
                     from_vca_ee_id = None
                     to_vca_ee_id = None
                     from_vca_endpoint = None
                     to_vca_endpoint = None
                     vca_list = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))
                     for vca in vca_list:
-                        if vca.get('vdu_id') == r.get('entities')[0].get('id') and vca.get('config_sw_installed'):
+                        key_to_check = "vdu_id"
+                        if vca.get("vdu_id") is None:
+                            key_to_check = "vnfd_id"
+                        if vca.get(key_to_check) == r.get('entities')[0].get('id') and vca.get('config_sw_installed'):
                             from_vca_ee_id = vca.get('ee_id')
                             from_vca_endpoint = r.get('entities')[0].get('endpoint')
                             from_vca_ee_id = vca.get('ee_id')
                             from_vca_endpoint = r.get('entities')[0].get('endpoint')
-                        if vca.get('vdu_id') == r.get('entities')[1].get('id') and vca.get('config_sw_installed'):
+                        if vca.get(key_to_check) == r.get('entities')[1].get('id') and vca.get('config_sw_installed'):
                             to_vca_ee_id = vca.get('ee_id')
                             to_vca_endpoint = r.get('entities')[1].get('endpoint')
                     if from_vca_ee_id and to_vca_ee_id:
                         # add relation
                             to_vca_ee_id = vca.get('ee_id')
                             to_vca_endpoint = r.get('entities')[1].get('endpoint')
                     if from_vca_ee_id and to_vca_ee_id:
                         # add relation
-                        await self.n2vc.add_relation(
+                        await self.vca_map[vca_type].add_relation(
                             ee_id_1=from_vca_ee_id,
                             ee_id_2=to_vca_ee_id,
                             endpoint_1=from_vca_endpoint,
                             ee_id_1=from_vca_ee_id,
                             ee_id_2=to_vca_ee_id,
                             endpoint_1=from_vca_endpoint,
@@ -2089,11 +2467,11 @@ class NsLcm(LcmBase):
                                     if vca.get('vdu_id') == r.get('entities')[0].get('id'):
                                         if vca_status.get('status') == 'BROKEN':
                                             # peer broken: remove relation from list
                                     if vca.get('vdu_id') == r.get('entities')[0].get('id'):
                                         if vca_status.get('status') == 'BROKEN':
                                             # peer broken: remove relation from list
-                                            ns_relations.remove(r)
+                                            vnf_relations.remove(r)
                                     if vca.get('vdu_id') == r.get('entities')[1].get('id'):
                                         if vca_status.get('status') == 'BROKEN':
                                             # peer broken: remove relation from list
                                     if vca.get('vdu_id') == r.get('entities')[1].get('id'):
                                         if vca_status.get('status') == 'BROKEN':
                                             # peer broken: remove relation from list
-                                            ns_relations.remove(r)
+                                            vnf_relations.remove(r)
                         except Exception:
                             # ignore
                             pass
                         except Exception:
                             # ignore
                             pass
@@ -2111,41 +2489,132 @@ class NsLcm(LcmBase):
             self.logger.warn(logging_text + ' ERROR adding relations: {}'.format(e))
             return False
 
             self.logger.warn(logging_text + ' ERROR adding relations: {}'.format(e))
             return False
 
-    def _write_db_callback(self, task, item, _id, on_done=None, on_exc=None):
-        """
-        callback for kdu install intended to store the returned kdu_instance at database
-        :return: None
-        """
-        db_update = {}
+    async def _install_kdu(self, nsr_id: str, nsr_db_path: str, vnfr_data: dict, kdu_index: int, kdud: dict,
+                           vnfd: dict, k8s_instance_info: dict, k8params: dict = None, timeout: int = 600):
+
         try:
         try:
-            result = task.result()
-            if on_done:
-                db_update[on_done] = str(result)
+            k8sclustertype = k8s_instance_info["k8scluster-type"]
+            # Instantiate kdu
+            db_dict_install = {"collection": "nsrs",
+                               "filter": {"_id": nsr_id},
+                               "path": nsr_db_path}
+
+            kdu_instance = await self.k8scluster_map[k8sclustertype].install(
+                cluster_uuid=k8s_instance_info["k8scluster-uuid"],
+                kdu_model=k8s_instance_info["kdu-model"],
+                atomic=True,
+                params=k8params,
+                db_dict=db_dict_install,
+                timeout=timeout,
+                kdu_name=k8s_instance_info["kdu-name"],
+                namespace=k8s_instance_info["namespace"])
+            self.update_db_2("nsrs", nsr_id, {nsr_db_path + ".kdu-instance": kdu_instance})
+
+            # Obtain services to obtain management service ip
+            services = await self.k8scluster_map[k8sclustertype].get_services(
+                cluster_uuid=k8s_instance_info["k8scluster-uuid"],
+                kdu_instance=kdu_instance,
+                namespace=k8s_instance_info["namespace"])
+
+            # Obtain management service info (if exists)
+            vnfr_update_dict = {}
+            if services:
+                vnfr_update_dict["kdur.{}.services".format(kdu_index)] = services
+                mgmt_services = [service for service in kdud.get("service", []) if service.get("mgmt-service")]
+                for mgmt_service in mgmt_services:
+                    for service in services:
+                        if service["name"].startswith(mgmt_service["name"]):
+                            # Mgmt service found, Obtain service ip
+                            ip = service.get("external_ip", service.get("cluster_ip"))
+                            if isinstance(ip, list) and len(ip) == 1:
+                                ip = ip[0]
+
+                            vnfr_update_dict["kdur.{}.ip-address".format(kdu_index)] = ip
+
+                            # Check if must update also mgmt ip at the vnf
+                            service_external_cp = mgmt_service.get("external-connection-point-ref")
+                            if service_external_cp:
+                                if deep_get(vnfd, ("mgmt-interface", "cp")) == service_external_cp:
+                                    vnfr_update_dict["ip-address"] = ip
+
+                            break
+                    else:
+                        self.logger.warn("Mgmt service name: {} not found".format(mgmt_service["name"]))
+
+            vnfr_update_dict["kdur.{}.status".format(kdu_index)] = "READY"
+            self.update_db_2("vnfrs", vnfr_data.get("_id"), vnfr_update_dict)
+
+            kdu_config = kdud.get("kdu-configuration")
+            if kdu_config and kdu_config.get("initial-config-primitive") and kdu_config.get("juju") is None:
+                initial_config_primitive_list = kdu_config.get("initial-config-primitive")
+                initial_config_primitive_list.sort(key=lambda val: int(val["seq"]))
+
+                for initial_config_primitive in initial_config_primitive_list:
+                    primitive_params_ = self._map_primitive_params(initial_config_primitive, {}, {})
+
+                    await asyncio.wait_for(
+                        self.k8scluster_map[k8sclustertype].exec_primitive(
+                            cluster_uuid=k8s_instance_info["k8scluster-uuid"],
+                            kdu_instance=kdu_instance,
+                            primitive_name=initial_config_primitive["name"],
+                            params=primitive_params_, db_dict={}),
+                        timeout=timeout)
+
         except Exception as e:
         except Exception as e:
-            if on_exc:
-                db_update[on_exc] = str(e)
-        if db_update:
+            # Prepare update db with error and raise exception
             try:
             try:
-                self.update_db_2(item, _id, db_update)
+                self.update_db_2("nsrs", nsr_id, {nsr_db_path + ".detailed-status": str(e)})
+                self.update_db_2("vnfrs", vnfr_data.get("_id"), {"kdur.{}.status".format(kdu_index): "ERROR"})
             except Exception:
             except Exception:
+                # ignore to keep original exception
                 pass
                 pass
+            # reraise original error
+            raise
+
+        return kdu_instance
 
     async def deploy_kdus(self, logging_text, nsr_id, nslcmop_id, db_vnfrs, db_vnfds, task_instantiation_info):
         # Launch kdus if present in the descriptor
 
 
     async def deploy_kdus(self, logging_text, nsr_id, nslcmop_id, db_vnfrs, db_vnfds, task_instantiation_info):
         # Launch kdus if present in the descriptor
 
-        k8scluster_id_2_uuic = {"helm-chart": {}, "juju-bundle": {}}
+        k8scluster_id_2_uuic = {"helm-chart-v3": {}, "helm-chart": {}, "juju-bundle": {}}
 
 
-        def _get_cluster_id(cluster_id, cluster_type):
+        async def _get_cluster_id(cluster_id, cluster_type):
             nonlocal k8scluster_id_2_uuic
             if cluster_id in k8scluster_id_2_uuic[cluster_type]:
                 return k8scluster_id_2_uuic[cluster_type][cluster_id]
 
             nonlocal k8scluster_id_2_uuic
             if cluster_id in k8scluster_id_2_uuic[cluster_type]:
                 return k8scluster_id_2_uuic[cluster_type][cluster_id]
 
+            # check if K8scluster is creating and wait look if previous tasks in process
+            task_name, task_dependency = self.lcm_tasks.lookfor_related("k8scluster", cluster_id)
+            if task_dependency:
+                text = "Waiting for related tasks '{}' on k8scluster {} to be completed".format(task_name, cluster_id)
+                self.logger.debug(logging_text + text)
+                await asyncio.wait(task_dependency, timeout=3600)
+
             db_k8scluster = self.db.get_one("k8sclusters", {"_id": cluster_id}, fail_on_empty=False)
             if not db_k8scluster:
                 raise LcmException("K8s cluster {} cannot be found".format(cluster_id))
             db_k8scluster = self.db.get_one("k8sclusters", {"_id": cluster_id}, fail_on_empty=False)
             if not db_k8scluster:
                 raise LcmException("K8s cluster {} cannot be found".format(cluster_id))
+
             k8s_id = deep_get(db_k8scluster, ("_admin", cluster_type, "id"))
             if not k8s_id:
             k8s_id = deep_get(db_k8scluster, ("_admin", cluster_type, "id"))
             if not k8s_id:
-                raise LcmException("K8s cluster '{}' has not been initilized for '{}'".format(cluster_id, cluster_type))
+                if cluster_type == "helm-chart-v3":
+                    try:
+                        # backward compatibility for existing clusters that have not been initialized for helm v3
+                        k8s_credentials = yaml.safe_dump(db_k8scluster.get("credentials"))
+                        k8s_id, uninstall_sw = await self.k8sclusterhelm3.init_env(k8s_credentials,
+                                                                                   reuse_cluster_uuid=cluster_id)
+                        db_k8scluster_update = {}
+                        db_k8scluster_update["_admin.helm-chart-v3.error_msg"] = None
+                        db_k8scluster_update["_admin.helm-chart-v3.id"] = k8s_id
+                        db_k8scluster_update["_admin.helm-chart-v3.created"] = uninstall_sw
+                        db_k8scluster_update["_admin.helm-chart-v3.operationalState"] = "ENABLED"
+                        self.update_db_2("k8sclusters", cluster_id, db_k8scluster_update)
+                    except Exception as e:
+                        self.logger.error(logging_text + "error initializing helm-v3 cluster: {}".format(str(e)))
+                        raise LcmException("K8s cluster '{}' has not been initialized for '{}'".format(cluster_id,
+                                                                                                       cluster_type))
+                else:
+                    raise LcmException("K8s cluster '{}' has not been initialized for '{}'".
+                                       format(cluster_id, cluster_type))
             k8scluster_id_2_uuic[cluster_type][cluster_id] = k8s_id
             return k8s_id
 
             k8scluster_id_2_uuic[cluster_type][cluster_id] = k8s_id
             return k8s_id
 
@@ -2157,15 +2626,22 @@ class NsLcm(LcmBase):
 
             index = 0
             updated_cluster_list = []
 
             index = 0
             updated_cluster_list = []
+            updated_v3_cluster_list = []
 
             for vnfr_data in db_vnfrs.values():
 
             for vnfr_data in db_vnfrs.values():
-                for kdur in get_iterable(vnfr_data, "kdur"):
+                for kdu_index, kdur in enumerate(get_iterable(vnfr_data, "kdur")):
+                    # Step 0: Prepare and set parameters
                     desc_params = self._format_additional_params(kdur.get("additionalParams"))
                     vnfd_id = vnfr_data.get('vnfd-id')
                     desc_params = self._format_additional_params(kdur.get("additionalParams"))
                     vnfd_id = vnfr_data.get('vnfd-id')
+                    kdud = next(kdud for kdud in db_vnfds[vnfd_id]["kdu"] if kdud["name"] == kdur["kdu-name"])
                     namespace = kdur.get("k8s-namespace")
                     if kdur.get("helm-chart"):
                         kdumodel = kdur["helm-chart"]
                     namespace = kdur.get("k8s-namespace")
                     if kdur.get("helm-chart"):
                         kdumodel = kdur["helm-chart"]
-                        k8sclustertype = "helm-chart"
+                        # Default version: helm3, if helm-version is v2 assign v2
+                        k8sclustertype = "helm-chart-v3"
+                        self.logger.debug("kdur: {}".format(kdur))
+                        if kdur.get("helm-version") and kdur.get("helm-version") == "v2":
+                            k8sclustertype = "helm-chart"
                     elif kdur.get("juju-bundle"):
                         kdumodel = kdur["juju-bundle"]
                         k8sclustertype = "juju-bundle"
                     elif kdur.get("juju-bundle"):
                         kdumodel = kdur["juju-bundle"]
                         k8sclustertype = "juju-bundle"
@@ -2178,7 +2654,7 @@ class NsLcm(LcmBase):
                         storage = deep_get(db_vnfds.get(vnfd_id), ('_admin', 'storage'))
                         if storage and storage.get('pkg-dir'):  # may be not present if vnfd has not artifacts
                             # path format: /vnfdid/pkkdir/helm-charts|juju-bundles/kdumodel
                         storage = deep_get(db_vnfds.get(vnfd_id), ('_admin', 'storage'))
                         if storage and storage.get('pkg-dir'):  # may be not present if vnfd has not artifacts
                             # path format: /vnfdid/pkkdir/helm-charts|juju-bundles/kdumodel
-                            filename = '{}/{}/{}s/{}'.format(storage["folder"], storage["'pkg-dir"], k8sclustertype,
+                            filename = '{}/{}/{}s/{}'.format(storage["folder"], storage["pkg-dir"], k8sclustertype,
                                                              kdumodel)
                             if self.fs.file_exists(filename, mode='file') or self.fs.file_exists(filename, mode='dir'):
                                 kdumodel = self.fs.path + filename
                                                              kdumodel)
                             if self.fs.file_exists(filename, mode='file') or self.fs.file_exists(filename, mode='dir'):
                                 kdumodel = self.fs.path + filename
@@ -2189,48 +2665,46 @@ class NsLcm(LcmBase):
 
                     k8s_cluster_id = kdur["k8s-cluster"]["id"]
                     step = "Synchronize repos for k8s cluster '{}'".format(k8s_cluster_id)
 
                     k8s_cluster_id = kdur["k8s-cluster"]["id"]
                     step = "Synchronize repos for k8s cluster '{}'".format(k8s_cluster_id)
-                    cluster_uuid = _get_cluster_id(k8s_cluster_id, k8sclustertype)
+                    cluster_uuid = await _get_cluster_id(k8s_cluster_id, k8sclustertype)
 
 
-                    if k8sclustertype == "helm-chart" and cluster_uuid not in updated_cluster_list:
+                    # Synchronize  repos
+                    if (k8sclustertype == "helm-chart" and cluster_uuid not in updated_cluster_list)\
+                            or (k8sclustertype == "helm-chart-v3" and cluster_uuid not in updated_v3_cluster_list):
                         del_repo_list, added_repo_dict = await asyncio.ensure_future(
                         del_repo_list, added_repo_dict = await asyncio.ensure_future(
-                            self.k8sclusterhelm.synchronize_repos(cluster_uuid=cluster_uuid))
+                            self.k8scluster_map[k8sclustertype].synchronize_repos(cluster_uuid=cluster_uuid))
                         if del_repo_list or added_repo_dict:
                         if del_repo_list or added_repo_dict:
-                            unset = {'_admin.helm_charts_added.' + item: None for item in del_repo_list}
-                            updated = {'_admin.helm_charts_added.' +
-                                       item: name for item, name in added_repo_dict.items()}
-                            self.logger.debug(logging_text + "repos synchronized on k8s cluster '{}' to_delete: {}, "
-                                                             "to_add: {}".format(k8s_cluster_id, del_repo_list,
-                                                                                 added_repo_dict))
+                            if k8sclustertype == "helm-chart":
+                                unset = {'_admin.helm_charts_added.' + item: None for item in del_repo_list}
+                                updated = {'_admin.helm_charts_added.' +
+                                           item: name for item, name in added_repo_dict.items()}
+                                updated_cluster_list.append(cluster_uuid)
+                            elif k8sclustertype == "helm-chart-v3":
+                                unset = {'_admin.helm_charts_v3_added.' + item: None for item in del_repo_list}
+                                updated = {'_admin.helm_charts_v3_added.' +
+                                           item: name for item, name in added_repo_dict.items()}
+                                updated_v3_cluster_list.append(cluster_uuid)
+                            self.logger.debug(logging_text + "repos synchronized on k8s cluster "
+                                                             "'{}' to_delete: {}, to_add: {}".
+                                              format(k8s_cluster_id, del_repo_list, added_repo_dict))
                             self.db.set_one("k8sclusters", {"_id": k8s_cluster_id}, updated, unset=unset)
                             self.db.set_one("k8sclusters", {"_id": k8s_cluster_id}, updated, unset=unset)
-                        updated_cluster_list.append(cluster_uuid)
 
 
+                    # Instantiate kdu
                     step = "Instantiating KDU {}.{} in k8s cluster {}".format(vnfr_data["member-vnf-index-ref"],
                                                                               kdur["kdu-name"], k8s_cluster_id)
                     step = "Instantiating KDU {}.{} in k8s cluster {}".format(vnfr_data["member-vnf-index-ref"],
                                                                               kdur["kdu-name"], k8s_cluster_id)
-
-                    k8s_instace_info = {"kdu-instance": None,
-                                        "k8scluster-uuid": cluster_uuid,
-                                        "k8scluster-type": k8sclustertype,
-                                        "member-vnf-index": vnfr_data["member-vnf-index-ref"],
-                                        "kdu-name": kdur["kdu-name"],
-                                        "kdu-model": kdumodel,
-                                        "namespace": namespace}
+                    k8s_instance_info = {"kdu-instance": None,
+                                         "k8scluster-uuid": cluster_uuid,
+                                         "k8scluster-type": k8sclustertype,
+                                         "member-vnf-index": vnfr_data["member-vnf-index-ref"],
+                                         "kdu-name": kdur["kdu-name"],
+                                         "kdu-model": kdumodel,
+                                         "namespace": namespace}
                     db_path = "_admin.deployed.K8s.{}".format(index)
                     db_path = "_admin.deployed.K8s.{}".format(index)
-                    db_nsr_update[db_path] = k8s_instace_info
+                    db_nsr_update[db_path] = k8s_instance_info
                     self.update_db_2("nsrs", nsr_id, db_nsr_update)
 
                     self.update_db_2("nsrs", nsr_id, db_nsr_update)
 
-                    db_dict = {"collection": "nsrs",
-                               "filter": {"_id": nsr_id},
-                               "path": db_path}
-
                     task = asyncio.ensure_future(
                     task = asyncio.ensure_future(
-                        self.k8scluster_map[k8sclustertype].install(cluster_uuid=cluster_uuid, kdu_model=kdumodel,
-                                                                    atomic=True, params=desc_params,
-                                                                    db_dict=db_dict, timeout=600,
-                                                                    kdu_name=kdur["kdu-name"], namespace=namespace))
-
-                    task.add_done_callback(partial(self._write_db_callback, item="nsrs", _id=nsr_id,
-                                                   on_done=db_path + ".kdu-instance",
-                                                   on_exc=db_path + ".detailed-status"))
+                        self._install_kdu(nsr_id, db_path, vnfr_data, kdu_index, kdud, db_vnfds[vnfd_id],
+                                          k8s_instance_info, k8params=desc_params, timeout=600))
                     self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_KDU-{}".format(index), task)
                     task_instantiation_info[task] = "Deploying KDU {}".format(kdur["kdu-name"])
 
                     self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_KDU-{}".format(index), task)
                     task_instantiation_info[task] = "Deploying KDU {}".format(kdur["kdu-name"])
 
@@ -2255,80 +2729,119 @@ class NsLcm(LcmBase):
         # launch instantiate_N2VC in a asyncio task and register task object
         # Look where information of this charm is at database <nsrs>._admin.deployed.VCA
         # if not found, create one entry and update database
         # launch instantiate_N2VC in a asyncio task and register task object
         # Look where information of this charm is at database <nsrs>._admin.deployed.VCA
         # if not found, create one entry and update database
-
         # fill db_nsr._admin.deployed.VCA.<index>
         # fill db_nsr._admin.deployed.VCA.<index>
-        vca_index = -1
-        for vca_index, vca_deployed in enumerate(db_nsr["_admin"]["deployed"]["VCA"]):
-            if not vca_deployed:
-                continue
-            if vca_deployed.get("member-vnf-index") == member_vnf_index and \
-                    vca_deployed.get("vdu_id") == vdu_id and \
-                    vca_deployed.get("kdu_name") == kdu_name and \
-                    vca_deployed.get("vdu_count_index", 0) == vdu_index:
-                break
-        else:
-            # not found, create one.
-            vca_deployed = {
-                "member-vnf-index": member_vnf_index,
-                "vdu_id": vdu_id,
-                "kdu_name": kdu_name,
-                "vdu_count_index": vdu_index,
-                "operational-status": "init",  # TODO revise
-                "detailed-status": "",  # TODO revise
-                "step": "initial-deploy",   # TODO revise
-                "vnfd_id": vnfd_id,
-                "vdu_name": vdu_name,
-            }
-            vca_index += 1
 
 
-            # create VCA and configurationStatus in db
-            db_dict = {
-                "_admin.deployed.VCA.{}".format(vca_index): vca_deployed,
-                "configurationStatus.{}".format(vca_index): dict()
-            }
-            self.update_db_2("nsrs", nsr_id, db_dict)
-
-            db_nsr["_admin"]["deployed"]["VCA"].append(vca_deployed)
+        self.logger.debug(logging_text + "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id, vdu_id))
+        if descriptor_config.get("juju"):   # There is one execution envioronment of type juju
+            ee_list = [descriptor_config]
+        elif descriptor_config.get("execution-environment-list"):
+            ee_list = descriptor_config.get("execution-environment-list")
+        else:  # other types as script are not supported
+            ee_list = []
+
+        for ee_item in ee_list:
+            self.logger.debug(logging_text + "_deploy_n2vc ee_item juju={}, helm={}".format(ee_item.get('juju'),
+                                                                                            ee_item.get("helm-chart")))
+            ee_descriptor_id = ee_item.get("id")
+            if ee_item.get("juju"):
+                vca_name = ee_item['juju'].get('charm')
+                vca_type = "lxc_proxy_charm" if ee_item['juju'].get('charm') is not None else "native_charm"
+                if ee_item['juju'].get('cloud') == "k8s":
+                    vca_type = "k8s_proxy_charm"
+                elif ee_item['juju'].get('proxy') is False:
+                    vca_type = "native_charm"
+            elif ee_item.get("helm-chart"):
+                vca_name = ee_item['helm-chart']
+                if ee_item.get("helm-version") and ee_item.get("helm-version") == "v2":
+                    vca_type = "helm"
+                else:
+                    vca_type = "helm-v3"
+            else:
+                self.logger.debug(logging_text + "skipping non juju neither charm configuration")
+                continue
 
 
-        # Launch task
-        task_n2vc = asyncio.ensure_future(
-            self.instantiate_N2VC(
-                logging_text=logging_text,
-                vca_index=vca_index,
-                nsi_id=nsi_id,
-                db_nsr=db_nsr,
-                db_vnfr=db_vnfr,
-                vdu_id=vdu_id,
-                kdu_name=kdu_name,
-                vdu_index=vdu_index,
-                deploy_params=deploy_params,
-                config_descriptor=descriptor_config,
-                base_folder=base_folder,
-                nslcmop_id=nslcmop_id,
-                stage=stage
+            vca_index = -1
+            for vca_index, vca_deployed in enumerate(db_nsr["_admin"]["deployed"]["VCA"]):
+                if not vca_deployed:
+                    continue
+                if vca_deployed.get("member-vnf-index") == member_vnf_index and \
+                        vca_deployed.get("vdu_id") == vdu_id and \
+                        vca_deployed.get("kdu_name") == kdu_name and \
+                        vca_deployed.get("vdu_count_index", 0) == vdu_index and \
+                        vca_deployed.get("ee_descriptor_id") == ee_descriptor_id:
+                    break
+            else:
+                # not found, create one.
+                target = "ns" if not member_vnf_index else "vnf/{}".format(member_vnf_index)
+                if vdu_id:
+                    target += "/vdu/{}/{}".format(vdu_id, vdu_index or 0)
+                elif kdu_name:
+                    target += "/kdu/{}".format(kdu_name)
+                vca_deployed = {
+                    "target_element": target,
+                    # ^ target_element will replace member-vnf-index, kdu_name, vdu_id ... in a single string
+                    "member-vnf-index": member_vnf_index,
+                    "vdu_id": vdu_id,
+                    "kdu_name": kdu_name,
+                    "vdu_count_index": vdu_index,
+                    "operational-status": "init",  # TODO revise
+                    "detailed-status": "",  # TODO revise
+                    "step": "initial-deploy",   # TODO revise
+                    "vnfd_id": vnfd_id,
+                    "vdu_name": vdu_name,
+                    "type": vca_type,
+                    "ee_descriptor_id": ee_descriptor_id
+                }
+                vca_index += 1
+
+                # create VCA and configurationStatus in db
+                db_dict = {
+                    "_admin.deployed.VCA.{}".format(vca_index): vca_deployed,
+                    "configurationStatus.{}".format(vca_index): dict()
+                }
+                self.update_db_2("nsrs", nsr_id, db_dict)
+
+                db_nsr["_admin"]["deployed"]["VCA"].append(vca_deployed)
+
+            # Launch task
+            task_n2vc = asyncio.ensure_future(
+                self.instantiate_N2VC(
+                    logging_text=logging_text,
+                    vca_index=vca_index,
+                    nsi_id=nsi_id,
+                    db_nsr=db_nsr,
+                    db_vnfr=db_vnfr,
+                    vdu_id=vdu_id,
+                    kdu_name=kdu_name,
+                    vdu_index=vdu_index,
+                    deploy_params=deploy_params,
+                    config_descriptor=descriptor_config,
+                    base_folder=base_folder,
+                    nslcmop_id=nslcmop_id,
+                    stage=stage,
+                    vca_type=vca_type,
+                    vca_name=vca_name,
+                    ee_config_descriptor=ee_item
+                )
             )
             )
-        )
-        self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_N2VC-{}".format(vca_index), task_n2vc)
-        task_instantiation_info[task_n2vc] = self.task_name_deploy_vca + " {}.{}".format(
-            member_vnf_index or "", vdu_id or "")
-
-    # Check if this VNFD has a configured terminate action
-    def _has_terminate_config_primitive(self, vnfd):
-        vnf_config = vnfd.get("vnf-configuration")
-        if vnf_config and vnf_config.get("terminate-config-primitive"):
-            return True
-        else:
-            return False
+            self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_N2VC-{}".format(vca_index), task_n2vc)
+            task_instantiation_info[task_n2vc] = self.task_name_deploy_vca + " {}.{}".format(
+                member_vnf_index or "", vdu_id or "")
 
     @staticmethod
 
     @staticmethod
-    def _get_terminate_config_primitive_seq_list(vnfd):
-        """ Get a numerically sorted list of the sequences for this VNFD's terminate action """
-        # No need to check for existing primitive twice, already done before
-        vnf_config = vnfd.get("vnf-configuration")
-        seq_list = vnf_config.get("terminate-config-primitive")
-        # Get all 'seq' tags in seq_list, order sequences numerically, ascending.
-        seq_list_sorted = sorted(seq_list, key=lambda x: int(x['seq']))
-        return seq_list_sorted
+    def _get_terminate_config_primitive(primitive_list, vca_deployed):
+        """ Get a sorted terminate config primitive list. In case ee_descriptor_id is present at vca_deployed,
+         it get only those primitives for this execution envirom"""
+
+        primitive_list = primitive_list or []
+        # filter primitives by ee_descriptor_id
+        ee_descriptor_id = vca_deployed.get("ee_descriptor_id")
+        primitive_list = [p for p in primitive_list if p.get("execution-environment-ref") == ee_descriptor_id]
+
+        if primitive_list:
+            primitive_list.sort(key=lambda val: int(val['seq']))
+
+        return primitive_list
 
     @staticmethod
     def _create_nslcmop(nsr_id, operation, params):
 
     @staticmethod
     def _create_nslcmop(nsr_id, operation, params):
@@ -2391,9 +2904,9 @@ class NsLcm(LcmBase):
             # _ns_execute_primitive() or RO.create_action() will NOT be executed
             return self.SUBOPERATION_STATUS_SKIP
         else:
             # _ns_execute_primitive() or RO.create_action() will NOT be executed
             return self.SUBOPERATION_STATUS_SKIP
         else:
-            # c. Reintent executing sub-operation
+            # c. retry executing sub-operation
             # The sub-operation exists, and operationState != 'COMPLETED'
             # The sub-operation exists, and operationState != 'COMPLETED'
-            # Update operationState = 'PROCESSING' to indicate a reintent.
+            # Update operationState = 'PROCESSING' to indicate a retry.
             operationState = 'PROCESSING'
             detailed_status = 'In progress'
             self._update_suboperation_status(
             operationState = 'PROCESSING'
             detailed_status = 'In progress'
             self._update_suboperation_status(
@@ -2406,7 +2919,7 @@ class NsLcm(LcmBase):
     # Find a sub-operation where all keys in a matching dictionary must match
     # Returns the index of the matching sub-operation, or SUBOPERATION_STATUS_NOT_FOUND if no match
     def _find_suboperation(self, db_nslcmop, match):
     # Find a sub-operation where all keys in a matching dictionary must match
     # Returns the index of the matching sub-operation, or SUBOPERATION_STATUS_NOT_FOUND if no match
     def _find_suboperation(self, db_nslcmop, match):
-        if (db_nslcmop and match):
+        if db_nslcmop and match:
             op_list = db_nslcmop.get('_admin', {}).get('operations', [])
             for i, op in enumerate(op_list):
                 if all(op.get(k) == match[k] for k in match):
             op_list = db_nslcmop.get('_admin', {}).get('operations', [])
             for i, op in enumerate(op_list):
                 if all(op.get(k) == match[k] for k in match):
@@ -2474,11 +2987,11 @@ class NsLcm(LcmBase):
     # Check for 3 different cases:
     # a. New: First time execution, return SUBOPERATION_STATUS_NEW
     # b. Skip: Existing sub-operation exists, operationState == 'COMPLETED', return SUBOPERATION_STATUS_SKIP
     # Check for 3 different cases:
     # a. New: First time execution, return SUBOPERATION_STATUS_NEW
     # b. Skip: Existing sub-operation exists, operationState == 'COMPLETED', return SUBOPERATION_STATUS_SKIP
-    # c. Reintent: Existing sub-operation exists, operationState != 'COMPLETED', return op_index to re-execute
+    # c. retry: Existing sub-operation exists, operationState != 'COMPLETED', return op_index to re-execute
     def _check_or_add_scale_suboperation(self, db_nslcmop, vnf_index, vnf_config_primitive, primitive_params,
                                          operationType, RO_nsr_id=None, RO_scaling_info=None):
         # Find this sub-operation
     def _check_or_add_scale_suboperation(self, db_nslcmop, vnf_index, vnf_config_primitive, primitive_params,
                                          operationType, RO_nsr_id=None, RO_scaling_info=None):
         # Find this sub-operation
-        if (RO_nsr_id and RO_scaling_info):
+        if RO_nsr_id and RO_scaling_info:
             operationType = 'SCALE-RO'
             match = {
                 'member_vnf_index': vnf_index,
             operationType = 'SCALE-RO'
             match = {
                 'member_vnf_index': vnf_index,
@@ -2537,7 +3050,8 @@ class NsLcm(LcmBase):
             if vca["member-vnf-index"] == vnf_index and vca["vdu_id"] == vdu_id:
                 return vca["ee_id"]
 
             if vca["member-vnf-index"] == vnf_index and vca["vdu_id"] == vdu_id:
                 return vca["ee_id"]
 
-    async def destroy_N2VC(self, logging_text, db_nslcmop, vca_deployed, config_descriptor, vca_index, destroy_ee=True):
+    async def destroy_N2VC(self, logging_text, db_nslcmop, vca_deployed, config_descriptor,
+                           vca_index, destroy_ee=True, exec_primitives=True):
         """
         Execute the terminate primitives and destroy the execution environment (if destroy_ee=False
         :param logging_text:
         """
         Execute the terminate primitives and destroy the execution environment (if destroy_ee=False
         :param logging_text:
@@ -2546,53 +3060,66 @@ class NsLcm(LcmBase):
         :param config_descriptor: Configuration descriptor of the NSD, VNFD, VNFD.vdu or VNFD.kdu
         :param vca_index: index in the database _admin.deployed.VCA
         :param destroy_ee: False to do not destroy, because it will be destroyed all of then at once
         :param config_descriptor: Configuration descriptor of the NSD, VNFD, VNFD.vdu or VNFD.kdu
         :param vca_index: index in the database _admin.deployed.VCA
         :param destroy_ee: False to do not destroy, because it will be destroyed all of then at once
+        :param exec_primitives: False to do not execute terminate primitives, because the config is not completed or has
+                            not executed properly
         :return: None or exception
         """
         :return: None or exception
         """
+
+        self.logger.debug(
+            logging_text + " vca_index: {}, vca_deployed: {}, config_descriptor: {}, destroy_ee: {}".format(
+                vca_index, vca_deployed, config_descriptor, destroy_ee
+            )
+        )
+
+        vca_type = vca_deployed.get("type", "lxc_proxy_charm")
+
         # execute terminate_primitives
         # execute terminate_primitives
-        terminate_primitives = config_descriptor.get("terminate-config-primitive")
-        vdu_id = vca_deployed.get("vdu_id")
-        vdu_count_index = vca_deployed.get("vdu_count_index")
-        vdu_name = vca_deployed.get("vdu_name")
-        vnf_index = vca_deployed.get("member-vnf-index")
-        if terminate_primitives and vca_deployed.get("needed_terminate"):
-            # Get all 'seq' tags in seq_list, order sequences numerically, ascending.
-            terminate_primitives = sorted(terminate_primitives, key=lambda x: int(x['seq']))
-            for seq in terminate_primitives:
-                # For each sequence in list, get primitive and call _ns_execute_primitive()
-                step = "Calling terminate action for vnf_member_index={} primitive={}".format(
-                    vnf_index, seq.get("name"))
-                self.logger.debug(logging_text + step)
-                # Create the primitive for each sequence, i.e. "primitive": "touch"
-                primitive = seq.get('name')
-                mapped_primitive_params = self._get_terminate_primitive_params(seq, vnf_index)
-                # The following 3 parameters are currently set to None for 'terminate':
-                # vdu_id, vdu_count_index, vdu_name
-
-                # Add sub-operation
-                self._add_suboperation(db_nslcmop,
-                                       vnf_index,
-                                       vdu_id,
-                                       vdu_count_index,
-                                       vdu_name,
-                                       primitive,
-                                       mapped_primitive_params)
-                # Sub-operations: Call _ns_execute_primitive() instead of action()
-                try:
-                    result, result_detail = await self._ns_execute_primitive(vca_deployed["ee_id"], primitive,
-                                                                             mapped_primitive_params)
-                except LcmException:
-                    # this happens when VCA is not deployed. In this case it is not needed to terminate
-                    continue
-                result_ok = ['COMPLETED', 'PARTIALLY_COMPLETED']
-                if result not in result_ok:
-                    raise LcmException("terminate_primitive {}  for vnf_member_index={} fails with "
-                                       "error {}".format(seq.get("name"), vnf_index, result_detail))
-            # set that this VCA do not need terminated
-            db_update_entry = "_admin.deployed.VCA.{}.needed_terminate".format(vca_index)
-            self.update_db_2("nsrs", db_nslcmop["nsInstanceId"], {db_update_entry: False})
+        if exec_primitives:
+            terminate_primitives = self._get_terminate_config_primitive(
+                config_descriptor.get("terminate-config-primitive"), vca_deployed)
+            vdu_id = vca_deployed.get("vdu_id")
+            vdu_count_index = vca_deployed.get("vdu_count_index")
+            vdu_name = vca_deployed.get("vdu_name")
+            vnf_index = vca_deployed.get("member-vnf-index")
+            if terminate_primitives and vca_deployed.get("needed_terminate"):
+                for seq in terminate_primitives:
+                    # For each sequence in list, get primitive and call _ns_execute_primitive()
+                    step = "Calling terminate action for vnf_member_index={} primitive={}".format(
+                        vnf_index, seq.get("name"))
+                    self.logger.debug(logging_text + step)
+                    # Create the primitive for each sequence, i.e. "primitive": "touch"
+                    primitive = seq.get('name')
+                    mapped_primitive_params = self._get_terminate_primitive_params(seq, vnf_index)
+
+                    # Add sub-operation
+                    self._add_suboperation(db_nslcmop,
+                                           vnf_index,
+                                           vdu_id,
+                                           vdu_count_index,
+                                           vdu_name,
+                                           primitive,
+                                           mapped_primitive_params)
+                    # Sub-operations: Call _ns_execute_primitive() instead of action()
+                    try:
+                        result, result_detail = await self._ns_execute_primitive(vca_deployed["ee_id"], primitive,
+                                                                                 mapped_primitive_params,
+                                                                                 vca_type=vca_type)
+                    except LcmException:
+                        # this happens when VCA is not deployed. In this case it is not needed to terminate
+                        continue
+                    result_ok = ['COMPLETED', 'PARTIALLY_COMPLETED']
+                    if result not in result_ok:
+                        raise LcmException("terminate_primitive {}  for vnf_member_index={} fails with "
+                                           "error {}".format(seq.get("name"), vnf_index, result_detail))
+                # set that this VCA do not need terminated
+                db_update_entry = "_admin.deployed.VCA.{}.needed_terminate".format(vca_index)
+                self.update_db_2("nsrs", db_nslcmop["nsInstanceId"], {db_update_entry: False})
+
+        if vca_deployed.get("prometheus_jobs") and self.prometheus:
+            await self.prometheus.update(remove_jobs=vca_deployed["prometheus_jobs"])
 
         if destroy_ee:
 
         if destroy_ee:
-            await self.n2vc.delete_execution_environment(vca_deployed["ee_id"])
+            await self.vca_map[vca_type].delete_execution_environment(vca_deployed["ee_id"])
 
     async def _delete_all_N2VC(self, db_nsr: dict):
         self._write_all_config_status(db_nsr=db_nsr, status='TERMINATING')
 
     async def _delete_all_N2VC(self, db_nsr: dict):
         self._write_all_config_status(db_nsr=db_nsr, status='TERMINATING')
@@ -2810,42 +3337,55 @@ class NsLcm(LcmBase):
 
             # Destroy individual execution environments when there are terminating primitives.
             # Rest of EE will be deleted at once
 
             # Destroy individual execution environments when there are terminating primitives.
             # Rest of EE will be deleted at once
-            if not operation_params.get("skip_terminate_primitives"):
-                stage[0] = "Stage 2/3 execute terminating primitives."
-                stage[1] = "Looking execution environment that needs terminate."
-                self.logger.debug(logging_text + stage[1])
-                for vca_index, vca in enumerate(get_iterable(nsr_deployed, "VCA")):
-                    config_descriptor = None
-                    if not vca or not vca.get("ee_id") or not vca.get("needed_terminate"):
-                        continue
-                    if not vca.get("member-vnf-index"):
-                        # ns
-                        config_descriptor = db_nsr.get("ns-configuration")
-                    elif vca.get("vdu_id"):
-                        db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
-                        vdud = next((vdu for vdu in db_vnfd.get("vdu", ()) if vdu["id"] == vca.get("vdu_id")), None)
-                        if vdud:
-                            config_descriptor = vdud.get("vdu-configuration")
-                    elif vca.get("kdu_name"):
-                        db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
-                        kdud = next((kdu for kdu in db_vnfd.get("kdu", ()) if kdu["name"] == vca.get("kdu_name")), None)
-                        if kdud:
-                            config_descriptor = kdud.get("kdu-configuration")
-                    else:
-                        config_descriptor = db_vnfds_from_member_index[vca["member-vnf-index"]].get("vnf-configuration")
-                    task = asyncio.ensure_future(self.destroy_N2VC(logging_text, db_nslcmop, vca, config_descriptor,
-                                                                   vca_index, False))
-                    tasks_dict_info[task] = "Terminating VCA {}".format(vca.get("ee_id"))
-
-                # wait for pending tasks of terminate primitives
-                if tasks_dict_info:
-                    self.logger.debug(logging_text + 'Waiting for terminate primitive pending tasks...')
-                    error_list = await self._wait_for_tasks(logging_text, tasks_dict_info,
-                                                            min(self.timeout_charm_delete, timeout_ns_terminate),
-                                                            stage, nslcmop_id)
-                    if error_list:
-                        return   # raise LcmException("; ".join(error_list))
-                    tasks_dict_info.clear()
+            # TODO - check before calling _destroy_N2VC
+            # if not operation_params.get("skip_terminate_primitives"):#
+            # or not vca.get("needed_terminate"):
+            stage[0] = "Stage 2/3 execute terminating primitives."
+            self.logger.debug(logging_text + stage[0])
+            stage[1] = "Looking execution environment that needs terminate."
+            self.logger.debug(logging_text + stage[1])
+            # self.logger.debug("nsr_deployed: {}".format(nsr_deployed))
+            for vca_index, vca in enumerate(get_iterable(nsr_deployed, "VCA")):
+                config_descriptor = None
+                if not vca or not vca.get("ee_id"):
+                    continue
+                if not vca.get("member-vnf-index"):
+                    # ns
+                    config_descriptor = db_nsr.get("ns-configuration")
+                elif vca.get("vdu_id"):
+                    db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
+                    vdud = next((vdu for vdu in db_vnfd.get("vdu", ()) if vdu["id"] == vca.get("vdu_id")), None)
+                    if vdud:
+                        config_descriptor = vdud.get("vdu-configuration")
+                elif vca.get("kdu_name"):
+                    db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
+                    kdud = next((kdu for kdu in db_vnfd.get("kdu", ()) if kdu["name"] == vca.get("kdu_name")), None)
+                    if kdud:
+                        config_descriptor = kdud.get("kdu-configuration")
+                else:
+                    config_descriptor = db_vnfds_from_member_index[vca["member-vnf-index"]].get("vnf-configuration")
+                vca_type = vca.get("type")
+                exec_terminate_primitives = (not operation_params.get("skip_terminate_primitives") and
+                                             vca.get("needed_terminate"))
+                # For helm we must destroy_ee. Also for native_charm, as juju_model cannot be deleted if there are
+                # pending native charms
+                destroy_ee = True if vca_type in ("helm", "helm-v3", "native_charm") else False
+                # self.logger.debug(logging_text + "vca_index: {}, ee_id: {}, vca_type: {} destroy_ee: {}".format(
+                #     vca_index, vca.get("ee_id"), vca_type, destroy_ee))
+                task = asyncio.ensure_future(
+                    self.destroy_N2VC(logging_text, db_nslcmop, vca, config_descriptor, vca_index,
+                                      destroy_ee, exec_terminate_primitives))
+                tasks_dict_info[task] = "Terminating VCA {}".format(vca.get("ee_id"))
+
+            # wait for pending tasks of terminate primitives
+            if tasks_dict_info:
+                self.logger.debug(logging_text + 'Waiting for tasks {}'.format(list(tasks_dict_info.keys())))
+                error_list = await self._wait_for_tasks(logging_text, tasks_dict_info,
+                                                        min(self.timeout_charm_delete, timeout_ns_terminate),
+                                                        stage, nslcmop_id)
+                tasks_dict_info.clear()
+                if error_list:
+                    return   # raise LcmException("; ".join(error_list))
 
             # remove All execution environments at once
             stage[0] = "Stage 3/3 delete all."
 
             # remove All execution environments at once
             stage[0] = "Stage 3/3 delete all."
@@ -2879,8 +3419,12 @@ class NsLcm(LcmBase):
 
             # remove from RO
             stage[1] = "Deleting ns from VIM."
 
             # remove from RO
             stage[1] = "Deleting ns from VIM."
-            task_delete_ro = asyncio.ensure_future(
-                self._terminate_RO(logging_text, nsr_deployed, nsr_id, nslcmop_id, stage))
+            if self.ng_ro:
+                task_delete_ro = asyncio.ensure_future(
+                    self._terminate_ng_ro(logging_text, nsr_deployed, nsr_id, nslcmop_id, stage))
+            else:
+                task_delete_ro = asyncio.ensure_future(
+                    self._terminate_RO(logging_text, nsr_deployed, nsr_id, nslcmop_id, stage))
             tasks_dict_info[task_delete_ro] = "Removing deployment from VIM"
 
             # rest of staff will be done at finally
             tasks_dict_info[task_delete_ro] = "Removing deployment from VIM"
 
             # rest of staff will be done at finally
@@ -2914,8 +3458,8 @@ class NsLcm(LcmBase):
             if error_list:
                 error_detail = "; ".join(error_list)
                 # self.logger.error(logging_text + error_detail)
             if error_list:
                 error_detail = "; ".join(error_list)
                 # self.logger.error(logging_text + error_detail)
-                error_description_nslcmop = 'Stage: {}. Detail: {}'.format(stage[0], error_detail)
-                error_description_nsr = 'Operation: TERMINATING.{}, Stage {}.'.format(nslcmop_id, stage[0])
+                error_description_nslcmop = '{} Detail: {}'.format(stage[0], error_detail)
+                error_description_nsr = 'Operation: TERMINATING.{}, {}.'.format(nslcmop_id, stage[0])
 
                 db_nsr_update["operational-status"] = "failed"
                 db_nsr_update["detailed-status"] = error_description_nsr + " Detail: " + error_detail
 
                 db_nsr_update["operational-status"] = "failed"
                 db_nsr_update["detailed-status"] = error_description_nsr + " Detail: " + error_detail
@@ -2949,6 +3493,12 @@ class NsLcm(LcmBase):
                 operation_state=nslcmop_operation_state,
                 other_update=db_nslcmop_update,
             )
                 operation_state=nslcmop_operation_state,
                 other_update=db_nslcmop_update,
             )
+            if ns_state == "NOT_INSTANTIATED":
+                try:
+                    self.db.set_list("vnfrs", {"nsr-id-ref": nsr_id}, {"_admin.nsState": "NOT_INSTANTIATED"})
+                except DbException as e:
+                    self.logger.warn(logging_text + 'Error writing VNFR status for nsr-id-ref: {} -> {}'.
+                                     format(nsr_id, e))
             if operation_params:
                 autoremove = operation_params.get("autoremove", False)
             if nslcmop_operation_state:
             if operation_params:
                 autoremove = operation_params.get("autoremove", False)
             if nslcmop_operation_state:
@@ -3048,6 +3598,14 @@ class NsLcm(LcmBase):
                                                                width=256)
             elif isinstance(calculated_params[param_name], str) and calculated_params[param_name].startswith("!!yaml "):
                 calculated_params[param_name] = calculated_params[param_name][7:]
                                                                width=256)
             elif isinstance(calculated_params[param_name], str) and calculated_params[param_name].startswith("!!yaml "):
                 calculated_params[param_name] = calculated_params[param_name][7:]
+            if parameter.get("data-type") == "INTEGER":
+                try:
+                    calculated_params[param_name] = int(calculated_params[param_name])
+                except ValueError:  # error converting string to int
+                    raise LcmException(
+                        "Parameter {} of primitive {} must be integer".format(param_name, primitive_desc["name"]))
+            elif parameter.get("data-type") == "BOOLEAN":
+                calculated_params[param_name] = not ((str(calculated_params[param_name])).lower() == 'false')
 
         # add always ns_config_info if primitive name is config
         if primitive_desc["name"] == "config":
 
         # add always ns_config_info if primitive name is config
         if primitive_desc["name"] == "config":
@@ -3055,7 +3613,8 @@ class NsLcm(LcmBase):
                 calculated_params["ns_config_info"] = instantiation_params["ns_config_info"]
         return calculated_params
 
                 calculated_params["ns_config_info"] = instantiation_params["ns_config_info"]
         return calculated_params
 
-    def _look_for_deployed_vca(self, deployed_vca, member_vnf_index, vdu_id, vdu_count_index, kdu_name=None):
+    def _look_for_deployed_vca(self, deployed_vca, member_vnf_index, vdu_id, vdu_count_index, kdu_name=None,
+                               ee_descriptor_id=None):
         # find vca_deployed record for this action. Raise LcmException if not found or there is not any id.
         for vca in deployed_vca:
             if not vca:
         # find vca_deployed record for this action. Raise LcmException if not found or there is not any id.
         for vca in deployed_vca:
             if not vca:
@@ -3066,35 +3625,43 @@ class NsLcm(LcmBase):
                 continue
             if kdu_name and kdu_name != vca["kdu_name"]:
                 continue
                 continue
             if kdu_name and kdu_name != vca["kdu_name"]:
                 continue
+            if ee_descriptor_id and ee_descriptor_id != vca["ee_descriptor_id"]:
+                continue
             break
         else:
             # vca_deployed not found
             break
         else:
             # vca_deployed not found
-            raise LcmException("charm for member_vnf_index={} vdu_id={} kdu_name={} vdu_count_index={} is not "
-                               "deployed".format(member_vnf_index, vdu_id, kdu_name, vdu_count_index))
+            raise LcmException("charm for member_vnf_index={} vdu_id={}.{} kdu_name={} execution-environment-list.id={}"
+                               " is not deployed".format(member_vnf_index, vdu_id, vdu_count_index, kdu_name,
+                                                         ee_descriptor_id))
 
         # get ee_id
         ee_id = vca.get("ee_id")
 
         # get ee_id
         ee_id = vca.get("ee_id")
+        vca_type = vca.get("type", "lxc_proxy_charm")  # default value for backward compatibility - proxy charm
         if not ee_id:
             raise LcmException("charm for member_vnf_index={} vdu_id={} kdu_name={} vdu_count_index={} has not "
                                "execution environment"
                                .format(member_vnf_index, vdu_id, kdu_name, vdu_count_index))
         if not ee_id:
             raise LcmException("charm for member_vnf_index={} vdu_id={} kdu_name={} vdu_count_index={} has not "
                                "execution environment"
                                .format(member_vnf_index, vdu_id, kdu_name, vdu_count_index))
-        return ee_id
+        return ee_id, vca_type
 
     async def _ns_execute_primitive(self, ee_id, primitive, primitive_params, retries=0,
 
     async def _ns_execute_primitive(self, ee_id, primitive, primitive_params, retries=0,
-                                    retries_interval=30, timeout=None) -> (str, str):
+                                    retries_interval=30, timeout=None,
+                                    vca_type=None, db_dict=None) -> (str, str):
         try:
             if primitive == "config":
                 primitive_params = {"params": primitive_params}
 
         try:
             if primitive == "config":
                 primitive_params = {"params": primitive_params}
 
+            vca_type = vca_type or "lxc_proxy_charm"
+
             while retries >= 0:
                 try:
                     output = await asyncio.wait_for(
             while retries >= 0:
                 try:
                     output = await asyncio.wait_for(
-                        self.n2vc.exec_primitive(
+                        self.vca_map[vca_type].exec_primitive(
                             ee_id=ee_id,
                             primitive_name=primitive,
                             params_dict=primitive_params,
                             progress_timeout=self.timeout_progress_primitive,
                             ee_id=ee_id,
                             primitive_name=primitive,
                             params_dict=primitive_params,
                             progress_timeout=self.timeout_progress_primitive,
-                            total_timeout=self.timeout_primitive),
+                            total_timeout=self.timeout_primitive,
+                            db_dict=db_dict),
                         timeout=timeout or self.timeout_primitive)
                     # execution was OK
                     break
                         timeout=timeout or self.timeout_primitive)
                     # execution was OK
                     break
@@ -3176,37 +3743,37 @@ class NsLcm(LcmBase):
                 self.update_db_2("nsrs", nsr_id, db_nsr_update)
 
             # look for primitive
                 self.update_db_2("nsrs", nsr_id, db_nsr_update)
 
             # look for primitive
-            config_primitive_desc = None
+            config_primitive_desc = descriptor_configuration = None
             if vdu_id:
                 for vdu in get_iterable(db_vnfd, "vdu"):
                     if vdu_id == vdu["id"]:
             if vdu_id:
                 for vdu in get_iterable(db_vnfd, "vdu"):
                     if vdu_id == vdu["id"]:
-                        for config_primitive in deep_get(vdu, ("vdu-configuration", "config-primitive"), ()):
-                            if config_primitive["name"] == primitive:
-                                config_primitive_desc = config_primitive
-                                break
+                        descriptor_configuration = vdu.get("vdu-configuration")
                         break
             elif kdu_name:
                 for kdu in get_iterable(db_vnfd, "kdu"):
                     if kdu_name == kdu["name"]:
                         break
             elif kdu_name:
                 for kdu in get_iterable(db_vnfd, "kdu"):
                     if kdu_name == kdu["name"]:
-                        for config_primitive in deep_get(kdu, ("kdu-configuration", "config-primitive"), ()):
-                            if config_primitive["name"] == primitive:
-                                config_primitive_desc = config_primitive
-                                break
+                        descriptor_configuration = kdu.get("kdu-configuration")
                         break
             elif vnf_index:
                         break
             elif vnf_index:
-                for config_primitive in deep_get(db_vnfd, ("vnf-configuration", "config-primitive"), ()):
-                    if config_primitive["name"] == primitive:
-                        config_primitive_desc = config_primitive
-                        break
+                descriptor_configuration = db_vnfd.get("vnf-configuration")
             else:
             else:
-                for config_primitive in deep_get(db_nsd, ("ns-configuration", "config-primitive"), ()):
+                descriptor_configuration = db_nsd.get("ns-configuration")
+
+            if descriptor_configuration and descriptor_configuration.get("config-primitive"):
+                for config_primitive in descriptor_configuration["config-primitive"]:
                     if config_primitive["name"] == primitive:
                         config_primitive_desc = config_primitive
                         break
 
                     if config_primitive["name"] == primitive:
                         config_primitive_desc = config_primitive
                         break
 
-            if not config_primitive_desc and not (kdu_name and primitive in ("upgrade", "rollback", "status")):
-                raise LcmException("Primitive {} not found at [ns|vnf|vdu]-configuration:config-primitive ".
-                                   format(primitive))
+            if not config_primitive_desc:
+                if not (kdu_name and primitive in ("upgrade", "rollback", "status")):
+                    raise LcmException("Primitive {} not found at [ns|vnf|vdu]-configuration:config-primitive ".
+                                       format(primitive))
+                primitive_name = primitive
+                ee_descriptor_id = None
+            else:
+                primitive_name = config_primitive_desc.get("execution-environment-primitive", primitive)
+                ee_descriptor_id = config_primitive_desc.get("execution-environment-ref")
 
             if vnf_index:
                 if vdu_id:
 
             if vnf_index:
                 if vdu_id:
@@ -3224,7 +3791,7 @@ class NsLcm(LcmBase):
                 kdu_action = True if not deep_get(kdu, ("kdu-configuration", "juju")) else False
 
             # TODO check if ns is in a proper status
                 kdu_action = True if not deep_get(kdu, ("kdu-configuration", "juju")) else False
 
             # TODO check if ns is in a proper status
-            if kdu_name and (primitive in ("upgrade", "rollback", "status") or kdu_action):
+            if kdu_name and (primitive_name in ("upgrade", "rollback", "status") or kdu_action):
                 # kdur and desc_params already set from before
                 if primitive_params:
                     desc_params.update(primitive_params)
                 # kdur and desc_params already set from before
                 if primitive_params:
                     desc_params.update(primitive_params)
@@ -3242,9 +3809,9 @@ class NsLcm(LcmBase):
                 db_dict = {"collection": "nsrs",
                            "filter": {"_id": nsr_id},
                            "path": "_admin.deployed.K8s.{}".format(index)}
                 db_dict = {"collection": "nsrs",
                            "filter": {"_id": nsr_id},
                            "path": "_admin.deployed.K8s.{}".format(index)}
-                self.logger.debug(logging_text + "Exec k8s {} on {}.{}".format(primitive, vnf_index, kdu_name))
-                step = "Executing kdu {}".format(primitive)
-                if primitive == "upgrade":
+                self.logger.debug(logging_text + "Exec k8s {} on {}.{}".format(primitive_name, vnf_index, kdu_name))
+                step = "Executing kdu {}".format(primitive_name)
+                if primitive_name == "upgrade":
                     if desc_params.get("kdu_model"):
                         kdu_model = desc_params.get("kdu_model")
                         del desc_params["kdu_model"]
                     if desc_params.get("kdu_model"):
                         kdu_model = desc_params.get("kdu_model")
                         del desc_params["kdu_model"]
@@ -3263,14 +3830,14 @@ class NsLcm(LcmBase):
                             timeout=timeout_ns_action),
                         timeout=timeout_ns_action + 10)
                     self.logger.debug(logging_text + " Upgrade of kdu {} done".format(detailed_status))
                             timeout=timeout_ns_action),
                         timeout=timeout_ns_action + 10)
                     self.logger.debug(logging_text + " Upgrade of kdu {} done".format(detailed_status))
-                elif primitive == "rollback":
+                elif primitive_name == "rollback":
                     detailed_status = await asyncio.wait_for(
                         self.k8scluster_map[kdu["k8scluster-type"]].rollback(
                             cluster_uuid=kdu.get("k8scluster-uuid"),
                             kdu_instance=kdu.get("kdu-instance"),
                             db_dict=db_dict),
                         timeout=timeout_ns_action)
                     detailed_status = await asyncio.wait_for(
                         self.k8scluster_map[kdu["k8scluster-type"]].rollback(
                             cluster_uuid=kdu.get("k8scluster-uuid"),
                             kdu_instance=kdu.get("kdu-instance"),
                             db_dict=db_dict),
                         timeout=timeout_ns_action)
-                elif primitive == "status":
+                elif primitive_name == "status":
                     detailed_status = await asyncio.wait_for(
                         self.k8scluster_map[kdu["k8scluster-type"]].status_kdu(
                             cluster_uuid=kdu.get("k8scluster-uuid"),
                     detailed_status = await asyncio.wait_for(
                         self.k8scluster_map[kdu["k8scluster-type"]].status_kdu(
                             cluster_uuid=kdu.get("k8scluster-uuid"),
@@ -3284,7 +3851,7 @@ class NsLcm(LcmBase):
                         self.k8scluster_map[kdu["k8scluster-type"]].exec_primitive(
                             cluster_uuid=kdu.get("k8scluster-uuid"),
                             kdu_instance=kdu_instance,
                         self.k8scluster_map[kdu["k8scluster-type"]].exec_primitive(
                             cluster_uuid=kdu.get("k8scluster-uuid"),
                             kdu_instance=kdu_instance,
-                            primitive_name=primitive,
+                            primitive_name=primitive_name,
                             params=params, db_dict=db_dict,
                             timeout=timeout_ns_action),
                         timeout=timeout_ns_action)
                             params=params, db_dict=db_dict,
                             timeout=timeout_ns_action),
                         timeout=timeout_ns_action)
@@ -3295,14 +3862,21 @@ class NsLcm(LcmBase):
                     detailed_status = ''
                     nslcmop_operation_state = 'FAILED'
             else:
                     detailed_status = ''
                     nslcmop_operation_state = 'FAILED'
             else:
+                ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"],
+                                                              member_vnf_index=vnf_index,
+                                                              vdu_id=vdu_id,
+                                                              vdu_count_index=vdu_count_index,
+                                                              ee_descriptor_id=ee_descriptor_id)
+                db_nslcmop_notif = {"collection": "nslcmops",
+                                    "filter": {"_id": nslcmop_id},
+                                    "path": "admin.VCA"}
                 nslcmop_operation_state, detailed_status = await self._ns_execute_primitive(
                 nslcmop_operation_state, detailed_status = await self._ns_execute_primitive(
-                    self._look_for_deployed_vca(nsr_deployed["VCA"],
-                                                member_vnf_index=vnf_index,
-                                                vdu_id=vdu_id,
-                                                vdu_count_index=vdu_count_index),
-                    primitive=primitive,
+                    ee_id,
+                    primitive=primitive_name,
                     primitive_params=self._map_primitive_params(config_primitive_desc, primitive_params, desc_params),
                     primitive_params=self._map_primitive_params(config_primitive_desc, primitive_params, desc_params),
-                    timeout=timeout_ns_action)
+                    timeout=timeout_ns_action,
+                    vca_type=vca_type,
+                    db_dict=db_nslcmop_notif)
 
             db_nslcmop_update["detailed-status"] = detailed_status
             error_description_nslcmop = detailed_status if nslcmop_operation_state == "FAILED" else ""
 
             db_nslcmop_update["detailed-status"] = detailed_status
             error_description_nslcmop = detailed_status if nslcmop_operation_state == "FAILED" else ""
@@ -3377,7 +3951,6 @@ class NsLcm(LcmBase):
         scale_process = None
         old_operational_status = ""
         old_config_status = ""
         scale_process = None
         old_operational_status = ""
         old_config_status = ""
-        vnfr_scaled = False
         try:
             # wait for any previous tasks in process
             step = "Waiting for previous operations to terminate"
         try:
             # wait for any previous tasks in process
             step = "Waiting for previous operations to terminate"
@@ -3472,8 +4045,25 @@ class NsLcm(LcmBase):
                 vdu_scaling_info["scaling_direction"] = "OUT"
                 vdu_scaling_info["vdu-create"] = {}
                 for vdu_scale_info in scaling_descriptor["vdu"]:
                 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)})
                     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":
                     vdu_scaling_info["vdu-create"][vdu_scale_info["vdu-id-ref"]] = vdu_scale_info.get("count", 1)
 
             elif scaling_type == "SCALE_IN":
@@ -3530,7 +4120,7 @@ class NsLcm(LcmBase):
                             raise LcmException(
                                 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-action"
                                 "[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:config-"
                             raise LcmException(
                                 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-action"
                                 "[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:config-"
-                                "primitive".format(scaling_group, config_primitive))
+                                "primitive".format(scaling_group, vnf_config_primitive))
 
                         vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info}
                         if db_vnfr.get("additionalParamsForVnf"):
 
                         vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info}
                         if db_vnfr.get("additionalParamsForVnf"):
@@ -3540,10 +4130,10 @@ class NsLcm(LcmBase):
                         db_nsr_update["config-status"] = "configuring pre-scaling"
                         primitive_params = self._map_primitive_params(config_primitive, {}, vnfr_params)
 
                         db_nsr_update["config-status"] = "configuring pre-scaling"
                         primitive_params = self._map_primitive_params(config_primitive, {}, vnfr_params)
 
-                        # Pre-scale reintent check: Check if this sub-operation has been executed before
+                        # Pre-scale retry check: Check if this sub-operation has been executed before
                         op_index = self._check_or_add_scale_suboperation(
                             db_nslcmop, nslcmop_id, vnf_index, vnf_config_primitive, primitive_params, 'PRE-SCALE')
                         op_index = self._check_or_add_scale_suboperation(
                             db_nslcmop, nslcmop_id, vnf_index, vnf_config_primitive, primitive_params, 'PRE-SCALE')
-                        if (op_index == self.SUBOPERATION_STATUS_SKIP):
+                        if op_index == self.SUBOPERATION_STATUS_SKIP:
                             # Skip sub-operation
                             result = 'COMPLETED'
                             result_detail = 'Done'
                             # Skip sub-operation
                             result = 'COMPLETED'
                             result_detail = 'Done'
@@ -3551,26 +4141,30 @@ class NsLcm(LcmBase):
                                               "vnf_config_primitive={} Skipped sub-operation, result {} {}".format(
                                                   vnf_config_primitive, result, result_detail))
                         else:
                                               "vnf_config_primitive={} Skipped sub-operation, result {} {}".format(
                                                   vnf_config_primitive, result, result_detail))
                         else:
-                            if (op_index == self.SUBOPERATION_STATUS_NEW):
+                            if op_index == self.SUBOPERATION_STATUS_NEW:
                                 # New sub-operation: Get index of this sub-operation
                                 op_index = len(db_nslcmop.get('_admin', {}).get('operations')) - 1
                                 self.logger.debug(logging_text + "vnf_config_primitive={} New sub-operation".
                                                   format(vnf_config_primitive))
                             else:
                                 # New sub-operation: Get index of this sub-operation
                                 op_index = len(db_nslcmop.get('_admin', {}).get('operations')) - 1
                                 self.logger.debug(logging_text + "vnf_config_primitive={} New sub-operation".
                                                   format(vnf_config_primitive))
                             else:
-                                # Reintent:  Get registered params for this existing sub-operation
+                                # retry:  Get registered params for this existing sub-operation
                                 op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
                                 vnf_index = op.get('member_vnf_index')
                                 vnf_config_primitive = op.get('primitive')
                                 primitive_params = op.get('primitive_params')
                                 op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
                                 vnf_index = op.get('member_vnf_index')
                                 vnf_config_primitive = op.get('primitive')
                                 primitive_params = op.get('primitive_params')
-                                self.logger.debug(logging_text + "vnf_config_primitive={} Sub-operation reintent".
+                                self.logger.debug(logging_text + "vnf_config_primitive={} Sub-operation retry".
                                                   format(vnf_config_primitive))
                             # Execute the primitive, either with new (first-time) or registered (reintent) args
                                                   format(vnf_config_primitive))
                             # Execute the primitive, either with new (first-time) or registered (reintent) args
+                            ee_descriptor_id = config_primitive.get("execution-environment-ref")
+                            primitive_name = config_primitive.get("execution-environment-primitive",
+                                                                  vnf_config_primitive)
+                            ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"],
+                                                                          member_vnf_index=vnf_index,
+                                                                          vdu_id=None,
+                                                                          vdu_count_index=None,
+                                                                          ee_descriptor_id=ee_descriptor_id)
                             result, result_detail = await self._ns_execute_primitive(
                             result, result_detail = await self._ns_execute_primitive(
-                                self._look_for_deployed_vca(nsr_deployed["VCA"],
-                                                            member_vnf_index=vnf_index,
-                                                            vdu_id=None,
-                                                            vdu_count_index=None),
-                                vnf_config_primitive, primitive_params)
+                                ee_id, primitive_name, primitive_params, vca_type)
                             self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
                                 vnf_config_primitive, result, result_detail))
                             # Update operationState = COMPLETED | FAILED
                             self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
                                 vnf_config_primitive, result, result_detail))
                             # Update operationState = COMPLETED | FAILED
@@ -3588,26 +4182,26 @@ class NsLcm(LcmBase):
             # if (RO_nsr_id and RO_scaling_info):
             if RO_scaling_info:
                 scale_process = "RO"
             # if (RO_nsr_id and RO_scaling_info):
             if RO_scaling_info:
                 scale_process = "RO"
-                # Scale RO reintent check: Check if this sub-operation has been executed before
+                # Scale RO retry check: Check if this sub-operation has been executed before
                 op_index = self._check_or_add_scale_suboperation(
                     db_nslcmop, vnf_index, None, None, 'SCALE-RO', RO_nsr_id, RO_scaling_info)
                 op_index = self._check_or_add_scale_suboperation(
                     db_nslcmop, vnf_index, None, None, 'SCALE-RO', RO_nsr_id, RO_scaling_info)
-                if (op_index == self.SUBOPERATION_STATUS_SKIP):
+                if op_index == self.SUBOPERATION_STATUS_SKIP:
                     # Skip sub-operation
                     result = 'COMPLETED'
                     result_detail = 'Done'
                     self.logger.debug(logging_text + "Skipped sub-operation RO, result {} {}".format(
                         result, result_detail))
                 else:
                     # Skip sub-operation
                     result = 'COMPLETED'
                     result_detail = 'Done'
                     self.logger.debug(logging_text + "Skipped sub-operation RO, result {} {}".format(
                         result, result_detail))
                 else:
-                    if (op_index == self.SUBOPERATION_STATUS_NEW):
+                    if op_index == self.SUBOPERATION_STATUS_NEW:
                         # New sub-operation: Get index of this sub-operation
                         op_index = len(db_nslcmop.get('_admin', {}).get('operations')) - 1
                         self.logger.debug(logging_text + "New sub-operation RO")
                     else:
                         # New sub-operation: Get index of this sub-operation
                         op_index = len(db_nslcmop.get('_admin', {}).get('operations')) - 1
                         self.logger.debug(logging_text + "New sub-operation RO")
                     else:
-                        # Reintent:  Get registered params for this existing sub-operation
+                        # retry:  Get registered params for this existing sub-operation
                         op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
                         RO_nsr_id = op.get('RO_nsr_id')
                         RO_scaling_info = op.get('RO_scaling_info')
                         op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
                         RO_nsr_id = op.get('RO_nsr_id')
                         RO_scaling_info = op.get('RO_scaling_info')
-                        self.logger.debug(logging_text + "Sub-operation RO reintent".format(
+                        self.logger.debug(logging_text + "Sub-operation RO retry for primitive {}".format(
                             vnf_config_primitive))
 
                     RO_desc = await self.RO.create_action("ns", RO_nsr_id, {"vdu-scaling": RO_scaling_info})
                             vnf_config_primitive))
 
                     RO_desc = await self.RO.create_action("ns", RO_nsr_id, {"vdu-scaling": RO_scaling_info})
@@ -3618,7 +4212,7 @@ class NsLcm(LcmBase):
                     db_nslcmop_update["_admin.deploy.RO"] = RO_nslcmop_id
 
                     RO_task_done = False
                     db_nslcmop_update["_admin.deploy.RO"] = RO_nslcmop_id
 
                     RO_task_done = False
-                    step = detailed_status = "Waiting RO_task_id={} to complete the scale action.".format(RO_nslcmop_id)
+                    step = detailed_status = "Waiting for VIM to scale. RO_task_id={}.".format(RO_nslcmop_id)
                     detailed_status_old = None
                     self.logger.debug(logging_text + step)
 
                     detailed_status_old = None
                     self.logger.debug(logging_text + step)
 
@@ -3638,11 +4232,16 @@ class NsLcm(LcmBase):
                                 detailed_status = step + "; {}".format(ns_status_info)
                             elif ns_status == "ACTIVE":
                                 RO_task_done = True
                                 detailed_status = step + "; {}".format(ns_status_info)
                             elif ns_status == "ACTIVE":
                                 RO_task_done = True
+                                self.scale_vnfr(db_vnfr, vdu_create=vdu_create, vdu_delete=vdu_delete)
                                 step = detailed_status = "Waiting ns ready at RO. RO_id={}".format(RO_nsr_id)
                                 self.logger.debug(logging_text + step)
                             else:
                                 assert False, "ROclient.check_action_status returns unknown {}".format(ns_status)
                         else:
                                 step = detailed_status = "Waiting ns ready at RO. RO_id={}".format(RO_nsr_id)
                                 self.logger.debug(logging_text + step)
                             else:
                                 assert False, "ROclient.check_action_status returns unknown {}".format(ns_status)
                         else:
+                            desc = await self.RO.show("ns", RO_nsr_id)
+                            ns_status, ns_status_info = self.RO.check_ns_status(desc)
+                            # deploymentStatus
+                            self._on_update_ro_db(nsrs_id=nsr_id, ro_descriptor=desc)
 
                             if ns_status == "ERROR":
                                 raise ROclient.ROClientException(ns_status_info)
 
                             if ns_status == "ERROR":
                                 raise ROclient.ROClientException(ns_status_info)
@@ -3651,15 +4250,7 @@ class NsLcm(LcmBase):
                             elif ns_status == "ACTIVE":
                                 step = detailed_status = \
                                     "Waiting for management IP address reported by the VIM. Updating VNFRs"
                             elif ns_status == "ACTIVE":
                                 step = detailed_status = \
                                     "Waiting for management IP address reported by the VIM. Updating VNFRs"
-                                if not vnfr_scaled:
-                                    self.scale_vnfr(db_vnfr, vdu_create=vdu_create, vdu_delete=vdu_delete)
-                                    vnfr_scaled = True
                                 try:
                                 try:
-                                    desc = await self.RO.show("ns", RO_nsr_id)
-
-                                    # deploymentStatus
-                                    self._on_update_ro_db(nsrs_id=nsr_id, ro_descriptor=desc)
-
                                     # nsr_deployed["nsr_ip"] = RO.get_ns_vnf_info(desc)
                                     self.ns_update_vnfr({db_vnfr["member-vnf-index-ref"]: db_vnfr}, desc)
                                     break
                                     # nsr_deployed["nsr_ip"] = RO.get_ns_vnf_info(desc)
                                     self.ns_update_vnfr({db_vnfr["member-vnf-index-ref"]: db_vnfr}, desc)
                                     break
@@ -3725,15 +4316,15 @@ class NsLcm(LcmBase):
                             if config_primitive["name"] == vnf_config_primitive:
                                 break
                         else:
                             if config_primitive["name"] == vnf_config_primitive:
                                 break
                         else:
-                            raise LcmException("Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:"
-                                               "scaling-config-action[vnf-config-primitive-name-ref='{}'] does not "
-                                               "match any vnf-configuration:config-primitive".format(scaling_group,
-                                                                                                     config_primitive))
+                            raise LcmException(
+                                "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-"
+                                "action[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:"
+                                "config-primitive".format(scaling_group, vnf_config_primitive))
                         scale_process = "VCA"
                         db_nsr_update["config-status"] = "configuring post-scaling"
                         primitive_params = self._map_primitive_params(config_primitive, {}, vnfr_params)
 
                         scale_process = "VCA"
                         db_nsr_update["config-status"] = "configuring post-scaling"
                         primitive_params = self._map_primitive_params(config_primitive, {}, vnfr_params)
 
-                        # Post-scale reintent check: Check if this sub-operation has been executed before
+                        # Post-scale retry check: Check if this sub-operation has been executed before
                         op_index = self._check_or_add_scale_suboperation(
                             db_nslcmop, nslcmop_id, vnf_index, vnf_config_primitive, primitive_params, 'POST-SCALE')
                         if op_index == self.SUBOPERATION_STATUS_SKIP:
                         op_index = self._check_or_add_scale_suboperation(
                             db_nslcmop, nslcmop_id, vnf_index, vnf_config_primitive, primitive_params, 'POST-SCALE')
                         if op_index == self.SUBOPERATION_STATUS_SKIP:
@@ -3750,20 +4341,24 @@ class NsLcm(LcmBase):
                                 self.logger.debug(logging_text + "vnf_config_primitive={} New sub-operation".
                                                   format(vnf_config_primitive))
                             else:
                                 self.logger.debug(logging_text + "vnf_config_primitive={} New sub-operation".
                                                   format(vnf_config_primitive))
                             else:
-                                # Reintent:  Get registered params for this existing sub-operation
+                                # retry:  Get registered params for this existing sub-operation
                                 op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
                                 vnf_index = op.get('member_vnf_index')
                                 vnf_config_primitive = op.get('primitive')
                                 primitive_params = op.get('primitive_params')
                                 op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
                                 vnf_index = op.get('member_vnf_index')
                                 vnf_config_primitive = op.get('primitive')
                                 primitive_params = op.get('primitive_params')
-                                self.logger.debug(logging_text + "vnf_config_primitive={} Sub-operation reintent".
+                                self.logger.debug(logging_text + "vnf_config_primitive={} Sub-operation retry".
                                                   format(vnf_config_primitive))
                             # Execute the primitive, either with new (first-time) or registered (reintent) args
                                                   format(vnf_config_primitive))
                             # Execute the primitive, either with new (first-time) or registered (reintent) args
+                            ee_descriptor_id = config_primitive.get("execution-environment-ref")
+                            primitive_name = config_primitive.get("execution-environment-primitive",
+                                                                  vnf_config_primitive)
+                            ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"],
+                                                                          member_vnf_index=vnf_index,
+                                                                          vdu_id=None,
+                                                                          vdu_count_index=None,
+                                                                          ee_descriptor_id=ee_descriptor_id)
                             result, result_detail = await self._ns_execute_primitive(
                             result, result_detail = await self._ns_execute_primitive(
-                                self._look_for_deployed_vca(nsr_deployed["VCA"],
-                                                            member_vnf_index=vnf_index,
-                                                            vdu_id=None,
-                                                            vdu_count_index=None),
-                                vnf_config_primitive, primitive_params)
+                                ee_id, primitive_name, primitive_params, vca_type)
                             self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
                                 vnf_config_primitive, result, result_detail))
                             # Update operationState = COMPLETED | FAILED
                             self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
                                 vnf_config_primitive, result, result_detail))
                             # Update operationState = COMPLETED | FAILED
@@ -3844,3 +4439,68 @@ class NsLcm(LcmBase):
                     self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
             self.logger.debug(logging_text + "Exit")
             self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_scale")
                     self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
             self.logger.debug(logging_text + "Exit")
             self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_scale")
+
+    async def add_prometheus_metrics(self, ee_id, artifact_path, ee_config_descriptor, vnfr_id, nsr_id, target_ip):
+        if not self.prometheus:
+            return
+        # look if exist a file called 'prometheus*.j2' and
+        artifact_content = self.fs.dir_ls(artifact_path)
+        job_file = next((f for f in artifact_content if f.startswith("prometheus") and f.endswith(".j2")), None)
+        if not job_file:
+            return
+        with self.fs.file_open((artifact_path, job_file), "r") as f:
+            job_data = f.read()
+
+        # TODO get_service
+        _, _, service = ee_id.partition(".")   # remove prefix   "namespace."
+        host_name = "{}-{}".format(service, ee_config_descriptor["metric-service"])
+        host_port = "80"
+        vnfr_id = vnfr_id.replace("-", "")
+        variables = {
+            "JOB_NAME": vnfr_id,
+            "TARGET_IP": target_ip,
+            "EXPORTER_POD_IP": host_name,
+            "EXPORTER_POD_PORT": host_port,
+        }
+        job_list = self.prometheus.parse_job(job_data, variables)
+        # ensure job_name is using the vnfr_id. Adding the metadata nsr_id
+        for job in job_list:
+            if not isinstance(job.get("job_name"), str) or vnfr_id not in job["job_name"]:
+                job["job_name"] = vnfr_id + "_" + str(randint(1, 10000))
+            job["nsr_id"] = nsr_id
+        job_dict = {jl["job_name"]: jl for jl in job_list}
+        if await self.prometheus.update(job_dict):
+            return list(job_dict.keys())
+
+    def get_vca_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
+        """
+        Get VCA Cloud and VCA Cloud Credentials for the VIM account
+
+        :param: vim_account_id:     VIM Account ID
+
+        :return: (cloud_name, cloud_credential)
+        """
+        config = self.get_vim_account_config(vim_account_id)
+        return config.get("vca_cloud"), config.get("vca_cloud_credential")
+
+    def get_vca_k8s_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
+        """
+        Get VCA K8s Cloud and VCA K8s Cloud Credentials for the VIM account
+
+        :param: vim_account_id:     VIM Account ID
+
+        :return: (cloud_name, cloud_credential)
+        """
+        config = self.get_vim_account_config(vim_account_id)
+        return config.get("vca_k8s_cloud"), config.get("vca_k8s_cloud_credential")
+
+    def get_vim_account_config(self, vim_account_id: str) -> dict:
+        """
+        Get VIM Account config from the OSM Database
+
+        :param: vim_account_id:     VIM Account ID
+
+        :return: Dictionary with the config of the vim account
+        """
+        vim_account = self.db.get_one(table="vim_accounts", q_filter={"_id": vim_account_id}, fail_on_empty=False)
+        return vim_account.get("config", {}) if vim_account else {}