Fix 1425 NG-RO unable to pin VNF with vim_account config
[osm/LCM.git] / osm_lcm / ns.py
index 8b2be1e..ab1ee3b 100644 (file)
@@ -31,6 +31,7 @@ from jinja2 import (
 )
 
 from osm_lcm import ROclient
+from osm_lcm.data_utils.nsr import get_deployed_kdu
 from osm_lcm.ng_ro import NgRoClient, NgRoException
 from osm_lcm.lcm_utils import (
     LcmException,
@@ -54,9 +55,10 @@ from osm_lcm.data_utils.vnfd import (
     get_scaling_aspect,
     get_number_of_instances,
     get_juju_ee_ref,
+    get_kdu_profile,
 )
 from osm_lcm.data_utils.list_utils import find_in_list
-from osm_lcm.data_utils.vnfr import get_osm_params, get_vdur_index
+from osm_lcm.data_utils.vnfr import get_osm_params, get_vdur_index, get_kdur
 from osm_lcm.data_utils.dict_utils import parse_yaml_strings
 from osm_lcm.data_utils.database.vim_account import VimAccountDB
 from n2vc.k8s_helm_conn import K8sHelmConnector
@@ -511,7 +513,7 @@ class NsLcm(LcmBase):
                     vdur_copy = deepcopy(vdur)
                     vdur_copy["status"] = "BUILD"
                     vdur_copy["status-detailed"] = None
-                    vdur_copy["ip-address"]: None
+                    vdur_copy["ip-address"] = None
                     vdur_copy["_id"] = str(uuid4())
                     vdur_copy["count-index"] += count + 1
                     vdur_copy["id"] = "{}-{}".format(
@@ -794,6 +796,37 @@ class NsLcm(LcmBase):
             if vld_params.get("common_id"):
                 target_vld["common_id"] = vld_params.get("common_id")
 
+        # modify target["ns"]["vld"] with instantiation parameters to override vnf vim-account
+        def update_ns_vld_target(target, ns_params):
+            for vnf_params in ns_params.get("vnf", ()):
+                if vnf_params.get("vimAccountId"):
+                    target_vnf = next(
+                        (
+                            vnfr
+                            for vnfr in db_vnfrs.values()
+                            if vnf_params["member-vnf-index"]
+                            == vnfr["member-vnf-index-ref"]
+                        ),
+                        None,
+                    )
+                    vdur = next((vdur for vdur in target_vnf.get("vdur", ())), None)
+                    for a_index, a_vld in enumerate(target["ns"]["vld"]):
+                        target_vld = find_in_list(
+                            get_iterable(vdur, "interfaces"),
+                            lambda iface: iface.get("ns-vld-id") == a_vld["name"],
+                        )
+                        if target_vld:
+                            if vnf_params.get("vimAccountId") not in a_vld.get(
+                                "vim_info", {}
+                            ):
+                                target["ns"]["vld"][a_index].get("vim_info").update(
+                                    {
+                                        "vim:{}".format(vnf_params["vimAccountId"]): {
+                                            "vim_network_name": ""
+                                        }
+                                    }
+                                )
+
         nslcmop_id = db_nslcmop["_id"]
         target = {
             "name": db_nsr["name"],
@@ -909,6 +942,8 @@ class NsLcm(LcmBase):
                 vld_params.update(vld_instantiation_params)
             parse_vld_instantiation_params(target_vim, target_vld, vld_params, None)
             target["ns"]["vld"].append(target_vld)
+        # Update the target ns_vld if vnf vim_account is overriden by instantiation params
+        update_ns_vld_target(target, ns_params)
 
         for vnfr in db_vnfrs.values():
             vnfd = find_in_list(
@@ -1555,9 +1590,13 @@ class NsLcm(LcmBase):
         raise LcmException("Configuration aborted because dependent charm/s timeout")
 
     def get_vca_id(self, db_vnfr: dict, db_nsr: dict):
-        return deep_get(db_vnfr, ("vca-id",)) or deep_get(
-            db_nsr, ("instantiate_params", "vcaId")
-        )
+        vca_id = None
+        if db_vnfr:
+            vca_id = deep_get(db_vnfr, ("vca-id",))
+        elif db_nsr:
+            vim_account_id = deep_get(db_nsr, ("instantiate_params", "vimAccountId"))
+            vca_id = VimAccountDB.get_vim_account_with_id(vim_account_id).get("vca")
+        return vca_id
 
     async def instantiate_N2VC(
         self,
@@ -1601,17 +1640,22 @@ class NsLcm(LcmBase):
 
             namespace = "{nsi}.{ns}".format(nsi=nsi_id if nsi_id else "", ns=nsr_id)
 
+            if vca_type == "native_charm":
+                index_number = 0
+            else:
+                index_number = vdu_index or 0
+
             if vnfr_id:
                 element_type = "VNF"
                 element_under_configuration = vnfr_id
-                namespace += ".{}-{}".format(vnfr_id, vdu_index or 0)
+                namespace += ".{}-{}".format(vnfr_id, index_number)
                 if vdu_id:
-                    namespace += ".{}-{}".format(vdu_id, vdu_index or 0)
+                    namespace += ".{}-{}".format(vdu_id, index_number)
                     element_type = "VDU"
-                    element_under_configuration = "{}-{}".format(vdu_id, vdu_index or 0)
+                    element_under_configuration = "{}-{}".format(vdu_id, index_number)
                     osm_config["osm"]["vdu_id"] = vdu_id
                 elif kdu_name:
-                    namespace += ".{}.{}".format(kdu_name, vdu_index or 0)
+                    namespace += ".{}".format(kdu_name)
                     element_type = "KDU"
                     element_under_configuration = kdu_name
                     osm_config["osm"]["kdu_name"] = kdu_name
@@ -1804,6 +1848,7 @@ class NsLcm(LcmBase):
                     config=config,
                     num_units=num_units,
                     vca_id=vca_id,
+                    vca_type=vca_type,
                 )
 
             # write in db flag of configuration_sw already installed
@@ -1920,6 +1965,7 @@ class NsLcm(LcmBase):
                     params_dict=primitive_params_,
                     db_dict=db_dict,
                     vca_id=vca_id,
+                    vca_type=vca_type,
                 )
                 # Once some primitive has been exec, check and write at db if it needs to exec terminated primitives
                 if check_if_terminated_needed:
@@ -2212,6 +2258,10 @@ class NsLcm(LcmBase):
             # read from db: operation
             stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id)
             db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
+            if db_nslcmop["operationParams"].get("additionalParamsForVnf"):
+                db_nslcmop["operationParams"]["additionalParamsForVnf"] = json.loads(
+                    db_nslcmop["operationParams"]["additionalParamsForVnf"]
+                )
             ns_params = db_nslcmop.get("operationParams")
             if ns_params and ns_params.get("timeout_ns_deploy"):
                 timeout_ns_deploy = ns_params["timeout_ns_deploy"]
@@ -2239,6 +2289,14 @@ class NsLcm(LcmBase):
 
             # for each vnf in ns, read vnfd
             for vnfr in db_vnfrs_list:
+                if vnfr.get("kdur"):
+                    kdur_list = []
+                    for kdur in vnfr["kdur"]:
+                        if kdur.get("additionalParams"):
+                            kdur["additionalParams"] = json.loads(kdur["additionalParams"])
+                        kdur_list.append(kdur)
+                    vnfr["kdur"] = kdur_list
+
                 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr
                 vnfd_id = vnfr["vnfd-id"]
                 vnfd_ref = vnfr["vnfd-ref"]
@@ -2388,9 +2446,7 @@ class NsLcm(LcmBase):
                     deploy_params_vdu["OSM"] = get_osm_params(
                         db_vnfr, vdu_id, vdu_count_index=0
                     )
-                    vdud_count = get_vdu_profile(vnfd, vdu_id).get(
-                        "max-number-of-instances", 1
-                    )
+                    vdud_count = get_number_of_instances(vnfd, vdu_id)
 
                     self.logger.debug("VDUD > {}".format(vdud))
                     self.logger.debug(
@@ -2846,13 +2902,16 @@ class NsLcm(LcmBase):
                 "path": nsr_db_path,
             }
 
-            kdu_instance = self.k8scluster_map[
-                k8sclustertype
-            ].generate_kdu_instance_name(
-                db_dict=db_dict_install,
-                kdu_model=k8s_instance_info["kdu-model"],
-                kdu_name=k8s_instance_info["kdu-name"],
-            )
+            if k8s_instance_info.get("kdu-deployment-name"):
+                kdu_instance = k8s_instance_info.get("kdu-deployment-name")
+            else:
+                kdu_instance = self.k8scluster_map[
+                    k8sclustertype
+                ].generate_kdu_instance_name(
+                    db_dict=db_dict_install,
+                    kdu_model=k8s_instance_info["kdu-model"],
+                    kdu_name=k8s_instance_info["kdu-name"],
+                )
             self.update_db_2(
                 "nsrs", nsr_id, {nsr_db_path + ".kdu-instance": kdu_instance}
             )
@@ -3091,6 +3150,7 @@ class NsLcm(LcmBase):
                         if kdud["name"] == kdur["kdu-name"]
                     )
                     namespace = kdur.get("k8s-namespace")
+                    kdu_deployment_name = kdur.get("kdu-deployment-name")
                     if kdur.get("helm-chart"):
                         kdumodel = kdur["helm-chart"]
                         # Default version: helm3, if helm-version is v2 assign v2
@@ -3203,6 +3263,7 @@ class NsLcm(LcmBase):
                         "kdu-name": kdur["kdu-name"],
                         "kdu-model": kdumodel,
                         "namespace": namespace,
+                        "kdu-deployment-name": kdu_deployment_name,
                     }
                     db_path = "_admin.deployed.K8s.{}".format(index)
                     db_nsr_update[db_path] = k8s_instance_info
