summaryrefslogtreecommitdiffstats
path: root/components/pm-subscription-handler
diff options
context:
space:
mode:
authorSagarS <sagar.shetty@est.tech>2021-09-08 14:46:32 +0100
committerSagarS <sagar.shetty@est.tech>2021-10-18 15:01:20 +0100
commit70de6a27b7722e3ed02d8e8a8c7933e053eacabb (patch)
treefb2c635116b705fdf64e3400233ad351ea0259f7 /components/pm-subscription-handler
parent2f208f94eddd090c1983f849f61742288c21af6f (diff)
[DCAEGEN2] PMSH Create Subscription public API
Issue-ID: DCAEGEN2-2819 Change-Id: I80636be25dc4f7b1c5ce7470c7a38c010cb339a1 Signed-off-by: SagarS <sagar.shetty@est.tech>
Diffstat (limited to 'components/pm-subscription-handler')
-rwxr-xr-xcomponents/pm-subscription-handler/Changelog.md1
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/aai_client.py14
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/api/controller.py38
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/custom_exception.py38
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/api/db_models.py21
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml34
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py87
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/services/nf_service.py75
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py275
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/network_function.py16
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py3
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py2
-rw-r--r--components/pm-subscription-handler/tests/data/create_subscription_request.json60
-rw-r--r--components/pm-subscription-handler/tests/services/test_measurement_group_service.py113
-rw-r--r--components/pm-subscription-handler/tests/services/test_nf_service.py105
-rw-r--r--components/pm-subscription-handler/tests/services/test_subscription_service.py353
-rw-r--r--components/pm-subscription-handler/tests/test_aai_service.py6
-rwxr-xr-xcomponents/pm-subscription-handler/tests/test_controller.py72
-rwxr-xr-xcomponents/pm-subscription-handler/tests/test_subscription.py2
19 files changed, 1286 insertions, 29 deletions
diff --git a/components/pm-subscription-handler/Changelog.md b/components/pm-subscription-handler/Changelog.md
index f98d2b8d..eff84191 100755
--- a/components/pm-subscription-handler/Changelog.md
+++ b/components/pm-subscription-handler/Changelog.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed
* Updated PMSH app configuration, simplified existing config (DCAEGEN2-2814)
* Created Schema definitions in swagger file according to the new structure (DCAEGEN2-2889)
+* Implemented Create Subscription public API (DCAEGEN2-2819)
## [1.3.2]
### Changed
diff --git a/components/pm-subscription-handler/pmsh_service/mod/aai_client.py b/components/pm-subscription-handler/pmsh_service/mod/aai_client.py
index 39adba46..d2aeb0f0 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/aai_client.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/aai_client.py
@@ -17,22 +17,20 @@
# ============LICENSE_END=====================================================
import json
from os import environ
-
import requests
from requests.auth import HTTPBasicAuth
-
import mod.network_function
import mod.pmsh_utils
from mod import logger
-def get_pmsh_nfs_from_aai(app_conf):
+def get_pmsh_nfs_from_aai(app_conf, nf_filter):
"""
Returns the Network Functions from AAI related to the Subscription.
Args:
app_conf (AppConfig): the AppConfig object.
-
+ nf_filter (NetworkFunctionFilter): the filter to apply on nf from aai
Returns:
NetworkFunctions (list): list of NetworkFunctions.
@@ -41,7 +39,7 @@ def get_pmsh_nfs_from_aai(app_conf):
"""
aai_nf_data = _get_all_aai_nf_data(app_conf)
if aai_nf_data:
- nfs = _filter_nf_data(aai_nf_data, app_conf)
+ nfs = _filter_nf_data(aai_nf_data, app_conf, nf_filter)
else:
raise RuntimeError('Failed to get data from AAI')
return nfs
@@ -114,14 +112,14 @@ def _get_aai_request_headers(**kwargs):
'RequestID': kwargs['request_id']}
-def _filter_nf_data(nf_data, app_conf):
+def _filter_nf_data(nf_data, app_conf, nf_filter):
"""
Returns a list of filtered NetworkFunctions using the nf_filter.
Args:
nf_data (dict): the nf json data from AAI.
app_conf (AppConfig): the AppConfig object.
-
+ nf_filter (NetworkFunctionFilter): filter data to apply on network functions
Returns:
NetworkFunction (list): a list of filtered NetworkFunction Objects.
@@ -142,7 +140,7 @@ def _filter_nf_data(nf_data, app_conf):
model_version_id=nf['properties'].get('model-version-id'))
if not new_nf.set_nf_model_params(app_conf):
continue
- if app_conf.nf_filter.is_nf_in_filter(new_nf):
+ if nf_filter.is_nf_in_filter(new_nf):
nf_list.append(new_nf)
except KeyError as e:
logger.error(f'Failed to parse AAI data: {e}', exc_info=True)
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 21d29caf..8af6c777 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
@@ -1,5 +1,5 @@
# ============LICENSE_START===================================================
-# Copyright (C) 2019-2020 Nordix Foundation.
+# Copyright (C) 2019-2021 Nordix Foundation.
# ============================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,6 +17,11 @@
# ============LICENSE_END=====================================================
from mod.subscription import Subscription
+from http import HTTPStatus
+from mod import logger
+from mod.api.services import subscription_service
+from connexion import NoContent
+from mod.api.custom_exception import InvalidDataException, DuplicateDataException
def status():
@@ -40,3 +45,34 @@ def get_all_sub_to_nf_relations():
"""
subs_dict = [s.serialize() for s in Subscription.get_all()]
return subs_dict
+
+
+def post_subscription(body):
+ """
+ Creates a subscription
+
+ Args:
+ body (dict): subscription request body to save.
+
+ Returns:
+ Success : NoContent, 201
+ Invalid Data : Invalid message, 400
+ Duplicate Data : Duplicate field detail, 409
+
+ Raises:
+ Error: If anything fails in the server.
+ """
+ response = NoContent, HTTPStatus.CREATED.value
+ try:
+ subscription_service.create_subscription(body['subscription'])
+ except DuplicateDataException as e:
+ logger.error(f'Failed to create subscription for '
+ f'{body["subscription"]["subscriptionName"]} due to duplicate data: {e}',
+ exc_info=True)
+ response = e.duplicate_field_info, HTTPStatus.CONFLICT.value
+ except InvalidDataException as e:
+ logger.error(f'Failed to create subscription for '
+ f'{body["subscription"]["subscriptionName"]} due to invalid data: {e}',
+ exc_info=True)
+ response = e.invalid_message, HTTPStatus.BAD_REQUEST.value
+ return response
diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/custom_exception.py b/components/pm-subscription-handler/pmsh_service/mod/api/custom_exception.py
new file mode 100644
index 00000000..606d500c
--- /dev/null
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/custom_exception.py
@@ -0,0 +1,38 @@
+# ============LICENSE_START===================================================
+# Copyright (C) 2021 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=====================================================
+
+class InvalidDataException(Exception):
+ """Exception raised for invalid inputs.
+
+ Attributes:
+ message -- detail on invalid data
+ """
+
+ def __init__(self, invalid_message):
+ self.invalid_message = invalid_message
+
+
+class DuplicateDataException(Exception):
+ """Exception raised for duplicate inputs.
+
+ Attributes:
+ message -- detail on duplicate field
+ """
+
+ def __init__(self, duplicate_field_info):
+ self.duplicate_field_info = duplicate_field_info
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 5c87fa55..2b340e24 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
@@ -179,9 +179,11 @@ class NetworkFunctionFilterModel(db.Model):
f'model_version_ids: {self.model_version_ids}, model_names: {self.model_names}'
def serialize(self):
- return {'subscription_name': self.subscription_name, 'nf_names': self.nf_names,
- 'model_invariant_ids': self.model_invariant_ids,
- 'model_version_ids': self.model_version_ids, 'model_names': self.model_names}
+ return {'subscriptionName': self.subscription_name,
+ '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)}
class MeasurementGroupModel(db.Model):
@@ -256,3 +258,16 @@ 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}'
+
+
+def convert_db_string_to_list(db_string):
+ """
+ Converts a db string to array and
+ removes empty strings
+ Args:
+ db_string (string): The db string to convert into an array
+ Returns:
+ list[string]: converted list of strings else empty
+ """
+ array_format = db_string.strip('{}').split(',')
+ return [x for x in array_format if x.strip() != ""]
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 2a6137cf..11cea4ee 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
@@ -21,6 +21,8 @@ info:
title: PM Subscription Handler Service
version: "2.0.0"
description: PM subscription handler enables control of performance management jobs on network functions in ONAP
+consumes:
+ - "application/json"
produces:
- "application/json"
basePath: "/"
@@ -89,6 +91,27 @@ paths:
503:
description: the pmsh service is unavailable
+ /subscription:
+ post:
+ tags:
+ - "Subscription"
+ description: >-
+ Create a PM Subscription
+ operationId: mod.api.controller.post_subscription
+ parameters:
+ - in: "body"
+ name: "body"
+ required: true
+ schema:
+ $ref: "#/definitions/subscription"
+ responses:
+ 201:
+ description: successfully created PM Subscription
+ 409:
+ description: Duplicate data
+ 400:
+ description: Invalid input
+
definitions:
subscription:
type: object
@@ -119,29 +142,30 @@ definitions:
nfFilter:
type: object
- minProperties: 1
+ description: "At least one valid filter value within nfFilter is required"
additionalProperties: false
properties:
nfNames:
type: array
- minItems: 1
items:
type: string
modelInvariantIDs:
type: array
- minItems: 1
items:
type: string
modelVersionIDs:
type: array
- minItems: 1
items:
type: string
modelNames:
type: array
- minItems: 1
items:
type: string
+ required:
+ - nfNames
+ - modelInvariantIDs
+ - modelVersionIDs
+ - modelNames
measurementGroup:
type: object
diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py b/components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py
new file mode 100644
index 00000000..329dc857
--- /dev/null
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py
@@ -0,0 +1,87 @@
+# ============LICENSE_START===================================================
+# Copyright (C) 2021 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.api.db_models import MeasurementGroupModel, NfMeasureGroupRelationalModel
+from mod import db, logger
+from mod.subscription import SubNfState
+from mod.api.services import nf_service
+from mod.pmsh_config import MRTopic, AppConfig
+
+
+def save_measurement_group(measurement_group, subscription_name):
+ """
+ Saves the measurement_group data request
+
+ Args:
+ measurement_group (dict) : measurement group to save
+ subscription_name (string) : subscription name to associate with measurement group.
+
+ Returns:
+ MeasurementGroupModel : measurement group saved in the database
+ """
+ logger.info(f'Saving measurement group for subscription request: {subscription_name}')
+ new_measurement_group = MeasurementGroupModel(
+ subscription_name=subscription_name,
+ measurement_group_name=measurement_group['measurementGroupName'],
+ administrative_state=measurement_group['administrativeState'],
+ file_based_gp=measurement_group['fileBasedGP'],
+ file_location=measurement_group['fileLocation'],
+ measurement_type=measurement_group['measurementTypes'],
+ managed_object_dns_basic=measurement_group['managedObjectDNsBasic'])
+ db.session.add(new_measurement_group)
+ return new_measurement_group
+
+
+def apply_nf_to_measgroup(nf_name, measurement_group_name):
+ """
+ Associate and saves the measurement group with Network function
+
+ Args:
+ nf_name (string): Network function name.
+ measurement_group_name (string): Measurement group name
+ """
+ new_nf_measure_grp_rel = NfMeasureGroupRelationalModel(
+ measurement_grp_name=measurement_group_name,
+ nf_name=nf_name,
+ nf_measure_grp_status=SubNfState.PENDING_CREATE.value
+ )
+ db.session.add(new_nf_measure_grp_rel)
+
+
+def publish_measurement_group(subscription_name, measurement_group, nf):
+ """
+ Publishes an event for measurement group against nfs to MR
+
+ Args:
+ subscription_name (string): Subscription name to publish against nf
+ measurement_group (MeasurementGroupModel): Measurement group to publish
+ nf (NetworkFunction): Network function to publish.
+ """
+ event_body = nf_service.create_nf_event_body(nf, 'CREATE')
+ event_body['subscription'] = {
+ "administrativeState": measurement_group.administrative_state,
+ "subscriptionName": subscription_name,
+ "fileBasedGP": measurement_group.file_based_gp,
+ "fileLocation": measurement_group.file_location,
+ "measurementGroup": {
+ "measurementGroupName": measurement_group.measurement_group_name,
+ "measurementTypes": measurement_group.measurement_type,
+ "managedObjectDNsBasic": measurement_group.managed_object_dns_basic
+ }
+ }
+ AppConfig.get_instance().publish_to_topic(MRTopic.POLICY_PM_PUBLISHER.value, event_body)
diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/services/nf_service.py b/components/pm-subscription-handler/pmsh_service/mod/api/services/nf_service.py
new file mode 100644
index 00000000..1fca766a
--- /dev/null
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/services/nf_service.py
@@ -0,0 +1,75 @@
+# ============LICENSE_START===================================================
+# Copyright (C) 2021 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 import db, aai_client, logger
+from mod.api.db_models import NetworkFunctionModel
+from mod.pmsh_config import AppConfig
+from mod.network_function import NetworkFunctionFilter
+
+
+def capture_filtered_nfs(sub_name):
+ """
+ Retrieves network functions from AAI client and
+ returns a list of filtered NetworkFunctions using the Filter
+
+ Args:
+ sub_name (string): The name of subscription inorder to perform filtering
+ Returns:
+ list[NetworkFunction]: a list of filtered NetworkFunction Objects.
+ """
+ logger.info(f'Getting filtered nfs for subscription: {sub_name}')
+ nf_filter = NetworkFunctionFilter.get_network_function_filter(sub_name)
+ return aai_client.get_pmsh_nfs_from_aai(AppConfig.get_instance(), nf_filter)
+
+
+def create_nf_event_body(nf, change_type):
+ """
+ Creates a network function event body to publish on MR
+
+ Args:
+ nf (NetworkFunction): the Network function to include in the event.
+ change_type (string): define the change type to be applied on node
+ Returns:
+ dict: network function event body to publish on MR.
+ """
+ app_conf = AppConfig.get_instance()
+ return {'nfName': nf.nf_name,
+ 'ipAddress': nf.ipv4_address if nf.ipv6_address in (None, '')
+ else nf.ipv6_address,
+ 'blueprintName': nf.sdnc_model_name,
+ 'blueprintVersion': nf.sdnc_model_version,
+ 'policyName': app_conf.operational_policy_name,
+ 'changeType': change_type,
+ 'closedLoopControlName': app_conf.control_loop_name}
+
+
+def save_nf(nf):
+ """
+ Saves the network function request to the db
+ Args:
+ nf (NetworkFunction) : requested network function to save
+ """
+ network_function = NetworkFunctionModel(nf_name=nf.nf_name,
+ ipv4_address=nf.ipv4_address,
+ ipv6_address=nf.ipv6_address,
+ model_invariant_id=nf.model_invariant_id,
+ model_version_id=nf.model_version_id,
+ model_name=nf.model_name,
+ sdnc_model_name=nf.sdnc_model_name,
+ sdnc_model_version=nf.sdnc_model_version)
+ db.session.add(network_function)
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
new file mode 100644
index 00000000..1485bebe
--- /dev/null
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py
@@ -0,0 +1,275 @@
+# ============LICENSE_START===================================================
+# Copyright (C) 2021 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 import db, logger
+from mod.api.db_models import SubscriptionModel, NfSubRelationalModel, \
+ NetworkFunctionFilterModel, NetworkFunctionModel
+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
+
+
+def create_subscription(subscription):
+ """
+ Creates a subscription
+
+ Args:
+ subscription (dict): subscription to save.
+
+ Raises:
+ DuplicateDataException: contains details on duplicate fields
+ Exception: contains runtime error details
+ """
+ logger.info(f'Initiating create subscription for: {subscription["subscriptionName"]}')
+ perform_validation(subscription)
+ try:
+ sub_name, measurement_groups = save_subscription_request(subscription)
+ db.session.commit()
+ logger.info(f'Successfully saved subscription request for: '
+ f'{subscription["subscriptionName"]}')
+ filtered_nfs = nf_service.capture_filtered_nfs(sub_name)
+ if filtered_nfs:
+ logger.info(f'Applying the filtered nfs for subscription: {sub_name}')
+ save_filtered_nfs(filtered_nfs)
+ apply_subscription_to_nfs(filtered_nfs, sub_name)
+ unlocked_msmt_groups = apply_measurement_grp_to_nfs(filtered_nfs, measurement_groups)
+ db.session.commit()
+ if unlocked_msmt_groups:
+ publish_measurement_grp_to_nfs(sub_name, filtered_nfs, unlocked_msmt_groups)
+ else:
+ logger.error(f'All measurement groups are locked for subscription: {sub_name}, '
+ f'please verify/check measurement groups.')
+ else:
+ logger.error(f'No network functions found for subscription: {sub_name}, '
+ f'please verify/check NetworkFunctionFilter.')
+ except IntegrityError as e:
+ db.session.rollback()
+ raise DuplicateDataException(f'DB Integrity issue encountered: {e.orig.args[0]}')
+ except Exception as e:
+ db.session.rollback()
+ raise e
+ finally:
+ db.session.remove()
+
+
+def publish_measurement_grp_to_nfs(subscription_name, filtered_nfs, measurement_groups):
+ """
+ Publishes an event for measurement groups against nfs
+
+ Args:
+ subscription_name (string): subscription name against nfs
+ filtered_nfs (list[NetworkFunction])): list of filtered network functions
+ measurement_groups (list[MeasurementGroupModel]): list of unlocked measurement group
+ """
+ for measurement_group in measurement_groups:
+ for nf in filtered_nfs:
+ try:
+ logger.info(f'Publishing event for nf name, measure_grp_name: {nf.nf_name},'
+ f'{measurement_group.measurement_group_name}')
+ measurement_group_service.publish_measurement_group(
+ subscription_name, measurement_group, nf)
+ except Exception as ex:
+ logger.error(f'Publish event failed for nf name, measure_grp_name, sub_name: '
+ f'{nf.nf_name},{measurement_group.measurement_group_name}, '
+ f'{subscription_name} with error: {ex}')
+
+
+def save_filtered_nfs(filtered_nfs):
+ """
+ Saves a network function
+
+ Args:
+ filtered_nfs (list[NetworkFunction]): list of filtered network functions to save.
+ """
+ pmsh_nf_names = list(nf.nf_name for nf in NetworkFunctionModel.query.all())
+ for nf in filtered_nfs:
+ if nf.nf_name not in pmsh_nf_names:
+ nf_service.save_nf(nf)
+
+
+def apply_subscription_to_nfs(filtered_nfs, subscription_name):
+ """
+ Associate and saves the subscription with Network functions
+
+ Args:
+ filtered_nfs (list[NetworkFunction]): list of filtered network functions to save.
+ subscription_name (string): subscription name to save against nfs
+ """
+ logger.info(f'Saving filtered nfs for subscription: {subscription_name}')
+ for nf in filtered_nfs:
+ new_nf_sub_rel = NfSubRelationalModel(subscription_name=subscription_name,
+ nf_name=nf.nf_name)
+ db.session.add(new_nf_sub_rel)
+
+
+def apply_measurement_grp_to_nfs(filtered_nfs, measurement_groups):
+ """
+ Saves measurement groups against nfs with status as PENDING_CREATE
+
+ Args:
+ filtered_nfs (list[NetworkFunction])): list of filtered network functions
+ measurement_groups (list[MeasurementGroupModel]): list of measurement group
+
+ Returns:
+ list[MeasurementGroupModel]: list of Unlocked measurement groups
+ """
+ unlocked_msmt_groups = []
+ for measurement_group in measurement_groups:
+ if measurement_group.administrative_state \
+ == AdministrativeState.UNLOCKED.value:
+ unlocked_msmt_groups.append(measurement_group)
+ for nf in filtered_nfs:
+ logger.info(f'Saving measurement group to nf name, measure_grp_name: {nf.nf_name},'
+ f'{measurement_group.measurement_group_name}')
+ measurement_group_service.apply_nf_to_measgroup(
+ nf.nf_name, measurement_group.measurement_group_name)
+ else:
+ logger.info(f'No nfs added as measure_grp_name: '
+ f'{measurement_group.measurement_group_name} is LOCKED')
+ return unlocked_msmt_groups
+
+
+def check_missing_data(subscription):
+ """
+ checks if the subscription request has missing data
+
+ Args:
+ subscription (dict): subscription to validate
+
+ Raises:
+ InvalidDataException: exception containing the list of invalid data.
+ """
+ if subscription['subscriptionName'].strip() in (None, ''):
+ raise InvalidDataException("No value provided in subscription name")
+
+ for measurement_group in subscription.get('measurementGroups'):
+ measurement_group_details = measurement_group['measurementGroup']
+ if measurement_group_details['administrativeState'].strip() in (None, ''):
+ raise InvalidDataException("No value provided for administrative state")
+ if measurement_group_details['measurementGroupName'].strip() in (None, ''):
+ raise InvalidDataException("No value provided for measurement group name")
+
+
+def perform_validation(subscription):
+ """
+ validates the subscription and if invalid raises an exception
+ to indicate duplicate/invalid request
+
+ Args:
+ subscription (dict): subscription to validate
+
+ Raises:
+ DuplicateDataException: exception containing the detail on duplicate data field.
+ InvalidDataException: exception containing the detail on invalid data.
+ """
+ logger.info(f'Performing subscription validation for: {subscription["subscriptionName"]}')
+ check_missing_data(subscription)
+ logger.info(f'No missing data found for: {subscription["subscriptionName"]}')
+ check_duplicate_fields(subscription["subscriptionName"])
+ logger.info(f'No duplicate data found for: {subscription["subscriptionName"]}')
+ validate_nf_filter(subscription["nfFilter"])
+ logger.info(f'Filter data is valid for: {subscription["subscriptionName"]}')
+
+
+def save_subscription_request(subscription):
+ """
+ Saves the subscription request consisting of:
+ network function filter and measurement groups
+
+ Args:
+ subscription (dict): subscription request to be saved.
+
+ Returns:
+ string: Subscription name
+ list[MeasurementGroupModel]: list of measurement groups
+ """
+ logger.info(f'Saving subscription request for: {subscription["subscriptionName"]}')
+ sub_name = save_subscription(subscription).subscription_name
+ save_nf_filter(subscription["nfFilter"], subscription["subscriptionName"])
+ measurement_groups = []
+ for measurement_group in subscription['measurementGroups']:
+ measurement_groups.append(
+ measurement_group_service.save_measurement_group(
+ measurement_group['measurementGroup'],
+ subscription["subscriptionName"]))
+ return sub_name, measurement_groups
+
+
+def check_duplicate_fields(subscription_name):
+ """
+ validates the subscription content if already present
+ and captures duplicate fields
+
+ Args:
+ subscription_name (string): subscription name
+
+ Raises:
+ InvalidDataException: exception containing the list of invalid data.
+ """
+
+ existing_subscription = (SubscriptionModel.query.filter(
+ SubscriptionModel.subscription_name == subscription_name).one_or_none())
+ if existing_subscription is not None:
+ raise DuplicateDataException(f'subscription Name: {subscription_name} already exists.')
+
+
+def save_subscription(subscription):
+ """
+ Saves the subscription data
+
+ Args:
+ subscription (dict): subscription model to be saved.
+ """
+ subscription_model = SubscriptionModel(subscription_name=subscription["subscriptionName"],
+ status=AdministrativeState.LOCKED.value)
+ db.session.add(subscription_model)
+ return subscription_model
+
+
+def validate_nf_filter(nf_filter):
+ """
+ checks if the nf filter is valid
+
+ Args:
+ nf_filter (dict): nf filter to validate
+
+ Raises:
+ InvalidDataException: if no field is available in nf_filter
+ """
+ for filter_name, filter_values in nf_filter.items():
+ filter_values[:] = [x for x in filter_values if x.strip()]
+ if not [filter_name for filter_name, val in nf_filter.items() if len(val) > 0]:
+ raise InvalidDataException("At least one filter within nfFilter must not be empty")
+
+
+def save_nf_filter(nf_filter, subscription_name):
+ """
+ Saves the nf_filter data request
+
+ Args:
+ nf_filter (dict) : network function filter to save
+ subscription_name (string) : subscription name to associate with nf filter.
+ """
+ logger.info(f'Saving nf filter for subscription request: {subscription_name}')
+ new_filter = NetworkFunctionFilterModel(subscription_name=subscription_name,
+ nf_names=nf_filter['nfNames'],
+ model_invariant_ids=nf_filter['modelInvariantIDs'],
+ model_version_ids=nf_filter['modelVersionIDs'],
+ model_names=nf_filter['modelNames'])
+ db.session.add(new_filter)
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 f7b682d4..2404c8b8 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/network_function.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/network_function.py
@@ -19,7 +19,7 @@
import re
from mod import logger, db
-from mod.api.db_models import NetworkFunctionModel
+from mod.api.db_models import NetworkFunctionModel, NetworkFunctionFilterModel
class NetworkFunction:
@@ -167,6 +167,20 @@ class NetworkFunctionFilter:
self.model_names = kwargs.get('modelNames')
self.regex_matcher = re.compile('|'.join(raw_regex for raw_regex in self.nf_names))
+ @staticmethod
+ def get_network_function_filter(sub_name):
+ """Gets the network function filter from the Database
+
+ Args:
+ sub_name (string): The name of the subscription
+
+ Returns:
+ NetworkFunctionFilter: Returns network function filter for sub_name
+ """
+ nf_filter = NetworkFunctionFilterModel.query.filter(
+ NetworkFunctionFilterModel.subscription_name == sub_name).one_or_none()
+ return NetworkFunctionFilter(**nf_filter.serialize())
+
def is_nf_in_filter(self, nf):
"""Match the nf fields against values in Subscription.nfFilter
diff --git a/components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py b/components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py
index 9c282ab7..a6fe38ad 100644
--- a/components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py
@@ -69,6 +69,9 @@ class AppConfig(metaclass=MetaSingleton):
self.streams_subscribes = app_config['config'].get('streams_subscribes')
# TODO: aaf_creds variable should be removed on code cleanup
self.aaf_creds = {'aaf_id': self.aaf_id, 'aaf_pass': self.aaf_pass}
+ # TODO: changes under discussion once resolve is confirmed will be removed
+ self.operational_policy_name = 'pmsh-operational-policy'
+ self.control_loop_name = 'pmsh-control-loop'
@staticmethod
def get_instance():
diff --git a/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py b/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py
index 6238a298..22654b82 100644
--- a/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py
@@ -78,7 +78,7 @@ class SubscriptionHandler:
self.app_conf.subscription.fileBasedGP,
self.app_conf.subscription.fileLocation,
self.app_conf.subscription.measurementGroups)
- nfs_in_aai = aai_client.get_pmsh_nfs_from_aai(self.app_conf)
+ nfs_in_aai = aai_client.get_pmsh_nfs_from_aai(self.app_conf, self.app_conf.nf_filter)
self.app_conf.subscription.create_subscription_on_nfs(nfs_in_aai, self.mr_pub,
self.app_conf)
self.app_conf.subscription.update_subscription_status()
diff --git a/components/pm-subscription-handler/tests/data/create_subscription_request.json b/components/pm-subscription-handler/tests/data/create_subscription_request.json
new file mode 100644
index 00000000..0b2f86f7
--- /dev/null
+++ b/components/pm-subscription-handler/tests/data/create_subscription_request.json
@@ -0,0 +1,60 @@
+{
+ "subscription": {
+ "subscriptionName": "ExtraPM-All-gNB-R2B",
+ "nfFilter": {
+ "nfNames": [
+ "^pnf.*",
+ "^vnf.*"
+ ],
+ "modelInvariantIDs": [
+ "8lk4578-d396-4efb-af02-6b83499b12f8",
+ "687kj45-d396-4efb-af02-6b83499b12f8"
+
+ ],
+ "modelVersionIDs": [
+ "e80a6ae3-cafd-4d24-850d-e14c084a5ca9"
+ ],
+ "modelNames": [
+ "PNF102"
+ ]
+ },
+ "measurementGroups": [
+ {
+ "measurementGroup": {
+ "measurementGroupName": "msrmt_grp_name",
+ "fileBasedGP":15,
+ "fileLocation":"pm.xml",
+ "administrativeState": "UNLOCKED",
+ "measurementTypes": [
+ {
+ "measurementType": "counter_a"
+ }
+ ],
+ "managedObjectDNsBasic": [
+ {
+ "DN": "string"
+ }
+ ]
+ }
+ },
+ {
+ "measurementGroup": {
+ "measurementGroupName": "msrmt_grp_name1",
+ "fileBasedGP":15,
+ "fileLocation":"pm.xml",
+ "administrativeState": "UNLOCKED",
+ "measurementTypes": [
+ {
+ "measurementType": "counter_a"
+ }
+ ],
+ "managedObjectDNsBasic": [
+ {
+ "DN": "string"
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/components/pm-subscription-handler/tests/services/test_measurement_group_service.py b/components/pm-subscription-handler/tests/services/test_measurement_group_service.py
new file mode 100644
index 00000000..e22b2303
--- /dev/null
+++ b/components/pm-subscription-handler/tests/services/test_measurement_group_service.py
@@ -0,0 +1,113 @@
+# ============LICENSE_START===================================================
+# Copyright (C) 2021 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
+from unittest.mock import patch
+from mod.network_function import NetworkFunction
+from mod.pmsh_config import AppConfig
+from mod import db
+from tests.base_setup import BaseClassSetup
+from mod.api.services import measurement_group_service
+from mod.api.db_models import MeasurementGroupModel, NfMeasureGroupRelationalModel
+from mod.subscription import SubNfState
+
+
+class MeasurementGroupServiceTestCase(BaseClassSetup):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ def setUp(self):
+ super().setUp()
+ with open(os.path.join(os.path.dirname(__file__),
+ '../data/create_subscription_request.json'), 'r') as data:
+ self.subscription_request = data.read()
+ with open(os.path.join(os.path.dirname(__file__), '../data/aai_xnfs.json'), 'r') as data:
+ self.aai_response_data = data.read()
+ with open(os.path.join(os.path.dirname(__file__), '../data/aai_model_info.json'),
+ 'r') as data:
+ self.good_model_info = data.read()
+
+ def tearDown(self):
+ super().tearDown()
+
+ @classmethod
+ def tearDownClass(cls):
+ super().tearDownClass()
+
+ @patch.object(AppConfig, 'publish_to_topic')
+ def test_publish_measurement_group(self, mock_mr):
+ super().setUpAppConf()
+ nf_1 = NetworkFunction(**{'nf_name': 'pnf_1',
+ 'ipv4_address': '204.120.0.15',
+ 'ipv6_address': '2001:db8:3333:4444:5555:6666:7777:8888',
+ 'model_invariant_id': 'some_id',
+ 'model_version_id': 'some_other_id'},
+ sdnc_model_name='blah',
+ sdnc_model_version=1.0, )
+ measurement_grp = MeasurementGroupModel('sub_publish',
+ 'msg_publish', 'UNLOCKED',
+ 15, 'pm.xml', [{"measurementType": "counter_a"}],
+ [{"DN": "string"}])
+ measurement_group_service.publish_measurement_group(
+ 'sub_publish', measurement_grp, nf_1)
+ mock_mr.assert_called_once_with('policy_pm_publisher',
+ {'nfName': 'pnf_1',
+ 'ipAddress': '2001:db8:3333:4444:5555:6666:7777:8888',
+ 'blueprintName': 'blah',
+ 'blueprintVersion': 1.0,
+ 'policyName': 'pmsh-operational-policy',
+ 'changeType': 'CREATE',
+ 'closedLoopControlName': 'pmsh-control-loop',
+ 'subscription':
+ {'administrativeState': 'UNLOCKED',
+ 'subscriptionName': 'sub_publish',
+ 'fileBasedGP': 15,
+ 'fileLocation': 'pm.xml',
+ 'measurementGroup':
+ {'measurementGroupName': 'msg_publish',
+ 'measurementTypes':
+ [{"measurementType": "counter_a"}],
+ 'managedObjectDNsBasic': [{"DN": "string"}]}}})
+
+ def test_save_measurement_group(self):
+ subscription = json.loads(self.subscription_request)['subscription']
+ mes_grp = subscription['measurementGroups'][0]['measurementGroup']
+ measurement_group_service.save_measurement_group(mes_grp, "ExtraPM-All-gNB-R2B")
+ db.session.commit()
+ measurement_grp = (MeasurementGroupModel.query.filter(
+ MeasurementGroupModel.measurement_group_name == mes_grp['measurementGroupName'],
+ MeasurementGroupModel.subscription_name == 'ExtraPM-All-gNB-R2B').one_or_none())
+ self.assertIsNotNone(measurement_grp)
+
+ def test_apply_nf_to_measgroup(self):
+ measurement_group_service.apply_nf_to_measgroup("pnf_test", "measure_grp_name")
+ db.session.commit()
+ measurement_grp_rel = (NfMeasureGroupRelationalModel.query.filter(
+ NfMeasureGroupRelationalModel.measurement_grp_name == 'measure_grp_name',
+ NfMeasureGroupRelationalModel.nf_name == 'pnf_test').one_or_none())
+ db.session.commit()
+ self.assertIsNotNone(measurement_grp_rel)
+ self.assertEqual(measurement_grp_rel.nf_measure_grp_status,
+ SubNfState.PENDING_CREATE.value)
+
+ 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)
+ return subscription
diff --git a/components/pm-subscription-handler/tests/services/test_nf_service.py b/components/pm-subscription-handler/tests/services/test_nf_service.py
new file mode 100644
index 00000000..6063a8bd
--- /dev/null
+++ b/components/pm-subscription-handler/tests/services/test_nf_service.py
@@ -0,0 +1,105 @@
+# ============LICENSE_START===================================================
+# Copyright (C) 2021 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
+from unittest.mock import patch
+from flask import current_app
+from mod.api.db_models import NetworkFunctionModel
+from mod import aai_client
+from tests.base_setup import BaseClassSetup
+from mod.api.services import nf_service
+from mod.network_function import NetworkFunctionFilter
+
+
+class NetworkFunctionServiceTestCase(BaseClassSetup):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ def setUp(self):
+ super().setUp()
+ current_app.config['app_config'] = self.app_conf
+ with open(os.path.join(os.path.dirname(__file__),
+ '../data/create_subscription_request.json'), 'r') as data:
+ self.subscription_request = data.read()
+ with open(os.path.join(os.path.dirname(__file__), '../data/aai_xnfs.json'), 'r') as data:
+ self.aai_response_data = data.read()
+ with open(os.path.join(os.path.dirname(__file__), '../data/aai_model_info.json'),
+ 'r') as data:
+ self.good_model_info = data.read()
+
+ def tearDown(self):
+ super().tearDown()
+
+ @classmethod
+ def tearDownClass(cls):
+ super().tearDownClass()
+
+ 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)
+ return subscription
+
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_capture_filtered_nfs(self, mock_filter_call, mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ subscription = json.loads(self.subscription_request)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ filtered_nfs = nf_service.capture_filtered_nfs(subscription["subscriptionName"])
+ self.assertEqual(len(filtered_nfs), 2)
+ self.assertEqual(filtered_nfs[0].nf_name, 'pnf201')
+ self.assertEqual(filtered_nfs[1].nf_name, 'pnf_33_ericsson')
+
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_create_nf_event_body(self, mock_filter_call, mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ subscription = json.loads(self.subscription_request)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ nf = nf_service.capture_filtered_nfs(subscription["subscriptionName"])[0]
+ event_body = nf_service.create_nf_event_body(nf, 'CREATE')
+ self.assertEqual(event_body['nfName'], nf.nf_name)
+ self.assertEqual(event_body['ipAddress'], nf.ipv6_address)
+ self.assertEqual(event_body['blueprintName'], nf.sdnc_model_name)
+ self.assertEqual(event_body['blueprintVersion'], nf.sdnc_model_version)
+ self.assertEqual(event_body['policyName'],
+ self.app_conf.operational_policy_name)
+ self.assertEqual(event_body['changeType'], 'CREATE')
+ self.assertEqual(event_body['closedLoopControlName'],
+ self.app_conf.control_loop_name)
+
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_save_nf_new_nf(self, mock_filter_call, mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ subscription = json.loads(self.subscription_request)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ nf = nf_service.capture_filtered_nfs(subscription["subscriptionName"])[0]
+ nf.nf_name = 'newnf1'
+ nf_service.save_nf(nf)
+ network_function = NetworkFunctionModel.query.filter(
+ NetworkFunctionModel.nf_name == nf.nf_name).one_or_none()
+ self.assertIsNotNone(network_function)
diff --git a/components/pm-subscription-handler/tests/services/test_subscription_service.py b/components/pm-subscription-handler/tests/services/test_subscription_service.py
new file mode 100644
index 00000000..dca6d871
--- /dev/null
+++ b/components/pm-subscription-handler/tests/services/test_subscription_service.py
@@ -0,0 +1,353 @@
+# ============LICENSE_START===================================================
+# Copyright (C) 2021 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 copy
+import json
+import os
+from unittest.mock import patch, MagicMock
+from mod.api.db_models import SubscriptionModel, MeasurementGroupModel, \
+ NfMeasureGroupRelationalModel, NetworkFunctionModel, NfSubRelationalModel, \
+ convert_db_string_to_list
+from mod.network_function import NetworkFunctionFilter
+from mod.subscription import SubNfState
+from mod import aai_client
+from mod.api.custom_exception import DuplicateDataException, InvalidDataException
+from mod.pmsh_config import AppConfig
+from tests.base_setup import BaseClassSetup
+from mod.api.services import subscription_service, nf_service, measurement_group_service
+
+
+class SubscriptionServiceTestCase(BaseClassSetup):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ def setUp(self):
+ super().setUp()
+ with open(os.path.join(os.path.dirname(__file__),
+ '../data/create_subscription_request.json'), 'r') as data:
+ self.subscription_request = data.read()
+ with open(os.path.join(os.path.dirname(__file__), '../data/aai_xnfs.json'), 'r') as data:
+ self.aai_response_data = data.read()
+ with open(os.path.join(os.path.dirname(__file__), '../data/aai_model_info.json'),
+ 'r') as data:
+ self.good_model_info = data.read()
+
+ def tearDown(self):
+ super().tearDown()
+
+ @classmethod
+ def tearDownClass(cls):
+ super().tearDownClass()
+
+ 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)
+ return subscription
+
+ @patch('mod.api.services.subscription_service.save_nf_filter', MagicMock(return_value=None))
+ @patch('mod.pmsh_config.AppConfig.publish_to_topic', MagicMock(return_value=None))
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_create_subscription(self, mock_filter_call, mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-new', 'msrmt_grp_name-new')
+ subscription = json.loads(subscription)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ subscription_service.create_subscription(subscription)
+ existing_subscription = (SubscriptionModel.query.filter(
+ SubscriptionModel.subscription_name == 'xtraPM-All-gNB-R2B-new').one_or_none())
+ self.assertIsNotNone(existing_subscription)
+ existing_measurement_grp = (MeasurementGroupModel.query.filter(
+ MeasurementGroupModel.measurement_group_name == 'msrmt_grp_name-new',
+ MeasurementGroupModel.subscription_name == 'xtraPM-All-gNB-R2B-new').one_or_none())
+ self.assertIsNotNone(existing_measurement_grp)
+ msr_grp_nf_rel = (NfMeasureGroupRelationalModel.query.filter(
+ NfMeasureGroupRelationalModel.measurement_grp_name == 'msrmt_grp_name-new')).all()
+ for pubslished_event in msr_grp_nf_rel:
+ self.assertEqual(pubslished_event.nf_measure_grp_status,
+ SubNfState.PENDING_CREATE.value)
+
+ @patch('mod.api.services.subscription_service.save_nf_filter', MagicMock(return_value=None))
+ @patch.object(AppConfig, 'publish_to_topic')
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_create_subscription_service_failed_rollback(self, mock_filter_call, mock_model_aai,
+ mock_aai, mock_publish):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ mock_publish.side_effect = InvalidDataException(["publish failed"])
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-fail1', 'msrmt_grp_name-fail1')
+ subscription = json.loads(subscription)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ try:
+ subscription_service.create_subscription(subscription)
+ except InvalidDataException as exception:
+ self.assertEqual(exception.invalid_message, ["AAI call failed"])
+
+ # Checking Rollback on publish failure with subscription and nfs captured
+ existing_subscription = (SubscriptionModel.query.filter(
+ SubscriptionModel.subscription_name == 'xtraPM-All-gNB-R2B-fail1').one_or_none())
+ self.assertIsNotNone(existing_subscription)
+ saved_nf_sub_rel = (NfSubRelationalModel.query.filter(
+ NfSubRelationalModel.subscription_name == 'xtraPM-All-gNB-R2B-fail1'))
+ self.assertIsNotNone(saved_nf_sub_rel)
+
+ @patch('mod.api.services.subscription_service.save_nf_filter', MagicMock(return_value=None))
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_create_subscription_service_on_aai_failed(self, mock_filter_call, mock_aai):
+ mock_aai.side_effect = InvalidDataException(["AAI call failed"])
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-fail', 'msrmt_grp_name-fail')
+ subscription = json.loads(subscription)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ try:
+ subscription_service.create_subscription(subscription)
+ except InvalidDataException as exception:
+ self.assertEqual(exception.invalid_message, ["AAI call failed"])
+
+ # Checking Rollback on AAI failure with subscription request saved
+ existing_subscription = (SubscriptionModel.query.filter(
+ SubscriptionModel.subscription_name == 'xtraPM-All-gNB-R2B-fail').one_or_none())
+ self.assertIsNotNone(existing_subscription)
+
+ def test_perform_validation_existing_sub(self):
+ try:
+ subscription_service.create_subscription(json.loads(self.subscription_request)
+ ['subscription'])
+ except DuplicateDataException as exception:
+ self.assertEqual(exception.duplicate_field_info,
+ "subscription Name: ExtraPM-All-gNB-R2B already exists.")
+
+ def test_missing_measurement_grp_name(self):
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-fail', '')
+ try:
+ subscription_service.create_subscription(json.loads(subscription)['subscription'])
+ except InvalidDataException as exception:
+ self.assertEqual(exception.invalid_message,
+ "No value provided for measurement group name")
+
+ def test_missing_administrative_state(self):
+ subscription = json.loads(self.create_test_subs('sub-fail', 'measurement_grp_name-fail'))
+ mes_grp = subscription['subscription']['measurementGroups'][0]['measurementGroup']
+ mes_grp['administrativeState'] = ''
+ try:
+ subscription_service.create_subscription(subscription['subscription'])
+ except InvalidDataException as exception:
+ self.assertEqual(exception.invalid_message,
+ "No value provided for administrative state")
+
+ @patch.object(subscription_service, 'save_nf_filter')
+ def test_save_subscription_request(self, mock_save_filter):
+ mock_save_filter.return_value = None
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-new1', 'msrmt_grp_name-new1')
+ subscription_service.save_subscription_request(json.loads(subscription)['subscription'])
+ existing_subscription = (SubscriptionModel.query.filter(
+ SubscriptionModel.subscription_name == 'xtraPM-All-gNB-R2B-new1').one_or_none())
+ self.assertIsNotNone(existing_subscription)
+ self.assertTrue(mock_save_filter.called)
+ existing_measurement_grp = (MeasurementGroupModel.query.filter(
+ MeasurementGroupModel.measurement_group_name == 'msrmt_grp_name-new1',
+ MeasurementGroupModel.subscription_name == 'xtraPM-All-gNB-R2B-new1').one_or_none())
+ self.assertIsNotNone(existing_measurement_grp)
+
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(measurement_group_service, 'apply_nf_to_measgroup')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_apply_measurement_grp_to_nfs(self, mock_filter_call, mock_apply_nf,
+ mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ mock_apply_nf.return_value = None
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-new2', 'msrmt_grp_name-new2')
+ subscription = json.loads(subscription)['subscription']
+ measurement_grp = MeasurementGroupModel('subscription_name_1',
+ 'msrmt_grp_name', 'UNLOCKED',
+ 15, 'pm.xml', [], [])
+ measurement2 = self.create_measurement_grp(measurement_grp, 'meas2', 'UNLOCKED')
+ measurement3 = self.create_measurement_grp(measurement_grp, 'meas3', 'LOCKED')
+ measurement_grps = [measurement_grp, measurement2, measurement3]
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ filtered_nfs = nf_service.capture_filtered_nfs(subscription["subscriptionName"])
+ subscription_service.apply_measurement_grp_to_nfs(filtered_nfs, measurement_grps)
+ # 2 measurement group with 2 nfs each contribute 4 calls
+ self.assertEqual(mock_apply_nf.call_count, 4)
+
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(AppConfig, 'publish_to_topic')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_publish_measurement_grp_to_nfs(self, mock_filter_call, mock_publish,
+ mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ mock_publish.return_value = None
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-new2', 'msrmt_grp_name-new2')
+ subscription = json.loads(subscription)['subscription']
+ measurement_grp = MeasurementGroupModel('subscription_name_1',
+ 'msrmt_grp_name', 'UNLOCKED',
+ 15, 'pm.xml', [], [])
+ measurement2 = self.create_measurement_grp(measurement_grp, 'meas2', 'UNLOCKED')
+ measurement3 = self.create_measurement_grp(measurement_grp, 'meas3', 'UNLOCKED')
+ measurement_grps = [measurement_grp, measurement2, measurement3]
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ filtered_nfs = nf_service.capture_filtered_nfs(subscription["subscriptionName"])
+ subscription_service.publish_measurement_grp_to_nfs(
+ subscription["subscriptionName"], filtered_nfs, measurement_grps)
+ # Two unlocked measurement Group published
+ self.assertEqual(mock_publish.call_count, 6)
+
+ patch.object(aai_client, 'get_aai_model_data')
+
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(AppConfig, 'publish_to_topic')
+ @patch('mod.logger.error')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_publish_measurement_grp_to_nfs_failed(self, mock_filter_call, mock_logger,
+ mock_publish, mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ mock_publish.side_effect = Exception('Publish failed')
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-new2', 'msrmt_grp_name-new2')
+ subscription = json.loads(subscription)['subscription']
+ measurement_grp = MeasurementGroupModel('subscription_name_1',
+ 'msrmt_grp_name', 'UNLOCKED',
+ 15, 'pm.xml', [], [])
+ measurement2 = self.create_measurement_grp(measurement_grp, 'meas2', 'UNLOCKED')
+ measurement3 = self.create_measurement_grp(measurement_grp, 'meas3', 'LOCKED')
+ measurement_grps = [measurement_grp, measurement2, measurement3]
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ filtered_nfs = nf_service.capture_filtered_nfs(subscription["subscriptionName"])
+ subscription_service.publish_measurement_grp_to_nfs(
+ subscription["subscriptionName"], filtered_nfs, measurement_grps)
+ mock_logger.assert_called_with('Publish event failed for nf name, measure_grp_name, '
+ 'sub_name: pnf_33_ericsson,meas2, xtraPM-All-gNB-R2B-new2 '
+ 'with error: Publish failed')
+
+ @patch('mod.api.services.subscription_service.save_nf_filter', MagicMock(return_value=None))
+ @patch('mod.pmsh_config.AppConfig.publish_to_topic', MagicMock(return_value=None))
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ @patch('mod.logger.error')
+ def test_create_subscription_all_locked_msg_grp(self, mock_logger, mock_filter_call,
+ mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-new2', 'msrmt_grp_name-new2')
+ subscription = subscription.replace('UNLOCKED', 'LOCKED')
+ subscription = json.loads(subscription)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ subscription_service.create_subscription(subscription)
+ mock_logger.assert_called_with('All measurement groups are locked for subscription: '
+ 'xtraPM-All-gNB-R2B-new2, please verify/check'
+ ' measurement groups.')
+
+ def create_measurement_grp(self, measurement, measurement_name, admin_status):
+ new_measurement = copy.deepcopy(measurement)
+ measurement.measurement_group_name = measurement_name
+ new_measurement.administrative_state = admin_status
+ return new_measurement
+
+ @patch('mod.api.services.subscription_service.save_nf_filter', MagicMock(return_value=None))
+ @patch('mod.pmsh_config.AppConfig.publish_to_topic', MagicMock(return_value=None))
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_save_filtered_nfs(self, mock_filter_call, mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-new', 'msrmt_grp_name-new')
+ subscription = json.loads(subscription)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ filtered_nfs = nf_service.capture_filtered_nfs(subscription["subscriptionName"])
+ subscription_service.save_filtered_nfs(filtered_nfs)
+
+ for nf in filtered_nfs:
+ saved_nf = (NetworkFunctionModel.query.filter(
+ NetworkFunctionModel.nf_name == nf.nf_name).one_or_none())
+ self.assertIsNotNone(saved_nf)
+
+ @patch('mod.api.services.subscription_service.save_nf_filter', MagicMock(return_value=None))
+ @patch('mod.pmsh_config.AppConfig.publish_to_topic', MagicMock(return_value=None))
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_apply_subscription_to_nfs(self, mock_filter_call, mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ subscription = json.loads(self.subscription_request)['subscription']
+ mock_filter_call.return_value = NetworkFunctionFilter(**subscription["nfFilter"])
+ filtered_nfs = nf_service.capture_filtered_nfs(subscription["subscriptionName"])
+ subscription_service.apply_subscription_to_nfs(filtered_nfs, 'xtraPM-All-gNB-R2B')
+
+ for nf in filtered_nfs:
+ saved_nf_sub_rel = (NfSubRelationalModel.query.filter(
+ NfSubRelationalModel.subscription_name == 'xtraPM-All-gNB-R2B',
+ NfSubRelationalModel.nf_name == nf.nf_name).one_or_none())
+ self.assertIsNotNone(saved_nf_sub_rel)
+
+ def test_check_missing_data_sub_name_missing(self):
+ subscription = self.create_test_subs('', 'msrmt_grp_name-new')
+ subscription = json.loads(subscription)['subscription']
+ try:
+ subscription_service.check_missing_data(subscription)
+ except InvalidDataException as invalidEx:
+ self.assertEqual(invalidEx.invalid_message, "No value provided in subscription name")
+
+ def test_check_missing_data_admin_status_missing(self):
+ subscription = self.subscription_request.replace(
+ 'UNLOCKED', '')
+ subscription = json.loads(subscription)['subscription']
+ try:
+ subscription_service.check_missing_data(subscription)
+ except InvalidDataException as invalidEx:
+ self.assertEqual(invalidEx.invalid_message,
+ "No value provided for administrative state")
+
+ def test_check_missing_data_msr_grp_name(self):
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-new', '')
+ subscription = json.loads(subscription)['subscription']
+ try:
+ subscription_service.check_missing_data(subscription)
+ except InvalidDataException as invalidEx:
+ self.assertEqual(invalidEx.invalid_message,
+ "No value provided for measurement group name")
+
+ def test_validate_nf_filter_with_no_filter_values(self):
+ nfFilter = '{"nfNames": [],"modelInvariantIDs": [], ' \
+ '"modelVersionIDs": [],"modelNames": []}'
+ try:
+ subscription_service.validate_nf_filter(json.loads(nfFilter))
+ except InvalidDataException as invalidEx:
+ self.assertEqual(invalidEx.invalid_message,
+ "At least one filter within nfFilter must not be empty")
+
+ def test_db_string_to_list(self):
+ db_string = '{"*pnf","*vnf"}'
+ db_array = convert_db_string_to_list(db_string)
+ self.assertEqual(len(db_array), 2)
+
+ def test_db_string_to_list_empty(self):
+ db_string = '{}'
+ db_array = convert_db_string_to_list(db_string)
+ self.assertEqual(len(db_array), 0)
diff --git a/components/pm-subscription-handler/tests/test_aai_service.py b/components/pm-subscription-handler/tests/test_aai_service.py
index 27c0f402..97f400c1 100644
--- a/components/pm-subscription-handler/tests/test_aai_service.py
+++ b/components/pm-subscription-handler/tests/test_aai_service.py
@@ -1,5 +1,5 @@
# ============LICENSE_START===================================================
-# Copyright (C) 2019-2020 Nordix Foundation.
+# Copyright (C) 2019-2021 Nordix Foundation.
# ============================================================================
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ class AaiClientTestCase(BaseClassSetup):
mock_get_session.return_value.status_code = 200
mock_get_session.return_value.text = self.good_model_info
mock_get_sdnc_params.return_value = True
- xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf)
+ xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf, self.app_conf.nf_filter)
self.assertEqual(self.app_conf.subscription.subscriptionName, 'ExtraPM-All-gNB-R2B')
self.assertEqual(self.app_conf.subscription.administrativeState, 'UNLOCKED')
self.assertEqual(len(xnfs), 3)
@@ -67,7 +67,7 @@ class AaiClientTestCase(BaseClassSetup):
mock_session.return_value.status_code = 404
with mock.patch('mod.aai_client._get_all_aai_nf_data', return_value=None):
with self.assertRaises(RuntimeError):
- aai_client.get_pmsh_nfs_from_aai(self.app_conf)
+ aai_client.get_pmsh_nfs_from_aai(self.app_conf, self.app_conf.nf_filter)
@responses.activate
def test_aai_client_get_all_aai_xnf_data_not_found(self):
diff --git a/components/pm-subscription-handler/tests/test_controller.py b/components/pm-subscription-handler/tests/test_controller.py
index c38cd976..a3a28163 100755
--- a/components/pm-subscription-handler/tests/test_controller.py
+++ b/components/pm-subscription-handler/tests/test_controller.py
@@ -1,5 +1,5 @@
# ============LICENSE_START===================================================
-# Copyright (C) 2019-2020 Nordix Foundation.
+# Copyright (C) 2019-2021 Nordix Foundation.
# ============================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,14 +17,15 @@
# ============LICENSE_END=====================================================
import json
import os
-from unittest.mock import patch
-
+from unittest.mock import patch, MagicMock
import responses
from requests import Session
-
from mod import aai_client
-from mod.api.controller import status, get_all_sub_to_nf_relations
+from mod.api.controller import status, get_all_sub_to_nf_relations, post_subscription
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
class ControllerTestCase(BaseClassSetup):
@@ -35,10 +36,14 @@ class ControllerTestCase(BaseClassSetup):
def setUp(self):
super().setUp()
+ super().setUpAppConf()
with open(os.path.join(os.path.dirname(__file__), 'data/aai_xnfs.json'), 'r') as data:
self.aai_response_data = data.read()
with open(os.path.join(os.path.dirname(__file__), 'data/aai_model_info.json'), 'r') as data:
self.good_model_info = data.read()
+ with open(os.path.join(os.path.dirname(__file__),
+ 'data/create_subscription_request.json'), 'r') as data:
+ self.subscription_request = data.read()
def tearDown(self):
super().tearDown()
@@ -62,10 +67,65 @@ class ControllerTestCase(BaseClassSetup):
'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.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)
+ return subscription
+
+ @patch('mod.api.services.subscription_service.save_nf_filter', MagicMock(return_value=None))
+ @patch('mod.pmsh_config.AppConfig.publish_to_topic', MagicMock(return_value=None))
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ @patch.object(NetworkFunctionFilter, 'get_network_function_filter')
+ def test_post_subscription(self, mock_filter_call, mock_model_aai, mock_aai):
+ mock_aai.return_value = json.loads(self.aai_response_data)
+ mock_model_aai.return_value = json.loads(self.good_model_info)
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-post', 'msrmt_grp_name-post')
+ subscription = json.loads(subscription)
+ mock_filter_call.return_value = NetworkFunctionFilter(
+ **subscription['subscription']["nfFilter"])
+ sub_name = subscription['subscription']['subscriptionName']
+ mes_grp = subscription['subscription']['measurementGroups'][0]['measurementGroup']
+ mes_grp_name = mes_grp['measurementGroupName']
+ response = post_subscription(subscription)
+ subscription = (SubscriptionModel.query.filter(
+ SubscriptionModel.subscription_name == sub_name).one_or_none())
+ self.assertIsNotNone(subscription)
+ msr_grp_nf_rel = (NfMeasureGroupRelationalModel.query.filter(
+ NfMeasureGroupRelationalModel.measurement_grp_name == mes_grp_name)).all()
+ for published_event in msr_grp_nf_rel:
+ self.assertEqual(published_event.nf_measure_grp_status,
+ SubNfState.PENDING_CREATE.value)
+ self.assertEqual(response[1], 201)
+
+ def test_post_subscription_duplicate_sub(self):
+ # Posting the same subscription request stored in previous test to get duplicate response
+ response = post_subscription(json.loads(self.subscription_request))
+ self.assertEqual(response[1], 409)
+ self.assertEqual(response[0], 'subscription Name: ExtraPM-All-gNB-R2B already exists.')
+
+ def test_post_subscription_invalid_filter(self):
+ subscription = self.create_test_subs('xtraPM-All-gNB-R2B-invalid', 'msrmt_grp_name-invalid')
+ subscription = json.loads(subscription)
+ subscription['subscription']['nfFilter']['nfNames'] = []
+ subscription['subscription']['nfFilter']['modelInvariantIDs'] = []
+ subscription['subscription']['nfFilter']['modelVersionIDs'] = []
+ subscription['subscription']['nfFilter']['modelNames'] = []
+ response = post_subscription(subscription)
+ self.assertEqual(response[1], 400)
+ self.assertEqual(response[0], 'At least one filter within nfFilter must not be empty')
+
+ def test_post_subscription_missing(self):
+ subscription = json.loads(self.subscription_request)
+ subscription['subscription']['subscriptionName'] = ''
+ response = post_subscription(subscription)
+ self.assertEqual(response[1], 400)
+ self.assertEqual(response[0], 'No value provided in subscription name')
diff --git a/components/pm-subscription-handler/tests/test_subscription.py b/components/pm-subscription-handler/tests/test_subscription.py
index 01c573e3..3921aa9b 100755
--- a/components/pm-subscription-handler/tests/test_subscription.py
+++ b/components/pm-subscription-handler/tests/test_subscription.py
@@ -48,7 +48,7 @@ class SubscriptionTest(BaseClassSetup):
self.mock_mr_sub = Mock()
self.mock_mr_pub = Mock()
self.app_conf.subscription.create()
- self.xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf)
+ self.xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf, self.app_conf.nf_filter)
self.sub_model = self.app_conf.subscription.get()
def tearDown(self):