Skip to content

Commit cbe2159

Browse files
[vnet] Add "vnet_route_check" script (sonic-net#1300)
* [vnet] Add "vnet_route_check" script * [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 9120766 commit cbe2159

File tree

3 files changed

+689
-0
lines changed

3 files changed

+689
-0
lines changed

scripts/vnet_route_check.py

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

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
'scripts/reboot',
118118
'scripts/route_check.py',
119119
'scripts/route_check_test.sh',
120+
'scripts/vnet_route_check.py',
120121
'scripts/sfpshow',
121122
'scripts/storyteller',
122123
'scripts/syseeprom-to-json',

0 commit comments

Comments
 (0)