Traffic Distributtion support added 34/84534/3
authorLukasz Rajewski <lukasz.rajewski@orange.com>
Mon, 8 Apr 2019 13:05:22 +0000 (15:05 +0200)
committerLukasz Rajewski <lukasz.rajewski@orange.com>
Thu, 18 Apr 2019 15:05:54 +0000 (17:05 +0200)
* New local polcies for vFW TD use case
* Fixed encoding for conductor_request template and parameters
  section modified to accept all requestParameters
* Conductor request can have many attributes in the
  'attributes' section - all that are defined in the vnf policy file
* Conductor request can have many request parameters in the
  'requestParameters' section. The parameters come from QueryPolicies.
  Before list of suppoted parameters was hardcoded
* Optional 'unique' parameter added to the placementDemand section.
  It is already supported by conductor for all inventory types
* Improved debug logs for local policies
* Unit tests added for expanded request format

Change-Id: I41f219c366a3a77881c7096e64a6272edbada23b
Issue-ID: OPTFRA-443
Signed-off-by: Lukasz Rajewski <lukasz.rajewski@orange.com>
22 files changed:
config/common_config.yaml
config/has_config.yaml
osdf/adapters/local_data/local_policies.py
osdf/adapters/policy/interface.py
osdf/models/api/placementRequest.py
osdf/optimizers/placementopt/conductor/api_builder.py
osdf/optimizers/placementopt/conductor/translation.py
osdf/templates/conductor_interface.json
test/conductor/test_conductor_calls.py
test/conductor/test_conductor_translation.py
test/placement-tests/request_placement_vfmod.json [new file with mode: 0644]
test/placement-tests/request_vfmod.json [new file with mode: 0644]
test/placement-tests/response_vfmod.json [new file with mode: 0644]
test/policy-local-files/QueryPolicy_vFW_TD.json [new file with mode: 0644]
test/policy-local-files/affinity_vFW_TD.json [new file with mode: 0644]
test/policy-local-files/meta-valid-policies.txt
test/policy-local-files/vnfPolicy_vFW_TD.json [new file with mode: 0644]
test/policy-local-files/vnfPolicy_vPGN_TD.json [new file with mode: 0644]
test/test_ConductorApiBuilder.py
test/test_api_validation.py
test/test_get_opt_query_data.py
test/test_process_placement_opt.py

index c513d5e..c786d74 100644 (file)
@@ -32,6 +32,12 @@ osdf_temp:  # special configuration required for "workarounds" or testing
             - Placement_Optimization_1.json
             - QueryPolicy_vFW.json
             - vnfPolicy_vFW.json
+        placement_policy_dir_vfw_td: "./test/policy-local-files/"
+        placement_policy_files_vfw_td:
+            - vnfPolicy_vFW_TD.json
+            - vnfPolicy_vPGN_TD.json
+            - affinity_vFW_TD.json
+            - QueryPolicy_vFW_TD.json
 service_info:
     vCPE:
         vcpeHostName: requestParameters.vcpeHostName
@@ -63,7 +69,7 @@ policy_info:
         policy_scope:
             default_scope: OSDF_CASABLANCA
             vcpe_scope: OSDF_CASABLANCA
-            vfw_scope: OSDF_CASABLANCA
+            vfw_scope: OSDF_DUBLIN
             secondary_scopes:
                 -
                     - get_param: service_name
index cf8a80c..38a4781 100644 (file)
@@ -1,24 +1,27 @@
 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,
-        customer-id: customerId,
-        service-type: serviceResourceId,
-        equipment-role: equipmentRole,
-        model-invariant-id: modelInvariantId,
-        model-version-id: modelVersionId
+        hypervisor: hypervisor
+        cloudVersion: cloud_version
+        cloudType: cloud_type
+        dataPlane: dataplane
+        networkRoles: network_roles
+        complex: complex
+        state: state
+        country: country
+        geoRegion: geo_region
+        exclusivityGroups: exclusivity_groups
+        replicationRole: replication_role
+        customerId: customer_id
+        serviceResourceId: service-type
+        equipmentRole: equipment-role
+        modelInvariantId: model-invariant-id
+        modelVersionId: model-version-id
+        cloudRegionId: cloud-region-id
+        orchestrationStatus: orchestration-status
+        provStatus: prov-status
     candidates:
         # for (k1, v1), if k1 is in demand, set prop[k2] = _get_candidates(demand[k1])
-        excludedCandidates: excluded_candidates,
+        excludedCandidates: excluded_candidates
         requiredCandidates: required_candidates
     extra_fields:
         # we have [k1, k2, k3, k4] type items and x is policy-content-properties
index 6e49388..dc6837a 100644 (file)
@@ -19,7 +19,7 @@
 import json
 import os
 import re
-
+from osdf.logging.osdf_logging import debug_log
 
 def get_local_policies(local_policy_folder, local_policy_list, policy_id_list=None):
     """
@@ -32,6 +32,7 @@ def get_local_policies(local_policy_folder, local_policy_list, policy_id_list=No
     :param policy_id_list: list of policies to get (if unspecified or None, get all)
     :return: get policies
     """
+    debug_log.debug("Policy folder: {}, local_list {}, policy id list {}".format(local_policy_folder, local_policy_list, policy_id_list))
     policies = []
     if policy_id_list:
         for policy_id in policy_id_list:
index 95bfacc..a7839c6 100644 (file)
@@ -157,10 +157,12 @@ def local_policies_location(req_json, osdf_config, service_type):
     if lp.get('global_disabled'):
         return None  # short-circuit to disable all local policies
     if lp.get('local_{}_policies_enabled'.format(service_type)):
