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.
|
# is called with the LUKSv1 payload offset taken into account.
|
||||||
block_device.resize.assert_called_once_with(new_size_minus_offset)
|
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('os_brick.encryptors.get_encryption_metadata')
|
||||||
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
|
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
|
||||||
def test_use_encryptor_connection_info_incomplete(self,
|
def test_use_encryptor_connection_info_incomplete(self,
|
||||||
|
|
|
@ -2711,7 +2711,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
instance=instance)
|
instance=instance)
|
||||||
block_device.resize(new_size)
|
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,
|
block_device, instance,
|
||||||
connection_info, encryption):
|
connection_info, encryption):
|
||||||
# TODO(lyarwood): Also handle the dm-crpyt encryption providers of
|
# 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 '
|
LOG.exception('Unknown error when attempting to find the '
|
||||||
'payload_offset for LUKSv1 encrypted disk '
|
'payload_offset for LUKSv1 encrypted disk '
|
||||||
'%s.', path, instance=instance)
|
'%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
|
# NOTE(lyarwood): Resize the decrypted device within the instance to
|
||||||
# the calculated size as with normal volumes.
|
# the calculated size as with normal volumes.
|
||||||
self._resize_attached_volume(
|
self._resize_attached_volume(
|
||||||
|
@ -2805,7 +2816,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
context, self._volume_api, volume_id, connection_info)
|
context, self._volume_api, volume_id, connection_info)
|
||||||
if encryption:
|
if encryption:
|
||||||
self._resize_attached_encrypted_volume(
|
self._resize_attached_encrypted_volume(
|
||||||
new_size, dev, instance,
|
context, new_size, dev, instance,
|
||||||
connection_info, encryption)
|
connection_info, encryption)
|
||||||
else:
|
else:
|
||||||
self._resize_attached_volume(
|
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