Merge "libvirt: Remove MIN_LIBVIRT_VIR_ERR_DEVICE_MISSING"

This commit is contained in:
Zuul 2021-01-25 10:59:30 +00:00 committed by Gerrit Code Review
commit 2a160f6812
4 changed files with 8 additions and 174 deletions

View File

@ -9575,51 +9575,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_build_metadata.assert_called_with(self.context, instance) mock_build_metadata.assert_called_with(self.context, instance)
mock_save.assert_called_with() mock_save.assert_called_with()
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion')
@mock.patch(
'nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume',
new=mock.Mock())
def test_detach_volume_supports_device_missing(
self, mock_get_version, mock_get_guest):
"""Assert that VIR_ERR_DEVICE_MISSING is only used if libvirt >= v4.1.0
"""
mock_guest = mock.Mock(spec=libvirt_guest.Guest)
mock_guest.get_power_state.return_value = power_state.RUNNING
mock_get_guest.return_value = mock_guest
v4_0_0 = versionutils.convert_version_to_int((4, 0, 0))
mock_get_version.return_value = v4_0_0
mountpoint = "/dev/foo"
expected_disk_dev = "foo"
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
drvr.detach_volume(
self.context, mock.sentinel.connection_info,
mock.sentinel.instance, mountpoint)
# Assert supports_device_missing_error_code=False is used
mock_guest.detach_device_with_retry.assert_called_once_with(
mock_guest.get_disk, expected_disk_dev, live=True,
supports_device_missing_error_code=False)
# reset and try again with v4.1.0
mock_guest.reset_mock()
mock_get_version.reset_mock()
v4_1_0 = versionutils.convert_version_to_int((4, 1, 0))
mock_get_version.return_value = v4_1_0
drvr.detach_volume(
self.context, mock.sentinel.connection_info,
mock.sentinel.instance, mountpoint)
# Assert supports_device_missing_error_code=True is used
mock_guest.detach_device_with_retry.assert_called_once_with(
mock_guest.get_disk, expected_disk_dev, live=True,
supports_device_missing_error_code=True)
@mock.patch('nova.virt.libvirt.host.Host._get_domain') @mock.patch('nova.virt.libvirt.host.Host._get_domain')
def test_detach_volume_with_vir_domain_affect_live_flag(self, def test_detach_volume_with_vir_domain_affect_live_flag(self,
mock_get_domain): mock_get_domain):
@ -19058,51 +19013,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
_disconnect_volume.assert_called_once_with( _disconnect_volume.assert_called_once_with(
self.context, connection_info, instance, encryption=None) self.context, connection_info, instance, encryption=None)
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion')
def test_detach_interface_supports_device_missing(
self, mock_get_version, mock_get_guest):
"""Assert that VIR_ERR_DEVICE_MISSING is only used if libvirt >= v4.1.0
"""
mock_guest = mock.Mock(spec=libvirt_guest.Guest)
mock_guest.get_power_state.return_value = power_state.RUNNING
mock_get_guest.return_value = mock_guest
v4_0_0 = versionutils.convert_version_to_int((4, 0, 0))
mock_get_version.return_value = v4_0_0
instance = objects.Instance(**self.test_instance)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
with mock.patch.object(drvr, 'vif_driver') as mock_vif_driver:
mock_vif_driver.get_config.return_value = mock.sentinel.cfg
mock_vif_driver.get_vif_devname.return_value = mock.sentinel.dev
drvr.detach_interface(
self.context, instance, mock.sentinel.vif)
# Assert supports_device_missing_error_code=False is used
mock_guest.detach_device_with_retry.assert_called_once_with(
mock_guest.get_interface_by_cfg, mock.sentinel.cfg, live=True,
alternative_device_name=mock.sentinel.dev,
supports_device_missing_error_code=False)
# reset and try again with v4.1.0
mock_guest.reset_mock()
mock_get_version.reset_mock()
v4_1_0 = versionutils.convert_version_to_int((4, 1, 0))
mock_get_version.return_value = v4_1_0
drvr.detach_interface(
self.context, instance, mock.sentinel.vif)
# Assert supports_device_missing_error_code=True is used
mock_guest.detach_device_with_retry.assert_called_once_with(
mock_guest.get_interface_by_cfg, mock.sentinel.cfg, live=True,
alternative_device_name=mock.sentinel.dev,
supports_device_missing_error_code=True)
def _test_attach_detach_interface_get_config(self, method_name): def _test_attach_detach_interface_get_config(self, method_name):
"""Tests that the get_config() method is properly called in """Tests that the get_config() method is properly called in
attach_interface() and detach_interface(). attach_interface() and detach_interface().

