Subscription API DB changes with IPV4, IPV6 updates 86/123286/8 1.3.2-pmsh
authorSagarS <sagar.shetty@est.tech>
Mon, 16 Aug 2021 15:55:10 +0000 (16:55 +0100)
committerSagarS <sagar.shetty@est.tech>
Thu, 26 Aug 2021 12:25:20 +0000 (13:25 +0100)
Issue-ID: DCAEGEN2-2823
Change-Id: Ie455b1016ae1372934fa874667226ca9972b8d7f
Signed-off-by: SagarS <sagar.shetty@est.tech>
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/subscription.py
components/pm-subscription-handler/pom.xml
components/pm-subscription-handler/tests/data/pm_subscription_event.json
components/pm-subscription-handler/tests/test_network_function.py
components/pm-subscription-handler/tests/test_subscription.py
components/pm-subscription-handler/tests/test_subscription_handler.py

index 58d40cc..bd5929c 100755 (executable)
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ## [1.3.2]
 ### Changed
 * Update to use version 2.2.1 of https://pypi.org/project/onap_dcae_cbs_docker_client/
+* Updated DB under Enhanced API for PMSH subscription management Feature (DCAEGEN2-2802) 
 
 ## [1.3.1]
 ### Changed
index fff91e7..e53ea3a 100755 (executable)
@@ -135,7 +135,8 @@ def _filter_nf_data(nf_data, app_conf):
             name_identifier = 'pnf-name' if nf['node-type'] == 'pnf' else 'vnf-name'
             new_nf = mod.network_function.NetworkFunction(
                 nf_name=nf['properties'].get(name_identifier),
-                ip_address=nf['properties'].get('ipaddress-v4-oam'),
+                ipv4_address=nf['properties'].get('ipaddress-v4-oam'),
+                ipv6_address=nf['properties'].get('ipaddress-v6-oam'),
                 model_invariant_id=nf['properties'].get('model-invariant-id'),
                 model_version_id=nf['properties'].get('model-version-id'))
             if not new_nf.set_nf_model_params(app_conf):
index 46d52f1..6fc4ba9 100755 (executable)
@@ -63,7 +63,8 @@ def process_aai_events(mr_sub, mr_pub, app, app_conf):
                                 f'is not "Active"')
                     continue
                 nf = NetworkFunction(nf_name=xnf_name,
-                                     ip_address=aai_entity['ipaddress-v4-oam'],
+                                     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):
index a9dd6ef..5c87fa5 100755 (executable)
@@ -1,5 +1,5 @@
 # ============LICENSE_START===================================================
-#  Copyright (C) 2019-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.
@@ -16,7 +16,7 @@
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=====================================================
 
-from sqlalchemy import Column, Integer, String, ForeignKey
+from sqlalchemy import Column, Integer, String, ForeignKey, JSON
 from sqlalchemy.orm import relationship
 
 from mod import db
@@ -25,7 +25,7 @@ from mod import db
 class SubscriptionModel(db.Model):
     __tablename__ = 'subscriptions'
     id = Column(Integer, primary_key=True, autoincrement=True)
-    subscription_name = Column(String(100), unique=True)
+    subscription_name = Column(String(100), unique=True, nullable=False)
     status = Column(String(20))
 
     nfs = relationship(
@@ -33,6 +33,16 @@ class SubscriptionModel(db.Model):
         cascade='all, delete-orphan',
         backref='subscription')
 
+    network_filter = relationship(
+        'NetworkFunctionFilterModel',
+        cascade='all, delete-orphan',
+        backref='subscription')
+
+    measurement_groups = relationship(
+        'MeasurementGroupModel',
+        cascade='all, delete-orphan',
+        backref='subscription')
+
     def __init__(self, subscription_name, status):
         self.subscription_name = subscription_name
         self.status = status
@@ -57,7 +67,8 @@ class NetworkFunctionModel(db.Model):
     __tablename__ = 'network_functions'
     id = Column(Integer, primary_key=True, autoincrement=True)
     nf_name = Column(String(100), unique=True)
-    ip_address = Column(String(50))
+    ipv4_address = Column(String(50))
+    ipv6_address = Column(String(50))
     model_invariant_id = Column(String(100))
     model_version_id = Column(String(100))
     model_name = Column(String(100))
@@ -70,11 +81,12 @@ class NetworkFunctionModel(db.Model):
         cascade='all, delete-orphan',
         backref='nf')
 
