Skip to content

Commit 84cfc8e

Browse files
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
1 parent 3b41633 commit 84cfc8e

File tree

3 files changed

+111
-18
lines changed

3 files changed

+111
-18
lines changed

nova/tests/unit/virt/libvirt/test_vif.py

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,10 +1057,71 @@ def test_tap_ethernet_vif_driver(self):
10571057
@mock.patch('nova.privsep.linux_net.device_exists', return_value=True)
10581058
@mock.patch('nova.privsep.linux_net.set_device_mtu')
10591059
@mock.patch('nova.privsep.linux_net.create_tap_dev')
1060-
def test_plug_tap(self, mock_create_tap_dev, mock_set_mtu,
1060+
def test_plug_tap_kvm_virtio(self, mock_create_tap_dev, mock_set_mtu,
10611061
mock_device_exists):
1062-
d = vif.LibvirtGenericVIFDriver()
1063-
d.plug(self.instance, self.vif_tap)
1062+
1063+
d1 = vif.LibvirtGenericVIFDriver()
1064+
ins = objects.Instance(
1065+
id=1, uuid='f0000000-0000-0000-0000-000000000001',
1066+
project_id=723, system_metadata={}
1067+
)
1068+
d1.plug(ins, self.vif_tap)
1069+
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz', None,
1070+
multiqueue=False)
1071+
1072+
mock_create_tap_dev.reset_mock()
1073+
1074+
d2 = vif.LibvirtGenericVIFDriver()
1075+
mq_ins = objects.Instance(
1076+
id=1, uuid='f0000000-0000-0000-0000-000000000001',
1077+
project_id=723, system_metadata={
1078+
'image_hw_vif_multiqueue_enabled': 'True'
1079+
}
1080+
)
1081+
d2.plug(mq_ins, self.vif_tap)
1082+
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz', None,
1083+
multiqueue=True)
1084+
1085+
@mock.patch('nova.privsep.linux_net.device_exists', return_value=True)
1086+
@mock.patch('nova.privsep.linux_net.set_device_mtu')
1087+
@mock.patch('nova.privsep.linux_net.create_tap_dev')
1088+
def test_plug_tap_mq_ignored_virt_type(
1089+
self, mock_create_tap_dev, mock_set_mtu, mock_device_exists):
1090+
1091+
self.flags(use_virtio_for_bridges=True,
1092+
virt_type='xen',
1093+
group='libvirt')
1094+
1095+
d1 = vif.LibvirtGenericVIFDriver()
1096+
ins = objects.Instance(
1097+
id=1, uuid='f0000000-0000-0000-0000-000000000001',
1098+
project_id=723, system_metadata={
1099+
'image_hw_vif_multiqueue_enabled': 'True'
1100+
}
1101+
)
1102+
d1.plug(ins, self.vif_tap)
1103+
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz',
1104+
None,
1105+
multiqueue=False)
1106+
1107+
@mock.patch('nova.privsep.linux_net.device_exists', return_value=True)
1108+
@mock.patch('nova.privsep.linux_net.set_device_mtu')
1109+
@mock.patch('nova.privsep.linux_net.create_tap_dev')
1110+
def test_plug_tap_mq_ignored_vif_model(
1111+
self, mock_create_tap_dev, mock_set_mtu, mock_device_exists):
1112+
1113+
d1 = vif.LibvirtGenericVIFDriver()
1114+
ins = objects.Instance(
1115+
id=1, uuid='f0000000-0000-0000-0000-000000000001',
1116+
project_id=723, system_metadata={
1117+
'image_hw_vif_multiqueue_enabled': 'True',
1118+
'image_hw_vif_model': 'e1000',
1119+
}
1120+
)
1121+
d1.plug(ins, self.vif_tap)
1122+
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz',
1123+
None,
1124+
multiqueue=False)
10641125

10651126
def test_unplug_tap(self):
10661127
d = vif.LibvirtGenericVIFDriver()

nova/virt/libvirt/vif.py

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,24 @@ def get_vif_devname(self, vif):
157157
return vif['devname']
158158
return ("nic" + vif['id'])[:network_model.NIC_NAME_LEN]
159159

