From 828f3f29a221f4415e0962e63a491462c1b02370 Mon Sep 17 00:00:00 2001 From: "selvi.j" Date: Tue, 16 May 2023 05:43:48 +0000 Subject: [PATCH] Feature 10980: Service Function Chaining Change-Id: Ib0e34f152fa5b0f601fffe67b91882f5cc2fc11c Signed-off-by: sritharan Signed-off-by: selvi.j --- osm_nbi/descriptor_topics.py | 41 ++++++++++++++ osm_nbi/instance_topics.py | 12 ++++ osm_nbi/tests/test_descriptor_topics.py | 56 +++++++++++++++++++ osm_nbi/tests/test_pkg_descriptors.py | 74 +++++++++++++++++++++++++ 4 files changed, 183 insertions(+) diff --git a/osm_nbi/descriptor_topics.py b/osm_nbi/descriptor_topics.py index 98a41d1..b165b76 100644 --- a/osm_nbi/descriptor_topics.py +++ b/osm_nbi/descriptor_topics.py @@ -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"))) diff --git a/osm_nbi/instance_topics.py b/osm_nbi/instance_topics.py index 8488a93..695a8f8 100644 --- a/osm_nbi/instance_topics.py +++ b/osm_nbi/instance_topics.py @@ -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( diff --git a/osm_nbi/tests/test_descriptor_topics.py b/osm_nbi/tests/test_descriptor_topics.py index dcde0a5..f6d4001 100755 --- a/osm_nbi/tests/test_descriptor_topics.py +++ b/osm_nbi/tests/test_descriptor_topics.py @@ -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() diff --git a/osm_nbi/tests/test_pkg_descriptors.py b/osm_nbi/tests/test_pkg_descriptors.py index 39c28fe..d77b79f 100644 --- a/osm_nbi/tests/test_pkg_descriptors.py +++ b/osm_nbi/tests/test_pkg_descriptors.py @@ -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 +""" -- 2.25.1