Feature 10980: Service Function Chaining 18/13318/5 release-v15.0-start
authorselvi.j <selvi.j@tataelxsi.co.in>
Tue, 16 May 2023 05:43:48 +0000 (05:43 +0000)
committergarciadeblas <gerardo.garciadeblas@telefonica.com>
Thu, 14 Dec 2023 11:47:37 +0000 (12:47 +0100)
Change-Id: Ib0e34f152fa5b0f601fffe67b91882f5cc2fc11c
Signed-off-by: sritharan <priyadarshini@tataelxsi.co.in>
Signed-off-by: selvi.j <selvi.j@tataelxsi.co.in>
osm_nbi/descriptor_topics.py
osm_nbi/instance_topics.py
osm_nbi/tests/test_descriptor_topics.py
osm_nbi/tests/test_pkg_descriptors.py

index 98a41d1..b165b76 100644 (file)
@@ -1467,6 +1467,8 @@ class NsdTopic(DescriptorTopic):
         # 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("virtual-link-desc")):
             self.validate_vld_mgmt_network_with_virtual_link_protocol_data(vld, indata)
+        for fg in get_iterable(indata.get("vnffgd")):
+            self.validate_vnffgd_data(fg, indata)
 
         self.validate_vnf_profiles_vnfd_id(indata)
 
@@ -1488,6 +1490,45 @@ class NsdTopic(DescriptorTopic):
                             http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
                         )
 
+    @staticmethod
+    def validate_vnffgd_data(fg, indata):
+        position_list = []
+        all_vnf_ids = set(get_iterable(fg.get("vnf-profile-id")))
+        for fgposition in get_iterable(fg.get("nfp-position-element")):
+            position_list.append(fgposition["id"])
+
+        for nfpd in get_iterable(fg.get("nfpd")):
+            nfp_position = []
+            for position in get_iterable(nfpd.get("position-desc-id")):
+                nfp_position = position.get("nfp-position-element-id")
+                if position == "nfp-position-element-id":
+                    nfp_position = position.get("nfp-position-element-id")
+                if nfp_position[0] not in position_list:
+                    raise EngineException(
+                        "Error at vnffgd nfpd[id='{}']:nfp-position-element-id='{}' "
+                        "does not match any nfp-position-element".format(
+                            nfpd["id"], nfp_position[0]
+                        ),
+                        http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+                    )
+
+                for cp in get_iterable(position.get("cp-profile-id")):
+                    for cpe in get_iterable(cp.get("constituent-profile-elements")):
+                        constituent_base_element_id = cpe.get(
+                            "constituent-base-element-id"
+                        )
+                        if (
+                            constituent_base_element_id
+                            and constituent_base_element_id not in all_vnf_ids
+                        ):
+                            raise EngineException(
+                                "Error at vnffgd constituent_profile[id='{}']:vnfd-id='{}' "
+                                "does not match any constituent-base-element-id".format(
+                                    cpe["id"], constituent_base_element_id
+                                ),
+                                http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+                            )
+
     @staticmethod
     def validate_vnf_profiles_vnfd_id(indata):
         all_vnfd_ids = set(get_iterable(indata.get("vnfd-id")))
index 8488a93..695a8f8 100644 (file)
@@ -562,6 +562,7 @@ class NsrTopic(BaseTopic):
             "image": [],
             "affinity-or-anti-affinity-group": [],
             "shared-volumes": [],
+            "vnffgd": [],
         }
         if "revision" in nsd["_admin"]:
             nsr_descriptor["revision"] = nsd["_admin"]["revision"]
@@ -641,6 +642,17 @@ class NsrTopic(BaseTopic):
                 )
                 vld["name"] = vld["id"]
             nsr_descriptor["vld"] = nsr_vld
+        if nsd.get("vnffgd"):
+            vnffgd = nsd.get("vnffgd")
+            for vnffg in vnffgd:
+                info = {}
+                for k, v in vnffg.items():
+                    if k == "id":
+                        info.update({k: v})
+                    if k == "nfpd":
+                        info.update({k: v})
+                nsr_descriptor["vnffgd"].append(info)
+
         return nsr_descriptor
 
     def _get_affinity_or_anti_affinity_group_data_from_vnfd(
index dcde0a5..f6d4001 100755 (executable)
@@ -32,6 +32,7 @@ from osm_nbi.tests.test_pkg_descriptors import (
     db_nsds_text,
     vnfd_exploit_text,
     vnfd_exploit_fixed_text,
+    db_sfc_nsds_text,
 )
 from osm_nbi.descriptor_topics import VnfdTopic, NsdTopic
 from osm_nbi.engine import EngineException
@@ -2197,6 +2198,61 @@ class Test_NsdTopic(TestCase):
             "Wrong exception text",
         )
 
