Skip to content

Cherry pick stable/2026-1.m3 commits - #123

Open
sven-rosenzweig wants to merge 77 commits into
stable/2026.1-m3from
toanju/2026.1-m3
Open

sven-rosenzweig wants to merge 77 commits into
stable/2026.1-m3from
toanju/2026.1-m3

Conversation

@sven-rosenzweig

Copy link
Copy Markdown

No description provided.

ralonsoh and others added 30 commits August 12, 2026 11:53
In the ``neutron-ovn-db-sync-util``, the ``AgentCache`` singleton is
created but never populated because the OVSDB monitor events that fill
it in the Neutron API (``ChassisAgentWriteEvent``,
``ChassisOVNAgentWriteEvent``) are not registered; the db-sync
``OVNMechanismDriver`` overrides ``post_fork_initialize`` as a no-op.

This results in an empty ``AgentCache`` when ``OvnSbSynchronizer``
methods like ``_get_hosts_mapped`` query it, making the sync unable
to correctly determine which hosts have OVN controller agents.

This patch adds ``AgentCache.populate()`` which reads all
``Chassis_Private`` rows from the SB database and creates the
corresponding agent entries, replicating the logic of the monitor
events. Call it in ``OvnSbSynchronizer.__init__`` so any sync method
sees a fully populated cache.

Conflicts:
  neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py

Closes-Bug: #2150109
Related-Bug: #2136864
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: If7bec416a9c9f5ed6cd8a04775606092dde2390a
(cherry picked from commit 606ef2a)
Neutron's Designate external DNS driver does not pass
edit_managed=True when constructing the admin designateclient.
When a floating IP has associated managed DNS records in
Designate, deleting the floating IP fails with HTTP 500 because
Designate rejects the deletion with "Managed records may not be
deleted".

Pass edit_managed=True only to the admin_client, which has admin
credentials and passes Designate's edit_managed_records policy
check. The regular user client must not set this flag because
Designate requires SYSTEM_ADMIN role for it. For forward DNS
deletion of managed records, fall back to admin_client when the
user client gets a BadRequest from Designate.

Closes-Bug: #2149807
Assisted-By: Claude Code 4.6 Opus
Change-Id: Iedf0f26708560fe063a71925d5d39542ca181154
Signed-off-by: Omer <oschwart@redhat.com>
(cherry picked from commit 44d061a)
…sts"

This reverts commit 1aa71c3.

Reason for revert:
Removal of eventlet has been completed.

Change-Id: I67e0f2cefff0afc53ec1e1a397331d3f74008398
Signed-off-by: Takashi Kajinami <kajinamit@oss.nttdata.com>
(cherry picked from commit 2e778ab)
The TaggingController.create() and update() methods enforce policy
action names using the plural collection key (e.g. create_networks:tags)
instead of the singular member name (e.g. create_network:tags). Since
the registered policy rules use the singular form, the unmatched plural
names fall through to oslo.policy's default rule, allowing project
readers to mutate tags on same-project resources.

Fix the delete_floatingips:tags policy rule name (should be singular
delete_floatingip:tags) and add a unit test that validates
_get_policy_action produces the correct singular form for all supported
resources and actions, and that each generated name matches an actually
registered policy rule.

Closes-Bug: #2150132
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I783510565e4fc4191b5494eb9a6dc0bdd3ace3fc
(cherry picked from commit 7401244)
The ``OVNL3RouterPlugin`` class registers a set of OVN events in the
``_post_fork_initialize`` method. The event processing should be done
only by Neutron API workers (aka, ``WorkerService`` instances).

Conflicts:
  neutron/services/ovn_l3/plugin.py

Closes-Bug: #2152562
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I206cd34b1d5559d52c8e841006cf4d6e1eb361ef
(cherry picked from commit 131d985)
Verify the port-forwarding floating IP id matches the
child floating IP id so we satisfy the policy requirements
for GET and PUT operations.

Closes-bug: #2150121

Change-Id: Ie68e57da6222965e79d015a5568190d8bc29b9e8
Signed-off-by: Brian Haley <haleyb.dev@gmail.com>
Assisted-by: Claude Sonnet 4.6
The _is_vlan_project_network method in DNSExtensionDriverML2 was only
checking the static network_vlan_ranges configuration from ml2_conf.ini,
ignoring the dynamic ranges provided by the network-segment-range
service plugin. This caused a KeyError when creating subnets or VMs on
VLAN networks with segment ranges managed via the API.

This fix updates the method to use
VlanTypeDriver.get_network_segment_ranges(), which properly handles both
static configuration and DB-based dynamic ranges when the
network-segment-range plugin is enabled.

