@@ -329,6 +329,52 @@ def get_asicdb_routes(namespace):
329329 return (selector , subs , sorted (rt ))
330330
331331
332+ def get_appdb_sids (namespace ):
333+ """
334+ helper to read SIDs table from APPL-DB.
335+ :return list of sorted SIDs with prefix ensured
336+ """
337+ db = swsscommon .DBConnector (APPL_DB_NAME , REDIS_TIMEOUT_MSECS , True , namespace )
338+ print_message (syslog .LOG_DEBUG , "APPL DB connected for sids" )
339+ tbl = swsscommon .Table (db , 'SRV6_MY_SID_TABLE' )
340+ keys = tbl .getKeys ()
341+
342+ sids = sorted (keys )
343+ print_message (syslog .LOG_DEBUG , json .dumps ({"APPL_MY_SID_TABLE" : sids }, indent = 4 ))
344+ return sids
345+
346+
347+ def get_asicdb_sids (namespace ):
348+ """
349+ helper to read SIDs table from APPL-DB.
350+ :return list of sorted SIDs with prefix ensured
351+ """
352+ db = swsscommon .DBConnector (ASIC_DB_NAME , REDIS_TIMEOUT_MSECS , True , namespace )
353+ print_message (syslog .LOG_DEBUG , "ASIC DB connected for sids" )
354+ tbl = swsscommon .Table (db , ASIC_TABLE_NAME )
355+ keys = tbl .getKeys ()
356+
357+ sids = []
358+ for k in keys :
359+ content = k .split (":" , 1 )
360+ if content [0 ] != 'SAI_OBJECT_TYPE_MY_SID_ENTRY' :
361+ continue
362+ sid_info = json .loads (content [1 ])
363+ sid_entry = ":" .join ([
364+ sid_info .get ("locator_block_len" , "0" ),
365+ sid_info .get ("locator_node_len" , "0" ),
366+ sid_info .get ("function_len" , "0" ),
367+ sid_info .get ("args_len" , "0" ),
368+ sid_info .get ("sid" )
369+ ])
370+
371+ sids .append (sid_entry )
372+
373+ sids = sorted (sids )
374+ print_message (syslog .LOG_DEBUG , json .dumps ({"ASIC_MY_SID_TABLE" : sids }, indent = 4 ))
375+ return sids
376+
377+
332378def is_suppress_fib_pending_enabled (namespace ):
333379 """
334380 Returns True if FIB suppression is enabled, False otherwise
@@ -575,7 +621,7 @@ def check_frr_pending_routes(namespace):
575621
576622 for _ , entries in frr_routes .items ():
577623 for entry in entries :
578- if entry ['protocol' ] in ('connected' , 'kernel' ):
624+ if entry ['protocol' ] in ('connected' , 'kernel' , 'static' ):
579625 continue
580626
581627 # TODO: Also handle VRF routes. Currently this script does not check for VRF routes so it would be incorrect for us
@@ -839,6 +885,74 @@ def check_routes(namespace):
839885 print_message (syslog .LOG_INFO , "All good!" )
840886 return 0 , None
841887
888+
889+ def check_sids_for_namespace (namespace ):
890+ """
891+ Process a Single Namespace for SIDs consistency check:
892+ Read APPL-DB & ASIC-DB, the relevant tables for SID checking.
893+ Checkout SIDs in ASIC-DB to match APPL-DB. Compared to check_routes,
894+ this function does not wait for subscriber updates, as SIDs are not
895+ expected to change frequently.
896+
897+ :return (0, None) on sucess, else (-1, results) where results holds
898+ the unjustifiable entries.
899+ """
900+
901+ results = {}
902+ sid_appl_miss = []
903+ sid_asic_miss = []
904+
905+ sids_asic = get_asicdb_sids (namespace )
906+
907+ sids_appl = get_appdb_sids (namespace )
908+
909+ # Diff APPL-DB SIDs & ASIC-DB SIDs
910+ sid_appl_miss , sid_asic_miss = diff_sorted_lists (sids_appl , sids_asic )
911+
912+ if sid_appl_miss :
913+ results ["missed_APPL_MY_SID_TABLE_entries" ] = sid_appl_miss
914+
915+ if sid_asic_miss :
916+ results ["missed_ASIC_MY_SID_TABLE_entries" ] = sid_asic_miss
917+
918+ return results
919+
920+
921+ def check_sids (namespace ):
922+ """
923+ Main function to parallelize SID checks across all namespaces.
924+ """
925+ namespace_list = []
926+ if namespace is not multi_asic .DEFAULT_NAMESPACE and namespace in multi_asic .get_namespace_list ():
927+ namespace_list .append (namespace )
928+ else :
929+ namespace_list = multi_asic .get_namespace_list ()
930+ print_message (syslog .LOG_INFO , "Checking SIDs for namespaces: " , namespace_list )
931+
932+ results = {}
933+
934+ # Use ThreadPoolExecutor to parallelize the check for each namespace
935+ with concurrent .futures .ThreadPoolExecutor () as executor :
936+ futures = {executor .submit (check_sids_for_namespace , ns ): ns for ns in namespace_list }
937+
938+ for future in concurrent .futures .as_completed (futures ):
939+ ns = futures [future ]
940+ try :
941+ result = future .result ()
942+ if result :
943+ results [ns ] = result
944+ except Exception as e :
945+ print_message (syslog .LOG_ERR , "Error processing namespace {}: {}" .format (ns , e ))
946+ return - 1 , results
947+
948+ if results :
949+ print_message (syslog .LOG_WARNING , "SIDs Check Failure results: {" , json .dumps (results , indent = 4 ), "}" )
950+ return - 1 , results
951+ else :
952+ print_message (syslog .LOG_INFO , "All good!" )
953+ return 0 , None
954+
955+
842956def main ():
843957 """
844958 main entry point, which mainly parses the args and call check_routes
@@ -884,9 +998,22 @@ def main():
884998
885999 while True :
8861000 signal .alarm (TIMEOUT_SECONDS )
887- ret , res = check_routes (namespace )
888- print_message (syslog .LOG_DEBUG , "ret={}, res={}" .format (ret , res ))
1001+ ret1 , res1 = check_routes (namespace )
1002+ print_message (syslog .LOG_DEBUG , "check_routes: ret={}, res={}" .format (ret1 , res1 ))
1003+ ret2 , res2 = check_sids (namespace )
1004+ print_message (syslog .LOG_DEBUG , "check_sids: ret={}, res={}" .format (ret2 , res2 ))
8891005 signal .alarm (0 )
1006+ if ret1 < 0 or ret2 < 0 :
1007+ ret = - 1
1008+ else :
1009+ ret = 0
1010+
1011+ if res1 is not None or res2 is not None :
1012+ res = dict ()
1013+ res .update (res1 if res1 else {})
1014+ res .update (res2 if res2 else {})
1015+ else :
1016+ res = None
8901017
8911018 if interval :
8921019 time .sleep (interval )
0 commit comments