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

Reverse-spec · Services

ovn-octavia-provider — Interactions

Interactions — 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

The provider talks to four systems. It has no SQL database, no RabbitMQ and no cache: its state is the OVN Northbound database, and it learns Octavia's state through the driver-agent sockets.

SystemProtocolLibraryDirectionSync/asyncConfiguration
OVN NorthboundOVSDB JSON-RPC (RFC 7047) over TCP, SSL or Unix socketovsdbapp + ovs IDLread (in-memory replica) + write (transact) + monitortransactions synchronous inside the helper thread[ovn] ovn_nb_connection (ovn_octavia_provider/common/config.py:24-32)
OVN SouthboundOVSDB JSON-RPCovsdbappmonitor only (Service_Monitor)events[ovn] ovn_sb_connection (ovn_octavia_provider/common/config.py:45-53)
Octavia driver agentJSON over Unix stream socketsoctavia_lib.api.drivers.driver_libout: status updates; out: object readssynchronous request/responsesockets /var/run/octavia/{status,get}.sock (octavia-lib:octavia_lib/api/drivers/driver_lib.py:26-28)
NeutronREST (HTTPS)openstacksdk network proxy, keystoneauth1 sessionoutsynchronous[neutron] (+ fallback [service_auth]) (ovn_octavia_provider/common/clients.py:115-149)
Octavia APIRESTopenstacksdk load-balancer proxyout (sync tool only)synchronous[service_auth] (ovn_octavia_provider/common/clients.py:152-183)
KeystoneRESTkeystoneauth1out (token)synchronous[neutron] / [service_auth] auth options (ovn_octavia_provider/common/config.py:144-163)

References: octavia: is Octavia stable/2026.2 @ 2900ed67cac2, octavia-lib: is octavia-lib stable/2026.2 @ a46b0ff4fa95.

1. OVN Northbound database

1.1 Connections

Who opens itPurposeTables replicatedLockWhere
each OvnProviderDriver instance (one per Octavia API call, see 01 § 1.1)read + transact for its helper thread; notifier=False (no events)Logical_Switch, Load_Balancer, Load_Balancer_Health_Check, Logical_Router, Logical_Switch_Port, Logical_Router_Port, Gateway_Chassis, NAT, HA_Chassis_Groupnoneovn_octavia_provider/driver.py:52, ovn_octavia_provider/helper.py:63-66, ovn_octavia_provider/ovsdb/impl_idl_ovn.py:208-225
driver agent, helperread + transact for the agent's own helper threadsamenoneovn_octavia_provider/agent.py:40
driver agent, eventsmonitor for LogicalRouterPortEvent, LogicalSwitchPortUpdateEventsameOVSDB lock neutron_ovn_octavia_event_lockovn_octavia_provider/agent.py:28, ovn_octavia_provider/agent.py:46-51
driver agent, maintenanceread + transact for the periodic taskssamenoneovn_octavia_provider/maintenance.py:66-70
octavia_ovn_sync Neutron pluginreuses Neutron's own NB API object (_nb_idl override) **[inferred: the helper uses ovn_nbdb_api, not _nb_idl, so the attribute set is probably unused]**——ovn_octavia_provider/ovn/db_sync.py:85-87

Connection parameters:

  • ovn_nb_connection is a list joined by commas, so several cluster members can be given (ovn_octavia_provider/common/config.py:193-194);
  • SSL key, certificate and CA are set process-wide through ovs.stream.Stream.ssl_set_* (ovn_octavia_provider/ovsdb/ovsdb_monitor.py:89-118); NB and SB therefore cannot use different client certificates in the same process [inferred: the last call wins];
  • the inactivity probe is [ovn] ovsdb_probe_interval ms (default 60000) (ovn_octavia_provider/ovsdb/impl_idl_ovn.py:156-160, ovn_octavia_provider/common/config.py:74-81);
  • every transaction has [ovn] ovsdb_connection_timeout s (default 180) (ovn_octavia_provider/helper.py:64, ovn_octavia_provider/common/config.py:66-69);
  • fetching the schema at start-up is retried with exponential back-off capped at [ovn] ovsdb_retry_max_interval (default 180 s) (ovn_octavia_provider/ovsdb/impl_idl_ovn.py:227-229, ovn_octavia_provider/common/utils.py:76-83).

