Set MTU on tap devices in Linux Bridge agent
Libvirt does not set the MTU of the tap device it creates when creating a bridge interface. It also does not set the MTU of the bridge itself. This cannot be fixed on the Nova side since libvirt doesn't appear to have support for setting MTUs on bridges until version 3x. This results in a situation where the first VM tap interface attached to a bridge will always have an MTU of 1500. The Neutron agent will then add in VLAN/VXLAN interfaces with the correct MTU; however, the bridge MTU will still be pinned to the smallest interface MTU attached to it. This breaks jumbo frames until all small MTU tap devices are removed from the bridge. This patch explicitly sets the MTU on tap devices to match the network MTU when processing the device. We will have to carry this workaround until we stop Nova from plugging taps into bridges[1] or until we drop support for older libvirts on the Nova side and have it set the MTU. This bug was introduced by change I53c0eb57da956b36f09731d25db989719e9bc9dc which reverted automatic setting of tap MTUs to match those of the physical device. 1. I23c5faaeab69aede1fd038a36f4a0b8f928498ce Closes-Bug: #1684038 Change-Id: Ia245a3e22339fce026901e24a82e836c8b27cc28
This commit is contained in:
parent
5f9b9c47cf
commit
df320474c5
|
@ -21,10 +21,12 @@ import six
|
|||
|
||||
class NetworkSegment(object):
|
||||
"""Represents a Neutron network segment"""
|
||||
def __init__(self, network_type, physical_network, segmentation_id):
|
||||
def __init__(self, network_type, physical_network, segmentation_id,
|
||||
mtu=None):
|
||||
self.network_type = network_type
|
||||
self.physical_network = physical_network
|
||||
self.segmentation_id = segmentation_id
|
||||
self.mtu = mtu
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
|
|
|
@ -245,7 +245,8 @@ class CommonAgentLoop(service.Service):
|
|||
segment = amb.NetworkSegment(
|
||||
device_details.get('network_type'),
|
||||
device_details['physical_network'],
|
||||
device_details.get('segmentation_id')
|
||||
device_details.get('segmentation_id'),
|
||||
device_details.get('mtu')
|
||||
)
|
||||
network_id = device_details['network_id']
|
||||
self.rpc_callbacks.add_network(network_id, segment)
|
||||
|
|
|
@ -445,12 +445,12 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
network_id: network_id})
|
||||
|
||||
def add_tap_interface(self, network_id, network_type, physical_network,
|
||||
segmentation_id, tap_device_name, device_owner):
|
||||
segmentation_id, tap_device_name, device_owner, mtu):
|
||||
"""Add tap interface and handle interface missing exceptions."""
|
||||
try:
|
||||
return self._add_tap_interface(network_id, network_type,
|
||||
physical_network, segmentation_id,
|
||||
tap_device_name, device_owner)
|
||||
tap_device_name, device_owner, mtu)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception() as ctx:
|
||||
if not ip_lib.device_exists(tap_device_name):
|
||||
|
@ -461,7 +461,7 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
return False
|
||||
|
||||
def _add_tap_interface(self, network_id, network_type, physical_network,
|
||||
segmentation_id, tap_device_name, device_owner):
|
||||
segmentation_id, tap_device_name, device_owner, mtu):
|
||||
"""Add tap interface.
|
||||
|
||||
If a VIF has been plugged into a network, this function will
|
||||
|
@ -483,6 +483,12 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
physical_network,
|
||||
segmentation_id):
|
||||
return False
|
||||
if mtu: # <-None with device_details from older neutron servers.
|
||||
# we ensure the MTU here because libvirt does not set the
|
||||
# MTU of a bridge it creates and the tap device it creates will
|
||||
# inherit from the bridge its plugged into, which will be 1500
|
||||
# at the time. See bug/1684326 for details.
|
||||
self._set_tap_mtu(tap_device_name, mtu)
|
||||
# Avoid messing with plugging devices into a bridge that the agent
|
||||
# does not own
|
||||
if not device_owner.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX):
|
||||
|
@ -504,12 +510,16 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
|||
"thus added elsewhere.", data)
|
||||
return True
|
||||
|
||||
def _set_tap_mtu(self, tap_device_name, mtu):
|
||||
ip_lib.IPDevice(tap_device_name).link.set_mtu(mtu)
|
||||
|
||||
def plug_interface(self, network_id, network_segment, tap_name,
|
||||
device_owner):
|
||||
return self.add_tap_interface(network_id, network_segment.network_type,
|
||||
network_segment.physical_network,
|
||||
network_segment.segmentation_id,
|
||||
tap_name, device_owner)
|
||||
tap_name, device_owner,
|
||||
network_segment.mtu)
|
||||
|
||||
def delete_bridge(self, bridge_name):
|
||||
bridge_device = bridge_lib.BridgeDevice(bridge_name)
|
||||
|
|
|
@ -120,6 +120,7 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
|
|||
'network_type': segment[api.NETWORK_TYPE],
|
||||
'segmentation_id': segment[api.SEGMENTATION_ID],
|
||||
'physical_network': segment[api.PHYSICAL_NETWORK],
|
||||
'mtu': port_context.network._network.get('mtu'),
|
||||
'fixed_ips': port['fixed_ips'],
|
||||
'device_owner': port['device_owner'],
|
||||
'allowed_address_pairs': port['allowed_address_pairs'],
|
||||
|
|
|
@ -478,7 +478,7 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
self.assertFalse(self.lbm.add_tap_interface("123",
|
||||
p_const.TYPE_VLAN,
|
||||
"physnet1", None,
|
||||
"tap1", "foo"))
|
||||
"tap1", "foo", None))
|
||||
|
||||
@mock.patch.object(ip_lib, "device_exists", return_value=True)
|
||||
def test_add_tap_interface_with_other_error(self, exists):
|
||||
|
@ -486,7 +486,7 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
side_effect=RuntimeError("No more fuel")):
|
||||
self.assertRaises(RuntimeError, self.lbm.add_tap_interface, "123",
|
||||
p_const.TYPE_VLAN, "physnet1", None, "tap1",
|
||||
"foo")
|
||||
"foo", None)
|
||||
|
||||
def test_add_tap_interface_owner_compute(self):
|
||||
with mock.patch.object(ip_lib, "device_exists"):
|
||||
|
@ -495,7 +495,7 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
p_const.TYPE_LOCAL,
|
||||
"physnet1", None,
|
||||
"tap1",
|
||||
"compute:1"))
|
||||
"compute:1", None))
|
||||
|
||||
def _test_add_tap_interface(self, dev_owner_prefix):
|
||||
with mock.patch.object(ip_lib, "device_exists") as de_fn:
|
||||
|
@ -503,13 +503,14 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
self.assertFalse(
|
||||
self.lbm.add_tap_interface("123", p_const.TYPE_VLAN,
|
||||
"physnet1", "1", "tap1",
|
||||
dev_owner_prefix))
|
||||
dev_owner_prefix, None))
|
||||
|
||||
de_fn.return_value = True
|
||||
bridge_device = mock.Mock()
|
||||
with mock.patch.object(self.lbm, "ensure_local_bridge") as en_fn,\
|
||||
mock.patch.object(bridge_lib, "BridgeDevice",
|
||||
return_value=bridge_device), \
|
||||
mock.patch.object(self.lbm, '_set_tap_mtu') as set_tap, \
|
||||
mock.patch.object(bridge_lib.BridgeDevice,
|
||||
"get_interface_bridge") as get_br:
|
||||
bridge_device.addif.retun_value = False
|
||||
|
@ -518,7 +519,8 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
p_const.TYPE_LOCAL,
|
||||
"physnet1", None,
|
||||
"tap1",
|
||||
dev_owner_prefix))
|
||||
dev_owner_prefix,
|
||||
None))
|
||||
en_fn.assert_called_with("123", "brq123")
|
||||
|
||||
self.lbm.bridge_mappings = {"physnet1": "brq999"}
|
||||
|
@ -526,7 +528,9 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
p_const.TYPE_LOCAL,
|
||||
"physnet1", None,
|
||||
"tap1",
|
||||
dev_owner_prefix))
|
||||
dev_owner_prefix,
|
||||
8765))
|
||||
set_tap.assert_called_with('tap1', 8765)
|
||||
en_fn.assert_called_with("123", "brq999")
|
||||
|
||||
get_br.return_value = False
|
||||
|
@ -535,7 +539,8 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
p_const.TYPE_LOCAL,
|
||||
"physnet1", None,
|
||||
"tap1",
|
||||
dev_owner_prefix))
|
||||
dev_owner_prefix,
|
||||
None))
|
||||
with mock.patch.object(self.lbm,
|
||||
"ensure_physical_in_bridge") as ens_fn:
|
||||
ens_fn.return_value = False
|
||||
|
@ -543,7 +548,8 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
p_const.TYPE_VLAN,
|
||||
"physnet1", "1",
|
||||
"tap1",
|
||||
dev_owner_prefix))
|
||||
dev_owner_prefix,
|
||||
None))
|
||||
|
||||
def test_add_tap_interface_owner_network(self):
|
||||
self._test_add_tap_interface(constants.DEVICE_OWNER_NETWORK_PREFIX)
|
||||
|
@ -552,13 +558,14 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
|||
self._test_add_tap_interface(constants.DEVICE_OWNER_NEUTRON_PREFIX)
|
||||
|
||||
def test_plug_interface(self):
|
||||
segment = amb.NetworkSegment(p_const.TYPE_VLAN, "physnet-1", "1")
|
||||
segment = amb.NetworkSegment(p_const.TYPE_VLAN, "physnet-1", "1", 1777)
|
||||
with mock.patch.object(self.lbm, "add_tap_interface") as add_tap:
|
||||
self.lbm.plug_interface("123", segment, "tap234",
|
||||
constants.DEVICE_OWNER_NETWORK_PREFIX)
|
||||
add_tap.assert_called_with("123", p_const.TYPE_VLAN, "physnet-1",
|
||||
"1", "tap234",
|
||||
constants.DEVICE_OWNER_NETWORK_PREFIX)
|
||||
constants.DEVICE_OWNER_NETWORK_PREFIX,
|
||||
1777)
|
||||
|
||||
def test_delete_bridge(self):
|
||||
with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\
|
||||
|
|
Loading…
Reference in New Issue