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
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
16 # SPDX-License-Identifier: Apache-2.0
17 # ============LICENSE_END=====================================================
20 from tenacity import retry, retry_if_exception_type, wait_exponential, stop_after_attempt
22 from mod import db, logger
23 from mod.api.db_models import SubscriptionModel, NfSubRelationalModel, NetworkFunctionModel
24 from mod.network_function import NetworkFunction
27 class SubNfState(Enum):
28 PENDING_CREATE = 'PENDING_CREATE'
29 CREATE_FAILED = 'CREATE_FAILED'
31 PENDING_DELETE = 'PENDING_DELETE'
32 DELETE_FAILED = 'DELETE_FAILED'
35 class AdministrativeState(Enum):
40 subscription_nf_states = {
41 AdministrativeState.LOCKED.value: {
42 'success': SubNfState.CREATED,
43 'failed': SubNfState.DELETE_FAILED
45 AdministrativeState.UNLOCKED.value: {
46 'success': SubNfState.CREATED,
47 'failed': SubNfState.CREATE_FAILED
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')
61 def prepare_subscription_event(self, xnf_name, app_conf):
62 """Prepare the sub event for publishing
65 xnf_name: the AAI xnf name.
66 app_conf (AppConfig): the application configuration.
69 dict: the Subscription event to be published.
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}
80 """ Creates a subscription database entry
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)
93 return new_subscription
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}')
101 def add_network_function_to_subscription(self, nf):
102 """ Associates a network function to a Subscription
105 nf : A NetworkFunction object.
107 current_sub = self.create()
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)
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)}')
131 def get(subscription_name):
132 """ Retrieves a subscription
135 subscription_name (str): The subscription name
138 Subscription object else None
140 return SubscriptionModel.query.filter(
141 SubscriptionModel.subscription_name == subscription_name).one_or_none()
145 """ Retrieves a list of subscriptions
148 list: Subscription list else empty
150 return SubscriptionModel.query.all()
153 def get_nf_names_per_sub(subscription_name):
154 """ Retrieves a list of network function names related to the subscription
157 subscription_name (str): The subscription name
160 list: List of network function names
162 nf_sub_rel = NfSubRelationalModel.query.filter(
163 NfSubRelationalModel.subscription_name == subscription_name).all()
165 for nf in nf_sub_rel:
166 list_of_nfs.append(nf.nf_name)
170 def update_subscription_status(self):
171 """ Updates the status of subscription in subscription table """
173 SubscriptionModel.query.filter(
174 SubscriptionModel.subscription_name == self.subscriptionName)\
175 .update({SubscriptionModel.status: self.administrativeState},
176 synchronize_session='evaluate')
179 except Exception as e:
180 logger.debug(f'Failed to update status of subscription: {self.subscriptionName}: {e}')
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."""
186 subscription = SubscriptionModel.query.filter(
187 SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
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)
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}')
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()
208 if self.administrativeState == AdministrativeState.UNLOCKED.value:
209 logger.info(f'{action} subscription initiated for {self.subscriptionName}.')
211 sub_nf_state = SubNfState.PENDING_CREATE.value
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}')
224 def get_all_nfs_subscription_relations():
225 """ Retrieves all network function to subscription relations
228 list: NetworkFunctions per Subscription list else empty
230 nf_per_subscriptions = NfSubRelationalModel.query.all()
232 return nf_per_subscriptions
235 def update_sub_nf_status(subscription_name, status, nf_name):
236 """ Updates the status of the subscription for a particular nf
239 subscription_name (str): The subscription name
240 nf_name (str): The network function name
241 status (str): Status of the subscription
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')
249 except Exception as e:
250 logger.debug(f'Failed to update status of nf: {nf_name} for subscription: '
251 f'{subscription_name}: {e}')
253 def _get_nf_models(self):
254 nf_sub_relationships = NfSubRelationalModel.query.filter(
255 NfSubRelationalModel.subscription_name == self.subscriptionName)
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)
264 def get_network_functions(self):
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