Merge remote-tracking branch 'origin/master' into v9.0

Change-Id: Ib429d0fbc6ecfeff1b29066683f71b1594cf5a46
Signed-off-by: garciadeblas <gerardo.garciadeblas@telefonica.com>
diff --git a/.gitignore-common b/.gitignore-common
index c65d2a2..771afd0 100644
--- a/.gitignore-common
+++ b/.gitignore-common
@@ -32,6 +32,9 @@
 .pydevproject
 .settings
 
+#vscode
+.vscode
+
 #local stuff files that end in ".local" or folders called "local"
 local
 osm_nbi/local
diff --git a/Dockerfile.local b/Dockerfile.local
index 5ba1240..0794a27 100644
--- a/Dockerfile.local
+++ b/Dockerfile.local
@@ -106,5 +106,5 @@
 ADD . /app/NBI
 
 # Run app.py when the container launches
-CMD python3 -m osm_nbi.nbi
+CMD ["python3", "-m", "osm_nbi.nbi"]
 
diff --git a/osm_nbi/admin_topics.py b/osm_nbi/admin_topics.py
index 4f9ab0c..819ec42 100644
--- a/osm_nbi/admin_topics.py
+++ b/osm_nbi/admin_topics.py
@@ -243,6 +243,8 @@
         if not session["force"] and edit_content.get("name"):
             self.check_unique_name(session, edit_content["name"], _id=_id)
 
+        return final_content
+
     def format_on_edit(self, final_content, edit_content):
         """
         Modifies final_content inserting admin information upon edition
@@ -459,8 +461,8 @@
         return oid
 
     def check_conflict_on_edit(self, session, final_content, edit_content, _id):
-        super(CommonVimWimSdn, self).check_conflict_on_edit(session, final_content, edit_content, _id)
-        super().check_conflict_on_edit(session, final_content, edit_content, _id)
+        final_content = super(CommonVimWimSdn, self).check_conflict_on_edit(session, final_content, edit_content, _id)
+        final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id)
         # Update Helm/Juju Repo lists
         repos = {"helm-chart": [], "juju-bundle": []}
         for proj in session.get("set_project", []):
@@ -473,6 +475,7 @@
             if rlist not in final_content["_admin"]:
                 final_content["_admin"][rlist] = []
             final_content["_admin"][rlist] += repos[k]
+        return final_content
 
     def check_conflict_on_del(self, session, _id, db_content):
         """
@@ -605,6 +608,8 @@
                     raise EngineException("You cannot remove system_admin role from admin user",
                                           http_code=HTTPStatus.FORBIDDEN)
 
+        return final_content
+
     def check_conflict_on_del(self, session, _id, db_content):
         """
         Check if deletion can be done because of dependencies if it is not force. To override
@@ -720,7 +725,7 @@
             if not content:
                 content = self.show(session, _id)
             indata = self._validate_input_edit(indata, content, force=session["force"])
-            self.check_conflict_on_edit(session, content, indata, _id=_id)
+            content = self.check_conflict_on_edit(session, content, indata, _id=_id)
             # self.format_on_edit(content, indata)
 
             if not ("password" in indata or "username" in indata or indata.get("remove_project_role_mappings") or
@@ -903,6 +908,7 @@
             # Check that project name is not used, regardless keystone already checks this
             if project_name and self.auth.get_project_list(filter_q={"name": project_name}):
                 raise EngineException("project '{}' is already used".format(project_name), HTTPStatus.CONFLICT)
+        return final_content
 
     def check_conflict_on_del(self, session, _id, db_content):
         """
@@ -1045,7 +1051,7 @@
             if not content:
                 content = self.show(session, _id)
             indata = self._validate_input_edit(indata, content, force=session["force"])
-            self.check_conflict_on_edit(session, content, indata, _id=_id)
+            content = self.check_conflict_on_edit(session, content, indata, _id=_id)
             self.format_on_edit(content, indata)
             content_original = copy.deepcopy(content)
             deep_update_rfc7396(content, indata)
@@ -1174,6 +1180,8 @@
             if roles and roles[0][BaseTopic.id_field("roles", _id)] != _id:
                 raise EngineException("role name '{}' exists".format(role_name), HTTPStatus.CONFLICT)
 
+        return final_content
+
     def check_conflict_on_del(self, session, _id, db_content):
         """
         Check if deletion can be done because of dependencies if it is not force. To override
@@ -1348,7 +1356,7 @@
                 content = self.show(session, _id)
             indata = self._validate_input_edit(indata, content, force=session["force"])
             deep_update_rfc7396(content, indata)
-            self.check_conflict_on_edit(session, content, indata, _id=_id)
+            content = self.check_conflict_on_edit(session, content, indata, _id=_id)
             self.format_on_edit(content, indata)
             self.auth.update_role(content)
         except ValidationError as e:
diff --git a/osm_nbi/base_topic.py b/osm_nbi/base_topic.py
index bbe6337..f597d17 100644
--- a/osm_nbi/base_topic.py
+++ b/osm_nbi/base_topic.py
@@ -231,10 +231,10 @@
         :param final_content: data once modified. This method may change it.
         :param edit_content: incremental data that contains the modifications to apply
         :param _id: internal _id
-        :return: None or raises EngineException
+        :return: final_content or raises EngineException
         """
         if not self.multiproject:
-            return
+            return final_content
         # Change public status
         if session["public"] is not None:
             if session["public"] and "ANY" not in final_content["_admin"]["projects_read"]:
@@ -249,6 +249,8 @@
                 if p not in final_content["_admin"]["projects_read"]:
                     final_content["_admin"]["projects_read"].append(p)
 
+        return final_content
+
     def check_unique_name(self, session, name, _id=None):
         """
         Check that the name is unique for this project
@@ -578,19 +580,16 @@
             if indata and session.get("set_project"):
                 raise EngineException("Cannot edit content and set to project (query string SET_PROJECT) at same time",
                                       HTTPStatus.UNPROCESSABLE_ENTITY)
-            
             # TODO self._check_edition(session, indata, _id, force)
             if not content:
                 content = self.show(session, _id)
-            
             indata = self._validate_input_edit(indata, content, force=session["force"])
-            
             deep_update_rfc7396(content, indata)
 
             # To allow project addressing by name AS WELL AS _id. Get the _id, just in case the provided one is a name
             _id = content.get("_id") or _id
 
-            self.check_conflict_on_edit(session, content, indata, _id=_id)
+            content = self.check_conflict_on_edit(session, content, indata, _id=_id)
             op_id = self.format_on_edit(content, indata)
 
             self.db.replace(self.topic, _id, content)
diff --git a/osm_nbi/descriptor_topics.py b/osm_nbi/descriptor_topics.py
index ed958b1..0ad3e83 100644
--- a/osm_nbi/descriptor_topics.py
+++ b/osm_nbi/descriptor_topics.py
@@ -17,6 +17,7 @@
 import yaml
 import json
 import importlib
+import copy
 # import logging
 from hashlib import md5
 from osm_common.dbbase import DbException, deep_update_rfc7396
@@ -32,6 +33,7 @@
 from osm_im.nst import nst as nst_im
 from pyangbind.lib.serialise import pybindJSONDecoder
 import pyangbind.lib.pybindJSON as pybindJSON
+from osm_nbi import utils
 
 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
 
@@ -42,7 +44,7 @@
         BaseTopic.__init__(self, db, fs, msg, auth)
 
     def check_conflict_on_edit(self, session, final_content, edit_content, _id):
-        super().check_conflict_on_edit(session, final_content, edit_content, _id)
+        final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id)
 
         def _check_unique_id_name(descriptor, position=""):
             for desc_key, desc_item in descriptor.items():
@@ -73,24 +75,29 @@
                 internal_keys[k] = final_content.pop(k)
         storage_params = internal_keys["_admin"].get("storage")
         serialized = self._validate_input_new(final_content, storage_params, session["force"])
+
         # 1.2. modify final_content with a serialized version
-        final_content.clear()
-        final_content.update(serialized)
+        final_content = copy.deepcopy(serialized)
         # 1.3. restore internal keys
         for k, v in internal_keys.items():
             final_content[k] = v
         if session["force"]:
-            return
+            return final_content
+
         # 2. check that this id is not present
         if "id" in edit_content:
             _filter = self._get_project_filter(session)
+
             _filter["id"] = final_content["id"]
             _filter["_id.neq"] = _id
+
             if self.db.get_one(self.topic, _filter, fail_on_empty=False):
                 raise EngineException("{} with id '{}' already exists for this project".format(self.topic[:-1],
                                                                                                final_content["id"]),
                                       HTTPStatus.CONFLICT)
 
+        return final_content
+
     @staticmethod
     def format_on_new(content, project_id=None, make_public=False):
         BaseTopic.format_on_new(content, project_id=project_id, make_public=make_public)
@@ -303,7 +310,7 @@
                 self._update_input_with_kwargs(indata, kwargs)
 
             deep_update_rfc7396(current_desc, indata)
-            self.check_conflict_on_edit(session, current_desc, indata, _id=_id)
+            current_desc = self.check_conflict_on_edit(session, current_desc, indata, _id=_id)
             current_desc["_admin"]["modified"] = time()
             self.db.replace(self.topic, _id, current_desc)
             self.fs.dir_rename(temp_folder, _id)
@@ -460,19 +467,13 @@
             raise EngineException("ERROR: Unsupported descriptor format. Please, use an ETSI SOL006 descriptor.",
                                   http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
         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
+            return utils.deep_update_dict(data, desc_out)
         except Exception as e:
             raise EngineException("Error in pyangbind validation: {}".format(str(e)),
                                   http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
@@ -499,7 +500,7 @@
         return clean_indata
 
     def check_conflict_on_edit(self, session, final_content, edit_content, _id):
-        super().check_conflict_on_edit(session, final_content, edit_content, _id)
+        final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id)
 
         # set type of vnfd
         contains_pdu = False
@@ -514,6 +515,7 @@
         elif contains_vdu:
             final_content["_admin"]["type"] = "vnfd"
         # if neither vud nor pdu do not fill type
+        return final_content
 
     def check_conflict_on_del(self, session, _id, db_content):
         """