@@ -3280,6 +3341,8 @@ class NsLcm(LcmBase):
         )
         if "execution-environment-list" in descriptor_config:
             ee_list = descriptor_config.get("execution-environment-list", [])
+        elif "juju" in descriptor_config:
+            ee_list = [descriptor_config]  # ns charms
         else:  # other types as script are not supported
             ee_list = []
 
@@ -3738,6 +3801,7 @@ class NsLcm(LcmBase):
             await self.vca_map[vca_type].delete_execution_environment(
                 vca_deployed["ee_id"],
                 scaling_in=scaling_in,
+                vca_type=vca_type,
                 vca_id=vca_id,
             )
 
@@ -4033,8 +4097,13 @@ class NsLcm(LcmBase):
 
             for vca_index, vca in enumerate(get_iterable(nsr_deployed, "VCA")):
                 config_descriptor = None
-
-                vca_id = self.get_vca_id(db_vnfrs_dict[vca["member-vnf-index"]], db_nsr)
+                vca_member_vnf_index = vca.get("member-vnf-index")
+                vca_id = self.get_vca_id(
+                    db_vnfrs_dict.get(vca_member_vnf_index)
+                    if vca_member_vnf_index
+                    else None,
+                    db_nsr,
+                )
                 if not vca or not vca.get("ee_id"):
                     continue
                 if not vca.get("member-vnf-index"):
