Fix wait for detach code to handle 'disk not found error'

Currently, the code does an initial detach from the persistent and
transient domains in one call, then follows up with a call of the
retryable function, which first checks the domain xml before retrying
the transient domain detach. If the transient domain detach (which is
asynchronous) completes after we checked the domain xml, trying to
detach it again will raise a LibvirtError exception. Then, our loop
code in `_do_wait_and_retry_detach` doesn't catch it and will respond
with error. We should be handling the `disk not found error` from
libvirt and consider the job complete.

Closes-Bug: #1642689

Change-Id: I131aaf28d2f5d5d964d4045e3d7d62207079cfb0
This commit is contained in:
int32bit 2016-11-23 18:21:16 +08:00 committed by melanie witt
parent a58c7e5173
commit d079f37f7c
2 changed files with 32 additions and 4 deletions

View File

@ -270,6 +270,26 @@ class GuestTestCase(test.NoDBTestCase):
exception.DeviceNotFound, self.guest.detach_device_with_retry, exception.DeviceNotFound, self.guest.detach_device_with_retry,
get_config, "/dev/vdb", persistent=True, live=True) get_config, "/dev/vdb", persistent=True, live=True)
@mock.patch.object(libvirt_guest.Guest, "detach_device")
def test_detach_device_with_retry_operation_failed(self, mock_detach):
conf = mock.Mock(spec=vconfig.LibvirtConfigGuestDevice)
conf.to_xml.return_value = "</xml>"
get_config = mock.Mock(return_value=conf)
fake_device = "vdb"
fake_exc = fakelibvirt.make_libvirtError(
fakelibvirt.libvirtError,
msg="invalid argument: no target device vdb",
error_code=fakelibvirt.VIR_ERR_OPERATION_FAILED,
error_message="disk vdb not found",
error_domain=fakelibvirt.VIR_FROM_DOMAIN)
mock_detach.side_effect = [None, fake_exc]
retry_detach = self.guest.detach_device_with_retry(
get_config, fake_device, persistent=True, live=True,
inc_sleep_time=.01, max_retry_count=3)
# Some time later, we can do the wait/retry to ensure detach
self.domain.detachDeviceFlags.reset_mock()
self.assertRaises(exception.DeviceNotFound, retry_detach)
def test_get_xml_desc(self): def test_get_xml_desc(self):
self.guest.get_xml_desc() self.guest.get_xml_desc()
self.domain.XMLDesc.assert_called_once_with(flags=0) self.domain.XMLDesc.assert_called_once_with(flags=0)

View File

@ -375,10 +375,18 @@ class Guest(object):
def _do_wait_and_retry_detach(): def _do_wait_and_retry_detach():
config = get_device_conf_func(device) config = get_device_conf_func(device)
if config is not None: if config is not None:
try:
# Device is already detached from persistent domain # Device is already detached from persistent domain
# and only transient domain needs update # and only transient domain needs update
self.detach_device(config, persistent=False, live=live) self.detach_device(config, persistent=False, live=live)
# Raise error since the device still existed on the guest except libvirt.libvirtError as ex:
with excutils.save_and_reraise_exception():
errcode = ex.get_error_code()
if errcode == libvirt.VIR_ERR_OPERATION_FAILED:
errmsg = ex.get_error_message()
if 'not found' in errmsg:
raise exception.DeviceNotFound(device=device)
reason = _("Unable to detach from guest transient domain.") reason = _("Unable to detach from guest transient domain.")
raise exception.DeviceDetachFailed(device=device, raise exception.DeviceDetachFailed(device=device,
reason=reason) reason=reason)