+        debug_log.debug('Loading local policies for service type: {}'.format(service_type))
         if service_type == "scheduling":
             return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type))
         else:
             service_name = req_json['serviceInfo']['serviceName']  # TODO: data_mapping.get_service_type(model_name)
+            debug_log.debug('Loading local policies for service name: {}'.format(service_name))
             return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), \
                    lp.get('{}_policy_files_{}'.format(service_type, service_name.lower()))
     return None
@@ -178,6 +180,8 @@ def get_policies(request_json, service_type):
     local_info = local_policies_location(request_json, osdf_config, service_type)
 
     if local_info:  # tuple containing location and list of files
+        if local_info[0] is None or local_info[1] is None:
+            raise ValueError("Error fetching local policy info")
         to_filter = None
         if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name":
             to_filter = request_json[service_type + "Info"]['policyId']
index 55f0a98..7d6bde4 100644 (file)
@@ -17,7 +17,7 @@
 #
 
 from .common import OSDFModel
-from schematics.types import BaseType, StringType, URLType, IntType
+from schematics.types import BaseType, StringType, URLType, IntType, BooleanType
 from schematics.types.compound import ModelType, ListType, DictType
 
 
@@ -71,6 +71,7 @@ class PlacementDemand(OSDFModel):
     resourceModuleName = StringType(required=True)
     serviceResourceId = StringType(required=True)
     tenantId = StringType()
+    unique = BooleanType() # to be implemented on the policy level
     resourceModelInfo = ModelType(ModelMetaData, required=True)
     existingCandidates = ListType(ModelType(Candidates))
     excludedCandidates = ListType(ModelType(Candidates))
index 187f9f5..08a7460 100644 (file)
@@ -25,6 +25,29 @@ from osdf.adapters.policy.utils import group_policies_gen
 from osdf.utils.programming_utils import list_flatten
 
 
+def _build_parameters(group_policies, request_json):
+    """
+    Function prepares parameters section for has request
+    :param group_policies: filtered policies
+    :param request_json: parameter data received from a client
+    :return:
+    """
+    initial_params = tr.get_opt_query_data(request_json, group_policies['request_param_query'])
+    params = dict()
+    params.update({"REQUIRED_MEM": initial_params.pop("requiredMemory", "")})
+    params.update({"REQUIRED_DISK": initial_params.pop("requiredDisk", "")})
+    params.update({"customer_lat": initial_params.pop("customerLatitude", 0.0)})
+    params.update({"customer_long": initial_params.pop("customerLongitude", 0.0)})
+    params.update({"service_name": request_json['serviceInfo']['serviceName']})
+    params.update({"service_id": request_json['serviceInfo']['serviceInstanceId']})
+
+    for key, val in initial_params.items():
+        if val and val != "":
+            params.update({key: val})
+
+    return params
+
+
 def conductor_api_builder(request_json, flat_policies: list, local_config,
                           template="osdf/templates/conductor_interface.json"):
     """Build an OSDF southbound API call for HAS-Conductor/Placement optimization
@@ -54,7 +77,7 @@ def conductor_api_builder(request_json, flat_policies: list, local_config,
     reservation_policy_list = tr.gen_reservation_policy(demand_vnf_name_list, gp['instance_reservation'])
     capacity_policy_list = tr.gen_capacity_policy(demand_vnf_name_list, gp['vim_fit'])
     hpa_policy_list = tr.gen_hpa_policy(demand_vnf_name_list, gp['hpa'])
-    req_params_dict = tr.get_opt_query_data(request_json, gp['request_param_query'])
+    req_params_dict = _build_parameters(gp, request_json)
     conductor_policies = [attribute_policy_list, distance_to_location_policy_list, inventory_policy_list,
                           resource_instance_policy_list, resource_region_policy_list, zone_policy_list,
                           reservation_policy_list, capacity_policy_list, hpa_policy_list]
@@ -70,12 +93,7 @@ def conductor_api_builder(request_json, flat_policies: list, local_config,
         name=req_info['requestId'],
         timeout=req_info['timeout'],
         limit=req_info['numSolutions'],
-        service_type=request_json['serviceInfo']['serviceName'],
-        service_id=request_json['serviceInfo']['serviceInstanceId'],
-        latitude=req_params_dict.get("customerLatitude", 0.0),
-        longitude=req_params_dict.get("customerLongitude", 0.0),
-        required_disk=req_params_dict.get("requiredDisk", ""),
-        required_mem=req_params_dict.get("requiredMemory", ""),
+        request_params=req_params_dict,
         json=json)
     json_payload = json.dumps(json.loads(rendered_req))  # need this because template's JSON is ugly!
     return json_payload
index 93b80bf..d14f3e1 100644 (file)
@@ -18,6 +18,7 @@
 import copy
 import json
 import yaml
+import re
 
 from osdf.utils.data_conversion import text_to_symbol
 from osdf.utils.programming_utils import dot_notation
@@ -227,6 +228,8 @@ def get_demand_properties(demand, policies):
                     inventory_type=policy_property['inventoryType'],
                     service_type=demand['serviceResourceId'],
                     service_resource_id=demand['serviceResourceId'])
+
+        prop.update({'unique': demand['unique']} if demand.get('unique') else {})
         prop['attributes'] = dict()
         prop['attributes'].update({'global-customer-id': policy_property['customerId']}
                                   if policy_property['customerId'] else {})
@@ -236,11 +239,35 @@ def get_demand_properties(demand, policies):
                                   if demand['resourceModelInfo']['modelVersionId'] else {})
         prop['attributes'].update({'equipment-role': policy_property['equipmentRole']}
                                   if policy_property['equipmentRole'] else {})
+
+        if policy_property.get('attributes'):
+            for attr_key, attr_val in policy_property['attributes'].items():
+                update_converted_attribute(attr_key, attr_val, prop)
+
         prop.update(get_candidates_demands(demand))
         demand_properties.append(prop)
     return demand_properties
 
 
+def update_converted_attribute(attr_key, attr_val, properties):
+    """
+    Updates dictonary of attributes with one specified in the arguments.
+    Automatically translates key namr from camelCase to hyphens
+    :param attr_key: key of the attribute
+    :param attr_val: value of the attribute
+    :param properties: dictionary with attributes to update
+    :return:
+    """
+    if attr_val:
+        remapping = policy_config_mapping['attributes']
+        if remapping.get(attr_key):
+            key_value = remapping.get(attr_key)
+        else:
+            key_value = re.sub('(.)([A-Z][a-z]+)', r'\1-\2', attr_key)
+            key_value = re.sub('([a-z0-9])([A-Z])', r'\1-\2', key_value).lower()
+        properties['attributes'].update({key_value: attr_val})
+
+
 def gen_demands(req_json, vnf_policies):
     """Generate list of demands based on request and VNF policies
     :param req_json: Request object from the client (e.g. MSO)
