Added vld:cp ip-address instantiation parameter
[osm/LCM.git] / osm_lcm / lcm.py
index 3266914..bd83c12 100644 (file)
@@ -11,12 +11,9 @@ import sys
 import traceback
 import ROclient
 # from osm_lcm import version as lcm_version, version_date as lcm_version_date, ROclient
-from osm_common import dbmemory
-from osm_common import dbmongo
-from osm_common import fslocal
-from osm_common import msglocal
-from osm_common import msgkafka
-from osm_common.dbbase import DbException
+from osm_common import dbmemory, dbmongo, fslocal, msglocal, msgkafka
+from osm_common import version as common_version
+from osm_common.dbbase import DbException, deep_update
 from osm_common.fsbase import FsException
 from osm_common.msgbase import MsgException
 from os import environ, path
@@ -31,6 +28,9 @@ from time import time
 
 __author__ = "Alfonso Tierno"
 min_RO_version = [0, 5, 72]
+# uncomment if LCM is installed as library and installed, and get them from __init__.py
+lcm_version = '0.1.14'
+lcm_version_date = '2018-09-11'
 
 
 class LcmException(Exception):
@@ -124,9 +124,10 @@ class TaskRegistry:
             for task_name, task in self.task_registry[topic][_id][op_id].items():
                 if target_task_name and target_task_name != task_name:
                     continue
-                result = task.cancel()
-                if result:
-                    self.logger.debug("{} _id={} order_id={} task={} cancelled".format(topic, _id, op_id, task_name))
+                # result =
+                task.cancel()
+                # if result:
+                #     self.logger.debug("{} _id={} order_id={} task={} cancelled".format(topic, _id, op_id, task_name))
 
 
 class Lcm:
@@ -166,12 +167,12 @@ class Lcm:
         config["database"]["logger_name"] = "lcm.db"
         config["storage"]["logger_name"] = "lcm.fs"
         config["message"]["logger_name"] = "lcm.msg"
-        if "logfile" in config["global"]:
+        if config["global"].get("logfile"):
             file_handler = logging.handlers.RotatingFileHandler(config["global"]["logfile"],
                                                                 maxBytes=100e6, backupCount=9, delay=0)
             file_handler.setFormatter(log_formatter_simple)
             self.logger.addHandler(file_handler)
-        else:
+        if not config["global"].get("nologging"):
             str_handler = logging.StreamHandler()
             str_handler.setFormatter(log_formatter_simple)
             self.logger.addHandler(str_handler)
@@ -183,14 +184,14 @@ class Lcm:
         for k1, logname in {"message": "lcm.msg", "database": "lcm.db", "storage": "lcm.fs"}.items():
             config[k1]["logger_name"] = logname
             logger_module = logging.getLogger(logname)
-            if "logfile" in config[k1]:
+            if config[k1].get("logfile"):
                 file_handler = logging.handlers.RotatingFileHandler(config[k1]["logfile"],
                                                                     maxBytes=100e6, backupCount=9, delay=0)
                 file_handler.setFormatter(log_formatter_simple)
                 logger_module.addHandler(file_handler)
-            if "loglevel" in config[k1]:
+            if config[k1].get("loglevel"):
                 logger_module.setLevel(config[k1]["loglevel"])
