Merges branch sol006 into master, allowing NBI to use, parse and validate SOL006... 46/10046/1
authorgarciaale <agarcia@whitestack.com>
Fri, 27 Nov 2020 13:38:35 +0000 (10:38 -0300)
committergarciaale <agarcia@whitestack.com>
Fri, 27 Nov 2020 13:39:09 +0000 (10:39 -0300)
Change-Id: I85db721d20c37d82641604a4da27639445ae2a8d
Signed-off-by: garciaale <agarcia@whitestack.com>
osm_nbi/descriptor_topics.py
osm_nbi/http/cert.pem
osm_nbi/http/privkey.pem
osm_nbi/instance_topics.py
osm_nbi/tests/test_db_descriptors.py
osm_nbi/tests/test_descriptor_topics.py
osm_nbi/tests/test_instance_topics.py
osm_nbi/tests/test_pkg_descriptors.py
osm_nbi/tests/test_pmjobs_topic.py
osm_nbi/utils.py [new file with mode: 0644]
tox.ini

index 3bdaa03..4e056f5 100644 (file)
@@ -16,6 +16,7 @@
 import tarfile
 import yaml
 import json
+import importlib
 # import logging
 from hashlib import md5
 from osm_common.dbbase import DbException, deep_update_rfc7396
@@ -26,8 +27,8 @@ from re import fullmatch
 from osm_nbi.validation import ValidationError, pdu_new_schema, pdu_edit_schema, \
     validate_input, vnfpkgop_new_schema
 from osm_nbi.base_topic import BaseTopic, EngineException, get_iterable
-from osm_im.vnfd import vnfd as vnfd_im
-from osm_im.nsd import nsd as nsd_im
+etsi_nfv_vnfd = importlib.import_module("osm_im.etsi-nfv-vnfd")
+etsi_nfv_nsd = importlib.import_module("osm_im.etsi-nfv-nsd")
 from osm_im.nst import nst as nst_im
 from pyangbind.lib.serialise import pybindJSONDecoder
 import pyangbind.lib.pybindJSON as pybindJSON
@@ -78,7 +79,6 @@ class DescriptorTopic(BaseTopic):
         # 1.3. restore internal keys
         for k, v in internal_keys.items():
             final_content[k] = v
-
         if session["force"]:
             return
         # 2. check that this id is not present
@@ -301,8 +301,6 @@ class DescriptorTopic(BaseTopic):
             # Override descriptor with query string kwargs
             if kwargs:
                 self._update_input_with_kwargs(indata, kwargs)
-            # it will call overrides method at VnfdTopic or NsdTopic
-            # indata = self._validate_input_edit(indata, force=session["force"])
 
             deep_update_rfc7396(current_desc, indata)
             self.check_conflict_on_edit(session, current_desc, indata, _id=_id)
@@ -396,33 +394,25 @@ class DescriptorTopic(BaseTopic):
                                       "future versions", http_code=HTTPStatus.NOT_ACCEPTABLE)
             return self.fs.file_open((storage['folder'], storage['zipfile']), "rb"), accept_zip
 
-    def pyangbind_validation(self, item, data, force=False):
-        try:
-            if item == "vnfds":
-                myvnfd = vnfd_im()
-                pybindJSONDecoder.load_ietf_json({'vnfd:vnfd-catalog': {'vnfd': [data]}}, None, None, obj=myvnfd,
-                                                 path_helper=True, skip_unknown=force)
-                out = pybindJSON.dumps(myvnfd, mode="ietf")
-            elif item == "nsds":
-                mynsd = nsd_im()
-                pybindJSONDecoder.load_ietf_json({'nsd:nsd-catalog': {'nsd': [data]}}, None, None, obj=mynsd,
-                                                 path_helper=True, skip_unknown=force)
-                out = pybindJSON.dumps(mynsd, mode="ietf")
-            elif item == "nsts":
-                mynst = nst_im()
-                pybindJSONDecoder.load_ietf_json({'nst': [data]}, None, None, obj=mynst,
-                                                 path_helper=True, skip_unknown=force)
-                out = pybindJSON.dumps(mynst, mode="ietf")
-            else:
-                raise EngineException("Not possible to validate '{}' item".format(item),
-                                      http_code=HTTPStatus.INTERNAL_SERVER_ERROR)
-
-            desc_out = self._remove_envelop(yaml.safe_load(out))
-            return desc_out
+    def _remove_yang_prefixes_from_descriptor(self, descriptor):
+        new_descriptor = {}
+        for k, v in descriptor.items():
+            new_v = v
+            if isinstance(v, dict):
+                new_v = self._remove_yang_prefixes_from_descriptor(v)
+            elif isinstance(v, list):
+                new_v = list()
+                for x in v:
+                    if isinstance(x, dict):
+                        new_v.append(self._remove_yang_prefixes_from_descriptor(x))
+                    else:
+                        new_v.append(x)
+            new_descriptor[k.split(':')[-1]] = new_v
+        return new_descriptor
 