index 7377c48..0b8e6a1 100755 (executable)
@@ -1,41 +1,39 @@
-{\r
-  "name": "{{ name }}",\r
-  "files": {},\r
-  "timeout": {{ timeout }},\r
-  "limit": {{ limit }},\r
-  "template": {\r
-    "homing_template_version": "2017-10-10",\r
-    "parameters": {\r
-           "service_name": "{{ service_type }}",\r
-        "service_id": "{{ service_id }}",\r
-        "customer_lat": {{ latitude }},\r
-        "customer_long": {{ longitude }},\r
-        "REQUIRED_DISK": "{{ required_disk }}",\r
-        "REQUIRED_MEM": "{{ required_mem }}"\r
-    },\r
-    "locations": {\r
-        "customer_loc": {\r
-            "latitude": { "get_param": "customer_lat" },\r
-            "longitude": { "get_param": "customer_long" }\r
-        }\r
-    },\r
-    "demands": {{ json.dumps(demand_list) }},\r
-    {% set comma_main = joiner(",") %}\r
-    "constraints": {\r
-      {% set comma=joiner(",") %}\r
-      {% for elem in policy_groups %} {{ comma() }}\r
-        {% for key, value in elem.items() %}\r
-          "{{key}}": {{ json.dumps(value) }}\r
-        {% endfor %}\r
-      {% endfor %}\r
-    },\r
-    "optimization": {\r
-      {% set comma=joiner(",") %}\r
-      {% for elem in optimization_policies %} {{ comma() }}\r
-        {% for key, value in elem.items() %}\r
-          "{{key}}": {{ json.dumps(value) }}\r
-        {% endfor %}\r
-      {% endfor %}\r
-    }\r
-  }\r
-}\r
+{
+  "name": "{{ name }}",
+  "files": {},
+  "timeout": {{ timeout }},
+  "limit": {{ limit }},
+  "template": {
+    "homing_template_version": "2017-10-10",
+    "parameters": {
+      {% set comma=joiner(",") %}
+      {% for key, value in request_params.items() %} {{ comma() }}
+        "{{key}}": {{ json.dumps(value) }}
+      {% endfor %}
+    },
+    "locations": {
+        "customer_loc": {
+            "latitude": { "get_param": "customer_lat" },
+            "longitude": { "get_param": "customer_long" }
+        }
+    },
+    "demands": {{ json.dumps(demand_list) }},
+    {% set comma_main = joiner(",") %}
+    "constraints": {
+      {% set comma=joiner(",") %}
+      {% for elem in policy_groups %} {{ comma() }}
+        {% for key, value in elem.items() %}
+          "{{key}}": {{ json.dumps(value) }}
+        {% endfor %}
+      {% endfor %}
+    },
+    "optimization": {
+      {% set comma=joiner(",") %}
+      {% for elem in optimization_policies %} {{ comma() }}
+        {% for key, value in elem.items() %}
+          "{{key}}": {{ json.dumps(value) }}
+        {% endfor %}
+      {% endfor %}
+    }
+  }
+}
index 1a96da7..77d9a72 100644 (file)
@@ -43,6 +43,11 @@ class TestConductorCalls(unittest.TestCase):
         policies = pol.get_local_policies("test/policy-local-files/", self.lp)
         conductor.request(req_json, self.osdf_config, policies)
 
+    def test_request_vfmod(self):
+        req_json = json_from_file("./test/placement-tests/request_vfmod.json")
+        policies = pol.get_local_policies("test/policy-local-files/", self.lp)
+        conductor.request(req_json, self.osdf_config, policies)
+
 
 if __name__ == "__main__":
     unittest.main()
index 0c7da94..27711f5 100644 (file)
@@ -28,16 +28,18 @@ 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"
+        self.main_dir = ""
+        self.conductor_api_template = self.main_dir + "osdf/templates/conductor_interface.json"
+        self.local_config_file = self.main_dir + "config/common_config.yaml"
+        policy_data_path = self.main_dir + "test/policy-local-files"
 
         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)
 
+        parameter_data_file = self.main_dir + "test/placement-tests/request.json"
         self.request_json = json_from_file(parameter_data_file)
+        parameter_data_file = self.main_dir + "test/placement-tests/request_vfmod.json"
+        self.request_vfmod_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):
@@ -49,6 +51,12 @@ class TestConductorTranslation(unittest.TestCase):
         res = tr.gen_demands(self.request_json, vnf_policies)
         assert res is not None
 
