Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ def set_binding(
self, segment_id: str, vif_type: str, vif_details: dict, status: str
) -> None: ...

def continue_binding(
self, segment_id: str, next_segments_to_bind: list
) -> None: ...

def allocate_dynamic_segment(self, segment: dict) -> dict: ...

def release_dynamic_segment(self, segment_id: str) -> dict: ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def bind_port(self, context: PortContext) -> None:
LOG.debug("Refusing to bind due to unsupported vnic_type: %s", vnic_type)
return

for segment in context.network.network_segments:
for segment in context.segments_to_bind:
if segment[api.NETWORK_TYPE] == p_const.TYPE_VXLAN:
self._bind_port_segment(context, segment)
return
Expand Down Expand Up @@ -335,12 +335,10 @@ def _bind_port_segment(self, context: PortContext, segment):
if trunk_details:
self.trunk_driver.configure_trunk(trunk_details, port_id)

LOG.debug("set_binding for segment: %s", segment)
context.set_binding(
segment_id=dynamic_segment[api.ID],
vif_type=portbindings.VIF_TYPE_OTHER,
vif_details={},
status=p_const.PORT_STATUS_ACTIVE,
LOG.debug("continue_binding for segment: %s", segment)
context.continue_binding(
segment_id=segment[api.ID],
next_segments_to_bind=[dynamic_segment],
)

def invoke_undersync(self, vlan_group_name: str):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass

import pytest
from neutron_lib.plugins.ml2 import api
from oslo_config import cfg

from neutron_understack.neutron_understack_mech import UnderstackDriver
Expand All @@ -26,11 +27,22 @@ def test_with_no_trunk(
mocker.patch.object(
port_context, "allocate_dynamic_segment", return_value=vlan_network_segment
)
mocker.patch.object(port_context, "continue_binding")
port_context._prepare_to_bind(port_context.network.network_segments)

understack_driver.bind_port(port_context)
understack_driver.trunk_driver = understack_trunk_driver

port_context.allocate_dynamic_segment.assert_called_once()
vxlan_segment = next(
s
for s in port_context.network.network_segments
if s[api.NETWORK_TYPE] == "vxlan"
)
port_context.continue_binding.assert_called_once_with(
segment_id=vxlan_segment[api.ID],
next_segments_to_bind=[vlan_network_segment],
)

@pytest.mark.parametrize("port_dict", [{"trunk": True}], indirect=True)
def test_with_trunk_details(
Expand All @@ -39,11 +51,14 @@ def test_with_trunk_details(
mocker.patch(
"neutron_understack.utils.fetch_subport_network_id", return_value="112233"
)
mocker.patch.object(port_context, "continue_binding")
port_context._prepare_to_bind(port_context.network.network_segments)

understack_driver.trunk_driver = understack_trunk_driver
mocker.patch.object(understack_driver.trunk_driver, "configure_trunk")
understack_driver.bind_port(port_context)
understack_driver.trunk_driver.configure_trunk.assert_called_once()
port_context.continue_binding.assert_called_once()


class TestCreateNetworkPostCommit:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from unittest.mock import MagicMock

import pytest
from neutron_lib import constants as p_const
from neutron_lib.api.definitions import portbindings
from neutron_lib.plugins.ml2 import api

from neutron_understack.undersync_mech import UnderstackUndersyncDriver


@pytest.fixture
def driver():
d = UnderstackUndersyncDriver()
d.initialize()
return d


def _make_context(vnic_type=portbindings.VNIC_BAREMETAL, segments=None):
context = MagicMock()
context.current = {portbindings.VNIC_TYPE: vnic_type}
context.segments_to_bind = segments or []
return context


@pytest.fixture
def vlan_segment():
def _make(segment_id="seg-vlan-1"):
return {
api.ID: segment_id,
api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.SEGMENTATION_ID: 100,
api.PHYSICAL_NETWORK: "physnet1",
api.MTU: 1500,
}

return _make


@pytest.fixture
def vxlan_segment():
def _make(segment_id="seg-vxlan-1"):
return {
api.ID: segment_id,
api.NETWORK_TYPE: p_const.TYPE_VXLAN,
api.SEGMENTATION_ID: 1000,
api.PHYSICAL_NETWORK: None,
api.MTU: 1450,
}

return _make


class TestUnderstackUndersyncDriverBindPort:
def test_binds_vlan_segment(self, driver, vlan_segment):
seg = vlan_segment()
ctx = _make_context(segments=[seg])

driver.bind_port(ctx)

ctx.set_binding.assert_called_once_with(
segment_id=seg[api.ID],
vif_type=portbindings.VIF_TYPE_OTHER,
vif_details={},
status=p_const.PORT_STATUS_ACTIVE,
)

def test_binds_first_vlan_segment_only(self, driver, vlan_segment):
seg1 = vlan_segment("seg-vlan-1")
seg2 = vlan_segment("seg-vlan-2")
ctx = _make_context(segments=[seg1, seg2])

driver.bind_port(ctx)

ctx.set_binding.assert_called_once_with(
segment_id=seg1[api.ID],
vif_type=portbindings.VIF_TYPE_OTHER,
vif_details={},
status=p_const.PORT_STATUS_ACTIVE,
)

def test_skips_vxlan_segment(self, driver, vxlan_segment):
ctx = _make_context(segments=[vxlan_segment()])

driver.bind_port(ctx)

ctx.set_binding.assert_not_called()

def test_skips_unsupported_vnic_type(self, driver, vlan_segment):
ctx = _make_context(vnic_type="direct", segments=[vlan_segment()])

driver.bind_port(ctx)

ctx.set_binding.assert_not_called()

def test_normal_vnic_type_is_supported(self, driver, vlan_segment):
seg = vlan_segment()
ctx = _make_context(vnic_type=portbindings.VNIC_NORMAL, segments=[seg])

driver.bind_port(ctx)

ctx.set_binding.assert_called_once()

def test_empty_segments_to_bind(self, driver):
ctx = _make_context(segments=[])

driver.bind_port(ctx)

ctx.set_binding.assert_not_called()
34 changes: 34 additions & 0 deletions python/neutron-understack/neutron_understack/undersync_mech.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from neutron_lib import constants as p_const
from neutron_lib.api.definitions import portbindings
from neutron_lib.plugins.ml2 import api
from neutron_lib.plugins.ml2.api import MechanismDriver

from .ml2_type_annotations import PortContext

SUPPORTED_VNIC_TYPES = [portbindings.VNIC_BAREMETAL, portbindings.VNIC_NORMAL]


class UnderstackUndersyncDriver(MechanismDriver):
@property
def connectivity(self): # type: ignore
return portbindings.CONNECTIVITY_L2

def initialize(self):
pass

def bind_port(self, context: PortContext) -> None:
vnic_type = context.current.get(
portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL
)
if vnic_type not in SUPPORTED_VNIC_TYPES:
return

for segment in context.segments_to_bind:
if segment[api.NETWORK_TYPE] == p_const.TYPE_VLAN:
context.set_binding(
segment_id=segment[api.ID],
vif_type=portbindings.VIF_TYPE_OTHER,
vif_details={},
status=p_const.PORT_STATUS_ACTIVE,
)
return
1 change: 1 addition & 0 deletions python/neutron-understack/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies = [

[project.entry-points."neutron.ml2.mechanism_drivers"]
understack = "neutron_understack.neutron_understack_mech:UnderstackDriver"
understack_undersync = "neutron_understack.undersync_mech:UnderstackUndersyncDriver"

[project.entry-points."neutron.ml2.type_drivers"]
understack_vxlan = "neutron_understack.type_understack_vxlan:UnderstackVxlanTypeDriver"
Expand Down
Loading