Skip to content

Commit

Permalink
Allow tap interface with multiqueue
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rodrigogansobarbieri committed Sep 11, 2020
1 parent 3b41633 commit 84cfc8e
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 18 deletions.
67 changes: 64 additions & 3 deletions nova/tests/unit/virt/libvirt/test_vif.py
Original file line number Diff line number Diff line change
Expand Up @@ -1057,10 +1057,71 @@ def test_tap_ethernet_vif_driver(self):
@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(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):
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):
d = vif.LibvirtGenericVIFDriver()
Expand Down
57 changes: 42 additions & 15 deletions nova/virt/libvirt/vif.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,24 @@ def get_vif_devname(self, vif):
return vif['devname']
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,
inst_type, virt_type, vnic_type):
# TODO(sahid): We should rewrite it. This method handles too
Expand All @@ -179,16 +197,9 @@ def get_base_config(self, instance, mac, image_meta,

rx_queue_size = CONF.libvirt.rx_queue_size

# 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 virt_type in ('kvm', 'qemu', 'parallels') and
CONF.libvirt.use_virtio_for_bridges):
model = network_model.VIF_MODEL_VIRTIO
# if model has already been defined,
# image_meta contents will override it
model = self.get_vif_model(image_meta=image_meta, vif_model=model)

if not is_vif_model_valid_for_virt(virt_type, model):
raise exception.UnsupportedHardware(model=model, virt=virt_type)
Expand Down Expand Up @@ -244,10 +255,7 @@ def _get_virtio_mq_settings(self, image_meta, flavor):
"""
driver = None
vhost_queues = None
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'):
if self._requests_multiqueue(image_meta):
driver = 'vhost'
max_tap_queues = self._get_max_tap_queues()
if max_tap_queues:
Expand All @@ -258,6 +266,19 @@ def _get_virtio_mq_settings(self, image_meta, flavor):

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):
# Note(sean-k-mooney): some linux distros have backported
# changes for newer kernels which make the kernel version
Expand Down Expand Up @@ -641,7 +662,13 @@ def plug_tap(self, instance, vif):
"""Plug a VIF_TYPE_TAP virtual interface."""
dev = self.get_vif_devname(vif)
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')
mtu = network.get_meta('mtu') if network else None
nova.privsep.linux_net.set_device_mtu(dev, mtu)
Expand Down
5 changes: 5 additions & 0 deletions releasenotes/notes/bug-1893263-769acadc4b6141d0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fixes:
- |
Addressed an issue that prevented instances using multiqueue feature from
being created successfully when their vif_type is TAP.

0 comments on commit 84cfc8e

Please sign in to comment.