-        self.logger.critical("starting osm/lcm version {} {}".format(lcm_version, lcm_version_date))
+        self.logger.critical("starting osm/lcm version {} {}".format(lcm_version, lcm_version_date))
         self.n2vc = N2VC(
             log=self.logger,
             server=config['VCA']['host'],
@@ -208,6 +209,9 @@ class Lcm:
         # or with list(map(int, version.split(".")))
         if N2VC_version < "0.0.2":
             raise LcmException("Not compatible osm/N2VC version '{}'. Needed '0.0.2' or higher".format(N2VC_version))
+        if common_version < "0.1.7":
+            raise LcmException("Not compatible osm/common version '{}'. Needed '0.1.7' or higher".format(
+                common_version))
 
         try:
             # TODO check database version
@@ -813,7 +817,12 @@ class Lcm:
         def ip_profile_2_RO(ip_profile):
             RO_ip_profile = deepcopy((ip_profile))
             if "dns-server" in RO_ip_profile:
-                RO_ip_profile["dns-address"] = RO_ip_profile.pop("dns-server")
+                if isinstance(RO_ip_profile["dns-server"], list):
+                    RO_ip_profile["dns-address"] = []
+                    for ds in RO_ip_profile.pop("dns-server"):
+                        RO_ip_profile["dns-address"].append(ds['address'])
+                else:
+                    RO_ip_profile["dns-address"] = RO_ip_profile.pop("dns-server")
             if RO_ip_profile.get("ip-version") == "ipv4":
                 RO_ip_profile["ip-version"] = "IPv4"
             if RO_ip_profile.get("ip-version") == "ipv6":
@@ -883,15 +892,15 @@ class Lcm:
                                 for vdu_descriptor in vnf_descriptor["vdu"]:
                                     for vdu_interface in vdu_descriptor["interface"]:
                                         if vdu_interface.get("internal-connection-point-ref") == icp_params["id-ref"]:
-                                            if vdu_descriptor["id"] not in RO_vnf["vdus"]:
-                                                RO_vnf["vdus"][vdu_descriptor["id"]] = {}
-                                            if "interfaces" not in RO_vnf["vdus"][vdu_descriptor["id"]]:
-                                                RO_vnf["vdus"][vdu_descriptor["id"]]["interfaces"] = {}
-                                            RO_ifaces = RO_vnf["vdus"][vdu_descriptor["id"]]["interfaces"]
-                                            if vdu_interface["name"] not in RO_ifaces:
-                                                RO_ifaces[vdu_interface["name"]] = {}
-
-                                            RO_ifaces[vdu_interface["name"]]["ip_address"] = icp_params["ip-address"]
+                                            RO_interface_update = {}
+                                            if icp_params.get("ip-address"):
+                                                RO_interface_update["ip_address"] = icp_params["ip-address"]
+                                            if icp_params.get("mac-address"):
+                                                RO_interface_update["mac_address"] = icp_params["mac-address"]
+                                            if RO_interface_update:
+                                                RO_vnf_update = {"vdus": {vdu_descriptor["id"]: {
+                                                    "interfaces": {vdu_interface["name"]: RO_interface_update}}}}
+                                                deep_update(RO_vnf, RO_vnf_update)
                                             iface_found = True
                                             break
                                     if iface_found:
@@ -923,6 +932,51 @@ class Lcm:
                             })
                     else:  # isinstance str
                         RO_vld["sites"].append({"netmap-use": vld_params["vim-network-name"]})
