diff --git a/components/neutron/values.yaml b/components/neutron/values.yaml index 660898ceb..d7967d7f0 100644 --- a/components/neutron/values.yaml +++ b/components/neutron/values.yaml @@ -76,7 +76,7 @@ conf: # We are also able to see which ones are used from the OpenStack API. # - 'segments' enables the /segments API for CRUD operations on network # segments. - service_plugins: "ovn-router,trunk,network_segment_range,segments,evpn" + service_plugins: "ovn-router,trunk,network_segment_range,segments,understack_vni" # we don't want HA L3 routers. It's a Python value so we need to quote it in YAML. l3_ha: "False" # we aren't using availability zones so having calls attempt to add things to diff --git a/python/neutron-understack/neutron_understack/l3_router/understack_vni_db.py b/python/neutron-understack/neutron_understack/l3_router/understack_vni_db.py index 329ccd364..e8cad0734 100644 --- a/python/neutron-understack/neutron_understack/l3_router/understack_vni_db.py +++ b/python/neutron-understack/neutron_understack/l3_router/understack_vni_db.py @@ -8,7 +8,6 @@ MIN_VNI = getattr(n_const, "MIN_VXLAN_VNI", 1) MAX_VNI = n_const.MAX_VXLAN_VNI -DEFAULT_VNI_RANGES = [f"{MIN_VNI}:{MAX_VNI}"] class UnderstackVNIInvalidRange(n_exc.NeutronException): diff --git a/python/neutron-understack/neutron_understack/tests/test_understack_vni.py b/python/neutron-understack/neutron_understack/tests/test_understack_vni.py index e7c773952..fcfd3103d 100644 --- a/python/neutron-understack/neutron_understack/tests/test_understack_vni.py +++ b/python/neutron-understack/neutron_understack/tests/test_understack_vni.py @@ -2,6 +2,8 @@ import pytest import sqlalchemy as sa +from neutron_lib.db import api as db_api +from oslo_db import exception as db_exc from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker @@ -94,6 +96,81 @@ def test_auto_allocation_reports_exhaustion(db_context): helper.allocate_vni_for_router(db_context, "router-2", 0) +def test_router_create_retries_auto_allocation_vni_race(mocker, db_context): + helper = understack_vni_db.UnderstackVniDbHelper(vni_ranges=["100:101"]) + plugin = vrf.UnderstackVniPlugin.__new__(vrf.UnderstackVniPlugin) + plugin._vni_db = helper + mocker.patch.object( + vrf.directory, + "get_plugin", + return_value=FakeFlavorPlugin(vrf._vrf_provider_driver()), + ) + mocker.patch("oslo_db.api.time.sleep") + + original_flush = db_context.session.flush + original_rollback = db_context.session.rollback + collided = False + winner_inserted = False + + def flush_with_collision(*args, **kwargs): + nonlocal collided + if not collided: + collided = True + raise db_exc.DBDuplicateEntry(columns=["vni"], value=100) + return original_flush(*args, **kwargs) + + def rollback_and_insert_winner(): + nonlocal winner_inserted + original_rollback() + if collided and not winner_inserted: + db_context.session.execute( + sa.text( + """ + INSERT INTO understack_router_vni_allocations + (vni, router_id) + VALUES (:vni, :router_id) + """ + ), + {"vni": 100, "router_id": "router-racer"}, + ) + db_context.session.commit() + winner_inserted = True + + mocker.patch.object(db_context.session, "flush", side_effect=flush_with_collision) + mocker.patch.object( + db_context.session, "rollback", side_effect=rollback_and_insert_winner + ) + + @db_api.retry_if_session_inactive() + def create_router(context, plugin): + router = { + "id": "router-1", + "flavor_id": "flavor-1", + apidef.EVPN_VNI: 0, + } + payload = SimpleNamespace( + context=context, + resource_id="router-1", + latest_state=router, + ) + + plugin._process_router_create(None, None, None, payload) + return payload.latest_state[apidef.EVPN_VNI] + + assert create_router(db_context, plugin) == 101 + + rows = db_context.session.execute( + sa.text( + """ + SELECT vni, router_id + FROM understack_router_vni_allocations + ORDER BY vni + """ + ) + ).fetchall() + assert rows == [(100, "router-racer"), (101, "router-1")] + + def test_vrf_router_create_allocates_vni(mocker): mocker.patch.object( vrf.directory, diff --git a/python/neutron-understack/uv.lock b/python/neutron-understack/uv.lock index 556085eef..d59082165 100644 --- a/python/neutron-understack/uv.lock +++ b/python/neutron-understack/uv.lock @@ -792,7 +792,7 @@ test = [ requires-dist = [ { name = "keystoneauth1", specifier = ">=3.14.0" }, { name = "neutron", specifier = ">=27,<29" }, - { name = "neutron-lib", specifier = ">=3,<4" }, + { name = "neutron-lib", specifier = ">=3,<5" }, { name = "openstacksdk", specifier = ">=4.9.0" }, { name = "oslo-config", specifier = ">=9.7.1" }, { name = "oslo-log", specifier = ">=7.1.0" },