+    def test_gen_vfmod_demands(self):
+        # need to run this only on vnf policies
+        vnf_policies = [x for x in self.policies if x["content"]["policyType"] == "vnfPolicy"]
+        res = tr.gen_demands(self.request_vfmod_json, vnf_policies)
+        assert res is not None
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/placement-tests/request_placement_vfmod.json b/test/placement-tests/request_placement_vfmod.json
new file mode 100644 (file)
index 0000000..4233416
--- /dev/null
@@ -0,0 +1,88 @@
+{
+    "name": "de4f04e3-0a65-470b-9d07-8ea6c2fb3e10",
+    "template": {
+        "constraints": {
+            "affinity_vFW_TD": {
+                "demands": ["vFW-SINK", "vPGN"],
+                "properties": {
+                    "category": "region",
+                    "qualifier": "same"
+                },
+                "type": "zone"
+            }
+        },
+        "parameters": {
+            "service_name": "vFW_TD",
+            "chosen_region": "RegionOne",
+            "service_id": "3e8d118c-10ca-4b4b-b3db-089b5e9e6a1c",
+            "customer_long": 2.2,
+            "REQUIRED_MEM": "",
+            "customer_lat": 1.1,
+            "REQUIRED_DISK": ""
+        },
+        "locations": {
+            "customer_loc": {
+                "longitude": {
+                    "get_param": "customer_long"
+                },
+                "latitude": {
+                    "get_param": "customer_lat"
+                }
+            }
+        },
+        "demands": {
+            "vFW-SINK": [{
+                "attributes": {
+                    "global-customer-id": "Demonstration",
+                    "cloud-region-id": {
+                        "get_param": "chosen_region"
+                    },
+                    "model-version-id": "763731df-84fd-494b-b824-01fc59a5ff2d",
+                    "orchestration-status": ["active"],
+                    "model-invariant-id": "e7227847-dea6-4374-abca-4561b070fe7d",
+                    "service_instance_id": {
+                        "get_param": "service_id"
+                    },
+                    "prov-status": "ACTIVE"
+                },
+                "inventory_provider": "aai",
+                "service_resource_id": "vFW-SINK-XX",
+                "inventory_type": "vfmodule",
+                "service_type": "vFW-SINK-XX",
+                "excluded_candidates": [{
+                    "inventory_type": "vfmodule",
+                    "candidate_id": ["e765d576-8755-4145-8536-0bb6d9b1dc9a"]
+                }]
+            }],
+            "vPGN": [{
+                "attributes": {
+                    "global-customer-id": "Demonstration",
+                    "cloud-region-id": {
+                        "get_param": "chosen_region"
+                    },
+                    "model-version-id": "e02a7e5c-9d27-4360-ab7c-73bb83b07e3b",
+                    "orchestration-status": ["active"],
+                    "model-invariant-id": "762472ef-5284-4daa-ab32-3e7bee2ec355",
+                    "service_instance_id": {
+                        "get_param": "service_id"
+                    },
+                    "prov-status": "ACTIVE"
+                },
+                "inventory_provider": "aai",
+                "service_resource_id": "vPGN-XX",
+                "unique": "false",
+                "inventory_type": "vfmodule",
+                "service_type": "vPGN-XX"
+            }]
+        },
+        "optimization": {
+            "minimize": {
+                "sum": []
+            }
+        },
+        "homing_template_version": "2017-10-10"
+    },
+    "limit": 100,
+    "files": {},
+    "timeout": 1200
+}
\ No newline at end of file
diff --git a/test/placement-tests/request_vfmod.json b/test/placement-tests/request_vfmod.json
new file mode 100644 (file)
index 0000000..1e95e22
--- /dev/null
@@ -0,0 +1,58 @@
+{
+    "requestInfo": {
+        "transactionId": "e576c75e-7536-4145-a1c0-d60b65bb1bb8",
+        "requestId": "de4f04e3-0a65-470b-9d07-8ea6c2fb3e10",
+        "callbackUrl": "http://0.0.0.0:9000/osdfCallback/",
+        "sourceId": "SO",
+        "requestType": "create",
+        "numSolutions": "100",
+        "optimizers": [
+            "placement"
+        ],
+        "timeout": 1200
+    },
+    "placementInfo": {
+        "requestParameters": {
+            "chosenRegion": "RegionOne"
+        },
+        "subscriberInfo": {
+            "globalSubscriberId": "dbc2c763-6383-42d6-880a-b7d5c5bc84d9",
+            "subscriberName": "oof-so-chm"
+        },
+        "placementDemands": [
+            {
+                "resourceModuleName": "vFW-SINK",
+                "serviceResourceId": "vFW-SINK-XX",
+                "resourceModelInfo": {
+                    "modelInvariantId": "e7227847-dea6-4374-abca-4561b070fe7d",
+                    "modelVersionId": "763731df-84fd-494b-b824-01fc59a5ff2d"
+                },
+                "excludedCandidates": [
+                    {
+                        "identifierType": "vfmodule",
+                        "identifiers": [
+                            "e765d576-8755-4145-8536-0bb6d9b1dc9a"
+                        ]
+                    }
+                ]
+            },
+            {
+                "resourceModuleName": "vPGN",
+                "serviceResourceId": "vPGN-XX",
+                "unique": "false",
+                "resourceModelInfo": {
+                    "modelInvariantId": "762472ef-5284-4daa-ab32-3e7bee2ec355",
+                    "modelVersionId": "e02a7e5c-9d27-4360-ab7c-73bb83b07e3b"
+                }
+            }
+        ]
+    },
+    "serviceInfo": {
+        "serviceInstanceId": "3e8d118c-10ca-4b4b-b3db-089b5e9e6a1c",
+        "serviceName": "vFW_TD",
+        "modelInfo": {
+            "modelInvariantId": "TD-invariantId",
+            "modelVersionId": "TD-versionId"
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/placement-tests/response_vfmod.json b/test/placement-tests/response_vfmod.json
new file mode 100644 (file)
index 0000000..06e40de
--- /dev/null
@@ -0,0 +1,229 @@
+{
+    "transactionId": "",
+    "requestStatus": "completed",
+    "solutions": {
+        "placementSolutions": [
+            [{
+                "assignmentInfo": [{
+                    "key": "locationType",
+                    "value": "att_aic"
+                }, {
+                    "key": "vnfHostName",
+                    "value": "vFW-FW-MC"
+                }, {
+                    "key": "locationId",
+                    "value": "RegionOne"
+                }, {
+                    "key": "isRehome",
+                    "value": "false"
+                }, {
+                    "key": "nf-name",
+                    "value": "vFW-FW-MC"
+                }, {
+                    "key": "vnf-type",
+                    "value": "5G_EVE_Demo/5G_EVE_FW 0"
+                }, {
+                    "key": "ipv6-oam-address",
+                    "value": ""
+                }, {
+                    "key": "ipv4-oam-address",
+                    "value": "oam_network_zb4J"
+                }, {
+                    "key": "vservers",
+                    "value": [{
+                        "vserver-id": "4a61b075-5ae0-4cfe-b213-27d3647a0578",
+                        "l-interfaces": [{
+                            "macaddr": "fa:16:3e:4a:00:56",
+                            "interface-name": "vnf-snk-r1-t1-mc-vfw_private_3_port-tntiamoj2res",
+                            "ipv4-addresses": ["10.100.100.1"],
+                            "ipv6-addresses": [],
+                            "interface-id": "ff27775b-a2b7-4e6e-8f71-6a5a5e6020cd",
+                            "network-name": "",
+                            "network-id": "59763a33-3296-4dc8-9ee6-2bdcd63322fc"
+                        }, {
+                            "macaddr": "fa:16:3e:a1:e8:c9",
+                            "interface-name": "vnf-snk-r1-t1-mc-vfw_private_2_port-hiay5zan4da6",
+                            "ipv4-addresses": ["10.0.110.1"],
+                            "ipv6-addresses": [],
+                            "interface-id": "0bb0bb92-a4d1-4104-b491-e469949f60a3",
+                            "network-name": "",
+                            "network-id": "cdb4bc25-2412-4b77-bbd5-791a02f8776d"
+                        }, {
+                            "macaddr": "fa:16:3e:45:e2:16",
+                            "interface-name": "vnf-snk-r1-t1-mc-vfw_private_0_port-7xlr5kjvsmk6",
+                            "ipv4-addresses": ["192.168.10.100"],
+                            "ipv6-addresses": [],
+                            "interface-id": "f0291365-6070-4baa-8470-8775bed7c2c4",
+                            "network-name": "",
+                            "network-id": "932ac514-639a-45b2-b1a3-4c5bb708b5c1"
+                        }, {
+                            "macaddr": "fa:16:3e:2f:0b:2f",
+                            "interface-name": "vnf-snk-r1-t1-mc-vfw_private_1_port-khio4swt2vy3",
+                            "ipv4-addresses": ["192.168.20.100"],
+                            "ipv6-addresses": [],
+                            "interface-id": "5ba290b0-0833-4008-acda-be1878b9ae0c",
+                            "network-name": "",
+                            "network-id": "bd64a2b0-0bdd-45b4-b755-65d5ebe1cee0"
+                        }],
+                        "vserver-name": "vfw-vfw-1-dt"
+                    }, {
+                        "vserver-id": "cf51eeab-8f75-4635-a01c-9f4bbd1e146e",
+                        "l-interfaces": [{
+                            "macaddr": "fa:16:3e:23:82:d7",
+                            "interface-name": "vnf-snk-r1-t1-mc-vsn_private_2_port-spbtqjnybz5g",
+                            "ipv4-addresses": ["10.100.100.3"],
+                            "ipv6-addresses": [],
+                            "interface-id": "1b3fd313-cde3-4df6-8ea8-bf4ae28e7e03",
+                            "network-name": "",
+                            "network-id": "59763a33-3296-4dc8-9ee6-2bdcd63322fc"
+                        }, {
+                            "macaddr": "fa:16:3e:fc:bd:16",
+                            "interface-name": "vnf-snk-r1-t1-mc-vsn_private_1_port-spqyrticfqan",
+                            "ipv4-addresses": ["10.0.110.3"],
+                            "ipv6-addresses": [],
+                            "interface-id": "1b33d675-f351-4766-8669-7314f774d52c",
+                            "network-name": "",
+                            "network-id": "cdb4bc25-2412-4b77-bbd5-791a02f8776d"
+                        }, {
+                            "macaddr": "fa:16:3e:3d:e9:c5",
+                            "interface-name": "vnf-snk-r1-t1-mc-vsn_private_0_port-5ijwpdueh2fl",
+                            "ipv4-addresses": ["192.168.20.250"],
+                            "ipv6-addresses": [],
+                            "interface-id": "cf82e256-8ccf-4e43-ba96-04ea2e47b5d2",
+                            "network-name": "",
+                            "network-id": "bd64a2b0-0bdd-45b4-b755-65d5ebe1cee0"
+                        }],
+                        "vserver-name": "vfw-vsn-1-dt"
+                    }]
+                }, {
+                    "key": "nf-type",
+                    "value": "vnf"
+                }, {
+                    "key": "vnfHostName",
+                    "value": "vFW-FW-MC"
+                }, {
+                    "key": "aic_version",
+                    "value": "1"
+                }, {
+                    "key": "cloudClli",
+                    "value": "clli1"
+                }, {
+                    "key": "service_instance_id",
+                    "value": "3e8d118c-10ca-4b4b-b3db-089b5e9e6a1c"
+                }, {
+                    "key": "cloudOwner",
+                    "value": "CloudOwner"
+                }, {
+                    "value": "vfw-0-mc",
+                    "key": "vf-module-name"
+                }, {
+                    "value": "85eec994-b635-42c7-87a1-d39720cad36d",
+                    "key": "vf-module-id"
+                }, {
+                    "key": "nf-id",
+                    "value": "4d2dc294-dbb3-44a2-8422-fa61b30c21a9"
+                }],
+                "serviceResourceId": "vFW-SINK-XX",
+                "solution": {
+                    "cloudOwner": "CloudOwner",
+                    "identifiers": ["85eec994-b635-42c7-87a1-d39720cad36d"],
+                    "identifierType": "vfmodule"
+                },
+                "resourceModuleName": "vFW-SINK"
+            }, {
+                "assignmentInfo": [{
+                    "key": "locationType",
+                    "value": "att_aic"
+                }, {
+                    "key": "vnfHostName",
+                    "value": "vFW-PKG-MC"
+                }, {
+                    "key": "locationId",
+                    "value": "RegionOne"
+                }, {
+                    "key": "isRehome",
+                    "value": "false"
+                }, {
+                    "key": "nf-name",
+                    "value": "vFW-PKG-MC"
+                }, {
+                    "key": "vnf-type",
+                    "value": "5G_EVE_Demo/5G_EVE_PKG 0"
+                }, {
+                    "key": "ipv6-oam-address",
+                    "value": ""
+                }, {
+                    "key": "ipv4-oam-address",
+                    "value": "oam_network_zb4J"
+                }, {
+                    "key": "vservers",
+                    "value": [{
+                        "vserver-id": "00bddefc-126e-4e4f-a18d-99b94d8d9a30",
+                        "l-interfaces": [{
+                            "macaddr": "fa:16:3e:c4:07:7f",
+                            "interface-name": "vnf-pkg-r1-t2-mc-vpg_private_2_port-mf7lu55usq7i",
+                            "ipv4-addresses": ["10.100.100.2"],
+                            "ipv6-addresses": [],
+                            "interface-id": "4b333af1-90d6-42ae-8389-d440e6ff0e93",
+                            "network-name": "",
+                            "network-id": "59763a33-3296-4dc8-9ee6-2bdcd63322fc"
+                        }, {
+                            "macaddr": "fa:16:3e:b5:86:38",
+                            "interface-name": "vnf-pkg-r1-t2-mc-vpg_private_1_port-734xxixicw6r",
+                            "ipv4-addresses": ["10.0.110.2"],
+                            "ipv6-addresses": [],
+                            "interface-id": "85dd57e9-6e3a-48d0-a784-4598d627e798",
+                            "network-name": "",
+                            "network-id": "cdb4bc25-2412-4b77-bbd5-791a02f8776d"
+                        }, {
+                            "macaddr": "fa:16:3e:ff:d8:6f",
+                            "interface-name": "vnf-pkg-r1-t2-mc-vpg_private_0_port-e5qdm3p5ijhe",
+                            "ipv4-addresses": ["192.168.10.200"],
+                            "ipv6-addresses": [],
+                            "interface-id": "edaff25a-878e-4706-ad52-4e3d51cf6a82",
+                            "network-name": "",
+                            "network-id": "932ac514-639a-45b2-b1a3-4c5bb708b5c1"
+                        }],
+                        "vserver-name": "zdfw1fwl01pgn01"
+                    }]
+                }, {
+                    "key": "nf-type",
+                    "value": "vnf"
+                }, {
+                    "key": "vnfHostName",
+                    "value": "vFW-PKG-MC"
+                }, {
+                    "key": "aic_version",
+                    "value": "1"
+                }, {
+                    "key": "cloudClli",
+                    "value": "clli1"
+                }, {
+                    "key": "service_instance_id",
+                    "value": "3e8d118c-10ca-4b4b-b3db-089b5e9e6a1c"
+                }, {
+                    "key": "cloudOwner",
+                    "value": "CloudOwner"
+                }, {
+                    "value": "pkg-0-mc",
+                    "key": "vf-module-name"
+                }, {
+                    "value": "d187d743-5932-4fb9-a42d-db0a5be5ba7e",
+                    "key": "vf-module-id"
+                }, {
+                    "key": "nf-id",
+                    "value": "fcbff633-47cc-4f38-a98d-4ba8285bd8b6"
+                }],
+                "serviceResourceId": "vPGN-XX",
+                "solution": {
+                    "cloudOwner": "CloudOwner",
+                    "identifiers": ["d187d743-5932-4fb9-a42d-db0a5be5ba7e"],
+                    "identifierType": "vfmodule"
+                },
+                "resourceModuleName": "vPGN"
+            }]
+        ]
+    },
+    "statusMessage": "",
+    "requestId": "de4f04e3-0a65-470b-9d07-8ea6c2fb3e10"
+}
\ No newline at end of file
diff --git a/test/policy-local-files/QueryPolicy_vFW_TD.json b/test/policy-local-files/QueryPolicy_vFW_TD.json
new file mode 100644 (file)
index 0000000..dcf7439
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "service": "queryPolicy",
+  "policyName": "OSDF_DUBLIN.QueryPolicy_vFW_TD",
+  "description": "Query policy for vFW TD",
+  "templateVersion": "OpenSource.version.1",
+  "version": "oofDublin",
+  "priority": "3",
+  "riskType": "test",
+  "riskLevel": "2",
+  "guard": "False",
+  "content": {
+    "queryProperties": [
+      {"attribute":"customerLatitude", "attribute_location": "customerLatitude", "value": 1.1},
+      {"attribute":"customerLongitude", "attribute_location": "customerLongitude", "value": 2.2},
+      {"attribute":"chosen_region", "attribute_location": "chosenRegion"}
+    ],
+    "policyScope": [
+            "TD",
+            "vFW-SINK",
+            "vPGN"
+    ],
+    "policyType": "request_param_query",
+    "serviceName": "vFW_TD",
+    "identity": "vFW_TD_Query_Policy",
+    "resources": [
+            "vFW-SINK",
+            "vPGN"
+    ]
+  }
+}
diff --git a/test/policy-local-files/affinity_vFW_TD.json b/test/policy-local-files/affinity_vFW_TD.json
new file mode 100644 (file)
index 0000000..371cbfc
--- /dev/null
@@ -0,0 +1,28 @@
+{
+    "service": "affinityPolicy",
+    "policyName": "OSDF_DUBLIN.Affinity_vFW_TD",
+    "description": "Affinity policy for vPGN Anchor and vFW destination point",
+    "templateVersion": "OpenSource.version.1",
+    "version": "oofDublin",
+    "priority": "3",
+    "riskType": "test",
+    "riskLevel": "2",
+    "guard": "False",
+    "content": {
+        "identity": "affinity_vFW_TD",
+        "policyScope": [
+            "TD",
+            "vFW-SINK",
+            "vPGN"
+        ],
+        "affinityProperty": {
+            "qualifier": "same",
+            "category": "region"
+        },
+        "policyType": "zone",
+        "resources": [
+            "vFW-SINK",
+            "vPGN"
+        ]
+    }
+}
\ No newline at end of file
index 772ec1a..99e3e88 100644 (file)
@@ -10,3 +10,7 @@ hpa_policy_vGMuxInfra_1.json
 hpa_policy_vG_1.json
 vnfPolicy_vG.json
 vnfPolicy_vGMuxInfra.json
+QueryPolicy_vFW_TD.json
+vnfPolicy_vFW_TD.json
+vnfPolicy_vPGN_TD.json
+affinity_vFW_TD.json
\ No newline at end of file
diff --git a/test/policy-local-files/vnfPolicy_vFW_TD.json b/test/policy-local-files/vnfPolicy_vFW_TD.json
new file mode 100644 (file)
index 0000000..efe8ffa
--- /dev/null
@@ -0,0 +1,35 @@
+{
+    "service": "vnfPolicy",
+    "policyName": "OSDF_DUBLIN.vnfPolicy_vFW_TD",
+    "description": "vnfPolicy",
+    "templateVersion": "OpenSource.version.1",
+    "version": "oofDublin",
+    "priority": "6",
+    "riskType": "test",
+    "riskLevel": "3",
+    "guard": "False",
+    "content": {
+        "identity": "vnf_vFW_TD",
+        "policyScope": ["TD", "vFW-SINK"],
+        "policyType": "vnfPolicy",
+        "resources": ["vFW-SINK"],
+        "applicableResources": "any",
+        "vnfProperties": [{
+            "inventoryProvider": "aai",
+            "serviceType": "",
+            "inventoryType": "vfmodule",
+            "customerId": "Demonstration",
+            "equipmentRole": "",
+            "attributes": {
+                "orchestrationStatus": ["active"],
+                "provStatus": "ACTIVE",
+                "cloudRegionId": {
+                    "get_param": "chosen_region"
+                },
+                "service_instance_id": {
+                    "get_param": "service_id"
+                }
+            }
+        }]
+    }
+}
\ No newline at end of file
diff --git a/test/policy-local-files/vnfPolicy_vPGN_TD.json b/test/policy-local-files/vnfPolicy_vPGN_TD.json
new file mode 100644 (file)
index 0000000..64740ce
--- /dev/null
@@ -0,0 +1,35 @@
+{
+    "service": "vnfPolicy",
+    "policyName": "OSDF_DUBLIN.vnfPolicy_vPGN_TD",
+    "description": "vnfPolicy",
+    "templateVersion": "OpenSource.version.1",
+    "version": "oofDublin",
+    "priority": "6",
+    "riskType": "test",
+    "riskLevel": "3",
+    "guard": "False",
+    "content": {
+        "identity": "vnf_vPGN_TD",
+        "policyScope": ["TD", "vPGN"],
+        "policyType": "vnfPolicy",
+        "resources": ["vPGN"],
+        "applicableResources": "any",
+        "vnfProperties": [{
+            "inventoryProvider": "aai",
+            "serviceType": "",
+            "inventoryType": "vfmodule",
+            "customerId": "Demonstration",
+            "equipmentRole": "",
+            "attributes": {
+                "orchestrationStatus": ["active"],
+                "provStatus": "ACTIVE",
+                "cloudRegionId": {
+                    "get_param": "chosen_region"
+                },
+                "service_instance_id": {
+                    "get_param": "service_id"
+                }
+            }
+        }]
+    }
+}
\ No newline at end of file
index f8683a3..7e38f4d 100644 (file)
@@ -28,28 +28,38 @@ 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"    # "test/placement-tests/request.json"
+        self.conductor_api_template = self.main_dir + "osdf/templates/conductor_interface.json"
+        self.local_config_file = self.main_dir + "config/common_config.yaml"
         policy_data_path = self.main_dir + "test/policy-local-files"                 # "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)
 
+        parameter_data_file = self.main_dir + "test/placement-tests/request.json"    # "test/placement-tests/request.json"
         self.request_json = json_from_file(parameter_data_file)
+        parameter_data_file = self.main_dir + "test/placement-tests/request_vfmod.json"
+        self.request_vfmod_json = json_from_file(parameter_data_file)
+        parameter_data_file = self.main_dir + "test/placement-tests/request_placement_vfmod.json"
+        self.request_placement_vfmod_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 = self.main_dir
-        conductor_api_template = main_dir + "osdf/templates/conductor_interface.json" # "osdf/templates/conductor_interface.json"
-        local_config_file = main_dir + "config/common_config.yaml"
         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)
+        local_config = yaml.load(open(self.local_config_file))
+        templ_string = conductor_api_builder(request_json, policies, local_config, self.conductor_api_template)
         templ_json = json.loads(templ_string)
         self.assertEqual(templ_json["name"], "yyy-yyy-yyyy")
 
+    def test_conductor_api_call_builder_vfmod(self):
+        request_json = self.request_vfmod_json
+        policies = self.policies
+        local_config = yaml.load(open(self.local_config_file))
+        templ_string = conductor_api_builder(request_json, policies, local_config, self.conductor_api_template)
+        templ_json = json.loads(templ_string)
+        self.assertEqual(templ_json, self.request_placement_vfmod_json)
+
 
 if __name__ == "__main__":
     unittest.main()
index 80e0ba0..389ff62 100644 (file)
@@ -30,6 +30,11 @@ class TestReqValidation(unittest.TestCase):
         req_json = json.loads(open(req_file).read())
         self.assertEqual(PlacementAPI(req_json).validate(), None)
 
+    def test_req_vfmod_validation(self):
+        req_file = "./test/placement-tests/request_vfmod.json"
+        req_json = json.loads(open(req_file).read())
+        self.assertEqual(PlacementAPI(req_json).validate(), None)
+
     def test_req_failure(self):
         req_json = {}
         self.assertRaises(ModelValidationError, lambda: PlacementAPI(req_json).validate())
@@ -42,6 +47,11 @@ class TestResponseValidation(unittest.TestCase):
         req_json = json.loads(open(req_file).read())
         self.assertEqual(PlacementResponse(req_json).validate(), None)
 
+    def test_res_vfmod_validation(self):
+        req_file = "./test/placement-tests/response_vfmod.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 880f93f..1e2db17 100644 (file)
@@ -1,40 +1,52 @@
-# -------------------------------------------------------------------------\r
-#   Copyright (c) 2017-2018 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
-import unittest\r
-import json\r
-from osdf.optimizers.placementopt.conductor.translation import get_opt_query_data\r
-\r
-\r
-class TestGetOptQueryData(unittest.TestCase):\r
-\r
-    def test_get_opt_query_data(self):\r
-        main_dir = ""\r
-        parameter_data_file = main_dir + "test/placement-tests/request.json"\r
-        policy_data_path = main_dir + "test/policy-local-files/"\r
-        \r
-        query_policy_data_file = ["QueryPolicy_vCPE.json"]\r
-        request_json = json.loads(open(parameter_data_file).read())\r
-        policies = [json.loads(open(policy_data_path + file).read()) for file in query_policy_data_file]\r
-        req_param_dict = get_opt_query_data(request_json, policies)\r
-        \r
-        self.assertTrue(req_param_dict is not None)\r
-\r
-\r
-if __name__ == "__main__":\r
-    unittest.main()\r
-\r
+# -------------------------------------------------------------------------
+#   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 unittest
+import json
+from osdf.optimizers.placementopt.conductor.translation import get_opt_query_data
+
+
+class TestGetOptQueryData(unittest.TestCase):
+
+    def test_get_opt_query_data(self):
+        main_dir = ""
+        parameter_data_file = main_dir + "test/placement-tests/request.json"
+        policy_data_path = main_dir + "test/policy-local-files/"
+
+        query_policy_data_file = ["QueryPolicy_vCPE.json"]
+        request_json = json.loads(open(parameter_data_file).read())
+        policies = [json.loads(open(policy_data_path + file).read()) for file in query_policy_data_file]
+        req_param_dict = get_opt_query_data(request_json, policies)
+
+        self.assertTrue(req_param_dict is not None)
+
+    def test_get_opt_query_data_vfmod(self):
+        main_dir = ""
+        parameter_data_file = main_dir + "test/placement-tests/request_vfmod.json"
+        policy_data_path = main_dir + "test/policy-local-files/"
+
+        query_policy_data_file = ["QueryPolicy_vFW_TD.json"]
+        request_json = json.loads(open(parameter_data_file).read())
+        policies = [json.loads(open(policy_data_path + file).read()) for file in query_policy_data_file]
+        req_param_dict = get_opt_query_data(request_json, policies)
+
+        self.assertTrue(req_param_dict is not None)
+
+
+if __name__ == "__main__":
+    unittest.main()
+
index 3219675..62a1ce6 100644 (file)
@@ -76,6 +76,20 @@ class TestProcessPlacementOpt(unittest.TestCase):
         local_config = yaml_from_file(local_config_file)
         templ_string = process_placement_opt(request_json, policies, local_config)        
 
+    def test_process_placement_opt_placementDemand_vfmodule(self):
+        main_dir = ""
+        parameter_data_file = main_dir + "test/placement-tests/request_vfmod.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)
+
+        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__":
     unittest.main()