New policies and required code changes 33/36333/2
authorSastry Isukapalli <sastry@research.att.com>
Sat, 17 Mar 2018 06:40:15 +0000 (02:40 -0400)
committerSastry Isukapalli <sastry@research.att.com>
Sat, 17 Mar 2018 07:07:32 +0000 (07:07 +0000)
Issue-ID: OPTFRA-146
Change-Id: I2261ae69f52b184cd7dcb7b86d5905538666a411
Signed-off-by: Sastry Isukapalli <sastry@research.att.com>
56 files changed:
config/common_config.yaml
config/has_config.yaml [new file with mode: 0644]
osdf/adapters/local_data/local_policies.py
osdf/adapters/policy/interface.py
osdf/adapters/policy/utils.py
osdf/adapters/sdc/constraint_handler.py [deleted file]
osdf/adapters/sdc/sdc.py [deleted file]
osdf/models/api/placementRequest.py
osdf/models/api/placementResponse.py
osdf/optimizers/licenseopt/simple_license_allocation.py
osdf/optimizers/placementopt/conductor/api_builder.py
osdf/optimizers/placementopt/conductor/conductor.py
osdf/optimizers/placementopt/conductor/remote_opt_processor.py
osdf/optimizers/placementopt/conductor/translation.py
osdf/utils/interfaces.py
osdfapp.py
test/conductor/test_conductor_translation.py [new file with mode: 0644]
test/config/common_config.yaml [new file with mode: 0644]
test/config/has_config.yaml [new file with mode: 0644]
test/config/osdf_config.yaml [new file with mode: 0755]
test/local_data/test_local_policies.py
test/operation/test_responses.py
test/placement-tests/request.json
test/placement-tests/response.json [new file with mode: 0644]
test/policy-local-files/Affinity_vCPE_1.json [new file with mode: 0644]
test/policy-local-files/Capacity_vGMuxInfra.json [new file with mode: 0644]
test/policy-local-files/Capacity_vG_1.json [new file with mode: 0644]
test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json [deleted file]
test/policy-local-files/CloudAttributePolicy_vG_1.json [deleted file]
test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json [deleted file]
test/policy-local-files/DistanceToLocationPolicy_vG_1.json [deleted file]
test/policy-local-files/Distance_vGMuxInfra_1.json [new file with mode: 0644]
test/policy-local-files/Distance_vG_1.json [new file with mode: 0644]
test/policy-local-files/INVALID-policies/INVALID-Affinity_vCPE_1.json [new file with mode: 0644]
test/policy-local-files/InventoryGroup_vGMuxInfra_1.json [deleted file]
test/policy-local-files/InventoryGroup_vG_1.json [deleted file]
test/policy-local-files/Min_Guarantee_vGMuxInfra_1.json [new file with mode: 0644]
test/policy-local-files/Placement_Optimization_1.json [moved from test/policy-local-files/PlacementOptimizationPolicy.json with 68% similarity]
test/policy-local-files/QueryPolicy_vCPE.json [new file with mode: 0644]
test/policy-local-files/ResourceInstancePolicy_vG_1.json [deleted file]
test/policy-local-files/VNFPolicy_vGMuxInfra_1.json [deleted file]
test/policy-local-files/VNFPolicy_vG_1.json [deleted file]
test/policy-local-files/ZonePolicy_vGMuxInfra_1.json [deleted file]
test/policy-local-files/ZonePolicy_vG_1.json [deleted file]
test/policy-local-files/hpa_policy_vGMuxInfra_1.json [new file with mode: 0644]
test/policy-local-files/hpa_policy_vG_1.json [new file with mode: 0644]
test/policy-local-files/meta-invalid-policies.txt [new file with mode: 0644]
test/policy-local-files/meta-valid-policies.txt [new file with mode: 0644]
test/policy-local-files/vnfPolicy_vG.json [new file with mode: 0644]
test/policy-local-files/vnfPolicy_vGMuxInfra.json [new file with mode: 0644]
test/policy/test_policy_interface.py [new file with mode: 0644]
test/test_ConductorApiBuilder.py
test/test_PolicyCalls.py
test/test_api_validation.py
test/test_process_placement_opt.py
tox.ini

