Skip to content

Commit 7ffd2b6

Browse files
authored
Merge pull request #929 from rackerlabs/router_trunk
feat: "router trunks"
2 parents 9d16aa2 + b06789c commit 7ffd2b6

File tree

10 files changed

+811
-44
lines changed

10 files changed

+811
-44
lines changed

python/neutron-understack/neutron_understack/config.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,28 @@
5050
"prefixes that need to be routable outside of a tenant environment."
5151
),
5252
),
53+
# TODO:: this can very likely be deprecated now
5354
cfg.StrOpt(
5455
"network_node_switchport_uuid",
5556
help=(
5657
"Nautobot UUID of the network node's switchport interface, that "
5758
"is used to trunk all vlans used by a neutron router."
5859
),
5960
),
61+
cfg.StrOpt(
62+
"network_node_trunk_uuid",
63+
help=(
64+
"UUID of the trunk that is used to trunk all vlans used by a Neutron"
65+
" router."
66+
),
67+
),
68+
cfg.StrOpt(
69+
"network_node_switchport_physnet",
70+
help=(
71+
"Name of the physnet configured on a network node's"
72+
"baremetal port that provides connectivity to OVN."
73+
),
74+
),
6075
cfg.BoolOpt(
6176
"enforce_unique_vlans_in_fabric",
6277
default=True,

python/neutron-understack/neutron_understack/ml2_type_annotations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class PortContext:
145145
network: NetworkContext
146146
status: str
147147
original_status: str
148+
plugin_context: Any
148149
_plugin_context: Any
149150
_plugin: Any
150151
vif_type: str

python/neutron-understack/neutron_understack/neutron_understack_mech.py

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import logging
22
from uuid import UUID
33

4-
import neutron_lib.api.definitions.portbindings as portbindings
54
from neutron_lib import constants as p_const
5+
from neutron_lib.api.definitions import portbindings
66
from neutron_lib.api.definitions import segment as segment_def
77
from neutron_lib.callbacks import events
88
from neutron_lib.callbacks import registry
@@ -12,6 +12,7 @@
1212
from oslo_config import cfg
1313

1414
from neutron_understack import config
15+
from neutron_understack import routers
1516
from neutron_understack import utils
1617
from neutron_understack.ironic import IronicClient
1718
from neutron_understack.nautobot import Nautobot
@@ -20,7 +21,6 @@
2021
from neutron_understack.undersync import Undersync
2122

2223
from .ml2_type_annotations import NetworkContext
23-
from .ml2_type_annotations import NetworkSegmentDict
2424
from .ml2_type_annotations import PortContext
2525

2626
LOG = logging.getLogger(__name__)
@@ -29,6 +29,9 @@
2929
config.register_ml2_understack_opts(cfg.CONF)
3030

3131

32+
SUPPORTED_VNIC_TYPES = [portbindings.VNIC_BAREMETAL, portbindings.VNIC_NORMAL]
33+
34+
3235
class UnderstackDriver(MechanismDriver):
3336
# See MechanismDriver docs for resource_provider_uuid5_namespace
3437
resource_provider_uuid5_namespace = UUID("6eae3046-4072-11ef-9bcf-d6be6370a162")
@@ -58,6 +61,12 @@ def subscribe(self):
5861
events.BEFORE_DELETE,
5962
cancellable=True,
6063
)
64+
registry.subscribe(
65+
routers.handle_router_interface_removal,
66+
resources.PORT,
67+
events.BEFORE_DELETE,
68+
cancellable=True,
69+
)
6170

6271
def create_network_precommit(self, context):
6372
pass
@@ -221,11 +230,14 @@ def delete_subnet_postcommit(self, context):
221230
{"prefix": prefix, "uuid": subnet_uuid},
222231
)
223232

224-
def create_port_precommit(self, context):
233+
def create_port_precommit(self, context: PortContext):
225234
pass
226235

227-
def create_port_postcommit(self, context):
228-
pass
236+
def create_port_postcommit(self, context: PortContext) -> None:
237+
# Provide network node(s) with connectivity to the networks where this
238+
# router port is attached to.
239+
if utils.is_router_interface(context):
240+
routers.create_port_postcommit(context)
229241

