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

Reverse-spec · Services

neutron — Rewrite pack

Rewrite pack — neutron — branch stable/2026.2

Branch stable/2026.2 @ eef1a0f7ee36 — repository https://opendev.org/openstack/neutron — analysed on 2026-10-28 Clients read: nova stable/2026.2 @ 8df1922926 (cited as nova:), python-neutronclient @ 596a9d9 (cited as neutronclient:)

Target: a re-implementation of the Neutron server for OpenKeel with an OVN (or Kube-OVN) backend, drop-in for Nova, Octavia, Horizon/Skyline and the openstack CLI. Scope is the ML2/OVN reference profile of 01-rest-api_stable-2026.2.md (_rewrite/tools/neutron-ovn.conf).

1. Contents of _rewrite/

FileUse in a rewrite
[routes_stable-2026.2.json](_rewrite/routes_stable-2026.2.json)the route table, extracted from the running application: 280 implemented operations (486 Routes entries with the artefacts), layer (pecan / extension middleware), plugin method and file:line, policy rule, success status, and per collection the full attribute map (allow_post, allow_put, validate, default, convert_to, is_visible, is_filter, is_sort_key, required_by_policy…), plus the 110 loaded extensions (aliases that GET /v2.0/extensions must return) and the 15 plugins
[routes-unimplemented_stable-2026.2.json](_rewrite/routes-unimplemented_stable-2026.2.json)54 registered operations without implementation (500 in Neutron) and the 118 Routes artefacts (…/new, …/{id}/edit): do not reproduce, answer 404/405
[openapi_stable-2026.2.json](_rewrite/openapi_stable-2026.2.json)OpenAPI 3.0.3 built from the attribute maps (tools/neutron_openapi.py): 128 paths, create/update/view schemas per collection, x-layer, x-handler, x-policy, x-neutron-validator, x-neutron-convert-to; the error body
[request-schemas_stable-2026.2.json](_rewrite/request-schemas_stable-2026.2.json)the same schemas, by collection
[api-ref_stable-2026.2.json](_rewrite/api-ref_stable-2026.2.json)351 documented operations of the neutron-lib api-ref, with parameters and documented error codes
[api-ref-coverage_stable-2026.2.json](_rewrite/api-ref-coverage_stable-2026.2.json)documented vs served: 151 documented operations not served by this profile (VPNaaS, FWaaS, BGP, TaaS, SFC, BGPVPN, local IPs, metering, NDP proxy, conntrack helpers…), 22 served operations not documented
[schema-mysql-migrated_stable-2026.2.sql](_rewrite/schema-mysql-migrated_stable-2026.2.sql)the schema production databases have: neutron-db-manage upgrade heads on MariaDB 11.4 (alembic heads 5c85685d616d, c7f8d9e0f1a2)
[schema-mysql_stable-2026.2.sql](_rewrite/schema-mysql_stable-2026.2.sql), [schema-postgresql_stable-2026.2.sql](_rewrite/schema-postgresql_stable-2026.2.sql)DDL generated from the 117 models
[schema-diff_stable-2026.2.md](_rewrite/schema-diff_stable-2026.2.md)13 differences: 5 legacy bgp_* tables only in migrations, 8 index variations
[policies_stable-2026.2.json](_rewrite/policies_stable-2026.2.json), [policies_stable-2026.2.yaml](_rewrite/policies_stable-2026.2.yaml)375 default rules (oslo.policy), with scope_types and operations
[config_stable-2026.2.conf](_rewrite/config_stable-2026.2.conf), [config-ml2_conf_stable-2026.2.conf](_rewrite/config-ml2_conf_stable-2026.2.conf), [config-neutron_ovn_metadata_agent_stable-2026.2.conf](_rewrite/config-neutron_ovn_metadata_agent_stable-2026.2.conf), [config-ovn_agent_stable-2026.2.conf](_rewrite/config-ovn_agent_stable-2026.2.conf)oslo-config-generator samples: server, ML2 + [ovn] + [ovn_nb_global], metadata agent, OVN agent
[exchanges_stable-2026.2.json](_rewrite/exchanges_stable-2026.2.json)2 728 HTTP exchanges recorded from 985 unit tests of the ML2/OVN driver and the OVN L3 plugin (0 failures), with the test ID
[response-schemas_stable-2026.2.json](_rewrite/response-schemas_stable-2026.2.json), [.md](_rewrite/response-schemas_stable-2026.2.md)response schemas inferred from those exchanges, 17 operations (networks, subnets, ports, subnet pools, security groups)
[observed-errors_stable-2026.2.md](_rewrite/observed-errors_stable-2026.2.md)error (status, type, message) combinations seen in that run
[pipeline-probes_stable-2026.2.json](_rewrite/pipeline-probes_stable-2026.2.json)40 requests sent through the real paste pipeline with the layer that served each one: evidence for the routing rules of 01 § 1.2 and § 3.1
tools/neutron-ovn.conf (profile), neutron_routes.py, table01.py, neutron_openapi.py, errors02.py, schemadiff.py, pipeline_probe.py, reinfer.py, adapter_neutron.py (conformance_capture adapter)