index d8f467b..bc62a58 100644 (file)
@@ -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 (file)
index 0000000..8cbeda2
--- /dev/null
@@ -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
index c63ae5a..e722fe2 100644 (file)
@@ -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
+        ]
index 4ddee15..288571f 100644 (file)
@@ -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
index a006f12..27885a5 100644 (file)
@@ -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 (file)
index 2aae9a0..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-# -------------------------------------------------------------------------\r
-#   Copyright (c) 2015-2017 AT&T Intellectual Property\r
-#\r
-#   Licensed under the Apache License, Version 2.0 (the "License");\r
-#   you may not use this file except in compliance with the License.\r
-#   You may obtain a copy of the License at\r
-#\r
-#       http://www.apache.org/licenses/LICENSE-2.0\r
-#\r
-#   Unless required by applicable law or agreed to in writing, software\r
-#   distributed under the License is distributed on an "AS IS" BASIS,\r
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-#   See the License for the specific language governing permissions and\r
-#   limitations under the License.\r
-#\r
-# -------------------------------------------------------------------------\r
-#\r
-\r
-from osdf.config.base import osdf_config\r
-from osdf.utils.programming_utils import dot_notation\r
-\r
-ns = {'p': 'http://xmlns.onap.org/sdc/license-model/1.0'}\r
-config_local = osdf_config.core\r
-\r
-\r
-def choose_license(license_artifacts, order_info, service_type):\r
-    entitlement_pool_uuids = []\r
-    license_key_group_uuids = []\r
-\r
-    for license_artifact in license_artifacts:\r
-        for feature in license_artifact.findall('./p:feature-group-list/', ns):\r
-            for entitlement in feature.findall('./p:entitlement-pool-list/', ns):\r
-                if is_valid(entitlement, order_info, service_type):\r
-                    entitlement_pool_uuid = entitlement.find('p:entitlement-pool-uuid', ns).text\r
-                    entitlement_pool_uuids.append(entitlement_pool_uuid)\r
-            for license_key_group in feature.findall('./p:license-key-group-list/', ns):\r
-                if is_valid(license_key_group, order_info, service_type):\r
-                    license_key_group_uuid = license_key_group.find('p:license-key-group-uuid', ns).text\r
-                    license_key_group_uuids.append(license_key_group_uuid)\r
-    return entitlement_pool_uuids, license_key_group_uuids\r
-\r
-\r
-# element is expected to be a license-key-group or entitlement-pool\r
-# if these elements diverge at a later date this method should be refactored\r
-def is_valid(element, order_info, service_type):\r
-    for limit in element.findall('./p:sp-limits/p:limit', ns):\r
-        # description = limit.find('p:description', ns).text\r
-        metric_value = limit.find('p:values', ns).text\r
-        metric = limit.find('p:metric', ns).text\r
-        try:\r
-            order_value = dot_notation(order_info, config_local['service_info'][service_type][metric])\r
-            # 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))\r
-            if isinstance(order_value, list): # it is possible a list is returned, for example a list of vnfs for vCPE\r
-                for arr_value in order_value:\r
-                    if str(metric_value) != str(arr_value):\r
-                        return False\r
-            else:\r
-                if str(metric_value) != str(order_value):\r
-                    return False\r
-        except KeyError:\r
-            return False\r
-    # vendor limits\r
-    for limit in element.findall('./p:vendor-limits/p:limit', ns):\r
-            # description = limit.find('p:description', ns).text\r
-            metric_value = limit.find('p:values', ns).text\r
-            metric = limit.find('p:metric', ns).text\r
-            try:\r
-                order_value = dot_notation(order_info, config_local['service_info'][service_type][metric])\r
-                if isinstance(order_value, list): # it is possible a list is returned, for example a list of vnfs for vCPE\r
-                    for arr_value in order_value:\r
-                        if str(metric_value) != str(arr_value):\r
-                            return False\r
-                else:\r
-                    if str(metric_value) != str(order_value):\r
-                        return False\r
-                # 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))\r
-\r
-            except KeyError:\r
-                return False\r
-    return True\r
-\r
diff --git a/osdf/adapters/sdc/sdc.py b/osdf/adapters/sdc/sdc.py
deleted file mode 100755 (executable)
index 43932ba..0000000
+++ /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
index df5f931..aa71eac 100644 (file)
@@ -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)
index e9746d6..75d33f0 100644 (file)
@@ -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)
index beafbe4..ad9acfc 100644 (file)
 # -------------------------------------------------------------------------
 #
 
