Skip to content

Commit c41285f

Browse files
daallyxieca
authored andcommitted
[neighbor_advertiser] Verify that DIPs returned from ferret are not in 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
1 parent a72b35f commit c41285f

1 file changed

Lines changed: 64 additions & 17 deletions

File tree

scripts/neighbor_advertiser

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import subprocess
1818
import sonic_device_util
1919
from swsssdk import ConfigDBConnector
2020
from swsssdk import SonicV2Connector
21+
from netaddr import IPAddress, IPNetwork
2122

2223

2324
#
@@ -26,6 +27,7 @@ from swsssdk import SonicV2Connector
2627

2728
DEFAULT_DURATION = 300
2829
DEFAULT_REQUEST_TIMEOUT = 2
30+
DEFAULT_FERRET_QUERY_RETRIES = 3
2931
SYSLOG_IDENTIFIER = 'neighbor_advertiser'
3032

3133

@@ -82,16 +84,49 @@ def log_error(msg):
8284
config_db = None
8385

8486

85-
#
86-
# Get switch info and intf addr
87-
#
88-
8987
def connect_config_db():
9088
global config_db
9189
config_db = ConfigDBConnector()
9290
config_db.connect()
9391

9492

93+
#
94+
# Check if a DIP returned from ferret is in any of this switch's VLANs
95+
#
96+
97+
vlan_interface_query = None
98+
99+
100+
def is_dip_in_device_vlan(ferret_dip):
101+
global vlan_interface_query
102+
103+
# Lazy load the vlan interfaces the first time we run this check.
104+
if not vlan_interface_query:
105+
vlan_interface_query = config_db.get_table('VLAN_INTERFACE')
106+
107+
ferret_dip = IPAddress(ferret_dip)
108+
109+
for vlan_interface in vlan_interface_query.iterkeys():
110+
if not is_ip_prefix_in_key(vlan_interface):
111+
log_info('{} does not have a subnet, skipping...'.format(vlan_interface))
112+
continue
113+
114+
vlan_subnet = IPNetwork(vlan_interface[1])
115+
116+
if ferret_dip.version != vlan_subnet.version:
117+
log_info('{} version (IPv{}) does not match provided DIP version (IPv{}), skipping...'.format(vlan_interface[0], vlan_subnet.version, ferret_dip.version))
118+
continue
119+
120+
if ferret_dip in vlan_subnet:
121+
return True
122+
123+
return False
124+
125+
126+
#
127+
# Get switch info and intf addr
128+
#
129+
95130
def get_switch_name():
96131
metadata = config_db.get_table('DEVICE_METADATA')
97132
return metadata['localhost']['hostname']
@@ -308,24 +343,36 @@ def post_neighbor_advertiser_slice(ferret_service_vip):
308343
url = 'http://{}:85{}{}'.format(ferret_service_vip, FERRET_NEIGHBOR_ADVERTISER_API_PREFIX, get_switch_name())
309344
response = None
310345

311-
try:
312-
response = requests.post(url, json = request_slice, timeout = DEFAULT_REQUEST_TIMEOUT)
313-
except Exception as e:
314-
log_error('The request failed, vip: {}, error: {}'.format(ferret_service_vip, e))
315-
316-
ferret_server_ipv4_addr = None
346+
for retry in range(DEFAULT_FERRET_QUERY_RETRIES):
347+
try:
348+
response = requests.post(url, json = request_slice, timeout = DEFAULT_REQUEST_TIMEOUT)
349+
except Exception as e:
350+
log_error('The request failed, vip: {}, error: {}'.format(ferret_service_vip, e))
351+
return None
352+
353+
# Handle response errors
354+
if not response:
355+
log_error('Failed to set up neighbor advertiser slice, vip: {}, no response obtained'.format(ferret_service_vip))
356+
return None
357+
if response and not response.ok:
358+
log_error('Failed to set up neighbor advertiser slice, vip: {}, error_code: {}, error_content: {}'.format(ferret_service_vip, response.status_code, response.content))
359+
return None
317360

318-
if response and response.ok:
319361
neighbor_advertiser_configuration = json.loads(response.content)
320-
save_as_json(neighbor_advertiser_configuration, NEIGHBOR_ADVERTISER_RESPONSE_CONFIG_PATH)
321362
ferret_server_ipv4_addr = neighbor_advertiser_configuration['ipv4Addr']
363+
364+
# Retry the request if the provided DIP is in the device VLAN
365+
if is_dip_in_device_vlan(ferret_server_ipv4_addr):
366+
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))
367+
continue
368+
369+
# If all the proceeding checks pass, return the provided DIP
370+
save_as_json(neighbor_advertiser_configuration, NEIGHBOR_ADVERTISER_RESPONSE_CONFIG_PATH)
322371
log_info('Successfully set up neighbor advertiser slice, vip: {}, dip: {}'.format(ferret_service_vip, ferret_server_ipv4_addr))
323-
elif response:
324-
log_error('Failed to set up neighbor advertiser slice, vip: {}, error_code: {}, error_content: {}'.format(ferret_service_vip, response.status_code, response.content))
325-
else:
326-
log_error('Failed to set up neighbor advertiser slice, vip: {}, no response obtained'.format(ferret_service_vip))
372+
return ferret_server_ipv4_addr
327373

328-
return ferret_server_ipv4_addr
374+
log_error('Failed to set up neighbor advertiser slice, vip: {}, returned dips were in device VLAN'.format(ferret_service_vip))
375+
return None
329376

330377

331378
def save_as_json(obj, file_path):

0 commit comments

Comments
 (0)