From 139176363ab87c7ae55ef1f224182c66a4d8fc2c Mon Sep 17 00:00:00 2001 From: Sastry Isukapalli Date: Thu, 22 Mar 2018 00:58:14 -0400 Subject: Fix "brain-overload" functions flagged by SONAR Refactored two brain-overload functions Moved a policy util function from conductor/api_builder.py to policy/utils.py Issue-ID: OPTFRA-195 Change-Id: I879e7f4f5d92cc530c66d3ed071bf5246397ba13 Signed-off-by: Sastry Isukapalli --- osdf/adapters/policy/interface.py | 59 +++++++++++++--------- osdf/adapters/policy/utils.py | 46 ++++++++++++----- .../licenseopt/simple_license_allocation.py | 6 +-- .../placementopt/conductor/api_builder.py | 29 +++-------- 4 files changed, 76 insertions(+), 64 deletions(-) diff --git a/osdf/adapters/policy/interface.py b/osdf/adapters/policy/interface.py index 288571f..02b8ff3 100644 --- a/osdf/adapters/policy/interface.py +++ b/osdf/adapters/policy/interface.py @@ -1,4 +1,4 @@ -# ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # Copyright (c) 2015-2017 AT&T Intellectual Property # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,12 +24,10 @@ import json from requests import RequestException from osdf.operation.exceptions import BusinessException from osdf.adapters.local_data.local_policies import get_local_policies -from osdf.adapters.policy.utils import _regex_policy_name +from osdf.adapters.policy.utils import policy_name_as_regex, retrieve_node from osdf.config.base import osdf_config -from osdf.logging.osdf_logging import audit_log, MH, metrics_log, error_log, debug_log +from osdf.logging.osdf_logging import audit_log, MH, metrics_log, debug_log from osdf.utils.interfaces import RestClient -from osdf.optimizers.placementopt.conductor.api_builder import retrieve_node -# from osdf.utils import data_mapping def get_by_name(rest_client, policy_name_list, wildcards=True): @@ -38,7 +36,7 @@ def get_by_name(rest_client, policy_name_list, wildcards=True): try: query_name = policy_name if wildcards: - query_name = _regex_policy_name(query_name) + query_name = policy_name_as_regex(query_name) policy_list.append(rest_client.request(json={"policyName": query_name})) except RequestException as err: audit_log.warn("Error in fetching policy: " + policy_name) @@ -68,46 +66,57 @@ def get_subscriber_role(rest_client, req, pmain, service_name, scope): """ subscriber_role = "DEFAULT" prov_status = [] - subs_name = get_subscriber_name(req, pmain) + subs_name = get_subscriber_name(req, pmain) # what if there is no subs_name if subs_name == "DEFAULT": return subscriber_role, prov_status - + policy_subs = pmain['policy_subscriber'] policy_scope = {"policyName": "{}.*".format(scope), "configAttributes": { "serviceType": "{}".format(service_name), "service": "{}".format(policy_subs)} } - policy_list = [] try: - policy_list.append(rest_client.request(json=policy_scope)) + policy_list = rest_client.request(json=policy_scope) except RequestException as err: - audit_log.warn("Error in fetching policy for {}: ".format(policy_subs)) + audit_log.warn("Error in fetching policy for {}, {}: ".format(policy_subs, err)) return subscriber_role, prov_status - - formatted_policies = [] - for x in itertools.chain(*policy_list): - if x['config'] is None: + + policies = list(itertools.chain(*policy_list)) + for x in policies: + if not x['config']: # some policy has no 'config' field, so it will be empty raise BusinessException("Config not found for policy with name %s" % x['policyName']) - else: - formatted_policies.append(json.loads(x['config'])) - - for policy in formatted_policies: + + formatted_policies = [json.loads(x['config']) for x in policies] + role, prov = _get_subscriber_role_from_policies(formatted_policies, subs_name, subscriber_role, prov_status) + return role, prov + + +def _get_subscriber_role_from_policies(policies, subs_name, default_role, default_prov): + """ + Get the first subscriber role found in policies + :param policies: JSON-loaded policies + :param subs_name: subscriber name + :param default_val: default role (e.g. "DEFAULT") + :param default_prov: default prov_status (e.g. []) + :return: role and prov_status + """ + for policy in policies: property_list = policy['content']['property'] for prop in property_list: if subs_name in prop['subscriberName']: subs_role_list = prop['subscriberRole'] prov_status = prop['provStatus'] - if isinstance(subs_role_list, list): # as list is returned - return subs_role_list[0], prov_status - return subscriber_role, prov_status - + if isinstance(subs_role_list, list): + return subs_role_list[0], prov_status # TODO: check what to do otherwise + return default_role, default_prov + def get_by_scope(rest_client, req, config_local, type_service): policy_list = [] pmain = config_local['policy_info'][type_service] pscope = pmain['policy_scope'] - + model_name = retrieve_node(req, pscope['service_name']) service_name = model_name @@ -198,5 +207,5 @@ def get_policies(request_json, service_type): policies = get_local_policies(local_info[0], local_info[1], to_filter) else: policies, prov_status = remote_api(request_json, osdf_config, service_type) - + return policies, prov_status diff --git a/osdf/adapters/policy/utils.py b/osdf/adapters/policy/utils.py index 27885a5..9acfa2a 100644 --- a/osdf/adapters/policy/utils.py +++ b/osdf/adapters/policy/utils.py @@ -15,9 +15,13 @@ # # ------------------------------------------------------------------------- # +import copy +import json from collections import defaultdict +from osdf.utils.programming_utils import dot_notation, list_flatten + def group_policies(flat_policies): """Filter policies using the following steps: @@ -27,29 +31,30 @@ def group_policies(flat_policies): :param flat_policies: list of flat policies :return: Filtered policies """ - aggregated_policies = {} - filter_policies = defaultdict(list) + filtered_policies = defaultdict(list) policy_name = [] - for policy in flat_policies: + policies = [x for x in flat_policies if x['content'].get('policy_type')] # drop ones without 'policy_type' + policy_types = set([x['content'].get('policyType') for x in policies]) + aggregated_policies = dict((x, defaultdict(list)) for x in policy_types) + + for policy in policies: policy_type = policy['content'].get('policyType') - if not policy_type: - continue - if policy_type not in aggregated_policies: - aggregated_policies[policy_type] = defaultdict(list) for resource in policy['content'].get('resourceInstanceType', []): aggregated_policies[policy_type][resource].append(policy) + for policy_type in aggregated_policies: for resource in aggregated_policies[policy_type]: - if len(aggregated_policies[policy_type][resource]) > 0: + if aggregated_policies[policy_type][resource]: aggregated_policies[policy_type][resource].sort(key=lambda x: x['priority'], reverse=True) - policy = aggregated_policies[policy_type][resource][0] - if policy['policyName'] not in policy_name: - filter_policies[policy['content']['policyType']].append(policy) - policy_name.append(policy['policyName']) - return filter_policies + prioritized_policy = aggregated_policies[policy_type][resource][0] + if prioritized_policy['policyName'] not in policy_name: + # TODO: Check logic here... should policy appear only once across all groups? + filtered_policies[prioritized_policy['content']['policyType']].append(prioritized_policy) + policy_name.append(prioritized_policy['policyName']) + return filtered_policies -def _regex_policy_name(policy_name): +def policy_name_as_regex(policy_name): """Get the correct policy name as a regex (e.g. OOF_HAS_vCPE.cloudAttributePolicy ends up in policy as OOF_HAS_vCPE.Config_MS_cloudAttributePolicy.1.xml So, for now, we query it as OOF_HAS_vCPE..*aicAttributePolicy.*) @@ -58,3 +63,16 @@ def _regex_policy_name(policy_name): """ p = policy_name.partition('.') return p[0] + p[1] + ".*" + p[2] + ".*" + + +def retrieve_node(req_json, reference): + """ + Get the child node(s) from the dot-notation [reference] and parent [req_json]. + For placement and other requests, there are encoded JSONs inside the request or policy, + so we need to expand it and then do a search over the parent plus expanded JSON. + """ + req_json_copy = copy.deepcopy(req_json) # since we expand the JSON in place, we work on a copy + if 'orderInfo' in req_json_copy['placementInfo']: + req_json_copy['placementInfo']['orderInfo'] = json.loads(req_json_copy['placementInfo']['orderInfo']) + info = dot_notation(req_json_copy, reference) + return list_flatten(info) if isinstance(info, list) else info \ No newline at end of file diff --git a/osdf/optimizers/licenseopt/simple_license_allocation.py b/osdf/optimizers/licenseopt/simple_license_allocation.py index ad9acfc..74d220f 100644 --- a/osdf/optimizers/licenseopt/simple_license_allocation.py +++ b/osdf/optimizers/licenseopt/simple_license_allocation.py @@ -31,10 +31,10 @@ def license_optim(request_json): license_info = [] - for licenseDemand in request_json.get('placementInfo', {}).get('demandInfo', {}).get('licenseDemands', []): + for demand in request_json.get('placementInfo', {}).get('demandInfo', {}).get('licenseDemands', []): license_info.append( - {'serviceResourceId': licenseDemand['serviceResourceId'], - 'resourceModuleName': licenseDemand['resourceModuleName'], + {'serviceResourceId': demand['serviceResourceId'], + 'resourceModuleName': demand['resourceModuleName'], 'entitlementPoolList': "NOT SUPPORTED", 'licenseKeyGroupList': "NOT SUPPORTED" }) diff --git a/osdf/optimizers/placementopt/conductor/api_builder.py b/osdf/optimizers/placementopt/conductor/api_builder.py index d8a2083..bfc7f19 100644 --- a/osdf/optimizers/placementopt/conductor/api_builder.py +++ b/osdf/optimizers/placementopt/conductor/api_builder.py @@ -16,17 +16,18 @@ # ------------------------------------------------------------------------- # -import copy import json + from jinja2 import Template -from osdf.utils.programming_utils import list_flatten, dot_notation + import osdf.optimizers.placementopt.conductor.translation as tr from osdf.adapters.policy.utils import group_policies +from osdf.utils.programming_utils import list_flatten def conductor_api_builder(request_json, flat_policies: list, local_config, prov_status, template="templates/conductor_interface.json"): - """Build a SNIRO southbound API call for Conductor/Placement optimization + """Build an OSDF southbound API call for HAS-Conductor/Placement optimization :param request_json: parameter data received from a client :param flat_policies: policy data received from the policy platform (flat policies) :param template: template to generate southbound API call to conductor @@ -61,7 +62,6 @@ def conductor_api_builder(request_json, flat_policies: list, local_config, prov_ req_info = request_json['requestInfo'] model_name = request_json['serviceInfo']['serviceName'] service_type = model_name - # service_type = data_mapping.get_service_type(model_name) service_info = local_config.get('service_info', {}).get(service_type, {}) order_info = {} if 'orderInfo' in request_json["placementInfo"]: @@ -72,7 +72,6 @@ def conductor_api_builder(request_json, flat_policies: list, local_config, prov_ subs_com_site_id = request_json['placementInfo']['subscriberInfo'].get('subscriberCommonSiteId', "") rendered_req = None if service_type == 'vCPE': - # data_mapping.normalize_user_params(order_info) rendered_req = templ.render( requestType=request_type, chosenComplex=subs_com_site_id, @@ -84,22 +83,8 @@ def conductor_api_builder(request_json, flat_policies: list, local_config, prov_ limit=req_info['numSolutions'], serviceType=service_type, serviceInstance=request_json['serviceInfo']['serviceInstanceId'], - provStatus = prov_status, - chosenRegion=order_info.get('requestParameters',{}).get('lcpCloudRegionId'), + provStatus=prov_status, + chosenRegion=order_info.get('requestParameters', {}).get('lcpCloudRegionId'), json=json) - json_payload = json.dumps(json.loads(rendered_req)) # need this because template's JSON is ugly! + json_payload = json.dumps(json.loads(rendered_req)) # need this because template's JSON is ugly! return json_payload - - -def retrieve_node(req_json, reference): - """ - Get the child node(s) from the dot-notation [reference] and parent [req_json]. - For placement and other requests, there are encoded JSONs inside the request or policy, - so we need to expand it and then do a search over the parent plus expanded JSON. - """ - req_json_copy = copy.deepcopy(req_json) # since we expand the JSON in place, we work on a copy - if 'orderInfo' in req_json_copy['placementInfo']: - req_json_copy['placementInfo']['orderInfo'] = json.loads(req_json_copy['placementInfo']['orderInfo']) - info = dot_notation(req_json_copy, reference) - return list_flatten(info) if isinstance(info, list) else info - -- cgit 1.2.3-korg