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

Reverse-spec · Services

neutron — Scenarios

End-to-end scenarios — neutron — branch stable/2026.2

Branch stable/2026.2 @ eef1a0f7ee36 — repository https://opendev.org/openstack/neutron — analysed on 2026-10-28

ML2/OVN reference profile. Participants: API = a neutron-server API worker (the one that receives the HTTP request, or the one the hash ring picks for an OVSDB event), MAINT = neutron-ovn-maintenance-worker, NB/SB = OVN databases, northd = ovn-northd, ctl = ovn-controller of a node, META = OVN metadata agent of that node. Statuses and messages are those of 02-errors_stable-2026.2.md; triggers are those of 05-async-processing_stable-2026.2.md.

1. Booting a VM on a project network

Nova creates (or receives) a port, binds it to the compute host, plugs the tap device and waits for network-vif-plugged before resuming the guest. Neutron's part: the logical switch port, the binding, the port status and the metadata namespace.

sequenceDiagram
  autonumber
  participant N as nova-compute
  participant API as neutron API worker
  participant DB as MariaDB
  participant NB as OVN NB
  participant SB as OVN SB
  participant C as ovn-controller (compute)
  participant M as OVN metadata agent (compute)
  N->>API: POST /v2.0/ports {network_id, device_id, device_owner: compute:nova}
  API->>DB: INSERT ports, ipallocations, standardattributes, ovn_revision_numbers
  API->>NB: transact insert Logical_Switch_Port (addresses, port_security, port groups, DHCP options)
  API->>DB: UPDATE ovn_revision_numbers (bump)
  API-->>N: 201 {status: DOWN, binding:vif_type: unbound}
  N->>API: PUT /v2.0/ports/{id} {binding:host_id: compute1}
  API->>DB: INSERT provisioningblocks (L2), ml2_port_bindings
  API->>API: bind_port: live OVN controller agent on compute1? physnet mapped?
  alt chassis found
    API->>DB: commit binding vif_type=ovs, vif_details {bridge_name: br-int, datapath_type}
    API->>NB: transact update LSP options:requested-chassis=compute1
    API-->>N: 200 {binding:vif_type: ovs}
  else no chassis / dead agent
    API-->>N: 200 {binding:vif_type: binding_failed}
    Note over N: Nova fails the build ("Binding failed for port")
  end
  N->>C: plug tap (OVS Interface external_ids:iface-id = port id)
  C->>SB: claim Port_Binding (chassis = compute1, up = true)
  SB->>NB: northd copies up=true to Logical_Switch_Port.up
  NB-->>API: update3 LSP up=true (LogicalSwitchPortUpdateUpEvent, hash ring)
  API->>DB: provisioning complete → UPDATE ports SET status=ACTIVE
  API->>N: POST /os-server-external-events network-vif-plugged (batched ≤ 2 s)
  SB-->>M: update3 Port_Binding chassis set to us (PortBindingUpdatedEvent)
  M->>M: create ovnmeta-<net> namespace, veth, 169.254.169.254, haproxy

Sources: create (neutron/plugins/ml2/plugin.py:1675, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:620-715), binding (neutron/plugins/ml2/plugin.py:651, neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1226-1405), status (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1453-1507), Nova event (neutron/notifiers/nova.py:209-246), metadata (neutron/agent/ovn/metadata/agent.py:754-866).

Diagnostics:

  • binding:vif_type = binding_failed: look for Refusing to bind port … no OVN chassis for host / … dead agent in the server log; check GET /v2.0/agents?host=compute1 (OVN Controller agent alive?) and the chassis ovn-bridge-mappings for flat/VLAN networks.
  • Port stays DOWN and Nova times out waiting for network-vif-plugged (default 300 s): check ovn-sbctl find Port_Binding logical_port=<id> (chassis, up); if OVN says up but Neutron says DOWN, the event was lost (hash ring empty, worker restart) — the maintenance task does not fix statuses, a port update or a restart of the API does [inferred].
  • VM gets no metadata: check the ovnmeta-<net> namespace on the node, the network's metadata port (device_owner=network:distributed) and the agent log (No port found in network …).

2. Router with an external gateway and a project subnet

sequenceDiagram
  autonumber
  actor U as Project user
  participant API as neutron API worker
  participant DB as MariaDB
  participant NB as OVN NB
  participant C as ovn-controller (gateway chassis)
  U->>API: POST /v2.0/routers {external_gateway_info: {network_id: public}}
  API->>DB: INSERT routers, gateway port (device_owner network:router_gateway), ovn_revision_numbers
  API->>NB: lr_add neutron-<router>; LRP lrp-<gw port>; scheduler picks ≤5 gateway chassis → HA_Chassis_Group; static route 0.0.0.0/0 via public gateway IP
  alt no candidate chassis
    Note over API: WARNING Gateway … was not scheduled on any chassis (router has no external connectivity)
  end
  API-->>U: 201 {status: ACTIVE}
  U->>API: PUT /v2.0/routers/{id}/add_router_interface {subnet_id}
  API->>DB: INSERT ports (network:router_interface, gateway IP), routerports
  API->>NB: LRP lrp-<port> (MAC, gateway CIDR); LSP type=router; NAT snat <subnet cidr> → <gateway IP>
  API-->>U: 200 {port_id, subnet_id, …}
  C->>C: highest-priority live chassis claims the chassisredirect port
  C-->>API: SB Port_Binding chassis set (PortBindingChassisEvent)
  API->>DB: UPDATE gateway port binding:host_id = gateway host

Sources: neutron/db/l3_db.py:312, neutron/services/ovn_l3/service_providers/ovn.py:67-132, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:1647-1677, 1465-1511, 1996-2056, scheduling 05 § 7.