The event lock is the OVSDB lock method: only the agent holding it handles events, and the others drop notifications while is_lock_contended (ovn_octavia_provider/ovsdb/ovsdb_monitor.py:68-76). Several driver agents can therefore run for high availability, but only one processes OVSDB events at a time. The lock does not cover the request queues, the periodic tasks or the API-side helpers.

1.2 Tables and columns written

TableColumns writtenWritten byRead by
Load_Balancername, protocol, external_ids (see 01 § 1.2), vips, selection_fields, options:affinity_timeout, health_check, ip_port_mappingsevery handlerevery handler, maintenance, Neutron (port-forwarding LBs share the table and are skipped through external_ids['neutron:device_owner'], ovn_octavia_provider/helper.py:921-924)
Load_Balancer_Health_Checkvip, options (interval, timeout, success_count, failure_count), external_ids (octavia:healthmonitor, octavia:pool_id, octavia:vip)hm_create, hm_sync, hm_update, hm_delete, handle_vip_fip, listener_createsame
Logical_Switchload_balancer (ls_lb_add / ls_lb_del)LB/member create and delete, LRP events—
Logical_Routerload_balancer (lr_lb_add / lr_lb_del)same—
NATexternal_mac, logical_port clearedhandle_member_dvr (ovn_octavia_provider/helper.py:3382-3385)handle_member_dvr (lookup by external_ids['neutron:fip_port_id'], ovn_octavia_provider/helper.py:3361-3364)
Logical_Switch_Portnone—member LSP lookup by address (ovn_octavia_provider/helper.py:3424-3442), VIP LSP FIP (ovn_octavia_provider/driver.py:868-872), maintenance (ovn_octavia_provider/maintenance.py:103-105)
Logical_Router_Port, Gateway_Chassis, HA_Chassis_Groupnone—finding the switches behind a router, skipping gateway ports (ovn_octavia_provider/helper.py:1245-1258)

The provider never writes Logical_Switch_Port or NAT rows it does not own except the two NAT columns above; Neutron's OVN mechanism driver owns them.

1.3 Transactions

Handlers accumulate ovsdbapp commands and commit them in one transaction (_execute_commands, ovn_octavia_provider/helper.py:956-960). Exceptions:

  • _get_or_create_ovn_lb sets the protocol of an empty row immediately (ovn_octavia_provider/helper.py:876-878);
  • neutron:member_status updates are separate one-command transactions (ovn_octavia_provider/helper.py:2661-2672);
  • ip_port_mappings changes are separate one-command transactions (ovn_octavia_provider/helper.py:3802-3808);
  • LB ↔ router associations fall back to one transaction per command after RowNotFound (ovn_octavia_provider/helper.py:1218-1235).

There is no optimistic locking: reads come from the local IDL replica and writes use db_set on map keys, so two writers updating the same external_ids key (two Octavia API processes, or API and agent) can lose an update [inferred]. ovsdb-server serialises transactions, and the vips column is rewritten from the writer's view of external_ids (db_clear + db_set, ovn_octavia_provider/helper.py:1444-1447).

1.4 Sample frame: listener creation

listener_create for listener L on port 80 with default pool P, on an LB row that has one member 10.0.0.10:8080, produces one transact (shape reconstructed from the ovsdbapp commands; the exact mutate/update encoding is chosen by the ovs IDL [inferred]):

{"id": 42, "method": "transact", "params": ["OVN_Northbound",
  {"op": "mutate", "table": "Load_Balancer", "where": [["_uuid", "==", ["uuid", "7d3e…"]]],
   "mutations": [["external_ids", "insert", ["map", [["listener_L", "80:pool_P"]]]]]},
  {"op": "update", "table": "Load_Balancer", "where": [["_uuid", "==", ["uuid", "7d3e…"]]],
   "row": {"protocol": ["set", ["tcp"]]}},
  {"op": "update", "table": "Load_Balancer", "where": [["_uuid", "==", ["uuid", "7d3e…"]]],
   "row": {"vips": ["map", []]}},
  {"op": "update", "table": "Load_Balancer", "where": [["_uuid", "==", ["uuid", "7d3e…"]]],
   "row": {"vips": ["map", [["172.24.4.5:80", "10.0.0.10:8080"]]]}}
]}

Resulting row (the contract a rewrite must produce):

