Skip to content

Commit 47b93bf

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Preserve UEFI NVRAM variable store" into stable/2025.2
2 parents ac1f6eb + 40f52a9 commit 47b93bf

7 files changed

Lines changed: 74 additions & 19 deletions

File tree

‎nova/tests/fixtures/libvirt.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def _reset():
101101
VIR_DOMAIN_UNDEFINE_MANAGED_SAVE = 1
102102
VIR_DOMAIN_UNDEFINE_NVRAM = 4
103103
VIR_DOMAIN_UNDEFINE_KEEP_TPM = 64
104+
VIR_DOMAIN_UNDEFINE_KEEP_NVRAM = 8
104105

105106
VIR_DOMAIN_AFFECT_CURRENT = 0
106107
VIR_DOMAIN_AFFECT_LIVE = 1

‎nova/tests/unit/virt/libvirt/test_config.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2838,6 +2838,7 @@ def _test_config_uefi(self):
28382838
obj.os_mach_type = "pc-q35-5.1"
28392839
obj.os_loader = '/tmp/OVMF_CODE.secboot.fd'
28402840
obj.os_loader_type = 'pflash'
2841+
obj.os_nvram = '/foo/bar/instance-00000012_VARS.fd'
28412842
obj.os_loader_secure = True
28422843
obj.os_loader_stateless = True
28432844
xml = obj.to_xml()
@@ -2852,6 +2853,7 @@ def _test_config_uefi(self):
28522853
<os>
28532854
<type machine="pc-q35-5.1">hvm</type>
28542855
<loader stateless='yes' secure='yes' readonly='yes' type='pflash'>/tmp/OVMF_CODE.secboot.fd</loader>
2856+
<nvram>/foo/bar/instance-00000012_VARS.fd</nvram>
28552857
</os>
28562858
</domain>""", # noqa: E501
28572859
xml,

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

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19229,10 +19229,11 @@ def test_undefine_domain_disarms_keep_vtpm_if_not_supported(
1922919229
fake_guest = mock.Mock()
1923019230
mock_get.return_value = fake_guest
1923119231

19232-
drvr._undefine_domain(instance, keep_vtpm=True)
19232+
drvr._undefine_domain(instance, keep_vtpm=True, keep_nvram=False)
1923319233

1923419234
fake_guest.delete_configuration.assert_called_once_with(
1923519235
keep_vtpm=False,
19236+
keep_nvram=False,
1923619237
)
1923719238

1923819239
# Check that it truly forces it to False and doesn't do a `not` or
@@ -19242,6 +19243,7 @@ def test_undefine_domain_disarms_keep_vtpm_if_not_supported(
1924219243

1924319244
fake_guest.delete_configuration.assert_called_once_with(
1924419245
keep_vtpm=False,
19246+
keep_nvram=False,
1924519247
)
1924619248

1924719249
@mock.patch.object(host.Host, "get_guest")
@@ -19252,9 +19254,11 @@ def test_undefine_domain_passes_keep_vtpm_if_supported(self, mock_get):
1925219254
fake_guest = mock.Mock()
1925319255
mock_get.return_value = fake_guest
1925419256

19255-
drvr._undefine_domain(instance, keep_vtpm=True)
19257+
drvr._undefine_domain(instance, keep_vtpm=True, keep_nvram=False)
1925619258

19257-
fake_guest.delete_configuration.assert_called_once_with(keep_vtpm=True)
19259+
fake_guest.delete_configuration.assert_called_once_with(
19260+
keep_vtpm=True,
19261+
keep_nvram=False)
1925819262

1925919263
# Check that it does not force keep_vtpm to true, just because it is
1926019264
# supported.
@@ -19263,8 +19267,27 @@ def test_undefine_domain_passes_keep_vtpm_if_supported(self, mock_get):
1926319267

1926419268
fake_guest.delete_configuration.assert_called_once_with(
1926519269
keep_vtpm=False,
19270+
keep_nvram=False,
1926619271
)
1926719272

19273+
@mock.patch.object(host.Host, "get_guest")
19274+
def test_undefine_domain_passes_keep_nvram_if_supported(self, mock_get):
19275+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
19276+
instance = objects.Instance(**self.test_instance)
19277+
fake_guest = mock.Mock()
19278+
mock_get.return_value = fake_guest
19279+
drvr._undefine_domain(instance, keep_nvram=True)
19280+
fake_guest.delete_configuration.assert_called_once_with(
19281+
keep_vtpm=False, keep_nvram=True)
19282+
# Check that it does not force keep_nvram to true, just because it is
19283+
# supported.
19284+
fake_guest.reset_mock()
19285+
drvr._undefine_domain(instance, keep_nvram=False)
19286+
fake_guest.delete_configuration.assert_called_once_with(
19287+
keep_vtpm=False,
19288+
keep_nvram=False,
19289+
)
19290+
1926819291
@mock.patch.object(host.Host, "list_instance_domains")
1926919292
@mock.patch.object(objects.BlockDeviceMappingList, "bdms_by_instance_uuid")
1927019293
@mock.patch.object(objects.InstanceList, "get_by_filters")
@@ -19564,7 +19587,7 @@ def test_get_instance_disk_info_from_config_raw_files(self,
1956419587
disk_actual_size = 3687091200
1956519588
disk_actual_size_blocks = disk_actual_size / 512
1956619589
expected_over_committed_disk_size = disk_virtual_size -\
19567-
disk_actual_size
19590+
disk_actual_size
1956819591

1956919592
mock_getsize.return_value = disk_virtual_size
1957019593
mock_stat.return_value = mock.Mock(st_blocks=disk_actual_size_blocks)
@@ -21836,7 +21859,10 @@ def test_cleanup_pass(
2183621859
mock_delete_files.assert_called_once_with(fake_inst)
2183721860
# vTPM secret should not be deleted until instance is deleted.
2183821861
mock_delete_vtpm.assert_not_called()
21839-
mock_undefine.assert_called_once_with(fake_inst, keep_vtpm=False)
21862+
mock_undefine.assert_called_once_with(
21863+
fake_inst,
21864+
keep_vtpm=False,
21865+
keep_nvram=False)
2184021866

2184121867
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._undefine_domain')
2184221868
@mock.patch('nova.crypto.delete_vtpm_secret')
@@ -21862,7 +21888,8 @@ def test_cleanup_preserves_tpm_if_not_destroying_disks(
2186221888
mock_get_mapping.assert_called_once_with(None)
2186321889
mock_delete_files.assert_not_called()
2186421890
mock_delete_vtpm.assert_not_called()
21865-
mock_undefine.assert_called_once_with(fake_inst, keep_vtpm=True)
21891+
mock_undefine.assert_called_once_with(fake_inst, keep_vtpm=True,
21892+
keep_nvram=True)
2186621893

2186721894
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._undefine_domain')
2186821895
@mock.patch('nova.crypto.delete_vtpm_secret')
@@ -21887,7 +21914,8 @@ def test_cleanup_instance_marked_deleted(
2188721914
drvr.cleanup('ctxt', fake_inst, 'netinfo')
2188821915
# vTPM secret should not be deleted until instance is deleted.
2188921916
mock_delete_vtpm.assert_not_called()
21890-
mock_undefine.assert_called_once_with(fake_inst, keep_vtpm=False)
21917+
mock_undefine.assert_called_once_with(fake_inst, keep_vtpm=False,
21918+
keep_nvram=False)
2189121919

2189221920
@mock.patch.object(libvirt_driver.LibvirtDriver, 'delete_instance_files',
2189321921
return_value=True)

‎nova/tests/unit/virt/libvirt/test_guest.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ def test_delete_configuration_with_keep_vtpm_true(self):
145145
fakelibvirt.VIR_DOMAIN_UNDEFINE_NVRAM |
146146
fakelibvirt.VIR_DOMAIN_UNDEFINE_KEEP_TPM)
147147

148+
def test_delete_configuration_keep_nvram(self):
149+
self.guest.delete_configuration(keep_nvram=True)
150+
self.domain.undefineFlags.assert_called_once_with(
151+
fakelibvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE |
152+
fakelibvirt.VIR_DOMAIN_UNDEFINE_KEEP_NVRAM)
153+
148154
def test_delete_configuration_exception(self):
149155
self.domain.undefineFlags.side_effect = fakelibvirt.libvirtError(
150156
'oops')

‎nova/virt/libvirt/driver.py‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,7 @@ def destroy(self, context, instance, network_info, block_device_info=None,
16251625
self.cleanup(context, instance, network_info, block_device_info,
16261626
destroy_disks, destroy_secrets=destroy_secrets)
16271627

1628-
def _delete_guest_configuration(self, guest, keep_vtpm):
1628+
def _delete_guest_configuration(self, guest, keep_vtpm, keep_nvram):
16291629
"""Wrapper around guest.delete_configuration which incorporates version
16301630
checks for the additional arguments.
16311631

