OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Services

ovn-octavia-provider — Scenarios

Scenarios — ovn-octavia-provider — branch stable/2026.2

Branch stable/2026.2 @ c56940998594 (11.0.0-2-gc569409) — repository https://opendev.org/openstack/ovn-octavia-provider — analysed on 2026-10-28

Five end-to-end flows. Participants: the user, octavia-api (with the driver and its helper thread), the Octavia driver agent (sockets and the ovn provider agent), Neutron, the OVN NB/SB databases and ovn-northd/ovn-controller. References: octavia: is Octavia stable/2026.2 @ 2900ed67cac2.

1. Create a load balancer with a listener, a pool and a member

The user creates the objects one by one (the full-graph variant queues the same requests from a single loadbalancer_create, ovn_octavia_provider/driver.py:195-214).

sequenceDiagram
  autonumber
  actor U as User
  participant API as octavia-api + driver
  participant H as helper thread (API process)
  participant N as Neutron
  participant NB as OVN NB
  participant DA as driver agent (status.sock)
  U->>API: POST /v2/lbaas/loadbalancers (provider=ovn)
  API->>N: POST /v2.0/ports ovn-lb-vip-<lb> (create_vip_port, synchronous)
  alt Neutron 409 and no same-name port
    API-->>U: 409 VIP address already in use
  end
  API->>H: queue lb_create
  API-->>U: 201 PENDING_CREATE
  H->>N: GET /v2.0/ports?network_id=… , GET /v2.0/subnets/{id}
  H->>NB: transact insert Load_Balancer(name=lb, protocol=[], external_ids)
  H->>NB: transact lr_lb_add / ls_lb_add (routers of the VIP subnet, VIP switch), ls_refs
  H->>DA: status LB ACTIVE/ONLINE
  U->>API: POST /v2/lbaas/listeners (TCP 80)
  API->>H: queue listener_create
  H->>NB: transact external_ids listener_<id>=80:, protocol=tcp, vips={}
  H->>DA: listener ACTIVE/ONLINE, LB ACTIVE
  U->>API: POST /v2/lbaas/pools (SOURCE_IP_PORT, listener)
  API->>H: queue pool_create
  H->>NB: transact external_ids pool_<id>='', listener value += pool_<id>, selection_fields
  H->>DA: pool ACTIVE/ONLINE, listener ACTIVE, LB ACTIVE
  U->>API: POST /v2/lbaas/pools/{id}/members (10.0.0.10:8080, subnet)
  API->>H: queue member_create, handle_member_dvr
  H->>NB: transact pool_<id>=member_<m>_10.0.0.10:8080_<subnet>, vips {"VIP:80": "10.0.0.10:8080"}
  H->>NB: ls_lb_add member switch (ls_refs+1), lr_lb_add member routers
  H->>NB: external_ids neutron:member_status {m: NO_MONITOR}
  H->>DA: full tree: member NO_MONITOR, pool ONLINE, listener ONLINE, LB ONLINE
  H->>NB: (handle_member_dvr) nothing unless LB VIP has a FIP

Diagnostics:

  • LB stuck in PENDING_CREATE: the helper thread could not send the status (look for Error while updating the load balancer status and check that /var/run/octavia/status.sock is served, ovn_octavia_provider/helper.py:429-431), or a handler raised outside its own try (Unexpected exception in request_handler, ovn_octavia_provider/helper.py:437).
  • LB ERROR right after creation: Neutron lookup of the VIP port failed (Cannot get info from neutron, ovn_octavia_provider/helper.py:1583), or the NB transaction failed. The VIP port has been deleted, but the OVN row may remain (ovn_octavia_provider/helper.py:1732-1760).
  • Traffic not balanced while everything is ACTIVE: check ovn-nbctl list Load_Balancer <lb>: vips stays empty until a listener with a pool that has enabled members exists (ovn_octavia_provider/helper.py:1396-1406); check that the LB is in the load_balancer column of the client's switch or router (ls_refs, lr_ref).

2. Add a health monitor and react to a failing member

sequenceDiagram
  autonumber
  actor U as User
  participant API as octavia-api + driver
  participant H as helper thread
  participant N as Neutron
  participant NB as OVN NB
  participant OVN as ovn-northd / ovn-controller
  participant SB as OVN SB
  participant AG as provider agent (event lock holder)
  participant DA as driver agent (status.sock)
  U->>API: POST /v2/lbaas/healthmonitors (TCP, delay 5, max_retries 3)
  alt NB schema without Load_Balancer.health_check, or type HTTP/PING
    API-->>U: 501 does not support a requested option
  end
  API->>H: queue hm_create
  API-->>U: 201 PENDING_CREATE
  H->>NB: transact insert Load_Balancer_Health_Check(vip "VIP:80", options), health_check += ref, octavia:healthmonitors
  loop each member
    H->>NB: read member LSP (address match on the member switch)
    H->>N: GET/POST /v2.0/ports ovn-lb-hm-<subnet> (device_owner ovn-lb-hm:distributed)
    H->>NB: ip_port_mappings[member ip] = "<member lsp>:<hm port ip>"
    H->>NB: neutron:member_status {m: ONLINE}
  end
  H->>DA: HM ACTIVE/ONLINE, pool/listener ONLINE, members ONLINE
  NB-->>OVN: ovn-northd creates SB Service_Monitor per backend
  OVN->>SB: Service_Monitor.status = offline (member stops answering)
  SB-->>AG: ServiceMonitorUpdateEvent (UPDATE)
  AG->>NB: find Load_Balancer by ip_port_mappings + protocol
  AG->>AG: queue hm_update_event
  AG->>NB: neutron:member_status {m: ERROR}
  AG->>DA: get.sock get_loadbalancer / get_pool / get_listener (admin states)
  AG->>DA: status member ERROR, pool ERROR or DEGRADED, listener/LB DEGRADED or ERROR

