aboutsummaryrefslogtreecommitdiffstats
path: root/osdf/optimizers
diff options
context:
space:
mode:
Diffstat (limited to 'osdf/optimizers')
-rw-r--r--osdf/optimizers/licenseopt/simple_license_allocation.py23
-rw-r--r--osdf/optimizers/placementopt/conductor/api_builder.py28
-rw-r--r--osdf/optimizers/placementopt/conductor/conductor.py3
-rw-r--r--osdf/optimizers/placementopt/conductor/remote_opt_processor.py4
-rw-r--r--osdf/optimizers/placementopt/conductor/translation.py125
5 files changed, 75 insertions, 108 deletions
diff --git a/osdf/optimizers/licenseopt/simple_license_allocation.py b/osdf/optimizers/licenseopt/simple_license_allocation.py
index beafbe4..ad9acfc 100644
--- a/osdf/optimizers/licenseopt/simple_license_allocation.py
+++ b/osdf/optimizers/licenseopt/simple_license_allocation.py
@@ -16,13 +16,6 @@
# -------------------------------------------------------------------------
#
-import json
-
-from requests import RequestException
-from osdf.adapters.sdc import sdc, constraint_handler
-from osdf.logging.osdf_logging import audit_log, metrics_log, MH
-from osdf.config.base import osdf_config
-
def license_optim(request_json):
"""
@@ -32,25 +25,17 @@ def license_optim(request_json):
:return: A tuple of licensekey-group-uuid-list and entitlement-group-uuid-list
"""
req_id = request_json["requestInfo"]["requestId"]
- config = osdf_config.deployment
- model_name = request_json['placementInfo']['serviceModelInfo']['modelName']
- # service_name = data_mapping.get_service_type(model_name)
+ model_name = request_json.get('placementInfo', {}).get('serviceInfo', {}).get('modelInfo', {}).get('modelName')
service_name = model_name
license_info = []
- order_info = json.loads(request_json["placementInfo"]["requestParameters"])
- if service_name == 'VPE':
- data_mapping.normalize_user_params(order_info)
- for licenseDemand in request_json['placementInfo']['demandInfo']['licenseDemand']:
- metrics_log.info(MH.requesting("sdc", req_id))
- license_artifacts = sdc.request(licenseDemand['resourceModelInfo']['modelVersionId'],request_json["requestInfo"]["requestId"], config)
- entitlement_pool_uuids, license_key_group_uuids = constraint_handler.choose_license(license_artifacts,order_info, service_name)
+ for licenseDemand in request_json.get('placementInfo', {}).get('demandInfo', {}).get('licenseDemands', []):
license_info.append(
{'serviceResourceId': licenseDemand['serviceResourceId'],
'resourceModuleName': licenseDemand['resourceModuleName'],
- 'entitlementPoolList': entitlement_pool_uuids,
- 'licenseKeyGroupList': license_key_group_uuids
+ 'entitlementPoolList': "NOT SUPPORTED",
+ 'licenseKeyGroupList': "NOT SUPPORTED"
})
return license_info
diff --git a/osdf/optimizers/placementopt/conductor/api_builder.py b/osdf/optimizers/placementopt/conductor/api_builder.py
index 0a874f7..d8a2083 100644
--- a/osdf/optimizers/placementopt/conductor/api_builder.py
+++ b/osdf/optimizers/placementopt/conductor/api_builder.py
@@ -18,7 +18,6 @@
import copy
import json
-# from osdf.utils import data_mapping
from jinja2 import Template
from osdf.utils.programming_utils import list_flatten, dot_notation
import osdf.optimizers.placementopt.conductor.translation as tr
@@ -39,10 +38,10 @@ def conductor_api_builder(request_json, flat_policies: list, local_config, prov_
gp = group_policies(flat_policies)
demand_vnf_name_list = []
- for placementDemand in request_json['placementInfo']['demandInfo']['placementDemand']:
+ for placementDemand in request_json['placementInfo']['placementDemands']:
demand_vnf_name_list.append(placementDemand['resourceModuleName'])
- demand_list = tr.gen_demands(request_json['placementInfo']['demandInfo'], gp['vnfPolicy'])
+ demand_list = tr.gen_demands(request_json, gp['vnfPolicy'])
attribute_policy_list = tr.gen_attribute_policy(demand_vnf_name_list, gp['attribute'])
distance_to_location_policy_list = tr.gen_distance_to_location_policy(
demand_vnf_name_list, gp['distance_to_location'])
@@ -60,7 +59,7 @@ def conductor_api_builder(request_json, flat_policies: list, local_config, prov_
reservation_policies = [x for x in reservation_policy_list if len(x) > 0]
reservation_groups = list_flatten(reservation_policies)
req_info = request_json['requestInfo']
- model_name = request_json['placementInfo']['serviceModelInfo']['modelName']
+ 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, {})
@@ -71,6 +70,7 @@ def conductor_api_builder(request_json, flat_policies: list, local_config, prov_
subs_com_site_id = ""
if 'subscriberInfo' in request_json['placementInfo']:
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(
@@ -83,28 +83,10 @@ def conductor_api_builder(request_json, flat_policies: list, local_config, prov_
timeout=req_info['timeout'],
limit=req_info['numSolutions'],
serviceType=service_type,
- serviceInstance=request_json['placementInfo']['serviceInstanceId'],
+ serviceInstance=request_json['serviceInfo']['serviceInstanceId'],
provStatus = prov_status,
chosenRegion=order_info.get('requestParameters',{}).get('lcpCloudRegionId'),
json=json)
- elif service_type == 'UNKNOWN':
- rendered_req = templ.render(
- requestType=request_type,
- chosenComplex=subs_com_site_id,
- demand_list=demand_list,
- policy_groups=policy_groups,
- reservation_groups=reservation_groups,
- optimization_policies=optimization_policy_list,
- name=req_info['requestId'],
- timeout=req_info['timeout'],
- limit=req_info['numSolutions'],
- serviceType=service_type,
- serviceInstance=request_json['placementInfo']['serviceInstanceId'],
- provStatus = prov_status,
- # process order data
- bandwidth=dot_notation(order_info, service_info['bandwidth']),
- bandwidth_unit=dot_notation(order_info, service_info['bandwidth_units']),
- 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/optimizers/placementopt/conductor/conductor.py b/osdf/optimizers/placementopt/conductor/conductor.py
index bdc7f17..32a4728 100644
--- a/osdf/optimizers/placementopt/conductor/conductor.py
+++ b/osdf/optimizers/placementopt/conductor/conductor.py
@@ -78,7 +78,8 @@ def request(req_object, osdf_config, grouped_policies, prov_status):
new_url = resp['plans'][0]['links'][0][0]['href'] # TODO: check why a list of lists
if total_time >= max_timeout:
- raise BusinessException("Conductor could not provide a solution within {} seconds, this transaction is timing out".format(max_timeout))
+ raise BusinessException("Conductor could not provide a solution within {} seconds,"
+ "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']))
diff --git a/osdf/optimizers/placementopt/conductor/remote_opt_processor.py b/osdf/optimizers/placementopt/conductor/remote_opt_processor.py
index f753a70..957547b 100644
--- a/osdf/optimizers/placementopt/conductor/remote_opt_processor.py
+++ b/osdf/optimizers/placementopt/conductor/remote_opt_processor.py
@@ -43,11 +43,11 @@ def process_placement_opt(request_json, policies, osdf_config, prov_status):
metrics_log.info(MH.inside_worker_thread(req_id))
license_info = None
- if 'licenseDemand' in request_json['placementInfo']['demandInfo']:
+ if request_json.get('licenseInfo', {}).get('licenseDemands'):
license_info = license_optim(request_json)
# Conductor only handles placement, only call Conductor if placementDemands exist
- if 'placementDemand' in request_json['placementInfo']['demandInfo']:
+ if request_json.get('licenseInfo', {}).get('licenseDemands'):
metrics_log.info(MH.requesting("placement/conductor", req_id))
placement_response = conductor.request(request_json, osdf_config, policies, prov_status)
if license_info: # Attach license solution if it exists
diff --git a/osdf/optimizers/placementopt/conductor/translation.py b/osdf/optimizers/placementopt/conductor/translation.py
index 262fa86..9111c81 100644
--- a/osdf/optimizers/placementopt/conductor/translation.py
+++ b/osdf/optimizers/placementopt/conductor/translation.py
@@ -15,16 +15,20 @@
#
# -------------------------------------------------------------------------
#
-
+import copy
import json
+import yaml
+
from osdf.utils.data_conversion import text_to_symbol
-# from osdf.utils import data_mapping
+
+policy_config_mapping = yaml.load(open('config/has_config.yaml')).get('policy_config_mapping')
+
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
- :param optimization_policy: optimization policy information provided in the incoming request
- :return: List of optimization policies in a format required by Conductor
+ :param optimization_policy: optimization objective policy information provided in the incoming request
+ :return: List of optimization objective policies in a format required by Conductor
"""
optimization_policy_list = []
for policy in optimization_policy:
@@ -47,7 +51,7 @@ def gen_optimization_policy(vnf_list, optimization_policy):
def get_matching_vnf(resource, vnf_list):
-
+
for vnf in vnf_list:
if resource in vnf:
return vnf
@@ -57,20 +61,16 @@ def get_matching_vnf(resource, vnf_list):
def get_matching_vnfs(resources, vnf_list, match_type="intersection"):
"""Get a list of matching VNFs from the list of resources
:param resources:
- :param vnf_list: List of vnf's to used in placement request
+ :param vnf_list: List of vnfs to used in placement request
:param match_type: "intersection" or "all" or "any" (any => send all_vnfs if there is any intersection)
:return: List of matching VNFs
"""
- common_vnfs = []
- for vnf in vnf_list:
- for resource in resources:
- if resource in vnf:
- common_vnfs.append(vnf)
- if match_type == "intersection": # specifically requested intersection
- return common_vnfs
- elif common_vnfs or match_type == "all": # ("any" and common) OR "all"
+ if match_type == "all": # don't bother with any comparisons
return resources
- return None
+ common_vnfs = set(vnf_list) & set(resources)
+ if match_type == "intersection": # specifically requested intersection
+ return list(common_vnfs)
+ return resources if common_vnfs else None # "any" match => all resources to be returned
def gen_policy_instance(vnf_list, resource_policy, match_type="intersection", rtype=None):
@@ -141,22 +141,11 @@ def gen_attribute_policy(vnf_list, attribute_policy):
cur_policies, related_policies = gen_policy_instance(vnf_list, attribute_policy, rtype=None)
for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy
properties = p_main['content']['cloudAttributeProperty']
+ attribute_mapping = policy_config_mapping['attributes'] # wanted attributes and mapping
p_new[p_main['content']['identity']]['properties'] = {
- 'evaluate': {
- 'hypervisor': properties.get('hypervisor', ''),
- 'cloud_version': properties.get('cloudVersion', ''),
- 'cloud_type': properties.get('cloudType', ''),
- 'dataplane': properties.get('dataPlane', ''),
- 'network_roles': properties.get('networkRoles', ''),
- 'complex': properties.get('complex', ''),
- 'state': properties.get('state', ''),
- 'country': properties.get('country', ''),
- 'geo_region': properties.get('geoRegion', ''),
- 'exclusivity_groups': properties.get('exclusivityGroups', ''),
- 'replication_role': properties.get('replicationRole', '')
- }
+ 'evaluate': dict((k, properties.get(attribute_mapping.get(k))) for k in attribute_mapping.keys())
}
- return cur_policies
+ return cur_policies # cur_policies gets updated in place...
def gen_zone_policy(vnf_list, zone_policy):
@@ -168,39 +157,49 @@ def gen_zone_policy(vnf_list, zone_policy):
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'])
+ remapping = policy_config_mapping['remapping']
+ extra = dict((x, demand['resourceModelInfo'][remapping[x]]) for x in attributes if x in remapping)
+ attributes.update(extra)
+ return attributes
+
+
+def get_candidates_demands(demand):
+ """Get demands related to candidates; e.g. excluded/required"""
+ res = {}
+ for k, v in policy_config_mapping['candidates'].items():
+ if k not in demand:
+ continue
+ res[v] = [{'inventory_type': x['candidateType'], 'candidate_id': x['candidates']} for x in demand[k]]
+ return res
+
+
+def get_policy_properties(demand, policies):
+ """Get policy_properties for cases where there is a match with the demand"""
+ for policy in policies:
+ if not set(policy['content'].get('resourceInstanceType', [])) & set(demand['resourceModuleName']):
+ continue # no match for this policy
+ for policy_property in policy['content']['property']:
+ yield policy_property
+
+
def get_demand_properties(demand, policies):
"""Get list demand properties objects (named tuples) from policy"""
- def _get_candidates(candidate_info):
- return [dict(inventory_type=x['candidateType'], candidate_id=x['candidates']) for x in candidate_info]
- properties = []
- for policy in policies:
- for resourceInstanceType in policy['content']['resourceInstanceType']:
- if resourceInstanceType in demand['resourceModuleName']:
- for x in policy['content']['property']:
- property = dict(inventory_provider=x['inventoryProvider'],
- inventory_type=x['inventoryType'],
- service_resource_id=demand['serviceResourceId'])
- if 'attributes' in x:
- attributes = {}
- for k,v in x['attributes'].items():
- # key=data_mapping.convert(k)
- key = k
- attributes[key] = v
- if(key=="model-invariant-id"):
- attributes[key]=demand['resourceModelInfo']['modelInvariantId']
- elif(key=="model-version-id"):
- attributes[key]=demand['resourceModelInfo']['modelVersionId']
- property.update({"attributes": attributes})
- if x['inventoryType'] == "cloud":
- property['region'] = {'get_param': "CHOSEN_REGION"}
- if 'exclusionCandidateInfo' in demand:
- property['excluded_candidates'] = _get_candidates(demand['exclusionCandidateInfo'])
- if 'requiredCandidateInfo' in demand:
- property['required_candidates'] = _get_candidates(demand['requiredCandidateInfo'])
- properties.append(property)
- if len(properties) == 0:
- properties.append(dict(customer_id="", service_type="", inventory_provider="", inventory_type=""))
- return properties
+ demand_properties = []
+ for policy_property in get_policy_properties(demand, policies):
+ prop = dict(inventory_provider=policy_property['inventoryProvider'],
+ inventory_type=policy_property['inventoryType'],
+ service_resource_id=demand['serviceResourceId'])
+ if 'attributes' in policy_property:
+ prop['attributes'] = get_augmented_policy_attributes(policy_property, demand)
+ for k1, v1, k2, v2 in policy_config_mapping['extra_fields']:
+ if k1 == v1:
+ prop[k2] = v2
+ prop.update(get_candidates_demands(demand)) # for excluded and partial-rehoming cases
+ demand_properties.append(prop)
+ return demand_properties
def gen_demands(req_json, vnf_policies):
@@ -210,7 +209,7 @@ def gen_demands(req_json, vnf_policies):
:return: list of demand parameters to populate the Conductor API call
"""
demand_dictionary = {}
- for placementDemand in req_json['placementDemand']:
- demand_dictionary.update({placementDemand['resourceModuleName']: get_demand_properties(placementDemand, vnf_policies)})
-
+ for demand in req_json['placementInfo']['placementDemands']:
+ demand_dictionary.update(
+ {demand['resourceModuleName']: get_demand_properties(demand, vnf_policies)})
return demand_dictionary