Skip to content

Commit 5d65680

Browse files
committed
libvirt: Set driver_iommu when attaching virtio devices to SEV instance
As called out in the original spec [1] virtio devices attached to a SEV enabled instance must have the iommu attribute enabled. This was done within the original implementation of the spec for all virtio devices defined when initially spawning the instance but does not include volume and interfaces that are later hot plugged. This change corrects this for both volumes and nics and in doing so slightly refactors the original designer code to make it usable in both cases. [1] https://specs.openstack.org/openstack/nova-specs/specs/train/implemented/amd-sev-libvirt-support.html#proposed-change Closes-Bug: #1930734 Change-Id: I11131a3f90b8af85e7151b519fb26d225629c391 (cherry picked from commit 4d8bf15)
1 parent 3bc0c90 commit 5d65680

File tree

6 files changed

+215
-74
lines changed

6 files changed

+215
-74
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def test_set_vif_mtu_config(self):
244244
designer.set_vif_mtu_config(conf, 9000)
245245
self.assertEqual(9000, conf.mtu)
246246

247-
def test_set_driver_iommu_for_sev(self):
247+
def test_set_driver_iommu(self):
248248
conf = fake_libvirt_data.fake_kvm_guest()
249249

250250
# obj.devices[11]
@@ -253,7 +253,7 @@ def test_set_driver_iommu_for_sev(self):
253253
controller.index = 0
254254
conf.add_device(controller)
255255

256-
designer.set_driver_iommu_for_sev(conf)
256+
designer.set_driver_iommu_for_all_devices(conf)
257257

258258
# All disks/interfaces/memballoon are expected to be virtio,
259259
# thus driver_iommu should be on

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

Lines changed: 145 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3299,7 +3299,7 @@ def test_get_guest_config_sev_no_feature(self):
32993299
self._setup_sev_guest)
33003300

33013301
@mock.patch.object(host.Host, 'get_domain_capabilities')
3302-
@mock.patch.object(designer, 'set_driver_iommu_for_sev')
3302+
@mock.patch.object(designer, 'set_driver_iommu_for_all_devices')
33033303
def test_get_guest_config_sev(self, mock_designer, fake_domain_caps):
33043304
self._setup_fake_domain_caps(fake_domain_caps)
33053305
cfg = self._setup_sev_guest()
@@ -6459,7 +6459,7 @@ def test_get_guest_config_with_qga_through_image_meta(self):
64596459
self.assertEqual(cfg.devices[6].target_name, "org.qemu.guest_agent.0")
64606460

64616461
@mock.patch.object(host.Host, 'get_domain_capabilities')
6462-
@mock.patch.object(designer, 'set_driver_iommu_for_sev')
6462+
@mock.patch.object(designer, 'set_driver_iommu_for_all_devices')
64636463
def test_get_guest_config_with_qga_through_image_meta_with_sev(
64646464
self, mock_designer, fake_domain_caps,
64656465
):
@@ -8817,22 +8817,60 @@ def _fake_libvirt_config_guest_disk(self):
88178817

88188818
return fake_config
88198819