@@ -559,8 +561,8 @@
 
         for vdu in get_iterable(indata.get("vdu")):
             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_vdu_charms_in_package(storage_params, indata)
 
         self._validate_vnf_charms_in_package(storage_params, indata)
 
@@ -619,14 +621,20 @@
                                           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
-        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_charms_in_package(self, storage_params, indata):
+        for df in indata["df"]:
+            if "lcm-operations-configuration" in df and "operate-vnf-op-config" in df["lcm-operations-configuration"]:
+                configs = df["lcm-operations-configuration"]["operate-vnf-op-config"].get("day1-2", [])
+                vdus = df["vdu-profile"]
+                for vdu in vdus:
+                    for config in configs:
+                        if config["id"] == vdu["id"] and utils.find_in_list(
+                            config.get("execution-environment-list", []),
+                            lambda ee: "juju" in ee
+                        ):
+                            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"):
@@ -636,13 +644,21 @@
                                   "package".format(indata["id"], vdu["id"]))
 
     def _validate_vnf_charms_in_package(self, storage_params, indata):
-        if not indata.get("vnf-configuration"):
-            return
-        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"]))
+        # Get VNF configuration through new container
+        for deployment_flavor in indata.get('df', []):
+            if "lcm-operations-configuration" not in deployment_flavor:
+                return
+            if "operate-vnf-op-config" not in deployment_flavor["lcm-operations-configuration"]:
+                return
+            for day_1_2_config in deployment_flavor["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"]:
+                if day_1_2_config["id"] == indata["id"]:
+                    if utils.find_in_list(
+                        day_1_2_config.get("execution-environment-list", []),
+                        lambda ee: "juju" in ee
+                    ):
+                        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"):
@@ -753,19 +769,25 @@
                                                   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"
+                    if "lcm-operations-configuration" not in df \
+                        or "operate-vnf-op-config" not in df["lcm-operations-configuration"] \
+                        or not utils.find_in_list(
+                            df["lcm-operations-configuration"]["operate-vnf-op-config"].get("day1-2", []),
+                            lambda config: config["id"] == indata["id"]):
+                        raise EngineException("'day1-2 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 configuration in get_iterable(indata["vnf-configuration"]):
+                    for configuration in get_iterable(
+                        df["lcm-operations-configuration"]["operate-vnf-op-config"].get("day1-2", [])
+                    ):
                         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"
+                                                  "day1-2 configuration:config-primitive:name"
                                                   .format(df["id"], sa["id"], sca["vnf-config-primitive-name-ref"]),
                                                   http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
 
@@ -971,10 +993,12 @@
                                               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)
+        final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id)
 
         self._check_descriptor_dependencies(session, final_content)
 
+        return final_content
+
     def check_conflict_on_del(self, session, _id, db_content):
         """
         Check that there is not any NSR that uses this NSD. Only NSRs belonging to this project are considered. Note
@@ -1078,9 +1102,10 @@
                                       "existing nsd".format(nsd_id), http_code=HTTPStatus.CONFLICT)
 
     def check_conflict_on_edit(self, session, final_content, edit_content, _id):
-        super().check_conflict_on_edit(session, final_content, edit_content, _id)
+        final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id)
 
         self._check_descriptor_dependencies(session, final_content)
+        return final_content
 
     def check_conflict_on_del(self, session, _id, db_content):
         """
diff --git a/osm_nbi/instance_topics.py b/osm_nbi/instance_topics.py
index 35c39d3..5088b24 100644
--- a/osm_nbi/instance_topics.py
+++ b/osm_nbi/instance_topics.py
@@ -177,33 +177,29 @@
                     additional_params[k] = "!!yaml " + safe_dump(v)
 
         if descriptor:
-            # check that enough parameters are supplied for the initial-config-primitive
-            # TODO: check for cloud-init
-            if member_vnf_index:
-                if kdu_name:
-                    initial_primitives = None
-                elif vdu_id:
-                    vdud = next(x for x in descriptor["vdu"] if x["id"] == vdu_id)
-                    initial_primitives = deep_get(vdud, ("vdu-configuration", "initial-config-primitive"))
-                else:
-                    vnf_configurations = get_iterable(descriptor.get("vnf-configuration"))
+            for df in descriptor.get("df", []):
+                # check that enough parameters are supplied for the initial-config-primitive
+                # TODO: check for cloud-init
+                if member_vnf_index:
                     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"))
