efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 1 | # ============LICENSE_START=================================================== |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 2 | # Copyright (C) 2019-2020 Nordix Foundation. |
efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 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 | import re |
ERIMROB | 26b76c0 | 2020-02-12 11:35:20 +0000 | [diff] [blame^] | 19 | from enum import Enum |
efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 20 | |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 21 | import mod.pmsh_logging as logger |
| 22 | from mod import db |
| 23 | from mod.db_models import SubscriptionModel, NfSubRelationalModel |
ERIMROB | 26b76c0 | 2020-02-12 11:35:20 +0000 | [diff] [blame^] | 24 | from tenacity import retry, retry_if_exception_type, wait_exponential, stop_after_attempt |
| 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' |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 38 | |
efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 39 | |
| 40 | class Subscription: |
| 41 | def __init__(self, **kwargs): |
| 42 | self.subscriptionName = kwargs.get('subscriptionName') |
| 43 | self.administrativeState = kwargs.get('administrativeState') |
| 44 | self.fileBasedGP = kwargs.get('fileBasedGP') |
| 45 | self.fileLocation = kwargs.get('fileLocation') |
| 46 | self.nfTypeModelInvariantId = kwargs.get('nfTypeModelInvariantId') |
| 47 | self.nfFilter = kwargs.get('nfFilter') |
| 48 | self.measurementGroups = kwargs.get('measurementGroups') |
| 49 | |
| 50 | def prepare_subscription_event(self, xnf_name): |
| 51 | """Prepare the sub event for publishing |
| 52 | |
| 53 | Args: |
| 54 | xnf_name: the AAI xnf name. |
| 55 | |
| 56 | Returns: |
| 57 | dict: the Subscription event to be published. |
| 58 | """ |
| 59 | clean_sub = {k: v for k, v in self.__dict__.items() if k != 'nfFilter'} |
ERIMROB | 26b76c0 | 2020-02-12 11:35:20 +0000 | [diff] [blame^] | 60 | clean_sub.update({'nfName': xnf_name, 'policyName': f'OP-{self.subscriptionName}', |
| 61 | 'changeType': 'DELETE' |
| 62 | if self.administrativeState == AdministrativeState.LOCKED.value |
| 63 | else 'CREATE'}) |
efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 64 | return clean_sub |
| 65 | |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 66 | def create(self): |
| 67 | """ Creates a subscription database entry |
efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 68 | |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 69 | Returns: |
| 70 | Subscription object |
| 71 | """ |
| 72 | existing_subscription = (SubscriptionModel.query.filter( |
| 73 | SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()) |
| 74 | |
| 75 | if existing_subscription is None: |
| 76 | new_subscription = SubscriptionModel(subscription_name=self.subscriptionName, |
| 77 | status=self.administrativeState) |
| 78 | |
| 79 | db.session.add(new_subscription) |
| 80 | db.session.commit() |
| 81 | |
| 82 | return new_subscription |
| 83 | |
| 84 | else: |
| 85 | logger.debug(f'Subscription {self.subscriptionName} already exists,' |
| 86 | f' returning this subscription..') |
| 87 | return existing_subscription |
| 88 | |
| 89 | def add_network_functions_to_subscription(self, nf_list): |
| 90 | """ Associates network functions to a Subscription |
| 91 | |
| 92 | Args: |
| 93 | nf_list : A list of NetworkFunction objects. |
| 94 | """ |
| 95 | current_sub = self.create() |
| 96 | logger.debug(f'Adding network functions to subscription {current_sub.subscription_name}') |
| 97 | |
| 98 | for nf in nf_list: |
| 99 | current_nf = nf.create() |
| 100 | |
| 101 | existing_entry = NfSubRelationalModel.query.filter( |
| 102 | NfSubRelationalModel.subscription_name == current_sub.subscription_name, |
| 103 | NfSubRelationalModel.nf_name == current_nf.nf_name).one_or_none() |
| 104 | if existing_entry is None: |
ERIMROB | 26b76c0 | 2020-02-12 11:35:20 +0000 | [diff] [blame^] | 105 | new_nf_sub = NfSubRelationalModel(current_sub.subscription_name, |
| 106 | nf.nf_name, SubNfState.PENDING_CREATE.value) |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 107 | new_nf_sub.nf = current_nf |
| 108 | logger.debug(current_nf) |
| 109 | current_sub.nfs.append(new_nf_sub) |
| 110 | |
| 111 | db.session.add(current_sub) |
| 112 | db.session.commit() |
| 113 | |
| 114 | @staticmethod |
| 115 | def get(subscription_name): |
| 116 | """ Retrieves a subscription |
| 117 | |
| 118 | Args: |
| 119 | subscription_name (str): The subscription name |
| 120 | |
| 121 | Returns: |
| 122 | Subscription object else None |
| 123 | """ |
| 124 | return SubscriptionModel.query.filter( |
| 125 | SubscriptionModel.subscription_name == subscription_name).one_or_none() |
| 126 | |
| 127 | @staticmethod |
| 128 | def get_all(): |
| 129 | """ Retrieves a list of subscriptions |
| 130 | |
| 131 | Returns: |
| 132 | list: Subscription list else empty |
| 133 | """ |
| 134 | return SubscriptionModel.query.all() |
| 135 | |
ERIMROB | 26b76c0 | 2020-02-12 11:35:20 +0000 | [diff] [blame^] | 136 | def update_subscription_status(self): |
| 137 | """ Updates the status of subscription in subscription table """ |
| 138 | SubscriptionModel.query.filter( |
| 139 | SubscriptionModel.subscription_name == self.subscriptionName). \ |
| 140 | update({SubscriptionModel.status: self.administrativeState}, |
| 141 | synchronize_session='evaluate') |
| 142 | |
| 143 | db.session.commit() |
| 144 | |
| 145 | def delete_subscription(self): |
| 146 | """ Deletes a subscription from the database """ |
| 147 | SubscriptionModel.query.filter( |
| 148 | SubscriptionModel.subscription_name == self.subscriptionName). \ |
| 149 | delete(synchronize_session='evaluate') |
| 150 | |
| 151 | db.session.commit() |
| 152 | |
| 153 | @retry(wait=wait_exponential(multiplier=1, min=30, max=120), stop=stop_after_attempt(3), |
| 154 | retry=retry_if_exception_type(Exception)) |
| 155 | def process_subscription(self, nfs, mr_pub): |
| 156 | action = 'Deactivate' |
| 157 | sub_nf_state = SubNfState.PENDING_DELETE.value |
| 158 | self.update_subscription_status() |
| 159 | |
| 160 | if self.administrativeState == AdministrativeState.UNLOCKED.value: |
| 161 | action = 'Activate' |
| 162 | sub_nf_state = SubNfState.PENDING_CREATE.value |
| 163 | |
| 164 | try: |
| 165 | for nf in nfs: |
| 166 | mr_pub.publish_subscription_event_data(self, nf.nf_name) |
| 167 | logger.debug(f'Publishing Event to {action} ' |
| 168 | f'Sub: {self.subscriptionName} for the nf: {nf.nf_name}') |
| 169 | self.add_network_functions_to_subscription(nfs) |
| 170 | self.update_sub_nf_status(self.subscriptionName, sub_nf_state, nf.nf_name) |
| 171 | except Exception as err: |
| 172 | raise Exception(f'Error publishing activation event to MR: {err}') |
| 173 | |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 174 | @staticmethod |
| 175 | def get_all_nfs_subscription_relations(): |
| 176 | """ Retrieves all network function to subscription relations |
| 177 | |
| 178 | Returns: |
| 179 | list: NetworkFunctions per Subscription list else empty |
| 180 | """ |
| 181 | nf_per_subscriptions = NfSubRelationalModel.query.all() |
| 182 | |
| 183 | return nf_per_subscriptions |
| 184 | |
ERIMROB | 26b76c0 | 2020-02-12 11:35:20 +0000 | [diff] [blame^] | 185 | @staticmethod |
| 186 | def update_sub_nf_status(subscription_name, status, nf_name): |
| 187 | """ Updates the status of the subscription for a particular nf |
| 188 | |
| 189 | Args: |
| 190 | subscription_name (str): The subscription name |
| 191 | nf_name (str): The network function name |
| 192 | status (str): Status of the subscription |
| 193 | """ |
| 194 | NfSubRelationalModel.query.filter( |
| 195 | NfSubRelationalModel.subscription_name == subscription_name, |
| 196 | NfSubRelationalModel.nf_name == nf_name). \ |
| 197 | update({NfSubRelationalModel.nf_sub_status: status}, synchronize_session='evaluate') |
| 198 | |
| 199 | db.session.commit() |
| 200 | |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 201 | |
| 202 | class NetworkFunctionFilter: |
efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 203 | def __init__(self, **kwargs): |
| 204 | self.nf_sw_version = kwargs.get('swVersions') |
| 205 | self.nf_names = kwargs.get('nfNames') |
| 206 | self.regex_matcher = re.compile('|'.join(raw_regex for raw_regex in self.nf_names)) |
| 207 | |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 208 | def is_nf_in_filter(self, nf_name): |
| 209 | """Match the nf name against regex values in Subscription.nfFilter.nfNames |
efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 210 | |
| 211 | Args: |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 212 | nf_name: the AAI nf name. |
efiacor | bbe05d8 | 2019-12-11 12:00:26 +0000 | [diff] [blame] | 213 | |
| 214 | Returns: |
| 215 | bool: True if matched, else False. |
| 216 | """ |
efiacor | 8b3fc62 | 2020-01-24 13:19:01 +0000 | [diff] [blame] | 217 | return self.regex_matcher.search(nf_name) |