Library versions of the extraction venv (Python 3.12, upper-constraints of requirements stable/2026.2 @ 3b75407b): neutron-lib 5.0.2.dev16 (local clone), ovsdbapp 2.19.0, ovs 3.7.1, oslo.policy 6.0.1, oslo.db 18.1.1, SQLAlchemy 2.0.52, pecan 1.8.0.

2. HTTP behaviour that the contracts do not show

2.1 Pipeline and layers

Order, outermost first (etc/neutron/api-paste.ini:7-10): CORS → X-Forwarded-* → request ID (x-openstack-request-id response header) → catch-all 500 → osprofiler → keystonemiddleware (401 on missing/invalid token, before anything else) → context (401 if the token has no user) → extension routing → pecan. Consequences for a rewrite:

  • a single router can replace both layers, but it must reproduce the union of what they accept: .json suffixes, dashed and underscored collection names (01 § 3.1), the unprefixed /v2.0/policies/{id}/packet_rate_limit_rules and /v2.0/policies/{id}/tags as well as the /qos/ forms;
  • serialisation differences between layers (compact or spaced JSON) are not a contract;
  • the 54 unimplemented routes and the Routes artefacts should become 404/405, not 500.

2.2 Error body

{"NeutronError": {"type": "<ExceptionClassName>", "message": "<text>", "detail": ""}} (02 § 1). **type is a hard contract**: python-neutronclient turns it into the exception class <type>Client (neutronclient:neutronclient/v2_0/client.py:58-86) and Nova catches those classes by name — InvalidIpForNetworkClient, IpAddressInUseClient, IpAddressAlreadyAllocatedClient, OverQuotaClient, IpAddressGenerationFailureClient, MacAddressInUseClient when creating ports (nova:nova/network/neutron.py:534-572), PortNotFoundClient, NetworkNotFoundClient elsewhere. The status code alone is not enough. The status for each type is in 02 § 5; observed-errors_stable-2026.2.md gives real messages.

2.3 Other cross-cutting rules

  • List filtering on any attribute with is_filter, repeated parameters OR-ed, fields, sort_key/sort_dir, limit/marker/page_reverse with <collection>_links (01 § 2.2); unknown filters → 400 (filter-validation).
  • Bulk create on every collection that allows it (allow_bulk), atomic for ML2 core resources.
  • If-Match: revision_number=N on PUT/DELETE → 412 on mismatch (01 § 2.3); every object returns revision_number, created_at, updated_at, tags, description.
  • Policy hiding: forbidden objects are 404 for GET/PUT/DELETE unless the caller may read them (02 § 2); forbidden items are silently dropped from lists; forbidden attributes are silently removed from responses.
  • GET /v2.0/extensions must list the aliases of routes_stable-2026.2.json (key extensions): Nova reads it every [neutron]/extension_sync_interval seconds (nova:nova/network/neutron.py:1408-1416) and switches features on binding-extended, port-resource-request-groups, dns-integration, ip-substring-filtering, fip-port-details, multi-provider, segment, security-groups-shared-filtering…

