diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py index 648d6dc408a2..209211c563bf 100644 --- a/nova/tests/unit/virt/libvirt/test_vif.py +++ b/nova/tests/unit/virt/libvirt/test_vif.py @@ -1123,10 +1123,71 @@ class LibvirtVifTestCase(test.NoDBTestCase): @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() diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 89c259a61811..d4801ad887fa 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -126,6 +126,24 @@ class LibvirtGenericVIFDriver(object): 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, host): # TODO(sahid): We should rewrite it. This method handles too @@ -146,16 +164,9 @@ class LibvirtGenericVIFDriver(object): conf, mac, model, driver, vhost_queues, rx_queue_size) return conf - # 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) @@ -212,10 +223,7 @@ class LibvirtGenericVIFDriver(object): """ 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: @@ -226,6 +234,19 @@ class LibvirtGenericVIFDriver(object): 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(kengo.sakai): In kernels prior to 3.0, # multiple queues on a tap interface is not supported. @@ -690,7 +711,13 @@ class LibvirtGenericVIFDriver(object): """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) diff --git a/releasenotes/notes/bug-1893263-769acadc4b6141d0.yaml b/releasenotes/notes/bug-1893263-769acadc4b6141d0.yaml new file mode 100644 index 000000000000..3874489513b7 --- /dev/null +++ b/releasenotes/notes/bug-1893263-769acadc4b6141d0.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Addressed an issue that prevented instances using multiqueue feature from + being created successfully when their vif_type is TAP.