Closes-Bug: #2140291
Signed-off-by: Kyuyeong Lee <kyu0.lee@samsung.com>
Change-Id: I8f3a2b1c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a
(cherry picked from commit cd8f88e)
Several default port policies that require network ownership incorrectly
included PROJECT_MANAGER. That rule checks the port project_id, not
network ownership, so any project manager could perform those actions
on shared/RBAC networks where they do not own the network.

Remove PROJECT_MANAGER from the affected create/update port policies
and rely on NET_OWNER_MEMBER or ADMIN_OR_NET_OWNER_MEMBER instead.
Project managers who own the network remain authorized through the
default Keystone role implication chain (manager implies member).

Closes-Bug: #2152115
Assisted-By: Claude Composer 2.5
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I4e258d28cdf72adcc13fc9d03749256c65881c45
(cherry picked from commit d0f61db)
The ``OVNL3RouterPlugin`` registers OVN OVSDB monitor events during
``_post_fork_initialize``. That callback is also invoked from
neutron-periodic-workers and other non-API processes, where the OVN IDL
connections are not initialized. Checking OVN readiness before filtering
by worker type raised ``MechanismDriverOVNNotReady`` and blocked worker
startup.

Return early when the trigger is not a Neutron API ``WorkerService``, and
only then validate OVN readiness and register monitor events.

Conflicts:
  neutron/services/ovn_l3/plugin.py

Related-Bug: #2154192
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I9e43fdb06ce7868e5fa699657d8da9a4aa4d02fb
(cherry picked from commit b6d260e)
The ``_is_tunnel_project_network`` method in ``DNSExtensionDriverML2``
was only checking the static tunnel ranges from ml2_conf.ini, ignoring
the dynamic ranges provided by the ``network-segment-range service``
plugin. When segment ranges are managed via the API, external DNS
eligibility for tenant tunnel networks could be wrong.

Update the method to use the ML2 tunnel type driver's
``get_network_segment_ranges()``, which handles both static
configuration and DB-based ranges when the network-segment-range plugin
is enabled.

Closes-Bug: #2154266
Related-Bug: #2140291
Assisted-By: Cursor Composer 2.5
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I5d96efa626d18b7589cf2563fdac3a8d399a69a8
(cherry picked from commit 6913303)
The openvswitch firewall driver has got OF rules to mark some
connections as invalid using mark CT_MARK_INVALID in conntrack.
Later packets which belongs to such invalid connection are dropped by
different OF rules in br-int.

The problem is that such conntrack entries were cleaned for each of the
ports for which filters were updated, even with deferred apply on. In
such case new OpenFlow rules are applied in deferred way after all ports
are processed by the agent.
That could lead to the situation where conntrack entries to mark some
kind of connection as invalid was deleted by the fw driver, but new OF
rules were not yet applied in the br-int. If during that time the same
packets were still flowing to/from the port, new conntrack entry to mark
such packets would be added back and that would not be cleaned after OF
rules were actually applied on the `br-int` bridge.

This patch fixes that issue be deferring cleanup of the conntrack
entries for all ports to be done after OF rules are actually updated in
`br-int`.

Assisted-by: claude-opus-4.6

Closes-bug: #2154561

Change-Id: I8655da7f7a0cf033ce4a7145f5345195881238e5
Signed-off-by: Slawek Kaplonski <skaplons@redhat.com>
(cherry picked from commit 8fde7d4)
Ports with mac=['unknown'] (e.g. network:dhcp ports without port
security) cause the metadata agent to crash with a ValueError when
iterating chassis ports during datapath provisioning. Add an explicit
check for mac=['unknown'] alongside the existing empty-MAC guard
to skip these ports gracefully.

Closes-Bug: #2150434

Assisted-By: Claude Opus 4.6
Change-Id: I966c7a22eca589770793a0ef30c3bbc591c41e41
Signed-off-by: Eduardo Olivares <eolivare@redhat.com>
(cherry picked from commit fecac2c)
Singleton conntrack helper API operations (GET/PUT/DELETE on
/routers/{router_id}/conntrack_helpers/{id}) authorized the request
against the URL ``router_id`` but loaded the helper by child ID only.
A project member with access to one router could therefore operate on
another tenant's helper by reusing its UUID in the path.

Validate that the loaded helper's ``router_id`` matches the URL
``router_id`` before returning, updating, or deleting it, following
the same pattern used for floating IP port forwarding sub-resources.