3. Core algorithms: formal specification

3.1 OVN translation (what to write in NB for each resource)

The specification is 03 § 1.2 (resource → NB rows and columns) with the exact naming: neutron-<uuid> for switches and routers, port UUID for LSPs, lrp-<port uuid> for LRPs, provnet-<segment uuid> for localnet ports, pg_<sg uuid with _> for port groups, neutron_pg_drop, pf-floatingip-<fip>-<proto>[-<port>] for port forwardings, and external_ids keys neutron:* (neutron/common/ovn/constants.py). These names are an interface: ovn-bgp-agent, ovn-octavia-provider and the metadata agent read them (see the sibling reverse-specs reverse-spec-ovn-bgp-agent, reverse-spec-ovn-octavia-provider). A rewrite that changes them breaks those components.

3.2 Consistency with OVN

Neutron writes OVN after the SQL commit and repairs with revision numbers (05 § 4.1). Contract to keep:

  1. every OVN row carries external_ids:neutron:revision_number = standardattributes.revision_number of the object written;
  2. a write never replaces an OVN row that has a higher revision (CheckRevisionNumberCommand);
  3. a periodic job (single leader) re-applies objects whose last-written revision differs from the DB revision for more than 60 s, and deletes OVN rows of deleted objects;
  4. creation failures roll back the Neutron object (01 § 4.1, 02 § 4). A Kubernetes-native rewrite may replace (3) by a controller reconcile loop; it must keep (1) because external OVN consumers rely on neutron:revision_number [inferred].

3.3 IP allocation

IPAM internal driver: requested IPs are checked against the subnet and allocation pools; free IPs are chosen from the pools (neutron/ipam/drivers/neutrondb_ipam/driver.py); IPv6 SLAAC/DHCPv6-stateless subnets compute the address from the MAC (EUI-64) and refuse explicit addresses (AllocationOnAutoAddressSubnet); a port on a network with segments gets addresses from the subnets of its host's segment (neutron/db/ipam_backend_mixin.py:704-758). Concurrency relies on unique constraints on ipamallocations and DB retries.

3.4 Router gateway scheduling

Pseudo-code and determinism in 05 § 7.1: candidates = gateway chassis with the physnet (and AZ), ≤ 5 chassis, priorities from 32 767 down, least-loaded per priority with anti-affinity for chassis already hosting the router; the final tie-break is not deterministic in Neutron itself (per-process hash()), so a rewrite needs only the same load distribution.

3.5 Port status and Nova events

State machine in 01 § 5; triggers in 05 § 3.1: ACTIVE only when OVN reports Logical_Switch_Port.up = true for a bound port, then network-vif-plugged (status completed) to Nova; DOWN and network-vif-unplugged on the reverse transition; network-vif-deleted on deletion; network-changed on fixed IP or floating IP changes (neutron/notifiers/nova.py:112-121, 209-269). Nova blocks the boot until network-vif-plugged arrives (default vif_plugging_timeout 300 s, fatal), so missing this event fails every boot.

3.6 Agent liveness

GET /agents content and alive computed from SB (05 § 3.2): bump NB_Global.nb_cfg at most every agent_down_time/2, alive when Chassis_Private.nb_cfg ≥ NB_Global.nb_cfg − 1 or updated less than agent_down_time ago. Nova does not use it; operators and Horizon/Skyline do.

3.7 Test data of these algorithms

No language-independent case file was produced for them: Neutron has no gabbi suite, its unit tests replace OVN by mocks (so they do not assert NB contents), and the functional tests that do (neutron/tests/functional/plugins/ml2/drivers/ovn/) need ovsdb-server binaries not available in the extraction environment. See § 7 and § 8.

4. What the clients expect

