From 5ed9b7d3cad56c6438bca305b6d2931bd320352b Mon Sep 17 00:00:00 2001 From: efiacor Date: Wed, 20 May 2020 15:18:41 +0100 Subject: [PMSH] Adding 'subscriptions' api endpoint Signed-off-by: efiacor Change-Id: I837045b3b618a98d4aabe190359d0ad47f27ca9f Issue-ID: DCAEGEN2-2154 --- components/pm-subscription-handler/Changelog.md | 5 + .../pmsh_service/mod/__init__.py | 2 +- .../pmsh_service/mod/api/__init__.py | 0 .../pmsh_service/mod/api/controller.py | 42 ++++++++ .../pmsh_service/mod/api/db_models.py | 107 +++++++++++++++++++ .../pmsh_service/mod/api/pmsh_swagger.yml | 89 ++++++++++++++++ .../pmsh_service/mod/db_models.py | 91 ---------------- .../pmsh_service/mod/healthcheck.py | 30 ------ .../pmsh_service/mod/network_function.py | 5 +- .../pmsh_service/mod/pmsh_swagger.yml | 34 ------ .../pmsh_service/mod/subscription.py | 116 +++++++++++---------- components/pm-subscription-handler/setup.py | 3 +- .../tests/test_controller.py | 69 ++++++++++++ .../tests/test_healthcheck.py | 27 ----- .../tests/test_network_function.py | 3 +- .../tests/test_policy_response_handler.py | 2 +- .../tests/test_subscription.py | 37 +++---- 17 files changed, 402 insertions(+), 260 deletions(-) create mode 100644 components/pm-subscription-handler/pmsh_service/mod/api/__init__.py create mode 100755 components/pm-subscription-handler/pmsh_service/mod/api/controller.py create mode 100755 components/pm-subscription-handler/pmsh_service/mod/api/db_models.py create mode 100644 components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml delete mode 100755 components/pm-subscription-handler/pmsh_service/mod/db_models.py delete mode 100755 components/pm-subscription-handler/pmsh_service/mod/healthcheck.py delete mode 100644 components/pm-subscription-handler/pmsh_service/mod/pmsh_swagger.yml create mode 100755 components/pm-subscription-handler/tests/test_controller.py delete mode 100755 components/pm-subscription-handler/tests/test_healthcheck.py (limited to 'components/pm-subscription-handler') diff --git a/components/pm-subscription-handler/Changelog.md b/components/pm-subscription-handler/Changelog.md index 3d2af6c3..2ec36fb8 100755 --- a/components/pm-subscription-handler/Changelog.md +++ b/components/pm-subscription-handler/Changelog.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [1.1.0] +### Changed +* Added new API endpoint to fetch all Subscription data (DCAEGEN2-2154) +* Added support for config-binding-docker module in PMSH config fetch (DCAEGEN2-2156) + ## [1.0.3] ### Fixed * Fixed bug where PMSH pushes subscription to xnf regardless of it's orchestration status (DCAEGEN2-2173) diff --git a/components/pm-subscription-handler/pmsh_service/mod/__init__.py b/components/pm-subscription-handler/pmsh_service/mod/__init__.py index e09ec285..5c0a5144 100644 --- a/components/pm-subscription-handler/pmsh_service/mod/__init__.py +++ b/components/pm-subscription-handler/pmsh_service/mod/__init__.py @@ -37,7 +37,7 @@ def _get_app(): def launch_api_server(app_config): connex_app = _get_app() - connex_app.add_api('pmsh_swagger.yml') + connex_app.add_api('api/pmsh_swagger.yml') connex_app.run(port=os.environ.get('PMSH_API_PORT', '8443'), ssl_context=(app_config.cert_path, app_config.key_path)) diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/__init__.py b/components/pm-subscription-handler/pmsh_service/mod/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py new file mode 100755 index 00000000..21d29caf --- /dev/null +++ b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py @@ -0,0 +1,42 @@ +# ============LICENSE_START=================================================== +# Copyright (C) 2019-2020 Nordix Foundation. +# ============================================================================ +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END===================================================== + +from mod.subscription import Subscription + + +def status(): + """ + Returns the health of the PMSH service + Args: + NA + Returns: + Dictionary detailing 'status' of either 'healthy' or 'unhealthy'. + Raises: + NA + """ + return {'status': 'healthy'} + + +def get_all_sub_to_nf_relations(): + """ Retrieves all subscription to nf relations + + Returns: + list: of Subscriptions and it's related Network Functions, else empty + """ + subs_dict = [s.serialize() for s in Subscription.get_all()] + return subs_dict diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py b/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py new file mode 100755 index 00000000..1d6f72b3 --- /dev/null +++ b/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py @@ -0,0 +1,107 @@ +# ============LICENSE_START=================================================== +# Copyright (C) 2019-2020 Nordix Foundation. +# ============================================================================ +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END===================================================== + +from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.orm import relationship + +from mod import db + + +class SubscriptionModel(db.Model): + __tablename__ = 'subscriptions' + id = Column(Integer, primary_key=True, autoincrement=True) + subscription_name = Column(String(100), unique=True) + status = Column(String(20)) + + nfs = relationship( + 'NfSubRelationalModel', + cascade='all, delete-orphan', + backref='subscription') + + def __init__(self, subscription_name, status): + self.subscription_name = subscription_name + self.status = status + + def __repr__(self): + return f'subscription_name: {self.subscription_name}, status: {self.status}' + + def __eq__(self, other): + if isinstance(self, other.__class__): + return self.subscription_name == other.subscription_name + return False + + def serialize(self): + sub_nfs = NfSubRelationalModel.query.filter( + NfSubRelationalModel.subscription_name == self.subscription_name).all() + return {'subscription_name': self.subscription_name, 'subscription_status': self.status, + 'network_functions': [sub_nf.serialize_nf() for sub_nf in sub_nfs]} + + +class NetworkFunctionModel(db.Model): + __tablename__ = 'network_functions' + id = Column(Integer, primary_key=True, autoincrement=True) + nf_name = Column(String(100), unique=True) + orchestration_status = Column(String(100)) + + subscriptions = relationship( + 'NfSubRelationalModel', + cascade='all, delete-orphan', + backref='nf') + + def __init__(self, nf_name, orchestration_status): + self.nf_name = nf_name + self.orchestration_status = orchestration_status + + def __repr__(self): + return f'nf_name: {self.nf_name}, orchestration_status: {self.orchestration_status}' + + +class NfSubRelationalModel(db.Model): + __tablename__ = 'nf_to_sub_rel' + __mapper_args__ = { + 'confirm_deleted_rows': False + } + id = Column(Integer, primary_key=True, autoincrement=True) + subscription_name = Column( + String, + ForeignKey(SubscriptionModel.subscription_name, ondelete='cascade', onupdate='cascade') + ) + nf_name = Column( + String, + ForeignKey(NetworkFunctionModel.nf_name, ondelete='cascade', onupdate='cascade') + ) + nf_sub_status = Column(String(20)) + + def __init__(self, subscription_name, nf_name, nf_sub_status=None): + self.subscription_name = subscription_name + self.nf_name = nf_name + self.nf_sub_status = nf_sub_status + + def __repr__(self): + return f'subscription_name: {self.subscription_name}, ' \ + f'nf_name: {self.nf_name}, nf_sub_status: {self.nf_sub_status}' + + def serialize(self): + return {'subscription_name': self.subscription_name, 'nf_name': self.nf_name, + 'nf_sub_status': self.nf_sub_status} + + def serialize_nf(self): + nf_orch_status = NetworkFunctionModel.query.filter( + NetworkFunctionModel.nf_name == self.nf_name).one_or_none().orchestration_status + return {'nf_name': self.nf_name, 'orchestration_status': nf_orch_status, + 'nf_sub_status': self.nf_sub_status} diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml b/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml new file mode 100644 index 00000000..58e6a788 --- /dev/null +++ b/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml @@ -0,0 +1,89 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Nordix Foundation. +# ================================================================================ +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END========================================================= + +swagger: "2.0" +info: + title: PM Subscription Handler Service + version: "1.1.0" + description: PM subscription handler enables control of performance management jobs on network functions in ONAP +produces: + - "application/json" +basePath: "/" +schemes: + - https +# Paths supported by the server application +paths: + /subscriptions: + get: + description: >- + Get all defined Subscriptions and their related Network Functions from ONAP. + operationId: mod.api.controller.get_all_sub_to_nf_relations + responses: + 200: + description: OK; Array of subscriptions are returned as an object + schema: + type: array + items: + type: object + properties: + subscription_name: + type: string + description: Name of the Subscription + subscription_status: + type: string + description: Status of the Subscription + network_functions: + type: array + items: + type: object + properties: + nf_name: + type: string + description: Name of the Network Function + nf_sub_status: + type: string + description: Status of the Subscription on the Network Function + orchestration_status: + type: string + description: Orchestration status of the Network Function + 401: + description: Unauthorized + 403: + description: Forbidden + 404: + description: there are no subscriptions defined + + /healthcheck: + get: + operationId: mod.api.controller.status + tags: + - "HealthCheck" + description: >- + This is the health check endpoint. If this returns a 200, the server is alive. + responses: + 200: + description: Successful response + schema: + type: object + properties: + status: + type: string + description: Overall health of PMSH + enum: [healthy, unhealthy] + 503: + description: the pmsh service is unavailable diff --git a/components/pm-subscription-handler/pmsh_service/mod/db_models.py b/components/pm-subscription-handler/pmsh_service/mod/db_models.py deleted file mode 100755 index d1836760..00000000 --- a/components/pm-subscription-handler/pmsh_service/mod/db_models.py +++ /dev/null @@ -1,91 +0,0 @@ -# ============LICENSE_START=================================================== -# Copyright (C) 2019-2020 Nordix Foundation. -# ============================================================================ -# 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. -# -# SPDX-License-Identifier: Apache-2.0 -# ============LICENSE_END===================================================== - -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.orm import relationship - -from mod import db - - -class SubscriptionModel(db.Model): - __tablename__ = 'subscriptions' - id = Column(Integer, primary_key=True, autoincrement=True) - subscription_name = Column(String(100), unique=True) - status = Column(String(20)) - - nfs = relationship( - 'NfSubRelationalModel', - cascade='all, delete-orphan', - backref='subscription') - - def __init__(self, subscription_name, status): - self.subscription_name = subscription_name - self.status = status - - def __repr__(self): - return f'Subscription: {self.subscription_name} {self.status}' - - def __eq__(self, other): - if isinstance(self, other.__class__): - return self.subscription_name == other.subscription_name - return False - - -class NetworkFunctionModel(db.Model): - __tablename__ = 'network_functions' - id = Column(Integer, primary_key=True, autoincrement=True) - nf_name = Column(String(100), unique=True) - orchestration_status = Column(String(100)) - - subscriptions = relationship( - 'NfSubRelationalModel', - cascade='all, delete-orphan', - backref='nf') - - def __init__(self, nf_name, orchestration_status): - self.nf_name = nf_name - self.orchestration_status = orchestration_status - - def __repr__(self): - return f'NetworkFunctionModel: {self.nf_name}, {self.orchestration_status}' - - -class NfSubRelationalModel(db.Model): - __tablename__ = 'nf_to_sub_rel' - __mapper_args__ = { - 'confirm_deleted_rows': False - } - id = Column(Integer, primary_key=True, autoincrement=True) - subscription_name = Column( - String, - ForeignKey(SubscriptionModel.subscription_name, ondelete='cascade', onupdate='cascade') - ) - nf_name = Column( - String, - ForeignKey(NetworkFunctionModel.nf_name, ondelete='cascade', onupdate='cascade') - ) - nf_sub_status = Column(String(20)) - - def __init__(self, subscription_name, nf_name, nf_sub_status=None): - self.subscription_name = subscription_name - self.nf_name = nf_name - self.nf_sub_status = nf_sub_status - - def __repr__(self): - return f'NetworkFunctionSubscriptions: {self.subscription_name}, ' \ - f'{self.nf_name}, {self.nf_sub_status}' diff --git a/components/pm-subscription-handler/pmsh_service/mod/healthcheck.py b/components/pm-subscription-handler/pmsh_service/mod/healthcheck.py deleted file mode 100755 index af82fc4e..00000000 --- a/components/pm-subscription-handler/pmsh_service/mod/healthcheck.py +++ /dev/null @@ -1,30 +0,0 @@ -# ============LICENSE_START=================================================== -# Copyright (C) 2019-2020 Nordix Foundation. -# ============================================================================ -# 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. -# -# SPDX-License-Identifier: Apache-2.0 -# ============LICENSE_END===================================================== - - -def status(): - """ - Returns the health of the PMSH service - Args: - NA - Returns: - Dictionary detailing 'status' of either 'healthy' or 'unhealthy'. - Raises: - NA - """ - return {'status': 'healthy'} diff --git a/components/pm-subscription-handler/pmsh_service/mod/network_function.py b/components/pm-subscription-handler/pmsh_service/mod/network_function.py index bf223184..0663be08 100755 --- a/components/pm-subscription-handler/pmsh_service/mod/network_function.py +++ b/components/pm-subscription-handler/pmsh_service/mod/network_function.py @@ -20,7 +20,7 @@ import re from enum import Enum from mod import pmsh_logging as logger, db -from mod.db_models import NetworkFunctionModel +from mod.api.db_models import NetworkFunctionModel class NetworkFunction: @@ -53,10 +53,9 @@ class NetworkFunction: orchestration_status=self.orchestration_status) db.session.add(new_nf) db.session.commit() - return new_nf else: - logger.debug(f'Network function {existing_nf} already exists,' + logger.debug(f'Network function {existing_nf.nf_name} already exists,' f' returning this network function..') return existing_nf diff --git a/components/pm-subscription-handler/pmsh_service/mod/pmsh_swagger.yml b/components/pm-subscription-handler/pmsh_service/mod/pmsh_swagger.yml deleted file mode 100644 index 7bfecd81..00000000 --- a/components/pm-subscription-handler/pmsh_service/mod/pmsh_swagger.yml +++ /dev/null @@ -1,34 +0,0 @@ -swagger: "2.0" -info: - title: PM Subscription Handler Service - version: "1.0.0" - description: This is the swagger file that outlines the PM subscription handler api -consumes: - - "application/json" -produces: - - "application/json" - -schemes: - - https - -# Paths supported by the server application -paths: - /healthcheck: - get: - operationId: "mod.healthcheck.status" - tags: - - "HealthCheck" - description: >- - This is the health check endpoint. If this returns a 200, the server is alive. - responses: - 200: - description: Successful response - schema: - type: object - properties: - status: - type: string - description: Overall health of PMSH - enum: [healthy, unhealthy] - 503: - description: the pmsh service is unavailable diff --git a/components/pm-subscription-handler/pmsh_service/mod/subscription.py b/components/pm-subscription-handler/pmsh_service/mod/subscription.py index 3add7200..7517ba9c 100755 --- a/components/pm-subscription-handler/pmsh_service/mod/subscription.py +++ b/components/pm-subscription-handler/pmsh_service/mod/subscription.py @@ -15,14 +15,13 @@ # # SPDX-License-Identifier: Apache-2.0 # ============LICENSE_END===================================================== - from enum import Enum from tenacity import retry, retry_if_exception_type, wait_exponential, stop_after_attempt import mod.pmsh_logging as logger from mod import db -from mod.db_models import SubscriptionModel, NfSubRelationalModel, NetworkFunctionModel +from mod.api.db_models import SubscriptionModel, NfSubRelationalModel, NetworkFunctionModel from mod.network_function import NetworkFunction @@ -84,35 +83,33 @@ class Subscription: Returns: Subscription object """ - existing_subscription = (SubscriptionModel.query.filter( - SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()) - - if existing_subscription is None: - new_subscription = SubscriptionModel(subscription_name=self.subscriptionName, - status=self.administrativeState) - - db.session.add(new_subscription) - db.session.commit() - - return new_subscription - - else: - logger.debug(f'Subscription {self.subscriptionName} already exists,' - f' returning this subscription..') - return existing_subscription - - def add_network_functions_to_subscription(self, nf_list): - """ Associates network functions to a Subscription + try: + existing_subscription = (SubscriptionModel.query.filter( + SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()) + if existing_subscription is None: + new_subscription = SubscriptionModel(subscription_name=self.subscriptionName, + status=self.administrativeState) + db.session.add(new_subscription) + db.session.commit() + return new_subscription + else: + logger.debug(f'Subscription {self.subscriptionName} already exists,' + f' returning this subscription..') + return existing_subscription + except Exception as e: + logger.debug(f'Failed to create subscription {self.subscriptionName} in the DB: {e}') + + def add_network_function_to_subscription(self, nf): + """ Associates a network function to a Subscription Args: - nf_list : A list of NetworkFunction objects. + nf : A NetworkFunction object. """ current_sub = self.create() - logger.debug(f'Adding network functions to subscription {current_sub.subscription_name}') - - for nf in nf_list: + try: current_nf = nf.create() - + logger.debug(f'Adding network function {nf.nf_name} to Subscription ' + f'{current_sub.subscription_name}') existing_entry = NfSubRelationalModel.query.filter( NfSubRelationalModel.subscription_name == current_sub.subscription_name, NfSubRelationalModel.nf_name == current_nf.nf_name).one_or_none() @@ -120,11 +117,14 @@ class Subscription: new_nf_sub = NfSubRelationalModel(current_sub.subscription_name, nf.nf_name, SubNfState.PENDING_CREATE.value) new_nf_sub.nf = current_nf - logger.debug(current_nf) current_sub.nfs.append(new_nf_sub) - - db.session.add(current_sub) - db.session.commit() + logger.debug(f'Network function {current_nf.nf_name} added to Subscription ' + f'{current_sub.subscription_name}') + db.session.add(current_sub) + db.session.commit() + except Exception as e: + logger.debug(f'Failed to add nf {nf.nf_name} to subscription ' + f'{current_sub.subscription_name}: {e}') @staticmethod def get(subscription_name): @@ -150,27 +150,34 @@ class Subscription: def update_subscription_status(self): """ Updates the status of subscription in subscription table """ - SubscriptionModel.query.filter( - SubscriptionModel.subscription_name == self.subscriptionName). \ - update({SubscriptionModel.status: self.administrativeState}, - synchronize_session='evaluate') + try: + SubscriptionModel.query.filter( + SubscriptionModel.subscription_name == self.subscriptionName)\ + .update({SubscriptionModel.status: self.administrativeState}, + synchronize_session='evaluate') - db.session.commit() + db.session.commit() + except Exception as e: + logger.debug(f'Failed to update status of subscription: {self.subscriptionName}: {e}') def delete_subscription(self): """ Deletes a subscription and all its association from the database. A network function that is only associated with the subscription being removed will also be deleted.""" - subscription = SubscriptionModel.query.filter( - SubscriptionModel.subscription_name == self.subscriptionName).one_or_none() - if subscription: - for nf_relationship in subscription.nfs: - other_nf_relationship = NfSubRelationalModel.query.filter( - NfSubRelationalModel.subscription_name != self.subscriptionName, - NfSubRelationalModel.nf_name == nf_relationship.nf_name).one_or_none() - if not other_nf_relationship: - db.session.delete(nf_relationship.nf) - db.session.delete(subscription) - db.session.commit() + try: + subscription = SubscriptionModel.query.filter( + SubscriptionModel.subscription_name == self.subscriptionName).one_or_none() + if subscription: + for nf_relationship in subscription.nfs: + other_nf_relationship = NfSubRelationalModel.query.filter( + NfSubRelationalModel.subscription_name != self.subscriptionName, + NfSubRelationalModel.nf_name == nf_relationship.nf_name).one_or_none() + if not other_nf_relationship: + db.session.delete(nf_relationship.nf) + db.session.delete(subscription) + db.session.commit() + except Exception as e: + logger.debug(f'Failed to delete subscription: {self.subscriptionName} ' + f'and it\'s relations from the DB: {e}') @retry(wait=wait_exponential(multiplier=1, min=30, max=120), stop=stop_after_attempt(3), retry=retry_if_exception_type(Exception)) @@ -188,7 +195,7 @@ class Subscription: mr_pub.publish_subscription_event_data(self, nf.nf_name, app_conf) logger.debug(f'Publishing Event to {action} ' f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}') - self.add_network_functions_to_subscription(nfs) + self.add_network_function_to_subscription(nf) self.update_sub_nf_status(self.subscriptionName, sub_nf_state, nf.nf_name) except Exception as err: raise Exception(f'Error publishing activation event to MR: {err}') @@ -213,12 +220,15 @@ class Subscription: nf_name (str): The network function name status (str): Status of the subscription """ - NfSubRelationalModel.query.filter( - NfSubRelationalModel.subscription_name == subscription_name, - NfSubRelationalModel.nf_name == nf_name). \ - update({NfSubRelationalModel.nf_sub_status: status}, synchronize_session='evaluate') - - db.session.commit() + try: + NfSubRelationalModel.query.filter( + NfSubRelationalModel.subscription_name == subscription_name, + NfSubRelationalModel.nf_name == nf_name). \ + update({NfSubRelationalModel.nf_sub_status: status}, synchronize_session='evaluate') + db.session.commit() + except Exception as e: + logger.debug(f'Failed to update status of nf: {nf_name} for subscription: ' + f'{subscription_name}: {e}') def _get_nf_models(self): nf_sub_relationships = NfSubRelationalModel.query.filter( diff --git a/components/pm-subscription-handler/setup.py b/components/pm-subscription-handler/setup.py index 0437f5dd..cc09dc8f 100644 --- a/components/pm-subscription-handler/setup.py +++ b/components/pm-subscription-handler/setup.py @@ -20,7 +20,7 @@ from setuptools import setup, find_packages setup( name="pm_subscription_handler", - version="1.0.0", + version="1.1.0", packages=find_packages(), author="lego@est.tech", author_email="lego@est.tech", @@ -34,6 +34,7 @@ setup( "connexion==2.5.0", "flask_sqlalchemy==2.4.1", "Flask==1.1.1", + "swagger-ui-bundle==0.0.6", "psycopg2-binary==2.8.4", "onap_dcae_cbs_docker_client==2.1.0"] ) diff --git a/components/pm-subscription-handler/tests/test_controller.py b/components/pm-subscription-handler/tests/test_controller.py new file mode 100755 index 00000000..8ef39460 --- /dev/null +++ b/components/pm-subscription-handler/tests/test_controller.py @@ -0,0 +1,69 @@ +# ============LICENSE_START=================================================== +# Copyright (C) 2019-2020 Nordix Foundation. +# ============================================================================ +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END===================================================== +import json +import os +import unittest +from test.support import EnvironmentVarGuard +from unittest.mock import patch + +from requests import Session + +from mod import aai_client, create_app, db +from mod.api.controller import status, get_all_sub_to_nf_relations +from mod.network_function import NetworkFunction + + +class ControllerTestCase(unittest.TestCase): + @patch('mod.get_db_connection_url') + @patch.object(Session, 'put') + def setUp(self, mock_session, mock_get_db_url): + mock_get_db_url.return_value = 'sqlite://' + with open(os.path.join(os.path.dirname(__file__), 'data/aai_xnfs.json'), 'r') as data: + self.aai_response_data = data.read() + mock_session.return_value.status_code = 200 + mock_session.return_value.text = self.aai_response_data + self.env = EnvironmentVarGuard() + self.env.set('AAI_SERVICE_HOST', '1.2.3.4') + self.env.set('AAI_SERVICE_PORT', '8443') + self.env.set('TESTING', 'True') + self.env.set('LOGS_PATH', './unit_test_logs') + with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data: + self.cbs_data_1 = json.load(data) + self.sub_1, self.xnfs = aai_client.get_pmsh_subscription_data(self.cbs_data_1) + self.nf_1 = NetworkFunction(nf_name='pnf_1', orchestration_status='Inventoried') + self.nf_2 = NetworkFunction(nf_name='pnf_2', orchestration_status='Active') + self.app = create_app() + self.app_context = self.app.app_context() + self.app_context.push() + db.create_all() + + def tearDown(self): + db.session.remove() + db.drop_all() + self.app_context.pop() + + def test_status_response_healthy(self): + self.assertEqual(status()['status'], 'healthy') + + def test_get_all_sub_to_nf_relations(self): + self.sub_1.create() + for nf in [self.nf_1, self.nf_2]: + self.sub_1.add_network_function_to_subscription(nf) + all_subs = get_all_sub_to_nf_relations() + self.assertEqual(len(all_subs[0]['network_functions']), 2) + self.assertEqual(all_subs[0]['subscription_name'], 'ExtraPM-All-gNB-R2B') diff --git a/components/pm-subscription-handler/tests/test_healthcheck.py b/components/pm-subscription-handler/tests/test_healthcheck.py deleted file mode 100755 index 1c40c3ff..00000000 --- a/components/pm-subscription-handler/tests/test_healthcheck.py +++ /dev/null @@ -1,27 +0,0 @@ -# ============LICENSE_START=================================================== -# Copyright (C) 2019-2020 Nordix Foundation. -# ============================================================================ -# 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. -# -# SPDX-License-Identifier: Apache-2.0 -# ============LICENSE_END===================================================== - -import unittest - -from mod.healthcheck import status - - -class HealthcheckTestCase(unittest.TestCase): - - def test_status_response_healthy(self): - self.assertEqual(status()['status'], 'healthy') diff --git a/components/pm-subscription-handler/tests/test_network_function.py b/components/pm-subscription-handler/tests/test_network_function.py index 4fca0778..138d99ad 100755 --- a/components/pm-subscription-handler/tests/test_network_function.py +++ b/components/pm-subscription-handler/tests/test_network_function.py @@ -71,7 +71,8 @@ class NetworkFunctionTests(TestCase): self.nf_1.create() self.nf_2.create() sub = Subscription(**{"subscriptionName": "sub"}) - sub.add_network_functions_to_subscription([self.nf_1, self.nf_2]) + for nf in [self.nf_1, self.nf_2]: + sub.add_network_function_to_subscription(nf) NetworkFunction.delete(nf_name=self.nf_1.nf_name) diff --git a/components/pm-subscription-handler/tests/test_policy_response_handler.py b/components/pm-subscription-handler/tests/test_policy_response_handler.py index 1cf947fb..6de33632 100644 --- a/components/pm-subscription-handler/tests/test_policy_response_handler.py +++ b/components/pm-subscription-handler/tests/test_policy_response_handler.py @@ -22,7 +22,7 @@ from unittest.mock import patch from tenacity import stop_after_attempt -from mod.db_models import SubscriptionModel +from mod.api.db_models import SubscriptionModel from mod.network_function import NetworkFunction from mod.subscription import AdministrativeState, SubNfState from mod.policy_response_handler import PolicyResponseHandler, policy_response_handle_functions diff --git a/components/pm-subscription-handler/tests/test_subscription.py b/components/pm-subscription-handler/tests/test_subscription.py index e6ee2b57..dc549c94 100755 --- a/components/pm-subscription-handler/tests/test_subscription.py +++ b/components/pm-subscription-handler/tests/test_subscription.py @@ -26,7 +26,7 @@ from tenacity import stop_after_attempt import mod.aai_client as aai_client from mod import db, create_app -from mod.db_models import NetworkFunctionModel +from mod.api.db_models import NetworkFunctionModel from mod.network_function import NetworkFunction, NetworkFunctionFilter, OrchestrationStatus from mod.pmsh_utils import AppConfig from mod.subscription import Subscription @@ -46,7 +46,7 @@ class SubscriptionTest(TestCase): mock_session.return_value.text = self.aai_response_data self.env = EnvironmentVarGuard() self.env.set('AAI_SERVICE_HOST', '1.2.3.4') - self.env.set('AAI_SERVICE_PORT_AAI_SSL', '8443') + self.env.set('AAI_SERVICE_PORT', '8443') self.env.set('TESTING', 'True') self.env.set('LOGS_PATH', './unit_test_logs') with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data: @@ -110,22 +110,21 @@ class SubscriptionTest(TestCase): self.assertEqual(1, len(self.sub_1.get_all())) def test_add_network_functions_per_subscription(self): - nf_array = [self.nf_1, self.nf_2] - self.sub_1.add_network_functions_to_subscription(nf_array) + for nf in [self.nf_1, self.nf_2]: + self.sub_1.add_network_function_to_subscription(nf) nfs_for_sub_1 = Subscription.get_all_nfs_subscription_relations() self.assertEqual(2, len(nfs_for_sub_1)) - new_nf_array = [NetworkFunction(nf_name='vnf_3', orchestration_status='Inventoried')] - self.sub_1.add_network_functions_to_subscription(new_nf_array) + new_nf = NetworkFunction(nf_name='vnf_3', orchestration_status='Inventoried') + self.sub_1.add_network_function_to_subscription(new_nf) nf_subs = Subscription.get_all_nfs_subscription_relations() print(nf_subs) self.assertEqual(3, len(nf_subs)) def test_add_duplicate_network_functions_per_subscription(self): - nf_array = [self.nf_1] - self.sub_1.add_network_functions_to_subscription(nf_array) + self.sub_1.add_network_function_to_subscription(self.nf_1) nf_subs = Subscription.get_all_nfs_subscription_relations() self.assertEqual(1, len(nf_subs)) - self.sub_1.add_network_functions_to_subscription(nf_array) + self.sub_1.add_network_function_to_subscription(self.nf_1) nf_subs = Subscription.get_all_nfs_subscription_relations() self.assertEqual(1, len(nf_subs)) @@ -139,8 +138,10 @@ class SubscriptionTest(TestCase): self.assertEqual('new_status', sub.status) def test_delete_subscription(self): - self.sub_1.add_network_functions_to_subscription([self.nf_1, self.nf_2]) - self.sub_2.add_network_functions_to_subscription([self.nf_2]) + for nf in [self.nf_1, self.nf_2]: + self.sub_1.add_network_function_to_subscription(nf) + for nf in [self.nf_2]: + self.sub_2.add_network_function_to_subscription(nf) self.sub_1.delete_subscription() @@ -152,8 +153,8 @@ class SubscriptionTest(TestCase): def test_update_sub_nf_status(self): sub_name = 'ExtraPM-All-gNB-R2B' - nf_array = [self.nf_1, self.nf_2] - self.sub_1.add_network_functions_to_subscription(nf_array) + for nf in [self.nf_1, self.nf_2]: + self.sub_1.add_network_function_to_subscription(nf) sub_nfs = Subscription.get_all_nfs_subscription_relations() self.assertEqual('PENDING_CREATE', sub_nfs[0].nf_sub_status) @@ -162,7 +163,7 @@ class SubscriptionTest(TestCase): self.assertEqual('Active', sub_nfs[0].nf_sub_status) self.assertEqual('PENDING_CREATE', sub_nfs[1].nf_sub_status) - @patch('mod.subscription.Subscription.add_network_functions_to_subscription') + @patch('mod.subscription.Subscription.add_network_function_to_subscription') @patch('mod.subscription.Subscription.update_sub_nf_status') @patch('mod.subscription.Subscription.update_subscription_status') def test_process_activate_subscription(self, mock_update_sub_status, @@ -204,16 +205,16 @@ class SubscriptionTest(TestCase): self.assertEqual(expected_sub_event, actual_sub_event) def test_get_nf_models(self): - nf_array = [self.nf_1, self.nf_2] - self.sub_1.add_network_functions_to_subscription(nf_array) + for nf in [self.nf_1, self.nf_2]: + self.sub_1.add_network_function_to_subscription(nf) nf_models = self.sub_1._get_nf_models() self.assertEqual(2, len(nf_models)) self.assertIsInstance(nf_models[0], NetworkFunctionModel) def test_get_network_functions(self): - nf_array = [self.nf_1, self.nf_2] - self.sub_1.add_network_functions_to_subscription(nf_array) + for nf in [self.nf_1, self.nf_2]: + self.sub_1.add_network_function_to_subscription(nf) nfs = self.sub_1.get_network_functions() self.assertEqual(2, len(nfs)) -- cgit 1.2.3-korg