summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/common_config.yaml34
-rw-r--r--config/has_config.yaml24
-rw-r--r--osdf/adapters/local_data/local_policies.py11
-rw-r--r--osdf/adapters/policy/interface.py20
-rw-r--r--osdf/adapters/policy/utils.py8
-rw-r--r--osdf/adapters/sdc/constraint_handler.py81
-rwxr-xr-xosdf/adapters/sdc/sdc.py40
-rw-r--r--osdf/models/api/placementRequest.py82
-rw-r--r--osdf/models/api/placementResponse.py33
-rw-r--r--osdf/optimizers/licenseopt/simple_license_allocation.py23
-rw-r--r--osdf/optimizers/placementopt/conductor/api_builder.py28
-rw-r--r--osdf/optimizers/placementopt/conductor/conductor.py3
-rw-r--r--osdf/optimizers/placementopt/conductor/remote_opt_processor.py4
-rw-r--r--osdf/optimizers/placementopt/conductor/translation.py125
-rw-r--r--osdf/utils/interfaces.py14
-rwxr-xr-xosdfapp.py44
-rw-r--r--test/conductor/test_conductor_translation.py53
-rw-r--r--test/config/common_config.yaml43
-rw-r--r--test/config/has_config.yaml24
-rwxr-xr-xtest/config/osdf_config.yaml34
-rw-r--r--test/local_data/test_local_policies.py12
-rw-r--r--test/operation/test_responses.py15
-rw-r--r--test/placement-tests/request.json186
-rw-r--r--test/placement-tests/response.json49
-rw-r--r--test/policy-local-files/Affinity_vCPE_1.json21
-rw-r--r--test/policy-local-files/Capacity_vGMuxInfra.json21
-rw-r--r--test/policy-local-files/Capacity_vG_1.json21
-rw-r--r--test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json34
-rw-r--r--test/policy-local-files/CloudAttributePolicy_vG_1.json34
-rw-r--r--test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json30
-rw-r--r--test/policy-local-files/DistanceToLocationPolicy_vG_1.json30
-rw-r--r--test/policy-local-files/Distance_vGMuxInfra_1.json21
-rw-r--r--test/policy-local-files/Distance_vG_1.json21
-rw-r--r--test/policy-local-files/INVALID-policies/INVALID-Affinity_vCPE_1.json21
-rw-r--r--test/policy-local-files/InventoryGroup_vGMuxInfra_1.json22
-rw-r--r--test/policy-local-files/InventoryGroup_vG_1.json22
-rw-r--r--test/policy-local-files/Min_Guarantee_vGMuxInfra_1.json21
-rw-r--r--test/policy-local-files/Placement_Optimization_1.json (renamed from test/policy-local-files/PlacementOptimizationPolicy.json)17
-rw-r--r--test/policy-local-files/QueryPolicy_vCPE.json20
-rw-r--r--test/policy-local-files/ResourceInstancePolicy_vG_1.json26
-rw-r--r--test/policy-local-files/VNFPolicy_vGMuxInfra_1.json36
-rw-r--r--test/policy-local-files/VNFPolicy_vG_1.json36
-rw-r--r--test/policy-local-files/ZonePolicy_vGMuxInfra_1.json26
-rw-r--r--test/policy-local-files/ZonePolicy_vG_1.json26
-rw-r--r--test/policy-local-files/hpa_policy_vGMuxInfra_1.json144
-rw-r--r--test/policy-local-files/hpa_policy_vG_1.json144
-rw-r--r--test/policy-local-files/meta-invalid-policies.txt4
-rw-r--r--test/policy-local-files/meta-valid-policies.txt12
-rw-r--r--test/policy-local-files/vnfPolicy_vG.json31
-rw-r--r--test/policy-local-files/vnfPolicy_vGMuxInfra.json31
-rw-r--r--test/policy/test_policy_interface.py64
-rw-r--r--test/test_ConductorApiBuilder.py39
-rw-r--r--test/test_PolicyCalls.py60
-rw-r--r--test/test_api_validation.py5
-rw-r--r--test/test_process_placement_opt.py58
-rw-r--r--tox.ini4
56 files changed, 1256 insertions, 836 deletions
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/Placement_Optimization_1.json
index 7c43435..5748c99 100644
--- a/test/policy-local-files/PlacementOptimizationPolicy.json
+++ b/test/policy-local-files/Placement_Optimization_1.json
@@ -1,9 +1,9 @@
{
"service": "PlacementOptimizationPolicy",
- "policyName": "PlacementOptimizationPolicy",
- "description": "PlacementOptimizationPolicy",
+ "policyName": "oofBeijing.PlacementOptimizationPolicy_vGMuxInfra",
+ "description": "Placement Optimization Policy for vGMuxInfra",
"templateVersion": "1702.03",
- "version": "1707",
+ "version": "oofBeijing",
"priority": "5",
"riskType": "test",
"riskLevel": "3",
@@ -15,7 +15,7 @@
"resource": ["vGMuxInfra"],
"customerLocationInfo": "customer_loc",
"parameter": "distance",
- "weight": "2",
+ "weight": "1",
"operator": "product"
},
{
@@ -29,13 +29,8 @@
"operator": "sum"
},
"identity": "optimization",
- "policyScope": {
- "serviceType": ["vCPE"],
- "geoRegion": ["US", "INTERNATIONAL"],
- "networkType": ["ip"],
- "resourceInstanceType": ["vGMuxInfra","vG"]
- },
- "type": "placementOptimization",
+ "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":["<CPUINST>", "<CPUINST>"], "operator": "ALL", "unit":""}
+ ]
+ }
+ ]
+ },
+ {
+ "flavorLabel": "flavor_label_vm_02",
+ "flavorProperties":[
+ {
+ "hpa_feature" : "cpuPinningy",
+ "mandatory" : "True",
+ "architecture": "generic",
+ "featureAttributes": [
+ {"attribute":"logicalCpuThreadPinningPolicy", "value":"<CPUTHREADPOLICY>", "operator": "=", "unit":""},
+ {"attribute":"logicalCpuPinningPolicy", "value": "<CPUPOLICY>","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": "<PCITYPEVALUE>","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": "<MEMORYPAGESIZE>", "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":["<CPUINST>", "<CPUINST>"], "operator": "ALL", "unit":""}
+ ]
+ }
+ ]
+ },
+ {
+ "flavorLabel": "flavor_label_vm_02",
+ "flavorProperties":[
+ {
+ "hpa_feature" : "cpuPinningy",
+ "mandatory" : "True",
+ "architecture": "generic",
+ "featureAttributes": [
+ {"attribute":"logicalCpuThreadPinningPolicy", "value":"<CPUTHREADPOLICY>", "operator": "=", "unit":""},
+ {"attribute":"logicalCpuPinningPolicy", "value": "<CPUPOLICY>","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": "<PCITYPEVALUE>","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": "<MEMORYPAGESIZE>", "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