230242
def update_port_precommit(self, context):
231243
pass
@@ -263,7 +275,7 @@ def _tenant_network_port_cleanup(self, context: PortContext):
263275
"""
264276
trunk_details = context.current.get("trunk_details", {})
265277
segment_id = context.original_top_bound_segment["id"]
266-
original_binding = context.original["binding:profile"]
278+
original_binding = context.original[portbindings.PROFILE]
267279
connected_interface_uuid = utils.fetch_connected_interface_uuid(
268280
original_binding, self.nb
269281
)
@@ -338,7 +350,18 @@ def bind_port(self, context: PortContext) -> None:
338350
which means that changes made here will get pushed to the switch at that
339351
time.
340352
"""
341-
if is_provisioning_network(context.current["network_id"]):
353+
port = context.current
354+
LOG.debug(
355+
"Attempting to bind port %(port)s on network %(net)s",
356+
{"port": port["id"], "net": port["network_id"]},
357+
)
358+
359+
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
360+
if vnic_type not in SUPPORTED_VNIC_TYPES:
361+
LOG.debug("Refusing to bind due to unsupported vnic_type: %s", vnic_type)
362+
return
363+
364+
if is_provisioning_network(port["network_id"]):
342365
self._set_nautobot_port_status(context, "Provisioning-Interface")
343366

344367
for segment in context.network.network_segments:
@@ -349,7 +372,7 @@ def bind_port(self, context: PortContext) -> None:
349372
def _bind_port_segment(self, context: PortContext, segment):
350373
network_id = context.current["network_id"]
351374
connected_interface_uuid = utils.fetch_connected_interface_uuid(
352-
context.current["binding:profile"], self.nb
375+
context.current[portbindings.PROFILE], self.nb
353376
)
354377
mac_address = context.current["mac_address"]
355378

@@ -372,13 +395,20 @@ def _bind_port_segment(self, context: PortContext, segment):
372395
vlan_group_name,
373396
)
374397

375-
current_vlan_segment = self._vlan_segment_for_physnet(context, vlan_group_name)
376-
dynamic_segment = current_vlan_segment or context.allocate_dynamic_segment(
377-
segment={
378-
"network_type": p_const.TYPE_VLAN,
379-
"physical_network": vlan_group_name,
380-
},
381-
)
398+
current_vlan_segment = utils.vlan_segment_for_physnet(context, vlan_group_name)
399+
if current_vlan_segment:
400+
LOG.info(
401+
"vlan segment: %(segment)s already preset for physnet: " "%(physnet)s",
402+
{"segment": current_vlan_segment, "physnet": vlan_group_name},
403+
)
404+
dynamic_segment = current_vlan_segment
405+
else:
406+
dynamic_segment = context.allocate_dynamic_segment(
407+
segment={
408+
"network_type": p_const.TYPE_VLAN,
409+
"physical_network": vlan_group_name,
410+
},
411+
)
382412

383413
LOG.debug("bind_port_segment: Native VLAN segment %s", dynamic_segment)
384414
dynamic_segment_vlan_id = dynamic_segment["segmentation_id"]
@@ -403,21 +433,6 @@ def _bind_port_segment(self, context: PortContext, segment):
403433
status=p_const.PORT_STATUS_ACTIVE,
404434
)
405435

406-
def _vlan_segment_for_physnet(
407-
self, context: PortContext, physnet: str
408-
) -> NetworkSegmentDict | None:
409-
for segment in context.network.network_segments:
410-
if (
411-
segment[api.NETWORK_TYPE] == p_const.TYPE_VLAN
412-
and segment[api.PHYSICAL_NETWORK] == physnet
413-
):
414-
LOG.info(
415-
"vlan segment: %(segment)s already preset for physnet: "
416-
"%(physnet)s",
417-
{"segment": segment, "physnet": physnet},
418-
)
419-
return segment
420-
421436
def invoke_undersync(self, vlan_group_name: str):
422437
self.undersync.sync_devices(
423438
vlan_group=vlan_group_name,
@@ -496,7 +511,7 @@ def _delete_vlan(self, segment):
496511
)
497512

498513
def _set_nautobot_port_status(self, context: PortContext, status: str):
499-
profile = context.current["binding:profile"]
514+
profile = context.current[portbindings.PROFILE]
500515
interface_uuid = utils.fetch_connected_interface_uuid(profile, self.nb)
501516
LOG.debug("Set interface %s to %s status", interface_uuid, status)
502517
self.nb.configure_port_status(interface_uuid, status=status)

0 commit comments

Comments
 (0)