Diagnostics:

  • HM ERROR at creation: member LSP not found (Member %(member)s Logical_Switch_Port not found…, ovn_octavia_provider/helper.py:3861) or the HM port could not be created (No port on network…, ovn_octavia_provider/helper.py:3874). Check the Neutron port ovn-lb-hm-<subnet id> and its NB LSP type=localport.
  • Member statuses never change: no provider agent holds the event lock or the agent is not enabled ([driver_agent] enabled_provider_agents = ovn, octavia:octavia/common/config.py:914); the SB Service_Monitor table is empty (wrong ip_port_mappings, see the IPv6 bracket fix in 05 § 4.3); or the event names an IP/port not in any pool (Member for event not found, ovn_octavia_provider/helper.py:4436).
  • The HM was created before the listener: its vip is '' and is only set when the listener is created (ovn_octavia_provider/helper.py:3512, ovn_octavia_provider/helper.py:2114-2121).

3. Attach a floating IP to the VIP

sequenceDiagram
  autonumber
  actor U as User
  participant N as Neutron (OVN mech driver)
  participant NB as OVN NB
  participant AG as provider agent
  participant H as agent helper thread
  U->>N: PUT /v2.0/floatingips/{id} port_id=<VIP port>
  N->>NB: NAT dnat_and_snat, LSP ovn-lb-vip-<lb>.external_ids neutron:port_fip=<FIP>
  NB-->>AG: LogicalSwitchPortUpdateEvent (external_ids changed, name ovn-lb-vip-*)
  AG->>N: GET /v2.0/ports/{vip port}
  AG->>H: queue handle_vip_fip (associate) per OVN row of the LB
  H->>NB: external_ids neutron:vip_fip=<FIP>, extra Load_Balancer_Health_Check for the FIP, vips += "FIP:80"
  Note over H,NB: No status is sent: the FIP is not an Octavia object

If members also have FIPs, the next member_create centralises their NAT (external_mac/logical_port cleared, ovn_octavia_provider/helper.py:3374-3385). Diagnostics: the FIP does not answer while the VIP does → check neutron:vip_fip on the OVN row and that vips has a FIP:port key (IPv4 only, ovn_octavia_provider/helper.py:1428-1437); run octavia-ovn-db-sync-util to re-apply it (ovn_octavia_provider/driver.py:856-897).

4. Add a router interface to a member network

sequenceDiagram
  autonumber
  actor U as User
  participant N as Neutron
  participant NB as OVN NB
  participant AG as provider agent
  participant H as agent helper thread
  U->>N: PUT /v2.0/routers/{id}/add_router_interface subnet=S2
  N->>NB: insert Logical_Router_Port lrp-<port>, LSP type=router
  NB-->>AG: LogicalRouterPortEvent (CREATE)
  AG->>NB: lookup Logical_Router neutron-<router>, Logical_Switch neutron-<net>
  AG->>H: queue lb_create_lrp_assoc (is_gw_port from neutron:is_ext_gw)
  H->>NB: lr_lb_add for LBs of the switch; ls_lb_add on the router's switches
  H->>NB: unless gateway port: ls_lb_add for the router's LBs on the new switch

Diagnostics: clients on the new network cannot reach the VIP → the LB is missing from the new switch's load_balancer column; look for The association of loadbalancer %s to the logical switch %s failed (ovn_octavia_provider/helper.py:336-338). LBs on provider networks are never attached to their own switch (ovn_octavia_provider/helper.py:1022-1030).

5. Delete a load balancer in cascade

sequenceDiagram
  autonumber
  actor U as User
  participant API as octavia-api + driver
  participant H as helper thread
  participant N as Neutron
  participant NB as OVN NB
  participant DA as driver agent
  U->>API: DELETE /v2/lbaas/loadbalancers/{id}?cascade=true
  API->>H: queue lb_delete (cascade)
  API-->>U: 204
  H->>NB: find Load_Balancer rows name=<lb>
  alt no OVN row
    H->>DA: get.sock get_loadbalancer (VIP port id)
    H->>N: DELETE /v2.0/ports/{vip} if name ovn-lb-vip-*
    H->>DA: LB DELETED
  else rows found
    loop each row (one per protocol)
      H->>NB: member_delete per pool (ip_port_mappings, ls_refs, vips)
      H->>N: PUT /v2.0/floatingips/{member fip} (empty update) if LB and member have FIPs
      H->>NB: transact clear health_check, ls_lb_del, lr_lb_del, lb_del
      H->>N: DELETE /v2.0/ports/{ovn-lb-hm-subnet} when unused
    end
    H->>N: DELETE /v2.0/ports/{vip} (if provider-created), additional VIP ports
    H->>DA: LB DELETED, listeners/pools/members/HMs DELETED
  end

Diagnostics: LB ERROR after delete → Exception occurred during deletion of loadbalancer (ovn_octavia_provider/helper.py:1864); a user-provided VIP port is kept on purpose (Preserving user-provided VIP port %s, ovn_octavia_provider/helper.py:1855); leftover Load_Balancer_Health_Check rows are removed by the next sync (hm_purge, ovn_octavia_provider/helper.py:4463-4500).

Source: reverse-spec-ovn-octavia-provider/04-scenarios_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.