[PMSH] Bug fix to include ip in event
[dcaegen2/services.git] / components / pm-subscription-handler / pmsh_service / mod / subscription.py
1 # ============LICENSE_START===================================================
2 #  Copyright (C) 2019-2020 Nordix Foundation.
3 # ============================================================================
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #      http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16 # SPDX-License-Identifier: Apache-2.0
17 # ============LICENSE_END=====================================================
18 from enum import Enum
19
20 from mod import db, logger
21 from mod.api.db_models import SubscriptionModel, NfSubRelationalModel, NetworkFunctionModel
22
23
24 class SubNfState(Enum):
25     PENDING_CREATE = 'PENDING_CREATE'
26     CREATE_FAILED = 'CREATE_FAILED'
27     CREATED = 'CREATED'
28     PENDING_DELETE = 'PENDING_DELETE'
29     DELETE_FAILED = 'DELETE_FAILED'
30
31
32 class AdministrativeState(Enum):
33     UNLOCKED = 'UNLOCKED'
34     LOCKED = 'LOCKED'
35     PENDING = 'PENDING'
36
37
38 subscription_nf_states = {
39     AdministrativeState.LOCKED.value: {
40         'success': SubNfState.CREATED,
41         'failed': SubNfState.DELETE_FAILED
42     },
43     AdministrativeState.UNLOCKED.value: {
44         'success': SubNfState.CREATED,
45         'failed': SubNfState.CREATE_FAILED
46     }
47 }
48
49
50 class Subscription:
51     def __init__(self, **kwargs):
52         self.subscriptionName = kwargs.get('subscriptionName')
53         self.administrativeState = kwargs.get('administrativeState')
54         self.fileBasedGP = kwargs.get('fileBasedGP')
55         self.fileLocation = kwargs.get('fileLocation')
56         self.nfFilter = kwargs.get('nfFilter')
57         self.measurementGroups = kwargs.get('measurementGroups')
58         self.create()
59
60     def create(self):
61         """ Creates a subscription database entry
62
63         Returns:
64             Subscription object
65         """
66         try:
67             existing_subscription = (SubscriptionModel.query.filter(
68                 SubscriptionModel.subscription_name == self.subscriptionName).one_or_none())
69             if existing_subscription is None:
70                 new_subscription = SubscriptionModel(subscription_name=self.subscriptionName,
71                                                      status=AdministrativeState.PENDING.value)
72                 db.session.add(new_subscription)
73                 db.session.commit()
74                 return new_subscription
75             else:
76                 logger.debug(f'Subscription {self.subscriptionName} already exists,'
77                              f' returning this subscription..')
78                 return existing_subscription
79         except Exception as e:
80             logger.error(f'Failed to create subscription {self.subscriptionName} in the DB: {e}',
81                          exc_info=True)
82         finally:
83             db.session.remove()
84
85     def update_subscription_status(self):
86         """ Updates the status of subscription in subscription table """
87         try:
88             SubscriptionModel.query.filter(
89                 SubscriptionModel.subscription_name == self.subscriptionName)\
90                 .update({SubscriptionModel.status: self.administrativeState},
91                         synchronize_session='evaluate')
92
93             db.session.commit()
94         except Exception as e:
95             logger.error(f'Failed to update status of subscription: {self.subscriptionName}: {e}',
96                          exc_info=True)
97         finally:
98             db.session.remove()
99
100     def prepare_subscription_event(self, nf, app_conf):
101         """Prepare the sub event for publishing
102
103         Args:
104             nf (NetworkFunction): the AAI nf.
105             app_conf (AppConfig): the application configuration.
106
107         Returns:
108             dict: the Subscription event to be published.
109         """
110         try:
111             clean_sub = {k: v for k, v in self.__dict__.items() if k != 'nfFilter'}
112             sub_event = {'nfName': nf.nf_name,
113                          'ipv4Address': nf.ip_address,
114                          'blueprintName': nf.sdnc_model_name,
115                          'blueprintVersion': nf.sdnc_model_version,
116                          'policyName': app_conf.operational_policy_name,
117                          'changeType': 'DELETE'
118                          if self.administrativeState == AdministrativeState.LOCKED.value
119                          else 'CREATE', 'closedLoopControlName': app_conf.control_loop_name,
120                          'subscription': clean_sub}
121             return sub_event
122         except Exception as e:
123             logger.error(f'Failed to prep Sub event for xNF {nf.nf_name}: {e}', exc_info=True)
124             raise
125
126     def add_network_function_to_subscription(self, nf, sub_model):
127         """ Associates a network function to a Subscription
128
129         Args:
130             sub_model(SubscriptionModel): The SubscriptionModel from the DB.
131             nf(NetworkFunction): A NetworkFunction object.
132         """
133         try:
134             current_nf = nf.create()
135             existing_entry = NfSubRelationalModel.query.filter(
136                 NfSubRelationalModel.subscription_name == self.subscriptionName,
137                 NfSubRelationalModel.nf_name == current_nf.nf_name).one_or_none()
138             if existing_entry is None:
139                 new_nf_sub = NfSubRelationalModel(self.subscriptionName,
140                                                   nf.nf_name, SubNfState.PENDING_CREATE.value)
141                 sub_model.nfs.append(new_nf_sub)
142                 db.session.add(sub_model)
143                 db.session.commit()
144                 logger.info(f'Network function {current_nf.nf_name} added to Subscription '
145                             f'{self.subscriptionName}')
146         except Exception as e:
147             logger.error(f'Failed to add nf {nf.nf_name} to subscription '
148                          f'{self.subscriptionName}: {e}', exc_info=True)
149             logger.debug(f'Subscription {self.subscriptionName} now contains these XNFs:'
150                          f'{[nf.nf_name for nf.nf_name in self.get_network_functions()]}')
151
152     def get(self):
153         """ Retrieves a SubscriptionModel object
154
155         Returns:
156             SubscriptionModel object else None
157         """
158         sub_model = SubscriptionModel.query.filter(
159             SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
160         return sub_model
161
162     def get_local_sub_admin_state(self):
163         """ Retrieves the subscription admin state
164
165         Returns:
166             str: The admin state of the SubscriptionModel
167         """
168         sub_model = SubscriptionModel.query.filter(
169             SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
170         db.session.remove()
171         return sub_model.status
172
173     @staticmethod
174     def get_all():
175         """ Retrieves a list of subscriptions
176
177         Returns:
178             list(SubscriptionModel): Subscriptions list else empty
179         """
180
181         sub_models = SubscriptionModel.query.all()
182         db.session.remove()
183         return sub_models
184
185     def activate_subscription(self, nfs, mr_pub, app_conf):
186         logger.info(f'Activate subscription initiated for {self.subscriptionName}.')
187         try:
188             existing_nfs = self.get_network_functions()
189             sub_model = self.get()
190             for nf in [new_nf for new_nf in nfs if new_nf not in existing_nfs]:
191                 logger.info(f'Publishing event to activate '
192                             f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
193                 mr_pub.publish_subscription_event_data(self, nf, app_conf)
194                 self.add_network_function_to_subscription(nf, sub_model)
195                 self.update_sub_nf_status(self.subscriptionName, SubNfState.PENDING_CREATE.value,
196                                           nf.nf_name)
197         except Exception as err:
198             raise Exception(f'Error publishing activation event to MR: {err}')
199
200     def deactivate_subscription(self, mr_pub, app_conf):
201         try:
202             nfs = self.get_network_functions()
203             if nfs:
204                 logger.info(f'Deactivate subscription initiated for {self.subscriptionName}.')
205                 for nf in nfs:
206                     mr_pub.publish_subscription_event_data(self, nf, app_conf)
207                     logger.debug(f'Publishing Event to deactivate '
208                                  f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
209                     self.update_sub_nf_status(self.subscriptionName,
210                                               SubNfState.PENDING_DELETE.value,
211                                               nf.nf_name)
212         except Exception as err:
213             raise Exception(f'Error publishing deactivation event to MR: {err}')
214
215     @staticmethod
216     def get_all_nfs_subscription_relations():
217         """ Retrieves all network function to subscription relations
218
219         Returns:
220             list(NfSubRelationalModel): NetworkFunctions per Subscription list else empty
221         """
222         nf_per_subscriptions = NfSubRelationalModel.query.all()
223         db.session.remove()
224         return nf_per_subscriptions
225
226     @staticmethod
227     def update_sub_nf_status(subscription_name, status, nf_name):
228         """ Updates the status of the subscription for a particular nf
229
230         Args:
231             subscription_name (str): The subscription name
232             nf_name (str): The network function name
233             status (str): Status of the subscription
234         """
235         try:
236             NfSubRelationalModel.query.filter(
237                 NfSubRelationalModel.subscription_name == subscription_name,
238                 NfSubRelationalModel.nf_name == nf_name). \
239                 update({NfSubRelationalModel.nf_sub_status: status}, synchronize_session='evaluate')
240             db.session.commit()
241         except Exception as e:
242             logger.error(f'Failed to update status of nf: {nf_name} for subscription: '
243                          f'{subscription_name}: {e}', exc_info=True)
244
245     def get_network_functions(self):
246         nf_sub_relationships = NfSubRelationalModel.query.filter(
247             NfSubRelationalModel.subscription_name == self.subscriptionName)
248         nfs = []
249         for nf_sub_entry in nf_sub_relationships:
250             nf_model_object = NetworkFunctionModel.query.filter(
251                 NetworkFunctionModel.nf_name == nf_sub_entry.nf_name).one_or_none()
252             nfs.append(nf_model_object.to_nf())
253         db.session.remove()
254         return nfs