@@ -4506,6 +4575,7 @@ class NsLcm(LcmBase):
                             total_timeout=self.timeout_primitive,
                             db_dict=db_dict,
                             vca_id=vca_id,
+                            vca_type=vca_type,
                         ),
                         timeout=timeout or self.timeout_primitive,
                     )
@@ -4592,6 +4662,10 @@ class NsLcm(LcmBase):
             step = "Getting information from database"
             db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
             db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
+            if db_nslcmop["operationParams"].get("primitive_params"):
+                db_nslcmop["operationParams"]["primitive_params"] = json.loads(
+                    db_nslcmop["operationParams"]["primitive_params"]
+                )
 
             nsr_deployed = db_nsr["_admin"].get("deployed")
             vnf_index = db_nslcmop["operationParams"].get("member_vnf_index")
@@ -4609,6 +4683,13 @@ class NsLcm(LcmBase):
                 db_vnfr = self.db.get_one(
                     "vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id}
                 )
+                if db_vnfr.get("kdur"):
+                    kdur_list = []
+                    for kdur in db_vnfr["kdur"]:
+                        if kdur.get("additionalParams"):
+                            kdur["additionalParams"] = json.loads(kdur["additionalParams"])
+                        kdur_list.append(kdur)
+                    db_vnfr["kdur"] = kdur_list
                 step = "Getting vnfd from database"
                 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
             else:
@@ -4947,14 +5028,6 @@ class NsLcm(LcmBase):
             self.update_db_2("nsrs", nsr_id, db_nsr_update)
             nsr_deployed = db_nsr["_admin"].get("deployed")
 
-            #######
-            nsr_deployed = db_nsr["_admin"].get("deployed")
-            vnf_index = db_nslcmop["operationParams"].get("member_vnf_index")
-            # vdu_id = db_nslcmop["operationParams"].get("vdu_id")
-            # vdu_count_index = db_nslcmop["operationParams"].get("vdu_count_index")
-            # vdu_name = db_nslcmop["operationParams"].get("vdu_name")
-            #######
-
             vnf_index = db_nslcmop["operationParams"]["scaleVnfData"][
                 "scaleByStepData"
             ]["member-vnf-index"]
@@ -5017,9 +5090,9 @@ class NsLcm(LcmBase):
                     db_nsr_update[
                         "_admin.scaling-group.{}.name".format(admin_scale_index)
                     ] = scaling_group
-            RO_scaling_info = []
-            VCA_scaling_info = []
-            vdu_scaling_info = {"scaling_group_name": scaling_group, "vdu": []}
+
+            vca_scaling_info = []
+            scaling_info = {"scaling_group_name": scaling_group, "vdu": [], "kdu": []}
             if scaling_type == "SCALE_OUT":
                 if "aspect-delta-details" not in scaling_descriptor:
                     raise LcmException(
@@ -5030,12 +5103,14 @@ class NsLcm(LcmBase):
                 # count if max-instance-count is reached
                 deltas = scaling_descriptor.get("aspect-delta-details")["deltas"]
 
-                vdu_scaling_info["scaling_direction"] = "OUT"
-                vdu_scaling_info["vdu-create"] = {}
+                scaling_info["scaling_direction"] = "OUT"
+                scaling_info["vdu-create"] = {}
+                scaling_info["kdu-create"] = {}
                 for delta in deltas:
-                    for vdu_delta in delta["vdu-delta"]:
+                    for vdu_delta in delta.get("vdu-delta", {}):
                         vdud = get_vdu(db_vnfd, vdu_delta["id"])
-                        vdu_index = get_vdur_index(db_vnfr, vdu_delta)
+                        # vdu_index also provides the number of instance of the targeted vdu
+                        vdu_count = vdu_index = get_vdur_index(db_vnfr, vdu_delta)
                         cloud_init_text = self._get_vdu_cloud_init_content(
                             vdud, db_vnfd
                         )
@@ -5056,10 +5131,18 @@ class NsLcm(LcmBase):
                         default_instance_num = get_number_of_instances(
                             db_vnfd, vdud["id"]
                         )
+                        instances_number = vdu_delta.get("number-of-instances", 1)
+                        nb_scale_op += instances_number
+
+                        new_instance_count = nb_scale_op + default_instance_num
+                        # Control if new count is over max and vdu count is less than max.
+                        # Then assign new instance count
+                        if new_instance_count > max_instance_count > vdu_count:
+                            instances_number = new_instance_count - max_instance_count
+                        else:
+                            instances_number = instances_number
 
-                        nb_scale_op += vdu_delta.get("number-of-instances", 1)
-
-                        if nb_scale_op + default_instance_num > max_instance_count:
+                        if new_instance_count > max_instance_count:
                             raise LcmException(
                                 "reached the limit of {} (max-instance-count) "
                                 "scaling-out operations for the "
@@ -5081,7 +5164,7 @@ class NsLcm(LcmBase):
                                         vdud["id"],
                                     )
                                 )
