From 75a0b893128d2fe9645f3131e0d41e7460422e68 Mon Sep 17 00:00:00 2001 From: Lukasz Rajewski Date: Wed, 13 Nov 2019 22:21:55 +0100 Subject: Add passthrough attributes Added passthrough attributes in placement request. Current attributes HAS request section changed to filtering_attributes and new passthrough attributes added in HAS request - this one is read from vnf policy passthroughAttributes section. Issue-ID: OPTFRA-610 Signed-off-by: Lukasz Rajewski Change-Id: Ie2719fdd94bb0b42ef7cf2cbf47f5f182a4e7347 --- conductor/conductor/controller/translator.py | 12 +++---- .../data/plugins/inventory_provider/aai.py | 41 ++++++++++++++-------- conductor/conductor/solver/service.py | 8 ++++- .../tests/unit/controller/test_translator.py | 6 ++-- .../plugins/inventory_provider/demand_list.json | 2 +- .../inventory_provider/service_demand_list.json | 2 +- .../data/plugins/inventory_provider/test_aai.py | 20 +++++++++++ .../inventory_provider/vfmodule_candidates.json | 3 ++ .../inventory_provider/vfmodule_demand_list.json | 5 ++- 9 files changed, 72 insertions(+), 27 deletions(-) diff --git a/conductor/conductor/controller/translator.py b/conductor/conductor/controller/translator.py index 85f63af..45e0ee2 100644 --- a/conductor/conductor/controller/translator.py +++ b/conductor/conductor/controller/translator.py @@ -47,7 +47,7 @@ INVENTORY_TYPES = ['cloud', 'service', 'transport', 'vfmodule'] DEFAULT_INVENTORY_PROVIDER = INVENTORY_PROVIDERS[0] CANDIDATE_KEYS = ['candidate_id', 'cost', 'inventory_type', 'location_id', 'location_type'] -DEMAND_KEYS = ['attributes', 'candidates', 'complex', 'conflict_identifier', +DEMAND_KEYS = ['filtering_attributes', 'passthrough_attributes', 'candidates', 'complex', 'conflict_identifier', 'customer_id', 'default_cost', 'excluded_candidates', 'existing_placement', 'flavor', 'inventory_provider', 'inventory_type', 'port_key', 'region', 'required_candidates', @@ -448,11 +448,11 @@ class Translator(object): # For service and vfmodule inventories, customer_id and # service_type MUST be specified if inventory_type == 'service' or inventory_type == 'vfmodule': - attributes = requirement.get('attributes') + filtering_attributes = requirement.get('filtering_attributes') - if attributes: - customer_id = attributes.get('customer-id') - global_customer_id = attributes.get('global-customer-id') + if filtering_attributes: + customer_id = filtering_attributes.get('customer-id') + global_customer_id = filtering_attributes.get('global-customer-id') if global_customer_id: customer_id = global_customer_id else: @@ -465,7 +465,7 @@ class Translator(object): "Customer ID not specified for " "demand {}".format(name) ) - if not attributes and not service_type: + if not filtering_attributes and not service_type: raise TranslatorException( "Service Type not specified for " "demand {}".format(name) diff --git a/conductor/conductor/data/plugins/inventory_provider/aai.py b/conductor/conductor/data/plugins/inventory_provider/aai.py index b69655c..26b390a 100644 --- a/conductor/conductor/data/plugins/inventory_provider/aai.py +++ b/conductor/conductor/data/plugins/inventory_provider/aai.py @@ -1187,26 +1187,27 @@ class AAI(base.InventoryProviderBase): inventory_type = requirement.get('inventory_type').lower() service_subscription = requirement.get('service_subscription') candidate_uniqueness = requirement.get('unique', 'true') - attributes = requirement.get('attributes') + filtering_attributes = requirement.get('filtering_attributes') + passthrough_attributes = requirement.get('passthrough_attributes') #TODO: may need to support multiple service_type and customer_id in the futrue #TODO: make it consistent for dash and underscore - if attributes: + if filtering_attributes: # catch equipment-role and service-type from template - equipment_role = attributes.get('equipment-role') - service_type = attributes.get('service-type') + equipment_role = filtering_attributes.get('equipment-role') + service_type = filtering_attributes.get('service-type') if equipment_role: service_type = equipment_role # catch global-customer-id and customer-id from template - global_customer_id = attributes.get('global-customer-id') - customer_id = attributes.get('customer-id') + global_customer_id = filtering_attributes.get('global-customer-id') + customer_id = filtering_attributes.get('customer-id') if global_customer_id: customer_id = global_customer_id - model_invariant_id = attributes.get('model-invariant-id') - model_version_id = attributes.get('model-version-id') - service_role = attributes.get('service-role') + model_invariant_id = filtering_attributes.get('model-invariant-id') + model_version_id = filtering_attributes.get('model-version-id') + service_role = filtering_attributes.get('service-role') # For earlier else: service_type = equipment_role = requirement.get('service_type') @@ -1306,7 +1307,7 @@ class AAI(base.InventoryProviderBase): cloud_region_attr['complex-name'] = region['complex_name'] cloud_region_attr['physical-location-id'] = region['physical_location_id'] - if attributes and (not self.match_inventory_attributes(attributes, cloud_region_attr, candidate['candidate_id'])): + if filtering_attributes and (not self.match_inventory_attributes(filtering_attributes, cloud_region_attr, candidate['candidate_id'])): self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, reason='attributes and match invetory attributes') continue @@ -1330,6 +1331,7 @@ class AAI(base.InventoryProviderBase): if required_candidates and not self.match_candidate_by_list(candidate, required_candidates, False, name, triage_translator_data): continue + self.add_passthrough_attributes(candidate, passthrough_attributes, name, triage_translator_data) # add candidate to demand candidates resolved_demands[name].append(candidate) LOG.debug(">>>>>>> Candidate <<<<<<<") @@ -1340,7 +1342,7 @@ class AAI(base.InventoryProviderBase): # First level query to get the list of generic vnfs vnf_by_model_invariant = list() - if attributes and model_invariant_id: + if filtering_attributes and model_invariant_id: raw_path = '/network/generic-vnfs/' \ '?model-invariant-id={}&depth=0'.format(model_invariant_id) @@ -1477,7 +1479,7 @@ class AAI(base.InventoryProviderBase): vnf['cloud-region-id'] = cloud_region_id vnf['physical-location-id'] = candidate.get('physical_location_id') - if attributes and not self.match_inventory_attributes(attributes, vnf, candidate['candidate_id']): + if filtering_attributes and not self.match_inventory_attributes(filtering_attributes, vnf, candidate['candidate_id']): self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, reason="attibute check error") continue @@ -1502,6 +1504,7 @@ class AAI(base.InventoryProviderBase): triage_translator_data): continue else: + self.add_passthrough_attributes(candidate, passthrough_attributes, name, triage_translator_data) resolved_demands[name].append(candidate) LOG.debug(">>>>>>> Candidate <<<<<<<") LOG.debug(json.dumps(candidate, indent=4)) @@ -1511,7 +1514,7 @@ class AAI(base.InventoryProviderBase): # First level query to get the list of generic vnfs vnf_by_model_invariant = list() - if attributes and model_invariant_id: + if filtering_attributes and model_invariant_id: raw_path = '/network/generic-vnfs/' \ '?model-invariant-id={}&depth=0'.format(model_invariant_id) @@ -1715,7 +1718,7 @@ class AAI(base.InventoryProviderBase): vnf_vf_module_inventory['physical-location-id'] = candidate.get('physical_location_id') vnf_vf_module_inventory['service_instance_id'] = vs_service_instance_id - if attributes and not self.match_inventory_attributes(attributes, vnf_vf_module_inventory, + if filtering_attributes and not self.match_inventory_attributes(filtering_attributes, vnf_vf_module_inventory, candidate['candidate_id']): self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, @@ -1744,6 +1747,7 @@ class AAI(base.InventoryProviderBase): triage_translator_data): continue else: + self.add_passthrough_attributes(candidate, passthrough_attributes, name, triage_translator_data) resolved_demands[name].append(candidate) LOG.debug(">>>>>>> Candidate <<<<<<<") LOG.debug(json.dumps(candidate, indent=4)) @@ -1870,6 +1874,7 @@ class AAI(base.InventoryProviderBase): candidate['region'] = \ complex_info.get('region') + self.add_passthrough_attributes(candidate, passthrough_attributes, name, triage_translator_data) # add candidate to demand candidates resolved_demands[name].append(candidate) @@ -1878,6 +1883,14 @@ class AAI(base.InventoryProviderBase): " {}".format(inventory_type)) return resolved_demands + def add_passthrough_attributes(self, candidate, passthrough_attributes, demand_name, triage_translator_data): + if passthrough_attributes is None: + return + if len(passthrough_attributes.items()) > 0: + candidate['passthrough_attributes'] = dict() + for key, value in passthrough_attributes.items(): + candidate['passthrough_attributes'][key] = value + def match_region(self, candidate, restricted_region_id, restricted_complex_id, demand_name, triage_translator_data): if self.match_candidate_attribute( candidate, diff --git a/conductor/conductor/solver/service.py b/conductor/conductor/solver/service.py index 978f735..fb7b2f0 100644 --- a/conductor/conductor/solver/service.py +++ b/conductor/conductor/solver/service.py @@ -556,7 +556,13 @@ class SolverService(cotyledon.Service): if resource.get('conflict_id'): rec["candidate"]["conflict_id"] = resource.get("conflict_id") - + if resource.get('passthrough_attributes'): + for key, value in resource.get('passthrough_attributes').items(): + if key in rec["attributes"]: + LOG.error('Passthrough attribute {} in demand {} already exist for candidate {}'. + format(key, demand_name, rec['candidate_id'])) + else: + rec["attributes"][key] = value # TODO(snarayanan): Add total value to recommendations? # msg = "--- total value of decision = {}" # LOG.debug(msg.format(_best_path.total_value)) diff --git a/conductor/conductor/tests/unit/controller/test_translator.py b/conductor/conductor/tests/unit/controller/test_translator.py index 0d4048a..c30d937 100644 --- a/conductor/conductor/tests/unit/controller/test_translator.py +++ b/conductor/conductor/tests/unit/controller/test_translator.py @@ -180,7 +180,7 @@ class TestNoExceptionTranslator(unittest.TestCase): "candidate_id": ["e765d576-8755-4145-8536-0bb6d9b1dc9a"], "inventory_type": "vfmodule" }], - "attributes": { + "filtering_attributes": { "prov-status": "ACTIVE", "global-customer-id": "Demonstration", "model-version-id": "763731df-84fd-494b-b824-01fc59a5ff2d", @@ -209,7 +209,7 @@ class TestNoExceptionTranslator(unittest.TestCase): "candidate_id": ["e765d576-8755-4145-8536-0bb6d9b1dc9a"], "inventory_type": "vfmodule" }], - "attributes": { + "filtering_attributes": { "prov-status": "ACTIVE", "global-customer-id": "Demonstration", "model-version-id": "763731df-84fd-494b-b824-01fc59a5ff2d", @@ -236,7 +236,7 @@ class TestNoExceptionTranslator(unittest.TestCase): "service_resource_id": "vFW-SINK-XX", "vlan_key": "vlan_key", "service_type": "vFW-SINK-XX", - "attributes": { + "filtering_attributes": { "cloud-region-id": { "get_param": "chosen_region" }, diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/demand_list.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/demand_list.json index c817fbb..8a54a25 100644 --- a/conductor/conductor/tests/unit/data/plugins/inventory_provider/demand_list.json +++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/demand_list.json @@ -1,7 +1,7 @@ { "demand_name": [ { - "attributes": { + "filtering_attributes": { "global-customer-id": "customer-123", "equipment-role": "TEST", "service-type": "TEST" diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/service_demand_list.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/service_demand_list.json index fb1059a..cffba57 100644 --- a/conductor/conductor/tests/unit/data/plugins/inventory_provider/service_demand_list.json +++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/service_demand_list.json @@ -5,7 +5,7 @@ "inventory_type": "service", "port_key": "vlan_port", "vlan_key": "vlan_key", - "attributes": { + "filtering_attributes": { "global-customer-id": "Demonstration", "model-version-id": "e02a7e5c-9d27-4360-ab7c-73bb83b07e3b", "model-invariant-id": "762472ef-5284-4daa-ab32-3e7bee2ec355" diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py b/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py index d77b644..9d1245d 100644 --- a/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py +++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py @@ -610,6 +610,26 @@ tenant/3c6c471ada7747fe8ff7f28e100b61e8/vservers/vserver/00bddefc-126e-4e4f-a18d self.assertIsNone(self.aai_ep.resolve_service_instance_id_for_vnf(candidate, bad_vnf, customer_id, service_type, demand_name, triage_translator_data)) + def test_add_passthrough_parameters(self): + triage_translator_data = None + + candidate = dict() + candidate['candidate_id'] = 'some_id' + candidate['location_id'] = 'some_location_id' + candidate['inventory_type'] = 'service' + + parameters = dict() + parameters['param_one'] = "value" + parameters['param_two'] = "value" + + candidate_info = copy.deepcopy(candidate) + candidate_info['passthrough_attributes'] = dict() + candidate_info['passthrough_attributes']['param_one'] = "value" + candidate_info['passthrough_attributes']['param_two'] = "value" + + self.aai_ep.add_passthrough_attributes(candidate, parameters, 'demand', None) + self.assertDictEqual(candidate, candidate_info) + def test_match_candidate_by_list(self): TraigeTranslator.collectDroppedCandiate = mock.MagicMock(return_value=None) triage_translator_data = None diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/vfmodule_candidates.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/vfmodule_candidates.json index 7343f34..e7b398e 100644 --- a/conductor/conductor/tests/unit/data/plugins/inventory_provider/vfmodule_candidates.json +++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/vfmodule_candidates.json @@ -51,6 +51,9 @@ "cloud_owner": "CloudOwner", "vnf-type": "5G_EVE_Demo/5G_EVE_PKG 0", "nf-name": "vFW-PKG-MC", + "passthrough_attributes": { + "td-role": "anchor" + }, "inventory_type": "vfmodule", "sriov_automation": "false", "uniqueness": "false", diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/vfmodule_demand_list.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/vfmodule_demand_list.json index 8069e0f..c6d34aa 100644 --- a/conductor/conductor/tests/unit/data/plugins/inventory_provider/vfmodule_demand_list.json +++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/vfmodule_demand_list.json @@ -6,7 +6,7 @@ "port_key": "vlan_port", "vlan_key": "vlan_key", "unique": "false", - "attributes": { + "filtering_attributes": { "prov-status": "ACTIVE", "global-customer-id": "Demonstration", "model-version-id": "e02a7e5c-9d27-4360-ab7c-73bb83b07e3b", @@ -15,6 +15,9 @@ "cloud-region-id": "RegionOne", "service_instance_id": "3e8d118c-10ca-4b4b-b3db-089b5e9e6a1c" }, + "passthrough_attributes": { + "td-role": "anchor" + }, "service_type": "vPGN-XX", "excluded_candidates": [{ "inventory_type": "vfmodule", -- cgit 1.2.3-korg