Make agent interface plugging utilize network MTU
This changes the 'plug' and 'plug_new' interfaces of the LinuxInterfaceDriver to accept an MTU argument. It then updates the dhcp agent and l3 agent to pass the MTU that is set on the network that the port belongs to. This allows it to take into account the overhead calculations that are done for encapsulation types. It's necessary for the L3 agent to have the MTU because it must recognize when fragmentation is needed so it can fragment or generate an ICMP error. It's necessary for the DHCP agent to have the MTU so it doesn't interfere when it plugs into a bridge with a larger than 1500 MTU (the bridge would reduce its MTU to match the agent). If an operator sets 'network_device_mtu', the value of that will be used instead to preserve previous behavior. Closes-Bug: #1549470 Closes-Bug: #1542108 Closes-Bug: #1542475 DocImpact: Neutron agents now support arbitrary MTU configurations on each network (including jumbo frames). This is accomplished by checking the MTU value defined for each network on which it is wiring VIFs. Co-Authored-By: Matt Kassawara <mkassawara@gmail.com> Change-Id: Ic091fa78dfd133179c71cbc847bf955a06cb248a
This commit is contained in:
parent
3fb153b15b
commit
4df8d9a701
@ -117,4 +117,5 @@ class DvrEdgeHaRouter(DvrEdgeRouter, HaRouter):
|
||||
self.driver.plug(port['network_id'], port['id'],
|
||||
interface_name, port['mac_address'],
|
||||
namespace=self.snat_namespace.name,
|
||||
prefix=dvr_snat_ns.SNAT_INT_DEV_PREFIX)
|
||||
prefix=dvr_snat_ns.SNAT_INT_DEV_PREFIX,
|
||||
mtu=port.get('mtu'))
|
||||
|
@ -104,7 +104,8 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
|
||||
sn_port['fixed_ips'],
|
||||
sn_port['mac_address'],
|
||||
interface_name,
|
||||
dvr_snat_ns.SNAT_INT_DEV_PREFIX)
|
||||
dvr_snat_ns.SNAT_INT_DEV_PREFIX,
|
||||
mtu=sn_port.get('mtu'))
|
||||
|
||||
def _dvr_internal_network_removed(self, port):
|
||||
super(DvrEdgeRouter, self)._dvr_internal_network_removed(port)
|
||||
@ -132,7 +133,8 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter):
|
||||
self.snat_namespace.name, port['network_id'],
|
||||
port['id'], port['fixed_ips'],
|
||||
port['mac_address'], interface_name,
|
||||
dvr_snat_ns.SNAT_INT_DEV_PREFIX)
|
||||
dvr_snat_ns.SNAT_INT_DEV_PREFIX,
|
||||
mtu=port.get('mtu'))
|
||||
|
||||
def _create_dvr_gateway(self, ex_gw_port, gw_interface_name):
|
||||
"""Create SNAT namespace."""
|
||||
|
@ -106,7 +106,8 @@ class FipNamespace(namespaces.Namespace):
|
||||
ex_gw_port['mac_address'],
|
||||
bridge=self.agent_conf.external_network_bridge,
|
||||
namespace=ns_name,
|
||||
prefix=FIP_EXT_DEV_PREFIX)
|
||||
prefix=FIP_EXT_DEV_PREFIX,
|
||||
mtu=ex_gw_port.get('mtu'))
|
||||
|
||||
ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
|
||||
self.driver.init_l3(interface_name, ip_cidrs, namespace=ns_name,
|
||||
@ -232,9 +233,11 @@ class FipNamespace(namespaces.Namespace):
|
||||
self._internal_ns_interface_added(str(fip_2_rtr),
|
||||
fip_2_rtr_name,
|
||||
fip_ns_name)
|
||||
if self.agent_conf.network_device_mtu:
|
||||
int_dev[0].link.set_mtu(self.agent_conf.network_device_mtu)
|
||||
int_dev[1].link.set_mtu(self.agent_conf.network_device_mtu)
|
||||
mtu = (self.agent_conf.network_device_mtu or
|
||||
ri.get_ex_gw_port().get('mtu'))
|
||||
if mtu:
|
||||
int_dev[0].link.set_mtu(mtu)
|
||||
int_dev[1].link.set_mtu(mtu)
|
||||
int_dev[0].link.set_up()
|
||||
int_dev[1].link.set_up()
|
||||
|
||||
|
@ -144,7 +144,8 @@ class HaRouter(router.RouterInfo):
|
||||
interface_name,
|
||||
self.ha_port['mac_address'],
|
||||
namespace=self.ha_namespace,
|
||||
prefix=HA_DEV_PREFIX)
|
||||
prefix=HA_DEV_PREFIX,
|
||||
mtu=self.ha_port.get('mtu'))
|
||||
ip_cidrs = common_utils.fixed_ip_cidrs(self.ha_port['fixed_ips'])
|
||||
self.driver.init_l3(interface_name, ip_cidrs,
|
||||
namespace=self.ha_namespace,
|
||||
@ -268,13 +269,13 @@ class HaRouter(router.RouterInfo):
|
||||
def _plug_ha_router_port(self, port, name_getter, prefix):
|
||||
port_id = port['id']
|
||||
interface_name = name_getter(port_id)
|
||||
|
||||
self.driver.plug(port['network_id'],
|
||||
port_id,
|
||||
interface_name,
|
||||
port['mac_address'],
|
||||
namespace=self.ha_namespace,
|
||||
prefix=prefix)
|
||||
prefix=prefix,
|
||||
mtu=port.get('mtu'))
|
||||
|
||||
self._disable_ipv6_addressing_on_interface(interface_name)
|
||||
self._add_vips(port, interface_name)
|
||||
|
@ -361,12 +361,12 @@ class RouterInfo(object):
|
||||
|
||||
def _internal_network_added(self, ns_name, network_id, port_id,
|
||||
fixed_ips, mac_address,
|
||||
interface_name, prefix):
|
||||
interface_name, prefix, mtu=None):
|
||||
LOG.debug("adding internal network: prefix(%s), port(%s)",
|
||||
prefix, port_id)
|
||||
self.driver.plug(network_id, port_id, interface_name, mac_address,
|
||||
namespace=ns_name,
|
||||
prefix=prefix)
|
||||
prefix=prefix, mtu=mtu)
|
||||
|
||||
ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips)
|
||||
self.driver.init_router_port(
|
||||
@ -391,7 +391,8 @@ class RouterInfo(object):
|
||||
fixed_ips,
|
||||
mac_address,
|
||||
interface_name,
|
||||
INTERNAL_DEV_PREFIX)
|
||||
INTERNAL_DEV_PREFIX,
|
||||
mtu=port.get('mtu'))
|
||||
|
||||
def internal_network_removed(self, port):
|
||||
interface_name = self.get_internal_device_name(port['id'])
|
||||
@ -557,7 +558,8 @@ class RouterInfo(object):
|
||||
ex_gw_port['mac_address'],
|
||||
bridge=self.agent_conf.external_network_bridge,
|
||||
namespace=ns_name,
|
||||
prefix=EXTERNAL_DEV_PREFIX)
|
||||
prefix=EXTERNAL_DEV_PREFIX,
|
||||
mtu=ex_gw_port.get('mtu'))
|
||||
|
||||
def _get_external_gw_ips(self, ex_gw_port):
|
||||
gateway_ips = []
|
||||
|
@ -1230,7 +1230,8 @@ class DeviceManager(object):
|
||||
port.id,
|
||||
interface_name,
|
||||
port.mac_address,
|
||||
namespace=network.namespace)
|
||||
namespace=network.namespace,
|
||||
mtu=network.get('mtu'))
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE('Unable to plug DHCP port for '
|
||||
|
@ -20,7 +20,7 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from neutron._i18n import _, _LE, _LI
|
||||
from neutron._i18n import _, _LE, _LI, _LW
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.agent.linux import utils
|
||||
@ -233,15 +233,15 @@ class LinuxInterfaceDriver(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def plug_new(self, network_id, port_id, device_name, mac_address,
|
||||
bridge=None, namespace=None, prefix=None):
|
||||
bridge=None, namespace=None, prefix=None, mtu=None):
|
||||
"""Plug in the interface only for new devices that don't exist yet."""
|
||||
|
||||
def plug(self, network_id, port_id, device_name, mac_address,
|
||||
bridge=None, namespace=None, prefix=None):
|
||||
bridge=None, namespace=None, prefix=None, mtu=None):
|
||||
if not ip_lib.device_exists(device_name,
|
||||
namespace=namespace):
|
||||
self.plug_new(network_id, port_id, device_name, mac_address,
|
||||
bridge, namespace, prefix)
|
||||
bridge, namespace, prefix, mtu)
|
||||
else:
|
||||
LOG.info(_LI("Device %s already exists"), device_name)
|
||||
|
||||
@ -269,7 +269,7 @@ class LinuxInterfaceDriver(object):
|
||||
|
||||
class NullDriver(LinuxInterfaceDriver):
|
||||
def plug_new(self, network_id, port_id, device_name, mac_address,
|
||||
bridge=None, namespace=None, prefix=None):
|
||||
bridge=None, namespace=None, prefix=None, mtu=None):
|
||||
pass
|
||||
|
||||
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||
@ -304,7 +304,7 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
||||
ovs.replace_port(device_name, *attrs)
|
||||
|
||||
def plug_new(self, network_id, port_id, device_name, mac_address,
|
||||
bridge=None, namespace=None, prefix=None):
|
||||
bridge=None, namespace=None, prefix=None, mtu=None):
|
||||
"""Plug in the interface."""
|
||||
if not bridge:
|
||||
bridge = self.conf.ovs_integration_bridge
|
||||
@ -328,11 +328,13 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
||||
|
||||
ns_dev.link.set_address(mac_address)
|
||||
|
||||
if self.conf.network_device_mtu:
|
||||
ns_dev.link.set_mtu(self.conf.network_device_mtu)
|
||||
mtu = self.conf.network_device_mtu or mtu
|
||||
if mtu:
|
||||
ns_dev.link.set_mtu(mtu)
|
||||
if self.conf.ovs_use_veth:
|
||||
root_dev.link.set_mtu(self.conf.network_device_mtu)
|
||||
|
||||
root_dev.link.set_mtu(mtu)
|
||||
else:
|
||||
LOG.warning(_LW("No MTU configured for port %s"), port_id)
|
||||
# Add an interface created by ovs to the namespace.
|
||||
if not self.conf.ovs_use_veth and namespace:
|
||||
namespace_obj = ip.ensure_namespace(namespace)
|
||||
@ -381,7 +383,7 @@ class IVSInterfaceDriver(LinuxInterfaceDriver):
|
||||
utils.execute(cmd, run_as_root=True)
|
||||
|
||||
def plug_new(self, network_id, port_id, device_name, mac_address,
|
||||
bridge=None, namespace=None, prefix=None):
|
||||
bridge=None, namespace=None, prefix=None, mtu=None):
|
||||
"""Plug in the interface."""
|
||||
ip = ip_lib.IPWrapper()
|
||||
tap_name = self._get_tap_name(device_name, prefix)
|
||||
@ -393,9 +395,12 @@ class IVSInterfaceDriver(LinuxInterfaceDriver):
|
||||
ns_dev = ip.device(device_name)
|
||||
ns_dev.link.set_address(mac_address)
|
||||
|
||||
if self.conf.network_device_mtu:
|
||||
ns_dev.link.set_mtu(self.conf.network_device_mtu)
|
||||
root_dev.link.set_mtu(self.conf.network_device_mtu)
|
||||
mtu = self.conf.network_device_mtu or mtu
|
||||
if mtu:
|
||||
ns_dev.link.set_mtu(mtu)
|
||||
root_dev.link.set_mtu(mtu)
|
||||
else:
|
||||
LOG.warning(_LW("No MTU configured for port %s"), port_id)
|
||||
|
||||
if namespace:
|
||||
namespace_obj = ip.ensure_namespace(namespace)
|
||||
@ -424,7 +429,7 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
|
||||
DEV_NAME_PREFIX = 'ns-'
|
||||
|
||||
def plug_new(self, network_id, port_id, device_name, mac_address,
|
||||
bridge=None, namespace=None, prefix=None):
|
||||
bridge=None, namespace=None, prefix=None, mtu=None):
|
||||
"""Plugin the interface."""
|
||||
ip = ip_lib.IPWrapper()
|
||||
|
||||
@ -436,9 +441,12 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
|
||||
namespace2=namespace)
|
||||
ns_veth.link.set_address(mac_address)
|
||||
|
||||
if self.conf.network_device_mtu:
|
||||
root_veth.link.set_mtu(self.conf.network_device_mtu)
|
||||
ns_veth.link.set_mtu(self.conf.network_device_mtu)
|
||||
mtu = self.conf.network_device_mtu or mtu
|
||||
if mtu:
|
||||
root_veth.link.set_mtu(mtu)
|
||||
ns_veth.link.set_mtu(mtu)
|
||||
else:
|
||||
LOG.warning(_LW("No MTU configured for port %s"), port_id)
|
||||
|
||||
root_veth.link.set_up()
|
||||
ns_veth.link.set_up()
|
||||
|
@ -51,6 +51,10 @@ class TestDvrRouter(framework.L3AgentTestFramework):
|
||||
self.agent.conf.agent_mode = 'dvr'
|
||||
self._test_update_floatingip_statuses(self.generate_dvr_router_info())
|
||||
|
||||
def test_dvr_router_lifecycle_ha_with_snat_with_fips_nmtu(self):
|
||||
self._dvr_router_lifecycle(enable_ha=True, enable_snat=True,
|
||||
use_port_mtu=True)
|
||||
|
||||
def test_dvr_router_lifecycle_without_ha_without_snat_with_fips(self):
|
||||
self._dvr_router_lifecycle(enable_ha=False, enable_snat=False)
|
||||
|
||||
@ -101,7 +105,7 @@ class TestDvrRouter(framework.L3AgentTestFramework):
|
||||
self._validate_fips_for_external_network(router2, fip2_ns)
|
||||
|
||||
def _dvr_router_lifecycle(self, enable_ha=False, enable_snat=False,
|
||||
custom_mtu=2000,
|
||||
custom_mtu=2000, use_port_mtu=False,
|
||||
ip_version=4,
|
||||
dual_stack=False):
|
||||
'''Test dvr router lifecycle
|
||||
@ -115,11 +119,19 @@ class TestDvrRouter(framework.L3AgentTestFramework):
|
||||
# Since by definition this is a dvr (distributed = true)
|
||||
# only dvr and dvr_snat are applicable
|
||||
self.agent.conf.agent_mode = 'dvr_snat' if enable_snat else 'dvr'
|
||||
self.agent.conf.network_device_mtu = custom_mtu
|
||||
|
||||
# We get the router info particular to a dvr router
|
||||
router_info = self.generate_dvr_router_info(
|
||||
enable_ha, enable_snat, extra_routes=True)
|
||||
if use_port_mtu:
|
||||
for key in ('_interfaces', '_snat_router_interfaces',
|
||||
'_floatingip_agent_interfaces'):
|
||||
for port in router_info[key]:
|
||||
port['mtu'] = custom_mtu
|
||||
router_info['gw_port']['mtu'] = custom_mtu
|
||||
router_info['_ha_interface']['mtu'] = custom_mtu
|
||||
else:
|
||||
self.agent.conf.network_device_mtu = custom_mtu
|
||||
|
||||
# We need to mock the get_agent_gateway_port return value
|
||||
# because the whole L3PluginApi is mocked and we need the port
|
||||
@ -156,6 +168,9 @@ class TestDvrRouter(framework.L3AgentTestFramework):
|
||||
router.get_internal_device_name,
|
||||
router.ns_name)
|
||||
utils.wait_until_true(device_exists)
|
||||
name = router.get_internal_device_name(device['id'])
|
||||
self.assertEqual(custom_mtu,
|
||||
ip_lib.IPDevice(name, router.ns_name).link.mtu)
|
||||
|
||||
ext_gateway_port = router_info['gw_port']
|
||||
self.assertTrue(self._namespace_exists(router.ns_name))
|
||||
|
@ -253,6 +253,15 @@ class DHCPAgentOVSTestCase(DHCPAgentOVSTestFramework):
|
||||
dhcp_enabled=dhcp_enabled)
|
||||
self.assert_dhcp_resources(network, dhcp_enabled)
|
||||
|
||||
def test_agent_mtu_set_on_interface_driver(self):
|
||||
network = self.network_dict_for_dhcp()
|
||||
network["mtu"] = 789
|
||||
self.configure_dhcp_for_network(network=network)
|
||||
port = network.ports[0]
|
||||
iface_name = self.get_interface_name(network, port)
|
||||
dev = ip_lib.IPDevice(iface_name, network.namespace)
|
||||
self.assertEqual(789, dev.link.mtu)
|
||||
|
||||
def test_good_address_allocation(self):
|
||||
network, port = self._get_network_port_for_allocation_test()
|
||||
network.ports.append(port)
|
||||
|
@ -1309,7 +1309,8 @@ class TestDeviceManager(base.BaseTestCase):
|
||||
port.id,
|
||||
'tap12345678-12',
|
||||
'aa:bb:cc:dd:ee:ff',
|
||||
namespace=net.namespace))
|
||||
namespace=net.namespace,
|
||||
mtu=None))
|
||||
self.mock_driver.assert_has_calls(expected)
|
||||
|
||||
dh._set_default_route.assert_called_once_with(net, 'tap12345678-12')
|
||||
|
@ -390,7 +390,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
||||
sn_port['fixed_ips'],
|
||||
sn_port['mac_address'],
|
||||
ri._get_snat_int_device_name(sn_port['id']),
|
||||
dvr_snat_ns.SNAT_INT_DEV_PREFIX)
|
||||
dvr_snat_ns.SNAT_INT_DEV_PREFIX,
|
||||
mtu=None)
|
||||
elif action == 'remove':
|
||||
self.device_exists.return_value = False
|
||||
ri.get_snat_port_for_internal_port = mock.Mock(
|
||||
|
20
releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml
Normal file
20
releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
features:
|
||||
- Use the value of the network 'mtu' attribute for the MTU
|
||||
of virtual network interfaces such as veth pairs, patch
|
||||
ports, and tap devices involving a particular network.
|
||||
- Enable end-to-end support for arbitrary MTUs including
|
||||
jumbo frames between instances and provider networks by
|
||||
moving MTU disparities between flat or VLAN networks and
|
||||
overlay networks from layer-2 devices to layer-3 devices
|
||||
that support path MTU discovery (PMTUD).
|
||||
upgrade:
|
||||
- Does not change MTU for existing virtual network interfaces.
|
||||
- Actions that create virtual network interfaces on an existing
|
||||
network with the 'mtu' attribute containing a value greater
|
||||
than zero could cause issues for network traffic traversing
|
||||
existing and new virtual network interfaces.
|
||||
fixes:
|
||||
- Explicitly configure MTU of virtual network interfaces
|
||||
rather than using default values or incorrect values that
|
||||
do not account for overlay protocol overhead.
|
Loading…
Reference in New Issue
Block a user