{"name": "a5f1…lb-id", "protocol": ["tcp"],
 "external_ids": {"neutron:vip": "172.24.4.5", "neutron:vip_port_id": "9c0b…", "enabled": "True",
                  "listener_L": "80:pool_P", "pool_P": "member_M_10.0.0.10:8080_<subnet>",
                  "ls_refs": "{\"neutron-<net>\": 2}", "lr_ref": "neutron-<router>",
                  "neutron:member_status": "{\"M\": \"NO_MONITOR\"}"},
 "vips": {"172.24.4.5:80": "10.0.0.10:8080"},
 "selection_fields": ["ip_dst", "ip_src", "tp_dst", "tp_src"]}

1.5 Monitored events

See 05. NB: Logical_Router_Port create/delete, Logical_Switch_Port update. SB: Service_Monitor update/delete (ovn_octavia_provider/agent.py:41-56, ovn_octavia_provider/ovsdb/impl_idl_ovn.py:232-234).

2. Octavia driver-agent sockets

The driver agent is an Octavia process (octavia-driver-agent) that serves three Unix sockets and runs the provider agents (octavia:octavia/api/drivers/driver_agent/driver_listener.py:134-185). The provider uses two of them; it never sends statistics.

2.1 Framing

Each call opens a new AF_UNIX/SOCK_STREAM connection (send timeout 5 s), sends <decimal length>\n then the JSON payload, and reads the reply with the same framing, giving up after 30 s (octavia-lib:octavia_lib/api/drivers/driver_lib.py:28-108).

2.2 Status socket — update_loadbalancer_status

Sent after every handler that returns a status (ovn_octavia_provider/helper.py:425-427, ovn_octavia_provider/helper.py:448-451). The :D suffix of disabled keys is removed from all values first (ovn_octavia_provider/helper.py:100-107).

187\n{"loadbalancers": [{"id": "a5f1…", "provisioning_status": "ACTIVE"}],
      "listeners": [{"id": "L", "provisioning_status": "ACTIVE", "operating_status": "ONLINE"}],
      "pools": [{"id": "P", "provisioning_status": "ACTIVE"}]}

Reply: {"status_code": 200}, or {"status_code": 500, "fault_string": …, "status_object": …, "status_object_id": …} (octavia:octavia/api/drivers/driver_agent/driver_updater.py:180-188, octavia-lib:octavia_lib/common/constants.py:16-24). The agent processes members, health monitors, pools, L7 rules and policies, listeners, then load balancers, and deletes the database record of any non-LB object reported DELETED (octavia:octavia/api/drivers/driver_agent/driver_updater.py:142-179, octavia:octavia/api/drivers/driver_agent/driver_updater.py:99-113). Keys: loadbalancers, listeners, pools, members, healthmonitors; statuses: provisioning ACTIVE, ERROR, DELETED; operating ONLINE, OFFLINE, ERROR, DEGRADED, NO_MONITOR.

Retries: 15 attempts with exponential back-off up to 75 s (ovn_octavia_provider/helper.py:448-451).

2.3 Get socket — object reads

Request {"object": "loadbalancers" | "pools" | "listeners", "id": "<uuid>"}, reply the provider data model as a dict (with render_unsets), or null when not found (octavia-lib:octavia_lib/api/drivers/driver_lib.py:163-185, octavia:octavia/api/drivers/driver_agent/driver_get.py:23-50).

CallWhyWhere
get_pool, get_loadbalancerfind the VIP subnet of a member without subnet_id (synchronous, in the Octavia API process)ovn_octavia_provider/helper.py:939-954
get_loadbalancerVIP port and additional VIPs of an LB missing from OVN (delete) or to copy into a new protocol rowovn_octavia_provider/helper.py:2048-2064
get_loadbalancer, get_pool, get_listeneradmin_state_up of every pool and listener when recomputing operating statuses — one to three socket round-trips per status computationovn_octavia_provider/helper.py:4325-4376
get_loadbalancerfull graph for the syncovn_octavia_provider/driver.py:916-918

The driver-lib object is created in each OvnProviderHelper, and the constructor fails when the sockets do not exist (octavia-lib:octavia_lib/api/drivers/driver_lib.py:40-55): the Octavia API host must share /var/run/octavia with a running driver agent.

3. Neutron REST calls

