Skip to content

Commit 44ed6e9

Browse files
Improved route_check tool and adopt to 20191130 image. (#898)
* Changes: 1) Add syslog support. 2) Enable forever periodic scan 3) Skip link local addresses 4) Skip eth0 routes 3) Adopt to 20191130 changes 3.1) APPl-DB INTF_TABLE may not have IP address 3.2) nexthop is never empty * No logical code change. A small name change. * 1) Adopt to 201811 -- Filter out 'lo' & 'docker0' in addition to 'eth0' as local routes 2) Ensure to read route entry w/o prefix, if not present with prefix
1 parent 6fba8db commit 44ed6e9

File tree

1 file changed

+119
-77
lines changed

1 file changed

+119
-77
lines changed

scripts/route_check.py

+119-77
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,51 @@
22
# -*- coding: utf-8 -*-
33

44
import os
5+
import re
56
import sys
6-
import getopt
7+
import argparse
78
import ipaddress
9+
import syslog
810
import json
11+
import time
12+
from enum import Enum
913
from swsssdk import ConfigDBConnector
1014

1115
os.environ['PYTHONUNBUFFERED']='True'
1216

1317
PREFIX_SEPARATOR = '/'
1418
IPV6_SEPARATOR = ':'
1519

16-
# Modes of operation from quiet to noisy
17-
MODE_QUIET = 0
18-
MODE_ERR = 1
19-
MODE_INFO = 2
20-
MODE_DEBUG = 3
21-
22-
mode = MODE_ERR
23-
24-
def set_mode(m):
25-
global mode
26-
if (m == 'QUIET'):
27-
mode = MODE_QUIET
28-
elif (m == 'ERR'):
29-
mode = MODE_ERR
30-
elif (m == 'INFO'):
31-
mode = MODE_INFO
32-
elif (m == 'DEBUG'):
33-
mode = MODE_DEBUG
34-
return mode
20+
MIN_SCAN_INTERVAL = 10 # Every 10 seconds
21+
MAX_SCAN_INTERVAL = 3600 # An hour
22+
23+
class Level(Enum):
24+
ERR = 'ERR'
25+
INFO = 'INFO'
26+
DEBUG = 'DEBUG'
27+
28+
def __str__(self):
29+
return self.value
30+
31+
report_level = syslog.LOG_ERR
32+
33+
def set_level(lvl):
34+
global report_level
35+
36+
if (lvl == Level.INFO):
37+
report_level = syslog.LOG_INFO
38+
39+
if (lvl == Level.DEBUG):
40+
report_level = syslog.LOG_DEBUG
41+
3542

3643
def print_message(lvl, *args):
37-
if (lvl <= mode):
44+
if (lvl <= report_level):
45+
msg = ""
3846
for arg in args:
39-
print arg
47+
msg += " " + str(arg)
48+
print(msg)
49+
syslog.syslog(lvl, msg)
4050

4151
def add_prefix(ip):
4252
if ip.find(IPV6_SEPARATOR) == -1:
@@ -48,12 +58,9 @@ def add_prefix(ip):
4858
def add_prefix_ifnot(ip):
4959
return ip if ip.find(PREFIX_SEPARATOR) != -1 else add_prefix(ip)
5060

51-
def ip_subnet(ip):
52-
if ip.find(":") == -1:
53-
net = ipaddress.IPv4Network(ip, False)
54-
else:
55-
net = ipaddress.IPv6Network(ip, False)
56-
return net.with_prefixlen
61+
def is_local(ip):
62+
t = ipaddress.ip_address(ip.split("/")[0].decode('utf-8'))
63+
return t.is_link_local
5764

5865
def cmps(s1, s2):
5966
if (s1 == s2):
@@ -93,103 +100,138 @@ def do_diff(t1, t2):
93100
def get_routes():
94101
db = ConfigDBConnector()
95102
db.db_connect('APPL_DB')
96-
print_message(MODE_DEBUG, "APPL DB connected for routes")
103+
print_message(syslog.LOG_DEBUG, "APPL DB connected for routes")
97104
keys = db.get_keys('ROUTE_TABLE')
98-
print_message(MODE_DEBUG, json.dumps({"ROUTE_TABLE": keys}, indent=4))
99105

