From fcb37e97e37137d3111924e993e75fdb83c2a0a0 Mon Sep 17 00:00:00 2001 From: krishnaa96 Date: Mon, 23 Mar 2020 13:11:07 +0530 Subject: Add functionality to support NSI selection Issue-ID: OPTFRA-677 Signed-off-by: krishnaa96 Change-Id: Ibc51e15fce4692a445df400053060d3a6977b4ce --- osdf/adapters/conductor/api_builder.py | 21 +++++--- osdf/adapters/conductor/conductor.py | 30 +++++++---- .../conductor/templates/conductor_interface.json | 2 + osdf/adapters/conductor/translation.py | 58 +++++++++++----------- 4 files changed, 66 insertions(+), 45 deletions(-) (limited to 'osdf') diff --git a/osdf/adapters/conductor/api_builder.py b/osdf/adapters/conductor/api_builder.py index 17057d8..c99c5eb 100644 --- a/osdf/adapters/conductor/api_builder.py +++ b/osdf/adapters/conductor/api_builder.py @@ -33,7 +33,8 @@ def _build_parameters(group_policies, service_info, request_parameters): :param request_parameters: request parameters :return: """ - initial_params = tr.get_opt_query_data(request_parameters, group_policies['onap.policies.optimization.QueryPolicy']) + initial_params = tr.get_opt_query_data(request_parameters, + group_policies['onap.policies.optimization.QueryPolicy']) params = dict() params.update({"REQUIRED_MEM": initial_params.pop("requiredMemory", "")}) params.update({"REQUIRED_DISK": initial_params.pop("requiredDisk", "")}) @@ -49,16 +50,19 @@ def _build_parameters(group_policies, service_info, request_parameters): return params -def conductor_api_builder(req_info, demands, request_parameters, service_info, flat_policies: list, local_config, +def conductor_api_builder(req_info, demands, request_parameters, service_info, + location_enabled, flat_policies: list, local_config, template="osdf/adapters/conductor/templates/conductor_interface.json"): """Build an OSDF southbound API call for HAS-Conductor/Placement optimization :param req_info: parameter data received from a client :param demands: list of demands :param request_parameters: request parameters :param service_info: service info object + :param location_enabled: boolean to check location to be sent in the request :param flat_policies: policy data received from the policy platform (flat policies) :param template: template to generate southbound API call to conductor - :param local_config: local configuration file with pointers for the service specific information + :param local_config: local configuration file with pointers for + the service specific information :return: json to be sent to Conductor/placement optimization """ @@ -88,10 +92,14 @@ def conductor_api_builder(req_info, demands, request_parameters, service_info, f demand_name_list, gp['onap.policies.optimization.Vim_fit']) hpa_policy_list = tr.gen_hpa_policy( demand_name_list, gp['onap.policies.optimization.HpaPolicy']) + threshold_policy_list = tr.gen_threshold_policy(demand_name_list, + gp['onap.policies.optimization.' + 'ThresholdPolicy']) req_params_dict = _build_parameters(gp, service_info, request_parameters) - conductor_policies = [attribute_policy_list, distance_to_location_policy_list, inventory_policy_list, - resource_instance_policy_list, resource_region_policy_list, zone_policy_list, - reservation_policy_list, capacity_policy_list, hpa_policy_list] + conductor_policies = [attribute_policy_list, distance_to_location_policy_list, + inventory_policy_list, resource_instance_policy_list, + resource_region_policy_list, zone_policy_list, reservation_policy_list, + capacity_policy_list, hpa_policy_list, threshold_policy_list] filtered_policies = [x for x in conductor_policies if len(x) > 0] policy_groups = list_flatten(filtered_policies) request_type = req_info.get('requestType', None) @@ -104,6 +112,7 @@ def conductor_api_builder(req_info, demands, request_parameters, service_info, f timeout=req_info['timeout'], limit=req_info['numSolutions'], request_params=req_params_dict, + location_enabled=location_enabled, json=json) json_payload = json.dumps(json.loads(rendered_req)) # need this because template's JSON is ugly! return json_payload diff --git a/osdf/adapters/conductor/conductor.py b/osdf/adapters/conductor/conductor.py index 00069a4..155d4d5 100644 --- a/osdf/adapters/conductor/conductor.py +++ b/osdf/adapters/conductor/conductor.py @@ -28,7 +28,8 @@ from osdf.utils.interfaces import RestClient from osdf.operation.exceptions import BusinessException -def request(req_info, demands, request_parameters, service_info, osdf_config, flat_policies): +def request(req_info, demands, request_parameters, service_info, location_enabled, + osdf_config, flat_policies): config = osdf_config.deployment local_config = osdf_config.core uid, passwd = config['conductorUsername'], config['conductorPassword'] @@ -43,13 +44,16 @@ def request(req_info, demands, request_parameters, service_info, osdf_config, fl if cond_minor_version is not None: x_minor_version = str(cond_minor_version) headers.update({'X-MinorVersion': x_minor_version}) - debug_log.debug("Versions set in HTTP header to conductor: X-MinorVersion: {} ".format(x_minor_version)) + debug_log.debug("Versions set in HTTP header to " + "conductor: X-MinorVersion: {} ".format(x_minor_version)) max_retries = config.get('conductorMaxRetries', 30) ping_wait_time = config.get('conductorPingWaitTime', 60) - rc = RestClient(userid=uid, passwd=passwd, method="GET", log_func=debug_log.debug, headers=headers) - conductor_req_json_str = conductor_api_builder(req_info, demands, request_parameters, service_info, flat_policies, + rc = RestClient(userid=uid, passwd=passwd, method="GET", log_func=debug_log.debug, + headers=headers) + conductor_req_json_str = conductor_api_builder(req_info, demands, request_parameters, + service_info, location_enabled, flat_policies, local_config) conductor_req_json = json.loads(conductor_req_json_str) @@ -77,14 +81,16 @@ def request(req_info, demands, request_parameters, service_info, osdf_config, fl "this transaction is timing out".format(max_timeout)) time.sleep(ping_wait_time) ctr += 1 - debug_log.debug("Attempt number {} url {}; prior status={}".format(ctr, new_url, resp['plans'][0]['status'])) + debug_log.debug("Attempt number {} url {}; prior status={}" + .format(ctr, new_url, resp['plans'][0]['status'])) total_time += ping_wait_time try: raw_resp = rc.request(new_url, raw_response=True) resp = raw_resp.json() except RequestException as e: - debug_log.debug("Conductor attempt {} for request_id {} has failed because {}".format(ctr, req_id, str(e))) + debug_log.debug("Conductor attempt {} for request_id {} has failed because {}" + .format(ctr, req_id, str(e))) def initial_request_to_conductor(rc, conductor_url, conductor_req_json): @@ -92,17 +98,21 @@ def initial_request_to_conductor(rc, conductor_url, conductor_req_json): :param rc: REST client object for calling conductor :param conductor_url: conductor's base URL to submit a placement request :param conductor_req_json: request json object to send to Conductor - :return: URL to check for follow up (similar to redirects); we keep checking these till we get a result/error + :return: URL to check for follow up (similar to redirects); + we keep checking these till we get a result/error """ debug_log.debug("Payload to Conductor: {}".format(json.dumps(conductor_req_json))) - raw_resp = rc.request(url=conductor_url, raw_response=True, method="POST", json=conductor_req_json) + raw_resp = rc.request(url=conductor_url, raw_response=True, method="POST", + json=conductor_req_json) resp = raw_resp.json() if resp["status"] != "template": raise RequestException(response=raw_resp, request=raw_resp.request) time.sleep(10) # 10 seconds wait time to avoid being too quick! plan_url = resp["links"][0][0]["href"] - debug_log.debug("Attempting to read the plan from the conductor provided url {}".format(plan_url)) - raw_resp = rc.request(raw_response=True, url=plan_url) # TODO: check why a list of lists for links + debug_log.debug("Attempting to read the plan from " + "the conductor provided url {}".format(plan_url)) + raw_resp = rc.request(raw_response=True, + url=plan_url) # TODO: check why a list of lists for links resp = raw_resp.json() if resp["plans"][0]["status"] in ["error"]: diff --git a/osdf/adapters/conductor/templates/conductor_interface.json b/osdf/adapters/conductor/templates/conductor_interface.json index 030d6a0..d4a9a0e 100755 --- a/osdf/adapters/conductor/templates/conductor_interface.json +++ b/osdf/adapters/conductor/templates/conductor_interface.json @@ -11,12 +11,14 @@ "{{key}}": {{ json.dumps(value) }} {% endfor %} }, + {% if location_enabled %} "locations": { "customer_loc": { "latitude": { "get_param": "customer_lat" }, "longitude": { "get_param": "customer_long" } } }, + {% endif %} "demands": {{ json.dumps(demand_list) }}, {% set comma_main = joiner(",") %} "constraints": { diff --git a/osdf/adapters/conductor/translation.py b/osdf/adapters/conductor/translation.py index 12dfc88..002af88 100644 --- a/osdf/adapters/conductor/translation.py +++ b/osdf/adapters/conductor/translation.py @@ -26,6 +26,19 @@ from osdf.utils.programming_utils import dot_notation policy_config_mapping = yaml.safe_load(open('config/has_config.yaml')).get('policy_config_mapping') +CONSTRAINT_TYPE_MAP = {"onap.policies.optimization.AttributePolicy": "attribute", + "onap.policies.optimization.DistancePolicy": "distance_to_location", + "onap.policies.optimization.InventoryGroupPolicy": "inventory_group", + "onap.policies.optimization.ResourceInstancePolicy": "instance_fit", + "onap.policies.optimization.ResourceRegionPolicy": "region_fit", + "onap.policies.optimization.AffinityPolicy": "zone", + "onap.policies.optimization.InstanceReservationPolicy": + "instance_reservation", + "onap.policies.optimization.Vim_fit": "vim_fit", + "onap.policies.optimization.HpaPolicy": "hpa", + "onap.policies.optimization.ThresholdPolicy": "threshold" + } + def get_opt_query_data(request_parameters, policies): """ @@ -44,6 +57,7 @@ def get_opt_query_data(request_parameters, policies): req_param_dict.update({queryProp['attribute']: attr_val}) return req_param_dict + def gen_optimization_policy(vnf_list, optimization_policy): """Generate optimization policy details to pass to Conductor :param vnf_list: List of vnf's to used in placement request @@ -105,7 +119,7 @@ def gen_policy_instance(vnf_list, resource_policy, match_type="intersection", rt for policy in resource_policy: pc = policy[list(policy.keys())[0]] default, demands = get_matching_vnfs(pc['properties']['resources'], vnf_list, match_type=match_type) - resource = {pc['properties']['identity']: {'type': map_constraint_type(pc['type']), 'demands': demands}} + resource = {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']), 'demands': demands}} if rtype: resource[pc['properties']['identity']]['properties'] = {'controller': pc[rtype]['controller'], @@ -115,13 +129,13 @@ def gen_policy_instance(vnf_list, resource_policy, match_type="intersection", rt if default: for d in demands: resource_repeated = True \ - if {pc['properties']['identity']: {'type': map_constraint_type(pc['type']), 'demands': d}} \ + if {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']), 'demands': d}} \ in resource_policy_list else False if resource_repeated: continue else: resource_policy_list.append( - {pc['properties']['identity']: {'type': map_constraint_type(pc['type']), 'demands': d }}) + {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']), 'demands': d }}) policy[list(policy.keys())[0]]['properties']['resources'] = d related_policies.append(policy) # Need to override the default policies, here delete the outdated policy stored in the db @@ -216,6 +230,14 @@ def gen_hpa_policy(vnf_list, hpa_policy): return cur_policies +def gen_threshold_policy(vnf_list, threshold_policy): + cur_policies, related_policies = gen_policy_instance(vnf_list, threshold_policy, rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): + pmz = p_main[list(p_main.keys())[0]]['properties']['thresholdProperty'] + p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = pmz + return cur_policies + + def get_augmented_policy_attributes(policy_property, demand): """Get policy attributes and augment them using policy_config_mapping and demand information""" attributes = copy.copy(policy_property['attributes']) @@ -260,13 +282,13 @@ def get_demand_properties(demand, policies): policy_property['unique'] else {}) prop['filtering_attributes'] = dict() prop['filtering_attributes'].update({'global-customer-id': policy_property['customerId']} - if policy_property['customerId'] else {}) + if 'customerId' in policy_property and policy_property['customerId'] else {}) prop['filtering_attributes'].update({'model-invariant-id': demand['resourceModelInfo']['modelInvariantId']} if demand['resourceModelInfo']['modelInvariantId'] else {}) prop['filtering_attributes'].update({'model-version-id': demand['resourceModelInfo']['modelVersionId']} if demand['resourceModelInfo']['modelVersionId'] else {}) prop['filtering_attributes'].update({'equipment-role': policy_property['equipmentRole']} - if policy_property['equipmentRole'] else {}) + if 'equipmentRole' in policy_property and policy_property['equipmentRole'] else {}) if policy_property.get('attributes'): for attr_key, attr_val in policy_property['attributes'].items(): @@ -304,7 +326,8 @@ def update_converted_attribute(attr_key, attr_val, properties, attribute_type): def gen_demands(demands, vnf_policies): """Generate list of demands based on request and VNF policies :param demands: A List of demands - :param vnf_policies: Policies associated with demand resources (e.g. from grouped_policies['vnfPolicy']) + :param vnf_policies: Policies associated with demand resources + (e.g. from grouped_policies['vnfPolicy']) :return: list of demand parameters to populate the Conductor API call """ demand_dictionary = {} @@ -315,29 +338,6 @@ def gen_demands(demands, vnf_policies): return demand_dictionary -def map_constraint_type(policy_type): - if "onap.policies.optimization.AttributePolicy" == policy_type: - return "attribute" - if "onap.policies.optimization.DistancePolicy" == policy_type: - return "distance_to_location" - if "onap.policies.optimization.InventoryGroupPolicy" == policy_type: - return "inventory_group" - if "onap.policies.optimization.ResourceInstancePolicy" == policy_type: - return "instance_fit" - if "onap.policies.optimization.ResourceRegionPolicy" == policy_type: - return "region_fit" - if "onap.policies.optimization.AffinityPolicy" == policy_type: - return "zone" - if "onap.policies.optimization.InstanceReservationPolicy" == policy_type: - return "instance_reservation" - if "onap.policies.optimization.Vim_fit" == policy_type: - return "vim_fit" - if "onap.policies.optimization.HpaPolicy" == policy_type: - return "hpa" - - return policy_type - - def gen_cloud_region(property): prop = {"cloud_region_attributes": dict()} if 'cloudRegion' in property: -- cgit 1.2.3-korg