Skip to content

Commit 0b05160

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "libvirt: Do not destroy volume secrets during _hard_reboot" into stable/wallaby
2 parents 9eea37c + 9cac2a8 commit 0b05160

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
@@ -9084,6 +9084,19 @@ def test_disconnect_volume_luks(self, mock_get_volume_encryptor):
90849084
uuids.volume_id)
90859085
mock_encryptor.detach_volume.assert_not_called()
90869086

9087+
# assert that no attempt to remove the secret is made when
9088+
# destroy_secrets=False
9089+
drvr._host.find_secret.reset_mock()
9090+
drvr._host.delete_secret.reset_mock()
9091+
drvr._disconnect_volume(
9092+
self.context,
9093+
connection_info,
9094+
instance,
9095+
encryption=encryption,
9096+
destroy_secrets=False
9097+
)
9098+
drvr._host.delete_secret.assert_not_called()
9099+
90879100
# assert that the encryptor is used if no secret is found
90889101
drvr._host.find_secret.reset_mock()
90899102
drvr._host.delete_secret.reset_mock()
@@ -10147,6 +10160,36 @@ def test_detach_encryptor_native_luks_device_path_secret_missing(
1014710160
mock_find_secret.assert_called_once_with('volume', uuids.volume_id)
1014810161
mock_get_encryptor.assert_not_called()
1014910162

10163+
@mock.patch('nova.virt.libvirt.host.Host.delete_secret')
10164+
@mock.patch('nova.virt.libvirt.host.Host.find_secret', new=mock.Mock())
10165+
def test_detach_encryptor_skip_secret_removal(self, mock_delete_secret):
10166+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
10167+
drvr._detach_encryptor(
10168+
self.context,
10169+
{
10170+
'data': {
10171+
'volume_id': uuids.volume_id
10172+
}
10173+
},
10174+
None,
10175+
destroy_secrets=False
10176+
)
10177+
# Assert that no attempt is made to delete the volume secert
10178+
mock_delete_secret.assert_not_called()
10179+
10180+
drvr._detach_encryptor(
10181+
self.context,
10182+
{
10183+
'data': {
10184+
'volume_id': uuids.volume_id
10185+
}
10186+
},
10187+
None,
10188+
destroy_secrets=True
10189+
)
10190+
# Assert that volume secert is deleted
10191+
mock_delete_secret.assert_called_once_with('volume', uuids.volume_id)
10192+
1015010193
def test_allow_native_luksv1(self):
1015110194
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
1015210195
self.assertFalse(drvr._allow_native_luksv1({}))
@@ -15793,7 +15836,8 @@ def destroy_side_effect(*args, **kwargs):
1579315836
mock_domain_destroy.assert_called_once_with()
1579415837
mock_teardown_container.assert_called_once_with(instance)
1579515838
mock_cleanup.assert_called_once_with(self.context, instance,
15796-
network_info, None, False)
15839+
network_info, None, False,
15840+
destroy_secrets=True)
1579715841

1579815842
@mock.patch.object(libvirt_driver.LibvirtDriver, 'cleanup')
1579915843
@mock.patch.object(libvirt_driver.LibvirtDriver, '_teardown_container')
@@ -15813,7 +15857,8 @@ def test_destroy_lxc_calls_teardown_container_when_no_domain(self,
1581315857
mock.call(instance)])
1581415858
mock_teardown_container.assert_called_once_with(instance)
1581515859
mock_cleanup.assert_called_once_with(self.context, instance,
15816-
network_info, None, False)
15860+
network_info, None, False,
15861+
destroy_secrets=True)
1581715862

1581815863
@mock.patch.object(host.Host, 'get_guest')
1581915864
def test_reboot_different_ids(self, mock_get):
@@ -16034,7 +16079,8 @@ def test_hard_reboot(self, mock_get_mdev, mock_destroy, mock_get_disk_info,
1603416079
mock_get_mdev.assert_called_once_with(instance)
1603516080
mock_destroy.assert_called_once_with(self.context, instance,
1603616081
network_info, destroy_disks=False,
16037-
block_device_info=block_device_info)
16082+
block_device_info=block_device_info,
16083+
destroy_secrets=False)
1603816084

