WIP - Fix N2VC integration issues
[osm/RO.git] / lcm / osm_lcm / lcm.py
index 3b4ab60..ceabda5 100644 (file)
@@ -15,7 +15,11 @@ from dbbase import DbException
 from fsbase import FsException
 from msgbase import MsgException
 from os import environ
-from vca import DeployApplication, RemoveApplication
+# from vca import DeployApplication, RemoveApplication
+from n2vc.vnf import N2VC
+import os.path
+import time
+
 from copy import deepcopy
 from http import HTTPStatus
 
@@ -44,6 +48,7 @@ class Lcm:
             "logger_name": "lcm.ROclient",
             "loglevel": "ERROR",
         }
+
         self.vca = config["VCA"]  # TODO VCA
         self.loop = None
 
@@ -78,6 +83,19 @@ class Lcm:
             if "loglevel" in config[k1]:
                 logger_module.setLevel(config[k1]["loglevel"])
 
+        self.n2vc = N2VC(
+            log=self.logger,
+            server=config['VCA']['host'],
+            port=config['VCA']['port'],
+            user=config['VCA']['user'],
+            secret=config['VCA']['secret'],
+            # TODO: This should point to the base folder where charms are stored,
+            # if there is a common one (like object storage). Otherwise, leave
+            # it unset and pass it via DeployCharms
+            # artifacts=config['VCA'][''],
+            artifacts=None,
+        )
+
         try:
             if config["database"]["driver"] == "mongo":
                 self.db = dbmongo.DbMongo()
@@ -115,6 +133,74 @@ class Lcm:
         except DbException as e:
             self.logger.error("Updating nsr_id={}: {}".format(nsr_id, e))
 
+    def n2vc_callback(self, model_name, application_name, workload_status, db_nsr, vnf_member_index, task=None):
+        """Update the lcm database with the status of the charm.
+
+        Updates the VNF's operational status with the state of the charm:
+        - blocked: The unit needs manual intervention
+        - maintenance: The unit is actively deploying/configuring
+        - waiting: The unit is waiting for another charm to be ready
+        - active: The unit is deployed, configured, and ready
+        - error: The charm has failed and needs attention.
+        - terminated: The charm has been destroyed
+
+        Updates the network service's config-status to reflect the state of all
+        charms.
+        """
+
+        if not workload_status and not task:
+            self.logger.error("Task create_ns={} n2vc_callback Enter with bad parameters")
+            return
+
+        if not workload_status:
+            self.logger.error("Task create_ns={} n2vc_callback Enter with bad parameters, no workload_status")
+            return
+
+        try:
+            self.logger.debug("[n2vc_callback] Workload status \"{}\"".format(workload_status))
+            nsr_id = db_nsr["_id"]
+            nsr_lcm = db_nsr["_admin"]["deploy"]
+            nsr_lcm["VCA"][vnf_member_index]['operational-status'] = workload_status
+
+            if task:
+                if task.cancelled():
+                    return
+
+                if task.done():
+                    exc = task.exception()
+                    if exc:
+                        nsr_lcm = db_nsr["_admin"]["deploy"]
+                        nsr_lcm["VCA"][vnf_member_index]['operational-status'] = "failed"
+                        db_nsr["detailed-status"] = "fail configuring vnf_index={} {}".format(vnf_member_index, exc)
+                        db_nsr["config-status"] = "failed"
+                        self.update_nsr_db(nsr_id, db_nsr)
+            else:
+                vca_status = nsr_lcm["VCA"][vnf_member_index]['operational-status']
+
+                units = len(nsr_lcm["VCA"])
+                active = 0
+                statusmap = {}
+                for vnf_index in nsr_lcm["VCA"]:
+                    if vca_status not in statusmap:
+                        # Initialize it
+                        statusmap[vca_status] = 0
+
+                    statusmap[vca_status] += 1
+
+                    if vca_status == "active":
+                        active += 1
+
+                cs = ""
+                for status in statusmap:
+                    cs += "{} ({}) ".format(status, statusmap[status])
+                db_nsr["config-status"] = cs
+                self.update_nsr_db(nsr_id, db_nsr)
+
+        except Exception as e:
+            # self.logger.critical("Task create_ns={} n2vc_callback Exception {}".format(nsr_id, e), exc_info=True)
+            self.logger.critical("Task create_ns n2vc_callback Exception {}".format(e), exc_info=True)
+        pass
+
     def vca_deploy_callback(self, db_nsr, vnf_index, status, task):
         # TODO study using this callback when VCA.DeployApplication success from VCAMonitor
         # By the moment this callback is used only to capture exception conditions from VCA DeployApplication
