Feature 10509 manual scaling for native k8s charm 88/10488/15 release-v10.0-start
authoraktas <emin.aktas@ulakhaberlesme.com.tr>
Mon, 15 Mar 2021 08:26:10 +0000 (11:26 +0300)
committeraktas <emin.aktas@ulakhaberlesme.com.tr>
Wed, 19 May 2021 13:18:39 +0000 (16:18 +0300)
Also includes improvements for scale function

Change-Id: I23f51b8c1b219681841d0b1f7f4db3a0d9ed4c7b
Signed-off-by: aktas <emin.aktas@ulakhaberlesme.com.tr>
osm_lcm/data_utils/nsr.py
osm_lcm/data_utils/vnfd.py
osm_lcm/data_utils/vnfr.py
osm_lcm/ns.py
osm_lcm/tests/test_db_descriptors.py
osm_lcm/tests/test_ns.py

index f62b0b4..006713c 100644 (file)
 # contact: fbravo@whitestack.com
 ##
 
+from osm_lcm.lcm_utils import get_iterable
+
 
 def get_vlds(nsr):
     return nsr.get("vld", ())
+
+
+def get_deployed_kdu(nsr_deployed, kdu_name, member_vnf_index):
+    deployed_kdu = None
+    index = None
+    for index, deployed_kdu in enumerate(get_iterable(nsr_deployed, "K8s")):
+        if (
+            kdu_name == deployed_kdu["kdu-name"]
+            and deployed_kdu["member-vnf-index"] == member_vnf_index
+        ):
+            break
+    return deployed_kdu, index
index 5351c41..17a98a9 100644 (file)
@@ -101,6 +101,13 @@ def get_vdu_profile(vnfd, vdu_profile_id):
     )
 
 
+def get_kdu_profile(vnfd, kdu_profile_id):
+    return list_utils.find_in_list(
+        vnfd.get("df", ())[0]["kdu-resource-profile"],
+        lambda kdu_profile: kdu_profile["id"] == kdu_profile_id,
+    )
+
+
 def get_configuration(vnfd, entity_id):
     lcm_ops_config = vnfd.get("df")[0].get("lcm-operations-configuration")
     if not lcm_ops_config:
index 7e4d164..fe98102 100644 (file)
@@ -69,3 +69,11 @@ def get_vdur_index(db_vnfr, vdu_delta):
         return len([x for x in vdur_list if x.get("vdu-id-ref") == vdu_delta["id"]])
     else:
         return 0
+
+
+def get_kdur(db_vnfr, kdu_name):
+    kdur_list = get_iterable(db_vnfr, "kdur")
+    if kdur_list:
+        return next(x for x in kdur_list if x.get("kdu-name") == kdu_name)
+    else:
+        return None
index 8b2be1e..ddd827e 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
@@ -4947,14 +4949,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 +5011,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 +5024,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 +5052,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 +5085,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 +5093,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 +5197,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
 
-                        nb_scale_op -= vdu_delta.get("number-of-instances", 1)
-                        if nb_scale_op + default_instance_num < min_instance_count:
+                        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
+
+                        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 +5223,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 +5317,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 +5361,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"])
 
@@ -5315,24 +5463,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 +5571,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 +5756,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"])
 
@@ -5761,6 +5966,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
     ):
