Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/neutron/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion python/neutron-understack/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading