aboutsummaryrefslogtreecommitdiffstats
path: root/src/onapsdk/nbi/nbi.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/onapsdk/nbi/nbi.py')
-rw-r--r--src/onapsdk/nbi/nbi.py490
1 files changed, 490 insertions, 0 deletions
diff --git a/src/onapsdk/nbi/nbi.py b/src/onapsdk/nbi/nbi.py
new file mode 100644
index 0000000..17356cb
--- /dev/null
+++ b/src/onapsdk/nbi/nbi.py
@@ -0,0 +1,490 @@
+"""NBI 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 enum import Enum
+from typing import Iterator
+from uuid import uuid4
+
+from onapsdk.aai.business.customer import Customer
+from onapsdk.exceptions import RequestError
+from onapsdk.onap_service import OnapService
+from onapsdk.utils import get_zulu_time_isoformat
+from onapsdk.utils.jinja import jinja_env
+from onapsdk.utils.mixins import WaitForFinishMixin
+from onapsdk.configuration import settings
+
+
+class Nbi(OnapService, ABC):
+ """NBI base class."""
+
+ base_url = settings.NBI_URL
+ api_version = settings.NBI_API_VERSION
+
+ @classmethod
+ def is_status_ok(cls) -> bool:
+ """Check NBI service status.
+
+ Returns:
+ bool: True if NBI works fine, False otherwise
+
+ """
+ try:
+ cls.send_message(
+ "GET",
+ "Check NBI status",
+ f"{cls.base_url}{cls.api_version}/status"
+ )
+ except RequestError as exc:
+ msg = f"An error occured during NBI status check: {exc}"
+ cls._logger.error(msg)
+ return False
+ return True
+
+
+class ServiceSpecification(Nbi):
+ """NBI service specification class."""
+
+ def __init__(self, # pylint: disable=too-many-arguments
+ unique_id: str,
+ name: str,
+ invariant_uuid: str,
+ category: str,
+ distribution_status: str,
+ version: str,
+ lifecycle_status: str) -> None:
+ """Service specification object initialization.
+
+ Args:
+ unique_id (str): Unique ID
+ name (str): Service specification name
+ invariant_uuid (str): Invariant UUID
+ category (str): Category
+ distribution_status (str): Service distribution status
+ version (str): Service version
+ lifecycle_status (str): Service lifecycle status
+ """
+ super().__init__()
+ self.unique_id: str = unique_id
+ self.name: str = name
+ self.invariant_uuid: str = invariant_uuid
+ self.category: str = category
+ self.distribution_status: str = distribution_status
+ self.version: str = version
+ self.lifecycle_status: str = lifecycle_status
+
+ def __repr__(self) -> str:
+ """Service specification representation.
+
+ Returns:
+ str: Service specification object human readable representation
+
+ """
+ return (f"ServiceSpecification(unique_id={self.unique_id}, name={self.name}, "
+ f"invariant_uuid={self.invariant_uuid}, category={self.category}, "
+ f"distribution_status={self.distribution_status}, version={self.version}, "
+ f"lifecycle_status={self.lifecycle_status})")
+
+ @classmethod
+ def get_all(cls) -> Iterator["ServiceSpecification"]:
+ """Get all service specifications.
+
+ Yields:
+ ServiceSpecification: Service specification object
+
+ """
+ for service_specification in cls.send_message_json("GET",
+ "Get service specifications from NBI",
+ (f"{cls.base_url}{cls.api_version}/"
+ "serviceSpecification")):
+ yield ServiceSpecification(
+ service_specification.get("id"),
+ service_specification.get("name"),
+ service_specification.get("invariantUUID"),
+ service_specification.get("category"),
+ service_specification.get("distributionStatus"),
+ service_specification.get("version"),
+ service_specification.get("lifecycleStatus"),
+ )
+
+ @classmethod
+ def get_by_id(cls, service_specification_id: str) -> "ServiceSpecification":
+ """Get service specification by ID.
+
+ Args:
+ service_specification_id (str): Service specification ID
+
+ Returns:
+ ServiceSpecification: Service specification object
+
+ """
+ service_specification: dict = cls.send_message_json(
+ "GET",
+ f"Get service specification with {service_specification_id} ID from NBI",
+ f"{cls.base_url}{cls.api_version}/serviceSpecification/{service_specification_id}"
+ )
+ return ServiceSpecification(
+ service_specification.get("id"),
+ service_specification.get("name"),
+ service_specification.get("invariantUUID"),
+ service_specification.get("category"),
+ service_specification.get("distributionStatus"),
+ service_specification.get("version"),
+ service_specification.get("lifecycleStatus"),
+ )
+
+
+class Service(Nbi):
+ """NBI service."""
+
+ def __init__(self, # pylint: disable=too-many-arguments
+ name: str,
+ service_id: str,
+ service_specification_name: str,
+ service_specification_id: str,
+ customer_id: str,
+ customer_role: str,
+ href: str) -> None:
+ """Service object initialization.
+
+ Args:
+ name (str): Service name
+ service_id (str): Service ID
+ service_specification_name (str): Service specification name
+ service_specification_id (str): Service specification ID
+ customer_id (str): Global customer ID
+ customer_role (str): Customer role
+ href (str): Service object href
+ """
+ super().__init__()
+ self.name: str = name
+ self.service_id: str = service_id
+ self._service_specification_name: str = service_specification_name
+ self._service_specification_id: str = service_specification_id
+ self._customer_id: str = customer_id
+ self.customer_role: str = customer_role
+ self.href: str = href
+
+ def __repr__(self) -> str:
+ """Service object representation.
+
+ Returns:
+ str: Human readable service object representation
+
+ """
+ return (f"Service(name={self.name}, service_id={self.service_id}, "
+ f"service_specification={self.service_specification}, customer={self.customer}, "
+ f"customer_role={self.customer_role})")
+
+ @classmethod
+ def get_all(cls, customer_id: str = 'generic') -> Iterator["Service"]:
+ """Get all services for selected customer.
+
+ Args:
+ customer_id (str): Global customer ID
+
+ Yields:
+ Service: Service object
+
+ """
+ for service in cls.send_message_json("GET",
+ "Get service instances from NBI",
+ f"{cls.base_url}{cls.api_version}/service?"
+ f"relatedParty.id={customer_id}"):
+ yield cls(service.get("name"),
+ service.get("id"),
+ service.get("serviceSpecification", {}).get("name"),
+ service.get("serviceSpecification", {}).get("id"),
+ service.get("relatedParty", {}).get("id"),
+ service.get("relatedParty", {}).get("role"),
+ service.get("href"))
+
+ @property
+ def customer(self) -> Customer:
+ """Service order Customer object.
+
+ Returns:
+ Customer: Customer object
+
+ """
+ if not self._customer_id:
+ return None
+ return Customer.get_by_global_customer_id(self._customer_id)
+
+ @property
+ def service_specification(self) -> ServiceSpecification:
+ """Service specification.
+
+ Returns:
+ ServiceSpecification: Service specification object
+
+ """
+ if not self._service_specification_id:
+ return None
+ return ServiceSpecification.get_by_id(self._service_specification_id)
+
+
+class ServiceOrder(Nbi, WaitForFinishMixin): # pylint: disable=too-many-instance-attributes
+ """Service order class."""
+
+ WAIT_FOR_SLEEP_TIME = 10
+
+ def __init__(self, # pylint: disable=too-many-arguments
+ unique_id: str,
+ href: str,
+ priority: str,
+ description: str,
+ category: str,
+ external_id: str,
+ service_instance_name: str,
+ state: str = None,
+ customer: Customer = None,
+ customer_id: str = None,
+ service_specification: ServiceSpecification = None,
+ service_specification_id: str = None) -> None:
+ """Service order object initialization.
+
+ Args:
+ unique_id (str): unique ID
+ href (str): object's href
+ priority (str): order priority
+ description (str): order description
+ category (str): category description
+ external_id (str): external ID
+ service_instance_name (str): name of service instance
+ state (str, optional): instantiation state. Defaults to None.
+ customer (Customer, optional): Customer object. Defaults to None.
+ customer_id (str, optional): global customer ID. Defaults to None.
+ service_specification (ServiceSpecification, optional): service specification object.
+ Defaults to None.
+ service_specification_id (str, optional): service specification ID. Defaults to None.
+ """
+ super().__init__()
+ self.unique_id: str = unique_id
+ self.href: str = href
+ self.priority: str = priority
+ self.category: str = category
+ self.description: str = description
+ self.external_id: str = external_id
+ self._customer: Customer = customer
+ self._customer_id: str = customer_id
+ self._service_specification: ServiceSpecification = service_specification
+ self._service_specification_id: str = service_specification_id
+ self.service_instance_name: str = service_instance_name
+ self.state: str = state
+
+ class StatusEnum(Enum):
+ """Status enum.
+
+ Store possible statuses for service order:
+ - completed,
+ - failed,
+ - inProgress.
+ If instantiation has status which is not covered by these values
+ `unknown` value is used.
+
+ """
+
+ ACKNOWLEDGED = "acknowledged"
+ IN_PROGRESS = "inProgress"
+ FAILED = "failed"
+ COMPLETED = "completed"
+ REJECTED = "rejected"
+ UNKNOWN = "unknown"
+
+ def __repr__(self) -> str:
+ """Service order object representation.
+
+ Returns:
+ str: Service order object representation.
+
+ """
+ return (f"ServiceOrder(unique_id={self.unique_id}, href={self.href}, "
+ f"priority={self.priority}, category={self.category}, "
+ f"description={self.description}, external_id={self.external_id}, "
+ f"customer={self.customer}, service_specification={self.service_specification}"
+ f"service_instance_name={self.service_instance_name}, state={self.state})")
+
+ @property
+ def customer(self) -> Customer:
+ """Get customer object used in service order.
+
+ Returns:
+ Customer: Customer object
+
+ """
+ if not self._customer:
+ if not self._customer_id:
+ self._logger.error("No customer ID")
+ return None
+ self._customer = Customer.get_by_global_customer_id(self._customer_id)
+ return self._customer
+
+ @property
+ def service_specification(self) -> ServiceSpecification:
+ """Service order service specification used in order item.
+
+ Returns:
+ ServiceSpecification: Service specification
+
+ """
+ if not self._service_specification:
+ if not self._service_specification_id:
+ self._logger.error("No service specification")
+ return None
+ self._service_specification = ServiceSpecification.\
+ get_by_id(self._service_specification_id)
+ return self._service_specification
+
+ @classmethod
+ def get_all(cls) -> Iterator["ServiceOrder"]:
+ """Get all service orders.
+
+ Returns:
+ Iterator[ServiceOrder]: ServiceOrder object
+
+ """
+ for service_order in cls.send_message_json("GET",
+ "Get all service orders",
+ f"{cls.base_url}{cls.api_version}/serviceOrder"):
+ service_order_related_party = None
+ if service_order.get("relatedParty") is not None:
+ service_order_related_party = service_order.get(
+ "relatedParty", [{}])[0].get("id")
+
+ yield ServiceOrder(
+ unique_id=service_order.get("id"),
+ href=service_order.get("href"),
+ priority=service_order.get("priority"),
+ category=service_order.get("category"),
+ description=service_order.get("description"),
+ external_id=service_order.get("externalId"),
+ customer_id=service_order_related_party,
+ service_specification_id=service_order.get("orderItem", [{}])[0].get("service")\
+ .get("serviceSpecification").get("id"),
+ service_instance_name=service_order.get("orderItem", [{}])[0].\
+ get("service", {}).get("name"),
+ state=service_order.get("state")
+ )
+
+ @classmethod
+ def create(cls,
+ customer: Customer,
+ service_specification: ServiceSpecification,
+ name: str = None,
+ external_id: str = None) -> "ServiceOrder":
+ """Create service order.
+
+ Returns:
+ ServiceOrder: ServiceOrder object
+
+ """
+ if external_id is None:
+ external_id = str(uuid4())
+ if name is None:
+ name = f"Python_ONAP_SDK_service_instance_{str(uuid4())}"
+ response: dict = cls.send_message_json(
+ "POST",
+ "Add service instance via ServiceOrder API",
+ f"{cls.base_url}{cls.api_version}/serviceOrder",
+ data=jinja_env()
+ .get_template("nbi_service_order_create.json.j2")
+ .render(
+ customer=customer,
+ service_specification=service_specification,
+ service_instance_name=name,
+ external_id=external_id,
+ request_time=get_zulu_time_isoformat()
+ )
+ )
+ return cls(
+ unique_id=response.get("id"),
+ href=response.get("href"),
+ priority=response.get("priority"),
+ description=response.get("description"),
+ category=response.get("category"),
+ external_id=response.get("externalId"),
+ customer=customer,
+ service_specification=service_specification,
+ service_instance_name=name
+ )
+
+ @property
+ def status(self) -> "StatusEnum":
+ """Service order instantiation status.
+
+ It's populated by call Service order endpoint.
+
+ Returns:
+ StatusEnum: Service order status.
+
+ """
+ response: dict = self.send_message_json("GET",
+ "Get service order status",
+ (f"{self.base_url}{self.api_version}/"
+ f"serviceOrder/{self.unique_id}"))
+ try:
+ return self.StatusEnum(response.get("state"))
+ except (KeyError, ValueError):
+ self._logger.exception("Invalid status")
+ return self.StatusEnum.UNKNOWN
+
+ @property
+ def completed(self) -> bool:
+ """Store an information if service order is completed or not.
+
+ Service orded is completed if it's status is COMPLETED.
+
+ Returns:
+ bool: True if service orded is completed, False otherwise.
+
+ """
+ return self.status == self.StatusEnum.COMPLETED
+
+ @property
+ def rejected(self) -> bool:
+ """Store an information if service order is rejected or not.
+
+ Service orded is completed if it's status is REJECTED.
+
+ Returns:
+ bool: True if service orded is rejected, False otherwise.
+
+ """
+ return self.status == self.StatusEnum.REJECTED
+
+ @property
+ def failed(self) -> bool:
+ """Store an information if service order is failed or not.
+
+ Service orded is completed if it's status is FAILED.
+
+ Returns:
+ bool: True if service orded is failed, False otherwise.
+
+ """
+ return self.status == self.StatusEnum.FAILED
+
+ @property
+ def finished(self) -> bool:
+ """Store an information if service order is finished or not.
+
+ Service orded is finished if it's status is not ACKNOWLEDGED or IN_PROGRESS.
+
+ Returns:
+ bool: True if service orded is finished, False otherwise.
+
+ """
+ return self.status not in [self.StatusEnum.ACKNOWLEDGED,
+ self.StatusEnum.IN_PROGRESS]