index a42449b..cc4f828 100644 (file)
@@ -224,6 +224,66 @@ db_nslcmops_text = """
     operationState: COMPLETED
     startTime: 1575034637.0445576
     statusEnteredTime: 1575034663.8484545
+
+-   _admin:
+      created: 1575034637.044651
+      modified: 1575034637.044651
+      projects_read:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+      projects_write:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+    _id: 52770491-a765-40ce-97a1-c6e200bba7b3
+    detailed-status: done
+    id: 52770491-a765-40ce-97a1-c6e200bba7b3
+    isAutomaticInvocation: false
+    isCancelPending: false
+    lcmOperationType: instantiate
+    links:
+      nsInstance: /osm/nslcm/v1/ns_instances/c54b14cb-69a8-45bc-b011-d6bea187dc0a
+      self: /osm/nslcm/v1/ns_lcm_op_occs/52770491-a765-40ce-97a1-c6e200bba7b3
+    nsInstanceId: 0bcb701c-ee4d-41ab-8ee6-f4156f7f114d
+    operationParams:
+          lcmOperationType: scale
+          nsInstanceId: c54b14cb-69a8-45bc-b011-d6bea187dc0a
+          scaleVnfData:
+            scaleByStepData:
+              member-vnf-index: native-kdu
+              scaling-group-descriptor: kdu_scaling_group
+            scaleVnfType: SCALE_OUT
+          scaleType: SCALE_VNF
+    operationState: COMPLETED
+    startTime: 1575034637.0445576
+    statusEnteredTime: 1575034663.8484545
+
+-   _admin:
+      created: 1575034637.044651
+      modified: 1575034637.044651
+      projects_read:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+      projects_write:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+    _id: 4013bbd2-b151-40ee-bcef-7e24ce5432f6
+    detailed-status: done
+    id: 4013bbd2-b151-40ee-bcef-7e24ce5432f6
+    isAutomaticInvocation: false
+    isCancelPending: false
+    lcmOperationType: instantiate
+    links:
+      nsInstance: /osm/nslcm/v1/ns_instances/c54b14cb-69a8-45bc-b011-d6bea187dc0a
+      self: /osm/nslcm/v1/ns_lcm_op_occs/4013bbd2-b151-40ee-bcef-7e24ce5432f6
+    nsInstanceId: 0bcb701c-ee4d-41ab-8ee6-f4156f7f114d
+    operationParams:
+          lcmOperationType: scale
+          nsInstanceId: c54b14cb-69a8-45bc-b011-d6bea187dc0a
+          scaleVnfData:
+            scaleByStepData:
+              member-vnf-index: native-kdu
+              scaling-group-descriptor: kdu_scaling_group_2
+            scaleVnfType: SCALE_OUT
+          scaleType: SCALE_VNF
+    operationState: COMPLETED
+    startTime: 1575034637.0445576
+    statusEnteredTime: 1575034663.8484545
 """
 
 db_nsrs_text = """
@@ -893,6 +953,73 @@ db_nsrs_text = """
         vim-network-name: mgmt
     vnfd-id:
     - 7ab0d10d-8ce2-4c68-aef6-cc5a437a9c62
+
+-   _admin:
+      created: 1575034637.011233
+      current-operation: null
+      deployed:
+        K8s:
+        - k8scluster-uuid: 73d96432-d692-40d2-8440-e0c73aee209c
+          kdu-instance: native-kdu-0
+          kdu-model: native-kdu-0
+          kdu-name: native-kdu
+          member-vnf-index: native-kdu
+          vnfr-id: 5ac34899-a23a-4b3c-918a-cd77acadbea6
+        RO:
+          detailed-status: Deployed at VIM
+          nsd_id: b03a8de8-1898-4142-bc6d-3b0787df567d
+          nsr_id: b5ce3e00-8647-415d-afaa-d5a612cf3074
+          nsr_status: ACTIVE
+          operational-status: running
+          vnfd:
+          - id: b9493dae-a4c9-4b96-8965-329581efb0a1
+            member-vnf-index: native-kdu
+        VCA: []
+      modified: 1575034637.011233
+      nsState: INSTANTIATED
+      nslcmop: null
+      operation-type: null
+      projects_read:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+      projects_write:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+    _id: c54b14cb-69a8-45bc-b011-d6bea187dc0a
+    additionalParamsForNs: null
+    admin-status: ENABLED
+    config-status: configured
+    constituent-vnfr-ref:
+    - 5ac34899-a23a-4b3c-918a-cd77acadbea6
+    create-time: 1575034636.9990137
+    datacenter: ea958ba5-4e58-4405-bf42-6e3be15d4c3a
+    description: default description
+    detailed-status: done
+    id: c54b14cb-69a8-45bc-b011-d6bea187dc0a
+    instantiate_params:
+      nsDescription: default description
+      nsName: native-kdu
+      nsdId: d0f63683-9032-4c6f-8928-ffd4674b9f69
+      vimAccountId: 74337dcb-ef54-41e7-bd2d-8c0d7fcd326f
+    name: native-kdu
+    name-ref: native-kdu
+    ns-instance-config-ref: c54b14cb-69a8-45bc-b011-d6bea187dc0a
+    nsd-id: d0f63683-9032-4c6f-8928-ffd4674b9f69
+    nsd-name-ref: native-kdu_ns
+    nsd-ref: native-kdu_ns
+    operational-events: []
+    operational-status: init
+    orchestration-progress: {}
+    resource-orchestrator: osmopenmano
+    short-name: native-kdu
+    ssh-authorized-key: null
+    vld:
+    - id: mgmtnet
+      name: null
+      status: ACTIVE
+      status-detailed: null
+      vim-id: 9b6a2ac4-767e-4ec9-9497-8ba63084c77f
+      vim-network-name: mgmt
+    vnfd-id:
+    - d96b1cdf-5ad6-49f7-bf65-907ada989293
 """
 
 ro_ns_text = """
