Refactor and bug fixes
[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 from mod.network_function import NetworkFunction
23
24
25 class SubNfState(Enum):
26     PENDING_CREATE = 'PENDING_CREATE'
27     CREATE_FAILED = 'CREATE_FAILED'
28     CREATED = 'CREATED'
29     PENDING_DELETE = 'PENDING_DELETE'
30     DELETE_FAILED = 'DELETE_FAILED'
31
32
33 class AdministrativeState(Enum):
34     UNLOCKED = 'UNLOCKED'
35     LOCKED = 'LOCKED'
36     PENDING = 'PENDING'
37
38
39 subscription_nf_states = {
40     AdministrativeState.LOCKED.value: {
41         'success': SubNfState.CREATED,
42         'failed': SubNfState.DELETE_FAILED
43     },
44     AdministrativeState.UNLOCKED.value: {
45         'success': SubNfState.CREATED,
46         'failed': SubNfState.CREATE_FAILED
47     }
48 }
49
50
51 class Subscription:
52     def __init__(self, **kwargs):
53         self.subscriptionName = kwargs.get('subscriptionName')
54         self.administrativeState = kwargs.get('administrativeState')
55         self.fileBasedGP = kwargs.get('fileBasedGP')
56         self.fileLocation = kwargs.get('fileLocation')
57         self.nfFilter = kwargs.get('nfFilter')
58         self.measurementGroups = kwargs.get('measurementGroups')
59         self.create()
60
61     def create(self):
62         """ Creates a subscription database entry
63
64         Returns:
65             Subscription object
66         """
67         try:
68             existing_subscription = (SubscriptionModel.query.filter(
69                 SubscriptionModel.subscription_name == self.subscriptionName).one_or_none())
70             if existing_subscription is None:
71                 new_subscription = SubscriptionModel(subscription_name=self.subscriptionName,
72                                                      status=AdministrativeState.PENDING.value)
73                 db.session.add(new_subscription)
74                 db.session.commit()
75                 return new_subscription
76             else:
77                 logger.debug(f'Subscription {self.subscriptionName} already exists,'
78                              f' returning this subscription..')
79                 return existing_subscription
80         except Exception as e:
81             logger.error(f'Failed to create subscription {self.subscriptionName} in the DB: {e}',
82                          exc_info=True)
83
84     def update_subscription_status(self):
85         """ Updates the status of subscription in subscription table """
86         try:
87             SubscriptionModel.query.filter(
88                 SubscriptionModel.subscription_name == self.subscriptionName)\
89                 .update({SubscriptionModel.status: self.administrativeState},
90                         synchronize_session='evaluate')
91
92             db.session.commit()
93         except Exception as e:
94             logger.error(f'Failed to update status of subscription: {self.subscriptionName}: {e}',
95                          exc_info=True)
96
97     def delete_subscription(self):
98         """ Deletes a subscription and all its association from the database. A network function
99         that is only associated with the subscription being removed will also be deleted."""
100         try:
101             subscription = SubscriptionModel.query.filter(
102                 SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
103             if subscription:
104                 for nf_relationship in subscription.nfs:
105                     other_nf_relationship = NfSubRelationalModel.query.filter(
106                         NfSubRelationalModel.subscription_name != self.subscriptionName,
107                         NfSubRelationalModel.nf_name == nf_relationship.nf_name).one_or_none()
108                     if not other_nf_relationship:
109                         db.session.delete(nf_relationship.nf)
110                 db.session.delete(subscription)
111                 db.session.commit()
112         except Exception as e:
113             logger.error(f'Failed to delete subscription: {self.subscriptionName} '
114                          f'and it\'s relations from the DB: {e}', exc_info=True)
115
116     def prepare_subscription_event(self, xnf_name, app_conf):
117         """Prepare the sub event for publishing
118
119         Args:
120             xnf_name: the AAI xnf name.
121             app_conf (AppConfig): the application configuration.
122
123         Returns:
124             dict: the Subscription event to be published.
125         """
126         try:
127             clean_sub = {k: v for k, v in self.__dict__.items() if k != 'nfFilter'}
128             sub_event = {'nfName': xnf_name, 'policyName': app_conf.operational_policy_name,
129                          'changeType': 'DELETE'
130                          if self.administrativeState == AdministrativeState.LOCKED.value
131                          else 'CREATE', 'closedLoopControlName': app_conf.control_loop_name,
132                          'subscription': clean_sub}
133             return sub_event
134         except Exception as e:
135             logger.error(f'Failed to prep Sub event for xNF {xnf_name}: {e}', exc_info=True)
136             raise
137
138     def add_network_function_to_subscription(self, nf, sub_model):
139         """ Associates a network function to a Subscription
140
141         Args:
142             sub_model(SubscriptionModel): The SubscriptionModel from the DB.
143             nf(NetworkFunction): A NetworkFunction object.
144         """
145         try:
146             current_nf = nf.create()
147             existing_entry = NfSubRelationalModel.query.filter(
148                 NfSubRelationalModel.subscription_name == self.subscriptionName,
149                 NfSubRelationalModel.nf_name == current_nf.nf_name).one_or_none()
150             if existing_entry is None:
151                 new_nf_sub = NfSubRelationalModel(self.subscriptionName,
152                                                   nf.nf_name, SubNfState.PENDING_CREATE.value)
153                 sub_model.nfs.append(new_nf_sub)
154                 db.session.add(sub_model)
155                 db.session.commit()
156                 logger.info(f'Network function {current_nf.nf_name} added to Subscription '
157                             f'{self.subscriptionName}')
158         except Exception as e:
159             logger.error(f'Failed to add nf {nf.nf_name} to subscription '
160                          f'{self.subscriptionName}: {e}', exc_info=True)
161             logger.debug(f'Subscription {self.subscriptionName} now contains these XNFs:'
162                          f'{Subscription.get_nf_names_per_sub(self.subscriptionName)}')
163
164     def get(self):
165         """ Retrieves a SubscriptionModel object
166
167         Returns:
168             SubscriptionModel object else None
169         """
170         return SubscriptionModel.query.filter(
171             SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
172
173     def get_local_sub_admin_state(self):
174         """ Retrieves the subscription admin state
175
176         Returns:
177             str: The admin state of the SubscriptionModel
178         """
179         sub_model = SubscriptionModel.query.filter(
180             SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
181         return sub_model.status
182
183     @staticmethod
184     def get_all():
185         """ Retrieves a list of subscriptions
186
187         Returns:
188             list(SubscriptionModel): Subscriptions list else empty
189         """
190         return SubscriptionModel.query.all()
191
192     @staticmethod
193     def get_nf_names_per_sub(subscription_name):
194         """ Retrieves a list of network function names related to the subscription
195
196         Args:
197             subscription_name (str): The subscription name
198
199         Returns:
200             list(str): List of network function names
201         """
202         nf_sub_rel = NfSubRelationalModel.query.filter(
203             NfSubRelationalModel.subscription_name == subscription_name).all()
204         list_of_nfs = []
205         for nf in nf_sub_rel:
206             list_of_nfs.append(nf.nf_name)
207
208         return list_of_nfs
209
210     def activate_subscription(self, nfs, mr_pub, app_conf):
211         logger.info(f'Activate subscription initiated for {self.subscriptionName}.')
212         try:
213             sub_model = self.get()
214             for nf in nfs:
215                 mr_pub.publish_subscription_event_data(self, nf.nf_name, app_conf)
216                 logger.info(f'Publishing event to activate '
217                             f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
218                 self.add_network_function_to_subscription(nf, sub_model)
219                 self.update_sub_nf_status(self.subscriptionName, SubNfState.PENDING_CREATE.value,
220                                           nf.nf_name)
221         except Exception as err:
222             raise Exception(f'Error publishing activation event to MR: {err}')
223
224     def deactivate_subscription(self, mr_pub, app_conf):
225         nfs = self.get_network_functions()
226         try:
227             if nfs:
228                 logger.info(f'Deactivate subscription initiated for {self.subscriptionName}.')
229                 for nf in nfs:
230                     mr_pub.publish_subscription_event_data(self, nf.nf_name, app_conf)
231                     logger.debug(f'Publishing Event to deactivate '
232                                  f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
233                     self.update_sub_nf_status(self.subscriptionName,
234                                               SubNfState.PENDING_DELETE.value,
235                                               nf.nf_name)
236         except Exception as err:
237             raise Exception(f'Error publishing deactivation event to MR: {err}')
238
239     @staticmethod
240     def get_all_nfs_subscription_relations():
241         """ Retrieves all network function to subscription relations
242
243         Returns:
244             list(NfSubRelationalModel): NetworkFunctions per Subscription list else empty
245         """
246         nf_per_subscriptions = NfSubRelationalModel.query.all()
247         return nf_per_subscriptions
248
249     @staticmethod
250     def update_sub_nf_status(subscription_name, status, nf_name):
251         """ Updates the status of the subscription for a particular nf
252
253         Args:
254             subscription_name (str): The subscription name
255             nf_name (str): The network function name
256             status (str): Status of the subscription
257         """
258         try:
259             NfSubRelationalModel.query.filter(
260                 NfSubRelationalModel.subscription_name == subscription_name,
261                 NfSubRelationalModel.nf_name == nf_name). \
262                 update({NfSubRelationalModel.nf_sub_status: status}, synchronize_session='evaluate')
263             db.session.commit()
264         except Exception as e:
265             logger.error(f'Failed to update status of nf: {nf_name} for subscription: '
266                          f'{subscription_name}: {e}', exc_info=True)
267
268     def _get_nf_models(self):
269         nf_sub_relationships = NfSubRelationalModel.query.filter(
270             NfSubRelationalModel.subscription_name == self.subscriptionName)
271         nf_models = []
272         for nf_sub_entry in nf_sub_relationships:
273             nf_model_object = NetworkFunctionModel.query.filter(
274                 NetworkFunctionModel.nf_name == nf_sub_entry.nf_name).one_or_none()
275             nf_models.append(nf_model_object)
276
277         return nf_models
278
279     def get_network_functions(self):
280         nfs = []
281         nf_models = self._get_nf_models()
282         for nf_model in nf_models:
283             nf = NetworkFunction(
284                 nf_name=nf_model.nf_name,
285                 orchestration_status=nf_model.orchestration_status
286             )
287             nfs.append(nf)
288
289         return nfs