summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoregernug <gerard.nugent@est.tech>2022-02-23 13:38:34 +0000
committeregernug <gerard.nugent@est.tech>2022-03-09 12:25:35 +0000
commit36987b3ac6eccb475e9976b95a08683a86682cdc (patch)
treeff0b5a0296ef94450a04ae1b5b1701e052e27777
parent6c9c8cc2a1a289ef0b4172d387376d0367549fe3 (diff)
[PMSH] Create Measurement Group API
Creates Measurement Group for an associated Subscription POST: /subscription/{subscription_name}/measurementGroups/{measurement_group_name} Measurement Group structure: { "measurementGroup": { "measurementGroupName": "string", "fileBasedGP": 0, "fileLocation": "string", "administrativeState": "LOCKED", "measurementTypes": [ { "measurementType": "string" } ], "managedObjectDNsBasic": [ { "DN": "string" } ] } } Returns: Success: 201 Invalid Data: 400 when measurement_group_name in URI and body do not match Not Found: 404 when subscription does not exist to associate measurement group to Duplicate Data: Measurement group with that name already exists Error raised for any server failure Issue-ID: DCAEGEN2-2920 Signed-off-by: egernug <gerard.nugent@est.tech> Change-Id: I812c5a891e9bed5433000f5da24e2667bf9a5d65
-rwxr-xr-xcomponents/pm-subscription-handler/Changelog.md2
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/api/controller.py56
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml31
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py63
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py14
-rw-r--r--components/pm-subscription-handler/tests/services/test_measurement_group_service.py51
-rwxr-xr-xcomponents/pm-subscription-handler/tests/test_controller.py37
7 files changed, 213 insertions, 41 deletions
diff --git a/components/pm-subscription-handler/Changelog.md b/components/pm-subscription-handler/Changelog.md
index 010fb1e5..8225ec82 100755
--- a/components/pm-subscription-handler/Changelog.md
+++ b/components/pm-subscription-handler/Changelog.md
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed
* Update Filter API (DCAEGEN2-2922)
* Cleaning up old App Config, subscription handler and it's subsequent calls (DCAEGEN2-3085)
+* Create Measurement Group API (DCAEGEN2-2920)
+
## [2.1.1]
### 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 2e811c28..1aad0519 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
@@ -22,7 +22,6 @@ from mod.api.services import subscription_service, measurement_group_service
from connexion import NoContent
from mod.api.custom_exception import InvalidDataException, DuplicateDataException, \
DataConflictException
-from mod.api.services.measurement_group_service import AdministrativeState
def status():
@@ -69,6 +68,58 @@ def post_subscription(body):
return response
+def post_meas_group(subscription_name, measurement_group_name, body):
+ """
+ Creates a measurement group for a subscription
+
+ Args:
+ subscription_name (String): Name of the subscription.
+ measurement_group_name (String): Name of the measurement group
+ body (dict): measurement group request body to save.
+
+ Returns:
+ Success : NoContent, 201
+ Invalid Data: Invalid message, 400
+ Not Found: Subscription no found, 404
+ Duplicate Data : Duplicate field detail, 409
+
+ Raises:
+ Error: If anything fails in the server.
+ """
+ response = NoContent, HTTPStatus.CREATED.value
+ try:
+ subscription = subscription_service.query_subscription_by_name(subscription_name)
+ if subscription is not None:
+ try:
+ measurement_group_service.create_measurement_group(subscription,
+ measurement_group_name, body)
+ except DuplicateDataException as e:
+ logger.error(f'Failed to create measurement group for '
+ f'{subscription_name} due to duplicate data: {e}',
+ exc_info=True)
+ response = e.args[0], HTTPStatus.CONFLICT.value
+ except InvalidDataException as e:
+ logger.error(f'Failed to create measurement group for '
+ f'{subscription_name} due to invalid data: {e}',
+ exc_info=True)
+ response = e.args[0], HTTPStatus.BAD_REQUEST.value
+ except Exception as e:
+ logger.error(f'Failed to create measurement group due to exception {e}')
+ response = e.args[0], HTTPStatus.INTERNAL_SERVER_ERROR.value
+ else:
+ logger.error('queried subscription was un successful with the name: '
+ f'{subscription_name}')
+ return {'error': 'Subscription was not defined with the name : '
+ f'{subscription_name}'}, HTTPStatus.NOT_FOUND.value
+
+ except Exception as exception:
+ logger.error(f'While querying the subscription with name: {subscription_name}, '
+ f'it occurred the following exception "{exception}"')
+ return {'error': 'Request was not processed due to Exception : '
+ f'{exception}'}, HTTPStatus.INTERNAL_SERVER_ERROR.value
+ return response
+
+
def get_subscription_by_name(subscription_name):
"""
Retrieves subscription based on the name
@@ -169,7 +220,8 @@ def delete_meas_group_by_name(subscription_name, measurement_group_name):
measurement_group_administrative_status = \
measurement_group_service.query_get_meas_group_admin_status(subscription_name,
measurement_group_name)
- if measurement_group_administrative_status == AdministrativeState.LOCKED.value:
+ if measurement_group_administrative_status == \
+ measurement_group_service.AdministrativeState.LOCKED.value:
if measurement_group_service.query_to_delete_meas_group(subscription_name,
measurement_group_name) == 1:
return None, HTTPStatus.NO_CONTENT
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 1f24f171..258ca51e 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
@@ -160,6 +160,37 @@ paths:
description: Exception occurred while querying database
/subscription/{subscription_name}/measurementGroups/{measurement_group_name}:
+ post:
+ description: Create a measurement group for a given subscription
+ operationId: mod.api.controller.post_meas_group
+ tags:
+ - "Measurement Group"
+ parameters:
+ - name : subscription_name
+ in: path
+ required: true
+ description: Name of the subscription
+ type: string
+ - name: measurement_group_name
+ in: path
+ required: true
+ description: Name of the measurement group
+ type: string
+ - in: "body"
+ name: "body"
+ required: true
+ schema:
+ $ref: "#/definitions/measurementGroup"
+ responses:
+ 201:
+ description: Successfully created measurement group
+ 404:
+ description: Subscription with the specified name not found
+ 409:
+ description: Duplicate data
+ 500:
+ description: Internal server error
+
get:
description: Get the measurement group and associated network functions
from PMSH by using sub name and meas group name
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
index 145a492c..29c4a27a 100644
--- 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
@@ -16,9 +16,10 @@
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=====================================================
-from mod.api.custom_exception import InvalidDataException, DataConflictException
-from mod.api.db_models import MeasurementGroupModel, NfMeasureGroupRelationalModel, \
- SubscriptionModel
+from mod.api.custom_exception import InvalidDataException, \
+ DataConflictException, DuplicateDataException
+from mod.api.db_models import MeasurementGroupModel, \
+ NfMeasureGroupRelationalModel, SubscriptionModel
from mod import db, logger
from mod.api.services import nf_service, subscription_service
from mod.network_function import NetworkFunction
@@ -59,6 +60,58 @@ mg_nf_states = {
}
+def create_measurement_group(subscription, measurement_group_name, body):
+ """
+ Creates a measurement group for a subscription
+
+ Args:
+ subscription (SubscriptionModel): Subscription.
+ measurement_group_name (String): Name of MeasGroup
+ body (dict): measurement group request body to save.
+
+ """
+ logger.info(f'Initiating create measurement group for: {measurement_group_name}')
+ check_duplication(subscription.subscription_name, measurement_group_name)
+ check_measurement_group_names_comply(measurement_group_name, body)
+ new_mg = [save_measurement_group(body, subscription.subscription_name)]
+ if body["administrativeState"] == AdministrativeState.UNLOCKED.value:
+ filtered_nfs = nf_service.capture_filtered_nfs(subscription.subscription_name)
+ subscription_service.add_new_filtered_nfs(filtered_nfs, new_mg, subscription)
+ else:
+ logger.info(f'Measurement Group {measurement_group_name} is not in an unlocked state')
+
+
+def check_measurement_group_names_comply(measurement_group_name, measurement_group):
+ """
+ Check if measurement_group_name matches the name in the URI
+
+ Args:
+ measurement_group_name (String): Name of the measurement group
+ measurement_group (dict): Measurement Group
+
+ """
+ if measurement_group_name != measurement_group["measurementGroupName"]:
+ logger.info(f'Changing measurement_group_name in body to {measurement_group_name}')
+ measurement_group["measurementGroupName"] = measurement_group_name
+
+
+def check_duplication(subscription_name, measurement_group_name):
+ """
+ Check if measurement group exists already
+
+ Args:
+ measurement_group_name (String): Name of the measurement group
+ subscription_name (string) : subscription name to associate with measurement group.
+
+ Raises:
+ DuplicateDataException: exception containing the detail on duplicate data field.
+ """
+ logger.info(f"Checking that measurement group {measurement_group_name} does not exist")
+ if query_meas_group_by_name(subscription_name, measurement_group_name):
+ raise DuplicateDataException(f'Measurement Group Name: '
+ f'{measurement_group_name} already exists.')
+
+
def save_measurement_group(measurement_group, subscription_name):
"""
Saves the measurement_group data request
@@ -165,10 +218,6 @@ def delete_nf_to_measurement_group(nf_name, measurement_group_name, status):
NfMeasureGroupRelationalModel.nf_name == nf_name).one_or_none()
db.session.delete(nf_measurement_group_rel)
db.session.commit()
- nf_relations = NfMeasureGroupRelationalModel.query.filter(
- NfMeasureGroupRelationalModel.nf_name == nf_name).all()
- if not nf_relations:
- NetworkFunction.delete(nf_name=nf_name)
except Exception as e:
logger.error(f'Failed to delete nf: {nf_name} for measurement group: '
f'{measurement_group_name} due to: {e}')
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 99b72dfb..6216a803 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,7 +23,6 @@ from mod.api.db_models import SubscriptionModel, NfSubRelationalModel, \
from mod.api.services import measurement_group_service, nf_service
from mod.api.custom_exception import InvalidDataException, DuplicateDataException, \
DataConflictException
-from mod.api.services.measurement_group_service import MgNfState, AdministrativeState
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import joinedload
@@ -155,7 +154,7 @@ def apply_measurement_grp_to_nfs(filtered_nfs, unlocked_mgs):
f'{measurement_group.measurement_group_name}')
measurement_group_service.apply_nf_status_to_measurement_group(
nf.nf_name, measurement_group.measurement_group_name,
- MgNfState.PENDING_CREATE.value)
+ measurement_group_service.MgNfState.PENDING_CREATE.value)
def check_missing_data(subscription):
@@ -261,7 +260,7 @@ def save_subscription(subscription):
SubscriptionModel(subscription_name=subscription["subscriptionName"],
operational_policy_name=subscription["operationalPolicyName"],
control_loop_name=control_loop_name,
- status=AdministrativeState.LOCKED.value)
+ status=measurement_group_service.AdministrativeState.LOCKED.value)
db.session.add(subscription_model)
return subscription_model
@@ -450,7 +449,7 @@ def get_unlocked_measurement_grps(sub_model):
unlocked_mgs = []
for measurement_group in sub_model.measurement_groups:
if measurement_group.administrative_state \
- == AdministrativeState.UNLOCKED.value:
+ == measurement_group_service.AdministrativeState.UNLOCKED.value:
unlocked_mgs.append(measurement_group)
else:
logger.info(f'No nfs added as measure_grp_name: '
@@ -474,7 +473,8 @@ def delete_filtered_nfs(del_nfs, sub_model, unlocked_mgs):
for mg in unlocked_mgs:
MeasurementGroupModel.query.filter(
MeasurementGroupModel.measurement_group_name == mg.measurement_group_name) \
- .update({MeasurementGroupModel.administrative_state: AdministrativeState.
+ .update({MeasurementGroupModel.administrative_state:
+ measurement_group_service.AdministrativeState.
FILTERING.value}, synchronize_session='evaluate')
db.session.commit()
nf_meas_relations = NfMeasureGroupRelationalModel.query.filter(
@@ -515,7 +515,9 @@ def validate_sub_mgs_state(sub_model):
DataConflictException: contains details on conflicting status in measurement group
"""
mg_names_processing = [mg for mg in sub_model.measurement_groups
- if mg.administrative_state in [AdministrativeState.FILTERING.value,
+ if mg.administrative_state in [measurement_group_service.
+ AdministrativeState.FILTERING.value,
+ measurement_group_service.
AdministrativeState.LOCKING.value]]
if mg_names_processing:
raise DataConflictException('Cannot update filter as subscription: '
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
index 25ab2581..7190069e 100644
--- a/components/pm-subscription-handler/tests/services/test_measurement_group_service.py
+++ b/components/pm-subscription-handler/tests/services/test_measurement_group_service.py
@@ -20,7 +20,8 @@ import json
import os
from unittest.mock import patch
-from mod.api.custom_exception import InvalidDataException, DataConflictException
+from mod.api.custom_exception import InvalidDataException, \
+ DataConflictException, DuplicateDataException
from mod.api.services.measurement_group_service import MgNfState
from mod.network_function import NetworkFunction, NetworkFunctionFilter
from mod.pmsh_config import AppConfig
@@ -146,13 +147,9 @@ class MeasurementGroupServiceTestCase(BaseClassSetup):
NfMeasureGroupRelationalModel.measurement_grp_name == 'measure_grp_name2',
NfMeasureGroupRelationalModel.nf_name == 'pnf_test2').one_or_none())
self.assertIsNone(measurement_grp_rel)
- network_function = (NetworkFunctionModel.query.filter(
- NetworkFunctionModel.nf_name == 'pnf_test2').one_or_none())
- self.assertIsNone(network_function)
@patch.object(NetworkFunction, 'delete')
- @patch('mod.logger.error')
- def test_delete_nf_to_measurement_group_failure(self, mock_logger, nf_delete_func):
+ def test_delete_nf_to_measurement_group_failure(self, nf_delete_func):
nf = NetworkFunction(nf_name='pnf_test2')
nf_service.save_nf(nf)
db.session.commit()
@@ -165,11 +162,6 @@ class MeasurementGroupServiceTestCase(BaseClassSetup):
NfMeasureGroupRelationalModel.measurement_grp_name == 'measure_grp_name2',
NfMeasureGroupRelationalModel.nf_name == 'pnf_test2').one_or_none())
self.assertIsNone(measurement_grp_rel)
- network_function = (NetworkFunctionModel.query.filter(
- NetworkFunctionModel.nf_name == 'pnf_test2').one_or_none())
- self.assertIsNotNone(network_function)
- mock_logger.assert_called_with('Failed to delete nf: pnf_test2 for '
- 'measurement group: measure_grp_name2 due to: delete failed')
@patch.object(db.session, 'commit')
@patch('mod.logger.error')
@@ -311,9 +303,6 @@ class MeasurementGroupServiceTestCase(BaseClassSetup):
NfMeasureGroupRelationalModel.measurement_grp_name == 'MG2',
NfMeasureGroupRelationalModel.nf_name == 'pnf_101').one_or_none())
self.assertIsNone(measurement_grp_rel)
- network_function = (NetworkFunctionModel.query.filter(
- NetworkFunctionModel.nf_name == 'pnf_101').one_or_none())
- self.assertIsNone(network_function)
meas_grp = measurement_group_service.query_meas_group_by_name('sub', 'MG2')
self.assertEqual(meas_grp.subscription_name, 'sub')
self.assertEqual(meas_grp.measurement_group_name, 'MG2')
@@ -337,13 +326,28 @@ class MeasurementGroupServiceTestCase(BaseClassSetup):
NfMeasureGroupRelationalModel.measurement_grp_name == 'MG2',
NfMeasureGroupRelationalModel.nf_name == 'pnf_101').one_or_none())
self.assertIsNone(measurement_grp_rel)
- network_function = (NetworkFunctionModel.query.filter(
- NetworkFunctionModel.nf_name == 'pnf_101').one_or_none())
- self.assertIsNone(network_function)
- meas_grp = measurement_group_service.query_meas_group_by_name('sub', 'MG2')
- self.assertEqual(meas_grp.subscription_name, 'sub')
- self.assertEqual(meas_grp.measurement_group_name, 'MG2')
- self.assertEqual(meas_grp.administrative_state, 'LOCKING')
+
+ def test_check_duplication_exception(self):
+ sub = create_subscription_data('sub')
+ db.session.add(sub)
+ try:
+ measurement_group_service.check_duplication('sub', 'MG1')
+ except DuplicateDataException as e:
+ self.assertEqual(e.args[0], 'Measurement Group Name: MG1 already exists.')
+
+ def test_check_measurement_group_names_comply(self):
+ mg = {'subscription_name': 'sub',
+ 'measurementGroupName': 'MG2',
+ 'administrativeState': 'UNLOCKED',
+ 'fileBasedGP': 15,
+ 'fileLocation': '/pm/pm.xml',
+ 'measurementTypes': '[{ "measurementType": "countera" }, '
+ '{ "measurementType": "counterb" }]',
+ 'managedObjectDNsBasic': '[{ "DN":"dna"},{"DN":"dnb"}]'}
+ try:
+ measurement_group_service.check_measurement_group_names_comply('MG1', mg)
+ except InvalidDataException as e:
+ self.assertEqual(e.args[0], 'Measurement Group Name in body does not match with URI')
def test_filter_nf_to_meas_grp_for_delete(self):
sub = create_subscription_data('sub')
@@ -355,13 +359,12 @@ class MeasurementGroupServiceTestCase(BaseClassSetup):
db.session.commit()
measurement_group_service.filter_nf_to_meas_grp(
"pnf_test2", "MG2", MgNfState.DELETED.value)
+ measurement_group_service.filter_nf_to_meas_grp("pnf_test2", "MG2",
+ MgNfState.DELETED.value)
measurement_grp_rel = (NfMeasureGroupRelationalModel.query.filter(
NfMeasureGroupRelationalModel.measurement_grp_name == 'MG2',
NfMeasureGroupRelationalModel.nf_name == 'pnf_test2').one_or_none())
self.assertIsNone(measurement_grp_rel)
- network_function = (NetworkFunctionModel.query.filter(
- NetworkFunctionModel.nf_name == 'pnf_test2').one_or_none())
- self.assertIsNone(network_function)
meas_grp = measurement_group_service.query_meas_group_by_name('sub', 'MG2')
self.assertEqual(meas_grp.subscription_name, 'sub')
self.assertEqual(meas_grp.measurement_group_name, 'MG2')
diff --git a/components/pm-subscription-handler/tests/test_controller.py b/components/pm-subscription-handler/tests/test_controller.py
index 797666d9..07c17be7 100755
--- a/components/pm-subscription-handler/tests/test_controller.py
+++ b/components/pm-subscription-handler/tests/test_controller.py
@@ -23,11 +23,12 @@ from http import HTTPStatus
from mod import aai_client, db
from mod.api.controller import status, post_subscription, get_subscription_by_name, \
get_subscriptions, get_meas_group_with_nfs, delete_subscription_by_name, update_admin_state, \
- delete_meas_group_by_name, put_nf_filter
+ delete_meas_group_by_name, post_meas_group, put_nf_filter
from mod.api.services.measurement_group_service import query_meas_group_by_name
from tests.base_setup import BaseClassSetup
from mod.api.custom_exception import InvalidDataException, DataConflictException
-from mod.api.db_models import SubscriptionModel, NfMeasureGroupRelationalModel
+from mod.api.db_models import SubscriptionModel, \
+ NfMeasureGroupRelationalModel, MeasurementGroupModel
from mod.network_function import NetworkFunctionFilter
from tests.base_setup import create_subscription_data, create_multiple_subscription_data, \
create_multiple_network_function_data
@@ -197,6 +198,38 @@ class ControllerTestCase(BaseClassSetup):
error, status_code = get_meas_group_with_nfs('sub1', 'MG1')
self.assertEqual(status_code, HTTPStatus.INTERNAL_SERVER_ERROR.value)
+ @patch.object(aai_client, '_get_all_aai_nf_data')
+ @patch.object(aai_client, 'get_aai_model_data')
+ def test_post_meas_group(self, 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_data = create_subscription_data('Post_MG')
+ measurement_grp = {'subscription_name': 'sub',
+ 'measurementGroupName': 'MG2',
+ 'administrativeState': 'UNLOCKED',
+ 'fileBasedGP': 15,
+ 'fileLocation': '/pm/pm.xml',
+ 'measurementTypes': '[{ "measurementType": "countera" }, '
+ '{ "measurementType": "counterb" }]',
+ 'managedObjectDNsBasic': '[{ "DN":"dna"},{"DN":"dnb"}]'}
+ db.session.add(subscription_data)
+ db.session.commit()
+ db.session.remove()
+ _, status_code = post_meas_group('Post_MG', 'MG3', measurement_grp)
+ self.assertEqual(status_code, 201)
+
+ def test_post_meas_group_with_duplicate(self):
+ subscription_data = create_subscription_data('Post_MG')
+ measurement_grp = MeasurementGroupModel('Post_MG', 'MG1', 'UNLOCKED', 15, '/pm/pm.xml',
+ '[{ "measurementType": "countera" }, '
+ '{ "measurementType": "counterb" }]',
+ '[{ "DN":"dna"},{"DN":"dnb"}]')
+ db.session.add(subscription_data)
+ db.session.commit()
+ db.session.remove()
+ _, status_code = post_meas_group('Post_MG', 'MG1', measurement_grp)
+ self.assertEqual(status_code, 409)
+
def test_delete_sub_when_state_unlocked(self):
subscription_unlocked_data = create_subscription_data('MG_unlocked')
subscription_unlocked_data.measurement_groups[0].measurement_group_name = 'unlock'