Hi,

This series fixes two bugs found while investigating a production incident
on OVN 25.09.0, and asks for direction on a third gap that the two fixes do
not cover.

Summary
-------

A load balancer backend is probed only by the ovn-controller of the chassis
its port is bound to, and only that ovn-controller writes the
Service_Monitor status.  When the chassis dies, nothing expires the last
"online": the Service_Monitor row keeps status=online until the chassis
comes back and probes again, and northd keeps sending traffic to the dead
backend for as long as that takes.

This is not specific to gateway chassis.  Every chassis that hosts a
backend probes it with its own pinctrl, so a compute-only hypervisor dying
produces the same result.  In the deployment below the controllers also
host VMs, which is only why the same host's timeline shows both the BFD
failover of its cr-lrps and the Service_Monitor that nobody probed anymore:
OVN did notice the chassis was gone within seconds, but that knowledge is
not connected to service monitors.

What happened
-------------

hv1 hosts VM B, a backend of a TCP load balancer with a health check.  hv1
is also a gateway chassis, which is incidental here.  The host suffered
I/O starvation, then died, then was hard-reset.  In stages:

1. ovn-controller stalled, host alive.  The main loop blocked for up to
   275 s at a time, the pinctrl thread for up to 246 s, and the SB
   connection was lost.  Nothing probed B.  Service_Monitor for B:
   status=online, chassis_name=hv1.

2. Host dead.  hv2 logged
       Changing chassis for lport cr-lrp-... from hv1 to hv2
   i.e. BFD detected the loss and the gateway failed over.  The
   Service_Monitor for B kept status=online.  VIP requests failed ~50%:
   the active backends were B (dead) and one healthy one.

3. Host rebooted, VM B not started.  Its tap does not exist, so
       ovs-vswitchd: could not open network device tap<B> (No such device)
   and the fresh ovn-controller released the ports still bound to it:
       binding|INFO|Removing iface tap<B> ovn-installed in OVS
       binding|INFO|Releasing lport <B> from this chassis (sb_readonly=0)
       if_status|WARN|Trying to release unknown interface <X>
   No "Setting lport <B> down in Southbound" followed.  SB:
       Port_Binding <B>:     chassis=[]  up=[true]
       Service_Monitor <B>:  chassis_name=hv1  status=online
   northd derived NB Logical_Switch_Port.up=false from the empty chassis,
   so the CMS saw the port DOWN while the Service_Monitor kept
   status=online.

4. VM B started, hv1 re-claimed the port, a probe succeeded and the
   Service_Monitor status finally changed.  Nothing in between touched it.

Why
---

1. Only the owning chassis probes and writes the status.
   controller/pinctrl.c sync_svc_monitors():
       if (pb->chassis != our_chassis) continue;
   Nothing else writes "online" and nothing expires the status.

2. northd's safety net only looks at "up".  Commit 23e203a3f ("northd: set
   svc_mon status to offline if port_binding released") sets
   Service_Monitor.status to offline when Port_Binding.up is false, but
   not when the port is unbound (chassis empty, up still true).
   chassis_name is also never cleared, so it keeps naming a chassis that
   stopped probing.  The same northd derives NB Logical_Switch_Port.up=false
   from an empty chassis, so the CMS and the Service_Monitor disagree about
   the same port.

3. ovn-controller can leave a port unbound-but-up.  A port that was already
   bound to this chassis when ovn-controller started is not tracked by
   if-status.  When the recompute path releases it (no local binding
   because the interface cannot be opened), release_lport() clears the
   chassis but if_status_mgr_release_iface() returns early on an unknown
   interface, so "up" is never cleared.  The incremental path
   (release_binding_lport()) does set it down.

4. Chassis liveness is not connected to service monitors.  BFD is enabled
   only between HA chassis group members and their ref_chassis
   (controller/bfd.c bfd_calculate_chassis()), its result lives in the
   local OVS Interface.bfd_status only, and it is consumed only for cr-lrp
   and external port election.  northd does not use Chassis_Private
   nb_cfg/nb_cfg_timestamp for anything but hv_cfg.

This series
-----------

Patch 1 (northd) treats an unbound backend port like a port that is down:
Service_Monitor.status is set to offline and chassis_name is cleared.
This covers stage 3 above and also "ovn-appctl exit" without --restart and
"ovn-sbctl chassis-del", both of which clear the chassis while leaving "up"
true.  ovn-controller still owns the transition back to "online" once the
port is bound again and a probe succeeds; northd never writes "online".
The ovn-ic service monitor test emulated probed backends by setting "up"
without a chassis; it now binds the ports.

Patch 2 (ovn-controller) sets "up" to false when release_lport() releases a
port that if-status does not track, matching the tracked path.  With it the
unbound-but-up state of stage 3 is not produced in the first place.

The two are independent and can be applied in either order.  We would like
both considered for branch-25.09: patch 2 applies there as is, patch 1
needs a small adaptation (the LSP health check path does not exist there)
which we can send separately.

What this series does not fix, and a question
---------------------------------------------

If the chassis simply dies (or its ovn-controller is stopped with SIGTERM,
which skips the cleanup path), Port_Binding.chassis and up stay as they
were, so neither patch applies and the Service_Monitor keeps status=online
until the chassis comes back.  This is stage 2 above, and it applies to
any hypervisor that hosts a backend.

The information needed to close this gap already exists: every chassis
hosting a backend has a BFD session with the gateway chassis (northd puts
it in ref_chassis as soon as a port is bound; we measured the session
coming up within about 1 s of the binding), and BFD reported hv1 down
within seconds.  What is missing is a consumer.  Two shapes we considered:

  A. ovn-controller on a gateway chassis sets Service_Monitor rows whose
     chassis_name is a BFD-down peer to "offline" (edge-triggered, only
     while it still sees a majority of its own BFD peers up).  No schema
     change, but a non-owner then writes the status, and true quorum is
     not possible because observers do not share their view.

  B. Each chassis publishes the set of BFD-down peers (e.g. a new Chassis
     column), and northd sets a Service_Monitor offline when a majority of
     the HA chassis of the groups that reference the backend's chassis
     report it down.  Single writer and a real quorum, at the cost of a
     schema change.

Before writing either we would like to know: is it acceptable for northd
(or a non-owning chassis) to set a Service_Monitor offline based on chassis
liveness at all, and if so which of the two shapes fits the project?
Should it be opt-in?  A stalled ovn-controller on a live host (stage 1)
is not visible to BFD and would need a prober liveness signal instead; we
consider that out of scope for now.

Jaygue Lee (2):
  northd: Mark unbound ports' service monitors offline.
  controller: Set untracked ports down on release.

 controller/binding.c    |  8 +++++
 northd/northd.c         | 25 ++++++++------
 tests/ovn-controller.at | 42 +++++++++++++++++++++++
 tests/ovn-ic.at         | 18 +++++-----
 tests/ovn-northd.at     | 75 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 150 insertions(+), 18 deletions(-)

-- 
2.49.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to