From f2adf542e878c96895210f97ebf1ebb763b2f465 Mon Sep 17 00:00:00 2001 From: Michal Jagiello Date: Mon, 17 Oct 2022 12:46:49 +0000 Subject: Release ONAP SDK Issue-ID: INT-2150 Signed-off-by: Michal Jagiello Change-Id: I650047c599a5aae6de7c6b42d38e34aea88578e2 --- src/onapsdk/so/instantiation.py | 957 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 957 insertions(+) create mode 100644 src/onapsdk/so/instantiation.py (limited to 'src/onapsdk/so/instantiation.py') diff --git a/src/onapsdk/so/instantiation.py b/src/onapsdk/so/instantiation.py new file mode 100644 index 0000000..bb0bde2 --- /dev/null +++ b/src/onapsdk/so/instantiation.py @@ -0,0 +1,957 @@ +"""Instantion module.""" +# Copyright 2022 Orange, 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. +from abc import ABC +from dataclasses import dataclass, field +from typing import Any, Dict, Iterable, List, Optional +from uuid import uuid4 +from dacite import from_dict + +from onapsdk.aai.business.owning_entity import OwningEntity +from onapsdk.exceptions import ( + APIError, InvalidResponse, ParameterError, ResourceNotFound, StatusError +) +from onapsdk.onap_service import OnapService +from onapsdk.sdnc import NetworkPreload, VfModulePreload +from onapsdk.sdc.service import Network, Service as SdcService, Vnf, VfModule +from onapsdk.utils.jinja import jinja_env +from onapsdk.utils.headers_creator import headers_so_creator +from onapsdk.configuration import settings + +from .so_element import OrchestrationRequest + + +@dataclass +class Operation: + """Operation class with data about method and suffix for VnfOperation.""" + + request_method: str + request_suffix: str + + +class VnfOperation(Operation): # pylint: disable=too-few-public-methods + """Class to store possible operations' data for vnfs (request method and suffix).""" + + UPDATE = Operation("PUT", "") + HEALTHCHECK = Operation("POST", "/healthcheck") + + +@dataclass +class SoServiceVfModule: + """Class to store a VfModule instance parameters.""" + + model_name: str + instance_name: str + parameters: Dict[str, Any] = field(default_factory=dict) + processing_priority: Optional[int] = None + + +@dataclass +class SoServiceXnf: + """Class to store a Xnf instance parameters.""" + + model_name: str + instance_name: str + parameters: Dict[str, Any] = field(default_factory=dict) + processing_priority: Optional[int] = None + + @classmethod + def load(cls, data: Dict[str, Any]) -> "SoServiceVnf": + """Create a vnf instance description object from the dict. + + Useful if you keep your instance data in file. + + Returns: + SoServiceVnf: SoServiceVnf object created from the dictionary + + """ + return from_dict(data_class=cls, data=data) + + +@dataclass +class SoServiceVnf(SoServiceXnf): + """Class to store a Vnf instance parameters.""" + + vf_modules: List[SoServiceVfModule] = field(default_factory=list) + + +@dataclass +class SoServicePnf(SoServiceXnf): + """Class to store a Pnf instance parameters.""" + + +@dataclass +class SoService: + """Class to store SO Service parameters used for macro instantiation. + + Contains value list: List of vnfs to instantiate + Contains value: subscription service type + """ + + subscription_service_type: str + vnfs: List[SoServiceVnf] = field(default_factory=list) + pnfs: List[SoServicePnf] = field(default_factory=list) + instance_name: Optional[str] = None + + @classmethod + def load(cls, data: Dict[str, Any]) -> "SoService": + """Create a service instance description object from the dict. + + Useful if you keep your instance data in file. + + Returns: + SoService: SoService object created from the dictionary + + """ + return from_dict(data_class=cls, data=data) + + + +@dataclass +class VnfParameters: + """Class to store vnf parameters used for macro instantiation. + + Contains value lists: List vnf Instantiation parameters and list of + vfModule parameters + """ + + name: str + vnf_parameters: Iterable["InstantiationParameter"] = None + vfmodule_parameters: Iterable["VfmoduleParameters"] = None + +@dataclass +class VfmoduleParameters: + """Class to store vfmodule parameters used for macro instantiation. + + Contains value lists: List of vfModule parameters + """ + + name: str + vfmodule_parameters: Iterable["InstantiationParameter"] = None + + +@dataclass +class InstantiationParameter: + """Class to store instantiation parameters used for preload or macro instantiation. + + Contains two values: name of parameter and it's value + """ + + name: str + value: str + + +@dataclass +class Subnet: # pylint: disable=too-many-instance-attributes + """Class to store subnet parameters used for preload.""" + + name: str + start_address: str + gateway_address: str + role: str = None + cidr_mask: str = "24" + ip_version: str = "4" + dhcp_enabled: bool = False + dhcp_start_address: Optional[str] = None + dhcp_end_address: Optional[str] = None + + def __post_init__(self) -> None: + """Post init subnet method. + + Checks if both dhcp_start_address and dhcp_end_address values are + provided if dhcp is enabled. + + Raises: + ParameterError: Neither dhcp_start_addres + nor dhcp_end_address are provided + + """ + if self.dhcp_enabled and \ + not all([self.dhcp_start_address, + self.dhcp_end_address]): + msg = "DHCP is enabled but neither DHCP " \ + "start nor end adresses are provided." + raise ParameterError(msg) + + +class Instantiation(OrchestrationRequest, ABC): + """Abstract class used for instantiation.""" + + def __init__(self, + name: str, + request_id: str, + instance_id: str) -> None: + """Instantiate object initialization. + + Initializator used by classes inherited from this abstract class. + + Args: + name (str): instantiated object name + request_id (str): request ID + instance_id (str): instance ID + """ + super().__init__(request_id) + self.name: str = name + self.instance_id: str = instance_id + + +class VfModuleInstantiation(Instantiation): # pytest: disable=too-many-ancestors + """VF module instantiation class.""" + + def __init__(self, + name: str, + request_id: str, + instance_id: str, + vf_module: VfModule) -> None: + """Initialize class object. + + Args: + name (str): vf module name + request_id (str): request ID + instance_id (str): instance ID + vnf_instantiation (VnfInstantiation): VNF instantiation class object + vf_module (VfModule): VF module used for instantiation + """ + super().__init__(name, request_id, instance_id) + self.vf_module: VfModule = vf_module + + @classmethod + def instantiate_ala_carte(cls, # pylint: disable=too-many-arguments + vf_module: "VfModule", + vnf_instance: "VnfInstance", + cloud_region: "CloudRegion", + tenant: "Tenant", + vf_module_instance_name: str = None, + vnf_parameters: Iterable["InstantiationParameter"] = None, + use_preload: bool = True) -> "VfModuleInstantiation": + """Instantiate VF module. + + Iterate throught vf modules from service Tosca file and instantiate vf modules. + + Args: + vf_module (VfModule): VfModule to instantiate + vnf_instance (VnfInstance): VnfInstance object + cloud_region (CloudRegion, optional): Cloud region to use in instantiation request. + Defaults to None. + tenant (Tenant, optional): Tenant to use in instnatiation request. + Defaults to None. + vf_module_instance_name_factory (str, optional): Factory to create VF module names. + It's going to be a prefix of name. Index of vf module in Tosca file will be + added to it. + If no value is provided it's going to be + "Python_ONAP_SDK_vf_module_service_instance_{str(uuid4())}". + Defaults to None. + vnf_parameters (Iterable[InstantiationParameter], optional): Parameters which are + going to be used in preload upload for vf modules or passed in "userParams". + Defaults to None. + use_preload (bool, optional): This flag determines whether instantiation parameters + are used as preload or "userParams" content. Defaults to True + + Yields: + Iterator[VfModuleInstantiation]: VfModuleInstantiation class object. + + """ + if vf_module_instance_name is None: + vf_module_instance_name = \ + f"Python_ONAP_SDK_vf_module_instance_{str(uuid4())}" + if use_preload: + VfModulePreload.upload_vf_module_preload( + vnf_instance, + vf_module_instance_name, + vf_module, + vnf_parameters + ) + vnf_parameters = None + sdc_service: SdcService = vnf_instance.service_instance.sdc_service + response: dict = cls.send_message_json( + "POST", + (f"Instantiate {sdc_service.name} " + f"service vf module {vf_module.name}"), + (f"{cls.base_url}/onap/so/infra/serviceInstantiation/{cls.api_version}/" + f"serviceInstances/{vnf_instance.service_instance.instance_id}/vnfs/" + f"{vnf_instance.vnf_id}/vfModules"), + data=jinja_env().get_template("instantiate_vf_module_ala_carte.json.j2"). + render( + vf_module_instance_name=vf_module_instance_name, + vf_module=vf_module, + service=sdc_service, + cloud_region=cloud_region, + tenant=tenant, + vnf_instance=vnf_instance, + vf_module_parameters=vnf_parameters or [] + ), + headers=headers_so_creator(OnapService.headers) + ) + return VfModuleInstantiation( + name=vf_module_instance_name, + request_id=response["requestReferences"].get("requestId"), + instance_id=response["requestReferences"].get("instanceId"), + vf_module=vf_module + ) + + +class NodeTemplateInstantiation(Instantiation, ABC): # pytest: disable=too-many-ancestors + """Base class for service's node_template object instantiation.""" + + def __init__(self, # pylint: disable=too-many-arguments + name: str, + request_id: str, + instance_id: str, + line_of_business: str, + platform: str) -> None: + """Node template object initialization. + + Args: + name (str): Node template name + request_id (str): Node template instantiation request ID + instance_id (str): Node template instance ID + line_of_business (str): LineOfBusiness name + platform (str): Platform name + """ + super().__init__(name, request_id, instance_id) + self.line_of_business = line_of_business + self.platform = platform + + +class VnfInstantiation(NodeTemplateInstantiation): # pylint: disable=too-many-ancestors + """VNF instantiation class.""" + + def __init__(self, # pylint: disable=too-many-arguments + name: str, + request_id: str, + instance_id: str, + line_of_business: str, + platform: str, + vnf: Vnf) -> None: + """Class VnfInstantion object initialization. + + Args: + name (str): VNF name + request_id (str): request ID + instance_id (str): instance ID + service_instantiation ([type]): ServiceInstantiation class object + line_of_business (str): LineOfBusiness name + platform (str): Platform name + vnf (Vnf): Vnf class object + """ + super().__init__(name, request_id, instance_id, line_of_business, platform) + self.vnf = vnf + + @classmethod + def create_from_request_response(cls, request_response: dict) -> "VnfInstantiation": + """Create VNF instantiation object based on request details. + + Raises: + ResourceNotFound: Service related with given object doesn't exist + ResourceNotFound: No ServiceInstantiation related with given VNF instantiation + ResourceNotFound: VNF related with given object doesn't exist + InvalidResponse: Invalid dictionary - couldn't create VnfInstantiation object + + Returns: + VnfInstantiation: VnfInstantiation object + + """ + if request_response.get("request", {}).get("requestScope") == "vnf" and \ + request_response.get("request", {}).get("requestType") == "createInstance": + service: SdcService = None + for related_instance in request_response.get("request", {}).get("requestDetails", {})\ + .get("relatedInstanceList", []): + if related_instance.get("relatedInstance", {}).get("modelInfo", {})\ + .get("modelType") == "service": + service = SdcService(related_instance.get("relatedInstance", {})\ + .get("modelInfo", {}).get("modelName")) + if not service: + raise ResourceNotFound("No related service in Vnf instance details response") + vnf: Vnf = None + for service_vnf in service.vnfs: + if service_vnf.name == request_response.get("request", {})\ + .get("requestDetails", {}).get("modelInfo", {}).get("modelCustomizationName"): + vnf = service_vnf + if not vnf: + raise ResourceNotFound("No vnf in service vnfs list") + return cls( + name=request_response.get("request", {})\ + .get("instanceReferences", {}).get("vnfInstanceName"), + request_id=request_response.get("request", {}).get("requestId"), + instance_id=request_response.get("request", {})\ + .get("instanceReferences", {}).get("vnfInstanceId"), + line_of_business=request_response.get("request", {})\ + .get("requestDetails", {}).get("lineOfBusiness", {}).get("lineOfBusinessName"), + platform=request_response.get("request", {})\ + .get("requestDetails", {}).get("platform", {}).get("platformName"), + vnf=vnf + ) + raise InvalidResponse("Invalid vnf instantions in response dictionary's requestList") + + @classmethod + def get_by_vnf_instance_name(cls, vnf_instance_name: str) -> "VnfInstantiation": + """Get VNF instantiation request by instance name. + + Raises: + InvalidResponse: Vnf instance with given name does not contain + requestList or the requestList does not contain any details. + + Returns: + VnfInstantiation: Vnf instantiation request object + + """ + response: dict = cls.send_message_json( + "GET", + f"Check {vnf_instance_name} service instantiation status", + (f"{cls.base_url}/onap/so/infra/orchestrationRequests/{cls.api_version}?" + f"filter=vnfInstanceName:EQUALS:{vnf_instance_name}"), + headers=headers_so_creator(OnapService.headers) + ) + key = "requestList" + if not response.get(key, []): + raise InvalidResponse(f"{key} of a Vnf instance is missing.") + for details in response[key]: + return cls.create_from_request_response(details) + msg = f"No details available in response dictionary's {key}." + raise InvalidResponse(msg) + + @classmethod + def instantiate_ala_carte(cls, # pylint: disable=too-many-arguments + aai_service_instance: "ServiceInstance", + vnf_object: "Vnf", + line_of_business: str, + platform: str, + cloud_region: "CloudRegion", + tenant: "Tenant", + sdc_service: "SdcService", + vnf_instance_name: str = None, + vnf_parameters: Iterable["InstantiationParameter"] = None + ) -> "VnfInstantiation": + """Instantiate Vnf using a'la carte method. + + Args: + vnf_object (Vnf): Vnf to instantiate + line_of_business_object (LineOfBusiness): LineOfBusiness to use in instantiation request + platform_object (Platform): Platform to use in instantiation request + cloud_region (CloudRegion): Cloud region to use in instantiation request. + tenant (Tenant): Tenant to use in instnatiation request. + vnf_instance_name (str, optional): Vnf instance name. Defaults to None. + vnf_parameters (Iterable[InstantiationParameter], optional): Instantiation parameters + that are sent in the request. Defaults to None + + Returns: + VnfInstantiation: VnfInstantiation object + + """ + if vnf_instance_name is None: + vnf_instance_name = \ + f"Python_ONAP_SDK_vnf_instance_{str(uuid4())}" + response: dict = cls.send_message_json( + "POST", + (f"Instantiate {sdc_service.name} " + f"service vnf {vnf_object.name}"), + (f"{cls.base_url}/onap/so/infra/serviceInstantiation/{cls.api_version}/" + f"serviceInstances/{aai_service_instance.instance_id}/vnfs"), + data=jinja_env().get_template("instantiate_vnf_ala_carte.json.j2"). + render( + instance_name=vnf_instance_name, + vnf=vnf_object, + service=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, + vnf_parameters=vnf_parameters or [] + ), + headers=headers_so_creator(OnapService.headers) + ) + return VnfInstantiation( + name=vnf_instance_name, + request_id=response["requestReferences"]["requestId"], + instance_id=response["requestReferences"]["instanceId"], + line_of_business=line_of_business, + platform=platform, + vnf=vnf_object + ) + + @classmethod + def instantiate_macro(cls, # pylint: disable=too-many-arguments, too-many-locals + aai_service_instance: "ServiceInstance", + vnf_object: "Vnf", + line_of_business: str, + platform: str, + cloud_region: "CloudRegion", + tenant: "Tenant", + sdc_service: "SdcService", + vnf_instance_name: str = None, + vnf_parameters: Iterable["InstantiationParameter"] = None, + so_vnf: "SoServiceVnf" = None + ) -> "VnfInstantiation": + """Instantiate Vnf using macro method. + + Args: + aai_service_instance (ServiceInstance): Service instance associated with request + vnf_object (Vnf): Vnf to instantiate + line_of_business (LineOfBusiness): LineOfBusiness to use in instantiation request + platform (Platform): Platform to use in instantiation request + cloud_region (CloudRegion): Cloud region to use in instantiation request. + tenant (Tenant): Tenant to use in instantiation request. + vnf_instance_name (str, optional): Vnf instance name. Defaults to None. + vnf_parameters (Iterable[InstantiationParameter], optional): Instantiation parameters + that are sent in the request. Defaults to None + so_vnf (SoServiceVnf): object with vnf instance parameters + + Returns: + VnfInstantiation: VnfInstantiation object + + """ + owning_entity_id = None + project = settings.PROJECT + + for relationship in aai_service_instance.relationships: + if relationship.related_to == "owning-entity": + owning_entity_id = relationship.relationship_data.pop().get("relationship-value") + if relationship.related_to == "project": + project = relationship.relationship_data.pop().get("relationship-value") + + owning_entity = OwningEntity.get_by_owning_entity_id( + owning_entity_id=owning_entity_id) + + if so_vnf: + template_file = "instantiate_vnf_macro_so_vnf.json.j2" + if so_vnf.instance_name: + vnf_instance_name = so_vnf.instance_name + else: + template_file = "instantiate_vnf_macro.json.j2" + if vnf_instance_name is None: + vnf_instance_name = \ + f"Python_ONAP_SDK_vnf_instance_{str(uuid4())}" + + response: dict = cls.send_message_json( + "POST", + (f"Instantiate {sdc_service.name} " + f"service vnf {vnf_object.name}"), + (f"{cls.base_url}/onap/so/infra/serviceInstantiation/{cls.api_version}/" + f"serviceInstances/{aai_service_instance.instance_id}/vnfs"), + data=jinja_env().get_template(template_file).render( + instance_name=vnf_instance_name, + vnf=vnf_object, + service=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), + project=project, + owning_entity=owning_entity, + line_of_business=line_of_business, + platform=platform, + service_instance=aai_service_instance, + vnf_parameters=vnf_parameters or [], + so_vnf=so_vnf + ), + headers=headers_so_creator(OnapService.headers) + ) + + return VnfInstantiation( + name=vnf_instance_name, + request_id=response["requestReferences"]["requestId"], + instance_id=response["requestReferences"]["instanceId"], + line_of_business=line_of_business, + platform=platform, + vnf=vnf_object + ) + + @classmethod + def so_action(cls, # pylint: disable=too-many-arguments, too-many-locals + vnf_instance: "VnfInstance", + operation_type: VnfOperation, + aai_service_instance: "ServiceInstance", + line_of_business: str, + platform: str, + sdc_service: "SdcService", + so_service: "SoService" = None + ) -> "VnfInstantiation": + """Execute SO action (update or healthcheck) for selected vnf with SO macro request. + + Args: + vnf_instance (VnfInstance): vnf instance object + operation_type (VnfOperation): name of the operation to trigger + aai_service_instance (AaiService): Service Instance object from aai + line_of_business (LineOfBusiness): LineOfBusiness name to use + in instantiation request + platform (Platform): Platform name to use in instantiation request + sdc_service (SdcService): Service model information + so_service (SoService, optional): SO values to use in SO request + + Raises: + StatusError: if the provided operation is not supported + + Returns: + VnfInstantiation: VnfInstantiation object + + """ + if operation_type not in (VnfOperation.HEALTHCHECK, VnfOperation.UPDATE): + raise StatusError("Operation not supported!") + + owning_entity_id = None + project = settings.PROJECT + + for relationship in aai_service_instance.relationships: + if relationship.related_to == "owning-entity": + owning_entity_id = relationship.relationship_data.pop().get("relationship-value") + if relationship.related_to == "project": + project = relationship.relationship_data.pop().get("relationship-value") + + owning_entity = OwningEntity.get_by_owning_entity_id( + owning_entity_id=owning_entity_id) + + response: dict = cls.send_message_json( + operation_type.request_method, + (f"So Action {sdc_service.name} " + f" vnf instance {vnf_instance.vnf_id}"), + (f"{cls.base_url}/onap/so/infra/serviceInstantiation/{cls.api_version}/" + f"serviceInstances/{aai_service_instance.instance_id}/vnfs/{vnf_instance.vnf_id}" + f"{operation_type.request_suffix}"), + data=jinja_env().get_template("instantiate_multi_vnf_service_macro.json.j2").render( + sdc_service=sdc_service, + cloud_region=next(aai_service_instance.service_subscription.cloud_regions), + tenant=next(aai_service_instance.service_subscription.tenants), + customer=aai_service_instance.service_subscription.customer, + project=project, + owning_entity=owning_entity, + line_of_business=line_of_business, + platform=platform, + service_instance_name=aai_service_instance.instance_name, + so_service=so_service + ), + headers=headers_so_creator(OnapService.headers) + ) + + return VnfInstantiation( + name=vnf_instance.vnf_name, + request_id=response["requestReferences"]["requestId"], + instance_id=response["requestReferences"]["instanceId"], + line_of_business=line_of_business, + platform=platform, + vnf=vnf_instance + ) + + +class ServiceInstantiation(Instantiation): # pylint: disable=too-many-ancestors + """Service instantiation class.""" + + def __init__(self, # pylint: disable=too-many-arguments + name: str, + request_id: str, + instance_id: str, + sdc_service: "SdcService", + cloud_region: "CloudRegion", + tenant: "Tenant", + customer: "Customer", + owning_entity: OwningEntity, + project: str) -> None: + """Class ServiceInstantiation object initialization. + + Args: + name (str): service instance name + request_id (str): service instantiation request ID + instance_id (str): service instantiation ID + sdc_service (SdcService): SdcService class object + cloud_region (CloudRegion): CloudRegion class object + tenant (Tenant): Tenant class object + customer (Customer): Customer class object + owning_entity (OwningEntity): OwningEntity class object + project (str): Project name + + """ + super().__init__(name, request_id, instance_id) + self.sdc_service = sdc_service + self.cloud_region = cloud_region + self.tenant = tenant + self.customer = customer + self.owning_entity = owning_entity + self.project = project + + @classmethod + def instantiate_ala_carte(cls, # pylint: disable=too-many-arguments + sdc_service: "SdcService", + cloud_region: "CloudRegion", + tenant: "Tenant", + customer: "Customer", + owning_entity: OwningEntity, + project: str, + service_subscription: "ServiceSubscription", + service_instance_name: str = None, + enable_multicloud: bool = False) -> "ServiceInstantiation": + """Instantiate service using SO a'la carte request. + + Args: + sdc_service (SdcService): Service to instantiate + cloud_region (CloudRegion): Cloud region to use in instantiation request + tenant (Tenant): Tenant to use in instantiation request + customer (Customer): Customer to use in instantiation request + owning_entity (OwningEntity): Owning entity to use in instantiation request + project (str): Project name to use in instantiation request + service_subscription (ServiceSubscription): Customer's service subsription. + service_instance_name (str, optional): Service instance name. Defaults to None. + enable_multicloud (bool, optional): Determines if Multicloud should be enabled + for instantiation request. Defaults to False. + + Raises: + StatusError: if a service is not distributed. + + Returns: + ServiceInstantiation: instantiation request object + + """ + if not sdc_service.distributed: + msg = f"Service {sdc_service.name} is not distributed." + raise StatusError(msg) + if service_instance_name is None: + service_instance_name = f"Python_ONAP_SDK_service_instance_{str(uuid4())}" + response: dict = cls.send_message_json( + "POST", + f"Instantiate {sdc_service.name} service a'la carte", + (f"{cls.base_url}/onap/so/infra/" + f"serviceInstantiation/{cls.api_version}/serviceInstances"), + data=jinja_env().get_template("instantiate_service_ala_carte.json.j2"). + render( + sdc_service=sdc_service, + cloud_region=cloud_region, + tenant=tenant, + customer=customer, + owning_entity=owning_entity, + service_instance_name=service_instance_name, + project=project, + enable_multicloud=enable_multicloud, + service_subscription=service_subscription + ), + headers=headers_so_creator(OnapService.headers) + ) + return cls( + name=service_instance_name, + request_id=response["requestReferences"].get("requestId"), + instance_id=response["requestReferences"].get("instanceId"), + sdc_service=sdc_service, + cloud_region=cloud_region, + tenant=tenant, + customer=customer, + owning_entity=owning_entity, + project=project + ) + + # pylint: disable=too-many-arguments, too-many-locals + @classmethod + def instantiate_macro(cls, + sdc_service: "SdcService", + customer: "Customer", + owning_entity: OwningEntity, + project: str, + line_of_business: str, + platform: str, + aai_service: "AaiService" = None, + cloud_region: "CloudRegion" = None, + tenant: "Tenant" = None, + service_instance_name: str = None, + vnf_parameters: Iterable["VnfParameters"] = None, + enable_multicloud: bool = False, + so_service: "SoService" = None, + service_subscription: "ServiceSubscription" = None + ) -> "ServiceInstantiation": + """Instantiate service using SO macro request. + + Args: + sdc_service (SdcService): Service to instantiate + customer (Customer): Customer to use in instantiation request + owning_entity (OwningEntity): Owning entity to use in instantiation request + project (Project): Project name to use in instantiation request + line_of_business_object (LineOfBusiness): LineOfBusiness name to use + in instantiation request + platform_object (Platform): Platform name to use in instantiation request + aai_service (AaiService): Service object from aai sdc + cloud_region (CloudRegion): Cloud region to use in instantiation request + tenant (Tenant): Tenant to use in instantiation request + service_instance_name (str, optional): Service instance name. Defaults to None. + vnf_parameters: (Iterable[VnfParameters]): Parameters which are + going to be used for vnfs instantiation. Defaults to None. + enable_multicloud (bool, optional): Determines if Multicloud should be enabled + for instantiation request. Defaults to False. + so_service (SoService, optional): SO values to use in instantiation request + service_subscription(ServiceSubscription, optional): Customer service subscription + for the instantiated service. Required if so_service is not provided. + + Raises: + StatusError: if a service is not distributed. + + Returns: + ServiceInstantiation: instantiation request object + + """ + template_file = "instantiate_service_macro.json.j2" + if so_service: + template_file = "instantiate_multi_vnf_service_macro.json.j2" + if so_service.instance_name: + service_instance_name = so_service.instance_name + else: + if not service_subscription: + raise ParameterError("If no so_service is provided, " + "service_subscription parameter is required!") + if service_instance_name is None: + service_instance_name = f"Python_ONAP_SDK_service_instance_{str(uuid4())}" + if not sdc_service.distributed: + msg = f"Service {sdc_service.name} is not distributed." + raise StatusError(msg) + + response: dict = cls.send_message_json( + "POST", + f"Instantiate {sdc_service.name} service macro", + (f"{cls.base_url}/onap/so/infra/" + f"serviceInstantiation/{cls.api_version}/serviceInstances"), + data=jinja_env().get_template(template_file). \ + render( + so_service=so_service, + sdc_service=sdc_service, + cloud_region=cloud_region, + tenant=tenant, + customer=customer, + owning_entity=owning_entity, + project=project, + aai_service=aai_service, + line_of_business=line_of_business, + platform=platform, + service_instance_name=service_instance_name, + vnf_parameters=vnf_parameters, + enable_multicloud=enable_multicloud, + service_subscription=service_subscription + ), + headers=headers_so_creator(OnapService.headers) + ) + return cls( + name=service_instance_name, + request_id=response["requestReferences"].get("requestId"), + instance_id=response["requestReferences"].get("instanceId"), + sdc_service=sdc_service, + cloud_region=cloud_region, + tenant=tenant, + customer=customer, + owning_entity=owning_entity, + project=project + ) + + @property + def aai_service_instance(self) -> "ServiceInstance": + """Service instance associated with service instantiation request. + + Raises: + StatusError: if a service is not instantiated - + not in COMPLETE status. + APIError: A&AI resource is not created + + Returns: + ServiceInstance: ServiceInstance + + """ + required_status = self.StatusEnum.COMPLETED + if self.status != required_status: + msg = (f"Service {self.name} is not instantiated - " + f"not in {required_status} status.") + raise StatusError(msg) + try: + service_subscription: "ServiceSubscription" = \ + self.customer.get_service_subscription_by_service_type(self.sdc_service.name) + return service_subscription.get_service_instance_by_name(self.name) + except APIError as exc: + self._logger.error("A&AI resources not created properly") + raise exc + + +class NetworkInstantiation(NodeTemplateInstantiation): # pylint: disable=too-many-ancestors + """Network instantiation class.""" + + def __init__(self, # pylint: disable=too-many-arguments + name: str, + request_id: str, + instance_id: str, + line_of_business: str, + platform: str, + network: Network) -> None: + """Class NetworkInstantiation object initialization. + + Args: + name (str): VNF name + request_id (str): request ID + instance_id (str): instance ID + service_instantiation ([type]): ServiceInstantiation class object + line_of_business (str): LineOfBusiness name + platform (str): Platform name + vnf (Network): Network class object + """ + super().__init__(name, request_id, instance_id, line_of_business, platform) + self.network = network + + @classmethod + def instantiate_ala_carte(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) -> "NetworkInstantiation": + """Instantiate Network using a'la carte method. + + Args: + 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. + + Returns: + NetworkInstantiation: NetworkInstantiation object + + """ + if network_instance_name is None: + network_instance_name = \ + f"Python_ONAP_SDK_network_instance_{str(uuid4())}" + NetworkPreload.upload_network_preload(network=network_object, + network_instance_name=network_instance_name, + subnets=subnets) + response: dict = cls.send_message_json( + "POST", + (f"Instantiate {aai_service_instance.sdc_service.name} " + f"service network {network_object.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_ala_carte.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 + ), + 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 + ) -- cgit 1.2.3-korg