@@ -86,6 +86,58 @@ def decorator(self, *args, **kwargs):
8686 return wrapper
8787
8888
89+ def log_maintenance_task (func = None , * , start_message = None ):
90+ """Wrap a maintenance task with timing logs.
91+
92+ Use ``@log_maintenance_task`` or pass optional ``start_message``: a static
93+ string logged at DEBUG immediately after the standard "Starting OVN
94+ maintenance task" line. For DEBUG lines that must run only after
95+ in-method guards (early returns), keep those in the task body instead of
96+ ``start_message``.
97+ """
98+
99+ def decorator (f ):
100+ @functools .wraps (f )
101+ def wrapper (* args , ** kwargs ):
102+ LOG .debug ("Starting OVN maintenance task: %s" , f .__name__ )
103+ if start_message :
104+ LOG .debug ("OVN maintenance task: %s" , start_message )
105+ watch = timeutils .StopWatch ()
106+ watch .start ()
107+ never_again = False
108+ result = None
109+ try :
110+ result = f (* args , ** kwargs )
111+ except periodics .NeverAgain :
112+ # Catch it, flag it, and move on to the shared logging below
113+ never_again = True
114+ except Exception :
115+ # Real failures still get their own exception logging
116+ watch .stop ()
117+ LOG .exception (
118+ "OVN maintenance task %(name)s failed after "
119+ "%(time).3f seconds" ,
120+ {'name' : f .__name__ , 'time' : watch .elapsed ()})
121+ raise
122+ # This handles BOTH normal success and NeverAgain
123+ watch .stop ()
124+ LOG .info (
125+ "OVN maintenance task %(name)s finished in "
126+ "%(time).3f seconds" ,
127+ {'name' : f .__name__ , 'time' : watch .elapsed ()})
128+
129+ # Re-raise the signal if the flag was set
130+ if never_again :
131+ raise periodics .NeverAgain ()
132+
133+ return result
134+ return wrapper
135+
136+ if func is not None :
137+ return decorator (func )
138+ return decorator
139+
140+
89141class MaintenanceThread :
90142
91143 def __init__ (self ):
@@ -180,7 +232,6 @@ def __init__(self, ovn_client):
180232 self ._sb_idl = self ._ovn_client ._sb_idl
181233 self ._idl = self ._nb_idl .idl
182234 self ._idl .set_lock (MAINTENANCE_NB_IDL_LOCK_NAME )
183- self ._sync_timer = timeutils .StopWatch ()
184235 super ().__init__ (ovn_client )
185236
186237 self ._resources_func_map = {
@@ -376,6 +427,8 @@ def _log(inconsistencies, type_):
376427
377428 @has_lock_periodic (spacing = ovn_const .DB_CONSISTENCY_CHECK_INTERVAL ,
378429 run_immediately = True )
430+ @log_maintenance_task (
431+ start_message = 'Checking Neutron and OVN revision consistency.' )
379432 def check_for_inconsistencies (self ):
380433 admin_context = n_context .get_admin_context ()
381434 create_update_inconsistencies = (
@@ -390,7 +443,6 @@ def check_for_inconsistencies(self):
390443 'and OVN databases started' )
391444 self ._log_maintenance_inconsistencies (create_update_inconsistencies ,
392445 delete_inconsistencies )
393- self ._sync_timer .restart ()
394446
395447 dbg_log_msg = ('Maintenance task: Fixing resource %(res_uuid)s '
396448 '(type: %(res_type)s) at %(type_)s' )
@@ -436,10 +488,6 @@ def check_for_inconsistencies(self):
436488 {'res_uuid' : row .resource_uuid ,
437489 'res_type' : row .resource_type })
438490
439- self ._sync_timer .stop ()
440- LOG .info ('Maintenance task: Synchronization completed '
441- '(took %.2f seconds)' , self ._sync_timer .elapsed ())
442-
443491 def _create_lrouter_port (self , context , port ):
444492 router_id = port ['device_id' ]
445493 iface_info = self ._ovn_client ._l3_plugin ._add_neutron_router_interface (
@@ -496,15 +544,14 @@ def _delete_floatingip_and_pf(self, context, fip_id):
496544 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
497545 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
498546 run_immediately = True )
547+ @log_maintenance_task (
548+ start_message = (
549+ 'Check missing prefix router_name' ,
550+ 'in external_ids of LRPs.' ))
499551 def update_lrouter_ports_ext_ids_name_prefix (self ):
500552 """Update OVN logical router ports if missing external ids
501553 "neutron-" prefix for router name.
502554 """
503- LOG .debug (
504- 'Maintenance task: Check missing prefix router_name in '
505- 'external_ids of LRPs.' )
506- self ._sync_timer .restart ()
507-
508555 lrp_key = ('external_ids' , '!=' ,
509556 {ovn_const .OVN_ROUTER_NAME_EXT_ID_KEY : '' })
510557 lrp_router_id_map = {
@@ -529,9 +576,6 @@ def update_lrouter_ports_ext_ids_name_prefix(self):
529576 'Logical_Router_Port' , lrp_name ,
530577 ('external_ids' , router_name )))
531578
532- self ._sync_timer .stop ()
533- LOG .info ('Maintenance task: Check missing prefix router_name in LRPs '
534- '(took %.2f seconds)' , self ._sync_timer .elapsed ())
535579 raise periodics .NeverAgain ()
536580
537581 # A static spacing value is used here, but this method will only run
@@ -540,13 +584,13 @@ def update_lrouter_ports_ext_ids_name_prefix(self):
540584 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
541585 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
542586 run_immediately = True )
587+ @log_maintenance_task (
588+ start_message = 'Check global DHCP options consistency.' )
543589 def check_global_dhcp_opts (self ):
544590 if (not ovn_conf .get_global_dhcpv4_opts () and
545591 not ovn_conf .get_global_dhcpv6_opts ()):
546592 # No need to scan the subnets if the settings are unset.
547593 raise periodics .NeverAgain ()
548- LOG .debug ('Maintenance task: Checking DHCP options on subnets' )
549- self ._sync_timer .restart ()
550594 fix_subnets = self ._check_subnet_global_dhcp_opts ()
551595 if fix_subnets :
552596 admin_context = n_context .get_admin_context ()
@@ -563,10 +607,6 @@ def check_global_dhcp_opts(self):
563607 LOG .exception ('Failed to update subnet %s' ,
564608 subnet ['id' ])
565609
566- self ._sync_timer .stop ()
567- LOG .info ('Maintenance task: DHCP options check completed '
568- '(took %.2f seconds)' , self ._sync_timer .elapsed ())
569-
570610 raise periodics .NeverAgain ()
571611
572612 # A static spacing value is used here, but this method will only run
@@ -575,6 +615,8 @@ def check_global_dhcp_opts(self):
575615 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
576616 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
577617 run_immediately = True )
618+ @log_maintenance_task (
619+ start_message = 'Check for IGMP snoop support.' )
578620 def check_for_igmp_snoop_support (self ):
579621 snooping_conf = ovs_conf .get_igmp_snooping_enabled ()
580622 flood_conf = ovs_conf .get_igmp_flood_unregistered ()
@@ -609,6 +651,10 @@ def check_for_igmp_snoop_support(self):
609651 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
610652 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
611653 run_immediately = True )
654+ @log_maintenance_task (
655+ start_message = (
656+ 'Ensure localnet ports have learn-fdb set per '
657+ 'configuration.' ))
612658 def check_localnet_port_has_learn_fdb (self ):
613659 ports = self ._nb_idl .db_find_rows (
614660 "Logical_Switch_Port" , ("type" , "=" , ovn_const .LSP_TYPE_LOCALNET )
@@ -639,6 +685,10 @@ def check_localnet_port_has_learn_fdb(self):
639685 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
640686 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
641687 run_immediately = True )
688+ @log_maintenance_task (
689+ start_message = (
690+ 'Align router gateway redirect-type for provider '
691+ 'VLAN/FLAT networks.' ))
642692 def check_redirect_type_router_gateway_ports (self ):
643693 """Check OVN router gateway ports
644694 Check for the option "redirect-type=bridged" value for
@@ -709,6 +759,10 @@ def check_redirect_type_router_gateway_ports(self):
709759 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
710760 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
711761 run_immediately = True )
762+ @log_maintenance_task (
763+ start_message = (
764+ 'Align reside-on-redirect-chassis on VLAN/FLAT '
765+ 'distributed ports.' ))
712766 def check_provider_distributed_ports (self ):
713767 """Check provider (VLAN and FLAT) distributed ports
714768 Check for the option "reside-on-redirect-chassis" value for
@@ -748,6 +802,10 @@ def check_provider_distributed_ports(self):
748802 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
749803 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
750804 run_immediately = True )
805+ @log_maintenance_task (
806+ start_message = (
807+ 'Apply FDB aging limits to NB_Global and provider '
808+ 'switches.' ))
751809 def check_fdb_aging_settings (self ):
752810 """Check FDB aging settings
753811
@@ -789,6 +847,8 @@ def check_fdb_aging_settings(self):
789847 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
790848 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
791849 run_immediately = True )
850+ @log_maintenance_task (
851+ start_message = 'Update MAC binding aging and router MAC age limits.' )
792852 def update_mac_aging_settings (self ):
793853 """Ensure that MAC_Binding aging options are set"""
794854 removal_limit = ovn_conf .get_ovn_mac_binding_removal_limit ()
@@ -804,6 +864,8 @@ def update_mac_aging_settings(self):
804864 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
805865 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
806866 run_immediately = True )
867+ @log_maintenance_task (
868+ start_message = 'Sync baremetal port DHCP options with configuration.' )
807869 def check_baremetal_ports_dhcp_options (self ):
808870 """Update baremetal ports DHCP options
809871
@@ -851,6 +913,10 @@ def check_baremetal_ports_dhcp_options(self):
851913 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
852914 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
853915 run_immediately = True )
916+ @log_maintenance_task (
917+ start_message = (
918+ 'Remove default routes with empty destination from '
919+ 'routers.' ))
854920 def check_router_default_route_empty_dst_ip (self ):
855921 """Check routers with default route with empty dst-ip (LP: #2002993).
856922 """
@@ -880,6 +946,10 @@ def check_router_default_route_empty_dst_ip(self):
880946 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
881947 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
882948 run_immediately = True )
949+ @log_maintenance_task (
950+ start_message = (
951+ 'Refresh ACL logging fair meter after configuration '
952+ 'reload.' ))
883953 def check_fair_meter_consistency (self ):
884954 """Update the logging meter after neutron-server reload
885955
@@ -894,6 +964,8 @@ def check_fair_meter_consistency(self):
894964 raise periodics .NeverAgain ()
895965
896966 @has_lock_periodic (spacing = 86400 , run_immediately = True )
967+ @log_maintenance_task (
968+ start_message = 'Purge stale hash ring nodes older than five days.' )
897969 def cleanup_old_hash_ring_nodes (self ):
898970 """Daily task to cleanup old stable Hash Ring node entries.
899971
@@ -906,6 +978,8 @@ def cleanup_old_hash_ring_nodes(self):
906978 hash_ring_db .cleanup_old_nodes (context , days = 5 )
907979
908980 @has_lock_periodic (spacing = 86400 , run_immediately = True )
981+ @log_maintenance_task (
982+ start_message = 'Apply ovn_nb_global settings to NB_Global options.' )
909983 def configure_nb_global (self ):
910984 """Configure Northbound OVN NB_Global options
911985
@@ -922,6 +996,10 @@ def configure_nb_global(self):
922996 raise periodics .NeverAgain ()
923997
924998 @has_lock_periodic (spacing = 86400 , run_immediately = True )
999+ @log_maintenance_task (
1000+ start_message = (
1001+ 'Sync router.distributed with distributed floating IP '
1002+ 'config.' ))
9251003 def update_router_distributed_flag (self ):
9261004 """Set "enable_distributed_floating_ip" on the router.distributed flag.
9271005
@@ -945,6 +1023,10 @@ def update_router_distributed_flag(self):
9451023 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
9461024 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
9471025 run_immediately = True )
1026+ @log_maintenance_task (
1027+ start_message = (
1028+ 'Set network type and physnet external_ids on logical '
1029+ 'switches.' ))
9481030 def set_network_type_and_physnet (self ):
9491031 """Add the network type and physnet to the Logical_Switch registers"""
9501032 context = n_context .get_admin_context ()
@@ -980,6 +1062,10 @@ def set_network_type_and_physnet(self):
9801062 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
9811063 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
9821064 run_immediately = True )
1065+ @log_maintenance_task (
1066+ start_message = (
1067+ 'Sync broadcast-arps-to-all-routers on external '
1068+ 'networks.' ))
9831069 def check_network_broadcast_arps_to_all_routers (self ):
9841070 """Check the broadcast-arps-to-all-routers config
9851071
@@ -1015,6 +1101,8 @@ def check_network_broadcast_arps_to_all_routers(self):
10151101
10161102 # TODO(racosta): Remove this method in the E+2 cycle (SLURP release)
10171103 @has_lock_periodic (spacing = 600 , run_immediately = True )
1104+ @log_maintenance_task (
1105+ start_message = 'Mark Neutron-owned static routes in OVN external_ids.' )
10181106 def update_router_static_routes (self ):
10191107 """Set external_ids column to any Neutron's owned static route.
10201108 """
@@ -1060,6 +1148,8 @@ def update_router_static_routes(self):
10601148 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
10611149 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
10621150 run_immediately = True )
1151+ @log_maintenance_task (
1152+ start_message = 'Set distributed floating IP flag in NB_Global.' )
10631153 def set_fip_distributed_flag (self ):
10641154 """Set the NB_Global.external_ids:fip-distributed flag.
10651155
@@ -1081,6 +1171,10 @@ def set_fip_distributed_flag(self):
10811171 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
10821172 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
10831173 run_immediately = True )
1174+ @log_maintenance_task (
1175+ start_message = (
1176+ 'Apply ovn_owned option on DNS records per '
1177+ 'configuration.' ))
10841178 def set_ovn_owned_dns_option (self ):
10851179 """Set the ovn_owned option as configured for the DNS records"""
10861180 cmds = []
@@ -1104,6 +1198,8 @@ def set_ovn_owned_dns_option(self):
11041198 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
11051199 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
11061200 run_immediately = True )
1201+ @log_maintenance_task (
1202+ start_message = 'Apply HA chassis failover BFD settings to NB_Global.' )
11071203 def update_ha_failover (self ):
11081204 """Set the OVN BFD settings to control the HA failover timeout"""
11091205 strategy = cfg .CONF .ovn .ha_failover_strategy
@@ -1160,6 +1256,8 @@ def update_qos_fip_rule_priority(self):
11601256 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
11611257 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
11621258 run_immediately = True )
1259+ @log_maintenance_task (
1260+ start_message = 'Backfill network_id on DHCP_Options external_ids.' )
11631261 def check_dhcp_options_consistency (self ):
11641262 admin_context = n_context .get_admin_context ()
11651263 cmds = []
@@ -1199,6 +1297,8 @@ def check_dhcp_options_consistency(self):
11991297 periodic_run_limit = ovn_const .MAINTENANCE_TASK_RETRY_LIMIT ,
12001298 spacing = ovn_const .MAINTENANCE_ONE_RUN_TASK_SPACING ,
12011299 run_immediately = True )
1300+ @log_maintenance_task (
1301+ start_message = 'Repair Address_Sets and ACLs for address group rules.' )
12021302 def update_security_group_with_address_group (self ):
12031303 """Create all Address_Set and update the corresponding ACLs"""
12041304 # 1. List all Address Groups with missing Address_Set registers.
0 commit comments