diff options
Diffstat (limited to 'conductor/conductor/data/plugins/inventory_provider/aai.py')
-rw-r--r-- | conductor/conductor/data/plugins/inventory_provider/aai.py | 1065 |
1 files changed, 704 insertions, 361 deletions
diff --git a/conductor/conductor/data/plugins/inventory_provider/aai.py b/conductor/conductor/data/plugins/inventory_provider/aai.py index 31064a6..d6fb724 100644 --- a/conductor/conductor/data/plugins/inventory_provider/aai.py +++ b/conductor/conductor/data/plugins/inventory_provider/aai.py @@ -20,6 +20,7 @@ import re import time import uuid +import copy import json from oslo_config import cfg @@ -787,6 +788,78 @@ class AAI(base.InventoryProviderBase): body = response.json() return body.get("generic-vnf", []) + def resolove_v_server_for_candidate(self, candidate, vs_link, add_interfaces, demand_name, triage_translator_data): + if not vs_link: + LOG.error(_LE("{} VSERVER link information not " + "available from A&AI").format(demand_name)) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="VSERVER link information not") + return None # move ahead with the next vnf + + if add_interfaces: + vs_link = vs_link + '?depth=2' + vs_path = self._get_aai_path_from_link(vs_link) + if not vs_path: + LOG.error(_LE("{} VSERVER path information not " + "available from A&AI - {}"). + format(demand_name, vs_path)) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="VSERVER path information not available from A&AI") + return None # move ahead with the next vnf + path = self._aai_versioned_path(vs_path) + response = self._request( + path=path, context="demand, VSERVER", + value="{}, {}".format(demand_name, vs_path)) + if response is None or response.status_code != 200: + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason=response.status_code) + return None + return response.json() + + def resolve_vf_modules_for_generic_vnf(self, candidate, vnf, demand_name, triage_translator_data): + raw_path = '/network/generic-vnfs/generic-vnf/{}?depth=1'.format(vnf.get("vnf-id")) + path = self._aai_versioned_path(raw_path) + + response = self._request('get', path=path, data=None) + if response is None or response.status_code != 200: + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason=response) + return None + generic_vnf_details = response.json() + + if generic_vnf_details is None or not generic_vnf_details.get('vf-modules') \ + or not generic_vnf_details.get('vf-modules').get('vf-module'): + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="Generic-VNF No detailed data for VF-modules") + return None + else: + return generic_vnf_details.get('vf-modules').get('vf-module') + + def resolve_cloud_regions_by_cloud_region_id(self, cloud_region_id): + cloud_region_uri = '/cloud-infrastructure/cloud-regions' \ + '/?cloud-region-id=' \ + + cloud_region_id + path = self._aai_versioned_path(cloud_region_uri) + + response = self._request('get', + path=path, + data=None) + if response is None or response.status_code != 200: + return None + + body = response.json() + return body.get('cloud-region', []) + def assign_candidate_existing_placement(self, candidate, existing_placement): """Assign existing_placement and cost parameters to candidate @@ -817,6 +890,289 @@ class AAI(base.InventoryProviderBase): return ''.join(conflict_id_list) + def resolve_v_server_links_for_vnf(self, vnf): + related_to = "vserver" + search_key = "cloud-region.cloud-owner" + rl_data_list = self._get_aai_rel_link_data( + data=vnf, related_to=related_to, + search_key=search_key) + vs_link_list = list() + for i in range(0, len(rl_data_list)): + vs_link_list.append(rl_data_list[i].get('link')) + return vs_link_list + + def resolve_complex_info_link_for_v_server(self, candidate, v_server, cloud_owner, cloud_region_id, service_type, + demand_name, triage_translator_data): + related_to = "pserver" + rl_data_list = self._get_aai_rel_link_data( + data=v_server, + related_to=related_to, + search_key=None + ) + if len(rl_data_list) > 1: + self._log_multiple_item_error( + demand_name, service_type, related_to, "item", + "VSERVER", v_server) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, reason="item VSERVER") + return None + rl_data = rl_data_list[0] + ps_link = rl_data.get('link') + + # Third level query to get cloud region from pserver + if not ps_link: + LOG.error(_LE("{} pserver related link " + "not found in A&AI: {}"). + format(demand_name, rl_data)) + # if HPA_feature is disabled + if not self.conf.HPA_enabled: + # Triage Tool Feature Changes + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="ps link not found") + return None + else: + if not (cloud_owner and cloud_region_id): + LOG.error("{} cloud-owner or cloud-region not " + "available from A&AI". + format(demand_name)) + # Triage Tool Feature Changes + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="Cloud owner and cloud region " + "id not found") + return None # move ahead with the next vnf + cloud_region_uri = \ + '/cloud-infrastructure/cloud-regions/cloud-region' \ + '/?cloud-owner=' + cloud_owner \ + + '&cloud-region-id=' + cloud_region_id + path = self._aai_versioned_path(cloud_region_uri) + response = self._request('get', + path=path, + data=None) + if response is None or response.status_code != 200: + # Triage Tool Feature Changes + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason=response) + return None + body = response.json() + else: + ps_path = self._get_aai_path_from_link(ps_link) + if not ps_path: + LOG.error(_LE("{} pserver path information " + "not found in A&AI: {}"). + format(demand_name, ps_link)) + # Triage Tool Feature Changes + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="ps path not found") + return None # move ahead with the next vnf + path = self._aai_versioned_path(ps_path) + response = self._request( + path=path, context="PSERVER", value=ps_path) + if response is None or response.status_code != 200: + # Triage Tool Feature Changes + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason=response) + return None + body = response.json() + + related_to = "complex" + search_key = "complex.physical-location-id" + rl_data_list = self._get_aai_rel_link_data( + data=body, + related_to=related_to, + search_key=search_key + ) + if len(rl_data_list) > 1: + if not self.match_vserver_attribute(rl_data_list): + self._log_multiple_item_error( + demand_name, service_type, related_to, search_key, "PSERVER", body) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], + demand_name, triage_translator_data, + reason="PSERVER error") + return None + return rl_data_list[0] + + def resolve_cloud_region_id_and_version_for_vnf(self, candidate, vnf, service_type, demand_name, + triage_translator_data): + related_to = "vserver" + search_key = "cloud-region.cloud-region-id" + + rl_data_list = self._get_aai_rel_link_data( + data=vnf, + related_to=related_to, + search_key=search_key + ) + if len(rl_data_list) > 1: + if not self.match_vserver_attribute(rl_data_list): + self._log_multiple_item_error( + demand_name, service_type, related_to, search_key, + "VNF", vnf) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="VNF error") + return None, None + cloud_region_rl_data = rl_data_list[0] + cloud_region_id = cloud_region_rl_data.get('d_value') + + # get version for service candidate + cloud_region_version_rl_data = {'d_value': ''} + if cloud_region_id: + regions = self.resolve_cloud_regions_by_cloud_region_id(cloud_region_id) + if regions is None: + return cloud_region_rl_data, None + + for region in regions: + if "cloud-region-version" in region: + cloud_region_version_rl_data['d_value'] = self._get_version_from_string(region["cloud-region-version"]) + + return cloud_region_rl_data, cloud_region_version_rl_data + + def resolve_cloud_owner_for_vnf(self, candidate, vnf, service_type, demand_name, triage_translator_data): + related_to = "vserver" + search_key = "cloud-region.cloud-owner" + rl_data_list = self._get_aai_rel_link_data( + data=vnf, related_to=related_to, + search_key=search_key) + + if len(rl_data_list) > 1: + if not self.match_vserver_attribute(rl_data_list): + self._log_multiple_item_error( + demand_name, service_type, related_to, search_key, + "VNF", vnf) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="VNF error") + return None + return rl_data_list[0] + + def resolve_global_customer_id_for_vnf(self, candidate, vnf, customer_id, service_type, demand_name, + triage_translator_data): + related_to = "service-instance" + search_key = "customer.global-customer-id" + match_key = "customer.global-customer-id" + rl_data_list = self._get_aai_rel_link_data( + data=vnf, + related_to=related_to, + search_key=search_key, + match_dict={'key': match_key, + 'value': customer_id} + ) + if len(rl_data_list) > 1: + if not self.match_vserver_attribute(rl_data_list): + self._log_multiple_item_error( + demand_name, service_type, related_to, search_key, "VNF", vnf) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], + demand_name, triage_translator_data, + reason=" match_vserver_attribute generic-vnf") + return None + return rl_data_list[0] + + def resolve_service_instance_id_for_vnf(self, candidate, vnf, customer_id, service_type, demand_name, + triage_translator_data): + related_to = "service-instance" + search_key = "service-instance.service-instance-id" + match_key = "customer.global-customer-id" + rl_data_list = self._get_aai_rel_link_data( + data=vnf, + related_to=related_to, + search_key=search_key, + match_dict={'key': match_key, + 'value': customer_id} + ) + if len(rl_data_list) > 1: + if not self.match_vserver_attribute(rl_data_list): + self._log_multiple_item_error( + demand_name, service_type, related_to, search_key, "VNF", vnf) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], + demand_name, triage_translator_data, + reason="multiple_item_error generic-vnf") + return None + return rl_data_list[0] + + def build_complex_info_for_candidate(self, candidate, vnf, complex_list, service_type, demand_name, + triage_translator_data): + if not complex_list or \ + len(complex_list) < 1: + LOG.error("Complex information not " + "available from A&AI") + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="Complex information not available from A&AI") + return + + # In the scenario where no pserver information is available + # assumption here is that cloud-region does not span across + # multiple complexes + if len(complex_list) > 1: + related_to = "complex" + search_key = "complex.physical-location-id" + if not self.match_vserver_attribute(complex_list): + self._log_multiple_item_error( + demand_name, service_type, related_to, search_key, + "VNF", vnf) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="Generic-vnf error") + return + + rl_data = complex_list[0] + complex_link = rl_data.get('link') + complex_id = rl_data.get('d_value') + + # Final query for the complex information + if not (complex_link and complex_id): + LOG.debug("{} complex information not " + "available from A&AI - {}". + format(demand_name, complex_link)) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="Complex information not available from A&AI") + return # move ahead with the next vnf + else: + complex_info = self._get_complex( + complex_link=complex_link, + complex_id=complex_id + ) + if not complex_info: + LOG.debug("{} complex information not " + "available from A&AI - {}". + format(demand_name, complex_link)) + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], demand_name, + triage_translator_data, + reason="Complex information not available from A&AI") + return # move ahead with the next vnf + candidate['physical_location_id'] = \ + complex_id + candidate['complex_name'] = \ + complex_info.get('complex-name') + candidate['latitude'] = \ + complex_info.get('latitude') + candidate['longitude'] = \ + complex_info.get('longitude') + candidate['state'] = \ + complex_info.get('state') + candidate['country'] = \ + complex_info.get('country') + candidate['city'] = \ + complex_info.get('city') + candidate['region'] = \ + complex_info.get('region') def resolve_demands(self, demands, plan_info, triage_translator_data): """Resolve demands into inventory candidate lists""" @@ -958,56 +1314,28 @@ class AAI(base.InventoryProviderBase): if conflict_identifier: candidate['conflict_id'] = self.resovle_conflict_id(conflict_identifier, candidate) - if self.match_candidate_attribute( - candidate, "candidate_id", - restricted_region_id, name, - inventory_type) or \ - self.match_candidate_attribute( - candidate, "physical_location_id", - restricted_complex_id, name, - inventory_type): + if not self.match_region(candidate, restricted_region_id, restricted_complex_id, name, + triage_translator_data): continue self.assign_candidate_existing_placement(candidate, existing_placement) # Pick only candidates not in the excluded list # if excluded candidate list is provided - if excluded_candidates: - has_excluded_candidate = False - for excluded_candidate in excluded_candidates: - if excluded_candidate \ - and excluded_candidate.get('inventory_type') == \ - candidate.get('inventory_type') \ - and excluded_candidate.get('candidate_id') == \ - candidate.get('candidate_id'): - has_excluded_candidate = True - break - - if has_excluded_candidate: - continue + if excluded_candidates and self.match_candidate_by_list(candidate, excluded_candidates, True, name, triage_translator_data): + continue # Pick only candidates in the required list # if required candidate list is provided - if required_candidates: - has_required_candidate = False - for required_candidate in required_candidates: - if required_candidate \ - and required_candidate.get('inventory_type') \ - == candidate.get('inventory_type') \ - and required_candidate.get('candidate_id') \ - == candidate.get('candidate_id'): - has_required_candidate = True - break - - if not has_required_candidate: - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="has_required_candidate") - continue + if required_candidates and not self.match_candidate_by_list(candidate, required_candidates, False, name, triage_translator_data): + continue # add candidate to demand candidates resolved_demands[name].append(candidate) + LOG.debug(">>>>>>> Candidate <<<<<<<") + LOG.debug(json.dumps(candidate, indent=4)) - elif inventory_type == 'service' \ + elif (inventory_type == 'service') \ and customer_id: # First level query to get the list of generic vnfs @@ -1045,7 +1373,7 @@ class AAI(base.InventoryProviderBase): candidate = dict() candidate['inventory_provider'] = 'aai' candidate['service_resource_id'] = service_resource_id - candidate['inventory_type'] = 'service' + candidate['inventory_type'] = inventory_type candidate['candidate_id'] = '' candidate['location_id'] = '' candidate['location_type'] = 'att_aic' @@ -1060,48 +1388,25 @@ class AAI(base.InventoryProviderBase): # start populating the candidate candidate['host_id'] = vnf.get("vnf-name") - related_to = "vserver" - search_key = "cloud-region.cloud-owner" - rl_data_list = self._get_aai_rel_link_data( - data=vnf, related_to=related_to, - search_key=search_key) - - if len(rl_data_list) > 1: - if not self.match_vserver_attribute(rl_data_list): - self._log_multiple_item_error( - name, service_type, related_to, search_key, - "GENERIC-VNF", vnf) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="Generic -vnf error") - continue - rl_data = rl_data_list[0] - - vs_link_list = list() - for i in range(0, len(rl_data_list)): - vs_link_list.append(rl_data_list[i].get('link')) + rl_data = self.resolve_cloud_owner_for_vnf(candidate, vnf, service_type, name, + triage_translator_data) + if rl_data is None: + continue + else: + cloud_owner = rl_data.get('d_value') - cloud_owner = rl_data.get('d_value') candidate['cloud_owner'] = cloud_owner + cloud_region_id_rl_data, cloud_region_version_rl_data = self.resolve_cloud_region_id_and_version_for_vnf( + candidate, vnf, service_type, name, triage_translator_data) - search_key = "cloud-region.cloud-region-id" + if cloud_region_id_rl_data is None or cloud_region_version_rl_data is None: + continue - rl_data_list = self._get_aai_rel_link_data( - data=vnf, - related_to=related_to, - search_key=search_key - ) - if len(rl_data_list) > 1: - if not self.match_vserver_attribute(rl_data_list): - self._log_multiple_item_error( - name, service_type, related_to, search_key, - "GENERIC-VNF", vnf) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason=" generic-vnf error") - continue - rl_data = rl_data_list[0] - cloud_region_id = rl_data.get('d_value') + cloud_region_id = cloud_region_id_rl_data.get('d_value') + cloud_region_version = cloud_region_version_rl_data.get('d_value') candidate['location_id'] = cloud_region_id + candidate['cloud_region_version'] = cloud_region_version # Added vim-id for short-term workaround if self.conf.HPA_enabled: @@ -1110,28 +1415,6 @@ class AAI(base.InventoryProviderBase): candidate['vim-id'] = \ candidate['cloud_owner'] + '_' + cloud_region_id - # get version for service candidate - if cloud_region_id: - cloud_region_uri = '/cloud-infrastructure/cloud-regions' \ - '/?cloud-region-id=' \ - + cloud_region_id - path = self._aai_versioned_path(cloud_region_uri) - - response = self._request('get', - path=path, - data=None) - if response is None or response.status_code != 200: - return None - - body = response.json() - regions = body.get('cloud-region', []) - - for region in regions: - if "cloud-region-version" in region: - candidate['cloud_region_version'] = \ - self._get_version_from_string( - region["cloud-region-version"]) - if self.check_sriov_automation( candidate['cloud_region_version'], name, candidate['host_id']): @@ -1139,250 +1422,60 @@ class AAI(base.InventoryProviderBase): else: candidate['sriov_automation'] = 'false' - related_to = "service-instance" - search_key = "customer.global-customer-id" - match_key = "customer.global-customer-id" - rl_data_list = self._get_aai_rel_link_data( - data=vnf, - related_to=related_to, - search_key=search_key, - match_dict={'key': match_key, - 'value': customer_id} - ) - if len(rl_data_list) > 1: - if not self.match_vserver_attribute(rl_data_list): - self._log_multiple_item_error( - name, service_type, related_to, search_key, - "GENERIC-VNF", vnf) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name,triage_translator_data, - reason=" match_vserver_attribute generic-vnf") - continue - rl_data = rl_data_list[0] - vs_cust_id = rl_data.get('d_value') + rl_data = self.resolve_global_customer_id_for_vnf(candidate, vnf, customer_id, service_type, + name, triage_translator_data) + if rl_data is None: + continue + else: + vs_cust_id = rl_data.get('d_value') - search_key = "service-instance.service-instance-id" - match_key = "customer.global-customer-id" - rl_data_list = self._get_aai_rel_link_data( - data=vnf, - related_to=related_to, - search_key=search_key, - match_dict={'key': match_key, - 'value': customer_id} - ) - if len(rl_data_list) > 1: - if not self.match_vserver_attribute(rl_data_list): - self._log_multiple_item_error( - name, service_type, related_to, search_key, - "GENERIC-VNF", vnf) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name,triage_translator_data, - reason="multiple_item_error generic-vnf") - continue - rl_data = rl_data_list[0] - vs_service_instance_id = rl_data.get('d_value') + rl_data = self.resolve_service_instance_id_for_vnf(candidate, vnf, customer_id, + service_type, name, triage_translator_data) + if rl_data is None: + continue + else: + vs_service_instance_id = rl_data.get('d_value') if vs_cust_id and vs_cust_id == customer_id: candidate['candidate_id'] = \ vs_service_instance_id else: # vserver is for a different customer - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, reason= "vserver is for a different customer") + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], name, + triage_translator_data, + reason= "vserver is for a different customer") continue # Second level query to get the pserver from vserver complex_list = list() + vs_link_list = self.resolve_v_server_links_for_vnf(vnf) for vs_link in vs_link_list: - if not vs_link: - LOG.error(_LE("{} VSERVER link information not " - "available from A&AI").format(name)) - LOG.debug("Related link data: {}".format(rl_data)) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="VSERVER link information not") - continue # move ahead with the next vnf - - vs_path = self._get_aai_path_from_link(vs_link) - if not vs_path: - LOG.error(_LE("{} VSERVER path information not " - "available from A&AI - {}"). - format(name, vs_path)) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="VSERVER path information not available from A&AI") - continue # move ahead with the next vnf - path = self._aai_versioned_path(vs_path) - response = self._request( - path=path, context="demand, VSERVER", - value="{}, {}".format(name, vs_path)) - if response is None or response.status_code != 200: - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason=response.status_code) + body = self.resolove_v_server_for_candidate(candidate, vs_link, True, name, + triage_translator_data) + if body is None: continue - body = response.json() - related_to = "pserver" - rl_data_list = self._get_aai_rel_link_data( - data=body, - related_to=related_to, - search_key=None - ) - if len(rl_data_list) > 1: - self._log_multiple_item_error( - name, service_type, related_to, "item", - "VSERVER", body) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="item VSERVER") + rl_data = self.resolve_complex_info_link_for_v_server(candidate, body, cloud_owner, + cloud_region_id, service_type, + name, triage_translator_data) + if rl_data is None: continue - rl_data = rl_data_list[0] - ps_link = rl_data.get('link') - - # Third level query to get cloud region from pserver - if not ps_link: - LOG.error(_LE("{} pserver related link " - "not found in A&AI: {}"). - format(name, rl_data)) - # if HPA_feature is disabled - if not self.conf.HPA_enabled: - # Triage Tool Feature Changes - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], - candidate['location_id'], name, - triage_translator_data, - reason="ps link not found") - continue - else: - if not (cloud_owner and cloud_region_id): - LOG.error("{} cloud-owner or cloud-region not " - "available from A&AI". - format(name)) - # Triage Tool Feature Changes - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], - candidate['location_id'], name, - triage_translator_data, - reason="Cloud owner and cloud region " - "id not found") - continue # move ahead with the next vnf - cloud_region_uri = \ - '/cloud-infrastructure/cloud-regions/cloud-region' \ - '/?cloud-owner=' + cloud_owner \ - + '&cloud-region-id=' + cloud_region_id - path = self._aai_versioned_path(cloud_region_uri) - response = self._request('get', - path=path, - data=None) - if response is None or response.status_code != 200: - # Triage Tool Feature Changes - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], - candidate['location_id'], name, - triage_translator_data, - reason=response) - continue - body = response.json() - else: - ps_path = self._get_aai_path_from_link(ps_link) - if not ps_path: - LOG.error(_LE("{} pserver path information " - "not found in A&AI: {}"). - format(name, ps_link)) - # Triage Tool Feature Changes - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], - candidate['location_id'], name, - triage_translator_data, - reason="ps path not found") - continue # move ahead with the next vnf - path = self._aai_versioned_path(ps_path) - response = self._request( - path=path, context="PSERVER", value=ps_path) - if response is None or response.status_code != 200: - # Triage Tool Feature Changes - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], - candidate['location_id'], name, - triage_translator_data, - reason=response) - continue - body = response.json() - - related_to = "complex" - search_key = "complex.physical-location-id" - rl_data_list = self._get_aai_rel_link_data( - data=body, - related_to=related_to, - search_key=search_key - ) - if len(rl_data_list) > 1: - if not self.match_vserver_attribute(rl_data_list): - self._log_multiple_item_error( - name, service_type, related_to, search_key, - "PSERVER", body) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="PSERVER error") - continue - rl_data = rl_data_list[0] - complex_list.append(rl_data) - - if not complex_list or \ - len(complex_list) < 1: - LOG.error("Complex information not " - "available from A&AI") - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="Complex information not available from A&AI") - continue - # In the scenario where no pserver information is available - # assumption here is that cloud-region does not span across - # multiple complexes - if len(complex_list) > 1: - if not self.match_vserver_attribute(complex_list): - self._log_multiple_item_error( - name, service_type, related_to, search_key, - "GENERIC-VNF", vnf) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="Generic-vnf error") - continue + complex_list.append(rl_data) - rl_data = complex_list[0] - complex_link = rl_data.get('link') - complex_id = rl_data.get('d_value') + self.build_complex_info_for_candidate(candidate, vnf, complex_list, service_type, name, + triage_translator_data) - # Final query for the complex information - if not (complex_link and complex_id): - LOG.debug("{} complex information not " - "available from A&AI - {}". - format(name, complex_link)) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="Complex information not available from A&AI") - continue # move ahead with the next vnf - else: - complex_info = self._get_complex( - complex_link=complex_link, - complex_id=complex_id - ) - if not complex_info: - LOG.debug("{} complex information not " - "available from A&AI - {}". - format(name, complex_link)) - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="Complex information not available from A&AI") - continue # move ahead with the next vnf - candidate['physical_location_id'] = \ - complex_id - candidate['complex_name'] = \ - complex_info.get('complex-name') - candidate['latitude'] = \ - complex_info.get('latitude') - candidate['longitude'] = \ - complex_info.get('longitude') - candidate['state'] = \ - complex_info.get('state') - candidate['country'] = \ - complex_info.get('country') - candidate['city'] = \ - complex_info.get('city') - candidate['region'] = \ - complex_info.get('region') + if "complex_name" not in candidate: + continue # add specifal parameters for comparsion vnf['global-customer-id'] = customer_id vnf['customer-id'] = customer_id vnf['cloud-region-id'] = cloud_region_id - vnf['physical-location-id'] = complex_id + vnf['physical-location-id'] = candidate.get('physical_location_id') if attributes and not self.match_inventory_attributes(attributes, vnf, candidate['candidate_id']): self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, @@ -1392,61 +1485,268 @@ class AAI(base.InventoryProviderBase): # Pick only candidates not in the excluded list # if excluded candidate list is provided - if excluded_candidates: - has_excluded_candidate = False - for excluded_candidate in excluded_candidates: - if excluded_candidate \ - and excluded_candidate.get('inventory_type') == \ - candidate.get('inventory_type') \ - and excluded_candidate.get('candidate_id') == \ - candidate.get('candidate_id'): - has_excluded_candidate = True - break - - if has_excluded_candidate: - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="excluded candidate") - continue + if excluded_candidates and self.match_candidate_by_list(candidate, excluded_candidates, True, + name, triage_translator_data): + continue # Pick only candidates in the required list # if required candidate list is provided - if required_candidates: - has_required_candidate = False - for required_candidate in required_candidates: - if required_candidate \ - and required_candidate.get('inventory_type') \ - == candidate.get('inventory_type') \ - and required_candidate.get('candidate_id') \ - == candidate.get('candidate_id'): - has_required_candidate = True - break - - if not has_required_candidate: - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="has_required_candidate candidate") - continue + if required_candidates and not self.match_candidate_by_list(candidate, required_candidates, + False, name, triage_translator_data): + continue # add the candidate to the demand # Pick only candidates from the restricted_region # or restricted_complex - if self.match_candidate_attribute( - candidate, - "location_id", - restricted_region_id, - name, - inventory_type) or \ - self.match_candidate_attribute( - candidate, - "physical_location_id", - restricted_complex_id, - name, - inventory_type): - self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], name, triage_translator_data, - reason="match candidate attribute") - + if not self.match_region(candidate, restricted_region_id, restricted_complex_id, name, + triage_translator_data): continue else: resolved_demands[name].append(candidate) + LOG.debug(">>>>>>> Candidate <<<<<<<") + LOG.debug(json.dumps(candidate, indent=4)) + + elif (inventory_type == 'vfmodule') \ + and customer_id: + + # First level query to get the list of generic vnfs + vnf_by_model_invariant = list() + if attributes and model_invariant_id: + + raw_path = '/network/generic-vnfs/' \ + '?model-invariant-id={}&depth=0'.format(model_invariant_id) + if model_version_id: + raw_path = '/network/generic-vnfs/' \ + '?model-invariant-id={}&model-version-id={}&depth=0'.format(model_invariant_id, + model_version_id) + path = self._aai_versioned_path(raw_path) + vnf_by_model_invariant = self.first_level_service_call(path, name, service_type) + + vnf_by_service_type = list() + if service_type or equipment_role: + path = self._aai_versioned_path( + '/network/generic-vnfs/' + '?equipment-role={}&depth=0'.format(service_type)) + vnf_by_service_type = self.first_level_service_call(path, name, service_type) + + generic_vnf = vnf_by_model_invariant + vnf_by_service_type + vnf_dict = dict() + + for vnf in generic_vnf: + # if this vnf already appears, skip it + vnf_id = vnf.get('vnf-id') + if vnf_id in vnf_dict: + continue + + # add vnf (with vnf_id as key) to the dictionary + vnf_dict[vnf_id] = vnf + + # create a default candidate + candidate = dict() + candidate['inventory_provider'] = 'aai' + candidate['service_resource_id'] = service_resource_id + candidate['inventory_type'] = inventory_type + candidate['candidate_id'] = '' + candidate['location_id'] = '' + candidate['location_type'] = 'att_aic' + candidate['host_id'] = '' + candidate['cost'] = self.conf.data.service_candidate_cost + candidate['cloud_owner'] = '' + candidate['cloud_region_version'] = '' + candidate['vlan_key'] = vlan_key + candidate['port_key'] = port_key + candidate['uniqueness'] = candidate_uniqueness + + # start populating the candidate + candidate['host_id'] = vnf.get("vnf-name") + + candidate['nf-name'] = vnf.get("vnf-name") + candidate['nf-id'] = vnf.get("vnf-id") + candidate['nf-type'] = 'vnf' + candidate['vnf-type'] = vnf.get("vnf-type") + candidate['ipv4-oam-address'] = '' + candidate['ipv6-oam-address'] = '' + + if vnf.get("ipv4-oam-address"): + candidate['ipv4-oam-address'] = vnf.get("ipv4-oam-address") + if vnf.get("ipv6-oam-address"): + candidate['ipv6-oam-address'] = vnf.get("ipv6-oam-address") + + rl_data = self.resolve_global_customer_id_for_vnf(candidate, vnf, customer_id, service_type, + name, triage_translator_data) + if rl_data is None: + continue + else: + vs_cust_id = rl_data.get('d_value') + + rl_data = self.resolve_service_instance_id_for_vnf(candidate, vnf, customer_id, + service_type, name, triage_translator_data) + if rl_data is None: + continue + else: + vs_service_instance_id = rl_data.get('d_value') + + if vs_cust_id and vs_cust_id == customer_id: + candidate['service_instance_id'] = vs_service_instance_id + else: # vserver is for a different customer + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], name, + triage_translator_data, + reason="candidate is for a different customer") + continue + + vf_modules_list = self.resolve_vf_modules_for_generic_vnf(candidate, vnf, name, + triage_translator_data) + if vf_modules_list is None: + continue + + candidate_base = candidate + for vf_module in vf_modules_list: + # for vfmodule demands we allow to have vfmodules from different cloud regions + candidate = copy.deepcopy(candidate_base) + candidate['candidate_id'] = vf_module.get("vf-module-id") + candidate['vf-module-name'] = vf_module.get("vf-module-name") + candidate['vf-module-id'] = vf_module.get("vf-module-id") + + rl_data = self.resolve_cloud_owner_for_vnf(candidate, vf_module, service_type, name, + triage_translator_data) + if rl_data is None: + continue + else: + cloud_owner = rl_data.get('d_value') + candidate['cloud_owner'] = cloud_owner + + cloud_region_id_rl_data, cloud_region_version_rl_data = self.resolve_cloud_region_id_and_version_for_vnf( + candidate, vf_module, service_type, name, triage_translator_data) + + if cloud_region_id_rl_data is None or cloud_region_version_rl_data is None: + continue + + cloud_region_id = cloud_region_id_rl_data.get('d_value') + cloud_region_version = cloud_region_version_rl_data.get('d_value') + candidate['location_id'] = cloud_region_id + candidate['cloud_region_version'] = cloud_region_version + + # Added vim-id for short-term workaround + if self.conf.HPA_enabled: + if not cloud_owner: + continue + candidate['vim-id'] = \ + candidate['cloud_owner'] + '_' + cloud_region_id + + if self.check_sriov_automation( + candidate['cloud_region_version'], name, + candidate['host_id']): + candidate['sriov_automation'] = 'true' + else: + candidate['sriov_automation'] = 'false' + + # Second level query to get the pserver from vserver + candidate['vservers'] = list() + complex_list = list() + vs_link_list = self.resolve_v_server_links_for_vnf(vf_module) + + for vs_link in vs_link_list: + + body = self.resolove_v_server_for_candidate(candidate, vs_link, True, name, + triage_translator_data) + if body is None: + continue + + candidate_vserver = dict() + candidate_vserver['vserver-id'] = body.get('vserver-id') + candidate_vserver['vserver-name'] = body.get('vserver-name') + + rl_data = self.resolve_complex_info_link_for_v_server(candidate, body, cloud_owner, + cloud_region_id, service_type, + name, triage_translator_data) + if rl_data is None: + continue + + #Interfaces info + if not body.get('l-interfaces') or not body.get('l-interfaces').get('l-interface'): + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], name, + triage_translator_data, + reason="VF-server interfaces error") + continue + else: + l_interfaces = body.get('l-interfaces').get('l-interface') + candidate_vserver['l-interfaces'] = list() + + for l_interface in l_interfaces: + vserver_interface = dict() + vserver_interface['interface-id'] = l_interface.get('interface-id') + vserver_interface['interface-name'] = l_interface.get('interface-name') + vserver_interface['macaddr'] = l_interface.get('macaddr') + vserver_interface['network-id'] = l_interface.get('network-name') + vserver_interface['network-name'] = '' + vserver_interface['ipv4-addresses'] = list() + vserver_interface['ipv6-addresses'] = list() + + if l_interface.get('l3-interface-ipv4-address-list'): + for ip_address_info in l_interface.get('l3-interface-ipv4-address-list'): + vserver_interface['ipv4-addresses'].\ + append(ip_address_info.get('l3-interface-ipv4-address')) + + if l_interface.get('l3-interface-ipv6-address-list'): + for ip_address_info in l_interface.get('l3-interface-ipv6-address-list'): + vserver_interface['ipv6-addresses'].\ + append(ip_address_info.get('l3-interface-ipv6-address')) + + candidate_vserver['l-interfaces'].append(vserver_interface) + + complex_list.append(rl_data) + candidate['vservers'].append(candidate_vserver) + + self.build_complex_info_for_candidate(candidate, vnf, complex_list, service_type, name, + triage_translator_data) + + if candidate.get("complex_name") is None: + continue + + ##add vf-module parameters for filtering + vnf_vf_module_inventory = copy.deepcopy(vnf) + vnf_vf_module_inventory.update(vf_module) + # add specifal parameters for comparsion + vnf_vf_module_inventory['global-customer-id'] = customer_id + vnf_vf_module_inventory['customer-id'] = customer_id + vnf_vf_module_inventory['cloud-region-id'] = cloud_region_id + 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, + candidate['candidate_id']): + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], + candidate['location_id'], name, + triage_translator_data, + reason="attibute check error") + continue + self.assign_candidate_existing_placement(candidate, existing_placement) + + # Pick only candidates not in the excluded list + # if excluded candidate list is provided + if excluded_candidates and self.match_candidate_by_list(candidate, excluded_candidates, True, + name, triage_translator_data): + continue + + # Pick only candidates in the required list + # if required candidate list is provided + if required_candidates and not self.match_candidate_by_list(candidate, required_candidates, + False, name, + triage_translator_data): + continue + + # add the candidate to the demand + # Pick only candidates from the restricted_region + # or restricted_complex + if not self.match_region(candidate, restricted_region_id, restricted_complex_id, name, + triage_translator_data): + continue + else: + resolved_demands[name].append(candidate) + LOG.debug(">>>>>>> Candidate <<<<<<<") + LOG.debug(json.dumps(candidate, indent=4)) elif inventory_type == 'transport' \ and customer_id and service_type and \ @@ -1578,6 +1878,49 @@ class AAI(base.InventoryProviderBase): " {}".format(inventory_type)) return resolved_demands + def match_region(self, candidate, restricted_region_id, restricted_complex_id, demand_name, triage_translator_data): + if self.match_candidate_attribute( + candidate, + "location_id", + restricted_region_id, + demand_name, + candidate.get('inventory_type')) or \ + self.match_candidate_attribute( + candidate, + "physical_location_id", + restricted_complex_id, + demand_name, + candidate.get('inventory_type')): + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], + demand_name, triage_translator_data, + reason="candidate region does not match") + return False + else: + return True + + def match_candidate_by_list(self, candidate, candidates_list, exclude, demand_name, triage_translator_data): + has_candidate = False + if candidates_list: + for list_candidate in candidates_list: + if list_candidate \ + and list_candidate.get('inventory_type') \ + == candidate.get('inventory_type') \ + and list_candidate.get('candidate_id') \ + == candidate.get('candidate_id'): + has_candidate = True + break + + if not exclude: + if not has_candidate: + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], + demand_name, triage_translator_data, + reason="has_required_candidate candidate") + elif has_candidate: + self.triage_translator.collectDroppedCandiate(candidate['candidate_id'], candidate['location_id'], + demand_name, triage_translator_data, + reason="excluded candidate") + return has_candidate + def match_hpa(self, candidate, features): """Match HPA features requirement with the candidate flavors """ hpa_provider = hpa_utils.HpaMatchProvider(candidate, features) |