100106
valid_rt = []
101-
skip_rt = []
102107
for k in keys:
103-
if db.get_entry('ROUTE_TABLE', k)['nexthop'] != '':
104-
valid_rt.append(add_prefix_ifnot(k))
105-
else:
106-
skip_rt.append(k)
108+
if not is_local(k):
109+
valid_rt.append(add_prefix_ifnot(k.lower()))
107110

108-
print_message(MODE_INFO, json.dumps({"skipped_routes" : skip_rt}, indent=4))
111+
print_message(syslog.LOG_DEBUG, json.dumps({"ROUTE_TABLE": sorted(valid_rt)}, indent=4))
109112
return sorted(valid_rt)
110113

111114
def get_route_entries():
112115
db = ConfigDBConnector()
113116
db.db_connect('ASIC_DB')
114-
print_message(MODE_DEBUG, "ASIC DB connected")
117+
print_message(syslog.LOG_DEBUG, "ASIC DB connected")
115118
keys = db.get_keys('ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY', False)
116-
print_message(MODE_DEBUG, json.dumps({"ASIC_ROUTE_ENTRY": keys}, indent=4))
117119

118120
rt = []
119121
for k in keys:
120-
rt.append(k.split("\"", -1)[3])
122+
e = k.lower().split("\"", -1)[3]
123+
if not is_local(e):
124+
rt.append(e)
125+
print_message(syslog.LOG_DEBUG, json.dumps({"ASIC_ROUTE_ENTRY": sorted(rt)}, indent=4))
121126
return sorted(rt)
122127

123128

124129
def get_interfaces():
125130
db = ConfigDBConnector()
126131
db.db_connect('APPL_DB')
127-
print_message(MODE_DEBUG, "APPL DB connected for interfaces")
132+
print_message(syslog.LOG_DEBUG, "APPL DB connected for interfaces")
128133

129134
intf = []
130135
keys = db.get_keys('INTF_TABLE')
131-
print_message(MODE_DEBUG, json.dumps({"APPL_DB_INTF": keys}, indent=4))
132136

133137
for k in keys:
134-
subk = k.split(':', -1)
135-
alias = subk[0]
136-
ip_prefix = ":".join(subk[1:])
137-
ip = add_prefix(ip_prefix.split("/", -1)[0])
138-
if (subk[0] == "eth0") or (subk[0] == "docker0"):
138+
lst = re.split(':', k.lower(), maxsplit=1)
139+
if len(lst) == 1:
140+
# No IP address in key; ignore
139141
continue
140-
if (subk[0] != "lo"):
141-
intf.append(ip_subnet(ip_prefix))
142-
intf.append(ip)
142+
143+
ip = add_prefix(lst[1].split("/", -1)[0])
144+
if not is_local(ip):
145+
intf.append(ip)
146+
147+
print_message(syslog.LOG_DEBUG, json.dumps({"APPL_DB_INTF": sorted(intf)}, indent=4))
143148
return sorted(intf)
144149

150+
def filter_out_local_interfaces(keys):
151+
rt = []
152+
local_if = set(['eth0', 'lo', 'docker0'])
153+
154+
db = ConfigDBConnector()
155+
db.db_connect('APPL_DB')
156+
157+
for k in keys:
158+
e = db.get_entry('ROUTE_TABLE', k)
159+
if not e:
160+
# Prefix might have been added. So try w/o it.
161+
e = db.get_entry('ROUTE_TABLE', k.split("/")[0])
162+
if not e or (e['ifname'] not in local_if):
163+
rt.append(k)
164+
165+
return rt
166+
145167
def check_routes():
146-
intf_miss = []
147-
rt_miss = []
148-
re_miss = []
168+
intf_appl_miss = []
169+
rt_appl_miss = []
170+
rt_asic_miss = []
149171

150172
results = {}
151173
err_present = False
152174