@@ -1511,6 +1638,90 @@ db_vnfds_text = """
     short-name: multikdu_knf
     vendor: Telefonica
     version: '1.0'
+
+-   _admin:
+      created: 1575031727.5383403
+      modified: 1575031727.5383403
+      onboardingState: ONBOARDED
+      operationalState: ENABLED
+      projects_read:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+      projects_write:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+      storage:
+        descriptor: native-kdu_knf/native-kdu_vnfd.yaml
+        folder: d96b1cdf-5ad6-49f7-bf65-907ada989293
+        fs: local
+        path: /app/storage/
+        pkg-dir: native-kdu_knf
+        zipfile: native-kdu_knf.tar.gz
+      usageState: NOT_IN_USE
+      userDefinedData: {}
+    _id: d96b1cdf-5ad6-49f7-bf65-907ada989293
+    connection-point:
+    - name: mgmt
+    description: KNF with two KDU using juju-bundle
+    df:
+    - id: native-kdu
+      kdu-resource-profile:
+        - id: scale-app
+          kdu-name: native-kdu
+          min-number-of-instances: 1
+          resource-name: app
+        - id: scale-app2
+          kdu-name: native-kdu
+          min-number-of-instances: 1
+          max-number-of-instances: 10
+          resource-name: app2
+      scaling-aspect:
+      - id: kdu_scaling_group
+        name: kdu_scaling_group
+        max-scale-level: 10
+        aspect-delta-details:
+          deltas:
+          - id: native-kdu-delta
+            kdu-resource-delta:
+            - id: scale-app
+              number-of-instances: 1
+      - id: kdu_scaling_group_2
+        name: kdu_scaling_group_2
+        max-scale-level: 10
+        aspect-delta-details:
+          deltas:
+          - id: native-kdu-delta
+            kdu-resource-delta:
+            - id: scale-app
+              number-of-instances: 1
+            - id: scale-app2
+              number-of-instances: 2
+      lcm-operations-configuration:
+        operate-vnf-op-config:
+          day1-2:
+          - id: native-kdu
+            initial-config-primitive:
+            - name: changecontent
+              parameter:
+              - data-type: STRING
+                name: application-name
+                value: nginx
+              - data-type: STRING
+                name: customtitle
+                value: Initial Config Primitive
+              seq: '1'
+    id: native-kdu_knf
+    k8s-cluster:
+      nets:
+      - external-connection-point-ref: mgmt
+        id: mgmtnet
+    kdu:
+    - juju-bundle: stable/native-kdu
+      name: native-kdu
+    mgmt-interface:
+      cp: mgmt
+    name: native-kdu_knf
+    short-name: native-kdu_knf
+    vendor: Ulak Haberlesme A.S.
+    version: '1.0'
 """
 
 db_vnfrs_text = """
@@ -1717,6 +1928,42 @@ db_vnfrs_text = """
     vim-account-id: 74337dcb-ef54-41e7-bd2d-8c0d7fcd326f
     vnfd-id: 7ab0d10d-8ce2-4c68-aef6-cc5a437a9c62
     vnfd-ref: multikdu_knf
