Skip to content

Commit 61f100d

Browse files
chrisbugmartinmo
authored andcommitted
Add logging decorator for OVN maintenance methods
The OVN maintenance methods had an inconsistent log strategy. This patch introduces a log_maintenance_task decorator using oslo_utils.timeutils to ensure all maintenance methods log their start and end times consistently without code duplication Closes-Bug: #2142833 Assisted-By: Claude composer-1.5 Change-Id: I990caa72db794c6812e3eb979155a67108385379 Signed-off-by: Chris Buggy <cbuggy@redhat.com> (cherry picked from commit e37278b) Signed-off-by: Martin Morgenstern <martin.morgenstern@cloudandheat.com>
1 parent 6f49b3b commit 61f100d

1 file changed

Lines changed: 120 additions & 20 deletions

File tree

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py

Lines changed: 120 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
89141
class 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

Comments
 (0)