-    def __init__(self, nf_name, ip_address, model_invariant_id,
+    def __init__(self, nf_name, ipv4_address, ipv6_address, model_invariant_id,
                  model_version_id, model_name, sdnc_model_name,
                  sdnc_model_version, retry_count=0):
         self.nf_name = nf_name
-        self.ip_address = ip_address
+        self.ipv4_address = ipv4_address
+        self.ipv6_address = ipv6_address
         self.model_invariant_id = model_invariant_id
         self.model_version_id = model_version_id
         self.model_name = model_name
@@ -90,7 +102,8 @@ class NetworkFunctionModel(db.Model):
         return NetworkFunction(sdnc_model_name=self.sdnc_model_name,
                                sdnc_model_version=self.sdnc_model_version,
                                **{'nf_name': self.nf_name,
-                                  'ip_address': self.ip_address,
+                                  'ipv4_address': self.ipv4_address,
+                                  'ipv6_address': self.ipv6_address,
                                   'model_invariant_id': self.model_invariant_id,
                                   'model_version_id': self.model_version_id})
 
@@ -129,10 +142,117 @@ class NfSubRelationalModel(db.Model):
             NetworkFunctionModel.nf_name == self.nf_name).one_or_none()
         db.session.remove()
         return {'nf_name': self.nf_name,
-                'ip_address': nf.ip_address,
+                'ipv4_address': nf.ipv4_address,
+                'ipv6_address': nf.ipv6_address,
                 'nf_sub_status': self.nf_sub_status,
                 'model_invariant_id': nf.model_invariant_id,
                 'model_version_id': nf.model_version_id,
                 'model_name': nf.model_name,
                 'sdnc_model_name': nf.sdnc_model_name,
                 'sdnc_model_version': nf.sdnc_model_version}
+
+
+class NetworkFunctionFilterModel(db.Model):
+    __tablename__ = 'nf_filter'
+    id = Column(Integer, primary_key=True, autoincrement=True)
+    subscription_name = Column(
+        String,
+        ForeignKey(SubscriptionModel.subscription_name, ondelete='cascade', onupdate='cascade'),
+        unique=True
+    )
+    nf_names = Column(String(100))
+    model_invariant_ids = Column(String(100))
+    model_version_ids = Column(String(100))
+    model_names = Column(String(100))
+
+    def __init__(self, subscription_name, nf_names, model_invariant_ids, model_version_ids,
+                 model_names):
+        self.subscription_name = subscription_name
+        self.nf_names = nf_names
+        self.model_invariant_ids = model_invariant_ids
+        self.model_version_ids = model_version_ids
+        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}'
+
+    def serialize(self):
+        return {'subscription_name': self.subscription_name, 'nf_names': self.nf_names,
+                'model_invariant_ids': self.model_invariant_ids,
+                'model_version_ids': self.model_version_ids, 'model_names': self.model_names}
+
+
+class MeasurementGroupModel(db.Model):
+    __tablename__ = 'measurement_group'
+    id = Column(Integer, primary_key=True, autoincrement=True)
+    subscription_name = Column(
+        String,
+        ForeignKey(SubscriptionModel.subscription_name, ondelete='cascade', onupdate='cascade')
+    )
+    measurement_group_name = Column(String(100), unique=True)
+    administrative_state = Column(String(20))
+    file_based_gp = Column(Integer)
+    file_location = Column(String(100))
+    measurement_type = Column(JSON)
+    managed_object_dns_basic = Column(JSON)
+
+    def __init__(self, subscription_name, measurement_group_name,
+                 administrative_state, file_based_gp, file_location,
+                 measurement_type, managed_object_dns_basic):
+        self.subscription_name = subscription_name
+        self.measurement_group_name = measurement_group_name
+        self.administrative_state = administrative_state
+        self.file_based_gp = file_based_gp
+        self.file_location = file_location
+        self.measurement_type = measurement_type
+        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}'
+
+    def serialize(self):
+        return {'subscription_name': self.subscription_name,
+                'measurement_group_name': self.measurement_group_name,
+                'administrative_state': self.administrative_state,
+                'file_based_gp': self.file_based_gp,
+                'file_location': self.file_location,
+                'measurement_type': self.measurement_type,
+                'managed_object_dns_basic': self.managed_object_dns_basic}
+
+
+class NfMeasureGroupRelationalModel(db.Model):
+    __tablename__ = 'nf_to_measure_grp_rel'
+    __mapper_args__ = {
+        'confirm_deleted_rows': False
+    }
+    id = Column(Integer, primary_key=True, autoincrement=True)
+    measurement_grp_name = Column(
+        String,
+        ForeignKey(MeasurementGroupModel.measurement_group_name, ondelete='cascade',
+                   onupdate='cascade')
+    )
+    nf_name = Column(
+        String,
+        ForeignKey(NetworkFunctionModel.nf_name, ondelete='cascade', onupdate='cascade')
+    )
+    nf_measure_grp_status = Column(String(20))
+    retry_count = Column(Integer)
+
+    def __init__(self, measurement_grp_name, nf_name, nf_measure_grp_status=None,
+                 retry_count=0):
+        self.measurement_grp_name = measurement_grp_name
+        self.nf_name = nf_name
+        self.nf_measure_grp_status = nf_measure_grp_status
+        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}'
index 83130a8..f7b682d 100755 (executable)
@@ -26,7 +26,8 @@ class NetworkFunction:
     def __init__(self, sdnc_model_name=None, sdnc_model_version=None, **kwargs):
         """ Object representation of the NetworkFunction. """
         self.nf_name = kwargs.get('nf_name')