+    def test_validate_vnffgd_descriptor_on_valid_descriptor(self):
+        indata = yaml.safe_load(db_sfc_nsds_text)[0]
+        vnffgd = indata.get("vnffgd")
+        fg = vnffgd[0]
+        self.topic.validate_vnffgd_data(fg, indata)
+
+    def test_validate_vnffgd_descriptor_not_matching_nfp_position_element(self):
+        indata = yaml.safe_load(db_sfc_nsds_text)[0]
+        vnffgd = indata.get("vnffgd")
+        fg = vnffgd[0]
+        nfpd = fg.get("nfpd")[0]
+        with self.assertRaises(EngineException) as e:
+            fg.update({"nfp-position-element": [{"id": "test1"}]})
+            self.topic.validate_vnffgd_data(fg, indata)
+        self.assertEqual(
+            e.exception.http_code,
+            HTTPStatus.UNPROCESSABLE_ENTITY,
+            "Wrong HTTP status code",
+        )
+        self.assertIn(
+            norm(
+                "Error at vnffgd nfpd[id='{}']:nfp-position-element-id='{}' "
+                "does not match any nfp-position-element".format(nfpd["id"], "test")
+            ),
+            norm(str(e.exception)),
+            "Wrong exception text",
+        )
+
+    def test_validate_vnffgd_descriptor_not_matching_constituent_base_element_id(
+        self,
+    ):
+        indata = yaml.safe_load(db_sfc_nsds_text)[0]
+        vnffgd = indata.get("vnffgd")
+        fg = vnffgd[0]
+        fg["nfpd"][0]["position-desc-id"][0]["cp-profile-id"][0][
+            "constituent-profile-elements"
+        ][0]["constituent-base-element-id"] = "error_vnf"
+        with self.assertRaises(EngineException) as e:
+            self.topic.validate_vnffgd_data(fg, indata)
+        self.assertEqual(
+            e.exception.http_code,
+            HTTPStatus.UNPROCESSABLE_ENTITY,
+            "Wrong HTTP status code",
+        )
+        self.assertIn(
+            norm(
+                "Error at vnffgd constituent_profile[id='{}']:vnfd-id='{}' "
+                "does not match any constituent-base-element-id".format(
+                    "vnf1", "error_vnf"
+                )
+            ),
+            norm(str(e.exception)),
+            "Wrong exception text",
+        )
+
 
 if __name__ == "__main__":
     unittest.main()
index 39c28fe..d77b79f 100644 (file)
@@ -295,3 +295,77 @@ db_nsds_text = """
                   - constituent-base-element-id: hackfest_vnf2
                     constituent-cpd-id: vnf-data-ext
 """
+
+db_sfc_nsds_text = """
+- _admin:
+    userDefinedData: {}
+    revision: 1
+    created: 1683713524.2696395
+    modified: 1683713524.3553684
+    projects_read:
+      - 93601899-b310-4a56-a765-91539d5f675d
+    projects_write:
+      - 93601899-b310-4a56-a765-91539d5f675d
+    onboardingState: ONBOARDED
+    operationalState: ENABLED
+    usageState: NOT_IN_USE
+    storage:
+      fs: mongo
+      path: /app/storage/
+      folder: '2eb45633-03e3-4909-a87d-a564f5943948:1'
+      pkg-dir: cirros_vnffg_ns
+      descriptor: cirros_vnffg_ns/cirros_vnffg_nsd.yaml
+      zipfile: package.tar.gz
+  _id: 2eb45633-03e3-4909-a87d-a564f5943948
+  id: cirros_vnffg-ns
+  designer: OSM
+  version: '1.0'
+  name: cirros_vnffg-ns
+
+  vnfd-id:
+    - cirros_vnffg-vnf
+
+  virtual-link-desc:
+    - id: osm-ext
+      mgmt-network: true
+
+  vnffgd:
+    - id: vnffg1
+      vnf-profile-id:
+        - Mid-vnf1
+      nfpd:
+        - id: forwardingpath1
+          position-desc-id:
+            - id: position1
+              cp-profile-id:
+                - id: cpprofile2
+                  constituent-profile-elements:
+                    - id: vnf1
+                      order: 0
+                      constituent-base-element-id: Mid-vnf1
+                      ingress-constituent-cpd-id: vnf-cp0-ext
+                      egress-constituent-cpd-id: vnf-cp0-ext
+              match-attributes:
+                - id: rule1_80
+                  ip-proto: 6
+                  source-ip-address: 20.20.1.2
+                  destination-ip-address: 20.20.3.5
+                  source-port: 0
+                  destination-port: 80
+              nfp-position-element-id:
+                - test
+      nfp-position-element:
+        - id: test
+
+  df:
+    - id: default-df
+      vnf-profile:
+        - id: '1'
+          virtual-link-connectivity:
+            - constituent-cpd-id:
+                - constituent-base-element-id: '1'
+                  constituent-cpd-id: eth0-ext
+              virtual-link-profile-id: osm-ext
+          vnfd-id: cirros_vnffg-vnf
+  description: Simple NS example with vnffgd
+"""