diff --git a/components/images-openstack.yaml b/components/images-openstack.yaml index d024a0d14..23e828889 100644 --- a/components/images-openstack.yaml +++ b/components/images-openstack.yaml @@ -43,7 +43,7 @@ images: neutron_metadata: "ghcr.io/rackerlabs/understack/neutron:2026.1" neutron_ovn_metadata: "ghcr.io/rackerlabs/understack/neutron:2026.1" neutron_openvswitch_agent: "ghcr.io/rackerlabs/understack/neutron:2026.1" - neutron_server: "ghcr.io/rackerlabs/understack/neutron:2026.1" + neutron_server: "ghcr.io/rackerlabs/understack/neutron:pr-2262" neutron_rpc_server: "ghcr.io/rackerlabs/understack/neutron:2026.1" neutron_bagpipe_bgp: "ghcr.io/rackerlabs/understack/neutron:2026.1" neutron_netns_cleanup_cron: "ghcr.io/rackerlabs/understack/neutron:2026.1" diff --git a/python/neutron-understack/neutron_understack/l3_router/vrf.py b/python/neutron-understack/neutron_understack/l3_router/vrf.py index 0be26f1c7..9cbd4c5d6 100644 --- a/python/neutron-understack/neutron_understack/l3_router/vrf.py +++ b/python/neutron-understack/neutron_understack/l3_router/vrf.py @@ -15,6 +15,7 @@ from neutron_understack import config from neutron_understack import evpn_compat +from neutron_understack import maintenance as understack_maintenance from neutron_understack.api.definitions import understack_vni as apidef from neutron_understack.l3_router import understack_vni_db @@ -129,6 +130,12 @@ def get_plugin_type(cls): def get_plugin_description(self): return "Understack router VNI allocation plugin" + def ovn_maintenance_periodics(self, ovn_client): + LOG.warning("NETDEV ovn_maintenance_periodics called") + return [ + understack_maintenance.NetdevRouterMaintenancePeriodics(self, ovn_client) + ] + @staticmethod @resource_extend.extends([apidef.COLLECTION_NAME]) def _extend_router_dict(router_res, router_db): diff --git a/python/neutron-understack/neutron_understack/maintenance.py b/python/neutron-understack/neutron_understack/maintenance.py new file mode 100644 index 000000000..47dd8e1f0 --- /dev/null +++ b/python/neutron-understack/neutron_understack/maintenance.py @@ -0,0 +1,38 @@ +"""Periodics run in the OVN maintenance worker. + +Proves the mechanism fires end to end (hook discovered, worker runs, once +cluster-wide) before the real reconciliation logic is added. +""" + +import socket + +from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance +from oslo_log import log as logging + +LOG = logging.getLogger(__name__) +RECONCILE_SPACING = 60 + + +class NetdevRouterMaintenancePeriodics: + """Reconcile netdev-router Ironic state from the OVN maintenance worker. + + Body is a no-op log line. + """ + + def __init__(self, plugin, ovn_client): + self._plugin = plugin + # Take the maintenance lock so exactly one neutron-server runs these + # periodics, mirroring the OVN maintenance / vpnaas (939978) pattern. + LOG.warning( + "NETDEV periodic __init__; _nb_idl=%r", + getattr(ovn_client, "_nb_idl", "MISSING"), + ) + self._idl = ovn_client._nb_idl.idl + self._idl.set_lock(maintenance.MAINTENANCE_NB_IDL_LOCK_NAME) + LOG.warning("NETDEV periodic lock set") + + @maintenance.has_lock_periodic(spacing=RECONCILE_SPACING, run_immediately=False) + def reconcile_netdev_routers(self): + # No-op: proves the periodic is scheduled and fires on exactly one + # neutron-server. host= lets us confirm it is not firing per-worker. + LOG.warning("NETDEV reconcile tick (host=%s)", socket.gethostname())