Merge "[Datalake] Admin UI release 1.1.0"
[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
83     def update_subscription_status(self):
84         """ Updates the status of subscription in subscription table """
85         try:
86             SubscriptionModel.query.filter(
87                 SubscriptionModel.subscription_name == self.subscriptionName)\
88                 .update({SubscriptionModel.status: self.administrativeState},
89                         synchronize_session='evaluate')
90
91             db.session.commit()
92         except Exception as e:
93             logger.error(f'Failed to update status of subscription: {self.subscriptionName}: {e}',
94                          exc_info=True)
95
96     def prepare_subscription_event(self, nf, app_conf):
97         """Prepare the sub event for publishing
98
99         Args:
100             nf (NetworkFunction): the AAI nf.
101             app_conf (AppConfig): the application configuration.
102
103         Returns:
104             dict: the Subscription event to be published.
105         """
106         try:
107             clean_sub = {k: v for k, v in self.__dict__.items() if k != 'nfFilter'}
108             sub_event = {'nfName': nf.nf_name, 'blueprintName': nf.sdnc_model_name,
109                          'blueprintVersion': nf.sdnc_model_version,
110                          'policyName': app_conf.operational_policy_name,
111                          'changeType': 'DELETE'
112                          if self.administrativeState == AdministrativeState.LOCKED.value
113                          else 'CREATE', 'closedLoopControlName': app_conf.control_loop_name,
114                          'subscription': clean_sub}
115             return sub_event
116         except Exception as e:
117             logger.error(f'Failed to prep Sub event for xNF {nf.nf_name}: {e}', exc_info=True)
118             raise
119
120     def add_network_function_to_subscription(self, nf, sub_model):
121         """ Associates a network function to a Subscription
122
123         Args:
124             sub_model(SubscriptionModel): The SubscriptionModel from the DB.
125             nf(NetworkFunction): A NetworkFunction object.
126         """
127         try:
128             current_nf = nf.create()
129             existing_entry = NfSubRelationalModel.query.filter(
130                 NfSubRelationalModel.subscription_name == self.subscriptionName,
131                 NfSubRelationalModel.nf_name == current_nf.nf_name).one_or_none()
132             if existing_entry is None:
133                 new_nf_sub = NfSubRelationalModel(self.subscriptionName,
134                                                   nf.nf_name, SubNfState.PENDING_CREATE.value)
135                 sub_model.nfs.append(new_nf_sub)
136                 db.session.add(sub_model)
137                 db.session.commit()
138                 logger.info(f'Network function {current_nf.nf_name} added to Subscription '
139                             f'{self.subscriptionName}')
140         except Exception as e:
141             logger.error(f'Failed to add nf {nf.nf_name} to subscription '
142                          f'{self.subscriptionName}: {e}', exc_info=True)
143             logger.debug(f'Subscription {self.subscriptionName} now contains these XNFs:'
144                          f'{[nf.nf_name for nf.nf_name in self.get_network_functions()]}')
145
146     def get(self):
147         """ Retrieves a SubscriptionModel object
148
149         Returns:
150             SubscriptionModel object else None
151         """
152         return SubscriptionModel.query.filter(
153             SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
154
155     def get_local_sub_admin_state(self):
156         """ Retrieves the subscription admin state
157
158         Returns:
159             str: The admin state of the SubscriptionModel
160         """
161         sub_model = SubscriptionModel.query.filter(
162             SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
163         return sub_model.status
164
165     @staticmethod
166     def get_all():
167         """ Retrieves a list of subscriptions
168
169         Returns:
170             list(SubscriptionModel): Subscriptions list else empty
171         """
172         return SubscriptionModel.query.all()
173
174     def activate_subscription(self, nfs, mr_pub, app_conf):
175         logger.info(f'Activate subscription initiated for {self.subscriptionName}.')
176         try:
177             existing_nfs = self.get_network_functions()
178             sub_model = self.get()
179             for nf in set(nfs + existing_nfs):
180                 logger.info(f'Publishing event to activate '
181                             f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
182                 mr_pub.publish_subscription_event_data(self, nf, app_conf)
183                 self.add_network_function_to_subscription(nf, sub_model)
184                 self.update_sub_nf_status(self.subscriptionName, SubNfState.PENDING_CREATE.value,
185                                           nf.nf_name)
186         except Exception as err:
187             raise Exception(f'Error publishing activation event to MR: {err}')
188
189     def deactivate_subscription(self, mr_pub, app_conf):
190         try:
191             nfs = self.get_network_functions()
192             if nfs:
193                 logger.info(f'Deactivate subscription initiated for {self.subscriptionName}.')
194                 for nf in nfs:
195                     mr_pub.publish_subscription_event_data(self, nf, app_conf)
196                     logger.debug(f'Publishing Event to deactivate '
197                                  f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
198                     self.update_sub_nf_status(self.subscriptionName,
199                                               SubNfState.PENDING_DELETE.value,
200                                               nf.nf_name)
201         except Exception as err:
202             raise Exception(f'Error publishing deactivation event to MR: {err}')
203
204     @staticmethod
205     def get_all_nfs_subscription_relations():
206         """ Retrieves all network function to subscription relations
207
208         Returns:
209             list(NfSubRelationalModel): NetworkFunctions per Subscription list else empty
210         """
211         nf_per_subscriptions = NfSubRelationalModel.query.all()
212         return nf_per_subscriptions
213
214     @staticmethod
215     def update_sub_nf_status(subscription_name, status, nf_name):
216         """ Updates the status of the subscription for a particular nf
217
218         Args:
219             subscription_name (str): The subscription name
220             nf_name (str): The network function name
221             status (str): Status of the subscription
222         """
223         try:
224             NfSubRelationalModel.query.filter(
225                 NfSubRelationalModel.subscription_name == subscription_name,
226                 NfSubRelationalModel.nf_name == nf_name). \
227                 update({NfSubRelationalModel.nf_sub_status: status}, synchronize_session='evaluate')
228             db.session.commit()
229         except Exception as e:
230             logger.error(f'Failed to update status of nf: {nf_name} for subscription: '
231                          f'{subscription_name}: {e}', exc_info=True)
232
233     def get_network_functions(self):
234         nf_sub_relationships = NfSubRelationalModel.query.filter(
235             NfSubRelationalModel.subscription_name == self.subscriptionName)
236         nfs = []
237         for nf_sub_entry in nf_sub_relationships:
238             nf_model_object = NetworkFunctionModel.query.filter(
239                 NetworkFunctionModel.nf_name == nf_sub_entry.nf_name).one_or_none()
240             nfs.append(nf_model_object.to_nf())
241
242         return nfs