summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rwxr-xr-xcomponents/pm-subscription-handler/Changelog.md4
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py143
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/api/controller.py37
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/mod/api/db_models.py78
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml85
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py17
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py2
-rw-r--r--components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py12
-rwxr-xr-xcomponents/pm-subscription-handler/pmsh_service/pmsh_service_main.py9
-rwxr-xr-xcomponents/pm-subscription-handler/tests/base_setup.py23
-rwxr-xr-xcomponents/pm-subscription-handler/tests/data/mr_aai_events.json3
-rwxr-xr-xcomponents/pm-subscription-handler/tests/test_aai_event_handler.py119
-rwxr-xr-xcomponents/pm-subscription-handler/tests/test_controller.py42
-rw-r--r--components/pm-subscription-handler/tests/test_subscription_handler.py3
-rw-r--r--components/pm-subscription-handler/tox.ini2
15 files changed, 471 insertions, 108 deletions
diff --git a/components/pm-subscription-handler/Changelog.md b/components/pm-subscription-handler/Changelog.md
index b3468736..db096b11 100755
--- a/components/pm-subscription-handler/Changelog.md
+++ b/components/pm-subscription-handler/Changelog.md
@@ -13,8 +13,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* Added 2 new attributes to the subscription model (DCAEGEN2-2913)
* Read subscription API by using subscription name (DCAEGEN2-2818)
* Read All subscriptions API (DCAEGEN2-2847)
-* PMSH Response Event Handler Integration (DCAEGEN2-2915)
+* Response Event Handler Integration (DCAEGEN2-2915)
* Updated to get NFs list when requesting a specific subscription (DCAEGEN2-2992)
+* AAI Event handler changes with new subscription format (DCAEGEN2-2912)
+* Read NFS associated with MG by using MGName and subName(DCAEGEN2-2993)
## [1.3.2]
### Changed
diff --git a/components/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py b/components/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py
index a00c164a..fe4166e7 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py
@@ -15,12 +15,14 @@
#
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=====================================================
-
import json
from enum import Enum
-
-from mod import logger
+from mod import logger, db
from mod.network_function import NetworkFunction
+from mod.pmsh_config import AppConfig, MRTopic
+from mod.api.db_models import SubscriptionModel
+from mod.network_function import NetworkFunctionFilter
+from mod.api.services import subscription_service
class XNFType(Enum):
@@ -33,53 +35,98 @@ class AAIEvent(Enum):
UPDATE = 'UPDATE'
-def process_aai_events(mr_sub, mr_pub, app, app_conf):
+def is_pnf_xnf(entity_type):
"""
- Processes AAI UPDATE events for each filtered xNFs where orchestration status is set to Active.
-
+ Applies measurement groups to network functions identified by AAI event
Args:
- mr_sub (_MrSub): MR subscriber
- mr_pub (_MrPub): MR publisher
- app (db): DB application
- app_conf (AppConfig): the application configuration.
+ entity_type (string): The type of network function
"""
- app.app_context().push()
- logger.info('Polling MR for XNF AAI events.')
- try:
- aai_events = mr_sub.get_from_topic('dcae_pmsh_aai_event')
- if aai_events is not None and len(aai_events) != 0:
- aai_events = [json.loads(e) for e in aai_events]
- xnf_events = [e for e in aai_events if e['event-header']['entity-type'] == (
- XNFType.PNF.value or XNFType.VNF.value)]
- for entry in xnf_events:
- logger.debug(f'AAI-EVENT entry: {entry}')
- aai_entity = entry['entity']
- action = entry['event-header']['action']
- entity_type = entry['event-header']['entity-type']
- xnf_name = aai_entity['pnf-name'] if entity_type == XNFType.PNF.value \
- else aai_entity['vnf-name']
- if aai_entity['orchestration-status'] != 'Active':
- logger.info(f'Skipping XNF {xnf_name} as its orchestration-status '
- f'is not "Active"')
- continue
- nf = NetworkFunction(nf_name=xnf_name,
- ipv4_address=aai_entity['ipaddress-v4-oam'],
- ipv6_address=aai_entity['ipaddress-v6-oam'],
- model_invariant_id=aai_entity['model-invariant-id'],
- model_version_id=aai_entity['model-version-id'])
- if not nf.set_nf_model_params(app_conf):
- continue
- if app_conf.nf_filter.is_nf_in_filter(nf):
- _process_event(action, nf, mr_pub, app_conf)
- except Exception as e:
- logger.error(f'Failed to process AAI event: {e}', exc_info=True)
+ return entity_type == (XNFType.PNF.value or XNFType.VNF.value)
+
+
+class AAIEventHandler:
+ """ Responsible for handling AAI update events in PMSH """
+
+ def __init__(self, app):
+ self.app = app
+ def execute(self):
+ """
+ Processes AAI UPDATE events for each filtered xNFs where
+ orchestration status is set to Active.
+ """
+ self.app.app_context().push()
+ logger.info('Polling MR for XNF AAI events.')
+ try:
+ aai_events = AppConfig.get_instance().get_from_topic(MRTopic.AAI_SUBSCRIBER.value,
+ 'dcae_pmsh_aai_event')
+ if aai_events is not None and len(aai_events) != 0:
+ pmsh_nf_names = list(nf.nf_name for nf in NetworkFunction.get_all())
+ aai_events = [json.loads(e) for e in aai_events]
+ xnf_events = [e for e in aai_events if is_pnf_xnf(e['event-header']['entity-type'])]
+ new_nfs = []
+ for entry in xnf_events:
+ logger.debug(f'AAI-EVENT entry: {entry}')
+ aai_entity = entry['entity']
+ action = entry['event-header']['action']
+ entity_type = entry['event-header']['entity-type']
+ xnf_name = aai_entity['pnf-name'] if entity_type == XNFType.PNF.value \
+ else aai_entity['vnf-name']
+ if aai_entity['orchestration-status'] != 'Active':
+ logger.info(f'Skipping XNF {xnf_name} as its orchestration-status '
+ f'is not "Active"')
+ continue
+ nf = NetworkFunction(nf_name=xnf_name,
+ ipv4_address=aai_entity['ipaddress-v4-oam'],
+ ipv6_address=aai_entity['ipaddress-v6-oam'],
+ model_invariant_id=aai_entity['model-invariant-id'],
+ model_version_id=aai_entity['model-version-id'])
+ if action == AAIEvent.DELETE.value and xnf_name in pmsh_nf_names:
+ logger.info(f'Delete event found for network function {nf.nf_name}')
+ NetworkFunction.delete(nf_name=nf.nf_name)
+ logger.info(f'{nf.nf_name} successfully deleted.')
+ elif action == AAIEvent.UPDATE.value and \
+ xnf_name not in pmsh_nf_names and \
+ nf.set_nf_model_params(AppConfig.get_instance()):
+ new_nfs.append(nf)
+ if new_nfs:
+ self.apply_nfs_to_subscriptions(new_nfs)
+ except Exception as e:
+ logger.error(f'Failed to process AAI event due to: {e}')
-def _process_event(action, nf, mr_pub, app_conf):
- if action == AAIEvent.UPDATE.value:
- logger.info(f'Update event found for network function {nf.nf_name}')
- app_conf.subscription.create_subscription_on_nfs([nf], mr_pub)
- elif action == AAIEvent.DELETE.value:
- logger.info(f'Delete event found for network function {nf.nf_name}')
- NetworkFunction.delete(nf_name=nf.nf_name)
- logger.info(f'{nf.nf_name} successfully deleted.')
+ @staticmethod
+ def apply_nfs_to_subscriptions(new_nfs):
+ """
+ Applies measurement groups to network functions identified by AAI event
+ Args:
+ new_nfs (list[NetworkFunction]): new network functions identified
+ """
+ subscriptions = db.session.query(SubscriptionModel).all()
+ if subscriptions:
+ for subscription in subscriptions:
+ try:
+ nf_filter = NetworkFunctionFilter(**subscription.network_filter.serialize())
+ filtered_nfs = []
+ for nf in new_nfs:
+ if nf_filter.is_nf_in_filter(nf):
+ filtered_nfs.append(nf)
+ if filtered_nfs:
+ subscription_service.save_filtered_nfs(filtered_nfs)
+ subscription_service. \
+ apply_subscription_to_nfs(filtered_nfs, subscription.subscription_name)
+ unlocked_meas_grp = subscription_service. \
+ apply_measurement_grp_to_nfs(filtered_nfs,
+ subscription.measurement_groups)
+ if unlocked_meas_grp:
+ subscription_service. \
+ publish_measurement_grp_to_nfs(subscription, filtered_nfs,
+ unlocked_meas_grp)
+ else:
+ logger.error(f'All measurement groups are locked for subscription: '
+ f'{subscription.subscription_name}, '
+ f'please verify/check measurement groups.')
+ db.session.commit()
+ except Exception as e:
+ logger.error(f'Failed to process AAI event for subscription: '
+ f'{subscription.subscription_name} due to: {e}')
+ db.session.remove()
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 e852324d..aee6df0e 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py
@@ -18,7 +18,7 @@
from http import HTTPStatus
from mod import logger
-from mod.api.services import subscription_service
+from mod.api.services import subscription_service, measurement_group_service
from connexion import NoContent
from mod.api.custom_exception import InvalidDataException, DuplicateDataException
@@ -112,3 +112,38 @@ def get_subscriptions():
logger.error(f'The following exception occurred while fetching subscriptions: {exception}')
return {'error': 'Request was not processed due to Exception : '
f'{exception}'}, HTTPStatus.INTERNAL_SERVER_ERROR.value
+
+
+def get_meas_group_with_nfs(subscription_name, measurement_group_name):
+ """
+ Retrieves the measurement group and it's associated network functions
+
+ Args:
+ subscription_name (String): Name of the subscription.
+ measurement_group_name (String): Name of the measurement group
+
+ Returns:
+ dict, HTTPStatus: measurement group info with associated nfs, 200
+ dict, HTTPStatus: measurement group was not defined, 404
+ dict, HTTPStatus: Exception details of failure, 500
+ """
+ logger.info('API call received to query measurement group and associated network'
+ f' functions by using sub name: {subscription_name} and measurement '
+ f'group name: {measurement_group_name}')
+ try:
+ meas_group = measurement_group_service.query_meas_group_by_name(subscription_name,
+ measurement_group_name)
+ if meas_group is not None:
+ return meas_group.meas_group_with_nfs(), HTTPStatus.OK.value
+ else:
+ logger.error('measurement group was not defined with the sub name: '
+ f'{subscription_name} and meas group name: '
+ f'{measurement_group_name}')
+ return {'error': 'measurement group was not defined with the sub name: '
+ f'{subscription_name} and meas group name: '
+ f'{measurement_group_name}'}, HTTPStatus.NOT_FOUND.value
+ except Exception as exception:
+ logger.error('The following exception occurred while fetching measurement group: '
+ f'{exception}')
+ return {'error': 'Request was not processed due to Exception : '
+ f'{exception}'}, HTTPStatus.INTERNAL_SERVER_ERROR.value
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 9ecc80e6..548e4f28 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
@@ -52,10 +52,10 @@ class SubscriptionModel(db.Model):
self.status = status
def __repr__(self):
- return f'subscription_name: {self.subscription_name},' \
- f'operational_policy_name: {self.operational_policy_name},' \
- f'control_loop_name: {self.control_loop_name},' \
- f'status: {self.status}'
+ return (f'subscription_name: {self.subscription_name}, '
+ f'operational_policy_name: {self.operational_policy_name}, '
+ f'control_loop_name: {self.control_loop_name}, '
+ f'status: {self.status}')
def __eq__(self, other):
if isinstance(self, other.__class__):
@@ -139,8 +139,8 @@ class NfSubRelationalModel(db.Model):
self.nf_sub_status = nf_sub_status
def __repr__(self):
- return f'subscription_name: {self.subscription_name}, ' \
- f'nf_name: {self.nf_name}, nf_sub_status: {self.nf_sub_status}'
+ return (f'subscription_name: {self.subscription_name}, '
+ f'nf_name: {self.nf_name}, nf_sub_status: {self.nf_sub_status}')
def serialize(self):
return {'subscription_name': self.subscription_name, 'nf_name': self.nf_name,
@@ -183,9 +183,9 @@ class NetworkFunctionFilterModel(db.Model):
self.model_names = model_names
def __repr__(self):
- return f'subscription_name: {self.subscription_name}, ' \
- f'nf_names: {self.nf_names}, model_invariant_ids: {self.model_invariant_ids}' \
- f'model_version_ids: {self.model_version_ids}, model_names: {self.model_names}'
+ return (f'subscription_name: {self.subscription_name}, '
+ f'nf_names: {self.nf_names}, model_invariant_ids: {self.model_invariant_ids}, '
+ f'model_version_ids: {self.model_version_ids}, model_names: {self.model_names}')
def serialize(self):
return {'nfNames': convert_db_string_to_list(self.nf_names),
@@ -220,13 +220,13 @@ class MeasurementGroupModel(db.Model):
self.managed_object_dns_basic = managed_object_dns_basic
def __repr__(self):
- return f'subscription_name: {self.subscription_name}, ' \
- f'measurement_group_name: {self.measurement_group_name},' \
- f'administrative_state: {self.administrative_state},' \
- f'file_based_gp: {self.file_based_gp},' \
- f'file_location: {self.file_location},' \
- f'measurement_type: {self.measurement_type}' \
- f'managed_object_dns_basic: {self.managed_object_dns_basic}'
+ return (f'subscription_name: {self.subscription_name}, '
+ f'measurement_group_name: {self.measurement_group_name}, '
+ f'administrative_state: {self.administrative_state}, '
+ f'file_based_gp: {self.file_based_gp}, '
+ f'file_location: {self.file_location}, '
+ f'measurement_type: {self.measurement_type}, '
+ f'managed_object_dns_basic: {self.managed_object_dns_basic}')
def serialize(self):
return {'measurementGroup': {'measurementGroupName': self.measurement_group_name,
@@ -236,6 +236,28 @@ class MeasurementGroupModel(db.Model):
'measurementTypes': self.measurement_type,
'managedObjectDNsBasic': self.managed_object_dns_basic}}
+ def meas_group_with_nfs(self):
+ """
+ Generates the dictionary of subscription name, measurement group name, administrative state
+ and network functions
+
+ Returns:
+ dict: of subscription name, measurement group name, administrative state
+ and network functions
+ """
+ meas_group_nfs = db.session.query(NfMeasureGroupRelationalModel).filter(
+ NfMeasureGroupRelationalModel.measurement_grp_name == self.measurement_group_name).all()
+ db.session.remove()
+ return {'subscriptionName': self.subscription_name,
+ 'measurementGroupName': self.measurement_group_name,
+ 'administrativeState': self.administrative_state,
+ 'fileBasedGP': self.file_based_gp,
+ 'fileLocation': self.file_location,
+ 'measurementTypes': self.measurement_type,
+ 'managedObjectDNsBasic': self.managed_object_dns_basic,
+ 'networkFunctions':
+ [meas_group_nf.serialize_meas_group_nfs() for meas_group_nf in meas_group_nfs]}
+
class NfMeasureGroupRelationalModel(db.Model):
__tablename__ = 'nf_to_measure_grp_rel'
@@ -263,8 +285,28 @@ class NfMeasureGroupRelationalModel(db.Model):
self.retry_count = retry_count
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}'
+ 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 serialize_meas_group_nfs(self):
+ """
+ Generates the dictionary of all the network function properties
+
+ Returns:
+ dict: of network function properties
+ """
+ nf = db.session.query(NetworkFunctionModel).filter(
+ NetworkFunctionModel.nf_name == self.nf_name).one_or_none()
+ db.session.remove()
+ return {'nfName': self.nf_name,
+ 'ipv4Address': nf.ipv4_address,
+ 'ipv6Address': nf.ipv6_address,
+ 'nfMgStatus': self.nf_measure_grp_status,
+ 'modelInvariantId': nf.model_invariant_id,
+ 'modelVersionId': nf.model_version_id,
+ 'modelName': nf.model_name,
+ 'sdncModelName': nf.sdnc_model_name,
+ 'sdncModelVersion': nf.sdnc_model_version}
def convert_db_string_to_list(db_string):
diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml b/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml
index 39f6dc3b..4dd1cc70 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
@@ -109,6 +109,34 @@ paths:
500:
description: Exception occurred while querying database
+ /subscription/{subscription_name}/measurementGroups/{measurement_group_name}:
+ get:
+ description: Get the measurement group and associated network functions
+ from PMSH by using sub name and meas group name
+ operationId: mod.api.controller.get_meas_group_with_nfs
+ 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 name
+ type: string
+ responses:
+ 200:
+ description: OK; Received requested measurement group with associated NF's
+ schema:
+ $ref : "#/definitions/measGroupWithNFs"
+ 404:
+ description: Measurement group with specified name not found
+ 500:
+ description: Exception occurred while querying database
+
definitions:
subscription:
type: object
@@ -215,3 +243,60 @@ definitions:
type: string
required:
- DN
+
+ measGroupWithNFs:
+ type: object
+ properties:
+ subscriptionName:
+ type: string
+ measurementGroupName:
+ type: string
+ administrativeState:
+ type: string
+ enum: [ LOCKED, UNLOCKED ]
+ fileBasedGP:
+ type: integer
+ fileLocation:
+ type: string
+ measurementTypes:
+ type: array
+ minItems: 1
+ items:
+ $ref: "#/definitions/measurementType"
+ managedObjectDNsBasic:
+ type: array
+ minItems: 1
+ items:
+ $ref: "#/definitions/managedObjectDNs"
+ network_functions:
+ type: array
+ items:
+ type: object
+ properties:
+ nfName:
+ type: string
+ description: Name of the Network Function
+ ipv4Address:
+ type: string
+ description: Address of the IPV4
+ ipv6Address:
+ type: string
+ description: Address of the IPV6
+ nfMgStatus:
+ type: string
+ description: status of network function for one meas group
+ modelInvariantId:
+ type: string
+ description: ID of the model invariant
+ modelVersionId:
+ type: string
+ description: ID of the model version
+ modelName:
+ type: string
+ description: Name of the model
+ sdncModelName:
+ type: string
+ description: Name of the sdnc model
+ sdncModelVersion:
+ type: string
+ description: Version of the sdnc model
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 cc07cc03..692efe27 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
@@ -135,3 +135,20 @@ def delete_nf_to_measurement_group(nf_name, measurement_group_name, status):
except Exception as e:
logger.error(f'Failed to delete nf: {nf_name} for measurement group: '
f'{measurement_group_name} due to: {e}')
+
+
+def query_meas_group_by_name(subscription_name, measurement_group_name):
+ """
+ Retrieves the measurement group by using sub name and measurement group name
+
+ Args:
+ subscription_name (String): Name of the subscription.
+ measurement_group_name (String): Name of the measurement group
+
+ Returns:
+ MeasurementGroupModel: queried measurement group (or) None
+ """
+ meas_group = db.session.query(MeasurementGroupModel).filter(
+ MeasurementGroupModel.subscription_name == subscription_name,
+ MeasurementGroupModel.measurement_group_name == measurement_group_name).one_or_none()
+ return meas_group
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 73467821..fc27f992 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
@@ -129,7 +129,7 @@ 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
+ filtered_nfs (list[NetworkFunction]): list of filtered network functions
measurement_groups (list[MeasurementGroupModel]): list of measurement group
Returns:
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 29f9121d..5fbb9a6c 100644
--- a/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py
@@ -18,9 +18,7 @@
from jsonschema import ValidationError
from mod import logger, aai_client
-from mod.aai_event_handler import process_aai_events
from mod.network_function import NetworkFunctionFilter
-from mod.pmsh_utils import PeriodicTask
from mod.subscription import AdministrativeState
@@ -73,7 +71,6 @@ class SubscriptionHandler:
def _activate(self, new_administrative_state):
if not self.app_conf.nf_filter:
self.app_conf.nf_filter = NetworkFunctionFilter(**self.app_conf.subscription.nfFilter)
- self._start_aai_event_thread()
self.app_conf.subscription.update_sub_params(new_administrative_state,
self.app_conf.subscription.fileBasedGP,
self.app_conf.subscription.fileLocation,
@@ -91,15 +88,6 @@ class SubscriptionHandler:
self.app_conf.subscription.delete_subscription_from_nfs(nfs, self.mr_pub)
self.app_conf.subscription.update_subscription_status()
- def _start_aai_event_thread(self):
- logger.info('Starting polling for NF info on AAI-EVENT topic on DMaaP MR.')
- self.aai_event_thread = PeriodicTask(20, process_aai_events, args=(self.aai_sub,
- self.mr_pub,
- self.app,
- self.app_conf))
- self.aai_event_thread.name = 'aai_event_thread'
- self.aai_event_thread.start()
-
def stop_aai_event_thread(self):
if self.aai_event_thread is not None:
self.aai_event_thread.cancel()
diff --git a/components/pm-subscription-handler/pmsh_service/pmsh_service_main.py b/components/pm-subscription-handler/pmsh_service/pmsh_service_main.py
index fe151c0d..1af01cf1 100755
--- a/components/pm-subscription-handler/pmsh_service/pmsh_service_main.py
+++ b/components/pm-subscription-handler/pmsh_service/pmsh_service_main.py
@@ -17,7 +17,7 @@
# ============LICENSE_END=====================================================
import sys
from signal import signal, SIGTERM
-
+from mod.aai_event_handler import AAIEventHandler
from mod import db, create_app, launch_api_server, logger
from mod.exit_handler import ExitHandler
from mod.pmsh_config import AppConfig as NewAppConfig
@@ -51,8 +51,13 @@ def main():
subscription_handler_thread.name = 'sub_handler_thread'
subscription_handler_thread.start()
- periodic_tasks = [subscription_handler_thread, policy_response_handler_thread]
+ aai_event_handler = AAIEventHandler(app)
+ aai_event_handler_thread = PeriodicTask(20, aai_event_handler.execute)
+ aai_event_handler_thread.name = 'aai_event_thread'
+ aai_event_handler_thread.start()
+ periodic_tasks = [subscription_handler_thread, policy_response_handler_thread,
+ aai_event_handler_thread]
signal(SIGTERM, ExitHandler(periodic_tasks=periodic_tasks,
app_conf=app_conf, subscription_handler=subscription_handler))
launch_api_server(pmsh_app_conf)
diff --git a/components/pm-subscription-handler/tests/base_setup.py b/components/pm-subscription-handler/tests/base_setup.py
index 33e2e91b..560eaeb8 100755
--- a/components/pm-subscription-handler/tests/base_setup.py
+++ b/components/pm-subscription-handler/tests/base_setup.py
@@ -22,7 +22,8 @@ from unittest import TestCase
from unittest.mock import patch, MagicMock
from mod import create_app, db
-from mod.api.db_models import NetworkFunctionFilterModel, MeasurementGroupModel, SubscriptionModel, NfSubRelationalModel
+from mod.api.db_models import NetworkFunctionFilterModel, MeasurementGroupModel, \
+ SubscriptionModel, NetworkFunctionModel, NfSubRelationalModel
from mod.network_function import NetworkFunctionFilter
from mod.pmsh_utils import AppConfig
from mod.pmsh_config import AppConfig as NewAppConfig
@@ -88,6 +89,26 @@ def create_multiple_subscription_data(subscription_names):
return subscriptions
+def create_multiple_network_function_data(nf_name_list):
+ """
+ Creates list of network function model objects
+
+ Args:
+ nf_name_list (list): Network function names
+
+ Returns
+ list: of network function model objects
+ """
+ nf_list = []
+ for nf_name in nf_name_list:
+ nf = NetworkFunctionModel(nf_name, '10.10.10.32', '2001:0db8:0:0:0:0:1428:57ab',
+ '687kj45-d396-4efb-af02-6b83499b12f8',
+ 'e80a6ae3-cafd-4d24-850d-e14c084a5ca9',
+ 'model_name', 'pm_control', '1.0.2')
+ nf_list.append(nf)
+ return nf_list
+
+
class BaseClassSetup(TestCase):
app = None
app_context = None
diff --git a/components/pm-subscription-handler/tests/data/mr_aai_events.json b/components/pm-subscription-handler/tests/data/mr_aai_events.json
index 19758982..42639d9f 100755
--- a/components/pm-subscription-handler/tests/data/mr_aai_events.json
+++ b/components/pm-subscription-handler/tests/data/mr_aai_events.json
@@ -2,7 +2,8 @@
"mr_response": [
"{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"pnf\",\"top-entity-type\":\"pnf\",\"entity-link\":\"/aai/v16/network/pnfs/pnf/pnf_newly_discovered\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"UPDATE\",\"sequence-number\":\"0\",\"id\":\"db09e090-196e-4f84-9645-e449b1cd3640\",\"source-name\":\"dcae-curl\",\"version\":\"v16\",\"timestamp\":\"20200203-15:14:08:807\"},\"entity\":{\"ipaddress-v4-oam\":\"10.10.10.37\",\"nf-role\":\"gNB\",\"equip-type\":\"val8\",\"relationship-list\":{\"relationship\":[{\"related-to\":\"service-instance\",\"relationship-data\":[{\"relationship-value\":\"Demonstration\",\"relationship-key\":\"customer.global-customer-id\"},{\"relationship-value\":\"vCPE\",\"relationship-key\":\"service-subscription.service-type\"},{\"relationship-value\":\"2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-key\":\"service-instance.service-instance-id\"}],\"related-link\":\"/aai/v16/business/customers/customer/Demonstration/service-subscriptions/service-subscription/vCPE/service-instances/service-instance/2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-label\":\"org.onap.relationships.inventory.ComposedOf\",\"related-to-property\":[{\"property-key\":\"service-instance.service-instance-name\",\"property-value\":\"Svc6_1\"}]}]},\"equip-vendor\":\"Ericsson\",\"serial-number\":\"6061ZW3\",\"ipaddress-v6-oam\":\"2001:0db8:0:0:0:0:1428:57ab\",\"equip-model\":\"val6\",\"in-maint\":false,\"resource-version\":\"1578668956804\",\"sw-version\":\"val7\",\"pnf-id\":\"eabcfaf7-b7f3-45fb-94e7-e6112fb3e8b8\",\"pnf-name\":\"pnf_newly_discovered\",\"model-invariant-id\":\"7129e420-d396-4efb-af02-6b83499b12f8\",\"model-version-id\":\"e80a6ae3-cafd-4d24-850d-e14c084a5ca9\",\"orchestration-status\":\"Active\"}}",
"{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"pnf\",\"top-entity-type\":\"pnf\",\"entity-link\":\"/aai/v16/network/pnfs/pnf/pnf_newly_discovered\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"UPDATE\",\"sequence-number\":\"0\",\"id\":\"db09e090-196e-4f84-9645-e449b1cd3640\",\"source-name\":\"dcae-curl\",\"version\":\"v16\",\"timestamp\":\"20200203-15:14:08:807\"},\"entity\":{\"orchestration-status\":\"Active\",\"ipaddress-v4-oam\":\"10.10.10.37\",\"nf-role\":\"gNB\",\"equip-type\":\"val8\",\"relationship-list\":{\"relationship\":[{\"related-to\":\"service-instance\",\"relationship-data\":[{\"relationship-value\":\"Demonstration\",\"relationship-key\":\"customer.global-customer-id\"},{\"relationship-value\":\"vCPE\",\"relationship-key\":\"service-subscription.service-type\"},{\"relationship-value\":\"2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-key\":\"service-instance.service-instance-id\"}],\"related-link\":\"/aai/v16/business/customers/customer/Demonstration/service-subscriptions/service-subscription/vCPE/service-instances/service-instance/2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-label\":\"org.onap.relationships.inventory.ComposedOf\",\"related-to-property\":[{\"property-key\":\"service-instance.service-instance-name\",\"property-value\":\"Svc6_1\"}]}]},\"equip-vendor\":\"Ericsson\",\"serial-number\":\"6061ZW3\",\"ipaddress-v6-oam\":\"2001:0db8:0:0:0:0:1428:57ab\",\"equip-model\":\"val6\",\"in-maint\":false,\"resource-version\":\"1578668956804\",\"sw-version\":\"val7\",\"pnf-id\":\"eabcfaf7-b7f3-45fb-94e7-e6112fb3e8b8\",\"pnf-name\":\"pnf_already_active\",\"model-invariant-id\":\"7129e420-d396-4efb-af02-6b83499b12f8\",\"model-version-id\":\"e80a6ae3-cafd-4d24-850d-e14c084a5ca9\",\"orchestration-status\":\"Active\"}}",
- "{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"pnf\",\"top-entity-type\":\"pnf\",\"entity-link\":\"/aai/v16/network/pnfs/pnf/pnf_newly_discovered\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"DELETE\",\"sequence-number\":\"0\",\"id\":\"db09e090-196e-4f84-9645-e449b1cd3640\",\"source-name\":\"dcae-curl\",\"version\":\"v16\",\"timestamp\":\"20200203-15:14:08:807\"},\"entity\":{\"ipaddress-v4-oam\":\"10.10.10.37\",\"nf-role\":\"gNB\",\"equip-type\":\"val8\",\"relationship-list\":{\"relationship\":[{\"related-to\":\"service-instance\",\"relationship-data\":[{\"relationship-value\":\"Demonstration\",\"relationship-key\":\"customer.global-customer-id\"},{\"relationship-value\":\"vCPE\",\"relationship-key\":\"service-subscription.service-type\"},{\"relationship-value\":\"2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-key\":\"service-instance.service-instance-id\"}],\"related-link\":\"/aai/v16/business/customers/customer/Demonstration/service-subscriptions/service-subscription/vCPE/service-instances/service-instance/2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-label\":\"org.onap.relationships.inventory.ComposedOf\",\"related-to-property\":[{\"property-key\":\"service-instance.service-instance-name\",\"property-value\":\"Svc6_1\"}]}]},\"equip-vendor\":\"Ericsson\",\"serial-number\":\"6061ZW3\",\"ipaddress-v6-oam\":\"2001:0db8:0:0:0:0:1428:57ab\",\"equip-model\":\"val6\",\"in-maint\":false,\"resource-version\":\"1578668956804\",\"sw-version\":\"val7\",\"pnf-id\":\"eabcfaf7-b7f3-45fb-94e7-e6112fb3e8b8\",\"pnf-name\":\"pnf_to_be_deleted\",\"model-invariant-id\":\"7129e420-d396-4efb-af02-6b83499b12f8\",\"model-version-id\":\"e80a6ae3-cafd-4d24-850d-e14c084a5ca9\",\"orchestration-status\":\"Active\"}}",
+ "{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"pnf\",\"top-entity-type\":\"pnf\",\"entity-link\":\"/aai/v16/network/pnfs/pnf/pnf_newly_discovered\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"DELETE\",\"sequence-number\":\"0\",\"id\":\"db09e090-196e-4f84-9645-e449b1cd3640\",\"source-name\":\"dcae-curl\",\"version\":\"v16\",\"timestamp\":\"20200203-15:14:08:807\"},\"entity\":{\"ipaddress-v4-oam\":\"10.10.10.37\",\"nf-role\":\"gNB\",\"equip-type\":\"val8\",\"relationship-list\":{\"relationship\":[{\"related-to\":\"service-instance\",\"relationship-data\":[{\"relationship-value\":\"Demonstration\",\"relationship-key\":\"customer.global-customer-id\"},{\"relationship-value\":\"vCPE\",\"relationship-key\":\"service-subscription.service-type\"},{\"relationship-value\":\"2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-key\":\"service-instance.service-instance-id\"}],\"related-link\":\"/aai/v16/business/customers/customer/Demonstration/service-subscriptions/service-subscription/vCPE/service-instances/service-instance/2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-label\":\"org.onap.relationships.inventory.ComposedOf\",\"related-to-property\":[{\"property-key\":\"service-instance.service-instance-name\",\"property-value\":\"Svc6_1\"}]}]},\"equip-vendor\":\"Ericsson\",\"serial-number\":\"6061ZW3\",\"ipaddress-v6-oam\":\"2001:0db8:0:0:0:0:1428:57ab\",\"equip-model\":\"val6\",\"in-maint\":false,\"resource-version\":\"1578668956804\",\"sw-version\":\"val7\",\"pnf-id\":\"eabcfaf7-b7f3-45fb-94e7-e6112fb3e8b8\",\"pnf-name\":\"pnf_to_be_deleted1\",\"model-invariant-id\":\"7129e420-d396-4efb-af02-6b83499b12f8\",\"model-version-id\":\"e80a6ae3-cafd-4d24-850d-e14c084a5ca9\",\"orchestration-status\":\"Active\"}}",
+ "{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"pnf\",\"top-entity-type\":\"pnf\",\"entity-link\":\"/aai/v16/network/pnfs/pnf/pnf_newly_discovered\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"DELETE\",\"sequence-number\":\"0\",\"id\":\"db09e090-196e-4f84-9645-e449b1cd3641\",\"source-name\":\"dcae-curl\",\"version\":\"v16\",\"timestamp\":\"20200203-15:14:08:807\"},\"entity\":{\"ipaddress-v4-oam\":\"10.10.10.38\",\"nf-role\":\"gNB\",\"equip-type\":\"val8\",\"relationship-list\":{\"relationship\":[{\"related-to\":\"service-instance\",\"relationship-data\":[{\"relationship-value\":\"Demonstration\",\"relationship-key\":\"customer.global-customer-id\"},{\"relationship-value\":\"vCPE\",\"relationship-key\":\"service-subscription.service-type\"},{\"relationship-value\":\"2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-key\":\"service-instance.service-instance-id\"}],\"related-link\":\"/aai/v16/business/customers/customer/Demonstration/service-subscriptions/service-subscription/vCPE/service-instances/service-instance/2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-label\":\"org.onap.relationships.inventory.ComposedOf\",\"related-to-property\":[{\"property-key\":\"service-instance.service-instance-name\",\"property-value\":\"Svc6_1\"}]}]},\"equip-vendor\":\"Ericsson\",\"serial-number\":\"6061ZW3\",\"ipaddress-v6-oam\":\"2001:0db8:0:0:0:0:1428:57ab\",\"equip-model\":\"val6\",\"in-maint\":false,\"resource-version\":\"1578668956804\",\"sw-version\":\"val7\",\"pnf-id\":\"eabcfaf7-b7f3-45fb-94e7-e6112fb3e8b8\",\"pnf-name\":\"pnf_to_be_deleted2\",\"model-invariant-id\":\"7129e420-d396-4efb-af02-6b83499b12f8\",\"model-version-id\":\"e80a6ae3-cafd-4d24-850d-e14c084a5ca9\",\"orchestration-status\":\"Active\"}}",
"{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"model-ver\",\"top-entity-type\":\"model\",\"entity-link\":\"/aai/v19/service-design-and-creation/models/model/60cd7bbf-0a6b-43ce-a5af-07cbf168ecdc/model-vers/model-ver/5485c7ac-825f-414d-aba4-e24147107d0b\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"UPDATE\",\"sequence-number\":\"0\",\"id\":\"e16cb0f9-3617-4a14-9c81-f7ee4cdf9df7\",\"source-name\":\"UNKNOWN\",\"version\":\"v19\",\"timestamp\":\"20200701-12:06:12:135\"},\"entity\":{\"model-type\":\"service\",\"model-vers\":{\"model-ver\":[{\"model-version-id\":\"5485c7ac-825f-414d-aba4-e24147107d0b\",\"resource-version\":\"1593605171956\",\"model-description\":\"catalog service description\",\"model-name\":\"Demo_pNF_WCcM9Z2VNE3eGfLIKulP\",\"model-elements\":{\"model-element\":[{\"relationship-list\":{\"relationship\":[{\"related-to\":\"model-ver\",\"relationship-data\":[{\"relationship-value\":\"82194af1-3c2c-485a-8f44-420e22a9eaa4\",\"relationship-key\":\"model.model-invariant-id\"},{\"relationship-value\":\"46b92144-923a-4d20-b85a-3cbd847668a9\",\"relationship-key\":\"model-ver.model-version-id\"}],\"related-link\":\"/aai/v19/service-design-and-creation/models/model/82194af1-3c2c-485a-8f44-420e22a9eaa4/model-vers/model-ver/46b92144-923a-4d20-b85a-3cbd847668a9\",\"relationship-label\":\"org.onap.relationships.inventory.IsA\",\"related-to-property\":[{\"property-key\":\"model-ver.model-name\",\"property-value\":\"service-instance\"}]}]},\"resource-version\":\"1593605012006\",\"model-elements\":{\"model-element\":[{\"relationship-list\":{\"relationship\":[{\"related-to\":\"model-ver\",\"relationship-data\":[{\"relationship-value\":\"c8fb1064-f38a-4a13-90a6-ec60289879ac\",\"relationship-key\":\"model.model-invariant-id\"},{\"relationship-value\":\"94c051c3-4484-425e-a107-6914816321a6\",\"relationship-key\":\"model-ver.model-version-id\"}],\"related-link\":\"/aai/v19/service-design-and-creation/models/model/c8fb1064-f38a-4a13-90a6-ec60289879ac/model-vers/model-ver/94c051c3-4484-425e-a107-6914816321a6\",\"relationship-label\":\"org.onap.relationships.inventory.IsA\",\"related-to-property\":[{\"property-key\":\"model-ver.model-name\",\"property-value\":\"pNF 77be3d89-b735\"}]}]},\"resource-version\":\"1593605012006\",\"new-data-del-flag\":\"T\",\"cardinality\":\"unbounded\",\"model-element-uuid\":\"44712769-981d-4970-9ea5-4c99b86f838e\"}]},\"new-data-del-flag\":\"T\",\"cardinality\":\"unbounded\",\"model-element-uuid\":\"4a601305-9e09-4152-98ad-b8c466d57813\"}]},\"model-version\":\"1.0\",\"distribution-status\":\"DISTRIBUTION_COMPLETE_OK\"}]},\"model-invariant-id\":\"60cd7bbf-0a6b-43ce-a5af-07cbf168ecdc\"}}",
"{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"customer\",\"top-entity-type\":\"customer\",\"entity-link\":\"/aai/v19/business/customers/customer/ETE_Customer_5638d7b4-a02d-4cc4-801e-77ee837d2855\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"CREATE\",\"sequence-number\":\"0\",\"id\":\"ef390383-3d08-4268-a013-d11c14efd716\",\"source-name\":\"robot-ete\",\"version\":\"v19\",\"timestamp\":\"20200701-12:06:50:388\"},\"entity\":{\"global-customer-id\":\"ETE_Customer_5638d7b4-a02d-4cc4-801e-77ee837d2855\",\"subscriber-type\":\"INFRA\",\"resource-version\":\"1593605210258\",\"subscriber-name\":\"ETE_Customer_5638d7b4-a02d-4cc4-801e-77ee837d2855\",\"service-subscriptions\":{\"service-subscription\":[{\"relationship-list\":{\"relationship\":[{\"related-to\":\"tenant\",\"relationship-data\":[{\"relationship-value\":\"CloudOwner\",\"relationship-key\":\"cloud-region.cloud-owner\"},{\"relationship-value\":\"RegionForPNF\",\"relationship-key\":\"cloud-region.cloud-region-id\"},{\"relationship-value\":\"dummy_tenant_id_for_pnf\",\"relationship-key\":\"tenant.tenant-id\"}],\"related-link\":\"/aai/v19/cloud-infrastructure/cloud-regions/cloud-region/CloudOwner/RegionForPNF/tenants/tenant/dummy_tenant_id_for_pnf\",\"relationship-label\":\"org.onap.relationships.inventory.Uses\",\"related-to-property\":[{\"property-key\":\"tenant.tenant-name\",\"property-value\":\"dummy_tenant_for_pnf\"}]}]},\"resource-version\":\"1593605210258\",\"service-type\":\"pNF\"}]}}}"
]
diff --git a/components/pm-subscription-handler/tests/test_aai_event_handler.py b/components/pm-subscription-handler/tests/test_aai_event_handler.py
index 0ae19429..d06b7728 100755
--- a/components/pm-subscription-handler/tests/test_aai_event_handler.py
+++ b/components/pm-subscription-handler/tests/test_aai_event_handler.py
@@ -1,5 +1,5 @@
# ============LICENSE_START===================================================
-# Copyright (C) 2020 Nordix Foundation.
+# Copyright (C) 2020-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.
@@ -15,27 +15,28 @@
#
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=====================================================
+import copy
import json
from os import path
-from unittest.mock import patch, Mock
-
-from mod.aai_event_handler import process_aai_events
+from unittest.mock import patch, MagicMock
+from mod.aai_event_handler import AAIEventHandler
+from mod.api.db_models import NetworkFunctionModel, NetworkFunctionFilterModel, \
+ MeasurementGroupModel, SubscriptionModel
+from mod.subscription import AdministrativeState
from tests.base_setup import BaseClassSetup
+from mod import db
class AAIEventHandlerTest(BaseClassSetup):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
def setUp(self):
super().setUp()
+ super().setUpAppConf()
with open(path.join(path.dirname(__file__), 'data/mr_aai_events.json'), 'r') as data:
self.mr_aai_events = json.load(data)["mr_response"]
- self.mock_mr_sub = Mock(get_from_topic=Mock(return_value=self.mr_aai_events))
- self.mock_mr_pub = Mock()
- self.mock_app = Mock()
def tearDown(self):
super().tearDown()
@@ -44,12 +45,100 @@ class AAIEventHandlerTest(BaseClassSetup):
def tearDownClass(cls):
super().tearDownClass()
- @patch('mod.network_function.NetworkFunction.set_nf_model_params')
- @patch('mod.subscription.Subscription.create_subscription_on_nfs')
+ @patch('mod.pmsh_config.AppConfig.get_from_topic')
@patch('mod.aai_event_handler.NetworkFunction.delete')
- def test_process_aai_update_and_delete_events(self, mock_nf_delete, mock_activate_sub,
- mock_set_sdnc_params):
+ def test_process_aai_delete_events(self, mock_nf_delete, mr_aai_mock):
+ mr_aai_mock.return_value = self.mr_aai_events
+ aai_handler = AAIEventHandler(self.app)
+ network_function = NetworkFunctionModel(
+ nf_name="pnf_to_be_deleted1",
+ ipv4_address='204.120.0.15',
+ ipv6_address='2001:db8:3333:4444:5555:6666:7777:8888',
+ model_invariant_id='123',
+ model_version_id='234',
+ model_name='pnf',
+ sdnc_model_name='p-node',
+ sdnc_model_version='v1')
+ db.session.add(network_function)
+ network_function2 = copy.deepcopy(network_function)
+ network_function2.nf_name = "pnf_to_be_deleted2"
+ db.session.add(network_function2)
+ db.session.commit()
+ aai_handler.execute()
+ self.assertEqual(mock_nf_delete.call_count, 2)
+
+ @patch('mod.aai_event_handler.AAIEventHandler.apply_nfs_to_subscriptions')
+ @patch('mod.network_function.NetworkFunction.set_nf_model_params')
+ @patch('mod.pmsh_config.AppConfig.get_from_topic')
+ def test_process_aai_update_events(self, mr_aai_mock, mock_set_sdnc_params, mock_apply_nfs):
+ mock_set_sdnc_params.return_value = True
+ mr_aai_mock.return_value = self.mr_aai_events
+ aai_handler = AAIEventHandler(self.app)
+ aai_handler.execute()
+ self.assertEqual(mock_apply_nfs.call_count, 1)
+
+ @patch('mod.pmsh_config.AppConfig.publish_to_topic', MagicMock(return_value=None))
+ @patch('mod.api.services.subscription_service.apply_measurement_grp_to_nfs')
+ @patch('mod.network_function.NetworkFunction.set_nf_model_params')
+ @patch('mod.pmsh_config.AppConfig.get_from_topic')
+ def test_process_aai_apply_nfs_to_subscriptions(self, mr_aai_mock, mock_set_sdnc_params,
+ apply_nfs_to_measure_grp):
mock_set_sdnc_params.return_value = True
- process_aai_events(self.mock_mr_sub, self.mock_mr_pub, self.mock_app, self.app_conf)
- self.assertEqual(mock_activate_sub.call_count, 2)
- mock_nf_delete.assert_called_once()
+ mr_aai_mock.return_value = self.mr_aai_events
+ aai_handler = AAIEventHandler(self.app)
+ subscription = SubscriptionModel(subscription_name='ExtraPM-All-gNB-R2B2',
+ operational_policy_name='operation_policy',
+ control_loop_name="control-loop",
+ status=AdministrativeState.UNLOCKED.value)
+ db.session.add(subscription)
+ db.session.commit()
+ generate_nf_filter_measure_grp('ExtraPM-All-gNB-R2B', 'msr_grp_name')
+ generate_nf_filter_measure_grp('ExtraPM-All-gNB-R2B2', 'msr_grp_name2')
+ db.session.commit()
+ aai_handler.execute()
+ self.assertEqual(apply_nfs_to_measure_grp.call_count, 2)
+
+ @patch('mod.pmsh_config.AppConfig.publish_to_topic', MagicMock(return_value=None))
+ @patch('mod.api.services.subscription_service.apply_measurement_grp_to_nfs')
+ @patch('mod.network_function.NetworkFunction.set_nf_model_params')
+ @patch('mod.logger.error')
+ @patch('mod.pmsh_config.AppConfig.get_from_topic')
+ def test_process_aai_apply_msg_failure(self, mr_aai_mock, mock_logger, mock_set_sdnc_params,
+ apply_nfs_to_measure_grp):
+ mock_set_sdnc_params.return_value = True
+ mr_aai_mock.return_value = self.mr_aai_events
+ apply_nfs_to_measure_grp.side_effect = Exception("publish failed")
+ aai_handler = AAIEventHandler(self.app)
+ generate_nf_filter_measure_grp('ExtraPM-All-gNB-R2B', 'msr_grp_name3')
+ db.session.commit()
+ aai_handler.execute()
+ mock_logger.assert_called_with('Failed to process AAI event for '
+ 'subscription: ExtraPM-All-gNB-R2B '
+ 'due to: publish failed')
+
+ @patch('mod.pmsh_config.AppConfig.publish_to_topic', MagicMock(return_value=None))
+ @patch('mod.network_function.NetworkFunction.set_nf_model_params')
+ @patch('mod.logger.error')
+ @patch('mod.pmsh_config.AppConfig.get_from_topic')
+ def test_process_aai__failure(self, mr_aai_mock, mock_logger, mock_set_sdnc_params):
+ mock_set_sdnc_params.return_value = True
+ mr_aai_mock.side_effect = Exception("AAI failure")
+ aai_handler = AAIEventHandler(self.app)
+ generate_nf_filter_measure_grp('ExtraPM-All-gNB-R2B', 'msr_grp_name3')
+ db.session.commit()
+ aai_handler.execute()
+ mock_logger.assert_called_with('Failed to process AAI event due to: AAI failure')
+
+
+def generate_nf_filter_measure_grp(sub_name, msg_name):
+ nf_filter = NetworkFunctionFilterModel(
+ subscription_name=sub_name, nf_names='{^pnf.*, ^vnf.*}',
+ model_invariant_ids='{}',
+ model_version_ids='{}',
+ model_names='{}')
+ measurement_group = MeasurementGroupModel(
+ subscription_name=sub_name, measurement_group_name=msg_name,
+ administrative_state='UNLOCKED', file_based_gp=15, file_location='pm.xml',
+ measurement_type=[], managed_object_dns_basic=[])
+ db.session.add(nf_filter)
+ db.session.add(measurement_group)
diff --git a/components/pm-subscription-handler/tests/test_controller.py b/components/pm-subscription-handler/tests/test_controller.py
index 77ab889c..962e8fb2 100755
--- a/components/pm-subscription-handler/tests/test_controller.py
+++ b/components/pm-subscription-handler/tests/test_controller.py
@@ -20,14 +20,16 @@ import os
from unittest.mock import patch, MagicMock
from http import HTTPStatus
-from mod import aai_client
-from mod.api.controller import status, post_subscription, get_subscription_by_name,\
- get_subscriptions
+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
from tests.base_setup import BaseClassSetup
from mod.api.db_models import SubscriptionModel, NfMeasureGroupRelationalModel
from mod.subscription import SubNfState
from mod.network_function import NetworkFunctionFilter
-from tests.base_setup import create_subscription_data, create_multiple_subscription_data
+from tests.base_setup import create_subscription_data, create_multiple_subscription_data, \
+ create_multiple_network_function_data
+from mod.api.services import measurement_group_service, nf_service
class ControllerTestCase(BaseClassSetup):
@@ -165,3 +167,35 @@ class ControllerTestCase(BaseClassSetup):
def test_get_subscriptions_api_exception(self):
subs, status_code = get_subscriptions()
self.assertEqual(status_code, HTTPStatus.INTERNAL_SERVER_ERROR.value)
+
+ def test_get_meas_group_with_nfs_api(self):
+ sub = create_subscription_data('sub1')
+ nf_list = create_multiple_network_function_data(['pnf101', 'pnf102'])
+ measurement_group_service.save_measurement_group(sub.measurement_groups[0].
+ serialize()['measurementGroup'],
+ sub.subscription_name)
+ for nf in nf_list:
+ nf_service.save_nf(nf)
+ measurement_group_service. \
+ apply_nf_status_to_measurement_group(nf.nf_name, sub.measurement_groups[0].
+ measurement_group_name,
+ SubNfState.PENDING_CREATE.value)
+ db.session.commit()
+ mg_with_nfs, status_code = get_meas_group_with_nfs('sub1', 'MG1')
+ self.assertEqual(status_code, HTTPStatus.OK.value)
+ self.assertEqual(mg_with_nfs['subscriptionName'], 'sub1')
+ self.assertEqual(mg_with_nfs['measurementGroupName'], 'MG1')
+ self.assertEqual(mg_with_nfs['administrativeState'], 'UNLOCKED')
+ self.assertEqual(len(mg_with_nfs['networkFunctions']), 2)
+
+ def test_get_meas_group_with_nfs_api_none(self):
+ error, status_code = get_meas_group_with_nfs('sub1', 'MG1')
+ self.assertEqual(error['error'], 'measurement group was not defined with '
+ 'the sub name: sub1 and meas group name: MG1')
+ self.assertEqual(status_code, HTTPStatus.NOT_FOUND.value)
+
+ @patch('mod.api.services.measurement_group_service.query_meas_group_by_name',
+ MagicMock(side_effect=Exception('something failed')))
+ def test_get_meas_group_with_nfs_api_exception(self):
+ error, status_code = get_meas_group_with_nfs('sub1', 'MG1')
+ self.assertEqual(status_code, HTTPStatus.INTERNAL_SERVER_ERROR.value)
diff --git a/components/pm-subscription-handler/tests/test_subscription_handler.py b/components/pm-subscription-handler/tests/test_subscription_handler.py
index fe338327..1843eb44 100644
--- a/components/pm-subscription-handler/tests/test_subscription_handler.py
+++ b/components/pm-subscription-handler/tests/test_subscription_handler.py
@@ -61,8 +61,6 @@ class SubscriptionHandlerTest(BaseClassSetup):
mock_logger.assert_called_with('Administrative State did not change '
'in the app config: UNLOCKED')
- @patch('mod.subscription_handler.SubscriptionHandler._start_aai_event_thread',
- MagicMock())
@patch('mod.pmsh_utils.AppConfig.refresh_config', MagicMock(return_value=get_pmsh_config()))
@patch('mod.subscription.Subscription.get_local_sub_admin_state')
@patch('mod.subscription.Subscription.create_subscription_on_nfs')
@@ -91,7 +89,6 @@ class SubscriptionHandlerTest(BaseClassSetup):
sub_handler.execute()
mock_deactivate_sub.assert_called_with(self.nfs, self.mock_mr_pub)
- @patch('mod.subscription_handler.SubscriptionHandler._start_aai_event_thread', MagicMock())
@patch('mod.pmsh_utils.AppConfig.refresh_config', MagicMock(return_value=get_pmsh_config()))
@patch('mod.subscription.Subscription.create_subscription_on_nfs')
@patch('mod.logger.error')
diff --git a/components/pm-subscription-handler/tox.ini b/components/pm-subscription-handler/tox.ini
index 6f0cb630..38acbbb8 100644
--- a/components/pm-subscription-handler/tox.ini
+++ b/components/pm-subscription-handler/tox.ini
@@ -31,7 +31,7 @@ deps=
setenv =
PYTHONPATH={toxinidir}/pmsh_service:{toxinidir}/pmsh_service/mod:{toxinidir}/tests
commands=
- pytest --junitxml xunit-results.xml --cov pmsh_service --cov-report xml --cov-report term \
+ pytest --junitxml xunit-results.xml --cov pmsh_service --cov-report xml --cov-report term-missing \
tests --verbose --cov-fail-under=70
[testenv:flake8]