libvirt: Detach volumes from a domain before detaching any encryptors
I983f80822a5 introduced retry logic around the detaching of volumes from
a libvirt domain. However it also reordered the surrounding code so that
we attempt to detach any encryptors from a volume first while the volume
is still attached to the domain. This can lead to `Device or resource
busy` errors if the volume is still being used by the instance when we
attempt to detach the encryptors.
Closes-bug: #1642628
Change-Id: Ia0f8e725ec8a0fbc44bd4592b021dea978cf4e4f
(cherry picked from commit d55c5136d0
)
This commit is contained in:
parent
c6743ca709
commit
7a7bbb6da2
|
@ -6233,6 +6233,40 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
|
||||
mock_get_domain.assert_called_once_with(instance)
|
||||
|
||||
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
|
||||
@mock.patch('nova.volume.encryptors.get_volume_encryptor')
|
||||
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
|
||||
def test_detach_volume_order_with_encryptors(self, mock_get_guest,
|
||||
mock_get_encryptor, mock_disconnect_volume):
|
||||
|
||||
mock_guest = mock.MagicMock(spec=libvirt_guest.Guest)
|
||||
mock_guest.get_power_state.return_value = power_state.RUNNING
|
||||
mock_get_guest.return_value = mock_guest
|
||||
mock_encryptor = mock.MagicMock(
|
||||
spec=nova.volume.encryptors.nop.NoOpEncryptor)
|
||||
mock_get_encryptor.return_value = mock_encryptor
|
||||
|
||||
mock_order = mock.Mock()
|
||||
mock_order.attach_mock(mock_disconnect_volume, 'disconnect_volume')
|
||||
mock_order.attach_mock(mock_guest.detach_device_with_retry(),
|
||||
'detach_volume')
|
||||
mock_order.attach_mock(mock_encryptor.detach_volume,
|
||||
'detach_encryptor')
|
||||
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
connection_info = {"driver_volume_type": "fake",
|
||||
"data": {"device_path": "/fake",
|
||||
"access_mode": "rw"}}
|
||||
encryption = {"provider": "NoOpEncryptor"}
|
||||
drvr.detach_volume(connection_info, instance, '/dev/vdc',
|
||||
encryption=encryption)
|
||||
|
||||
mock_order.assert_has_calls([
|
||||
mock.call.detach_volume(),
|
||||
mock.call.detach_encryptor(**encryption),
|
||||
mock.call.disconnect_volume(connection_info, 'vdc')])
|
||||
|
||||
def test_multi_nic(self):
|
||||
network_info = _fake_network_info(self, 2)
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
|
|
|
@ -1279,20 +1279,20 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
state = guest.get_power_state(self._host)
|
||||
live = state in (power_state.RUNNING, power_state.PAUSED)
|
||||
|
||||
# The volume must be detached from the VM before disconnecting it
|
||||
# from its encryptor. Otherwise, the encryptor may report that the
|
||||
# volume is still in use.
|
||||
wait_for_detach = guest.detach_device_with_retry(guest.get_disk,
|
||||
disk_dev,
|
||||
persistent=True,
|
||||
live=live)
|
||||
wait_for_detach()
|
||||
|
||||
if encryption:
|
||||
# The volume must be detached from the VM before
|
||||
# disconnecting it from its encryptor. Otherwise, the
|
||||
# encryptor may report that the volume is still in use.
|
||||
encryptor = self._get_volume_encryptor(connection_info,
|
||||
encryption)
|
||||
encryptor.detach_volume(**encryption)
|
||||
|
||||
wait_for_detach()
|
||||
except exception.InstanceNotFound:
|
||||
# NOTE(zhaoqin): If the instance does not exist, _lookup_by_name()
|
||||
# will throw InstanceNotFound exception. Need to
|
||||
|
|
Loading…
Reference in New Issue