ExpectationWhere the client relies on it
error type names map to client exception classes (<type>Client)neutronclient:neutronclient/v2_0/client.py:58-86
port creation errors InvalidIpForNetwork (400), IpAddressInUse/IpAddressAlreadyAllocated (409), OverQuota (409), IpAddressGenerationFailure (409), MacAddressInUse (409)nova:nova/network/neutron.py:534-572
DELETE /ports/{id}/bindings/{host} answers 404 for a missing binding (Nova ignores it)nova:nova/network/neutron.py:404-416
PUT /ports/{id}/bindings/{host}/activate answers 409 when already active (Nova logs and continues)nova:nova/network/neutron.py:3224-3232
GET /v2.0/extensions lists the aliases above; Nova caches themnova:nova/network/neutron.py:1408-1416
binding:host_id, binding:profile (migrating_to, allocation), binding:vif_type, binding:vif_details, resource_request with request_groups and same_subtree; numa_affinity_policynova:nova/network/constants.py:20-38
network-vif-plugged / network-vif-unplugged / network-vif-deleted / network-changed external events, with tag = port ID and server_uuid = device_idneutron/notifiers/nova.py:112-121, 242-246
metadata: X-Instance-ID, X-Tenant-ID, X-Instance-ID-Signature = HMAC-SHA256 hex with metadata_proxy_shared_secret05 § 5.4, neutron/common/utils.py:1090-1094
ovn-bgp-agent / ovn-octavia-provider: OVN NB/SB naming and external_ids keys§ 3.1 and the sibling reverse-specs

5. Database strategy

  • Reuse the migrated schema (schema-mysql-migrated_stable-2026.2.sql) if the rewrite must take over an existing Neutron database; otherwise the model DDL is equivalent except for the legacy bgp_* tables and index variations (schema-diff_stable-2026.2.md).
  • Child rows reference their parent with ON DELETE CASCADE foreign keys (e.g. allocation pools and IP allocations, neutron/db/models_v2.py:84-102); ovn_revision_numbers.standard_attr_id is ON DELETE SET NULL on purpose (neutron/db/models/ovn.py:27) (that is how deleted-but-not-in-OVN objects are found, 05 § 4.1).
  • Optimistic locking on standardattributes.revision_number (SQLAlchemy version counter) with up to 20 retries; no SELECT … FOR UPDATE in the hot paths; subnet changes serialise per network through network_subnet_lock.
  • Galera: Neutron runs on multi-writer Galera thanks to the retries (deadlocks are retried) [inferred].
  • Start-up data: default segment ranges (network_segment_ranges with default = 1) are created from the [ml2_type_*] options when the network_segment_range plugin starts; default security groups are created lazily per project.

6. Authentication, policies, configuration and CLI

  • keystonemiddleware auth_token ([keystone_authtoken]), then neutron_lib.context.Context.from_environ; roles admin, member, reader, service (Nova uses the service role for binding attributes); [oslo_policy] enforce_scope / enforce_new_defaults (both on in the profile).
  • 375 rules with their operations in policies_stable-2026.2.json; attribute-level rules (create_port:binding:host_id…) must be applied per attribute present in the request, and get_<resource>:<attr> rules strip attributes from responses.
  • Configuration: options actually read by the ML2/OVN code paths are in config_stable-2026.2.conf ([DEFAULT], [quotas], [nova], [placement]) and config-ml2_conf_stable-2026.2.conf ([ml2], [ml2_type_geneve], [ml2_type_vlan], [ovn], [ovn_nb_global]); the metadata agent's in config-neutron_ovn_metadata_agent_stable-2026.2.conf.
  • CLI tools that deployment tooling calls: neutron-db-manage upgrade --expand/--contract, neutron-ovn-db-sync-util --ovn-neutron_sync_mode={log,repair,migrate}, neutron-status upgrade check.

