Allow tap interface with multiqueue

When vif_type="tap" (such as when using calico),
attempting to create an instance using an image that has
the property hw_vif_multiqueue_enabled=True fails, because
the interface is always being created without multiqueue
flags.

This change checks if the property is defined and passes
the multiqueue parameter to create the tap interface
accordingly.

In case the multiqueue parameter is passed but the
vif_model is not virtio (or unspecified), the old
behavior is maintained.

Change-Id: I0307c43dcd0cace1620d2ac75925651d4ee2e96c
Closes-bug: #1893263
This commit is contained in:
Rodrigo Barbieri 2020-08-27 17:20:19 -03:00
parent 3b41633222
commit 84cfc8e9ab
3 changed files with 111 additions and 18 deletions

View File

@ -1057,10 +1057,71 @@ class LibvirtVifTestCase(test.NoDBTestCase):
@mock.patch('nova.privsep.linux_net.device_exists', return_value=True) @mock.patch('nova.privsep.linux_net.device_exists', return_value=True)
@mock.patch('nova.privsep.linux_net.set_device_mtu') @mock.patch('nova.privsep.linux_net.set_device_mtu')
@mock.patch('nova.privsep.linux_net.create_tap_dev') @mock.patch('nova.privsep.linux_net.create_tap_dev')
def test_plug_tap(self, mock_create_tap_dev, mock_set_mtu, def test_plug_tap_kvm_virtio(self, mock_create_tap_dev, mock_set_mtu,
mock_device_exists): mock_device_exists):
d = vif.LibvirtGenericVIFDriver()
d.plug(self.instance, self.vif_tap) d1 = vif.LibvirtGenericVIFDriver()
ins = objects.Instance(
id=1, uuid='f0000000-0000-0000-0000-000000000001',
project_id=723, system_metadata={}
)
d1.plug(ins, self.vif_tap)
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz', None,
multiqueue=False)
mock_create_tap_dev.reset_mock()
d2 = vif.LibvirtGenericVIFDriver()
mq_ins = objects.Instance(
id=1, uuid='f0000000-0000-0000-0000-000000000001',
project_id=723, system_metadata={
'image_hw_vif_multiqueue_enabled': 'True'
}
)
d2.plug(mq_ins, self.vif_tap)
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz', None,
multiqueue=True)
@mock.patch('nova.privsep.linux_net.device_exists', return_value=True)
@mock.patch('nova.privsep.linux_net.set_device_mtu')
@mock.patch('nova.privsep.linux_net.create_tap_dev')
def test_plug_tap_mq_ignored_virt_type(
self, mock_create_tap_dev, mock_set_mtu, mock_device_exists):
self.flags(use_virtio_for_bridges=True,
virt_type='xen',
group='libvirt')
d1 = vif.LibvirtGenericVIFDriver()
ins = objects.Instance(
id=1, uuid='f0000000-0000-0000-0000-000000000001',
project_id=723, system_metadata={
'image_hw_vif_multiqueue_enabled': 'True'
}
)
d1.plug(ins, self.vif_tap)
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz',
None,
multiqueue=False)
@mock.patch('nova.privsep.linux_net.device_exists', return_value=True)
@mock.patch('nova.privsep.linux_net.set_device_mtu')
@mock.patch('nova.privsep.linux_net.create_tap_dev')
def test_plug_tap_mq_ignored_vif_model(
self, mock_create_tap_dev, mock_set_mtu, mock_device_exists):
d1 = vif.LibvirtGenericVIFDriver()
ins = objects.Instance(
id=1, uuid='f0000000-0000-0000-0000-000000000001',
project_id=723, system_metadata={
'image_hw_vif_multiqueue_enabled': 'True',
'image_hw_vif_model': 'e1000',
}
)
d1.plug(ins, self.vif_tap)
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz',
None,
multiqueue=False)
def test_unplug_tap(self): def test_unplug_tap(self):
d = vif.LibvirtGenericVIFDriver() d = vif.LibvirtGenericVIFDriver()

View File

@ -157,6 +157,24 @@ class LibvirtGenericVIFDriver(object):
return vif['devname'] return vif['devname']
return ("nic" + vif['id'])[:network_model.NIC_NAME_LEN] return ("nic" + vif['id'])[:network_model.NIC_NAME_LEN]
def get_vif_model(self, image_meta=None, vif_model=None):
model = vif_model
# If the user has specified a 'vif_model' against the
# image then honour that model
if image_meta:
model = osinfo.HardwareProperties(image_meta).network_model
# If the virt type is KVM/QEMU/VZ(Parallels), then use virtio according
# to the global config parameter
if (model is None and CONF.libvirt.virt_type in
('kvm', 'qemu', 'parallels') and
CONF.libvirt.use_virtio_for_bridges):
model = network_model.VIF_MODEL_VIRTIO
return model
def get_base_config(self, instance, mac, image_meta, def get_base_config(self, instance, mac, image_meta,
inst_type, virt_type, vnic_type): inst_type, virt_type, vnic_type):
# TODO(sahid): We should rewrite it. This method handles too # TODO(sahid): We should rewrite it. This method handles too
@ -179,16 +197,9 @@ class LibvirtGenericVIFDriver(object):
rx_queue_size = CONF.libvirt.rx_queue_size rx_queue_size = CONF.libvirt.rx_queue_size
# If the user has specified a 'vif_model' against the # if model has already been defined,
# image then honour that model # image_meta contents will override it
if image_meta: model = self.get_vif_model(image_meta=image_meta, vif_model=model)
model = osinfo.HardwareProperties(image_meta).network_model
# If the virt type is KVM/QEMU/VZ(Parallels), then use virtio according
# to the global config parameter
if (model is None and virt_type in ('kvm', 'qemu', 'parallels') and
CONF.libvirt.use_virtio_for_bridges):
model = network_model.VIF_MODEL_VIRTIO
if not is_vif_model_valid_for_virt(virt_type, model): if not is_vif_model_valid_for_virt(virt_type, model):
raise exception.UnsupportedHardware(model=model, virt=virt_type) raise exception.UnsupportedHardware(model=model, virt=virt_type)
@ -244,10 +255,7 @@ class LibvirtGenericVIFDriver(object):
""" """
driver = None driver = None
vhost_queues = None vhost_queues = None
if not isinstance(image_meta, objects.ImageMeta): if self._requests_multiqueue(image_meta):
image_meta = objects.ImageMeta.from_dict(image_meta)
img_props = image_meta.properties
if img_props.get('hw_vif_multiqueue_enabled'):
driver = 'vhost' driver = 'vhost'
max_tap_queues = self._get_max_tap_queues() max_tap_queues = self._get_max_tap_queues()
if max_tap_queues: if max_tap_queues:
@ -258,6 +266,19 @@ class LibvirtGenericVIFDriver(object):
return (driver, vhost_queues) return (driver, vhost_queues)
def _requests_multiqueue(self, image_meta):
"""Check if multiqueue property is set in the image metadata."""
if not isinstance(image_meta, objects.ImageMeta):
image_meta = objects.ImageMeta.from_dict(image_meta)
img_props = image_meta.properties
if img_props.get('hw_vif_multiqueue_enabled'):
return True
return False
def _get_max_tap_queues(self): def _get_max_tap_queues(self):
# Note(sean-k-mooney): some linux distros have backported # Note(sean-k-mooney): some linux distros have backported
# changes for newer kernels which make the kernel version # changes for newer kernels which make the kernel version
@ -641,7 +662,13 @@ class LibvirtGenericVIFDriver(object):
"""Plug a VIF_TYPE_TAP virtual interface.""" """Plug a VIF_TYPE_TAP virtual interface."""
dev = self.get_vif_devname(vif) dev = self.get_vif_devname(vif)
mac = vif['details'].get(network_model.VIF_DETAILS_TAP_MAC_ADDRESS) mac = vif['details'].get(network_model.VIF_DETAILS_TAP_MAC_ADDRESS)
nova.privsep.linux_net.create_tap_dev(dev, mac) image_meta = instance.image_meta
vif_model = self.get_vif_model(image_meta=image_meta)
# TODO(ganso): explore whether multiqueue works for other vif models
# that go through this code path.
multiqueue = (self._requests_multiqueue(image_meta) and
vif_model == network_model.VIF_MODEL_VIRTIO)
nova.privsep.linux_net.create_tap_dev(dev, mac, multiqueue=multiqueue)
network = vif.get('network') network = vif.get('network')
mtu = network.get_meta('mtu') if network else None mtu = network.get_meta('mtu') if network else None
nova.privsep.linux_net.set_device_mtu(dev, mtu) nova.privsep.linux_net.set_device_mtu(dev, mtu)

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Addressed an issue that prevented instances using multiqueue feature from
being created successfully when their vif_type is TAP.