-                                VCA_scaling_info.append(
+                                vca_scaling_info.append(
                                     {
                                         "osm_vdu_id": vdu_delta["id"],
                                         "member-vnf-index": vnf_index,
@@ -5089,33 +5172,102 @@ class NsLcm(LcmBase):
                                         "vdu_index": vdu_index + x,
                                     }
                                 )
-                        RO_scaling_info.append(
+                        scaling_info["vdu-create"][vdu_delta["id"]] = instances_number
+                    for kdu_delta in delta.get("kdu-resource-delta", {}):
+                        kdu_profile = get_kdu_profile(db_vnfd, kdu_delta["id"])
+                        kdu_name = kdu_profile["kdu-name"]
+                        resource_name = kdu_profile["resource-name"]
+
+                        # Might have different kdus in the same delta
+                        # Should have list for each kdu
+                        if not scaling_info["kdu-create"].get(kdu_name, None):
+                            scaling_info["kdu-create"][kdu_name] = []
+
+                        kdur = get_kdur(db_vnfr, kdu_name)
+                        if kdur.get("helm-chart"):
+                            k8s_cluster_type = "helm-chart-v3"
+                            self.logger.debug("kdur: {}".format(kdur))
+                            if (
+                                kdur.get("helm-version")
+                                and kdur.get("helm-version") == "v2"
+                            ):
+                                k8s_cluster_type = "helm-chart"
+                            raise NotImplementedError
+                        elif kdur.get("juju-bundle"):
+                            k8s_cluster_type = "juju-bundle"
+                        else:
+                            raise LcmException(
+                                "kdu type for kdu='{}.{}' is neither helm-chart nor "
+                                "juju-bundle. Maybe an old NBI version is running".format(
+                                    db_vnfr["member-vnf-index-ref"], kdu_name
+                                )
+                            )
+
+                        max_instance_count = 10
+                        if kdu_profile and "max-number-of-instances" in kdu_profile:
+                            max_instance_count = kdu_profile.get(
+                                "max-number-of-instances", 10
+                            )
+
+                        nb_scale_op += kdu_delta.get("number-of-instances", 1)
+                        deployed_kdu, _ = get_deployed_kdu(
+                            nsr_deployed, kdu_name, vnf_index
+                        )
+                        if deployed_kdu is None:
+                            raise LcmException(
+                                "KDU '{}' for vnf '{}' not deployed".format(
+                                    kdu_name, vnf_index
+                                )
+                            )
+                        kdu_instance = deployed_kdu.get("kdu-instance")
+                        instance_num = await self.k8scluster_map[
+                            k8s_cluster_type
+                        ].get_scale_count(resource_name, kdu_instance, vca_id=vca_id)
+                        kdu_replica_count = instance_num + kdu_delta.get(
+                            "number-of-instances", 1
+                        )
+
+                        # Control if new count is over max and instance_num is less than max.
+                        # Then assign max instance number to kdu replica count
+                        if kdu_replica_count > max_instance_count > instance_num:
+                            kdu_replica_count = max_instance_count
+                        if kdu_replica_count > max_instance_count:
+                            raise LcmException(
+                                "reached the limit of {} (max-instance-count) "
+                                "scaling-out operations for the "
+                                "scaling-group-descriptor '{}'".format(
+                                    instance_num, scaling_group
+                                )
+                            )
+
+                        for x in range(kdu_delta.get("number-of-instances", 1)):
+                            vca_scaling_info.append(
+                                {
+                                    "osm_kdu_id": kdu_name,
+                                    "member-vnf-index": vnf_index,
+                                    "type": "create",
+                                    "kdu_index": instance_num + x - 1,
+                                }
+                            )
+                        scaling_info["kdu-create"][kdu_name].append(
                             {
-                                "osm_vdu_id": vdu_delta["id"],
                                 "member-vnf-index": vnf_index,
                                 "type": "create",
-                                "count": vdu_delta.get("number-of-instances", 1),
+                                "k8s-cluster-type": k8s_cluster_type,
+                                "resource-name": resource_name,
+                                "scale": kdu_replica_count,
                             }
                         )
-                        if cloud_init_list:
-                            RO_scaling_info[-1]["cloud_init"] = cloud_init_list
-                        vdu_scaling_info["vdu-create"][vdu_delta["id"]] = vdu_delta.get(
-                            "number-of-instances", 1
-                        )
-
             elif scaling_type == "SCALE_IN":
-                if (
-                    "min-instance-count" in scaling_descriptor
-                    and scaling_descriptor["min-instance-count"] is not None
-                ):
-                    min_instance_count = int(scaling_descriptor["min-instance-count"])
-
-                vdu_scaling_info["scaling_direction"] = "IN"
-                vdu_scaling_info["vdu-delete"] = {}
                 deltas = scaling_descriptor.get("aspect-delta-details")["deltas"]
+
+                scaling_info["scaling_direction"] = "IN"
+                scaling_info["vdu-delete"] = {}
+                scaling_info["kdu-delete"] = {}
+
                 for delta in deltas:
-                    for vdu_delta in delta["vdu-delta"]:
-                        vdu_index = get_vdur_index(db_vnfr, vdu_delta)
+                    for vdu_delta in delta.get("vdu-delta", {}):
+                        vdu_count = vdu_index = get_vdur_index(db_vnfr, vdu_delta)
                         min_instance_count = 0
                         vdu_profile = get_vdu_profile(db_vnfd, vdu_delta["id"])
                         if vdu_profile and "min-number-of-instances" in vdu_profile:
@@ -5124,26 +5276,25 @@ class NsLcm(LcmBase):
                         default_instance_num = get_number_of_instances(
                             db_vnfd, vdu_delta["id"]
                         )
+                        instance_num = vdu_delta.get("number-of-instances", 1)
+                        nb_scale_op -= instance_num
+
+                        new_instance_count = nb_scale_op + default_instance_num
+
+                        if new_instance_count < min_instance_count < vdu_count:
+                            instances_number = min_instance_count - new_instance_count
+                        else:
+                            instances_number = instance_num
 
-                        nb_scale_op -= vdu_delta.get("number-of-instances", 1)
-                        if nb_scale_op + default_instance_num < min_instance_count:
+                        if new_instance_count < min_instance_count:
                             raise LcmException(
                                 "reached the limit of {} (min-instance-count) scaling-in operations for the "
                                 "scaling-group-descriptor '{}'".format(
                                     nb_scale_op, scaling_group
                                 )
                             )
-                        RO_scaling_info.append(
-                            {
-                                "osm_vdu_id": vdu_delta["id"],
-                                "member-vnf-index": vnf_index,
-                                "type": "delete",
-                                "count": vdu_delta.get("number-of-instances", 1),
-                                "vdu_index": vdu_index - 1,
-                            }
-                        )
                         for x in range(vdu_delta.get("number-of-instances", 1)):
-                            VCA_scaling_info.append(
+                            vca_scaling_info.append(
                                 {
                                     "osm_vdu_id": vdu_delta["id"],
                                     "member-vnf-index": vnf_index,
@@ -5151,17 +5302,93 @@ class NsLcm(LcmBase):
                                     "vdu_index": vdu_index - 1 - x,
                                 }
                             )
-                        vdu_scaling_info["vdu-delete"][vdu_delta["id"]] = vdu_delta.get(
+                        scaling_info["vdu-delete"][vdu_delta["id"]] = instances_number
+                    for kdu_delta in delta.get("kdu-resource-delta", {}):
+                        kdu_profile = get_kdu_profile(db_vnfd, kdu_delta["id"])
+                        kdu_name = kdu_profile["kdu-name"]
+                        resource_name = kdu_profile["resource-name"]
+
+                        if not scaling_info["kdu-delete"].get(kdu_name, None):
+                            scaling_info["kdu-delete"][kdu_name] = []
+
+                        kdur = get_kdur(db_vnfr, kdu_name)
+                        if kdur.get("helm-chart"):
+                            k8s_cluster_type = "helm-chart-v3"
+                            self.logger.debug("kdur: {}".format(kdur))
+                            if (
+                                kdur.get("helm-version")
+                                and kdur.get("helm-version") == "v2"
+                            ):
+                                k8s_cluster_type = "helm-chart"
+                            raise NotImplementedError
+                        elif kdur.get("juju-bundle"):
+                            k8s_cluster_type = "juju-bundle"
+                        else:
+                            raise LcmException(
+                                "kdu type for kdu='{}.{}' is neither helm-chart nor "
+                                "juju-bundle. Maybe an old NBI version is running".format(
+                                    db_vnfr["member-vnf-index-ref"], kdur["kdu-name"]
+                                )
+                            )
+
+                        min_instance_count = 0
+                        if kdu_profile and "min-number-of-instances" in kdu_profile:
+                            min_instance_count = kdu_profile["min-number-of-instances"]
+
+                        nb_scale_op -= kdu_delta.get("number-of-instances", 1)
+                        deployed_kdu, _ = get_deployed_kdu(
+                            nsr_deployed, kdu_name, vnf_index
+                        )
+                        if deployed_kdu is None:
+                            raise LcmException(
+                                "KDU '{}' for vnf '{}' not deployed".format(
+                                    kdu_name, vnf_index
+                                )
+                            )
+                        kdu_instance = deployed_kdu.get("kdu-instance")
+                        instance_num = await self.k8scluster_map[
+                            k8s_cluster_type
+                        ].get_scale_count(resource_name, kdu_instance, vca_id=vca_id)
+                        kdu_replica_count = instance_num - kdu_delta.get(
                             "number-of-instances", 1
                         )
 
+                        if kdu_replica_count < min_instance_count < instance_num:
+                            kdu_replica_count = min_instance_count
+                        if kdu_replica_count < min_instance_count:
+                            raise LcmException(
+                                "reached the limit of {} (min-instance-count) scaling-in operations for the "
+                                "scaling-group-descriptor '{}'".format(
+                                    instance_num, scaling_group
+                                )
+                            )
+
+                        for x in range(kdu_delta.get("number-of-instances", 1)):
+                            vca_scaling_info.append(
+                                {
+                                    "osm_kdu_id": kdu_name,
+                                    "member-vnf-index": vnf_index,
+                                    "type": "delete",
+                                    "kdu_index": instance_num - x - 1,
+                                }
+                            )
+                        scaling_info["kdu-delete"][kdu_name].append(
+                            {
+                                "member-vnf-index": vnf_index,
+                                "type": "delete",
+                                "k8s-cluster-type": k8s_cluster_type,
+                                "resource-name": resource_name,
+                                "scale": kdu_replica_count,
+                            }
+                        )
+
             # update VDU_SCALING_INFO with the VDUs to delete ip_addresses
-            vdu_delete = copy(vdu_scaling_info.get("vdu-delete"))
-            if vdu_scaling_info["scaling_direction"] == "IN":
+            vdu_delete = copy(scaling_info.get("vdu-delete"))
+            if scaling_info["scaling_direction"] == "IN":
                 for vdur in reversed(db_vnfr["vdur"]):
                     if vdu_delete.get(vdur["vdu-id-ref"]):
                         vdu_delete[vdur["vdu-id-ref"]] -= 1
-                        vdu_scaling_info["vdu"].append(
+                        scaling_info["vdu"].append(
                             {
                                 "name": vdur.get("name") or vdur.get("vdu-name"),
                                 "vdu_id": vdur["vdu-id-ref"],
@@ -5169,7 +5396,7 @@ class NsLcm(LcmBase):
                             }
                         )
                         for interface in vdur["interfaces"]:
-                            vdu_scaling_info["vdu"][-1]["interface"].append(
+                            scaling_info["vdu"][-1]["interface"].append(
                                 {
                                     "name": interface["name"],
                                     "ip_address": interface["ip-address"],
@@ -5213,7 +5440,7 @@ class NsLcm(LcmBase):
                                 "primitive".format(scaling_group, vnf_config_primitive)
                             )
 
-                        vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info}
+                        vnfr_params = {"VDU_SCALE_INFO": scaling_info}
                         if db_vnfr.get("additionalParamsForVnf"):
                             vnfr_params.update(db_vnfr["additionalParamsForVnf"])
 
@@ -5226,7 +5453,6 @@ class NsLcm(LcmBase):
                         # 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,
@@ -5315,24 +5541,33 @@ class NsLcm(LcmBase):
             ] = time()
 
             # SCALE-IN VCA - BEGIN
-            if VCA_scaling_info:
+            if vca_scaling_info:
                 step = db_nslcmop_update[
                     "detailed-status"
                 ] = "Deleting the execution environments"
                 scale_process = "VCA"
-                for vdu_info in VCA_scaling_info:
-                    if vdu_info["type"] == "delete":
-                        member_vnf_index = str(vdu_info["member-vnf-index"])
+                for vca_info in vca_scaling_info:
+                    if vca_info["type"] == "delete":
+                        member_vnf_index = str(vca_info["member-vnf-index"])
                         self.logger.debug(
-                            logging_text + "vdu info: {}".format(vdu_info)
-                        )
-                        vdu_id = vdu_info["osm_vdu_id"]
-                        vdu_index = int(vdu_info["vdu_index"])
-                        stage[
-                            1
-                        ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
-                            member_vnf_index, vdu_id, vdu_index
+                            logging_text + "vdu info: {}".format(vca_info)
                         )
+                        if vca_info.get("osm_vdu_id"):
+                            vdu_id = vca_info["osm_vdu_id"]
+                            vdu_index = int(vca_info["vdu_index"])
+                            stage[
+                                1
+                            ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
+                                member_vnf_index, vdu_id, vdu_index
+                            )
+                        else:
+                            vdu_index = 0
+                            kdu_id = vca_info["osm_kdu_id"]
+                            stage[
+                                1
+                            ] = "Scaling member_vnf_index={}, kdu_id={}, vdu_index={} ".format(
+                                member_vnf_index, kdu_id, vdu_index
+                            )
                         stage[2] = step = "Scaling in VCA"
                         self._write_op_status(op_id=nslcmop_id, stage=stage)
                         vca_update = db_nsr["_admin"]["deployed"]["VCA"]
@@ -5414,117 +5649,165 @@ class NsLcm(LcmBase):
             # SCALE-IN VCA - END
 
             # SCALE RO - BEGIN
-            if RO_scaling_info:
+            if scaling_info.get("vdu-create") or scaling_info.get("vdu-delete"):
                 scale_process = "RO"
                 if self.ro_config.get("ng"):
                     await self._scale_ng_ro(
-                        logging_text,
-                        db_nsr,
-                        db_nslcmop,
-                        db_vnfr,
-                        vdu_scaling_info,
-                        stage,
+                        logging_text, db_nsr, db_nslcmop, db_vnfr, scaling_info, stage
                     )
-            vdu_scaling_info.pop("vdu-create", None)
-            vdu_scaling_info.pop("vdu-delete", None)
+            scaling_info.pop("vdu-create", None)
+            scaling_info.pop("vdu-delete", None)
+
+            scale_process = None
+            # SCALE RO - END
+
+            # SCALE KDU - BEGIN
+            if scaling_info.get("kdu-create") or scaling_info.get("kdu-delete"):
+                scale_process = "KDU"
+                await self._scale_kdu(
+                    logging_text, nsr_id, nsr_deployed, db_vnfd, vca_id, scaling_info
+                )
+            scaling_info.pop("kdu-create", None)
+            scaling_info.pop("kdu-delete", None)
 
             scale_process = None
+            # SCALE KDU - END
+
             if db_nsr_update:
                 self.update_db_2("nsrs", nsr_id, db_nsr_update)
-            # SCALE RO - END
 
             # SCALE-UP VCA - BEGIN
-            if VCA_scaling_info:
+            if vca_scaling_info:
                 step = db_nslcmop_update[
                     "detailed-status"
                 ] = "Creating new execution environments"
                 scale_process = "VCA"
-                for vdu_info in VCA_scaling_info:
-                    if vdu_info["type"] == "create":
-                        member_vnf_index = str(vdu_info["member-vnf-index"])
+                for vca_info in vca_scaling_info:
+                    if vca_info["type"] == "create":
+                        member_vnf_index = str(vca_info["member-vnf-index"])
                         self.logger.debug(
-                            logging_text + "vdu info: {}".format(vdu_info)
+                            logging_text + "vdu info: {}".format(vca_info)
                         )
                         vnfd_id = db_vnfr["vnfd-ref"]
-                        vdu_index = int(vdu_info["vdu_index"])
-                        deploy_params = {"OSM": get_osm_params(db_vnfr)}
-                        if db_vnfr.get("additionalParamsForVnf"):
-                            deploy_params.update(
-                                parse_yaml_strings(
-                                    db_vnfr["additionalParamsForVnf"].copy()
+                        if vca_info.get("osm_vdu_id"):
+                            vdu_index = int(vca_info["vdu_index"])
+                            deploy_params = {"OSM": get_osm_params(db_vnfr)}
+                            if db_vnfr.get("additionalParamsForVnf"):
+                                deploy_params.update(
+                                    parse_yaml_strings(
+                                        db_vnfr["additionalParamsForVnf"].copy()
+                                    )
                                 )
+                            descriptor_config = get_configuration(
+                                db_vnfd, db_vnfd["id"]
                             )
-                        descriptor_config = get_configuration(db_vnfd, db_vnfd["id"])
-                        if descriptor_config:
-                            vdu_id = None
-                            vdu_name = None
-                            kdu_name = None
-                            self._deploy_n2vc(
-                                logging_text=logging_text
-                                + "member_vnf_index={} ".format(member_vnf_index),
-                                db_nsr=db_nsr,
-                                db_vnfr=db_vnfr,
-                                nslcmop_id=nslcmop_id,
-                                nsr_id=nsr_id,
-                                nsi_id=nsi_id,
-                                vnfd_id=vnfd_id,
-                                vdu_id=vdu_id,
-                                kdu_name=kdu_name,
-                                member_vnf_index=member_vnf_index,
-                                vdu_index=vdu_index,
-                                vdu_name=vdu_name,
-                                deploy_params=deploy_params,
-                                descriptor_config=descriptor_config,
-                                base_folder=base_folder,
-                                task_instantiation_info=tasks_dict_info,
-                                stage=stage,
-                            )
-                        vdu_id = vdu_info["osm_vdu_id"]
-                        vdur = find_in_list(
-                            db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id
-                        )
-                        descriptor_config = get_configuration(db_vnfd, vdu_id)
-                        if vdur.get("additionalParams"):
-                            deploy_params_vdu = parse_yaml_strings(
-                                vdur["additionalParams"]
+                            if descriptor_config:
+                                vdu_id = None
+                                vdu_name = None
+                                kdu_name = None
+                                self._deploy_n2vc(
+                                    logging_text=logging_text
+                                    + "member_vnf_index={} ".format(member_vnf_index),
+                                    db_nsr=db_nsr,
+                                    db_vnfr=db_vnfr,
+                                    nslcmop_id=nslcmop_id,
+                                    nsr_id=nsr_id,
+                                    nsi_id=nsi_id,
+                                    vnfd_id=vnfd_id,
+                                    vdu_id=vdu_id,
+                                    kdu_name=kdu_name,
+                                    member_vnf_index=member_vnf_index,
+                                    vdu_index=vdu_index,
+                                    vdu_name=vdu_name,
+                                    deploy_params=deploy_params,
+                                    descriptor_config=descriptor_config,
+                                    base_folder=base_folder,
+                                    task_instantiation_info=tasks_dict_info,
+                                    stage=stage,
+                                )
+                            vdu_id = vca_info["osm_vdu_id"]
+                            vdur = find_in_list(
+                                db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id
                             )
-                        else:
-                            deploy_params_vdu = deploy_params
-                        deploy_params_vdu["OSM"] = get_osm_params(
-                            db_vnfr, vdu_id, vdu_count_index=vdu_index
-                        )
-                        if descriptor_config:
-                            vdu_name = None
-                            kdu_name = None
-                            stage[
-                                1
-                            ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
-                                member_vnf_index, vdu_id, vdu_index
+                            descriptor_config = get_configuration(db_vnfd, vdu_id)
+                            if vdur.get("additionalParams"):
+                                deploy_params_vdu = parse_yaml_strings(
+                                    vdur["additionalParams"]
+                                )
+                            else:
+                                deploy_params_vdu = deploy_params
+                            deploy_params_vdu["OSM"] = get_osm_params(
+                                db_vnfr, vdu_id, vdu_count_index=vdu_index
                             )
-                            stage[2] = step = "Scaling out VCA"
-                            self._write_op_status(op_id=nslcmop_id, stage=stage)
-                            self._deploy_n2vc(
-                                logging_text=logging_text
-                                + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
+                            if descriptor_config:
+                                vdu_name = None
+                                kdu_name = None
+                                stage[
+                                    1
+                                ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
                                     member_vnf_index, vdu_id, vdu_index
-                                ),
-                                db_nsr=db_nsr,
-                                db_vnfr=db_vnfr,
-                                nslcmop_id=nslcmop_id,
-                                nsr_id=nsr_id,
-                                nsi_id=nsi_id,
-                                vnfd_id=vnfd_id,
-                                vdu_id=vdu_id,
-                                kdu_name=kdu_name,
-                                member_vnf_index=member_vnf_index,
-                                vdu_index=vdu_index,
-                                vdu_name=vdu_name,
-                                deploy_params=deploy_params_vdu,
-                                descriptor_config=descriptor_config,
-                                base_folder=base_folder,
-                                task_instantiation_info=tasks_dict_info,
-                                stage=stage,
-                            )
+                                )
+                                stage[2] = step = "Scaling out VCA"
+                                self._write_op_status(op_id=nslcmop_id, stage=stage)
+                                self._deploy_n2vc(
+                                    logging_text=logging_text
+                                    + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
+                                        member_vnf_index, vdu_id, vdu_index
+                                    ),
+                                    db_nsr=db_nsr,
+                                    db_vnfr=db_vnfr,
+                                    nslcmop_id=nslcmop_id,
+                                    nsr_id=nsr_id,
+                                    nsi_id=nsi_id,
+                                    vnfd_id=vnfd_id,
+                                    vdu_id=vdu_id,
+                                    kdu_name=kdu_name,
+                                    member_vnf_index=member_vnf_index,
+                                    vdu_index=vdu_index,
+                                    vdu_name=vdu_name,
+                                    deploy_params=deploy_params_vdu,
+                                    descriptor_config=descriptor_config,
+                                    base_folder=base_folder,
+                                    task_instantiation_info=tasks_dict_info,
+                                    stage=stage,
+                                )
+                        else:
+                            kdu_name = vca_info["osm_kdu_id"]
+                            descriptor_config = get_configuration(db_vnfd, kdu_name)
+                            if descriptor_config:
+                                vdu_id = None
+                                kdu_index = int(vca_info["kdu_index"])
+                                vdu_name = None
+                                kdur = next(
+                                    x
+                                    for x in db_vnfr["kdur"]
+                                    if x["kdu-name"] == kdu_name
+                                )
+                                deploy_params_kdu = {"OSM": get_osm_params(db_vnfr)}
+                                if kdur.get("additionalParams"):
+                                    deploy_params_kdu = parse_yaml_strings(
+                                        kdur["additionalParams"]
+                                    )
+
+                                self._deploy_n2vc(
+                                    logging_text=logging_text,
+                                    db_nsr=db_nsr,
+                                    db_vnfr=db_vnfr,
+                                    nslcmop_id=nslcmop_id,
+                                    nsr_id=nsr_id,
+                                    nsi_id=nsi_id,
+                                    vnfd_id=vnfd_id,
+                                    vdu_id=vdu_id,
+                                    kdu_name=kdu_name,
+                                    member_vnf_index=member_vnf_index,
+                                    vdu_index=kdu_index,
+                                    vdu_name=vdu_name,
+                                    deploy_params=deploy_params_kdu,
+                                    descriptor_config=descriptor_config,
+                                    base_folder=base_folder,
+                                    task_instantiation_info=tasks_dict_info,
+                                    stage=stage,
+                                )
             # SCALE-UP VCA - END
             scale_process = None
 
@@ -5551,7 +5834,7 @@ class NsLcm(LcmBase):
                             vnf_config_primitive
                         )
 
-                        vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info}
+                        vnfr_params = {"VDU_SCALE_INFO": scaling_info}
                         if db_vnfr.get("additionalParamsForVnf"):
                             vnfr_params.update(db_vnfr["additionalParamsForVnf"])
 
@@ -5578,7 +5861,6 @@ class NsLcm(LcmBase):
                         # 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,
@@ -5761,6 +6043,107 @@ class NsLcm(LcmBase):
             self.logger.debug(logging_text + "Exit")
             self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_scale")
 
+    async def _scale_kdu(
+        self, logging_text, nsr_id, nsr_deployed, db_vnfd, vca_id, scaling_info
+    ):
+        _scaling_info = scaling_info.get("kdu-create") or scaling_info.get("kdu-delete")
+        for kdu_name in _scaling_info:
+            for kdu_scaling_info in _scaling_info[kdu_name]:
+                deployed_kdu, index = get_deployed_kdu(
+                    nsr_deployed, kdu_name, kdu_scaling_info["member-vnf-index"]
+                )
+                cluster_uuid = deployed_kdu["k8scluster-uuid"]
+                kdu_instance = deployed_kdu["kdu-instance"]
+                scale = int(kdu_scaling_info["scale"])
+                k8s_cluster_type = kdu_scaling_info["k8s-cluster-type"]
+
+                db_dict = {
+                    "collection": "nsrs",
+                    "filter": {"_id": nsr_id},
+                    "path": "_admin.deployed.K8s.{}".format(index),
+                }
+
+                step = "scaling application {}".format(
+                    kdu_scaling_info["resource-name"]
+                )
+                self.logger.debug(logging_text + step)
+
+                if kdu_scaling_info["type"] == "delete":
+                    kdu_config = get_configuration(db_vnfd, kdu_name)
+                    if (
+                        kdu_config
+                        and kdu_config.get("terminate-config-primitive")
+                        and get_juju_ee_ref(db_vnfd, kdu_name) is None
+                    ):
+                        terminate_config_primitive_list = kdu_config.get(
+                            "terminate-config-primitive"
+                        )
+                        terminate_config_primitive_list.sort(
+                            key=lambda val: int(val["seq"])
+                        )
+
+                        for (
+                            terminate_config_primitive
+                        ) in terminate_config_primitive_list:
+                            primitive_params_ = self._map_primitive_params(
+                                terminate_config_primitive, {}, {}
+                            )
+                            step = "execute terminate config primitive"
+                            self.logger.debug(logging_text + step)
+                            await asyncio.wait_for(
+                                self.k8scluster_map[k8s_cluster_type].exec_primitive(
+                                    cluster_uuid=cluster_uuid,
+                                    kdu_instance=kdu_instance,
+                                    primitive_name=terminate_config_primitive["name"],
+                                    params=primitive_params_,
+                                    db_dict=db_dict,
+                                    vca_id=vca_id,
+                                ),
+                                timeout=600,
+                            )
+
+                await asyncio.wait_for(
+                    self.k8scluster_map[k8s_cluster_type].scale(
+                        kdu_instance,
+                        scale,
+                        kdu_scaling_info["resource-name"],
+                        vca_id=vca_id,
+                    ),
+                    timeout=self.timeout_vca_on_error,
+                )
+
+                if kdu_scaling_info["type"] == "create":
+                    kdu_config = get_configuration(db_vnfd, kdu_name)
+                    if (
+                        kdu_config
+                        and kdu_config.get("initial-config-primitive")
+                        and get_juju_ee_ref(db_vnfd, kdu_name) 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, {}, {}
+                            )
+                            step = "execute initial config primitive"
+                            self.logger.debug(logging_text + step)
+                            await asyncio.wait_for(
+                                self.k8scluster_map[k8s_cluster_type].exec_primitive(
+                                    cluster_uuid=cluster_uuid,
+                                    kdu_instance=kdu_instance,
+                                    primitive_name=initial_config_primitive["name"],
+                                    params=primitive_params_,
+                                    db_dict=db_dict,
+                                    vca_id=vca_id,
+                                ),
+                                timeout=600,
+                            )
+
     async def _scale_ng_ro(
         self, logging_text, db_nsr, db_nslcmop, db_vnfr, vdu_scaling_info, stage
     ):