2
2
# -*- coding: utf-8 -*-
3
3
4
4
import os
5
+ import re
5
6
import sys
6
- import getopt
7
+ import argparse
7
8
import ipaddress
9
+ import syslog
8
10
import json
11
+ import time
12
+ from enum import Enum
9
13
from swsssdk import ConfigDBConnector
10
14
11
15
os .environ ['PYTHONUNBUFFERED' ]= 'True'
12
16
13
17
PREFIX_SEPARATOR = '/'
14
18
IPV6_SEPARATOR = ':'
15
19
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
+
35
42
36
43
def print_message (lvl , * args ):
37
- if (lvl <= mode ):
44
+ if (lvl <= report_level ):
45
+ msg = ""
38
46
for arg in args :
39
- print arg
47
+ msg += " " + str (arg )
48
+ print (msg )
49
+ syslog .syslog (lvl , msg )
40
50
41
51
def add_prefix (ip ):
42
52
if ip .find (IPV6_SEPARATOR ) == - 1 :
@@ -48,12 +58,9 @@ def add_prefix(ip):
48
58
def add_prefix_ifnot (ip ):
49
59
return ip if ip .find (PREFIX_SEPARATOR ) != - 1 else add_prefix (ip )
50
60
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
57
64
58
65
def cmps (s1 , s2 ):
59
66
if (s1 == s2 ):
@@ -93,103 +100,138 @@ def do_diff(t1, t2):
93
100
def get_routes ():
94
101
db = ConfigDBConnector ()
95
102
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" )
97
104
keys = db .get_keys ('ROUTE_TABLE' )
98
- print_message (MODE_DEBUG , json .dumps ({"ROUTE_TABLE" : keys }, indent = 4 ))
99
105
100
106
valid_rt = []
101
- skip_rt = []
102
107
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 ()))
107
110
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 ))
109
112
return sorted (valid_rt )
110
113
111
114
def get_route_entries ():
112
115
db = ConfigDBConnector ()
113
116
db .db_connect ('ASIC_DB' )
114
- print_message (MODE_DEBUG , "ASIC DB connected" )
117
+ print_message (syslog . LOG_DEBUG , "ASIC DB connected" )
115
118
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 ))
117
119
118
120
rt = []
119
121
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 ))
121
126
return sorted (rt )
122
127
123
128
124
129
def get_interfaces ():
125
130
db = ConfigDBConnector ()
126
131
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" )
128
133
129
134
intf = []
130
135
keys = db .get_keys ('INTF_TABLE' )
131
- print_message (MODE_DEBUG , json .dumps ({"APPL_DB_INTF" : keys }, indent = 4 ))
132
136
133
137
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
139
141
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 ))
143
148
return sorted (intf )
144
149
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
+
145
167
def check_routes ():
146
- intf_miss = []
147
- rt_miss = []
148
- re_miss = []
168
+ intf_appl_miss = []
169
+ rt_appl_miss = []
170
+ rt_asic_miss = []
149
171
150
172
results = {}
151
173
err_present = False
152
174
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 )
155
181
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
158
193
err_present = True
159
194
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
162
197
err_present = True
163
198
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
166
201
err_present = True
167
202
168
203
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" )
171
206
return - 1
172
207
else :
173
- print_message (MODE_ERR , "All good!" )
208
+ print_message (syslog . LOG_INFO , "All good!" )
174
209
return 0
175
210
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
-
181
211
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
186
227
187
- for opt , arg in opts :
188
- if opt in ("-m" , "--mode" ):
189
- set_mode (arg )
228
+ while True :
229
+ ret = check_routes ()
190
230
191
- ret = check_routes ()
192
- sys .exit (ret )
231
+ if interval :
232
+ time .sleep (interval )
233
+ else :
234
+ sys .exit (ret )
193
235
194
236
195
237
if __name__ == "__main__" :
0 commit comments