Skip to content

Commit 75ef63f

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "libvirt: Do not destroy volume secrets during _hard_reboot"
2 parents d9f4339 + 26d65fc commit 75ef63f

File tree

2 files changed

+124
-11
lines changed

2 files changed

+124
-11
lines changed

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

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9049,6 +9049,19 @@ def test_disconnect_volume_luks(self, mock_get_volume_encryptor):
90499049
uuids.volume_id)
90509050
mock_encryptor.detach_volume.assert_not_called()
90519051

9052+
# assert that no attempt to remove the secret is made when
9053+
# destroy_secrets=False
9054+
drvr._host.find_secret.reset_mock()
9055+
drvr._host.delete_secret.reset_mock()
9056+
drvr._disconnect_volume(
9057+
self.context,
9058+
connection_info,
9059+
instance,
9060+
encryption=encryption,
9061+
destroy_secrets=False
9062+
)
9063+
drvr._host.delete_secret.assert_not_called()
9064+
90529065
# assert that the encryptor is used if no secret is found
90539066
drvr._host.find_secret.reset_mock()
90549067
drvr._host.delete_secret.reset_mock()
@@ -10112,6 +10125,36 @@ def test_detach_encryptor_native_luks_device_path_secret_missing(
1011210125
mock_find_secret.assert_called_once_with('volume', uuids.volume_id)
1011310126
mock_get_encryptor.assert_not_called()
1011410127

10128+
@mock.patch('nova.virt.libvirt.host.Host.delete_secret')
10129+
@mock.patch('nova.virt.libvirt.host.Host.find_secret', new=mock.Mock())
10130+
def test_detach_encryptor_skip_secret_removal(self, mock_delete_secret):
10131+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
10132+
drvr._detach_encryptor(
10133+
self.context,
10134+
{
10135+
'data': {
10136+
'volume_id': uuids.volume_id
10137+
}
10138+
},
10139+
None,
10140+
destroy_secrets=False
10141+
)
10142+
# Assert that no attempt is made to delete the volume secert
10143+
mock_delete_secret.assert_not_called()
10144+
10145+
drvr._detach_encryptor(
10146+
self.context,
10147+
{
10148+
'data': {
10149+
'volume_id': uuids.volume_id
10150+
}
10151+
},
10152+
None,
10153+
destroy_secrets=True
10154+
)
10155+
# Assert that volume secert is deleted
10156+
mock_delete_secret.assert_called_once_with('volume', uuids.volume_id)
10157+
1011510158
def test_allow_native_luksv1(self):
1011610159
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
1011710160
self.assertFalse(drvr._allow_native_luksv1({}))
@@ -15752,7 +15795,8 @@ def destroy_side_effect(*args, **kwargs):
1575215795
mock_domain_destroy.assert_called_once_with()
1575315796
mock_teardown_container.assert_called_once_with(instance)
1575415797
mock_cleanup.assert_called_once_with(self.context, instance,
15755-
network_info, None, False)
15798+
network_info, None, False,
15799+
destroy_secrets=True)
1575615800

1575715801
@mock.patch.object(libvirt_driver.LibvirtDriver, 'cleanup')
1575815802
@mock.patch.object(libvirt_driver.LibvirtDriver, '_teardown_container')
@@ -15772,7 +15816,8 @@ def test_destroy_lxc_calls_teardown_container_when_no_domain(self,
1577215816
mock.call(instance)])
1577315817
mock_teardown_container.assert_called_once_with(instance)
1577415818
mock_cleanup.assert_called_once_with(self.context, instance,
15775-
network_info, None, False)
15819+
network_info, None, False,
15820+
destroy_secrets=True)
1577615821

1577715822
@mock.patch.object(host.Host, 'get_guest')
1577815823
def test_reboot_different_ids(self, mock_get):
@@ -15993,7 +16038,8 @@ def test_hard_reboot(self, mock_get_mdev, mock_destroy, mock_get_disk_info,
1599316038
mock_get_mdev.assert_called_once_with(instance)
1599416039
mock_destroy.assert_called_once_with(self.context, instance,
1599516040
network_info, destroy_disks=False,
15996-
block_device_info=block_device_info)
16041+
block_device_info=block_device_info,
16042+
destroy_secrets=False)
1599716043