+                    if "lcm-operations-configuration" in df \
+                       and "operate-vnf-op-config" in df["lcm-operations-configuration"]:
+                        for config in df["lcm-operations-configuration"]["operate-vnf-op-config"].get("day1-2", []):
+                            for primitive in get_iterable(config.get("initial-config-primitive")):
+                                initial_primitives.append(primitive)
+                else:
+                    initial_primitives = deep_get(descriptor, ("ns-configuration", "initial-config-primitive"))
 
-            for initial_primitive in get_iterable(initial_primitives):
-                for param in get_iterable(initial_primitive.get("parameter")):
-                    if param["value"].startswith("<") and param["value"].endswith(">"):
-                        if param["value"] in ("<rw_mgmt_ip>", "<VDU_SCALE_INFO>", "<ns_config_info>"):
-                            continue
-                        if not additional_params or param["value"][1:-1] not in additional_params:
-                            raise EngineException("Parameter '{}' needed for vnfd[id={}]:vnf-configuration:"
-                                                  "initial-config-primitive[name={}] not supplied".
-                                                  format(param["value"], descriptor["id"],
-                                                         initial_primitive["name"]))
+                for initial_primitive in get_iterable(initial_primitives):
+                    for param in get_iterable(initial_primitive.get("parameter")):
+                        if param["value"].startswith("<") and param["value"].endswith(">"):
+                            if param["value"] in ("<rw_mgmt_ip>", "<VDU_SCALE_INFO>", "<ns_config_info>"):
+                                continue
+                            if not additional_params or param["value"][1:-1] not in additional_params:
+                                raise EngineException("Parameter '{}' needed for vnfd[id={}]:day1-2 configuration:"
+                                                      "initial-config-primitive[name={}] not supplied".
+                                                      format(param["value"], descriptor["id"],
+                                                             initial_primitive["name"]))
 
         return additional_params or None, other_params or None
 
@@ -226,7 +222,7 @@
             step = "validating input parameters"
             ns_request = self._remove_envelop(indata)
             self._update_input_with_kwargs(ns_request, kwargs)
-            self._validate_input_new(ns_request, session["force"])
+            ns_request = self._validate_input_new(ns_request, session["force"])
 
             step = "getting nsd id='{}' from database".format(ns_request.get("nsdId"))
             nsd = self._get_nsd_from_db(ns_request["nsdId"], session)
@@ -423,18 +419,13 @@
 
                     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)
+                        image_data = self._get_image_data_from_vnfd(vnfd, sw_image_id)
+                        self._add_image_to_nsr(nsr_descriptor, image_data)
+
+                    # also add alternative images to the list of images
+                    for alt_image in vdu.get("alternative-sw-image-desc", ()):
+                        image_data = self._get_image_data_from_vnfd(vnfd, alt_image)
+                        self._add_image_to_nsr(nsr_descriptor, image_data)
 
             for vld in nsr_vld:
                 vld["vnfd-connection-point-ref"] = all_vld_connection_point_data.get(vld.get("id"), [])
@@ -443,6 +434,28 @@
 
         return nsr_descriptor
 
+    def _get_image_data_from_vnfd(self, vnfd, 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"]
+        if sw_image_desc.get("vim-type"):
+            image_data["vim-type"] = sw_image_desc["vim-type"]
+        return image_data
+
+    def _add_image_to_nsr(self, nsr_descriptor, image_data):
+        """
+        Adds image to nsr checking first it is not already added
+        """
+        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)
+
     def _create_vnfr_descriptor_from_vnfd(self, nsd, vnfd, vnfd_id, vnf_index, nsr_descriptor,
                                           ns_request, ns_k8s_namespace):
         vnfr_id = str(uuid4())
@@ -532,7 +545,23 @@
             vnfr_descriptor["kdur"].append(kdur)
 
         vnfd_mgmt_cp = vnfd.get("mgmt-cp")
+
         for vdu in vnfd.get("vdu", ()):
+            vdu_mgmt_cp = []
+            try:
+                configs = vnfd.get("df")[0]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"]
+                vdu_config = utils.find_in_list(configs, lambda config: config["id"] == vdu["id"])
+            except Exception:
+                vdu_config = None
+            
+            if vdu_config:
+                external_connection_ee = utils.filter_in_list(
+                    vdu_config.get("execution-environment-list", []),
+                    lambda ee: "external-connection-point-ref" in ee
+                )
+                for ee in external_connection_ee:
+                    vdu_mgmt_cp.append(ee["external-connection-point-ref"])
+
             additional_params, vdu_params = self._format_additional_params(
                 ns_request, vnf_index, vdu_id=vdu["id"], descriptor=vnfd)
             vdur = {
@@ -559,6 +588,13 @@
                     "connection-point-id": icp["id"],
                     "name": icp.get("id"),
                 }