+
+-   _admin:
+      created: 1575034637.009597
+      modified: 1575034637.009597
+      nsState: NOT_INSTANTIATED
+      projects_read:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+      projects_write:
+      - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+    _id: 5ac34899-a23a-4b3c-918a-cd77acadbea6
+    additionalParamsForVnf: null
+    connection-point:
+    - connection-point-id: null
+      id: null
+      name: mgmt
+    created-time: 1575034636.9990137
+    id: 5ac34899-a23a-4b3c-918a-cd77acadbea6
+    ip-address: null
+    k8s-cluster:
+      nets:
+      - external-connection-point-ref: mgmt
+        id: mgmtnet
+        ns-vld-id: mgmtnet
+        vim_net: internal
+    kdur:
+    - ip-address: null
+      juju-bundle: app-bundle
+      k8s-cluster:
+        id: e7169dab-f71a-4f1f-b82b-432605e8c4b3
+      kdu-name: native-kdu
+    member-vnf-index-ref: native-kdu
+    nsr-id-ref: c54b14cb-69a8-45bc-b011-d6bea187dc0a
+    vdur: []
+    vim-account-id: 74337dcb-ef54-41e7-bd2d-8c0d7fcd326f
+    vnfd-id: d96b1cdf-5ad6-49f7-bf65-907ada989293
+    vnfd-ref: native-kdu_knf
 """
 
 db_nslcmops_scale_text = """
@@ -1792,4 +2039,14 @@ test_ids = {
         "instantiate": "cf3aa178-7640-4174-b921-2330e6f2aad6",
         "terminate": None,
     },
+    "TEST-NATIVE-KDU": {
+        "ns": "c54b14cb-69a8-45bc-b011-d6bea187dc0a",
+        "instantiate": "52770491-a765-40ce-97a1-c6e200bba7b3",
+        "terminate": None,
+    },
+    "TEST-NATIVE-KDU-2": {
+        "ns": "c54b14cb-69a8-45bc-b011-d6bea187dc0a",
+        "instantiate": "4013bbd2-b151-40ee-bcef-7e24ce5432f6",
+        "terminate": None,
+    },
 }
index 192f8e0..62de111 100644 (file)
@@ -394,6 +394,37 @@ class TestMyNS(asynctest.TestCase):
         self.assertEqual(return_value, expected_value)
         # print("scale_result: {}".format(self.db.get_one("nslcmops", {"_id": nslcmop_id}).get("detailed-status")))
 
+        # Test scale() for native kdu
+        # this also includes testing _scale_kdu()
+        nsr_id = descriptors.test_ids["TEST-NATIVE-KDU"]["ns"]
+        nslcmop_id = descriptors.test_ids["TEST-NATIVE-KDU"]["instantiate"]
+
+        self.my_ns.k8sclusterjuju.scale = asynctest.mock.CoroutineMock()
+        self.my_ns.k8sclusterjuju.exec_primitive = asynctest.mock.CoroutineMock()
+        self.my_ns.k8sclusterjuju.get_scale_count = asynctest.mock.CoroutineMock(
+            return_value=1
+        )
+        await self.my_ns.scale(nsr_id, nslcmop_id)
+        expected_value = "COMPLETED"
+        return_value = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get(
+            "operationState"
+        )
+        self.assertEqual(return_value, expected_value)
+        self.my_ns.k8sclusterjuju.scale.assert_called_once()
+
+        # Test scale() for native kdu with 2 resource
+        nsr_id = descriptors.test_ids["TEST-NATIVE-KDU-2"]["ns"]
+        nslcmop_id = descriptors.test_ids["TEST-NATIVE-KDU-2"]["instantiate"]
+
+        self.my_ns.k8sclusterjuju.get_scale_count.return_value = 2
+        await self.my_ns.scale(nsr_id, nslcmop_id)
+        expected_value = "COMPLETED"
+        return_value = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get(
+            "operationState"
+        )
+        self.assertEqual(return_value, expected_value)
+        self.my_ns.k8sclusterjuju.scale.assert_called()
+
     async def test_vca_status_refresh(self):
         nsr_id = descriptors.test_ids["TEST-A"]["ns"]
         nslcmop_id = descriptors.test_ids["TEST-A"]["instantiate"]
@@ -415,7 +446,10 @@ class TestMyNS(asynctest.TestCase):
                                 "operate-vnf-op-config"
                             ]["day1-2"]
                         ):
-                            if "juju" in v["execution-environment-list"][k]:
+                            if (
+                                v.get("execution-environment-list")
+                                and "juju" in v["execution-environment-list"][k]
+                            ):
                                 expected_value = self.db.get_list("nsrs")[i][
                                     "vcaStatus"
                                 ]