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
This commit is contained in:
Gorka Eguileor 2022-03-30 19:49:18 +02:00
parent b0851b0e9c
commit 8fbaeba11f
3 changed files with 77 additions and 2 deletions

View File

@ -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,

View File

@ -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(

View File

@ -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