46
46
import signal
47
47
import traceback
48
48
import subprocess
49
+ import concurrent .futures
49
50
50
51
from ipaddress import ip_network
51
52
from swsscommon import swsscommon
@@ -338,10 +339,18 @@ def is_suppress_fib_pending_enabled(namespace):
338
339
return state == 'enabled'
339
340
340
341
341
- def get_frr_routes ( namespace ):
342
+ def fetch_routes ( cmd ):
342
343
"""
343
- Read routes from zebra through CLI command
344
- :return frr routes dictionary
344
+ Fetch routes using the given command.
345
+ """
346
+ output = subprocess .check_output (cmd , text = True )
347
+ return json .loads (output )
348
+
349
+
350
+ def get_frr_routes_parallel (namespace ):
351
+ """
352
+ Read routes from zebra through CLI command for IPv4 and IPv6 in parallel
353
+ :return combined IPv4 and IPv6 routes dictionary.
345
354
"""
346
355
if namespace == multi_asic .DEFAULT_NAMESPACE :
347
356
v4_route_cmd = ['show' , 'ip' , 'route' , 'json' ]
@@ -350,12 +359,18 @@ def get_frr_routes(namespace):
350
359
v4_route_cmd = ['show' , 'ip' , 'route' , '-n' , namespace , 'json' ]
351
360
v6_route_cmd = ['show' , 'ipv6' , 'route' , '-n' , namespace , 'json' ]
352
361
353
- output = subprocess .check_output (v4_route_cmd , text = True )
354
- routes = json .loads (output )
355
- output = subprocess .check_output (v6_route_cmd , text = True )
356
- routes .update (json .loads (output ))
357
- print_message (syslog .LOG_DEBUG , "FRR Routes: namespace={}, routes={}" .format (namespace , routes ))
358
- return routes
362
+ with concurrent .futures .ThreadPoolExecutor () as executor :
363
+ future_v4 = executor .submit (fetch_routes , v4_route_cmd )
364
+ future_v6 = executor .submit (fetch_routes , v6_route_cmd )
365
+
366
+ # Wait for both results to complete
367
+ v4_routes = future_v4 .result ()
368
+ v6_routes = future_v6 .result ()
369
+
370
+ # Combine both IPv4 and IPv6 routes
371
+ v4_routes .update (v6_routes )
372
+ print_message (syslog .LOG_DEBUG , "FRR Routes: namespace={}, routes={}" .format (namespace , v4_routes ))
373
+ return v4_routes
359
374
360
375
361
376
def get_interfaces (namespace ):
@@ -556,7 +571,7 @@ def check_frr_pending_routes(namespace):
556
571
retries = FRR_CHECK_RETRIES
557
572
for i in range (retries ):
558
573
missed_rt = []
559
- frr_routes = get_frr_routes (namespace )
574
+ frr_routes = get_frr_routes_parallel (namespace )
560
575
561
576
for _ , entries in frr_routes .items ():
562
577
for entry in entries :
@@ -689,8 +704,9 @@ def _filter_out_neigh_route(routes, neighs):
689
704
return rt_appl_miss , rt_asic_miss
690
705
691
706
692
- def check_routes (namespace ):
707
+ def check_routes_for_namespace (namespace ):
693
708
"""
709
+ Process a Single Namespace:
694
710
The heart of this script which runs the checks.
695
711
Read APPL-DB & ASIC-DB, the relevant tables for route checking.
696
712
Checkout routes in ASIC-DB to match APPL-DB, discounting local &
@@ -708,98 +724,113 @@ def check_routes(namespace):
708
724
:return (0, None) on sucess, else (-1, results) where results holds
709
725
the unjustifiable entries.
710
726
"""
711
- namespace_list = []
712
- if namespace is not multi_asic .DEFAULT_NAMESPACE and namespace in multi_asic .get_namespace_list ():
713
- namespace_list .append (namespace )
714
- else :
715
- namespace_list = multi_asic .get_namespace_list ()
716
- print_message (syslog .LOG_INFO , "Checking routes for namespaces: " , namespace_list )
717
727
718
728
results = {}
719
- adds = {}
720
- deletes = {}
721
- for namespace in namespace_list :
722
- intf_appl_miss = []
723
- rt_appl_miss = []
724
- rt_asic_miss = []
725
- rt_frr_miss = []
726
- adds [namespace ] = []
727
- deletes [namespace ] = []
729
+ adds = []
730
+ deletes = []
731
+ intf_appl_miss = []
732
+ rt_appl_miss = []
733
+ rt_asic_miss = []
734
+ rt_frr_miss = []
728
735
729
- selector , subs , rt_asic = get_asicdb_routes (namespace )
736
+ selector , subs , rt_asic = get_asicdb_routes (namespace )
730
737
731
- rt_appl = get_appdb_routes (namespace )
732
- intf_appl = get_interfaces (namespace )
738
+ rt_appl = get_appdb_routes (namespace )
739
+ intf_appl = get_interfaces (namespace )
733
740
734
- # Diff APPL-DB routes & ASIC-DB routes
735
- rt_appl_miss , rt_asic_miss = diff_sorted_lists (rt_appl , rt_asic )
741
+ # Diff APPL-DB routes & ASIC-DB routes
742
+ rt_appl_miss , rt_asic_miss = diff_sorted_lists (rt_appl , rt_asic )
736
743
737
- # Check missed ASIC routes against APPL-DB INTF_TABLE
738
- _ , rt_asic_miss = diff_sorted_lists (intf_appl , rt_asic_miss )
739
- rt_asic_miss = filter_out_default_routes (rt_asic_miss )
740
- rt_asic_miss = filter_out_vnet_routes (namespace , rt_asic_miss )
741
- rt_asic_miss = filter_out_standalone_tunnel_routes (namespace , rt_asic_miss )
742
- rt_asic_miss = filter_out_soc_ip_routes (namespace , rt_asic_miss )
744
+ # Check missed ASIC routes against APPL-DB INTF_TABLE
745
+ _ , rt_asic_miss = diff_sorted_lists (intf_appl , rt_asic_miss )
746
+ rt_asic_miss = filter_out_default_routes (rt_asic_miss )
747
+ rt_asic_miss = filter_out_vnet_routes (namespace , rt_asic_miss )
748
+ rt_asic_miss = filter_out_standalone_tunnel_routes (namespace , rt_asic_miss )
749
+ rt_asic_miss = filter_out_soc_ip_routes (namespace , rt_asic_miss )
743
750
751
+ # Check APPL-DB INTF_TABLE with ASIC table route entries
752
+ intf_appl_miss , _ = diff_sorted_lists (intf_appl , rt_asic )
744
753
745
- # Check APPL-DB INTF_TABLE with ASIC table route entries
746
- intf_appl_miss , _ = diff_sorted_lists ( intf_appl , rt_asic )
754
+ if rt_appl_miss :
755
+ rt_appl_miss = filter_out_local_interfaces ( namespace , rt_appl_miss )
747
756
748
- if rt_appl_miss :
749
- rt_appl_miss = filter_out_local_interfaces (namespace , rt_appl_miss )
757
+ if rt_appl_miss :
758
+ rt_appl_miss = filter_out_voq_neigh_routes (namespace , rt_appl_miss )
750
759
751
- if rt_appl_miss :
752
- rt_appl_miss = filter_out_voq_neigh_routes (namespace , rt_appl_miss )
760
+ # NOTE: On dualtor environment, ignore any route miss for the
761
+ # neighbors learned from the vlan subnet.
762
+ if rt_appl_miss or rt_asic_miss :
763
+ rt_appl_miss , rt_asic_miss = filter_out_vlan_neigh_route_miss (namespace , rt_appl_miss , rt_asic_miss )
753
764
754
- # NOTE: On dualtor environment, ignore any route miss for the
755
- # neighbors learned from the vlan subnet.
756
- if rt_appl_miss or rt_asic_miss :
757
- rt_appl_miss , rt_asic_miss = filter_out_vlan_neigh_route_miss (namespace , rt_appl_miss , rt_asic_miss )
765
+ if rt_appl_miss or rt_asic_miss :
766
+ # Look for subscribe updates for a second
767
+ adds , deletes = get_subscribe_updates (selector , subs )
758
768
759
- if rt_appl_miss or rt_asic_miss :
760
- # Look for subscribe updates for a second
761
- adds [namespace ], deletes [namespace ] = get_subscribe_updates (selector , subs )
769
+ # Drop all those for which SET received
770
+ rt_appl_miss , _ = diff_sorted_lists (rt_appl_miss , adds )
762
771
763
- # Drop all those for which SET received
764
- rt_appl_miss , _ = diff_sorted_lists (rt_appl_miss , adds [ namespace ] )
772
+ # Drop all those for which DEL received
773
+ rt_asic_miss , _ = diff_sorted_lists (rt_asic_miss , deletes )
765
774
766
- # Drop all those for which DEL received
767
- rt_asic_miss , _ = diff_sorted_lists ( rt_asic_miss , deletes [ namespace ])
775
+ if rt_appl_miss :
776
+ results [ "missed_ROUTE_TABLE_routes" ] = rt_appl_miss
768
777
769
- if rt_appl_miss :
770
- if namespace not in results :
771
- results [namespace ] = {}
772
- results [namespace ]["missed_ROUTE_TABLE_routes" ] = rt_appl_miss
778
+ if intf_appl_miss :
779
+ results ["missed_INTF_TABLE_entries" ] = intf_appl_miss
773
780
774
- if intf_appl_miss :
775
- if namespace not in results :
776
- results [namespace ] = {}
777
- results [namespace ]["missed_INTF_TABLE_entries" ] = intf_appl_miss
781
+ if rt_asic_miss :
782
+ results ["Unaccounted_ROUTE_ENTRY_TABLE_entries" ] = rt_asic_miss
778
783
779
- if rt_asic_miss :
780
- if namespace not in results :
781
- results [namespace ] = {}
782
- results [namespace ]["Unaccounted_ROUTE_ENTRY_TABLE_entries" ] = rt_asic_miss
784
+ rt_frr_miss = check_frr_pending_routes (namespace )
783
785
784
- rt_frr_miss = check_frr_pending_routes (namespace )
786
+ if rt_frr_miss :
787
+ results ["missed_FRR_routes" ] = rt_frr_miss
785
788
786
- if rt_frr_miss :
787
- if namespace not in results :
788
- results [namespace ] = {}
789
- results [namespace ]["missed_FRR_routes" ] = rt_frr_miss
789
+ if results :
790
+ if rt_frr_miss and not rt_appl_miss and not rt_asic_miss :
791
+ print_message (syslog .LOG_ERR , "Some routes are not set offloaded in FRR{} \
792
+ but all routes in APPL_DB and ASIC_DB are in sync" .format (namespace ))
793
+ if is_suppress_fib_pending_enabled (namespace ):
794
+ mitigate_installed_not_offloaded_frr_routes (namespace , rt_frr_miss , rt_appl )
795
+
796
+ return results , adds , deletes
790
797
791
- if results :
792
- if rt_frr_miss and not rt_appl_miss and not rt_asic_miss :
793
- print_message (syslog .LOG_ERR , "Some routes are not set offloaded in FRR{} \
794
- but all routes in APPL_DB and ASIC_DB are in sync" .format (namespace ))
795
- if is_suppress_fib_pending_enabled (namespace ):
796
- mitigate_installed_not_offloaded_frr_routes (namespace , rt_frr_miss , rt_appl )
798
+
799
+ def check_routes (namespace ):
800
+ """
801
+ Main function to parallelize route checks across all namespaces.
802
+ """
803
+ namespace_list = []
804
+ if namespace is not multi_asic .DEFAULT_NAMESPACE and namespace in multi_asic .get_namespace_list ():
805
+ namespace_list .append (namespace )
806
+ else :
807
+ namespace_list = multi_asic .get_namespace_list ()
808
+ print_message (syslog .LOG_INFO , "Checking routes for namespaces: " , namespace_list )
809
+
810
+ results = {}
811
+ all_adds = {}
812
+ all_deletes = {}
813
+
814
+ # Use ThreadPoolExecutor to parallelize the check for each namespace
815
+ with concurrent .futures .ThreadPoolExecutor () as executor :
816
+ futures = {executor .submit (check_routes_for_namespace , ns ): ns for ns in namespace_list }
817
+
818
+ for future in concurrent .futures .as_completed (futures ):
819
+ ns = futures [future ]
820
+ try :
821
+ result , adds , deletes = future .result ()
822
+ if result :
823
+ results [ns ] = result
824
+ all_adds [ns ] = adds
825
+ all_deletes [ns ] = deletes
826
+ except Exception as e :
827
+ print_message (syslog .LOG_ERR , "Error processing namespace {}: {}" .format (ns , e ))
797
828
798
829
if results :
799
830
print_message (syslog .LOG_WARNING , "Failure results: {" , json .dumps (results , indent = 4 ), "}" )
800
831
print_message (syslog .LOG_WARNING , "Failed. Look at reported mismatches above" )
801
- print_message (syslog .LOG_WARNING , "add: " , json .dumps (adds , indent = 4 ))
802
- print_message (syslog .LOG_WARNING , "del: " , json .dumps (deletes , indent = 4 ))
832
+ print_message (syslog .LOG_WARNING , "add: " , json .dumps (all_adds , indent = 4 ))
833
+ print_message (syslog .LOG_WARNING , "del: " , json .dumps (all_deletes , indent = 4 ))
803
834
return - 1 , results
804
835
else :
805
836
print_message (syslog .LOG_INFO , "All good!" )
@@ -862,6 +893,5 @@ def main():
862
893
return ret , res
863
894
864
895
865
-
866
896
if __name__ == "__main__" :
867
897
sys .exit (main ()[0 ])
0 commit comments