From 1a9638f5d5fc78f7e8be700e71b506fed3cc9d2d Mon Sep 17 00:00:00 2001 From: Sastry Isukapalli Date: Sat, 17 Mar 2018 02:40:15 -0400 Subject: New policies and required code changes Issue-ID: OPTFRA-146 Change-Id: I2261ae69f52b184cd7dcb7b86d5905538666a411 Signed-off-by: Sastry Isukapalli --- config/common_config.yaml | 34 ++-- config/has_config.yaml | 24 +++ osdf/adapters/local_data/local_policies.py | 11 ++ osdf/adapters/policy/interface.py | 20 +-- osdf/adapters/policy/utils.py | 8 +- osdf/adapters/sdc/constraint_handler.py | 81 --------- osdf/adapters/sdc/sdc.py | 40 ----- osdf/models/api/placementRequest.py | 82 ++++----- osdf/models/api/placementResponse.py | 33 ++-- .../licenseopt/simple_license_allocation.py | 23 +-- .../placementopt/conductor/api_builder.py | 28 +--- .../optimizers/placementopt/conductor/conductor.py | 3 +- .../placementopt/conductor/remote_opt_processor.py | 4 +- .../placementopt/conductor/translation.py | 125 +++++++------- osdf/utils/interfaces.py | 14 ++ osdfapp.py | 44 ++--- test/conductor/test_conductor_translation.py | 53 ++++++ test/config/common_config.yaml | 43 +++++ test/config/has_config.yaml | 24 +++ test/config/osdf_config.yaml | 34 ++++ test/local_data/test_local_policies.py | 12 +- test/operation/test_responses.py | 15 +- test/placement-tests/request.json | 186 +++++++++++---------- test/placement-tests/response.json | 49 ++++++ test/policy-local-files/Affinity_vCPE_1.json | 21 +++ test/policy-local-files/Capacity_vGMuxInfra.json | 21 +++ test/policy-local-files/Capacity_vG_1.json | 21 +++ .../CloudAttributePolicy_vGMuxInfra_1.json | 34 ---- .../CloudAttributePolicy_vG_1.json | 34 ---- .../DistanceToLocationPolicy_vGMuxInfra_1.json | 30 ---- .../DistanceToLocationPolicy_vG_1.json | 30 ---- test/policy-local-files/Distance_vGMuxInfra_1.json | 21 +++ test/policy-local-files/Distance_vG_1.json | 21 +++ .../INVALID-policies/INVALID-Affinity_vCPE_1.json | 21 +++ .../InventoryGroup_vGMuxInfra_1.json | 22 --- test/policy-local-files/InventoryGroup_vG_1.json | 22 --- .../Min_Guarantee_vGMuxInfra_1.json | 21 +++ .../PlacementOptimizationPolicy.json | 41 ----- .../Placement_Optimization_1.json | 36 ++++ test/policy-local-files/QueryPolicy_vCPE.json | 20 +++ .../ResourceInstancePolicy_vG_1.json | 26 --- .../policy-local-files/VNFPolicy_vGMuxInfra_1.json | 36 ---- test/policy-local-files/VNFPolicy_vG_1.json | 36 ---- .../ZonePolicy_vGMuxInfra_1.json | 26 --- test/policy-local-files/ZonePolicy_vG_1.json | 26 --- .../hpa_policy_vGMuxInfra_1.json | 144 ++++++++++++++++ test/policy-local-files/hpa_policy_vG_1.json | 144 ++++++++++++++++ test/policy-local-files/meta-invalid-policies.txt | 4 + test/policy-local-files/meta-valid-policies.txt | 12 ++ test/policy-local-files/vnfPolicy_vG.json | 31 ++++ test/policy-local-files/vnfPolicy_vGMuxInfra.json | 31 ++++ test/policy/test_policy_interface.py | 64 +++++++ test/test_ConductorApiBuilder.py | 39 ++--- test/test_PolicyCalls.py | 60 ++++--- test/test_api_validation.py | 5 + test/test_process_placement_opt.py | 58 ++++--- tox.ini | 4 + 57 files changed, 1286 insertions(+), 866 deletions(-) create mode 100644 config/has_config.yaml delete mode 100644 osdf/adapters/sdc/constraint_handler.py delete mode 100755 osdf/adapters/sdc/sdc.py create mode 100644 test/conductor/test_conductor_translation.py create mode 100644 test/config/common_config.yaml create mode 100644 test/config/has_config.yaml create mode 100755 test/config/osdf_config.yaml create mode 100644 test/placement-tests/response.json create mode 100644 test/policy-local-files/Affinity_vCPE_1.json create mode 100644 test/policy-local-files/Capacity_vGMuxInfra.json create mode 100644 test/policy-local-files/Capacity_vG_1.json delete mode 100644 test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json delete mode 100644 test/policy-local-files/CloudAttributePolicy_vG_1.json delete mode 100644 test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json delete mode 100644 test/policy-local-files/DistanceToLocationPolicy_vG_1.json create mode 100644 test/policy-local-files/Distance_vGMuxInfra_1.json create mode 100644 test/policy-local-files/Distance_vG_1.json create mode 100644 test/policy-local-files/INVALID-policies/INVALID-Affinity_vCPE_1.json delete mode 100644 test/policy-local-files/InventoryGroup_vGMuxInfra_1.json delete mode 100644 test/policy-local-files/InventoryGroup_vG_1.json create mode 100644 test/policy-local-files/Min_Guarantee_vGMuxInfra_1.json delete mode 100644 test/policy-local-files/PlacementOptimizationPolicy.json create mode 100644 test/policy-local-files/Placement_Optimization_1.json create mode 100644 test/policy-local-files/QueryPolicy_vCPE.json delete mode 100644 test/policy-local-files/ResourceInstancePolicy_vG_1.json delete mode 100644 test/policy-local-files/VNFPolicy_vGMuxInfra_1.json delete mode 100644 test/policy-local-files/VNFPolicy_vG_1.json delete mode 100644 test/policy-local-files/ZonePolicy_vGMuxInfra_1.json delete mode 100644 test/policy-local-files/ZonePolicy_vG_1.json create mode 100644 test/policy-local-files/hpa_policy_vGMuxInfra_1.json create mode 100644 test/policy-local-files/hpa_policy_vG_1.json create mode 100644 test/policy-local-files/meta-invalid-policies.txt create mode 100644 test/policy-local-files/meta-valid-policies.txt create mode 100644 test/policy-local-files/vnfPolicy_vG.json create mode 100644 test/policy-local-files/vnfPolicy_vGMuxInfra.json create mode 100644 test/policy/test_policy_interface.py diff --git a/config/common_config.yaml b/config/common_config.yaml index d8f467b..bc62a58 100644 --- a/config/common_config.yaml +++ b/config/common_config.yaml @@ -1,29 +1,29 @@ osdf_system: libpath: /opt/app/osdf/libs - sniro_ports: + osdf_ports: internal: 24699 # inside the Docker container, the app listens to this port external: 14699 # clients use this port on DockerHost - # Important Note: At deployment time, we need to ensure the port mapping is done - ssl_context: ['./../etc/sniromanager.crt', './../etc/sniromanager.key'] + osdf_ip_default: 0.0.0.0 +# # Important Note: At deployment time, we need to ensure the port mapping is done +# ssl_context: ['./../etc/sniromanager.crt', './../etc/sniromanager.key'] -osdf_temp: # hacks required for "workarounds" or testing +osdf_temp: # special configuration required for "workarounds" or testing local_policies: global_disabled: True local_placement_policies_enabled: True placement_policy_files_vcpe: # workaroud for policy platform glitches (or "work-arounds" for other components) - - CloudAttributePolicy_vGMuxInfra_1.json - - CloudAttributePolicy_vG_1.json - - DistanceToLocationPolicy_vGMuxInfra_1.json - - DistanceToLocationPolicy_vG_1.json - - InventoryGroup_vGMuxInfra_1.json - - InventoryGroup_vG_1.json - - PlacementOptimizationPolicy.json - - ResourceInstancePolicy_vG_1.json - - VNFPolicy_vGMuxInfra_1.json - - VNFPolicy_vG_1.json - - ZonePolicy_vGMuxInfra_1.json - - ZonePolicy_vG_1.json - + - Affinity_vCPE_1.json + - Capacity_vGMuxInfra.json + - Capacity_vG_1.json + - Distance_vGMuxInfra_1.json + - Distance_vG_1.json + - Min_Guarantee_vGMuxInfra_1.json + - Placement_Optimization_1.json + - QueryPolicy_vCPE.json + - hpa_policy_vGMuxInfra_1.json + - hpa_policy_vG_1.json + - vnfPolicy_vG.json + - vnfPolicy_vGMuxInfra.json service_info: vCPE: vcpeHostName: requestParameters.vcpeHostName diff --git a/config/has_config.yaml b/config/has_config.yaml new file mode 100644 index 0000000..8cbeda2 --- /dev/null +++ b/config/has_config.yaml @@ -0,0 +1,24 @@ +policy_config_mapping: + attributes: + hypervisor: hypervisor, + cloud_version: cloudVersion, + cloud_type: cloudType, + dataplane: dataPlane, + network_roles: networkRoles, + complex: complex, + state: state, + country: country, + geo_region: geoRegion, + exclusivity_groups: exclusivityGroups, + replication_role: replicationRole, + remapping: + model-invariant-id: modelInvariantId, + model-version-id: modelVersionId + candidates: + # for (k1, v1), if k1 is in demand, set prop[k2] = _get_candidates(demand[k1]) + exclusionCandidateInfo: excluded_candidates, + requiredCandidateInfo: required_candidates + extra_fields: + # we have [k1, k2, k3, k4] type items and x is policy-content-properties + # if x[k1] == k2: set prop[k3] = k4 + - [inventoryType, cloud, region, {get_param: CHOSEN_REGION}] \ No newline at end of file diff --git a/osdf/adapters/local_data/local_policies.py b/osdf/adapters/local_data/local_policies.py index c63ae5a..e722fe2 100644 --- a/osdf/adapters/local_data/local_policies.py +++ b/osdf/adapters/local_data/local_policies.py @@ -18,6 +18,7 @@ import json import os +import re def get_local_policies(local_policy_folder, local_policy_list, policy_id_list=None): @@ -38,3 +39,13 @@ def get_local_policies(local_policy_folder, local_policy_list, policy_id_list=No with open(os.path.join(local_policy_folder, fname)) as fid: policies.append(json.load(fid)) return policies + + +def get_policy_names_from_file(fname_for_list_of_files): + """Get policy names; take care of comments, empty lines, etc""" + with open(fname_for_list_of_files) as fid: + return [ + re.sub(r'#.*$', '', x).strip() # remove inline comments and strip spaces + for x in fid + if not re.search(r'^#|^$', x.strip()) # remove blank or comments-only lines + ] diff --git a/osdf/adapters/policy/interface.py b/osdf/adapters/policy/interface.py index 4ddee15..288571f 100644 --- a/osdf/adapters/policy/interface.py +++ b/osdf/adapters/policy/interface.py @@ -110,9 +110,7 @@ def get_by_scope(rest_client, req, config_local, type_service): model_name = retrieve_node(req, pscope['service_name']) service_name = model_name - # service_name = data_mapping.get_request_service_type(req) - # if service_name is None: - # service_name = data_mapping.get_service_type(model_name) + scope = pscope['scope_{}'.format(service_name.lower())] subscriber_role, prov_status = get_subscriber_role(rest_client, req, pmain, service_name, scope) policy_type_list = pmain['policy_type_{}'.format(service_name.lower())] @@ -134,9 +132,7 @@ def remote_api(req_json, osdf_config, service_type="placement"): :param service_type: the type of service to call: "placement", "scheduling" :return: all related policies and provStatus retrieved from Subscriber policy """ -# if not req_json[service_type + "Info"]['policyId']: -# return [] - + prov_status = None config = osdf_config.deployment uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword'] pcuid, pcpasswd = config['policyClientUsername'], config['policyClientPassword'] @@ -168,16 +164,18 @@ def local_policies_location(req_json, osdf_config, service_type): :param service_type: placement supported for now, but can be any other service :return: a tuple (folder, file_list) or None """ - lp = osdf_config.core.get('osdf_hacks', {}).get('local_policies', {}) + lp = osdf_config.core.get('osdf_temp', {}).get('local_policies', {}) if lp.get('global_disabled'): return None # short-circuit to disable all local policies if lp.get('local_{}_policies_enabled'.format(service_type)): if service_type == "scheduling": return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type)) else: - model_name = retrieve_node(req_json, osdf_config.core['policy_info'][service_type]['policy_scope']['service_name']) - service_name = data_mapping.get_service_type(model_name) - return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), lp.get('{}_policy_files_{}'.format(service_type, service_name.lower())) + required_node = osdf_config.core['policy_info'][service_type]['policy_scope']['service_name'] + model_name = retrieve_node(req_json, required_node) + service_name = model_name # TODO: data_mapping.get_service_type(model_name) + return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), \ + lp.get('{}_policy_files_{}'.format(service_type, service_name.lower())) return None @@ -199,6 +197,6 @@ def get_policies(request_json, service_type): to_filter = request_json[service_type + "Info"]['policyId'] policies = get_local_policies(local_info[0], local_info[1], to_filter) else: - policies, prov_status= remote_api(request_json, osdf_config, service_type) + 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 a006f12..27885a5 100644 --- a/osdf/adapters/policy/utils.py +++ b/osdf/adapters/policy/utils.py @@ -31,10 +31,12 @@ def group_policies(flat_policies): filter_policies = defaultdict(list) policy_name = [] for policy in flat_policies: - policy_type = policy['content']['type'] + 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']['policyScope']['resourceInstanceType']: + 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]: @@ -42,7 +44,7 @@ def group_policies(flat_policies): 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']['type']].append(policy) + filter_policies[policy['content']['policyType']].append(policy) policy_name.append(policy['policyName']) return filter_policies diff --git a/osdf/adapters/sdc/constraint_handler.py b/osdf/adapters/sdc/constraint_handler.py deleted file mode 100644 index 2aae9a0..0000000 --- a/osdf/adapters/sdc/constraint_handler.py +++ /dev/null @@ -1,81 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (c) 2015-2017 AT&T Intellectual Property -# -# 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. -# -# ------------------------------------------------------------------------- -# - -from osdf.config.base import osdf_config -from osdf.utils.programming_utils import dot_notation - -ns = {'p': 'http://xmlns.onap.org/sdc/license-model/1.0'} -config_local = osdf_config.core - - -def choose_license(license_artifacts, order_info, service_type): - entitlement_pool_uuids = [] - license_key_group_uuids = [] - - for license_artifact in license_artifacts: - for feature in license_artifact.findall('./p:feature-group-list/', ns): - for entitlement in feature.findall('./p:entitlement-pool-list/', ns): - if is_valid(entitlement, order_info, service_type): - entitlement_pool_uuid = entitlement.find('p:entitlement-pool-uuid', ns).text - entitlement_pool_uuids.append(entitlement_pool_uuid) - for license_key_group in feature.findall('./p:license-key-group-list/', ns): - if is_valid(license_key_group, order_info, service_type): - license_key_group_uuid = license_key_group.find('p:license-key-group-uuid', ns).text - license_key_group_uuids.append(license_key_group_uuid) - return entitlement_pool_uuids, license_key_group_uuids - - -# element is expected to be a license-key-group or entitlement-pool -# if these elements diverge at a later date this method should be refactored -def is_valid(element, order_info, service_type): - for limit in element.findall('./p:sp-limits/p:limit', ns): - # description = limit.find('p:description', ns).text - metric_value = limit.find('p:values', ns).text - metric = limit.find('p:metric', ns).text - try: - order_value = dot_notation(order_info, config_local['service_info'][service_type][metric]) - # print("The order has the value %s for the metric %s and the limit specifies the value %s. The limit has the description %s." % (order_value, metric, metric_value, description)) - if isinstance(order_value, list): # it is possible a list is returned, for example a list of vnfs for vCPE - for arr_value in order_value: - if str(metric_value) != str(arr_value): - return False - else: - if str(metric_value) != str(order_value): - return False - except KeyError: - return False - # vendor limits - for limit in element.findall('./p:vendor-limits/p:limit', ns): - # description = limit.find('p:description', ns).text - metric_value = limit.find('p:values', ns).text - metric = limit.find('p:metric', ns).text - try: - order_value = dot_notation(order_info, config_local['service_info'][service_type][metric]) - if isinstance(order_value, list): # it is possible a list is returned, for example a list of vnfs for vCPE - for arr_value in order_value: - if str(metric_value) != str(arr_value): - return False - else: - if str(metric_value) != str(order_value): - return False - # print("The order has the value %s for the metric %s and the limit specifies the value %s. The limit has the description %s." % (order_value, metric, metric_value, description)) - - except KeyError: - return False - return True - diff --git a/osdf/adapters/sdc/sdc.py b/osdf/adapters/sdc/sdc.py deleted file mode 100755 index 43932ba..0000000 --- a/osdf/adapters/sdc/sdc.py +++ /dev/null @@ -1,40 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (c) 2015-2017 AT&T Intellectual Property -# -# 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. -# -# ------------------------------------------------------------------------- -# - -from osdf.utils.interfaces import RestClient -import xml.etree.ElementTree as ET - -def request(model_version_id, request_id, config): - """Get all of the license artifacts from SDC using service_resource_id and model_version_id - :param model_version_id: model_version_id - :param request_id: request_id - :return: license artifacts from SDC - """ - base_url = config['sdcUrl'] - uid, passwd = config['sdcUsername'], config['sdcPassword'] - headers = {"CSP_UID": config['sdcMechId'], "X-ONAP-InstanceID": "osdf"} - rc = RestClient(userid=uid, passwd=passwd, headers=headers, method="GET", req_id=request_id) - resource_data = rc.request(base_url + '/resources/{}/metadata'.format(model_version_id)) - - artifact_ids = [x['artifactURL'].split("/resources/")[-1] # get the part after /resources/ - for x in resource_data.get('artifacts', []) if x.get('artifactType') == "VF_LICENSE"] - artifact_urls = [base_url + '/resources/' + str(artifact_id) for artifact_id in artifact_ids] - licenses = [] - for x in artifact_urls: - licenses.append(ET.fromstring(rc.request(x, asjson=False))) - return licenses diff --git a/osdf/models/api/placementRequest.py b/osdf/models/api/placementRequest.py index df5f931..aa71eac 100644 --- a/osdf/models/api/placementRequest.py +++ b/osdf/models/api/placementRequest.py @@ -26,60 +26,61 @@ class RequestInfo(OSDFModel): transactionId = StringType(required=True) requestId = StringType(required=True) callbackUrl = URLType(required=True) + callbackHeader = DictType(BaseType) sourceId = StringType(required=True) - optimizers = ListType(StringType(required=True)) + requestType = StringType(required=True) numSolutions = IntType() + optimizers = ListType(StringType(required=True)) timeout = IntType() - requestType = StringType() -class CandidateInfo(OSDFModel): +class Candidates(OSDFModel): """Preferred candidate for a resource (sent as part of a request from client)""" - candidateType = StringType(required=True) - candidates = ListType(StringType(required=True)) + identifierType = StringType(required=True) + identifiers = ListType(StringType(required=True)) + cloudOwner = StringType() -class ResourceModelInfo(OSDFModel): +class ModelMetaData(OSDFModel): """Model information for a specific resource""" - modelCustomizationId = StringType(required=True) modelInvariantId = StringType(required=True) + modelVersionId = StringType(required=True) modelName = StringType() - modelVersion = StringType() - modelVersionId = StringType() modelType = StringType() + modelVersion = StringType() + modelCustomizationName = StringType(required=True) -class ExistingLicenseInfo(OSDFModel): - entitlementPoolUUID = ListType(StringType()) - licenseKeyGroupUUID = ListType(StringType()) +class LicenseModel(OSDFModel): + entitlementPoolUUID = ListType(StringType(required=True)) + licenseKeyGroupUUID = ListType(StringType(required=True)) -class LicenseDemand(OSDFModel): - resourceInstanceType = StringType(required=True) - serviceResourceId = StringType(required=True) +class LicenseDemands(OSDFModel): resourceModuleName = StringType(required=True) - resourceModelInfo = ModelType(ResourceModelInfo) - existingLicense = ModelType(ExistingLicenseInfo) + serviceResourceId = StringType(required=True) + resourceModelInfo = ModelType(ModelMetaData, required=True) + existingLicenses = ModelType(LicenseModel) + + +class LicenseInfo(OSDFModel): + licenseDemands = ListType(ModelType(LicenseDemands)) class PlacementDemand(OSDFModel): - resourceInstanceType = StringType(required=True) - serviceResourceId = StringType(required=True) resourceModuleName = StringType(required=True) - exclusionCandidateInfo = ListType(ModelType(CandidateInfo)) - requiredCandidateInfo = ListType(ModelType(CandidateInfo)) - resourceModelInfo = ModelType(ResourceModelInfo) - tenantId = StringType(required=True) - tenantName = StringType() - -class ExistingPlacementInfo(OSDFModel): - serviceInstanceId = StringType(required=True) + serviceResourceId = StringType(required=True) + tenantId = StringType() + resourceModelInfo = ModelType(ModelMetaData, required=True) + existingCandidates = ListType(ModelType(Candidates)) + excludedCandidates = ListType(ModelType(Candidates)) + requiredCandidates = ListType(ModelType(Candidates)) -class DemandInfo(OSDFModel): - """Requested resources (sent as part of a request from client)""" - placementDemand = ListType(ModelType(PlacementDemand)) - licenseDemand = ListType(ModelType(LicenseDemand)) +class ServiceInfo(OSDFModel): + serviceInstanceId = StringType(required=True) + modelInfo = ModelType(ModelMetaData, required=True) + serviceName = StringType(required=True) class SubscriberInfo(OSDFModel): @@ -89,27 +90,16 @@ class SubscriberInfo(OSDFModel): subscriberCommonSiteId = StringType() -class ServiceModelInfo(OSDFModel): - """ASDC Service model information""" - modelType = StringType(required=True) - modelInvariantId = StringType(required=True) - modelVersionId = StringType(required=True) - modelName = StringType(required=True) - modelVersion = StringType(required=True) - - class PlacementInfo(OSDFModel): """Information specific to placement optimization""" - serviceModelInfo = ModelType(ServiceModelInfo, required=True) - subscriberInfo = ModelType(SubscriberInfo, required=True) - demandInfo = ModelType(DemandInfo, required=True) requestParameters = DictType(BaseType) - policyId = ListType(StringType()) - serviceInstanceId = StringType(required=True) - existingPlacement = ModelType(ExistingPlacementInfo) + placementDemands = ListType(ModelType(PlacementDemand), min_size=1) + subscriberInfo = ModelType(SubscriberInfo) class PlacementAPI(OSDFModel): """Request for placement optimization (specific to optimization and additional metadata""" requestInfo = ModelType(RequestInfo, required=True) placementInfo = ModelType(PlacementInfo, required=True) + licenseInfo = ModelType(LicenseInfo) + serviceInfo = ModelType(ServiceInfo, required=True) diff --git a/osdf/models/api/placementResponse.py b/osdf/models/api/placementResponse.py index e9746d6..75d33f0 100644 --- a/osdf/models/api/placementResponse.py +++ b/osdf/models/api/placementResponse.py @@ -26,32 +26,39 @@ from schematics.types.compound import ModelType, ListType class LicenseSolution(OSDFModel): serviceResourceId = StringType(required=True) resourceModuleName = StringType(required=True) - entitlementPoolList = ListType(StringType(required=True)) - licenseKeyGroupList = ListType(StringType(required=True)) + entitlementPoolUUID = ListType(StringType(required=True)) + licenseKeyGroupUUID = ListType(StringType(required=True)) + entitlementPoolInvariantUUID = ListType(StringType(required=True)) + licenseKeyGroupInvariantUUID = ListType(StringType(required=True)) + + +class Candidates(OSDFModel): + """Preferred candidate for a resource (sent as part of a request from client)""" + identifierType = StringType(required=True) + identifiers = ListType(StringType(required=True)) + cloudOwner = StringType() class AssignmentInfo(OSDFModel): - variableName = StringType(required=True) - variableValue = StringType(required=True) + key = StringType(required=True) + value = StringType(required=True) class PlacementSolution(OSDFModel): serviceResourceId = StringType(required=True) resourceModuleName = StringType(required=True) - inventoryType = StringType(required=True) - serviceInstanceId = StringType() - cloudRegionId = StringType() + solution = ModelType(Candidates, required=True) assignmentInfo = ListType(ModelType(AssignmentInfo)) -class SolutionInfo(OSDFModel): - placement = ListType(ModelType(PlacementSolution), min_size=1) - license = ListType(ModelType(LicenseSolution), min_size=1) +class Solution(OSDFModel): + placementSolutions = ListType(ListType(ModelType(PlacementSolution), min_size=1)) + licenseSolutions = ListType(ModelType(LicenseSolution), min_size=1) class PlacementResponse(OSDFModel): transactionId = StringType(required=True) requestId = StringType(required=True) - requestState = StringType(required=True) - statusMessage = StringType(required=True) - solutionInfo = ModelType(SolutionInfo) + requestStatus = StringType(required=True) + statusMessage = StringType() + solutions = ModelType(Solution, required=True) 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 diff --git a/osdf/utils/interfaces.py b/osdf/utils/interfaces.py index 7a0e3a9..a869d6d 100644 --- a/osdf/utils/interfaces.py +++ b/osdf/utils/interfaces.py @@ -16,7 +16,9 @@ # ------------------------------------------------------------------------- # +import json import requests +import yaml from osdf.config.base import osdf_config, creds_prefixes from osdf.logging.osdf_logging import MH, debug_log @@ -35,6 +37,18 @@ def get_rest_client(request_json, service): return RestClient(url=callback_url, userid=c_userid, passwd=c_passwd) +def json_from_file(file_name): + """Read a policy from a file""" + with open(file_name) as fid: + return json.load(fid) + + +def yaml_from_file(file_name): + """Read a policy from a file""" + with open(file_name) as fid: + return yaml.load(fid) + + class RestClient(object): """Simple REST Client that supports get/post and basic auth""" diff --git a/osdfapp.py b/osdfapp.py index a2af0bf..6854061 100755 --- a/osdfapp.py +++ b/osdfapp.py @@ -43,44 +43,46 @@ from osdf.operation.exceptions import BusinessException from osdf.operation.error_handling import request_exception_to_json_body, internal_error_message from requests import RequestException from schematics.exceptions import DataError -from osdf.logging.osdf_logging import MH, audit_log, error_log +from osdf.logging.osdf_logging import MH, audit_log, error_log, debug_log from osdf.models.api.placementRequest import PlacementAPI ERROR_TEMPLATE = osdf.ERROR_TEMPLATE app = Flask(__name__) - - BAD_CLIENT_REQUEST_MESSAGE = 'Client sent an invalid request' -# An exception explicitly raised due to some business rule + @app.errorhandler(BusinessException) def handle_business_exception(e): + """An exception explicitly raised due to some business rule""" error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) err_msg = ERROR_TEMPLATE.render(description=str(e)) response = Response(err_msg, content_type='application/json; charset=utf-8') response.status_code = 400 return response -# Returns a detailed synchronous message to the calling client when osdf fails due to a remote call to another system + @app.errorhandler(RequestException) def handle_request_exception(e): + """Returns a detailed synchronous message to the calling client + when osdf fails due to a remote call to another system""" error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) err_msg = request_exception_to_json_body(e) response = Response(err_msg, content_type='application/json; charset=utf-8') response.status_code = 400 return response -# Returns a detailed message to the calling client when the initial synchronous message is invalid + @app.errorhandler(DataError) def handle_data_error(e): + """Returns a detailed message to the calling client when the initial synchronous message is invalid""" error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) body_dictionary = { "serviceException": { "text": BAD_CLIENT_REQUEST_MESSAGE, - "exceptionMessage": str(e.messages), + "exceptionMessage": str(e.errors), "errorType": "InvalidClientRequest" } } @@ -91,7 +93,7 @@ def handle_data_error(e): return response -@app.route("/osdf/api/v2/placement", methods=["POST"]) +@app.route("/api/oof/v1/placement", methods=["POST"]) @auth_basic.login_required def do_placement_opt(): """Perform placement optimization after validating the request and fetching policies @@ -106,7 +108,7 @@ def do_placement_opt(): PlacementAPI(request_json).validate() # Currently policies are being used only during placement, so only fetch them if placement demands is not empty - policies = {} + policies, prov_status = {}, None if 'placementDemand' in request_json['placementInfo']['demandInfo']: policies, prov_status = get_policies(request_json, "placement") @@ -119,9 +121,9 @@ def do_placement_opt(): req_id=req_id, text="Accepted placement request. Response will be posted to callback URL") -# Returned when unexpected coding errors occur during initial synchronous processing @app.errorhandler(500) def internal_failure(error): + """Returned when unexpected coding errors occur during initial synchronous processing""" error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) response = Response(internal_error_message, content_type='application/json; charset=utf-8') response.status_code = 500 @@ -129,19 +131,20 @@ def internal_failure(error): def get_options(argv): - program_version_string = '%%prog %s' % ("v1.0") + program_version_string = '%%prog %s' % "v1.0" program_longdesc = "" program_license = "" parser = OptionParser(version=program_version_string, epilog=program_longdesc, description=program_license) parser.add_option("-l", "--local", dest="local", help="run locally", action="store_true", default=False) - parser.add_option("-t", "--devtest", dest="devtest", help="run in dev/test environment", action="store_true", default=False) + parser.add_option("-t", "--devtest", dest="devtest", help="run in dev/test environment", action="store_true", + default=False) parser.add_option("-d", "--debughost", dest="debughost", help="IP Address of host running debug server", default='') parser.add_option("-p", "--debugport", dest="debugport", help="Port number of debug server", type=int, default=5678) - (opts, args) = parser.parse_args(argv) + opts, args = parser.parse_args(argv) - if (opts.debughost != ''): - print('pydevd.settrace(%s, port=%s)' % (opts.debughost, opts.debugport)) + if opts.debughost: + debug_log.debug('pydevd.settrace({}, port={})'.format(opts.debughost, opts.debugport)) pydevd.settrace(opts.debughost, port=opts.debugport) return opts @@ -151,14 +154,17 @@ if __name__ == "__main__": sys_conf = osdf_config['core']['osdf_system'] ports = sys_conf['osdf_ports'] internal_port, external_port = ports['internal'], ports['external'] - ssl_context = tuple(sys_conf['ssl_context']) - local_host = "0.0.0.0" # NOSONAR + local_host = sys_conf['osdf_ip_default'] common_app_opts = dict(host=local_host, threaded=True, use_reloader=False) + ssl_opts = sys_conf.get('ssl_context') + if ssl_opts: + common_app_opts.update({'ssl_context': tuple(ssl_opts)}) + opts = get_options(sys.argv) - if (not opts.local and not opts.devtest): # normal deployment - app.run(port=internal_port, ssl_context=ssl_context, debug=False, **common_app_opts) + if not opts.local and not opts.devtest: # normal deployment + app.run(port=internal_port, debug=False, **common_app_opts) else: port = internal_port if opts.local else external_port app.run(port=port, debug=True, **common_app_opts) diff --git a/test/conductor/test_conductor_translation.py b/test/conductor/test_conductor_translation.py new file mode 100644 index 0000000..b277b6a --- /dev/null +++ b/test/conductor/test_conductor_translation.py @@ -0,0 +1,53 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2017-2018 AT&T Intellectual Property +# +# 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. +# +# ------------------------------------------------------------------------- +# +import mock +import unittest + +from flask import Response +from mock import patch +from osdf.adapters.local_data import local_policies +from osdf.optimizers.placementopt.conductor import translation as tr +from osdf.utils.interfaces import json_from_file, yaml_from_file + + +class TestConductorTranslation(unittest.TestCase): + + def setUp(self): + main_dir = "" + conductor_api_template = main_dir + "osdf/templates/conductor_interface.json" + parameter_data_file = main_dir + "test/placement-tests/request.json" + policy_data_path = main_dir + "test/policy-local-files/" + local_config_file = main_dir + "config/common_config.yaml" + + valid_policies_list_file = policy_data_path + '/' + 'meta-valid-policies.txt' + valid_policies_files = local_policies.get_policy_names_from_file(valid_policies_list_file) + + self.request_json = json_from_file(parameter_data_file) + self.policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files] + + def tearDown(self): + pass + + def test_gen_demands(self): + res = tr.gen_demands(self.request_json, self.policies) + assert res is not None + + +if __name__ == "__main__": + unittest.main() + diff --git a/test/config/common_config.yaml b/test/config/common_config.yaml new file mode 100644 index 0000000..1c041d9 --- /dev/null +++ b/test/config/common_config.yaml @@ -0,0 +1,43 @@ +osdf_system: + libpath: /opt/app/osdf/libs + osdf_ports: + internal: 24699 # inside the Docker container, the app listens to this port + external: 14699 # clients use this port on DockerHost + osdf_ip_default: 0.0.0.0 +# # Important Note: At deployment time, we need to ensure the port mapping is done +# ssl_context: ['./../etc/sniromanager.crt', './../etc/sniromanager.key'] + +osdf_temp: # special configuration required for "workarounds" or testing + local_policies: + global_disabled: False + local_placement_policies_enabled: True + placement_policy_files_vcpe: # workaroud for policy platform glitches (or "work-arounds" for other components) + - Affinity_vCPE_1.json + - Capacity_vGMuxInfra.json + - Capacity_vG_1.json + - Distance_vGMuxInfra_1.json + - Distance_vG_1.json + - Min_Guarantee_vGMuxInfra_1.json + - Placement_Optimization_1.json + - QueryPolicy_vCPE.json + - hpa_policy_vGMuxInfra_1.json + - hpa_policy_vG_1.json + - vnfPolicy_vG.json + - vnfPolicy_vGMuxInfra.json +service_info: + vCPE: + vcpeHostName: requestParameters.vcpeHostName + e2eVpnKey: requestParameters.e2eVpnKey + +policy_info: + placement: + policy_fetch: by_scope + policy_scope: + default_scope: OSDF_R2 + scope_vcpe: OSDF_R2 + service_name: placementInfo.serviceModelInfo.modelName + policy_subscriber: SubscriberPolicy + subscriber_name: placementInfo.subscriberInfo.subscriberName + default: # if no explicit service related information is needed + policy_fetch: by_name + policy_scope: none diff --git a/test/config/has_config.yaml b/test/config/has_config.yaml new file mode 100644 index 0000000..8cbeda2 --- /dev/null +++ b/test/config/has_config.yaml @@ -0,0 +1,24 @@ +policy_config_mapping: + attributes: + hypervisor: hypervisor, + cloud_version: cloudVersion, + cloud_type: cloudType, + dataplane: dataPlane, + network_roles: networkRoles, + complex: complex, + state: state, + country: country, + geo_region: geoRegion, + exclusivity_groups: exclusivityGroups, + replication_role: replicationRole, + remapping: + model-invariant-id: modelInvariantId, + model-version-id: modelVersionId + candidates: + # for (k1, v1), if k1 is in demand, set prop[k2] = _get_candidates(demand[k1]) + exclusionCandidateInfo: excluded_candidates, + requiredCandidateInfo: required_candidates + extra_fields: + # we have [k1, k2, k3, k4] type items and x is policy-content-properties + # if x[k1] == k2: set prop[k3] = k4 + - [inventoryType, cloud, region, {get_param: CHOSEN_REGION}] \ No newline at end of file diff --git a/test/config/osdf_config.yaml b/test/config/osdf_config.yaml new file mode 100755 index 0000000..69ebdf0 --- /dev/null +++ b/test/config/osdf_config.yaml @@ -0,0 +1,34 @@ +osdfUserNameForSO: "" # The OSDF Manager username for MSO. +odfPasswordForSO: "" # The OSDF Manager password for MSO. + +# msoUrl: "" # The SO url for call back. This will be part of the request, so no need +soUsername: "" # SO username for call back. +soPassword: "" # SO password for call back. + +conductorUrl: "https://OOF-HAS-CONDUCTOR-HOST:8091" +conductorUsername: "CONDUCTOR-USER" +conductorPassword: "CONDUCTOR-PASSWD" +conductorPingWaitTime: 60 # seconds to wait before calling the conductor retry URL +conductorMaxRetries: 30 # if we don't get something in 30 minutes, give up + +# Policy Platform -- requires ClientAuth, Authorization, and Environment +policyPlatformUrl: https://POLICY-URL:8081/pdp/getConfig # Policy Dev platform URL +policyPlatformEnv: TEST # Environment for policy platform +policyPlatformUsername: POLICY-USER # Policy platform username. +policyPlatformPassword: POLICY-PASSWD # Policy platform password. +policyClientUsername: POLICY-CLIENT-USER # For use with ClientAuth +policyClientPassword: POLICY-CLIENT-PASSWD # For use with ClientAuth + +messageReaderHosts: https://DMAAP-HOST1:3905,https://DMAAP-HOST2:3905,https://DMAAP-HOST3:3905 +messageReaderTopic: org.onap.oof.osdf.multicloud +messageReaderAafUserId: DMAAP-OSDF-MC-USER +messageReaderAafPassword: DMAAP-OSDF-MC-PASSWD + +sdcUrl: https://SDC-HOST:8443/sdc/v1/catalog +sdcUsername: SDC-OSDF-USER +sdcPassword: SDC-OSDF-PASSWD +sdcONAPInstanceID: ONAP-OSDF + +osdfPlacementUrl: "http://127.0.0.1:24699/osdf/api/v2/placement" +osdfPlacementUsername: "test" +osdfPlacementPassword: "testpwd" diff --git a/test/local_data/test_local_policies.py b/test/local_data/test_local_policies.py index 120b186..13154c5 100644 --- a/test/local_data/test_local_policies.py +++ b/test/local_data/test_local_policies.py @@ -15,9 +15,8 @@ # # ------------------------------------------------------------------------- # +import re import unittest -import json -import yaml from osdf.adapters.local_data import local_policies @@ -27,9 +26,12 @@ class TestLocalPolicies(unittest.TestCase): def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) self.folder = './test/policy-local-files' - self.invalid_policies = ['INVALID-one.json', 'INVALID-two.json'] - self.valid_policies = ['CloudAttributePolicy_vG_1.json', 'CloudAttributePolicy_vGMuxInfra_1.json'] - + self.valid_policies_file = self.folder + '/' + 'meta-valid-policies.txt' + self.invalid_policies_file = self.folder + '/' + 'meta-invalid-policies.txt' + self.valid_policies = local_policies.get_policy_names_from_file(self.valid_policies_file) + self.invalid_policies = local_policies.get_policy_names_from_file(self.invalid_policies_file) + + def test_get_local_policies_no_policies(self): with self.assertRaises(FileNotFoundError): res = local_policies.get_local_policies(self.folder, self.invalid_policies) diff --git a/test/operation/test_responses.py b/test/operation/test_responses.py index 253cb7c..2e089ec 100644 --- a/test/operation/test_responses.py +++ b/test/operation/test_responses.py @@ -28,7 +28,20 @@ class TestLocalPolicies(unittest.TestCase): super(self.__class__, self).__init__(*args, **kwargs) self.folder = './test/policy-local-files' self.invalid_policies = ['INVALID-one.json', 'INVALID-two.json'] - self.valid_policies = ['CloudAttributePolicy_vG_1.json', 'CloudAttributePolicy_vGMuxInfra_1.json'] + self.valid_policies = [ + 'Affinity_vCPE_1.json', + 'Capacity_vG_1.json', + 'Distance_vG_1.json', + 'Placement_Optimization_1.json', + 'hpa_policy_vGMuxInfra_1.json', + 'vnfPolicy_vG.json', + 'Capacity_vGMuxInfra.json', + 'Distance_vGMuxInfra_1.json', + 'Min_Guarantee_vGMuxInfra_1.json', + 'QueryPolicy_vCPE.json', + 'hpa_policy_vG_1.json', + 'vnfPolicy_vGMuxInfra.json' + ] def test_get_local_policies_no_policies(self): with self.assertRaises(FileNotFoundError): diff --git a/test/placement-tests/request.json b/test/placement-tests/request.json index 2fd425c..c4cb31f 100644 --- a/test/placement-tests/request.json +++ b/test/placement-tests/request.json @@ -1,87 +1,101 @@ -{ - "requestInfo": { - "transactionId": "xxx-xxx-xxxx", - "requestId": "yyy-yyy-yyyy", - "callbackUrl": "https://test.url.com:5000/callback/", - "sourceId": "so", - "optimizers": ["placement"], - "numSolutions": 1, - "timeout": 600 - }, - "placementInfo": { - "serviceModelInfo": { - "modelType": "service", - "modelInvariantId": "fad5f4d5-1c94-4890-927d-9cec6e82997f", - "modelVersionId": "6e13c5e1-f172-436c-9cc4-0d64c94eb7f4", - "modelName": "vCPE", - "modelVersion": "1.0" - }, - "subscriberInfo": { - "globalSubscriberId": "SUB12_0325_UD_0833", - "subscriberName": "SUB_12_0325_UD_0833", - "subscriberCommonSiteId": "DALTX0101" - }, - "demandInfo": { - "placementDemand": [{ - "resourceInstanceType": "allotted", - "serviceResourceId": "61d563e8-e714-4393-8f99-cc480144a05e", - "resourceModuleName": "vGMuxInfra", - "exclusionCandidateInfo": [{ - "candidateType": "cloud", - "candidates": ["MDT54NJ", "BDM78NJ"] - }, { - "candidateType": "service", - "candidates": ["RT76U8F789", "PO098HJG"] - } - ], - "requiredCandidateInfo": [{ - "candidateType": "cloud", - "candidates": ["DHU87NY"] - }, { - "candidateType": "service", - "candidates": ["YHT675YH"] - } - ], - "resourceModelInfo": { - "modelCustomizationId": "", - "modelInvariantId": "h59988ce-3d81-4e07-81b5-53d3aa821134", - "modelName": "", - "modelVersion": "2.0", - "modelVersionId": "51d563e8-e714-4393-8f99-cc480144a05e", - "modelType": "allotted" - }, - "tenantId": "", - "tenantName": "" - }, { - "resourceInstanceType": "allotted", - "serviceResourceId": "71d563e8-e714-4393-8f99-cc480144a05e", - "resourceModuleName": "vG", - "resourceModelInfo": { - "modelCustomizationId": "", - "modelInvariantId": "a59988ce-3d81-4e07-81b5-53d3aa821134", - "modelName": "", - "modelVersion": "2.0", - "modelVersionId": "91d563e8-e714-4393-8f99-cc480144a05e", - "modelType": "allotted" - }, - "tenantId": "", - "tenantName": "" - } - ], - "licenseDemand": [] - }, - "policyId": [ - "" - ], - "serviceInstanceId": "1234-fsdf-23sdf-24kjnk", - "requestParameters": { - - "commonSiteId": "DALTX0101", - "vendorName": "xyz", - "e2eVpnKey": "200", - "vcpeHostName": "USOSTCDALTX0101UJZZ11" - - } - - } +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestId": "yyy-yyy-yyyy", + "callbackUrl": "https://wiki.onap.org:5000/callbackUrl/", + "sourceId": "SO", + "requestType": "create", + "numSolutions": 1, + "optimizers": ["placement"], + "timeout": 600 + }, + "placementInfo": { + "requestParameters": { "customerLatitude": 32.89748, "customerLongitude": -97.040443, "customerName": "xyz" }, + "placementDemands": [ + { + "resourceModuleName": "vGMuxInfra", + "serviceResourceId": "vGMuxInfra-xx", + "tenantId": "vGMuxInfra-tenant", + "resourceModelInfo": { + "modelInvariantId": "vGMuxInfra-modelInvariantId", + "modelVersionId": "vGMuxInfra-versionId", + "modelName": "vGMuxInfra-model", + "modelType": "resource", + "modelVersion": "1.0", + "modelCustomizationName": "vGMuxInfra-customeModelName" + } + }, + { + "resourceModuleName": "vG", + "serviceResourceId": "71d563e8-e714-4393-8f99-cc480144a05e", + "tenantId": "vG-tenant", + "resourceModelInfo": { + "modelInvariantId": "vG-modelInvariantId", + "modelVersionId": "vG-versionId", + "modelName": "vG-model", + "modelType": "resource", + "modelVersion": "1.0", + "modelCustomizationName": "vG-customeModelName" + }, + "existingCandidates": [ + { + "identifierType": "serviceInstanceId", + "cloudOwner": "", + "identifiers": ["gjhd-098-fhd-987"] + } + ], + "excludedCandidates": [ + { + "identifierType": "serviceInstanceId", + "cloudOwner": "", + "identifiers": ["gjhd-098-fhd-987"] + }, + { + "identifierType": "vimId", + "cloudOwner": "vmware", + "identifiers": ["NYMDT67"] + } + ], + "requiredCandidates": [ + { + "identifierType": "vimId", + "cloudOwner": "amazon", + "identifiers": ["TXAUS219"] + } + ] + } + ] + }, + "serviceInfo": { + "serviceInstanceId": "d61b2543-5914-4b8f-8e81-81e38575b8ec", + "serviceName": "vCPE", + "modelInfo": { + "modelInvariantId": "vCPE-invariantId", + "modelVersionId": "vCPE-versionId", + "modelName": "vCPE-model", + "modelType": "service", + "modelVersion": "1.0", + "modelCustomizationName": "vCPE-customeModelName" + } + }, + "licenseInfo": { + "licenseDemands": [ + { + "resourceModuleName": "vGMuxInfra", + "serviceResourceId": "vGMuxInfra-xx", + "resourceModelInfo": { + "modelInvariantId": "vGMuxInfra-modelInvariantId", + "modelVersionId": "vGMuxInfra-versionId", + "modelName": "vGMuxInfra-model", + "modelType": "resource", + "modelVersion": "1.0", + "modelCustomizationName": "vGMuxInfra-customeModelName" + }, + "existingLicenses": { + "entitlementPoolUUID": ["87257b49-9602-4ca1-9817-094e52bc873b", "43257b49-9602-4fe5-9337-094e52bc9435"], + "licenseKeyGroupUUID": ["87257b49-9602-4ca1-9817-094e52bc873b", "43257b49-9602-4fe5-9337-094e52bc9435"] + } + } + ] + } } \ No newline at end of file diff --git a/test/placement-tests/response.json b/test/placement-tests/response.json new file mode 100644 index 0000000..f6c1bea --- /dev/null +++ b/test/placement-tests/response.json @@ -0,0 +1,49 @@ +{ + "transactionId": "xxx-xxx-xxxx", + "requestId": "yyy-yyy-yyyy", + "requestStatus": "completed", + "statusMessage": "Success!", + "solutions": { + "placementSolutions": [ + [ + { + "resourceModuleName": "vGMuxInfra", + "serviceResourceId": "someResourceId", + "solution": { + "identifierType": "serviceInstanceId", + "identifiers": ["gjhd-098-fhd-987"] + }, + "assignmentInfo": [ + { "key": "cloudOwner", "value": "amazon" }, + { "key": "vnfHostName", "value": "ahr344gh" }, + { "key": "isRehome", "value": "False" }, + { "key": "cloudRegionId", "value": "1ac71fb8-ad43-4e16-9459-c3f372b8236d" } + ] + }, + { + "resourceModuleName": "vG", + "serviceResourceId": "someResourceId", + "solution": { + "identifierType": "cloudRegionId", + "cloudOwner": "amazon", + "identifiers": ["gjhd-098-fhd-987"] + }, + "assignmentInfo": [ + { "key": "cloudOwner", "value": "amazon" }, + { "key": "cloudRegionId", "value": "1ac71fb8-ad43-4e16-9459-c3f372b8236d" } + ] + } + ] + ], + "licenseSolutions": [ + { + "resourceModuleName": "vGMuxInfra", + "serviceResourceId": "someResourceId", + "entitlementPoolUUID": ["1ac71fb8-ad43-4e16-9459-c3f372b8236d", "834fc71fb8-ad43-4fh7-9459-c3f372b8236f"], + "licenseKeyGroupUUID": ["1ac71fb8-ad43-4e16-9459-c3f372b8236d", "834fc71fb8-ad43-4fh7-9459-c3f372b8236f"], + "entitlementPoolInvariantUUID": ["1ac71fb8-ad43-4e16-9459-c3f372b8236d", "834fc71fb8-ad43-4fh7-9459-c3f372b8236f"], + "licenseKeyGroupInvariantUUID": ["1ac71fb8-ad43-4e16-9459-c3f372b8236d", "834fc71fb8-ad43-4fh7-9459-c3f372b8236f"] + } + ] + } +} \ No newline at end of file diff --git a/test/policy-local-files/Affinity_vCPE_1.json b/test/policy-local-files/Affinity_vCPE_1.json new file mode 100644 index 0000000..c42e9d8 --- /dev/null +++ b/test/policy-local-files/Affinity_vCPE_1.json @@ -0,0 +1,21 @@ +{ + "service": "affinityPolicy", + "policyName": "oofBeijing.affinityPolicy_vcpe", + "description": "Affinity policy for vCPE", + "templateVersion": "1702.03", + "version": "oofBeijing", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "affinity_vCPE", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "affinityProperty": { + "qualifier": "different", + "category": "complex" + }, + "policyType": "affinityPolicy", + "resources": ["vGMuxInfra", "vG"] + } +} diff --git a/test/policy-local-files/Capacity_vGMuxInfra.json b/test/policy-local-files/Capacity_vGMuxInfra.json new file mode 100644 index 0000000..9eec54d --- /dev/null +++ b/test/policy-local-files/Capacity_vGMuxInfra.json @@ -0,0 +1,21 @@ +{ + "service": "capacityPolicy", + "policyName": "oofBeijing.capacityPolicy_vGMuxInfra", + "description": "Capacity policy for vGMuxInfra", + "templateVersion": "1702.03", + "version": "oofBeijing", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "capacity_vGMuxInfra", + "policyScope": ["VCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], + "resources": ["vGMuxInfra"], + "capacityProperty": { + "cpu": {"value": 2, "operator": ">"}, + "memory": {"value": 4, "operator": ">", "unit": "Gb"} + }, + "policyType": "capacityPolicy" + } +} diff --git a/test/policy-local-files/Capacity_vG_1.json b/test/policy-local-files/Capacity_vG_1.json new file mode 100644 index 0000000..d5644a8 --- /dev/null +++ b/test/policy-local-files/Capacity_vG_1.json @@ -0,0 +1,21 @@ +{ + "service": "capacityPolicy", + "policyName": "oofBeijing.capacityPolicy_vGMuxInfra", + "description": "Capacity policy for vGMuxInfra", + "templateVersion": "1702.03", + "version": "oofBeijing", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "capacity_vGMuxInfra", + "policyScope": ["VCPE", "US", "INTERNATIONAL", "ip", "vG"], + "resources": ["vG"], + "capacityProperty": { + "cpu": {"value": 2, "operator": ">"}, + "memory": {"value": 4, "operator": ">", "unit": "Gb"} + }, + "policyType": "capacityPolicy" + } +} diff --git a/test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json b/test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json deleted file mode 100644 index 57c0039..0000000 --- a/test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "service": "CloudAttributePolicy", - "policyName": "CloudAttributePolicy_vGMuxInfra", - "description": "Attribute policy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "3", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vGMuxInfra_cloud_attributes", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vGMuxInfra"] - }, - "cloudAttributeProperty": { - "networkRoles": { - "all": [ - "vGMuxInfra.OAM", - "vGMuxInfra.SR_IOV_Provider2_1", - "vGMuxInfra.SR_IOV_Provider2_2" - ] - }, - "complex": { - "any": [ ] - } - }, - "type": "attribute", - "resourceInstanceType": ["vGMuxInfra"] - } -} diff --git a/test/policy-local-files/CloudAttributePolicy_vG_1.json b/test/policy-local-files/CloudAttributePolicy_vG_1.json deleted file mode 100644 index cbe2a88..0000000 --- a/test/policy-local-files/CloudAttributePolicy_vG_1.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "service": "CloudAttributePolicy", - "policyName": "cloud AttributePolicy_vG", - "description": "Attribute policy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "10", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vG_cloud_attributes", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vG"] - }, - "cloudAttributeProperty": { - "networkRoles": { - "all": [ - "vG.OAM", - "vG.SR_IOV_Provider2_1", - "vG.SR_IOV_Provider2_2" - ] - }, - "complex": { - "any": [ ] - } - }, - "type": "attribute", - "resourceInstanceType": ["vG"] - } -} diff --git a/test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json b/test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json deleted file mode 100644 index 414c167..0000000 --- a/test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "service": "DistanceToLocationPolicy", - "policyName": "DistanceToLocationPolicy_vGMuxInfra", - "description": "DistanceToLocationPolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "distanceToLocationProperty": { - "locationInfo": "customer_loc", - "distanceCondition": { - "parameter": "distance", - "value": "50000 km", - "operator": "less" - } - }, - "identity": "distance-vGMuxInfra", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vGMuxInfra"] - }, - "type": "distance_to_location", - "resourceInstanceType": ["vGMuxInfra"] - } -} diff --git a/test/policy-local-files/DistanceToLocationPolicy_vG_1.json b/test/policy-local-files/DistanceToLocationPolicy_vG_1.json deleted file mode 100644 index 737ee19..0000000 --- a/test/policy-local-files/DistanceToLocationPolicy_vG_1.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "service": "DistanceToLocationPolicy", - "policyName": "DistanceToLocationPolicy_vG", - "description": "DistanceToLocationPolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "distanceToLocationProperty": { - "locationInfo": "customer_loc", - "distanceCondition": { - "parameter": "distance", - "value": "50000 km", - "operator": "less" - } - }, - "identity": "distance-vG", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vG"] - }, - "type": "distance_to_location", - "resourceInstanceType": ["vG"] - } -} diff --git a/test/policy-local-files/Distance_vGMuxInfra_1.json b/test/policy-local-files/Distance_vGMuxInfra_1.json new file mode 100644 index 0000000..a835ef1 --- /dev/null +++ b/test/policy-local-files/Distance_vGMuxInfra_1.json @@ -0,0 +1,21 @@ +{ + "service": "distancePolicy", + "policyName": "oofBeijing.distancePolicy_vGMuxInfra", + "description": "Distance Policy for vGMuxInfra", + "templateVersion": "0.0.1", + "version": "oofBeijing", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "distanceProperties": { + "locationInfo": "customer_location", + "distance": { "value": "500", "operator": "<", "unit": "km" } + }, + "identity": "distance-vGMuxInfra", + "resources": ["vGMuxInfra"], + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], + "policyType": "distancePolicy" + } +} diff --git a/test/policy-local-files/Distance_vG_1.json b/test/policy-local-files/Distance_vG_1.json new file mode 100644 index 0000000..1af021a --- /dev/null +++ b/test/policy-local-files/Distance_vG_1.json @@ -0,0 +1,21 @@ +{ + "service": "distancePolicy", + "policyName": "oofBeijing.distancePolicy_vG", + "description": "Distance Policy for vG", + "templateVersion": "0.0.1", + "version": "oofBeijing", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "distanceProperties": { + "locationInfo": "customer_location", + "distance": { "value": "1500", "operator": "<", "unit": "km" } + }, + "identity": "distance-vG", + "resources": ["vG"], + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], + "policyType": "distancePolicy" + } +} diff --git a/test/policy-local-files/INVALID-policies/INVALID-Affinity_vCPE_1.json b/test/policy-local-files/INVALID-policies/INVALID-Affinity_vCPE_1.json new file mode 100644 index 0000000..86670c3 --- /dev/null +++ b/test/policy-local-files/INVALID-policies/INVALID-Affinity_vCPE_1.json @@ -0,0 +1,21 @@ +{ + "service": "affinityPolicy", + "MISSINGpolicyName": "oofBeijing.affinityPolicy_vcpe", + "description": "Affinity policy for vCPE", + "templateVersion": "1702.03", + "version": "oofBeijing", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "affinity_vCPE", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "affinityProperty": { + "qualifier": "different", + "category": "complex" + }, + "policyType": "affinityPolicy", + "resources": ["vGMuxInfra", "vG"] + } +} diff --git a/test/policy-local-files/InventoryGroup_vGMuxInfra_1.json b/test/policy-local-files/InventoryGroup_vGMuxInfra_1.json deleted file mode 100644 index 20ff7f7..0000000 --- a/test/policy-local-files/InventoryGroup_vGMuxInfra_1.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "service": "InventoryGroupPolicy", - "policyName": "InventoryGroupPolicy_vGMuxInfra", - "description": "InventoryGroupPolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "6", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vGMuxInfra-pri-sec-2", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vGMuxInfra"] - }, - "type": "inventory_group", - "resourceInstanceType": ["vGMuxInfra"] - } -} diff --git a/test/policy-local-files/InventoryGroup_vG_1.json b/test/policy-local-files/InventoryGroup_vG_1.json deleted file mode 100644 index 99ae309..0000000 --- a/test/policy-local-files/InventoryGroup_vG_1.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "service": "InventoryGroupPolicy", - "policyName": "InventoryGroupPolicy_vG", - "description": "InventoryGroupPolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "6", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vG-pri-sec-1", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vG"] - }, - "type": "inventory_group", - "resourceInstanceType": ["vG"] - } -} diff --git a/test/policy-local-files/Min_Guarantee_vGMuxInfra_1.json b/test/policy-local-files/Min_Guarantee_vGMuxInfra_1.json new file mode 100644 index 0000000..654c548 --- /dev/null +++ b/test/policy-local-files/Min_Guarantee_vGMuxInfra_1.json @@ -0,0 +1,21 @@ +{ + "service": "minGuaranteePolicy", + "policyName": "oofBeijing.minGuaranee_vGMuxInfra", + "description": "Min guarantee policy for vGMuxInfra", + "templateVersion": "1702.03", + "version": "oofBeijing", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "minGuarantee_vGMuxInfra", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], + "minGuaranteeProperty": { + "cpu": {"value": "true", "operator": "="}, + "memory": {"value": "false", "operator": "="} + }, + "policyType": "minGuaraneePolicy", + "resourceInstanceType": ["vGMuxInfra"] + } +} diff --git a/test/policy-local-files/PlacementOptimizationPolicy.json b/test/policy-local-files/PlacementOptimizationPolicy.json deleted file mode 100644 index 7c43435..0000000 --- a/test/policy-local-files/PlacementOptimizationPolicy.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "service": "PlacementOptimizationPolicy", - "policyName": "PlacementOptimizationPolicy", - "description": "PlacementOptimizationPolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "5", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "objectiveParameter": { - "parameterAttributes": [ - { - "resource": ["vGMuxInfra"], - "customerLocationInfo": "customer_loc", - "parameter": "distance", - "weight": "2", - "operator": "product" - }, - { - "resource": ["vG"], - "customerLocationInfo": "customer_loc", - "parameter": "distance", - "weight": "1", - "operator": "product" - } - ], - "operator": "sum" - }, - "identity": "optimization", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vGMuxInfra","vG"] - }, - "type": "placementOptimization", - "objective": "minimize" - } -} diff --git a/test/policy-local-files/Placement_Optimization_1.json b/test/policy-local-files/Placement_Optimization_1.json new file mode 100644 index 0000000..5748c99 --- /dev/null +++ b/test/policy-local-files/Placement_Optimization_1.json @@ -0,0 +1,36 @@ +{ + "service": "PlacementOptimizationPolicy", + "policyName": "oofBeijing.PlacementOptimizationPolicy_vGMuxInfra", + "description": "Placement Optimization Policy for vGMuxInfra", + "templateVersion": "1702.03", + "version": "oofBeijing", + "priority": "5", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "objectiveParameter": { + "parameterAttributes": [ + { + "resource": ["vGMuxInfra"], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + }, + { + "resource": ["vG"], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + } + ], + "operator": "sum" + }, + "identity": "optimization", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "policyType": "placementOptimization", + "objective": "minimize" + } +} diff --git a/test/policy-local-files/QueryPolicy_vCPE.json b/test/policy-local-files/QueryPolicy_vCPE.json new file mode 100644 index 0000000..4ed83f9 --- /dev/null +++ b/test/policy-local-files/QueryPolicy_vCPE.json @@ -0,0 +1,20 @@ +{ + "service": "optimizationQueryPolicy", + "policyName": "oofBeijing.queryPolicy_vCPE", + "description": "Optimization query policy for vCPE", + "templateVersion": "0.0.1", + "version": "oofBeijing", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "queryProperties": [ + {"attribute":"locationId", "value": "orderInfo.customerLocation"}, + {"attribute":"id", "value": "orderInfo.vpnInfo"}, + {"attribute":"upstreamBW", "value": "orderInfo.vpnInfo"} + ], + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "type": "optimizationQueryPolicy" + } +} diff --git a/test/policy-local-files/ResourceInstancePolicy_vG_1.json b/test/policy-local-files/ResourceInstancePolicy_vG_1.json deleted file mode 100644 index 21ae0e4..0000000 --- a/test/policy-local-files/ResourceInstancePolicy_vG_1.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "service": "ResourceInstancePolicy", - "policyName": "ResourceInstancePolicy_vG", - "description": "ResourceInstancePolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "5", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vG-resourceInstance", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vG", "vGMuxInfra"] - }, - "resourceInstanceProperty": { - "request": "{\"test\": \"123\"}", - "controller": "SDN-C" - }, - "type": "instance_fit", - "resourceInstanceType": ["vG", "vGMuxInfra"] - } -} diff --git a/test/policy-local-files/VNFPolicy_vGMuxInfra_1.json b/test/policy-local-files/VNFPolicy_vGMuxInfra_1.json deleted file mode 100644 index b0963d6..0000000 --- a/test/policy-local-files/VNFPolicy_vGMuxInfra_1.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "service": "VNFPolicy", - "policyName": "VNFPolicy_vGMuxInfra", - "description": "VNFPolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "6", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vGMuxInfra-pri-sec-1", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vGMuxInfra"] - }, - "property": [ - { - "inventoryProvider": "aai", - "serviceType": "", - "inventoryType": "cloud", - "customerId": "" - }, - { - "inventoryProvider": "aai", - "serviceType": "vGMuxInfraaaS", - "inventoryType": "service", - "customerId": "21014aa2-526b-11e6-beb8-9e71128cae77" - } - ], - "type": "vnfPolicy", - "resourceInstanceType": ["vGMuxInfra"] - } -} diff --git a/test/policy-local-files/VNFPolicy_vG_1.json b/test/policy-local-files/VNFPolicy_vG_1.json deleted file mode 100644 index de0a158..0000000 --- a/test/policy-local-files/VNFPolicy_vG_1.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "service": "VNFPolicy", - "policyName": "VNFPolicy_vG", - "description": "VNFPolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "6", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vG-pri-sec-1", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vG"] - }, - "property": [ - { - "inventoryProvider": "aai", - "serviceType": "", - "inventoryType": "cloud", - "customerId": " " - }, - { - "inventoryProvider": "aai", - "serviceType": "vGaaS", - "inventoryType": "service", - "customerId": "21014aa2-526b-11e6-beb8-9e71128cae77" - } - ], - "type": "vnfPolicy", - "resourceInstanceType": ["vG"] - } -} diff --git a/test/policy-local-files/ZonePolicy_vGMuxInfra_1.json b/test/policy-local-files/ZonePolicy_vGMuxInfra_1.json deleted file mode 100644 index 9f941e4..0000000 --- a/test/policy-local-files/ZonePolicy_vGMuxInfra_1.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "service": "ZonePolicy", - "policyName": "ZonePolicy_vGMuxInfra", - "description": "ZonePolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "5", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "identity": "zone-vGMuxInfra", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vGMuxInfra"] - }, - "zoneProperty": { - "qualifier": "different", - "category": "complex" - }, - "type": "zone", - "resourceInstanceType": ["vGMuxInfra"] - } -} diff --git a/test/policy-local-files/ZonePolicy_vG_1.json b/test/policy-local-files/ZonePolicy_vG_1.json deleted file mode 100644 index 8104f6b..0000000 --- a/test/policy-local-files/ZonePolicy_vG_1.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "service": "ZonePolicy", - "policyName": "ZonePolicy_vG", - "description": "ZonePolicy", - "templateVersion": "1702.03", - "version": "1707", - "priority": "5", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "identity": "zone-vG", - "policyScope": { - "serviceType": ["vCPE"], - "geoRegion": ["US", "INTERNATIONAL"], - "networkType": ["ip"], - "resourceInstanceType": ["vG"] - }, - "zoneProperty": { - "qualifier": "different", - "category": "complex" - }, - "type": "zone", - "resourceInstanceType": ["vG"] - } -} diff --git a/test/policy-local-files/hpa_policy_vGMuxInfra_1.json b/test/policy-local-files/hpa_policy_vGMuxInfra_1.json new file mode 100644 index 0000000..bf09532 --- /dev/null +++ b/test/policy-local-files/hpa_policy_vGMuxInfra_1.json @@ -0,0 +1,144 @@ +{ + "service": "hpaPolicy", + "policyName": "oofBeijing.hpaPolicy_vGMuxInfra", + "description": "HPA policy for vGMuxInfra", + "templateVersion": "0.0.1", + "version": "1.0", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "resources": "vGMuxInfra", + "identity": "hpaPolicy_vGMuxInfra", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], + "policyType": "hpaPolicy", + "flavorFeatures": [ + { + "flavorLabel": "flavor_label_vm_01", + "flavorProperties":[ + { + "hpa_feature" : "cpuTopology", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute":"numCpuSockets", "values": "2","operator": ">=", "unit": ""}, + {"attribute":"numCpuSockets", "values": "4","operator": "<=", "unit": ""}, + {"attribute":"numCpuCores", "value": "2", "operator":">=", "unit": ""}, + {"attribute":"numCpuCores", "value": "4", "operator":"<=", "unit": ""}, + {"attribute":"numCpuThreads", "value": "4", "operator":">=", "unit": ""}, + {"attribute":"numCpuThreads", "value": "8", "operator":"<=", "unit": ""} + ] + }, + { + "hpa_feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "numVirtualCpu", "value": "6", "operator": "=", "unit": ""}, + {"attribute": "virtualMemSize", "value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa_feature" : "ovsDpdk", + "mandatory" : "False", + "score" : "3", + "architecture": "generic", + "featureAttributes": [ + {"attribute":"dataProcessingAccelerationLibrary", "value":"ovsDpdk_version", "operator": "=", "unit":""} + ] + }, + { + "hpa_feature" : "cpuInstructionSetExtensions", + "mandatory" : "True", + "architecture": "INTEL-64", + "featureAttributes": [ + {"attribute":"instructionSetExtensions", "value":["", ""], "operator": "ALL", "unit":""} + ] + } + ] + }, + { + "flavorLabel": "flavor_label_vm_02", + "flavorProperties":[ + { + "hpa_feature" : "cpuPinningy", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute":"logicalCpuThreadPinningPolicy", "value":"", "operator": "=", "unit":""}, + {"attribute":"logicalCpuPinningPolicy", "value": "","operator": "=", "unit":""} + ] + }, + { + "hpa_feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "numVirtualCpu", "value": "6", "operator": "=", "unit": ""}, + {"attribute": "virtualMemSize", "value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa_feature" : "localStorage", + "mandatory" : "False", + "score" : "5", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "diskSize", "value": "2", "operator": "=", "unit": "GB"}, + {"attribute": "ephemeralDiskSize", "value": "2", "operator": "=", "unit": "GB"}, + {"attribute": "swapMemSize", "value":"16", "operator": "=", "unit": "MB"} + ] + }, + { + "hpa_feature" : "pcie", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "pciCount", "value": "2", "operator": "=", "unit": ""}, + {"attribute": "pciVendorId", "value":"8086", "operator": "=", "unit": ""}, + {"attribute": "pciDeviceId", "value": "2", "operator": "=", "unit": ""}, + {"attribute": "functionType", "value": "","operator": "=", "unit": ""} + ] + } + ] + }, + { + "flavorLabel": "flavor_label_vm_03", + "flavorProperties":[ + { + "hpa_feature" : "numa", + "mandatory" : "False", + "score" : "5", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "numaNodes", "value": "2", "operator": "=", "unit": ""}, + {"attribute": "numaCpu-0", "values":"2", "operator": "=", "unit": ""}, + {"attribute": "numaMem-0", "value": "2048", "operator": "=", "unit": "MB"}, + {"attribute": "numaCpu-1", "values":"4", "operator": "=", "unit": ""}, + {"attribute": "numaMem-1", "value": "4096", "operator": "=", "unit": "MB"} + ] + }, + { + "hpa_feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "numVirtualCpu", "value": "6", "operator": "=", "unit": ""}, + {"attribute": "virtualMemSize", "value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa_feature" : "hugePages", + "mandatory" : "False", + "score" : "7", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "memoryPageSize", "value": "", "operator": "=", "unit": ""} + ] + } + ] + } + ] + } +} diff --git a/test/policy-local-files/hpa_policy_vG_1.json b/test/policy-local-files/hpa_policy_vG_1.json new file mode 100644 index 0000000..98d8cea --- /dev/null +++ b/test/policy-local-files/hpa_policy_vG_1.json @@ -0,0 +1,144 @@ +{ + "service": "hpaPolicy", + "policyName": "oofBeijing.hpaPolicy_vG", + "description": "HPA policy for vG", + "templateVersion": "0.0.1", + "version": "1.0", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "resources": "vG", + "identity": "hpaPolicy_vG", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], + "policyType": "hpaPolicy", + "flavorFeatures": [ + { + "flavorLabel": "flavor_label_vm_01", + "flavorProperties":[ + { + "hpa_feature" : "cpuTopology", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute":"numCpuSockets", "values": "2","operator": ">=", "unit": ""}, + {"attribute":"numCpuSockets", "values": "4","operator": "<=", "unit": ""}, + {"attribute":"numCpuCores", "value": "2", "operator":">=", "unit": ""}, + {"attribute":"numCpuCores", "value": "4", "operator":"<=", "unit": ""}, + {"attribute":"numCpuThreads", "value": "4", "operator":">=", "unit": ""}, + {"attribute":"numCpuThreads", "value": "8", "operator":"<=", "unit": ""} + ] + }, + { + "hpa_feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "numVirtualCpu", "value": "6", "operator": "=", "unit": ""}, + {"attribute": "virtualMemSize", "value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa_feature" : "ovsDpdk", + "mandatory" : "False", + "score" : "3", + "architecture": "generic", + "featureAttributes": [ + {"attribute":"dataProcessingAccelerationLibrary", "value":"ovsDpdk_version", "operator": "=", "unit":""} + ] + }, + { + "hpa_feature" : "cpuInstructionSetExtensions", + "mandatory" : "True", + "architecture": "INTEL-64", + "featureAttributes": [ + {"attribute":"instructionSetExtensions", "value":["", ""], "operator": "ALL", "unit":""} + ] + } + ] + }, + { + "flavorLabel": "flavor_label_vm_02", + "flavorProperties":[ + { + "hpa_feature" : "cpuPinningy", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute":"logicalCpuThreadPinningPolicy", "value":"", "operator": "=", "unit":""}, + {"attribute":"logicalCpuPinningPolicy", "value": "","operator": "=", "unit":""} + ] + }, + { + "hpa_feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "numVirtualCpu", "value": "6", "operator": "=", "unit": ""}, + {"attribute": "virtualMemSize", "value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa_feature" : "localStorage", + "mandatory" : "False", + "score" : "5", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "diskSize", "value": "2", "operator": "=", "unit": "GB"}, + {"attribute": "ephemeralDiskSize", "value": "2", "operator": "=", "unit": "GB"}, + {"attribute": "swapMemSize", "value":"16", "operator": "=", "unit": "MB"} + ] + }, + { + "hpa_feature" : "pcie", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "pciCount", "value": "2", "operator": "=", "unit": ""}, + {"attribute": "pciVendorId", "value":"8086", "operator": "=", "unit": ""}, + {"attribute": "pciDeviceId", "value": "2", "operator": "=", "unit": ""}, + {"attribute": "functionType", "value": "","operator": "=", "unit": ""} + ] + } + ] + }, + { + "flavorLabel": "flavor_label_vm_03", + "flavorProperties":[ + { + "hpa_feature" : "numa", + "mandatory" : "False", + "score" : "5", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "numaNodes", "value": "2", "operator": "=", "unit": ""}, + {"attribute": "numaCpu-0", "values":"2", "operator": "=", "unit": ""}, + {"attribute": "numaMem-0", "value": "2048", "operator": "=", "unit": "MB"}, + {"attribute": "numaCpu-1", "values":"4", "operator": "=", "unit": ""}, + {"attribute": "numaMem-1", "value": "4096", "operator": "=", "unit": "MB"} + ] + }, + { + "hpa_feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "numVirtualCpu", "value": "6", "operator": "=", "unit": ""}, + {"attribute": "virtualMemSize", "value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa_feature" : "hugePages", + "mandatory" : "False", + "score" : "7", + "architecture": "generic", + "featureAttributes": [ + {"attribute": "memoryPageSize", "value": "", "operator": "=", "unit": ""} + ] + } + ] + } + ] + } +} diff --git a/test/policy-local-files/meta-invalid-policies.txt b/test/policy-local-files/meta-invalid-policies.txt new file mode 100644 index 0000000..9d9ed9b --- /dev/null +++ b/test/policy-local-files/meta-invalid-policies.txt @@ -0,0 +1,4 @@ +INVALID-policies/NO-FILE-EXISTS.json # non-existent policy file + +# missing policy name +INVALID-policies/INVALID-Affinity_vCPE_1.json diff --git a/test/policy-local-files/meta-valid-policies.txt b/test/policy-local-files/meta-valid-policies.txt new file mode 100644 index 0000000..a0ce3fe --- /dev/null +++ b/test/policy-local-files/meta-valid-policies.txt @@ -0,0 +1,12 @@ +Affinity_vCPE_1.json +Capacity_vGMuxInfra.json +Capacity_vG_1.json +Distance_vGMuxInfra_1.json +Distance_vG_1.json +Min_Guarantee_vGMuxInfra_1.json +Placement_Optimization_1.json +QueryPolicy_vCPE.json +hpa_policy_vGMuxInfra_1.json +hpa_policy_vG_1.json +vnfPolicy_vG.json +vnfPolicy_vGMuxInfra.json diff --git a/test/policy-local-files/vnfPolicy_vG.json b/test/policy-local-files/vnfPolicy_vG.json new file mode 100644 index 0000000..ead038c --- /dev/null +++ b/test/policy-local-files/vnfPolicy_vG.json @@ -0,0 +1,31 @@ +{ + "service": "VnfPolicy", + "policyName": "oofBeijing.vnfPolicy_vG", + "description": "vnfPolicy", + "templateVersion": "1702.03", + "version": "oofBeijing", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vnf_vG", + "policyScope": ["vCPE", "INTERNATIONAL", "ip", "vG"], + "policyType": "vnf_policy", + "resources": ["vG"], + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "cloud", + "customerId": "" + }, + { + "inventoryProvider": "multicloud", + "serviceType": "HNGATEWAY", + "inventoryType": "service", + "customerId": "21014aa2-526b-11e6-beb8-9e71128cae77" + } + ] + } +} diff --git a/test/policy-local-files/vnfPolicy_vGMuxInfra.json b/test/policy-local-files/vnfPolicy_vGMuxInfra.json new file mode 100644 index 0000000..787563b --- /dev/null +++ b/test/policy-local-files/vnfPolicy_vGMuxInfra.json @@ -0,0 +1,31 @@ +{ + "service": "VnfPolicy", + "policyName": "oofBeijing.vnfPolicy_vGMuxInfra", + "description": "vnfPolicy", + "templateVersion": "1702.03", + "version": "oofBeijing", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vnf_vGMuxInfra", + "policyScope": ["vCPE", "INTERNATIONAL", "ip", "vGMuxInfra"], + "policyType": "vnf_policy", + "resources": ["vGMuxInfra"], + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "cloud", + "customerId": "" + }, + { + "inventoryProvider": "multicloud", + "serviceType": "HNGATEWAY", + "inventoryType": "service", + "customerId": "21014aa2-526b-11e6-beb8-9e71128cae77" + } + ] + } +} diff --git a/test/policy/test_policy_interface.py b/test/policy/test_policy_interface.py new file mode 100644 index 0000000..5dc75c3 --- /dev/null +++ b/test/policy/test_policy_interface.py @@ -0,0 +1,64 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2017-2018 AT&T Intellectual Property +# +# 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. +# +# ------------------------------------------------------------------------- +# +import mock +import os +import unittest + +from osdf.adapters.local_data import local_policies +import osdf.config.loader as config_loader +from osdf.utils.interfaces import json_from_file +from osdf.utils.programming_utils import DotDict +from osdf.optimizers.placementopt.conductor import translation as tr +from osdf.adapters.policy import interface as pol + + +class TestPolicyInterface(unittest.TestCase): + + def setUp(self): + self.config_spec = { + "deployment": os.environ.get("OSDF_MANAGER_CONFIG_FILE", "config/osdf_config.yaml"), + "core": "config/common_config.yaml" + } + self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) + + main_dir = "" + conductor_api_template = main_dir + "osdf/templates/conductor_interface.json" + parameter_data_file = main_dir + "test/placement-tests/request.json" + policy_data_path = main_dir + "test/policy-local-files/" + local_config_file = main_dir + "config/common_config.yaml" + + valid_policies_list_file = policy_data_path + '/' + 'meta-valid-policies.txt' + self.valid_policies_files = local_policies.get_policy_names_from_file(valid_policies_list_file) + + self.request_json = json_from_file(parameter_data_file) + self.policies = [json_from_file(policy_data_path + '/' + name) for name in self.valid_policies_files] + + def tearDown(self): + pass + + def test_gen_demands(self): + res = tr.gen_demands(self.request_json, self.policies) + assert res is not None + + def test_get_by_name(self): + pol.get_by_name(mock.MagicMock(), self.valid_policies_files[0]) + + +if __name__ == "__main__": + unittest.main() + diff --git a/test/test_ConductorApiBuilder.py b/test/test_ConductorApiBuilder.py index f809c56..8395a47 100644 --- a/test/test_ConductorApiBuilder.py +++ b/test/test_ConductorApiBuilder.py @@ -18,33 +18,34 @@ import unittest import json import yaml + +from osdf.adapters.local_data import local_policies from osdf.optimizers.placementopt.conductor.api_builder import conductor_api_builder +from osdf.utils.interfaces import json_from_file class TestConductorApiBuilder(unittest.TestCase): + def setUp(self): + self.main_dir = "" + conductor_api_template = self.main_dir + "osdf/templates/conductor_interface.json" + parameter_data_file = self.main_dir + "test/placement-tests/request.json" + policy_data_path = self.main_dir + "test/policy-local-files/" + local_config_file = self.main_dir + "config/common_config.yaml" + + valid_policies_list_file = policy_data_path + '/' + 'meta-valid-policies.txt' + valid_policies_files = local_policies.get_policy_names_from_file(valid_policies_list_file) + + self.request_json = json_from_file(parameter_data_file) + self.policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files] + + def test_conductor_api_call_builder(self): - #main_dir = ".." - main_dir = "" + main_dir = self.main_dir conductor_api_template = main_dir + "osdf/templates/conductor_interface.json" - parameter_data_file = main_dir + "test/placement-tests/request.json" - policy_data_path = main_dir + "test/policy-local-files/" local_config_file = main_dir + "config/common_config.yaml" - - policy_data_files = ["CloudAttributePolicy_vGMuxInfra_1.json", - "CloudAttributePolicy_vG_1.json", - "DistanceToLocationPolicy_vGMuxInfra_1.json", - "DistanceToLocationPolicy_vG_1.json", - "InventoryGroup_vGMuxInfra_1.json", - "InventoryGroup_vG_1.json", - "PlacementOptimizationPolicy.json", - "ResourceInstancePolicy_vG_1.json", - "VNFPolicy_vGMuxInfra_1.json", - "VNFPolicy_vG_1.json", - "ZonePolicy_vGMuxInfra_1.json", - "ZonePolicy_vG_1.json"] - request_json = json.loads(open(parameter_data_file).read()) - policies = [json.loads(open(policy_data_path + file).read()) for file in policy_data_files] + request_json = self.request_json + policies = self.policies local_config = yaml.load(open(local_config_file)) templ_string = conductor_api_builder(request_json, policies, local_config, [], conductor_api_template) templ_json = json.loads(templ_string) diff --git a/test/test_PolicyCalls.py b/test/test_PolicyCalls.py index 0378dbd..83cce55 100644 --- a/test/test_PolicyCalls.py +++ b/test/test_PolicyCalls.py @@ -18,16 +18,32 @@ import json import unittest +from osdf.adapters.local_data import local_policies from osdf.config.base import osdf_config from osdf.adapters.policy import interface -from osdf.utils.interfaces import RestClient +from osdf.utils.interfaces import RestClient, json_from_file import yaml from mock import patch from osdf.optimizers.placementopt.conductor import translation class TestPolicyCalls(unittest.TestCase): - + + def setUp(self): + main_dir = "" + parameter_data_file = main_dir + "test/placement-tests/request.json" + policy_data_path = main_dir + "test/policy-local-files/" + local_config_file = main_dir + "config/common_config.yaml" + + valid_policies_list_file = policy_data_path + '/' + 'meta-valid-policies.txt' + valid_policies_files = local_policies.get_policy_names_from_file(valid_policies_list_file) + + self.request_json = json_from_file(parameter_data_file) + self.policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files] + + def tearDown(self): + pass + def test_get_subscriber_name(self): req_json_obj = json.loads(open("./test/placement-tests/request_mso.json").read()) config_core = osdf_config.core @@ -36,8 +52,7 @@ class TestPolicyCalls(unittest.TestCase): subs_name = interface.get_subscriber_name(req_json_obj, pmain) print("subscriber_name=", subs_name) self.assertEquals(subs_name, "Avteet_Chayal") - - + def test_get_subscriber_name_null(self): req_json_file = "./test/placement-tests/request_mso_subs_name_null.json" req_json_obj = json.loads(open(req_json_file).read()) @@ -48,7 +63,6 @@ class TestPolicyCalls(unittest.TestCase): subs_name = interface.get_subscriber_name(req_json_obj, pmain) print("subscriber_name=", subs_name) self.assertEquals(subs_name, "DEFAULT") - def test_get_subscriber_name_blank(self): req_json_file = "./test/placement-tests/request_mso_subs_name_blank.json" @@ -60,7 +74,6 @@ class TestPolicyCalls(unittest.TestCase): subs_name = interface.get_subscriber_name(req_json_obj, pmain) print("subscriber_name=", subs_name) self.assertEquals(subs_name, "DEFAULT") - def test_get_subscriber_name_default(self): req_json_file = "./test/placement-tests/request_mso_subs_name_default.json" @@ -73,7 +86,6 @@ class TestPolicyCalls(unittest.TestCase): print("subscriber_name=", subs_name) self.assertEquals(subs_name, "DEFAULT") - def test_get_subscriber_name_none(self): req_json_file = "./test/placement-tests/request_mso_subs_name_none.json" req_json_obj = json.loads(open(req_json_file).read()) @@ -84,49 +96,35 @@ class TestPolicyCalls(unittest.TestCase): subs_name = interface.get_subscriber_name(req_json_obj, pmain) print("subscriber_name=", subs_name) self.assertEquals(subs_name, "DEFAULT") - def test_get_by_scope(self): req_json_file = "./test/placement-tests/testScoperequest.json" allPolicies = "./test/placement-tests/scopePolicies.json" req_json_obj = json.loads(open(req_json_file).read()) req_json_obj2 = json.loads(open(allPolicies).read()) - config_core = osdf_config.core yamlFile = "./test/placement-tests/test_by_scope.yaml" with open(yamlFile) as yamlFile2: - policyConfigFile = yaml.load(yamlFile2) - with patch('osdf.adapters.policy.interface.get_subscriber_role', return_value=('FFA Homing', [])) as mock_open: - with patch('osdf.utils.interfaces.RestClient.request', return_value = req_json_obj2): - policiesList = interface.get_by_scope(RestClient, req_json_obj, policyConfigFile, 'placement') - print(policiesList) - #catches Exception if policiesList is null + policy_config_file = yaml.load(yamlFile2) + with patch('osdf.adapters.policy.interface.get_subscriber_role', + return_value=('FFA Homing', [])) as mock_open: + with patch('osdf.utils.interfaces.RestClient.request', return_value=req_json_obj2): + policiesList = interface.get_by_scope(RestClient, req_json_obj, policy_config_file, 'placement') self.assertTrue(policiesList, 'is null') self.assertRaises(Exception) def test_gen_demands(self): actionsList = [] genDemandslist = [] - req_json = "./test/placement-tests/testScoperequest.json" - policiesList = "./test/placement-tests/vnfGroupPolicies.txt" - fh = json.loads(open(policiesList).read()) - #print(fh) + req_json = "./test/placement-tests/request.json" req_json = json.loads(open(req_json).read()) - config_core = osdf_config.core - service_type = req_json['placementInfo'].get('serviceType', None) - # service_type = data_mapping.get_request_service_type(req_json_file) - genDemands = translation.gen_demands(req_json['placementInfo']['demandInfo'], fh) - #print(genDemands) - #print(req_json_file['placementInfo']['demandInfo']['placementDemand'][0]) - for action in req_json['placementInfo']['demandInfo']['placementDemand']: - #print(action['resourceModuleName']) + genDemands = translation.gen_demands(req_json, self.policies) + for action in req_json['placementInfo']['placementDemands']: actionsList.append(action['resourceModuleName']) for key2,value in genDemands.items(): - #print(key2) genDemandslist.append(key2) - #genDemandslist.remove('Primary IP_Mux_Demux updated_1 0') - #catches Exception if lists are not equal - self.assertListEqual(genDemandslist, actionsList, 'generated demands are not equal to the passed input [placementDemand][resourceModuleName] list') + self.assertListEqual(genDemandslist, actionsList, 'generated demands are not equal to the passed input' + '[placementDemand][resourceModuleName] list') if __name__ == '__main__': unittest.main() diff --git a/test/test_api_validation.py b/test/test_api_validation.py index 9045530..80e0ba0 100644 --- a/test/test_api_validation.py +++ b/test/test_api_validation.py @@ -37,6 +37,11 @@ class TestReqValidation(unittest.TestCase): class TestResponseValidation(unittest.TestCase): + def test_res_validation(self): + req_file = "./test/placement-tests/response.json" + req_json = json.loads(open(req_file).read()) + self.assertEqual(PlacementResponse(req_json).validate(), None) + def test_invalid_response(self): resp_json = {} self.assertRaises(ModelValidationError, lambda: PlacementResponse(resp_json).validate()) diff --git a/test/test_process_placement_opt.py b/test/test_process_placement_opt.py index 3cf9701..01be17d 100644 --- a/test/test_process_placement_opt.py +++ b/test/test_process_placement_opt.py @@ -15,39 +15,51 @@ # # ------------------------------------------------------------------------- # +import mock import unittest -import json -import yaml -from osdf.optimizers.placementopt.conductor.remote_opt_processor import process_placement_opt + +from flask import Response from mock import patch +from osdf.adapters.local_data import local_policies +from osdf.optimizers.placementopt.conductor.remote_opt_processor import process_placement_opt +from osdf.utils.interfaces import json_from_file, yaml_from_file + -class TestConductorApiBuilder(unittest.TestCase): +class TestProcessPlacementOpt(unittest.TestCase): - def test_conductor_api_call_builder(self): - #main_dir = ".." + def setUp(self): + mock_req_accept_message = Response("Accepted Request", content_type='application/json; charset=utf-8') + self.patcher_req = patch('osdf.optimizers.placementopt.conductor.conductor.request', + return_value={"solutionInfo": {"placementInfo": "dummy"}}) + self.patcher_req_accept = patch('osdf.operation.responses.osdf_response_for_request_accept', + return_value=mock_req_accept_message) + self.patcher_callback = patch( + 'osdf.optimizers.placementopt.conductor.remote_opt_processor.process_placement_opt', + return_value=mock_req_accept_message) + self.patcher_RestClient = patch( + 'osdf.utils.interfaces.RestClient', return_value=mock.MagicMock()) + self.Mock_req = self.patcher_req.start() + self.Mock_req_accept = self.patcher_req_accept.start() + self.Mock_callback = self.patcher_callback.start() + self.Mock_RestClient = self.patcher_RestClient.start() + + def tearDown(self): + patch.stopall() + + def test_process_placement_opt(self): main_dir = "" conductor_api_template = main_dir + "osdf/templates/conductor_interface.json" parameter_data_file = main_dir + "test/placement-tests/request.json" policy_data_path = main_dir + "test/policy-local-files/" local_config_file = main_dir + "config/common_config.yaml" - policy_data_files = ["CloudAttributePolicy_vGMuxInfra_1.json", - "CloudAttributePolicy_vG_1.json", - "DistanceToLocationPolicy_vGMuxInfra_1.json", - "DistanceToLocationPolicy_vG_1.json", - "InventoryGroup_vGMuxInfra_1.json", - "InventoryGroup_vG_1.json", - "PlacementOptimizationPolicy.json", - "ResourceInstancePolicy_vG_1.json", - "VNFPolicy_vGMuxInfra_1.json", - "VNFPolicy_vG_1.json", - "ZonePolicy_vGMuxInfra_1.json", - "ZonePolicy_vG_1.json"] - request_json = json.loads(open(parameter_data_file).read()) - policies = [json.loads(open(policy_data_path + file).read()) for file in policy_data_files] - local_config = yaml.load(open(local_config_file)) - with patch('osdf.optimizers.placementopt.conductor.conductor.request', return_value={"solutionInfo": {"placementInfo": "dummy"}}): - templ_string = process_placement_opt(request_json, policies, local_config, []) + valid_policies_list_file = policy_data_path + '/' + 'meta-valid-policies.txt' + valid_policies_files = local_policies.get_policy_names_from_file(valid_policies_list_file) + + request_json = json_from_file(parameter_data_file) + policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files] + local_config = yaml_from_file(local_config_file) + templ_string = process_placement_opt(request_json, policies, local_config, []) if __name__ == "__main__": diff --git a/tox.ini b/tox.ini index 681ec2e..dbbde5e 100644 --- a/tox.ini +++ b/tox.ini @@ -8,9 +8,13 @@ distribute = False commands = coverage run --module pytest --junitxml xunit-results.xml coverage xml --omit=".tox/py3/*","test/*" + coverage report --omit=".tox/py3/*","test/*" # TODO: need to update the above "omit" when we package osdf as pip-installable deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test/test-requirements.txt +[run] +source=./osdf/,osdfapp.py + [testenv:py3] basepython=python3.6 -- cgit 1.2.3-korg