[OVN] Set MTU of the VETH interfaces between OVS and metadata

The VETH pair between the metadata namespace and the local OVS now
has the same MTU as the network associated to this metadata service.
The "LSP.external_ids" field has a new key defined: "neutron:mtu".
This is the value of the network MTU.

This patch does not update the previous metadata datapaths nor the
existing LSP. This change will affect only to new created ports
and the corresponding metadata datapaths.

Conflicts:
    neutron/agent/ovn/metadata/agent.py

Closes-Bug: #2053274
Change-Id: I7ff300e9634e5e3fc68d70540392109fd8b9babc
(cherry picked from commit 47b4d14955)
This commit is contained in:
Rodolfo Alonso Hernandez 2024-02-21 15:34:13 +00:00
parent ac63999b66
commit 101898fde8
3 changed files with 55 additions and 56 deletions

View File

@ -100,7 +100,7 @@ class PortBindingEvent(row_event.RowEvent):
with _SYNC_STATE_LOCK.read_lock():
self.log_row(row)
try:
self.agent.provision_datapath(row.datapath)
self.agent.provision_datapath(row)
except ConfigException:
# We're now in the reader lock mode, we need to exit the
# context and then use writer lock
@ -414,12 +414,12 @@ class MetadataAgent(object):
"br-int instead.")
return 'br-int'
def get_networks_datapaths(self):
"""Return a set of datapath objects of the VIF ports on the current
def get_networks_port_bindings(self):
"""Return a set of Port_Binding objects of the VIF ports on the current
chassis.
"""
ports = self.sb_idl.get_ports_on_chassis(self.chassis)
return set(p.datapath for p in self._vif_ports(ports))
return list(self._vif_ports(ports))
@_sync_lock
def sync(self, provision=True):
@ -434,12 +434,12 @@ class MetadataAgent(object):
system_namespaces = tuple(
ns.decode('utf-8') if isinstance(ns, bytes) else ns
for ns in ip_lib.list_network_namespaces())
net_datapaths = self.get_networks_datapaths()
metadata_namespaces = [
net_port_bindings = self.get_networks_port_bindings()
metadata_namespaces = set(
self._get_namespace_name(
ovn_utils.get_network_name_from_datapath(datapath))
for datapath in net_datapaths
]
for datapath in (pb.datapath for pb in net_port_bindings)
)
unused_namespaces = [ns for ns in system_namespaces if
ns.startswith(NS_PREFIX) and
ns not in metadata_namespaces]
@ -453,8 +453,8 @@ class MetadataAgent(object):
# even those that are already running. This is to make sure
# everything within each namespace is up to date.
if provision:
for datapath in net_datapaths:
self.provision_datapath(datapath)
for port_binding in net_port_bindings:
self.provision_datapath(port_binding)
@staticmethod
def _get_veth_name(datapath):
@ -625,7 +625,7 @@ class MetadataAgent(object):
return net_name, datapath_ports_ips, metadata_port_info
def provision_datapath(self, datapath):
def provision_datapath(self, port_binding):
"""Provision the datapath so that it can serve metadata.
This function will create the namespace and VETH pair if needed
@ -633,11 +633,13 @@ class MetadataAgent(object):
metadata port of the network. It will also remove existing IP from
the namespace if they are no longer needed.
:param datapath: datapath object.
:return: The metadata namespace name for the datapath or None
if namespace was not provisioned
:param port_binding: Port_Binding object.
:return: The metadata namespace name for the Port_Binding.datapath or
None if namespace was not provisioned
"""
datapath = port_binding.datapath
mtu = int(port_binding.external_ids.get(
ovn_const.OVN_NETWORK_MTU_EXT_ID_KEY) or '0')
provision_params = self._get_provision_params(datapath)
if not provision_params:
return
@ -666,6 +668,11 @@ class MetadataAgent(object):
# Configure the MAC address.
ip2.link.set_address(metadata_port_info.mac)
# Set VETH ports MTU.
if mtu:
ip1.link.set_mtu(mtu)
ip2.link.set_mtu(mtu)
# Make sure both ends of the VETH are up
ip1.link.set_up()
ip2.link.set_up()

View File

@ -79,6 +79,7 @@ OvnPortInfo = collections.namedtuple(
"address6_scope_id",
"vnic_type",
"capabilities",
"mtu",
],
)
@ -324,6 +325,7 @@ class OVNClient(object):
address6_scope_id = ""
dhcpv4_options = self._get_port_dhcp_options(port, const.IP_VERSION_4)
dhcpv6_options = self._get_port_dhcp_options(port, const.IP_VERSION_6)
mtu = ''
if vtep_physical_switch:
vtep_logical_switch = bp_info.bp_param.get('vtep-logical-switch')
port_type = 'vtep'
@ -421,10 +423,10 @@ class OVNClient(object):
ovn_const.VIF_DETAILS_PF_MAC_ADDRESS in bp_info.bp_param):
port_net = self._plugin.get_network(
context, port['network_id'])
mtu = str(port_net['mtu'])
options.update({
ovn_const.LSP_OPTIONS_VIF_PLUG_TYPE_KEY: 'representor',
ovn_const.LSP_OPTIONS_VIF_PLUG_MTU_REQUEST_KEY: str(
port_net['mtu']),
ovn_const.LSP_OPTIONS_VIF_PLUG_MTU_REQUEST_KEY: mtu,
ovn_const.LSP_OPTIONS_VIF_PLUG_REPRESENTOR_PF_MAC_KEY: (
bp_info.bp_param.get(
ovn_const.VIF_DETAILS_PF_MAC_ADDRESS)),
@ -465,7 +467,7 @@ class OVNClient(object):
parent_name, tag, dhcpv4_options, dhcpv6_options,
cidrs.strip(), device_owner, sg_ids,
address4_scope_id, address6_scope_id,
bp_info.vnic_type, bp_info.capabilities
bp_info.vnic_type, bp_info.capabilities, mtu
)
def update_port_dhcp_options(self, port_info, txn):
@ -506,6 +508,7 @@ class OVNClient(object):
ovn_const.OVN_PORT_VNIC_TYPE_KEY: port_info.vnic_type,
ovn_const.OVN_PORT_BP_CAPABILITIES_KEY:
';'.join(port_info.capabilities),
ovn_const.OVN_NETWORK_MTU_EXT_ID_KEY: port_info.mtu,
}
return port_info, external_ids

View File

@ -28,6 +28,7 @@ from neutron.agent.linux.ip_lib import IpNetnsCommand as ip_netns
from neutron.agent.linux.ip_lib import IPWrapper as ip_wrap
from neutron.agent.ovn.metadata import agent
from neutron.agent.ovn.metadata import driver
from neutron.common.ovn import constants as ovn_const
from neutron.conf.agent.metadata import config as meta_conf
from neutron.conf.agent.ovn.metadata import config as ovn_meta_conf
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
@ -100,14 +101,8 @@ class TestMetadataAgent(base.BaseTestCase):
self.agent.sync()
pdp.assert_has_calls(
[
mock.call(p.datapath)
for p in self.ports
],
any_order=True
)
pdp.assert_has_calls([mock.call(p) for p in self.ports],
any_order=True)
lnn.assert_called_once_with()
tdp.assert_not_called()
@ -124,13 +119,8 @@ class TestMetadataAgent(base.BaseTestCase):
self.agent.sync()
pdp.assert_has_calls(
[
mock.call(p.datapath)
for p in self.ports
],
any_order=True
)
pdp.assert_has_calls([mock.call(p) for p in self.ports],
any_order=True)
lnn.assert_called_once_with()
tdp.assert_called_once_with('3')
@ -149,27 +139,23 @@ class TestMetadataAgent(base.BaseTestCase):
side_effect=Exception()) as tdp:
self.agent.sync()
pdp.assert_has_calls(
[
mock.call(p.datapath)
for p in self.ports
],
any_order=True
)
pdp.assert_has_calls([mock.call(p) for p in self.ports],
any_order=True)
lnn.assert_called_once_with()
tdp.assert_called_once_with('3')
def test_get_networks_datapaths(self):
"""Test get_networks_datapaths returns only datapath objects for the
networks containing vif ports of type ''(blank) and 'external'.
def test_get_networks_port_bindings(self):
"""Test get_networks_port_bindings returns only the port binding
objects for ports with VIF type empty ('') or 'external'.
This test simulates that this chassis has the following ports:
* datapath '1': 1 port type '' , 1 port 'external' and
1 port 'unknown'
* datapath '2': 1 port type ''
* datapath '3': 1 port with type 'external'
* datapath '4': 1 port with type 'unknown'
* port0: datapath 1, type ''
* port1: datapath 1, type 'external'
* port2: datapath 1, type 'unknown'
* port3: datapath 2, type ''
* port4: datapath 3, type 'external'
* port5: datapath 4, type 'unknown'
It is expected that only datapaths '1', '2' and '3' are returned
Only port bindings from ports 0, 1, 3, and 4 are expected.
"""
datapath_1 = DatapathInfo(uuid='uuid1',
@ -192,11 +178,8 @@ class TestMetadataAgent(base.BaseTestCase):
with mock.patch.object(self.agent.sb_idl, 'get_ports_on_chassis',
return_value=ports):
expected_datapaths = set([datapath_1, datapath_2, datapath_3])
self.assertSetEqual(
expected_datapaths,
self.agent.get_networks_datapaths()
)
self.assertEqual([ports[0], ports[1], ports[3], ports[4]],
self.agent.get_networks_port_bindings())
def test_teardown_datapath(self):
"""Test teardown datapath.
@ -424,7 +407,8 @@ class TestMetadataAgent(base.BaseTestCase):
mock.patch.object(agent.MetadataAgent, '_get_namespace_name',
return_value=nemaspace_name),\
mock.patch.object(ip_link, 'set_up') as link_set_up,\
mock.patch.object(ip_link, 'set_address') as link_set_addr,\
mock.patch.object(ip_link, 'set_address') as link_set_addr, \
mock.patch.object(ip_link, 'set_mtu') as link_set_mtu, \
mock.patch.object(ip_addr, 'list', return_value=[]),\
mock.patch.object(
ip_addr, 'add_multiple') as ip_addr_add_multiple,\
@ -442,7 +426,11 @@ class TestMetadataAgent(base.BaseTestCase):
# We need to assert that it was deleted first.
self.agent.ovs_idl.list_br.return_value.execute.return_value = (
['br-int', 'br-fake'])
self.agent.provision_datapath('fake_datapath')
mtu = 1500
port_binding = mock.Mock(
datapath='fake_datapath',
external_ids={ovn_const.OVN_NETWORK_MTU_EXT_ID_KEY: str(mtu)})
self.agent.provision_datapath(port_binding)
# Check that the port was deleted from br-fake
self.agent.ovs_idl.del_port.assert_called_once_with(
@ -452,6 +440,7 @@ class TestMetadataAgent(base.BaseTestCase):
nemaspace_name)
# Make sure that the two ends of the VETH pair have been set as up.
self.assertEqual(2, link_set_up.call_count)
link_set_mtu.assert_has_calls([mock.call(mtu), mock.call(mtu)])
link_set_addr.assert_called_once_with('aa:bb:cc:dd:ee:ff')
# Make sure that the port has been added to OVS.
self.agent.ovs_idl.add_port.assert_called_once_with(