1599816044
mock_get_guest_xml.assert_called_once_with(self.context, instance,
1599916045
network_info, mock.ANY, mock.ANY,
@@ -19278,6 +19324,59 @@ def test_cleanup_instance_dir_with_rbd_workaround(self,
1927819324
self.assertTrue(instance.cleaned)
1927919325
save.assert_called_once_with()
1928019326

19327+
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
19328+
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._undefine_domain',
19329+
new=mock.Mock())
19330+
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_vpmems',
19331+
new=mock.Mock(return_value=None))
19332+
def test_cleanup_destroy_secrets(self, mock_disconnect_volume):
19333+
block_device_info = {
19334+
'block_device_mapping': [
19335+
{
19336+
'connection_info': mock.sentinel.connection_info
19337+
}
19338+
]
19339+
}
19340+
instance = objects.Instance(self.context, **self.test_instance)
19341+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
19342+
19343+
# Pass destroy_vifs=False and destroy_disks=False as we only care about
19344+
# asserting the behaviour of destroy_secrets in this test.
19345+
drvr.cleanup(
19346+
self.context,
19347+
instance,
19348+
network_info={},
19349+
block_device_info=block_device_info,
19350+
destroy_vifs=False,
19351+
destroy_disks=False,
19352+
destroy_secrets=False
19353+
)
19354+
drvr.cleanup(
19355+
self.context,
19356+
instance,
19357+
network_info={},
19358+
block_device_info=block_device_info,
19359+
destroy_vifs=False,
19360+
destroy_disks=False,
19361+
)
19362+
19363+
# Assert that disconnect_volume is called with destroy_secrets=False
19364+
# and destroy_secrets=True by default
19365+
mock_disconnect_volume.assert_has_calls([
19366+
mock.call(
19367+
self.context,
19368+
mock.sentinel.connection_info,
19369+
instance,
19370+
destroy_secrets=False
19371+
),
19372+
mock.call(
19373+
self.context,
19374+
mock.sentinel.connection_info,
19375+
instance,
19376+
destroy_secrets=True
19377+
)
19378+
])
19379+
1928119380
@mock.patch.object(libvirt_driver.LibvirtDriver, '_get_volume_encryption')
1928219381
@mock.patch.object(libvirt_driver.LibvirtDriver, '_allow_native_luksv1')
1928319382
def test_swap_volume_native_luks_blocked(self, mock_allow_native_luksv1,

nova/virt/libvirt/driver.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,7 +1411,7 @@ def destroy(self, context, instance, network_info, block_device_info=None,
14111411
# unblock the waiting threads and clean up.
14121412
self._device_event_handler.cleanup_waiters(instance.uuid)
14131413
self.cleanup(context, instance, network_info, block_device_info,
1414-
destroy_disks)
1414+
destroy_disks, destroy_secrets=destroy_secrets)
14151415

14161416
def _undefine_domain(self, instance):
14171417
try:
@@ -1485,11 +1485,12 @@ def cleanup(self, context, instance, network_info, block_device_info=None,
14851485
block_device_info=block_device_info,
14861486
destroy_vifs=destroy_vifs,
14871487
cleanup_instance_dir=cleanup_instance_dir,
1488-
cleanup_instance_disks=cleanup_instance_disks)
1488+
cleanup_instance_disks=cleanup_instance_disks,
1489+
destroy_secrets=destroy_secrets)
14891490

14901491
def _cleanup(self, context, instance, network_info, block_device_info=None,
14911492
destroy_vifs=True, cleanup_instance_dir=False,
1492-
cleanup_instance_disks=False):
1493+
cleanup_instance_disks=False, destroy_secrets=True):
14931494
"""Cleanup the domain and any attached resources from the host.
14941495

14951496
This method cleans up any pmem devices, unplugs VIFs, disconnects
@@ -1530,7 +1531,9 @@ def _cleanup(self, context, instance, network_info, block_device_info=None,
15301531
continue
15311532

15321533
try:
1533-
self._disconnect_volume(context, connection_info, instance)
1534+
self._disconnect_volume(
1535+
context, connection_info, instance,
1536+
destroy_secrets=destroy_secrets)
15341537
except Exception as exc:
15351538
with excutils.save_and_reraise_exception() as ctxt:
15361539
if cleanup_instance_disks:
@@ -1847,8 +1850,13 @@ def _should_disconnect_target(self, context, instance, multiattach,
18471850
return (False if connection_count > 1 else True)
18481851

18491852
def _disconnect_volume(self, context, connection_info, instance,
1850-
encryption=None):
1851-
self._detach_encryptor(context, connection_info, encryption=encryption)
1853+
encryption=None, destroy_secrets=True):
1854+
self._detach_encryptor(
1855+
context,
1856+
connection_info,
1857+
encryption=encryption,
1858+
destroy_secrets=destroy_secrets
1859+
)
18521860
vol_driver = self._get_volume_driver(connection_info)
18531861
volume_id = driver_block_device.get_volume_id(connection_info)
18541862
multiattach = connection_info.get('multiattach', False)
@@ -1961,7 +1969,8 @@ def _attach_encryptor(self, context, connection_info, encryption):
19611969
encryption)
19621970
encryptor.attach_volume(context, **encryption)
19631971

1964-
def _detach_encryptor(self, context, connection_info, encryption):
1972+
def _detach_encryptor(self, context, connection_info, encryption,
1973+
destroy_secrets=True):
19651974
"""Detach the frontend encryptor if one is required by the volume.
19661975

19671976
The request context is only used when an encryption metadata dict is
@@ -1973,7 +1982,11 @@ def _detach_encryptor(self, context, connection_info, encryption):
19731982
"""
19741983
volume_id = driver_block_device.get_volume_id(connection_info)
19751984
if volume_id and self._host.find_secret('volume', volume_id):
1985+
if not destroy_secrets:
1986+
LOG.debug("Skipping volume secret destruction")
1987+
return
19761988
return self._host.delete_secret('volume', volume_id)
1989+
19771990
if encryption is None:
19781991
encryption = self._get_volume_encryption(context, connection_info)
19791992

@@ -3729,7 +3742,8 @@ def _hard_reboot(self, context, instance, network_info,
37293742
# we can here without losing data. This allows us to re-initialise from
37303743
# scratch, and hopefully fix, most aspects of a non-functioning guest.
37313744
self.destroy(context, instance, network_info, destroy_disks=False,
3732-
block_device_info=block_device_info)
3745+
block_device_info=block_device_info,
3746+
destroy_secrets=False)
37333747

37343748
# Convert the system metadata to image metadata
37353749
# NOTE(mdbooth): This is a workaround for stateless Nova compute

0 commit comments

Comments
 (0)