fix(configuration): juju related changes and configuration issues
[osm/NBI.git] / osm_nbi / descriptor_topics.py
index ed958b1..0ad3e83 100644 (file)
@@ -17,6 +17,7 @@ import tarfile
 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 @@ 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
+from osm_nbi import utils
 
 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
 
@@ -42,7 +44,7 @@ class DescriptorTopic(BaseTopic):
         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 @@ class DescriptorTopic(BaseTopic):
                 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 @@ class DescriptorTopic(BaseTopic):
                 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 @@ class VnfdTopic(DescriptorTopic):
             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 @@ class VnfdTopic(DescriptorTopic):
         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 @@ class VnfdTopic(DescriptorTopic):
         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 @@ class VnfdTopic(DescriptorTopic):
 
         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 @@ class VnfdTopic(DescriptorTopic):
                                           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 @@ class VnfdTopic(DescriptorTopic):
                                   "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 @@ class VnfdTopic(DescriptorTopic):
                                                   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 @@ class NsdTopic(DescriptorTopic):
                                               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 @@ class NstTopic(DescriptorTopic):
                                       "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):
         """