-        self.ip_address = kwargs.get('ip_address')
+        self.ipv4_address = kwargs.get('ipv4_address')
+        self.ipv6_address = kwargs.get('ipv6_address')
         self.model_invariant_id = kwargs.get('model_invariant_id')
         self.model_version_id = kwargs.get('model_version_id')
         self.model_name = kwargs.get('model_name')
@@ -35,13 +36,14 @@ class NetworkFunction:
 
     @classmethod
     def nf_def(cls):
-        return cls(nf_name=None, ip_address=None, model_invariant_id=None,
-                   model_version_id=None, model_name=None,
+        return cls(nf_name=None, ipv4_address=None, ipv6_address=None,
+                   model_invariant_id=None, model_version_id=None, model_name=None,
                    sdnc_model_name=None, sdnc_model_version=None)
 
     def __str__(self):
         return f'nf-name: {self.nf_name}, ' \
-               f'ipaddress-v4-oam: {self.ip_address}, ' \
+               f'ipaddress-v4-oam: {self.ipv4_address}, ' \
+               f'ipaddress-v6-oam: {self.ipv6_address}, ' \
                f'model-invariant-id: {self.model_invariant_id}, ' \
                f'model-version-id: {self.model_version_id}, ' \
                f'model-name: {self.model_name}, ' \
@@ -51,7 +53,8 @@ class NetworkFunction:
     def __eq__(self, other):
         return \
             self.nf_name == other.nf_name and \
-            self.ip_address == other.ip_address and \
+            self.ipv4_address == other.ipv4_address and \
+            self.ipv6_address == other.ipv6_address and \
             self.model_invariant_id == other.model_invariant_id and \
             self.model_version_id == other.model_version_id and \
             self.model_name == other.model_name and \
@@ -59,8 +62,8 @@ class NetworkFunction:
             self.sdnc_model_version == other.sdnc_model_version
 
     def __hash__(self):
-        return hash((self.nf_name, self.ip_address, self.model_invariant_id,
-                     self.model_version_id, self.model_name,
+        return hash((self.nf_name, self.ipv4_address, self.ipv6_address,
+                     self.model_invariant_id, self.model_version_id, self.model_name,
                      self.sdnc_model_name, self.sdnc_model_version))
 
     def create(self):
@@ -70,7 +73,8 @@ class NetworkFunction:
 
         if existing_nf is None:
             new_nf = NetworkFunctionModel(nf_name=self.nf_name,
-                                          ip_address=self.ip_address,
+                                          ipv4_address=self.ipv4_address,
+                                          ipv6_address=self.ipv6_address,
                                           model_invariant_id=self.model_invariant_id,
                                           model_version_id=self.model_version_id,
                                           model_name=self.model_name,
index fdc1394..7e878cd 100755 (executable)
@@ -106,7 +106,7 @@ class Subscription:
         """ Updates the status of subscription in subscription table """
         try:
             SubscriptionModel.query.filter(
-                SubscriptionModel.subscription_name == self.subscriptionName)\
+                SubscriptionModel.subscription_name == self.subscriptionName) \
                 .update({SubscriptionModel.status: self.administrativeState},
                         synchronize_session='evaluate')
 
@@ -133,14 +133,16 @@ class Subscription:
                 change_type = 'DELETE'
             else:
                 change_type = 'CREATE'
-            sub_event = {'nfName': nf.nf_name,
-                         'ipv4Address': nf.ip_address,
-                         'blueprintName': nf.sdnc_model_name,
-                         'blueprintVersion': nf.sdnc_model_version,
-                         'policyName': app_conf.operational_policy_name,
-                         'changeType': change_type,
-                         'closedLoopControlName': app_conf.control_loop_name,
-                         'subscription': clean_sub}
+
+            sub_event = {
+                'nfName': nf.nf_name,
+                'ipAddress': nf.ipv4_address if nf.ipv6_address in (None, '') else nf.ipv6_address,
+                'blueprintName': nf.sdnc_model_name,
+                'blueprintVersion': nf.sdnc_model_version,
+                'policyName': app_conf.operational_policy_name,
+                'changeType': change_type,
+                'closedLoopControlName': app_conf.control_loop_name,
+                'subscription': clean_sub}
             return sub_event
         except Exception as e:
             logger.error(f'Failed to prep Sub event for xNF {nf.nf_name}: {e}', exc_info=True)
index fbf4497..a0b58f4 100644 (file)
@@ -32,7 +32,7 @@
   <groupId>org.onap.dcaegen2.services</groupId>
   <artifactId>pmsh</artifactId>
   <name>dcaegen2-services-pm-subscription-handler</name>
-  <version>1.3.1-SNAPSHOT</version>
+  <version>1.3.2-SNAPSHOT</version>
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <sonar.sources>.</sonar.sources>
index cd547d9..6b94373 100755 (executable)
@@ -5,7 +5,7 @@
    "policyName":"pmsh-operational-policy",\r
    "changeType":"CREATE",\r
    "closedLoopControlName":"pmsh-control-loop",\r
-   "ipv4Address": "1.2.3.4",\r
+   "ipAddress": "204.120.0.15",\r
    "subscription":{\r
       "subscriptionName":"ExtraPM-All-gNB-R2B",\r
       "administrativeState":"UNLOCKED",\r
index 5a1a6ba..3e38b9c 100755 (executable)
@@ -1,5 +1,5 @@
 # ============LICENSE_START===================================================
-#  Copyright (C) 2019-2020 Nordix Foundation.
+#  Copyright (C) 2019-2021 Nordix Foundation.
 # ============================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -33,12 +33,14 @@ class NetworkFunctionTests(BaseClassSetup):
         super().setUp()
         self.nf_1 = NetworkFunction(sdnc_model_name='blah', sdnc_model_version=1.0,
                                     **{'nf_name': 'pnf_1',
-                                       'ip_address': '1.2.3.4',
+                                       'ipv4_address': '204.120.0.15',
+                                       'ipv6_address': '2001:db8:3333:4444:5555:6666:7777:8888',
                                        'model_invariant_id': 'some_id',
                                        'model_version_id': 'some_other_id'})
         self.nf_2 = NetworkFunction(sdnc_model_name='blah', sdnc_model_version=2.0,
                                     **{'nf_name': 'pnf_2',
-                                       'ip_address': '1.2.3.4',
+                                       'ipv4_address': '204.120.0.15',
+                                       'ipv6_address': '2001:db8:3333:4444:5555:6666:7777:8888',
                                        'model_invariant_id': 'some_id',
                                        'model_version_id': 'some_other_id'})
         with open(os.path.join(os.path.dirname(__file__), 'data/aai_model_info.json'), 'r') as data:
index b18f41e..01c573e 100755 (executable)
@@ -1,5 +1,5 @@
 # ============LICENSE_START===================================================
-#  Copyright (C) 2019-2020 Nordix Foundation.
+#  Copyright (C) 2019-2021 Nordix Foundation.
 # ============================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -140,7 +140,24 @@ class SubscriptionTest(BaseClassSetup):
                                'data/pm_subscription_event.json'), 'r') as data:
             expected_sub_event = json.load(data)
         nf = NetworkFunction(nf_name='pnf_1',
-                             ip_address='1.2.3.4',
+                             ipv4_address='204.120.0.15',
+                             ipv6_address='',
+                             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_prepare_subscription_event_with_ipv6(self):
+        with open(os.path.join(os.path.dirname(__file__),
+                               'data/pm_subscription_event.json'), 'r') as data:
+            expected_sub_event = json.load(data)
+        expected_sub_event['ipAddress'] = '2001:db8:3333:4444:5555:6666:7777:8888'
+        nf = NetworkFunction(nf_name='pnf_1',
+                             ipv4_address='204.120.0.15',
+                             ipv6_address='2001:db8:3333:4444:5555:6666:7777:8888',
                              model_invariant_id='some-id',
                              model_version_id='some-id')
         nf.sdnc_model_name = 'some-name'
index 2293ee5..ecc45f6 100644 (file)
@@ -109,7 +109,8 @@ class SubscriptionHandlerTest(BaseClassSetup):
            MagicMock(return_value=NetworkFunctionModel(nf_name='pnf_1',
                                                        model_invariant_id='some-id',
                                                        model_version_id='some-id',
-                                                       ip_address='ip_address',
+                                                       ipv4_address='ip_address4',
+                                                       ipv6_address='ip_address6',
                                                        model_name='model_name',
                                                        sdnc_model_name='sdnc_model_name',
                                                        sdnc_model_version='sdnc_model_version')))
@@ -145,7 +146,8 @@ class SubscriptionHandlerTest(BaseClassSetup):
            MagicMock(return_value=NetworkFunctionModel(nf_name='pnf_1',
                                                        model_invariant_id='some-id',
                                                        model_version_id='some-id',
-                                                       ip_address='ip_address',
+                                                       ipv4_address='ip_address4',
+                                                       ipv6_address='ip_address6',
                                                        model_name='model_name',
                                                        sdnc_model_name='sdnc_model_name',
                                                        sdnc_model_version='sdnc_model_version',