pass along physical_network to neutron from the baremetal port

When plugging a baremetal port in using the 'neutron' interface, send
the 'physical_network' value of the baremetal port to Neutron as part of the
binding_profile for the port. This can be useful for VXLAN underlay
connected machines where the networks in Neutron are VXLAN networks
which then have segments on them that are VLAN based segments which bind
the VNI to a VLAN for attachment for the node to connect to the VNI.

Ref: https://bugs.launchpad.net/ovn-bgp-agent/+bug/2017890
Ref: https://bugs.launchpad.net/neutron/+bug/2114451
Ref: https://review.opendev.org/c/openstack/neutron-specs/+/952166

Partial-Bug: #2105855
Assisted-by: Claude Code 2.0
Change-Id: I6e0185e203489676d530e6955929997f4871b8fa
Signed-off-by: Doug Goldstein <cardoe@cardoe.com>
This commit is contained in:
Doug Goldstein
2025-10-22 12:58:41 -05:00
parent cd06a3f205
commit e721c56e68
5 changed files with 123 additions and 0 deletions

View File

@@ -371,6 +371,10 @@ def add_ports_to_network(task, network_uuid, security_groups=None):
binding_profile['vtep-logical-switch'] = vtep_logical_switch
binding_profile['vtep-physical-switch'] = vtep_physical_switch
# Include physical_network if available
if ironic_port.physical_network:
binding_profile['physical_network'] = ironic_port.physical_network
update_port_attrs['binding:profile'] = binding_profile
if not ironic_port.pxe_enabled:

View File

@@ -272,6 +272,11 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
binding_profile = {'local_link_information': local_link_info}
if local_group_info:
binding_profile['local_group_information'] = local_group_info
# Include physical_network if available
if port_like_obj.physical_network:
binding_profile['physical_network'] = port_like_obj.physical_network
port_attrs['binding:profile'] = binding_profile
if client_id_opt:

View File

@@ -329,6 +329,44 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
self._test_add_ports_to_network(is_client_id=False,
security_groups=sg_ids)
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
def test_add_ports_to_network_with_physical_network(self, update_mock):
# Test that physical_network is included in binding:profile
self.node.network_interface = 'neutron'
self.node.save()
port = self.ports[0]
port.physical_network = 'physnet1'
port.save()
expected_create_attrs = {
'network_id': self.network_uuid,
'admin_state_up': True,
'binding:vnic_type': 'baremetal',
'device_id': self.node.uuid
}
expected_update_attrs = {
'device_owner': 'baremetal:none',
'binding:host_id': self.node.uuid,
'mac_address': port.address,
'binding:profile': {
'local_link_information': [port.local_link_connection],
'physical_network': 'physnet1'
}
}
self.client_mock.create_port.return_value = self.neutron_port
update_mock.return_value = self.neutron_port
expected = {port.uuid: self.neutron_port['id']}
with task_manager.acquire(self.context, self.node.uuid) as task:
ports = neutron.add_ports_to_network(task, self.network_uuid)
self.assertEqual(expected, ports)
self.client_mock.create_port.assert_called_once_with(
**expected_create_attrs)
update_mock.assert_called_once_with(
self.context, self.neutron_port['id'],
expected_update_attrs)
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
def test__add_ip_addresses_for_ipv6_stateful(self, mock_update):
subnet_id = uuidutils.generate_uuid()

View File

@@ -492,6 +492,76 @@ class TestCommonFunctions(db_base.DbTestCase):
nclient, self.vif_id, 'ACTIVE', fail_on_binding_failure=True)
self.assertTrue(mock_update.called)
@mock.patch.object(neutron_common, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron_common, 'wait_for_port_status', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
def test_plug_port_to_tenant_network_with_physical_network(
self, mock_gc, wait_mock_status, mock_update):
# Test that physical_network is included in binding:profile for port
nclient = mock.MagicMock()
mock_gc.return_value = nclient
self.port.internal_info = {common.TENANT_VIF_KEY: self.vif_id}
self.port.physical_network = 'physnet1'
self.port.save()
expected_attrs = {
'binding:vnic_type': neutron_common.VNIC_BAREMETAL,
'binding:host_id': self.node.uuid,
'mac_address': self.port.address,
'binding:profile': {
'local_link_information': [self.port.local_link_connection],
'physical_network': 'physnet1'
}
}
with task_manager.acquire(self.context, self.node.id) as task:
common.plug_port_to_tenant_network(task, self.port)
mock_update.assert_called_once_with(
task.context, self.vif_id, expected_attrs)
@mock.patch.object(neutron_common, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron_common, 'wait_for_port_status', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
def test_plug_portgroup_to_tenant_network_with_physical_network(
self, mock_gc, wait_mock_status, mock_update):
# Test that physical_network is included in binding:profile for
# a portgroup
nclient = mock.MagicMock()
mock_gc.return_value = nclient
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id, address='00:54:00:cf:2d:01',
physical_network='physnet1')
port1 = obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
portgroup_id=pg.id, uuid=uuidutils.generate_uuid())
port2 = obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:02',
portgroup_id=pg.id, uuid=uuidutils.generate_uuid())
pg.internal_info = {common.TENANT_VIF_KEY: self.vif_id}
pg.save()
expected_attrs = {
'binding:vnic_type': neutron_common.VNIC_BAREMETAL,
'binding:host_id': self.node.uuid,
'mac_address': pg.address,
'binding:profile': {
'local_link_information': [port1.local_link_connection,
port2.local_link_connection],
'local_group_information': {
'id': pg.uuid,
'name': pg.name,
'bond_mode': pg.mode,
'bond_properties': {}
},
'physical_network': 'physnet1'
}
}
with task_manager.acquire(self.context, self.node.id) as task:
common.plug_port_to_tenant_network(task, pg)
mock_update.assert_called_once_with(
task.context, self.vif_id, expected_attrs)
class TestVifPortIDMixin(db_base.DbTestCase):

View File

@@ -0,0 +1,6 @@
---
features:
- |
When plugging a baremetal port in using the 'neutron' interface, send
the 'physical_network' value of the baremetal port to Neutron as part of the
binding_profile for the port.