stop setting mtu when plugging vhost-user ports
vhost-user is a userspace protocol to establish connectivity
between a virto-net frontend typically qemu and a
userspace virtio backend such as ovs with dpdk.
vhost-user interfaces exist only in userspace from the host perspective
and are not represented in the linux networking stack as kernel netdevs.
As a result attempting to set the mtu on a vhost-user interface
using ifconfig or ip link will fail with a device not found error.
- this change removes a call to _set_device_mtu when plugging
vhost-user interfaces.
- this change prevents the device not found error from occurring
which stopped vms booting with vhost-user interfaces
due to an uncaught exception resulting in a failure to set the
interface type in ovs.
- this change make creating vhost-user interface
an atomic action.
This latent bug is only triggered when the mtu value is set to a
value other than 0 which was the default proir to mitaka.
Conflicts:
nova/network/model.py
nova/tests/unit/virt/libvirt/test_vif.py
nova/virt/libvirt/vif.py
Change-Id: I2e17723d5052d57cd1557bd8a173c06ea0dcb2d4
Closes-Bug: #1533876
(cherry picked from commit adf7ba61dd
)
This commit is contained in:
parent
80d9e63db3
commit
98464d54d0
|
@ -37,6 +37,7 @@ import six
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _, _LE, _LW
|
from nova.i18n import _, _LE, _LW
|
||||||
|
from nova.network import model as network_model
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova import paths
|
from nova import paths
|
||||||
from nova.pci import utils as pci_utils
|
from nova.pci import utils as pci_utils
|
||||||
|
@ -1368,15 +1369,34 @@ def _ovs_vsctl(args):
|
||||||
raise exception.AgentError(method=full_args)
|
raise exception.AgentError(method=full_args)
|
||||||
|
|
||||||
|
|
||||||
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id, mtu=None):
|
def _create_ovs_vif_cmd(bridge, dev, iface_id, mac,
|
||||||
_ovs_vsctl(['--', '--if-exists', 'del-port', dev, '--',
|
instance_id, interface_type=None):
|
||||||
'add-port', bridge, dev,
|
cmd = ['--', '--if-exists', 'del-port', dev, '--',
|
||||||
'--', 'set', 'Interface', dev,
|
'add-port', bridge, dev,
|
||||||
'external-ids:iface-id=%s' % iface_id,
|
'--', 'set', 'Interface', dev,
|
||||||
'external-ids:iface-status=active',
|
'external-ids:iface-id=%s' % iface_id,
|
||||||
'external-ids:attached-mac=%s' % mac,
|
'external-ids:iface-status=active',
|
||||||
'external-ids:vm-uuid=%s' % instance_id])
|
'external-ids:attached-mac=%s' % mac,
|
||||||
_set_device_mtu(dev, mtu)
|
'external-ids:vm-uuid=%s' % instance_id]
|
||||||
|
if interface_type:
|
||||||
|
cmd += ['type=%s' % interface_type]
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
|
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id,
|
||||||
|
mtu=None, interface_type=None):
|
||||||
|
_ovs_vsctl(_create_ovs_vif_cmd(bridge, dev, iface_id,
|
||||||
|
mac, instance_id,
|
||||||
|
interface_type))
|
||||||
|
# Note at present there is no support for setting the
|
||||||
|
# mtu for vhost-user type ports.
|
||||||
|
if interface_type != network_model.OVS_VHOSTUSER_INTERFACE_TYPE:
|
||||||
|
_set_device_mtu(dev, mtu)
|
||||||
|
else:
|
||||||
|
LOG.debug("MTU not set on %(interface_name)s interface "
|
||||||
|
"of type %(interface_type)s.",
|
||||||
|
{'interface_name': dev,
|
||||||
|
'interface_type': interface_type})
|
||||||
|
|
||||||
|
|
||||||
def delete_ovs_vif_port(bridge, dev):
|
def delete_ovs_vif_port(bridge, dev):
|
||||||
|
@ -1384,10 +1404,6 @@ def delete_ovs_vif_port(bridge, dev):
|
||||||
delete_net_dev(dev)
|
delete_net_dev(dev)
|
||||||
|
|
||||||
|
|
||||||
def ovs_set_vhostuser_port_type(dev):
|
|
||||||
_ovs_vsctl(['--', 'set', 'Interface', dev, 'type=dpdkvhostuser'])
|
|
||||||
|
|
||||||
|
|
||||||
def create_ivs_vif_port(dev, iface_id, mac, instance_id):
|
def create_ivs_vif_port(dev, iface_id, mac, instance_id):
|
||||||
utils.execute('ivs-ctl', 'add-port',
|
utils.execute('ivs-ctl', 'add-port',
|
||||||
dev, run_as_root=True)
|
dev, run_as_root=True)
|
||||||
|
|
|
@ -77,6 +77,8 @@ VIF_DETAILS_VHOSTUSER_SOCKET = 'vhostuser_socket'
|
||||||
# Specifies whether vhost-user socket should be plugged
|
# Specifies whether vhost-user socket should be plugged
|
||||||
# into ovs bridge. Valid values are True and False
|
# into ovs bridge. Valid values are True and False
|
||||||
VIF_DETAILS_VHOSTUSER_OVS_PLUG = 'vhostuser_ovs_plug'
|
VIF_DETAILS_VHOSTUSER_OVS_PLUG = 'vhostuser_ovs_plug'
|
||||||
|
# ovs vhost user interface type name
|
||||||
|
OVS_VHOSTUSER_INTERFACE_TYPE = 'dpdkvhostuser'
|
||||||
|
|
||||||
# Constants for dictionary keys in the 'vif_details' field that are
|
# Constants for dictionary keys in the 'vif_details' field that are
|
||||||
# valid for VIF_TYPE_TAP.
|
# valid for VIF_TYPE_TAP.
|
||||||
|
|
|
@ -34,6 +34,7 @@ from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.network import driver
|
from nova.network import driver
|
||||||
from nova.network import linux_net
|
from nova.network import linux_net
|
||||||
|
from nova.network import model as network_model
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
@ -1210,13 +1211,37 @@ class LinuxNetworkTestCase(test.NoDBTestCase):
|
||||||
linux_net._set_device_mtu('fake-dev')
|
linux_net._set_device_mtu('fake-dev')
|
||||||
ex.assert_has_calls(calls)
|
ex.assert_has_calls(calls)
|
||||||
|
|
||||||
def _ovs_vif_port(self, calls):
|
def _ovs_vif_port(self, calls, interface_type=None):
|
||||||
with mock.patch.object(utils, 'execute', return_value=('', '')) as ex:
|
with mock.patch.object(utils, 'execute', return_value=('', '')) as ex:
|
||||||
linux_net.create_ovs_vif_port('fake-bridge', 'fake-dev',
|
linux_net.create_ovs_vif_port('fake-bridge', 'fake-dev',
|
||||||
'fake-iface-id', 'fake-mac',
|
'fake-iface-id', 'fake-mac',
|
||||||
'fake-instance-uuid')
|
'fake-instance-uuid',
|
||||||
|
interface_type=interface_type)
|
||||||
ex.assert_has_calls(calls)
|
ex.assert_has_calls(calls)
|
||||||
|
|
||||||
|
def test_ovs_vif_port_cmd(self):
|
||||||
|
expected = ['--', '--if-exists',
|
||||||
|
'del-port', 'fake-dev', '--', 'add-port',
|
||||||
|
'fake-bridge', 'fake-dev',
|
||||||
|
'--', 'set', 'Interface', 'fake-dev',
|
||||||
|
'external-ids:iface-id=fake-iface-id',
|
||||||
|
'external-ids:iface-status=active',
|
||||||
|
'external-ids:attached-mac=fake-mac',
|
||||||
|
'external-ids:vm-uuid=fake-instance-uuid'
|
||||||
|
]
|
||||||
|
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
|
||||||
|
'fake-iface-id', 'fake-mac',
|
||||||
|
'fake-instance-uuid')
|
||||||
|
|
||||||
|
self.assertEqual(expected, cmd)
|
||||||
|
|
||||||
|
expected += ['type=fake-type']
|
||||||
|
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
|
||||||
|
'fake-iface-id', 'fake-mac',
|
||||||
|
'fake-instance-uuid',
|
||||||
|
'fake-type')
|
||||||
|
self.assertEqual(expected, cmd)
|
||||||
|
|
||||||
def test_ovs_vif_port(self):
|
def test_ovs_vif_port(self):
|
||||||
calls = [
|
calls = [
|
||||||
mock.call('ovs-vsctl', '--timeout=120', '--', '--if-exists',
|
mock.call('ovs-vsctl', '--timeout=120', '--', '--if-exists',
|
||||||
|
@ -1231,6 +1256,22 @@ class LinuxNetworkTestCase(test.NoDBTestCase):
|
||||||
]
|
]
|
||||||
self._ovs_vif_port(calls)
|
self._ovs_vif_port(calls)
|
||||||
|
|
||||||
|
@mock.patch.object(linux_net, '_ovs_vsctl')
|
||||||
|
@mock.patch.object(linux_net, '_create_ovs_vif_cmd')
|
||||||
|
@mock.patch.object(linux_net, '_set_device_mtu')
|
||||||
|
def test_ovs_vif_port_with_type_vhostuser(self, mock_set_device_mtu,
|
||||||
|
mock_create_cmd, mock_vsctl):
|
||||||
|
linux_net.create_ovs_vif_port(
|
||||||
|
'fake-bridge',
|
||||||
|
'fake-dev', 'fake-iface-id', 'fake-mac',
|
||||||
|
"fake-instance-uuid", mtu=1500,
|
||||||
|
interface_type=network_model.OVS_VHOSTUSER_INTERFACE_TYPE)
|
||||||
|
mock_create_cmd.assert_called_once_with('fake-bridge',
|
||||||
|
'fake-dev', 'fake-iface-id', 'fake-mac',
|
||||||
|
"fake-instance-uuid", network_model.OVS_VHOSTUSER_INTERFACE_TYPE)
|
||||||
|
self.assertFalse(mock_set_device_mtu.called)
|
||||||
|
self.assertTrue(mock_vsctl.called)
|
||||||
|
|
||||||
def test_ovs_vif_port_with_mtu(self):
|
def test_ovs_vif_port_with_mtu(self):
|
||||||
self.flags(network_device_mtu=10000)
|
self.flags(network_device_mtu=10000)
|
||||||
calls = [
|
calls = [
|
||||||
|
@ -1371,16 +1412,6 @@ class LinuxNetworkTestCase(test.NoDBTestCase):
|
||||||
self.assertEqual(2, len(executes))
|
self.assertEqual(2, len(executes))
|
||||||
self.mox.UnsetStubs()
|
self.mox.UnsetStubs()
|
||||||
|
|
||||||
def test_ovs_set_vhostuser_type(self):
|
|
||||||
calls = [
|
|
||||||
mock.call('ovs-vsctl', '--timeout=120', '--', 'set',
|
|
||||||
'Interface', 'fake-dev', 'type=dpdkvhostuser',
|
|
||||||
run_as_root=True)
|
|
||||||
]
|
|
||||||
with mock.patch.object(utils, 'execute', return_value=('', '')) as ex:
|
|
||||||
linux_net.ovs_set_vhostuser_port_type('fake-dev')
|
|
||||||
ex.assert_has_calls(calls)
|
|
||||||
|
|
||||||
@mock.patch('os.path.exists', return_value=True)
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
@mock.patch('nova.utils.execute')
|
@mock.patch('nova.utils.execute')
|
||||||
def test_remove_bridge(self, mock_execute, mock_exists):
|
def test_remove_bridge(self, mock_execute, mock_exists):
|
||||||
|
|
|
@ -1312,23 +1312,18 @@ class LibvirtVifTestCase(test.NoDBTestCase):
|
||||||
def test_vhostuser_ovs_plug(self):
|
def test_vhostuser_ovs_plug(self):
|
||||||
|
|
||||||
calls = {
|
calls = {
|
||||||
'create_ovs_vif_port': [mock.call('br0',
|
'create_ovs_vif_port': [
|
||||||
'usv-xxx-yyy-zzz',
|
mock.call(
|
||||||
'aaa-bbb-ccc',
|
'br0', 'usv-xxx-yyy-zzz', 'aaa-bbb-ccc',
|
||||||
'ca:fe:de:ad:be:ef',
|
'ca:fe:de:ad:be:ef', 'instance-uuid', 9000,
|
||||||
'instance-uuid',
|
interface_type=network_model.OVS_VHOSTUSER_INTERFACE_TYPE
|
||||||
9000)],
|
)]
|
||||||
'ovs_set_vhostuser_port_type': [mock.call('usv-xxx-yyy-zzz')]
|
|
||||||
}
|
}
|
||||||
with contextlib.nested(
|
with mock.patch.object(linux_net,
|
||||||
mock.patch.object(linux_net, 'create_ovs_vif_port'),
|
'create_ovs_vif_port') as create_ovs_vif_port:
|
||||||
mock.patch.object(linux_net, 'ovs_set_vhostuser_port_type')
|
|
||||||
) as (create_ovs_vif_port, ovs_set_vhostuser_port_type):
|
|
||||||
d = vif.LibvirtGenericVIFDriver()
|
d = vif.LibvirtGenericVIFDriver()
|
||||||
d.plug_vhostuser(self.instance, self.vif_vhostuser_ovs)
|
d.plug_vhostuser(self.instance, self.vif_vhostuser_ovs)
|
||||||
create_ovs_vif_port.assert_has_calls(calls['create_ovs_vif_port'])
|
create_ovs_vif_port.assert_has_calls(calls['create_ovs_vif_port'])
|
||||||
ovs_set_vhostuser_port_type.assert_has_calls(
|
|
||||||
calls['ovs_set_vhostuser_port_type'])
|
|
||||||
|
|
||||||
def test_vhostuser_ovs_unplug(self):
|
def test_vhostuser_ovs_unplug(self):
|
||||||
calls = {
|
calls = {
|
||||||
|
|
|
@ -675,10 +675,11 @@ class LibvirtGenericVIFDriver(object):
|
||||||
port_name = os.path.basename(
|
port_name = os.path.basename(
|
||||||
vif['details'][network_model.VIF_DETAILS_VHOSTUSER_SOCKET])
|
vif['details'][network_model.VIF_DETAILS_VHOSTUSER_SOCKET])
|
||||||
mtu = vif['network'].get_meta('mtu')
|
mtu = vif['network'].get_meta('mtu')
|
||||||
linux_net.create_ovs_vif_port(self.get_bridge_name(vif),
|
linux_net.create_ovs_vif_port(
|
||||||
port_name, iface_id, vif['address'],
|
self.get_bridge_name(vif),
|
||||||
instance.uuid, mtu)
|
port_name, iface_id, vif['address'],
|
||||||
linux_net.ovs_set_vhostuser_port_type(port_name)
|
instance.uuid, mtu,
|
||||||
|
interface_type=network_model.OVS_VHOSTUSER_INTERFACE_TYPE)
|
||||||
|
|
||||||
def plug_vrouter(self, instance, vif):
|
def plug_vrouter(self, instance, vif):
|
||||||
"""Plug into Contrail's network port
|
"""Plug into Contrail's network port
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- When plugging virtual interfaces of type vhost-user the MTU value will
|
||||||
|
not be applied to the interface by nova. vhost-user ports exist only in
|
||||||
|
userspace and are not backed by kernel netdevs, for this reason it is
|
||||||
|
not possible to set the mtu on a vhost-user interface using standard
|
||||||
|
tools such as ifconfig or ip link.
|
Loading…
Reference in New Issue