@@ -280,7 +366,6 @@ class Lcm:
                 vnf_index = str(c_vnf["member-vnf-index"])
                 vnfd = needed_vnfd[vnfd_id]
                 if vnfd.get("vnf-configuration") and vnfd["vnf-configuration"].get("juju"):
-                    nsr_lcm["VCA"][vnf_index] = {}
                     vnfd_to_config += 1
                     proxy_charm = vnfd["vnf-configuration"]["juju"]["charm"]
 
@@ -293,17 +378,45 @@ class Lcm:
                         base_folder["file"],
                         proxy_charm
                     )
+
+                    # Setup the runtime parameters for this VNF
+                    params = {
+                        'rw_mgmt_ip': nsr_lcm['nsr_ip'][vnf_index],
+                    }
+
+                    # ns_name will be ignored in the current version of N2VC
+                    # but will be implemented for the next point release.
+                    ns_name = 'default'
+                    application_name = self.n2vc.FormatApplicationName(
+                        'default',
+                        vnfd['name'],
+                        vnf_index,
+                    )
+
+                    nsr_lcm["VCA"][vnf_index] = {
+                        "model": ns_name,
+                        "application": application_name,
+                        "operational-status": "init",
+                        "vnfd_id": vnfd_id,
+                    }
+
+                    self.logger.debug("Passing artifacts path '{}' for {}".format(charm_path, proxy_charm))
                     task = asyncio.ensure_future(
-                        DeployApplication(
-                            self.config['VCA'],
-                            self.db,
-                            db_nsr,
-                            vnfd,
-                            vnf_index,
-                            charm_path,
+                        self.n2vc.DeployCharms(
+                            ns_name,             # The network service name
+                            application_name,    # The application name
+                            vnfd,                # The vnf descriptor
+                            charm_path,          # Path to charm
+                            params,              # Runtime params, like mgmt ip
+                            {},                  # for native charms only
+                            self.n2vc_callback,  # Callback for status changes
+                            db_nsr,              # Callback parameter
+                            vnf_index,           # Callback parameter
+                            None,                # Callback parameter (task)
                         )
                     )
-                    task.add_done_callback(functools.partial(self.vca_deploy_callback, db_nsr, vnf_index, None))
+                    task.add_done_callback(functools.partial(self.n2vc_callback, None, None, None, None, db_nsr))
+
                     self.lcm_tasks[nsr_id][order_id]["create_charm:" + vnf_index] = task
             db_nsr["config-status"] = "configuring" if vnfd_to_config else "configured"
             db_nsr["detailed-status"] = "Configuring 1/{}".format(vnfd_to_config) if vnfd_to_config else "done"
@@ -345,15 +458,20 @@ class Lcm:
                 step = db_nsr["detailed-status"] = "Deleting charms"
                 self.logger.debug(logging_text + step)
                 for vnf_index, deploy_info in nsr_lcm["VCA"].items():
-                    if deploy_info and deploy_info.get("appliation"):
+                    if deploy_info and deploy_info.get("application"):
+                        # n2vc_callback(self, model_name, application_name, workload_status, db_nsr, vnf_member_index, task=None):
+
+                        # self.n2vc.RemoveCharms(model_name, application_name, self.n2vc_callback, model_name, application_name)
                         task = asyncio.ensure_future(
-                            RemoveApplication(
-                                self.config['VCA'],
-                                self.db,
+                            self.n2vc.RemoveCharms(
+                                deploy_info['model'],
+                                deploy_info['application'],
+                                self.n2vc_callback,
                                 db_nsr,
                                 vnf_index,
                             )
                         )
+
                         self.lcm_tasks[nsr_id][order_id]["delete_charm:" + vnf_index] = task
             except Exception as e:
                 self.logger.debug(logging_text + "Failed while deleting charms: {}".format(e))