1603916085
mock_get_guest_xml.assert_called_once_with(self.context, instance,
1604016086
network_info, mock.ANY, mock.ANY,
@@ -19321,6 +19367,59 @@ def test_cleanup_instance_dir_with_rbd_workaround(self,
1932119367
self.assertTrue(instance.cleaned)
1932219368
save.assert_called_once_with()
1932319369

19370+
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
19371+
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._undefine_domain',
19372+
new=mock.Mock())
19373+
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_vpmems',
19374+
new=mock.Mock(return_value=None))
19375+
def test_cleanup_destroy_secrets(self, mock_disconnect_volume):
19376+
block_device_info = {
19377+
'block_device_mapping': [
19378+
{
19379+
'connection_info': mock.sentinel.connection_info
19380+
}
19381+
]
19382+
}
19383+
instance = objects.Instance(self.context, **self.test_instance)
19384+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
19385+
19386+
# Pass destroy_vifs=False and destroy_disks=False as we only care about
19387+
# asserting the behaviour of destroy_secrets in this test.
19388+
drvr.cleanup(
19389+
self.context,
19390+
instance,
19391+
network_info={},
19392+
block_device_info=block_device_info,
19393+
destroy_vifs=False,
19394+
destroy_disks=False,
19395+
destroy_secrets=False
19396+
)
19397+
drvr.cleanup(
19398+
self.context,
19399+
instance,
19400+
network_info={},
19401+
block_device_info=block_device_info,
19402+
destroy_vifs=False,
19403+
destroy_disks=False,
19404+
)
19405+
19406+
# Assert that disconnect_volume is called with destroy_secrets=False
19407+
# and destroy_secrets=True by default
19408+
mock_disconnect_volume.assert_has_calls([
19409+
mock.call(
19410+
self.context,
19411+
mock.sentinel.connection_info,
19412+
instance,
19413+
destroy_secrets=False
19414+
),
19415+
mock.call(
19416+
self.context,
19417+
mock.sentinel.connection_info,
19418+
instance,
19419+
destroy_secrets=True
19420+
)
19421+
])
19422+
1932419423
@mock.patch.object(libvirt_driver.LibvirtDriver, '_get_volume_encryption')
1932519424
@mock.patch.object(libvirt_driver.LibvirtDriver, '_allow_native_luksv1')
1932619425
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
@@ -1408,7 +1408,7 @@ def destroy(self, context, instance, network_info, block_device_info=None,
14081408
# unblock the waiting threads and clean up.
14091409
self._device_event_handler.cleanup_waiters(instance.uuid)
14101410
self.cleanup(context, instance, network_info, block_device_info,
1411-
destroy_disks)
1411+
destroy_disks, destroy_secrets=destroy_secrets)
14121412

14131413
def _undefine_domain(self, instance):
14141414
try:
@@ -1482,11 +1482,12 @@ def cleanup(self, context, instance, network_info, block_device_info=None,
14821482
block_device_info=block_device_info,
14831483
destroy_vifs=destroy_vifs,
14841484
cleanup_instance_dir=cleanup_instance_dir,
1485-
cleanup_instance_disks=cleanup_instance_disks)
1485+
cleanup_instance_disks=cleanup_instance_disks,
1486+
destroy_secrets=destroy_secrets)
14861487

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

14921493
This method cleans up any pmem devices, unplugs VIFs, disconnects
@@ -1527,7 +1528,9 @@ def _cleanup(self, context, instance, network_info, block_device_info=None,
15271528
continue
15281529

15291530
try:
1530-
self._disconnect_volume(context, connection_info, instance)
1531+
self._disconnect_volume(
1532+
context, connection_info, instance,
1533+
destroy_secrets=destroy_secrets)
15311534
except Exception as exc:
15321535
with excutils.save_and_reraise_exception() as ctxt:
15331536
if cleanup_instance_disks:
@@ -1844,8 +1847,13 @@ def _should_disconnect_target(self, context, instance, multiattach,
18441847
return (False if connection_count > 1 else True)
18451848

18461849
def _disconnect_volume(self, context, connection_info, instance,
1847-
encryption=None):
1848-
self._detach_encryptor(context, connection_info, encryption=encryption)
1850+
encryption=None, destroy_secrets=True):
1851+
self._detach_encryptor(
1852+
context,
1853+
connection_info,
1854+
encryption=encryption,
1855+
destroy_secrets=destroy_secrets
1856+
)
18491857
vol_driver = self._get_volume_driver(connection_info)
18501858
volume_id = driver_block_device.get_volume_id(connection_info)
18511859
multiattach = connection_info.get('multiattach', False)
@@ -1962,7 +1970,8 @@ def _attach_encryptor(self, context, connection_info, encryption):
19621970
encryption)
19631971
encryptor.attach_volume(context, **encryption)
19641972

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

19681977
The request context is only used when an encryption metadata dict is
@@ -1974,7 +1983,11 @@ def _detach_encryptor(self, context, connection_info, encryption):
19741983
"""
19751984
volume_id = driver_block_device.get_volume_id(connection_info)
19761985
if volume_id and self._host.find_secret('volume', volume_id):
1986+
if not destroy_secrets:
1987+
LOG.debug("Skipping volume secret destruction")
1988+
return
19771989
return self._host.delete_secret('volume', volume_id)
1990+
19781991
if encryption is None:
19791992
encryption = self._get_volume_encryption(context, connection_info)
19801993

@@ -3735,7 +3748,8 @@ def _hard_reboot(self, context, instance, network_info,
37353748
# we can here without losing data. This allows us to re-initialise from
37363749
# scratch, and hopefully fix, most aspects of a non-functioning guest.
37373750
self.destroy(context, instance, network_info, destroy_disks=False,
3738-
block_device_info=block_device_info)
3751+
block_device_info=block_device_info,
3752+
destroy_secrets=False)
37393753

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

0 commit comments

Comments
 (0)