From 70de6a27b7722e3ed02d8e8a8c7933e053eacabb Mon Sep 17 00:00:00 2001 From: SagarS Date: Wed, 8 Sep 2021 14:46:32 +0100 Subject: [DCAEGEN2] PMSH Create Subscription public API Issue-ID: DCAEGEN2-2819 Change-Id: I80636be25dc4f7b1c5ce7470c7a38c010cb339a1 Signed-off-by: SagarS --- .../pmsh_service/mod/aai_client.py | 14 +- .../pmsh_service/mod/api/controller.py | 38 ++- .../pmsh_service/mod/api/custom_exception.py | 38 +++ .../pmsh_service/mod/api/db_models.py | 21 +- .../pmsh_service/mod/api/pmsh_swagger.yml | 34 ++- .../mod/api/services/measurement_group_service.py | 87 +++++++ .../pmsh_service/mod/api/services/nf_service.py | 75 ++++++ .../mod/api/services/subscription_service.py | 275 +++++++++++++++++++++ .../pmsh_service/mod/network_function.py | 16 +- .../pmsh_service/mod/pmsh_config.py | 3 + .../pmsh_service/mod/subscription_handler.py | 2 +- 11 files changed, 584 insertions(+), 19 deletions(-) create mode 100644 components/pm-subscription-handler/pmsh_service/mod/api/custom_exception.py create mode 100644 components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py create mode 100644 components/pm-subscription-handler/pmsh_service/mod/api/services/nf_service.py create mode 100644 components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py (limited to 'components/pm-subscription-handler/pmsh_service') diff --git a/components/pm-subscription-handler/pmsh_service/mod/aai_client.py b/components/pm-subscription-handler/pmsh_service/mod/aai_client.py index 39adba46..d2aeb0f0 100755 --- a/components/pm-subscription-handler/pmsh_service/mod/aai_client.py +++ b/components/pm-subscription-handler/pmsh_service/mod/aai_client.py @@ -17,22 +17,20 @@ # ============LICENSE_END===================================================== import json from os import environ - import requests from requests.auth import HTTPBasicAuth - import mod.network_function import mod.pmsh_utils from mod import logger -def get_pmsh_nfs_from_aai(app_conf): +def get_pmsh_nfs_from_aai(app_conf, nf_filter): """ Returns the Network Functions from AAI related to the Subscription. Args: app_conf (AppConfig): the AppConfig object. - + nf_filter (NetworkFunctionFilter): the filter to apply on nf from aai Returns: NetworkFunctions (list): list of NetworkFunctions. @@ -41,7 +39,7 @@ def get_pmsh_nfs_from_aai(app_conf): """ aai_nf_data = _get_all_aai_nf_data(app_conf) if aai_nf_data: - nfs = _filter_nf_data(aai_nf_data, app_conf) + nfs = _filter_nf_data(aai_nf_data, app_conf, nf_filter) else: raise RuntimeError('Failed to get data from AAI') return nfs @@ -114,14 +112,14 @@ def _get_aai_request_headers(**kwargs): 'RequestID': kwargs['request_id']} -def _filter_nf_data(nf_data, app_conf): +def _filter_nf_data(nf_data, app_conf, nf_filter): """ Returns a list of filtered NetworkFunctions using the nf_filter. Args: nf_data (dict): the nf json data from AAI. app_conf (AppConfig): the AppConfig object. - + nf_filter (NetworkFunctionFilter): filter data to apply on network functions Returns: NetworkFunction (list): a list of filtered NetworkFunction Objects. @@ -142,7 +140,7 @@ def _filter_nf_data(nf_data, app_conf): model_version_id=nf['properties'].get('model-version-id')) if not new_nf.set_nf_model_params(app_conf): continue - if app_conf.nf_filter.is_nf_in_filter(new_nf): + if nf_filter.is_nf_in_filter(new_nf): nf_list.append(new_nf) except KeyError as e: logger.error(f'Failed to parse AAI data: {e}', exc_info=True) diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py index 21d29caf..8af6c777 100755 --- a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py +++ b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py @@ -1,5 +1,5 @@ # ============LICENSE_START=================================================== -# Copyright (C) 2019-2020 Nordix Foundation. +# Copyright (C) 2019-2021 Nordix Foundation. # ============================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,11 @@ # ============LICENSE_END===================================================== from mod.subscription import Subscription +from http import HTTPStatus +from mod import logger +from mod.api.services import subscription_service +from connexion import NoContent +from mod.api.custom_exception import InvalidDataException, DuplicateDataException def status(): @@ -40,3 +45,34 @@ def get_all_sub_to_nf_relations(): """ subs_dict = [s.serialize() for s in Subscription.get_all()] return subs_dict + + +def post_subscription(body): + """ + Creates a subscription + + Args: + body (dict): subscription request body to save. + + Returns: + Success : NoContent, 201 + Invalid Data : Invalid message, 400 + Duplicate Data : Duplicate field detail, 409 + + Raises: + Error: If anything fails in the server. + """ + response = NoContent, HTTPStatus.CREATED.value + try: + subscription_service.create_subscription(body['subscription']) + except DuplicateDataException as e: + logger.error(f'Failed to create subscription for ' + f'{body["subscription"]["subscriptionName"]} due to duplicate data: {e}', + exc_info=True) + response = e.duplicate_field_info, HTTPStatus.CONFLICT.value + except InvalidDataException as e: + logger.error(f'Failed to create subscription for ' + f'{body["subscription"]["subscriptionName"]} due to invalid data: {e}', + exc_info=True) + response = e.invalid_message, HTTPStatus.BAD_REQUEST.value + return response diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/custom_exception.py b/components/pm-subscription-handler/pmsh_service/mod/api/custom_exception.py new file mode 100644 index 00000000..606d500c --- /dev/null +++ b/components/pm-subscription-handler/pmsh_service/mod/api/custom_exception.py @@ -0,0 +1,38 @@ +# ============LICENSE_START=================================================== +# Copyright (C) 2021 Nordix Foundation. +# ============================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END===================================================== + +class InvalidDataException(Exception): + """Exception raised for invalid inputs. + + Attributes: + message -- detail on invalid data + """ + + def __init__(self, invalid_message): + self.invalid_message = invalid_message + + +class DuplicateDataException(Exception): + """Exception raised for duplicate inputs. + + Attributes: + message -- detail on duplicate field + """ + + def __init__(self, duplicate_field_info): + self.duplicate_field_info = duplicate_field_info diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py b/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py index 5c87fa55..2b340e24 100755 --- a/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py +++ b/components/pm-subscription-handler/pmsh_service/mod/api/db_models.py @@ -179,9 +179,11 @@ class NetworkFunctionFilterModel(db.Model): f'model_version_ids: {self.model_version_ids}, model_names: {self.model_names}' def serialize(self): - return {'subscription_name': self.subscription_name, 'nf_names': self.nf_names, - 'model_invariant_ids': self.model_invariant_ids, - 'model_version_ids': self.model_version_ids, 'model_names': self.model_names} + return {'subscriptionName': self.subscription_name, + 'nfNames': convert_db_string_to_list(self.nf_names), + 'modelInvariantIDs': convert_db_string_to_list(self.model_invariant_ids), + 'modelVersionIDs': convert_db_string_to_list(self.model_version_ids), + 'modelNames': convert_db_string_to_list(self.model_names)} class MeasurementGroupModel(db.Model): @@ -256,3 +258,16 @@ class NfMeasureGroupRelationalModel(db.Model): def __repr__(self): return f'measurement_grp_name: {self.measurement_grp_name}, ' \ f'nf_name: {self.nf_name}, nf_measure_grp_status: {self.nf_measure_grp_status}' + + +def convert_db_string_to_list(db_string): + """ + Converts a db string to array and + removes empty strings + Args: + db_string (string): The db string to convert into an array + Returns: + list[string]: converted list of strings else empty + """ + array_format = db_string.strip('{}').split(',') + return [x for x in array_format if x.strip() != ""] diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml b/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml index 2a6137cf..11cea4ee 100644 --- a/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml +++ b/components/pm-subscription-handler/pmsh_service/mod/api/pmsh_swagger.yml @@ -21,6 +21,8 @@ info: title: PM Subscription Handler Service version: "2.0.0" description: PM subscription handler enables control of performance management jobs on network functions in ONAP +consumes: + - "application/json" produces: - "application/json" basePath: "/" @@ -89,6 +91,27 @@ paths: 503: description: the pmsh service is unavailable + /subscription: + post: + tags: + - "Subscription" + description: >- + Create a PM Subscription + operationId: mod.api.controller.post_subscription + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/subscription" + responses: + 201: + description: successfully created PM Subscription + 409: + description: Duplicate data + 400: + description: Invalid input + definitions: subscription: type: object @@ -119,29 +142,30 @@ definitions: nfFilter: type: object - minProperties: 1 + description: "At least one valid filter value within nfFilter is required" additionalProperties: false properties: nfNames: type: array - minItems: 1 items: type: string modelInvariantIDs: type: array - minItems: 1 items: type: string modelVersionIDs: type: array - minItems: 1 items: type: string modelNames: type: array - minItems: 1 items: type: string + required: + - nfNames + - modelInvariantIDs + - modelVersionIDs + - modelNames measurementGroup: type: object diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py b/components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py new file mode 100644 index 00000000..329dc857 --- /dev/null +++ b/components/pm-subscription-handler/pmsh_service/mod/api/services/measurement_group_service.py @@ -0,0 +1,87 @@ +# ============LICENSE_START=================================================== +# Copyright (C) 2021 Nordix Foundation. +# ============================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END===================================================== + +from mod.api.db_models import MeasurementGroupModel, NfMeasureGroupRelationalModel +from mod import db, logger +from mod.subscription import SubNfState +from mod.api.services import nf_service +from mod.pmsh_config import MRTopic, AppConfig + + +def save_measurement_group(measurement_group, subscription_name): + """ + Saves the measurement_group data request + + Args: + measurement_group (dict) : measurement group to save + subscription_name (string) : subscription name to associate with measurement group. + + Returns: + MeasurementGroupModel : measurement group saved in the database + """ + logger.info(f'Saving measurement group for subscription request: {subscription_name}') + new_measurement_group = MeasurementGroupModel( + subscription_name=subscription_name, + measurement_group_name=measurement_group['measurementGroupName'], + administrative_state=measurement_group['administrativeState'], + file_based_gp=measurement_group['fileBasedGP'], + file_location=measurement_group['fileLocation'], + measurement_type=measurement_group['measurementTypes'], + managed_object_dns_basic=measurement_group['managedObjectDNsBasic']) + db.session.add(new_measurement_group) + return new_measurement_group + + +def apply_nf_to_measgroup(nf_name, measurement_group_name): + """ + Associate and saves the measurement group with Network function + + Args: + nf_name (string): Network function name. + measurement_group_name (string): Measurement group name + """ + new_nf_measure_grp_rel = NfMeasureGroupRelationalModel( + measurement_grp_name=measurement_group_name, + nf_name=nf_name, + nf_measure_grp_status=SubNfState.PENDING_CREATE.value + ) + db.session.add(new_nf_measure_grp_rel) + + +def publish_measurement_group(subscription_name, measurement_group, nf): + """ + Publishes an event for measurement group against nfs to MR + + Args: + subscription_name (string): Subscription name to publish against nf + measurement_group (MeasurementGroupModel): Measurement group to publish + nf (NetworkFunction): Network function to publish. + """ + event_body = nf_service.create_nf_event_body(nf, 'CREATE') + event_body['subscription'] = { + "administrativeState": measurement_group.administrative_state, + "subscriptionName": subscription_name, + "fileBasedGP": measurement_group.file_based_gp, + "fileLocation": measurement_group.file_location, + "measurementGroup": { + "measurementGroupName": measurement_group.measurement_group_name, + "measurementTypes": measurement_group.measurement_type, + "managedObjectDNsBasic": measurement_group.managed_object_dns_basic + } + } + AppConfig.get_instance().publish_to_topic(MRTopic.POLICY_PM_PUBLISHER.value, event_body) diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/services/nf_service.py b/components/pm-subscription-handler/pmsh_service/mod/api/services/nf_service.py new file mode 100644 index 00000000..1fca766a --- /dev/null +++ b/components/pm-subscription-handler/pmsh_service/mod/api/services/nf_service.py @@ -0,0 +1,75 @@ +# ============LICENSE_START=================================================== +# Copyright (C) 2021 Nordix Foundation. +# ============================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END===================================================== + +from mod import db, aai_client, logger +from mod.api.db_models import NetworkFunctionModel +from mod.pmsh_config import AppConfig +from mod.network_function import NetworkFunctionFilter + + +def capture_filtered_nfs(sub_name): + """ + Retrieves network functions from AAI client and + returns a list of filtered NetworkFunctions using the Filter + + Args: + sub_name (string): The name of subscription inorder to perform filtering + Returns: + list[NetworkFunction]: a list of filtered NetworkFunction Objects. + """ + logger.info(f'Getting filtered nfs for subscription: {sub_name}') + nf_filter = NetworkFunctionFilter.get_network_function_filter(sub_name) + return aai_client.get_pmsh_nfs_from_aai(AppConfig.get_instance(), nf_filter) + + +def create_nf_event_body(nf, change_type): + """ + Creates a network function event body to publish on MR + + Args: + nf (NetworkFunction): the Network function to include in the event. + change_type (string): define the change type to be applied on node + Returns: + dict: network function event body to publish on MR. + """ + app_conf = AppConfig.get_instance() + return {'nfName': nf.nf_name, + 'ipAddress': nf.ipv4_address if nf.ipv6_address in (None, '') + else nf.ipv6_address, + 'blueprintName': nf.sdnc_model_name, + 'blueprintVersion': nf.sdnc_model_version, + 'policyName': app_conf.operational_policy_name, + 'changeType': change_type, + 'closedLoopControlName': app_conf.control_loop_name} + + +def save_nf(nf): + """ + Saves the network function request to the db + Args: + nf (NetworkFunction) : requested network function to save + """ + network_function = NetworkFunctionModel(nf_name=nf.nf_name, + ipv4_address=nf.ipv4_address, + ipv6_address=nf.ipv6_address, + model_invariant_id=nf.model_invariant_id, + model_version_id=nf.model_version_id, + model_name=nf.model_name, + sdnc_model_name=nf.sdnc_model_name, + sdnc_model_version=nf.sdnc_model_version) + db.session.add(network_function) diff --git a/components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py b/components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py new file mode 100644 index 00000000..1485bebe --- /dev/null +++ b/components/pm-subscription-handler/pmsh_service/mod/api/services/subscription_service.py @@ -0,0 +1,275 @@ +# ============LICENSE_START=================================================== +# Copyright (C) 2021 Nordix Foundation. +# ============================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END===================================================== + +from mod import db, logger +from mod.api.db_models import SubscriptionModel, NfSubRelationalModel, \ + NetworkFunctionFilterModel, NetworkFunctionModel +from mod.api.services import measurement_group_service, nf_service +from mod.api.custom_exception import InvalidDataException, DuplicateDataException +from mod.subscription import AdministrativeState +from sqlalchemy.exc import IntegrityError + + +def create_subscription(subscription): + """ + Creates a subscription + + Args: + subscription (dict): subscription to save. + + Raises: + DuplicateDataException: contains details on duplicate fields + Exception: contains runtime error details + """ + logger.info(f'Initiating create subscription for: {subscription["subscriptionName"]}') + perform_validation(subscription) + try: + sub_name, measurement_groups = save_subscription_request(subscription) + db.session.commit() + logger.info(f'Successfully saved subscription request for: ' + f'{subscription["subscriptionName"]}') + filtered_nfs = nf_service.capture_filtered_nfs(sub_name) + if filtered_nfs: + logger.info(f'Applying the filtered nfs for subscription: {sub_name}') + save_filtered_nfs(filtered_nfs) + apply_subscription_to_nfs(filtered_nfs, sub_name) + unlocked_msmt_groups = apply_measurement_grp_to_nfs(filtered_nfs, measurement_groups) + db.session.commit() + if unlocked_msmt_groups: + publish_measurement_grp_to_nfs(sub_name, filtered_nfs, unlocked_msmt_groups) + else: + logger.error(f'All measurement groups are locked for subscription: {sub_name}, ' + f'please verify/check measurement groups.') + else: + logger.error(f'No network functions found for subscription: {sub_name}, ' + f'please verify/check NetworkFunctionFilter.') + except IntegrityError as e: + db.session.rollback() + raise DuplicateDataException(f'DB Integrity issue encountered: {e.orig.args[0]}') + except Exception as e: + db.session.rollback() + raise e + finally: + db.session.remove() + + +def publish_measurement_grp_to_nfs(subscription_name, filtered_nfs, measurement_groups): + """ + Publishes an event for measurement groups against nfs + + Args: + subscription_name (string): subscription name against nfs + filtered_nfs (list[NetworkFunction])): list of filtered network functions + measurement_groups (list[MeasurementGroupModel]): list of unlocked measurement group + """ + for measurement_group in measurement_groups: + for nf in filtered_nfs: + try: + logger.info(f'Publishing event for nf name, measure_grp_name: {nf.nf_name},' + f'{measurement_group.measurement_group_name}') + measurement_group_service.publish_measurement_group( + subscription_name, measurement_group, nf) + except Exception as ex: + logger.error(f'Publish event failed for nf name, measure_grp_name, sub_name: ' + f'{nf.nf_name},{measurement_group.measurement_group_name}, ' + f'{subscription_name} with error: {ex}') + + +def save_filtered_nfs(filtered_nfs): + """ + Saves a network function + + Args: + filtered_nfs (list[NetworkFunction]): list of filtered network functions to save. + """ + pmsh_nf_names = list(nf.nf_name for nf in NetworkFunctionModel.query.all()) + for nf in filtered_nfs: + if nf.nf_name not in pmsh_nf_names: + nf_service.save_nf(nf) + + +def apply_subscription_to_nfs(filtered_nfs, subscription_name): + """ + Associate and saves the subscription with Network functions + + Args: + filtered_nfs (list[NetworkFunction]): list of filtered network functions to save. + subscription_name (string): subscription name to save against nfs + """ + logger.info(f'Saving filtered nfs for subscription: {subscription_name}') + for nf in filtered_nfs: + new_nf_sub_rel = NfSubRelationalModel(subscription_name=subscription_name, + nf_name=nf.nf_name) + db.session.add(new_nf_sub_rel) + + +def apply_measurement_grp_to_nfs(filtered_nfs, measurement_groups): + """ + Saves measurement groups against nfs with status as PENDING_CREATE + + Args: + filtered_nfs (list[NetworkFunction])): list of filtered network functions + measurement_groups (list[MeasurementGroupModel]): list of measurement group + + Returns: + list[MeasurementGroupModel]: list of Unlocked measurement groups + """ + unlocked_msmt_groups = [] + for measurement_group in measurement_groups: + if measurement_group.administrative_state \ + == AdministrativeState.UNLOCKED.value: + unlocked_msmt_groups.append(measurement_group) + for nf in filtered_nfs: + logger.info(f'Saving measurement group to nf name, measure_grp_name: {nf.nf_name},' + f'{measurement_group.measurement_group_name}') + measurement_group_service.apply_nf_to_measgroup( + nf.nf_name, measurement_group.measurement_group_name) + else: + logger.info(f'No nfs added as measure_grp_name: ' + f'{measurement_group.measurement_group_name} is LOCKED') + return unlocked_msmt_groups + + +def check_missing_data(subscription): + """ + checks if the subscription request has missing data + + Args: + subscription (dict): subscription to validate + + Raises: + InvalidDataException: exception containing the list of invalid data. + """ + if subscription['subscriptionName'].strip() in (None, ''): + raise InvalidDataException("No value provided in subscription name") + + for measurement_group in subscription.get('measurementGroups'): + measurement_group_details = measurement_group['measurementGroup'] + if measurement_group_details['administrativeState'].strip() in (None, ''): + raise InvalidDataException("No value provided for administrative state") + if measurement_group_details['measurementGroupName'].strip() in (None, ''): + raise InvalidDataException("No value provided for measurement group name") + + +def perform_validation(subscription): + """ + validates the subscription and if invalid raises an exception + to indicate duplicate/invalid request + + Args: + subscription (dict): subscription to validate + + Raises: + DuplicateDataException: exception containing the detail on duplicate data field. + InvalidDataException: exception containing the detail on invalid data. + """ + logger.info(f'Performing subscription validation for: {subscription["subscriptionName"]}') + check_missing_data(subscription) + logger.info(f'No missing data found for: {subscription["subscriptionName"]}') + check_duplicate_fields(subscription["subscriptionName"]) + logger.info(f'No duplicate data found for: {subscription["subscriptionName"]}') + validate_nf_filter(subscription["nfFilter"]) + logger.info(f'Filter data is valid for: {subscription["subscriptionName"]}') + + +def save_subscription_request(subscription): + """ + Saves the subscription request consisting of: + network function filter and measurement groups + + Args: + subscription (dict): subscription request to be saved. + + Returns: + string: Subscription name + list[MeasurementGroupModel]: list of measurement groups + """ + logger.info(f'Saving subscription request for: {subscription["subscriptionName"]}') + sub_name = save_subscription(subscription).subscription_name + save_nf_filter(subscription["nfFilter"], subscription["subscriptionName"]) + measurement_groups = [] + for measurement_group in subscription['measurementGroups']: + measurement_groups.append( + measurement_group_service.save_measurement_group( + measurement_group['measurementGroup'], + subscription["subscriptionName"])) + return sub_name, measurement_groups + + +def check_duplicate_fields(subscription_name): + """ + validates the subscription content if already present + and captures duplicate fields + + Args: + subscription_name (string): subscription name + + Raises: + InvalidDataException: exception containing the list of invalid data. + """ + + existing_subscription = (SubscriptionModel.query.filter( + SubscriptionModel.subscription_name == subscription_name).one_or_none()) + if existing_subscription is not None: + raise DuplicateDataException(f'subscription Name: {subscription_name} already exists.') + + +def save_subscription(subscription): + """ + Saves the subscription data + + Args: + subscription (dict): subscription model to be saved. + """ + subscription_model = SubscriptionModel(subscription_name=subscription["subscriptionName"], + status=AdministrativeState.LOCKED.value) + db.session.add(subscription_model) + return subscription_model + + +def validate_nf_filter(nf_filter): + """ + checks if the nf filter is valid + + Args: + nf_filter (dict): nf filter to validate + + Raises: + InvalidDataException: if no field is available in nf_filter + """ + for filter_name, filter_values in nf_filter.items(): + filter_values[:] = [x for x in filter_values if x.strip()] + if not [filter_name for filter_name, val in nf_filter.items() if len(val) > 0]: + raise InvalidDataException("At least one filter within nfFilter must not be empty") + + +def save_nf_filter(nf_filter, subscription_name): + """ + Saves the nf_filter data request + + Args: + nf_filter (dict) : network function filter to save + subscription_name (string) : subscription name to associate with nf filter. + """ + logger.info(f'Saving nf filter for subscription request: {subscription_name}') + new_filter = NetworkFunctionFilterModel(subscription_name=subscription_name, + nf_names=nf_filter['nfNames'], + model_invariant_ids=nf_filter['modelInvariantIDs'], + model_version_ids=nf_filter['modelVersionIDs'], + model_names=nf_filter['modelNames']) + db.session.add(new_filter) diff --git a/components/pm-subscription-handler/pmsh_service/mod/network_function.py b/components/pm-subscription-handler/pmsh_service/mod/network_function.py index f7b682d4..2404c8b8 100755 --- a/components/pm-subscription-handler/pmsh_service/mod/network_function.py +++ b/components/pm-subscription-handler/pmsh_service/mod/network_function.py @@ -19,7 +19,7 @@ import re from mod import logger, db -from mod.api.db_models import NetworkFunctionModel +from mod.api.db_models import NetworkFunctionModel, NetworkFunctionFilterModel class NetworkFunction: @@ -167,6 +167,20 @@ class NetworkFunctionFilter: self.model_names = kwargs.get('modelNames') self.regex_matcher = re.compile('|'.join(raw_regex for raw_regex in self.nf_names)) + @staticmethod + def get_network_function_filter(sub_name): + """Gets the network function filter from the Database + + Args: + sub_name (string): The name of the subscription + + Returns: + NetworkFunctionFilter: Returns network function filter for sub_name + """ + nf_filter = NetworkFunctionFilterModel.query.filter( + NetworkFunctionFilterModel.subscription_name == sub_name).one_or_none() + return NetworkFunctionFilter(**nf_filter.serialize()) + def is_nf_in_filter(self, nf): """Match the nf fields against values in Subscription.nfFilter diff --git a/components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py b/components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py index 9c282ab7..a6fe38ad 100644 --- a/components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py +++ b/components/pm-subscription-handler/pmsh_service/mod/pmsh_config.py @@ -69,6 +69,9 @@ class AppConfig(metaclass=MetaSingleton): self.streams_subscribes = app_config['config'].get('streams_subscribes') # TODO: aaf_creds variable should be removed on code cleanup self.aaf_creds = {'aaf_id': self.aaf_id, 'aaf_pass': self.aaf_pass} + # TODO: changes under discussion once resolve is confirmed will be removed + self.operational_policy_name = 'pmsh-operational-policy' + self.control_loop_name = 'pmsh-control-loop' @staticmethod def get_instance(): diff --git a/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py b/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py index 6238a298..22654b82 100644 --- a/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py +++ b/components/pm-subscription-handler/pmsh_service/mod/subscription_handler.py @@ -78,7 +78,7 @@ class SubscriptionHandler: self.app_conf.subscription.fileBasedGP, self.app_conf.subscription.fileLocation, self.app_conf.subscription.measurementGroups) - nfs_in_aai = aai_client.get_pmsh_nfs_from_aai(self.app_conf) + nfs_in_aai = aai_client.get_pmsh_nfs_from_aai(self.app_conf, self.app_conf.nf_filter) self.app_conf.subscription.create_subscription_on_nfs(nfs_in_aai, self.mr_pub, self.app_conf) self.app_conf.subscription.update_subscription_status() -- cgit 1.2.3-korg