7. Conformance strategy

  • Black-box suite: neutron-tempest-plugin (cloned at ../neutron-tempest-plugin) and Tempest tempest/api/network are the language-independent conformance tests for a rewrite; run them with the extensions of routes_stable-2026.2.json enabled. Not run here (they need a running cloud).
  • Differential testing: replay exchanges_stable-2026.2.json (unit-test exchanges, not replayable as-is: the tests build part of their data through the Python API) and pipeline-probes_stable-2026.2.json (self-contained, start from an empty database) against the rewrite; compare statuses, NeutronError.type and the response shapes of response-schemas_stable-2026.2.json.
  • Caveat: the unit tests call the pecan app directly (neutron/tests/common/test_db_base_plugin_v2.py:169), not the paste pipeline, so the recorded exchanges of extension resources went through pecan's extension controllers, not through the legacy controller that serves them in production (§ 2.1, 01 § 1.2). Statuses and bodies are the same for the recorded operations; the routing quirks are only covered by the probes.
  • OVN contract: the ML2/OVN functional tests (neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py) start real ovsdb-servers and assert NB/SB contents; run them in an environment with OVN binaries to record NB transactions per API call (not done here).

8. Gaps found while building the pack

  • No NB/SB contents were recorded (no OVN binaries in the extraction environment): the OVN mapping of 03 § 1.2 comes from reading the code.
  • response-schemas cover 17 operations only (the OVN unit tests exercise core resources); the other response shapes come from the attribute maps (openapi_stable-2026.2.json, x- flags) and the api-ref.
  • 22 served operations are absent from the api-ref (api-ref-coverage_stable-2026.2.json): QoS alias rules, packet-rate rules without /qos, quotas/project, quotas/tenant, ports/{id}/bindings/{host} GET/PUT…; and the api-ref uses underscored paths (network_segment_ranges, minimum_packet_rate_rules) for collections the server registers dashed (01 § 3.1).
  • Unimplemented routes answering 500 (01 § 3.1).
  • The ML2/OVN unit tests log several TypeError: 'Mock' object is not iterable tracebacks from background code while passing; harmless for the capture.
  • The OVN metadata agent is deprecated but still shipped; its replacement shares the code (05 § 5.1).

9. Non-functional requirements

  • OVSDB: one NB and one SB connection per worker in Neutron (memory = full NB replica per worker); a rewrite should keep one replica per process and must tolerate RAFT leader changes and reconnections (full resync of in-memory state on reconnect).
  • Port up → Nova event latency: event push + ≤ 2 s batching; Nova's default timeout is 300 s.
  • Metadata: one lookup per request scans the SB Port_Binding table on the node (05 § 5.4); keep an index by (network, IP) in a rewrite.
  • Maintenance: 300 s repair period, 60 s grace, one leader.

10. Rewrite checklist

  • [ ] Serve every operation of routes_stable-2026.2.json (minus routes-unimplemented) with the request schemas of request-schemas_stable-2026.2.json, both path spellings and .json suffixes.
  • [ ] Error body NeutronError with the exact type names and statuses of 02 § 5.
  • [ ] GET /v2.0/extensions with the 110 aliases; versions document at /.
  • [ ] Filters, fields, sorting, pagination links, bulk create, If-Match/412, tags API.
  • [ ] Policy engine with the 375 default rules, 404 hiding, attribute stripping.
  • [ ] Quotas with reservations (409 OverQuota), quotas/{id}/details, default.
  • [ ] ML2 binding with vif_type ovs/vhostuser, vif_details (bridge_name, datapath_type, port_filter, connectivity), binding-extended API for live migration (requested-chassis with two chassis, activation).
  • [ ] OVN NB objects named and tagged as in 03 § 1.2, with neutron:revision_number; revision-guarded writes; repair loop.
  • [ ] Port status from LSP up, provisioning blocks, Nova external events.
  • [ ] Router gateway scheduling and rescheduling on chassis events (05 § 7).
  • [ ] Agents API from SB Chassis/Chassis_Private, liveness via nb_cfg.
  • [ ] Metadata proxy per node with the HMAC signature (or Kube-OVN's equivalent), 169.254.169.254 and fe80::a9fe:a9fe.
  • [ ] AMQP notifications <resource>.<action>.start/.end if Designate/Ceilometer consumers are kept.
  • [ ] Schema: migrated DDL; neutron-db-manage equivalent for upgrades.

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