153-
rt_miss, re_miss = do_diff(get_routes(), get_route_entries())
154-
intf_miss, re_miss = do_diff(get_interfaces(), re_miss)
175+
rt_appl = get_routes()
176+
rt_asic = get_route_entries()
177+
intf_appl = get_interfaces()
178+
179+
# Diff APPL-DB routes & ASIC-DB routes
180+
rt_appl_miss, rt_asic_miss = do_diff(rt_appl, rt_asic)
155181

156-
if (len(rt_miss) != 0):
157-
results["missed_ROUTE_TABLE_routes"] = rt_miss
182+
# Check missed ASIC routes against APPL-DB INTF_TABLE
183+
_, rt_asic_miss = do_diff(intf_appl, rt_asic_miss)
184+
185+
# Check APPL-DB INTF_TABLE with ASIC table route entries
186+
intf_appl_miss, _ = do_diff(intf_appl, rt_asic)
187+
188+
if (len(rt_appl_miss) != 0):
189+
rt_appl_miss = filter_out_local_interfaces(rt_appl_miss)
190+
191+
if (len(rt_appl_miss) != 0):
192+
results["missed_ROUTE_TABLE_routes"] = rt_appl_miss
158193
err_present = True
159194

160-
if (len(intf_miss) != 0):
161-
results["missed_INTF_TABLE_entries"] = intf_miss
195+
if (len(intf_appl_miss) != 0):
196+
results["missed_INTF_TABLE_entries"] = intf_appl_miss
162197
err_present = True
163198

164-
if (len(re_miss) != 0):
165-
results["Unaccounted_ROUTE_ENTRY_TABLE_entries"] = re_miss
199+
if (len(rt_asic_miss) != 0):
200+
results["Unaccounted_ROUTE_ENTRY_TABLE_entries"] = rt_asic_miss
166201
err_present = True
167202

168203
if err_present:
169-
print_message(MODE_ERR, "results: {", json.dumps(results, indent=4), "}")
170-
print_message(MODE_ERR, "Failed. Look at reported mismatches above")
204+
print_message(syslog.LOG_ERR, "results: {", json.dumps(results, indent=4), "}")
205+
print_message(syslog.LOG_ERR, "Failed. Look at reported mismatches above")
171206
return -1
172207
else:
173-
print_message(MODE_ERR, "All good!")
208+
print_message(syslog.LOG_INFO, "All good!")
174209
return 0
175210

176-
def usage():
177-
print sys.argv[0], "[-m <QUIET|ERR|INFO|DEBUG>]"
178-
print sys.argv[0], "[--mode=<QUIET|ERR|INFO|DEBUG>]"
179-
sys.exit(-1)
180-
181211
def main(argv):
182-
try:
183-
opts, argv = getopt.getopt(argv, "m:", ["mode="])
184-
except getopt.GetoptError:
185-
usage()
212+
interval = 0
213+
parser=argparse.ArgumentParser(description="Verify routes between APPL-DB & ASIC-DB are in sync")
214+
parser.add_argument('-m', "--mode", type=Level, choices=list(Level), default='ERR')
215+
parser.add_argument("-i", "--interval", type=int, default=0, help="Scan interval in seconds")
216+
args = parser.parse_args()
217+
218+
set_level(args.mode)
219+
220+
if args.interval:
221+
if (args.interval < MIN_SCAN_INTERVAL):
222+
interval = MIN_SCAN_INTERVAL
223+
elif (args.interval > MAX_SCAN_INTERVAL):
224+
interval = MAX_SCAN_INTERVAL
225+
else:
226+
interval = args.interval
186227

187-
for opt, arg in opts:
188-
if opt in ("-m", "--mode"):
189-
set_mode(arg)
228+
while True:
229+
ret = check_routes()
190230

191-
ret = check_routes()
192-
sys.exit(ret)
231+
if interval:
232+
time.sleep(interval)
233+
else:
234+
sys.exit(ret)
193235

194236

195237
if __name__ == "__main__":

0 commit comments

Comments
 (0)