+
+                if "port-security-enabled" in icp:
+                    vdu_icp["port-security-enabled"] = icp["port-security-enabled"]
+
+                if "port-security-disable-strategy" in icp:
+                    vdu_icp["port-security-disable-strategy"] = icp["port-security-disable-strategy"]
+
                 vdur["internal-connection-point"].append(vdu_icp)
 
                 for iface in icp.get("virtual-network-interface-requirement", ()):
@@ -577,7 +613,11 @@
 
                     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
+                        vdu_iface["mgmt-interface"] = True
+
+                    for ecp in vdu_mgmt_cp:
+                        if vdu_iface.get("external-connection-point-ref") == ecp:
+                            vdu_iface["mgmt-interface"] = True
 
                     if iface.get("virtual-interface"):
                         vdu_iface.update(deepcopy(iface["virtual-interface"]))
@@ -588,13 +628,25 @@
                         # 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 vlc_index, vlc in \
+                                        enumerate(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")
+                                            # if iface type is SRIOV or PASSTHROUGH, set pci-interfaces flag to True
+                                            if vdu_iface.get("type") in ("SR-IOV", "PCI-PASSTHROUGH"):
+                                                nsr_descriptor["vld"][vlc_index]["pci-interfaces"] = True
                                             break
                     elif vdu_iface.get("internal-connection-point-ref"):
                         vdu_iface["vnf-vld-id"] = icp.get("int-virtual-link-desc")
+                        # TODO: store fixed IP address in the record (if it exists in the ICP)
+                        # if iface type is SRIOV or PASSTHROUGH, set pci-interfaces flag to True
+                        if vdu_iface.get("type") in ("SR-IOV", "PCI-PASSTHROUGH"):
+                            ivld_index = utils.find_index_in_list(vnfd.get("int-virtual-link-desc", ()),
+                                                                  lambda ivld:
+                                                                  ivld["id"] == icp.get("int-virtual-link-desc")
+                                                                  )
+                            vnfr_descriptor["vld"][ivld_index]["pci-interfaces"] = True
 
                     vdur["interfaces"].append(vdu_iface)
 
@@ -608,6 +660,19 @@
                 )
                 vdur["ns-image-id"] = nsr_sw_image_data["id"]
 
+            if vdu.get("alternative-sw-image-desc"):
+                alt_image_ids = []
+                for alt_image_id in vdu.get("alternative-sw-image-desc", ()):
+                    sw_image = utils.find_in_list(
+                        vnfd.get("sw-image-desc", ()),
+                        lambda image: image["id"] == alt_image_id)
+                    nsr_sw_image_data = utils.find_in_list(
+                        nsr_descriptor["image"],
+                        lambda nsr_image: (nsr_image.get("image") == sw_image.get("image"))
+                    )
+                    alt_image_ids.append(nsr_sw_image_data["id"])
+                vdur["alt-image-ids"] = alt_image_ids
+
             flavor_data_name = vdu["id"][:56] + "-flv"
             nsr_flavor_desc = utils.find_in_list(
                 nsr_descriptor["flavor"],
@@ -689,18 +754,28 @@
             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"])
+            try:
+                configs = vnfd.get("df")[0]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"]
+            except Exception:
+                configs = []
+
             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")
+                descriptor_configuration = utils.find_in_list(
+                    configs,
+                    lambda config: config["id"] == indata["vdu_id"]
+                ).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
-                kdud = next((k for k in vnfd["kdu"] if k["name"] == indata["kdu_name"]), None)
-                descriptor_configuration = deep_get(kdud, ("kdu-configuration", "config-primitive"))
+                descriptor_configuration = utils.find_in_list(
+                    configs,
+                    lambda config: config["id"] == indata.get("kdu_name")
+                ).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")
+                descriptor_configuration = utils.find_in_list(
+                    configs,
+                    lambda config: config["id"] == vnfd["id"]
+                ).get("config-primitive")
         else:  # use a NSD
             descriptor_configuration = nsd.get("ns-configuration", {}).get("config-primitive")
 
@@ -1098,6 +1173,8 @@
                                 vnfr_update[vnfr_update_text + ".mac-address"] = increment_ip_mac(
                                     iface_inst_param.get("mac-address"), vdur.get("count-index", 0))
                                 vnfr_update[vnfr_update_text + ".fixed-mac"] = True
+                            if iface_inst_param.get("floating-ip-required"):
+                                vnfr_update[vnfr_update_text + ".floating-ip-required"] = True
                 # get vnf.internal-vld.internal-conection-point instantiation params to update vnfr.vdur.interfaces
                 # TODO update vld with the ip-profile
                 for ivld_inst_param in get_iterable(vnf_inst_params.get("internal-vld")):
@@ -1418,7 +1495,7 @@
             slice_request = self._remove_envelop(indata)
             # Override descriptor with query string kwargs
             self._update_input_with_kwargs(slice_request, kwargs)
-            self._validate_input_new(slice_request, session["force"])
+            slice_request = self._validate_input_new(slice_request, session["force"])
 
             # look for nstd
             step = "getting nstd id='{}' from database".format(slice_request.get("nstId"))
diff --git a/osm_nbi/pmjobs_topics.py b/osm_nbi/pmjobs_topics.py
index 6ce8b3d..3142744 100644
--- a/osm_nbi/pmjobs_topics.py
+++ b/osm_nbi/pmjobs_topics.py
@@ -38,15 +38,14 @@
         else:
             for vnfr in vnfr_desc:
                 vnfd_desc = self.db.get_one("vnfds", {"_id": vnfr["vnfd-id"]}, fail_on_empty=True, fail_on_more=False)
-                if vnfd_desc.get("vdu"):
-                    for vdu in vnfd_desc['vdu']:
-                        # Checks for vdu metric in vdu-configuration
-                        if 'vdu-configuration' in vdu and 'metrics' in vdu['vdu-configuration']:
-                            metric_list.extend([quote(metric['name']) 
-                                               for metric in vdu["vdu-configuration"]["metrics"]])
-                # Checks for vnf metric in vnf-configutaion
-                if 'vnf-configuration' in vnfd_desc and 'metrics' in vnfd_desc['vnf-configuration']:
-                    metric_list.extend([quote(metric['name']) for metric in vnfd_desc["vnf-configuration"]["metrics"]])
+                try:
+                    configs = vnfd_desc.get("df")[0]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"]
+                except Exception:
+                    configs = []
+
+                for config in configs:
+                    if "metrics" in config:
+                        metric_list.extend([quote(metric['name']) for metric in config["metrics"]])
         metric_list = list(set(metric_list))
         return metric_list
 
diff --git a/osm_nbi/tests/run_test.py b/osm_nbi/tests/run_test.py
index 3033a34..9e85358 100755
--- a/osm_nbi/tests/run_test.py
+++ b/osm_nbi/tests/run_test.py
@@ -3195,7 +3195,6 @@
             elif o == "--fail-fast":
                 fail_fast = True
             elif o == "--test":
-                # print("asdfadf", o, a, a.split(","))
                 for _test in a.split(","):
                     if _test not in test_classes:
                         print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test),
