diff options
-rw-r--r-- | docs/source/schemas/resources/shared/xnf_resources.rst | 18 | ||||
-rw-r--r-- | docs/source/schemas/version_1_0.rst | 32 | ||||
-rw-r--r-- | docs/source/schemas/version_1_1.rst | 36 | ||||
-rw-r--r-- | onap_data_provider/resources/data_dictionary_resource.py | 66 | ||||
-rw-r--r-- | onap_data_provider/resources/resource_creator.py | 7 | ||||
-rw-r--r-- | onap_data_provider/resources/sdc_properties_mixins.py | 53 | ||||
-rw-r--r-- | onap_data_provider/resources/xnf_resource.py | 12 | ||||
-rw-r--r-- | onap_data_provider/schemas/infra.schema | 38 | ||||
-rw-r--r-- | onap_data_provider/schemas/infra_1_1.schema | 40 | ||||
-rw-r--r-- | samples/service.yaml | 54 | ||||
-rw-r--r-- | samples/xnfs.yaml | 6 | ||||
-rw-r--r-- | tests/test_data_dictionary_resource.py | 34 | ||||
-rw-r--r-- | tests/test_pnf_resource.py | 36 | ||||
-rw-r--r-- | tests/test_vnf_resource.py | 36 |
14 files changed, 442 insertions, 26 deletions
diff --git a/docs/source/schemas/resources/shared/xnf_resources.rst b/docs/source/schemas/resources/shared/xnf_resources.rst new file mode 100644 index 0000000..71ad609 --- /dev/null +++ b/docs/source/schemas/resources/shared/xnf_resources.rst @@ -0,0 +1,18 @@ +Resources +^^^^^^^^^ + +.. list-table:: + :header-rows: 1 + + * - Property + - Type + - Required + - Comment + * - name + - string + - YES + - + * - xnf_type + - string + - YES + -
\ No newline at end of file diff --git a/docs/source/schemas/version_1_0.rst b/docs/source/schemas/version_1_0.rst index ffc65ca..a7f700a 100644 --- a/docs/source/schemas/version_1_0.rst +++ b/docs/source/schemas/version_1_0.rst @@ -196,6 +196,10 @@ PNF - List of `PNF Properties 1.0`_ - NO - + * - resources + - List of `PNF Resources 1.0`_ + - NO + - .. _PNF deployment artifact 1.0: @@ -205,6 +209,10 @@ PNF .. include:: /schemas/resources/shared/xnf_property.rst +.. _PNF resources 1.0: + +.. include:: /schemas/resources/shared/xnf_resources.rst + VNF --- @@ -231,6 +239,10 @@ VNF - List of `VNF properties 1.0`_ - NO - + * - resources + - List of `VNF Resources 1.0`_ + - NO + - .. _VNF deployment artifact 1.0: @@ -248,6 +260,11 @@ VNF .. include:: /schemas/resources/shared/line_of_business.rst +.. _VNF resources 1.0: + +.. include:: /schemas/resources/shared/xnf_resources.rst + + MSB k8s definition ------------------ @@ -399,3 +416,18 @@ VF modules instantiation parameters - List of key-value parameters - NO - + +Data dictionary +--------------- + +.. list-table:: + :header-rows: 1 + + * - Property + - Type + - Required + - Comment + * - file-path + - string + - YES + - diff --git a/docs/source/schemas/version_1_1.rst b/docs/source/schemas/version_1_1.rst index 152e018..ce74c91 100644 --- a/docs/source/schemas/version_1_1.rst +++ b/docs/source/schemas/version_1_1.rst @@ -200,6 +200,10 @@ Service inputs - boolean - NO - Determines if nested input is going to be created + * - resource-property + - boolean + - NO + - Determines whether use resource's property as an input * - resource - boolean - NO @@ -243,6 +247,10 @@ PNF - List of `PNF Properties 1.1`_ - NO - + * - resources + - List of `PNF Resources 1.1`_ + - NO + - .. _PNF deployment artifact 1.1: @@ -252,6 +260,10 @@ PNF .. include:: /schemas/resources/shared/xnf_property.rst +.. _PNF resources 1.1: + +.. include:: /schemas/resources/shared/xnf_resources.rst + VNF --- @@ -290,6 +302,10 @@ VNF - List of `VNF properties 1.1`_ - NO - + * - resources + - List of `VNF Resources 1.1`_ + - NO + - .. _VNF deployment artifact 1.1: @@ -299,6 +315,11 @@ VNF .. include:: /schemas/resources/shared/owning_entity.rst +.. _VNF resources 1.1: + +.. include:: /schemas/resources/shared/xnf_resources.rst + + MSB k8s definition ------------------ @@ -450,3 +471,18 @@ VF modules instantiation parameters - List of key-value parameters - NO - + +Data dictionary +--------------- + +.. list-table:: + :header-rows: 1 + + * - Property + - Type + - Required + - Comment + * - file-path + - string + - YES + - diff --git a/onap_data_provider/resources/data_dictionary_resource.py b/onap_data_provider/resources/data_dictionary_resource.py new file mode 100644 index 0000000..4c9402e --- /dev/null +++ b/onap_data_provider/resources/data_dictionary_resource.py @@ -0,0 +1,66 @@ +"""Customer resource module.""" +""" + Copyright 2022 Deutsche Telekom AG + + 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. +""" + +import logging +from typing import Any, Dict +from onapsdk.cds import DataDictionarySet # type: ignore + +from onapsdk.exceptions import FileError # type: ignore +from onap_data_provider.resources.resource import Resource + + +class DataDictionarySetResource(Resource): + """DataDictionarySet resource class + + creates Data Dictionaries + """ + + def __init__(self, data: Dict[str, Any]) -> None: + """Initialize data dictionary set resource + + Args: + data (Dict[str, Any]): Data needed to create resource. + """ + super().__init__(data) + self._data_dictionaries: DataDictionarySet = None + + def create(self) -> None: + """Create data dictionaries. + + Create data dictionaries from input data. + """ + if self.data_dictionaries: + self.data_dictionaries.upload() + + @property + def data_dictionaries(self) -> DataDictionarySet: + """Get DataDictionarySet. + + Returns: + DataDictionarySet: Created DataDictionarySet containing DataDictionary instances. + """ + if not self._data_dictionaries: + try: + self._data_dictionaries = DataDictionarySet.load_from_file( + self.data.get("file-path") + ) + except FileError: + logging.error( + f"Error when reading from file {self.data.get('file-path')}" + ) + return None + return self._data_dictionaries diff --git a/onap_data_provider/resources/resource_creator.py b/onap_data_provider/resources/resource_creator.py index 34cbafd..de9ee9b 100644 --- a/onap_data_provider/resources/resource_creator.py +++ b/onap_data_provider/resources/resource_creator.py @@ -38,6 +38,7 @@ from .service_instance_resource import ( from .vendor_resource import VendorResource from .vnf_resource import VnfResource from .vsp_resource import VspResource +from .data_dictionary_resource import DataDictionarySetResource from ..versions import VersionsEnum if typing.TYPE_CHECKING: @@ -128,6 +129,11 @@ class ResourceCreator(ABC): VersionsEnum.V1_0: MsbK8SDefinitionResource, VersionsEnum.V1_1: MsbK8SDefinitionResource, }, + "data-dictionaries": { + VersionsEnum.NONE: DataDictionarySetResource, + VersionsEnum.V1_0: DataDictionarySetResource, + VersionsEnum.V1_1: DataDictionarySetResource, + }, } @classmethod @@ -157,6 +163,7 @@ class ResourceCreator(ABC): - platform: PlatformResource - owning-entity: OwningEntityResource - msb-k8s-definition: MsbK8SDefinitionResource + - data-dictionaries: DataDictionarySetResource Args: resource_type (str): Resource type to create diff --git a/onap_data_provider/resources/sdc_properties_mixins.py b/onap_data_provider/resources/sdc_properties_mixins.py index 28d06c3..1b93365 100644 --- a/onap_data_provider/resources/sdc_properties_mixins.py +++ b/onap_data_provider/resources/sdc_properties_mixins.py @@ -20,8 +20,9 @@ from typing import Any, Dict, List, Union from onapsdk.exceptions import SDKException, ValidationError, ParameterError # type: ignore from onapsdk.sdc.component import Component # type: ignore -from onapsdk.sdc.properties import NestedInput, Property # type: ignore +from onapsdk.sdc.properties import NestedInput, Property, ComponentProperty # type: ignore from onapsdk.sdc.sdc_resource import SdcResource # type: ignore +from onapsdk.sdc.vf import Vf # type: ignore class SdcPropertiesMixins(ABC): @@ -116,24 +117,50 @@ class SdcPropertiesMixins(ABC): comp: Component = propresource.get_component_by_name(data["resource"]) propresource.declare_input(NestedInput(comp.sdc_resource, comp.sdc_resource.get_input(data["name"]))) + def declare_resource_property_input( + self, sdc_resource: Union[SdcResource, Component], input_data: Dict[str, Any] + ) -> None: + """Declare input from resource's property. + + Args: + sdc_resource (SdcResource): Resource for which input is going to be declared + input_data (Dict[str, Any]): Data used for input creation. + """ + if not isinstance(sdc_resource, Vf): + logging.error("Resource property as input is currently supported only for Vf resources.") + return + resource_component: Component = sdc_resource.get_component_by_name( + input_data["resource"] + ) + component_property: ComponentProperty = resource_component.get_property( + input_data["name"] + ) + sdc_resource.declare_input(component_property) + def set_inputs( - self, propresource: Union[SdcResource, Component], data: List[Dict[str, Any]] + self, sdc_resource: Union[SdcResource, Component], inputs_data: List[Dict[str, Any]], ) -> None: - """Set inputs of an SdcResource. + """Set inputs of an SdcResource. Args: - sdcresource (SdcResource): the SdcResource the inputs should belong to - data (Dict[str, Any]): Data needed to create resource. + sdc_resource (SdcResource): the SdcResource the inputs should belong to + inputs_data (Dict[str, Any]): Input data to be set into resource. """ - for property_data in data: # type: Dict[str, Any] - if property_data.get("nested-input"): - self.declare_nested_input(propresource, property_data) + for input_data in inputs_data: # type: Dict[str, Any] + if input_data.get("nested-input"): + self.declare_nested_input(sdc_resource, input_data) + elif input_data.get("resource-property"): + self.declare_resource_property_input(sdc_resource, input_data) + # In case resource already has input with given name then set its value only elif any( - (prop.name == property_data["name"] for prop in propresource.inputs) + ( + resource_input.name == input_data["name"] + for resource_input in sdc_resource.inputs + ) ): - propresource.set_input_default_value( - propresource.get_input(property_data["name"]), - property_data.get("value"), + sdc_resource.set_input_default_value( + sdc_resource.get_input(input_data["name"]), + input_data.get("value"), ) else: - self.declare_input(propresource, property_data) + self.declare_input(sdc_resource, input_data) diff --git a/onap_data_provider/resources/xnf_resource.py b/onap_data_provider/resources/xnf_resource.py index 63b01dc..29b5011 100644 --- a/onap_data_provider/resources/xnf_resource.py +++ b/onap_data_provider/resources/xnf_resource.py @@ -16,10 +16,12 @@ """ from abc import ABC +import logging from typing import Any, Dict from onapsdk.sdc.vsp import Vsp # type: ignore from onapsdk.sdc.sdc_resource import SdcResource # type: ignore from onapsdk.sdc.properties import Property # type: ignore +from onapsdk.sdc.vfc import Vfc # type: ignore from .sdc_properties_mixins import SdcPropertiesMixins @@ -44,13 +46,21 @@ class XnfResource(SdcPropertiesMixins, ABC): if (vsp_name := data.get("vsp")) is not None: self._xnf.vsp = Vsp(vsp_name) self._xnf.create() - if (artifact_data := data.get("deployment_artifact")) is not None: + if data.get("deployment_artifact") is not None: self._xnf.add_deployment_artifact( artifact_type=data["deployment_artifact"]["artifact_type"], artifact_name=data["deployment_artifact"]["artifact_name"], artifact_label=data["deployment_artifact"]["artifact_label"], artifact=data["deployment_artifact"]["artifact_file_name"], ) + if (resources := data.get("resources")) is not None: + for resource_data in resources: + if resource_data["xnf_type"] == "VFC": + self._xnf.add_resource(Vfc(name=resource_data["name"])) + else: + logging.warning( + f"Provided xNF resource of type {resource_data['xnf_type']} is not supported." + ) self.set_properties(self._xnf, data.get("properties", [])) self.set_inputs(self._xnf, data.get("inputs", [])) self._xnf.onboard() diff --git a/onap_data_provider/schemas/infra.schema b/onap_data_provider/schemas/infra.schema index 55c5cbf..d276755 100644 --- a/onap_data_provider/schemas/infra.schema +++ b/onap_data_provider/schemas/infra.schema @@ -318,6 +318,18 @@ properties: - artifact_file_name properties: *props inputs: *props + resources: + type: array + items: + - type: object + properties: + xnf_type: + type: string + name: + type: string + required: + - xnf_type + - name required: - name required: @@ -352,6 +364,18 @@ properties: - artifact_file_name properties: *props inputs: *props + resources: + type: array + items: + - type: object + properties: + xnf_type: + type: string + name: + type: string + required: + - xnf_type + - name required: - name required: @@ -525,3 +549,17 @@ properties: - name - version - artifact + data-dictionaries-sets: + type: array + items: + - type: object + properties: + data-dictionaries: + type: object + properities: + file-path: + type: string + required: + - file-path + required: + - data-dictionaries diff --git a/onap_data_provider/schemas/infra_1_1.schema b/onap_data_provider/schemas/infra_1_1.schema index 063e78a..8320b74 100644 --- a/onap_data_provider/schemas/infra_1_1.schema +++ b/onap_data_provider/schemas/infra_1_1.schema @@ -292,6 +292,8 @@ properties: type: string nested-input: type: boolean + resource-property: + type: boolean resource: type: string value: @@ -352,6 +354,18 @@ properties: - artifact_file_name properties: *props inputs: *props + resources: + type: array + items: + - type: object + properties: + xnf_type: + type: string + name: + type: string + required: + - xnf_type + - name required: - name required: @@ -392,6 +406,18 @@ properties: - artifact_file_name properties: *props inputs: *props + resources: + type: array + items: + - type: object + properties: + xnf_type: + type: string + name: + type: string + required: + - xnf_type + - name required: - name required: @@ -567,3 +593,17 @@ properties: - name - version - artifact + data-dictionaries-sets: + type: array + items: + - type: object + properties: + data-dictionaries: + type: object + properities: + file-path: + type: string + required: + - file-path + required: + - data-dictionaries diff --git a/samples/service.yaml b/samples/service.yaml index 36c92d7..8472f1b 100644 --- a/samples/service.yaml +++ b/samples/service.yaml @@ -24,11 +24,21 @@ resources: - name: sample-vnf # Make sure it exists! type: VF properties: - controller_actor: "CDS" - skip_post_instantiation_configuration: False - sdnc_artifact_name: "vnf" - sdnc_model_version: "1.0.0" - sdnc_model_name: "ubuntu20" + - name: controller_actor + type: string + value: "CDS" + - name: skip_post_instantiation_configuration + type: boolean + value: False + - name: sdnc_artifact_name + type: string + value: "vnf" + - name: sdnc_model_version + type: string + value: "1.0.0" + - name: sdnc_model_name + type: string + value: "ubuntu20" - service: name: sample-service-with-pnf resources: @@ -40,13 +50,37 @@ resources: - name: sample-pnf # Make sure it exists! type: PNF properties: - controller_actor: "CDS" - skip_post_instantiation_configuration: False - sdnc_artifact_name: "vnf" - sdnc_model_version: "1.0.0" - sdnc_model_name: "ubuntu20" + - name: controller_actor + type: string + value: "CDS" + - name: skip_post_instantiation_configuration + type: boolean + value: False + - name: sdnc_artifact_name + type: string + value: "vnf" + - name: sdnc_model_version + type: string + value: "1.0.0" + - name: sdnc_model_name + type: string + value: "ubuntu20" - service: name: sample-service-with-vl resources: - name: sample-vl # Make sure it exists! type: VL + + - service: + name: test-nested-inputs-service-1 + category: Network L4+ + description: LFN demo service + instantiation-type: Macro + resources: + - name: test-ar-pnf + type: PNF + inputs: + - resource-property: true + resource: test-ar-pnf + name: resource-property-name + value: test diff --git a/samples/xnfs.yaml b/samples/xnfs.yaml index 81f344b..805a2c1 100644 --- a/samples/xnfs.yaml +++ b/samples/xnfs.yaml @@ -25,6 +25,12 @@ resources: vendor: sample-vendor # Make sure it exists! category: Allotted Resource subcategory: Allotted Resource + - vnf: + name: sample-vnf-with-vfc-as-property + vsp: sample-vsp # Make sure it exists! + resources: + - xnf_type: "VFC" + name: "AllottedResource" pnfs: - pnf: diff --git a/tests/test_data_dictionary_resource.py b/tests/test_data_dictionary_resource.py new file mode 100644 index 0000000..31369f7 --- /dev/null +++ b/tests/test_data_dictionary_resource.py @@ -0,0 +1,34 @@ +from unittest.mock import patch, PropertyMock + +from onapsdk.cds import DataDictionarySet +from onapsdk.exceptions import FileError +from onap_data_provider.resources.data_dictionary_resource import ( + DataDictionarySetResource, +) + + +@patch( + "onap_data_provider.resources.data_dictionary_resource.DataDictionarySet.load_from_file" +) +def test_data_dictionary_resource_data_dictionary_set( + mock_data_dictionary_set_load_from_file, +): + dds = DataDictionarySetResource({"json-file-path": "test"}) + mock_data_dictionary_set_load_from_file.return_value = 1 + assert dds.data_dictionaries is not None + + +@patch("onap_data_provider.resources.data_dictionary_resource.DataDictionarySet.upload") +@patch( + "onap_data_provider.resources.data_dictionary_resource.DataDictionarySet.load_from_file" +) +def test_data_dictionary_resource_data_dictionary_set_create( + mock_load_from_file, mock_data_dictionary_set_upload +): + ddsr = DataDictionarySetResource({"file-path": "test"}) + mock_load_from_file.side_effect = FileError + mock_data_dictionary_set_upload.assert_not_called() + mock_load_from_file.side_effect = None + mock_load_from_file.return_value = DataDictionarySet() + ddsr.create() + mock_data_dictionary_set_upload.assert_called() diff --git a/tests/test_pnf_resource.py b/tests/test_pnf_resource.py index c58717a..62dc3ae 100644 --- a/tests/test_pnf_resource.py +++ b/tests/test_pnf_resource.py @@ -1,4 +1,4 @@ -from unittest.mock import patch, PropertyMock +from unittest.mock import MagicMock, patch, PropertyMock from onap_data_provider.resources.pnf_resource import PnfResource @@ -26,3 +26,37 @@ def test_pnf_resource_pnf(mock_pnf_created): assert pnf_resource.pnf is None mock_pnf_created.return_value = True assert pnf_resource.pnf is not None + + +@patch( + "onap_data_provider.resources.pnf_resource.Pnf.create", +) +@patch( + "onap_data_provider.resources.pnf_resource.Pnf.add_resource", +) +@patch( + "onap_data_provider.resources.pnf_resource.Pnf.onboard", +) +@patch( + "onap_data_provider.resources.pnf_resource.PnfResource.pnf", + new_callable=PropertyMock, +) +@patch( + "onap_data_provider.resources.xnf_resource.Vfc", +) +def test_pnf_resource_onboards_with_vfc( + mock_vfc, mock_pnf, mock_onboard, mock_add_resource, mock_pnf_create +): + mock_vfc = MagicMock() + mock_pnf.return_value = None + data_no_composition = {"name": "test_pnf"} + pnf_resource = PnfResource(data_no_composition) + pnf_resource.create() + mock_add_resource.assert_not_called() + data_with_composition = { + "name": "test_pnf", + "resources": [{"name": "test", "xnf_type": "VFC"}], + } + pnf_resource = PnfResource(data_with_composition) + pnf_resource.create() + mock_add_resource.assert_called_once() diff --git a/tests/test_vnf_resource.py b/tests/test_vnf_resource.py index 6398aec..e35cded 100644 --- a/tests/test_vnf_resource.py +++ b/tests/test_vnf_resource.py @@ -1,4 +1,4 @@ -from unittest.mock import patch, PropertyMock +from unittest.mock import MagicMock, patch, PropertyMock from onap_data_provider.resources.vnf_resource import VnfResource @@ -26,3 +26,37 @@ def test_vnf_resource_vnf(mock_vnf_created): assert vnf_resource.vnf is None mock_vnf_created.return_value = True assert vnf_resource.vnf is not None + + +@patch( + "onap_data_provider.resources.vnf_resource.Vf.create", +) +@patch( + "onap_data_provider.resources.vnf_resource.Vf.add_resource", +) +@patch( + "onap_data_provider.resources.vnf_resource.Vf.onboard", +) +@patch( + "onap_data_provider.resources.vnf_resource.VnfResource.vnf", + new_callable=PropertyMock, +) +@patch( + "onap_data_provider.resources.xnf_resource.Vfc", +) +def test_vnf_resource_onboards_with_vfc( + mock_vfc, mock_vnf, mock_onboard, mock_add_resource, mock_vnf_create +): + mock_vfc = MagicMock() + mock_vnf.return_value = None + data_no_composition = {"name": "test_vnf"} + vnf_resource = VnfResource(data_no_composition) + vnf_resource.create() + mock_add_resource.assert_not_called() + data_with_composition = { + "name": "test_vnf", + "resources": [{"name": "test", "xnf_type": "VFC"}], + } + vnf_resource = VnfResource(data_with_composition) + vnf_resource.create() + mock_add_resource.assert_called_once() |