diff options
author | 2022-02-24 17:07:01 +0000 | |
---|---|---|
committer | 2022-03-02 13:47:51 +0000 | |
commit | 5f69c24ad78121a2840b5299583791e557f8b535 (patch) | |
tree | 22e84dc45427065d7bfa35e2ee0dcc80311a0753 /components/pm-subscription-handler/pmsh_service/mod | |
parent | 37762006756658532012d9b8e4286e80acb612c4 (diff) |
[PMSH] Update Filter API
Issue-ID: DCAEGEN2-2922
Change-Id: Ibf0ef167642027429b3ba91daea60228cf5fa4c6
Signed-off-by: SagarS <sagar.shetty@est.tech>
Diffstat (limited to 'components/pm-subscription-handler/pmsh_service/mod')
7 files changed, 320 insertions, 50 deletions
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 de3aa5f3..57d3e021 100755 --- a/components/pm-subscription-handler/pmsh_service/mod/api/controller.py +++ b/components/pm-subscription-handler/pmsh_service/mod/api/controller.py @@ -267,3 +267,36 @@ def update_admin_state(subscription_name, measurement_group_name, body): f' due to Exception : {exception}', HTTPStatus.INTERNAL_SERVER_ERROR return response + + +def put_nf_filter(subscription_name, body): + """ + Performs network function filter update for the respective subscription + + Args: + subscription_name (String): Name of the subscription. + body (dict): Request body with nf filter data to update. + Returns: + string, HTTPStatus: Successfully updated network function Filter, 200 + string, HTTPStatus: Invalid request details, 400 + string, HTTPStatus: Cannot update as Locked/Filtering request is in progress, 409 + string, HTTPStatus: Exception details of server failure, 500 + """ + logger.info('Performing network function filter update for subscription ' + f'with sub name: {subscription_name} ') + response = 'Successfully updated network function Filter', HTTPStatus.OK.value + try: + subscription_service.update_filter(subscription_name, body) + except InvalidDataException as exception: + logger.error(exception.args[0]) + response = exception.args[0], HTTPStatus.BAD_REQUEST.value + except DataConflictException as exception: + logger.error(exception.args[0]) + response = exception.args[0], HTTPStatus.CONFLICT.value + except Exception as exception: + logger.error('Update nf filter request was not processed for sub name: ' + f'{subscription_name} due to Exception : {exception}') + response = 'Update nf filter request was not processed for sub name: ' \ + f'{subscription_name} due to Exception : {exception}', \ + HTTPStatus.INTERNAL_SERVER_ERROR + return response 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 274e0ebb..1f24f171 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 @@ -131,6 +131,33 @@ paths: 500: description: Exception occurred on the server + /subscription/{subscription_name}/nfFilter: + put: + tags: + - "Subscription" + description: >- + Update a Subscription nf filter + operationId: mod.api.controller.put_nf_filter + parameters: + - name: subscription_name + in: path + required: true + description: The name of the subscription to update nf filters + type: string + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/nfFilter" + responses: + 201: + description: Successfully updated nf filter + 409: + description: Conflicting data + 400: + description: Invalid input + 500: + description: Exception occurred while querying database /subscription/{subscription_name}/measurementGroups/{measurement_group_name}: get: @@ -162,9 +189,9 @@ paths: delete: description: Delete a measurement group - operationId: mod.api.controller.delete_meas_group + operationId: mod.api.controller.delete_meas_group_by_name tags: - - "measurement group" + - "Measurement Group" parameters: - name : subscription_name in: path 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 index b272e5b8..07d1b642 100644 --- 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 @@ -24,6 +24,7 @@ from mod.api.services import nf_service, subscription_service from mod.network_function import NetworkFunction from mod.pmsh_config import MRTopic, AppConfig from mod.subscription import AdministrativeState, SubNfState +from sqlalchemy import or_ def save_measurement_group(measurement_group, subscription_name): @@ -318,3 +319,35 @@ def update_admin_status(measurement_group, status): deactivate_nfs(sub_model, measurement_group, nf_meas_relations) elif status == AdministrativeState.UNLOCKED.value: activate_nfs(sub_model, measurement_group) + + +def filter_nf_to_meas_grp(nf_name, measurement_group_name, status): + """ Performs successful status update for a nf under filter update + request for a particular subscription and measurement group + + Args: + nf_name (string): The network function name + measurement_group_name (string): Measurement group name + status (string): status of the network function for measurement group + """ + try: + if status == SubNfState.DELETED.value: + delete_nf_to_measurement_group(nf_name, measurement_group_name, + SubNfState.DELETED.value) + elif status == SubNfState.CREATED.value: + update_measurement_group_nf_status(measurement_group_name, + SubNfState.CREATED.value, nf_name) + nf_measurement_group_rels = NfMeasureGroupRelationalModel.query.filter( + NfMeasureGroupRelationalModel.measurement_grp_name == measurement_group_name, + or_(NfMeasureGroupRelationalModel.nf_measure_grp_status.like('PENDING_%'), + NfMeasureGroupRelationalModel.nf_measure_grp_status.like('%_FAILED')) + ).all() + if not nf_measurement_group_rels: + MeasurementGroupModel.query.filter( + MeasurementGroupModel.measurement_group_name == measurement_group_name). \ + update({MeasurementGroupModel.administrative_state: AdministrativeState. + UNLOCKED.value}, synchronize_session='evaluate') + db.session.commit() + except Exception as e: + logger.error('Failed update filter response for measurement group name: ' + f'{measurement_group_name}, nf name: {nf_name} due to: {e}') 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 index ce463ed0..a3c2a036 100644 --- 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 @@ -17,7 +17,7 @@ # ============LICENSE_END===================================================== from mod import db, aai_client, logger -from mod.api.db_models import NetworkFunctionModel +from mod.api.db_models import NetworkFunctionModel, NetworkFunctionFilterModel from mod.pmsh_config import AppConfig from mod.network_function import NetworkFunctionFilter @@ -74,3 +74,22 @@ def save_nf(nf): sdnc_model_name=nf.sdnc_model_name, sdnc_model_version=nf.sdnc_model_version) db.session.add(network_function) + + +def save_nf_filter_update(sub_name, nf_filter): + """ + Updates the network function filter for the subscription in the db + + Args: + sub_name (String): Name of the Subscription + nf_filter (dict): filter object to update in the subscription + """ + NetworkFunctionFilterModel.query.filter( + NetworkFunctionFilterModel.subscription_name == sub_name). \ + update({NetworkFunctionFilterModel.nf_names: nf_filter['nfNames'], + NetworkFunctionFilterModel.model_invariant_ids: nf_filter['modelInvariantIDs'], + NetworkFunctionFilterModel.model_version_ids: nf_filter['modelVersionIDs'], + NetworkFunctionFilterModel.model_names: nf_filter['modelNames']}, + synchronize_session='evaluate') + db.session.commit() + logger.info(f'Successfully saved filter for subscription: {sub_name}') 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 index 338ab89e..032fc4a0 100644 --- 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 @@ -18,9 +18,11 @@ from mod import db, logger from mod.api.db_models import SubscriptionModel, NfSubRelationalModel, \ - NetworkFunctionFilterModel, NetworkFunctionModel, MeasurementGroupModel + NetworkFunctionFilterModel, NetworkFunctionModel, MeasurementGroupModel, \ + NfMeasureGroupRelationalModel from mod.api.services import measurement_group_service, nf_service -from mod.api.custom_exception import InvalidDataException, DuplicateDataException +from mod.api.custom_exception import InvalidDataException, DuplicateDataException, \ + DataConflictException from mod.subscription import AdministrativeState, SubNfState from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import joinedload @@ -40,32 +42,16 @@ def create_subscription(subscription): logger.info(f'Initiating create subscription for: {subscription["subscriptionName"]}') perform_validation(subscription) try: - sub_model, measurement_groups = save_subscription_request(subscription) + sub_model = 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_model.subscription_name) - if filtered_nfs: - logger.info(f'Applying the filtered nfs for subscription: ' - f'{sub_model.subscription_name}') - save_filtered_nfs(filtered_nfs) - apply_subscription_to_nfs(filtered_nfs, sub_model.subscription_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_model, filtered_nfs, - unlocked_msmt_groups) - else: - logger.error(f'All measurement groups are locked for subscription: ' - f'{sub_model.subscription_name}, ' - f'please verify/check measurement groups.') - else: - logger.error(f'No network functions found for subscription: ' - f'{sub_model.subscription_name}, ' - f'please verify/check NetworkFunctionFilter.') + unlocked_mgs = get_unlocked_measurement_grps(sub_model) + add_new_filtered_nfs(filtered_nfs, unlocked_mgs, sub_model) except IntegrityError as e: db.session.rollback() - raise DuplicateDataException(f'DB Integrity issue encountered: {e.orig.args[0]}') + raise DuplicateDataException(f'DB Integrity issue encountered: {e.orig.args[0]}') from e except Exception as e: db.session.rollback() raise e @@ -73,6 +59,36 @@ def create_subscription(subscription): db.session.remove() +def add_new_filtered_nfs(filtered_nfs, unlocked_mgs, sub_model): + """ + Inserts the filtered nfs in measurement groups of subscription + + Args: + filtered_nfs (List[NetworkFunction]): nfs to be inserted + unlocked_mgs (List[MeasurementGroupModel]): mgs to be updated with new nfs + sub_model (SubscriptionModel): subscription model to update + """ + if filtered_nfs: + logger.info(f'Applying the filtered nfs for subscription: ' + f'{sub_model.subscription_name}') + save_filtered_nfs(filtered_nfs) + apply_subscription_to_nfs(filtered_nfs, sub_model.subscription_name) + db.session.commit() + if unlocked_mgs: + apply_measurement_grp_to_nfs(filtered_nfs, unlocked_mgs) + db.session.commit() + publish_measurement_grp_to_nfs(sub_model, filtered_nfs, + unlocked_mgs) + else: + logger.error(f'All measurement groups are locked for subscription: ' + f'{sub_model.subscription_name}, ' + f'please verify/check measurement groups.') + else: + logger.error(f'No network functions found for subscription: ' + f'{sub_model.subscription_name}, ' + f'please verify/check NetworkFunctionFilter.') + + def publish_measurement_grp_to_nfs(sub_model, filtered_nfs, measurement_groups): """ @@ -124,32 +140,22 @@ def apply_subscription_to_nfs(filtered_nfs, subscription_name): db.session.add(new_nf_sub_rel) -def apply_measurement_grp_to_nfs(filtered_nfs, measurement_groups): +def apply_measurement_grp_to_nfs(filtered_nfs, unlocked_mgs): """ 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 + unlocked_mgs (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_status_to_measurement_group( - nf.nf_name, measurement_group.measurement_group_name, - SubNfState.PENDING_CREATE.value) - else: - logger.info(f'No nfs added as measure_grp_name: ' - f'{measurement_group.measurement_group_name} is LOCKED') - return unlocked_msmt_groups + for measurement_group in unlocked_mgs: + 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_status_to_measurement_group( + nf.nf_name, measurement_group.measurement_group_name, + SubNfState.PENDING_CREATE.value) def check_missing_data(subscription): @@ -217,7 +223,7 @@ def save_subscription_request(subscription): measurement_group_service.save_measurement_group( measurement_group['measurementGroup'], subscription["subscriptionName"])) - return sub_model, measurement_groups + return sub_model def check_duplicate_fields(subscription_name): @@ -380,3 +386,139 @@ def query_to_delete_subscription_by_name(subscription_name): .filter_by(subscription_name=subscription_name).delete() db.session.commit() return effected_rows + + +def is_duplicate_filter(nf_filter, db_network_filter): + """ + Checks if the network function filter is unchanged for the subscription + + Args: + nf_filter (dict): filter object to update in the subscription + db_network_filter (NetworkFunctionFilterModel): nf filter object from db + + Returns: + (boolean) : True is nf filters are same else False + """ + return nf_filter == db_network_filter.serialize() + + +def update_filter(sub_name, nf_filter): + """ + Updates the network function filter for the subscription + + Args: + sub_name (String): Name of the Subscription + nf_filter (dict): filter object to update in the subscription + + Returns: + InvalidDataException: contains details on invalid fields + DataConflictException: contains details on conflicting state of a field + Exception: contains runtime error details + """ + sub_model = query_subscription_by_name(sub_name) + if sub_model is None: + raise InvalidDataException('Requested subscription is not available ' + f'with sub name: {sub_name} for nf filter update') + if is_duplicate_filter(nf_filter, sub_model.network_filter): + raise InvalidDataException('Duplicate nf filter update requested for subscription ' + f'with sub name: {sub_name}') + validate_sub_mgs_state(sub_model) + nf_service.save_nf_filter_update(sub_name, nf_filter) + del_nfs, new_nfs = extract_del_new_nfs(sub_model) + NfSubRelationalModel.query.filter( + NfSubRelationalModel.subscription_name == sub_name, + NfSubRelationalModel.nf_name.in_(del_nfs)).delete() + db.session.commit() + unlocked_mgs = get_unlocked_measurement_grps(sub_model) + if unlocked_mgs: + add_new_filtered_nfs(new_nfs, unlocked_mgs, sub_model) + delete_filtered_nfs(del_nfs, sub_model, unlocked_mgs) + db.session.remove() + + +def get_unlocked_measurement_grps(sub_model): + """ + Gets unlocked measurement groups and logs locked measurement groups + + Args: + sub_model (SubscriptionModel): Subscription model to perform nfs delete + + Returns: + unlocked_mgs (List[MeasurementGroupModel]): unlocked msgs in a subscription + + """ + unlocked_mgs = [] + for measurement_group in sub_model.measurement_groups: + if measurement_group.administrative_state \ + == AdministrativeState.UNLOCKED.value: + unlocked_mgs.append(measurement_group) + else: + logger.info(f'No nfs added as measure_grp_name: ' + f'{measurement_group.measurement_group_name} is LOCKED') + return unlocked_mgs + + +def delete_filtered_nfs(del_nfs, sub_model, unlocked_mgs): + """ + Removes unfiltered nfs + + Args: + del_nfs (List[String]): Names of nfs to be deleted + sub_model (SubscriptionModel): Subscription model to perform nfs delete + unlocked_mgs (List[MeasurementGroupModel]): unlocked msgs to perform nfs delete + + """ + if del_nfs: + logger.info(f'Removing nfs from subscription: ' + f'{sub_model.subscription_name}') + for mg in unlocked_mgs: + MeasurementGroupModel.query.filter( + MeasurementGroupModel.measurement_group_name == mg.measurement_group_name) \ + .update({MeasurementGroupModel.administrative_state: AdministrativeState. + FILTERING.value}, synchronize_session='evaluate') + db.session.commit() + nf_meas_relations = NfMeasureGroupRelationalModel.query.filter( + NfMeasureGroupRelationalModel.measurement_grp_name == mg. + measurement_group_name, NfMeasureGroupRelationalModel. + nf_name.in_(del_nfs)).all() + measurement_group_service.deactivate_nfs(sub_model, mg, nf_meas_relations) + + +def extract_del_new_nfs(sub_model): + """ + Captures nfs to be deleted and created for the subscription + + Args: + sub_model (SubscriptionModel): Subscription model to perform nfs delete + + Returns: + del_nfs (List[String]): Names of nfs to be deleted + new_nfs (List[NetworkFunction]): nfs to be inserted + """ + filtered_nfs = nf_service.capture_filtered_nfs(sub_model.subscription_name) + filtered_nf_names = [nf.nf_name for nf in filtered_nfs] + existing_nf_names = [nf.nf_name for nf in sub_model.nfs] + new_nfs = list(filter(lambda x: x.nf_name not in existing_nf_names, filtered_nfs)) + del_nfs = [nf.nf_name for nf in sub_model.nfs if nf.nf_name not in filtered_nf_names] + return del_nfs, new_nfs + + +def validate_sub_mgs_state(sub_model): + """ + Validates if any measurement group in subscription has + status Locking or Filtering + + Args: + sub_model (SubscriptionModel): Subscription model to perform validation before nf filter + + Returns: + DataConflictException: contains details on conflicting status in measurement group + """ + mg_names_processing = [mg for mg in sub_model.measurement_groups + if mg.administrative_state in [AdministrativeState.FILTERING.value, + AdministrativeState.LOCKING.value]] + if mg_names_processing: + raise DataConflictException('Cannot update filter as subscription: ' + f'{sub_model.subscription_name} is under ' + 'transitioning state for the following measurement ' + f'groups: {mg_names_processing}') diff --git a/components/pm-subscription-handler/pmsh_service/mod/policy_response_handler.py b/components/pm-subscription-handler/pmsh_service/mod/policy_response_handler.py index a0a7bd67..5065ce8a 100644 --- a/components/pm-subscription-handler/pmsh_service/mod/policy_response_handler.py +++ b/components/pm-subscription-handler/pmsh_service/mod/policy_response_handler.py @@ -18,8 +18,8 @@ import json from mod.pmsh_config import MRTopic, AppConfig from mod import logger -from mod.subscription import AdministrativeState, subscription_nf_states -from mod.api.db_models import MeasurementGroupModel +from mod.subscription import AdministrativeState, subscription_nf_states, SubNfState +from mod.api.db_models import MeasurementGroupModel, NfMeasureGroupRelationalModel from mod.api.services import measurement_group_service policy_response_handle_functions = { @@ -34,6 +34,10 @@ policy_response_handle_functions = { AdministrativeState.LOCKING.value: { 'success': measurement_group_service.lock_nf_to_meas_grp, 'failed': measurement_group_service.update_measurement_group_nf_status + }, + AdministrativeState.FILTERING.value: { + 'success': measurement_group_service.filter_nf_to_meas_grp, + 'failed': measurement_group_service.update_measurement_group_nf_status } } @@ -86,8 +90,19 @@ class PolicyResponseHandler: logger.info(f'Response from MR: measurement group name: {measurement_group_name} for ' f'NF: {nf_name} received, updating the DB') try: - nf_measure_grp_status = subscription_nf_states[administrative_state][response_message]\ - .value + + if administrative_state == AdministrativeState.FILTERING.value: + nf_msg_rel = NfMeasureGroupRelationalModel.query.filter( + NfMeasureGroupRelationalModel.measurement_grp_name == measurement_group_name, + NfMeasureGroupRelationalModel.nf_name == nf_name + ).one_or_none() + if nf_msg_rel.nf_measure_grp_status == SubNfState.PENDING_DELETE.value: + administrative_state = AdministrativeState.LOCKING.value + elif nf_msg_rel.nf_measure_grp_status == SubNfState.PENDING_CREATE.value: + administrative_state = AdministrativeState.UNLOCKED.value + + nf_measure_grp_status = (subscription_nf_states[administrative_state] + [response_message]).value policy_response_handle_functions[administrative_state][response_message]( measurement_group_name=measurement_group_name, status=nf_measure_grp_status, nf_name=nf_name) diff --git a/components/pm-subscription-handler/pmsh_service/mod/subscription.py b/components/pm-subscription-handler/pmsh_service/mod/subscription.py index 603343f0..ddb6e1f5 100755 --- a/components/pm-subscription-handler/pmsh_service/mod/subscription.py +++ b/components/pm-subscription-handler/pmsh_service/mod/subscription.py @@ -1,5 +1,5 @@ # ============LICENSE_START=================================================== -# Copyright (C) 2019-2021 Nordix Foundation. +# Copyright (C) 2019-2022 Nordix Foundation. # ============================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ class AdministrativeState(Enum): UNLOCKED = 'UNLOCKED' LOCKING = 'LOCKING' LOCKED = 'LOCKED' + FILTERING = 'FILTERING' subscription_nf_states = { |