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

Reverse-spec · Services

neutron — REST API

REST API — neutron — branch stable/2026.2

Branch stable/2026.2 @ eef1a0f7ee36 — repository https://opendev.org/openstack/neutron — analysed on 2026-10-28 Library: neutron-lib stable/2026.2 @ 1ee72052 (cited as neutron-lib:)

Scope: the Networking API v2.0 as served by neutron-server with the ML2/OVN reference profile of this specification: core plugin ml2, mechanism driver ovn, service plugins ovn-router, trunk, qos, segments, port_forwarding, log, network_segment_range, placement plus Neutron's default service plugins (auto_allocate, tag, timestamp, network_ip_availability, flavors, revisions, neutron/plugins/common/constants.py:30). The configuration is _rewrite/tools/neutron-ovn.conf. The route table below was extracted from the running application built with that profile (_rewrite/tools/neutron_routes.py), not from the documentation: Neutron has no route table in its code. Another set of plugins gives another API; the extensions that this profile does not load (BGP, EVPN, local IP, NDP proxy, conntrack helpers, metering, VPN, FWaaS…) are out of scope.

Error statuses are only summarised here; the catalogue with the situations is in 02-errors_stable-2026.2.md. What happens in OVN after each call is in 05-async-processing_stable-2026.2.md and 04-scenarios_stable-2026.2.md.

1. How a request reaches the code

1.1 Paste pipeline

neutron-server (or the WSGI script neutron/wsgi/api.py) loads etc/neutron/api-paste.ini:

PathPipeline (outermost first), auth_strategy = keystoneDefined at
/cors → http_proxy_to_wsgi → neutronversions (pecan RootController)etc/neutron/api-paste.ini:12
/healthcheckoslo.middleware Healthcheck, backend disable_by_file (/var/lib/neutron/healthcheck_disable)etc/neutron/api-paste.ini:55
/v2.0cors → http_proxy_to_wsgi → request_id → catch_errors → osprofiler → authtoken (keystonemiddleware) → keystonecontext → extensions → neutronapiapp_v2_0 (pecan)etc/neutron/api-paste.ini:7-10

The pipeline is chosen by [DEFAULT]/auth_strategy (neutron/auth.py:57). With noauth, authtoken and keystonecontext are replaced by fake_project_id, which gives the request an admin context with project fake_project_id (neutron/auth.py:42-54).

  • keystonecontext builds the neutron_lib.context.Context from the X-* headers set by keystonemiddleware and answers 401 when there is no user ID (neutron/auth.py:29-39).
  • catch_errors turns any exception that escapes the pipeline into a 500 with a plain body.

1.2 Two routing layers

Point of attention for a rewrite. Two different web stacks serve the API, and they do not behave identically:

  1. Extension middleware (extensions filter, neutron/api/extensions.py:138). At start-up it builds a routes.Mapper from every ResourceExtension returned by the loaded extensions (neutron/api/extensions.py:147-184): one mapper.resource() per collection (index, create, show, update, delete), plus the member actions (PUT /routers/{id}/add_router_interface…), collection actions (GET /quotas/project…) and collection methods (PUT /networks/{id}/tags…). A matched request is handed to neutron.api.v2.resource.Resource (neutron/api/v2/resource.py:34), which calls the legacy controller neutron.api.v2.base.Controller (neutron/api/v2/base.py:46) or a custom controller (quotas, tags, agent schedulers). Every extension resource is served here: routers, floating IPs, security groups, QoS, trunks, segments, RBAC, quotas, agents…
  2. pecan (neutron/pecan_wsgi/app.py:27). It only receives what the mapper did not match (neutron/api/extensions.py:263-274): the core collections networks, subnets, ports, subnetpools (neutron/pecan_wsgi/startup.py:32-58), GET /v2.0/ and GET /v2.0/extensions[/{alias}]. Pecan also builds controllers for the extension resources (neutron/pecan_wsgi/startup.py:61-120), but they are unreachable while the extensions filter is in the pipeline.

This was checked by instrumenting both controllers in the reference profile (_rewrite/tools/pipeline_probe.py, results in _rewrite/pipeline-probes_stable-2026.2.json): GET /v2.0/networks reaches pecan's CollectionsController.get, while GET /v2.0/routers, /qos/policies, /floatingips, /security-groups, /segments, /trunks, /address-scopes reach base.Controller.index.

Visible differences between the two layers:

Behaviourpecan (networks, subnets, ports, subnetpools)extension middleware (all other resources)
Request processinghooks in priority order (neutron/pecan_wsgi/app.py:31-40): user filter 90, context 95, exception translation 100, body validation 120, quota 130, notifier 135, query parameters 139, policy 140base.Controller methods (neutron/api/v2/base.py:373-709)
Invalid JSON body400 HTTPBadRequest "Body contains invalid data" (neutron/pecan_wsgi/hooks/body_validation.py:45-51)400 MalformedRequestBody "Malformed request body: Cannot understand JSON." (neutron/api/wsgi.py)
JSON serialisationcompact ({"networks":[]})with spaces ({"routers": []})
Unknown method on a collection (HEAD, PATCH…)405 (neutron/pecan_wsgi/controllers/resource.py:147-152)405 from Routes
GET /v2.0/<collection>/new, GET …/{id}/edit404 (no such ID)500 for /new (Routes generates new/edit actions, the controller has no such method)
Policy failure on GET of one item404 "The resource could not be found." (neutron/pecan_wsgi/hooks/policy_enforcement.py:195-210)same text (neutron/api/v2/base.py:396-400)
.json suffix (/v2.0/routers.json)accepted (guess_content_type_from_ext, neutron/pecan_wsgi/app.py:45)accepted (neutron/api/extensions.py:166)

1.3 Handler naming convention

Neither layer has explicit handlers: the controller derives the plugin method from the resource name (neutron/api/v2/base.py:125-137, neutron/pecan_wsgi/controllers/utils.py:163-173):

OperationPlugin methodPolicy rule checked
GET /<collection>get_<collection> (sub-resource: get_<parent>_<collection>)get_<resource> on each item (items that fail are silently dropped)
POST /<collection>create_<resource>, or create_<resource>_bulk for a bulk body when the plugin supports native bulkcreate_<resource> per item
GET /<collection>/{id}get_<resource>get_<resource>; failure → 404
PUT /<collection>/{id}update_<resource>update_<resource>; failure → 404 if the caller cannot even get it, else 403
DELETE /<collection>/{id}delete_<resource>delete_<resource>; same 404/403 rule
PUT /<collection>/{id}/<action><action> (member action)<action> after a get_<resource> check (neutron/api/v2/base.py:224-271)

Attribute-level rules (create_port:binding:host_id, get_network:provider:segmentation_id…) are checked for each attribute present in the body or the response. In responses, attributes the caller may not see are removed rather than refused (neutron/api/v2/base.py:159-193, neutron/pecan_wsgi/hooks/policy_enforcement.py:223-261). The 375 default rules are in _rewrite/policies_stable-2026.2.yaml.

2. Cross-cutting request rules

2.1 Request body

  • The body must be {"<resource>": {...}}, or {"<collection>": [...]} for a bulk create (neutron/api/v2/base.py:712-775). Errors: 400 "Resource body required", "Bulk operation not supported", "Resources required", "Unable to find '<resource>' in request body", "Object '<resource>' contains invalid data".
  • Each attribute is checked against the resource's RESOURCE_ATTRIBUTE_MAP entry (neutron-lib:neutron_lib/api/attributes.py): unknown attribute → 400 "Unrecognized attribute(s)"; allow_post: False on create or allow_put: False on update → 400 "Attribute '…' not allowed in POST" / "Cannot update read-only attribute …" (neutron/api/v2/base.py:769-772); missing attribute without default → 400; failed validate (type:uuid, type:subnet, type:ip_address…) → 400 with the validator's message; convert_to failure → 400.
  • project_id/tenant_id are filled from the context on create (AttributeInfo.populate_project_id, neutron/api/v2/base.py:761). A non-admin may only set them to its own project; in noauth mode a create without project_id is refused with 400 "Running without keystone AuthN requires that project_id is specified".
  • DELETE with a body → 400 "Request body is not supported in DELETE." (neutron/api/v2/base.py:568-570, neutron/pecan_wsgi/controllers/resource.py:78-80).

2.2 Listing: filters, fields, sorting, pagination

  • Filters: any query parameter that names an attribute filters on equality; repeated parameters are OR-ed (neutron/api/api_common.py:70-115). With the filter-validation extension (enabled, [DEFAULT]/filter_validation = True), an unknown filter → 400 (neutron/api/api_common.py:113). ip-substring-filtering allows fixed_ips=ip_address_substr=… on ports. empty-string-filtering allows name=.
  • Fields: fields=a&fields=b restricts the response; the attributes needed by the policy engine are fetched anyway and stripped afterwards (neutron/api/v2/base.py:215-222).
  • Sorting: sort_key/sort_dir (asc|desc), several allowed; only attributes with is_sort_key: True (neutron/api/api_common.py:223-250), otherwise 400.
  • Pagination: limit, marker, page_reverse. limit is capped by [DEFAULT]/pagination_max_limit (default -1, no cap) (neutron/api/api_common.py:171-215). The response then carries <collection>_links with next/previous hrefs (neutron/api/v2/base.py:343-345). ML2 and most service plugins do native SQL pagination; otherwise it is emulated in memory.
  • A GET on a collection also resynchronises the caller's quota usage counters for that resource (neutron/api/v2/base.py:347-348).

2.3 Optimistic concurrency: If-Match

With the revision-if-match extension, PUT/DELETE accept If-Match: revision_number=<n> (neutron/api/api_common.py:47-62). The constraint is stored in the context and checked by the revision plugin in the before_flush phase of the transaction that bumps standardattributes.revision_number (neutron/services/revisions/revision_plugin.py:301); a mismatch raises RevisionNumberConstraintFailed, a webob.exc.HTTPPreconditionFailed → 412 "Constrained to <n>, but current revision is <m>" (neutron/services/revisions/revision_plugin.py:324). Every resource with standard attributes carries revision_number, created_at, updated_at, description, tags.

2.4 Quotas

Before a create, a reservation is made per project for the number of items (neutron/api/v2/base.py:488-500, neutron/pecan_wsgi/hooks/quota_enforcement.py:36-77); it is committed after success and cancelled on failure. Exceeding the limit raises OverQuota → 409 "Quota exceeded for resources: […]". Resources with quotas in this profile: network, subnet, subnetpool, port, router, floatingip, router_route, rbac_policy, security_group, security_group_rule, trunk (GET /v2.0/quotas/{project}/default in the reference run). The driver is [quotas]/quota_driver (default DbQuotaNoLockDriver).

2.5 Notifications and callbacks emitted by every write

  • AMQP notifications <resource>.create.start/.end, .update.start/.end, .delete.start/.end on the network publisher (neutron/api/v2/base.py:439-441, 511-514, 571-573, 605-611, 627-629, 701-702; pecan: neutron/pecan_wsgi/hooks/notifier.py:37-112). Payload: the request body (.start) or the API view of the result (.end); deletes add <resource>_id. See 03.
  • In-process callback BEFORE_RESPONSE on the resource with an APIEventPayload (neutron/api/v2/base.py:515-520). Member actions (add_router_interface…) emit no API notification.
  • All handlers are wrapped in retry_db_errors (neutron/api/v2/base.py:373, neutron/pecan_wsgi/controllers/utils.py:73): deadlocks, StaleDataError and RetryRequest are retried (oslo.db wrap_db_retry, 20 attempts with back-off, neutron-lib:neutron_lib/db/api.py).

2.6 Success statuses

POST → 201 with the created object; DELETE → 204 without body; others → 200. Exceptions: the collection methods of tags (PUT /…/tags 200, DELETE /…/tags 204), PUT /…/tags/{tag} 201, quotas DELETE 204.

2.7 Error body

{"NeutronError": {"type": "NetworkNotFound", "message": "Network 3c7… could not be found.", "detail": ""}}

type is the exception class name (neutron/api/api_common.py:520-532). The class → status mapping is neutron-lib:neutron_lib/api/faults.py:19-29; anything else is a 500 with a generic message; NotImplementedError → 501 with key NotImplementedError instead of NeutronError (neutron/api/api_common.py:500-509). Details in 02.

3. Summary of operations

Generated from _rewrite/routes_stable-2026.2.json (_rewrite/tools/table01.py). "Handler" is the plugin method that does the work, with the def line of its implementation in the class actually loaded (ML2 for core resources, OVNL3RouterPlugin for L3…). "Layer": pecan or ext (extension middleware), see § 1.2. Rows …/tags are repeated for every taggable resource (networks, subnets, ports, subnetpools, routers, floatingips, security-groups, trunks, policies, network-segment-ranges). The error statuses of each family are given in § 4 and catalogued in 02.

MethodPathHandler (plugin method)Policy ruleSuccessLayer
GET/neutron/pecan_wsgi/controllers/root.py:60—200pecan
GET/v2.0/neutron/pecan_wsgi/controllers/root.py:98—200pecan
GET/v2.0/address-groupsneutron/db/address_group_db.py:169 get_address_groupsget_address_group200ext
POST/v2.0/address-groupsneutron/db/address_group_db.py:129 create_address_groupcreate_address_group201ext
GET/v2.0/address-groups/{id}neutron/db/address_group_db.py:165 get_address_groupget_address_group200ext
PUT/v2.0/address-groups/{id}neutron/db/address_group_db.py:151 update_address_groupupdate_address_group200ext
DELETE/v2.0/address-groups/{id}neutron/db/address_group_db.py:181 delete_address_groupdelete_address_group204ext
PUT/v2.0/address-groups/{id}/add_addressesneutron/db/address_group_db.py:84 add_addressesadd_addresses200ext
PUT/v2.0/address-groups/{id}/remove_addressesneutron/db/address_group_db.py:108 remove_addressesremove_addresses200ext
GET/v2.0/address-scopesneutron/db/address_scope_db.py:107 get_address_scopesget_address_scope200ext
POST/v2.0/address-scopesneutron/db/address_scope_db.py:70 create_address_scopecreate_address_scope201ext
GET/v2.0/address-scopes/{id}neutron/db/address_scope_db.py:103 get_address_scopeget_address_scope200ext
PUT/v2.0/address-scopes/{id}neutron/db/address_scope_db.py:91 update_address_scopeupdate_address_scope200ext
DELETE/v2.0/address-scopes/{id}neutron/db/address_scope_db.py:122 delete_address_scopedelete_address_scope204ext
GET/v2.0/agentsneutron/db/agents_db.py:294 get_agentsget_agent200ext
POST/v2.0/agentsneutron/extensions/agent.py:58 create_agentcreate_agent201ext
GET/v2.0/agents/{agent_id}/dhcp-networksneutron/extensions/dhcpagentscheduler.py:37 index—200ext
POST/v2.0/agents/{agent_id}/dhcp-networksneutron/extensions/dhcpagentscheduler.py:45 create—201ext
DELETE/v2.0/agents/{agent_id}/dhcp-networks/{id}neutron/extensions/dhcpagentscheduler.py:57 delete—204ext
GET/v2.0/agents/{agent_id}/l3-routersneutron/extensions/l3agentscheduler.py:54 index—200ext
POST/v2.0/agents/{agent_id}/l3-routersneutron/extensions/l3agentscheduler.py:62 create—201ext
PUT/v2.0/agents/{agent_id}/l3-routers/{id}neutron/extensions/l3agentscheduler.py:76 update—200ext
DELETE/v2.0/agents/{agent_id}/l3-routers/{id}neutron/extensions/l3agentscheduler.py:89 delete—204ext
GET/v2.0/agents/{id}neutron/db/agents_db.py:342 get_agentget_agent200ext
PUT/v2.0/agents/{id}neutron/db/agentschedulers_db.py:82 update_agentupdate_agent200ext
DELETE/v2.0/agents/{id}neutron/db/agents_db.py:269 delete_agentdelete_agent204ext
GET/v2.0/auto-allocated-topology/{id}neutron/services/auto_allocate/db.py:132 get_auto_allocated_topologyget_auto_allocated_topology200ext
DELETE/v2.0/auto-allocated-topology/{id}neutron/services/auto_allocate/db.py:162 delete_auto_allocated_topologydelete_auto_allocated_topology204ext
GET/v2.0/availability_zonesneutron/db/agents_db.py:139 get_availability_zonesget_availability_zone200ext
GET/v2.0/default-security-group-rulesneutron/db/securitygroups_db.py:693 get_default_security_group_rulesget_default_security_group_rule200ext
POST/v2.0/default-security-group-rulesneutron/db/securitygroups_db.py:595 create_default_security_group_rulecreate_default_security_group_rule201ext
GET/v2.0/default-security-group-rules/{id}neutron/db/securitygroups_db.py:715 get_default_security_group_ruleget_default_security_group_rule200ext
DELETE/v2.0/default-security-group-rules/{id}neutron/db/securitygroups_db.py:651 delete_default_security_group_ruledelete_default_security_group_rule204ext
GET/v2.0/extensionsneutron/pecan_wsgi/controllers/extensions.py—200pecan
GET/v2.0/extensions/{alias}neutron/pecan_wsgi/controllers/extensions.py—200pecan
GET/v2.0/flavorsneutron/db/flavors_db.py:125 get_flavorsget_flavor200ext
POST/v2.0/flavorsneutron/db/flavors_db.py:93 create_flavorcreate_flavor201ext
GET/v2.0/flavors/{flavor_id}/next_providers/{id}neutron/db/flavors_db.py:217 get_flavor_next_providerget_flavor_next_provider200ext
POST/v2.0/flavors/{flavor_id}/service_profilesneutron/db/flavors_db.py:134 create_flavor_service_profilecreate_flavor_service_profile201ext
GET/v2.0/flavors/{flavor_id}/service_profiles/{id}neutron/db/flavors_db.py:157 get_flavor_service_profileget_flavor_service_profile200ext
DELETE/v2.0/flavors/{flavor_id}/service_profiles/{id}neutron/db/flavors_db.py:148 delete_flavor_service_profiledelete_flavor_service_profile204ext
GET/v2.0/flavors/{id}neutron/db/flavors_db.py:109 get_flavorget_flavor200ext
PUT/v2.0/flavors/{id}neutron/db/flavors_db.py:101 update_flavorupdate_flavor200ext
DELETE/v2.0/flavors/{id}neutron/db/flavors_db.py:113 delete_flavordelete_flavor204ext
GET/v2.0/floatingip-poolsneutron/db/l3_fip_pools_db.py:37 get_floatingip_poolsget_floatingip_pool200ext
GET/v2.0/floatingipsneutron/db/l3_db.py:1783 get_floatingipsget_floatingip200ext
POST/v2.0/floatingipsneutron/services/ovn_l3/plugin.py:239 create_floatingipcreate_floatingip201ext
GET/v2.0/floatingips/{floatingip_id}/port_forwardingsneutron/services/portforwarding/pf_plugin.py:686 get_floatingip_port_forwardingsget_floatingip_port_forwarding200ext
POST/v2.0/floatingips/{floatingip_id}/port_forwardingsneutron/services/portforwarding/pf_plugin.py:352 create_floatingip_port_forwardingcreate_floatingip_port_forwarding201ext
GET/v2.0/floatingips/{floatingip_id}/port_forwardings/{id}neutron/services/portforwarding/pf_plugin.py:668 get_floatingip_port_forwardingget_floatingip_port_forwarding200ext
PUT/v2.0/floatingips/{floatingip_id}/port_forwardings/{id}neutron/services/portforwarding/pf_plugin.py:406 update_floatingip_port_forwardingupdate_floatingip_port_forwarding200ext
DELETE/v2.0/floatingips/{floatingip_id}/port_forwardings/{id}neutron/services/portforwarding/pf_plugin.py:697 delete_floatingip_port_forwardingdelete_floatingip_port_forwarding204ext
GET/v2.0/floatingips/{floatingip_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/floatingips/{floatingip_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/floatingips/{floatingip_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/floatingips/{floatingip_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/floatingips/{floatingip_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/floatingips/{floatingip_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/floatingips/{floatingip_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/floatingips/{id}neutron/db/l3_db.py:1778 get_floatingipget_floatingip200ext
PUT/v2.0/floatingips/{id}neutron/db/l3_db.py:1729 update_floatingipupdate_floatingip200ext
DELETE/v2.0/floatingips/{id}neutron/db/l3_db.py:1774 delete_floatingipdelete_floatingip204ext
GET/v2.0/log/loggable-resourcesneutron/services/logapi/logging_plugin.py:141 get_loggable_resourcesget_loggable_resource200ext
GET/v2.0/log/logsneutron/services/logapi/logging_plugin.py:75 get_logsget_log200ext
POST/v2.0/log/logsneutron/services/logapi/logging_plugin.py:95 create_logcreate_log201ext
GET/v2.0/log/logs/{id}neutron/services/logapi/logging_plugin.py:91 get_logget_log200ext
PUT/v2.0/log/logs/{id}neutron/services/logapi/logging_plugin.py:115 update_logupdate_log200ext
DELETE/v2.0/log/logs/{id}neutron/services/logapi/logging_plugin.py:131 delete_logdelete_log204ext
GET/v2.0/network-ip-availabilitiesneutron/services/network_ip_availability/plugin.py:50 get_network_ip_availabilitiesget_network_ip_availability200ext
GET/v2.0/network-ip-availabilities/{id}neutron/services/network_ip_availability/plugin.py:58 get_network_ip_availabilityget_network_ip_availability200ext
GET/v2.0/network-segment-rangesneutron/services/network_segment_range/plugin.py:193 get_network_segment_rangesget_network_segment_range200ext
POST/v2.0/network-segment-rangesneutron/services/network_segment_range/plugin.py:156 create_network_segment_rangecreate_network_segment_range201ext
GET/v2.0/network-segment-ranges/{id}neutron/services/network_segment_range/plugin.py:187 get_network_segment_rangeget_network_segment_range200ext
PUT/v2.0/network-segment-ranges/{id}neutron/services/network_segment_range/plugin.py:217 update_network_segment_rangeupdate_network_segment_range200ext
DELETE/v2.0/network-segment-ranges/{id}neutron/services/network_segment_range/plugin.py:248 delete_network_segment_rangedelete_network_segment_range204ext
GET/v2.0/network_segment_ranges/{network_segment_range_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/network_segment_ranges/{network_segment_range_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/network_segment_ranges/{network_segment_range_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/network_segment_ranges/{network_segment_range_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/network_segment_ranges/{network_segment_range_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/network_segment_ranges/{network_segment_range_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/network_segment_ranges/{network_segment_range_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/networksneutron/plugins/ml2/plugin.py:1393 get_networksget_network200pecan
POST/v2.0/networksneutron/plugins/ml2/plugin.py:1296 create_networkcreate_network201pecan
GET/v2.0/networks/{network_id}/dhcp-agentsneutron/extensions/dhcpagentscheduler.py:70 index—200ext
GET/v2.0/networks/{network_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/networks/{network_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/networks/{network_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/networks/{network_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/networks/{network_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/networks/{network_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/networks/{network_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/networks/{id}neutron/plugins/ml2/plugin.py:1384 get_networkget_network200pecan
PUT/v2.0/networks/{id}neutron/plugins/ml2/plugin.py:1325 update_networkupdate_network200pecan
DELETE/v2.0/networks/{id}neutron/plugins/ml2/plugin.py:1424 delete_networkdelete_network204pecan
GET/v2.0/policies/{policy_id}/packet_rate_limit_rulesneutron/services/qos/qos_plugin.py:1065 get_policy_packet_rate_limit_rules -> get_policy_rulesget_policy_packet_rate_limit_rule200ext
POST/v2.0/policies/{policy_id}/packet_rate_limit_rulesneutron/services/qos/qos_plugin.py:862 create_policy_packet_rate_limit_rule -> create_policy_rulecreate_policy_packet_rate_limit_rule201ext
GET/v2.0/policies/{policy_id}/packet_rate_limit_rules/{id}neutron/services/qos/qos_plugin.py:1024 get_policy_packet_rate_limit_rule -> get_policy_ruleget_policy_packet_rate_limit_rule200ext
PUT/v2.0/policies/{policy_id}/packet_rate_limit_rules/{id}neutron/services/qos/qos_plugin.py:901 update_policy_packet_rate_limit_rule -> update_policy_ruleupdate_policy_packet_rate_limit_rule200ext
DELETE/v2.0/policies/{policy_id}/packet_rate_limit_rules/{id}neutron/services/qos/qos_plugin.py:975 delete_policy_packet_rate_limit_rule -> delete_policy_ruledelete_policy_packet_rate_limit_rule204ext
GET/v2.0/policies/{policy_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/policies/{policy_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/policies/{policy_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/policies/{policy_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/policies/{policy_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/policies/{policy_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/policies/{policy_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/portsneutron/db/db_base_plugin_v2.py:1733 get_portsget_port200pecan
POST/v2.0/portsneutron/plugins/ml2/plugin.py:1675 create_portcreate_port201pecan
GET/v2.0/ports/{port_id}/bindingsneutron/plugins/ml2/plugin.py:2847 get_port_bindingsget_port_binding200ext
POST/v2.0/ports/{port_id}/bindingsneutron/plugins/ml2/plugin.py:2800 create_port_bindingcreate_port_binding201ext
GET/v2.0/ports/{port_id}/bindings/{id}neutron/plugins/ml2/plugin.py:2864 get_port_bindingget_port_binding200ext
PUT/v2.0/ports/{port_id}/bindings/{id}neutron/plugins/ml2/plugin.py:2882 update_port_bindingupdate_port_binding200ext
DELETE/v2.0/ports/{port_id}/bindings/{id}neutron/plugins/ml2/plugin.py:2970 delete_port_bindingdelete_port_binding204ext
PUT/v2.0/ports/{port_id}/bindings/{id}/activateneutron/plugins/ml2/plugin.py:2917 activateactivate200ext
GET/v2.0/ports/{port_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/ports/{port_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/ports/{port_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/ports/{port_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/ports/{port_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/ports/{port_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/ports/{port_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/ports/{id}neutron/db/db_base_plugin_v2.py:1691 get_portget_port200pecan
PUT/v2.0/ports/{id}neutron/plugins/ml2/plugin.py:1966 update_portupdate_port200pecan
DELETE/v2.0/ports/{id}neutron/plugins/ml2/plugin.py:2228 delete_portdelete_port204pecan
GET/v2.0/qos/alias-bandwidth-limit-rulesneutron/services/qos/qos_plugin.py:1065 get_alias_bandwidth_limit_rules -> get_policy_rulesget_alias_bandwidth_limit_rule200ext
POST/v2.0/qos/alias-bandwidth-limit-rulesneutron/services/qos/qos_plugin.py:862 create_alias_bandwidth_limit_rule -> create_policy_rulecreate_alias_bandwidth_limit_rule201ext
GET/v2.0/qos/alias-bandwidth-limit-rules/{id}neutron/services/qos/qos_plugin.py:1024 get_alias_bandwidth_limit_rule -> get_policy_ruleget_alias_bandwidth_limit_rule200ext
PUT/v2.0/qos/alias-bandwidth-limit-rules/{id}neutron/services/qos/qos_plugin.py:901 update_alias_bandwidth_limit_rule -> update_policy_ruleupdate_alias_bandwidth_limit_rule200ext
DELETE/v2.0/qos/alias-bandwidth-limit-rules/{id}neutron/services/qos/qos_plugin.py:975 delete_alias_bandwidth_limit_rule -> delete_policy_ruledelete_alias_bandwidth_limit_rule204ext
GET/v2.0/qos/alias-dscp-marking-rulesneutron/services/qos/qos_plugin.py:1065 get_alias_dscp_marking_rules -> get_policy_rulesget_alias_dscp_marking_rule200ext
POST/v2.0/qos/alias-dscp-marking-rulesneutron/services/qos/qos_plugin.py:862 create_alias_dscp_marking_rule -> create_policy_rulecreate_alias_dscp_marking_rule201ext
GET/v2.0/qos/alias-dscp-marking-rules/{id}neutron/services/qos/qos_plugin.py:1024 get_alias_dscp_marking_rule -> get_policy_ruleget_alias_dscp_marking_rule200ext
PUT/v2.0/qos/alias-dscp-marking-rules/{id}neutron/services/qos/qos_plugin.py:901 update_alias_dscp_marking_rule -> update_policy_ruleupdate_alias_dscp_marking_rule200ext
DELETE/v2.0/qos/alias-dscp-marking-rules/{id}neutron/services/qos/qos_plugin.py:975 delete_alias_dscp_marking_rule -> delete_policy_ruledelete_alias_dscp_marking_rule204ext
GET/v2.0/qos/alias-minimum-bandwidth-rulesneutron/services/qos/qos_plugin.py:1065 get_alias_minimum_bandwidth_rules -> get_policy_rulesget_alias_minimum_bandwidth_rule200ext
POST/v2.0/qos/alias-minimum-bandwidth-rulesneutron/services/qos/qos_plugin.py:862 create_alias_minimum_bandwidth_rule -> create_policy_rulecreate_alias_minimum_bandwidth_rule201ext
GET/v2.0/qos/alias-minimum-bandwidth-rules/{id}neutron/services/qos/qos_plugin.py:1024 get_alias_minimum_bandwidth_rule -> get_policy_ruleget_alias_minimum_bandwidth_rule200ext
PUT/v2.0/qos/alias-minimum-bandwidth-rules/{id}neutron/services/qos/qos_plugin.py:901 update_alias_minimum_bandwidth_rule -> update_policy_ruleupdate_alias_minimum_bandwidth_rule200ext
DELETE/v2.0/qos/alias-minimum-bandwidth-rules/{id}neutron/services/qos/qos_plugin.py:975 delete_alias_minimum_bandwidth_rule -> delete_policy_ruledelete_alias_minimum_bandwidth_rule204ext
GET/v2.0/qos/alias-minimum-packet-rate-rulesneutron/services/qos/qos_plugin.py:1065 get_alias_minimum_packet_rate_rules -> get_policy_rulesget_alias_minimum_packet_rate_rule200ext
POST/v2.0/qos/alias-minimum-packet-rate-rulesneutron/services/qos/qos_plugin.py:862 create_alias_minimum_packet_rate_rule -> create_policy_rulecreate_alias_minimum_packet_rate_rule201ext
GET/v2.0/qos/alias-minimum-packet-rate-rules/{id}neutron/services/qos/qos_plugin.py:1024 get_alias_minimum_packet_rate_rule -> get_policy_ruleget_alias_minimum_packet_rate_rule200ext
PUT/v2.0/qos/alias-minimum-packet-rate-rules/{id}neutron/services/qos/qos_plugin.py:901 update_alias_minimum_packet_rate_rule -> update_policy_ruleupdate_alias_minimum_packet_rate_rule200ext
DELETE/v2.0/qos/alias-minimum-packet-rate-rules/{id}neutron/services/qos/qos_plugin.py:975 delete_alias_minimum_packet_rate_rule -> delete_policy_ruledelete_alias_minimum_packet_rate_rule204ext
GET/v2.0/qos/policiesneutron/services/qos/qos_plugin.py:822 get_policiesget_policy200ext
POST/v2.0/qos/policiesneutron/services/qos/qos_plugin.py:733 create_policycreate_policy201ext
GET/v2.0/qos/policies/{policy_id}/bandwidth_limit_rulesneutron/services/qos/qos_plugin.py:1065 get_policy_bandwidth_limit_rules -> get_policy_rulesget_policy_bandwidth_limit_rule200ext
POST/v2.0/qos/policies/{policy_id}/bandwidth_limit_rulesneutron/services/qos/qos_plugin.py:862 create_policy_bandwidth_limit_rule -> create_policy_rulecreate_policy_bandwidth_limit_rule201ext
GET/v2.0/qos/policies/{policy_id}/bandwidth_limit_rules/{id}neutron/services/qos/qos_plugin.py:1024 get_policy_bandwidth_limit_rule -> get_policy_ruleget_policy_bandwidth_limit_rule200ext
PUT/v2.0/qos/policies/{policy_id}/bandwidth_limit_rules/{id}neutron/services/qos/qos_plugin.py:901 update_policy_bandwidth_limit_rule -> update_policy_ruleupdate_policy_bandwidth_limit_rule200ext
DELETE/v2.0/qos/policies/{policy_id}/bandwidth_limit_rules/{id}neutron/services/qos/qos_plugin.py:975 delete_policy_bandwidth_limit_rule -> delete_policy_ruledelete_policy_bandwidth_limit_rule204ext
GET/v2.0/qos/policies/{policy_id}/dscp_marking_rulesneutron/services/qos/qos_plugin.py:1065 get_policy_dscp_marking_rules -> get_policy_rulesget_policy_dscp_marking_rule200ext
POST/v2.0/qos/policies/{policy_id}/dscp_marking_rulesneutron/services/qos/qos_plugin.py:862 create_policy_dscp_marking_rule -> create_policy_rulecreate_policy_dscp_marking_rule201ext
GET/v2.0/qos/policies/{policy_id}/dscp_marking_rules/{id}neutron/services/qos/qos_plugin.py:1024 get_policy_dscp_marking_rule -> get_policy_ruleget_policy_dscp_marking_rule200ext
PUT/v2.0/qos/policies/{policy_id}/dscp_marking_rules/{id}neutron/services/qos/qos_plugin.py:901 update_policy_dscp_marking_rule -> update_policy_ruleupdate_policy_dscp_marking_rule200ext
DELETE/v2.0/qos/policies/{policy_id}/dscp_marking_rules/{id}neutron/services/qos/qos_plugin.py:975 delete_policy_dscp_marking_rule -> delete_policy_ruledelete_policy_dscp_marking_rule204ext
GET/v2.0/qos/policies/{policy_id}/minimum-packet-rate-rulesneutron/services/qos/qos_plugin.py:1065 get_policy_minimum_packet_rate_rules -> get_policy_rulesget_policy_minimum_packet_rate_rule200ext
POST/v2.0/qos/policies/{policy_id}/minimum-packet-rate-rulesneutron/services/qos/qos_plugin.py:862 create_policy_minimum_packet_rate_rule -> create_policy_rulecreate_policy_minimum_packet_rate_rule201ext
GET/v2.0/qos/policies/{policy_id}/minimum-packet-rate-rules/{id}neutron/services/qos/qos_plugin.py:1024 get_policy_minimum_packet_rate_rule -> get_policy_ruleget_policy_minimum_packet_rate_rule200ext
PUT/v2.0/qos/policies/{policy_id}/minimum-packet-rate-rules/{id}neutron/services/qos/qos_plugin.py:901 update_policy_minimum_packet_rate_rule -> update_policy_ruleupdate_policy_minimum_packet_rate_rule200ext
DELETE/v2.0/qos/policies/{policy_id}/minimum-packet-rate-rules/{id}neutron/services/qos/qos_plugin.py:975 delete_policy_minimum_packet_rate_rule -> delete_policy_ruledelete_policy_minimum_packet_rate_rule204ext
GET/v2.0/qos/policies/{policy_id}/minimum_bandwidth_rulesneutron/services/qos/qos_plugin.py:1065 get_policy_minimum_bandwidth_rules -> get_policy_rulesget_policy_minimum_bandwidth_rule200ext
POST/v2.0/qos/policies/{policy_id}/minimum_bandwidth_rulesneutron/services/qos/qos_plugin.py:862 create_policy_minimum_bandwidth_rule -> create_policy_rulecreate_policy_minimum_bandwidth_rule201ext
GET/v2.0/qos/policies/{policy_id}/minimum_bandwidth_rules/{id}neutron/services/qos/qos_plugin.py:1024 get_policy_minimum_bandwidth_rule -> get_policy_ruleget_policy_minimum_bandwidth_rule200ext
PUT/v2.0/qos/policies/{policy_id}/minimum_bandwidth_rules/{id}neutron/services/qos/qos_plugin.py:901 update_policy_minimum_bandwidth_rule -> update_policy_ruleupdate_policy_minimum_bandwidth_rule200ext
DELETE/v2.0/qos/policies/{policy_id}/minimum_bandwidth_rules/{id}neutron/services/qos/qos_plugin.py:975 delete_policy_minimum_bandwidth_rule -> delete_policy_ruledelete_policy_minimum_bandwidth_rule204ext
GET/v2.0/qos/policies/{id}neutron/services/qos/qos_plugin.py:808 get_policyget_policy200ext
PUT/v2.0/qos/policies/{id}neutron/services/qos/qos_plugin.py:760 update_policyupdate_policy200ext
DELETE/v2.0/qos/policies/{id}neutron/services/qos/qos_plugin.py:786 delete_policydelete_policy204ext
GET/v2.0/qos/rule-typesneutron/services/qos/qos_plugin.py:847 get_rule_typesget_rule_type200ext
GET/v2.0/qos/rule-types/{id}neutron/services/qos/qos_plugin.py:840 get_rule_typeget_rule_type200ext
GET/v2.0/quotasneutron/extensions/quotasv2.py:96 index—200ext
POST/v2.0/quotasneutron/extensions/quotasv2.py:92 create—201ext
GET/v2.0/quotas/projectneutron/extensions/quotasv2.py:109 project—200ext
GET/v2.0/quotas/tenantneutron/extensions/quotasv2.py:103 tenant—200ext
GET/v2.0/quotas/{id}neutron/extensions/quotasv2.py:121 show—200ext
PUT/v2.0/quotas/{id}neutron/extensions/quotasv2.py:130 update—200ext
DELETE/v2.0/quotas/{id}neutron/extensions/quotasv2.py:126 delete—204ext
GET/v2.0/quotas/{id}/defaultneutron/extensions/quotasv2.py:82 default—200ext
GET/v2.0/quotas/{id}/detailsneutron/extensions/quotasv2_detail.py:54 details—200ext
GET/v2.0/rbac-policiesneutron/db/rbac_db_mixin.py:134 get_rbac_policiesget_rbac_policy200ext
POST/v2.0/rbac-policiesneutron/db/rbac_db_mixin.py:37 create_rbac_policycreate_rbac_policy201ext
GET/v2.0/rbac-policies/{id}neutron/db/rbac_db_mixin.py:129 get_rbac_policyget_rbac_policy200ext
PUT/v2.0/rbac-policies/{id}neutron/db/rbac_db_mixin.py:75 update_rbac_policyupdate_rbac_policy200ext
DELETE/v2.0/rbac-policies/{id}neutron/db/rbac_db_mixin.py:99 delete_rbac_policydelete_rbac_policy204ext
GET/v2.0/routersneutron/db/l3_db.py:688 get_routersget_router200ext
POST/v2.0/routersneutron/db/l3_db.py:312 create_routercreate_router201ext
GET/v2.0/routers/{router_id}/l3-agentsneutron/extensions/l3agentscheduler.py:111 index—200ext
GET/v2.0/routers/{router_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/routers/{router_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/routers/{router_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/routers/{router_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/routers/{router_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/routers/{router_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/routers/{router_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/routers/{id}neutron/db/l3_db.py:682 get_routerget_router200ext
PUT/v2.0/routers/{id}neutron/db/extraroute_db.py:63 update_routerupdate_router200ext
DELETE/v2.0/routers/{id}neutron/db/l3_db.py:638 delete_routerdelete_router204ext
PUT/v2.0/routers/{id}/add_external_gatewaysneutron/db/l3_extra_gws_db.py:461 add_external_gatewaysadd_external_gateways200ext
PUT/v2.0/routers/{id}/add_extraroutesneutron/db/extraroute_db.py:221 add_extraroutesadd_extraroutes200ext
PUT/v2.0/routers/{id}/add_router_interfaceneutron/db/l3_db.py:2272 add_router_interfaceadd_router_interface200ext
PUT/v2.0/routers/{id}/remove_external_gatewaysneutron/db/l3_extra_gws_db.py:478 remove_external_gatewaysremove_external_gateways200ext
PUT/v2.0/routers/{id}/remove_extraroutesneutron/db/extraroute_db.py:238 remove_extraroutesremove_extraroutes200ext
PUT/v2.0/routers/{id}/remove_router_interfaceneutron/db/l3_db.py:2279 remove_router_interfaceremove_router_interface200ext
PUT/v2.0/routers/{id}/update_external_gatewaysneutron/db/l3_extra_gws_db.py:582 update_external_gatewaysupdate_external_gateways200ext
GET/v2.0/security-group-rulesneutron/db/securitygroups_db.py:1046 get_security_group_rulesget_security_group_rule200ext
POST/v2.0/security-group-rulesneutron/db/securitygroups_rpc_base.py:63 create_security_group_rulecreate_security_group_rule201ext
GET/v2.0/security-group-rules/{id}neutron/db/securitygroups_db.py:1086 get_security_group_ruleget_security_group_rule200ext
DELETE/v2.0/security-group-rules/{id}neutron/db/securitygroups_rpc_base.py:77 delete_security_group_ruledelete_security_group_rule204ext
GET/v2.0/security-groupsneutron/db/securitygroups_db.py:166 get_security_groupsget_security_group200ext
POST/v2.0/security-groupsneutron/db/securitygroups_db.py:89 create_security_groupcreate_security_group201ext
GET/v2.0/security-groups-default-statefulnessneutron/db/security_groups_default_statefulness.py:79 get_security_groups_default_statefulnessget_security_groups_default_statefulness200ext
POST/v2.0/security-groups-default-statefulnessneutron/db/security_groups_default_statefulness.py:52 create_security_groups_default_statefulnesscreate_security_groups_default_statefulness201ext
GET/v2.0/security-groups-default-statefulness/{id}neutron/db/security_groups_default_statefulness.py:79 get_security_groups_default_statefulnessget_security_groups_default_statefulness200ext
PUT/v2.0/security-groups-default-statefulness/{id}neutron/db/security_groups_default_statefulness.py:70 update_security_groups_default_statefulnessupdate_security_groups_default_statefulness200ext
DELETE/v2.0/security-groups-default-statefulness/{id}neutron/db/security_groups_default_statefulness.py:95 delete_security_groups_default_statefulnessdelete_security_groups_default_statefulness204ext
GET/v2.0/security-groups/{security_group_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/security-groups/{security_group_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/security-groups/{security_group_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/security-groups/{security_group_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/security-groups/{security_group_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/security-groups/{security_group_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/security-groups/{security_group_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/security-groups/{id}neutron/db/securitygroups_db.py:208 get_security_groupget_security_group200ext
PUT/v2.0/security-groups/{id}neutron/db/securitygroups_db.py:303 update_security_groupupdate_security_group200ext
DELETE/v2.0/security-groups/{id}neutron/db/securitygroups_db.py:261 delete_security_groupdelete_security_group204ext
GET/v2.0/segmentsneutron/services/segments/db.py:169 get_segmentsget_segment200ext
POST/v2.0/segmentsneutron/services/segments/db.py:89 create_segmentcreate_segment201ext
GET/v2.0/segments/{id}neutron/services/segments/db.py:164 get_segmentget_segment200ext
PUT/v2.0/segments/{id}neutron/services/segments/db.py:154 update_segmentupdate_segment200ext
DELETE/v2.0/segments/{id}neutron/services/segments/db.py:192 delete_segmentdelete_segment204ext
GET/v2.0/service-providersneutron/db/servicetype_db.py:48 get_service_providersget_service_provider200ext
GET/v2.0/service_profilesneutron/db/flavors_db.py:207 get_service_profilesget_service_profile200ext
POST/v2.0/service_profilesneutron/db/flavors_db.py:168 create_service_profilecreate_service_profile201ext
GET/v2.0/service_profiles/{id}neutron/db/flavors_db.py:198 get_service_profileget_service_profile200ext
PUT/v2.0/service_profiles/{id}neutron/db/flavors_db.py:183 update_service_profileupdate_service_profile200ext
DELETE/v2.0/service_profiles/{id}neutron/db/flavors_db.py:202 delete_service_profiledelete_service_profile204ext
GET/v2.0/subnetpoolsneutron/db/db_base_plugin_v2.py:1430 get_subnetpoolsget_subnetpool200ext
POST/v2.0/subnetpoolsneutron/db/db_base_plugin_v2.py:1352 create_subnetpoolcreate_subnetpool201ext
GET/v2.0/subnetpools/{subnetpool_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/subnetpools/{subnetpool_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/subnetpools/{subnetpool_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/subnetpools/{subnetpool_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/subnetpools/{subnetpool_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/subnetpools/{subnetpool_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/subnetpools/{subnetpool_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/subnetpools/{id}neutron/db/db_base_plugin_v2.py:1425 get_subnetpoolget_subnetpool200ext
PUT/v2.0/subnetpools/{id}neutron/db/db_base_plugin_v2.py:1387 update_subnetpoolupdate_subnetpool200ext
DELETE/v2.0/subnetpools/{id}neutron/db/db_base_plugin_v2.py:1452 delete_subnetpooldelete_subnetpool204ext
PUT/v2.0/subnetpools/{id}/add_prefixesneutron/db/db_base_plugin_v2.py:1792 add_prefixesadd_prefixes200ext
PUT/v2.0/subnetpools/{id}/onboard_network_subnetsneutron/db/db_base_plugin_v2.py:1461 onboard_network_subnetsonboard_network_subnets200ext
PUT/v2.0/subnetpools/{id}/remove_prefixesneutron/db/db_base_plugin_v2.py:1810 remove_prefixesremove_prefixes200ext
GET/v2.0/subnetsneutron/db/db_base_plugin_v2.py:1214 get_subnetsget_subnet200pecan
POST/v2.0/subnetsneutron/plugins/ml2/plugin.py:1491 create_subnetcreate_subnet201pecan
GET/v2.0/subnets/{subnet_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/subnets/{subnet_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/subnets/{subnet_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/subnets/{subnet_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/subnets/{subnet_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/subnets/{subnet_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/subnets/{subnet_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/subnets/{id}neutron/db/db_base_plugin_v2.py:1209 get_subnetget_subnet200pecan
PUT/v2.0/subnets/{id}neutron/plugins/ml2/plugin.py:1526 update_subnetupdate_subnet200pecan
DELETE/v2.0/subnets/{id}neutron/plugins/ml2/plugin.py:1548 delete_subnetdelete_subnet204pecan
GET/v2.0/trunksneutron/services/trunk/plugin.py:226 get_trunksget_trunk200ext
POST/v2.0/trunksneutron/services/trunk/plugin.py:236 create_trunkcreate_trunk201ext
GET/v2.0/trunks/{trunk_id}/tagsneutron/extensions/tagging.py:206 index—200ext
POST/v2.0/trunks/{trunk_id}/tagsneutron/extensions/tagging.py:228 create—200ext
PUT/v2.0/trunks/{trunk_id}/tagsneutron/extensions/tagging.py:269 update_all—200ext
DELETE/v2.0/trunks/{trunk_id}/tagsneutron/extensions/tagging.py:306 delete_all—204ext
GET/v2.0/trunks/{trunk_id}/tags/{id}neutron/extensions/tagging.py:216 show—204ext
PUT/v2.0/trunks/{trunk_id}/tags/{id}neutron/extensions/tagging.py:247 update—201ext
DELETE/v2.0/trunks/{trunk_id}/tags/{id}neutron/extensions/tagging.py:288 delete—204ext
GET/v2.0/trunks/{id}neutron/services/trunk/plugin.py:220 get_trunkget_trunk200ext
PUT/v2.0/trunks/{id}neutron/services/trunk/plugin.py:276 update_trunkupdate_trunk200ext
DELETE/v2.0/trunks/{id}neutron/services/trunk/plugin.py:300 delete_trunkdelete_trunk204ext
PUT/v2.0/trunks/{id}/add_subportsneutron/services/trunk/plugin.py:331 add_subportsadd_subports200ext
GET/v2.0/trunks/{id}/get_subportsneutron/services/trunk/plugin.py:447 get_subportsget_subports200ext
PUT/v2.0/trunks/{id}/remove_subportsneutron/services/trunk/plugin.py:389 remove_subportsremove_subports200ext

3.1 Routes that exist but are not implemented

The Routes mapper registers the five standard operations for every extension collection, plus GET …/new and GET …/{id}/edit for each (mapper.resource, neutron/api/extensions.py:180-184). When the plugin or the custom controller lacks the matching method, the request fails with 500 "Request Failed: internal server error while processing your request." (AttributeError caught by neutron/api/v2/resource.py:97-110), except when an earlier step answers first (an update or delete fetches the object, so an unknown ID gives 404). Checked in the reference run: POST /v2.0/availability_zones, GET /v2.0/service-providers/x, GET /v2.0/floatingip-pools/x, GET /v2.0/routers/x/l3-agents/y, GET /v2.0/auto-allocated-topology (list), GET /v2.0/routers/new → 500; PUT /v2.0/security-group-rules/x → 404. The 54 such operations are in _rewrite/routes-unimplemented_stable-2026.2.json; among them /v2.0/qos/ports and /v2.0/qos/networks (all methods), registered by a QoS extension although no plugin serves them (GET /v2.0/qos/ports → 500 in the reference run). The alias QoS rule collections (/v2.0/qos/alias-<type>-rules) only work by ID: listing them answers 500. A rewrite should answer 404 or 405 for them; no client can depend on a 500.

Two spellings of the same collection. Collections whose name contains a dash are registered dashed in the mapper (/v2.0/network-segment-ranges, /v2.0/qos/policies/{id}/minimum-packet-rate-rules). The underscore spelling used by parts of the api-ref (/v2.0/network_segment_ranges, …/minimum_packet_rate_rules) is not matched by the mapper but answered by pecan's fallback controllers, with the same data in compact JSON (checked in the reference run). Tag sub-resources are registered with the underscore form of the parent (/v2.0/network_segment_ranges/{id}/tags). A rewrite should accept both spellings.

QoS packet-rate-limit rules are registered without the /qos prefix (/v2.0/policies/{policy_id}/packet_rate_limit_rules, and the tags of QoS policies under /v2.0/policies/{policy_id}/tags). The documented form /v2.0/qos/policies/{policy_id}/packet_rate_limit_rules also answers in the reference run: the mapper does not match it, so it falls through to the pecan controllers that neutron/pecan_wsgi/startup.py:61-120 builds for extension resources (neutron/pecan_wsgi/controllers/root.py:121-150); the probe run confirms it: the /qos/… form is served by pecan, the unprefixed form by the legacy controller, both answering QosPolicyNotFound for an unknown policy (_rewrite/pipeline-probes_stable-2026.2.json).

4. Functional description by resource

The behaviour after the response (OVN NB transactions, status changes) is described in 05; the sheets below say what each call is for, its rules and its synchronous effects. All sheets assume the reference profile.

4.1 Networks — /v2.0/networks

  • Purpose: create an L2 broadcast domain. In OVN it becomes a Logical_Switch named neutron-<network id>.
  • Actor: project member (create_network: admin or role:member on its own project); provider attributes (provider:network_type, provider:physical_network, provider:segmentation_id, segments) are admin-only to set and to read (create_network:provider:network_type = rule:admin_only); shared and router:external are admin-only on create.
  • Business rules:
    • Without provider attributes, ML2 allocates a segment of the first [ml2]/project_network_types type (here geneve, VNI from [ml2_type_geneve]/vni_ranges or from the network segment ranges) (neutron/plugins/ml2/plugin.py:1296).
    • The OVN driver only accepts local, flat, geneve, vxlan, vlan (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:608-613) and refuses a segmentation ID above OVN's NB_Global.options:max_tunid (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:622-643); both → 400 InvalidInput.
    • mtu is computed from the segment type and [DEFAULT]/global_physnet_mtu minus the Geneve header ([ml2_type_geneve]/max_header_size); with net-mtu-writable the owner may lower it, but not below the MTU needed by its IPv6 subnets: NetworkMTUSubnetConflict (neutron/db/db_base_plugin_v2.py:561-569).
    • shared: false on a network that other projects use → 409 InvalidSharedSetting (neutron/db/db_base_plugin_v2.py:328, 376).
    • availability_zone_hints must name OVN availability zones (gateway chassis with ovn-cms-options=availability-zones=…), see § 4.17.
  • Flow: 1. (sync) precommit: segments validated, initial row in ovn_revision_numbers (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:737-764); commit. 2. (sync, after commit, before the response) postcommit: one OVSDB transaction creates the Logical_Switch with external_ids (name, MTU, revision, AZ hints, network type, physnet) and other_config (multicast snooping, VLAN passthrough, FDB ageing), plus a localnet port per segment that has a physical network (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:2414-2494); the revision is bumped; then the metadata port (device_owner: network:distributed) is created when [ovn]/ovn_metadata_enabled is set (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:3196). 3. (async) nothing else; if step 2 failed, the network stays in the Neutron DB and the maintenance task re-creates the switch (05).
  • Outcome: status: ACTIVE immediately; admin_state_up has no data-plane effect in OVN for networks.
  • Limitations: a postcommit failure on create deletes the network again and returns the error (neutron/plugins/ml2/plugin.py:1301-1316); on update and delete, a postcommit failure is only logged and repaired later by the maintenance task.
  • Delete: refused with 409 NetworkInUse while non-service ports exist (neutron/db/db_base_plugin_v2.py:581); DHCP, router-gateway and distributed (metadata) ports are deleted automatically. OVN: ls_del (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:2496-2500).

4.2 Subnets — /v2.0/subnets

  • Purpose: attach an IPv4 or IPv6 address range to a network; in OVN it becomes a DHCP_Options row (when enable_dhcp) used by ovn-controller's native DHCP responder.
  • Actor: owner of the network (create_subnet: admin or member with rule:network_owner); a subnet on a shared network may be created by others only if the policy allows it.
  • Business rules (neutron/db/db_base_plugin_v2.py:934, neutron/db/ipam_backend_mixin.py): cidr or subnetpool_id (+ prefixlen) required; gateway inside the CIDR and outside the allocation pools (GatewayConflictWithAllocationPools, neutron/db/ipam_backend_mixin.py:468); pools inside the CIDR and not overlapping (InvalidAllocationPool, OutOfBoundsAllocationPool, OverlappingAllocationPools, neutron/db/ipam_backend_mixin.py:326-356); at most [DEFAULT]/max_dns_nameservers (5) DNS servers and [DEFAULT]/max_subnet_host_routes (20) host routes (neutron/db/db_base_plugin_v2.py:779, 793); ipv6_ra_mode/ipv6_address_mode combinations are checked (neutron/db/db_base_plugin_v2.py:721-753); all subnets of a network must use subnet pools of the same address scope (NetworkSubnetPoolAffinityError, neutron/db/ipam_backend_mixin.py:282-294); with routed networks every subnet of a multi-segment network must have a segment_id (SubnetsNotAllAssociatedWithSegments, neutron/db/ipam_backend_mixin.py:382-389); overlapping CIDRs on the same network are refused.
  • Flow: (sync) IPAM allocation (internal driver, tables ipamsubnets, ipamallocationpools), precommit revision row, commit; postcommit creates or updates DHCP_Options and the metadata port's IP on that subnet (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:834-851, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:2985). An update of gateway_ip, host_routes or DNS servers also updates routers attached to the subnet (neutron/services/ovn_l3/plugin.py:395-430).
  • Delete: 409 SubnetInUse while ports other than DHCP/metadata/router-gateway use it (neutron/db/db_base_plugin_v2.py:1151, 1162).

4.3 Ports — /v2.0/ports

  • Purpose: reserve a MAC and IP addresses on a network for a VM interface, a router interface, a floating IP, a load-balancer VIP… In OVN it becomes a Logical_Switch_Port (LSP) named after the port ID.
  • Actor: admin, owner of the network, or any member on a shared network (create_port); binding attributes (binding:host_id, binding:profile) are writable only by admin or the service role (Nova), binding:vif_details readable only by them; mac_address, fixed_ips with an explicit IP on a network not owned, device_owner values starting with network: and port_security_enabled have their own rules (neutron/conf/policies/port.py).
  • Business rules: MAC unique on the network (MacAddressInUse, neutron/plugins/ml2/plugin.py:1723); requested IPs valid for the subnet/network (InvalidIpForSubnet, InvalidIpForNetwork, neutron/db/ipam_backend_mixin.py:437, 449), free (IpAddressAlreadyAllocated), or allocation fails with IpAddressGenerationFailure (neutron/db/ipam_pluggable_backend.py:140); allowed_address_pairs and security groups require port_security_enabled (AddressPairAndPortSecurityRequired, PortSecurityAndIPRequiredForSecurityGroups, neutron/plugins/ml2/plugin.py:1594-1602); OVN refuses allowed address pairs that reuse the IP of the network's metadata port (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:645-695); device_id of a router must belong to the caller (DeviceIDNotOwnedByTenant, neutron/db/db_base_plugin_v2.py:1786-1789); the device_owner of an OVN-"ignored" port (e.g. network:floatingip) cannot be changed to or from (OVNPortUpdateError, 400, neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:952-967); fixed IPs of a port that carries a floating IP cannot be changed (neutron/db/l3_db.py:1825); unsupported binding:profile content → 400 (validate_and_get_data_from_binding_profile).
  • Flow (create) (neutron/plugins/ml2/plugin.py:1675):
    1. (sync, transaction) port row, IP allocation, security-group bindings (default SG of the project if none given), extension data; precommit of OVN: initial revision, and a provisioning block L2 when the port is bound to a host that has an OVN chassis (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:866-932).
    2. (sync, after commit) postcommit: one NB transaction creates the LSP with addresses, port_security, options (requested-chassis…), external_ids, DHCP options, the port group of each security group (or the drop group), DNS records and QoS (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:620-715); the DHCP provisioning entity is completed at once (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:943-950).
    3. (sync) if binding:host_id is set, ML2 binds the port (neutron/plugins/ml2/plugin.py:651, up to MAX_BIND_TRIES = 10 attempts): the OVN driver binds only if the host has a live OVN controller agent and, for flat/VLAN segments, a bridge mapping for the physical network; it chooses vif_type ovs (or vhostuser for DPDK, agilio_ovs for virtio-forwarder) and returns the integration bridge and datapath type in vif_details (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1226-1405). Otherwise binding:vif_type stays binding_failed (no error is returned).
    4. (async) when ovn-controller claims the port, the Logical_Switch_Port.up column becomes true; the server's OVSDB event calls set_port_status_up, which completes the provisioning block; ML2 then sets status: ACTIVE and Nova receives network-vif-plugged (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1453-1507, neutron/plugins/ml2/plugin.py:370). See 05 and the port lifecycle below.
  • Outcome: status: DOWN in the response; ACTIVE once OVN reports the port up; back to DOWN when it goes down.
  • Update: re-binding when binding:host_id changes (live migration uses the bindings sub-resource, § 4.4); OVN LSP updated after commit, with a retry on RevisionConflict during a live migration (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:969-996).
  • Delete: router interface and gateway ports can only be deleted through the router API (ServicePortInUse 409, neutron/plugins/ml2/plugin.py:2224, neutron/db/l3_db.py:1864); floating IPs associated with the port are disassociated (neutron/db/l3_db.py:1868); OVN LSP removed after commit (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1209-1224).
  • Limitations: binding:vnic_type other than normal, direct, direct-physical, macvtap, vdpa, remote-managed, baremetal, virtio-forwarder is not bound by OVN (neutron/common/ovn/constants.py:502-510); direct ports become OVN external ports served by the HA chassis group of the network.

4.4 Port bindings — /v2.0/ports/{port_id}/bindings (extension binding-extended)

  • Purpose: let Nova prepare a port on the destination host of a live migration before switching to it.
  • Actor: admin or service role.
  • Rules: one binding per host; POST for a host that already has a binding → 409 PortBindingAlreadyExists (neutron/plugins/ml2/plugin.py:2808); the new binding is INACTIVE; binding failure → 500-class PortBindingError (neutron/plugins/ml2/plugin.py:2835).
  • **PUT …/bindings/{host}/activate** (neutron/plugins/ml2/plugin.py:2917): makes the host's binding ACTIVE and the old one INACTIVE; 409 PortBindingAlreadyActive if already active (neutron/plugins/ml2/plugin.py:2929). In OVN the LSP options:requested-chassis lists both chassis during the migration and is reduced afterwards (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:552).
  • DELETE …/bindings/{host}: removes an inactive binding; deleting the active one is refused.

4.5 Subnet pools and address scopes — /v2.0/subnetpools, /v2.0/address-scopes

  • Purpose: hand out subnet CIDRs automatically from a set of prefixes; an address scope groups pools whose addresses may be routed without NAT.
  • Rules: prefixes of a pool may only grow (IllegalSubnetPoolPrefixUpdate, neutron/db/db_base_plugin_v2.py:108); the pool's address scope must have the same IP version (IllegalSubnetPoolIpVersionAssociationToAddressScope, neutron/db/db_base_plugin_v2.py:1267) and its prefixes must not overlap another pool of the scope (AddressScopePrefixConflict, neutron/db/db_base_plugin_v2.py:1283); a pool in use cannot be deleted (SubnetPoolDeleteError, neutron/db/db_base_plugin_v2.py:1457); is_default pools are used when a subnet is created with use_default_subnetpool. PUT /subnetpools/{id}/add_prefixes and remove_prefixes (extension subnetpool-prefix-ops) are member actions. POST /subnetpools/{id}/onboard_network_subnets adopts existing subnets (subnet_onboard).
  • OVN effect: the address scope of the router's subnets is copied to the LRP external_ids and controls whether the gateway SNATs [inferred, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:1889].

4.6 Routers — /v2.0/routers

  • Purpose: route between project subnets and towards an external network, with SNAT for the private subnets. In OVN: a Logical_Router neutron-<router id>, one Logical_Router_Port (LRP) per interface and gateway port, NAT rows for SNAT/floating IPs, Logical_Router_Static_Route for the default route and extra routes, gateway ports scheduled on gateway chassis.
  • Actor: project member; external_gateway_info.external_fixed_ips and enable_snat are admin-only by default; ha, distributed admin-only.
  • Business rules: the gateway network must be router:external (neutron/db/l3_db.py:419); a requested gateway IP may not be the subnet's gateway IP (neutron/db/l3_db.py:429); removing the gateway while floating IPs use it → 409 RouterExternalGatewayInUseByFloatingIp (neutron/db/l3_db.py:474); a router with interfaces cannot be deleted (RouterInUse 409, neutron/db/l3_db.py:638, 514, 634); availability_zone_hints must name existing OVN AZs (neutron/services/ovn_l3/plugin.py:469-482).
  • Flow (create): (sync) DB row, gateway port creation on the external network (neutron/db/l3_db.py:516), precommit revision row (neutron/services/ovn_l3/service_providers/ovn.py:46); after commit the AFTER_CREATE callback creates the logical router with its options (always_learn_from_arp_request=false, dynamic_neigh_routers=true, MAC-binding age), the gateway LRP, its HA chassis group, the default route and the SNAT rules (neutron/services/ovn_l3/service_providers/ovn.py:66-78, neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:1647-1677, 1465).
  • Outcome: status: ACTIVE immediately. OVN routers have no PENDING_* states; failures are logged and the router is fixed by the maintenance task.
  • **PUT /routers/{id}/add_router_interface** {"subnet_id"} or {"port_id"} (neutron/db/l3_db.py:981): exactly one of them (neutron/db/l3_db.py:749-759); the subnet must have a gateway IP (neutron/db/l3_db.py:917), not overlap another subnet already on the router (neutron/db/l3_db.py:622), and belong to the caller unless admin (neutron/db/l3_db.py:936); a port must have at least one fixed IP (neutron/db/l3_db.py:781) and not be in use (PortInUse, neutron/db/l3_db.py:776); only one IPv4 subnet per router port (neutron/db/l3_db.py:835); IPv6 subnets of one network share one port (neutron/db/l3_db.py:899). Response: {"id", "subnet_id", "subnet_ids", "port_id", "network_id", "project_id", "tenant_id"} with 200. OVN: LRP created by the ROUTER_INTERFACE AFTER_CREATE callback (neutron/services/ovn_l3/service_providers/ovn.py:112-132) and the LSP of the port gets type: router.
  • **PUT /routers/{id}/remove_router_interface**: by subnet or port; 404 RouterInterfaceNotFound/RouterInterfaceNotFoundForSubnet (neutron/db/l3_db.py:1163, 1207); 409 RouterInterfaceInUseByRoute when an extra route still uses the subnet (neutron/db/extraroute_db.py:169); 409 RouterInUse when floating IPs depend on it (neutron/db/l3_db.py:1154).
  • Extra routes (routes attribute, PUT add_extraroutes/remove_extraroutes from extraroute-atomic, neutron/db/extraroute_db.py:221): next hop must be on a connected subnet (InvalidRoutes, neutron/db/extraroute_db.py:88-93); at most [DEFAULT]/max_routes (30, and quota router_route). OVN: static routes (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:1567).
  • Multiple gateways (external-gateway-multihoming): PUT add_external_gateways, update_external_gateways, remove_external_gateways (neutron/db/l3_extra_gws_db.py:461) manage the external_gateways list; OVN can spread the default route over them with ECMP and BFD (enable_default_route_ecmp, enable_default_route_bfd).
  • Gateway placement: the gateway port is scheduled on up to 5 gateway chassis with priorities (an HA_Chassis_Group), chosen by [ovn]/ovn_l3_scheduler among the chassis that have enable-chassis-as-gw and the external network's physnet, filtered by the router's AZ hints; see 05-async-processing_stable-2026.2.md § 7. No candidate does not fail the API call: the router is created without external connectivity.
  • L3 agent scheduling under OVN: GET /routers/{id}/l3-agents and /agents/{id}/l3-routers are answered by the OVN plugin from the gateway chassis priorities of the router (neutron/services/ovn_l3/plugin.py:500-650); PUT /agents/{agent_id}/l3-routers/{router_id} with ha_chassis_priority (extension l3-agent-scheduler-ha-chassis-priority) changes the priority of a chassis.

4.7 Floating IPs — /v2.0/floatingips

  • Purpose: map a public IPv4 address of an external network to a private fixed IP (1:1 NAT). OVN: a NAT row of type dnat_and_snat on the router, distributed on the compute node when [ovn]/enable_distributed_floating_ip is set, centralised on the gateway chassis otherwise.
  • Rules (neutron/db/l3_db.py:1510): the floating network must be external and have an IPv4 subnet (neutron/db/l3_db.py:1528-1532); the internal port must belong to the same project unless admin (neutron/db/l3_db.py:1375), have an IPv4 fixed IP, and if it has several, fixed_ip_address must say which (neutron/db/l3_db.py:1395-1406); fixed_ip_address without port_id → 400 (neutron/db/l3_db.py:1447); the port's subnet must be attached to a router that has a gateway on the floating network (ExternalGatewayForFloatingIPNotFound 404, or 409 "Router … has no external gateway on network …", neutron/db/l3_db.py:1357); a port/IP pair may carry only one floating IP (FloatingIPPortAlreadyAssociated 409, neutron/db/l3_db.py:1467); the address pool may be exhausted (ExternalIpAddressExhausted 400, neutron/db/l3_db.py:1578).
  • Flow: (sync) allocate a port with device_owner: network:floatingip on the external network, create the row (DOWN), commit; OVN: initial revision in precommit, then create_floatingip adds the NAT row and, if the FIP has a router, **sets status: ACTIVE immediately** (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:1280-1301); the response of POST may still show DOWN because the status update happens after the object was built [inferred].
  • Update: port_id: null disassociates (NAT removed, status DOWN); changing port_id re-creates the NAT. qos_policy_id applies a FIP QoS policy (OVN: meters on the NAT, qos-fip).
  • Port forwardings — /v2.0/floatingips/{fip_id}/port_forwardings (neutron/services/portforwarding/pf_plugin.py:352): DNAT of a protocol/port (or range) of a floating IP that is not associated with a port; OVN implements each as a VIP of a Load_Balancer named pf-floatingip-<fip id>-<tcp|udp> (one per FIP and protocol; port ranges get their own balancer suffixed -<external_port>), attached to the router and to the router's internal switches, VIP <floating ip>:<external_port> → <internal ip>:<internal_port> (neutron/services/portforwarding/drivers/ovn/driver.py:42-132). Rules (neutron/services/portforwarding/pf_plugin.py:352): the floating IP must not be associated with a port (FipInUseByPortForwarding, PortHasBindingFloatingIP, neutron/services/portforwarding/pf_plugin.py:181, 345), the internal port must have an IPv4 address and the internal IP must be one of its fixed IPs (neutron/services/portforwarding/pf_plugin.py:293, 316, 615), the floating IP's router must reach the internal port's subnet (neutron/services/portforwarding/pf_plugin.py:595), no duplicate entry (neutron/services/portforwarding/pf_plugin.py:392, 463) and no collision between port ranges of the same floating IP (neutron/services/portforwarding/pf_plugin.py:482), all 400; a port with forwardings cannot be deleted (PortHasPortForwarding, neutron/services/portforwarding/pf_plugin.py:149). With distributed floating IPs, forwardings on routers attached to provider networks are logged as not working (neutron/services/portforwarding/drivers/ovn/driver.py:134-160).

4.8 Security groups — /v2.0/security-groups, /v2.0/security-group-rules

  • Purpose: stateful (or stateless, stateful: false) per-port firewall. OVN: one Port_Group per group (pg_<id with _>) holding the LSPs, and one ACL per rule on that port group; ports with port security but no group are in the neutron_pg_drop group.
  • Rules: each project gets a default group created on first need, with rules copied from the default security group rules templates (/v2.0/default-security-group-rules, admin-only, neutron/db/securitygroups_db.py:595); default cannot be deleted (SecurityGroupCannotRemoveDefault, neutron/db/securitygroups_db.py:271) or renamed (neutron/db/securitygroups_db.py:324); a group in use by ports cannot be deleted (SecurityGroupInUse 409, neutron/db/securitygroups_db.py:266); stateful cannot change while in use (neutron/db/securitygroups_db.py:314); rule checks: protocol required with ports, port range valid, ICMP type/code ranges, ethertype consistent with protocol and remote prefix, only one of remote_ip_prefix/remote_group_id/remote_address_group_id (neutron/db/securitygroups_db.py:527, 768-853), no duplicate rule (SecurityGroupRuleExists 409, neutron/db/securitygroups_db.py:1014). Rules cannot be updated (no PUT, § 3.1).
  • Flow: (sync) DB rows; OVN port group and ACLs created in callbacks before the response (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:476-606). stateful groups give allow-related ACLs, stateless give allow-stateless.
  • **/v2.0/security-groups-default-statefulness** (neutron/db/security_groups_default_statefulness.py:52-95): per-project default of stateful for new groups.
  • Address groups — /v2.0/address-groups with PUT add_addresses/remove_addresses (neutron/db/address_group_db.py:84-129): named lists of CIDRs used as remote_address_group_id; OVN keeps them in Address_Set rows (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py:3115-3165).

4.9 QoS — /v2.0/qos/policies and rule sub-resources

  • Purpose: bandwidth limits, DSCP marking and minimum bandwidth guarantees for ports, networks, floating IPs and router gateways.
  • Rules: the OVN driver supports bandwidth_limit (egress and ingress), dscp_marking, minimum_bandwidth (neutron/services/qos/drivers/ovn/driver.py:25-41) on normal ports; GET /qos/rule-types lists what the loaded drivers support (neutron/services/qos/qos_plugin.py:847); a rule type the driver cannot apply to a bound port → 409/400 at association time; minimum_bandwidth and minimum_packet_rate rules feed Placement resource requests (port-resource-request-groups), so a port with such a policy carries resource_request that Nova must honour; a policy in use cannot be deleted (QosPolicyInUse 409); is_default (extension qos-default) makes it the project default for new networks.
  • Rule paths: /v2.0/qos/policies/{policy_id}/bandwidth_limit_rules, dscp_marking_rules, minimum_bandwidth_rules, minimum-packet-rate-rules, /v2.0/policies/{policy_id}/packet_rate_limit_rules (see § 3.1), and the alias forms /v2.0/qos/alias-<type>-rules/{id} that address a rule by ID only. All are synthesised by QoSPluginBase.__getattr__ (neutron/extensions/qos.py:106-185) onto the generic create_policy_rule, update_policy_rule… (neutron/services/qos/qos_plugin.py:862-1065).
  • OVN effect (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py:114-207): per port and direction, at most one NB QoS row on the port's logical switch, direction from-lport (Neutron egress, inport == "<port>") or to-lport (ingress, outport == "<port>"), priority 2002, bandwidth rate = max_kbps and burst = max_burst_kbps (kbit/s), action dscp = dscp_mark; a bandwidth-limit rule and a DSCP rule of the same direction are merged into that one row. Floating IP and router gateway policies use priority 2003 with ip4.src/ip4.dst == <FIP> and is_chassis_resident("<gateway port>") (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py:34-35). Minimum bandwidth and the egress maximum of ports on provider (physnet) networks are written as LSP options:qos_min_rate / qos_max_rate in bit/s (max_kbps × 1000), applied by OVN on the localnet egress queue (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py:225-266). The effective policy of a port is its own qos_policy_id, else its network's (neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py:310).

4.10 Trunks — /v2.0/trunks

  • Purpose: carry several networks to one VM interface with VLAN tags (sub-ports).
  • Rules (neutron/services/trunk/plugin.py:236, neutron/services/trunk/rules.py): the parent port must not be in another trunk or be a sub-port (TrunkPortInUse, ParentPortInUse, neutron/services/trunk/rules.py:67-95); sub-ports need segmentation_type and segmentation_id (neutron/services/trunk/rules.py:275-296), a unique segmentation_id (1–4094 for vlan) inside the trunk, and an MTU not greater than the parent's (SubPortMtuGreaterThanTrunkPortMtu, neutron/services/trunk/rules.py:258-273); a port used as parent or sub-port cannot be deleted (PortInUseAsTrunkParent, PortInUseAsSubPort, neutron/services/trunk/rules.py:43-58); a trunk in a transient state cannot be changed (TrunkDisabled, neutron/services/trunk/rules.py:37-40); PUT add_subports/remove_subports (neutron/services/trunk/plugin.py:331, 389); GET get_subports; a trunk whose parent is bound cannot be deleted unless the driver allows it.
  • OVN effect: each sub-port's LSP gets parent_name = parent port and tag = VLAN; its binding profile and status follow the parent (neutron/services/trunk/drivers/ovn/trunk_driver.py:50-120).
  • Status: ACTIVE set at create and on every sub-port change by the OVN trunk driver (neutron/services/trunk/drivers/ovn/trunk_driver.py:183-215), and when the parent port becomes ACTIVE (neutron/services/trunk/drivers/ovn/trunk_driver.py:217-258).

4.11 Segments and network segment ranges — /v2.0/segments, /v2.0/network-segment-ranges

  • Segments (neutron/services/segments/db.py:89-192): admin-only management of the L2 segments of a network ("routed provider networks"); each segment has its own subnets, and hosts are mapped to segments through the OVN chassis bridge mappings (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1554-1594). In OVN, a network with several segments is one logical switch with one localnet port per segment that has a physical network (create_segment_provnet_port, neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:720-735). With the placement service plugin, segments are reported to Placement as resource providers with an IPV4_ADDRESS inventory [inferred].
  • Network segment ranges (neutron/services/network_segment_range/plugin.py:156-247): admin-only pools of VLAN IDs / VNIs, shared or reserved for a project, from which project networks get their segmentation ID; ranges in use cannot shrink below allocated IDs.

4.12 RBAC — /v2.0/rbac-policies

  • Purpose: share a network, QoS policy, security group, address scope, subnet pool or address group with another project (access_as_shared), or make a network external for some projects (access_as_external) (neutron/db/rbac_db_mixin.py:37-133).
  • Rules: only the owner (or admin) creates entries; deleting an entry that a project still uses (ports on the shared network) → 409 RbacPolicyInUse; target * = all projects; quota rbac_policy.

4.13 Quotas — /v2.0/quotas

  • GET /quotas (admin: all projects with non-default quotas), GET /quotas/{project}, PUT /quotas/{project} {"quota": {...}} (admin), DELETE /quotas/{project} (back to defaults), GET /quotas/{project}/default, GET /quotas/{project}/details (limit, used, reserved), GET /quotas/project (the caller's) (neutron/extensions/quotasv2.py:82-130, neutron/extensions/quotasv2_detail.py:54). With quota-check-limit, PUT with "check_limit": true refuses a limit below current usage. Defaults come from [quotas]/quota_<resource>.

4.14 Agents and availability zones — /v2.0/agents, /v2.0/availability_zones

  • Under OVN there are no agent rows for OVN controllers: GET /agents is answered by the OVN driver from the Southbound Chassis/Chassis_Private tables (types OVN Controller agent, OVN Controller Gateway agent, OVN Metadata agent, OVN Neutron agent) merged with the DB agents (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1715-1776); alive is computed from the nb_cfg heartbeat (05).
  • PUT /agents/{id} only changes description/admin_state_up; DELETE removes a dead chassis' agent (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1730-1776). POST /agents → 400 "Agent creation using API is not supported." (neutron/extensions/agent.py:58).
  • GET /availability_zones: OVN AZs come from gateway chassis ovn-cms-options (neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py:1640-1660).
  • The DHCP agent scheduler API (/agents/{id}/dhcp-networks, /networks/{id}/dhcp-agents) is loaded but meaningless with OVN's native DHCP: GET /networks/x/dhcp-agents answers an empty list.

4.15 Tags — /v2.0/<collection>/{id}/tags

Replace all (PUT …/tags {"tags": [...]}), add one (PUT …/tags/{tag} → 201), check one (GET …/tags/{tag} → 204 or 404), remove one or all (neutron/extensions/tagging.py:206-306). Tags are also accepted in the create body (tag-creation) and are filterable (tags, tags-any, not-tags, not-tags-any). A tag ending in .json is truncated by the Routes suffix handling (neutron/api/v2/resource.py:74-87).

4.16 Network logging — /v2.0/log/logs, /v2.0/log/loggable-resources

Admin-only. Logs accepted/dropped packets of security groups (resource_type: security_group, the only type of the OVN driver, neutron/services/logapi/drivers/ovn/driver.py:45). OVN sets log, meter (acl_log_meter) and severity on the ACLs; ovn-controller writes the entries to its own log (no Neutron agent).

4.17 Other read-only or administrative resources

  • GET /v2.0/network-ip-availabilities[/{network_id}] (admin): used and total IPs per subnet (neutron/services/network_ip_availability/plugin.py:50).
  • GET /v2.0/auto-allocated-topology/{project_id} (neutron/services/auto_allocate/db.py:132): "get me a network" — creates on first call a network, subnets from the default subnet pools and a router to the default external network, and returns the network ID; ?fields=dry-run only validates. DELETE removes it.
  • /v2.0/flavors, /v2.0/service_profiles, /v2.0/flavors/{id}/service_profiles (neutron/db/flavors_db.py:93-217): admin-defined flavours of service providers; with the OVN L3 plugin a router flavor_id can select a user-defined L3 provider (neutron/services/ovn_l3/service_providers/user_defined.py).
  • GET /v2.0/service-providers (neutron/db/servicetype_db.py:48), GET /v2.0/floatingip-pools, GET /v2.0/qos/rule-types[/{type}].
  • GET /v2.0/extensions[/{alias}]: list of the 110 loaded extensions (_rewrite/routes_stable-2026.2.json, key extensions); clients (Nova, Octavia, Horizon) decide features from this list, so a rewrite must return the same aliases.
  • GET /: version document {"versions": [{"id": "v2.0", "status": "CURRENT", "links": […]}]} (neutron/pecan_wsgi/controllers/root.py:60-69).

5. Lifecycles

Port (status)

stateDiagram-v2
  [*] --> DOWN: POST /ports (API)
  DOWN --> DOWN: binding (host_id set, provisioning block L2 added)
  DOWN --> ACTIVE: LSP up=true (OVSDB event) → provisioning complete
  ACTIVE --> DOWN: LSP up=false (OVSDB event) → block re-added
  ACTIVE --> DOWN: binding host changed (migration)
  DOWN --> ACTIVE: router port: LSP up (status set directly)
  DOWN --> [*]: DELETE /ports
  ACTIVE --> [*]: DELETE /ports

BUILD and ERROR exist in the model but the OVN driver does not use them. The maintenance task re-applies the status after a resync (05).

Floating IP (status)

stateDiagram-v2
  [*] --> DOWN: POST without port_id
  [*] --> ACTIVE: POST with port_id (OVN NAT created after commit)
  DOWN --> ACTIVE: PUT port_id (NAT created)
  ACTIVE --> DOWN: PUT port_id=null / port deleted
  ACTIVE --> [*]: DELETE
  DOWN --> [*]: DELETE

Trunk (status)

stateDiagram-v2
  [*] --> ACTIVE: POST /trunks (OVN trunk driver)
  ACTIVE --> ACTIVE: add_subports / remove_subports
  DOWN --> ACTIVE: parent port becomes ACTIVE
  ACTIVE --> [*]: DELETE

The DOWN, BUILD, DEGRADED, ERROR statuses of the trunk model are only set by the OVS agent driver, which is out of scope.

Networks, subnets, routers, security groups and QoS policies have no asynchronous status under OVN: networks and routers are ACTIVE as soon as the call returns.

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