summaryrefslogtreecommitdiffstats
path: root/components/pm-subscription-handler
diff options
context:
space:
mode:
authorraviteja.karumuri <raviteja.karumuri@est.tech>2021-11-09 17:30:03 +0000
committerraviteja.karumuri <raviteja.karumuri@est.tech>2021-11-23 14:40:05 +0000
commit86f4eb23f256c3d7b748791bcff96e61c94fbd0f (patch)
tree76bbdca2d5407bbb8e16f6be1e8751630237468a /components/pm-subscription-handler
parent1685b700294ee0c1eaa1365d67a42861af0fe721 (diff)
[PMSH] Read subscription API by Name
Issue-ID: DCAEGEN2-2818 Signed-off-by: Raviteja, Karumuri <raviteja.karumuri@est.tech> Change-Id: Ie6925b4f4111e6f50c3b7dcd8eba670b89e63de3
Diffstat (limited to 'components/pm-subscription-handler')
-rwxr-xr-xcomponents/pm-subscription-handler/Changelog.md1
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/api/controller.py42
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/api/db_models.py38
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml62
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py22
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/subscription.py12
-rwxr-xr-xcomponents/pm-subscription-handler/tests/base_setup.py20
-rwxr-xr-xcomponents/pm-subscription-handler/tests/test_controller.py55
-rwxr-xr-xcomponents/pm-subscription-handler/tests/test_subscription.py6
9 files changed, 144 insertions, 114 deletions
diff --git a/components/pm-subscription-handler/Changelog.md b/components/pm-subscription-handler/Changelog.md
index 5fe19ba0..f02fc5eb 100755
--- a/components/pm-subscription-handler/Changelog.md
+++ b/components/pm-subscription-handler/Changelog.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* Created Schema definitions in swagger file according to the new structure (DCAEGEN2-2889)
* Implemented Create Subscription public API (DCAEGEN2-2819)
* Added 2 new attributes to the subscription model (DCAEGEN2-2913)
+* Read subscription API by using subscription name (DCAEGEN2-2818)
## [1.3.2]
### Changed
diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
index 8af6c777..80d86362 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
@@ -16,7 +16,6 @@
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=====================================================
-from mod.subscription import Subscription
from http import HTTPStatus
from mod import logger
from mod.api.services import subscription_service
@@ -37,16 +36,6 @@ def status():
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
-
-
def post_subscription(body):
"""
Creates a subscription
@@ -76,3 +65,34 @@ def post_subscription(body):
exc_info=True)
response = e.invalid_message, HTTPStatus.BAD_REQUEST.value
return response
+
+
+def get_subscription_by_name(subscription_name):
+ """
+ Retrieves subscription based on the name
+
+ Args:
+ subscription_name (String): Name of the subscription.
+
+ Returns:
+ success: dict of single Subscription, 200
+ None: subscription not defined, 404
+ Exception: Details about exception, 500
+ """
+ logger.info('API call received to fetch subscription by name')
+ try:
+ subscription = subscription_service.get_subscription_by_name(subscription_name)
+ if subscription is not None:
+ logger.info(f'subscription object with the name "{subscription_name}" '
+ 'was fetched successfully from database')
+ return subscription.serialize(), HTTPStatus.OK
+ else:
+ logger.error(f'subscription object with the name "{subscription_name}" '
+ 'was un successful to fetch from database')
+ return {'error': 'Subscription was not defined with the name : '
+ f'{subscription_name}'}, HTTPStatus.NOT_FOUND
+ except Exception as exception:
+ logger.error(f'The following exception occurred "{exception}" while fetching subscription '
+ f'with the name "{subscription_name}"')
+ return {'error': 'Request was not processed due to Exception : '
+ f'{exception}'}, HTTPStatus.INTERNAL_SERVER_ERROR
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
index 49ca0581..96a803ba 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py
@@ -38,7 +38,7 @@ class SubscriptionModel(db.Model):
network_filter = relationship(
'NetworkFunctionFilterModel',
cascade='all, delete-orphan',
- backref='subscription')
+ backref='subscription', uselist=False)
measurement_groups = relationship(
'MeasurementGroupModel',
@@ -63,14 +63,12 @@ class SubscriptionModel(db.Model):
return False
def serialize(self):
- sub_nfs = NfSubRelationalModel.query.filter(
- NfSubRelationalModel.subscription_name == self.subscription_name).all()
- db.session.remove()
- return {'subscription_name': self.subscription_name,
- 'operational_policy_name': self.operational_policy_name,
- 'control_loop_name': self.control_loop_name,
- 'subscription_status': self.status,
- 'network_functions': [sub_nf.serialize_nf() for sub_nf in sub_nfs]}
+ return {'subscription': {'subscriptionName': self.subscription_name,
+ 'operationalPolicyName': self.operational_policy_name,
+ 'controlLoopName': self.control_loop_name,
+ 'nfFilter': self.network_filter.serialize(),
+ 'measurementGroups':
+ [mg.serialize() for mg in self.measurement_groups]}}
class NetworkFunctionModel(db.Model):
@@ -141,7 +139,7 @@ class NfSubRelationalModel(db.Model):
def __repr__(self):
return f'subscription_name: {self.subscription_name}, ' \
- f'nf_name: {self.nf_name}, nf_sub_status: {self.nf_sub_status}'
+ 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,
@@ -185,12 +183,11 @@ class NetworkFunctionFilterModel(db.Model):
def __repr__(self):
return f'subscription_name: {self.subscription_name}, ' \
- f'nf_names: {self.nf_names}, model_invariant_ids: {self.model_invariant_ids}' \
+ f'nf_names: {self.nf_names}, model_invariant_ids: {self.model_invariant_ids}' \
f'model_version_ids: {self.model_version_ids}, model_names: {self.model_names}'
def serialize(self):
- return {'subscriptionName': self.subscription_name,
- 'nfNames': convert_db_string_to_list(self.nf_names),
+ return {'nfNames': convert_db_string_to_list(self.nf_names),
'modelInvariantIDs': convert_db_string_to_list(self.model_invariant_ids),
'modelVersionIDs': convert_db_string_to_list(self.model_version_ids),
'modelNames': convert_db_string_to_list(self.model_names)}
@@ -231,13 +228,12 @@ class MeasurementGroupModel(db.Model):
f'managed_object_dns_basic: {self.managed_object_dns_basic}'
def serialize(self):
- return {'subscription_name': self.subscription_name,
- 'measurement_group_name': self.measurement_group_name,
- 'administrative_state': self.administrative_state,
- 'file_based_gp': self.file_based_gp,
- 'file_location': self.file_location,
- 'measurement_type': self.measurement_type,
- 'managed_object_dns_basic': self.managed_object_dns_basic}
+ return {'measurementGroup': {'measurementGroupName': self.measurement_group_name,
+ 'administrativeState': self.administrative_state,
+ 'fileBasedGP': self.file_based_gp,
+ 'fileLocation': self.file_location,
+ 'measurementTypes': self.measurement_type,
+ 'managedObjectDNsBasic': self.managed_object_dns_basic}}
class NfMeasureGroupRelationalModel(db.Model):
@@ -267,7 +263,7 @@ class NfMeasureGroupRelationalModel(db.Model):
def __repr__(self):
return f'measurement_grp_name: {self.measurement_grp_name}, ' \
- f'nf_name: {self.nf_name}, nf_measure_grp_status: {self.nf_measure_grp_status}'
+ f'nf_name: {self.nf_name}, nf_measure_grp_status: {self.nf_measure_grp_status}'
def convert_db_string_to_list(db_string):
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
index 11cea4ee..f27fb7ab 100644
--- a/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml
@@ -31,46 +31,6 @@ schemes:
- http
# 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
@@ -112,6 +72,28 @@ paths:
400:
description: Invalid input
+ /subscription/{subscription_name}:
+ get:
+ description: Get the Subscription from ONAP specified by Name
+ operationId: mod.api.controller.get_subscription_by_name
+ tags:
+ - "Subscription"
+ parameters:
+ - name: subscription_name
+ in: path
+ required: true
+ description: Name of the subscription
+ type: string
+ responses:
+ 200:
+ description: OK; Requested Subscription was returned
+ schema:
+ $ref : "#/definitions/subscription"
+ 404:
+ description: Subscription with specified name not found
+ 500:
+ description: Exception occurs while querying database
+
definitions:
subscription:
type: object
diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py b/components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py
index ea1640c2..c41bb18b 100644
--- a/components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py
@@ -23,6 +23,7 @@ from mod.api.services import measurement_group_service, nf_service
from mod.api.custom_exception import InvalidDataException, DuplicateDataException
from mod.subscription import AdministrativeState
from sqlalchemy.exc import IntegrityError
+from sqlalchemy.orm import joinedload
def create_subscription(subscription):
@@ -203,7 +204,7 @@ def save_subscription_request(subscription):
subscription (dict): subscription request to be saved.
Returns:
- string: Subscription name
+ SubscriptionModel: subscription object which was added to the session
list[MeasurementGroupModel]: list of measurement groups
"""
logger.info(f'Saving subscription request for: {subscription["subscriptionName"]}')
@@ -289,3 +290,22 @@ def save_nf_filter(nf_filter, subscription_name):
model_version_ids=nf_filter['modelVersionIDs'],
model_names=nf_filter['modelNames'])
db.session.add(new_filter)
+
+
+def get_subscription_by_name(subscription_name):
+ """
+ Retrieves the subscription information by name
+
+ Args:
+ subscription_name (String): Name of the Subscription
+
+ Returns:
+ SubscriptionModel: If subscription was defined else None
+ """
+ logger.info(f'Attempting to fetch subscription by name: {subscription_name}')
+ subscription_model = db.session.query(SubscriptionModel) \
+ .options(joinedload(SubscriptionModel.network_filter),
+ joinedload(SubscriptionModel.measurement_groups)) \
+ .filter_by(subscription_name=subscription_name).first()
+ db.session.remove()
+ return subscription_model
diff --git a/components/pm-subscription-handler/pmsh_service/mod/subscription.py b/components/pm-subscription-handler/pmsh_service/mod/subscription.py
index bdfed189..603343f0 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/subscription.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/subscription.py
@@ -202,18 +202,6 @@ class Subscription:
db.session.remove()
return sub_model.status
- @staticmethod
- def get_all():
- """ Retrieves a list of subscriptions
-
- Returns:
- list(SubscriptionModel): Subscriptions list else empty
- """
-
- sub_models = SubscriptionModel.query.all()
- db.session.remove()
- return sub_models
-
def create_subscription_on_nfs(self, nfs, mr_pub):
""" Publishes an event to create a Subscription on an nf
diff --git a/components/pm-subscription-handler/tests/base_setup.py b/components/pm-subscription-handler/tests/base_setup.py
index e422ceac..4328f59c 100755
--- a/components/pm-subscription-handler/tests/base_setup.py
+++ b/components/pm-subscription-handler/tests/base_setup.py
@@ -15,12 +15,14 @@
#
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=====================================================
+import copy
import json
import os
from unittest import TestCase
from unittest.mock import patch, MagicMock
from mod import create_app, db
+from mod.api.db_models import NetworkFunctionFilterModel, MeasurementGroupModel, SubscriptionModel
from mod.network_function import NetworkFunctionFilter
from mod.pmsh_utils import AppConfig
from mod.pmsh_config import AppConfig as NewAppConfig
@@ -31,6 +33,24 @@ def get_pmsh_config(file_path='data/cbs_data_1.json'):
return json.load(data)
+def subscription_data(subscription_name):
+ nf_filter = NetworkFunctionFilterModel(subscription_name, '{^pnf.*,^vnf.*}',
+ '{}', '{}', '{}')
+ mg_first = MeasurementGroupModel(subscription_name, 'MG1', 'UNLOCKED', 15, '/pm/pm.xml',
+ '[{ "measurementType": "countera" }, '
+ '{ "measurementType": "counterb" }]',
+ '[{ "DN":"dna"},{"DN":"dnb"}]')
+ mg_second = copy.deepcopy(mg_first)
+ mg_second.measurement_group_name = 'MG2'
+ mg_second.administrative_state = 'LOCKED'
+ mg_list = [mg_first, mg_second]
+ subscription_model = SubscriptionModel(subscription_name, 'pmsh_operational_policy',
+ 'pmsh_control_loop_name', 'LOCKED')
+ subscription_model.network_filter = nf_filter
+ subscription_model.measurement_groups = mg_list
+ return subscription_model
+
+
class BaseClassSetup(TestCase):
app = None
app_context = None
diff --git a/components/pm-subscription-handler/tests/test_controller.py b/components/pm-subscription-handler/tests/test_controller.py
index a3a28163..7bd72a29 100755
--- a/components/pm-subscription-handler/tests/test_controller.py
+++ b/components/pm-subscription-handler/tests/test_controller.py
@@ -18,14 +18,15 @@
import json
import os
from unittest.mock import patch, MagicMock
-import responses
-from requests import Session
+from http import HTTPStatus
+
from mod import aai_client
-from mod.api.controller import status, get_all_sub_to_nf_relations, post_subscription
+from mod.api.controller import status, post_subscription, get_subscription_by_name
from tests.base_setup import BaseClassSetup
from mod.api.db_models import SubscriptionModel, NfMeasureGroupRelationalModel
from mod.subscription import SubNfState
from mod.network_function import NetworkFunctionFilter
+from tests.base_setup import subscription_data
class ControllerTestCase(BaseClassSetup):
@@ -55,26 +56,6 @@ class ControllerTestCase(BaseClassSetup):
def test_status_response_healthy(self):
self.assertEqual(status()['status'], 'healthy')
- @patch.object(Session, 'get')
- @patch.object(Session, 'put')
- def test_get_all_sub_to_nf_relations(self, mock_put_session, mock_get_session):
- mock_put_session.return_value.status_code = 200
- mock_put_session.return_value.text = self.aai_response_data
- mock_get_session.return_value.status_code = 200
- mock_get_session.return_value.text = self.good_model_info
- responses.add(responses.GET,
- 'https://aai:8443/aai/v20/service-design-and-creation/models/model/'
- '7129e420-d396-4efb-af02-6b83499b12f8/model-vers/model-ver/'
- 'e80a6ae3-cafd-4d24-850d-e14c084a5ca9',
- json=json.loads(self.good_model_info), status=200)
- self.xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf, self.app_conf.nf_filter)
- sub_model = self.app_conf.subscription.get()
- for nf in self.xnfs:
- self.app_conf.subscription.add_network_function_to_subscription(nf, sub_model)
- all_subs = get_all_sub_to_nf_relations()
- self.assertEqual(len(all_subs[0]['network_functions']), 3)
- self.assertEqual(all_subs[0]['subscription_name'], 'ExtraPM-All-gNB-R2B')
-
def create_test_subs(self, new_sub_name, new_msrmt_grp_name):
subscription = self.subscription_request.replace('ExtraPM-All-gNB-R2B', new_sub_name)
subscription = subscription.replace('msrmt_grp_name', new_msrmt_grp_name)
@@ -129,3 +110,31 @@ class ControllerTestCase(BaseClassSetup):
response = post_subscription(subscription)
self.assertEqual(response[1], 400)
self.assertEqual(response[0], 'No value provided in subscription name')
+
+ @patch('mod.api.services.subscription_service.get_subscription_by_name',
+ MagicMock(return_value=subscription_data('sub_demo')))
+ def test_get_subscription_by_name_api(self):
+ sub, status_code = get_subscription_by_name('sub_demo')
+ self.assertEqual(status_code, HTTPStatus.OK)
+ self.assertEqual(sub['subscription']['subscriptionName'], 'sub_demo')
+ self.assertEqual(sub['subscription']['nfFilter']['nfNames'],
+ ['^pnf.*', '^vnf.*'])
+ self.assertEqual(sub['subscription']['controlLoopName'],
+ 'pmsh_control_loop_name')
+ self.assertEqual(len(sub['subscription']['measurementGroups']), 2)
+ self.assertEqual(sub['subscription']['operationalPolicyName'],
+ 'pmsh_operational_policy')
+
+ @patch('mod.api.services.subscription_service.get_subscription_by_name',
+ MagicMock(return_value=None))
+ def test_get_subscription_by_name_api_error(self):
+ sub, status_code = get_subscription_by_name('sub_demo')
+ self.assertEqual(status_code, HTTPStatus.NOT_FOUND)
+ self.assertEqual(sub['error'],
+ 'Subscription was not defined with the name : sub_demo')
+
+ @patch('mod.api.services.subscription_service.get_subscription_by_name',
+ MagicMock(side_effect=Exception('something failed')))
+ def test_get_subscription_by_name_api_exception(self):
+ sub, status_code = get_subscription_by_name('sub_demo')
+ self.assertEqual(status_code, HTTPStatus.INTERNAL_SERVER_ERROR)
diff --git a/components/pm-subscription-handler/tests/test_subscription.py b/components/pm-subscription-handler/tests/test_subscription.py
index 538baf39..5c40c4fd 100755
--- a/components/pm-subscription-handler/tests/test_subscription.py
+++ b/components/pm-subscription-handler/tests/test_subscription.py
@@ -75,12 +75,6 @@ class SubscriptionTest(BaseClassSetup):
self.app_conf.subscription.add_network_function_to_subscription(list(self.xnfs)[1],
self.sub_model)
- def test_create_existing_subscription(self):
- sub1 = self.app_conf.subscription.create()
- same_sub1 = self.app_conf.subscription.create()
- self.assertEqual(sub1, same_sub1)
- self.assertEqual(1, len(self.app_conf.subscription.get_all()))
-
def test_add_duplicate_network_functions_per_subscription(self):
self.app_conf.subscription.add_network_function_to_subscription(list(self.xnfs)[0],
self.sub_model)