blob: 3add72006f9a1778c90002254c45915975fcc4d6 [file] [log] [blame]
efiacorbbe05d82019-12-11 12:00:26 +00001# ============LICENSE_START===================================================
efiacor8b3fc622020-01-24 13:19:01 +00002# Copyright (C) 2019-2020 Nordix Foundation.
efiacorbbe05d82019-12-11 12:00:26 +00003# ============================================================================
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=====================================================
ERIMROBb074a922020-02-27 10:05:37 +000018
ERIMROB26b76c02020-02-12 11:35:20 +000019from enum import Enum
efiacorbbe05d82019-12-11 12:00:26 +000020
ERIMROBb074a922020-02-27 10:05:37 +000021from tenacity import retry, retry_if_exception_type, wait_exponential, stop_after_attempt
22
efiacor8b3fc622020-01-24 13:19:01 +000023import mod.pmsh_logging as logger
24from mod import db
ERIMROBe97cde02020-04-01 17:54:19 +010025from mod.db_models import SubscriptionModel, NfSubRelationalModel, NetworkFunctionModel
26from mod.network_function import NetworkFunction
ERIMROB26b76c02020-02-12 11:35:20 +000027
28
29class SubNfState(Enum):
30 PENDING_CREATE = 'PENDING_CREATE'
31 CREATE_FAILED = 'CREATE_FAILED'
32 CREATED = 'CREATED'
33 PENDING_DELETE = 'PENDING_DELETE'
34 DELETE_FAILED = 'DELETE_FAILED'
35
36
37class AdministrativeState(Enum):
38 UNLOCKED = 'UNLOCKED'
39 LOCKED = 'LOCKED'
efiacor8b3fc622020-01-24 13:19:01 +000040
efiacorbbe05d82019-12-11 12:00:26 +000041
ERIMROBb074a922020-02-27 10:05:37 +000042subscription_nf_states = {
43 AdministrativeState.LOCKED.value: {
44 'success': SubNfState.CREATED,
45 'failed': SubNfState.DELETE_FAILED
46 },
47 AdministrativeState.UNLOCKED.value: {
48 'success': SubNfState.CREATED,
49 'failed': SubNfState.CREATE_FAILED
50 }
51}
52
53
efiacorbbe05d82019-12-11 12:00:26 +000054class Subscription:
55 def __init__(self, **kwargs):
56 self.subscriptionName = kwargs.get('subscriptionName')
57 self.administrativeState = kwargs.get('administrativeState')
58 self.fileBasedGP = kwargs.get('fileBasedGP')
59 self.fileLocation = kwargs.get('fileLocation')
efiacorbbe05d82019-12-11 12:00:26 +000060 self.nfFilter = kwargs.get('nfFilter')
61 self.measurementGroups = kwargs.get('measurementGroups')
62
emartinc19a0a82020-02-27 13:56:52 +000063 def prepare_subscription_event(self, xnf_name, app_conf):
efiacorbbe05d82019-12-11 12:00:26 +000064 """Prepare the sub event for publishing
65
66 Args:
67 xnf_name: the AAI xnf name.
emartinc19a0a82020-02-27 13:56:52 +000068 app_conf (AppConfig): the application configuration.
efiacorbbe05d82019-12-11 12:00:26 +000069
70 Returns:
71 dict: the Subscription event to be published.
72 """
73 clean_sub = {k: v for k, v in self.__dict__.items() if k != 'nfFilter'}
emartinc19a0a82020-02-27 13:56:52 +000074 sub_event = {'nfName': xnf_name, 'policyName': app_conf.operational_policy_name,
75 'changeType': 'DELETE'
76 if self.administrativeState == AdministrativeState.LOCKED.value
77 else 'CREATE', 'closedLoopControlName': app_conf.control_loop_name,
78 'subscription': clean_sub}
79 return sub_event
efiacorbbe05d82019-12-11 12:00:26 +000080
efiacor8b3fc622020-01-24 13:19:01 +000081 def create(self):
82 """ Creates a subscription database entry
efiacorbbe05d82019-12-11 12:00:26 +000083
efiacor8b3fc622020-01-24 13:19:01 +000084 Returns:
85 Subscription object
86 """
87 existing_subscription = (SubscriptionModel.query.filter(
88 SubscriptionModel.subscription_name == self.subscriptionName).one_or_none())
89
90 if existing_subscription is None:
91 new_subscription = SubscriptionModel(subscription_name=self.subscriptionName,
92 status=self.administrativeState)
93
94 db.session.add(new_subscription)
95 db.session.commit()
96
97 return new_subscription
98
99 else:
100 logger.debug(f'Subscription {self.subscriptionName} already exists,'
101 f' returning this subscription..')
102 return existing_subscription
103
104 def add_network_functions_to_subscription(self, nf_list):
105 """ Associates network functions to a Subscription
106
107 Args:
108 nf_list : A list of NetworkFunction objects.
109 """
110 current_sub = self.create()
111 logger.debug(f'Adding network functions to subscription {current_sub.subscription_name}')
112
113 for nf in nf_list:
114 current_nf = nf.create()
115
116 existing_entry = NfSubRelationalModel.query.filter(
117 NfSubRelationalModel.subscription_name == current_sub.subscription_name,
118 NfSubRelationalModel.nf_name == current_nf.nf_name).one_or_none()
119 if existing_entry is None:
ERIMROB26b76c02020-02-12 11:35:20 +0000120 new_nf_sub = NfSubRelationalModel(current_sub.subscription_name,
121 nf.nf_name, SubNfState.PENDING_CREATE.value)
efiacor8b3fc622020-01-24 13:19:01 +0000122 new_nf_sub.nf = current_nf
123 logger.debug(current_nf)
124 current_sub.nfs.append(new_nf_sub)
125
126 db.session.add(current_sub)
127 db.session.commit()
128
129 @staticmethod
130 def get(subscription_name):
131 """ Retrieves a subscription
132
133 Args:
134 subscription_name (str): The subscription name
135
136 Returns:
137 Subscription object else None
138 """
139 return SubscriptionModel.query.filter(
140 SubscriptionModel.subscription_name == subscription_name).one_or_none()
141
142 @staticmethod
143 def get_all():
144 """ Retrieves a list of subscriptions
145
146 Returns:
147 list: Subscription list else empty
148 """
149 return SubscriptionModel.query.all()
150
ERIMROB26b76c02020-02-12 11:35:20 +0000151 def update_subscription_status(self):
152 """ Updates the status of subscription in subscription table """
153 SubscriptionModel.query.filter(
154 SubscriptionModel.subscription_name == self.subscriptionName). \
155 update({SubscriptionModel.status: self.administrativeState},
156 synchronize_session='evaluate')
157
158 db.session.commit()
159
160 def delete_subscription(self):
emartine7f69142020-02-24 14:13:03 +0000161 """ Deletes a subscription and all its association from the database. A network function
162 that is only associated with the subscription being removed will also be deleted."""
163 subscription = SubscriptionModel.query.filter(
164 SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
165 if subscription:
166 for nf_relationship in subscription.nfs:
167 other_nf_relationship = NfSubRelationalModel.query.filter(
168 NfSubRelationalModel.subscription_name != self.subscriptionName,
169 NfSubRelationalModel.nf_name == nf_relationship.nf_name).one_or_none()
170 if not other_nf_relationship:
171 db.session.delete(nf_relationship.nf)
172 db.session.delete(subscription)
173 db.session.commit()
ERIMROB26b76c02020-02-12 11:35:20 +0000174
175 @retry(wait=wait_exponential(multiplier=1, min=30, max=120), stop=stop_after_attempt(3),
176 retry=retry_if_exception_type(Exception))
emartinc19a0a82020-02-27 13:56:52 +0000177 def process_subscription(self, nfs, mr_pub, app_conf):
ERIMROB26b76c02020-02-12 11:35:20 +0000178 action = 'Deactivate'
179 sub_nf_state = SubNfState.PENDING_DELETE.value
180 self.update_subscription_status()
181
182 if self.administrativeState == AdministrativeState.UNLOCKED.value:
183 action = 'Activate'
184 sub_nf_state = SubNfState.PENDING_CREATE.value
185
186 try:
187 for nf in nfs:
emartinc19a0a82020-02-27 13:56:52 +0000188 mr_pub.publish_subscription_event_data(self, nf.nf_name, app_conf)
ERIMROB26b76c02020-02-12 11:35:20 +0000189 logger.debug(f'Publishing Event to {action} '
190 f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}')
191 self.add_network_functions_to_subscription(nfs)
192 self.update_sub_nf_status(self.subscriptionName, sub_nf_state, nf.nf_name)
193 except Exception as err:
194 raise Exception(f'Error publishing activation event to MR: {err}')
195
efiacor8b3fc622020-01-24 13:19:01 +0000196 @staticmethod
197 def get_all_nfs_subscription_relations():
198 """ Retrieves all network function to subscription relations
199
200 Returns:
201 list: NetworkFunctions per Subscription list else empty
202 """
203 nf_per_subscriptions = NfSubRelationalModel.query.all()
204
205 return nf_per_subscriptions
206
ERIMROB26b76c02020-02-12 11:35:20 +0000207 @staticmethod
208 def update_sub_nf_status(subscription_name, status, nf_name):
209 """ Updates the status of the subscription for a particular nf
210
211 Args:
212 subscription_name (str): The subscription name
213 nf_name (str): The network function name
214 status (str): Status of the subscription
215 """
216 NfSubRelationalModel.query.filter(
217 NfSubRelationalModel.subscription_name == subscription_name,
218 NfSubRelationalModel.nf_name == nf_name). \
219 update({NfSubRelationalModel.nf_sub_status: status}, synchronize_session='evaluate')
220
221 db.session.commit()
ERIMROBe97cde02020-04-01 17:54:19 +0100222
223 def _get_nf_models(self):
224 nf_sub_relationships = NfSubRelationalModel.query.filter(
225 NfSubRelationalModel.subscription_name == self.subscriptionName)
226 nf_models = []
227 for nf_sub_entry in nf_sub_relationships:
228 nf_model_object = NetworkFunctionModel.query.filter(
229 NetworkFunctionModel.nf_name == nf_sub_entry.nf_name).one_or_none()
230 nf_models.append(nf_model_object)
231
232 return nf_models
233
234 def get_network_functions(self):
235 nfs = []
236 nf_models = self._get_nf_models()
237 for nf_model in nf_models:
238 nf = NetworkFunction(
239 nf_name=nf_model.nf_name,
240 orchestration_status=nf_model.orchestration_status
241 )
242 nfs.append(nf)
243
244 return nfs