Skip to content

Commit e54fb69

Browse files
[201911][vnet] Add "vnet_route_check" script (#1443)
Added "vnet_route_check" tool for check VNET route consistency. vnet_route_check.py: tool that verifies VNET routes consistancy between SONiC and vendor SDK DBs. Signed-off-by: Volodymyr Samotiy <[email protected]>
1 parent 603ac53 commit e54fb69

File tree

2 files changed

+356
-0
lines changed

2 files changed

+356
-0
lines changed

scripts/vnet_route_check.py

+355
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
#!/usr/bin/env python
2+
3+
import os
4+
import sys
5+
import syslog
6+
from swsssdk import ConfigDBConnector
7+
from swsssdk import SonicV2Connector
8+
9+
10+
''' vnet_route_check.py: tool that verifies VNET routes consistancy between SONiC and vendor SDK DBs.
11+
12+
Logically VNET route verification logic consists of 3 parts:
13+
1. Get VNET routes entries that are missed in ASIC_DB but present in APP_DB.
14+
2. Get VNET routes entries that are missed in APP_DB but present in ASIC_DB.
15+
3. Get VNET routes entries that are missed in SDK but present in ASIC_DB.
16+
17+
Returns 0 if there is no inconsistancy found and all VNET routes are aligned in all DBs.
18+
Returns -1 if there is incosistancy found and prints differences between DBs to standart output and syslog.
19+
20+
Format of differences output:
21+
{
22+
"results": {
23+
"missed_in_asic_db_routes": {
24+
"<vnet_name>": {
25+
"routes": [
26+
"<pfx>/<pfx_len>"
27+
]
28+
}
29+
},
30+
"missed_in_app_db_routes": {
31+
"<vnet_name>": {
32+
"routes": [
33+
"<pfx>/<pfx_len>"
34+
]
35+
}
36+
},
37+
"missed_in_sdk_routes": {
38+
"<vnet_name>": {
39+
"routes": [
40+
"<pfx>/<pfx_len>"
41+
]
42+
}
43+
}
44+
}
45+
}
46+
'''
47+
48+
49+
RC_OK = 0
50+
RC_ERR = -1
51+
52+
53+
report_level = syslog.LOG_ERR
54+
write_to_syslog = True
55+
56+
57+
def set_level(lvl, log_to_syslog):
58+
global report_level
59+
global write_to_syslog
60+
61+
write_to_syslog = log_to_syslog
62+
report_level = lvl
63+
64+
65+
def print_message(lvl, *args):
66+
if (lvl <= report_level):
67+
msg = ""
68+
for arg in args:
69+
msg += " " + str(arg)
70+
print(msg)
71+
if write_to_syslog:
72+
syslog.syslog(lvl, msg)
73+
74+
75+
def check_vnet_cfg():
76+
''' Returns True if VNET is configured in APP_DB or False if no VNET configuration.
77+
'''
78+
db = ConfigDBConnector()
79+
db.db_connect('APPL_DB')
80+
81+
vnet_db_keys = db.get_keys('VNET_TABLE')
82+
83+
return True if vnet_db_keys else False
84+
85+
86+
def get_vnet_intfs():
87+
''' Returns dictionary of VNETs and related VNET interfaces.
88+
Format: { <vnet_name>: [ <vnet_rif_name> ] }
89+
'''
90+
config_db = ConfigDBConnector()
91+
config_db.connect('CONFIG_DB')
92+
93+
intfs_data = config_db.get_table('INTERFACE')
94+
vlan_intfs_data = config_db.get_table('VLAN_INTERFACE')
95+
96+
vnet_intfs = {}
97+
98+
for intf_name, intf_attrs in intfs_data.items():
99+
if 'vnet_name' in intf_attrs:
100+
vnet_name = intf_attrs['vnet_name']
101+
if vnet_name in vnet_intfs:
102+
vnet_intfs[vnet_name].append(intf_name)
103+
else:
104+
vnet_intfs[vnet_name] = [intf_name]
105+
106+
for intf_name, intf_attrs in vlan_intfs_data.items():
107+
if 'vnet_name' in intf_attrs:
108+
vnet_name = intf_attrs['vnet_name']
109+
if vnet_name in vnet_intfs:
110+
vnet_intfs[vnet_name].append(intf_name)
111+
else:
112+
vnet_intfs[vnet_name] = [intf_name]
113+
114+
return vnet_intfs
115+
116+
117+
def get_all_rifs_oids():
118+
''' Returns dictionary of all router interfaces and their OIDs.
119+
Format: { <rif_name>: <rif_oid> }
120+
'''
121+
db = SonicV2Connector(host='127.0.0.1')
122+
db.connect(db.COUNTERS_DB)
123+
124+
rif_name_oid_map = db.get_all(db.COUNTERS_DB, 'COUNTERS_RIF_NAME_MAP')
125+
126+
return rif_name_oid_map
127+
128+
129+
def get_vnet_rifs_oids():
130+
''' Returns dictionary of VNET interfaces and their OIDs.
131+
Format: { <vnet_rif_name>: <vnet_rif_oid> }
132+
'''
133+
vnet_intfs = get_vnet_intfs()
134+
intfs_oids = get_all_rifs_oids()
135+
136+
vnet_intfs = [vnet_intfs[k] for k in vnet_intfs]
137+
vnet_intfs = [val for sublist in vnet_intfs for val in sublist]
138+
139+
vnet_rifs_oids_map = {}
140+
141+
for intf_name in intfs_oids or {}:
142+
if intf_name in vnet_intfs:
143+
vnet_rifs_oids_map[intf_name] = intfs_oids[intf_name]
144+
145+
return vnet_rifs_oids_map
146+
147+
148+
def get_vrf_entries():
149+
''' Returns dictionary of VNET interfaces and corresponding VRF OIDs.
150+
Format: { <vnet_rif_name>: <vrf_oid> }
151+
'''
152+
db = ConfigDBConnector()
153+
db.db_connect('ASIC_DB')
154+
155+
vnet_rifs_oids = get_vnet_rifs_oids()
156+
157+
rif_vrf_map = {}
158+
for vnet_rif_name in vnet_rifs_oids:
159+
rif_attrs = db.get_all(db.ASIC_DB, 'ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:{}'.format(vnet_rifs_oids[vnet_rif_name]))
160+
rif_vrf_map[vnet_rif_name] = rif_attrs['SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID']
161+
162+
return rif_vrf_map
163+
164+
165+
def filter_out_vnet_ip2me_routes(vnet_routes):
166+
''' Filters out IP2ME routes from the provided dictionary with VNET routes
167+
Format: { <vnet_name>: { 'routes': [ <pfx/pfx_len> ], 'vrf_oid': <oid> } }
168+
'''
169+
db = ConfigDBConnector()
170+
db.db_connect('APPL_DB')
171+
172+
vnet_intfs = get_vnet_intfs()
173+
all_rifs_db_keys = db.get_keys('INTF_TABLE')
174+
175+
vnet_intfs = [vnet_intfs[k] for k in vnet_intfs]
176+
vnet_intfs = [val for sublist in vnet_intfs for val in sublist]
177+
178+
vnet_ip2me_routes = []
179+
for rif in all_rifs_db_keys:
180+
rif_attrs = rif.split(':')
181+
# Skip RIF entries without IP prefix and prefix length (they have only one attribute - RIF name)
182+
if len(rif_attrs) == 1:
183+
continue
184+
185+
# rif_attrs[0] - RIF name
186+
# rif_attrs[1] - IP prefix and prefix legth
187+
# IP2ME routes have '/32' prefix length so replace it and add to the list
188+
if rif_attrs[0] in vnet_intfs:
189+
vnet_ip2me_routes.append(rif_attrs[1].replace('/24', '/32'))
190+
191+
for vnet, vnet_attrs in vnet_routes.items():
192+
for route in vnet_attrs['routes']:
193+
if route in vnet_ip2me_routes:
194+
vnet_attrs['routes'].remove(route)
195+
196+
if not vnet_attrs['routes']:
197+
vnet_routes.pop(vnet)
198+
199+
200+
def get_vnet_routes_from_app_db():
201+
''' Returns dictionary of VNET routes configured per each VNET in APP_DB.
202+
Format: { <vnet_name>: { 'routes': [ <pfx/pfx_len> ], 'vrf_oid': <oid> } }
203+
'''
204+
db = ConfigDBConnector()
205+
db.db_connect('APPL_DB')
206+
207+
vnet_intfs = get_vnet_intfs()
208+
vnet_vrfs = get_vrf_entries()
209+
210+
vnet_routes_db_keys = db.get_keys('VNET_ROUTE_TABLE') + db.get_keys('VNET_ROUTE_TUNNEL_TABLE')
211+
212+
vnet_routes = {}
213+
214+
for vnet_route_db_key in vnet_routes_db_keys:
215+
vnet_route_list = vnet_route_db_key.split(':')
216+
vnet_name = vnet_route_list[0]
217+
vnet_route = vnet_route_list[1]
218+
219+
if vnet_name not in vnet_routes:
220+
vnet_routes[vnet_name] = {}
221+
vnet_routes[vnet_name]['routes'] = []
222+
223+
intf = vnet_intfs[vnet_name][0]
224+
vnet_routes[vnet_name]['vrf_oid'] = vnet_vrfs.get(intf, 'None')
225+
226+
vnet_routes[vnet_name]['routes'].append(vnet_route)
227+
228+
return vnet_routes
229+
230+
231+
def get_vnet_routes_from_asic_db():
232+
''' Returns dictionary of VNET routes configured per each VNET in ASIC_DB.
233+
Format: { <vnet_name>: { 'routes': [ <pfx/pfx_len> ], 'vrf_oid': <oid> } }
234+
'''
235+
db = ConfigDBConnector()
236+
db.db_connect('ASIC_DB')
237+
238+
vnet_vrfs = get_vrf_entries()
239+
vnet_vrfs_oids = [vnet_vrfs[k] for k in vnet_vrfs]
240+
241+
vnet_intfs = get_vnet_intfs()
242+
243+
vrf_oid_to_vnet_map = {}
244+
245+
for vnet_name, vnet_rifs in vnet_intfs.items():
246+
for vnet_rif, vrf_oid in vnet_vrfs.items():
247+
if vnet_rif in vnet_rifs:
248+
vrf_oid_to_vnet_map[vrf_oid] = vnet_name
249+
250+
routes_db_keys = db.get_keys('ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY', False)
251+
vnet_routes = {}
252+
253+
for route_db_key in routes_db_keys:
254+
route_attrs = route_db_key.lower().split('\"', -1)
255+
# route_attrs[11] - VRF OID for the VNET route
256+
# route_attrs[3] - VNET route IP subnet
257+
vrf_oid = route_attrs[11]
258+
ip_addr = route_attrs[3]
259+
260+
if vrf_oid in vnet_vrfs_oids:
261+
if vrf_oid_to_vnet_map[vrf_oid] not in vnet_routes:
262+
vnet_name = vrf_oid_to_vnet_map[vrf_oid]
263+
264+
vnet_routes[vnet_name] = {}
265+
vnet_routes[vnet_name]['routes'] = []
266+
vnet_routes[vnet_name]['vrf_oid'] = vrf_oid
267+
268+
vnet_routes[vnet_name]['routes'].append(ip_addr)
269+
270+
filter_out_vnet_ip2me_routes(vnet_routes)
271+
272+
return vnet_routes
273+
274+
275+
def get_vnet_routes_diff(routes_1, routes_2):
276+
''' Returns all routes present in routes_2 dictionary but missed in routes_1
277+
Format: { <vnet_name>: { 'routes': [ <pfx/pfx_len> ] } }
278+
'''
279+
280+
routes = {}
281+
282+
for vnet_name, vnet_attrs in routes_2.items():
283+
if vnet_name not in routes_1:
284+
routes[vnet_name] = routes
285+
else:
286+
for vnet_route in vnet_attrs['routes']:
287+
if vnet_route not in routes_1[vnet_name]['routes']:
288+
if vnet_name not in routes:
289+
routes[vnet_name] = {}
290+
routes[vnet_name]['routes'] = []
291+
routes[vnet_name]['routes'].append(vnet_route)
292+
293+
return routes
294+
295+
296+
def get_sdk_vnet_routes_diff(routes):
297+
''' Returns all routes present in routes dictionary but missed in SAI/SDK
298+
Format: { <vnet_name>: { 'routes': [ <pfx/pfx_len> ], 'vrf_oid': <oid> } }
299+
'''
300+
routes_diff = {}
301+
302+
res = os.system('docker exec syncd test -f /usr/bin/vnet_route_check.py')
303+
if res != 0:
304+
return routes_diff
305+
306+
for vnet_name, vnet_routes in routes.items():
307+
vnet_routes = routes[vnet_name]["routes"]
308+
vnet_vrf_oid = routes[vnet_name]["vrf_oid"]
309+
310+
res = os.system('docker exec syncd "/usr/bin/vnet_route_check.py {} {}"'.format(vnet_vrf_oid, vnet_routes))
311+
if res:
312+
routes_diff[vnet_name] = {}
313+
routes_diff[vnet_name]['routes'] = res
314+
315+
return routes_diff
316+
317+
318+
def main():
319+
320+
rc = RC_OK
321+
322+
# Don't run VNET routes consistancy logic if there is no VNET configuration
323+
if not check_vnet_cfg():
324+
return rc
325+
326+
app_db_vnet_routes = get_vnet_routes_from_app_db()
327+
asic_db_vnet_routes = get_vnet_routes_from_asic_db()
328+
329+
missed_in_asic_db_routes = get_vnet_routes_diff(asic_db_vnet_routes, app_db_vnet_routes)
330+
missed_in_app_db_routes = get_vnet_routes_diff(app_db_vnet_routes, asic_db_vnet_routes)
331+
missed_in_sdk_routes = get_sdk_vnet_routes_diff(asic_db_vnet_routes)
332+
333+
res = {}
334+
res['results'] = {}
335+
rc = RC_OK
336+
337+
if missed_in_asic_db_routes:
338+
res['results']['missed_in_asic_db_routes'] = missed_in_asic_db_routes
339+
340+
if missed_in_app_db_routes:
341+
res['results']['missed_in_app_db_routes'] = missed_in_app_db_routes
342+
343+
if missed_in_sdk_routes:
344+
res['results']['missed_in_sdk_routes'] = missed_in_sdk_routes
345+
346+
if res['results']:
347+
rc = RC_ERR
348+
print_message(syslog.LOG_ERR, res)
349+
print_message(syslog.LOG_ERR, 'Vnet Route Mismatch reported')
350+
351+
sys.exit(rc)
352+
353+
354+
if __name__ == "__main__":
355+
main()

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
'scripts/reboot',
109109
'scripts/route_check.py',
110110
'scripts/route_check_test.sh',
111+
'scripts/vnet_route_check.py',
111112
'scripts/sfpshow',
112113
'scripts/sonic_sku_create.py',
113114
'scripts/syseeprom-to-json',

0 commit comments

Comments
 (0)