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:
parent
b0851b0e9c
commit
8fbaeba11f
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue