Fix Bug 2159: Use provider-network profile when specified even if it's overlay or...
[osm/RO.git] / NG-RO / osm_ng_ro / ns.py
index 096d4df..19ff791 100644 (file)
@@ -23,7 +23,7 @@ from random import choice as random_choice
 from threading import Lock
 from time import time
 from traceback import format_exc as traceback_format_exc
-from typing import Any, Dict, Tuple, Type
+from typing import Any, Dict, List, Optional, Tuple, Type
 from uuid import uuid4
 
 from cryptography.hazmat.backends import default_backend as crypto_default_backend
@@ -31,6 +31,7 @@ from cryptography.hazmat.primitives import serialization as crypto_serialization
 from cryptography.hazmat.primitives.asymmetric import rsa
 from jinja2 import (
     Environment,
+    select_autoescape,
     StrictUndefined,
     TemplateError,
     TemplateNotFound,
@@ -77,8 +78,8 @@ def get_process_id():
 
             if text_id:
                 return text_id
-    except Exception:
-        pass
+    except Exception as error:
+        logging.exception(f"{error} occured while getting process id")
 
     # Return a random id
     return "".join(random_choice("0123456789abcdef") for _ in range(12))
@@ -380,7 +381,10 @@ class Ns(object):
             str: [description]
         """
         try:
-            env = Environment(undefined=StrictUndefined)
+            env = Environment(
+                undefined=StrictUndefined,
+                autoescape=select_autoescape(default_for_string=True, default=True),
+            )
             template = env.from_string(cloud_init_content)
 
             return template.render(params or {})
@@ -643,44 +647,57 @@ class Ns(object):
             Tuple[Dict[str, Any], bool]: [description]
         """
         numa = {}
+        numa_list = []
         epa_vcpu_set = False
 
         if guest_epa_quota.get("numa-node-policy"):
             numa_node_policy = guest_epa_quota.get("numa-node-policy")
 
             if numa_node_policy.get("node"):
-                numa_node = numa_node_policy["node"][0]
-
-                if numa_node.get("num-cores"):
-                    numa["cores"] = numa_node["num-cores"]
-                    epa_vcpu_set = True
-
-                paired_threads = numa_node.get("paired-threads", {})
-                if paired_threads.get("num-paired-threads"):
-                    numa["paired-threads"] = int(
-                        numa_node["paired-threads"]["num-paired-threads"]
-                    )
-                    epa_vcpu_set = True
+                for numa_node in numa_node_policy["node"]:
+                    vcpu_list = []
+                    if numa_node.get("id"):
+                        numa["id"] = int(numa_node["id"])
+
+                    if numa_node.get("vcpu"):
+                        for vcpu in numa_node.get("vcpu"):
+                            vcpu_id = int(vcpu.get("id"))
+                            vcpu_list.append(vcpu_id)
+                        numa["vcpu"] = vcpu_list
+
+                    if numa_node.get("num-cores"):
+                        numa["cores"] = numa_node["num-cores"]
+                        epa_vcpu_set = True
+
+                    paired_threads = numa_node.get("paired-threads", {})
+                    if paired_threads.get("num-paired-threads"):
+                        numa["paired_threads"] = int(
+                            numa_node["paired-threads"]["num-paired-threads"]
+                        )
+                        epa_vcpu_set = True
 
-                if paired_threads.get("paired-thread-ids"):
-                    numa["paired-threads-id"] = []
+                    if paired_threads.get("paired-thread-ids"):
+                        numa["paired-threads-id"] = []
 
-                    for pair in paired_threads["paired-thread-ids"]:
-                        numa["paired-threads-id"].append(
-                            (
-                                str(pair["thread-a"]),
-                                str(pair["thread-b"]),
+                        for pair in paired_threads["paired-thread-ids"]:
+                            numa["paired-threads-id"].append(
+                                (
+                                    str(pair["thread-a"]),
+                                    str(pair["thread-b"]),
+                                )
                             )
-                        )
 
-                if numa_node.get("num-threads"):
-                    numa["threads"] = int(numa_node["num-threads"])
-                    epa_vcpu_set = True
+                    if numa_node.get("num-threads"):
+                        numa["threads"] = int(numa_node["num-threads"])
+                        epa_vcpu_set = True
 
-                if numa_node.get("memory-mb"):
-                    numa["memory"] = max(int(int(numa_node["memory-mb"]) / 1024), 1)
+                    if numa_node.get("memory-mb"):
+                        numa["memory"] = max(int(int(numa_node["memory-mb"]) / 1024), 1)
 
-        return numa, epa_vcpu_set
+                    numa_list.append(numa)
+                    numa = {}
+
+        return numa_list, epa_vcpu_set
 
     @staticmethod
     def _process_guest_epa_cpu_pinning_params(
@@ -728,23 +745,39 @@ class Ns(object):
         """
         extended = {}
         numa = {}
+        numa_list = []
 
         if target_flavor.get("guest-epa"):
             guest_epa = target_flavor["guest-epa"]
 
-            numa, epa_vcpu_set = Ns._process_guest_epa_numa_params(
+            numa_list, epa_vcpu_set = Ns._process_guest_epa_numa_params(
                 guest_epa_quota=guest_epa
             )
 
             if guest_epa.get("mempage-size"):
                 extended["mempage-size"] = guest_epa.get("mempage-size")
 
+            if guest_epa.get("cpu-pinning-policy"):
+                extended["cpu-pinning-policy"] = guest_epa.get("cpu-pinning-policy")
+
+            if guest_epa.get("cpu-thread-pinning-policy"):
+                extended["cpu-thread-pinning-policy"] = guest_epa.get(
+                    "cpu-thread-pinning-policy"
+                )
+
+            if guest_epa.get("numa-node-policy"):
+                if guest_epa.get("numa-node-policy").get("mem-policy"):
+                    extended["mem-policy"] = guest_epa.get("numa-node-policy").get(
+                        "mem-policy"
+                    )
+
             tmp_numa, epa_vcpu_set = Ns._process_guest_epa_cpu_pinning_params(
                 guest_epa_quota=guest_epa,
                 vcpu_count=int(target_flavor.get("vcpu-count", 1)),
                 epa_vcpu_set=epa_vcpu_set,
             )
-            numa.update(tmp_numa)
+            for numa in numa_list:
+                numa.update(tmp_numa)
 
             extended.update(
                 Ns._process_guest_epa_quota_params(
@@ -754,7 +787,7 @@ class Ns(object):
             )
 
         if numa:
-            extended["numas"] = [numa]
+            extended["numas"] = numa_list
 
         return extended
 
@@ -916,7 +949,7 @@ class Ns(object):
                     "id": vim_info.get("vim_network_id"),
                 },
             }
-        elif target_vld.get("mgmt-network"):
+        elif target_vld.get("mgmt-network") and not vim_info.get("provider_network"):
             extra_dict["find_params"] = {
                 "mgmt": True,
                 "name": target_vld["id"],
@@ -943,24 +976,25 @@ class Ns(object):
     @staticmethod
     def find_persistent_root_volumes(
         vnfd: dict,
-        target_vdu: str,
+        target_vdu: dict,
         vdu_instantiation_volumes_list: list,
         disk_list: list,
-    ) -> (list, dict):
+    ) -> Dict[str, any]:
         """Find the persistent root volumes and add them to the disk_list
-        by parsing the instantiation parameters
+        by parsing the instantiation parameters.
 
         Args:
-            vnfd:   VNFD
-            target_vdu: processed VDU
-            vdu_instantiation_volumes_list: instantiation parameters for the each VDU as a list
-            disk_list:  to be filled up
+            vnfd    (dict):                                 VNF descriptor
+            target_vdu      (dict):                         processed VDU
+            vdu_instantiation_volumes_list  (list):         instantiation parameters for the each VDU as a list
+            disk_list   (list):                             to be filled up
 
         Returns:
-            disk_list:  filled VDU list which is used for VDU creation
+            persistent_root_disk    (dict):                 Details of persistent root disk
 
         """
         persistent_root_disk = {}
+        # There can be only one root disk, when we find it, it will return the result
 
         for vdu, vsd in product(
             vnfd.get("vdu", ()), vnfd.get("virtual-storage-desc", ())
@@ -988,8 +1022,7 @@ class Ns(object):
 
                             disk_list.append(persistent_root_disk[vsd["id"]])
 
-                            # There can be only one root disk, when we find it, it will return the result
-                            return disk_list, persistent_root_disk
+                            return persistent_root_disk
 
                     else:
 
@@ -997,22 +1030,22 @@ class Ns(object):
                             persistent_root_disk[vsd["id"]] = {
                                 "image_id": vdu.get("sw-image-desc"),
                                 "size": root_disk.get("size-of-storage"),
+                                "keep": Ns.is_volume_keeping_required(root_disk),
                             }
 
                             disk_list.append(persistent_root_disk[vsd["id"]])
-                            return disk_list, persistent_root_disk
 
-        return disk_list, persistent_root_disk
+                            return persistent_root_disk
 
     @staticmethod
     def find_persistent_volumes(
         persistent_root_disk: dict,
-        target_vdu: str,
+        target_vdu: dict,
         vdu_instantiation_volumes_list: list,
         disk_list: list,
-    ) -> list:
+    ) -> None:
         """Find the ordinary persistent volumes and add them to the disk_list
-        by parsing the instantiation parameters
+        by parsing the instantiation parameters.
 
         Args:
             persistent_root_disk:   persistent root disk dictionary
@@ -1020,9 +1053,6 @@ class Ns(object):
             vdu_instantiation_volumes_list: instantiation parameters for the each VDU as a list
             disk_list:  to be filled up
 
-        Returns:
-            disk_list:  filled VDU list which is used for VDU creation
-
         """
         # Find the ordinary volumes which are not added to the persistent_root_disk
         persistent_disk = {}
@@ -1044,145 +1074,277 @@ class Ns(object):
                     if disk["id"] not in persistent_disk.keys():
                         persistent_disk[disk["id"]] = {
                             "size": disk.get("size-of-storage"),
+                            "keep": Ns.is_volume_keeping_required(disk),
                         }
                         disk_list.append(persistent_disk[disk["id"]])
 
-        return disk_list
-
     @staticmethod
-    def _process_vdu_params(
-        target_vdu: Dict[str, Any],
-        indata: Dict[str, Any],
-        vim_info: Dict[str, Any],
-        target_record_id: str,
-        **kwargs: Dict[str, Any],
-    ) -> Dict[str, Any]:
-        """Function to process VDU parameters.
+    def is_volume_keeping_required(virtual_storage_desc: Dict[str, Any]) -> bool:
+        """Function to decide keeping persistent volume
+        upon VDU deletion.
 
         Args:
-            target_vdu (Dict[str, Any]): [description]
-            indata (Dict[str, Any]): [description]
-            vim_info (Dict[str, Any]): [description]
-            target_record_id (str): [description]
+            virtual_storage_desc (Dict[str, Any]): virtual storage description dictionary
 
         Returns:
-            Dict[str, Any]: [description]
+            bool (True/False)
         """
-        vnfr_id = kwargs.get("vnfr_id")
-        nsr_id = kwargs.get("nsr_id")
-        vnfr = kwargs.get("vnfr")
-        vdu2cloud_init = kwargs.get("vdu2cloud_init")
-        tasks_by_target_record_id = kwargs.get("tasks_by_target_record_id")
-        logger = kwargs.get("logger")
-        db = kwargs.get("db")
-        fs = kwargs.get("fs")
-        ro_nsr_public_key = kwargs.get("ro_nsr_public_key")
 
-        vnf_preffix = "vnfrs:{}".format(vnfr_id)
-        ns_preffix = "nsrs:{}".format(nsr_id)
-        image_text = ns_preffix + ":image." + target_vdu["ns-image-id"]
-        flavor_text = ns_preffix + ":flavor." + target_vdu["ns-flavor-id"]
-        extra_dict = {"depends_on": [image_text, flavor_text]}
-        net_list = []
+        if not virtual_storage_desc.get("vdu-storage-requirements"):
+            return False
+        for item in virtual_storage_desc.get("vdu-storage-requirements", {}):
+            if item.get("key") == "keep-volume" and item.get("value") == "true":
+                return True
+        return False
+
+    @staticmethod
+    def _sort_vdu_interfaces(target_vdu: dict) -> None:
+        """Sort the interfaces according to position number.
+
+        Args:
+            target_vdu  (dict):     Details of VDU to be created
 
+        """
         # If the position info is provided for all the interfaces, it will be sorted
         # according to position number ascendingly.
-        if all(i.get("position") for i in target_vdu["interfaces"]):
-            sorted_interfaces = sorted(
-                target_vdu["interfaces"],
-                key=lambda x: (x.get("position") is None, x.get("position")),
-            )
-            target_vdu["interfaces"] = sorted_interfaces
+        sorted_interfaces = sorted(
+            target_vdu["interfaces"],
+            key=lambda x: (x.get("position") is None, x.get("position")),
+        )
+        target_vdu["interfaces"] = sorted_interfaces
+
+    @staticmethod
+    def _partially_locate_vdu_interfaces(target_vdu: dict) -> None:
+        """Only place the interfaces which has specific position.
+
+        Args:
+            target_vdu  (dict):     Details of VDU to be created
 
+        """
         # If the position info is provided for some interfaces but not all of them, the interfaces
         # which has specific position numbers will be placed and others' positions will not be taken care.
-        else:
-            if any(i.get("position") for i in target_vdu["interfaces"]):
-                n = len(target_vdu["interfaces"])
-                sorted_interfaces = [-1] * n
-                k, m = 0, 0
-                while k < n:
-                    if target_vdu["interfaces"][k].get("position"):
+        if any(
+            i.get("position") + 1
+            for i in target_vdu["interfaces"]
+            if i.get("position") is not None
+        ):
+            n = len(target_vdu["interfaces"])
+            sorted_interfaces = [-1] * n
+            k, m = 0, 0
+
+            while k < n:
+                if target_vdu["interfaces"][k].get("position") is not None:
+                    if any(i.get("position") == 0 for i in target_vdu["interfaces"]):
+                        idx = target_vdu["interfaces"][k]["position"] + 1
+                    else:
                         idx = target_vdu["interfaces"][k]["position"]
-                        sorted_interfaces[idx - 1] = target_vdu["interfaces"][k]
-                    k += 1
-                while m < n:
-                    if not target_vdu["interfaces"][m].get("position"):
-                        idy = sorted_interfaces.index(-1)
-                        sorted_interfaces[idy] = target_vdu["interfaces"][m]
-                    m += 1
+                    sorted_interfaces[idx - 1] = target_vdu["interfaces"][k]
+                k += 1
 
-                target_vdu["interfaces"] = sorted_interfaces
+            while m < n:
+                if target_vdu["interfaces"][m].get("position") is None:
+                    idy = sorted_interfaces.index(-1)
+                    sorted_interfaces[idy] = target_vdu["interfaces"][m]
+                m += 1
 
-        # If the position info is not provided for the interfaces, interfaces will be attached
-        # according to the order in the VNFD.
+            target_vdu["interfaces"] = sorted_interfaces
+
+    @staticmethod
+    def _prepare_vdu_cloud_init(
+        target_vdu: dict, vdu2cloud_init: dict, db: object, fs: object
+    ) -> Dict:
+        """Fill cloud_config dict with cloud init details.
+
+        Args:
+            target_vdu  (dict):         Details of VDU to be created
+            vdu2cloud_init  (dict):     Cloud init dict
+            db  (object):               DB object
+            fs  (object):               FS object
+
+        Returns:
+            cloud_config (dict):        Cloud config details of VDU
+
+        """
+        # cloud config
+        cloud_config = {}
+
+        if target_vdu.get("cloud-init"):
+            if target_vdu["cloud-init"] not in vdu2cloud_init:
+                vdu2cloud_init[target_vdu["cloud-init"]] = Ns._get_cloud_init(
+                    db=db,
+                    fs=fs,
+                    location=target_vdu["cloud-init"],
+                )
+
+            cloud_content_ = vdu2cloud_init[target_vdu["cloud-init"]]
+            cloud_config["user-data"] = Ns._parse_jinja2(
+                cloud_init_content=cloud_content_,
+                params=target_vdu.get("additionalParams"),
+                context=target_vdu["cloud-init"],
+            )
+
+        if target_vdu.get("boot-data-drive"):
+            cloud_config["boot-data-drive"] = target_vdu.get("boot-data-drive")
+
+        return cloud_config
+
+    @staticmethod
+    def _check_vld_information_of_interfaces(
+        interface: dict, ns_preffix: str, vnf_preffix: str
+    ) -> Optional[str]:
+        """Prepare the net_text by the virtual link information for vnf and ns level.
+        Args:
+            interface   (dict):         Interface details
+            ns_preffix  (str):          Prefix of NS
+            vnf_preffix (str):          Prefix of VNF
+
+        Returns:
+            net_text    (str):          information of net
+
+        """
+        net_text = ""
+        if interface.get("ns-vld-id"):
+            net_text = ns_preffix + ":vld." + interface["ns-vld-id"]
+        elif interface.get("vnf-vld-id"):
+            net_text = vnf_preffix + ":vld." + interface["vnf-vld-id"]
+
+        return net_text
+
+    @staticmethod
+    def _prepare_interface_port_security(interface: dict) -> None:
+        """
+
+        Args:
+            interface   (dict):     Interface details
+
+        """
+        if "port-security-enabled" in interface:
+            interface["port_security"] = interface.pop("port-security-enabled")
+
+        if "port-security-disable-strategy" in interface:
+            interface["port_security_disable_strategy"] = interface.pop(
+                "port-security-disable-strategy"
+            )
+
+    @staticmethod
+    def _create_net_item_of_interface(interface: dict, net_text: str) -> dict:
+        """Prepare net item including name, port security, floating ip etc.
+
+        Args:
+            interface   (dict):         Interface details
+            net_text    (str):          information of net
+
+        Returns:
+            net_item    (dict):         Dict including net details
+
+        """
+
+        net_item = {
+            x: v
+            for x, v in interface.items()
+            if x
+            in (
+                "name",
+                "vpci",
+                "port_security",
+                "port_security_disable_strategy",
+                "floating_ip",
+            )
+        }
+        net_item["net_id"] = "TASK-" + net_text
+        net_item["type"] = "virtual"
+
+        return net_item
+
+    @staticmethod
+    def _prepare_type_of_interface(
+        interface: dict, tasks_by_target_record_id: dict, net_text: str, net_item: dict
+    ) -> None:
+        """Fill the net item type by interface type such as SR-IOV, OM-MGMT, bridge etc.
+
+        Args:
+            interface   (dict):                     Interface details
+            tasks_by_target_record_id   (dict):     Task details
+            net_text    (str):                      information of net
+            net_item    (dict):                     Dict including net details
+
+        """
+        # TODO mac_address: used for  SR-IOV ifaces #TODO for other types
+        # TODO floating_ip: True/False (or it can be None)
+
+        if interface.get("type") in ("SR-IOV", "PCI-PASSTHROUGH"):
+            # Mark the net create task as type data
+            if deep_get(
+                tasks_by_target_record_id,
+                net_text,
+                "extra_dict",
+                "params",
+                "net_type",
+            ):
+                tasks_by_target_record_id[net_text]["extra_dict"]["params"][
+                    "net_type"
+                ] = "data"
+
+            net_item["use"] = "data"
+            net_item["model"] = interface["type"]
+            net_item["type"] = interface["type"]
+
+        elif (
+            interface.get("type") == "OM-MGMT"
+            or interface.get("mgmt-interface")
+            or interface.get("mgmt-vnf")
+        ):
+            net_item["use"] = "mgmt"
+
+        else:
+            # If interface.get("type") in ("VIRTIO", "E1000", "PARAVIRT"):
+            net_item["use"] = "bridge"
+            net_item["model"] = interface.get("type")
+
+    @staticmethod
+    def _prepare_vdu_interfaces(
+        target_vdu: dict,
+        extra_dict: dict,
+        ns_preffix: str,
+        vnf_preffix: str,
+        logger: object,
+        tasks_by_target_record_id: dict,
+        net_list: list,
+    ) -> None:
+        """Prepare the net_item and add net_list, add mgmt interface to extra_dict.
+
+        Args:
+            target_vdu  (dict):                             VDU to be created
+            extra_dict  (dict):                             Dictionary to be filled
+            ns_preffix  (str):                              NS prefix as string
+            vnf_preffix (str):                              VNF prefix as string
+            logger  (object):                               Logger Object
+            tasks_by_target_record_id  (dict):              Task details
+            net_list    (list):                             Net list of VDU
+        """
         for iface_index, interface in enumerate(target_vdu["interfaces"]):
-            if interface.get("ns-vld-id"):
-                net_text = ns_preffix + ":vld." + interface["ns-vld-id"]
-            elif interface.get("vnf-vld-id"):
-                net_text = vnf_preffix + ":vld." + interface["vnf-vld-id"]
-            else:
+
+            net_text = Ns._check_vld_information_of_interfaces(
+                interface, ns_preffix, vnf_preffix
+            )
+            if not net_text:
+                # Interface not connected to any vld
                 logger.error(
                     "Interface {} from vdu {} not connected to any vld".format(
                         iface_index, target_vdu["vdu-name"]
                     )
                 )
-
-                continue  # interface not connected to any vld
+                continue
 
             extra_dict["depends_on"].append(net_text)
 
-            if "port-security-enabled" in interface:
-                interface["port_security"] = interface.pop("port-security-enabled")
+            Ns._prepare_interface_port_security(interface)
 
-            if "port-security-disable-strategy" in interface:
-                interface["port_security_disable_strategy"] = interface.pop(
-                    "port-security-disable-strategy"
-                )
-
-            net_item = {
-                x: v
-                for x, v in interface.items()
-                if x
-                in (
-                    "name",
-                    "vpci",
-                    "port_security",
-                    "port_security_disable_strategy",
-                    "floating_ip",
-                )
-            }
-            net_item["net_id"] = "TASK-" + net_text
-            net_item["type"] = "virtual"
-
-            # TODO mac_address: used for  SR-IOV ifaces #TODO for other types
-            # TODO floating_ip: True/False (or it can be None)
-            if interface.get("type") in ("SR-IOV", "PCI-PASSTHROUGH"):
-                # mark the net create task as type data
-                if deep_get(
-                    tasks_by_target_record_id,
-                    net_text,
-                    "extra_dict",
-                    "params",
-                    "net_type",
-                ):
-                    tasks_by_target_record_id[net_text]["extra_dict"]["params"][
-                        "net_type"
-                    ] = "data"
+            net_item = Ns._create_net_item_of_interface(interface, net_text)
 
-                net_item["use"] = "data"
-                net_item["model"] = interface["type"]
-                net_item["type"] = interface["type"]
-            elif (
-                interface.get("type") == "OM-MGMT"
-                or interface.get("mgmt-interface")
-                or interface.get("mgmt-vnf")
-            ):
-                net_item["use"] = "mgmt"
-            else:
-                # if interface.get("type") in ("VIRTIO", "E1000", "PARAVIRT"):
-                net_item["use"] = "bridge"
-                net_item["model"] = interface.get("type")
+            Ns._prepare_type_of_interface(
+                interface, tasks_by_target_record_id, net_text, net_item
+            )
 
             if interface.get("ip-address"):
                 net_item["ip_address"] = interface["ip-address"]
@@ -1197,27 +1359,18 @@ class Ns(object):
             elif interface.get("mgmt-interface"):
                 extra_dict["mgmt_vdu_interface"] = iface_index
 
-        # cloud config
-        cloud_config = {}
-
-        if target_vdu.get("cloud-init"):
-            if target_vdu["cloud-init"] not in vdu2cloud_init:
-                vdu2cloud_init[target_vdu["cloud-init"]] = Ns._get_cloud_init(
-                    db=db,
-                    fs=fs,
-                    location=target_vdu["cloud-init"],
-                )
-
-            cloud_content_ = vdu2cloud_init[target_vdu["cloud-init"]]
-            cloud_config["user-data"] = Ns._parse_jinja2(
-                cloud_init_content=cloud_content_,
-                params=target_vdu.get("additionalParams"),
-                context=target_vdu["cloud-init"],
-            )
+    @staticmethod
+    def _prepare_vdu_ssh_keys(
+        target_vdu: dict, ro_nsr_public_key: dict, cloud_config: dict
+    ) -> None:
+        """Add ssh keys to cloud config.
 
-        if target_vdu.get("boot-data-drive"):
-            cloud_config["boot-data-drive"] = target_vdu.get("boot-data-drive")
+        Args:
+           target_vdu  (dict):                 Details of VDU to be created
+           ro_nsr_public_key   (dict):          RO NSR public Key
+           cloud_config  (dict):               Cloud config details
 
+        """
         ssh_keys = []
 
         if target_vdu.get("ssh-keys"):
@@ -1229,12 +1382,193 @@ class Ns(object):
         if ssh_keys:
             cloud_config["key-pairs"] = ssh_keys
 
+    @staticmethod
+    def _select_persistent_root_disk(vsd: dict, vdu: dict) -> dict:
+        """Selects the persistent root disk if exists.
+        Args:
+            vsd (dict):             Virtual storage descriptors in VNFD
+            vdu (dict):             VNF descriptor
+
+        Returns:
+            root_disk   (dict):     Selected persistent root disk
+        """
+        if vsd.get("id") == vdu.get("virtual-storage-desc", [[]])[0]:
+            root_disk = vsd
+            if root_disk.get(
+                "type-of-storage"
+            ) == "persistent-storage:persistent-storage" and root_disk.get(
+                "size-of-storage"
+            ):
+                return root_disk
+
+    @staticmethod
+    def _add_persistent_root_disk_to_disk_list(
+        vnfd: dict, target_vdu: dict, persistent_root_disk: dict, disk_list: list
+    ) -> None:
+        """Find the persistent root disk and add to disk list.
+
+        Args:
+            vnfd  (dict):                           VNF descriptor
+            target_vdu  (dict):                     Details of VDU to be created
+            persistent_root_disk    (dict):         Details of persistent root disk
+            disk_list   (list):                     Disks of VDU
+
+        """
+        for vdu in vnfd.get("vdu", ()):
+            if vdu["name"] == target_vdu["vdu-name"]:
+                for vsd in vnfd.get("virtual-storage-desc", ()):
+                    root_disk = Ns._select_persistent_root_disk(vsd, vdu)
+                    if not root_disk:
+                        continue
+
+                    persistent_root_disk[vsd["id"]] = {
+                        "image_id": vdu.get("sw-image-desc"),
+                        "size": root_disk["size-of-storage"],
+                        "keep": Ns.is_volume_keeping_required(root_disk),
+                    }
+
+                    disk_list.append(persistent_root_disk[vsd["id"]])
+                    break
+
+    @staticmethod
+    def _add_persistent_ordinary_disks_to_disk_list(
+        target_vdu: dict,
+        persistent_root_disk: dict,
+        persistent_ordinary_disk: dict,
+        disk_list: list,
+    ) -> None:
+        """Fill the disk list by adding persistent ordinary disks.
+
+        Args:
+            target_vdu  (dict):                     Details of VDU to be created
+            persistent_root_disk    (dict):         Details of persistent root disk
+            persistent_ordinary_disk    (dict):     Details of persistent ordinary disk
+            disk_list   (list):                     Disks of VDU
+
+        """
+        if target_vdu.get("virtual-storages"):
+            for disk in target_vdu["virtual-storages"]:
+                if (
+                    disk.get("type-of-storage")
+                    == "persistent-storage:persistent-storage"
+                    and disk["id"] not in persistent_root_disk.keys()
+                ):
+                    persistent_ordinary_disk[disk["id"]] = {
+                        "size": disk["size-of-storage"],
+                        "keep": Ns.is_volume_keeping_required(disk),
+                    }
+                    disk_list.append(persistent_ordinary_disk[disk["id"]])
+
+    @staticmethod
+    def _prepare_vdu_affinity_group_list(
+        target_vdu: dict, extra_dict: dict, ns_preffix: str
+    ) -> List[Dict[str, any]]:
+        """Process affinity group details to prepare affinity group list.
+
+        Args:
+            target_vdu  (dict):     Details of VDU to be created
+            extra_dict  (dict):     Dictionary to be filled
+            ns_preffix  (str):      Prefix as string
+
+        Returns:
+
+            affinity_group_list (list):     Affinity group details
+
+        """
+        affinity_group_list = []
+
+        if target_vdu.get("affinity-or-anti-affinity-group-id"):
+            for affinity_group_id in target_vdu["affinity-or-anti-affinity-group-id"]:
+                affinity_group = {}
+                affinity_group_text = (
+                    ns_preffix + ":affinity-or-anti-affinity-group." + affinity_group_id
+                )
+
+                if not isinstance(extra_dict.get("depends_on"), list):
+                    raise NsException("Invalid extra_dict format.")
+
+                extra_dict["depends_on"].append(affinity_group_text)
+                affinity_group["affinity_group_id"] = "TASK-" + affinity_group_text
+                affinity_group_list.append(affinity_group)
+
+        return affinity_group_list
+
+    @staticmethod
+    def _process_vdu_params(
+        target_vdu: Dict[str, Any],
+        indata: Dict[str, Any],
+        vim_info: Dict[str, Any],
+        target_record_id: str,
+        **kwargs: Dict[str, Any],
+    ) -> Dict[str, Any]:
+        """Function to process VDU parameters.
+
+        Args:
+            target_vdu (Dict[str, Any]): [description]
+            indata (Dict[str, Any]): [description]
+            vim_info (Dict[str, Any]): [description]
+            target_record_id (str): [description]
+
+        Returns:
+            Dict[str, Any]: [description]
+        """
+        vnfr_id = kwargs.get("vnfr_id")
+        nsr_id = kwargs.get("nsr_id")
+        vnfr = kwargs.get("vnfr")
+        vdu2cloud_init = kwargs.get("vdu2cloud_init")
+        tasks_by_target_record_id = kwargs.get("tasks_by_target_record_id")
+        logger = kwargs.get("logger")
+        db = kwargs.get("db")
+        fs = kwargs.get("fs")
+        ro_nsr_public_key = kwargs.get("ro_nsr_public_key")
+
+        vnf_preffix = "vnfrs:{}".format(vnfr_id)
+        ns_preffix = "nsrs:{}".format(nsr_id)
+        image_text = ns_preffix + ":image." + target_vdu["ns-image-id"]
+        flavor_text = ns_preffix + ":flavor." + target_vdu["ns-flavor-id"]
+        extra_dict = {"depends_on": [image_text, flavor_text]}
+        net_list = []
+
         persistent_root_disk = {}
+        persistent_ordinary_disk = {}
         vdu_instantiation_volumes_list = []
         disk_list = []
         vnfd_id = vnfr["vnfd-id"]
         vnfd = db.get_one("vnfds", {"_id": vnfd_id})
 
+        # If the position info is provided for all the interfaces, it will be sorted
+        # according to position number ascendingly.
+        if all(
+            True if i.get("position") is not None else False
+            for i in target_vdu["interfaces"]
+        ):
+
+            Ns._sort_vdu_interfaces(target_vdu)
+
+        # If the position info is provided for some interfaces but not all of them, the interfaces
+        # which has specific position numbers will be placed and others' positions will not be taken care.
+        else:
+
+            Ns._partially_locate_vdu_interfaces(target_vdu)
+
+        # If the position info is not provided for the interfaces, interfaces will be attached
+        # according to the order in the VNFD.
+        Ns._prepare_vdu_interfaces(
+            target_vdu,
+            extra_dict,
+            ns_preffix,
+            vnf_preffix,
+            logger,
+            tasks_by_target_record_id,
+            net_list,
+        )
+
+        # cloud config
+        cloud_config = Ns._prepare_vdu_cloud_init(target_vdu, vdu2cloud_init, db, fs)
+
+        # Prepare VDU ssh keys
+        Ns._prepare_vdu_ssh_keys(target_vdu, ro_nsr_public_key, cloud_config)
+
         if target_vdu.get("additionalParams"):
             vdu_instantiation_volumes_list = (
                 target_vdu.get("additionalParams").get("OSM").get("vdu_volumes")
@@ -1243,13 +1577,13 @@ class Ns(object):
         if vdu_instantiation_volumes_list:
 
             # Find the root volumes and add to the disk_list
-            (disk_list, persistent_root_disk,) = Ns.find_persistent_root_volumes(
+            persistent_root_disk = Ns.find_persistent_root_volumes(
                 vnfd, target_vdu, vdu_instantiation_volumes_list, disk_list
             )
 
             # Find the ordinary volumes which are not added to the persistent_root_disk
             # and put them to the disk list
-            disk_list = Ns.find_persistent_volumes(
+            Ns.find_persistent_volumes(
                 persistent_root_disk,
                 target_vdu,
                 vdu_instantiation_volumes_list,
@@ -1257,45 +1591,19 @@ class Ns(object):
             )
 
         else:
+            # Vdu_instantiation_volumes_list is empty
+            # First get add the persistent root disks to disk_list
+            Ns._add_persistent_root_disk_to_disk_list(
+                vnfd, target_vdu, persistent_root_disk, disk_list
+            )
+            # Add the persistent non-root disks to disk_list
+            Ns._add_persistent_ordinary_disks_to_disk_list(
+                target_vdu, persistent_root_disk, persistent_ordinary_disk, disk_list
+            )
 
-            # vdu_instantiation_volumes_list is empty
-            for vdu in vnfd.get("vdu", ()):
-                if vdu["name"] == target_vdu["vdu-name"]:
-                    for vsd in vnfd.get("virtual-storage-desc", ()):
-                        if vsd.get("id") == vdu.get("virtual-storage-desc", [[]])[0]:
-                            root_disk = vsd
-                            if root_disk.get(
-                                "type-of-storage"
-                            ) == "persistent-storage:persistent-storage" and root_disk.get(
-                                "size-of-storage"
-                            ):
-                                persistent_root_disk[vsd["id"]] = {
-                                    "image_id": vdu.get("sw-image-desc"),
-                                    "size": root_disk["size-of-storage"],
-                                }
-                                disk_list.append(persistent_root_disk[vsd["id"]])
-
-            if target_vdu.get("virtual-storages"):
-                for disk in target_vdu["virtual-storages"]:
-                    if (
-                        disk.get("type-of-storage")
-                        == "persistent-storage:persistent-storage"
-                        and disk["id"] not in persistent_root_disk.keys()
-                    ):
-                        disk_list.append({"size": disk["size-of-storage"]})
-
-        affinity_group_list = []
-
-        if target_vdu.get("affinity-or-anti-affinity-group-id"):
-            affinity_group = {}
-            for affinity_group_id in target_vdu["affinity-or-anti-affinity-group-id"]:
-                affinity_group_text = (
-                    ns_preffix + ":affinity-or-anti-affinity-group." + affinity_group_id
-                )
-
-                extra_dict["depends_on"].append(affinity_group_text)
-                affinity_group["affinity_group_id"] = "TASK-" + affinity_group_text
-                affinity_group_list.append(affinity_group)
+        affinity_group_list = Ns._prepare_vdu_affinity_group_list(
+            target_vdu, extra_dict, ns_preffix
+        )
 
         extra_dict["params"] = {
             "name": "{}-{}-{}-{}".format(
@@ -1641,7 +1949,7 @@ class Ns(object):
                     target_record_id = "{}.{}".format(db_record, existing_item["id"])
                     item_ = item
 
-                    if target_vim.startswith("sdn"):
+                    if target_vim.startswith("sdn") or target_vim.startswith("wim"):
                         # item must be sdn-net instead of net if target_vim is a sdn
                         item_ = "sdn_net"
                         target_record_id += ".sdn"
@@ -1689,13 +1997,13 @@ class Ns(object):
                 target_record_id = "{}.{}".format(db_record, target_item["id"])
                 item_ = item
 
-                if target_vim.startswith("sdn"):
+                if target_vim.startswith("sdn") or target_vim.startswith("wim"):
                     # item must be sdn-net instead of net if target_vim is a sdn
                     item_ = "sdn_net"
                     target_record_id += ".sdn"
 
                 kwargs = {}
-                self.logger.warning(
+                self.logger.debug(
                     "ns.calculate_diff_items target_item={}".format(target_item)
                 )
                 if process_params == Ns._process_flavor_params:
@@ -1704,14 +2012,12 @@ class Ns(object):
                             "db": self.db,
                         }
                     )
-                    self.logger.warning(
+                    self.logger.debug(
                         "calculate_diff_items for flavor kwargs={}".format(kwargs)
                     )
 
                 if process_params == Ns._process_vdu_params:
-                    self.logger.warning(
-                        "calculate_diff_items self.fs={}".format(self.fs)
-                    )
+                    self.logger.debug("calculate_diff_items self.fs={}".format(self.fs))
                     kwargs.update(
                         {
                             "vnfr_id": vnfr_id,
@@ -1725,7 +2031,7 @@ class Ns(object):
                             "ro_nsr_public_key": ro_nsr_public_key,
                         }
                     )
-                    self.logger.warning("calculate_diff_items kwargs={}".format(kwargs))
+                    self.logger.debug("calculate_diff_items kwargs={}".format(kwargs))
 
                 extra_dict = process_params(
                     target_item,
@@ -1863,7 +2169,7 @@ class Ns(object):
                 extra_dict=change.get("extra_dict", None),
             )
 
-            self.logger.warning("ns.define_all_tasks task={}".format(task))
+            self.logger.debug("ns.define_all_tasks task={}".format(task))
             tasks_by_target_record_id[change["target_record_id"]] = task
             db_new_tasks.append(task)
 
@@ -1947,7 +2253,7 @@ class Ns(object):
 
         for db_task in db_new_tasks:
             target_id = db_task.pop("target_id")
-            self.logger.warning("target_id={} db_task={}".format(target_id, db_task))
+            self.logger.debug("target_id={} db_task={}".format(target_id, db_task))
 
             action = db_task.get("action", None)
 
@@ -1968,7 +2274,7 @@ class Ns(object):
                 db_ro_task["vim_info"]["vim_id"] = db_task.get("vim_id", None)
 
             nb_ro_tasks += 1
-            self.logger.warning("upload_all_tasks db_ro_task={}".format(db_ro_task))
+            self.logger.debug("upload_all_tasks db_ro_task={}".format(db_ro_task))
             self.db.create("ro_tasks", db_ro_task)
 
         self.logger.debug(