Closes-Bug: #2152109
Assisted-By: Claude Opus 4.6
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: Icb4a7d2e8f1c35609d8a4b7e2f6c9d0a1b3e5f78
(cherry picked from commit bc8a554)
The operation for these actions is PUT, not POST.

Closes-Bug: #2156054
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: Iec4b8ddf3717ddc781acfb46ada81839f853bdea
(cherry picked from commit 8ccf3f9)
When a VIF port is picked up by the OVS agent ``rpc_loop`` before OVS
has assigned it a valid ofport (the underlying TAP device may not yet
exist), ``port_alive()`` and ``port_dead()`` pass the invalid value
(``[]`` or ``-1``) through to ``uninstall_flows()`` /
``drop_port()``.  The resulting OpenFlow FlowMod with ``in_port=None``
causes os-ken's ``send_msg`` to hang until the ``of_request_timeout``
(300 s) fires, blocking the ``rpc_loop`` for the entire duration and
preventing all subsequent port processing.

Guard ``port_alive()``, ``port_dead()`` and ``treat_vif_port()`` so
they return early when the ofport is unassigned or invalid.
``treat_vif_port()`` returns False so the port is not marked as bound;
the OVSDB monitor will detect the ofport change on a later iteration
and re-trigger processing.

Closes-Bug: #2155883
Assisted-By: Claude Opus 4.6
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: Ic137f8a2862794c3c7ac670e643ca532873e474b
(cherry picked from commit 8a4a4b1)
When a port is inserted into OVS, the kernel may transiently assign
``ofport=-1`` (``INVALID_OFPORT``) before settling on a valid value.
Previously, ``_process_port`` only deferred ports with
``UNASSIGNED_OFPORT`` (``[]``), so a port arriving with ``ofport=-1``
was immediately added to the processing pipeline. Patch [1] then
correctly skipped OF operations in ``treat_vif_port`` for such ports
but never re-queued them, which meant the ``network-vif-plugged``
event was never sent to Nova, causing a 300 s VIF-plug timeout.

Extend the ``_process_port`` check to also defer ``INVALID_OFPORT``
ports via the existing ``ports_not_ready_yet`` mechanism. On the next
``rpc_loop`` iteration the port attributes are re-read from OVS; if
the ofport is now valid the port is processed normally, otherwise it
is deferred again.

[1]https://review.opendev.org/c/openstack/neutron/+/992423

Closes-Bug: #2155883
Assisted-By: Claude Opus 4.6
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I9fbc8f75f8084461901a004dfcb07e42f76db62b
(cherry picked from commit 7ebcce0)
When a service plugin package (e.g. neutron-fwaas, neutron-vpnaas,
networking-sfc) is installed but its service plugin is not configured
in ``service_plugins``, the quota details API endpoint
(GET /v2.0/quotas/{project_id}/details) returns a 500 Server Error.

The installed package registers quota resources (e.g. firewall_group,
firewall_policy, firewall_rule) at import time via
``resource_helper.build_resource_info(register_quota=True)``. When the
quota details endpoint iterates over all registered resources to
count usage, it calls ``_count_resource()`` which looks for a plugin
that provides ``get_<collection>_count`` or ``get_<collection>``.
Since the service plugin is not loaded, no plugin supports counting
those resources, and a ``NotImplementedError`` is raised.

Catch the ``NotImplementedError`` in
``DbQuotaDriver.get_detailed_project_quotas()`` and skip the resource
instead of letting the exception propagate as a 500 error. Also
guard the project-specific limit update loop against skipped
resources.

Closes-Bug: #2155846
Assisted-By: Claude Opus 4.6
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I923e90279edf3de3fa85c83fd46e1b5dec0468de
With tunneling disabled (enable_tunneling=False), there is no br-tun and
self.tun_br is None. _restore_local_vlan_map(), called from __init__(),
calls self.tun_br.get_flood_to_tun_ofports() unconditionally for every
integration-bridge port that carries a net_uuid, so the agent crashes:

  AttributeError: 'NoneType' object has no attribute
  'get_flood_to_tun_ofports'

The branch is only reached once the agent has populated net_uuid on the
ports, so the crash hits on agent restart and recurs on every restart,
preventing the agent from restoring dataplane flows until manual recovery.

Skip the flood-port restore when tunneling is disabled, leaving
tun_ofports as an empty set as for ports with no tunnel flood entries.

Closes-Bug: #2156566
Change-Id: I4741480f8a7fbce51f521579e78d5687773c29b8
Signed-off-by: Seyeong Kim <seyeong.kim@canonical.com>
(cherry picked from commit 92181b3)
Increased ``allowed_network_downtime`` to 7.0 in the
``neutron-ovs-tempest-slow`` CI job. That will improve the stability of
test ``test_server_connectivity_live_migration``.