8820+
@mock.patch.object(libvirt_driver.LibvirtDriver, '_sev_enabled',
8821+
new=mock.Mock(return_value=False))
88208822
@mock.patch.object(volume_drivers.LibvirtFakeVolumeDriver, 'get_config')
88218823
@mock.patch.object(libvirt_driver.LibvirtDriver, '_set_cache_mode')
8822-
def test_get_volume_config(self, _set_cache_mode, get_config):
8824+
def test_get_volume_config(self, mock_set_cache_mode, mock_get_config):
88238825
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
8824-
connection_info = {'driver_volume_type': 'fake',
8825-
'data': {'device_path': '/fake',
8826-
'access_mode': 'rw'}}
8827-
disk_info = {'bus': 'fake-bus', 'type': 'fake-type',
8828-
'dev': 'vdb'}
8829-
config_guest_disk = self._fake_libvirt_config_guest_disk()
8826+
instance = objects.Instance(**self.test_instance)
8827+
connection_info = {
8828+
'driver_volume_type': 'fake',
8829+
'data': {
8830+
'device_path': '/fake',
8831+
'access_mode': 'rw'
8832+
}
8833+
}
8834+
generated_config = self._fake_libvirt_config_guest_disk()
8835+
mock_get_config.return_value = copy.deepcopy(generated_config)
8836+
8837+
returned_config = drvr._get_volume_config(
8838+
instance,
8839+
connection_info,
8840+
mock.sentinel.disk_info)
8841+
8842+
mock_get_config.assert_called_once_with(
8843+
connection_info,
8844+
mock.sentinel.disk_info)
8845+
mock_set_cache_mode.assert_called_once_with(returned_config)
8846+
self.assertEqual(generated_config.to_xml(), returned_config.to_xml())
8847+
8848+
@mock.patch.object(libvirt_driver.LibvirtDriver, '_sev_enabled',
8849+
new=mock.Mock(return_value=True))
8850+
@mock.patch.object(libvirt_driver.LibvirtDriver, '_set_cache_mode',
8851+
new=mock.Mock())
8852+
@mock.patch.object(volume_drivers.LibvirtFakeVolumeDriver, 'get_config')
8853+
def test_get_volume_config_sev(self, mock_get_config):
8854+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
8855+
instance = objects.Instance(**self.test_instance)
8856+
connection_info = {
8857+
'driver_volume_type': 'fake',
8858+
'data': {
8859+
'device_path': '/fake',
8860+
'access_mode': 'rw'
8861+
}
8862+
}
8863+
generated_config = self._fake_libvirt_config_guest_disk()
8864+
generated_config.target_bus = 'virtio'
8865+
mock_get_config.return_value = copy.deepcopy(generated_config)
8866+
8867+
returned_config = drvr._get_volume_config(
8868+
instance,
8869+
connection_info,
8870+
mock.sentinel.disk_info)
88308871

8831-
get_config.return_value = copy.deepcopy(config_guest_disk)
8832-
config = drvr._get_volume_config(connection_info, disk_info)
8833-
get_config.assert_called_once_with(connection_info, disk_info)
8834-
_set_cache_mode.assert_called_once_with(config)
8835-
self.assertEqual(config_guest_disk.to_xml(), config.to_xml())
8872+
# Assert that driver_iommu is enabled for this virtio volume
8873+
self.assertTrue(returned_config.driver_iommu)
88368874

88378875
@mock.patch.object(libvirt_driver.LibvirtDriver, '_get_volume_driver')
88388876
@mock.patch.object(libvirt_driver.LibvirtDriver, '_attach_encryptor')
@@ -9292,7 +9330,7 @@ def test_attach_volume_with_vir_domain_affect_live_flag(self,
92929330
mock_connect_volume.assert_called_with(
92939331
self.context, connection_info, instance, encryption=None)
92949332
mock_get_volume_config.assert_called_with(
9295-
connection_info, disk_info)
9333+
instance, connection_info, disk_info)
92969334
mock_dom.attachDeviceFlags.assert_called_with(
92979335
mock_conf.to_xml(), flags=flags)
92989336
mock_check_discard.assert_called_with(mock_conf, instance)
@@ -11492,8 +11530,13 @@ def test_live_migration_update_volume_xml(self, mock_xml,
1149211530
drvr._live_migration_uri(target_connection),
1149311531
params=params, flags=0)
1149411532
mock_updated_guest_xml.assert_called_once_with(
11495-
guest, migrate_data, mock.ANY, get_vif_config=None,
11496-
new_resources=None)
11533+
instance_ref,
11534+
guest,
11535+
migrate_data,
11536+
mock.ANY,
11537+
get_vif_config=None,
11538+
new_resources=None
11539+
)
1149711540

