diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 369f3214ad8e..de77aa4d5fab 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -8354,8 +8354,9 @@ class LibvirtConnTestCase(test.NoDBTestCase, @mock.patch('os_brick.encryptors.get_encryption_metadata') @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._use_native_luks') def test_detach_encryptor_encrypted_volume_meta_missing(self, - mock_get_encryptor, mock_get_metadata): + mock_use_native_luks, mock_get_encryptor, mock_get_metadata): """Assert that if missing the encryption metadata of an encrypted volume is fetched and then used to detach the encryptor for the volume. """ @@ -8365,6 +8366,7 @@ class LibvirtConnTestCase(test.NoDBTestCase, encryption = {'provider': 'luks', 'control_location': 'front-end'} mock_get_metadata.return_value = encryption connection_info = {'data': {'volume_id': uuids.volume_id}} + mock_use_native_luks.return_value = False drvr._detach_encryptor(self.context, connection_info, None) @@ -8376,8 +8378,9 @@ class LibvirtConnTestCase(test.NoDBTestCase, @mock.patch('os_brick.encryptors.get_encryption_metadata') @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._use_native_luks') def test_detach_encryptor_encrypted_volume_meta_provided(self, - mock_get_encryptor, mock_get_metadata): + mock_use_native_luks, mock_get_encryptor, mock_get_metadata): """Assert that when provided there are no further attempts to fetch the encryption metadata for the volume and that the provided metadata is then used to detach the volume. @@ -8387,6 +8390,7 @@ class LibvirtConnTestCase(test.NoDBTestCase, mock_get_encryptor.return_value = mock_encryptor encryption = {'provider': 'luks', 'control_location': 'front-end'} connection_info = {'data': {'volume_id': uuids.volume_id}} + mock_use_native_luks.return_value = False drvr._detach_encryptor(self.context, connection_info, encryption) @@ -8395,6 +8399,27 @@ class LibvirtConnTestCase(test.NoDBTestCase, encryption) mock_encryptor.detach_volume.assert_called_once_with(**encryption) + @mock.patch('nova.virt.libvirt.host.Host.find_secret') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._use_native_luks') + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor') + def test_detach_encryptor_native_luks_device_path_secret_missing(self, + mock_get_encryptor, mock_use_native_luks, mock_find_secret): + """Assert that the encryptor is not built when native LUKS is + available, the associated volume secret is missing and device_path is + also missing from the connection_info. + """ + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + encryption = {'provider': 'luks', 'control_location': 'front-end', + 'encryption_key_id': uuids.encryption_key_id} + connection_info = {'data': {'volume_id': uuids.volume_id}} + mock_find_secret.return_value = False + mock_use_native_luks.return_value = True + + drvr._detach_encryptor(self.context, connection_info, encryption) + + mock_find_secret.assert_called_once_with('volume', uuids.volume_id) + mock_get_encryptor.assert_not_called() + @mock.patch.object(host.Host, "has_min_version") def test_use_native_luks(self, mock_has_min_version): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index f0cbeac56e5a..351b00648264 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -1435,6 +1435,14 @@ class LibvirtDriver(driver.ComputeDriver): return self._host.delete_secret('volume', volume_id) if encryption is None: encryption = self._get_volume_encryption(context, connection_info) + # NOTE(lyarwood): Handle bug #1821696 where volume secrets have been + # removed manually by returning if native LUKS decryption is available + # and device_path is not present in the connection_info. This avoids + # VolumeEncryptionNotSupported being thrown when we incorrectly build + # the encryptor below due to the secrets not being present above. + if (encryption and self._use_native_luks(encryption) and + not connection_info['data'].get('device_path')): + return if encryption: encryptor = self._get_volume_encryptor(connection_info, encryption)