-        except Exception as e:
-            raise EngineException("Error in pyangbind validation: {}".format(str(e)),
-                                  http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+    def pyangbind_validation(self, item, data, force=False):
+        raise EngineException("Not possible to validate '{}' item".format(item),
+                              http_code=HTTPStatus.INTERNAL_SERVER_ERROR)
 
     def _validate_input_edit(self, indata, content, force=False):
         # not needed to validate with pyangbind becuase it will be validated at check_conflict_on_edit
@@ -465,23 +455,40 @@ class VnfdTopic(DescriptorTopic):
     def __init__(self, db, fs, msg, auth):
         DescriptorTopic.__init__(self, db, fs, msg, auth)
 
+    def pyangbind_validation(self, item, data, force=False):
+        try:
+            virtual_compute_descriptors = data.get('virtual-compute-desc')
+            virtual_storage_descriptors = data.get('virtual-storage-desc')
+            myvnfd = etsi_nfv_vnfd.etsi_nfv_vnfd()
+            pybindJSONDecoder.load_ietf_json({'etsi-nfv-vnfd:vnfd': data}, None, None, obj=myvnfd,
+                                             path_helper=True, skip_unknown=force)
+            out = pybindJSON.dumps(myvnfd, mode="ietf")
+            desc_out = self._remove_envelop(yaml.safe_load(out))
+            desc_out = self._remove_yang_prefixes_from_descriptor(desc_out)
+            if virtual_compute_descriptors:
+                desc_out['virtual-compute-desc'] = virtual_compute_descriptors
+            if virtual_storage_descriptors:
+                desc_out['virtual-storage-desc'] = virtual_storage_descriptors
+            return desc_out
+        except Exception as e:
+            raise EngineException("Error in pyangbind validation: {}".format(str(e)),
+                                  http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+
     @staticmethod
     def _remove_envelop(indata=None):
         if not indata:
             return {}
         clean_indata = indata
-        if clean_indata.get('vnfd:vnfd-catalog'):
-            clean_indata = clean_indata['vnfd:vnfd-catalog']
-        elif clean_indata.get('vnfd-catalog'):
-            clean_indata = clean_indata['vnfd-catalog']
-        if clean_indata.get('vnfd'):
-            if not isinstance(clean_indata['vnfd'], list) or len(clean_indata['vnfd']) != 1:
-                raise EngineException("'vnfd' must be a list of only one element")
-            clean_indata = clean_indata['vnfd'][0]
-        elif clean_indata.get('vnfd:vnfd'):
-            if not isinstance(clean_indata['vnfd:vnfd'], list) or len(clean_indata['vnfd:vnfd']) != 1:
-                raise EngineException("'vnfd:vnfd' must be a list of only one element")
-            clean_indata = clean_indata['vnfd:vnfd'][0]
+
+        if clean_indata.get('etsi-nfv-vnfd:vnfd'):
+            if not isinstance(clean_indata['etsi-nfv-vnfd:vnfd'], dict):
+                raise EngineException("'etsi-nfv-vnfd:vnfd' must be a dict")
+            clean_indata = clean_indata['etsi-nfv-vnfd:vnfd']
+        elif clean_indata.get('vnfd'):
+            if not isinstance(clean_indata['vnfd'], dict):
+                raise EngineException("'vnfd' must be dict")
+            clean_indata = clean_indata['vnfd']
+
         return clean_indata
 
     def check_conflict_on_edit(self, session, final_content, edit_content, _id):
@@ -536,89 +543,83 @@ class VnfdTopic(DescriptorTopic):
         indata.pop("onboardingState", None)
         indata.pop("operationalState", None)
         indata.pop("usageState", None)
-
         indata.pop("links", None)
 
         indata = self.pyangbind_validation("vnfds", indata, force)
         # Cross references validation in the descriptor
-        self.validate_mgmt_interfaces_connection_points(indata)
+
+        self.validate_mgmt_interface_connection_point(indata)
 
         for vdu in get_iterable(indata.get("vdu")):
-            self.validate_vdu_connection_point_refs(vdu, indata)
+            self.validate_vdu_internal_connection_points(vdu)
             self._validate_vdu_charms_in_package(storage_params, vdu, indata)
             self._validate_vdu_cloud_init_in_package(storage_params, vdu, indata)
 
         self._validate_vnf_charms_in_package(storage_params, indata)
 
-        self.validate_internal_vlds(indata)
+        self.validate_external_connection_points(indata)
+        self.validate_internal_virtual_links(indata)
         self.validate_monitoring_params(indata)
         self.validate_scaling_group_descriptor(indata)
 
         return indata
 
     @staticmethod
-    def validate_mgmt_interfaces_connection_points(indata):
+    def validate_mgmt_interface_connection_point(indata):
         if not indata.get("vdu"):
             return
-        if not indata.get("mgmt-interface"):
-            raise EngineException("'mgmt-interface' is a mandatory field and it is not defined",
+        if not indata.get("mgmt-cp"):
+            raise EngineException("'mgmt-cp' is a mandatory field and it is not defined",
                                   http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-        if indata["mgmt-interface"].get("cp"):
-            for cp in get_iterable(indata.get("connection-point")):
-                if cp["name"] == indata["mgmt-interface"]["cp"]:
-                    break
-            else:
-                raise EngineException("mgmt-interface:cp='{}' must match an existing connection-point"
-                                      .format(indata["mgmt-interface"]["cp"]),
+
+        for cp in get_iterable(indata.get("ext-cpd")):
+            if cp["id"] == indata["mgmt-cp"]:
+                break
+        else:
+            raise EngineException("mgmt-cp='{}' must match an existing ext-cpd".format(indata["mgmt-cp"]),
+                                  http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+
+    @staticmethod
+    def validate_vdu_internal_connection_points(vdu):
+        int_cpds = set()
+        for cpd in get_iterable(vdu.get("int-cpd")):
+            cpd_id = cpd.get("id")
+            if cpd_id and cpd_id in int_cpds:
+                raise EngineException("vdu[id='{}']:int-cpd[id='{}'] is already used by other int-cpd"
+                                      .format(vdu["id"], cpd_id),
                                       http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+            int_cpds.add(cpd_id)
 
     @staticmethod
-    def validate_vdu_connection_point_refs(vdu, indata):
-        icp_refs = []
-        ecp_refs = []
-        for interface in get_iterable(vdu.get("interface")):
-            if interface.get("external-connection-point-ref"):
-                if interface.get("external-connection-point-ref") in ecp_refs:
-                    raise EngineException("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{}' "
-                                          "is referenced by other interface"
-                                          .format(vdu["id"], interface["name"],
-                                                  interface["external-connection-point-ref"]),
-                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-                ecp_refs.append(interface.get("external-connection-point-ref"))
-                for cp in get_iterable(indata.get("connection-point")):
-                    if cp["name"] == interface["external-connection-point-ref"]:
-                        break
-                else:
-                    raise EngineException("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{}' "
-                                          "must match an existing connection-point"
-                                          .format(vdu["id"], interface["name"],
-                                                  interface["external-connection-point-ref"]),
-                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-            elif interface.get("internal-connection-point-ref"):
-                if interface.get("internal-connection-point-ref") in icp_refs:
-                    raise EngineException("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}' "
-                                          "is referenced by other interface"
-                                          .format(vdu["id"], interface["name"],
-                                                  interface["internal-connection-point-ref"]),
-                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-                icp_refs.append(interface.get("internal-connection-point-ref"))
-                for internal_cp in get_iterable(vdu.get("internal-connection-point")):
-                    if interface["internal-connection-point-ref"] == internal_cp.get("id"):
-                        break
-                else:
-                    raise EngineException("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}' "
-                                          "must match an existing vdu:internal-connection-point"
-                                          .format(vdu["id"], interface["name"],
-                                                  interface["internal-connection-point-ref"]),
+    def validate_external_connection_points(indata):
+        all_vdus_int_cpds = set()
+        for vdu in get_iterable(indata.get("vdu")):
+            for int_cpd in get_iterable(vdu.get("int-cpd")):
+                all_vdus_int_cpds.add((vdu.get("id"), int_cpd.get("id")))
+
+        ext_cpds = set()
+        for cpd in get_iterable(indata.get("ext-cpd")):
+            cpd_id = cpd.get("id")
+            if cpd_id and cpd_id in ext_cpds:
+                raise EngineException("ext-cpd[id='{}'] is already used by other ext-cpd".format(cpd_id),
+                                      http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+            ext_cpds.add(cpd_id)
+
+            int_cpd = cpd.get("int-cpd")
+            if int_cpd:
+                if (int_cpd.get("vdu-id"), int_cpd.get("cpd")) not in all_vdus_int_cpds:
+                    raise EngineException("ext-cpd[id='{}']:int-cpd must match an existing vdu int-cpd".format(cpd_id),
                                           http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+            # TODO: Validate k8s-cluster-net points to a valid k8s-cluster:nets ?
 
     def _validate_vdu_charms_in_package(self, storage_params, vdu, indata):
         if not vdu.get("vdu-configuration"):
             return
-        if vdu["vdu-configuration"].get("juju"):
-            if not self._validate_package_folders(storage_params, 'charms'):
-                raise EngineException("Charm defined in vnf[id={}]:vdu[id={}] but not present in "
-                                      "package".format(indata["id"], vdu["id"]))
+        for vdu_configuration in get_iterable(indata.get("vdu-configuration")):
+            if vdu_configuration.get("juju"):
+                if not self._validate_package_folders(storage_params, 'charms'):
+                    raise EngineException("Charm defined in vnf[id={}] but not present in "
+                                          "package".format(indata["id"]))
 
     def _validate_vdu_cloud_init_in_package(self, storage_params, vdu, indata):
         if not vdu.get("cloud-init-file"):
@@ -630,10 +631,11 @@ class VnfdTopic(DescriptorTopic):
     def _validate_vnf_charms_in_package(self, storage_params, indata):
         if not indata.get("vnf-configuration"):
             return
-        if indata["vnf-configuration"].get("juju"):
-            if not self._validate_package_folders(storage_params, 'charms'):
-                raise EngineException("Charm defined in vnf[id={}] but not present in "
-                                      "package".format(indata["id"]))
+        for vnf_configuration in get_iterable(indata.get("vnf-configuration")):
+            if vnf_configuration.get("juju"):
+                if not self._validate_package_folders(storage_params, 'charms'):
+                    raise EngineException("Charm defined in vnf[id={}] but not present in "
+                                          "package".format(indata["id"]))
 
     def _validate_package_folders(self, storage_params, folder, file=None):
         if not storage_params or not storage_params.get("pkg-dir"):
@@ -652,116 +654,113 @@ class VnfdTopic(DescriptorTopic):
             return False
 
     @staticmethod
-    def validate_internal_vlds(indata):
-        vld_names = []  # For detection of duplicated VLD names
-        for ivld in get_iterable(indata.get("internal-vld")):
-            ivld_name = ivld.get("name")
-            if ivld_name and ivld_name in vld_names:
-                raise EngineException("Duplicated VLD name '{}' in vnfd[id={}]:internal-vld[id={}]"
-                                      .format(ivld["name"], indata["id"], ivld["id"]),
+    def validate_internal_virtual_links(indata):
+        all_ivld_ids = set()
+        for ivld in get_iterable(indata.get("int-virtual-link-desc")):
+            ivld_id = ivld.get("id")
+            if ivld_id and ivld_id in all_ivld_ids:
+                raise EngineException("Duplicated VLD id in int-virtual-link-desc[id={}]".format(ivld_id),
                                       http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
             else:
-                vld_names.append(ivld_name)
-
-            for icp in get_iterable(ivld.get("internal-connection-point")):
-                icp_mark = False
-                for vdu in get_iterable(indata.get("vdu")):
-                    for internal_cp in get_iterable(vdu.get("internal-connection-point")):
-                        if icp["id-ref"] == internal_cp["id"]:
-                            icp_mark = True
-                            break
-                    if icp_mark:
-                        break
-                else:
-                    raise EngineException("internal-vld[id='{}']:internal-connection-point='{}' must match an existing "
-                                          "vdu:internal-connection-point".format(ivld["id"], icp["id-ref"]),
-                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-            if ivld.get("ip-profile-ref"):
-                for ip_prof in get_iterable(indata.get("ip-profiles")):
-                    if ip_prof["name"] == get_iterable(ivld.get("ip-profile-ref")):
-                        break
-                else:
-                    raise EngineException("internal-vld[id='{}']:ip-profile-ref='{}' does not exist".format(
-                        ivld["id"], ivld["ip-profile-ref"]),
+                all_ivld_ids.add(ivld_id)
+
+        for vdu in get_iterable(indata.get("vdu")):
+            for int_cpd in get_iterable(vdu.get("int-cpd")):
+                int_cpd_ivld_id = int_cpd.get("int-virtual-link-desc")
+                if int_cpd_ivld_id and int_cpd_ivld_id not in all_ivld_ids:
+                    raise EngineException(
+                        "vdu[id='{}']:int-cpd[id='{}']:int-virtual-link-desc='{}' must match an existing "
+                        "int-virtual-link-desc".format(vdu["id"], int_cpd["id"], int_cpd_ivld_id),
                         http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
 
+        for df in get_iterable(indata.get("df")):
+            for vlp in get_iterable(df.get("virtual-link-profile")):
+                vlp_ivld_id = vlp.get("id")
+                if vlp_ivld_id and vlp_ivld_id not in all_ivld_ids:
+                    raise EngineException("df[id='{}']:virtual-link-profile='{}' must match an existing "
+                                          "int-virtual-link-desc".format(df["id"], vlp_ivld_id),
+                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+
     @staticmethod
     def validate_monitoring_params(indata):
-        for mp in get_iterable(indata.get("monitoring-param")):
-            if mp.get("vdu-monitoring-param"):
-                mp_vmp_mark = False
-                for vdu in get_iterable(indata.get("vdu")):
-                    for vmp in get_iterable(vdu.get("monitoring-param")):
-                        if vmp["id"] == mp["vdu-monitoring-param"].get("vdu-monitoring-param-ref") and vdu["id"] == \
-                                mp["vdu-monitoring-param"]["vdu-ref"]:
-                            mp_vmp_mark = True
-                            break
-                    if mp_vmp_mark:
-                        break
+        all_monitoring_params = set()
+        for ivld in get_iterable(indata.get("int-virtual-link-desc")):
+            for mp in get_iterable(ivld.get("monitoring-parameters")):
+                mp_id = mp.get("id")
+                if mp_id and mp_id in all_monitoring_params:
+                    raise EngineException("Duplicated monitoring-parameter id in "
+                                          "int-virtual-link-desc[id='{}']:monitoring-parameters[id='{}']"
+                                          .format(ivld["id"], mp_id),
+                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
                 else:
-                    raise EngineException("monitoring-param:vdu-monitoring-param:vdu-monitoring-param-ref='{}' not "
-                                          "defined at vdu[id='{}'] or vdu does not exist"
-                                          .format(mp["vdu-monitoring-param"]["vdu-monitoring-param-ref"],
-                                                  mp["vdu-monitoring-param"]["vdu-ref"]),
+                    all_monitoring_params.add(mp_id)
+
+        for vdu in get_iterable(indata.get("vdu")):
+            for mp in get_iterable(vdu.get("monitoring-parameter")):
+                mp_id = mp.get("id")
+                if mp_id and mp_id in all_monitoring_params:
+                    raise EngineException("Duplicated monitoring-parameter id in "
+                                          "vdu[id='{}']:monitoring-parameter[id='{}']"
+                                          .format(vdu["id"], mp_id),
                                           http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-            elif mp.get("vdu-metric"):
-                mp_vm_mark = False
-                for vdu in get_iterable(indata.get("vdu")):
-                    if vdu.get("vdu-configuration"):
-                        for metric in get_iterable(vdu["vdu-configuration"].get("metrics")):
-                            if metric["name"] == mp["vdu-metric"]["vdu-metric-name-ref"] and vdu["id"] == \
-                                    mp["vdu-metric"]["vdu-ref"]:
-                                mp_vm_mark = True
-                                break
-                        if mp_vm_mark:
-                            break
                 else:
-                    raise EngineException("monitoring-param:vdu-metric:vdu-metric-name-ref='{}' not defined at "
-                                          "vdu[id='{}'] or vdu does not exist"
-                                          .format(mp["vdu-metric"]["vdu-metric-name-ref"],
-                                                  mp["vdu-metric"]["vdu-ref"]),
+                    all_monitoring_params.add(mp_id)
+
+        for df in get_iterable(indata.get("df")):
+            for mp in get_iterable(df.get("monitoring-parameter")):
+                mp_id = mp.get("id")
+                if mp_id and mp_id in all_monitoring_params:
+                    raise EngineException("Duplicated monitoring-parameter id in "
+                                          "df[id='{}']:monitoring-parameter[id='{}']"
+                                          .format(df["id"], mp_id),
                                           http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+                else:
+                    all_monitoring_params.add(mp_id)
 
     @staticmethod
     def validate_scaling_group_descriptor(indata):
-        for sgd in get_iterable(indata.get("scaling-group-descriptor")):
-            for sp in get_iterable(sgd.get("scaling-policy")):
-                for sc in get_iterable(sp.get("scaling-criteria")):
-                    for mp in get_iterable(indata.get("monitoring-param")):
-                        if mp["id"] == get_iterable(sc.get("vnf-monitoring-param-ref")):
-                            break
-                    else:
-                        raise EngineException("scaling-group-descriptor[name='{}']:scaling-criteria[name='{}']:"
-                                              "vnf-monitoring-param-ref='{}' not defined in any monitoring-param"
-                                              .format(sgd["name"], sc["name"], sc["vnf-monitoring-param-ref"]),
+        all_monitoring_params = set()
+        for ivld in get_iterable(indata.get("int-virtual-link-desc")):
+            for mp in get_iterable(ivld.get("monitoring-parameters")):
+                all_monitoring_params.add(mp.get("id"))
+
+        for vdu in get_iterable(indata.get("vdu")):
+            for mp in get_iterable(vdu.get("monitoring-parameter")):
+                all_monitoring_params.add(mp.get("id"))
+
+        for df in get_iterable(indata.get("df")):
+            for mp in get_iterable(df.get("monitoring-parameter")):
+                all_monitoring_params.add(mp.get("id"))
+
+        for df in get_iterable(indata.get("df")):
+            for sa in get_iterable(df.get("scaling-aspect")):
+                for sp in get_iterable(sa.get("scaling-policy")):
+                    for sc in get_iterable(sp.get("scaling-criteria")):
+                        sc_monitoring_param = sc.get("vnf-monitoring-param-ref")
+                        if sc_monitoring_param and sc_monitoring_param not in all_monitoring_params:
+                            raise EngineException("df[id='{}']:scaling-aspect[id='{}']:scaling-policy"
+                                                  "[name='{}']:scaling-criteria[name='{}']: "
+                                                  "vnf-monitoring-param-ref='{}' not defined in any monitoring-param"
+                                                  .format(df["id"], sa["id"], sp["name"], sc["name"],
+                                                          sc_monitoring_param),
+                                                  http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+
+                for sca in get_iterable(sa.get("scaling-config-action")):
+                    if not indata.get("vnf-configuration"):
+                        raise EngineException("'vnf-configuration' not defined in the descriptor but it is referenced "
+                                              "by df[id='{}']:scaling-aspect[id='{}']:scaling-config-action"
+                                              .format(df["id"], sa["id"]),
                                               http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-            for sgd_vdu in get_iterable(sgd.get("vdu")):
-                sgd_vdu_mark = False
-                for vdu in get_iterable(indata.get("vdu")):
-                    if vdu["id"] == sgd_vdu["vdu-id-ref"]:
-                        sgd_vdu_mark = True
-                        break
-                if sgd_vdu_mark:
-                    break
-            else:
-                raise EngineException("scaling-group-descriptor[name='{}']:vdu-id-ref={} does not match any vdu"
-                                      .format(sgd["name"], sgd_vdu["vdu-id-ref"]),
-                                      http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-            for sca in get_iterable(sgd.get("scaling-config-action")):
-                if not indata.get("vnf-configuration"):
-                    raise EngineException("'vnf-configuration' not defined in the descriptor but it is referenced by "
-                                          "scaling-group-descriptor[name='{}']:scaling-config-action"
-                                          .format(sgd["name"]),
-                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-                for primitive in get_iterable(indata["vnf-configuration"].get("config-primitive")):
-                    if primitive["name"] == sca["vnf-config-primitive-name-ref"]:
-                        break
-                else:
-                    raise EngineException("scaling-group-descriptor[name='{}']:scaling-config-action:vnf-config-"
-                                          "primitive-name-ref='{}' does not match any "
-                                          "vnf-configuration:config-primitive:name"
-                                          .format(sgd["name"], sca["vnf-config-primitive-name-ref"]),
-                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+                    for configuration in get_iterable(indata["vnf-configuration"]):
+                        for primitive in get_iterable(configuration.get("config-primitive")):
+                            if primitive["name"] == sca["vnf-config-primitive-name-ref"]:
+                                break
+                        else:
+                            raise EngineException("df[id='{}']:scaling-aspect[id='{}']:scaling-config-action:vnf-"
+                                                  "config-primitive-name-ref='{}' does not match any "
+                                                  "vnf-configuration:config-primitive:name"
+                                                  .format(df["id"], sa["id"], sca["vnf-config-primitive-name-ref"]),
+                                                  http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
 
     def delete_extra(self, session, _id, db_content, not_send_msg=None):
         """
@@ -797,24 +796,33 @@ class NsdTopic(DescriptorTopic):
     def __init__(self, db, fs, msg, auth):
         DescriptorTopic.__init__(self, db, fs, msg, auth)
 
+    def pyangbind_validation(self, item, data, force=False):
+        try:
+            mynsd = etsi_nfv_nsd.etsi_nfv_nsd()
+            pybindJSONDecoder.load_ietf_json({'nsd': {'nsd': [data]}}, None, None, obj=mynsd,
+                                             path_helper=True, skip_unknown=force)
+            out = pybindJSON.dumps(mynsd, mode="ietf")
+            desc_out = self._remove_envelop(yaml.safe_load(out))
+            desc_out = self._remove_yang_prefixes_from_descriptor(desc_out)
+            return desc_out
+        except Exception as e:
+            raise EngineException("Error in pyangbind validation: {}".format(str(e)),
+                                  http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+
     @staticmethod
     def _remove_envelop(indata=None):
         if not indata:
             return {}
         clean_indata = indata
 
-        if clean_indata.get('nsd:nsd-catalog'):
-            clean_indata = clean_indata['nsd:nsd-catalog']
-        elif clean_indata.get('nsd-catalog'):
-            clean_indata = clean_indata['nsd-catalog']
+        if clean_indata.get('nsd'):
+            clean_indata = clean_indata['nsd']
+        elif clean_indata.get('etsi-nfv-nsd:nsd'):
+            clean_indata = clean_indata['etsi-nfv-nsd:nsd']
         if clean_indata.get('nsd'):
             if not isinstance(clean_indata['nsd'], list) or len(clean_indata['nsd']) != 1:
                 raise EngineException("'nsd' must be a list of only one element")
             clean_indata = clean_indata['nsd'][0]
-        elif clean_indata.get('nsd:nsd'):
-            if not isinstance(clean_indata['nsd:nsd'], list) or len(clean_indata['nsd:nsd']) != 1:
-                raise EngineException("'nsd:nsd' must be a list of only one element")
-            clean_indata = clean_indata['nsd:nsd'][0]
         return clean_indata
 
     def _validate_input_new(self, indata, storage_params, force=False):
@@ -827,54 +835,37 @@ class NsdTopic(DescriptorTopic):
         indata = self.pyangbind_validation("nsds", indata, force)
         # Cross references validation in the descriptor
         # TODO validata that if contains cloud-init-file or charms, have artifacts _admin.storage."pkg-dir" is not none
-        for vld in get_iterable(indata.get("vld")):
-            self.validate_vld_mgmt_network_with_ip_profile_ref(vld)
-            self.validate_vld_connection_point_refs(vld, indata)
+        for vld in get_iterable(indata.get("virtual-link-desc")):
+            self.validate_vld_mgmt_network_with_virtual_link_protocol_data(vld, indata)
 
-        for fgd in get_iterable(indata.get("vnffgd")):
-            self.validate_fgd_classifiers(fgd)
+        self.validate_vnf_profiles_vnfd_id(indata)
 
         return indata
 
     @staticmethod
-    def validate_vld_mgmt_network_with_ip_profile_ref(vld):
-        if vld.get("mgmt-network") and vld.get("ip-profile-ref"):
-            raise EngineException("Error at vld[id='{}']:ip-profile-ref"
-                                  " You cannot set an ip-profile when mgmt-network is True"
-                                  .format(vld["id"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-
-    @staticmethod
-    def validate_vld_connection_point_refs(vld, indata):
-        for vnfd_cp in get_iterable(vld.get("vnfd-connection-point-ref")):
-            for constituent_vnfd in get_iterable(indata.get("constituent-vnfd")):
-                if vnfd_cp["member-vnf-index-ref"] == constituent_vnfd["member-vnf-index"]:
-                    if vnfd_cp.get("vnfd-id-ref") and vnfd_cp["vnfd-id-ref"] != constituent_vnfd["vnfd-id-ref"]:
-                        raise EngineException("Error at vld[id='{}']:vnfd-connection-point-ref[vnfd-id-ref='{}'] "
-                                              "does not match constituent-vnfd[member-vnf-index='{}']:vnfd-id-ref"
-                                              " '{}'".format(vld["id"], vnfd_cp["vnfd-id-ref"],
-                                                             constituent_vnfd["member-vnf-index"],
-                                                             constituent_vnfd["vnfd-id-ref"]),
-                                              http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-                    break
-            else:
-                raise EngineException("Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}'] "
-                                      "does not match any constituent-vnfd:member-vnf-index"
-                                      .format(vld["id"], vnfd_cp["member-vnf-index-ref"]),
-                                      http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+    def validate_vld_mgmt_network_with_virtual_link_protocol_data(vld, indata):
+        if not vld.get("mgmt-network"):
+            return
+        vld_id = vld.get("id")
+        for df in get_iterable(indata.get("df")):
+            for vlp in get_iterable(df.get("virtual-link-profile")):
+                if vld_id and vld_id == vlp.get("virtual-link-desc-id"):
+                    if vlp.get("virtual-link-protocol-data"):
+                        raise EngineException("Error at df[id='{}']:virtual-link-profile[id='{}']:virtual-link-"
+                                              "protocol-data You cannot set a virtual-link-protocol-data "
+                                              "when mgmt-network is True"
+                                              .format(df["id"], vlp["id"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
 
     @staticmethod
-    def validate_fgd_classifiers(fgd):
-        for cls in get_iterable(fgd.get("classifier")):
-            rspref = cls.get("rsp-id-ref")
-            for rsp in get_iterable(fgd.get("rsp")):
-                rspid = rsp.get("id")
-                if rspid and rspref and rspid == rspref:
-                    break
-            else:
-                raise EngineException(
-                    "Error at vnffgd[id='{}']:classifier[id='{}']:rsp-id-ref '{}' does not match any rsp:id"
-                    .format(fgd["id"], cls["id"], rspref),
-                    http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+    def validate_vnf_profiles_vnfd_id(indata):
+        all_vnfd_ids = set(get_iterable(indata.get("vnfd-id")))
+        for df in get_iterable(indata.get("df")):
+            for vnf_profile in get_iterable(df.get("vnf-profile")):
+                vnfd_id = vnf_profile.get("vnfd-id")
+                if vnfd_id and vnfd_id not in all_vnfd_ids:
+                    raise EngineException("Error at df[id='{}']:vnf_profile[id='{}']:vnfd-id='{}' "
+                                          "does not match any vnfd-id".format(df["id"], vnf_profile["id"], vnfd_id),
+                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
 
     def _validate_input_edit(self, indata, content, force=False):
         # not needed to validate with pyangbind becuase it will be validated at check_conflict_on_edit
@@ -923,42 +914,44 @@ class NsdTopic(DescriptorTopic):
         """
         if session["force"]:
             return
-        member_vnfd_index = self._get_descriptor_constituent_vnfds_by_member_vnfd_index(session, descriptor)
+        vnfds_index = self._get_descriptor_constituent_vnfds_index(session, descriptor)
 
         # Cross references validation in the descriptor and vnfd connection point validation
-        for vld in get_iterable(descriptor.get("vld")):
-            self.validate_vld_connection_point_refs_vnfd_connection_points(vld, member_vnfd_index)
-
-    def _get_descriptor_constituent_vnfds_by_member_vnfd_index(self, session, descriptor):
-        member_vnfd_index = {}
-        if descriptor.get("constituent-vnfd") and not session["force"]:
-            for vnf in descriptor["constituent-vnfd"]:
-                vnfd_id = vnf["vnfd-id-ref"]
+        for df in get_iterable(descriptor.get("df")):
+            self.validate_df_vnf_profiles_constituent_connection_points(df, vnfds_index)
+
+    def _get_descriptor_constituent_vnfds_index(self, session, descriptor):
+        vnfds_index = {}
+        if descriptor.get("vnfd-id") and not session["force"]:
+            for vnfd_id in get_iterable(descriptor.get("vnfd-id")):
                 query_filter = self._get_project_filter(session)
                 query_filter["id"] = vnfd_id
                 vnf_list = self.db.get_list("vnfds", query_filter)
                 if not vnf_list:
-                    raise EngineException("Descriptor error at 'constituent-vnfd':'vnfd-id-ref'='{}' references a non "
+                    raise EngineException("Descriptor error at 'vnfd-id'='{}' references a non "
                                           "existing vnfd".format(vnfd_id), http_code=HTTPStatus.CONFLICT)
-
-                member_vnfd_index[vnf["member-vnf-index"]] = vnf_list[0]
-        return member_vnfd_index
+                vnfds_index[vnfd_id] = vnf_list[0]
+        return vnfds_index
 
     @staticmethod
-    def validate_vld_connection_point_refs_vnfd_connection_points(vld, member_vnfd_index):
-        for referenced_vnfd_cp in get_iterable(vld.get("vnfd-connection-point-ref")):
-            # look if this vnfd contains this connection point
-            vnfd = member_vnfd_index.get(referenced_vnfd_cp["member-vnf-index-ref"])
-            for vnfd_cp in get_iterable(vnfd.get("connection-point")):
-                if referenced_vnfd_cp.get("vnfd-connection-point-ref") == vnfd_cp["name"]:
-                    break
-            else:
-                raise EngineException(
-                    "Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}']:vnfd-"
-                    "connection-point-ref='{}' references a non existing conection-point:name inside vnfd '{}'"
-                    .format(vld["id"], referenced_vnfd_cp["member-vnf-index-ref"],
-                            referenced_vnfd_cp["vnfd-connection-point-ref"], vnfd["id"]),
-                    http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+    def validate_df_vnf_profiles_constituent_connection_points(df, vnfds_index):
+        for vnf_profile in get_iterable(df.get("vnf-profile")):
+            vnfd = vnfds_index.get(vnf_profile["vnfd-id"])
+            all_vnfd_ext_cpds = set()
+            for ext_cpd in get_iterable(vnfd.get("ext-cpd")):
+                if ext_cpd.get('id'):
+                    all_vnfd_ext_cpds.add(ext_cpd.get('id'))
+
+            for virtual_link in get_iterable(vnf_profile.get("virtual-link-connectivity")):
+                for vl_cpd in get_iterable(virtual_link.get("constituent-cpd-id")):
+                    vl_cpd_id = vl_cpd.get('constituent-cpd-id')
+                    if vl_cpd_id and vl_cpd_id not in all_vnfd_ext_cpds:
+                        raise EngineException("Error at df[id='{}']:vnf-profile[id='{}']:virtual-link-connectivity"
+                                              "[virtual-link-profile-id='{}']:constituent-cpd-id='{}' references a "
+                                              "non existing ext-cpd:id inside vnfd '{}'"
+                                              .format(df["id"], vnf_profile["id"],
+                                                      virtual_link["virtual-link-profile-id"], vl_cpd_id, vnfd["id"]),
+                                              http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
 
     def check_conflict_on_edit(self, session, final_content, edit_content, _id):
         super().check_conflict_on_edit(session, final_content, edit_content, _id)
@@ -1015,6 +1008,18 @@ class NstTopic(DescriptorTopic):
     def __init__(self, db, fs, msg, auth):
         DescriptorTopic.__init__(self, db, fs, msg, auth)
 
+    def pyangbind_validation(self, item, data, force=False):
+        try:
+            mynst = nst_im()
+            pybindJSONDecoder.load_ietf_json({'nst': [data]}, None, None, obj=mynst,
+                                             path_helper=True, skip_unknown=force)
+            out = pybindJSON.dumps(mynst, mode="ietf")
+            desc_out = self._remove_envelop(yaml.safe_load(out))
+            return desc_out
+        except Exception as e:
+            raise EngineException("Error in pyangbind validation: {}".format(str(e)),
+                                  http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+
     @staticmethod
     def _remove_envelop(indata=None):
         if not indata:
index 0c45f8e..f9d63a8 100644 (file)
@@ -1,3 +1,16 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 -----BEGIN CERTIFICATE-----
 MIID6zCCAtOgAwIBAgIJAP5stGSyLGztMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
 VQQGEwJlczEPMA0GA1UECAwGTWFkcmlkMQ8wDQYDVQQHDAZNYWRyaWQxFzAVBgNV
index b4369e0..a6510e2 100644 (file)
@@ -1,3 +1,16 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 -----BEGIN RSA PRIVATE KEY-----
 MIIEogIBAAKCAQEA15Qu6L8mhTGYM+RnDLevBhPgj0m7amHTQu6fv8529X6viPzG
 RiYH4eHyAER4HbqC3mu8ADLyrohwQjG2LZrbv3ZnDwTzOPs6dfqkUkBSOdlQlW6P
index 43f2853..2613430 100644 (file)
@@ -21,11 +21,11 @@ from copy import copy, deepcopy
 from osm_nbi.validation import validate_input, ValidationError, ns_instantiate, ns_terminate, ns_action, ns_scale,\
     nsi_instantiate
 from osm_nbi.base_topic import BaseTopic, EngineException, get_iterable, deep_get, increment_ip_mac
-# from descriptor_topics import DescriptorTopic
 from yaml import safe_dump
 from osm_common.dbbase import DbException
 from osm_common.msgbase import MsgException
 from osm_common.fsbase import FsException
+from osm_nbi import utils
 from re import match  # For checking that additional parameter names are valid Jinja2 identifiers
 
 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
@@ -186,7 +186,11 @@ class NsrTopic(BaseTopic):
                     vdud = next(x for x in descriptor["vdu"] if x["id"] == vdu_id)
                     initial_primitives = deep_get(vdud, ("vdu-configuration", "initial-config-primitive"))
                 else:
-                    initial_primitives = deep_get(descriptor, ("vnf-configuration", "initial-config-primitive"))
+                    vnf_configurations = get_iterable(descriptor.get("vnf-configuration"))
+                    initial_primitives = []
+                    for vnfc in vnf_configurations:
+                        for primitive in get_iterable(vnfc.get("initial-config-primitive")):
+                            initial_primitives.append(primitive)
             else:
                 initial_primitives = deep_get(descriptor, ("ns-configuration", "initial-config-primitive"))
 
@@ -215,344 +219,51 @@ class NsrTopic(BaseTopic):
             EngineException, ValidationError, DbException, FsException, MsgException.
             Note: Exceptions are not captured on purpose. They should be captured at called
         """
-
         try:
             step = "checking quotas"
             self.check_quota(session)
 
             step = "validating input parameters"
             ns_request = self._remove_envelop(indata)
-            # Override descriptor with query string kwargs
             self._update_input_with_kwargs(ns_request, kwargs)
             self._validate_input_new(ns_request, session["force"])
 
-            # look for nsd
             step = "getting nsd id='{}' from database".format(ns_request.get("nsdId"))
-            _filter = self._get_project_filter(session)
-            _filter["_id"] = ns_request["nsdId"]
-            nsd = self.db.get_one("nsds", _filter)
-            del _filter["_id"]
+            nsd = self._get_nsd_from_db(ns_request["nsdId"], session)
+            ns_k8s_namespace = self._get_ns_k8s_namespace(nsd, ns_request, session)
 
-            # check NSD is not disabled
             step = "checking nsdOperationalState"
-            if nsd["_admin"]["operationalState"] == "DISABLED":
-                raise EngineException("nsd with id '{}' is DISABLED, and thus cannot be used to create "
-                                      "a network service".format(ns_request["nsdId"]), http_code=HTTPStatus.CONFLICT)
-
-            nsr_id = str(uuid4())
+            self._check_nsd_operational_state(nsd, ns_request)
 
-            now = time()
             step = "filling nsr from input data"
-            additional_params, _ = self._format_additional_params(ns_request, descriptor=nsd)
-
-            # use for k8s-namespace from ns_request or additionalParamsForNs. By default, the project_id
-            ns_k8s_namespace = session["project_id"][0] if session["project_id"] else None
-            if ns_request and ns_request.get("k8s-namespace"):
-                ns_k8s_namespace = ns_request["k8s-namespace"]
-            if additional_params and additional_params.get("k8s-namespace"):
-                ns_k8s_namespace = additional_params["k8s-namespace"]
-
-            nsr_descriptor = {
-                "name": ns_request["nsName"],
-                "name-ref": ns_request["nsName"],
-                "short-name": ns_request["nsName"],
-                "admin-status": "ENABLED",
-                "nsState": "NOT_INSTANTIATED",
-                "currentOperation": "IDLE",
-                "currentOperationID": None,
-                "errorDescription": None,
-                "errorDetail": None,
-                "deploymentStatus": None,
-                "configurationStatus": None,
-                "vcaStatus": None,
-                "nsd": {k: v for k, v in nsd.items() if k in ("vld", "_id", "id", "constituent-vnfd", "name",
-                                                              "ns-configuration")},
-                "datacenter": ns_request["vimAccountId"],
-                "resource-orchestrator": "osmopenmano",
-                "description": ns_request.get("nsDescription", ""),
-                "constituent-vnfr-ref": [],
-
-                "operational-status": "init",    # typedef ns-operational-
-                "config-status": "init",         # typedef config-states
-                "detailed-status": "scheduled",
-
-                "orchestration-progress": {},
-                # {"networks": {"active": 0, "total": 0}, "vms": {"active": 0, "total": 0}},
-
-                "create-time": now,
-                "nsd-name-ref": nsd["name"],
-                "operational-events": [],   # "id", "timestamp", "description", "event",
-                "nsd-ref": nsd["id"],
-                "nsd-id": nsd["_id"],
-                "vnfd-id": [],
-                "instantiate_params": self._format_ns_request(ns_request),
-                "additionalParamsForNs": additional_params,
-                "ns-instance-config-ref": nsr_id,
-                "id": nsr_id,
-                "_id": nsr_id,
-                # "input-parameter": xpath, value,
-                "ssh-authorized-key": ns_request.get("ssh_keys"),  # TODO remove
-                "vld": nsd.get("vld") or [],
-                "flavor": [],
-                "image": [],
-            }
-            ns_request["nsr_id"] = nsr_id
-            if ns_request and ns_request.get("config-units"):
-                nsr_descriptor["config-units"] = ns_request["config-units"]
-
-            # Create vld
-            if nsd.get("vld"):
-                nsr_descriptor["vld"] = nsd["vld"]
+            nsr_id = str(uuid4())
+            nsr_descriptor = self._create_nsr_descriptor_from_nsd(nsd, ns_request, nsr_id)
 
-            # Create VNFR
+            # Create VNFRs
             needed_vnfds = {}
-            for member_vnf in nsd.get("constituent-vnfd", ()):
-                vnfd_id = member_vnf["vnfd-id-ref"]
-                step = "getting vnfd id='{}' constituent-vnfd='{}' from database".format(
-                    member_vnf["vnfd-id-ref"], member_vnf["member-vnf-index"])
+            # TODO: Change for multiple df support
+            vnf_profiles = nsd.get("df", [[]])[0].get("vnf-profile", ())
+            for vnfp in vnf_profiles:
+                vnfd_id = vnfp.get("vnfd-id")
+                vnf_index = vnfp.get("id")
+                step = "getting vnfd id='{}' constituent-vnfd='{}' from database".format(vnfd_id, vnf_index)
                 if vnfd_id not in needed_vnfds:
-                    # Obtain vnfd
-                    _filter["id"] = vnfd_id
-                    vnfd = self.db.get_one("vnfds", _filter, fail_on_empty=True, fail_on_more=True)
-                    del _filter["id"]
-                    vnfd.pop("_admin")
+                    vnfd = self._get_vnfd_from_db(vnfd_id, session)
                     needed_vnfds[vnfd_id] = vnfd
                     nsr_descriptor["vnfd-id"].append(vnfd["_id"])
                 else:
                     vnfd = needed_vnfds[vnfd_id]
-                step = "filling vnfr  vnfd-id='{}' constituent-vnfd='{}'".format(
-                    member_vnf["vnfd-id-ref"], member_vnf["member-vnf-index"])
-                vnfr_id = str(uuid4())
-                additional_params, vnf_params = self._format_additional_params(ns_request,
-                                                                               member_vnf["member-vnf-index"],
-                                                                               descriptor=vnfd)
-                vnfr_descriptor = {
-                    "id": vnfr_id,
-                    "_id": vnfr_id,
-                    "nsr-id-ref": nsr_id,
-                    "member-vnf-index-ref": member_vnf["member-vnf-index"],
-                    "additionalParamsForVnf": additional_params,
-                    "created-time": now,
-                    # "vnfd": vnfd,        # at OSM model.but removed to avoid data duplication TODO: revise
-                    "vnfd-ref": vnfd_id,
-                    "vnfd-id": vnfd["_id"],    # not at OSM model, but useful
-                    "vim-account-id": None,
-                    "vdur": [],
-                    "connection-point": [],
-                    "ip-address": None,  # mgmt-interface filled by LCM
-                }
-                vnf_k8s_namespace = ns_k8s_namespace
-                if vnf_params:
-                    if vnf_params.get("k8s-namespace"):
-                        vnf_k8s_namespace = vnf_params["k8s-namespace"]
-                    if vnf_params.get("config-units"):
-                        vnfr_descriptor["config-units"] = vnf_params["config-units"]
-
-                # Create vld
-                if vnfd.get("internal-vld"):
-                    vnfr_descriptor["vld"] = []
-                    for vnfd_vld in vnfd.get("internal-vld"):
-                        vnfr_descriptor["vld"].append(
-                            {key: vnfd_vld[key] for key in ("id", "vim-network-name", "vim-network-id") if key in
-                             vnfd_vld})
-
-                vnfd_mgmt_cp = vnfd["mgmt-interface"].get("cp")
-                for cp in vnfd.get("connection-point", ()):
-                    vnf_cp = {
-                        "name": cp["name"],
-                        "connection-point-id": cp.get("id"),
-                        "id": cp.get("id"),
-                        # "ip-address", "mac-address" # filled by LCM
-                        # vim-id  # TODO it would be nice having a vim port id
-                    }
-                    vnfr_descriptor["connection-point"].append(vnf_cp)
-
-                # Create k8s-cluster information
-                if vnfd.get("k8s-cluster"):
-                    vnfr_descriptor["k8s-cluster"] = vnfd["k8s-cluster"]
-                    for net in get_iterable(vnfr_descriptor["k8s-cluster"].get("nets")):
-                        if net.get("external-connection-point-ref"):
-                            for nsd_vld in get_iterable(nsd.get("vld")):
-                                for nsd_vld_cp in get_iterable(nsd_vld.get("vnfd-connection-point-ref")):
-                                    if nsd_vld_cp.get("vnfd-connection-point-ref") == \
-                                            net["external-connection-point-ref"] and \
-                                            nsd_vld_cp.get("member-vnf-index-ref") == member_vnf["member-vnf-index"]:
-                                        net["ns-vld-id"] = nsd_vld["id"]
-                                        break
-                                else:
-                                    continue
-                                break
-                        elif net.get("internal-connection-point-ref"):
-                            for vnfd_ivld in get_iterable(vnfd.get("internal-vld")):
-                                for vnfd_ivld_icp in get_iterable(vnfd_ivld.get("internal-connection-point")):
-                                    if vnfd_ivld_icp.get("id-ref") == net["internal-connection-point-ref"]:
-                                        net["vnf-vld-id"] = vnfd_ivld["id"]
-                                        break
-                                else:
-                                    continue
-                                break
-                # update kdus
-                for kdu in get_iterable(vnfd.get("kdu")):
-                    additional_params, kdu_params = self._format_additional_params(ns_request,
-                                                                                   member_vnf["member-vnf-index"],
-                                                                                   kdu_name=kdu["name"],
-                                                                                   descriptor=vnfd)
-                    kdu_k8s_namespace = vnf_k8s_namespace
-                    kdu_model = kdu_params.get("kdu_model") if kdu_params else None
-                    if kdu_params and kdu_params.get("k8s-namespace"):
-                        kdu_k8s_namespace = kdu_params["k8s-namespace"]
-
-                    kdur = {
-                        "additionalParams": additional_params,
-                        "k8s-namespace": kdu_k8s_namespace,
-                        "kdu-name": kdu["name"],
-                        # TODO      "name": ""     Name of the VDU in the VIM
-                        "ip-address": None,  # mgmt-interface filled by LCM
-                        "k8s-cluster": {},
-                    }
-                    if kdu_params and kdu_params.get("config-units"):
-                        kdur["config-units"] = kdu_params["config-units"]
-                    if kdu.get("helm-version"):
-                        kdur["helm-version"] = kdu["helm-version"]
-                    for k8s_type in ("helm-chart", "juju-bundle"):
-                        if kdu.get(k8s_type):
-                            kdur[k8s_type] = kdu_model or kdu[k8s_type]
-                    if not vnfr_descriptor.get("kdur"):
-                        vnfr_descriptor["kdur"] = []
-                    vnfr_descriptor["kdur"].append(kdur)
 
-                for vdu in vnfd.get("vdu", ()):
-                    additional_params, vdu_params = self._format_additional_params(
-                        ns_request, member_vnf["member-vnf-index"], vdu_id=vdu["id"], descriptor=vnfd)
-                    vdur = {
-                        "vdu-id-ref": vdu["id"],
-                        # TODO      "name": ""     Name of the VDU in the VIM
-                        "ip-address": None,  # mgmt-interface filled by LCM
-                        # "vim-id", "flavor-id", "image-id", "management-ip" # filled by LCM
-                        "internal-connection-point": [],
-                        "interfaces": [],
-                        "additionalParams": additional_params,
-                        "vdu-name": vdu["name"],
-                    }
-                    if vdu_params and vdu_params.get("config-units"):
-                        vdur["config-units"] = vdu_params["config-units"]
-                    if deep_get(vdu, ("supplemental-boot-data", "boot-data-drive")):
-                        vdur["boot-data-drive"] = vdu["supplemental-boot-data"]["boot-data-drive"]
-                    if vdu.get("pdu-type"):
-                        vdur["pdu-type"] = vdu["pdu-type"]
-                        vdur["name"] = vdu["pdu-type"]
-
-                    # flavor
-                    flavor_data = copy(vdu.get("vm-flavor", {}))
-                    flavor_data["guest-epa"] = vdu.get("guest-epa")
-                    f = next((f for f in nsr_descriptor["flavor"] if
-                              all(f.get(k) == flavor_data[k] for k in flavor_data)), None)
-                    if not f:
-                        flavor_data["name"] = vdu["id"][:56] + "-flv"
-                        flavor_data["id"] = str(len(nsr_descriptor["flavor"]))
-                        nsr_descriptor["flavor"].append(flavor_data)
-                        f = flavor_data
-                    vdur["ns-flavor-id"] = f["id"]
-
-                    # image
-                    if vdu.get("image"):
-                        image_data = {"image": vdu["image"], "image_checksum": vdu.get("image_checksum")}
-                    img = next((f for f in nsr_descriptor["image"] if
-                                all(f.get(k) == image_data[k] for k in image_data)), None)
-                    if not img:
-                        image_data["id"] = str(len(nsr_descriptor["image"]))
-                        nsr_descriptor["image"].append(image_data)
-                        img = image_data
-                    vdur["ns-image-id"] = img["id"]
-
-                    # TODO volumes: name, volume-id
-                    for icp in vdu.get("internal-connection-point", ()):
-                        vdu_icp = {
-                            "id": icp["id"],
-                            "connection-point-id": icp["id"],
-                            "name": icp.get("name"),
-                        }
-                        vdur["internal-connection-point"].append(vdu_icp)
-                    for iface in vdu.get("interface", ()):
-                        vdu_iface = {
-                            x: iface[x] for x in ("name", "ip-address", "mac-address", "internal-connection-point-ref",
-                                                  "external-connection-point-ref") if iface.get(x) is not None}
-                        if iface.get("ip-address"):
-                            vdu_iface["fixed-ip"] = True
-                        if iface.get("mac-address"):
-                            vdu_iface["fixed-mac"] = True
-                        if vnfd_mgmt_cp and iface.get("external-connection-point-ref") == vnfd_mgmt_cp:
-                            vdu_iface["mgmt-vnf"] = True
-                        if iface.get("mgmt-interface"):
-                            vdu_iface["mgmt-interface"] = True  # TODO change to mgmt-vdu
-                        if iface.get("virtual-interface"):
-                            vdu_iface.update({x: iface["virtual-interface"][x] for x in ("type", "vcpi", "bandwidth")
-                                              if iface["virtual-interface"].get(x) is not None})
-
-                        # look for network where this interface is connected
-                        if iface.get("external-connection-point-ref"):
-                            for vld_index, nsd_vld in enumerate(get_iterable(nsd.get("vld"))):
-                                for nsd_vld_cp in get_iterable(nsd_vld.get("vnfd-connection-point-ref")):
-                                    if nsd_vld_cp.get("vnfd-connection-point-ref") == \
-                                            iface["external-connection-point-ref"] and \
-                                            nsd_vld_cp.get("member-vnf-index-ref") == member_vnf["member-vnf-index"]:
-                                        vdu_iface["ns-vld-id"] = nsd_vld["id"]
-                                        # if interface is of type PCI, mark connected vld as of type PCI
-                                        if vdu_iface.get("type") in ("SR-IOV", "PCI-PASSTHROUGH"):
-                                            nsr_descriptor["vld"][vld_index]["pci-interfaces"] = True
-                                        break
-                                else:
-                                    continue
-                                break
-                        elif iface.get("internal-connection-point-ref"):
-                            for vld_index, vnfd_ivld in enumerate(get_iterable(vnfd.get("internal-vld"))):
-                                for vnfd_ivld_icp in get_iterable(vnfd_ivld.get("internal-connection-point")):
-                                    if vnfd_ivld_icp.get("id-ref") == iface["internal-connection-point-ref"]:
-                                        vdu_iface["vnf-vld-id"] = vnfd_ivld["id"]
-                                        if vnfd_ivld_icp.get("ip-address"):
-                                            vdu_iface["ip-address"] = vnfd_ivld_icp["ip-address"]
-                                        # if interface is of type PCI, mark connected vld as of type PCI
-                                        if vdu_iface.get("type") in ("SR-IOV", "PCI-PASSTHROUGH"):
-                                            vnfr_descriptor["vld"][vld_index]["pci-interfaces"] = True
-                                        break
-                                else:
-                                    continue
-                                break
-                        if iface.get("position") is not None:
-                            vdur["interfaces"].insert(iface["position"], vdu_iface)
-                        else:
-                            vdur["interfaces"].append(vdu_iface)
-                    count = vdu.get("count", 1)
-                    if count is None:
-                        count = 1
-                    count = int(count)    # TODO remove when descriptor serialized with payngbind
-                    for index in range(0, count):
-                        if index:
-                            vdur = deepcopy(vdur)
-                            for iface in vdur["interfaces"]:
-                                if iface.get("ip-address"):
-                                    iface["ip-address"] = increment_ip_mac(iface["ip-address"])
-                                if iface.get("mac-address"):
-                                    iface["mac-address"] = increment_ip_mac(iface["mac-address"])
-
-                        vdur["_id"] = str(uuid4())
-                        vdur["count-index"] = index
-                        vdur["id"] = "{}-{}".format(vdur["vdu-id-ref"], index)
-                        vnfr_descriptor["vdur"].append(vdur)
-
-                step = "creating vnfr vnfd-id='{}' constituent-vnfd='{}' at database".format(
-                    member_vnf["vnfd-id-ref"], member_vnf["member-vnf-index"])
-
-                # add at database
-                self.format_on_new(vnfr_descriptor, session["project_id"], make_public=session["public"])
-                self.db.create("vnfrs", vnfr_descriptor)
-                rollback.append({"topic": "vnfrs", "_id": vnfr_id})
-                nsr_descriptor["constituent-vnfr-ref"].append(vnfr_id)
+                step = "filling vnfr  vnfd-id='{}' constituent-vnfd='{}'".format(vnfd_id, vnf_index)
+                vnfr_descriptor = self._create_vnfr_descriptor_from_vnfd(nsd, vnfd, vnfd_id, vnf_index, nsr_descriptor,
+                                                                         ns_request, ns_k8s_namespace)
+
+                step = "creating vnfr vnfd-id='{}' constituent-vnfd='{}' at database".format(vnfd_id, vnf_index)
+                self._add_vnfr_to_db(vnfr_descriptor, rollback, session)
+                nsr_descriptor["constituent-vnfr-ref"].append(vnfr_descriptor["id"])
 
             step = "creating nsr at database"
-            self.format_on_new(nsr_descriptor, session["project_id"], make_public=session["public"])
-            self.db.create("nsrs", nsr_descriptor)
-            rollback.append({"topic": "nsrs", "_id": nsr_id})
+            self._add_nsr_to_db(nsr_descriptor, rollback, session)
 
             step = "creating nsr temporal folder"
             self.fs.mkdir(nsr_id)
@@ -561,6 +272,369 @@ class NsrTopic(BaseTopic):
         except (ValidationError, EngineException, DbException, MsgException, FsException) as e:
             raise type(e)("{} while '{}'".format(e, step), http_code=e.http_code)
 
+    def _get_nsd_from_db(self, nsd_id, session):
+        _filter = self._get_project_filter(session)
+        _filter["_id"] = nsd_id
+        return self.db.get_one("nsds", _filter)
+
+    def _get_vnfd_from_db(self, vnfd_id, session):
+        _filter = self._get_project_filter(session)
+        _filter["id"] = vnfd_id
+        vnfd = self.db.get_one("vnfds", _filter, fail_on_empty=True, fail_on_more=True)
+        vnfd.pop("_admin")
+        return vnfd
+
+    def _add_nsr_to_db(self, nsr_descriptor, rollback, session):
+        self.format_on_new(nsr_descriptor, session["project_id"], make_public=session["public"])
+        self.db.create("nsrs", nsr_descriptor)
+        rollback.append({"topic": "nsrs", "_id": nsr_descriptor["id"]})
+
+    def _add_vnfr_to_db(self, vnfr_descriptor, rollback, session):
+        self.format_on_new(vnfr_descriptor, session["project_id"], make_public=session["public"])
+        self.db.create("vnfrs", vnfr_descriptor)
+        rollback.append({"topic": "vnfrs", "_id": vnfr_descriptor["id"]})
+
+    def _check_nsd_operational_state(self, nsd, ns_request):
+        if nsd["_admin"]["operationalState"] == "DISABLED":
+            raise EngineException("nsd with id '{}' is DISABLED, and thus cannot be used to create "
+                                  "a network service".format(ns_request["nsdId"]), http_code=HTTPStatus.CONFLICT)
+
+    def _get_ns_k8s_namespace(self, nsd, ns_request, session):
+        additional_params, _ = self._format_additional_params(ns_request, descriptor=nsd)
+        # use for k8s-namespace from ns_request or additionalParamsForNs. By default, the project_id
+        ns_k8s_namespace = session["project_id"][0] if session["project_id"] else None
+        if ns_request and ns_request.get("k8s-namespace"):
+            ns_k8s_namespace = ns_request["k8s-namespace"]
+        if additional_params and additional_params.get("k8s-namespace"):
+            ns_k8s_namespace = additional_params["k8s-namespace"]
+
+        return ns_k8s_namespace
+
+    def _create_nsr_descriptor_from_nsd(self, nsd, ns_request, nsr_id):
+        now = time()
+        additional_params, _ = self._format_additional_params(ns_request, descriptor=nsd)
+
+        nsr_descriptor = {
+            "name": ns_request["nsName"],
+            "name-ref": ns_request["nsName"],
+            "short-name": ns_request["nsName"],
+            "admin-status": "ENABLED",
+            "nsState": "NOT_INSTANTIATED",
+            "currentOperation": "IDLE",
+            "currentOperationID": None,
+            "errorDescription": None,
+            "errorDetail": None,
+            "deploymentStatus": None,
+            "configurationStatus": None,
+            "vcaStatus": None,
+            "nsd": {k: v for k, v in nsd.items()},
+            "datacenter": ns_request["vimAccountId"],
+            "resource-orchestrator": "osmopenmano",
+            "description": ns_request.get("nsDescription", ""),
+            "constituent-vnfr-ref": [],
+            "operational-status": "init",  # typedef ns-operational-
+            "config-status": "init",  # typedef config-states
+            "detailed-status": "scheduled",
+            "orchestration-progress": {},
+            "create-time": now,
+            "nsd-name-ref": nsd["name"],
+            "operational-events": [],  # "id", "timestamp", "description", "event",
+            "nsd-ref": nsd["id"],
+            "nsd-id": nsd["_id"],
+            "vnfd-id": [],
+            "instantiate_params": self._format_ns_request(ns_request),
+            "additionalParamsForNs": additional_params,
+            "ns-instance-config-ref": nsr_id,
+            "id": nsr_id,
+            "_id": nsr_id,
+            "ssh-authorized-key": ns_request.get("ssh_keys"),  # TODO remove
+            "flavor": [],
+            "image": [],
+        }
+        ns_request["nsr_id"] = nsr_id
+        if ns_request and ns_request.get("config-units"):
+            nsr_descriptor["config-units"] = ns_request["config-units"]
+
+        # Create vld
+        if nsd.get("virtual-link-desc"):
+            nsr_vld = deepcopy(nsd.get("virtual-link-desc", []))
+            # Fill each vld with vnfd-connection-point-ref data
+            # TODO: Change for multiple df support
+            all_vld_connection_point_data = {vld.get("id"): [] for vld in nsr_vld}
+            vnf_profiles = nsd.get("df", [[]])[0].get("vnf-profile", ())
+            for vnf_profile in vnf_profiles:
+                for vlc in vnf_profile.get("virtual-link-connectivity", ()):
+                    for cpd in vlc.get("constituent-cpd-id", ()):
+                        all_vld_connection_point_data[vlc.get("virtual-link-profile-id")].append({
+                            "member-vnf-index-ref": cpd.get("constituent-base-element-id"),
+                            "vnfd-connection-point-ref": cpd.get("constituent-cpd-id"),
+                            "vnfd-id-ref": vnf_profile.get("vnfd-id")
+                        })
+
+                vnfd = self.db.get_one("vnfds",
+                                       {"id": vnf_profile.get("vnfd-id")},
+                                       fail_on_empty=True,
+                                       fail_on_more=True)
+
+                for vdu in vnfd.get("vdu", ()):
+                    flavor_data = {}
+                    guest_epa = {}
+                    # Find this vdu compute and storage descriptors
+                    vdu_virtual_compute = {}
+                    vdu_virtual_storage = {}
+                    for vcd in vnfd.get("virtual-compute-desc", ()):
+                        if vcd.get("id") == vdu.get("virtual-compute-desc"):
+                            vdu_virtual_compute = vcd
+                    for vsd in vnfd.get("virtual-storage-desc", ()):
+                        if vsd.get("id") == vdu.get("virtual-storage-desc", [[]])[0]:
+                            vdu_virtual_storage = vsd
+                    # Get this vdu vcpus, memory and storage info for flavor_data
+                    if vdu_virtual_compute.get("virtual-cpu", {}).get("num-virtual-cpu"):
+                        flavor_data["vcpu-count"] = vdu_virtual_compute["virtual-cpu"]["num-virtual-cpu"]
+                    if vdu_virtual_compute.get("virtual-memory", {}).get("size"):
+                        flavor_data["memory-mb"] = float(vdu_virtual_compute["virtual-memory"]["size"]) * 1024.0
+                    if vdu_virtual_storage.get("size-of-storage"):
+                        flavor_data["storage-gb"] = vdu_virtual_storage["size-of-storage"]
+                    # Get this vdu EPA info for guest_epa
+                    if vdu_virtual_compute.get("virtual-cpu", {}).get("cpu-quota"):
+                        guest_epa["cpu-quota"] = vdu_virtual_compute["virtual-cpu"]["cpu-quota"]
+                    if vdu_virtual_compute.get("virtual-cpu", {}).get("pinning"):
+                        vcpu_pinning = vdu_virtual_compute["virtual-cpu"]["pinning"]
+                        if vcpu_pinning.get("thread-policy"):
+                            guest_epa["cpu-thread-pinning-policy"] = vcpu_pinning["thread-policy"]
+                        if vcpu_pinning.get("policy"):
+                            cpu_policy = "SHARED" if vcpu_pinning["policy"] == "dynamic" else "DEDICATED"
+                            guest_epa["cpu-pinning-policy"] = cpu_policy
+                    if vdu_virtual_compute.get("virtual-memory", {}).get("mem-quota"):
+                        guest_epa["mem-quota"] = vdu_virtual_compute["virtual-memory"]["mem-quota"]
+                    if vdu_virtual_compute.get("virtual-memory", {}).get("mempage-size"):
+                        guest_epa["mempage-size"] = vdu_virtual_compute["virtual-memory"]["mempage-size"]
+                    if vdu_virtual_compute.get("virtual-memory", {}).get("numa-node-policy"):
+                        guest_epa["numa-node-policy"] = vdu_virtual_compute["virtual-memory"]["numa-node-policy"]
+                    if vdu_virtual_storage.get("disk-io-quota"):
+                        guest_epa["disk-io-quota"] = vdu_virtual_storage["disk-io-quota"]
+
+                    if guest_epa:
+                        flavor_data["guest-epa"] = guest_epa
+
+                    flavor_data["name"] = vdu["id"][:56] + "-flv"
+                    flavor_data["id"] = str(len(nsr_descriptor["flavor"]))
+                    nsr_descriptor["flavor"].append(flavor_data)
+
+                    sw_image_id = vdu.get("sw-image-desc")
+                    if sw_image_id:
+                        sw_image_desc = utils.find_in_list(vnfd.get("sw-image-desc", ()),
+                                                           lambda sw: sw["id"] == sw_image_id)
+                        image_data = {}
+                        if sw_image_desc.get("image"):
+                            image_data["image"] = sw_image_desc["image"]
+                        if sw_image_desc.get("checksum"):
+                            image_data["image_checksum"] = sw_image_desc["checksum"]["hash"]
+                    img = next((f for f in nsr_descriptor["image"] if
+                                all(f.get(k) == image_data[k] for k in image_data)), None)
+                    if not img:
+                        image_data["id"] = str(len(nsr_descriptor["image"]))
+                        nsr_descriptor["image"].append(image_data)
+
+            for vld in nsr_vld:
+                vld["vnfd-connection-point-ref"] = all_vld_connection_point_data.get(vld.get("id"), [])
+                vld["name"] = vld["id"]
+            nsr_descriptor["vld"] = nsr_vld
+
+        return nsr_descriptor
+
+    def _create_vnfr_descriptor_from_vnfd(self, nsd, vnfd, vnfd_id, vnf_index, nsr_descriptor,
+                                          ns_request, ns_k8s_namespace):
+        vnfr_id = str(uuid4())
+        nsr_id = nsr_descriptor["id"]
+        now = time()
+        additional_params, vnf_params = self._format_additional_params(ns_request, vnf_index, descriptor=vnfd)
+
+        vnfr_descriptor = {
+            "id": vnfr_id,
+            "_id": vnfr_id,
+            "nsr-id-ref": nsr_id,
+            "member-vnf-index-ref": vnf_index,
+            "additionalParamsForVnf": additional_params,
+            "created-time": now,
+            # "vnfd": vnfd,        # at OSM model.but removed to avoid data duplication TODO: revise
+            "vnfd-ref": vnfd_id,
+            "vnfd-id": vnfd["_id"],  # not at OSM model, but useful
+            "vim-account-id": None,
+            "vdur": [],
+            "connection-point": [],
+            "ip-address": None,  # mgmt-interface filled by LCM
+        }
+        vnf_k8s_namespace = ns_k8s_namespace
+        if vnf_params:
+            if vnf_params.get("k8s-namespace"):
+                vnf_k8s_namespace = vnf_params["k8s-namespace"]
+            if vnf_params.get("config-units"):
+                vnfr_descriptor["config-units"] = vnf_params["config-units"]
+
+        # Create vld
+        if vnfd.get("int-virtual-link-desc"):
+            vnfr_descriptor["vld"] = []
+            for vnfd_vld in vnfd.get("int-virtual-link-desc"):
+                vnfr_descriptor["vld"].append({key: vnfd_vld[key] for key in vnfd_vld})
+
+        for cp in vnfd.get("ext-cpd", ()):
+            vnf_cp = {
+                "name": cp.get("id"),
+                "connection-point-id": cp.get("int-cpd").get("cpd"),
+                "connection-point-vdu-id": cp.get("int-cpd").get("vdu-id"),
+                "id": cp.get("id"),
+                # "ip-address", "mac-address" # filled by LCM
+                # vim-id  # TODO it would be nice having a vim port id
+            }
+            vnfr_descriptor["connection-point"].append(vnf_cp)
+
+        # Create k8s-cluster information
+        # TODO: Validate if a k8s-cluster net can have more than one ext-cpd ?
+        if vnfd.get("k8s-cluster"):
+            vnfr_descriptor["k8s-cluster"] = vnfd["k8s-cluster"]
+            all_k8s_cluster_nets_cpds = {}
+            for cpd in get_iterable(vnfd.get("ext-cpd")):
+                if cpd.get("k8s-cluster-net"):
+                    all_k8s_cluster_nets_cpds[cpd.get("k8s-cluster-net")] = cpd.get("id")
+            for net in get_iterable(vnfr_descriptor["k8s-cluster"].get("nets")):
+                if net.get("id") in all_k8s_cluster_nets_cpds:
+                    net["external-connection-point-ref"] = all_k8s_cluster_nets_cpds[net.get("id")]
+
+        # update kdus
+        # TODO: Change for multiple df support
+        all_kdu_profiles = vnfd.get("df", [[]])[0].get("kdu-profile", ())
+        all_kdu_profiles_models = {profile.get("name"): profile.get("kdu-model-id") for profile in all_kdu_profiles}
+        all_kdu_models = vnfd.get("kdu-model", ())
+        all_kdu_models = {model.get("id"): model for model in all_kdu_models}
+        for kdu in get_iterable(vnfd.get("kdu")):
+            additional_params, kdu_params = self._format_additional_params(ns_request,
+                                                                           vnf_index,
+                                                                           kdu_name=kdu["name"],
+                                                                           descriptor=vnfd)
+            kdu_k8s_namespace = vnf_k8s_namespace
+            kdu_model = kdu_params.get("kdu_model") if kdu_params else None
+            if kdu_params and kdu_params.get("k8s-namespace"):
+                kdu_k8s_namespace = kdu_params["k8s-namespace"]
+
+            kdur = {
+                "additionalParams": additional_params,
+                "k8s-namespace": kdu_k8s_namespace,
+                "kdu-name": kdu.get("name"),
+                # TODO      "name": ""     Name of the VDU in the VIM
+                "ip-address": None,  # mgmt-interface filled by LCM
+                "k8s-cluster": {},
+            }
+            if kdu_params and kdu_params.get("config-units"):
+                kdur["config-units"] = kdu_params["config-units"]
+
+            kdu_model_data = all_kdu_models[all_kdu_profiles_models[kdur["name"]]]
+            kdur[kdu_model_data.get("kdu-model-type")] = kdu_model or kdu_model_data
+            if not vnfr_descriptor.get("kdur"):
+                vnfr_descriptor["kdur"] = []
+            vnfr_descriptor["kdur"].append(kdur)
+
+        vnfd_mgmt_cp = vnfd.get("mgmt-cp")
+        for vdu in vnfd.get("vdu", ()):
+            additional_params, vdu_params = self._format_additional_params(
+                ns_request, vnf_index, vdu_id=vdu["id"], descriptor=vnfd)
+            vdur = {
+                "vdu-id-ref": vdu["id"],
+                # TODO      "name": ""     Name of the VDU in the VIM
+                "ip-address": None,  # mgmt-interface filled by LCM
+                # "vim-id", "flavor-id", "image-id", "management-ip" # filled by LCM
+                "internal-connection-point": [],
+                "interfaces": [],
+                "additionalParams": additional_params,
+                "vdu-name": vdu["name"]
+            }
+            if vdu_params and vdu_params.get("config-units"):
+                vdur["config-units"] = vdu_params["config-units"]
+            if deep_get(vdu, ("supplemental-boot-data", "boot-data-drive")):
+                vdur["boot-data-drive"] = vdu["supplemental-boot-data"]["boot-data-drive"]
+            if vdu.get("pdu-type"):
+                vdur["pdu-type"] = vdu["pdu-type"]
+                vdur["name"] = vdu["pdu-type"]
+            # TODO volumes: name, volume-id
+            for icp in vdu.get("int-cpd", ()):
+                vdu_icp = {
+                    "id": icp["id"],
+                    "connection-point-id": icp["id"],
+                    "name": icp.get("id"),
+                }
+                vdur["internal-connection-point"].append(vdu_icp)
+
+                for iface in icp.get("virtual-network-interface-requirement", ()):
+                    iface_fields = ("name", "mac-address")
+                    vdu_iface = {x: iface[x] for x in iface_fields if iface.get(x) is not None}
+
+                    vdu_iface["internal-connection-point-ref"] = vdu_icp["id"]
+                    for ext_cp in vnfd.get("ext-cpd", ()):
+                        if not ext_cp.get("int-cpd"):
+                            continue
+                        if ext_cp["int-cpd"].get("vdu-id") != vdu["id"]:
+                            continue
+                        if icp["id"] == ext_cp["int-cpd"].get("cpd"):
+                            vdu_iface["external-connection-point-ref"] = ext_cp.get("id")
+                            break
+
+                    if vnfd_mgmt_cp and vdu_iface.get("external-connection-point-ref") == vnfd_mgmt_cp:
+                        vdu_iface["mgmt-vnf"] = True
+                        vdu_iface["mgmt-interface"] = True  # TODO change to mgmt-vdu
+
+                    if iface.get("virtual-interface"):
+                        vdu_iface.update(deepcopy(iface["virtual-interface"]))
+
+                    # look for network where this interface is connected
+                    iface_ext_cp = vdu_iface.get("external-connection-point-ref")
+                    if iface_ext_cp:
+                        # TODO: Change for multiple df support
+                        for df in get_iterable(nsd.get("df")):
+                            for vnf_profile in get_iterable(df.get("vnf-profile")):
+                                for vlc in get_iterable(vnf_profile.get("virtual-link-connectivity")):
+                                    for cpd in get_iterable(vlc.get("constituent-cpd-id")):
+                                        if cpd.get("constituent-cpd-id") == iface_ext_cp:
+                                            vdu_iface["ns-vld-id"] = vlc.get("virtual-link-profile-id")
+                                            break
+                    elif vdu_iface.get("internal-connection-point-ref"):
+                        vdu_iface["vnf-vld-id"] = icp.get("int-virtual-link-desc")
+
+                    vdur["interfaces"].append(vdu_iface)
+
+            if vdu.get("sw-image-desc"):
+                sw_image = utils.find_in_list(
+                    vnfd.get("sw-image-desc", ()),
+                    lambda image: image["id"] == vdu.get("sw-image-desc"))
+                nsr_sw_image_data = utils.find_in_list(
+                    nsr_descriptor["image"],
+                    lambda nsr_image: (nsr_image.get("image") == sw_image.get("image"))
+                )
+                vdur["ns-image-id"] = nsr_sw_image_data["id"]
+
+            flavor_data_name = vdu["id"][:56] + "-flv"
+            nsr_flavor_desc = utils.find_in_list(
+                nsr_descriptor["flavor"],
+                lambda flavor: flavor["name"] == flavor_data_name)
+
+            if nsr_flavor_desc:
+                vdur["ns-flavor-id"] = nsr_flavor_desc["id"]
+
+            count = int(vdu.get("count", 1))
+            for index in range(0, count):
+                vdur = deepcopy(vdur)
+                for iface in vdur["interfaces"]:
+                    if iface.get("ip-address"):
+                        iface["ip-address"] = increment_ip_mac(iface["ip-address"])
+                    if iface.get("mac-address"):
+                        iface["mac-address"] = increment_ip_mac(iface["mac-address"])
+
+                vdur["_id"] = str(uuid4())
+                vdur["id"] = vdur["_id"]
+                vdur["count-index"] = index
+                vnfr_descriptor["vdur"].append(vdur)
+
+        return vnfr_descriptor
+
     def edit(self, session, _id, indata=None, kwargs=None, content=None):
         raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
 
@@ -604,185 +678,208 @@ class NsLcmOpTopic(BaseTopic):
         :param indata: descriptor with the parameters of the operation
         :return: None
         """
+        if operation == "action":
+            self._check_action_ns_operation(indata, nsr)
+        elif operation == "scale":
+            self._check_scale_ns_operation(indata, nsr)
+        elif operation == "instantiate":
+            self._check_instantiate_ns_operation(indata, nsr, session)
+
+    def _check_action_ns_operation(self, indata, nsr):
+        nsd = nsr["nsd"]
+        # check vnf_member_index
+        if indata.get("vnf_member_index"):
+            indata["member_vnf_index"] = indata.pop("vnf_member_index")  # for backward compatibility
+        if indata.get("member_vnf_index"):
+            vnfd = self._get_vnfd_from_vnf_member_index(indata["member_vnf_index"], nsr["_id"])
+            if indata.get("vdu_id"):
+                self._check_valid_vdu(vnfd, indata["vdu_id"])
+                # TODO: Change the [0] as vdu-configuration is now a list
+                descriptor_configuration = vnfd.get("vdu-configuration", [{}])[0].get("config-primitive")
+            elif indata.get("kdu_name"):
+                self._check_valid_kdu(vnfd, indata["kdu_name"])
+                # TODO: Change the [0] as kdu-configuration is now a list
+                descriptor_configuration = vnfd.get("kdu-configuration", [{}])[0].get("config-primitive")
+            else:
+                # TODO: Change the [0] as vnf-configuration is now a list
+                descriptor_configuration = vnfd.get("vnf-configuration", [{}])[0].get("config-primitive")
+        else:  # use a NSD
+            descriptor_configuration = nsd.get("ns-configuration", {}).get("config-primitive")
+
+        # For k8s allows default primitives without validating the parameters
+        if indata.get("kdu_name") and indata["primitive"] in ("upgrade", "rollback", "status", "inspect", "readme"):
+            # TODO should be checked that rollback only can contains revsision_numbe????
+            if not indata.get("member_vnf_index"):
+                raise EngineException("Missing action parameter 'member_vnf_index' for default KDU primitive '{}'"
+                                      .format(indata["primitive"]))
+            return
+        # if not, check primitive
+        for config_primitive in get_iterable(descriptor_configuration):
+            if indata["primitive"] == config_primitive["name"]:
+                # check needed primitive_params are provided
+                if indata.get("primitive_params"):
+                    in_primitive_params_copy = copy(indata["primitive_params"])
+                else:
+                    in_primitive_params_copy = {}
+                for paramd in get_iterable(config_primitive.get("parameter")):
+                    if paramd["name"] in in_primitive_params_copy:
+                        del in_primitive_params_copy[paramd["name"]]
+                    elif not paramd.get("default-value"):
+                        raise EngineException("Needed parameter {} not provided for primitive '{}'".format(
+                            paramd["name"], indata["primitive"]))
+                # check no extra primitive params are provided
+                if in_primitive_params_copy:
+                    raise EngineException("parameter/s '{}' not present at vnfd /nsd for primitive '{}'".format(
+                        list(in_primitive_params_copy.keys()), indata["primitive"]))
+                break
+        else:
+            raise EngineException("Invalid primitive '{}' is not present at vnfd/nsd".format(indata["primitive"]))
+
+    def _check_scale_ns_operation(self, indata, nsr):
+        vnfd = self._get_vnfd_from_vnf_member_index(indata["scaleVnfData"]["scaleByStepData"]["member-vnf-index"],
+                                                    nsr["_id"])
+        for scaling_group in get_iterable(vnfd.get("scaling-group-descriptor")):
+            if indata["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"] == scaling_group["name"]:
+                break
+        else:
+            raise EngineException("Invalid scaleVnfData:scaleByStepData:scaling-group-descriptor '{}' is not "
+                                  "present at vnfd:scaling-group-descriptor"
+                                  .format(indata["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"]))
+
+    def _check_instantiate_ns_operation(self, indata, nsr, session):
         vnf_member_index_to_vnfd = {}  # map between vnf_member_index to vnf descriptor.
         vim_accounts = []
         wim_accounts = []
         nsd = nsr["nsd"]
-
-        def check_valid_vnf_member_index(member_vnf_index):
-            # Obtain vnf descriptor. The vnfr is used to get the vnfd._id used for this member_vnf_index
+        self._check_valid_vim_account(indata["vimAccountId"], vim_accounts, session)
+        self._check_valid_wim_account(indata.get("wimAccountId"), wim_accounts, session)
+        for in_vnf in get_iterable(indata.get("vnf")):
+            member_vnf_index = in_vnf["member-vnf-index"]
             if vnf_member_index_to_vnfd.get(member_vnf_index):
-                return vnf_member_index_to_vnfd[member_vnf_index]
-            vnfr = self.db.get_one("vnfrs",
-                                   {"nsr-id-ref": nsr["_id"], "member-vnf-index-ref": member_vnf_index},
-                                   fail_on_empty=False)
-            if not vnfr:
-                raise EngineException("Invalid parameter member_vnf_index='{}' is not one of the "
-                                      "nsd:constituent-vnfd".format(member_vnf_index))
-            vnfd = self.db.get_one("vnfds", {"_id": vnfr["vnfd-id"]}, fail_on_empty=False)
-            if not vnfd:
-                raise EngineException("vnfd id={} has been deleted!. Operation cannot be performed".
-                                      format(vnfr["vnfd-id"]))
-            vnf_member_index_to_vnfd[member_vnf_index] = vnfd  # add to cache, avoiding a later look for
-            return vnfd
-
-        def check_valid_vdu(vnfd, vdu_id):
-            for vdud in get_iterable(vnfd.get("vdu")):
-                if vdud["id"] == vdu_id:
-                    return vdud
+                vnfd = vnf_member_index_to_vnfd[member_vnf_index]
             else:
-                raise EngineException("Invalid parameter vdu_id='{}' not present at vnfd:vdu:id".format(vdu_id))
-
-        def check_valid_kdu(vnfd, kdu_name):
-            for kdud in get_iterable(vnfd.get("kdu")):
-                if kdud["name"] == kdu_name:
-                    return kdud
+                vnfd = self._get_vnfd_from_vnf_member_index(member_vnf_index, nsr["_id"])
+                vnf_member_index_to_vnfd[member_vnf_index] = vnfd  # add to cache, avoiding a later look for
+            self._check_vnf_instantiation_params(in_vnf, vnfd)
+            if in_vnf.get("vimAccountId"):
+                self._check_valid_vim_account(in_vnf["vimAccountId"], vim_accounts, session)
+
+        for in_vld in get_iterable(indata.get("vld")):
+            self._check_valid_wim_account(in_vld.get("wimAccountId"), wim_accounts, session)
+            for vldd in get_iterable(nsd.get("virtual-link-desc")):
+                if in_vld["name"] == vldd["id"]:
+                    break
             else:
-                raise EngineException("Invalid parameter kdu_name='{}' not present at vnfd:kdu:name".format(kdu_name))
+                raise EngineException("Invalid parameter vld:name='{}' is not present at nsd:vld".format(
+                    in_vld["name"]))
+
+    def _get_vnfd_from_vnf_member_index(self, member_vnf_index, nsr_id):
+        # Obtain vnf descriptor. The vnfr is used to get the vnfd._id used for this member_vnf_index
+        vnfr = self.db.get_one("vnfrs",
+                               {"nsr-id-ref": nsr_id, "member-vnf-index-ref": member_vnf_index},
+                               fail_on_empty=False)
+        if not vnfr:
+            raise EngineException("Invalid parameter member_vnf_index='{}' is not one of the "
+                                  "nsd:constituent-vnfd".format(member_vnf_index))
+        vnfd = self.db.get_one("vnfds", {"_id": vnfr["vnfd-id"]}, fail_on_empty=False)
+        if not vnfd:
+            raise EngineException("vnfd id={} has been deleted!. Operation cannot be performed".
+                                  format(vnfr["vnfd-id"]))
+        return vnfd
+
+    def _check_valid_vdu(self, vnfd, vdu_id):
+        for vdud in get_iterable(vnfd.get("vdu")):
+            if vdud["id"] == vdu_id:
+                return vdud
+        else:
+            raise EngineException("Invalid parameter vdu_id='{}' not present at vnfd:vdu:id".format(vdu_id))
 
-        def _check_vnf_instantiation_params(in_vnfd, vnfd):
+    def _check_valid_kdu(self, vnfd, kdu_name):
+        for kdud in get_iterable(vnfd.get("kdu")):
+            if kdud["name"] == kdu_name:
+                return kdud
+        else:
+            raise EngineException("Invalid parameter kdu_name='{}' not present at vnfd:kdu:name".format(kdu_name))
+
+    def _check_vnf_instantiation_params(self, in_vnf, vnfd):
+        for in_vdu in get_iterable(in_vnf.get("vdu")):
+            for vdu in get_iterable(vnfd.get("vdu")):
+                if in_vdu["id"] == vdu["id"]:
+                    for volume in get_iterable(in_vdu.get("volume")):
+                        for volumed in get_iterable(vdu.get("virtual-storage-desc")):
+                            if volumed["id"] == volume["name"]:
+                                break
+                        else:
+                            raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}']:"
+                                                  "volume:name='{}' is not present at "
+                                                  "vnfd:vdu:virtual-storage-desc list".
+                                                  format(in_vnf["member-vnf-index"], in_vdu["id"],
+                                                         volume["id"]))
+
+                    vdu_if_names = set()
+                    for cpd in get_iterable(vdu.get("int-cpd")):
+                        for iface in get_iterable(cpd.get("virtual-network-interface-requirement")):
+                            vdu_if_names.add(iface.get("name"))
+
+                    for in_iface in get_iterable(in_vdu["interface"]):
+                        if in_iface["name"] in vdu_if_names:
+                            break
+                        else:
+                            raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}']:"
+                                                  "int-cpd[id='{}'] is not present at vnfd:vdu:int-cpd"
+                                                  .format(in_vnf["member-vnf-index"], in_vdu["id"],
+                                                          in_iface["name"]))
+                    break
 
-            for in_vdu in get_iterable(in_vnfd.get("vdu")):
-                for vdu in get_iterable(vnfd.get("vdu")):
-                    if in_vdu["id"] == vdu["id"]:
-                        for volume in get_iterable(in_vdu.get("volume")):
-                            for volumed in get_iterable(vdu.get("volumes")):
-                                if volumed["name"] == volume["name"]:
-                                    break
-                            else:
-                                raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}']:"
-                                                      "volume:name='{}' is not present at vnfd:vdu:volumes list".
-                                                      format(in_vnf["member-vnf-index"], in_vdu["id"],
-                                                             volume["name"]))
-                        for in_iface in get_iterable(in_vdu["interface"]):
-                            for iface in get_iterable(vdu.get("interface")):
-                                if in_iface["name"] == iface["name"]:
-                                    break
-                            else:
-                                raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}']:"
-                                                      "interface[name='{}'] is not present at vnfd:vdu:interface"
-                                                      .format(in_vnf["member-vnf-index"], in_vdu["id"],
-                                                              in_iface["name"]))
-                        break
-                else:
-                    raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}'] is is not present "
-                                          "at vnfd:vdu".format(in_vnf["member-vnf-index"], in_vdu["id"]))
-
-            for in_ivld in get_iterable(in_vnfd.get("internal-vld")):
-                for ivld in get_iterable(vnfd.get("internal-vld")):
-                    if in_ivld["name"] in (ivld["id"], ivld.get("name")):
-                        for in_icp in get_iterable(in_ivld.get("internal-connection-point")):
-                            for icp in ivld["internal-connection-point"]:
-                                if in_icp["id-ref"] == icp["id-ref"]:
-                                    break
-                            else:
-                                raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:internal-vld[name"
-                                                      "='{}']:internal-connection-point[id-ref:'{}'] is not present at "
-                                                      "vnfd:internal-vld:name/id:internal-connection-point"
-                                                      .format(in_vnf["member-vnf-index"], in_ivld["name"],
-                                                              in_icp["id-ref"]))
+            else:
+                raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}'] is not present "
+                                      "at vnfd:vdu".format(in_vnf["member-vnf-index"], in_vdu["id"]))
+
+        vnfd_ivlds_cpds = {ivld.get("id"): set() for ivld in get_iterable(vnfd.get("int-virtual-link-desc"))}
+        for vdu in get_iterable(vnfd.get("vdu")):
+            for cpd in get_iterable(vnfd.get("int-cpd")):
+                if cpd.get("int-virtual-link-desc"):
+                    vnfd_ivlds_cpds[cpd.get("int-virtual-link-desc")] = cpd.get("id")
+
+        for in_ivld in get_iterable(in_vnf.get("internal-vld")):
+            if in_ivld.get("name") in vnfd_ivlds_cpds:
+                for in_icp in get_iterable(in_ivld.get("internal-connection-point")):
+                    if in_icp["id-ref"] in vnfd_ivlds_cpds[in_ivld.get("name")]:
                         break
-                else:
-                    raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:internal-vld:name='{}'"
-                                          " is not present at vnfd '{}'".format(in_vnf["member-vnf-index"],
-                                                                                in_ivld["name"], vnfd["id"]))
-
-        def check_valid_vim_account(vim_account):
-            if vim_account in vim_accounts:
-                return
-            try:
-                db_filter = self._get_project_filter(session)
-                db_filter["_id"] = vim_account
-                self.db.get_one("vim_accounts", db_filter)
-            except Exception:
-                raise EngineException("Invalid vimAccountId='{}' not present for the project".format(vim_account))
-            vim_accounts.append(vim_account)
-
-        def check_valid_wim_account(wim_account):
-            if not isinstance(wim_account, str):
-                return
-            elif wim_account in wim_accounts:
-                return
-            try:
-                db_filter = self._get_project_filter(session, write=False, show_all=True)
-                db_filter["_id"] = wim_account
-                self.db.get_one("wim_accounts", db_filter)
-            except Exception:
-                raise EngineException("Invalid wimAccountId='{}' not present for the project".format(wim_account))
-            wim_accounts.append(wim_account)
-
-        if operation == "action":
-            # check vnf_member_index
-            if indata.get("vnf_member_index"):
-                indata["member_vnf_index"] = indata.pop("vnf_member_index")    # for backward compatibility
-            if indata.get("member_vnf_index"):
-                vnfd = check_valid_vnf_member_index(indata["member_vnf_index"])
-                if indata.get("vdu_id"):
-                    vdud = check_valid_vdu(vnfd, indata["vdu_id"])
-                    descriptor_configuration = vdud.get("vdu-configuration", {}).get("config-primitive")
-                elif indata.get("kdu_name"):
-                    kdud = check_valid_kdu(vnfd, indata["kdu_name"])
-                    descriptor_configuration = kdud.get("kdu-configuration", {}).get("config-primitive")
-                else:
-                    descriptor_configuration = vnfd.get("vnf-configuration", {}).get("config-primitive")
-            else:  # use a NSD
-                descriptor_configuration = nsd.get("ns-configuration", {}).get("config-primitive")
-
-            # For k8s allows default primitives without validating the parameters
-            if indata.get("kdu_name") and indata["primitive"] in ("upgrade", "rollback", "status", "inspect", "readme"):
-                # TODO should be checked that rollback only can contains revsision_numbe????
-                if not indata.get("member_vnf_index"):
-                    raise EngineException("Missing action parameter 'member_vnf_index' for default KDU primitive '{}'"
-                                          .format(indata["primitive"]))
-                return
-            # if not, check primitive
-            for config_primitive in get_iterable(descriptor_configuration):
-                if indata["primitive"] == config_primitive["name"]:
-                    # check needed primitive_params are provided
-                    if indata.get("primitive_params"):
-                        in_primitive_params_copy = copy(indata["primitive_params"])
                     else:
-                        in_primitive_params_copy = {}
-                    for paramd in get_iterable(config_primitive.get("parameter")):
-                        if paramd["name"] in in_primitive_params_copy:
-                            del in_primitive_params_copy[paramd["name"]]
-                        elif not paramd.get("default-value"):
-                            raise EngineException("Needed parameter {} not provided for primitive '{}'".format(
-                                paramd["name"], indata["primitive"]))
-                    # check no extra primitive params are provided
-                    if in_primitive_params_copy:
-                        raise EngineException("parameter/s '{}' not present at vnfd /nsd for primitive '{}'".format(
-                            list(in_primitive_params_copy.keys()), indata["primitive"]))
-                    break
+                        raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:internal-vld[name"
+                                              "='{}']:internal-connection-point[id-ref:'{}'] is not present at "
+                                              "vnfd:internal-vld:name/id:internal-connection-point"
+                                              .format(in_vnf["member-vnf-index"], in_ivld["name"],
+                                                      in_icp["id-ref"]))
             else:
-                raise EngineException("Invalid primitive '{}' is not present at vnfd/nsd".format(indata["primitive"]))
-        if operation == "scale":
-            vnfd = check_valid_vnf_member_index(indata["scaleVnfData"]["scaleByStepData"]["member-vnf-index"])
-            for scaling_group in get_iterable(vnfd.get("scaling-group-descriptor")):
-                if indata["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"] == scaling_group["name"]:
-                    break
-            else:
-                raise EngineException("Invalid scaleVnfData:scaleByStepData:scaling-group-descriptor '{}' is not "
-                                      "present at vnfd:scaling-group-descriptor".format(
-                                          indata["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"]))
-        if operation == "instantiate":
-            # check vim_account
-            check_valid_vim_account(indata["vimAccountId"])
-            check_valid_wim_account(indata.get("wimAccountId"))
-            for in_vnf in get_iterable(indata.get("vnf")):
-                vnfd = check_valid_vnf_member_index(in_vnf["member-vnf-index"])
-                _check_vnf_instantiation_params(in_vnf, vnfd)
-                if in_vnf.get("vimAccountId"):
-                    check_valid_vim_account(in_vnf["vimAccountId"])
-
-            for in_vld in get_iterable(indata.get("vld")):
-                check_valid_wim_account(in_vld.get("wimAccountId"))
-                for vldd in get_iterable(nsd.get("vld")):
-                    if in_vld["name"] == vldd["name"] or in_vld["name"] == vldd["id"]:
-                        break
-                else:
-                    raise EngineException("Invalid parameter vld:name='{}' is not present at nsd:vld".format(
-                        in_vld["name"]))
+                raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:internal-vld:name='{}'"
+                                      " is not present at vnfd '{}'".format(in_vnf["member-vnf-index"],
+                                                                            in_ivld["name"], vnfd["id"]))
+
+    def _check_valid_vim_account(self, vim_account, vim_accounts, session):
+        if vim_account in vim_accounts:
+            return
+        try:
+            db_filter = self._get_project_filter(session)
+            db_filter["_id"] = vim_account
+            self.db.get_one("vim_accounts", db_filter)
+        except Exception:
+            raise EngineException("Invalid vimAccountId='{}' not present for the project".format(vim_account))
+        vim_accounts.append(vim_account)
+
+    def _check_valid_wim_account(self, wim_account, wim_accounts, session):
+        if not isinstance(wim_account, str):
+            return
+        if wim_account in wim_accounts:
+            return
+        try:
+            db_filter = self._get_project_filter(session, write=False, show_all=True)
+            db_filter["_id"] = wim_account
+            self.db.get_one("wim_accounts", db_filter)
+        except Exception:
+            raise EngineException("Invalid wimAccountId='{}' not present for the project".format(wim_account))
+        wim_accounts.append(wim_account)
 
     def _look_for_pdu(self, session, rollback, vnfr, vim_account, vnfr_update, vnfr_update_rollback):
         """
@@ -1304,54 +1401,6 @@ class NsiTopic(BaseTopic):
             if not nsis_list:
                 self.db.set_one("nsts", {"_id": nsir_admin["nst-id"]}, {"_admin.usageState": "NOT_IN_USE"})
 
-    # def delete(self, session, _id, dry_run=False):
-    #     """
-    #     Delete item by its internal _id
-    #     :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
-    #     :param _id: server internal id
-    #     :param dry_run: make checking but do not delete
-    #     :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
-    #     """
-    #     # TODO add admin to filter, validate rights
-    #     BaseTopic.delete(self, session, _id, dry_run=True)
-    #     if dry_run:
-    #         return
-    #
-    #     # Deleting the nsrs belonging to nsir
-    #     nsir = self.db.get_one("nsis", {"_id": _id})
-    #     for nsrs_detailed_item in nsir["_admin"]["nsrs-detailed-list"]:
-    #         nsr_id = nsrs_detailed_item["nsrId"]
-    #         if nsrs_detailed_item.get("shared"):
-    #             _filter = {"_admin.nsrs-detailed-list.ANYINDEX.shared": True,
-    #                        "_admin.nsrs-detailed-list.ANYINDEX.nsrId": nsr_id,
-    #                        "_id.ne": nsir["_id"]}
-    #             nsi = self.db.get_one("nsis", _filter, fail_on_empty=False, fail_on_more=False)
-    #             if nsi:  # last one using nsr
-    #                 continue
-    #         try:
-    #             self.nsrTopic.delete(session, nsr_id, dry_run=False)
-    #         except (DbException, EngineException) as e:
-    #             if e.http_code == HTTPStatus.NOT_FOUND:
-    #                 pass
-    #             else:
-    #                 raise
-    #     # deletes NetSlice instance object
-    #     v = self.db.del_one("nsis", {"_id": _id})
-    #
-    #     # makes a temporal list of nsilcmops objects related to the _id given and deletes them from db
-    #     _filter = {"netsliceInstanceId": _id}
-    #     self.db.del_list("nsilcmops", _filter)
-    #
-    #     # Search if nst is being used by other nsi
-    #     nsir_admin = nsir.get("_admin")
-    #     if nsir_admin:
-    #         if nsir_admin.get("nst-id"):
-    #             nsis_list = self.db.get_one("nsis", {"nst-id": nsir_admin["nst-id"]},
-    #                                         fail_on_empty=False, fail_on_more=False)
-    #             if not nsis_list:
-    #                 self.db.set_one("nsts", {"_id": nsir_admin["nst-id"]}, {"_admin.usageState": "NOT_IN_USE"})
-    #     return v
-
     def new(self, rollback, session, indata=None, kwargs=None, headers=None):
         """
         Creates a new netslice instance record into database. It also creates needed nsrs and vnfrs
index 057230c..1e6eff9 100644 (file)
@@ -75,172 +75,167 @@ db_vnfds_text = """
         usageState: NOT_IN_USE
         userDefinedData: {}
     _id: 7637bcf8-cf14-42dc-ad70-c66fcf1e6e77
-    connection-point:
-    -   id: vnf-mgmt
-        name: vnf-mgmt
-        short-name: vnf-mgmt
-        type: VPORT
-    -   id: vnf-data
-        name: vnf-data
-        short-name: vnf-data
-        type: VPORT
-    description: A VNF consisting of 2 VDUs connected to an internal VL, and one VDU
-        with cloud-init
     id: hackfest3charmed-vnf
-    internal-vld:
-    -   id: internal
-        internal-connection-point:
-        -   id-ref: mgmtVM-internal
-        -   id-ref: dataVM-internal
-        name: internal
-        short-name: internal
-        type: ELAN
-    logo: osm.png
-    mgmt-interface:
-        cp: vnf-mgmt
-    monitoring-param:
-    -   aggregation-type: AVERAGE
-        id: monitor1
-        name: monitor1
-        vdu-monitoring-param:
-            vdu-monitoring-param-ref: dataVM_cpu_util
-            vdu-ref: dataVM
-    -   id: dataVM_users
-        aggregation-type: AVERAGE
-        name: dataVM_users
-        vdu-metric:
-            vdu-metric-name-ref: users
-            vdu-ref: dataVM
-    -   id: dataVM_load
-        aggregation-type: AVERAGE
-        name: dataVM_load
-        vdu-metric:
-            vdu-metric-name-ref: load
-            vdu-ref: dataVM
-    name: hackfest3charmed-vnf
-    scaling-group-descriptor:
-    -   max-instance-count: 10
-        name: scale_dataVM
-        scaling-config-action:
-        -   trigger: post-scale-out
-            vnf-config-primitive-name-ref: touch
-        -   trigger: pre-scale-in
-            vnf-config-primitive-name-ref: touch
-        scaling-policy:
-        -   cooldown-time: 60
-            name: auto_cpu_util_above_threshold
-            scaling-criteria:
-            -   name: cpu_util_above_threshold
-                scale-in-relational-operation: LE
-                scale-in-threshold: '15.0000000000'
-                scale-out-relational-operation: GE
-                scale-out-threshold: '60.0000000000'
-                vnf-monitoring-param-ref: monitor1
-            scaling-type: automatic
-            threshold-time: 0
-        vdu:
-        -   count: 1
-            vdu-id-ref: dataVM
-    short-name: hackfest3charmed-vnf
+    description: >-
+      A VNF consisting of 2 VDUs connected to an internal VL, and one VDU with
+      cloud-init
+    product-name: hackfest3charmed-vnf
+    version: '1.0'
+    mgmt-cp: vnf-mgmt-ext
+  
+    virtual-compute-desc:
+      - id: mgmt-compute
+        virtual-cpu:
+          num-virtual-cpu: 1
+        virtual-memory:
+          size: 1
+      - id: data-compute
+        virtual-cpu:
+          num-virtual-cpu: 1
+        virtual-memory:
+          size: 1
+  
+    virtual-storage-desc:
+      - id: mgmt-storage
+        size-of-storage: 10
+      - id: data-storage
+        size-of-storage: 10
+  
+    sw-image-desc:
+      - id: hackfest3-mgmt
+        name: hackfest3-mgmt
+  
     vdu:
-    -   count: '1'
-        cloud-init-file: cloud-config.txt
-        id: mgmtVM
-        image: hackfest3-mgmt
-        interface:
-        -   external-connection-point-ref: vnf-mgmt
-            name: mgmtVM-eth0
-            position: 1
-            type: EXTERNAL
-            virtual-interface:
-                type: VIRTIO
-        -   internal-connection-point-ref: mgmtVM-internal
-            name: mgmtVM-eth1
-            position: 2
-            type: INTERNAL
-            virtual-interface:
-                type: VIRTIO
-        internal-connection-point:
-        -   id: mgmtVM-internal
-            name: mgmtVM-internal
-            short-name: mgmtVM-internal
-            type: VPORT
+      - id: mgmtVM
         name: mgmtVM
-        vm-flavor:
-            memory-mb: '1024'
-            storage-gb: '10'
-            vcpu-count: 1
-    -   count: '1'
-        id: dataVM
-        image: hackfest3-mgmt
-        interface:
-        -   internal-connection-point-ref: dataVM-internal
-            name: dataVM-eth0
-            position: 1
-            type: INTERNAL
-            virtual-interface:
-                type: VIRTIO
-        -   external-connection-point-ref: vnf-data
-            name: dataVM-xe0
-            position: 2
-            type: EXTERNAL
-            virtual-interface:
-                type: VIRTIO
-        internal-connection-point:
-        -   id: dataVM-internal
-            name: dataVM-internal
-            short-name: dataVM-internal
-            type: VPORT
-        monitoring-param:
-        -   id: dataVM_cpu_util
-            nfvi-metric: cpu_utilization
+        cloud-init-file: cloud-config.txt
+        sw-image-desc: hackfest3-mgmt
+        virtual-compute-desc: mgmt-compute
+        virtual-storage-desc:
+          - mgmt-storage
+        int-cpd:
+          - id: vnf-mgmt
+            virtual-network-interface-requirement:
+              - name: mgmtVM-eth0
+                position: 1
+                virtual-interface:
+                  type: VIRTIO
+          - id: mgmtVM-internal
+            int-virtual-link-desc: internal
+            virtual-network-interface-requirement:
+              - name: mgmtVM-eth1
+                position: 2
+                virtual-interface:
+                  type: VIRTIO
+      - id: dataVM
         name: dataVM
-        vdu-configuration:
-          initial-config-primitive:
-          -   parameter:
-              -   value: "<rw_mgmt_ip>"
-                  name: ssh-hostname
-              -   value: ubuntu
-                  name: ssh-username
-              -   value: osm2018
-                  name: ssh-password
-              name: config
-              seq: '1'
-          metrics:
-          -   name: users
-          -   name: load
-          juju:
-            proxy: true
-            charm: testmetrics
-        vm-flavor:
-            memory-mb: '1024'
-            storage-gb: '10'
-            vcpu-count: 1
-    version: '1.0'
+        sw-image-desc: hackfest3-mgmt
+        virtual-compute-desc: data-compute
+        virtual-storage-desc:
+          - data-storage
+        int-cpd:
+          - id: dataVM-internal
+            int-virtual-link-desc: internal
+            virtual-network-interface-requirement:
+              - name: dataVM-eth1
+                position: 1
+                virtual-interface:
+                  type: VIRTIO
+          - id: vnf-data
+            virtual-network-interface-requirement:
+              - name: dataVM-eth0
+                position: 2
+                virtual-interface:
+                  type: VIRTIO
+        monitoring-parameter:
+          - id: dataVM_cpu_util
+            name: dataVM_cpu_util
+            performance-metric: cpu_utilization
+  
+    int-virtual-link-desc:
+      - id: internal
+  
+    ext-cpd:
+      - id: vnf-mgmt-ext
+        int-cpd: # Connection to int-cpd
+          vdu-id: mgmtVM
+          cpd: vnf-mgmt
+      - id: vnf-data-ext
+        int-cpd: # Connection to int-cpd
+          vdu-id: dataVM
+          cpd: vnf-data
+  
+    df:
+      - id: hackfest_default
+        vnf-configuration-id: vnf-configuration-example
+        vdu-profile:
+          - id: mgmtVM
+            min-number-of-instances: 1
+          - id: dataVM
+            min-number-of-instances: 1
+            max-number-of-instances: 10
+            vdu-configuration-id: vdu-configuration-example
+        instantiation-level:
+          - id: default
+            vdu-level:
+              - vdu-id: mgmtVM
+                number-of-instances: 1
+              - vdu-id: dataVM
+                number-of-instances: 1
+        scaling-aspect:
+          - id: scale_dataVM
+            name: scale_dataVM
+            max-scale-level: 10
+            aspect-delta-details:
+              deltas:
+                - id: delta1
+                  vdu-delta:
+                    - id: vdudelta1
+                      number-of-instances: 1
+            scaling-policy:
+              - name: auto_cpu_util_above_threshold
+                scaling-type: automatic
+                enabled: true
+                threshold-time: 0
+                cooldown-time: 60
+                scaling-criteria:
+                  - name: cpu_util_above_threshold
+                    scale-in-relational-operation: LE
+                    scale-in-threshold: '15.0000000000'
+                    scale-out-relational-operation: GE
+                    scale-out-threshold: '60.0000000000'
+                    vnf-monitoring-param-ref: dataVM_cpu_util
+            scaling-config-action:
+              - trigger: post-scale-out
+                vnf-config-primitive-name-ref: touch
+              - trigger: pre-scale-in
+                vnf-config-primitive-name-ref: touch
+  
     vnf-configuration:
-        config-primitive:
-        -   name: touch
-            parameter:
-            -   data-type: STRING
-                default-value: <touch_filename2>
-                name: filename
+      - id: vnf-configuration-example
         initial-config-primitive:
-        -   name: config
+          - seq: "1"
+            name: config
             parameter:
-            -   name: ssh-hostname
+              - name: ssh-hostname
                 value: <rw_mgmt_ip>
-            -   name: ssh-username
+              - name: ssh-username
                 value: ubuntu
-            -   name: ssh-password
+              - name: ssh-password
                 value: osm4u
-            seq: '1'
-        -   name: touch
+          - seq: "2"
+            name: touch
             parameter:
-            -   name: filename
+              - name: filename
                 value: <touch_filename>
-            seq: '2'
+        config-primitive:
+          - name: touch
+            parameter:
+              - data-type: STRING
+                default-value: <touch_filename2>
+                name: filename
         juju:
-            charm: simple
+          charm: simple
 """
 
 db_nsds_text = """
@@ -264,43 +259,46 @@ db_nsds_text = """
         usageState: NOT_IN_USE
         userDefinedData: {}
     _id: 8c2f8b95-bb1b-47ee-8001-36dc090678da
-    constituent-vnfd:
-    -   member-vnf-index: '1'
-        vnfd-id-ref: hackfest3charmed-vnf
-    -   member-vnf-index: '2'
-        vnfd-id-ref: hackfest3charmed-vnf
-    description: NS with 2 VNFs hackfest3charmed-vnf connected by datanet and mgmtnet
-        VLs
     id: hackfest3charmed-ns
-    logo: osm.png
     name: hackfest3charmed-ns
-    short-name: hackfest3charmed-ns
+    description: NS with 2 VNFs hackfest3charmed-vnf connected by datanet and mgmtnet VLs
+    designer: OSM
     version: '1.0'
-    vld:
-    -   id: mgmt
-        mgmt-network: true
-        name: mgmt
-        short-name: mgmt
-        type: ELAN
-        vim-network-name: mgmt
-        vnfd-connection-point-ref:
-        -   member-vnf-index-ref: '1'
-            vnfd-connection-point-ref: vnf-mgmt
-            vnfd-id-ref: hackfest3charmed-vnf
-        -   member-vnf-index-ref: '2'
-            vnfd-connection-point-ref: vnf-mgmt
-            vnfd-id-ref: hackfest3charmed-vnf
-    -   id: datanet
-        name: datanet
-        short-name: datanet
-        type: ELAN
-        vnfd-connection-point-ref:
-        -   member-vnf-index-ref: '1'
-            vnfd-connection-point-ref: vnf-data
-            vnfd-id-ref: hackfest3charmed-vnf
-        -   member-vnf-index-ref: '2'
-            vnfd-connection-point-ref: vnf-data
-            vnfd-id-ref: hackfest3charmed-vnf
+
+    vnfd-id:
+      - hackfest3charmed-vnf
+
+    virtual-link-desc:
+      - id: mgmt
+        mgmt-network: "true"
+      - id: datanet
+        mgmt-network: "false"
+
+    df:
+      - id: hackfest_charmed_DF
+        vnf-profile:
+          - id: hackfest_vnf1 # member-vnf-index-ref: 1
+            vnfd-id: hackfest3charmed-vnf
+            virtual-link-connectivity:
+              - virtual-link-profile-id: mgmt
+                constituent-cpd-id:
+                  - constituent-base-element-id: hackfest_vnf1
+                    constituent-cpd-id: vnf-mgmt-ext
+              - virtual-link-profile-id: datanet
+                constituent-cpd-id:
+                  - constituent-base-element-id: hackfest_vnf1
+                    constituent-cpd-id: vnf-data-ext
+          - id: hackfest_vnf2 # member-vnf-index-ref: 2
+            vnfd-id: hackfest3charmed-vnf
+            virtual-link-connectivity:
+              - virtual-link-profile-id: mgmt
+                constituent-cpd-id:
+                  - constituent-base-element-id: hackfest_vnf2
+                    constituent-cpd-id: vnf-mgmt-ext
+              - virtual-link-profile-id: datanet
+                constituent-cpd-id:
+                  - constituent-base-element-id: hackfest_vnf2
+                    constituent-cpd-id: vnf-data-ext
 """
 
 db_nsrs_text = """
@@ -356,43 +354,46 @@ db_nsrs_text = """
             usageState: NOT_IN_USE
             userDefinedData: {}
         _id: 8c2f8b95-bb1b-47ee-8001-36dc090678da
-        constituent-vnfd:
-        -   member-vnf-index: '1'
-            vnfd-id-ref: hackfest3charmed-vnf
-        -   member-vnf-index: '2'
-            vnfd-id-ref: hackfest3charmed-vnf
-        description: NS with 2 VNFs hackfest3charmed-vnf connected by datanet and
-            mgmtnet VLs
         id: hackfest3charmed-ns
-        logo: osm.png
         name: hackfest3charmed-ns
-        short-name: hackfest3charmed-ns
+        description: NS with 2 VNFs hackfest3charmed-vnf connected by datanet and mgmtnet VLs
+        designer: OSM
         version: '1.0'
-        vld:
-        -   id: mgmt
-            mgmt-network: true
-            name: mgmt
-            short-name: mgmt
-            type: ELAN
-            vim-network-name: mgmt
-            vnfd-connection-point-ref:
-            -   member-vnf-index-ref: '1'
-                vnfd-connection-point-ref: vnf-mgmt
-                vnfd-id-ref: hackfest3charmed-vnf
-            -   member-vnf-index-ref: '2'
-                vnfd-connection-point-ref: vnf-mgmt
-                vnfd-id-ref: hackfest3charmed-vnf
-        -   id: datanet
-            name: datanet
-            short-name: datanet
-            type: ELAN
-            vnfd-connection-point-ref:
-            -   member-vnf-index-ref: '1'
-                vnfd-connection-point-ref: vnf-data
-                vnfd-id-ref: hackfest3charmed-vnf
-            -   member-vnf-index-ref: '2'
-                vnfd-connection-point-ref: vnf-data
-                vnfd-id-ref: hackfest3charmed-vnf
+
+        vnfd-id:
+          - hackfest3charmed-vnf
+
+        virtual-link-desc:
+          - id: mgmt
+            mgmt-network: "true"
+          - id: datanet
+            mgmt-network: "false"
+
+        df:
+          - id: hackfest_charmed_DF
+            vnf-profile:
+              - id: hackfest_vnf1 # member-vnf-index-ref: 1
+                vnfd-id: hackfest3charmed-vnf
+                virtual-link-connectivity:
+                  - virtual-link-profile-id: mgmt
+                    constituent-cpd-id:
+                      - constituent-base-element-id: hackfest_vnf1
+                        constituent-cpd-id: vnf-mgmt-ext
+                  - virtual-link-profile-id: datanet
+                    constituent-cpd-id:
+                      - constituent-base-element-id: hackfest_vnf1
+                        constituent-cpd-id: vnf-data-ext
+              - id: hackfest_vnf2 # member-vnf-index-ref: 2
+                vnfd-id: hackfest3charmed-vnf
+                virtual-link-connectivity:
+                  - virtual-link-profile-id: mgmt
+                    constituent-cpd-id:
+                      - constituent-base-element-id: hackfest_vnf2
+                        constituent-cpd-id: vnf-mgmt-ext
+                  - virtual-link-profile-id: datanet
+                    constituent-cpd-id:
+                      - constituent-base-element-id: hackfest_vnf2
+                        constituent-cpd-id: vnf-data-ext
     nsd-id: 8c2f8b95-bb1b-47ee-8001-36dc090678da
     nsd-name-ref: hackfest3charmed-ns
     nsd-ref: hackfest3charmed-ns
index 5fbb337..516c178 100755 (executable)
@@ -112,9 +112,9 @@ class Test_VnfdTopic(TestCase):
             self.assertEqual(db_args[1]["_admin"]["projects_read"], [test_pid], "Wrong read-only project list")
             self.assertEqual(db_args[1]["_admin"]["projects_write"], [test_pid], "Wrong read-write project list")
             tmp1 = test_vnfd["vdu"][0]["cloud-init-file"]
-            tmp2 = test_vnfd["vnf-configuration"]["juju"]
+            tmp2 = test_vnfd["vnf-configuration"][0]["juju"]
             del test_vnfd["vdu"][0]["cloud-init-file"]
-            del test_vnfd["vnf-configuration"]["juju"]
+            del test_vnfd["vnf-configuration"][0]["juju"]
             try:
                 self.db.get_one.side_effect = [{"_id": did, "_admin": db_vnfd_content["_admin"]}, None]
                 self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
@@ -145,21 +145,10 @@ class Test_VnfdTopic(TestCase):
                 compare_desc(self, test_vnfd, db_args[2], "VNFD")
             finally:
                 test_vnfd["vdu"][0]["cloud-init-file"] = tmp1
-                test_vnfd["vnf-configuration"]["juju"] = tmp2
+                test_vnfd["vnf-configuration"][0]["juju"] = tmp2
         self.db.get_one.side_effect = lambda table, filter, fail_on_empty=None, fail_on_more=None: \
             {"_id": did, "_admin": db_vnfd_content["_admin"]}
-        with self.subTest(i=2, t='Check Pyangbind Validation: required properties'):
-            tmp = test_vnfd["id"]
-            del test_vnfd["id"]
-            try:
-                with self.assertRaises(EngineException, msg="Accepted VNFD with a missing required property") as e:
-                    self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
-                self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("Error in pyangbind validation: '{}'".format("id")),
-                              norm(str(e.exception)), "Wrong exception text")
-            finally:
-                test_vnfd["id"] = tmp
-        with self.subTest(i=3, t='Check Pyangbind Validation: additional properties'):
+        with self.subTest(i=2, t='Check Pyangbind Validation: additional properties'):
             test_vnfd["extra-property"] = 0
             try:
                 with self.assertRaises(EngineException, msg="Accepted VNFD with an additional property") as e:
@@ -170,9 +159,9 @@ class Test_VnfdTopic(TestCase):
                               norm(str(e.exception)), "Wrong exception text")
             finally:
                 del test_vnfd["extra-property"]
-        with self.subTest(i=4, t='Check Pyangbind Validation: property types'):
-            tmp = test_vnfd["short-name"]
-            test_vnfd["short-name"] = {"key": 0}
+        with self.subTest(i=3, t='Check Pyangbind Validation: property types'):
+            tmp = test_vnfd["product-name"]
+            test_vnfd["product-name"] = {"key": 0}
             try:
                 with self.assertRaises(EngineException, msg="Accepted VNFD with a wrongly typed property") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
@@ -181,215 +170,146 @@ class Test_VnfdTopic(TestCase):
                                    .format("json object contained a key that did not exist", "key")),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                test_vnfd["short-name"] = tmp
-        with self.subTest(i=5, t='Check Input Validation: cloud-init'):
+                test_vnfd["product-name"] = tmp
+        with self.subTest(i=4, t='Check Input Validation: cloud-init'):
             with self.assertRaises(EngineException, msg="Accepted non-existent cloud_init file") as e:
                 self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
             self.assertEqual(e.exception.http_code, HTTPStatus.BAD_REQUEST, "Wrong HTTP status code")
             self.assertIn(norm("{} defined in vnf[id={}]:vdu[id={}] but not present in package"
                                .format("cloud-init", test_vnfd["id"], test_vnfd["vdu"][0]["id"])),
                           norm(str(e.exception)), "Wrong exception text")
-        with self.subTest(i=6, t='Check Input Validation: vnf-configuration[juju]'):
+        with self.subTest(i=5, t='Check Input Validation: vnf-configuration[juju]'):
             del test_vnfd["vdu"][0]["cloud-init-file"]
             with self.assertRaises(EngineException, msg="Accepted non-existent charm in VNF configuration") as e:
                 self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
             self.assertEqual(e.exception.http_code, HTTPStatus.BAD_REQUEST, "Wrong HTTP status code")
             self.assertIn(norm("{} defined in vnf[id={}] but not present in package".format("charm", test_vnfd["id"])),
                           norm(str(e.exception)), "Wrong exception text")
-        with self.subTest(i=7, t='Check Input Validation: mgmt-interface'):
-            del test_vnfd["vnf-configuration"]["juju"]
-            tmp = test_vnfd["mgmt-interface"]
-            del test_vnfd["mgmt-interface"]
+            del test_vnfd["vnf-configuration"][0]["juju"]
+        with self.subTest(i=6, t='Check Input Validation: mgmt-cp'):
+            tmp = test_vnfd["mgmt-cp"]
+            del test_vnfd["mgmt-cp"]
             try:
                 with self.assertRaises(EngineException, msg="Accepted VNFD without management interface") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("'{}' is a mandatory field and it is not defined".format("mgmt-interface")),
+                self.assertIn(norm("'{}' is a mandatory field and it is not defined".format("mgmt-cp")),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                test_vnfd["mgmt-interface"] = tmp
-        with self.subTest(i=8, t='Check Input Validation: mgmt-interface[cp]'):
-            tmp = test_vnfd["mgmt-interface"]["cp"]
-            test_vnfd["mgmt-interface"]["cp"] = "wrong-cp"
+                test_vnfd["mgmt-cp"] = tmp
+        with self.subTest(i=7, t='Check Input Validation: mgmt-cp connection point'):
+            tmp = test_vnfd["mgmt-cp"]
+            test_vnfd["mgmt-cp"] = "wrong-cp"
             try:
-                with self.assertRaises(EngineException,
-                                       msg="Accepted wrong management interface connection point") as e:
+                with self.assertRaises(EngineException, msg="Accepted wrong mgmt-cp connection point") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("mgmt-interface:cp='{}' must match an existing connection-point"
-                                   .format(test_vnfd["mgmt-interface"]["cp"])),
+                self.assertIn(norm("mgmt-cp='{}' must match an existing ext-cpd".format(test_vnfd["mgmt-cp"])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                test_vnfd["mgmt-interface"]["cp"] = tmp
-        with self.subTest(i=9, t='Check Input Validation: vdu[interface][external-connection-point-ref]'):
-            tmp = test_vnfd["vdu"][0]["interface"][0]["external-connection-point-ref"]
-            test_vnfd["vdu"][0]["interface"][0]["external-connection-point-ref"] = "wrong-cp"
+                test_vnfd["mgmt-cp"] = tmp
+        with self.subTest(i=8, t='Check Input Validation: vdu int-cpd'):
+            ext_cpd = test_vnfd["ext-cpd"][1]
+            tmp = ext_cpd["int-cpd"]["cpd"]
+            ext_cpd["int-cpd"]["cpd"] = "wrong-cpd"
             try:
-                with self.assertRaises(EngineException,
-                                       msg="Accepted wrong VDU interface external connection point reference") as e:
+                with self.assertRaises(EngineException, msg="Accepted wrong ext-cpd internal connection point") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{}'"
-                                   " must match an existing connection-point"
-                                   .format(test_vnfd["vdu"][0]["id"], test_vnfd["vdu"][0]["interface"][0]["name"],
-                                           test_vnfd["vdu"][0]["interface"][0]["external-connection-point-ref"])),
-                              norm(str(e.exception)), "Wrong exception text")
-            finally:
-                test_vnfd["vdu"][0]["interface"][0]["external-connection-point-ref"] = tmp
-        with self.subTest(i=10, t='Check Input Validation: vdu[interface][internal-connection-point-ref]'):
-            tmp = test_vnfd["vdu"][1]["interface"][0]["internal-connection-point-ref"]
-            test_vnfd["vdu"][1]["interface"][0]["internal-connection-point-ref"] = "wrong-cp"
-            try:
-                with self.assertRaises(EngineException,
-                                       msg="Accepted wrong VDU interface internal connection point reference") as e:
-                    self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
-                self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}'"
-                                   " must match an existing vdu:internal-connection-point"
-                                   .format(test_vnfd["vdu"][1]["id"], test_vnfd["vdu"][1]["interface"][0]["name"],
-                                           test_vnfd["vdu"][1]["interface"][0]["internal-connection-point-ref"])),
-                              norm(str(e.exception)), "Wrong exception text")
-            finally:
-                test_vnfd["vdu"][1]["interface"][0]["internal-connection-point-ref"] = tmp
-        with self.subTest(i=11, t='Check Input Validation: vdu[vdu-configuration][juju]'):
-            test_vnfd["vdu"][0]["vdu-configuration"] = {"juju": {"charm": "wrong-charm"}}
-            try:
-                with self.assertRaises(EngineException, msg="Accepted non-existent charm in VDU configuration") as e:
-                    self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
-                self.assertEqual(e.exception.http_code, HTTPStatus.BAD_REQUEST, "Wrong HTTP status code")
-                self.assertIn(norm("{} defined in vnf[id={}]:vdu[id={}] but not present in package"
-                                   .format("charm", test_vnfd["id"], test_vnfd["vdu"][0]["id"])),
+                self.assertIn(norm("ext-cpd[id='{}']:int-cpd must match an existing vdu int-cpd".format(ext_cpd["id"])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                del test_vnfd["vdu"][0]["vdu-configuration"]
-        with self.subTest(i=12, t='Check Input Validation: Duplicated VLD name'):
-            test_vnfd["internal-vld"].append(deepcopy(test_vnfd["internal-vld"][0]))
-            test_vnfd["internal-vld"][1]["id"] = "wrong-internal-vld"
+                ext_cpd["int-cpd"]["cpd"] = tmp
+        with self.subTest(i=9, t='Check Input Validation: Duplicated VLD'):
+            test_vnfd['int-virtual-link-desc'].insert(0, {'id': 'internal'})
             try:
                 with self.assertRaises(EngineException, msg="Accepted duplicated VLD name") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("Duplicated VLD name '{}' in vnfd[id={}]:internal-vld[id={}]"
-                                   .format(test_vnfd["internal-vld"][1]["name"], test_vnfd["id"],
-                                           test_vnfd["internal-vld"][1]["id"])),
-                              norm(str(e.exception)), "Wrong exception text")
+                self.assertIn(
+                    norm("identifier id '{}' is not unique".format(test_vnfd['int-virtual-link-desc'][0]["id"])),
+                    norm(str(e.exception)), "Wrong exception text")
             finally:
-                del test_vnfd["internal-vld"][1]
-        with self.subTest(i=13, t='Check Input Validation: internal-vld[internal-connection-point][id-ref])'):
-            tmp = test_vnfd["internal-vld"][0]["internal-connection-point"][0]["id-ref"]
-            test_vnfd["internal-vld"][0]["internal-connection-point"][0]["id-ref"] = "wrong-icp-id-ref"
+                del test_vnfd['int-virtual-link-desc'][0]
+        with self.subTest(i=10, t='Check Input Validation: vdu int-virtual-link-desc'):
+            vdu = test_vnfd['vdu'][0]
+            int_cpd = vdu['int-cpd'][1]
+            tmp = int_cpd['int-virtual-link-desc']
+            int_cpd['int-virtual-link-desc'] = 'non-existing-int-virtual-link-desc'
             try:
-                with self.assertRaises(EngineException, msg="Accepted non-existent internal VLD ICP id-ref") as e:
+                with self.assertRaises(EngineException, msg="Accepted int-virtual-link-desc") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("internal-vld[id='{}']:internal-connection-point='{}' must match an existing "
-                                   "vdu:internal-connection-point"
-                                   .format(test_vnfd["internal-vld"][0]["id"],
-                                           test_vnfd["internal-vld"][0]["internal-connection-point"][0]["id-ref"])),
+                self.assertIn(norm("vdu[id='{}']:int-cpd[id='{}']:int-virtual-link-desc='{}' must match an existing "
+                                   "int-virtual-link-desc".format(vdu["id"], int_cpd["id"],
+                                                                  int_cpd['int-virtual-link-desc'])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                test_vnfd["internal-vld"][0]["internal-connection-point"][0]["id-ref"] = tmp
-        with self.subTest(i=14, t='Check Input Validation: internal-vld[ip-profile-ref])'):
-            test_vnfd["ip-profiles"] = [{"name": "fake-ip-profile-ref"}]
-            test_vnfd["internal-vld"][0]["ip-profile-ref"] = "wrong-ip-profile-ref"
+                int_cpd['int-virtual-link-desc'] = tmp
+        with self.subTest(i=11, t='Check Input Validation: virtual-link-profile)'):
+            fake_ivld_profile = {'id': 'fake-profile-ref', 'flavour': 'fake-flavour'}
+            df = test_vnfd['df'][0]
+            df['virtual-link-profile'] = [fake_ivld_profile]
             try:
-                with self.assertRaises(EngineException, msg="Accepted non-existent IP Profile Ref") as e:
+                with self.assertRaises(EngineException, msg="Accepted non-existent Profile Ref") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("internal-vld[id='{}']:ip-profile-ref='{}' does not exist"
-                                   .format(test_vnfd["internal-vld"][0]["id"],
-                                           test_vnfd["internal-vld"][0]["ip-profile-ref"])),
+                self.assertIn(norm("df[id='{}']:virtual-link-profile='{}' must match an existing "
+                                   "int-virtual-link-desc".format(df["id"], fake_ivld_profile["id"])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                del test_vnfd["ip-profiles"]
-                del test_vnfd["internal-vld"][0]["ip-profile-ref"]
-        with self.subTest(i=15, t='Check Input Validation: vdu[monitoring-param])'):
-            test_vnfd["monitoring-param"] = [{"id": "fake-mp-id", "vdu-monitoring-param": {
-                "vdu-monitoring-param-ref": "fake-vdu-mp-ref", "vdu-ref": "fake-vdu-ref"}}]
-            test_vnfd["vdu"][0]["monitoring-param"] = [{"id": "wrong-vdu-mp-id"}]
+                del df['virtual-link-profile']
+        with self.subTest(i=12, t='Check Input Validation: scaling-criteria monitoring-param-ref'):
+            vdu = test_vnfd['vdu'][1]
+            affected_df = test_vnfd['df'][0]
+            sa = affected_df['scaling-aspect'][0]
+            sp = sa['scaling-policy'][0]
+            sc = sp['scaling-criteria'][0]
+            tmp = vdu.pop('monitoring-parameter')
             try:
-                with self.assertRaises(EngineException, msg="Accepted non-existent VDU Monitorimg Param") as e:
+                with self.assertRaises(EngineException, msg="Accepted non-existent Scaling Group Policy Criteria") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                mp = test_vnfd["monitoring-param"][0]["vdu-monitoring-param"]
-                self.assertIn(norm("monitoring-param:vdu-monitoring-param:vdu-monitoring-param-ref='{}' not defined"
-                                   " at vdu[id='{}'] or vdu does not exist"
-                                   .format(mp["vdu-monitoring-param-ref"], mp["vdu-ref"])),
+                self.assertIn(norm("df[id='{}']:scaling-aspect[id='{}']:scaling-policy"
+                                   "[name='{}']:scaling-criteria[name='{}']: "
+                                   "vnf-monitoring-param-ref='{}' not defined in any monitoring-param"
+                                   .format(affected_df["id"], sa["id"], sp["name"],
+                                           sc["name"], sc["vnf-monitoring-param-ref"])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                del test_vnfd["monitoring-param"]
-                del test_vnfd["vdu"][0]["monitoring-param"]
-        with self.subTest(i=16, t='Check Input Validation: vdu[vdu-configuration][metrics]'):
-            test_vnfd["monitoring-param"] = [{"id": "fake-mp-id", "vdu-metric": {
-                "vdu-metric-name-ref": "fake-vdu-mp-ref", "vdu-ref": "fake-vdu-ref"}}]
-            test_vnfd["vdu"][0]["vdu-configuration"] = {"metrics": [{"name": "wrong-vdu-mp-id"}]}
+                vdu['monitoring-parameter'] = tmp
+        with self.subTest(i=13, t='Check Input Validation: scaling-aspect vnf-configuration'):
+            df = test_vnfd['df'][0]
+            tmp = test_vnfd.pop('vnf-configuration')
             try:
-                with self.assertRaises(EngineException, msg="Accepted non-existent VDU Configuration Metric") as e:
+                with self.assertRaises(EngineException, msg="Accepted non-existent Scaling Group VDU ID Reference") \
+                        as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                mp = test_vnfd["monitoring-param"][0]["vdu-metric"]
-                self.assertIn(norm("monitoring-param:vdu-metric:vdu-metric-name-ref='{}' not defined"
-                                   " at vdu[id='{}'] or vdu does not exist"
-                                   .format(mp["vdu-metric-name-ref"], mp["vdu-ref"])),
+                self.assertIn(norm("'vnf-configuration' not defined in the descriptor but it is referenced "
+                                   "by df[id='{}']:scaling-aspect[id='{}']:scaling-config-action"
+                                   .format(df["id"], df['scaling-aspect'][0]["id"])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                del test_vnfd["monitoring-param"]
-                del test_vnfd["vdu"][0]["vdu-configuration"]
-        with self.subTest(i=17, t='Check Input Validation: scaling-group-descriptor[scaling-policy][scaling-criteria]'):
-            test_vnfd["monitoring-param"] = [{"id": "fake-mp-id"}]
-            test_vnfd["scaling-group-descriptor"] = [{
-                "name": "fake-vnf-sg-name",
-                "vdu": [{"vdu-id-ref": "wrong-vdu-id-ref"}],
-                "scaling-policy": [{"name": "fake-vnf-sp-name", "scaling-criteria": [{
-                    "name": "fake-vnf-sc-name", "vnf-monitoring-param-ref": "wrong-vnf-mp-id"}]}]}]
-            with self.assertRaises(EngineException, msg="Accepted non-existent Scaling Group Policy Criteria") as e:
-                self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
-            self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-            sg = test_vnfd["scaling-group-descriptor"][0]
-            sc = sg["scaling-policy"][0]["scaling-criteria"][0]
-            self.assertIn(norm("scaling-group-descriptor[name='{}']:scaling-criteria[name='{}']:"
-                               "vnf-monitoring-param-ref='{}' not defined in any monitoring-param"
-                               .format(sg["name"], sc["name"], sc["vnf-monitoring-param-ref"])),
-                          norm(str(e.exception)), "Wrong exception text")
-        with self.subTest(i=18, t='Check Input Validation: scaling-group-descriptor[vdu][vdu-id-ref]'):
-            sc["vnf-monitoring-param-ref"] = "fake-mp-id"
-            with self.assertRaises(EngineException, msg="Accepted non-existent Scaling Group VDU ID Reference") as e:
-                self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
-            self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-            self.assertIn(norm("scaling-group-descriptor[name='{}']:vdu-id-ref={} does not match any vdu"
-                               .format(sg["name"], sg["vdu"][0]["vdu-id-ref"])),
-                          norm(str(e.exception)), "Wrong exception text")
-        with self.subTest(i=19, t='Check Input Validation: scaling-group-descriptor[scaling-config-action]'):
-            tmp = test_vnfd["vnf-configuration"]
-            del test_vnfd["vnf-configuration"]
-            sg["vdu"][0]["vdu-id-ref"] = test_vnfd["vdu"][0]["id"]
-            sg["scaling-config-action"] = [{"trigger": "pre-scale-in"}]
+                test_vnfd["vnf-configuration"] = tmp
+        with self.subTest(i=14, t='Check Input Validation: scaling-config-action'):
+            df = test_vnfd['df'][0]
+            tmp = test_vnfd['vnf-configuration'][0]['config-primitive']
+            test_vnfd['vnf-configuration'][0]['config-primitive'] = [{'name': 'wrong-primitive'}]
             try:
-                with self.assertRaises(EngineException, msg="Accepted non-existent Scaling Group VDU ID Reference") \
-                        as e:
+                with self.assertRaises(EngineException,
+                                       msg="Accepted non-existent Scaling Group VDU ID Reference") as e:
                     self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("'vnf-configuration' not defined in the descriptor but it is referenced"
-                                   " by scaling-group-descriptor[name='{}']:scaling-config-action"
-                                   .format(sg["name"])),
+                self.assertIn(norm("df[id='{}']:scaling-aspect[id='{}']:scaling-config-action:vnf-"
+                                   "config-primitive-name-ref='{}' does not match any "
+                                   "vnf-configuration:config-primitive:name"
+                                   .format(df["id"], df['scaling-aspect'][0]["id"],
+                                           sa['scaling-config-action'][0]['vnf-config-primitive-name-ref'])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                test_vnfd["vnf-configuration"] = tmp
-        with self.subTest(i=20, t='Check Input Validation: scaling-group-descriptor[scaling-config-action]'
-                                  '[vnf-config-primitive-name-ref]'):
-            sg["scaling-config-action"][0]["vnf-config-primitive-name-ref"] = "wrong-sca-prim-name"
-            with self.assertRaises(EngineException, msg="Accepted non-existent Scaling Group VDU ID Reference") as e:
-                self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
-            self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-            self.assertIn(norm("scaling-group-descriptor[name='{}']:scaling-config-action:"
-                               "vnf-config-primitive-name-ref='{}' does not match"
-                               " any vnf-configuration:config-primitive:name"
-                               .format(sg["name"], sg["scaling-config-action"][0]["vnf-config-primitive-name-ref"])),
-                          norm(str(e.exception)), "Wrong exception text")
-            # del test_vnfd["monitoring-param"]
-            # del test_vnfd["scaling-group-descriptor"]
-        with self.subTest(i=21, t='Check Input Validation: everything right'):
-            sg["scaling-config-action"][0]["vnf-config-primitive-name-ref"] = "touch"
+                test_vnfd['vnf-configuration'][0]['config-primitive'] = tmp
+        with self.subTest(i=15, t='Check Input Validation: everything right'):
             test_vnfd["id"] = "fake-vnfd-id"
             self.db.get_one.side_effect = [{"_id": did, "_admin": db_vnfd_content["_admin"]}, None]
             rc = self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
@@ -397,13 +317,14 @@ class Test_VnfdTopic(TestCase):
         return
 
     def test_edit_vnfd(self):
-        did = db_vnfd_content["_id"]
+        vnfd_content = deepcopy(db_vnfd_content)
+        did = vnfd_content["_id"]
         self.fs.file_exists.return_value = True
         self.fs.dir_ls.return_value = True
         with self.subTest(i=1, t='Normal Edition'):
             now = time()
-            self.db.get_one.side_effect = [db_vnfd_content, None]
-            data = {"id": "new-vnfd-id", "name": "new-vnfd-name"}
+            self.db.get_one.side_effect = [vnfd_content, None]
+            data = {"id": "new-vnfd-id", "product-name": "new-vnfd-name"}
             self.topic.edit(fake_session, did, data)
             db_args = self.db.replace.call_args[0]
             msg_args = self.msg.write.call_args[0]
@@ -413,30 +334,30 @@ class Test_VnfdTopic(TestCase):
             self.assertEqual(msg_args[2], data, "Wrong message content")
             self.assertEqual(db_args[0], self.topic.topic, "Wrong DB topic")
             self.assertEqual(db_args[1], did, "Wrong DB ID")
-            self.assertEqual(db_args[2]["_admin"]["created"], db_vnfd_content["_admin"]["created"],
+            self.assertEqual(db_args[2]["_admin"]["created"], vnfd_content["_admin"]["created"],
                              "Wrong creation time")
             self.assertGreater(db_args[2]["_admin"]["modified"], now,
                                "Wrong modification time")
-            self.assertEqual(db_args[2]["_admin"]["projects_read"], db_vnfd_content["_admin"]["projects_read"],
+            self.assertEqual(db_args[2]["_admin"]["projects_read"], vnfd_content["_admin"]["projects_read"],
                              "Wrong read-only project list")
-            self.assertEqual(db_args[2]["_admin"]["projects_write"], db_vnfd_content["_admin"]["projects_write"],
+            self.assertEqual(db_args[2]["_admin"]["projects_write"], vnfd_content["_admin"]["projects_write"],
                              "Wrong read-write project list")
             self.assertEqual(db_args[2]["id"], data["id"], "Wrong VNFD ID")
-            self.assertEqual(db_args[2]["name"], data["name"], "Wrong VNFD Name")
+            self.assertEqual(db_args[2]["product-name"], data["product-name"], "Wrong VNFD Name")
         with self.subTest(i=2, t='Conflict on Edit'):
-            data = {"id": "fake-vnfd-id", "name": "new-vnfd-name"}
-            self.db.get_one.side_effect = [db_vnfd_content, {"_id": str(uuid4()), "id": data["id"]}]
+            data = {"id": "fake-vnfd-id", "product-name": "new-vnfd-name"}
+            self.db.get_one.side_effect = [vnfd_content, {"_id": str(uuid4()), "id": data["id"]}]
             with self.assertRaises(EngineException, msg="Accepted existing VNFD ID") as e:
                 self.topic.edit(fake_session, did, data)
             self.assertEqual(e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code")
             self.assertIn(norm("{} with id '{}' already exists for this project".format("vnfd", data["id"])),
                           norm(str(e.exception)), "Wrong exception text")
         with self.subTest(i=3, t='Check Envelope'):
-            data = {"vnfd": {"id": "new-vnfd-id-1", "name": "new-vnfd-name"}}
+            data = {"vnfd": [{"id": "new-vnfd-id-1", "product-name": "new-vnfd-name"}]}
             with self.assertRaises(EngineException, msg="Accepted VNFD with wrong envelope") as e:
-                self.topic.edit(fake_session, did, data)
+                self.topic.edit(fake_session, did, data, content=vnfd_content)
             self.assertEqual(e.exception.http_code, HTTPStatus.BAD_REQUEST, "Wrong HTTP status code")
-            self.assertIn("'vnfd' must be a list of only one element", norm(str(e.exception)), "Wrong exception text")
+            self.assertIn("'vnfd' must be dict", norm(str(e.exception)), "Wrong exception text")
         return
 
     def test_delete_vnfd(self):
@@ -515,211 +436,205 @@ class Test_VnfdTopic(TestCase):
             self.fs.file_delete.assert_not_called()
         return
 
-    def test_validate_mgmt_interfaces_connection_points_on_valid_descriptor(self):
+    def test_validate_mgmt_interface_connection_point_on_valid_descriptor(self):
         indata = deepcopy(db_vnfd_content)
-        self.topic.validate_mgmt_interfaces_connection_points(indata)
+        self.topic.validate_mgmt_interface_connection_point(indata)
 
-    def test_validate_mgmt_interfaces_connection_points_when_missing_connection_point(self):
+    def test_validate_mgmt_interface_connection_point_when_missing_connection_point(self):
         indata = deepcopy(db_vnfd_content)
-        indata['connection-point'] = []
+        indata['ext-cpd'] = []
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_mgmt_interfaces_connection_points(indata)
+            self.topic.validate_mgmt_interface_connection_point(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("mgmt-interface:cp='{}' must match an existing connection-point"
-                           .format(indata["mgmt-interface"]["cp"])),
+        self.assertIn(norm("mgmt-cp='{}' must match an existing ext-cpd"
+                           .format(indata["mgmt-cp"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_mgmt_interfaces_connection_points_when_missing_mgmt_interface(self):
+    def test_validate_mgmt_interface_connection_point_when_missing_mgmt_cp(self):
         indata = deepcopy(db_vnfd_content)
-        indata.pop('mgmt-interface')
+        indata.pop('mgmt-cp')
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_mgmt_interfaces_connection_points(indata)
+            self.topic.validate_mgmt_interface_connection_point(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("'mgmt-interface' is a mandatory field and it is not defined"),
+        self.assertIn(norm("'mgmt-cp' is a mandatory field and it is not defined"),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_vdu_connection_point_refs_on_valid_descriptor(self):
+    def test_validate_vdu_internal_connection_points_on_valid_descriptor(self):
         indata = db_vnfd_content
         vdu = indata['vdu'][0]
-        self.topic.validate_vdu_connection_point_refs(vdu, indata)
+        self.topic.validate_vdu_internal_connection_points(vdu)
 
-    def test_validate_vdu_connection_point_refs_when_missing_internal_connection_point(self):
-        indata = deepcopy(db_vnfd_content)
-        vdu = indata['vdu'][0]
-        vdu.pop('internal-connection-point')
-        with self.assertRaises(EngineException) as e:
-            self.topic.validate_vdu_connection_point_refs(vdu, indata)
-        self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}' "
-                           "must match an existing vdu:internal-connection-point"
-                           .format(vdu["id"], vdu['interface'][1]["name"],
-                                   vdu['interface'][1]["internal-connection-point-ref"])),
-                      norm(str(e.exception)), "Wrong exception text")
+    def test_validate_external_connection_points_on_valid_descriptor(self):
+        indata = db_vnfd_content
+        self.topic.validate_external_connection_points(indata)
 
-    def test_validate_vdu_connection_point_refs_when_missing_external_connection_point(self):
+    def test_validate_external_connection_points_when_missing_internal_connection_point(self):
         indata = deepcopy(db_vnfd_content)
         vdu = indata['vdu'][0]
-        indata.pop('connection-point')
+        vdu.pop('int-cpd')
+        affected_ext_cpd = indata["ext-cpd"][0]
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_vdu_connection_point_refs(vdu, indata)
+            self.topic.validate_external_connection_points(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{}' "
-                           "must match an existing connection-point"
-                           .format(vdu["id"], vdu['interface'][0]["name"],
-                                   vdu['interface'][0]["external-connection-point-ref"])),
+        self.assertIn(norm("ext-cpd[id='{}']:int-cpd must match an existing vdu int-cpd"
+                           .format(affected_ext_cpd["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_vdu_connection_point_refs_on_duplicated_internal_connection_point_ref(self):
+    def test_validate_vdu_internal_connection_points_on_duplicated_internal_connection_point(self):
         indata = deepcopy(db_vnfd_content)
         vdu = indata['vdu'][0]
-        duplicated_interface = {'name': 'dup-mgmt-eth1', 'position': 3,
-                                'internal-connection-point-ref': 'mgmtVM-internal'}
-        vdu['interface'].insert(0, duplicated_interface)
+        duplicated_cpd = {'id': 'vnf-mgmt', 'order': 3,
+                          'virtual-network-interface-requirement': [{'name': 'duplicated'}]}
+        vdu['int-cpd'].insert(0, duplicated_cpd)
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_vdu_connection_point_refs(vdu, indata)
+            self.topic.validate_vdu_internal_connection_points(vdu)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}' "
-                           "is referenced by other interface"
-                           .format(vdu["id"], vdu['interface'][2]["name"],
-                                   vdu['interface'][2]["internal-connection-point-ref"])),
+        self.assertIn(norm("vdu[id='{}']:int-cpd[id='{}'] is already used by other int-cpd"
+                           .format(vdu["id"], duplicated_cpd["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_vdu_connection_point_refs_on_duplicated_external_connection_point_ref(self):
+    def test_validate_external_connection_points_on_duplicated_external_connection_point(self):
         indata = deepcopy(db_vnfd_content)
-        vdu = indata['vdu'][0]
-        duplicated_interface = {'name': 'dup-mgmt-eth0', 'position': 3, 'external-connection-point-ref': 'vnf-mgmt'}
-        vdu['interface'].insert(0, duplicated_interface)
+        duplicated_cpd = {'id': 'vnf-mgmt-ext', 'int-cpd': {'vdu-id': 'dataVM', 'cpd': 'vnf-data'}}
+        indata['ext-cpd'].insert(0, duplicated_cpd)
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_vdu_connection_point_refs(vdu, indata)
+            self.topic.validate_external_connection_points(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{}' "
-                           "is referenced by other interface"
-                           .format(vdu["id"], vdu['interface'][1]["name"],
-                                   vdu['interface'][1]["external-connection-point-ref"])),
+        self.assertIn(norm("ext-cpd[id='{}'] is already used by other ext-cpd"
+                           .format(duplicated_cpd["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_internal_vlds_on_valid_descriptor(self):
+    def test_validate_internal_virtual_links_on_valid_descriptor(self):
         indata = db_vnfd_content
-        self.topic.validate_internal_vlds(indata)
+        self.topic.validate_internal_virtual_links(indata)
 
-    def test_validate_internal_vlds_on_duplicated_vld(self):
+    def test_validate_internal_virtual_links_on_duplicated_ivld(self):
         indata = deepcopy(db_vnfd_content)
-        duplicated_vld = {'id': 'internal', 'name': 'internal',
-                          'internal-connection-point': [{'id-ref': 'mgmtVM-internal'}, {'id-ref': 'dataVM-internal'}]}
-        indata['internal-vld'].insert(0, duplicated_vld)
+        duplicated_vld = {'id': 'internal'}
+        indata['int-virtual-link-desc'].insert(0, duplicated_vld)
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_internal_vlds(indata)
+            self.topic.validate_internal_virtual_links(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("Duplicated VLD name '{}' in vnfd[id={}]:internal-vld[id={}]"
-                           .format(indata['internal-vld'][1]["name"], indata["id"], indata['internal-vld'][1]["id"])),
+        self.assertIn(norm("Duplicated VLD id in int-virtual-link-desc[id={}]"
+                           .format(duplicated_vld["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_internal_vlds_when_missing_internal_connection_point(self):
+    def test_validate_internal_virtual_links_when_missing_ivld_on_connection_point(self):
         indata = deepcopy(db_vnfd_content)
-        ivld = indata['internal-vld'][0]
-        icp = ivld['internal-connection-point'][0]
-        indata['vdu'][0]['internal-connection-point'].pop()
+        vdu = indata['vdu'][0]
+        affected_int_cpd = vdu['int-cpd'][0]
+        affected_int_cpd['int-virtual-link-desc'] = 'non-existing-int-virtual-link-desc'
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_internal_vlds(indata)
+            self.topic.validate_internal_virtual_links(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("internal-vld[id='{}']:internal-connection-point='{}' must match an existing "
-                           "vdu:internal-connection-point".format(ivld["id"], icp["id-ref"])),
+        self.assertIn(norm("vdu[id='{}']:int-cpd[id='{}']:int-virtual-link-desc='{}' must match an existing "
+                           "int-virtual-link-desc".format(vdu["id"], affected_int_cpd["id"],
+                                                          affected_int_cpd['int-virtual-link-desc'])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_internal_vlds_when_missing_ip_profile(self):
+    def test_validate_internal_virtual_links_when_missing_ivld_on_profile(self):
         indata = deepcopy(db_vnfd_content)
-        ivld = indata['internal-vld'][0]
-        ivld['ip-profile-ref'] = 'non-existing-ip-profile'
+        affected_ivld_profile = {'id': 'non-existing-int-virtual-link-desc'}
+        df = indata['df'][0]
+        df['virtual-link-profile'] = [affected_ivld_profile]
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_internal_vlds(indata)
+            self.topic.validate_internal_virtual_links(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("internal-vld[id='{}']:ip-profile-ref='{}' does not exist".format(
-            ivld["id"], ivld["ip-profile-ref"])),
-            norm(str(e.exception)), "Wrong exception text")
+        self.assertIn(norm("df[id='{}']:virtual-link-profile='{}' must match an existing "
+                           "int-virtual-link-desc".format(df["id"], affected_ivld_profile["id"])),
+                      norm(str(e.exception)), "Wrong exception text")
 
     def test_validate_monitoring_params_on_valid_descriptor(self):
         indata = db_vnfd_content
         self.topic.validate_monitoring_params(indata)
 
-    def test_validate_monitoring_params_when_missing_vdu(self):
+    def test_validate_monitoring_params_on_duplicated_ivld_monitoring_param(self):
         indata = deepcopy(db_vnfd_content)
-        monitoring_param = indata['monitoring-param'][0]
-        indata['vdu'].pop()
+        duplicated_mp = {'id': 'cpu', 'name': 'cpu', 'performance_metric': 'cpu'}
+        affected_ivld = indata['int-virtual-link-desc'][0]
+        affected_ivld['monitoring-parameters'] = [duplicated_mp, duplicated_mp]
         with self.assertRaises(EngineException) as e:
             self.topic.validate_monitoring_params(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("monitoring-param:vdu-monitoring-param:vdu-monitoring-param-ref='{}' not "
-                           "defined at vdu[id='{}'] or vdu does not exist"
-                           .format(monitoring_param["vdu-monitoring-param"]["vdu-monitoring-param-ref"],
-                                   monitoring_param["vdu-monitoring-param"]["vdu-ref"])),
+        self.assertIn(norm("Duplicated monitoring-parameter id in "
+                           "int-virtual-link-desc[id='{}']:monitoring-parameters[id='{}']"
+                           .format(affected_ivld["id"], duplicated_mp["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_monitoring_params_when_missing_vdu_monitoring_param_ref(self):
+    def test_validate_monitoring_params_on_duplicated_vdu_monitoring_param(self):
         indata = deepcopy(db_vnfd_content)
-        monitoring_param = indata['monitoring-param'][0]
-        indata['vdu'][1]['monitoring-param'] = []
+        duplicated_mp = {'id': 'dataVM_cpu_util', 'name': 'dataVM_cpu_util', 'performance_metric': 'cpu'}
+        affected_vdu = indata['vdu'][1]
+        affected_vdu['monitoring-parameter'].insert(0, duplicated_mp)
         with self.assertRaises(EngineException) as e:
             self.topic.validate_monitoring_params(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("monitoring-param:vdu-monitoring-param:vdu-monitoring-param-ref='{}' not "
-                           "defined at vdu[id='{}'] or vdu does not exist"
-                           .format(monitoring_param["vdu-monitoring-param"]["vdu-monitoring-param-ref"],
-                                   monitoring_param["vdu-monitoring-param"]["vdu-ref"])),
+        self.assertIn(norm("Duplicated monitoring-parameter id in "
+                           "vdu[id='{}']:monitoring-parameter[id='{}']"
+                           .format(affected_vdu["id"], duplicated_mp["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_scaling_group_descriptor_on_valid_descriptor(self):
-        indata = db_vnfd_content
-        self.topic.validate_scaling_group_descriptor(indata)
-
-    def test_validate_scaling_group_descriptor_when_missing_vnf_monitoring_param_ref(self):
+    def test_validate_monitoring_params_on_duplicated_df_monitoring_param(self):
         indata = deepcopy(db_vnfd_content)
-        sgd = indata['scaling-group-descriptor'][0]
-        sc = sgd['scaling-policy'][0]['scaling-criteria'][0]
-        indata['monitoring-param'] = []
+        duplicated_mp = {'id': 'memory', 'name': 'memory', 'performance_metric': 'memory'}
+        affected_df = indata['df'][0]
+        affected_df['monitoring-parameter'] = [duplicated_mp, duplicated_mp]
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_scaling_group_descriptor(indata)
+            self.topic.validate_monitoring_params(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("scaling-group-descriptor[name='{}']:scaling-criteria[name='{}']:"
-                           "vnf-monitoring-param-ref='{}' not defined in any monitoring-param"
-                           .format(sgd["name"], sc["name"], sc["vnf-monitoring-param-ref"])),
+        self.assertIn(norm("Duplicated monitoring-parameter id in "
+                           "df[id='{}']:monitoring-parameter[id='{}']"
+                           .format(affected_df["id"], duplicated_mp["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_scaling_group_descriptor_when_missing_vdu(self):
+    def test_validate_scaling_group_descriptor_on_valid_descriptor(self):
+        indata = db_vnfd_content
+        self.topic.validate_scaling_group_descriptor(indata)
+
+    def test_validate_scaling_group_descriptor_when_missing_monitoring_param(self):
         indata = deepcopy(db_vnfd_content)
-        sgd = indata['scaling-group-descriptor'][0]
-        sgd_vdu = sgd['vdu'][0]
-        indata['vdu'].pop()
+        vdu = indata['vdu'][1]
+        affected_df = indata['df'][0]
+        affected_sa = affected_df['scaling-aspect'][0]
+        affected_sp = affected_sa['scaling-policy'][0]
+        affected_sc = affected_sp['scaling-criteria'][0]
+        vdu.pop('monitoring-parameter')
         with self.assertRaises(EngineException) as e:
             self.topic.validate_scaling_group_descriptor(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("scaling-group-descriptor[name='{}']:vdu-id-ref={} does not match any vdu"
-                           .format(sgd["name"], sgd_vdu["vdu-id-ref"])),
+        self.assertIn(norm("df[id='{}']:scaling-aspect[id='{}']:scaling-policy"
+                           "[name='{}']:scaling-criteria[name='{}']: "
+                           "vnf-monitoring-param-ref='{}' not defined in any monitoring-param"
+                           .format(affected_df["id"], affected_sa["id"], affected_sp["name"], affected_sc["name"],
+                                   affected_sc["vnf-monitoring-param-ref"])),
                       norm(str(e.exception)), "Wrong exception text")
 
     def test_validate_scaling_group_descriptor_when_missing_vnf_configuration(self):
         indata = deepcopy(db_vnfd_content)
-        sgd = indata['scaling-group-descriptor'][0]
+        df = indata['df'][0]
+        affected_sa = df['scaling-aspect'][0]
         indata.pop('vnf-configuration')
         with self.assertRaises(EngineException) as e:
             self.topic.validate_scaling_group_descriptor(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("'vnf-configuration' not defined in the descriptor but it is referenced by "
-                           "scaling-group-descriptor[name='{}']:scaling-config-action"
-                           .format(sgd["name"])),
+        self.assertIn(norm("'vnf-configuration' not defined in the descriptor but it is referenced "
+                           "by df[id='{}']:scaling-aspect[id='{}']:scaling-config-action"
+                           .format(df["id"], affected_sa["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
     def test_validate_scaling_group_descriptor_when_missing_scaling_config_action_primitive(self):
         indata = deepcopy(db_vnfd_content)
-        sgd = indata['scaling-group-descriptor'][0]
-        sca = sgd['scaling-config-action'][0]
-        indata['vnf-configuration']['config-primitive'] = []
+        df = indata['df'][0]
+        affected_sa = df['scaling-aspect'][0]
+        affected_sca_primitive = affected_sa['scaling-config-action'][0]['vnf-config-primitive-name-ref']
+        indata['vnf-configuration'][0]['config-primitive'] = []
         with self.assertRaises(EngineException) as e:
             self.topic.validate_scaling_group_descriptor(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("scaling-group-descriptor[name='{}']:scaling-config-action:vnf-config-"
-                           "primitive-name-ref='{}' does not match any "
+        self.assertIn(norm("df[id='{}']:scaling-aspect[id='{}']:scaling-config-action:vnf-"
+                           "config-primitive-name-ref='{}' does not match any "
                            "vnf-configuration:config-primitive:name"
-                           .format(sgd["name"], sca["vnf-config-primitive-name-ref"])),
+                           .format(df["id"], affected_sa["id"], affected_sca_primitive)),
                       norm(str(e.exception)), "Wrong exception text")
 
 
@@ -821,8 +736,8 @@ class Test_NsdTopic(TestCase):
             finally:
                 del test_nsd["extra-property"]
         with self.subTest(i=4, t='Check Pyangbind Validation: property types'):
-            tmp = test_nsd["short-name"]
-            test_nsd["short-name"] = {"key": 0}
+            tmp = test_nsd["designer"]
+            test_nsd["designer"] = {"key": 0}
             try:
                 with self.assertRaises(EngineException, msg="Accepted NSD with a wrongly typed property") as e:
                     self.topic.upload_content(fake_session, did, test_nsd, {}, {"Content-Type": []})
@@ -831,111 +746,65 @@ class Test_NsdTopic(TestCase):
                                    .format("json object contained a key that did not exist", "key")),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                test_nsd["short-name"] = tmp
-        with self.subTest(i=5, t='Check Input Validation: vld[mgmt-network+ip-profile]'):
-            tmp = test_nsd["vld"][0]["vim-network-name"]
-            del test_nsd["vld"][0]["vim-network-name"]
-            test_nsd["vld"][0]["ip-profile-ref"] = "fake-ip-profile"
+                test_nsd["designer"] = tmp
+        with self.subTest(i=5, t='Check Input Validation: mgmt-network+virtual-link-protocol-data'):
+            df = test_nsd['df'][0]
+            mgmt_profile = {'id': 'id', 'virtual-link-desc-id': 'mgmt',
+                            'virtual-link-protocol-data': {'associated-layer-protocol': 'ipv4'}}
+            df['virtual-link-profile'] = [mgmt_profile]
             try:
                 with self.assertRaises(EngineException, msg="Accepted VLD with mgmt-network+ip-profile") as e:
                     self.topic.upload_content(fake_session, did, test_nsd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("Error at vld[id='{}']:ip-profile-ref"
-                                   " You cannot set an ip-profile when mgmt-network is True"
-                                   .format(test_nsd["vld"][0]["id"])),
+                self.assertIn(norm("Error at df[id='{}']:virtual-link-profile[id='{}']:virtual-link-protocol-data"
+                                   " You cannot set a virtual-link-protocol-data when mgmt-network is True"
+                                   .format(df["id"], mgmt_profile["id"])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                test_nsd["vld"][0]["vim-network-name"] = tmp
-                del test_nsd["vld"][0]["ip-profile-ref"]
-        with self.subTest(i=6, t='Check Input Validation: vld[vnfd-connection-point-ref][vnfd-id-ref]'):
-            tmp = test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["vnfd-id-ref"]
-            test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["vnfd-id-ref"] = "wrong-vnfd-id-ref"
-            try:
-                with self.assertRaises(EngineException, msg="Accepted VLD with wrong vnfd-connection-point-ref") as e:
-                    self.topic.upload_content(fake_session, did, test_nsd, {}, {"Content-Type": []})
-                self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("Error at vld[id='{}']:vnfd-connection-point-ref[vnfd-id-ref='{}']"
-                                   " does not match constituent-vnfd[member-vnf-index='{}']:vnfd-id-ref '{}'"
-                                   .format(test_nsd["vld"][0]["id"],
-                                           test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["vnfd-id-ref"],
-                                           test_nsd["constituent-vnfd"][0]["member-vnf-index"],
-                                           test_nsd["constituent-vnfd"][0]["vnfd-id-ref"])),
-                              norm(str(e.exception)), "Wrong exception text")
-            finally:
-                test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["vnfd-id-ref"] = tmp
-        with self.subTest(i=7, t='Check Input Validation: vld[vnfd-connection-point-ref][member-vnf-index-ref]'):
-            tmp = test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["member-vnf-index-ref"]
-            test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["member-vnf-index-ref"] = "wrong-member-vnf-index-ref"
-            try:
-                with self.assertRaises(EngineException, msg="Accepted VLD with wrong vnfd-connection-point-ref") as e:
-                    self.topic.upload_content(fake_session, did, test_nsd, {}, {"Content-Type": []})
-                self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}']"
-                                   " does not match any constituent-vnfd:member-vnf-index"
-                                   .format(test_nsd["vld"][0]["id"],
-                                           test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["member-vnf-index-ref"])),
-                              norm(str(e.exception)), "Wrong exception text")
-            finally:
-                test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["member-vnf-index-ref"] = tmp
-        with self.subTest(i=8, t='Check Input Validation: vnffgd[classifier][rsp-id-ref]'):
-            test_nsd["vnffgd"] = [{"id": "fake-vnffgd-id",
-                                   "rsp": [{"id": "fake-rsp-id"}],
-                                   "classifier": [{"id": "fake-vnffgd-classifier-id", "rsp-id-ref": "wrong-rsp-id"}]}]
-            try:
-                with self.assertRaises(EngineException, msg="Accepted VNF FGD with wrong classifier rsp-id-ref") as e:
-                    self.topic.upload_content(fake_session, did, test_nsd, {}, {"Content-Type": []})
-                self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("Error at vnffgd[id='{}']:classifier[id='{}']:rsp-id-ref '{}'"
-                                   " does not match any rsp:id"
-                                   .format(test_nsd["vnffgd"][0]["id"],
-                                           test_nsd["vnffgd"][0]["classifier"][0]["id"],
-                                           test_nsd["vnffgd"][0]["classifier"][0]["rsp-id-ref"])),
-                              norm(str(e.exception)), "Wrong exception text")
-            finally:
-                test_nsd["vnffgd"][0]["classifier"][0]["rsp-id-ref"] = "fake-rsp-id"
-        with self.subTest(i=9, t='Check Descriptor Dependencies: constituent-vnfd[vnfd-id-ref]'):
+                del df['virtual-link-profile']
+        with self.subTest(i=6, t='Check Descriptor Dependencies: constituent-vnfd[vnfd-id-ref]'):
             self.db.get_one.side_effect = [{"_id": did, "_admin": db_nsd_content["_admin"]}, None]
             self.db.get_list.return_value = []
             try:
                 with self.assertRaises(EngineException, msg="Accepted wrong constituent VNFD ID reference") as e:
                     self.topic.upload_content(fake_session, did, test_nsd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code")
-                self.assertIn(norm("Descriptor error at 'constituent-vnfd':'vnfd-id-ref'='{}'"
-                                   " references a non existing vnfd"
-                                   .format(test_nsd["constituent-vnfd"][0]["vnfd-id-ref"])),
+                self.assertIn(norm("'vnfd-id'='{}' references a non existing vnfd".format(test_nsd['vnfd-id'][0])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
                 pass
-        with self.subTest(i=10, t='Check Descriptor Dependencies: '
-                                  'vld[vnfd-connection-point-ref][vnfd-connection-point-ref]'):
-            tmp = test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["vnfd-connection-point-ref"]
-            test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["vnfd-connection-point-ref"] = "wrong-vnfd-cp-ref"
+        with self.subTest(i=7, t='Check Descriptor Dependencies: '
+                                 'vld[vnfd-connection-point-ref][vnfd-connection-point-ref]'):
+            vnfd_descriptor = deepcopy(db_vnfd_content)
+            df = test_nsd['df'][0]
+            affected_vnf_profile = df['vnf-profile'][0]
+            affected_virtual_link = affected_vnf_profile['virtual-link-connectivity'][1]
+            affected_cpd = vnfd_descriptor['ext-cpd'].pop()
             self.db.get_one.side_effect = [{"_id": did, "_admin": db_nsd_content["_admin"]}, None]
-            self.db.get_list.return_value = [db_vnfd_content]
+            self.db.get_list.return_value = [vnfd_descriptor]
             try:
                 with self.assertRaises(EngineException, msg="Accepted wrong VLD CP reference") as e:
                     self.topic.upload_content(fake_session, did, test_nsd, {}, {"Content-Type": []})
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-                self.assertIn(norm("Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}']:"
-                                   "vnfd-connection-point-ref='{}' references a non existing conection-point:name"
-                                   " inside vnfd '{}'"
-                                   .format(test_nsd["vld"][0]["id"],
-                                           test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["member-vnf-index-ref"],
-                                           test_nsd["vld"][0]["vnfd-connection-point-ref"][0]
-                                           ["vnfd-connection-point-ref"],
-                                           db_vnfd_content["id"])),
+                self.assertIn(norm("Error at df[id='{}']:vnf-profile[id='{}']:virtual-link-connectivity"
+                                   "[virtual-link-profile-id='{}']:constituent-cpd-id='{}' references a "
+                                   "non existing ext-cpd:id inside vnfd '{}'"
+                                   .format(df["id"], affected_vnf_profile["id"],
+                                           affected_virtual_link["virtual-link-profile-id"], affected_cpd["id"],
+                                           vnfd_descriptor["id"])),
                               norm(str(e.exception)), "Wrong exception text")
             finally:
-                test_nsd["vld"][0]["vnfd-connection-point-ref"][0]["vnfd-connection-point-ref"] = tmp
+                pass
         return
 
     def test_edit_nsd(self):
-        did = db_nsd_content["_id"]
+        nsd_content = deepcopy(db_nsd_content)
+        did = nsd_content["_id"]
         self.fs.file_exists.return_value = True
         self.fs.dir_ls.return_value = True
         with self.subTest(i=1, t='Normal Edition'):
             now = time()
-            self.db.get_one.side_effect = [db_nsd_content, None]
+            self.db.get_one.side_effect = [nsd_content, None]
             self.db.get_list.return_value = [db_vnfd_content]
             data = {"id": "new-nsd-id", "name": "new-nsd-name"}
             self.topic.edit(fake_session, did, data)
@@ -947,27 +816,28 @@ class Test_NsdTopic(TestCase):
             self.assertEqual(msg_args[2], data, "Wrong message content")
             self.assertEqual(db_args[0], self.topic.topic, "Wrong DB topic")
             self.assertEqual(db_args[1], did, "Wrong DB ID")
-            self.assertEqual(db_args[2]["_admin"]["created"], db_nsd_content["_admin"]["created"],
+            self.assertEqual(db_args[2]["_admin"]["created"], nsd_content["_admin"]["created"],
                              "Wrong creation time")
             self.assertGreater(db_args[2]["_admin"]["modified"], now, "Wrong modification time")
-            self.assertEqual(db_args[2]["_admin"]["projects_read"], db_nsd_content["_admin"]["projects_read"],
+            self.assertEqual(db_args[2]["_admin"]["projects_read"], nsd_content["_admin"]["projects_read"],
                              "Wrong read-only project list")
-            self.assertEqual(db_args[2]["_admin"]["projects_write"], db_nsd_content["_admin"]["projects_write"],
+            self.assertEqual(db_args[2]["_admin"]["projects_write"], nsd_content["_admin"]["projects_write"],
                              "Wrong read-write project list")
             self.assertEqual(db_args[2]["id"], data["id"], "Wrong NSD ID")
             self.assertEqual(db_args[2]["name"], data["name"], "Wrong NSD Name")
         with self.subTest(i=2, t='Conflict on Edit'):
             data = {"id": "fake-nsd-id", "name": "new-nsd-name"}
-            self.db.get_one.side_effect = [db_nsd_content, {"_id": str(uuid4()), "id": data["id"]}]
+            self.db.get_one.side_effect = [nsd_content, {"_id": str(uuid4()), "id": data["id"]}]
             with self.assertRaises(EngineException, msg="Accepted existing NSD ID") as e:
                 self.topic.edit(fake_session, did, data)
             self.assertEqual(e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code")
             self.assertIn(norm("{} with id '{}' already exists for this project".format("nsd", data["id"])),
                           norm(str(e.exception)), "Wrong exception text")
         with self.subTest(i=3, t='Check Envelope'):
-            data = {"nsd": {"id": "new-nsd-id", "name": "new-nsd-name"}}
+            data = {"nsd": {"nsd": {"id": "new-nsd-id", "name": "new-nsd-name"}}}
+            self.db.get_one.side_effect = [nsd_content, None]
             with self.assertRaises(EngineException, msg="Accepted NSD with wrong envelope") as e:
-                self.topic.edit(fake_session, did, data)
+                self.topic.edit(fake_session, did, data, content=nsd_content)
             self.assertEqual(e.exception.http_code, HTTPStatus.BAD_REQUEST, "Wrong HTTP status code")
             self.assertIn("'nsd' must be a list of only one element", norm(str(e.exception)), "Wrong exception text")
         return
@@ -1047,95 +917,82 @@ class Test_NsdTopic(TestCase):
             self.fs.file_delete.assert_not_called()
         return
 
-    def test_validate_vld_mgmt_network_with_ip_profile_ref_on_valid_descriptor(self):
+    def test_validate_vld_mgmt_network_with_virtual_link_protocol_data_on_valid_descriptor(self):
         indata = deepcopy(db_nsd_content)
-        vld = indata['vld'][0]
-        self.topic.validate_vld_mgmt_network_with_ip_profile_ref(vld)
+        vld = indata['virtual-link-desc'][0]
+        self.topic.validate_vld_mgmt_network_with_virtual_link_protocol_data(vld, indata)
 
-    def test_validate_vld_mgmt_network_with_ip_profile_ref_when_both_defined(self):
+    def test_validate_vld_mgmt_network_with_virtual_link_protocol_data_when_both_defined(self):
         indata = deepcopy(db_nsd_content)
-        vld = indata['vld'][0]
-        vld['ip-profile-ref'] = 'a-profile-ref'
+        vld = indata['virtual-link-desc'][0]
+        df = indata['df'][0]
+        affected_vlp = {'id': 'id', 'virtual-link-desc-id': 'mgmt',
+                        'virtual-link-protocol-data': {'associated-layer-protocol': 'ipv4'}}
+        df['virtual-link-profile'] = [affected_vlp]
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_vld_mgmt_network_with_ip_profile_ref(vld)
+            self.topic.validate_vld_mgmt_network_with_virtual_link_protocol_data(vld, indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("Error at vld[id='{}']:ip-profile-ref"
-                           " You cannot set an ip-profile when mgmt-network is True"
-                           .format(vld["id"])),
+        self.assertIn(norm("Error at df[id='{}']:virtual-link-profile[id='{}']:virtual-link-protocol-data"
+                           " You cannot set a virtual-link-protocol-data when mgmt-network is True"
+                           .format(df["id"], affected_vlp["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_vld_connection_point_refs_on_valid_descriptor(self):
+    def test_validate_vnf_profiles_vnfd_id_on_valid_descriptor(self):
         indata = deepcopy(db_nsd_content)
-        vld = indata['vld'][0]
-        self.topic.validate_vld_connection_point_refs(vld, indata)
-
-    def test_validate_vld_connection_point_refs_when_missing_constituent_vnfd(self):
-        indata = deepcopy(db_nsd_content)
-        vld = indata['vld'][0]
-        vnfd_cp = vld['vnfd-connection-point-ref'][0]
-        indata['constituent-vnfd'] = []
-        with self.assertRaises(EngineException) as e:
-            self.topic.validate_vld_connection_point_refs(vld, indata)
-        self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}'] "
-                           "does not match any constituent-vnfd:member-vnf-index"
-                           .format(vld["id"], vnfd_cp["member-vnf-index-ref"])),
-                      norm(str(e.exception)), "Wrong exception text")
+        self.topic.validate_vnf_profiles_vnfd_id(indata)
 
-    def test_validate_vld_connection_point_refs_on_unmatched_constituent_vnfd(self):
+    def test_validate_vnf_profiles_vnfd_id_when_missing_vnfd(self):
         indata = deepcopy(db_nsd_content)
-        vld = indata['vld'][0]
-        vnfd_cp = vld['vnfd-connection-point-ref'][0]
-        constituent_vnfd = indata['constituent-vnfd'][0]
-        constituent_vnfd['vnfd-id-ref'] = 'unmatched-vnfd-id-ref'
+        df = indata['df'][0]
+        affected_vnf_profile = df['vnf-profile'][0]
+        indata['vnfd-id'] = ['non-existing-vnfd']
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_vld_connection_point_refs(vld, indata)
+            self.topic.validate_vnf_profiles_vnfd_id(indata)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("Error at vld[id='{}']:vnfd-connection-point-ref[vnfd-id-ref='{}'] "
-                           "does not match constituent-vnfd[member-vnf-index='{}']:vnfd-id-ref"
-                           " '{}'".format(vld["id"], vnfd_cp["vnfd-id-ref"],
-                                          constituent_vnfd["member-vnf-index"],
-                                          constituent_vnfd["vnfd-id-ref"])),
+        self.assertIn(norm("Error at df[id='{}']:vnf_profile[id='{}']:vnfd-id='{}' "
+                           "does not match any vnfd-id"
+                           .format(df["id"], affected_vnf_profile["id"], affected_vnf_profile['vnfd-id'])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_validate_vld_connection_point_refs_vnfd_connection_points_on_valid_descriptor(self):
+    def test_validate_df_vnf_profiles_constituent_connection_points_on_valid_descriptor(self):
         nsd_descriptor = deepcopy(db_nsd_content)
-        vnfd_1_descriptor = deepcopy(db_vnfd_content)
-        vnfd_2_descriptor = deepcopy(db_vnfd_content)
-        vld = nsd_descriptor['vld'][0]
-        member_vnfd_index = {'1': vnfd_1_descriptor, '2': vnfd_2_descriptor}
-        self.topic.validate_vld_connection_point_refs_vnfd_connection_points(vld, member_vnfd_index)
+        vnfd_descriptor = deepcopy(db_vnfd_content)
+        df = nsd_descriptor['df'][0]
+        vnfds_index = {vnfd_descriptor['id']: vnfd_descriptor}
+        self.topic.validate_df_vnf_profiles_constituent_connection_points(df, vnfds_index)
 
-    def test_validate_vld_connection_point_refs_vnfd_connection_points_when_missing_connection_point(self):
+    def test_validate_df_vnf_profiles_constituent_connection_points_when_missing_connection_point(self):
         nsd_descriptor = deepcopy(db_nsd_content)
-        vnfd_1_descriptor = deepcopy(db_vnfd_content)
-        vnfd_2_descriptor = deepcopy(db_vnfd_content)
-        vld = nsd_descriptor['vld'][0]
-        referenced_vnfd_cp = vld['vnfd-connection-point-ref'][0]
-        member_vnfd_index = {'1': vnfd_1_descriptor, '2': vnfd_2_descriptor}
-        vnfd_1_descriptor['connection-point'] = []
+        vnfd_descriptor = deepcopy(db_vnfd_content)
+        df = nsd_descriptor['df'][0]
+        affected_vnf_profile = df['vnf-profile'][0]
+        affected_virtual_link = affected_vnf_profile['virtual-link-connectivity'][1]
+        vnfds_index = {vnfd_descriptor['id']: vnfd_descriptor}
+        affected_cpd = vnfd_descriptor['ext-cpd'].pop()
         with self.assertRaises(EngineException) as e:
-            self.topic.validate_vld_connection_point_refs_vnfd_connection_points(vld, member_vnfd_index)
+            self.topic.validate_df_vnf_profiles_constituent_connection_points(df, vnfds_index)
         self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
-        self.assertIn(norm("Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}']:vnfd-"
-                           "connection-point-ref='{}' references a non existing conection-point:name inside vnfd '{}'"
-                           .format(vld["id"], referenced_vnfd_cp["member-vnf-index-ref"],
-                                   referenced_vnfd_cp["vnfd-connection-point-ref"], vnfd_1_descriptor["id"])),
+        self.assertIn(norm("Error at df[id='{}']:vnf-profile[id='{}']:virtual-link-connectivity"
+                           "[virtual-link-profile-id='{}']:constituent-cpd-id='{}' references a "
+                           "non existing ext-cpd:id inside vnfd '{}'"
+                           .format(df["id"], affected_vnf_profile["id"],
+                                   affected_virtual_link["virtual-link-profile-id"], affected_cpd["id"],
+                                   vnfd_descriptor["id"])),
                       norm(str(e.exception)), "Wrong exception text")
 
-    def test_check_conflict_on_edit_when_missing_vnfd_index(self):
+    def test_check_conflict_on_edit_when_missing_constituent_vnfd_id(self):
         nsd_descriptor = deepcopy(db_nsd_content)
-        invalid_vnfd_id_ref = 'invalid-vnfd-id-ref'
+        invalid_vnfd_id = 'invalid-vnfd-id'
         nsd_descriptor['id'] = 'invalid-vnfd-id-ns'
-        nsd_descriptor['constituent-vnfd'][0]['vnfd-id-ref'] = invalid_vnfd_id_ref
-        nsd_descriptor['vld'][0]['vnfd-connection-point-ref'][0]['vnfd-id-ref'] = invalid_vnfd_id_ref
-        nsd_descriptor['vld'][1]['vnfd-connection-point-ref'][0]['vnfd-id-ref'] = invalid_vnfd_id_ref
+        nsd_descriptor['vnfd-id'][0] = invalid_vnfd_id
+        nsd_descriptor['df'][0]['vnf-profile'][0]['vnfd-id'] = invalid_vnfd_id
+        nsd_descriptor['df'][0]['vnf-profile'][1]['vnfd-id'] = invalid_vnfd_id
         with self.assertRaises(EngineException) as e:
             self.db.get_list.return_value = []
             self.topic.check_conflict_on_edit(fake_session, nsd_descriptor, [], 'id')
         self.assertEqual(e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code")
-        self.assertIn(norm("Descriptor error at 'constituent-vnfd':'vnfd-id-ref'='{}' references a non "
-                           "existing vnfd".format(invalid_vnfd_id_ref)),
+        self.assertIn(norm("Descriptor error at 'vnfd-id'='{}' references a non "
+                           "existing vnfd".format(invalid_vnfd_id)),
                       norm(str(e.exception)), "Wrong exception text")
 
 
index 9dc5cc1..bff7775 100644 (file)
@@ -171,8 +171,10 @@ class TestNsrTopic(unittest.TestCase):
             "nsdId": self.nsd_id,
             "nsName": "name",
             "vimAccountId": self.vim_id,
-            "additionalParamsForVnf": [{"member-vnf-index": "1", "additionalParams": {"touch_filename": "file"}},
-                                       {"member-vnf-index": "2", "additionalParams": {"touch_filename": "file"}}]
+            "additionalParamsForVnf": [
+                {"member-vnf-index": "hackfest_vnf1", "additionalParams": {"touch_filename": "file"}},
+                {"member-vnf-index": "hackfest_vnf2", "additionalParams": {"touch_filename": "file"}}
+            ]
         }
         rollback = []
         headers = {}
@@ -210,8 +212,7 @@ class TestNsrTopic(unittest.TestCase):
             self.assertTrue(created_item["_admin"]["nsState"] == "NOT_INSTANTIATED",
                             "Database record must contain '_admin.nstate=NOT INSTANTIATE'")
 
-        self.assertEqual(len(created_vnfrs), len(self.nsd["constituent-vnfd"]),
-                         "created a mismatch number of vnfr at database")
+        self.assertEqual(len(created_vnfrs), 2, "created a mismatch number of vnfr at database")
         self.assertEqual(len(created_nsrs), 1, "Only one nsrs must be created at database")
         self.assertEqual(len(rollback), len(created_vnfrs) + 1, "rollback mismatch with created items at database")
 
index 80fe8d3..4552e24 100644 (file)
@@ -42,143 +42,167 @@ db_vnfds_text = """
         usageState: NOT_IN_USE
         userDefinedData: {}
     _id: 7637bcf8-cf14-42dc-ad70-c66fcf1e6e77
-    connection-point:
-    -   id: vnf-mgmt
-        name: vnf-mgmt
-        short-name: vnf-mgmt
-        type: VPORT
-    -   id: vnf-data
-        name: vnf-data
-        short-name: vnf-data
-        type: VPORT
-    description: A VNF consisting of 2 VDUs connected to an internal VL, and one VDU
-        with cloud-init
     id: hackfest3charmed-vnf
-    internal-vld:
-    -   id: internal
-        internal-connection-point:
-        -   id-ref: mgmtVM-internal
-        -   id-ref: dataVM-internal
-        name: internal
-        short-name: internal
-        type: ELAN
-    logo: osm.png
-    mgmt-interface:
-        cp: vnf-mgmt
-    monitoring-param:
-    -   aggregation-type: AVERAGE
-        id: monitor1
-        name: monitor1
-        vdu-monitoring-param:
-            vdu-monitoring-param-ref: dataVM_cpu_util
-            vdu-ref: dataVM
-    name: hackfest3charmed-vnf
-    scaling-group-descriptor:
-    -   max-instance-count: 10
-        name: scale_dataVM
-        scaling-config-action:
-        -   trigger: post-scale-out
-            vnf-config-primitive-name-ref: touch
-        -   trigger: pre-scale-in
-            vnf-config-primitive-name-ref: touch
-        scaling-policy:
-        -   cooldown-time: 60
-            name: auto_cpu_util_above_threshold
-            scaling-criteria:
-            -   name: cpu_util_above_threshold
-                scale-in-relational-operation: LE
-                scale-in-threshold: '15.0000000000'
-                scale-out-relational-operation: GE
-                scale-out-threshold: '60.0000000000'
-                vnf-monitoring-param-ref: monitor1
-            scaling-type: automatic
-            threshold-time: 0
-        vdu:
-        -   count: 1
-            vdu-id-ref: dataVM
-    short-name: hackfest3charmed-vnf
+    description: >-
+      A VNF consisting of 2 VDUs connected to an internal VL, and one VDU with
+      cloud-init
+    product-name: hackfest3charmed-vnf
+    version: '1.0'
+    mgmt-cp: vnf-mgmt-ext
+  
+    virtual-compute-desc:
+      - id: mgmt-compute
+        virtual-cpu:
+          num-virtual-cpu: 2
+        virtual-memory:
+          size: '2'
+      - id: data-compute
+        virtual-cpu:
+          num-virtual-cpu: 2
+        virtual-memory:
+          size: '2'
+  
+    virtual-storage-desc:
+      - id: mgmt-storage
+        size-of-storage: '20'
+      - id: data-storage
+        size-of-storage: '20'
+  
+    sw-image-desc:
+      - id: hackfest3-mgmt
+        name: hackfest3-mgmt
+  
     vdu:
-    -   count: '1'
-        cloud-init-file: cloud-config.txt
-        id: mgmtVM
-        image: hackfest3-mgmt
-        interface:
-        -   external-connection-point-ref: vnf-mgmt
-            name: mgmtVM-eth0
-            position: 1
-            type: EXTERNAL
-            virtual-interface:
-                type: VIRTIO
-        -   internal-connection-point-ref: mgmtVM-internal
-            name: mgmtVM-eth1
-            position: 2
-            type: INTERNAL
-            virtual-interface:
-                type: VIRTIO
-        internal-connection-point:
-        -   id: mgmtVM-internal
-            name: mgmtVM-internal
-            short-name: mgmtVM-internal
-            type: VPORT
+      - id: mgmtVM
         name: mgmtVM
-        vm-flavor:
-            memory-mb: '1024'
-            storage-gb: '10'
-            vcpu-count: 1
-    -   count: '1'
-        id: dataVM
-        image: hackfest3-mgmt
-        interface:
-        -   internal-connection-point-ref: dataVM-internal
-            name: dataVM-eth0
-            position: 1
-            type: INTERNAL
-            virtual-interface:
-                type: VIRTIO
-        -   external-connection-point-ref: vnf-data
-            name: dataVM-xe0
-            position: 2
-            type: EXTERNAL
-            virtual-interface:
-                type: VIRTIO
-        internal-connection-point:
-        -   id: dataVM-internal
-            name: dataVM-internal
-            short-name: dataVM-internal
-            type: VPORT
-        monitoring-param:
-        -   id: dataVM_cpu_util
-            nfvi-metric: cpu_utilization
+        cloud-init-file: cloud-config.txt
+        sw-image-desc: hackfest3-mgmt
+        virtual-compute-desc: mgmt-compute
+        virtual-storage-desc:
+          - mgmt-storage
+        int-cpd:
+          - id: vnf-mgmt
+            virtual-network-interface-requirement:
+              - name: mgmtVM-eth0
+                position: 1
+                virtual-interface:
+                  type: VIRTIO
+          - id: mgmtVM-internal
+            int-virtual-link-desc: internal
+            virtual-network-interface-requirement:
+              - name: mgmtVM-eth1
+                position: 2
+                virtual-interface:
+                  type: VIRTIO
+      - id: dataVM
         name: dataVM
-        vm-flavor:
-            memory-mb: '1024'
-            storage-gb: '10'
-            vcpu-count: 1
-    version: '1.0'
+        sw-image-desc: hackfest3-mgmt
+        virtual-compute-desc: data-compute
+        virtual-storage-desc:
+          - data-storage
+        int-cpd:
+          - id: dataVM-internal
+            int-virtual-link-desc: internal
+            virtual-network-interface-requirement:
+              - name: dataVM-eth1
+                position: 1
+                virtual-interface:
+                  type: VIRTIO
+          - id: vnf-data
+            virtual-network-interface-requirement:
+              - name: dataVM-eth0
+                position: 2
+                virtual-interface:
+                  type: VIRTIO
+        monitoring-parameter:
+          - id: dataVM_cpu_util
+            name: dataVM_cpu_util
+            performance-metric: cpu_utilization
+  
+    int-virtual-link-desc:
+      - id: internal
+  
+    ext-cpd:
+      - id: vnf-mgmt-ext
+        int-cpd: # Connection to int-cpd
+          vdu-id: mgmtVM
+          cpd: vnf-mgmt
+      - id: vnf-data-ext
+        int-cpd: # Connection to int-cpd
+          vdu-id: dataVM
+          cpd: vnf-data
+  
+    df:
+      - id: hackfest_default
+        vnf-configuration-id: vnf-configuration-example
+        vdu-profile:
+          - id: mgmtVM
+            min-number-of-instances: 1
+          - id: dataVM
+            min-number-of-instances: 1
+            max-number-of-instances: 10
+            vdu-configuration-id: vdu-configuration-example
+        instantiation-level:
+          - id: default
+            vdu-level:
+              - vdu-id: mgmtVM
+                number-of-instances: 1
+              - vdu-id: dataVM
+                number-of-instances: 1
+        scaling-aspect:
+          - id: scale_dataVM
+            name: scale_dataVM
+            max-scale-level: 10
+            aspect-delta-details:
+              deltas:
+                - id: delta1
+                  vdu-delta:
+                    - id: vdudelta1
+                      number-of-instances: 1
+            scaling-policy:
+              - name: auto_cpu_util_above_threshold
+                scaling-type: automatic
+                enabled: true
+                threshold-time: 0
+                cooldown-time: 60
+                scaling-criteria:
+                  - name: cpu_util_above_threshold
+                    scale-in-relational-operation: LE
+                    scale-in-threshold: '15.0000000000'
+                    scale-out-relational-operation: GE
+                    scale-out-threshold: '60.0000000000'
+                    vnf-monitoring-param-ref: dataVM_cpu_util
+            scaling-config-action:
+              - trigger: post-scale-out
+                vnf-config-primitive-name-ref: touch
+              - trigger: pre-scale-in
+                vnf-config-primitive-name-ref: touch
+  
     vnf-configuration:
-        config-primitive:
-        -   name: touch
-            parameter:
-            -   data-type: STRING
-                default-value: <touch_filename2>
-                name: filename
+      - id: vnf-configuration-example
         initial-config-primitive:
-        -   name: config
+          - seq: "1"
+            name: config
             parameter:
-            -   name: ssh-hostname
+              - name: ssh-hostname
                 value: <rw_mgmt_ip>
-            -   name: ssh-username
+              - name: ssh-username
                 value: ubuntu
-            -   name: ssh-password
+              - name: ssh-password
                 value: osm4u
-            seq: '1'
-        -   name: touch
+          - seq: "2"
+            name: touch
             parameter:
-            -   name: filename
+              - name: filename
                 value: <touch_filename>
-            seq: '2'
+        config-primitive:
+          - name: touch
+            parameter:
+              - data-type: STRING
+                default-value: <touch_filename2>
+                name: filename
         juju:
-            charm: simple
+          charm: simple
 """
 
 db_nsds_text = """
@@ -202,41 +226,44 @@ db_nsds_text = """
         usageState: NOT_IN_USE
         userDefinedData: {}
     _id: 8c2f8b95-bb1b-47ee-8001-36dc090678da
-    constituent-vnfd:
-    -   member-vnf-index: '1'
-        vnfd-id-ref: hackfest3charmed-vnf
-    -   member-vnf-index: '2'
-        vnfd-id-ref: hackfest3charmed-vnf
-    description: NS with 2 VNFs hackfest3charmed-vnf connected by datanet and mgmtnet
-        VLs
     id: hackfest3charmed-ns
-    logo: osm.png
     name: hackfest3charmed-ns
-    short-name: hackfest3charmed-ns
+    description: NS with 2 VNFs hackfest3charmed-vnf connected by datanet and mgmtnet VLs
+    designer: OSM
     version: '1.0'
-    vld:
-    -   id: mgmt
+
+    vnfd-id:
+      - hackfest3charmed-vnf
+
+    virtual-link-desc:
+      - id: mgmt
         mgmt-network: true
-        name: mgmt
-        short-name: mgmt
-        type: ELAN
-        vim-network-name: mgmt
-        vnfd-connection-point-ref:
-        -   member-vnf-index-ref: '1'
-            vnfd-connection-point-ref: vnf-mgmt
-            vnfd-id-ref: hackfest3charmed-vnf
-        -   member-vnf-index-ref: '2'
-            vnfd-connection-point-ref: vnf-mgmt
-            vnfd-id-ref: hackfest3charmed-vnf
-    -   id: datanet
-        name: datanet
-        short-name: datanet
-        type: ELAN
-        vnfd-connection-point-ref:
-        -   member-vnf-index-ref: '1'
-            vnfd-connection-point-ref: vnf-data
-            vnfd-id-ref: hackfest3charmed-vnf
-        -   member-vnf-index-ref: '2'
-            vnfd-connection-point-ref: vnf-data
-            vnfd-id-ref: hackfest3charmed-vnf
+      - id: datanet
+        mgmt-network: false
+
+    df:
+      - id: hackfest_charmed_DF
+        vnf-profile:
+          - id: hackfest_vnf1 # member-vnf-index-ref: 1
+            vnfd-id: hackfest3charmed-vnf
+            virtual-link-connectivity:
+              - virtual-link-profile-id: mgmt
+                constituent-cpd-id:
+                  - constituent-base-element-id: hackfest_vnf1
+                    constituent-cpd-id: vnf-mgmt-ext
+              - virtual-link-profile-id: datanet
+                constituent-cpd-id:
+                  - constituent-base-element-id: hackfest_vnf1
+                    constituent-cpd-id: vnf-data-ext
+          - id: hackfest_vnf2 # member-vnf-index-ref: 2
+            vnfd-id: hackfest3charmed-vnf
+            virtual-link-connectivity:
+              - virtual-link-profile-id: mgmt
+                constituent-cpd-id:
+                  - constituent-base-element-id: hackfest_vnf2
+                    constituent-cpd-id: vnf-mgmt-ext
+              - virtual-link-profile-id: datanet
+                constituent-cpd-id:
+                  - constituent-base-element-id: hackfest_vnf2
+                    constituent-cpd-id: vnf-data-ext
 """
index 5bcb383..84bf048 100644 (file)
@@ -61,19 +61,6 @@ class PmJobsTopicTest(asynctest.TestCase):
                 response = yaml.load(empty, Loader=yaml.Loader)
             mock_res.get(endpoint, payload=response)
 
-    def test_get_vnf_metric_list(self):
-        with self.subTest("Test case1 failed in test_get_vnf_metric_list"):
-            metric_list = self.pmjobs_topic._get_vnf_metric_list(self.nsr_id)
-            self.assertCountEqual(metric_list, self.metric_check_list,
-                                  "VNF metric list is not correctly fetched")
-        with self.subTest("Test case2 failed in test_get_vnf_metric_list"):
-            wrong_ns_id = "88d90b0c-faff-4bbc-cccc-aaaaaaaaaaaa"
-            with self.assertRaises(EngineException, msg="ns not found") as e:
-                self.pmjobs_topic._get_vnf_metric_list(wrong_ns_id)
-            self.assertEqual(e.exception.http_code, HTTPStatus.NOT_FOUND, "Wrong HTTP status code")
-            self.assertIn("NS not found with id {}".format(wrong_ns_id),
-                          str(e.exception), "Wrong exception text")
-
     async def test_prom_metric_request(self):
         with self.subTest("Test case1 failed in test_prom"):
             prom_response = yaml.load(prom_res, Loader=yaml.Loader)
@@ -92,7 +79,7 @@ class PmJobsTopicTest(asynctest.TestCase):
             with aioresponses() as mock_res:
                 self.set_get_mock_res(mock_res, self.nsr_id, self.metric_check_list)
                 result = self.pmjobs_topic.show(self.session, self.nsr_id)
-            self.assertEqual(len(result['entries']), 3, "Number of metrics returned")
+            self.assertEqual(len(result['entries']), 1, "Number of metrics returned")
             self.assertCountEqual(result, show_response, "Response is valid")
         with self.subTest("Test case2 failed in test_show"):
             wrong_ns_id = "88d90b0c-faff-4bbc-cccc-aaaaaaaaaaaa"
diff --git a/osm_nbi/utils.py b/osm_nbi/utils.py
new file mode 100644 (file)
index 0000000..bb9e33e
--- /dev/null
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM NBI module
+# All Rights Reserved to Whitestack, LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact: fbravo@whitestack.com or agarcia@whitestack.com
+##
+
+
+def find_in_list(the_list, condition_lambda):
+    for item in the_list:
+        if condition_lambda(item):
+            return item
+    else:
+        return None
+
+
+def find_index_in_list(the_list, condition_lambda):
+    for index, item in enumerate(the_list):
+        if condition_lambda(item):
+            return index
+    else:
+        return -1
diff --git a/tox.ini b/tox.ini
index beb079c..656be6c 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -14,7 +14,7 @@
 # limitations under the License.
 
 [tox]
-envlist = cover, flake8, unittest
+envlist = cover, flake8
 
 [testenv]
 usedevelop = True