-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
index 0a874f7..d8a2083 100644 (file)
@@ -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
 
index bdc7f17..32a4728 100644 (file)
@@ -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']))
index f753a70..957547b 100644 (file)
@@ -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
index 262fa86..9111c81 100644 (file)
 #
 # -------------------------------------------------------------------------
 #
-
+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
index 7a0e3a9..a869d6d 100644 (file)
@@ -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"""
 
index a2af0bf..6854061 100755 (executable)
@@ -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 (file)
index 0000000..b277b6a
--- /dev/null
@@ -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 (file)
index 0000000..1c041d9
--- /dev/null
@@ -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 (file)
index 0000000..8cbeda2
--- /dev/null
@@ -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 (executable)
index 0000000..69ebdf0
--- /dev/null
@@ -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"
index 120b186..13154c5 100644 (file)
@@ -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)
index 253cb7c..2e089ec 100644 (file)
@@ -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):
index 2fd425c..c4cb31f 100644 (file)
-{\r
-       "requestInfo": {\r
-               "transactionId": "xxx-xxx-xxxx",\r
-               "requestId": "yyy-yyy-yyyy",\r
-               "callbackUrl": "https://test.url.com:5000/callback/",\r
-               "sourceId": "so",\r
-               "optimizers": ["placement"],\r
-               "numSolutions": 1,\r
-               "timeout": 600\r
-       },\r
-       "placementInfo": {\r
-               "serviceModelInfo": {\r
-                       "modelType": "service",\r
-                       "modelInvariantId": "fad5f4d5-1c94-4890-927d-9cec6e82997f",\r
-                       "modelVersionId": "6e13c5e1-f172-436c-9cc4-0d64c94eb7f4",\r
-                       "modelName": "vCPE",\r
-                       "modelVersion": "1.0"\r
-               },\r
-               "subscriberInfo": {\r
-                       "globalSubscriberId": "SUB12_0325_UD_0833",\r
-                       "subscriberName": "SUB_12_0325_UD_0833",\r
-                       "subscriberCommonSiteId": "DALTX0101"\r
-               },\r
-               "demandInfo": {\r
-                       "placementDemand": [{\r
-                                       "resourceInstanceType": "allotted",\r
-                                       "serviceResourceId": "61d563e8-e714-4393-8f99-cc480144a05e",\r
-                                       "resourceModuleName": "vGMuxInfra",\r
-                                       "exclusionCandidateInfo": [{\r
-                                                       "candidateType": "cloud",\r
-                                                       "candidates": ["MDT54NJ", "BDM78NJ"]\r
-                                               }, {\r
-                                                       "candidateType": "service",\r
-                                                       "candidates": ["RT76U8F789", "PO098HJG"]\r
-                                               }\r
-                                       ],\r
-                                       "requiredCandidateInfo": [{\r
-                                                       "candidateType": "cloud",\r
-                                                       "candidates": ["DHU87NY"]\r
-                                               }, {\r
-                                                       "candidateType": "service",\r
-                                                       "candidates": ["YHT675YH"]\r
-                                               }\r
-                                       ],\r
-                                       "resourceModelInfo": {\r
-                                               "modelCustomizationId": "",\r
-                                               "modelInvariantId": "h59988ce-3d81-4e07-81b5-53d3aa821134",\r
-                                               "modelName": "",\r
-                                               "modelVersion": "2.0",\r
-                                               "modelVersionId": "51d563e8-e714-4393-8f99-cc480144a05e",\r
-                                               "modelType": "allotted"\r
-                                       },\r
-                                       "tenantId": "",\r
-                                       "tenantName": ""\r
-                               }, {\r
-                                       "resourceInstanceType": "allotted",\r
-                                       "serviceResourceId": "71d563e8-e714-4393-8f99-cc480144a05e",\r
-                                       "resourceModuleName": "vG",\r
-                                       "resourceModelInfo": {\r
-                                               "modelCustomizationId": "",\r
-                                               "modelInvariantId": "a59988ce-3d81-4e07-81b5-53d3aa821134",\r
-                                               "modelName": "",\r
-                                               "modelVersion": "2.0",\r
-                                               "modelVersionId": "91d563e8-e714-4393-8f99-cc480144a05e",\r
-                                               "modelType": "allotted"\r
-                                       },\r
-                                       "tenantId": "",\r
-                                       "tenantName": ""\r
-                               }\r
-                       ],\r
-                       "licenseDemand": []\r
-               },\r
-               "policyId": [\r
-                       ""\r
-               ],\r
-               "serviceInstanceId": "1234-fsdf-23sdf-24kjnk",\r
-               "requestParameters": {\r
-\r
-                               "commonSiteId": "DALTX0101",\r
-                               "vendorName": "xyz",\r
-                               "e2eVpnKey": "200",\r
-                               "vcpeHostName": "USOSTCDALTX0101UJZZ11"\r
-\r
-               }\r
-\r
-       }\r
+{
+  "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 (file)
index 0000000..f6c1bea
--- /dev/null
@@ -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 (file)
index 0000000..c42e9d8
--- /dev/null
@@ -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 (file)
index 0000000..9eec54d
--- /dev/null
@@ -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 (file)
index 0000000..d5644a8
--- /dev/null
@@ -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 (file)
index 57c0039..0000000
+++ /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 (file)
index cbe2a88..0000000
+++ /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 (file)
index 414c167..0000000
+++ /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 (file)
index 737ee19..0000000
+++ /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 (file)
index 0000000..a835ef1
--- /dev/null
@@ -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 (file)
index 0000000..1af021a
--- /dev/null
@@ -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 (file)
index 0000000..86670c3
--- /dev/null
@@ -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 (file)
index 20ff7f7..0000000
+++ /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 (file)
index 99ae309..0000000
+++ /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 (file)
index 0000000..654c548
--- /dev/null
@@ -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"]
+    }
+}
@@ -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"
                 },
                 {
             "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 (file)
index 0000000..4ed83f9
--- /dev/null
@@ -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 (file)
index 21ae0e4..0000000
+++ /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 (file)
index b0963d6..0000000
+++ /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 (file)
index de0a158..0000000
+++ /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 (file)
index 9f941e4..0000000
+++ /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 (file)
index 8104f6b..0000000
+++ /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 (file)
index 0000000..bf09532
--- /dev/null
@@ -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 (file)
index 0000000..98d8cea
--- /dev/null
@@ -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 (file)
index 0000000..9d9ed9b
--- /dev/null
@@ -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 (file)
index 0000000..a0ce3fe
--- /dev/null
@@ -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 (file)
index 0000000..ead038c
--- /dev/null
@@ -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 (file)
index 0000000..787563b
--- /dev/null
@@ -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 (file)
index 0000000..5dc75c3
--- /dev/null
@@ -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()
+
index f809c56..8395a47 100644 (file)
 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)
index 0378dbd..83cce55 100644 (file)
 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()
index 9045530..80e0ba0 100644 (file)
@@ -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())
index 3cf9701..01be17d 100644 (file)
 #
 # -------------------------------------------------------------------------
 #
+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 (file)
--- 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