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
7 changes: 4 additions & 3 deletions components/neutron/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ conf:
ml2_conf:
ml2:
extension_drivers: 'port_security'
# set the default ml2 backend to our plugin, neutron_understack
# we'll need to use the ovn ML2 plugin to hook the routers to our network
mechanism_drivers: "understack,undersync,ovn"
# ovn is listed first so it creates virtual ports for routers before
# our baremetal drivers run, matching the recommended ordering from
# https://docs.openstack.org/networking-baremetal/latest/
mechanism_drivers: "ovn,understack,baremetal,undersync"
tenant_network_types: "vxlan"
type_drivers: "vlan,vxlan"
ml2_type_vxlan:
Expand Down
12 changes: 9 additions & 3 deletions docs/design-guide/neutron-networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ many of these features can be achieved.

To enable this we are using the following plugins/features of Neutron:

- [OVN driver][ovn-driver] for general [OVN][ovn] support
- [OVN driver][ovn-driver] for general [OVN][ovn] support — loaded first so it
creates virtual ports for routers before the baremetal drivers run, as
recommended by [networking-baremetal][networking-baremetal]
- [networking-baremetal][networking-baremetal] to have Neutron aware of the physical
networks of Ironic baremetal ports.
- our custom mechanism drivers `understack` and `undersync` (both must be loaded)
- our custom mechanism drivers `understack` and `undersync` (both must be loaded,
with `baremetal` from [networking-baremetal][networking-baremetal] loaded between them)
- [ovn-router][ovn-admin] as the L3 router plugin
- [trunk plugin][neutron-trunk] service plugin
- [network segment range][neutron-network-segment-range] service plugin
Expand Down Expand Up @@ -377,12 +380,15 @@ The names and the IDs all match, along with the VLAN ID of the segment where the
## ML2 Mechanism Operations

Our ML2 mechanism is split across two drivers that must both be present in
`mechanism_drivers`:
`mechanism_drivers`, with the `baremetal` driver from
[networking-baremetal][networking-baremetal] loaded between them:

- `understack` — the primary driver responsible for allocating dynamic VLAN
segments on VXLAN networks (`bind_port()`), releasing them when ports are
removed (`delete_port_postcommit()`), and triggering switch configuration
updates (`update_port_postcommit()`)
- `baremetal` — the [networking-baremetal][networking-baremetal] driver that
makes Neutron aware of the physical networks of Ironic baremetal ports
- `undersync` — handles level-1 binding by calling `set_binding()`
on the VLAN segment that `understack` allocated via `continue_binding()`;
without it port binding fails at level 1
Expand Down
2 changes: 1 addition & 1 deletion docs/operator-guide/openstack-neutron.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ conf:
# replacing so you'll need to pay attention
# to any changes your environment might have
# from the default
mechanism_drivers: "logger,understack,undersync,ovn"
mechanism_drivers: "logger,ovn,understack,baremetal,undersync"
logging:
loggers:
# for 'keys' we are attempt to append 'mechanism_logger'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,56 @@ def test_with_simple_port(self, understack_driver, port_context):

@pytest.mark.usefixtures("_ironic_baremetal_port_physical_network")
class TestBindPort:
def test_does_not_bind_vlan_only_segments(
self,
mocker,
port_context,
understack_driver,
vlan_network_segment,
):
"""At level 1 understack receives only the VLAN segment and should do nothing.

undersync is responsible for that binding.
"""
port_context._prepare_to_bind([vlan_network_segment])
mocker.patch.object(port_context, "continue_binding")

understack_driver.bind_port(port_context)

port_context.continue_binding.assert_not_called()

def test_uses_existing_vlan_segment(
self,
mocker,
port_context,
understack_driver,
vlan_network_segment,
):
"""When a VLAN segment already exists for the physnet, reuse it.

It should not allocate a new dynamic segment.
"""
mocker.patch.object(port_context, "allocate_dynamic_segment")
mocker.patch.object(port_context, "continue_binding")
mocker.patch(
"neutron_understack.neutron_understack_mech.utils.vlan_segment_for_physnet",
return_value=vlan_network_segment,
)
port_context._prepare_to_bind(port_context.network.network_segments)

understack_driver.bind_port(port_context)

port_context.allocate_dynamic_segment.assert_not_called()
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],
)

def test_with_no_trunk(
self,
mocker,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@
from neutron_lib.api.definitions import portbindings
from neutron_lib.plugins.ml2 import api

from neutron_understack.undersync_mech import UnderstackUndersyncDriver
from neutron_understack.undersync_mech import UndersyncDriver


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


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

Expand Down Expand Up @@ -50,7 +50,7 @@ def _make(segment_id="seg-vxlan-1"):
return _make


class TestUnderstackUndersyncDriverBindPort:
class TestUndersyncDriverBindPort:
def test_binds_vlan_segment(self, driver, vlan_segment):
seg = vlan_segment()
ctx = _make_context(segments=[seg])
Expand Down Expand Up @@ -100,6 +100,21 @@ def test_normal_vnic_type_is_supported(self, driver, vlan_segment):

ctx.set_binding.assert_called_once()

def test_binds_vlan_when_preceded_by_vxlan(
self, driver, vxlan_segment, vlan_segment
):
vlan = vlan_segment()
ctx = _make_context(segments=[vxlan_segment(), vlan])

driver.bind_port(ctx)

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

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

Expand Down

This file was deleted.

27 changes: 24 additions & 3 deletions python/neutron-understack/neutron_understack/undersync_mech.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import logging

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

LOG = logging.getLogger(__name__)

SUPPORTED_VNIC_TYPES = [portbindings.VNIC_BAREMETAL, portbindings.VNIC_NORMAL]


class UnderstackUndersyncDriver(MechanismDriver):
class UndersyncDriver(MechanismDriver):
@property
def connectivity(self): # type: ignore
return portbindings.CONNECTIVITY_L2
Expand All @@ -17,18 +21,35 @@ def initialize(self):
pass

def bind_port(self, context: PortContext) -> None:
vnic_type = context.current.get(
portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL
port = context.current
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
LOG.debug(
"bind_port called for port %s vnic_type %s segments %s",
port["id"],
vnic_type,
context.segments_to_bind,
)
if vnic_type not in SUPPORTED_VNIC_TYPES:
LOG.debug("Skipping unsupported vnic_type %s", vnic_type)
return

for segment in context.segments_to_bind:
if segment[api.NETWORK_TYPE] == p_const.TYPE_VLAN:
LOG.debug(
"bind_port: setting binding for port %s on VLAN segment %s",
port["id"],
segment,
)
context.set_binding(
segment_id=segment[api.ID],
vif_type=portbindings.VIF_TYPE_OTHER,
vif_details={},
status=p_const.PORT_STATUS_ACTIVE,
)
return

LOG.warning(
"bind_port: no VLAN segment found for port %s in %s",
port["id"],
context.segments_to_bind,
)
5 changes: 1 addition & 4 deletions python/neutron-understack/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ dependencies = [

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

[project.entry-points."neutron.ml2.type_drivers"]
understack_vxlan = "neutron_understack.type_understack_vxlan:UnderstackVxlanTypeDriver"
undersync = "neutron_understack.undersync_mech:UndersyncDriver"

[project.entry-points."neutron.service_plugins"]
l3_service_cisco_asa = "neutron_understack.l3_service_cisco_asa:CiscoAsa"
Expand Down
Loading