Closes-Bug: #2157882
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: Idad087b2a0abd76d1297882011bc7deb6924c8ca
(cherry picked from commit 4aabb3e)
Python's `requests` lib is always asking for 'gzip,deflate'
encoding by itself, and decoding those by itself too,
but does not remove the content-encoding header from response,
so don't rely on this header alone to decide if content should
be decoded or not.
Also, content-length and transfer-encoding are left untouched,
even if they correspond to originally gzip-ed content.
This all interferes when user data is itself gzip-ed and
there's a proper web server like apache2 that itself generates
proper gzip-ed response.

Clean-up erroneous headers, and let webob recalculate them as needed.

Related-Bug: #2120723
Closes-Bug: #2156587
Change-Id: Ia5d0cbdc5c715462438e38135e441ce9f5ea98c1
Signed-off-by: Pavlo Shchelokovskyy <shchelokovskyy@gmail.com>
(cherry picked from commit d70bdc9)
Seeing random failures for MacAddressInUse in
unit tests, extending the range to lower such
conflicts.

Closes-Bug: #2158374
Signed-off-by: Yatin Karel <ykarel@redhat.com>
Change-Id: I24f429adfcdbf7411663459998c36479aef92350
(cherry picked from commit 2683e23)
When a subnet is deleted before its segment, ``_delete_nova_inventory``
is called twice for the same resource provider. The second call now
catches ``PlacementResourceProviderNotFound`` at DEBUG level instead of
logging a misleading INFO message.

Closes-Bug: #2158118
Assisted-By: Claude Opus 4.6
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: Icf643cc9dff81ce65e26fe0bc704942d4772400d
(cherry picked from commit 2856ae5)
When multiple Neutron API workers concurrently create or update
external ports on the same network, they race to create the same
``HA_Chassis_Group``. The ``ha_chassis_group_add`` command uses
``may_exist=True``, but this check relies on the local IDL cache
which can be stale, causing duplicate inserts and OVSDB constraint
violations.

The previous code called ``sync_ha_chassis_group_network()`` passing
the parent transaction. Because ovsdbapp merges nested transactions
on the same thread (``nested=True`` by default), the HA Chassis
Group creation ended up in the same transaction as the port
creation, making it impossible to retry independently.

Fix this by:
1. Passing ``txn=None`` to ``sync_ha_chassis_group_network()`` in
   ``create_port()`` and ``update_port()`` so that ``ovn_context``
   manages its own transaction.
2. Using ``nested=False`` in ``_run_with_retry()`` to force a truly
   independent OVSDB transaction that is not merged with the outer
   port transaction.
3. Retrying on ``RuntimeError`` (constraint violations) with
   ``tenacity``, giving the IDL cache time to catch up between
   attempts.

Closes-Bug: #2158358
Assisted-By: Claude Opus 4.6
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I9426e6bc508556a2bfe82e5c384ec07701fd18fb
(cherry picked from commit 70394b3)
We switched these jobs to python3.12 long back but recently
it was noticed the job was still running with python3.9 during
tox venv prep. The job failed after more-itertools update[1] as
the newer version is not compatible with python3.9.
This patch switches venv prep to also run with python3.12.

[1] https://review.opendev.org/c/openstack/requirements/+/983340

Depends-On: https://review.opendev.org/c/zuul/zuul-jobs/+/984404
Signed-off-by: Yatin Karel <ykarel@redhat.com>
Change-Id: Iecdf6b45c070711a3448e7bade3577c340c291cc
(cherry picked from commit a5420f5)
When searching for an LRP to find its MAC when implementing
openflow rules for a BGP bridge, we need to consider only local chassis.

Closes-Bug: #2146077

Change-Id: If070b7c9fcf54db14b93f422f5cbd9a08a4ea696
Signed-off-by: Jakub Libosvar <libosvar@redhat.com>
(cherry picked from commit b197e46)
RPC workers do not need to process OVN database notification events
such as port status updates or chassis agent events. These events are
only relevant to the API workers that handle the OVSDB monitoring
loop.

When an ``RpcWorker`` creates its OVN IDL connections via
``from_worker()``, use ``BaseOvnIdl`` and ``BaseOvnSbIdl`` instead of
the monitoring IDL classes (``OvnNbIdl``, ``OvnSbIdl``). This avoids
registering unnecessary event watchers in RPC workers, reducing
overhead and preventing potential event handling errors.

