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