Diagnostics: no external connectivity → ovn-nbctl lrp-get-gateway-chassis lrp-<gw port> / ha-chassis-group-list; empty means no chassis has enable-chassis-as-gw and the external physnet. 400 on add_router_interface: subnet without gateway IP, overlapping CIDR, subnet of another project (02 § 5).

3. Associating a floating IP (distributed)

sequenceDiagram
  autonumber
  actor U as Project user
  participant API as neutron API worker
  participant DB as MariaDB
  participant NB as OVN NB
  participant SB as OVN SB
  U->>API: POST /v2.0/floatingips {floating_network_id, port_id}
  API->>DB: INSERT port (network:floatingip), floatingips (router_id, fixed_ip), status DOWN
  alt no router between the port's subnet and the floating network
    API-->>U: 404 ExternalGatewayForFloatingIPNotFound
  end
  API->>NB: NAT dnat_and_snat (external_ip, logical_ip, logical_port, external_mac if LSP up)
  API->>DB: UPDATE floatingips SET status=ACTIVE; bump revision
  API-->>U: 201
  NB-->>API: NAT created (FIPAddDeleteEvent)
  API->>SB: ovsdb-client transact: delete MAC_Binding where ip = floating IP
  Note over API,SB: when the VM port comes up later, set_port_status_up sets NAT external_mac

Sources: neutron/db/l3_db.py:1510, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:1010-1112, 1280-1301, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py:835-870, neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1416-1442.

Diagnostics: FIP ACTIVE but unreachable → check the NAT row (ovn-nbctl lr-nat-list neutron-<router>), whether external_mac is set (distributed mode needs the port up and a physnet on the compute node), and stale ARP entries upstream.

4. Adding a security group rule

sequenceDiagram
  autonumber
  actor U as Project user
  participant API as neutron API worker
  participant DB as MariaDB
  participant NB as OVN NB
  participant C as ovn-controller (all nodes with ports in the group)
  U->>API: POST /v2.0/security-group-rules {security_group_id, direction, protocol, port_range…}
  API->>DB: INSERT securitygrouprules (validation: duplicates → 409, invalid ranges → 400)
  API->>NB: ACL on Port_Group pg_<sg> (priority 1002, allow-related, match "outport == @pg_… && ip4 && tcp && tcp.dst == 22")
  API-->>U: 201
  NB->>C: northd → logical flows → OpenFlow on every node hosting a member port

Sources: neutron/db/securitygroups_db.py:1014, neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:537-566, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:3078-3095.

Diagnostics: rule present in Neutron but not in OVN → ovn-nbctl acl-list pg_<sg>; the maintenance task re-creates missing ACLs within 5 minutes (check_for_inconsistencies).

5. Live migration of a VM

sequenceDiagram
  autonumber
  participant N as Nova (conductor/compute)
  participant API as neutron API worker
  participant NB as OVN NB
  participant SB as OVN SB
  participant D as ovn-controller (destination)
  participant M as metadata agent (destination)
  N->>API: POST /v2.0/ports/{id}/bindings {host: dest}
  API-->>N: 201 binding INACTIVE (vif_details for dest)
  N->>API: PUT /v2.0/ports/{id} {binding:profile: {migrating_to: dest}}
  API->>NB: LSP options:requested-chassis = "src,dest"
  SB-->>M: Port_Binding additional_chassis += dest (provision namespace early)
  D->>SB: claim as additional chassis when the tap appears
  N->>API: PUT /v2.0/ports/{id}/bindings/dest/activate
  API->>NB: requested-chassis = dest
  SB-->>API: Port_Binding chassis changed while up (PortBindingChassisUpdateEvent) → bump nb_cfg, wait for dest Chassis_Private.nb_cfg
  API->>N: network-vif-plugged / network-changed events

Sources: neutron/plugins/ml2/plugin.py:2800, 2917, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:552, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py:226-385, neutron/agent/ovn/metadata/agent.py:197-244.

Diagnostics: PortBindingAlreadyExists (409) when Nova retries a migration with a stale inactive binding: delete it with DELETE /ports/{id}/bindings/{host}; RevisionConflict retries during migration are logged at DEBUG and retried once (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:969-996).

6. OVN unavailable during an API call, then repair

sequenceDiagram
  autonumber
  actor U as User
  participant API as neutron API worker
  participant DB as MariaDB
  participant NB as OVN NB
  participant MAINT as maintenance worker
  U->>API: PUT /v2.0/networks/{id} {name: new}
  API->>DB: UPDATE networks, standardattributes.revision_number = 5 (commit)
  API-xNB: transact update Logical_Switch (timeout 180 s / connection refused)
  API-->>U: 500 (update stored, OVN not updated)
  loop every 300 s, holding the OVSDB lock
    MAINT->>DB: SELECT ovn_revision_numbers ⋈ standardattributes WHERE revisions differ AND older than 60 s
    MAINT->>NB: update Logical_Switch with revision 5
    MAINT->>DB: UPDATE ovn_revision_numbers SET revision_number = 5
  end

For a create, the same failure makes ML2 delete the new object and return the error; for a delete, the API answers 204 and the maintenance task removes the OVN row later (02 § 4).

Sources: neutron/plugins/ml2/plugin.py:1366-1378, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py:488-549, neutron/db/ovn_revision_numbers_db.py:224-263.

Diagnostics: neutron-ovn-db-sync-util --ovn-neutron_sync_mode=log lists the differences; the maintenance log shows Maintenance task: Failed to fix resource … for a resource that keeps failing.

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