diff options
-rw-r--r-- | src/onapsdk/aai/business/service.py | 29 | ||||
-rw-r--r-- | src/onapsdk/so/instantiation.py | 79 | ||||
-rw-r--r-- | src/onapsdk/so/templates/instantiate_network_vnf_macro_base.json.j2 | 144 | ||||
-rw-r--r-- | tests/test_aai_service_instance.py | 20 | ||||
-rw-r--r-- | tests/test_aai_site_resource.py | 1 | ||||
-rw-r--r-- | tests/test_so_instantiation.py | 21 |
6 files changed, 291 insertions, 3 deletions
diff --git a/src/onapsdk/aai/business/service.py b/src/onapsdk/aai/business/service.py index d0f7876..49b94cd 100644 --- a/src/onapsdk/aai/business/service.py +++ b/src/onapsdk/aai/business/service.py @@ -467,10 +467,12 @@ class ServiceInstance(Instance): # pylint: disable=too-many-instance-attributes network: "Network", line_of_business: "LineOfBusiness", platform: "Platform", + a_la_carte: bool = True, cloud_region: "CloudRegion" = None, tenant: "Tenant" = None, network_instance_name: str = None, - subnets: Iterator["Subnet"] = None) -> "NetworkInstantiation": + subnets: Iterator["Subnet"] = None, + network_details: "NetworkDetails" = None) -> "NetworkInstantiation": """Add network into service instance. Instantiate vl. @@ -491,6 +493,9 @@ class ServiceInstance(Instance): # pylint: disable=too-many-instance-attributes If no value is provided it's going to be "Python_ONAP_SDK_network_instance_{str(uuid4())}". Defaults to None. + subnets(Subnet list, Optional): subnets use in instantiation request. + a_la_carte (bool): instantiation type for vnf. Defaults to True. + network_details(NetworkDetails) : generic NetworkDetails structure. Raises: StatusError: Service orchestration status is not "Active" @@ -502,7 +507,23 @@ class ServiceInstance(Instance): # pylint: disable=too-many-instance-attributes if not self.active: raise StatusError(self.ACTIVE_STATUS_MESSAGE) - return NetworkInstantiation.instantiate_ala_carte( + if a_la_carte: + return NetworkInstantiation.instantiate_ala_carte( + self, + network, + line_of_business, + platform, + cloud_region=cloud_region, + tenant=tenant, + network_instance_name=network_instance_name, + subnets=subnets + ) + + if network_details.vnf_id is None: + msg = '"vnf_id" is required on instantiate_macro method' + raise ParameterError(msg) + + return NetworkInstantiation.instantiate_macro( self, network, line_of_business, @@ -510,9 +531,11 @@ class ServiceInstance(Instance): # pylint: disable=too-many-instance-attributes cloud_region=cloud_region, tenant=tenant, network_instance_name=network_instance_name, - subnets=subnets + subnets=subnets, + network_details=network_details ) + def delete(self, a_la_carte: bool = True) -> "ServiceDeletionRequest": """Create service deletion request. diff --git a/src/onapsdk/so/instantiation.py b/src/onapsdk/so/instantiation.py index 084a112..b84e3a1 100644 --- a/src/onapsdk/so/instantiation.py +++ b/src/onapsdk/so/instantiation.py @@ -220,6 +220,24 @@ class Subnet: # pylint: disable=too-many-instance-attributes raise ParameterError(msg) + +@dataclass +class NetworkDetailsElement: # pylint: disable=too-many-instance-attributes + """Class to store network details child resource parameters.""" + + network_details_element_type: Optional[str] + network_details_element_parameters: Dict[str, str] + child_resources: Optional[List["NetworkDetailsElement"]] = None + + +@dataclass +class NetworkDetails: # pylint: disable=too-many-instance-attributes + """Class to store network details instantiation parameters.""" + + network_type: str + vnf_id: str + child_resources: List[NetworkDetailsElement] + related_to: List[NetworkDetailsElement] class Instantiation(OrchestrationRequest, ABC): """Abstract class used for instantiation.""" @@ -1247,3 +1265,64 @@ class NetworkInstantiation(NodeTemplateInstantiation): # pylint: disable=too-ma platform=platform, network=network_object ) + + @classmethod + def instantiate_macro(cls, # pylint: disable=too-many-arguments + aai_service_instance: "ServiceInstance", + network_object: "Network", + line_of_business: str, + platform: str, + cloud_region: "CloudRegion", + tenant: "Tenant", + network_instance_name: str = None, + subnets: Iterable[Subnet] = None, + network_details: "NetworkDetails" = None) -> "NetworkInstantiation": + """Instantiate Network using a'la carte method. + + Args: + aai_service_instance (Service Instance object) : AAI Service Instance obj + network_object (Network): Network to instantiate + line_of_business (str): LineOfBusiness name to use in instantiation request + platform (str): Platform name to use in instantiation request + cloud_region (CloudRegion): Cloud region to use in instantiation request. + tenant (Tenant): Tenant to use in instnatiation request. + network_instance_name (str, optional): Network instance name. Defaults to None. + subnets(Array) : subnet array + network_details : generic network structure + + Returns: + NetworkInstantiation: NetworkInstantiation object + + """ + if network_instance_name is None: + network_instance_name = \ + f"Python_ONAP_SDK_network_instance_{str(uuid4())}" + response: dict = cls.send_message_json( + "POST", + f"Instantiate {aai_service_instance.sdc_service.name} ", + (f"{cls.base_url}/onap/so/infra/serviceInstantiation/{cls.api_version}/" + f"serviceInstances/{aai_service_instance.instance_id}/networks"), + data=jinja_env().get_template("instantiate_network_vnf_macro_base.json.j2"). + render( + instance_name=network_instance_name, + network=network_object, + service=aai_service_instance.sdc_service, + cloud_region=cloud_region or \ + next(aai_service_instance.service_subscription.cloud_regions), + tenant=tenant or next(aai_service_instance.service_subscription.tenants), + line_of_business=line_of_business, + platform=platform, + service_instance=aai_service_instance, + subnets=subnets, + network_details=network_details + ), + headers=headers_so_creator(OnapService.headers) + ) + return cls( + name=network_instance_name, + request_id=response["requestReferences"]["requestId"], + instance_id=response["requestReferences"]["instanceId"], + line_of_business=line_of_business, + platform=platform, + network=network_object + ) diff --git a/src/onapsdk/so/templates/instantiate_network_vnf_macro_base.json.j2 b/src/onapsdk/so/templates/instantiate_network_vnf_macro_base.json.j2 new file mode 100644 index 0000000..29e600c --- /dev/null +++ b/src/onapsdk/so/templates/instantiate_network_vnf_macro_base.json.j2 @@ -0,0 +1,144 @@ +{ + "requestDetails": { + "requestInfo": { + "instanceName": "{{ instance_name }}", + "source": "VID", + "suppressRollback": false, + "requestorId": "test", + "productFamilyId": "{{ service_instance.model_invariant_id }}" + }, + "modelInfo": { + "modelType": "network", + "modelInvariantId": "{{ network.model_invariant_id }}", + "modelVersionId": "{{ network.model_version_id }}", + "modelName": "{{ network.model_name }}", + "modelVersion": "{{ network.model_version }}", + "modelCustomizationId": "{{ network.model_customization_id }}", + "modelCustomizationName": "{{ network.name }}" + }, + "requestParameters": { + "userParams": [ + { + "Homing_Solution": "none" + }, + { + "service": { + "resources": { + "networks": [ + { + "modelInfo": { + "modelType": "network", + "modelInvariantId": "{{ network.model_invariant_id }}", + "modelVersionId": "{{ network.model_version_id }}", + "modelName": "{{ network.model_name }}", + "modelVersion": "{{ network.model_version }}", + "modelCustomizationId": "{{ network.model_customization_id }}", + "modelCustomizationName": "{{ network.name }}" + }, + "cloudConfiguration": { + "tenantId": "{{ tenant.tenant_id }}", + "cloudOwner": "{{ cloud_region.cloud_owner }}", + "lcpCloudRegionId": "{{ cloud_region.cloud_region_id }}" + }, + "productFamilyId": "network-test", + "instanceName": "network1", + "networkDetails": [ + { + "networkType": "{{ network_details.network_type }}", + "vnf-id": "{{ network_details.vnf_id }}"{% if network_details.child_resources %},{% endif %} + {% if network_details.child_resources %} + "child-resources": [ + {% for child_resource in network_details.child_resources %} + { + "{{ child_resource.network_details_element_type }}": { + {% for key, value in child_resource.network_details_element_parameters.items() %} + "{{ key }}": "{{ value }}"{% if not loop.last %},{% endif %}{% if loop.last and child_resource.child_resources %},{% endif %} + {% endfor %} + {% if child_resource.child_resources %} + "child-resources": [ + {% for child_resource_child_resource in child_resource.child_resources %} + { + {% for key, value in child_resource_child_resource.network_details_element_parameters.items() %} + "{{ key }}": "{{ value }}"{% if not loop.last %},{% endif %} + {% endfor %} + }{% if not loop.last %},{% endif %} + {% endfor %} + ] + {% endif %} + } + }{% if not loop.last %},{% endif %} + {% endfor %} + ], + {% endif %} + {% if network_details.related_to %} + "related-to": [ + {% for related_to in network_details.related_to %} + { + "{{ related_to.network_details_element_type }}": { + {% for key, value in related_to.network_details_element_parameters.items() %} + "{{ key }}": "{{ value }}"{% if not loop.last %},{% endif %}{% if loop.last and related_to.child_resources %},{% endif %} + {% endfor %} + {% if related_to.child_resources %} + "child-resources": [ + {% for related_to_child_resource in related_to.child_resources %} + { + {% for key, value in related_to_child_resource.network_details_element_parameters.items() %} + "{{ key }}": "{{ value }}"{% if not loop.last %},{% endif %} + {% endfor %} + }{% if not loop.last %},{% endif %} + {% endfor %} + ] + {% endif %} + } + }{% if not loop.last %},{% endif %} + {% endfor %} + ] + {% endif %} + } + ], + "instanceParams": [] + } + ] + }, + "modelInfo": { + "modelType": "network", + "modelInvariantId": "{{ network.model_invariant_id }}", + "modelVersionId": "{{ network.model_version_id }}", + "modelName": "{{ network.model_name }}", + "modelVersion": "{{ network.model_version }}", + "modelCustomizationId": "{{ network.model_customization_id }}", + "modelCustomizationName": "{{ network.name }}" + }, + "instanceParams": [] + } + } + ], + "aLaCarte": false, + "testApi": "GR_API" + }, + "cloudConfiguration": { + "tenantId": "{{ tenant.tenant_id }}", + "cloudOwner": "{{ cloud_region.cloud_owner }}", + "lcpCloudRegionId": "{{ cloud_region.cloud_region_id }}" + }, + "lineOfBusiness": { + "lineOfBusinessName": "{{ line_of_business }}" + }, + "platform": { + "platformName": "{{ platform }}" + }, + "relatedInstanceList": [{ + "relatedInstance": { + "instanceId": "{{ service_instance.instance_id }}", + "modelInfo": { + "modelType": "service", + "modelName": "{{ service.name }}", + "modelInvariantId": "{{ service.unique_uuid }}", + "modelVersion": "1.0", + "modelVersionId": "{{ service.identifier }}" + } + } + }] + }, + "serviceInstanceId" : "{{ service_instance.instance_id }}" +}
\ No newline at end of file diff --git a/tests/test_aai_service_instance.py b/tests/test_aai_service_instance.py index 3b758d8..070c5be 100644 --- a/tests/test_aai_service_instance.py +++ b/tests/test_aai_service_instance.py @@ -21,6 +21,8 @@ from onapsdk.so.deletion import ServiceDeletionRequest from onapsdk.so.instantiation import NetworkInstantiation, VnfInstantiation from onapsdk.exceptions import StatusError +from src.onapsdk.so.instantiation import NetworkDetails + RELATIONSHIPS_VNF = { "relationship": [ { @@ -245,6 +247,24 @@ def test_service_instance_add_network(mock_sdc_service, mock_network_instantiati mock.MagicMock()) mock_network_instantiation.assert_called_once() +@mock.patch.object(NetworkInstantiation, "instantiate_macro") +@mock.patch.object(ServiceInstance, "sdc_service", new_callable=mock.PropertyMock) +def test_service_instance_generic_network(mock_sdc_service, mock_network_instantiation): + service_instance = ServiceInstance(service_subscription=mock.MagicMock(), + instance_id="test_service_instance_id") + service_instance.orchestration_status = "Active" + network_details = NetworkDetails(vnf_id="vnf_id", + network_type="generic-network", + child_resources=mock.MagicMock(), + related_to=mock.MagicMock()) + service_instance.add_network(mock.MagicMock(), + mock.MagicMock(), + mock.MagicMock(), + a_la_carte=False, + tenant=mock.MagicMock(), + cloud_region=mock.MagicMock(), + network_details=network_details) + mock_network_instantiation.assert_called_once() @mock.patch.object(ServiceDeletionRequest, "send_request") @mock.patch.object(ServiceInstance, "sdc_service", new_callable=mock.PropertyMock) diff --git a/tests/test_aai_site_resource.py b/tests/test_aai_site_resource.py index 0150eff..17527a2 100644 --- a/tests/test_aai_site_resource.py +++ b/tests/test_aai_site_resource.py @@ -101,3 +101,4 @@ def test_site_resource_link_to_site_resource(mock_add_relationship): "relationship-key": "site_resource.site-resource-id", "relationship-value": "test-site-resource-id", }] + diff --git a/tests/test_so_instantiation.py b/tests/test_so_instantiation.py index e3dcfd7..0269110 100644 --- a/tests/test_so_instantiation.py +++ b/tests/test_so_instantiation.py @@ -1359,3 +1359,24 @@ def test_so_service_vnf_load_from_yaml(): assert len(so_vnf_vf_module_2.parameters) == 2 assert so_vnf_vf_module_2.parameters["param-vfm2"] == "value-vfm2" assert so_vnf_vf_module_2.parameters["param-vfm3"] == "value-vfm3" + + +@mock.patch.object(NetworkInstantiation, "send_message_json") +@mock.patch.object(NetworkPreload, "send_message_json") +def test_network_instantiation(mock_network_preload, mock_network_instantiation_send_message): + aai_service_instance_mock = mock.MagicMock() + aai_service_instance_mock.instance_id = "test_instance_id" + vnf_instantiation = NetworkInstantiation. \ + instantiate_macro(aai_service_instance=aai_service_instance_mock, + network_object=mock.MagicMock(), + line_of_business="test_lob", + platform="test_platform", + cloud_region=mock.MagicMock(), + tenant=mock.MagicMock(), + network_details=mock.MagicMock()) + mock_network_instantiation_send_message.assert_called_once() + method, _, url = mock_network_instantiation_send_message.call_args[0] + assert method == "POST" + assert url == (f"{NetworkInstantiation.base_url}/onap/so/infra/serviceInstantiation/" + f"{NetworkInstantiation.api_version}/serviceInstances/" + f"{aai_service_instance_mock.instance_id}/networks") |