1149811541
def test_live_migration_update_vifs_xml(self):
1149911542
"""Tests that when migrate_data.vifs is populated, the destination
@@ -11519,9 +11562,14 @@ def test_live_migration_update_vifs_xml(self):
1151911562
guest = libvirt_guest.Guest(mock.MagicMock())
1152011563
fake_xml = '<domain type="qemu"/>'
1152111564

11522-
def fake_get_updated_guest_xml(guest, migrate_data, get_volume_config,
11523-
get_vif_config=None,
11524-
new_resources=None):
11565+
def fake_get_updated_guest_xml(
11566+
instance,
11567+
guest,
11568+
migrate_data,
11569+
get_volume_config,
11570+
get_vif_config=None,
11571+
new_resources=None
11572+
):
1152511573
self.assertIsNotNone(get_vif_config)
1152611574
return fake_xml
1152711575

@@ -11688,18 +11736,30 @@ def test_update_volume_xml(self):
1168811736
conf.source_type = "block"
1168911737
conf.source_path = bdmi.connection_info['data'].get('device_path')
1169011738

11739+
instance = objects.Instance(**self.test_instance)
1169111740
guest = libvirt_guest.Guest(mock.MagicMock())
11741+
1169211742
with test.nested(
11693-
mock.patch.object(drvr, '_get_volume_config',
11694-
return_value=conf),
11695-
mock.patch.object(guest, 'get_xml_desc',
11696-
return_value=initial_xml)):
11697-
config = libvirt_migrate.get_updated_guest_xml(guest,
11698-
objects.LibvirtLiveMigrateData(
11699-
bdms=[bdmi],
11700-
serial_listen_addr='127.0.0.1',
11701-
serial_listen_ports=[1234]),
11702-
drvr._get_volume_config)
11743+
mock.patch.object(
11744+
drvr, '_get_volume_config', return_value=conf
11745+
),
11746+
mock.patch.object(
11747+
guest, 'get_xml_desc', return_value=initial_xml
11748+
),
11749+
mock.patch.object(
11750+
drvr, '_sev_enabled', new=mock.Mock(return_value=False)
11751+
)
11752+
):
11753+
config = libvirt_migrate.get_updated_guest_xml(
11754+
instance,
11755+
guest,
11756+
objects.LibvirtLiveMigrateData(
11757+
bdms=[bdmi],
11758+
serial_listen_addr='127.0.0.1',
11759+
serial_listen_ports=[1234]
11760+
),
11761+
drvr._get_volume_config
11762+
)
1170311763
parser = etree.XMLParser(remove_blank_text=True)
1170411764
config = etree.fromstring(config, parser)
1170511765
target_xml = etree.fromstring(target_xml, parser)
@@ -11884,18 +11944,30 @@ def test_update_volume_xml_no_serial(self):
1188411944
conf.source_type = "block"
1188511945
conf.source_path = bdmi.connection_info['data'].get('device_path')
1188611946

11947+
instance = objects.Instance(**self.test_instance)
1188711948
guest = libvirt_guest.Guest(mock.MagicMock())
11949+
1188811950
with test.nested(
11889-
mock.patch.object(drvr, '_get_volume_config',
11890-
return_value=conf),
11891-
mock.patch.object(guest, 'get_xml_desc',
11892-
return_value=initial_xml)):
11893-
config = libvirt_migrate.get_updated_guest_xml(guest,
11951+
mock.patch.object(
11952+
drvr, '_get_volume_config', return_value=conf
11953+
),
11954+
mock.patch.object(
11955+
guest, 'get_xml_desc', return_value=initial_xml
11956+
),
11957+
mock.patch.object(
11958+
drvr, '_sev_enabled', new=mock.Mock(return_value=False)
11959+
)
11960+
):
11961+
config = libvirt_migrate.get_updated_guest_xml(
11962+
instance,
11963+
guest,
1189411964
objects.LibvirtLiveMigrateData(
1189511965
bdms=[bdmi],
1189611966
serial_listen_addr = '127.0.0.1',
11897-
serial_listen_ports = [1234]),
11898-
drvr._get_volume_config)
11967+
serial_listen_ports = [1234]
11968+
),
11969+
drvr._get_volume_config
11970+
)
1189911971
self.assertEqual(target_xml, config)
1190011972

1190111973
def test_update_volume_xml_no_connection_info(self):
@@ -11918,19 +11990,30 @@ def test_update_volume_xml_no_connection_info(self):
1191811990
format='qcow')
1191911991
bdmi.connection_info = {}
1192011992
conf = vconfig.LibvirtConfigGuestDisk()
11993+
instance = objects.Instance(**self.test_instance)
1192111994
guest = libvirt_guest.Guest(mock.MagicMock())
11995+
1192211996
with test.nested(
11923-
mock.patch.object(drvr, '_get_volume_config',
11924-
return_value=conf),
11925-
mock.patch.object(guest, 'get_xml_desc',
11926-
return_value=initial_xml)):
11997+
mock.patch.object(
11998+
drvr, '_get_volume_config', return_value=conf
11999+
),
12000+
mock.patch.object(
12001+
guest, 'get_xml_desc', return_value=initial_xml
12002+
),
12003+
mock.patch.object(
12004+
drvr, '_sev_enabled', new=mock.Mock(return_value=False)
12005+
)
12006+
):
1192712007
config = libvirt_migrate.get_updated_guest_xml(
12008+
instance,
1192812009
guest,
1192912010
objects.LibvirtLiveMigrateData(
1193012011
bdms=[bdmi],
1193112012
serial_listen_addr='127.0.0.1',
11932-
serial_listen_ports=[1234]),
11933-
drvr._get_volume_config)
12013+
serial_listen_ports=[1234]
12014+
),
12015+
drvr._get_volume_config
12016+
)
1193412017
self.assertEqual(target_xml, config)
1193512018

1193612019
@mock.patch.object(host.Host, 'has_min_version', return_value=True)
@@ -19077,7 +19160,9 @@ def test_get_guest_storage_config(self):
1907719160
self.assertIsNone(instance.default_swap_device)
1907819161
connect_volume.assert_called_with(self.context,
1907919162
bdm['connection_info'], instance)
19080-
get_volume_config.assert_called_with(bdm['connection_info'],
19163+
get_volume_config.assert_called_with(
19164+
instance,
19165+
bdm['connection_info'],
1908119166
{'bus': 'virtio', 'type': 'disk', 'dev': 'vdc'})
1908219167
volume_save.assert_called_once_with()
1908319168

@@ -22855,16 +22940,19 @@ def test_attach_interface_guest_set_metadata(self):
2285522940
mock_save.assert_called_once_with()
2285622941
mock_set_metadata.assert_called_once_with(config_meta)
2285722942

22943+
@mock.patch('nova.virt.libvirt.designer.set_driver_iommu_for_device')
22944+
@mock.patch.object(libvirt_driver.LibvirtDriver, '_sev_enabled')
2285822945
@mock.patch.object(objects.Instance, 'get_network_info')
2285922946
@mock.patch.object(objects.Instance, 'save')
2286022947
@mock.patch.object(libvirt_driver.LibvirtDriver, '_build_device_metadata')
2286122948
@mock.patch.object(FakeVirtDomain, 'info')
2286222949
@mock.patch.object(FakeVirtDomain, 'attachDeviceFlags')
2286322950
@mock.patch.object(host.Host, '_get_domain')
2286422951
def _test_attach_interface(self, power_state, expected_flags,
22865-
mock_get_domain, mock_attach,
22866-
mock_info, mock_build, mock_save,
22867-
mock_get_network_info):
22952+
mock_get_domain, mock_attach, mock_info,
22953+
mock_build, mock_save, mock_get_network_info,
22954+
mock_sev_enabled, mock_designer_set_iommu,
22955+
sev_enabled=False):
2286822956
instance = self._create_instance()
2286922957
network_info = _fake_network_info(self)
2287022958
domain = FakeVirtDomain(fake_xml="""
@@ -22882,6 +22970,7 @@ def _test_attach_interface(self, power_state, expected_flags,
2288222970
</domain>""")
2288322971
mock_get_domain.return_value = domain
2288422972
mock_info.return_value = [power_state, 1, 2, 3, 4]
22973+
mock_sev_enabled.return_value = sev_enabled
2288522974

2288622975
fake_image_meta = objects.ImageMeta.from_dict(
2288722976
{'id': instance.image_ref})
@@ -22907,13 +22996,22 @@ def _test_attach_interface(self, power_state, expected_flags,
2290722996
mock_get_network_info.assert_called_once_with()
2290822997
mock_attach.assert_called_once_with(expected.to_xml(),
2290922998
flags=expected_flags)
22999+
if sev_enabled:
23000+
mock_designer_set_iommu.assert_called_once_with(expected)
2291023001

2291123002
def test_attach_interface_with_running_instance(self):
2291223003
self._test_attach_interface(
2291323004
power_state.RUNNING,
2291423005
(fakelibvirt.VIR_DOMAIN_AFFECT_CONFIG |
2291523006
fakelibvirt.VIR_DOMAIN_AFFECT_LIVE))
2291623007

23008+
def test_attach_interface_with_sev(self):
23009+
self._test_attach_interface(
23010+
power_state.RUNNING,
23011+
(fakelibvirt.VIR_DOMAIN_AFFECT_CONFIG |
23012+
fakelibvirt.VIR_DOMAIN_AFFECT_LIVE),
23013+
sev_enabled=True)
23014+
2291723015
def test_attach_interface_with_pause_instance(self):
2291823016
self._test_attach_interface(
2291923017
power_state.PAUSED,

0 commit comments

Comments
 (0)