diff --git a/scripts/neighbor_advertiser b/scripts/neighbor_advertiser index b723aa9026e9..3c515db6aa42 100644 --- a/scripts/neighbor_advertiser +++ b/scripts/neighbor_advertiser @@ -18,6 +18,7 @@ import subprocess import sonic_device_util from swsssdk import ConfigDBConnector from swsssdk import SonicV2Connector +from netaddr import IPAddress, IPNetwork # @@ -26,6 +27,7 @@ from swsssdk import SonicV2Connector DEFAULT_DURATION = 300 DEFAULT_REQUEST_TIMEOUT = 2 +DEFAULT_FERRET_QUERY_RETRIES = 3 SYSLOG_IDENTIFIER = 'neighbor_advertiser' @@ -82,16 +84,49 @@ def log_error(msg): config_db = None -# -# Get switch info and intf addr -# - def connect_config_db(): global config_db config_db = ConfigDBConnector() config_db.connect() +# +# Check if a DIP returned from ferret is in any of this switch's VLANs +# + +vlan_interface_query = None + + +def is_dip_in_device_vlan(ferret_dip): + global vlan_interface_query + + # Lazy load the vlan interfaces the first time we run this check. + if not vlan_interface_query: + vlan_interface_query = config_db.get_table('VLAN_INTERFACE') + + ferret_dip = IPAddress(ferret_dip) + + for vlan_interface in vlan_interface_query.iterkeys(): + if not is_ip_prefix_in_key(vlan_interface): + log_info('{} does not have a subnet, skipping...'.format(vlan_interface)) + continue + + vlan_subnet = IPNetwork(vlan_interface[1]) + + if ferret_dip.version != vlan_subnet.version: + log_info('{} version (IPv{}) does not match provided DIP version (IPv{}), skipping...'.format(vlan_interface[0], vlan_subnet.version, ferret_dip.version)) + continue + + if ferret_dip in vlan_subnet: + return True + + return False + + +# +# Get switch info and intf addr +# + def get_switch_name(): metadata = config_db.get_table('DEVICE_METADATA') return metadata['localhost']['hostname'] @@ -308,24 +343,36 @@ def post_neighbor_advertiser_slice(ferret_service_vip): url = 'http://{}:85{}{}'.format(ferret_service_vip, FERRET_NEIGHBOR_ADVERTISER_API_PREFIX, get_switch_name()) response = None - try: - response = requests.post(url, json = request_slice, timeout = DEFAULT_REQUEST_TIMEOUT) - except Exception as e: - log_error('The request failed, vip: {}, error: {}'.format(ferret_service_vip, e)) - - ferret_server_ipv4_addr = None + for retry in range(DEFAULT_FERRET_QUERY_RETRIES): + try: + response = requests.post(url, json = request_slice, timeout = DEFAULT_REQUEST_TIMEOUT) + except Exception as e: + log_error('The request failed, vip: {}, error: {}'.format(ferret_service_vip, e)) + return None + + # Handle response errors + if not response: + log_error('Failed to set up neighbor advertiser slice, vip: {}, no response obtained'.format(ferret_service_vip)) + return None + if response and not response.ok: + log_error('Failed to set up neighbor advertiser slice, vip: {}, error_code: {}, error_content: {}'.format(ferret_service_vip, response.status_code, response.content)) + return None - if response and response.ok: neighbor_advertiser_configuration = json.loads(response.content) - save_as_json(neighbor_advertiser_configuration, NEIGHBOR_ADVERTISER_RESPONSE_CONFIG_PATH) ferret_server_ipv4_addr = neighbor_advertiser_configuration['ipv4Addr'] + + # Retry the request if the provided DIP is in the device VLAN + if is_dip_in_device_vlan(ferret_server_ipv4_addr): + log_info('Failed to set up neighbor advertiser slice, vip: {}, dip {} is in device VLAN (attempt {}/{})'.format(ferret_service_vip, ferret_server_ipv4_addr, retry + 1, DEFAULT_FERRET_QUERY_RETRIES)) + continue + + # If all the proceeding checks pass, return the provided DIP + save_as_json(neighbor_advertiser_configuration, NEIGHBOR_ADVERTISER_RESPONSE_CONFIG_PATH) log_info('Successfully set up neighbor advertiser slice, vip: {}, dip: {}'.format(ferret_service_vip, ferret_server_ipv4_addr)) - elif response: - log_error('Failed to set up neighbor advertiser slice, vip: {}, error_code: {}, error_content: {}'.format(ferret_service_vip, response.status_code, response.content)) - else: - log_error('Failed to set up neighbor advertiser slice, vip: {}, no response obtained'.format(ferret_service_vip)) + return ferret_server_ipv4_addr - return ferret_server_ipv4_addr + log_error('Failed to set up neighbor advertiser slice, vip: {}, returned dips were in device VLAN'.format(ferret_service_vip)) + return None def save_as_json(obj, file_path):