From fc3ead31e631f69fabf0baaa20c10bf955ce374b Mon Sep 17 00:00:00 2001 From: Lukasz Rajewski Date: Mon, 8 Apr 2019 15:05:22 +0200 Subject: Traffic Distributtion support added * 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 --- config/common_config.yaml | 8 +- config/has_config.yaml | 37 ++-- osdf/adapters/local_data/local_policies.py | 3 +- osdf/adapters/policy/interface.py | 4 + osdf/models/api/placementRequest.py | 3 +- .../placementopt/conductor/api_builder.py | 32 ++- .../placementopt/conductor/translation.py | 27 +++ osdf/templates/conductor_interface.json | 80 ++++--- test/conductor/test_conductor_calls.py | 5 + test/conductor/test_conductor_translation.py | 18 +- test/placement-tests/request_placement_vfmod.json | 88 ++++++++ test/placement-tests/request_vfmod.json | 58 ++++++ test/placement-tests/response_vfmod.json | 229 +++++++++++++++++++++ test/policy-local-files/QueryPolicy_vFW_TD.json | 30 +++ test/policy-local-files/affinity_vFW_TD.json | 28 +++ test/policy-local-files/meta-valid-policies.txt | 4 + test/policy-local-files/vnfPolicy_vFW_TD.json | 35 ++++ test/policy-local-files/vnfPolicy_vPGN_TD.json | 35 ++++ test/test_ConductorApiBuilder.py | 24 ++- test/test_api_validation.py | 10 + test/test_get_opt_query_data.py | 92 +++++---- test/test_process_placement_opt.py | 14 ++ 22 files changed, 744 insertions(+), 120 deletions(-) create mode 100644 test/placement-tests/request_placement_vfmod.json create mode 100644 test/placement-tests/request_vfmod.json create mode 100644 test/placement-tests/response_vfmod.json create mode 100644 test/policy-local-files/QueryPolicy_vFW_TD.json create mode 100644 test/policy-local-files/affinity_vFW_TD.json create mode 100644 test/policy-local-files/vnfPolicy_vFW_TD.json create mode 100644 test/policy-local-files/vnfPolicy_vPGN_TD.json diff --git a/config/common_config.yaml b/config/common_config.yaml index c513d5e..c786d74 100644 --- a/config/common_config.yaml +++ b/config/common_config.yaml @@ -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 diff --git a/config/has_config.yaml b/config/has_config.yaml index cf8a80c..38a4781 100644 --- a/config/has_config.yaml +++ b/config/has_config.yaml @@ -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 diff --git a/osdf/adapters/local_data/local_policies.py b/osdf/adapters/local_data/local_policies.py index 6e49388..dc6837a 100644 --- a/osdf/adapters/local_data/local_policies.py +++ b/osdf/adapters/local_data/local_policies.py @@ -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: diff --git a/osdf/adapters/policy/interface.py b/osdf/adapters/policy/interface.py index 95bfacc..a7839c6 100644 --- a/osdf/adapters/policy/interface.py +++ b/osdf/adapters/policy/interface.py @@ -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'] diff --git a/osdf/models/api/placementRequest.py b/osdf/models/api/placementRequest.py index 55f0a98..7d6bde4 100644 --- a/osdf/models/api/placementRequest.py +++ b/osdf/models/api/placementRequest.py @@ -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)) diff --git a/osdf/optimizers/placementopt/conductor/api_builder.py b/osdf/optimizers/placementopt/conductor/api_builder.py index 187f9f5..08a7460 100644 --- a/osdf/optimizers/placementopt/conductor/api_builder.py +++ b/osdf/optimizers/placementopt/conductor/api_builder.py @@ -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 diff --git a/osdf/optimizers/placementopt/conductor/translation.py b/osdf/optimizers/placementopt/conductor/translation.py index 93b80bf..d14f3e1 100644 --- a/osdf/optimizers/placementopt/conductor/translation.py +++ b/osdf/optimizers/placementopt/conductor/translation.py @@ -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) diff --git a/osdf/templates/conductor_interface.json b/osdf/templates/conductor_interface.json index 7377c48..0b8e6a1 100755 --- a/osdf/templates/conductor_interface.json +++ b/osdf/templates/conductor_interface.json @@ -1,41 +1,39 @@ -{ - "name": "{{ name }}", - "files": {}, - "timeout": {{ timeout }}, - "limit": {{ limit }}, - "template": { - "homing_template_version": "2017-10-10", - "parameters": { - "service_name": "{{ service_type }}", - "service_id": "{{ service_id }}", - "customer_lat": {{ latitude }}, - "customer_long": {{ longitude }}, - "REQUIRED_DISK": "{{ required_disk }}", - "REQUIRED_MEM": "{{ required_mem }}" - }, - "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 %} - } - } -} +{ + "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 %} + } + } +} diff --git a/test/conductor/test_conductor_calls.py b/test/conductor/test_conductor_calls.py index 1a96da7..77d9a72 100644 --- a/test/conductor/test_conductor_calls.py +++ b/test/conductor/test_conductor_calls.py @@ -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() diff --git a/test/conductor/test_conductor_translation.py b/test/conductor/test_conductor_translation.py index 0c7da94..27711f5 100644 --- a/test/conductor/test_conductor_translation.py +++ b/test/conductor/test_conductor_translation.py @@ -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 index 0000000..4233416 --- /dev/null +++ b/test/placement-tests/request_placement_vfmod.json @@ -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 index 0000000..1e95e22 --- /dev/null +++ b/test/placement-tests/request_vfmod.json @@ -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 index 0000000..06e40de --- /dev/null +++ b/test/placement-tests/response_vfmod.json @@ -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 index 0000000..dcf7439 --- /dev/null +++ b/test/policy-local-files/QueryPolicy_vFW_TD.json @@ -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 index 0000000..371cbfc --- /dev/null +++ b/test/policy-local-files/affinity_vFW_TD.json @@ -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 diff --git a/test/policy-local-files/meta-valid-policies.txt b/test/policy-local-files/meta-valid-policies.txt index 772ec1a..99e3e88 100644 --- a/test/policy-local-files/meta-valid-policies.txt +++ b/test/policy-local-files/meta-valid-policies.txt @@ -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 index 0000000..efe8ffa --- /dev/null +++ b/test/policy-local-files/vnfPolicy_vFW_TD.json @@ -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 index 0000000..64740ce --- /dev/null +++ b/test/policy-local-files/vnfPolicy_vPGN_TD.json @@ -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 diff --git a/test/test_ConductorApiBuilder.py b/test/test_ConductorApiBuilder.py index f8683a3..7e38f4d 100644 --- a/test/test_ConductorApiBuilder.py +++ b/test/test_ConductorApiBuilder.py @@ -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() diff --git a/test/test_api_validation.py b/test/test_api_validation.py index 80e0ba0..389ff62 100644 --- a/test/test_api_validation.py +++ b/test/test_api_validation.py @@ -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()) diff --git a/test/test_get_opt_query_data.py b/test/test_get_opt_query_data.py index 880f93f..1e2db17 100644 --- a/test/test_get_opt_query_data.py +++ b/test/test_get_opt_query_data.py @@ -1,40 +1,52 @@ -# ------------------------------------------------------------------------- -# 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) - - -if __name__ == "__main__": - unittest.main() - +# ------------------------------------------------------------------------- +# 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() + diff --git a/test/test_process_placement_opt.py b/test/test_process_placement_opt.py index 3219675..62a1ce6 100644 --- a/test/test_process_placement_opt.py +++ b/test/test_process_placement_opt.py @@ -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() -- cgit 1.2.3-korg