Closes-Bug: #2158611
Assisted-By: Claude Opus 4.6
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I99e4f116678b7da35ba8ab19059af342ca4858e9
(cherry picked from commit e6a0f5a)
Migration plugins registered under the ``neutron.ovn.db_migration``
entry point are plain callables (functions), not subclasses of
``BaseOvnDbSynchronizer``. The shared ``_load_drivers`` helper applies
an ``issubclass`` guard that is only valid for class-based sync plugins,
causing a ``TypeError`` when loading function-based migration plugins and
ultimately a ``NoMatches`` exception that aborts the OVS-to-OVN database
migration step.

Fix this by giving ``load_db_migration_drivers`` its own
``EnabledExtensionManager`` construction that skips the ``issubclass``
check and simply filters by driver name.

Closes-Bug: #2160210

Assisted-By: Claude Sonnet 4.6
Change-Id: I5c9b689558a9553ff6968868b43bf3d9239c92ad
Signed-off-by: Michal Nasiadka <mnasiadka@gmail.com>
(cherry picked from commit c615e18)
Out of convenience for running the tests locally, we should by default
pick our own "upper-constraints.txt" and not upstream's.
sebageek and others added 30 commits August 14, 2026 09:45
Upstream Neutron has the assumption that all subnets of a network are in
the same subnet pool (and therefore same address scope), while our brand
of Neutron requires all subnets in a network to be either in the same
address scope or in no address scope at all.

Neutron adds the address scope to each port in the l3 router sync data,
but looks at all subnets present in the network. We now change this
behavior to only add the address scope when the router port is actually
in a subnet with this address scope. With this DAPNets (Directly
Accessible Private Networks) and non-DAPNets can live in peace together.

add missing key device_owner in test test__populate_ports_for_subnets_mixed_address_scopes

For populating subnets for ports, it is checked whether the port is owned by a router. Runing the test with neutron yoga requires the key device_owner otherwise the test will fail with a KeyError.
Upgrading from openstack version Caracal to Gazpacho, the location,
where the python flag is being honored, changes. This is due to the
upstream eventlet removal.

rewrite import due to pylinter error - C0415: Import outside toplevel
… by SIGWINCH

Upgrading openstack from version ussuri to yoga,  the _get_application_ was removed. In order to enable guru mediation reporting, triggered by SIGWINCH, the setup moves to _eventlet_api_server.
The removal goes back to [upstream commit](609508b). With [commit](a7c4426) guru mediation reporting was introduced in ussuri.
We want to get a better impression of what our users do with our
metadata service and for this we want to improve the logging. The
current logging logs part of the request (part by eventlet.wsgi, part by
Neutron itself) or the complete ports request ("which ports do belong to
the requester) as a multiline string representation of the request
object.

To make debugging this easier for us we now log an extra single line for
each request, containing many informations, as the current OpenStack
network, client ip, user-agent, request path etc., which can then be
easily consumed by some log parser (fluentd + grok in our case).
When sending an OPTIONS request to the metadata agent this request gets
proxied to Nova, which then returns an empty HTTP response with
content-length: 0 and no content-type key. This results in a KeyError,
when we try to access this attribute. Using get() solves this and
results in sending no content-type header to the client who requested
this.

In our environment this can be reproduced with:
curl http://169.254.169.254/lol -X OPTIONS
When the user of our metadata service provides an X-Forwarded-For header
in the request it will be forwarded to the neutron metadata service by
haproxy, alongside with haproxy's own X-Forwarded-For header. On Neutron
side this is then processed by eventlet.wsgi, which merges all values of
headers that appear multiple times (separated by ','). The result of the
merged addresses is then used by neutron in
_get_instance_and_tenant_id(), where it is converted to an
netaddr.IPAddress. As this is not a valid value, the AddrFormatError
exception is raised.

We do not want this exception to be raised, but even before that we
don't want the user of a metadata service to be able to manipulate the
X-Forwarded-For header in any way. Therefore we are now deleting this
header in the haproxy frontend.
The OwnerCheck makes it possible to define rules in the policy that
allow the owner of a network to see all ports in the network - even if
they belong to another project that has the network shared into it. To
be able to do this policy check, OwnerCheck queries out the related
Network object e.g. when listing ports and caches the result for 5s by
default.

Since the project_id/tenant_id of an object - or at least of a Network -
does not change during its lifetime, expiring the cache this often will
lead to more load on the DB without much benefit - we only get at small
amount of RAM back.

Therefore, we allow administrators to increase the expiration time with
the config option `owner_check_cache_expiration_time`. Administrators
should check if their policy only uses static attributes and if they do
not have a huge amount of those objects when increasing this setting.
When multiple subnets are configured in the same network, the DHCP agent
will choose the first suitable subnet with a gateway ip set as default
gateway, where the subnets are in a generally stable, but arbitrary
order. In some cases only some of the subnets in a network actually have
a router in it, meaning a DHCP agent might loose Internet access (used
for example for DNS).

Now we sort the subnets before selecting a default gateway. Subnets are
sorted first by if their gateway ip is present on a port in the network
and then by their created_at time, so we always have a stable order.
Also, using the first created subnet seems like a good decision, as it
will probably be the one with the least fluctuations. We won't filter
for a router port, as the user could also provide a custom router via a
VM in their network. In case no subnet is present on a port we will
still use one of them as default route, just to preserve the old
behavior.
External subnets might have an external gateway IP outside of the
subnets allocation pool. This IP is not allocated by the OpenStack IPAM,
but is used as default gateway. A user might create a floating IP for
this specific IP, which results in double usage of the IP. To prevent
this we disallow allocating gateway IPs of external subnets as floating
IPs.

This commit only checks that on FIP creation the IP is not a gateway IP
of any subnet it is allocated inside. We do not check this condition if
the subnet's gateway IP is modified.

This bug has been discussed with upstream, but was found to be invalid
by them, because in the default configuration only admins can create a
FIP with a predetermined IP address. For us this is different, but
additionally I think we also shouldn't allow admins to do this, as in
our infra this brings the network down.

Discussion with upstream can be found here:
https://bugs.launchpad.net/neutron/+bug/1959699
1. Motivation and goals
The motivation is to assist the upstream DNS resolvers
in identifying the clients using the neutron dnsmasq resolvers.

The actual goal is monitoring of the DNS traffic for suspicious
activity and uncovering compromised systems.

2. Implementation
This is done by passing each client's IP address and network id
to the upstream resolvers by adding that information as eDNS
payload to the DNS query going out.

If this boolean option is toggled then the following two command
line options will be appended to dnsmasq:
--add-cpe-id=<network_id>
--umbrella

The former will add the network id to eDNS option 65074.
The latter will add the client IP to eDNS option 20292.

3. Caveat
The `--umbrella` option was introduced in dnsmasq 2.86.
A dnsmasq version check is in place to make sure the option does
not get activated if the currently installed version is older
than that.
In this case a warning will be logged and the fingerprinting will
be reduced to the network id only.
When we have a network with stateful DHCPv6 we still need router
advertisements to tell the hosts the prefix length of the current
network (else all hosts will just have /128 routes). In cases where a
network does not have a router (or don't use an l3 plugin with ra
support) we do not have something in the network that sends out ras
(radvd itself is launched on the l3 agent with Neutron's default
implementation).

Therefore we now let dnsmasq take over this task. With --enable-ra we
enable dnsmasq's ra implementation. We use --ra-param=$iface,0,0 to
deactivate that dnsmasq is advertising itself as default gateway. Params
are (interface, use default ra-interval, router lifetime).
Currently it is possible to delete a active binding via the port
binding API extension. Deleting a active port binding leaves the port
in a state of not being editable anymore.
Each attempt editing the port results in a PortNotFound error.

During a nova live migration a port entered the state described above.
For some reasons nova was not able to set the binding of the new target
host to active, thus the live migration rollback process started.
During the rollback, the newly created binding got deleted (apparently
already set to active) leaving the port with no active binding.
Thus setting old binding information was not possible anymore.

Fixing such a zombie requires to set the status of a binding to active
(either via db directly or port binding extension)
It can happen, that we clean up a network segment while there's already
another port binding in progress. That port binding can then find the
segment in the first steps, but is later unable to commit the binding to
DB as a foreign key constraint fails.

We catch the foreign key constraint failing (DBReferenceError) and raise
a retryable RetryRequest instance instead, which the retry_db_errors()
decorator around _bind_port_if_needed() should handle.
Using the 'regular' /etc/resolv.conf inside the network namespaces
prevents proper DNS resolution, as these nameservers usually are
unreachable from within the namespaces.

This change enables creation of a resolv.conf in each namespace.

The new setting netns_resolvconf enables this feature, by default it is
disabled.

Default nameservers are 127.0.0.1 and ::1 if IPv6 is enabled, for search
domains the networks dns_domain is used if set.

These defaults can be overwritten via settings file.

To skip setting nameservers, search domains or options in the generated
file and not use the dynamic defaults, one can add the settings key in
the configuration file but leave out any value, e.g.:

  netns_resolvconf = yes
  netns_resolvconf_search =
  netns_resolvconf_options =
  netns_resolvconf_nameservers =

This would create an empty resolv.conf.
This adds support for configuring custom upstream DNS servers and to
disable the edns logging for neutron networks, based on the name of
the OpenStack domain or, for development, the project id.

By changing the upstream DNS servers used by dnsmasq, local hostnames
are still resolvable, but queries for external names will be sent to
the custom set of resolvers from the configuration. This is in contrast
to the existing dns server setting for networks, which changes the
nameservers announced via DHCP.

The configuration is done in neutron server.

When a dhcp-agent first configures a network, it does an rpc
request to retrieve all necessary configuration data. On the server
side we add the custom DNS servers and a flag for the edns logging to
the returned data. The agent code then uses this data to configure the
dnsmasq instances accordingly. When that data is absent the local
configuration is used as default, like before.

To decide if the network is to be configured with a custom set of
nameservers, the neutron rpc server will query keystone for the domain
of the network and match the domain name against its configuration file.

To help development and debugging, support to directly match the id
of a project was also added.
After updating to Caracal and using openstacksdk 3.0.0 the sdk
refused initializing the identity client correctly to talk to
keystone.

This apparently happens when a full config is given to the Connection,
and should not have worked before.

Also now calling identity directly without indirection, which also
should be more efficient because it uses a direct get for the id.
To customize behavior in Nova’s server creation process, an effective
approach is to send custom events that Nova can wait on.

In our case, we want to pause the server creation until the ML2 port binding
 (handled by the NSX-T agent) is completed.
This helps avoid a race condition.
Modify the general default for port security in the rpc server.

The previous commit changed only the defaults for the linuxbridge agent.
However, the rpc server also needs to be updated to ensure that the
default for port security is consistent.

fixes: 34819de
This allows to disable the configuration of the anti-spoofing rules
using a feature flag. This is currently required since we do not yet
have port security enabled and using the Linux bridge driver will still
configure the anti-spoofing rules by default.

For the spoofing case this configures DHCP to be allowed by default like
in the anti-spoofing case.

In addition, this adds default allow rules towards the metadata server
for the anti spoofing case as well.
When we migrate a vm between two kvm hypervisors that are using the
linuxbridge driver, we need to make sure that the network is present
when the vm comes up. This is important so the VMs/HVs rARP packets are
send out and the network fabric behind the HV knows where to find the VM
after a migration.

With linuxbridge and Nova it is not clear who actually creates the
bridge and adds the tap interface to it. Generally it seems to be that
Nova does this first and Neutron only discovers this in one of its
agent's loops. But here we have a problem: The bond is only created and
put into the bridge once the port binding is active. It seems like OVS
had a similar problem, which was patched here[0]. The code looks very
similar to our problematic piece of code, so we adapt the idea: The port
is not only bound if the host of the port matches, but also if the
migrating_to field from the binding profile matches the host. This helps
us getting the bond faster into the bridge. Additionally, we can - via
config - increase the polling timeout of the linuxbridge agent to make
sure we do our deed as fast as possible.

Our patch looks a bit different from [0]: We don't fetch the active
bindings, as all the information we need is already present in the port
dict that we have. In some cases (at least in the tests) the profile
might be a string and not already a json-decoded data structure, so we
need to do the decoding ourselves in some cases (same as [0]).

As a midterm solution it would probably make sense to get this done in a
quicker, more reactive fashion (i.e. not wait for a loop, but directly
react on the event). Another option would be to migrate away from
linuxbridge, but one step after the other.

Note that this is only happening on "cold" hosts, which have never seen
the OpenStack network so far. Linuxbridge does not seem to clean up the
bridge or bond interface (which might also be something which we need to
look into in the future).

[0] openstack@f8a22c7
Admin-shutting an agent leads to the agent being completely vacated.
However we have cases in which we want an agent to stay online, but
prevent any new workloads to be scheduled on it. This can be a capacity
consideration or because it is meant to be decomissioned.

In addition this allows us in the dev environment to replace agents with
experimental versions, without causing disruption. It is still possible
to manually schedule a network on this dhcp-agent for testing.

A similar feature was implemented for asr1k-neutron-l3 here:

sapcc/asr1k-neutron-l3@425cc81

Note: We are not filtering in `AutoScheduler.auto_schedule_networks`.
This method gets called with the host as parameter, so the scheduling
decision is already made.
…g and DNS

We already allow configuration of custom upstream DNS servers for
dnsmasq, based on the OpenStack domain or -- for testing -- based on
the project-id.

