[PMSSH] Add sdnc model info to event publish 85/112285/5 1.1.1-pmsh
authorefiacor <fiachra.corcoran@est.tech>
Tue, 8 Sep 2020 15:26:50 +0000 (16:26 +0100)
committerefiacor <fiachra.corcoran@est.tech>
Thu, 10 Sep 2020 12:00:50 +0000 (13:00 +0100)
Signed-off-by: efiacor <fiachra.corcoran@est.tech>
Change-Id: I89e25e99a2d541a29cf4a8dde986fab5b1812c9e
Issue-ID: DCAEGEN2-2405

25 files changed:
components/pm-subscription-handler/Changelog.md
components/pm-subscription-handler/pmsh_service/mod/aai_client.py
components/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py
components/pm-subscription-handler/pmsh_service/mod/api/db_models.py
components/pm-subscription-handler/pmsh_service/mod/network_function.py
components/pm-subscription-handler/pmsh_service/mod/pmsh_utils.py
components/pm-subscription-handler/pmsh_service/mod/subscription.py
components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py
components/pm-subscription-handler/tests/base_setup.py [new file with mode: 0755]
components/pm-subscription-handler/tests/data/aai_model_info.json [new file with mode: 0644]
components/pm-subscription-handler/tests/data/aai_model_info_no_sdnc.json [new file with mode: 0644]
components/pm-subscription-handler/tests/data/cbs_data_1.json
components/pm-subscription-handler/tests/data/filter_test_data.json
components/pm-subscription-handler/tests/data/pm_subscription_event.json
components/pm-subscription-handler/tests/log_config.yaml
components/pm-subscription-handler/tests/test_aai_event_handler.py
components/pm-subscription-handler/tests/test_aai_service.py
components/pm-subscription-handler/tests/test_controller.py
components/pm-subscription-handler/tests/test_exit_handler.py
components/pm-subscription-handler/tests/test_network_function.py
components/pm-subscription-handler/tests/test_network_function_filter.py
components/pm-subscription-handler/tests/test_pmsh_utils.py
components/pm-subscription-handler/tests/test_policy_response_handler.py
components/pm-subscription-handler/tests/test_subscription.py
components/pm-subscription-handler/tests/test_subscription_handler.py

index 8f4ce1b..1d97b58 100755 (executable)
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Changed
 * Moved to alpine base image (DCAEGEN2-2292)
 * Added model-invariant-id and model-version-id to filter (DCAEGEN2-2151)
+* Added support for multiple CDS blueprints (DCAEGEN2-2405) 
 
 ## [1.1.0]
 ### Changed
index c8dea9f..73bf8d4 100755 (executable)
@@ -21,9 +21,9 @@ from os import environ
 import requests
 from requests.auth import HTTPBasicAuth
 
+import mod.network_function
+import mod.pmsh_utils
 from mod import logger
-from mod.network_function import NetworkFunction
-from mod.pmsh_utils import mdc_handler
 
 
 def get_pmsh_nfs_from_aai(app_conf):
@@ -34,21 +34,20 @@ def get_pmsh_nfs_from_aai(app_conf):
         app_conf (AppConfig): the AppConfig object.
 
     Returns:
-        set(NetworkFunctions): set of NetworkFunctions.
+        NetworkFunctions (list): list of NetworkFunctions.
 
     Raises:
         RuntimeError: if AAI Network Function data cannot be retrieved.
     """
     aai_nf_data = _get_all_aai_nf_data(app_conf)
     if aai_nf_data:
-        nfs = _filter_nf_data(aai_nf_data, app_conf.nf_filter)
+        nfs = _filter_nf_data(aai_nf_data, app_conf)
     else:
         raise RuntimeError('Failed to get data from AAI')
     return nfs
 
 
-@mdc_handler
-def _get_all_aai_nf_data(app_conf, **kwargs):
+def _get_all_aai_nf_data(app_conf):
     """
     Return queried nf data from the AAI service.
 
@@ -61,24 +60,19 @@ def _get_all_aai_nf_data(app_conf, **kwargs):
     nf_data = None
     try:
         session = requests.Session()
-        aai_endpoint = f'{_get_aai_service_url()}{"/aai/v19/query"}'
+        aai_named_query_endpoint = f'{_get_aai_service_url()}{"/query"}'
         logger.info('Fetching XNFs from AAI.')
-        headers = {'accept': 'application/json',
-                   'content-type': 'application/json',
-                   'x-fromappid': 'dcae-pmsh',
-                   'x-transactionid': kwargs['request_id'],
-                   'InvocationID': kwargs['invocation_id'],
-                   'RequestID': kwargs['request_id']}
-        json_data = """
-                    {'start':
-                        ['network/pnfs',
-                        'network/generic-vnfs']
-                    }"""
+        headers = _get_aai_request_headers()
+        data = """
+                {'start':
+                    ['network/pnfs',
+                    'network/generic-vnfs']
+                }"""
         params = {'format': 'simple', 'nodesOnly': 'true'}