Client: openstack.connection.Connection(session=…).network, created once per process (singleton, ovn_octavia_provider/common/clients.py:105-138). Region [neutron] region_name, interface [neutron] valid_interfaces[0], endpoint [neutron] endpoint_override (with insecure/cafile for HTTPS). Missing auth options in [neutron] are copied from [service_auth] (ovn_octavia_provider/common/clients.py:49-102); deprecated endpoint, endpoint_type, ca_certificates_file are mapped to their keystoneauth equivalents (ovn_octavia_provider/common/config.py:109-131).

Method + pathWhenRetryErrors handledDefined at
POST /v2.0/portsVIP and additional VIP ports (create_vip_port)none409 → reuse same-name port; other → clean-up + DriverErrorovn_octavia_provider/helper.py:3068-3104
POST /v2.0/portsHM source port ovn-lb-hm-<subnet> (port security off, device_owner ovn-lb-hm:distributed)noneany HttpException → clean-up, Noneovn_octavia_provider/helper.py:123-139
GET /v2.0/ports/{id}VIP port (create, delete, FIP events), user-provided VIP port, maintenancenone404 handled on deleteovn_octavia_provider/helper.py:1474, ovn_octavia_provider/helper.py:371, ovn_octavia_provider/helper.py:3124, ovn_octavia_provider/helper.py:1850, ovn_octavia_provider/maintenance.py:182
GET /v2.0/ports?network_id=find the VIP port by address in lb_create10 s—ovn_octavia_provider/helper.py:1480-1490
GET /v2.0/ports?name=ovn-lb-hm-<subnet>HM port clean-up10 s—ovn_octavia_provider/helper.py:169-171
find_port(name_or_id, network_id) (GET /v2.0/ports/{name} then GET /v2.0/ports?name=…) **[inferred: openstacksdk find_* behaviour]**HM port and reused VIP port10 s—ovn_octavia_provider/helper.py:195-198, ovn_octavia_provider/helper.py:3093-3096
GET /v2.0/ports?device_owner=network:distributedmaintenance (HM port owner migration)none—ovn_octavia_provider/maintenance.py:85-86
PUT /v2.0/ports/{id} (device_owner, device_id)maintenance tasksnone404 skipped (VIP task)ovn_octavia_provider/maintenance.py:93-95, ovn_octavia_provider/maintenance.py:219-222
DELETE /v2.0/ports/{id}VIP ports on LB delete or create failure, unused HM ports15 attempts, ≤ 75 s back-off404 loggedovn_octavia_provider/helper.py:3169-3176
GET /v2.0/subnets/{id}VIP subnet CIDR, member subnet → network, router lookupnone404 loggedovn_octavia_provider/helper.py:1478, ovn_octavia_provider/helper.py:795, ovn_octavia_provider/helper.py:947, ovn_octavia_provider/helper.py:1002, ovn_octavia_provider/helper.py:2784, ovn_octavia_provider/helper.py:3341, ovn_octavia_provider/helper.py:3427
GET /v2.0/networks/{id}sync: is the VIP on a provider networknone—ovn_octavia_provider/helper.py:594
GET /v2.0/floatingips?port_id=sync: FIP of the VIP portnoneHttpException loggedovn_octavia_provider/helper.py:3444-3451
GET + PUT /v2.0/floatingips/{id} (description unchanged)member deleted while LB and member have FIPs: forces Neutron to recompute the member's NAT (re-distribute it under DVR)none404 loggedovn_octavia_provider/helper.py:3398-3410

All calls use the Octavia service user. Calls made inside driver methods (VIP ports, subnet lookup for members) run synchronously in the Octavia API request; the others run in helper threads.

4. Octavia REST (sync tool only)

GET /v2/lbaas/loadbalancers?provider=ovn (paginated by openstacksdk), retried for 10 s on HttpException (ovn_octavia_provider/helper.py:485-486, ovn_octavia_provider/driver.py:911-913), with the [service_auth] credentials (ovn_octavia_provider/common/clients.py:152-172).

5. Other

  • Configuration files: the Neutron sync plugin reads Octavia's configuration from /etc/octavia/octavia.conf, ~/octavia.conf or ./octavia.conf (first found) and temporarily replaces cfg.CONF while calling the driver (ovn_octavia_provider/ovn/db_sync.py:89-152).
  • No subprocess, no privsep, no files written.

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