From 8fbaeba11f445bcf6c6be1f5f7b7aeeb6995c9cd Mon Sep 17 00:00:00 2001 From: Gorka Eguileor Date: Wed, 30 Mar 2022 19:49:18 +0200 Subject: [PATCH] Fix extending non LUKSv1 encrypted volumes Patch fixing bug #1861071 resolved the issue of extending LUKS v1 volumes when nova connects them via libvirt instead of through os-brick, but nova side still fails to extend in-use volumes when they don't go through libvirt (i.e., LUKS v2). The logs will show a very similar error, but the user won't know that this has happened and Cinder will show the new size: libvirt.libvirtError: internal error: unable to execute QEMU command 'block_resize': Cannot grow device files There are 2 parts to this problem: - The device mapper device is not automatically extended. - Nova tries to use the encrypted block device size as the size of the decrypted device. This patch leverages the "extend_volume" method in os-brick connectors to extend the device mapper device, after the encrypted device has been extended, and use the size of the decrypted volume for the block_resize operation. Related change: I351f1a7769c9f915e4cd280f05a8b8b87f40df84 Closes-Bug: #1967157 Change-Id: Ia1411f11ec4bf44af6a42d5f96c8a0903846ed66 --- nova/tests/unit/virt/libvirt/test_driver.py | 55 +++++++++++++++++++ nova/virt/libvirt/driver.py | 15 ++++- .../notes/bug-1967157-extend-encrypted.yaml | 9 +++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/bug-1967157-extend-encrypted.yaml diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index fd3d322b198c..792f36aed8ed 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -10033,6 +10033,61 @@ class LibvirtConnTestCase(test.NoDBTestCase, # is called with the LUKSv1 payload offset taken into account. block_device.resize.assert_called_once_with(new_size_minus_offset) + @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor') + @mock.patch('os_brick.encryptors.get_encryption_metadata') + def test_extend_volume_os_brick_block(self, mock_get_encryption_metadata, + mock_get_encryptor): + """Test extend volume that uses an os-brick encryptor.""" + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + instance = objects.Instance(**self.test_instance) + connection_info = { + 'serial': uuids.volume_id, + 'driver_volume_type': 'fake', + 'data': { + 'device_path': mock.sentinel.device_path, + 'access_mode': 'rw' + } + } + + block_device = mock.Mock(spec=libvirt_guest.BlockDevice, + _disk=mock.sentinel.disk) + guest = mock.Mock(spec=libvirt_guest.Guest) + guest.get_block_device.return_value = block_device + guest.get_power_state.return_value = power_state.RUNNING + + # The requested_size is provided to extend_volume in bytes. + new_size = 20 * units.Gi + # Decrypted volume size reported by os-brick will be smaller + new_size_minus_offset = new_size - (16384 * units.Ki) + + mock_brick_extend = mock_get_encryptor.return_value.extend_volume + mock_brick_extend.return_value = new_size_minus_offset + + drvr._host.get_guest = mock.Mock(return_value=guest) + drvr._extend_volume = mock.Mock(return_value=new_size) + + encryption = {'provider': 'luks2', 'control_location': 'front-end'} + mock_get_encryption_metadata.return_value = encryption + + # Extend the volume to new_size + drvr.extend_volume(self.context, connection_info, instance, new_size) + + # Assert that the expected calls are made prior to the device resize. + drvr._host.get_guest.assert_called_once_with(instance) + guest.get_power_state.assert_called_once_with(drvr._host) + guest.get_block_device(mock.sentinel.device_path) + + # Assert calls to the os-brick encryptor extend + mock_get_encryptor.assert_called_once_with(connection_info, encryption) + mock_brick_extend.assert_called_once_with(self.context, **encryption) + + mock_get_encryption_metadata.assert_called_once_with( + self.context, drvr._volume_api, uuids.volume_id, connection_info) + + # Assert that the Libvirt call to resize the device within the instance + # is called with the size reported by os-brick + block_device.resize.assert_called_once_with(new_size_minus_offset) + @mock.patch('os_brick.encryptors.get_encryption_metadata') @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor') def test_use_encryptor_connection_info_incomplete(self, diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 94e7b1945aa7..90b4b2b880b9 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -2711,7 +2711,7 @@ class LibvirtDriver(driver.ComputeDriver): instance=instance) block_device.resize(new_size) - def _resize_attached_encrypted_volume(self, original_new_size, + def _resize_attached_encrypted_volume(self, context, original_new_size, block_device, instance, connection_info, encryption): # TODO(lyarwood): Also handle the dm-crpyt encryption providers of @@ -2757,6 +2757,17 @@ class LibvirtDriver(driver.ComputeDriver): LOG.exception('Unknown error when attempting to find the ' 'payload_offset for LUKSv1 encrypted disk ' '%s.', path, instance=instance) + + else: # os-brick encryptor driver + encryptor = self._get_volume_encryptor(connection_info, encryption) + decrypted_device_new_size = encryptor.extend_volume(context, + **encryption) + if decrypted_device_new_size is None: + raise exception.VolumeExtendFailed( + volume_id=block_device._disk, + reason="Encryptor extend failed." + ) + # NOTE(lyarwood): Resize the decrypted device within the instance to # the calculated size as with normal volumes. self._resize_attached_volume( @@ -2805,7 +2816,7 @@ class LibvirtDriver(driver.ComputeDriver): context, self._volume_api, volume_id, connection_info) if encryption: self._resize_attached_encrypted_volume( - new_size, dev, instance, + context, new_size, dev, instance, connection_info, encryption) else: self._resize_attached_volume( diff --git a/releasenotes/notes/bug-1967157-extend-encrypted.yaml b/releasenotes/notes/bug-1967157-extend-encrypted.yaml new file mode 100644 index 000000000000..8ff5f6a2f9fa --- /dev/null +++ b/releasenotes/notes/bug-1967157-extend-encrypted.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Extending attached encrypted volumes that failed before because they were + not being decrypted using libvirt (any other than LUKS) now work as + expected and the new size will be visible within the instance. See + `Bug 1967157`_ for more details. + + .. _Bug 1967157: https://bugs.launchpad.net/nova/+bug/1967157