diff --git a/osm_nbi/tests/test_db_descriptors.py b/osm_nbi/tests/test_db_descriptors.py
index 1e6eff9..8b0b226 100644
--- a/osm_nbi/tests/test_db_descriptors.py
+++ b/osm_nbi/tests/test_db_descriptors.py
@@ -167,14 +167,12 @@
   
     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:
@@ -210,32 +208,38 @@
                 vnf-config-primitive-name-ref: touch
               - trigger: pre-scale-in
                 vnf-config-primitive-name-ref: touch
-  
-    vnf-configuration:
-      - id: vnf-configuration-example
-        initial-config-primitive:
-          - seq: "1"
-            name: config
-            parameter:
-              - name: ssh-hostname
-                value: <rw_mgmt_ip>
-              - name: ssh-username
-                value: ubuntu
-              - name: ssh-password
-                value: osm4u
-          - seq: "2"
-            name: touch
-            parameter:
-              - name: filename
-                value: <touch_filename>
-        config-primitive:
-          - name: touch
-            parameter:
-              - data-type: STRING
-                default-value: <touch_filename2>
-                name: filename
-        juju:
-          charm: simple
+        lcm-operations-configuration:
+          operate-vnf-op-config:
+            day1-2:
+            - id: hackfest3charmed-vnf
+              execution-environment-list:
+                - id: simple-ee
+                  juju:
+                    charm: simple
+              initial-config-primitive:
+                - seq: "1"
+                  execution-environment-ref: simple-ee
+                  name: config
+                  parameter:
+                    - name: ssh-hostname
+                      value: <rw_mgmt_ip>
+                    - name: ssh-username
+                      value: ubuntu
+                    - name: ssh-password
+                      value: osm4u
+                - seq: "2"
+                  execution-environment-ref: simple-ee
+                  name: touch
+                  parameter:
+                    - name: filename
+                      value: <touch_filename>
+              config-primitive:
+                - name: touch
+                  execution-environment-ref: simple-ee
+                  parameter:
+                    - data-type: STRING
+                      default-value: <touch_filename2>
+                      name: filename
 """
 
 db_nsds_text = """
diff --git a/osm_nbi/tests/test_descriptor_topics.py b/osm_nbi/tests/test_descriptor_topics.py
index 516c178..b52b6e0 100755
--- a/osm_nbi/tests/test_descriptor_topics.py
+++ b/osm_nbi/tests/test_descriptor_topics.py
@@ -112,11 +112,23 @@
             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"][0]["juju"]
+            tmp2 = test_vnfd["df"][
+                0
+            ]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"][
+                0
+            ]["execution-environment-list"][
+                0
+            ]["juju"]
             del test_vnfd["vdu"][0]["cloud-init-file"]
