Bug 2317: Fix for Able to scale-in parent VM after a failed scale-out and NS update...
[osm/LCM.git] / osm_lcm / ns.py
index fad3972..71ede28 100644 (file)
@@ -97,7 +97,6 @@ from osm_lcm.data_utils.vnfr import (
 from osm_lcm.data_utils.dict_utils import parse_yaml_strings
 from osm_lcm.data_utils.database.vim_account import VimAccountDB
 from n2vc.definitions import RelationEndpoint
-from n2vc.k8s_helm_conn import K8sHelmConnector
 from n2vc.k8s_helm3_conn import K8sHelm3Connector
 from n2vc.k8s_juju_conn import K8sJujuConnector
 
@@ -172,15 +171,6 @@ class NsLcm(LcmBase):
             on_update_db=self._on_update_n2vc_db,
         )
 
-        self.k8sclusterhelm2 = K8sHelmConnector(
-            kubectl_command=self.vca_config.kubectlpath,
-            helm_command=self.vca_config.helmpath,
-            log=self.logger,
-            on_update_db=None,
-            fs=self.fs,
-            db=self.db,
-        )
-
         self.k8sclusterhelm3 = K8sHelm3Connector(
             kubectl_command=self.vca_config.kubectlpath,
             helm_command=self.vca_config.helm3path,
@@ -200,7 +190,6 @@ class NsLcm(LcmBase):
         )
 
         self.k8scluster_map = {
-            "helm-chart": self.k8sclusterhelm2,
             "helm-chart-v3": self.k8sclusterhelm3,
             "chart": self.k8sclusterhelm3,
             "juju-bundle": self.k8sclusterjuju,
@@ -1647,7 +1636,7 @@ class NsLcm(LcmBase):
 
             vca_id = self.get_vca_id(db_vnfr, db_nsr)
             # create or register execution environment in VCA
-            if vca_type in ("lxc_proxy_charm", "k8s_proxy_charm", "helm", "helm-v3"):
+            if vca_type in ("lxc_proxy_charm", "k8s_proxy_charm", "helm-v3"):
                 self._write_configuration_status(
                     nsr_id=nsr_id,
                     vca_index=vca_index,
@@ -1669,7 +1658,7 @@ class NsLcm(LcmBase):
                         db_dict=db_dict,
                         vca_id=vca_id,
                     )
-                elif vca_type == "helm" or vca_type == "helm-v3":
+                elif vca_type == "helm-v3":
                     ee_id, credentials = await self.vca_map[
                         vca_type
                     ].create_execution_environment(
@@ -1815,7 +1804,7 @@ class NsLcm(LcmBase):
 
             # if SSH access is required, then get execution environment SSH public
             # if native charm we have waited already to VM be UP
-            if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"):
+            if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm-v3"):
                 pub_key = None
                 user = None
                 # self.logger.debug("get ssh key block")
@@ -1957,7 +1946,7 @@ class NsLcm(LcmBase):
                 # TODO register in database that primitive is done
 
             # STEP 7 Configure metrics
-            if vca_type == "helm" or vca_type == "helm-v3":
+            if vca_type == "helm-v3":
                 # TODO: review for those cases where the helm chart is a reference and
                 # is not part of the NF package
                 prometheus_jobs = await self.extract_prometheus_scrape_jobs(
@@ -2965,7 +2954,15 @@ class NsLcm(LcmBase):
                 stage[1] = stage[2] = ""
             except asyncio.CancelledError:
                 error_list.append("Cancelled")
-                # TODO cancel all tasks
+                await self._cancel_pending_tasks(logging_text, tasks_dict_info)
+                await self._wait_for_tasks(
+                    logging_text,
+                    tasks_dict_info,
+                    timeout_ns_deploy,
+                    stage,
+                    nslcmop_id,
+                    nsr_id=nsr_id,
+                )
             except Exception as exc:
                 error_list.append(str(exc))
 
@@ -3651,7 +3648,6 @@ class NsLcm(LcmBase):
 
         k8scluster_id_2_uuic = {
             "helm-chart-v3": {},
-            "helm-chart": {},
             "juju-bundle": {},
         }
 
@@ -3750,11 +3746,6 @@ class NsLcm(LcmBase):
                         # Default version: helm3, if helm-version is v2 assign v2
                         k8sclustertype = "helm-chart-v3"
                         self.logger.debug("kdur: {}".format(kdur))
-                        if (
-                            kdur.get("helm-version")
-                            and kdur.get("helm-version") == "v2"
-                        ):
-                            k8sclustertype = "helm-chart"
                     elif kdur.get("juju-bundle"):
                         kdumodel = kdur["juju-bundle"]
                         k8sclustertype = "juju-bundle"
@@ -3975,10 +3966,7 @@ class NsLcm(LcmBase):
                     vca_type = "native_charm"
             elif ee_item.get("helm-chart"):
                 vca_name = ee_item["helm-chart"]
-                if ee_item.get("helm-version") and ee_item.get("helm-version") == "v2":
-                    vca_type = "helm"
-                else:
-                    vca_type = "helm-v3"
+                vca_type = "helm-v3"
             else:
                 self.logger.debug(
                     logging_text + "skipping non juju neither charm configuration"
@@ -4494,9 +4482,7 @@ class NsLcm(LcmBase):
                 ) and vca.get("needed_terminate")
                 # For helm we must destroy_ee. Also for native_charm, as juju_model cannot be deleted if there are
                 # pending native charms
-                destroy_ee = (
-                    True if vca_type in ("helm", "helm-v3", "native_charm") else False
-                )
+                destroy_ee = True if vca_type in ("helm-v3", "native_charm") else False
                 # self.logger.debug(logging_text + "vca_index: {}, ee_id: {}, vca_type: {} destroy_ee: {}".format(
                 #     vca_index, vca.get("ee_id"), vca_type, destroy_ee))
                 task = asyncio.ensure_future(
@@ -4636,7 +4622,14 @@ class NsLcm(LcmBase):
                 stage[1] = stage[2] = ""
             except asyncio.CancelledError:
                 error_list.append("Cancelled")
-                # TODO cancell all tasks
+                await self._cancel_pending_tasks(logging_text, tasks_dict_info)
+                await self._wait_for_tasks(
+                    logging_text,
+                    tasks_dict_info,
+                    timeout_ns_terminate,
+                    stage,
+                    nslcmop_id,
+                )
             except Exception as exc:
                 error_list.append(str(exc))
             # update status at database
@@ -4799,6 +4792,11 @@ class NsLcm(LcmBase):
             self._write_op_status(nslcmop_id, stage)
         return error_detail_list
 
+    async def _cancel_pending_tasks(self, logging_text, created_tasks_info):
+        for task, name in created_tasks_info.items():
+            self.logger.debug(logging_text + "Cancelling task: " + name)
+            task.cancel()
+
     @staticmethod
     def _map_primitive_params(primitive_desc, params, instantiation_params):
         """
@@ -5163,7 +5161,7 @@ class NsLcm(LcmBase):
                 kdu_action = (
                     True
                     if primitive_name in actions
-                    and kdu["k8scluster-type"] not in ("helm-chart", "helm-chart-v3")
+                    and kdu["k8scluster-type"] != "helm-chart-v3"
                     else False
                 )
 
@@ -5853,13 +5851,7 @@ class NsLcm(LcmBase):
                                     # add chart to list and all parameters
                                     step = "Getting helm chart name"
                                     chart_name = ee_item.get("helm-chart")
-                                    if (
-                                        ee_item.get("helm-version")
-                                        and ee_item.get("helm-version") == "v2"
-                                    ):
-                                        vca_type = "helm"
-                                    else:
-                                        vca_type = "helm-v3"
+                                    vca_type = "helm-v3"
                                     step = "Setting Helm chart artifact paths"
 
                                     helm_artifacts.append(
@@ -5911,7 +5903,25 @@ class NsLcm(LcmBase):
                         if result == "FAILED":
                             nslcmop_operation_state = result
                             error_description_nslcmop = detailed_status
+                            old_operational_status = "failed"
                         db_nslcmop_update["detailed-status"] = detailed_status
+                        db_nsr_update["detailed-status"] = detailed_status
+                        scaling_aspect = get_scaling_aspect(latest_vnfd)
+                        scaling_group_desc = db_nsr.get("_admin").get(
+                            "scaling-group", None
+                        )
+                        if scaling_group_desc:
+                            for aspect in scaling_aspect:
+                                scaling_group_id = aspect.get("id")
+                                for scale_index, scaling_group in enumerate(
+                                    scaling_group_desc
+                                ):
+                                    if scaling_group.get("name") == scaling_group_id:
+                                        db_nsr_update[
+                                            "_admin.scaling-group.{}.nb-scale-op".format(
+                                                scale_index
+                                            )
+                                        ] = 0
                         self.logger.debug(
                             logging_text
                             + " step {} Done with result {} {}".format(
@@ -6426,11 +6436,6 @@ class NsLcm(LcmBase):
                         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"
                         elif kdur.get("juju-bundle"):
                             k8s_cluster_type = "juju-bundle"
                         else:
@@ -6559,11 +6564,6 @@ class NsLcm(LcmBase):
                         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"
                         elif kdur.get("juju-bundle"):
                             k8s_cluster_type = "juju-bundle"
                         else:
@@ -7179,16 +7179,31 @@ class NsLcm(LcmBase):
                 exc_info=True,
             )
         finally:
+            error_list = list()
+            if exc:
+                error_list.append(str(exc))
             self._write_ns_status(
                 nsr_id=nsr_id,
                 ns_state=None,
                 current_operation="IDLE",
                 current_operation_id=None,
             )
-            if tasks_dict_info:
-                stage[1] = "Waiting for instantiate pending tasks."
-                self.logger.debug(logging_text + stage[1])
-                exc = await self._wait_for_tasks(
+            try:
+                if tasks_dict_info:
+                    stage[1] = "Waiting for instantiate pending tasks."
+                    self.logger.debug(logging_text + stage[1])
+                    exc = await self._wait_for_tasks(
+                        logging_text,
+                        tasks_dict_info,
+                        self.timeout.ns_deploy,
+                        stage,
+                        nslcmop_id,
+                        nsr_id=nsr_id,
+                    )
+            except asyncio.CancelledError:
+                error_list.append("Cancelled")
+                await self._cancel_pending_tasks(logging_text, tasks_dict_info)
+                await self._wait_for_tasks(
                     logging_text,
                     tasks_dict_info,
                     self.timeout.ns_deploy,
@@ -7196,10 +7211,13 @@ class NsLcm(LcmBase):
                     nslcmop_id,
                     nsr_id=nsr_id,
                 )
-            if exc:
+            if error_list:
+                error_detail = "; ".join(error_list)
                 db_nslcmop_update[
                     "detailed-status"
-                ] = error_description_nslcmop = "FAILED {}: {}".format(step, exc)
+                ] = error_description_nslcmop = "FAILED {}: {}".format(
+                    step, error_detail
+                )
                 nslcmop_operation_state = "FAILED"
                 if db_nsr:
                     db_nsr_update["operational-status"] = old_operational_status
@@ -7213,7 +7231,7 @@ class NsLcm(LcmBase):
                         db_nsr_update[
                             "detailed-status"
                         ] = "FAILED scaling nslcmop={} {}: {}".format(
-                            nslcmop_id, step, exc
+                            nslcmop_id, step, error_detail
                         )
             else:
                 error_description_nslcmop = None
@@ -7962,10 +7980,25 @@ class NsLcm(LcmBase):
                 exc_info=True,
             )
         finally:
-            if tasks_dict_info:
-                stage[1] = "Waiting for healing pending tasks."
-                self.logger.debug(logging_text + stage[1])
-                exc = await self._wait_for_tasks(
+            error_list = list()
+            if exc:
+                error_list.append(str(exc))
+            try:
+                if tasks_dict_info:
+                    stage[1] = "Waiting for healing pending tasks."
+                    self.logger.debug(logging_text + stage[1])
+                    exc = await self._wait_for_tasks(
+                        logging_text,
+                        tasks_dict_info,
+                        self.timeout.ns_deploy,
+                        stage,
+                        nslcmop_id,
+                        nsr_id=nsr_id,
+                    )
+            except asyncio.CancelledError:
+                error_list.append("Cancelled")
+                await self._cancel_pending_tasks(logging_text, tasks_dict_info)
+                await self._wait_for_tasks(
                     logging_text,
                     tasks_dict_info,
                     self.timeout.ns_deploy,
@@ -7973,17 +8006,22 @@ class NsLcm(LcmBase):
                     nslcmop_id,
                     nsr_id=nsr_id,
                 )
-            if exc:
+            if error_list:
+                error_detail = "; ".join(error_list)
                 db_nslcmop_update[
                     "detailed-status"
-                ] = error_description_nslcmop = "FAILED {}: {}".format(step, exc)
+                ] = error_description_nslcmop = "FAILED {}: {}".format(
+                    step, error_detail
+                )
                 nslcmop_operation_state = "FAILED"
                 if db_nsr:
                     db_nsr_update["operational-status"] = old_operational_status
                     db_nsr_update["config-status"] = old_config_status
                     db_nsr_update[
                         "detailed-status"
-                    ] = "FAILED healing nslcmop={} {}: {}".format(nslcmop_id, step, exc)
+                    ] = "FAILED healing nslcmop={} {}: {}".format(
+                        nslcmop_id, step, error_detail
+                    )
                     for task, task_name in tasks_dict_info.items():
                         if not task.done() or task.cancelled() or task.exception():
                             if task_name.startswith(self.task_name_deploy_vca):
@@ -8183,10 +8221,7 @@ class NsLcm(LcmBase):
                     vca_type = "native_charm"
             elif ee_item.get("helm-chart"):
                 vca_name = ee_item["helm-chart"]
-                if ee_item.get("helm-version") and ee_item.get("helm-version") == "v2":
-                    vca_type = "helm"
-                else:
-                    vca_type = "helm-v3"
+                vca_type = "helm-v3"
             else:
                 self.logger.debug(
                     logging_text + "skipping non juju neither charm configuration"
@@ -8518,7 +8553,7 @@ class NsLcm(LcmBase):
 
             # if SSH access is required, then get execution environment SSH public
             # if native charm we have waited already to VM be UP
-            if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"):
+            if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm-v3"):
                 pub_key = None
                 user = None
                 # self.logger.debug("get ssh key block")
@@ -8726,6 +8761,10 @@ class NsLcm(LcmBase):
         db_nslcmop = None
         db_nslcmop_update = {}
         nslcmop_operation_state = None
+        old_db_update = {}
+        q_filter = {}
+        old_vdu_index = None
+        old_flavor_id = None
         db_nsr_update = {}
         target = {}
         exc = None
@@ -8749,6 +8788,59 @@ class NsLcm(LcmBase):
             )
             db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
             operationParams = db_nslcmop.get("operationParams")
+            # Update the VNFRS and NSRS with the requested flavour detail, So that ro tasks can function properly
+            db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
+            db_flavor = db_nsr.get("flavor")
+            db_flavor_index = str(len(db_flavor))
+            change_vnf_flavor_data = operationParams["changeVnfFlavorData"]
+            flavor_dict = change_vnf_flavor_data["additionalParams"]
+            count_index = flavor_dict["vduCountIndex"]
+            vdu_id_ref = flavor_dict["vduid"]
+            flavor_dict_update = {
+                "id": db_flavor_index,
+                "memory-mb": flavor_dict["virtualMemory"],
+                "name": f"{vdu_id_ref}-{count_index}-flv",
+                "storage-gb": flavor_dict["sizeOfStorage"],
+                "vcpu-count": flavor_dict["numVirtualCpu"],
+            }
+            db_flavor.append(flavor_dict_update)
+            db_update = {}
+            db_update["flavor"] = db_flavor
+            ns_q_filter = {
+                "_id": nsr_id,
+            }
+            self.db.set_one(
+                "nsrs",
+                q_filter=ns_q_filter,
+                update_dict=db_update,
+                fail_on_empty=True,
+            )
+            db_vnfr = self.db.get_one(
+                "vnfrs", {"_id": change_vnf_flavor_data["vnfInstanceId"]}
+            )
+            for vdu_index, vdur in enumerate(db_vnfr.get("vdur", ())):
+                if (
+                    vdur.get("count-index") == count_index
+                    and vdur.get("vdu-id-ref") == vdu_id_ref
+                ):
+                    old_flavor_id = vdur.get("ns-flavor-id", 0)
+                    old_vdu_index = vdu_index
+                    filter_text = {
+                        "_id": change_vnf_flavor_data["vnfInstanceId"],
+                        "vdur.count-index": count_index,
+                        "vdur.vdu-id-ref": vdu_id_ref,
+                    }
+                    q_filter.update(filter_text)
+                    db_update = {}
+                    db_update[
+                        "vdur.{}.ns-flavor-id".format(vdu_index)
+                    ] = db_flavor_index
+                    self.db.set_one(
+                        "vnfrs",
+                        q_filter=q_filter,
+                        update_dict=db_update,
+                        fail_on_empty=True,
+                    )
             target = {}
             target.update(operationParams)
             desc = await self.RO.vertical_scale(nsr_id, target)
@@ -8762,7 +8854,12 @@ class NsLcm(LcmBase):
                 self.timeout.verticalscale,
                 operation="verticalscale",
             )
-        except (ROclient.ROClientException, DbException, LcmException) as e:
+        except (
+            NgRoException,
+            ROclient.ROClientException,
+            DbException,
+            LcmException,
+        ) as e:
             self.logger.error("Exit Exception {}".format(e))
             exc = e
         except asyncio.CancelledError:
@@ -8783,6 +8880,9 @@ class NsLcm(LcmBase):
             if exc:
                 db_nslcmop_update["detailed-status"] = "FAILED {}: {}".format(step, exc)
                 nslcmop_operation_state = "FAILED"
+                old_db_update[
+                    "vdur.{}.ns-flavor-id".format(old_vdu_index)
+                ] = old_flavor_id
             else:
                 nslcmop_operation_state = "COMPLETED"
                 db_nslcmop_update["detailed-status"] = "Done"
@@ -8795,6 +8895,16 @@ class NsLcm(LcmBase):
                 operation_state=nslcmop_operation_state,
                 other_update=db_nslcmop_update,
             )
+            if old_vdu_index and old_db_update != {}:
+                self.logger.critical(
+                    "Reverting Old Flavor -- : {}".format(old_db_update)
+                )
+                self.db.set_one(
+                    "vnfrs",
+                    q_filter=q_filter,
+                    update_dict=old_db_update,
+                    fail_on_empty=True,
+                )
             if nslcmop_operation_state:
                 try:
                     msg = {