View File

@ -311,8 +311,7 @@ class GuestTestCase(test.NoDBTestCase):
mock_detach.side_effect = [None, fake_exc] mock_detach.side_effect = [None, fake_exc]
retry_detach = self.guest.detach_device_with_retry( retry_detach = self.guest.detach_device_with_retry(
get_config, fake_device, live=True, get_config, fake_device, live=True,
inc_sleep_time=.01, max_retry_count=3, inc_sleep_time=.01, max_retry_count=3)
supports_device_missing_error_code=supports_device_missing)
# Some time later, we can do the wait/retry to ensure detach # Some time later, we can do the wait/retry to ensure detach
self.assertRaises(exception.DeviceNotFound, retry_detach) self.assertRaises(exception.DeviceNotFound, retry_detach)
# Check that the save_and_reraise_exception context manager didn't log # Check that the save_and_reraise_exception context manager didn't log
@ -321,20 +320,6 @@ class GuestTestCase(test.NoDBTestCase):
self.assertNotIn('Original exception being dropped', self.assertNotIn('Original exception being dropped',
self.stdlog.logger.output) self.stdlog.logger.output)
# TODO(lyarwood): Remove this test once MIN_LIBVIRT_VERSION is >= 4.1.0
def test_detach_device_with_retry_second_detach_operation_failed(self):
self._test_detach_device_with_retry_second_detach_failure(
error_code=fakelibvirt.VIR_ERR_OPERATION_FAILED,
error_message="operation failed: disk vdb not found",
supports_device_missing=False)
# TODO(lyarwood): Remove this test once MIN_LIBVIRT_VERSION is >= 4.1.0
def test_detach_device_with_retry_second_detach_internal_error(self):
self._test_detach_device_with_retry_second_detach_failure(
error_code=fakelibvirt.VIR_ERR_INTERNAL_ERROR,
error_message="operation failed: disk vdb not found",
supports_device_missing=False)
def test_detach_device_with_retry_second_detach_device_missing(self): def test_detach_device_with_retry_second_detach_device_missing(self):
self._test_detach_device_with_retry_second_detach_failure( self._test_detach_device_with_retry_second_detach_failure(
error_code=fakelibvirt.VIR_ERR_DEVICE_MISSING, error_code=fakelibvirt.VIR_ERR_DEVICE_MISSING,
@ -374,8 +359,7 @@ class GuestTestCase(test.NoDBTestCase):
# succeeds afterward # succeeds afterward
self.domain.detachDeviceFlags.side_effect = [fake_exc, None] self.domain.detachDeviceFlags.side_effect = [fake_exc, None]
retry_detach = self.guest.detach_device_with_retry(get_config, retry_detach = self.guest.detach_device_with_retry(get_config,
fake_device, live=True, inc_sleep_time=.01, max_retry_count=3, fake_device, live=True, inc_sleep_time=.01, max_retry_count=3)
supports_device_missing_error_code=supports_device_missing)
# We should have tried to detach from the persistent domain # We should have tried to detach from the persistent domain
self.domain.detachDeviceFlags.assert_called_once_with( self.domain.detachDeviceFlags.assert_called_once_with(
"</xml>", flags=(fakelibvirt.VIR_DOMAIN_AFFECT_CONFIG | "</xml>", flags=(fakelibvirt.VIR_DOMAIN_AFFECT_CONFIG |
@ -387,27 +371,6 @@ class GuestTestCase(test.NoDBTestCase):
self.domain.detachDeviceFlags.assert_called_once_with( self.domain.detachDeviceFlags.assert_called_once_with(
"</xml>", flags=fakelibvirt.VIR_DOMAIN_AFFECT_LIVE) "</xml>", flags=fakelibvirt.VIR_DOMAIN_AFFECT_LIVE)
# TODO(lyarwood): Remove this test once MIN_LIBVIRT_VERSION is >= 4.1.0
def test_detach_device_with_retry_first_detach_operation_failed(self):
self._test_detach_device_with_retry_first_detach_failure(
error_code=fakelibvirt.VIR_ERR_OPERATION_FAILED,
error_message="operation failed: disk vdb not found",
supports_device_missing=False)
# TODO(lyarwood): Remove this test once MIN_LIBVIRT_VERSION is >= 4.1.0
def test_detach_device_with_retry_first_detach_internal_error(self):
self._test_detach_device_with_retry_first_detach_failure(
error_code=fakelibvirt.VIR_ERR_INTERNAL_ERROR,
error_message="operation failed: disk vdb not found",
supports_device_missing=False)
# TODO(lyarwood): Remove this test once MIN_LIBVIRT_VERSION is >= 4.1.0
def test_detach_device_with_retry_first_detach_invalid_arg(self):
self._test_detach_device_with_retry_first_detach_failure(
error_code=fakelibvirt.VIR_ERR_INVALID_ARG,
error_message="invalid argument: no target device vdb",
supports_device_missing=False)
def test_detach_device_with_retry_first_detach_device_missing(self): def test_detach_device_with_retry_first_detach_device_missing(self):
self._test_detach_device_with_retry_first_detach_failure( self._test_detach_device_with_retry_first_detach_failure(
error_code=fakelibvirt.VIR_ERR_DEVICE_MISSING, error_code=fakelibvirt.VIR_ERR_DEVICE_MISSING,

View File

@ -254,8 +254,6 @@ LIBVIRT_PERF_EVENT_PREFIX = 'VIR_PERF_PARAM_'
MIN_LIBVIRT_BLOCKDEV = (6, 0, 0) MIN_LIBVIRT_BLOCKDEV = (6, 0, 0)
MIN_QEMU_BLOCKDEV = (4, 2, 0) MIN_QEMU_BLOCKDEV = (4, 2, 0)
MIN_LIBVIRT_VIR_ERR_DEVICE_MISSING = (4, 1, 0)
# Virtual TPM (vTPM) support # Virtual TPM (vTPM) support
MIN_LIBVIRT_VTPM = (5, 6, 0) MIN_LIBVIRT_VTPM = (5, 6, 0)
@ -2052,11 +2050,8 @@ class LibvirtDriver(driver.ComputeDriver):
# detaching any attached encryptors or disconnecting the underlying # detaching any attached encryptors or disconnecting the underlying
# volume in _disconnect_volume. Otherwise, the encryptor or volume # volume in _disconnect_volume. Otherwise, the encryptor or volume
# driver may report that the volume is still in use. # driver may report that the volume is still in use.
supports_device_missing = self._host.has_min_version(
MIN_LIBVIRT_VIR_ERR_DEVICE_MISSING)
wait_for_detach = guest.detach_device_with_retry( wait_for_detach = guest.detach_device_with_retry(
guest.get_disk, disk_dev, live=live, guest.get_disk, disk_dev, live=live)
supports_device_missing_error_code=supports_device_missing)
wait_for_detach() wait_for_detach()
except exception.InstanceNotFound: except exception.InstanceNotFound:
@ -2257,12 +2252,9 @@ class LibvirtDriver(driver.ComputeDriver):
live = state in (power_state.RUNNING, power_state.PAUSED) live = state in (power_state.RUNNING, power_state.PAUSED)
# Now we are going to loop until the interface is detached or we # Now we are going to loop until the interface is detached or we
# timeout. # timeout.
supports_device_missing = self._host.has_min_version(
MIN_LIBVIRT_VIR_ERR_DEVICE_MISSING)
wait_for_detach = guest.detach_device_with_retry( wait_for_detach = guest.detach_device_with_retry(
guest.get_interface_by_cfg, cfg, live=live, guest.get_interface_by_cfg, cfg, live=live,
alternative_device_name=self.vif_driver.get_vif_devname(vif), alternative_device_name=self.vif_driver.get_vif_devname(vif))
supports_device_missing_error_code=supports_device_missing)
wait_for_detach() wait_for_detach()
except exception.DeviceDetachFailed: except exception.DeviceDetachFailed:
# We failed to detach the device even with the retry loop, so let's # We failed to detach the device even with the retry loop, so let's

View File

@ -368,8 +368,7 @@ class Guest(object):
def detach_device_with_retry(self, get_device_conf_func, device, live, def detach_device_with_retry(self, get_device_conf_func, device, live,
max_retry_count=7, inc_sleep_time=10, max_retry_count=7, inc_sleep_time=10,
max_sleep_time=60, max_sleep_time=60,
alternative_device_name=None, alternative_device_name=None):
supports_device_missing_error_code=False):
"""Detaches a device from the guest. After the initial detach request, """Detaches a device from the guest. After the initial detach request,
a function is returned which can be used to ensure the device is a function is returned which can be used to ensure the device is
successfully removed from the guest domain (retrying the removal as successfully removed from the guest domain (retrying the removal as
@ -390,16 +389,8 @@ class Guest(object):
max_sleep_time will be used as the sleep time. max_sleep_time will be used as the sleep time.
:param alternative_device_name: This is an alternative identifier for :param alternative_device_name: This is an alternative identifier for
the device if device is not an ID, used solely for error messages. the device if device is not an ID, used solely for error messages.
:param supports_device_missing_error_code: does the installed version
of libvirt provide the
VIR_ERR_DEVICE_MISSING error
code.
""" """
alternative_device_name = alternative_device_name or device alternative_device_name = alternative_device_name or device
unplug_libvirt_error_codes = set([
libvirt.VIR_ERR_OPERATION_FAILED,
libvirt.VIR_ERR_INTERNAL_ERROR
])
def _try_detach_device(conf, persistent=False, live=False): def _try_detach_device(conf, persistent=False, live=False):
# Raise DeviceNotFound if the device isn't found during detach # Raise DeviceNotFound if the device isn't found during detach
@ -412,31 +403,9 @@ class Guest(object):
except libvirt.libvirtError as ex: except libvirt.libvirtError as ex:
with excutils.save_and_reraise_exception(reraise=False) as ctx: with excutils.save_and_reraise_exception(reraise=False) as ctx:
errcode = ex.get_error_code() if ex.get_error_code() == libvirt.VIR_ERR_DEVICE_MISSING:
# TODO(lyarwood): Remove libvirt.VIR_ERR_OPERATION_FAILED raise exception.DeviceNotFound(
# and libvirt.VIR_ERR_INTERNAL_ERROR once device=alternative_device_name)
# MIN_LIBVIRT_VERSION is >= 4.1.0
if supports_device_missing_error_code:
unplug_libvirt_error_codes.add(
libvirt.VIR_ERR_DEVICE_MISSING)
if errcode in unplug_libvirt_error_codes:
# TODO(lyarwood): Remove the following error message
# check once we only care about VIR_ERR_DEVICE_MISSING
errmsg = ex.get_error_message()
if 'not found' in errmsg:
# This will be raised if the live domain
# detach fails because the device is not found
raise exception.DeviceNotFound(
device=alternative_device_name)
# TODO(lyarwood): Remove libvirt.VIR_ERR_INVALID_ARG once
# MIN_LIBVIRT_VERSION is >= 4.1.0
elif errcode == libvirt.VIR_ERR_INVALID_ARG:
errmsg = ex.get_error_message()
if 'no target device' in errmsg:
# This will be raised if the persistent domain
# detach fails because the device is not found
raise exception.DeviceNotFound(
device=alternative_device_name)
# Re-raise the original exception if we're not raising # Re-raise the original exception if we're not raising
# DeviceNotFound instead. This will avoid logging of a # DeviceNotFound instead. This will avoid logging of a
# "Original exception being dropped" traceback. # "Original exception being dropped" traceback.