-            del test_vnfd["vnf-configuration"][0]["juju"]
+            del test_vnfd["df"][
+                0
+            ]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"][
+                0
+            ]["execution-environment-list"][
+                0
+            ]["juju"]
             try:
-                self.db.get_one.side_effect = [{"_id": did, "_admin": db_vnfd_content["_admin"]}, None]
+                self.db.get_one.side_effect = [{"_id": did, "_admin": deepcopy(db_vnfd_content["_admin"])}, None]
                 self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
                 msg_args = self.msg.write.call_args[0]
                 test_vnfd["_id"] = did
@@ -130,7 +142,7 @@
                 self.assertEqual(db_args[0], self.topic.topic, "Wrong DB topic")
                 self.assertEqual(db_args[1], did, "Wrong DB VNFD id")
                 admin = db_args[2]["_admin"]
-                db_admin = db_vnfd_content["_admin"]
+                db_admin = deepcopy(db_vnfd_content["_admin"])
                 self.assertEqual(admin["type"], "vnfd", "Wrong descriptor type")
                 self.assertEqual(admin["created"], db_admin["created"], "Wrong creation time")
                 self.assertGreater(admin["modified"], db_admin["created"], "Wrong modification time")
@@ -145,9 +157,15 @@
                 compare_desc(self, test_vnfd, db_args[2], "VNFD")
             finally:
                 test_vnfd["vdu"][0]["cloud-init-file"] = tmp1
-                test_vnfd["vnf-configuration"][0]["juju"] = tmp2
+                test_vnfd["df"][
+                    0
+                ]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"][
+                    0
+                ]["execution-environment-list"][
+                    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"]}
+            {"_id": did, "_admin": deepcopy(db_vnfd_content["_admin"])}
         with self.subTest(i=2, t='Check Pyangbind Validation: additional properties'):
             test_vnfd["extra-property"] = 0
             try:
@@ -178,14 +196,21 @@
             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=5, t='Check Input Validation: vnf-configuration[juju]'):
+        with self.subTest(i=5, t='Check Input Validation: day1-2 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": []})
+            print(str(e.exception))
             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")
-            del test_vnfd["vnf-configuration"][0]["juju"]
+            del test_vnfd["df"][
+                0
+            ]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"][
+                0
+            ]["execution-environment-list"][
+                0
+            ]["juju"]
         with self.subTest(i=6, t='Check Input Validation: mgmt-cp'):
             tmp = test_vnfd["mgmt-cp"]
             del test_vnfd["mgmt-cp"]
@@ -280,22 +305,30 @@
                 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')
+            tmp = test_vnfd["df"][0]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"].pop()
             try:
                 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 "
+                self.assertIn(norm("'day1-2 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:
-                test_vnfd["vnf-configuration"] = tmp
+                test_vnfd["df"][0]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"].append(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'}]
+            tmp = test_vnfd["df"][0].get(
+                "lcm-operations-configuration"
+            ).get(
+                "operate-vnf-op-config"
+            )["day1-2"][0]['config-primitive']
+            test_vnfd["df"][0].get(
+                "lcm-operations-configuration"
+            ).get(
+                "operate-vnf-op-config"
+            )["day1-2"][0]['config-primitive'] = [{'name': 'wrong-primitive'}]
             try:
                 with self.assertRaises(EngineException,
                                        msg="Accepted non-existent Scaling Group VDU ID Reference") as e:
@@ -303,15 +336,24 @@
                 self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
                 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"
+                                   "day1-2 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'][0]['config-primitive'] = tmp
+                test_vnfd["df"][0].get(
+                    "lcm-operations-configuration"
+                ).get(
+                    "operate-vnf-op-config"
+                )["day1-2"][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]
+            test_vnfd["df"][0].get(
+                "lcm-operations-configuration"
+            ).get(
+                "operate-vnf-op-config"
+            )["day1-2"][0]["id"] = "fake-vnfd-id"
+            self.db.get_one.side_effect = [{"_id": did, "_admin": deepcopy(db_vnfd_content["_admin"])}, None]
             rc = self.topic.upload_content(fake_session, did, test_vnfd, {}, {"Content-Type": []})
             self.assertTrue(rc, "Input Validation: Unexpected failure")
         return
@@ -323,8 +365,10 @@
         self.fs.dir_ls.return_value = True
         with self.subTest(i=1, t='Normal Edition'):
             now = time()
-            self.db.get_one.side_effect = [vnfd_content, None]
-            data = {"id": "new-vnfd-id", "product-name": "new-vnfd-name"}
+            self.db.get_one.side_effect = [deepcopy(vnfd_content), None]
+            data = {
+                "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]
@@ -342,11 +386,10 @@
                              "Wrong read-only project list")
             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]["product-name"], data["product-name"], "Wrong VNFD Name")
         with self.subTest(i=2, t='Conflict on Edit'):