160+
def get_vif_model(self, image_meta=None, vif_model=None):
161+
162+
model = vif_model
163+
164+
# If the user has specified a 'vif_model' against the
165+
# image then honour that model
166+
if image_meta:
167+
model = osinfo.HardwareProperties(image_meta).network_model
168+
169+
# If the virt type is KVM/QEMU/VZ(Parallels), then use virtio according
170+
# to the global config parameter
171+
if (model is None and CONF.libvirt.virt_type in
172+
('kvm', 'qemu', 'parallels') and
173+
CONF.libvirt.use_virtio_for_bridges):
174+
model = network_model.VIF_MODEL_VIRTIO
175+
176+
return model
177+
160178
def get_base_config(self, instance, mac, image_meta,
161179
inst_type, virt_type, vnic_type):
162180
# TODO(sahid): We should rewrite it. This method handles too
@@ -179,16 +197,9 @@ def get_base_config(self, instance, mac, image_meta,
179197

180198
rx_queue_size = CONF.libvirt.rx_queue_size
181199

182-
# If the user has specified a 'vif_model' against the
183-
# image then honour that model
184-
if image_meta:
185-
model = osinfo.HardwareProperties(image_meta).network_model
186-
187-
# If the virt type is KVM/QEMU/VZ(Parallels), then use virtio according
188-
# to the global config parameter
189-
if (model is None and virt_type in ('kvm', 'qemu', 'parallels') and
190-
CONF.libvirt.use_virtio_for_bridges):
191-
model = network_model.VIF_MODEL_VIRTIO
200+
# if model has already been defined,
201+
# image_meta contents will override it
202+
model = self.get_vif_model(image_meta=image_meta, vif_model=model)
192203

193204
if not is_vif_model_valid_for_virt(virt_type, model):
194205
raise exception.UnsupportedHardware(model=model, virt=virt_type)
@@ -244,10 +255,7 @@ def _get_virtio_mq_settings(self, image_meta, flavor):
244255
"""
245256
driver = None
246257
vhost_queues = None
247-
if not isinstance(image_meta, objects.ImageMeta):
248-
image_meta = objects.ImageMeta.from_dict(image_meta)
249-
img_props = image_meta.properties
250-
if img_props.get('hw_vif_multiqueue_enabled'):
258+
if self._requests_multiqueue(image_meta):
251259
driver = 'vhost'
252260
max_tap_queues = self._get_max_tap_queues()
253261
if max_tap_queues:
@@ -258,6 +266,19 @@ def _get_virtio_mq_settings(self, image_meta, flavor):
258266

259267
return (driver, vhost_queues)
260268

269+
def _requests_multiqueue(self, image_meta):
270+
"""Check if multiqueue property is set in the image metadata."""
271+
272+
if not isinstance(image_meta, objects.ImageMeta):
273+
image_meta = objects.ImageMeta.from_dict(image_meta)
274+
275+
img_props = image_meta.properties
276+
277+
if img_props.get('hw_vif_multiqueue_enabled'):
278+
return True
279+
280+
return False
281+
261282
def _get_max_tap_queues(self):
262283
# Note(sean-k-mooney): some linux distros have backported
263284
# changes for newer kernels which make the kernel version
@@ -641,7 +662,13 @@ def plug_tap(self, instance, vif):
641662
"""Plug a VIF_TYPE_TAP virtual interface."""
642663
dev = self.get_vif_devname(vif)
643664
mac = vif['details'].get(network_model.VIF_DETAILS_TAP_MAC_ADDRESS)
644-
nova.privsep.linux_net.create_tap_dev(dev, mac)
665+
image_meta = instance.image_meta
666+
vif_model = self.get_vif_model(image_meta=image_meta)
667+
# TODO(ganso): explore whether multiqueue works for other vif models
668+
# that go through this code path.
669+
multiqueue = (self._requests_multiqueue(image_meta) and
670+
vif_model == network_model.VIF_MODEL_VIRTIO)
671+
nova.privsep.linux_net.create_tap_dev(dev, mac, multiqueue=multiqueue)
645672
network = vif.get('network')
646673
mtu = network.get_meta('mtu') if network else None
647674
nova.privsep.linux_net.set_device_mtu(dev, mtu)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
Addressed an issue that prevented instances using multiqueue feature from
5+
being created successfully when their vif_type is TAP.

0 commit comments

Comments
 (0)