@@ -1644,13 +1644,14 @@ def _delete_guest_configuration(self, guest, keep_vtpm):
16441644
)
16451645
keep_vtpm = False
16461646

1647-
guest.delete_configuration(keep_vtpm=keep_vtpm)
1647+
guest.delete_configuration(keep_vtpm=keep_vtpm, keep_nvram=keep_nvram)
16481648

1649-
def _undefine_domain(self, instance, keep_vtpm=False):
1649+
def _undefine_domain(self, instance, keep_vtpm=False, keep_nvram=False):
16501650
try:
16511651
guest = self._host.get_guest(instance)
16521652
try:
1653-
self._delete_guest_configuration(guest, keep_vtpm=keep_vtpm)
1653+
self._delete_guest_configuration(guest, keep_vtpm=keep_vtpm,
1654+
keep_nvram=keep_nvram)
16541655
except libvirt.libvirtError as e:
16551656
with excutils.save_and_reraise_exception() as ctxt:
16561657
errcode = e.get_error_code()
@@ -1737,9 +1738,10 @@ def _cleanup(self, context, instance, network_info, block_device_info=None,
17371738
:param destroy_vifs: if plugged vifs should be unplugged
17381739
:param cleanup_instance_dir: If the instance dir should be removed
17391740
:param cleanup_instance_disks: If the instance disks should be removed.
1740-
Also removes ephemeral encryption secrets, if present.
1741-
:param destroy_secrets: If the cinder volume encryption libvirt secrets
1742-
should be deleted.
1741+
Also removes ephemeral encryption secrets, if present, as well as
1742+
vTPM and NVRAM data.
1743+
:param destroy_secrets: If the cinder volume encryption secrets should
1744+
be deleted.
17431745
"""
17441746
# zero the data on backend pmem device
17451747
vpmems = self._get_vpmems(instance)
@@ -1813,7 +1815,8 @@ def _cleanup(self, context, instance, network_info, block_device_info=None,
18131815
self._cleanup_ephemeral_encryption_secrets(
18141816
context, instance, block_device_info)
18151817

1816-
self._undefine_domain(instance, keep_vtpm=not cleanup_instance_disks)
1818+
self._undefine_domain(instance, keep_vtpm=not cleanup_instance_disks,
1819+
keep_nvram=not cleanup_instance_disks)
18171820

18181821
def _cleanup_ephemeral_encryption_secrets(
18191822
self, context, instance, block_device_info
@@ -2388,7 +2391,8 @@ def _swap_volume(self, guest, disk_dev, conf, resize_to):
23882391
# undefine it. If any part of this block fails, the domain is
23892392
# re-defined regardless.
23902393
if guest.has_persistent_configuration():
2391-
self._delete_guest_configuration(guest, keep_vtpm=True)
2394+
self._delete_guest_configuration(guest, keep_vtpm=True,
2395+
keep_nvram=True)
23922396

23932397
try:
23942398
dev.copy(conf.to_xml(), reuse_ext=True)
@@ -3517,7 +3521,8 @@ def _live_snapshot(self, context, instance, guest, disk_path, out_path,
35173521
# If any part of this block fails, the domain is
35183522
# re-defined regardless.
35193523
if guest.has_persistent_configuration():
3520-
self._delete_guest_configuration(guest, keep_vtpm=True)
3524+
self._delete_guest_configuration(guest, keep_vtpm=True,
3525+
keep_nvram=True)
35213526

35223527
# NOTE (rmk): Establish a temporary mirror of our root disk and
35233528
# issue an abort once we have a complete copy.

‎nova/virt/libvirt/guest.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,15 @@ def get_vcpus_info(self):
295295
yield VCPUInfo(
296296
id=vcpu[0], cpu=vcpu[3], state=vcpu[1], time=vcpu[2])
297297

298-
def delete_configuration(self, keep_vtpm=False):
298+
def delete_configuration(self, keep_vtpm=False, keep_nvram=False):
299299
"""Undefines a domain from hypervisor.
300300
301301
:param keep_vtpm: If true, the vTPM data will be preserved. Otherwise,
302302
it will be deleted. Defaults to false (that is, deleting the vTPM
303303
data).
304+
:param keep_nvram: If true, the NVRAM data will be preserved.
305+
Otherwise, it will be deleted. Defaults to false (that is, deleting
306+
the NVRAM data).
304307
305308
Calling this with `keep_vtpm` set to True should, eventually, be
306309
followed up with a call where it is set to False (after re-defining
@@ -312,9 +315,12 @@ def delete_configuration(self, keep_vtpm=False):
312315
"""
313316
try:
314317
flags = libvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE
315-
flags |= libvirt.VIR_DOMAIN_UNDEFINE_NVRAM
316318
if keep_vtpm:
317319
flags |= libvirt.VIR_DOMAIN_UNDEFINE_KEEP_TPM
320+
if keep_nvram:
321+
flags |= libvirt.VIR_DOMAIN_UNDEFINE_KEEP_NVRAM
322+
else:
323+
flags |= libvirt.VIR_DOMAIN_UNDEFINE_NVRAM
318324
self._domain.undefineFlags(flags)
319325
except libvirt.libvirtError:
320326
LOG.debug("Error from libvirt during undefineFlags for guest "
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
NVRAM variable store is preserved during stop/start, hard reboot, and live
5+
migration by passing the corresponding flag to libvirt.
6+
7+
See https://bugs.launchpad.net/nova/+bug/1633447 for more details.

0 commit comments

Comments
 (0)