Skip to content

Commit

Permalink
[neighbor_advertiser] Verify that DIPs returned from ferret are not i…
Browse files Browse the repository at this point in the history
…n device VLAN (sonic-net#670)

* [neighbor_advertiser] Verify that DIPs returned from ferret are not in device VLANs
* Add a method in neighbor_advertiser to check if a DIP is in any of the device's VLANs
* Check that DIPs returned from ferret are not in any of the device's VLANs before proceeding
* Add a retry to attempt to request different DIPs from a given ferret VIP

Signed-off-by: Danny Allen [email protected]

* Remove extraneous local var

* Handle error cases

* Incorporate PR feedback

* Revert structure and clarifying comments

* Show retry attempts

* Clarify device vlan check

* Filters out interfaces that have no subnet or an IPv6 subnet

* Refactor to use is_ip_prefix_in_key

* Fix runtime errors
  • Loading branch information
daall authored and yxieca committed Sep 27, 2019
1 parent a72b35f commit c41285f
Showing 1 changed file with 64 additions and 17 deletions.
81 changes: 64 additions & 17 deletions scripts/neighbor_advertiser
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import subprocess
import sonic_device_util
from swsssdk import ConfigDBConnector
from swsssdk import SonicV2Connector
from netaddr import IPAddress, IPNetwork


#
Expand All @@ -26,6 +27,7 @@ from swsssdk import SonicV2Connector

DEFAULT_DURATION = 300
DEFAULT_REQUEST_TIMEOUT = 2
DEFAULT_FERRET_QUERY_RETRIES = 3
SYSLOG_IDENTIFIER = 'neighbor_advertiser'


Expand Down Expand Up @@ -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']
Expand Down Expand Up @@ -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):
Expand Down

0 comments on commit c41285f

Please sign in to comment.