-        response = session.put(aai_endpoint, headers=headers,
+        response = session.put(aai_named_query_endpoint, headers=headers,
                                auth=HTTPBasicAuth(app_conf.aaf_creds.get('aaf_id'),
                                                   app_conf.aaf_creds.get('aaf_pass')),
-                               data=json_data, params=params,
+                               data=data, params=params,
                                verify=(app_conf.ca_cert_path if app_conf.enable_tls else False),
                                cert=(app_conf.cert_params if app_conf.enable_tls else None))
         response.raise_for_status()
@@ -103,39 +97,89 @@ def _get_aai_service_url():
     """
     try:
         aai_ssl_port = environ['AAI_SERVICE_PORT']
-        return f'https://aai:{aai_ssl_port}'
+        return f'https://aai:{aai_ssl_port}/aai/v20'
     except KeyError as e:
         logger.error(f'Failed to get AAI env var: {e}', exc_info=True)
         raise
 
 
-def _filter_nf_data(nf_data, nf_filter):
+@mod.pmsh_utils.mdc_handler
+def _get_aai_request_headers(**kwargs):
+    return {'accept': 'application/json',
+            'content-type': 'application/json',
+            'x-fromappid': 'dcae-pmsh',
+            'x-transactionid': kwargs['request_id'],
+            'InvocationID': kwargs['invocation_id'],
+            'RequestID': kwargs['request_id']}
+
+
+def _filter_nf_data(nf_data, app_conf):
     """
     Returns a list of filtered NetworkFunctions using the nf_filter.
 
     Args:
-        nf_data(dict): the nf json data from AAI.
-        nf_filter(NetworkFunctionFilter): the NetworkFunctionFilter to be applied.
+        nf_data (dict): the nf json data from AAI.
+        app_conf (AppConfig): the AppConfig object.
 
     Returns:
-        set(NetworkFunctions): a set of filtered NetworkFunctions.
+        NetworkFunction (list): a list of filtered NetworkFunction Objects.
 
     Raises:
         KeyError: if AAI data cannot be parsed.
     """
-    nf_set = set()
+    nf_list = []
     try:
         for nf in nf_data['results']:
+            if nf['properties'].get('orchestration-status') != 'Active':
+                continue
             name_identifier = 'pnf-name' if nf['node-type'] == 'pnf' else 'vnf-name'
-            orchestration_status = nf['properties'].get('orchestration-status')
-            model_invariant_id = nf['properties'].get('model-invariant-id')
-            model_version_id = nf['properties'].get('model-version-id')
-            if nf_filter.is_nf_in_filter(nf['properties'].get(name_identifier),
-                                         model_invariant_id, model_version_id, orchestration_status):
-                nf_set.add(NetworkFunction(
-                    nf_name=nf['properties'].get(name_identifier),
-                    orchestration_status=orchestration_status))
+            new_nf = mod.network_function.NetworkFunction(
+                nf_name=nf['properties'].get(name_identifier),
+                model_invariant_id=nf['properties'].get('model-invariant-id'),
+                model_version_id=nf['properties'].get('model-version-id'))
+            if not new_nf.set_sdnc_params(app_conf):
+                continue
+            if app_conf.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)
         raise
-    return nf_set
+    return nf_list
+
+
+def get_aai_model_data(app_conf, model_invariant_id, model_version_id, nf_name):
+    """
+    Get the sdnc_model info for the xNF from AAI.
+
+    Args:
+        model_invariant_id (str): the AAI model-invariant-id
+        model_version_id (str): the AAI model-version-id
+        app_conf (AppConfig): the AppConfig object.
+        nf_name (str): The xNF name.
+
+    Returns:
+        json (dict): the sdnc_model json object.
+
+    Raises:
+        Exception: if AAI model data cannot be retrieved.
+    """
+    try:
+        session = requests.Session()
+        aai_model_ver_endpoint = \
+            f'{_get_aai_service_url()}/service-design-and-creation/models/model/' \
+            f'{model_invariant_id}/model-vers/model-ver/{model_version_id}'
+
+        logger.info(f'Fetching sdnc-model info for xNF: {nf_name} from AAI.')
+        headers = _get_aai_request_headers()
+        response = session.get(aai_model_ver_endpoint, headers=headers,
+                               auth=HTTPBasicAuth(app_conf.aaf_creds.get('aaf_id'),
+                                                  app_conf.aaf_creds.get('aaf_pass')),
+                               verify=(app_conf.ca_cert_path if app_conf.enable_tls else False),
+                               cert=(app_conf.cert_params if app_conf.enable_tls else None))
+        response.raise_for_status()
+        if response.ok:
+            data = json.loads(response.text)
+            logger.debug(f'Successfully fetched sdnc-model info from AAI: {data}')
+            return data
+    except Exception:
+        raise
index 9236932..61b42b5 100755 (executable)
@@ -58,28 +58,26 @@ def process_aai_events(mr_sub, mr_pub, app, app_conf):
                 entity_type = entry['event-header']['entity-type']
                 xnf_name = aai_entity['pnf-name'] if entity_type == XNFType.PNF.value \
                     else aai_entity['vnf-name']
-                new_status = aai_entity['orchestration-status']
-                model_invariant_id = aai_entity['model-invariant-id']
-                model_version_id = aai_entity['model-version-id']
-
-                if app_conf.nf_filter.is_nf_in_filter(xnf_name, model_invariant_id, model_version_id, new_status):
-                    _process_event(action, new_status, xnf_name, mr_pub, app_conf)
+                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,
+                                     model_invariant_id=aai_entity['model-invariant-id'],
+                                     model_version_id=aai_entity['model-version-id'])
+                if not nf.set_sdnc_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)
 
 
-def _process_event(action, new_status, xnf_name, mr_pub, app_conf):
+def _process_event(action, nf, mr_pub, app_conf):
     if action == AAIEvent.UPDATE.value:
-        logger.info(f'Update event found for network function {xnf_name}')
-        local_xnf = NetworkFunction.get(xnf_name)
-
-        if local_xnf is None:
-            app_conf.subscription.activate_subscription([NetworkFunction(
-                nf_name=xnf_name, orchestration_status=new_status)], mr_pub, app_conf)
-        else:
-            logger.debug(f"Update Event for network function {xnf_name} will not be processed "
-                         f" as it's state is set to {local_xnf.orchestration_status}.")
+        logger.info(f'Update event found for network function {nf.nf_name}')
+        app_conf.subscription.activate_subscription([nf], mr_pub, app_conf)
     elif action == AAIEvent.DELETE.value:
-        logger.info(f'Delete event found for network function {xnf_name}')
-        NetworkFunction.delete(nf_name=xnf_name)
-        logger.info(f'{xnf_name} successfully deleted.')
+        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.')
index 1d6f72b..ff172d1 100755 (executable)
@@ -56,19 +56,34 @@ class NetworkFunctionModel(db.Model):
     __tablename__ = 'network_functions'
     id = Column(Integer, primary_key=True, autoincrement=True)
     nf_name = Column(String(100), unique=True)
-    orchestration_status = Column(String(100))
+    model_invariant_id = Column(String(100))
+    model_version_id = Column(String(100))
+    sdnc_model_name = Column(String(100))
+    sdnc_model_version = Column(String(100))
 
     subscriptions = relationship(
         'NfSubRelationalModel',
         cascade='all, delete-orphan',
         backref='nf')
 
-    def __init__(self, nf_name, orchestration_status):
+    def __init__(self, nf_name, model_invariant_id, model_version_id, sdnc_model_name=None,
+                 sdnc_model_version=None):
         self.nf_name = nf_name
-        self.orchestration_status = orchestration_status
+        self.model_invariant_id = model_invariant_id
+        self.model_version_id = model_version_id
+        self.sdnc_model_name = sdnc_model_name
+        self.sdnc_model_version = sdnc_model_version
 
     def __repr__(self):
-        return f'nf_name: {self.nf_name}, orchestration_status: {self.orchestration_status}'
+        return str(self.to_nf())
+
+    def to_nf(self):
+        from mod.network_function import NetworkFunction
+        return NetworkFunction(**{'nf_name': self.nf_name,
+                                  'model_invariant_id': self.model_invariant_id,
+                                  'model_version_id': self.model_version_id,
+                                  'sdnc_model_name': self.sdnc_model_name,
+                                  'sdnc_model_version': self.sdnc_model_version})
 
 
 class NfSubRelationalModel(db.Model):
@@ -101,7 +116,11 @@ class NfSubRelationalModel(db.Model):
                 'nf_sub_status': self.nf_sub_status}
 
     def serialize_nf(self):
-        nf_orch_status = NetworkFunctionModel.query.filter(
-            NetworkFunctionModel.nf_name == self.nf_name).one_or_none().orchestration_status
-        return {'nf_name': self.nf_name, 'orchestration_status': nf_orch_status,
-                'nf_sub_status': self.nf_sub_status}
+        nf = NetworkFunctionModel.query.filter(
+            NetworkFunctionModel.nf_name == self.nf_name).one_or_none()
+        return {'nf_name': self.nf_name,
+                'nf_sub_status': self.nf_sub_status,
+                'model_invariant_id': nf.model_invariant_id,
+                'model_version_id': nf.model_version_id,
+                'sdnc_model_name': nf.sdnc_model_name,
+                'sdnc_model_version': nf.sdnc_model_version}
index 62cd546..fd94038 100755 (executable)
@@ -18,6 +18,7 @@
 
 import re
 
+import mod.aai_client
 from mod import logger, db
 from mod.api.db_models import NetworkFunctionModel
 
@@ -26,21 +27,34 @@ class NetworkFunction:
     def __init__(self, **kwargs):
         """ Object representation of the NetworkFunction. """
         self.nf_name = kwargs.get('nf_name')
-        self.orchestration_status = kwargs.get('orchestration_status')
+        self.model_invariant_id = kwargs.get('model_invariant_id')
+        self.model_version_id = kwargs.get('model_version_id')
+        self.sdnc_model_name = None
+        self.sdnc_model_version = None
 
     @classmethod
     def nf_def(cls):
-        return cls(nf_name=None, orchestration_status=None)
+        return cls(nf_name=None, model_invariant_id=None, model_version_id=None,
+                   sdnc_model_name=None, sdnc_model_version=None)
 
     def __str__(self):
-        return f'nf-name: {self.nf_name}, orchestration-status: {self.orchestration_status}'
+        return f'nf-name: {self.nf_name}, ' \
+               f'model-invariant-id: {self.model_invariant_id}, ' \
+               f'model-version-id: {self.model_version_id}, ' \
+               f'sdnc-model-name: {self.sdnc_model_name}, ' \
+               f'sdnc-model-version: {self.sdnc_model_version}'
 
     def __eq__(self, other):
-        return self.nf_name == other.nf_name and \
-            self.orchestration_status == other.orchestration_status
+        return \
+            self.nf_name == other.nf_name and \
+            self.model_invariant_id == other.model_invariant_id and \
+            self.model_version_id == other.model_version_id and \
+            self.sdnc_model_name == other.sdnc_model_name and \
+            self.sdnc_model_version == other.sdnc_model_version
 
     def __hash__(self):
-        return hash((self.nf_name, self.orchestration_status))
+        return hash((self.nf_name, self.model_invariant_id,
+                     self.model_version_id, self.sdnc_model_name, self.sdnc_model_version))
 
     def create(self):
         """ Creates a NetworkFunction database entry """
@@ -49,7 +63,10 @@ class NetworkFunction:
 
         if existing_nf is None:
             new_nf = NetworkFunctionModel(nf_name=self.nf_name,
-                                          orchestration_status=self.orchestration_status)
+                                          model_invariant_id=self.model_invariant_id,
+                                          model_version_id=self.model_version_id,
+                                          sdnc_model_name=self.sdnc_model_name,
+                                          sdnc_model_version=self.sdnc_model_version)
             db.session.add(new_nf)
             db.session.commit()
             logger.info(f'Network Function {new_nf.nf_name} successfully created.')
@@ -59,6 +76,23 @@ class NetworkFunction:
                          f' returning this network function..')
             return existing_nf
 
+    def set_sdnc_params(self, app_conf):
+        params_set = True
+        try:
+            sdnc_model_data = mod.aai_client.get_aai_model_data(app_conf, self.model_invariant_id,
+                                                                self.model_version_id, self.nf_name)
+            try:
+                self.sdnc_model_name = sdnc_model_data['sdnc-model-name']
+                self.sdnc_model_version = sdnc_model_data['sdnc-model-version']
+                return params_set
+            except KeyError as e:
+                logger.info(f'Skipping NF {self.nf_name} as there is no '
+                            f'sdnc-model data associated in AAI: {e}', exc_info=True)
+                return not params_set
+        except Exception as e:
+            logger.error(f'Failed to get sdnc-model info for XNFs from AAI: {e}', exc_info=True)
+            return not params_set
+
     @staticmethod
     def get(nf_name):
         """ Retrieves a network function
@@ -90,33 +124,27 @@ class NetworkFunction:
             db.session.commit()
 
 
-
 class NetworkFunctionFilter:
     def __init__(self, **kwargs):
         self.nf_names = kwargs.get('nfNames')
-        self.model_invariant_ids = kwargs.get('modelInvariantUUIDs')
+        self.model_invariant_ids = kwargs.get('modelInvariantIDs')
         self.model_version_ids = kwargs.get('modelVersionIDs')
         self.regex_matcher = re.compile('|'.join(raw_regex for raw_regex in self.nf_names))
 
-    def is_nf_in_filter(self, nf_name, model_invariant_id, model_version_id, orchestration_status):
-        """Match the nf name against regex values in Subscription.nfFilter.nfNames
+    def is_nf_in_filter(self, nf):
+        """Match the nf fields against values in Subscription.nfFilter
 
         Args:
-            nf_name (str): the AAI nf name.
-            invariant_uuid (str): the AAI model-invariant-id
-            uuid (str): the AAI model-version-id
-            orchestration_status (str): orchestration status of the nf
+            nf (NetworkFunction): The NF to be filtered.
 
         Returns:
             bool: True if matched, else False.
         """
         match = True
-        if orchestration_status != 'Active':
-            match = False
-        if self.nf_names and self.regex_matcher.search(nf_name) is None:
+        if self.nf_names and self.regex_matcher.search(nf.nf_name) is None:
             match = False
-        if self.model_invariant_ids and not model_invariant_id in self.model_invariant_ids:
+        if self.model_invariant_ids and nf.model_invariant_id not in self.model_invariant_ids:
             match = False
-        if self.model_version_ids and not model_version_id in self.model_version_ids:
+        if self.model_version_ids and nf.model_version_id not in self.model_version_ids:
             match = False
         return match
index fd24fca..24eade9 100755 (executable)
@@ -25,8 +25,8 @@ from onaplogging.mdcContext import MDC
 from requests.auth import HTTPBasicAuth
 from tenacity import wait_fixed, stop_after_attempt, retry, retry_if_exception_type
 
+import mod.network_function
 from mod import logger
-from mod.network_function import NetworkFunctionFilter
 from mod.subscription import Subscription
 
 
@@ -77,7 +77,7 @@ class AppConfig:
         self.operational_policy_name = conf['config'].get('operational_policy_name')
         self.control_loop_name = conf['config'].get('control_loop_name')
         self.subscription = Subscription(**conf['policy']['subscription'])
-        self.nf_filter = NetworkFunctionFilter(**self.subscription.nfFilter)
+        self.nf_filter = mod.network_function.NetworkFunctionFilter(**self.subscription.nfFilter)
 
     def __new__(cls, *args, **kwargs):
         if AppConfig.INSTANCE is None:
@@ -223,17 +223,17 @@ class _MrPub(_DmaapMrClient):
         except Exception as e:
             raise e
 
-    def publish_subscription_event_data(self, subscription, xnf_name, app_conf):
+    def publish_subscription_event_data(self, subscription, nf, app_conf):
         """
         Update the Subscription dict with xnf and policy name then publish to DMaaP MR topic.
 
         Args:
             subscription (Subscription): the `Subscription` <Subscription> object.
-            xnf_name (str): the xnf to include in the event.
+            nf (NetworkFunction): the NetworkFunction to include in the event.
             app_conf (AppConfig): the application configuration.
         """
         try:
-            subscription_event = subscription.prepare_subscription_event(xnf_name, app_conf)
+            subscription_event = subscription.prepare_subscription_event(nf, app_conf)
             self.publish_to_topic(subscription_event)
         except Exception as e:
             logger.error(f'Failed to publish to topic {self.topic_url}: {e}', exc_info=True)
index 97bfc40..21e2399 100755 (executable)
@@ -19,7 +19,6 @@ from enum import Enum
 
 from mod import db, logger
 from mod.api.db_models import SubscriptionModel, NfSubRelationalModel, NetworkFunctionModel
-from mod.network_function import NetworkFunction
 
 
 class SubNfState(Enum):
@@ -94,30 +93,11 @@ class Subscription:
             logger.error(f'Failed to update status of subscription: {self.subscriptionName}: {e}',
                          exc_info=True)
 
-    def delete_subscription(self):
-        """ Deletes a subscription and all its association from the database. A network function
-        that is only associated with the subscription being removed will also be deleted."""
-        try:
-            subscription = SubscriptionModel.query.filter(
-                SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
-            if subscription:
-                for nf_relationship in subscription.nfs:
-                    other_nf_relationship = NfSubRelationalModel.query.filter(
-                        NfSubRelationalModel.subscription_name != self.subscriptionName,
-                        NfSubRelationalModel.nf_name == nf_relationship.nf_name).one_or_none()
-                    if not other_nf_relationship:
-                        db.session.delete(nf_relationship.nf)
-                db.session.delete(subscription)
-                db.session.commit()
-        except Exception as e:
-            logger.error(f'Failed to delete subscription: {self.subscriptionName} '
-                         f'and it\'s relations from the DB: {e}', exc_info=True)
-
-    def prepare_subscription_event(self, xnf_name, app_conf):
+    def prepare_subscription_event(self, nf, app_conf):
         """Prepare the sub event for publishing
 
         Args:
-            xnf_name: the AAI xnf name.
+            nf (NetworkFunction): the AAI nf.
             app_conf (AppConfig): the application configuration.
 
         Returns:
@@ -125,14 +105,16 @@ class Subscription:
         """
         try:
             clean_sub = {k: v for k, v in self.__dict__.items() if k != 'nfFilter'}
-            sub_event = {'nfName': xnf_name, 'policyName': app_conf.operational_policy_name,
+            sub_event = {'nfName': nf.nf_name, 'blueprintName': nf.sdnc_model_name,
+                         'blueprintVersion': nf.sdnc_model_version,
+                         'policyName': app_conf.operational_policy_name,
                          'changeType': 'DELETE'
                          if self.administrativeState == AdministrativeState.LOCKED.value
                          else 'CREATE', 'closedLoopControlName': app_conf.control_loop_name,
                          'subscription': clean_sub}
             return sub_event
         except Exception as e:
-            logger.error(f'Failed to prep Sub event for xNF {xnf_name}: {e}', exc_info=True)
+            logger.error(f'Failed to prep Sub event for xNF {nf.nf_name}: {e}', exc_info=True)
             raise
 
     def add_network_function_to_subscription(self, nf, sub_model):
@@ -159,7 +141,7 @@ class Subscription:
             logger.error(f'Failed to add nf {nf.nf_name} to subscription '
                          f'{self.subscriptionName}: {e}', exc_info=True)
             logger.debug(f'Subscription {self.subscriptionName} now contains these XNFs:'
-                         f'{Subscription.get_nf_names_per_sub(self.subscriptionName)}')
+                         f'{[nf.nf_name for nf.nf_name in self.get_network_functions()]}')
 
     def get(self):
         """ Retrieves a SubscriptionModel object
@@ -189,32 +171,15 @@ class Subscription:
         """
         return SubscriptionModel.query.all()
 
-    @staticmethod
-    def get_nf_names_per_sub(subscription_name):
-        """ Retrieves a list of network function names related to the subscription
-
-        Args:
-            subscription_name (str): The subscription name
-
-        Returns:
-            list(str): List of network function names
-        """
-        nf_sub_rel = NfSubRelationalModel.query.filter(
-            NfSubRelationalModel.subscription_name == subscription_name).all()
-        list_of_nfs = []
-        for nf in nf_sub_rel:
-            list_of_nfs.append(nf.nf_name)
-
-        return list_of_nfs
-
     def activate_subscription(self, nfs, mr_pub, app_conf):
         logger.info(f'Activate subscription initiated for {self.subscriptionName}.')
         try:
+            existing_nfs = self.get_network_functions()
             sub_model = self.get()
-            for nf in nfs:
-                mr_pub.publish_subscription_event_data(self, nf.nf_name, app_conf)
+            for nf in set(nfs + existing_nfs):
                 logger.info(f'Publishing event to activate '
                             f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
+                mr_pub.publish_subscription_event_data(self, nf, app_conf)
                 self.add_network_function_to_subscription(nf, sub_model)
                 self.update_sub_nf_status(self.subscriptionName, SubNfState.PENDING_CREATE.value,
                                           nf.nf_name)
@@ -222,12 +187,12 @@ class Subscription:
             raise Exception(f'Error publishing activation event to MR: {err}')
 
     def deactivate_subscription(self, mr_pub, app_conf):
-        nfs = self.get_network_functions()
         try:
+            nfs = self.get_network_functions()
             if nfs:
                 logger.info(f'Deactivate subscription initiated for {self.subscriptionName}.')
                 for nf in nfs:
-                    mr_pub.publish_subscription_event_data(self, nf.nf_name, app_conf)
+                    mr_pub.publish_subscription_event_data(self, nf, app_conf)
                     logger.debug(f'Publishing Event to deactivate '
                                  f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
                     self.update_sub_nf_status(self.subscriptionName,
@@ -265,25 +230,13 @@ class Subscription:
             logger.error(f'Failed to update status of nf: {nf_name} for subscription: '
                          f'{subscription_name}: {e}', exc_info=True)
 
-    def _get_nf_models(self):
+    def get_network_functions(self):
         nf_sub_relationships = NfSubRelationalModel.query.filter(
             NfSubRelationalModel.subscription_name == self.subscriptionName)
-        nf_models = []
+        nfs = []
         for nf_sub_entry in nf_sub_relationships:
             nf_model_object = NetworkFunctionModel.query.filter(
                 NetworkFunctionModel.nf_name == nf_sub_entry.nf_name).one_or_none()
-            nf_models.append(nf_model_object)
-
-        return nf_models
-
-    def get_network_functions(self):
-        nfs = []
-        nf_models = self._get_nf_models()
-        for nf_model in nf_models:
-            nf = NetworkFunction(
-                nf_name=nf_model.nf_name,
-                orchestration_status=nf_model.orchestration_status
-            )
-            nfs.append(nf)
+            nfs.append(nf_model_object.to_nf())
 
         return nfs
index e74a173..6de702f 100644 (file)
@@ -54,8 +54,8 @@ class SubscriptionHandler:
     def _activate(self, local_admin_state, new_administrative_state):
         logger.info(f'Administrative State has changed from {local_admin_state} '
                     f'to {new_administrative_state}.')
-        existing_nfs_in_aai = aai.get_pmsh_nfs_from_aai(self.app_conf)
-        self.app_conf.subscription.activate_subscription(existing_nfs_in_aai, self.mr_pub,
+        nfs_in_aai = aai.get_pmsh_nfs_from_aai(self.app_conf)
+        self.app_conf.subscription.activate_subscription(nfs_in_aai, self.mr_pub,
                                                          self.app_conf)
         self.app_conf.subscription.update_subscription_status()
         logger.info('Start listening for new NFs on AAI-EVENT topic in MR.')
diff --git a/components/pm-subscription-handler/tests/base_setup.py b/components/pm-subscription-handler/tests/base_setup.py
new file mode 100755 (executable)
index 0000000..2e50dde
--- /dev/null
@@ -0,0 +1,57 @@
+# ============LICENSE_START===================================================
+#  Copyright (C) 2020 Nordix Foundation.
+# ============================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=====================================================
+import json
+import os
+from unittest import TestCase
+from unittest.mock import patch, MagicMock
+
+from mod import create_app, db
+from mod.pmsh_utils import AppConfig
+
+
+def get_pmsh_config():
+    with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
+        return json.load(data)
+
+
+class BaseClassSetup(TestCase):
+    app = None
+    app_context = None
+
+    @classmethod
+    @patch('mod.get_db_connection_url', MagicMock(return_value='sqlite://'))
+    @patch('mod.update_logging_config', MagicMock())
+    def setUpClass(cls):
+        os.environ['LOGGER_CONFIG'] = os.path.join(os.path.dirname(__file__), 'log_config.yaml')
+        os.environ['LOGS_PATH'] = '.'
+        cls.app = create_app()
+        cls.app_context = cls.app.app_context()
+        cls.app_context.push()
+
+    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config', MagicMock(return_value=get_pmsh_config()))
+    def setUp(self):
+        os.environ['AAI_SERVICE_PORT'] = '8443'
+        db.create_all()
+        self.app_conf = AppConfig()
+
+    def tearDown(self):
+        db.drop_all()
+
+    @classmethod
+    def tearDownClass(cls):
+        db.session.remove()
diff --git a/components/pm-subscription-handler/tests/data/aai_model_info.json b/components/pm-subscription-handler/tests/data/aai_model_info.json
new file mode 100644 (file)
index 0000000..625de52
--- /dev/null
@@ -0,0 +1,36 @@
+{
+    "model-version-id": "6d25b637-8bca-47e2-af1a-61258424183d",
+    "model-name": "PNF102",
+    "model-version": "1.0",
+    "model-description": "sartgserg",
+    "sdnc-model-name": "pm_control",
+    "sdnc-model-version": "1.0.0",
+    "resource-version": "1598626661947",
+    "relationship-list": {
+        "relationship": [
+            {
+                "related-to": "model-element",
+                "relationship-label": "org.onap.relationships.inventory.IsA",
+                "related-link": "/aai/v20/service-design-and-creation/models/model/c1a44771-3aa8-4888-a4f4-be89d1caa0cb/model-vers/model-ver/7256a992-10a7-4ac8-8c2c-63c67e5c48c8/model-elements/model-element/fddc70fe-8343-48c1-af2e-b54f551a32ee/model-elements/model-element/7bff45b7-8254-44e5-b7ad-6e10dee6dfc3",
+                "relationship-data": [
+                    {
+                        "relationship-key": "model.model-invariant-id",
+                        "relationship-value": "c1a44771-3aa8-4888-a4f4-be89d1caa0cb"
+                    },
+                    {
+                        "relationship-key": "model-ver.model-version-id",
+                        "relationship-value": "7256a992-10a7-4ac8-8c2c-63c67e5c48c8"
+                    },
+                    {
+                        "relationship-key": "model-element.model-element-uuid",
+                        "relationship-value": "fddc70fe-8343-48c1-af2e-b54f551a32ee"
+                    },
+                    {
+                        "relationship-key": "model-element.model-element-uuid",
+                        "relationship-value": "7bff45b7-8254-44e5-b7ad-6e10dee6dfc3"
+                    }
+                ]
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/components/pm-subscription-handler/tests/data/aai_model_info_no_sdnc.json b/components/pm-subscription-handler/tests/data/aai_model_info_no_sdnc.json
new file mode 100644 (file)
index 0000000..fffdfa9
--- /dev/null
@@ -0,0 +1,34 @@
+{
+    "model-version-id": "b7469cc5-be51-41cc-b37f-361537656771",
+    "model-name": "PNF104",
+    "model-version": "1.0",
+    "model-description": "aervaer",
+    "resource-version": "1599053901976",
+    "relationship-list": {
+        "relationship": [
+            {
+                "related-to": "model-element",
+                "relationship-label": "org.onap.relationships.inventory.IsA",
+                "related-link": "/aai/v20/service-design-and-creation/models/model/9f751399-59f7-46e4-aaf4-f78b7bf8af84/model-vers/model-ver/135b8bab-b24c-4c6d-b042-dcd7fcd937ba/model-elements/model-element/3c323039-c6ab-47bb-b8e0-d1e57a78fbea/model-elements/model-element/8a424916-020f-4c83-b785-7c04ac06567a",
+                "relationship-data": [
+                    {
+                        "relationship-key": "model.model-invariant-id",
+                        "relationship-value": "9f751399-59f7-46e4-aaf4-f78b7bf8af84"
+                    },
+                    {
+                        "relationship-key": "model-ver.model-version-id",
+                        "relationship-value": "135b8bab-b24c-4c6d-b042-dcd7fcd937ba"
+                    },
+                    {
+                        "relationship-key": "model-element.model-element-uuid",
+                        "relationship-value": "3c323039-c6ab-47bb-b8e0-d1e57a78fbea"
+                    },
+                    {
+                        "relationship-key": "model-element.model-element-uuid",
+                        "relationship-value": "8a424916-020f-4c83-b785-7c04ac06567a"
+                    }
+                ]
+            }
+        ]
+    }
+}
\ No newline at end of file
index 5c2e029..8651534 100644 (file)
@@ -10,7 +10,7 @@
                "^pnf.*",
                "^vnf.*"
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
 
             ],
             "modelVersionIDs": [
index 28dc61d..0d8fc7c 100644 (file)
@@ -6,7 +6,7 @@
                "^pnf.*",
                "^vnf.*"
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
 
             ],
             "modelVersionIDs": [
@@ -16,7 +16,6 @@
       "nfName": "pnf1",
       "modelInvariantUUID": "7129e420-d396-4efb-af02-6b83499b12f8",
       "modelVersionID": "e80a6ae3-cafd-4d24-850d-e14c084a5ca9",
-      "orchestration_status": "Active",
       "expectedResult": true
     },
     {
@@ -26,7 +25,7 @@
                "^pnf.*",
                "^vnf.*"
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
 
             ],
             "modelVersionIDs": [
       "nfName": "PNF-33",
       "modelInvariantUUID": "7129e420-d396-4efb-af02-6b83499b12f8",
       "modelVersionID": "e80a6ae3-cafd-4d24-850d-e14c084a5ca9",
-      "orchestration_status": "Active",
       "expectedResult": false
     },
     {
-      "testName": "test_filter_true_on_modelInvariantUUIDs",
+      "testName": "test_filter_true_on_modelInvariantIDs",
       "nfFilter":{
             "nfNames":[
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
                 "5845y423-g654-6fju-po78-8n53154532k6",
                 "7129e420-d396-4efb-af02-6b83499b12f8"
             ],
       "nfName": "pnf1",
       "modelInvariantUUID": "7129e420-d396-4efb-af02-6b83499b12f8",
       "modelVersionID": "e80a6ae3-cafd-4d24-850d-e14c084a5ca9",
-      "orchestration_status": "Active",
       "expectedResult": true
     },
     {
-      "testName": "test_filter_false_on_modelInvariantUUIDs",
+      "testName": "test_filter_false_on_modelInvariantIDs",
       "nfFilter":{
             "nfNames":[
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
                 "5845y423-g654-6fju-po78-8n53154532k6",
                 "7129e420-d396-4efb-af02-6b83499b12f8"
             ],
       "nfName": "pnf1",
       "modelInvariantUUID": "WrongModelInvariantUUID",
       "modelVersionID": "e80a6ae3-cafd-4d24-850d-e14c084a5ca9",
-      "orchestration_status": "Active",
       "expectedResult": false
     },
     {
-      "testName": "test_filter_false_on_modelInvariantUUIDs_being_false_and_pnfname_being_true",
+      "testName": "test_filter_false_on_modelInvariantIDs_being_false_and_pnfname_being_true",
       "nfFilter":{
             "nfNames":[
                "^pnf.*",
                "^vnf.*"
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
                 "7129e420-d396-4efb-af02-6b83499b12f8"
             ],
             "modelVersionIDs": [
@@ -94,7 +90,6 @@
       "nfName": "pnf1",
       "modelInvariantUUID": "WrongModelInvariantUUID",
       "modelVersionID": "e80a6ae3-cafd-4d24-850d-e14c084a5ca9",
-      "orchestration_status": "Active",
       "expectedResult": false
     },
     {
       "nfFilter":{
             "nfNames":[
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
             ],
             "modelVersionIDs": [
                 "e80a6ae3-cafd-4d24-850d-e14c084a5ca9"
       "nfName": "pnf1",
       "modelInvariantUUID": "7129e420-d396-4efb-af02-6b83499b12f8",
       "modelVersionID": "e80a6ae3-cafd-4d24-850d-e14c084a5ca9",
-      "orchestration_status": "Active",
       "expectedResult": true
     },
     {
       "nfFilter":{
             "nfNames":[
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
             ],
             "modelVersionIDs": [
                 "e80a6ae3-cafd-4d24-850d-e14c084a5ca9"
       "nfName": "pnf1",
       "modelInvariantUUID": "7129e420-d396-4efb-af02-6b83499b12f8",
       "modelVersionID": "WrongModelVersionID",
-      "orchestration_status": "Active",
       "expectedResult": false
     },
     {
                  "^pnf.*",
                  "^vnf.*"
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
             ],
             "modelVersionIDs": [
                 "e80a6ae3-cafd-4d24-850d-e14c084a5ca9"
       "nfName": "pnf1",
       "modelInvariantUUID": "7129e420-d396-4efb-af02-6b83499b12f8",
       "modelVersionID": "WrongModelVersionID",
-      "orchestration_status": "Active",
-      "expectedResult": false
-    },
-    {
-      "testName": "test_filter_false_on_OrchestrationStatus",
-      "nfFilter":{
-            "nfNames":[
-                 "^pnf.*",
-                 "^vnf.*"
-            ],
-            "modelInvariantUUIDs": [
-            ],
-            "modelVersionIDs": [
-            ]
-         },
-      "nfName": "pnf1",
-      "modelInvariantUUID": "7129e420-d396-4efb-af02-6b83499b12f8",
-      "modelVersionID": "e80a6ae3-cafd-4d24-850d-e14c084a5ca9",
-      "orchestration_status": "Inventoried",
       "expectedResult": false
     }
-  ]
\ No newline at end of file
+]
index e190aa2..9416ec2 100755 (executable)
@@ -1,5 +1,7 @@
 {\r
    "nfName":"pnf_1",\r
+   "blueprintName": "some-name",\r
+   "blueprintVersion": "some-version",\r
    "policyName":"pmsh-operational-policy",\r
    "changeType":"CREATE",\r
    "closedLoopControlName":"pmsh-control-loop",\r
index 1c3abd8..f3f3977 100755 (executable)
@@ -5,9 +5,16 @@ disable_existing_loggers: true
 loggers:
   onap_logger:
     level: DEBUG
-    handlers: [stdout_handler]
+    handlers: [onap_log_handler, stdout_handler]
     propagate: false
 handlers:
+  onap_log_handler:
+    class: logging.handlers.RotatingFileHandler
+    filename: ./application.log
+    mode: a
+    maxBytes: 10000000
+    backupCount: 10
+    formatter: mdcFormatter
   stdout_handler:
     class: logging.StreamHandler
     formatter: mdcFormatter
index 9ac7647..dc5243d 100755 (executable)
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=====================================================
 import json
-import os
 from os import path
-from test.support import EnvironmentVarGuard
-from unittest import TestCase
 from unittest.mock import patch, Mock
 
-from mod import create_app, db
 from mod.aai_event_handler import process_aai_events
-from mod.network_function import NetworkFunction
-from mod.pmsh_utils import AppConfig
+from tests.base_setup import BaseClassSetup
 
 
-class AAIEventHandlerTest(TestCase):
+class AAIEventHandlerTest(BaseClassSetup):
 
-    @patch('mod.get_db_connection_url')
-    @patch('mod.update_logging_config')
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    def setUp(self, mock_get_pmsh_config, mock_update_config, mock_get_db_url):
-        mock_get_db_url.return_value = 'sqlite://'
-        with open(path.join(path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+    def setUp(self):
+        super().setUp()
         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.env = EnvironmentVarGuard()
-        self.env.set('LOGGER_CONFIG', os.path.join(os.path.dirname(__file__), 'log_config.yaml'))
         self.mock_mr_sub = Mock(get_from_topic=Mock(return_value=self.mr_aai_events))
         self.mock_mr_pub = Mock()
         self.mock_app = Mock()
-        self.app = create_app()
-        self.app_context = self.app.app_context()
-        self.app_context.push()
-        db.create_all()
 
     def tearDown(self):
-        db.session.remove()
-        db.drop_all()
-        self.app_context.pop()
+        super().tearDown()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
 
+    @patch('mod.network_function.NetworkFunction.set_sdnc_params')
     @patch('mod.subscription.Subscription.activate_subscription')
     @patch('mod.aai_event_handler.NetworkFunction.delete')
-    @patch('mod.aai_event_handler.NetworkFunction.get')
-    def test_process_aai_update_and_delete_events(self, mock_nf_get, mock_nf_delete,
-                                                  mock_activate_sub):
-        pnf_already_active = NetworkFunction(nf_name='pnf_already_active',
-                                             orchestration_status='Active')
-        mock_nf_get.side_effect = [None, pnf_already_active]
-        expected_nf_for_processing = NetworkFunction(
-            nf_name='pnf_newly_discovered', orchestration_status='Active')
-
+    def test_process_aai_update_and_delete_events(self, mock_nf_delete, mock_activate_sub,
+                                                  mock_set_sdnc_params):
+        mock_set_sdnc_params.return_value = True
         process_aai_events(self.mock_mr_sub, self.mock_mr_pub, self.mock_app, self.app_conf)
-
-        mock_activate_sub.assert_called_once_with([expected_nf_for_processing],
-                                                  self.mock_mr_pub, self.app_conf)
-        mock_nf_delete.assert_called_once_with(nf_name='pnf_to_be_deleted')
+        self.assertEqual(mock_activate_sub.call_count, 2)
+        mock_nf_delete.assert_called_once()
index 4b7be15..7a3b846 100644 (file)
 # ============LICENSE_END=====================================================
 import json
 import os
-from test.support import EnvironmentVarGuard
-from unittest import mock, TestCase
+from unittest import mock
 from unittest.mock import patch
 
 import responses
-from requests import Session
+from requests import Session, HTTPError
 
 import mod.aai_client as aai_client
-from mod import create_app
-from mod.pmsh_utils import AppConfig
-
-
-class AaiClientTestCase(TestCase):
-
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    @patch('mod.update_logging_config')
-    @patch('mod.get_db_connection_url')
-    def setUp(self, mock_get_db_url, mock_update_config, mock_get_pmsh_config):
-        mock_get_db_url.return_value = 'sqlite://'
-        self.env = EnvironmentVarGuard()
-        self.env.set('AAI_SERVICE_PORT', '8443')
-        self.env.set('LOGGER_CONFIG', os.path.join(os.path.dirname(__file__), 'log_config.yaml'))
-        with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+from tests.base_setup import BaseClassSetup
+
+
+class AaiClientTestCase(BaseClassSetup):
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+    def setUp(self):
+        super().setUp()
         with open(os.path.join(os.path.dirname(__file__), 'data/aai_xnfs.json'), 'r') as data:
             self.aai_response_data = data.read()
-        self.app = create_app()
+        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('mod.network_function.NetworkFunction.set_sdnc_params')
+    @patch.object(Session, 'get')
     @patch.object(Session, 'put')
-    def test_aai_client_get_pm_sub_data_success(self, mock_session):
-        mock_session.return_value.status_code = 200
-        mock_session.return_value.text = self.aai_response_data
+    def test_aai_client_get_pm_sub_data_success(self, mock_put_session, mock_get_session,
+                                                mock_get_sdnc_params):
+        mock_put_session.return_value.status_code = 200
+        mock_put_session.return_value.text = self.aai_response_data
+        mock_get_session.return_value.status_code = 200
+        mock_get_session.return_value.text = self.good_model_info
+        mock_get_sdnc_params.return_value = True
         xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf)
         self.assertEqual(self.app_conf.subscription.subscriptionName, 'ExtraPM-All-gNB-R2B')
         self.assertEqual(self.app_conf.subscription.administrativeState, 'UNLOCKED')
@@ -61,26 +67,48 @@ class AaiClientTestCase(TestCase):
         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.cbs_data)
+                aai_client.get_pmsh_nfs_from_aai(self.app_conf)
 
     @responses.activate
     def test_aai_client_get_all_aai_xnf_data_not_found(self):
         responses.add(responses.PUT,
-                      'https://1.2.3.4:8443/aai/v19/query?format=simple&nodesOnly=true',
+                      'https://1.2.3.4:8443/aai/v20/query?format=simple&nodesOnly=true',
                       json={'error': 'not found'}, status=404)
         self.assertIsNone(aai_client._get_all_aai_nf_data(self.app_conf))
 
     @responses.activate
     def test_aai_client_get_all_aai_xnf_data_success(self):
         responses.add(responses.PUT,
-                      'https://aai:8443/aai/v19/query?format=simple&nodesOnly=true',
+                      'https://aai:8443/aai/v20/query?format=simple&nodesOnly=true',
                       json={'dummy_data': 'blah_blah'}, status=200)
         self.assertIsNotNone(aai_client._get_all_aai_nf_data(self.app_conf))
 
+    @responses.activate
+    def test_aai_client_get_sdnc_params_success(self):
+        responses.add(responses.GET,
+                      'https://aai:8443/aai/v20/service-design-and-creation/models/model/'
+                      '6fb9f466-7a79-4109-a2a3-72b340aca53d/model-vers/model-ver/'
+                      '6d25b637-8bca-47e2-af1a-61258424183d',
+                      json=json.loads(self.good_model_info), status=200)
+        self.assertIsNotNone(aai_client.get_aai_model_data(self.app_conf,
+                                                           '6fb9f466-7a79-4109-a2a3-72b340aca53d',
+                                                           '6d25b637-8bca-47e2-af1a-61258424183d',
+                                                           'pnf_1'))
+
+    @responses.activate
+    def test_aai_client_get_sdnc_params_fail(self):
+        responses.add(responses.GET,
+                      'https://aai:8443/aai/v20/service-design-and-creation/models/model/'
+                      '9fb9f466-7a79-4109-a2a3-72b340aca53d/model-vers/model-ver/'
+                      'b7469cc5-be51-41cc-b37f-361537656771', status=404)
+        with self.assertRaises(HTTPError):
+            aai_client.get_aai_model_data(self.app_conf, '9fb9f466-7a79-4109-a2a3-72b340aca53d',
+                                          'b7469cc5-be51-41cc-b37f-361537656771', 'pnf_2')
+
     def test_aai_client_get_aai_service_url_fail(self):
-        self.env.clear()
+        os.environ.clear()
         with self.assertRaises(KeyError):
             aai_client._get_aai_service_url()
 
     def test_aai_client_get_aai_service_url_success(self):
-        self.assertEqual('https://aai:8443', aai_client._get_aai_service_url())
+        self.assertEqual('https://aai:8443/aai/v20', aai_client._get_aai_service_url())
index 4fcecc3..c38cd97 100755 (executable)
 # ============LICENSE_END=====================================================
 import json
 import os
-import unittest
-from test.support import EnvironmentVarGuard
 from unittest.mock import patch
 
+import responses
 from requests import Session
 
-from mod import aai_client, create_app, db
+from mod import aai_client
 from mod.api.controller import status, get_all_sub_to_nf_relations
-from mod.network_function import NetworkFunction
-from mod.pmsh_utils import AppConfig
+from tests.base_setup import BaseClassSetup
 
 
-class ControllerTestCase(unittest.TestCase):
+class ControllerTestCase(BaseClassSetup):
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    @patch('mod.update_logging_config')
-    @patch('mod.get_db_connection_url')
-    @patch.object(Session, 'put')
-    def setUp(self, mock_session, mock_get_db_url, mock_update_config, mock_get_pmsh_config):
-        mock_get_db_url.return_value = 'sqlite://'
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+    def setUp(self):
+        super().setUp()
         with open(os.path.join(os.path.dirname(__file__), 'data/aai_xnfs.json'), 'r') as data:
             self.aai_response_data = data.read()
-        mock_session.return_value.status_code = 200
-        mock_session.return_value.text = self.aai_response_data
-        self.env = EnvironmentVarGuard()
-        self.env.set('AAI_SERVICE_PORT', '8443')
-        self.env.set('LOGGER_CONFIG', os.path.join(os.path.dirname(__file__), 'log_config.yaml'))
-        with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.nf_1 = NetworkFunction(nf_name='pnf_1', orchestration_status='Inventoried')
-        self.nf_2 = NetworkFunction(nf_name='pnf_2', orchestration_status='Active')
-        self.app = create_app()
-        self.app_context = self.app.app_context()
-        self.app_context.push()
-        db.create_all()
-        self.app_conf = AppConfig()
-        self.xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf)
+        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):
-        db.session.remove()
-        db.drop_all()
+        super().tearDown()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
 
     def test_status_response_healthy(self):
         self.assertEqual(status()['status'], 'healthy')
 
-    def test_get_all_sub_to_nf_relations(self):
+    @patch.object(Session, 'get')
+    @patch.object(Session, 'put')
+    def test_get_all_sub_to_nf_relations(self, mock_put_session, mock_get_session):
+        mock_put_session.return_value.status_code = 200
+        mock_put_session.return_value.text = self.aai_response_data
+        mock_get_session.return_value.status_code = 200
+        mock_get_session.return_value.text = self.good_model_info
+        responses.add(responses.GET,
+                      'https://aai:8443/aai/v20/service-design-and-creation/models/model/'
+                      '7129e420-d396-4efb-af02-6b83499b12f8/model-vers/model-ver/'
+                      'e80a6ae3-cafd-4d24-850d-e14c084a5ca9',
+                      json=json.loads(self.good_model_info), status=200)
+        self.xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf)
         sub_model = self.app_conf.subscription.get()
-        for nf in [self.nf_1, self.nf_2]:
+        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']), 2)
+        self.assertEqual(len(all_subs[0]['network_functions']), 3)
         self.assertEqual(all_subs[0]['subscription_name'], 'ExtraPM-All-gNB-R2B')
index d41bd03..c3cc024 100755 (executable)
 #
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=====================================================
-import json
 import os
 from signal import SIGTERM, signal
-from test.support import EnvironmentVarGuard
-from unittest import TestCase
 from unittest.mock import patch, Mock
 
-from mod.api.db_models import NetworkFunctionModel
 from mod.exit_handler import ExitHandler
-from mod.pmsh_utils import AppConfig
 from mod.subscription import Subscription
+from tests.base_setup import BaseClassSetup
 
 
-class ExitHandlerTests(TestCase):
-    @patch('mod.subscription.Subscription.create')
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
+class ExitHandlerTests(BaseClassSetup):
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
     @patch('mod.pmsh_utils.PeriodicTask')
-    def setUp(self, mock_periodic_task, mock_get_pmsh_config, mock_sub_create):
-        self.env = EnvironmentVarGuard()
-        self.env.set('LOGGER_CONFIG', os.path.join(os.path.dirname(__file__), 'log_config.yaml'))
-        with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
-        mock_get_pmsh_config.return_value = self.cbs_data
+    def setUp(self, mock_periodic_task):
+        super().setUp()
         self.mock_aai_event_thread = mock_periodic_task
-        self.app_conf = AppConfig()
         self.sub = self.app_conf.subscription
 
-    @patch('mod.logger.debug')
+    def tearDown(self):
+        super().tearDown()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
+
     @patch.object(Subscription, 'update_sub_nf_status')
     @patch.object(Subscription, 'update_subscription_status')
-    @patch.object(Subscription, '_get_nf_models',
-                  return_value=[NetworkFunctionModel('pnf1', 'ACTIVE')])
-    def test_terminate_signal_successful(self, mock_sub_get_nf_models, mock_upd_sub_status,
-                                         mock_upd_subnf_status, mock_logger):
+    def test_terminate_signal_successful(self, mock_upd_sub_status,
+                                         mock_upd_subnf_status):
         handler = ExitHandler(periodic_tasks=[self.mock_aai_event_thread],
                               app_conf=self.app_conf,
                               subscription_handler=Mock())
index cdfb1eb..f79ec93 100755 (executable)
 # ============LICENSE_END=====================================================
 import json
 import os
-from test.support import EnvironmentVarGuard
-from unittest import TestCase
 from unittest.mock import patch
 
-from mod import db, create_app
 from mod.network_function import NetworkFunction
-from mod.pmsh_utils import AppConfig
-from mod.subscription import Subscription
-
-
-class NetworkFunctionTests(TestCase):
-
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    @patch('mod.update_logging_config')
-    @patch('mod.get_db_connection_url')
-    def setUp(self, mock_get_db_url, mock_update_config, mock_get_pmsh_config):
-        mock_get_db_url.return_value = 'sqlite://'
-        self.nf_1 = NetworkFunction(nf_name='pnf_1', orchestration_status='Inventoried')
-        self.nf_2 = NetworkFunction(nf_name='pnf_2', orchestration_status='Active')
-        with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.env = EnvironmentVarGuard()
-        self.env.set('LOGGER_CONFIG', os.path.join(os.path.dirname(__file__), 'log_config.yaml'))
-        self.app = create_app()
-        self.app_context = self.app.app_context()
-        self.app_context.push()
-        db.create_all()
-        self.app_conf = AppConfig()
+from tests.base_setup import BaseClassSetup
+
+
+class NetworkFunctionTests(BaseClassSetup):
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+    def setUp(self):
+        super().setUp()
+        self.nf_1 = NetworkFunction(nf_name='pnf_1',
+                                    model_invariant_id='some-id',
+                                    model_version_id='some-id')
+        self.nf_2 = NetworkFunction(nf_name='pnf_2',
+                                    model_invariant_id='some-id',
+                                    model_version_id='some-id')
+        with open(os.path.join(os.path.dirname(__file__), 'data/aai_model_info.json'), 'r') as data:
+            self.good_model_info = json.loads(data.read())
+        with open(os.path.join(os.path.dirname(__file__),
+                               'data/aai_model_info_no_sdnc.json'), 'r') as data:
+            self.bad_model_info = json.loads(data.read())
 
     def tearDown(self):
-        db.session.remove()
-        db.drop_all()
-        self.app_context.pop()
+        super().tearDown()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
 
     def test_get_network_function(self):
         self.nf_1.create()
@@ -77,16 +75,21 @@ class NetworkFunctionTests(TestCase):
         self.assertEqual(nf, same_nf)
 
     def test_delete_network_function(self):
-        self.nf_1.create()
-        self.nf_2.create()
-        sub = Subscription(**{"subscriptionName": "sub"})
+        sub = self.app_conf.subscription
         for nf in [self.nf_1, self.nf_2]:
             sub.add_network_function_to_subscription(nf, self.app_conf.subscription.get())
-
+        nfs = NetworkFunction.get_all()
+        self.assertEqual(2, len(nfs))
         NetworkFunction.delete(nf_name=self.nf_1.nf_name)
-
         nfs = NetworkFunction.get_all()
         self.assertEqual(1, len(nfs))
-        self.assertEqual(1, len(Subscription.get_all_nfs_subscription_relations()))
-        pnf_1_deleted = [nf for nf in nfs if nf.nf_name != self.nf_1.nf_name]
-        self.assertTrue(pnf_1_deleted)
+
+    @patch('mod.aai_client.get_aai_model_data')
+    def test_set_sdnc_params_true(self, mock_get_aai_model):
+        mock_get_aai_model.return_value = self.good_model_info
+        self.assertTrue(self.nf_1.set_sdnc_params(self.app_conf))
+
+    @patch('mod.aai_client.get_aai_model_data')
+    def test_set_sdnc_params_false(self, mock_get_aai_model):
+        mock_get_aai_model.return_value = self.bad_model_info
+        self.assertFalse(self.nf_1.set_sdnc_params(self.app_conf))
index 95c9b7b..2151925 100644 (file)
@@ -20,7 +20,7 @@ import json
 import os
 from unittest import TestCase
 from parameterized import parameterized
-from mod.network_function import NetworkFunctionFilter
+from mod.network_function import NetworkFunctionFilter, NetworkFunction
 
 
 def custom_name_func(testcase_func, param_num, param):
@@ -32,7 +32,8 @@ def custom_name_func(testcase_func, param_num, param):
 
 def load_test_cases():
     test_parameters = []
-    with open(os.path.join(os.path.dirname(__file__), 'data/filter_test_data.json'), 'r') as test_data:
+    with open(os.path.join(os.path.dirname(__file__),
+                           'data/filter_test_data.json'), 'r') as test_data:
         loaded_test_data = json.load(test_data)
     for test in loaded_test_data:
         params = [value for key, value in test.items()]
@@ -43,44 +44,44 @@ def load_test_cases():
 class NetworkFunctionFilterTest(TestCase):
 
     @parameterized.expand(load_test_cases, name_func=custom_name_func)
-    def test(self, test_name, nf_filter, nf_name, model_invariant_uuid, model_version_id, orchestration_status,
+    def test(self, test_name, nf_filter, nf_name, model_invariant_uuid, model_version_id,
              expected_result):
         nf_filter = NetworkFunctionFilter(**nf_filter)
-        self.assertEqual(nf_filter.is_nf_in_filter(nf_name,
-                                                   model_invariant_uuid,
-                                                   model_version_id,
-                                                   orchestration_status), expected_result)
+        self.assertEqual(nf_filter.is_nf_in_filter(NetworkFunction(nf_name=nf_name,
+                                                   model_invariant_id=model_invariant_uuid,
+                                                   model_version_id=model_version_id)),
+                         expected_result)
 
-    def test_filter_true_on_multiple_modelInvariantUUIDs(self):
+    def test_filter_true_on_multiple_modelInvariantIDs(self):
         nf_filter = NetworkFunctionFilter(**{
             "nfNames": [
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
                 '5845y423-g654-6fju-po78-8n53154532k6',
                 '7129e420-d396-4efb-af02-6b83499b12f8'
             ],
             "modelVersionIDs": [
             ]
         })
-        self.assertTrue(nf_filter.is_nf_in_filter('pnf1',
-                                                  '7129e420-d396-4efb-af02-6b83499b12f8',
-                                                  'e80a6ae3-cafd-4d24-850d-e14c084a5ca9',
-                                                  'Active'))
+        self.assertTrue(nf_filter.is_nf_in_filter(
+            NetworkFunction(nf_name='pnf1',
+                            model_invariant_id='7129e420-d396-4efb-af02-6b83499b12f8',
+                            model_version_id='e80a6ae3-cafd-4d24-850d-e14c084a5ca9')))
 
-    def test_filter_false_on_modelInvariantUUIDs_being_false_and_pnfname_being_true(self):
+    def test_filter_false_on_modelInvariantIDs_being_false_and_pnfname_being_true(self):
         nf_filter = NetworkFunctionFilter(**{
             "nfNames": [
                 "^pnf.*",
                 "^vnf.*"
             ],
-            "modelInvariantUUIDs": [
+            "modelInvariantIDs": [
                 '5845y423-g654-6fju-po78-8n53154532k6',
                 '7129e420-d396-4efb-af02-6b83499b12f8'
             ],
             "modelVersionIDs": [
             ]
         })
-        self.assertFalse(nf_filter.is_nf_in_filter('pnf1',
-                                                   'WrongModelInvariantUUID',
-                                                   'e80a6ae3-cafd-4d24-850d-e14c084a5ca9',
-                                                   'Active'))
\ No newline at end of file
+        self.assertFalse(nf_filter.is_nf_in_filter(
+            NetworkFunction(nf_name='pnf1',
+                            model_invariant_id='WrongModelInvariantUUID',
+                            model_version_id='e80a6ae3-cafd-4d24-850d-e14c084a5ca9')))
index 6e5585f..1bc039d 100644 (file)
 #
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=====================================================
-import json
-import os
 from test.support import EnvironmentVarGuard
-from unittest import TestCase
-from unittest.mock import patch
+from unittest.mock import patch, Mock
 
 import responses
 from requests import Session
 from tenacity import RetryError
 
-from mod import db, get_db_connection_url, create_app
+from mod import get_db_connection_url
+from mod.network_function import NetworkFunction
 from mod.pmsh_utils import AppConfig
+from tests.base_setup import BaseClassSetup
+from tests.base_setup import get_pmsh_config
 
 
-class PmshUtilsTestCase(TestCase):
+class PmshUtilsTestCase(BaseClassSetup):
 
-    @patch('mod.update_logging_config')
-    @patch('mod.create_app')
-    @patch('mod.get_db_connection_url')
-    def setUp(self, mock_get_db_url, mock_app, mock_update_config):
-        mock_get_db_url.return_value = 'sqlite://'
-        with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
-        self.env = EnvironmentVarGuard()
-        self.env.set('LOGGER_CONFIG', os.path.join(os.path.dirname(__file__), 'log_config.yaml'))
-        self.mock_app = mock_app
-        self.app = create_app()
-        self.app_context = self.app.app_context()
-        self.app_context.push()
-        db.create_all()
-
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    def test_utils_get_mr_sub(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+    def setUp(self):
+        super().setUp()
+        self.mock_app = Mock()
+
+    def tearDown(self):
+        super().tearDown()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
+
+    def test_utils_get_mr_sub(self):
         mr_policy_sub = self.app_conf.get_mr_sub('policy_pm_subscriber')
         self.assertTrue(mr_policy_sub.aaf_id, 'dcae@dcae.onap.org')
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    def test_utils_get_mr_sub_fails_with_invalid_name(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_utils_get_mr_sub_fails_with_invalid_name(self):
         with self.assertRaises(KeyError):
             self.app_conf.get_mr_sub('invalid_sub')
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    def test_utils_get_mr_pub(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_utils_get_mr_pub(self):
         mr_policy_pub = self.app_conf.get_mr_pub('policy_pm_publisher')
         self.assertTrue(mr_policy_pub.aaf_pass, 'demo123456!')
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    def test_utils_get_mr_pub_fails_with_invalid_name(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_utils_get_mr_pub_fails_with_invalid_name(self):
         with self.assertRaises(KeyError):
             self.app_conf.get_mr_pub('invalid_pub')
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    def test_utils_get_cert_data(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_utils_get_cert_data(self):
         self.assertEqual(self.app_conf.cert_params, ('/opt/app/pmsh/etc/certs/cert.pem',
                                                      '/opt/app/pmsh/etc/certs/key.pem'))
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
     @patch.object(Session, 'post')
-    def test_mr_pub_publish_to_topic_success(self, mock_session, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_mr_pub_publish_to_topic_success(self, mock_session):
         mock_session.return_value.status_code = 200
         mr_policy_pub = self.app_conf.get_mr_pub('policy_pm_publisher')
         with patch('requests.Session.post') as session_post_call:
             mr_policy_pub.publish_to_topic({"dummy_val": "43c4ee19-6b8d-4279-a80f-c507850aae47"})
             session_post_call.assert_called_once()
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
     @responses.activate
-    def test_mr_pub_publish_to_topic_fail(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_mr_pub_publish_to_topic_fail(self):
         responses.add(responses.POST,
                       'https://message-router:3905/events/org.onap.dmaap.mr.PM_SUBSCRIPTIONS',
                       json={'error': 'Client Error'}, status=400)
@@ -104,21 +83,19 @@ class PmshUtilsTestCase(TestCase):
         with self.assertRaises(Exception):
             mr_policy_pub.publish_to_topic({"dummy_val": "43c4ee19-6b8d-4279-a80f-c507850aae47"})
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    def test_mr_pub_publish_sub_event_data_success(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_mr_pub_publish_sub_event_data_success(self):
         mr_policy_pub = self.app_conf.get_mr_pub('policy_pm_publisher')
         with patch('mod.pmsh_utils._MrPub.publish_to_topic') as pub_to_topic_call:
-            mr_policy_pub.publish_subscription_event_data(self.app_conf.subscription, 'pnf201',
-                                                          self.app_conf)
+            mr_policy_pub.publish_subscription_event_data(
+                self.app_conf.subscription,
+                NetworkFunction(nf_name='pnf_1',
+                                model_invariant_id='some-id',
+                                model_version_id='some-id'),
+                self.app_conf)
             pub_to_topic_call.assert_called_once()
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
     @responses.activate
-    def test_mr_sub_get_from_topic_success(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_mr_sub_get_from_topic_success(self):
         policy_mr_sub = self.app_conf.get_mr_sub('policy_pm_subscriber')
         responses.add(responses.GET,
                       'https://message-router:3905/events/org.onap.dmaap.mr.PM_SUBSCRIPTIONS/'
@@ -127,11 +104,8 @@ class PmshUtilsTestCase(TestCase):
         mr_topic_data = policy_mr_sub.get_from_topic(1)
         self.assertIsNotNone(mr_topic_data)
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
     @responses.activate
-    def test_mr_sub_get_from_topic_fail(self, mock_get_pmsh_config):
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+    def test_mr_sub_get_from_topic_fail(self):
         policy_mr_sub = self.app_conf.get_mr_sub('policy_pm_subscriber')
         responses.add(responses.GET,
                       'https://message-router:3905/events/org.onap.dmaap.mr.PM_SUBSCRIPTIONS/'
@@ -158,15 +132,14 @@ class PmshUtilsTestCase(TestCase):
     @patch('mod.logger.info')
     @patch('mod.pmsh_utils.get_all')
     def test_refresh_config_success(self, mock_cbs_client_get_all, mock_logger):
-        mock_cbs_client_get_all.return_value = self.cbs_data
-        self.app_conf = AppConfig()
+        mock_cbs_client_get_all.return_value = get_pmsh_config()
         self.app_conf.refresh_config()
         mock_logger.assert_called_with('AppConfig data has been refreshed')
 
     @patch('mod.logger.error')
     @patch('mod.pmsh_utils.get_all')
     def test_refresh_config_fail(self, mock_cbs_client_get_all, mock_logger):
-        mock_cbs_client_get_all.return_value = self.cbs_data
+        mock_cbs_client_get_all.return_value = get_pmsh_config()
         self.app_conf = AppConfig()
         mock_cbs_client_get_all.side_effect = Exception
         with self.assertRaises(RetryError):
index 9dd73ee..26a06fc 100644 (file)
 #
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=====================================================
-import json
-import os
-from unittest import TestCase
 from unittest.mock import patch
 
 from mod.api.db_models import SubscriptionModel
 from mod.network_function import NetworkFunction
-from mod.pmsh_utils import AppConfig
 from mod.policy_response_handler import PolicyResponseHandler, policy_response_handle_functions
 from mod.subscription import AdministrativeState, SubNfState
+from tests.base_setup import BaseClassSetup
 
 
-class PolicyResponseHandlerTest(TestCase):
+class PolicyResponseHandlerTest(BaseClassSetup):
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
 
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
     @patch('mod.create_app')
-    @patch('mod.subscription.Subscription')
     @patch('mod.pmsh_utils._MrSub')
-    def setUp(self, mock_mr_sub, mock_sub, mock_app, mock_get_app_conf):
-        with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
+    def setUp(self, mock_mr_sub, mock_app):
+        super().setUp()
         self.mock_policy_mr_sub = mock_mr_sub
-        mock_get_app_conf.return_value = self.cbs_data
-        self.app_conf = AppConfig()
-        self.sub = self.app_conf.subscription
-        self.mock_app = mock_app
         self.nf = NetworkFunction(nf_name='nf1')
         self.policy_response_handler = PolicyResponseHandler(self.mock_policy_mr_sub,
                                                              self.app_conf,
-                                                             self.mock_app)
+                                                             mock_app)
+
+    def tearDown(self):
+        super().tearDown()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
 
     @patch('mod.network_function.NetworkFunction.delete')
     def test_handle_response_locked_success(self, mock_delete):
index c7845e7..9bfe825 100755 (executable)
 # ============LICENSE_END=====================================================
 import json
 import os
-from test.support import EnvironmentVarGuard
-from unittest import TestCase
-from unittest.mock import patch
+from unittest.mock import patch, Mock
 
 from requests import Session
 
 import mod.aai_client as aai_client
-from mod import db, create_app
-from mod.api.db_models import NetworkFunctionModel
 from mod.network_function import NetworkFunction
-from mod.pmsh_utils import AppConfig
 from mod.subscription import Subscription
+from tests.base_setup import BaseClassSetup
 
 
-class SubscriptionTest(TestCase):
-    @patch('mod.update_logging_config')
-    @patch('mod.pmsh_utils._MrPub')
-    @patch('mod.pmsh_utils._MrSub')
-    @patch('mod.get_db_connection_url')
+class SubscriptionTest(BaseClassSetup):
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+    @patch.object(Session, 'get')
     @patch.object(Session, 'put')
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
-    def setUp(self, mock_get_pmsh_config, mock_session, mock_get_db_url,
-              mock_mr_sub, mock_mr_pub, mock_update_config):
-        mock_get_db_url.return_value = 'sqlite://'
+    def setUp(self, mock_session_put, mock_session_get):
+        super().setUp()
         with open(os.path.join(os.path.dirname(__file__), 'data/aai_xnfs.json'), 'r') as data:
             self.aai_response_data = data.read()
-        mock_session.return_value.status_code = 200
-        mock_session.return_value.text = self.aai_response_data
-        self.env = EnvironmentVarGuard()
-        self.env.set('AAI_SERVICE_PORT', '8443')
-        self.env.set('LOGGER_CONFIG', os.path.join(os.path.dirname(__file__), 'log_config.yaml'))
-        with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
-        mock_get_pmsh_config.return_value = self.cbs_data
-        self.mock_mr_sub = mock_mr_sub
-        self.mock_mr_pub = mock_mr_pub
-        self.app = create_app()
-        self.app_context = self.app.app_context()
-        self.app_context.push()
-        db.create_all()
-        self.app_conf = AppConfig()
+        mock_session_put.return_value.status_code = 200
+        mock_session_put.return_value.text = self.aai_response_data
+        with open(os.path.join(os.path.dirname(__file__), 'data/aai_model_info.json'), 'r') as data:
+            self.aai_model_data = data.read()
+        mock_session_get.return_value.status_code = 200
+        mock_session_get.return_value.text = self.aai_model_data
+        self.mock_mr_sub = Mock()
+        self.mock_mr_pub = Mock()
         self.xnfs = aai_client.get_pmsh_nfs_from_aai(self.app_conf)
         self.sub_model = self.app_conf.subscription.get()
 
     def tearDown(self):
-        db.drop_all()
-        db.session.remove()
-        self.app_context.pop()
+        super().tearDown()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
 
     def test_sub_measurement_group(self):
         self.assertEqual(len(self.app_conf.subscription.measurementGroups), 2)
@@ -84,8 +75,6 @@ class SubscriptionTest(TestCase):
                                                                         self.sub_model)
         self.app_conf.subscription.add_network_function_to_subscription(list(self.xnfs)[1],
                                                                         self.sub_model)
-        nfs = Subscription.get_nf_names_per_sub(self.app_conf.subscription.subscriptionName)
-        self.assertEqual(2, len(nfs))
 
     def test_create_existing_subscription(self):
         sub1 = self.app_conf.subscription.create()
@@ -121,16 +110,6 @@ class SubscriptionTest(TestCase):
 
         self.assertEqual('new_status', sub.status)
 
-    def test_delete_subscription(self):
-        for nf in self.xnfs:
-            self.app_conf.subscription.add_network_function_to_subscription(nf, self.sub_model)
-        self.app_conf.subscription.delete_subscription()
-        self.assertEqual(0, len(Subscription.get_all()))
-        self.assertEqual(None, self.app_conf.subscription.get())
-        self.assertEqual(0, len(Subscription.get_all_nfs_subscription_relations()))
-        self.assertEqual(0, len(NetworkFunction.get_all()))
-        self.assertEqual(None, NetworkFunction.get(nf_name=list(self.xnfs)[0].nf_name))
-
     def test_update_sub_nf_status(self):
         sub_name = 'ExtraPM-All-gNB-R2B'
         for nf in self.xnfs:
@@ -172,18 +151,15 @@ class SubscriptionTest(TestCase):
         with open(os.path.join(os.path.dirname(__file__),
                                'data/pm_subscription_event.json'), 'r') as data:
             expected_sub_event = json.load(data)
-        actual_sub_event = self.app_conf.subscription.prepare_subscription_event('pnf_1',
-                                                                                 self.app_conf)
+        nf = NetworkFunction(nf_name='pnf_1',
+                             model_invariant_id='some-id',
+                             model_version_id='some-id')
+        nf.sdnc_model_name = 'some-name'
+        nf.sdnc_model_version = 'some-version'
+        actual_sub_event = self.app_conf.subscription.prepare_subscription_event(nf, self.app_conf)
+        print(actual_sub_event)
         self.assertEqual(expected_sub_event, actual_sub_event)
 
-    def test_get_nf_models(self):
-        for nf in self.xnfs:
-            self.app_conf.subscription.add_network_function_to_subscription(nf, self.sub_model)
-        nf_models = self.app_conf.subscription._get_nf_models()
-
-        self.assertEqual(3, len(nf_models))
-        self.assertIsInstance(nf_models[0], NetworkFunctionModel)
-
     def test_get_network_functions(self):
         for nf in self.xnfs:
             self.app_conf.subscription.add_network_function_to_subscription(nf, self.sub_model)
index a661166..bf72382 100644 (file)
 #
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=====================================================
-import json
-import os
-from test.support import EnvironmentVarGuard
-from unittest import TestCase
-from unittest.mock import patch
+from unittest.mock import patch, Mock
 
-from mod import create_app, db
 from mod.network_function import NetworkFunction
-from mod.pmsh_utils import AppConfig
 from mod.subscription import AdministrativeState
 from mod.subscription_handler import SubscriptionHandler
+from tests.base_setup import BaseClassSetup
 
 
-class SubscriptionHandlerTest(TestCase):
+class SubscriptionHandlerTest(BaseClassSetup):
 
     @classmethod
     def setUpClass(cls):
-        cls.env = EnvironmentVarGuard()
-        cls.env.set('AAI_SERVICE_PORT', '8443')
-        cls.env.set('LOGGER_CONFIG', os.path.join(os.path.dirname(__file__), 'log_config.yaml'))
-        cls.nfs = [NetworkFunction(nf_name='pnf_1'), NetworkFunction(nf_name='pnf_2')]
+        super().setUpClass()
 
-    @patch('mod.get_db_connection_url')
-    @patch('mod.update_logging_config')
-    @patch('mod.pmsh_utils.AppConfig._get_pmsh_config')
     @patch('mod.pmsh_utils._MrPub')
-    @patch('mod.pmsh_utils.PeriodicTask')
-    def setUp(self, mock_periodic_task, mock_mr_pub, mock_get_pmsh_config, mock_update_config,
-              mock_get_db_url):
-        mock_get_db_url.return_value = 'sqlite://'
-        with open(os.path.join(os.path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
-            self.cbs_data = json.load(data)
-        mock_get_pmsh_config.return_value = self.cbs_data
+    def setUp(self, mock_mr_pub):
+        super().setUp()
+        self.nfs = [NetworkFunction(nf_name='pnf_1',
+                                    model_invariant_id='some-id',
+                                    model_version_id='some-id'),
+                    NetworkFunction(nf_name='pnf_2',
+                                    model_invariant_id='some-id',
+                                    model_version_id='some-id')]
         self.mock_mr_pub = mock_mr_pub
-        self.mock_aai_event_thread = mock_periodic_task
-        self.mock_policy_event_thread = mock_periodic_task
-        self.app = create_app()
-        self.app.app_context().push()
-        db.create_all()
-        self.app_conf = AppConfig()
+        self.mock_aai_event_thread = Mock()
+        self.mock_policy_event_thread = Mock()
 
     def tearDown(self):
-        db.drop_all()
-        db.session.remove()
+        super().tearDown()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
 
     @patch('mod.subscription.Subscription.get_local_sub_admin_state')
     @patch('mod.logger.info')