We currently can configure only two sets of DNS upstream servers:
One for SAP OpenStack domains and one for external OpenStack domains.
For external domains we also always disable the edns and umbrella logging
options in dnsmasq.

Now the requirement arose to configure DNS servers individually on a
per-domain basis and also to be able to enable logging individually.

The requirements now are:

- use default DNS servers and enable logging when no config is set
- enable logging and custom upstreams for specific domains individually
- allow fallback for domains matching a certain prefix to use a
  different default set of resolvers and setting for logging.

This is achieved via a new configuration setting 'config_file' in the
customdns section of neutron.conf that points to a yaml file.

This file allows setting logging and upstream dns settings as follows:

```yaml
  matches:

    -  domain_name_prefixes:
        - external-abc
       project_ids:
        - 5dc81c6355ff478188f8fda11a971c41
       upstream_dns_servers:
        - 192.0.2.10
        - 192.0.2.20
        ednslogging: True
    -  domain_name_prefixes:
        - external-abcd
        - external-
       upstream_dns_servers:
        - 192.0.2.30
        - 192.0.2.40
        ednslogging: False
```

related:
f39dcce
f186f86
c4bdb29
When importing dhcp_rpc without first registering the DNSSETTINGS_OPTS
config options, the import will fail with a
oslo_config.cfg.NoSuchOptError exception, as we were instantiating
CustomNetworkConfigurator() as a class variable.

This results in the code doing the import (in our case the nsx-t agent
from a custom driver) crashing on startup.

To fix this, we now create this object only once when creating the first
instance of DhcpRpcCallback.
Enable container readiness checks by writing the DHCP agent's status
to a local JSON file. The status includes sync state, timestamp, and
network namespace information, allowing containers to determine readiness
without querying the Neutron database.

Previously, we relied on the agent's status in the Neutron DB for
readiness checks. This caused database overload during mass restarts,
especially under high customer request volumes, as readiness checks
added unnecessary pressure to the DB.

The status file tracks whether all active networks have corresponding
network namespaces, reporting "synced", "unsynced", or "error" states.
This eliminates external dependencies and reduces database load during
container lifecycle events.
The customdns configuration is now done using a yaml config file pointed
to in the neutron config file.

This commit removes the now unused options from the configuration,
removes the obsolete code and the tests and cleans up the code.

With this change the yaml configuration file is now mandatory when the
customdns feature is enabled.

We also renamed some of the tests to better describe their purpose and
renamed the "doubled" tests for the functionality back to the original
names by removing the _yaml postfix.

Additional tests ensure the configuration file is required when the
customdns feature is enabled and exceptions are handled accordingly.
Problem:
The sync_loop runs only when triggered by specific events (e.g.,
network create/delete operations). This means the agent status check
does not run regularly as previously assumed.

In rapid create/delete scenarios, the status can become unsynced:
the network exists in the database but the network namespace is never
created. From Neutron's perspective everything appears fine, but the
agent status shows unsynced and never updates.

Solution:
Use the cache as the source of truth for agent readiness checks. The
cache is kept up-to-date by the agent on every operation (create/
delete network/port/subnet). Run regular checks against the cache to
determine if all networks are synced and mark the agent ready
accordingly.

For detecting when the cache is out of sync, we rely on other
measures such as the custom exporter for network namespaces and
MariaDB exports.
This writes the list of synced networks into
a file, that can be shared to a k8s sidecar container.
Moved the file to /run/dhcp-agent to enable shared
mount between containers.

This enables a prometheus exporter, running in a sidecar,
to export the synced networks in an agent.

Because a restart of the dhcp-agent container will remove
all network namespaces from the kernel but not the files
in /run/netns, sharing this as mount is not an option.

This will prevent the dhcp-agent from starting up again,
because the namespace files are in place, but no namespaces.
Similar to the recently introduced feature of setting custom DNS
upstream servers for dnsmasq based on the OpenStack Domain or
project of a network, this adds support for configuring the NTP servers
distributed via DHCP option 42 to the clients.

Note: To take effect, the option must not be set in the dnsmasq config
file, as commandline options are overwritten when they are only allowed
to be given once.
We extended the DHCP agent to write the synced networks to a status
file.

With the new release Neutron moved away from Eventlet, introducing
changes, breaking our custom agent tests.

Explicitly call init_host (which internally also calls sync_state)
to initialise the network cache.

In the safe_get_network_info method we fetch fresh network
information. As this would return a plain Mock object, not containing
the network_id and subnet information, the test would fail without
mocking it.

With the drop of eventlet, the network cache spawns a thread
fetching updates. We need to stop this thread to stop the tests
from hanging.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.