+                if "vnfd-connection-point-ref" in vld_params:
+                    for cp_params in vld_params["vnfd-connection-point-ref"]:
+                        # look for interface
+                        for constituent_vnfd in nsd["constituent-vnfd"]:
+                            if constituent_vnfd["member-vnf-index"] == cp_params["member-vnf-index-ref"]:
+                                vnf_descriptor = vnfd_dict[constituent_vnfd["vnfd-id-ref"]]
+                                break
+                        else:
+                            raise LcmException(
+                                "Invalid instantiate parameter vld:vnfd-connection-point-ref:member-vnf-index-ref={} "
+                                "is not present at nsd:constituent-vnfd".format(cp_params["member-vnf-index-ref"]))
+                        match_cp = False
+                        for vdu_descriptor in vnf_descriptor["vdu"]:
+                            for interface_descriptor in vdu_descriptor["interface"]:
+                                if interface_descriptor.get("external-connection-point-ref") == \
+                                        cp_params["vnfd-connection-point-ref"]:
+                                    match_cp = True
+                                    break
+                            if match_cp:
+                                break
+                        else:
+                            raise LcmException(
+                                "Invalid instantiate parameter vld:vnfd-connection-point-ref:member-vnf-index-ref={}:"
+                                "vnfd-connection-point-ref={} is not present at vnfd={}".format(
+                                    cp_params["member-vnf-index-ref"],
+                                    cp_params["vnfd-connection-point-ref"],
+                                    vnf_descriptor["id"]))
+                        RO_cp_params = {}
+                        if cp_params.get("ip-address"):
+                            RO_cp_params["ip_address"] = cp_params["ip-address"]
+                        if cp_params.get("mac-address"):
+                            RO_cp_params["mac_address"] = cp_params["mac-address"]
+                        if RO_cp_params:
+                            RO_vnf_params = {
+                                cp_params["member-vnf-index-ref"]: {
+                                    "vdus": {
+                                        vdu_descriptor["id"]: {
+                                            "interfaces": {
+                                                interface_descriptor["name"]: RO_cp_params
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            deep_update(RO_ns_params["vnfs"], RO_vnf_params)
                 if RO_vld:
                     RO_ns_params["networks"][vld_params["name"]] = RO_vld
         return RO_ns_params
@@ -1435,28 +1489,61 @@ class Lcm:
             # remove from RO
             RO_fail = False
             RO = ROclient.ROClient(self.loop, **self.ro_config)
+
             # Delete ns
-            if nsr_lcm and nsr_lcm.get("RO") and nsr_lcm["RO"].get("nsr_id"):
-                RO_nsr_id = nsr_lcm["RO"]["nsr_id"]
-                try:
+            RO_nsr_id = RO_delete_action = None
+            if nsr_lcm and nsr_lcm.get("RO"):
+                RO_nsr_id = nsr_lcm["RO"].get("nsr_id")
+                RO_delete_action = nsr_lcm["RO"].get("nsr_delete_action_id")
+            try:
+                if RO_nsr_id:
                     step = db_nsr_update["detailed-status"] = db_nslcmop_update["detailed-status"] = "Deleting ns at RO"
                     self.logger.debug(logging_text + step)
-                    await RO.delete("ns", RO_nsr_id)
+                    desc = await RO.delete("ns", RO_nsr_id)
+                    RO_delete_action = desc["action_id"]
+                    db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = RO_delete_action
                     db_nsr_update["_admin.deployed.RO.nsr_id"] = None
                     db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
-                except ROclient.ROClientException as e:
-                    if e.http_code == 404:  # not found
-                        db_nsr_update["_admin.deployed.RO.nsr_id"] = None
-                        db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
-                        self.logger.debug(logging_text + "RO_ns_id={} already deleted".format(RO_nsr_id))
-                    elif e.http_code == 409:   # conflict
-                        failed_detail.append("RO_ns_id={} delete conflict: {}".format(RO_nsr_id, e))
-                        self.logger.debug(logging_text + failed_detail[-1])
-                        RO_fail = True
-                    else:
-                        failed_detail.append("RO_ns_id={} delete error: {}".format(RO_nsr_id, e))
-                        self.logger.error(logging_text + failed_detail[-1])
-                        RO_fail = True
+                if RO_delete_action:
+                    # wait until NS is deleted from VIM
+                    step = detailed_status = "Waiting ns deleted from VIM. RO_id={}".format(RO_nsr_id)
+                    detailed_status_old = None
+                    self.logger.debug(logging_text + step)
+
+                    delete_timeout = 20 * 60   # 20 minutes
+                    while delete_timeout > 0:
+                        desc = await RO.show("ns", item_id_name=RO_nsr_id, extra_item="action",
+                                             extra_item_id=RO_delete_action)
+                        ns_status, ns_status_info = RO.check_action_status(desc)
+                        if ns_status == "ERROR":
+                            raise ROclient.ROClientException(ns_status_info)
+                        elif ns_status == "BUILD":
+                            detailed_status = step + "; {}".format(ns_status_info)
+                        elif ns_status == "ACTIVE":
+                            break
+                        else:
+                            assert False, "ROclient.check_action_status returns unknown {}".format(ns_status)
+                        await asyncio.sleep(5, loop=self.loop)
+                        delete_timeout -= 5
+                        if detailed_status != detailed_status_old:
+                            detailed_status_old = db_nslcmop_update["detailed-status"] = detailed_status
+                            self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
+                    else:  # delete_timeout <= 0:
+                        raise ROclient.ROClientException("Timeout waiting ns deleted from VIM")
+
+            except ROclient.ROClientException as e:
+                if e.http_code == 404:  # not found
+                    db_nsr_update["_admin.deployed.RO.nsr_id"] = None
+                    db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
+                    self.logger.debug(logging_text + "RO_ns_id={} already deleted".format(RO_nsr_id))
+                elif e.http_code == 409:   # conflict
+                    failed_detail.append("RO_ns_id={} delete conflict: {}".format(RO_nsr_id, e))
+                    self.logger.debug(logging_text + failed_detail[-1])
+                    RO_fail = True
+                else:
+                    failed_detail.append("RO_ns_id={} delete error: {}".format(RO_nsr_id, e))
+                    self.logger.error(logging_text + failed_detail[-1])
+                    RO_fail = True
 
             # Delete nsd
             if not RO_fail and nsr_lcm and nsr_lcm.get("RO") and nsr_lcm["RO"].get("nsd_id"):