-            data = {"id": "fake-vnfd-id", "product-name": "new-vnfd-name"}
-            self.db.get_one.side_effect = [vnfd_content, {"_id": str(uuid4()), "id": data["id"]}]
+            data = {"id": "hackfest3charmed-vnf", "product-name": "new-vnfd-name"}
+            self.db.get_one.side_effect = [deepcopy(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")
@@ -613,11 +656,11 @@
         indata = deepcopy(db_vnfd_content)
         df = indata['df'][0]
         affected_sa = df['scaling-aspect'][0]
-        indata.pop('vnf-configuration')
+        indata["df"][0]["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"].pop()
         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 "
+        self.assertIn(norm("'day1-2 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")
@@ -627,13 +670,13 @@
         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'] = []
+        df["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"][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("df[id='{}']:scaling-aspect[id='{}']:scaling-config-action:vnf-"
                            "config-primitive-name-ref='{}' does not match any "
-                           "vnf-configuration:config-primitive:name"
+                           "day1-2 configuration:config-primitive:name"
                            .format(df["id"], affected_sa["id"], affected_sca_primitive)),
                       norm(str(e.exception)), "Wrong exception text")
 
@@ -804,7 +847,7 @@
         self.fs.dir_ls.return_value = True
         with self.subTest(i=1, t='Normal Edition'):
             now = time()
-            self.db.get_one.side_effect = [nsd_content, None]
+            self.db.get_one.side_effect = [deepcopy(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)
@@ -989,7 +1032,7 @@
         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')
+            nsd_descriptor = 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 'vnfd-id'='{}' references a non "
                            "existing vnfd".format(invalid_vnfd_id)),
diff --git a/osm_nbi/tests/test_pkg_descriptors.py b/osm_nbi/tests/test_pkg_descriptors.py
index 4552e24..91e5641 100644
--- a/osm_nbi/tests/test_pkg_descriptors.py
+++ b/osm_nbi/tests/test_pkg_descriptors.py
@@ -134,14 +134,12 @@
   
     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:
@@ -177,32 +175,38 @@
                 vnf-config-primitive-name-ref: touch
               - trigger: pre-scale-in
                 vnf-config-primitive-name-ref: touch
-  
-    vnf-configuration:
-      - id: vnf-configuration-example
-        initial-config-primitive:
-          - seq: "1"
-            name: config
-            parameter:
-              - name: ssh-hostname
-                value: <rw_mgmt_ip>
-              - name: ssh-username
-                value: ubuntu
-              - name: ssh-password
-                value: osm4u
-          - seq: "2"
-            name: touch
-            parameter:
-              - name: filename
-                value: <touch_filename>
-        config-primitive:
-          - name: touch
-            parameter:
-              - data-type: STRING
-                default-value: <touch_filename2>
-                name: filename
-        juju:
-          charm: simple
+        lcm-operations-configuration:
+          operate-vnf-op-config:
+            day1-2:
+            - id: hackfest3charmed-vnf
+              execution-environment-list:
+                - id: simple-ee
+                  juju:
+                    charm: simple
+              initial-config-primitive:
+                - seq: "1"
+                  execution-environment-ref: simple-ee
+                  name: config
+                  parameter:
+                    - name: ssh-hostname
+                      value: <rw_mgmt_ip>
+                    - name: ssh-username
+                      value: ubuntu
+                    - name: ssh-password
+                      value: osm4u
+                - seq: "2"
+                  execution-environment-ref: simple-ee
+                  name: touch
+                  parameter:
+                    - name: filename
+                      value: <touch_filename>
+              config-primitive:
+                - name: touch
+                  execution-environment-ref: simple-ee
+                  parameter:
+                    - data-type: STRING
+                      default-value: <touch_filename2>
+                      name: filename
 """
 
 db_nsds_text = """
diff --git a/osm_nbi/utils.py b/osm_nbi/utils.py
index bb9e33e..73fc40f 100644
--- a/osm_nbi/utils.py
+++ b/osm_nbi/utils.py
@@ -31,9 +31,36 @@
         return None
 
 
+def filter_in_list(the_list, condition_lambda):
+    ret = []
+    for item in the_list:
+        if condition_lambda(item):
+            ret.append(item)
+    return ret
+
+
 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
+
+
+def deep_update_dict(data, updated_data):
+    if isinstance(data, list):
+        processed_items_data = []
+        for index, item in enumerate(data):
+            processed_items_data.append(deep_update_dict(item, updated_data[index]))
+        return processed_items_data
+
+    if isinstance(data, dict):
+        for key in data.keys():
+            if key in updated_data:
+                if not isinstance(data[key], dict) and not isinstance(data[key], list):
+                    data[key] = updated_data[key]
+                else:
+                    data[key] = deep_update_dict(data[key], updated_data[key])
+        return data
+
+    return data