Skip to content

Commit d793835

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "libvirt: Do not reference VIR_ERR_DEVICE_MISSING when libvirt is < v4.1.0"
2 parents c488124 + bc96af5 commit d793835

File tree

4 files changed

+141
-21
lines changed

4 files changed

+141
-21
lines changed

nova/tests/unit/virt/libvirt/test_driver.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9461,6 +9461,51 @@ def test_attach_volume_with_vir_domain_affect_live_flag(self,
94619461
mock_build_metadata.assert_called_with(self.context, instance)
94629462
mock_save.assert_called_with()
94639463

9464+
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
9465+
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion')
9466+
@mock.patch(
9467+
'nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume',
9468+
new=mock.Mock())
9469+
def test_detach_volume_supports_device_missing(
9470+
self, mock_get_version, mock_get_guest):
9471+
"""Assert that VIR_ERR_DEVICE_MISSING is only used if libvirt >= v4.1.0
9472+
"""
9473+
mock_guest = mock.Mock(spec=libvirt_guest.Guest)
9474+
mock_guest.get_power_state.return_value = power_state.RUNNING
9475+
mock_get_guest.return_value = mock_guest
9476+
9477+
v4_0_0 = versionutils.convert_version_to_int((4, 0, 0))
9478+
mock_get_version.return_value = v4_0_0
9479+
9480+
mountpoint = "/dev/foo"
9481+
expected_disk_dev = "foo"
9482+
9483+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
9484+
drvr.detach_volume(
9485+
self.context, mock.sentinel.connection_info,
9486+
mock.sentinel.instance, mountpoint)
9487+
9488+
# Assert supports_device_missing_error_code=False is used
9489+
mock_guest.detach_device_with_retry.assert_called_once_with(
9490+
mock_guest.get_disk, expected_disk_dev, live=True,
9491+
supports_device_missing_error_code=False)
9492+
9493+
# reset and try again with v4.1.0
9494+
mock_guest.reset_mock()
9495+
mock_get_version.reset_mock()
9496+
9497+
v4_1_0 = versionutils.convert_version_to_int((4, 1, 0))
9498+
mock_get_version.return_value = v4_1_0
9499+
9500+
drvr.detach_volume(
9501+
self.context, mock.sentinel.connection_info,
9502+
mock.sentinel.instance, mountpoint)
9503+
9504+
# Assert supports_device_missing_error_code=True is used
9505+
mock_guest.detach_device_with_retry.assert_called_once_with(
9506+
mock_guest.get_disk, expected_disk_dev, live=True,
9507+
supports_device_missing_error_code=True)
9508+
94649509
@mock.patch('nova.virt.libvirt.host.Host._get_domain')
94659510
def test_detach_volume_with_vir_domain_affect_live_flag(self,
94669511
mock_get_domain):
@@ -18813,6 +18858,51 @@ def test_detach_volume_with_instance_not_found(self):
1881318858
_disconnect_volume.assert_called_once_with(
1881418859
self.context, connection_info, instance, encryption=None)
1881518860

18861+
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
18862+
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion')
18863+
def test_detach_interface_supports_device_missing(
18864+
self, mock_get_version, mock_get_guest):
18865+
"""Assert that VIR_ERR_DEVICE_MISSING is only used if libvirt >= v4.1.0
18866+
"""
18867+
mock_guest = mock.Mock(spec=libvirt_guest.Guest)
18868+
mock_guest.get_power_state.return_value = power_state.RUNNING
18869+
mock_get_guest.return_value = mock_guest
18870+
18871+
v4_0_0 = versionutils.convert_version_to_int((4, 0, 0))
18872+
mock_get_version.return_value = v4_0_0
18873+
18874+
instance = objects.Instance(**self.test_instance)
18875+
18876+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
18877+
with mock.patch.object(drvr, 'vif_driver') as mock_vif_driver:
18878+
mock_vif_driver.get_config.return_value = mock.sentinel.cfg
18879+
mock_vif_driver.get_vif_devname.return_value = mock.sentinel.dev
18880+
18881+
drvr.detach_interface(
18882+
self.context, instance, mock.sentinel.vif)
18883+
18884+
# Assert supports_device_missing_error_code=False is used
18885+
mock_guest.detach_device_with_retry.assert_called_once_with(
18886+
mock_guest.get_interface_by_cfg, mock.sentinel.cfg, live=True,
18887+
alternative_device_name=mock.sentinel.dev,
18888+
supports_device_missing_error_code=False)
18889+
18890+
# reset and try again with v4.1.0
18891+
mock_guest.reset_mock()
18892+
mock_get_version.reset_mock()
18893+
18894+
v4_1_0 = versionutils.convert_version_to_int((4, 1, 0))
18895+
mock_get_version.return_value = v4_1_0
18896+
18897+
drvr.detach_interface(
18898+
self.context, instance, mock.sentinel.vif)
18899+
18900+
# Assert supports_device_missing_error_code=True is used
18901+
mock_guest.detach_device_with_retry.assert_called_once_with(
18902+
mock_guest.get_interface_by_cfg, mock.sentinel.cfg, live=True,
18903+
alternative_device_name=mock.sentinel.dev,
18904+
supports_device_missing_error_code=True)
18905+
1881618906
def _test_attach_detach_interface_get_config(self, method_name):
1881718907
"""Tests that the get_config() method is properly called in
1881818908
attach_interface() and detach_interface().

nova/tests/unit/virt/libvirt/test_guest.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ def test_detach_device_with_retry_device_not_found_alt_name(self):
294294

295295
@mock.patch.object(libvirt_guest.Guest, "detach_device")
296296
def _test_detach_device_with_retry_second_detach_failure(
297-
self, mock_detach, error_code=fakelibvirt.VIR_ERR_DEVICE_MISSING,
298-
error_message="device not found: disk vdb not found"):
297+
self, mock_detach, error_code=None, error_message=None,
298+
supports_device_missing=False):
299299
# This simulates a retry of the transient/live domain detach
300300
# failing because the device is not found
301301
conf = mock.Mock(spec=vconfig.LibvirtConfigGuestDevice)
@@ -312,7 +312,8 @@ def _test_detach_device_with_retry_second_detach_failure(
312312
mock_detach.side_effect = [None, fake_exc]
313313
retry_detach = self.guest.detach_device_with_retry(
314314
get_config, fake_device, live=True,
315-
inc_sleep_time=.01, max_retry_count=3)
315+
inc_sleep_time=.01, max_retry_count=3,
316+
supports_device_missing_error_code=supports_device_missing)
316317
# Some time later, we can do the wait/retry to ensure detach
317318
self.assertRaises(exception.DeviceNotFound, retry_detach)
318319
# Check that the save_and_reraise_exception context manager didn't log
@@ -325,20 +326,25 @@ def _test_detach_device_with_retry_second_detach_failure(
325326
def test_detach_device_with_retry_second_detach_operation_failed(self):
326327
self._test_detach_device_with_retry_second_detach_failure(
327328
error_code=fakelibvirt.VIR_ERR_OPERATION_FAILED,
328-
error_message="operation failed: disk vdb not found")
329+
error_message="operation failed: disk vdb not found",
330+
supports_device_missing=False)
329331

330332
# TODO(lyarwood): Remove this test once MIN_LIBVIRT_VERSION is >= 4.1.0
331333
def test_detach_device_with_retry_second_detach_internal_error(self):
332334
self._test_detach_device_with_retry_second_detach_failure(
333335
error_code=fakelibvirt.VIR_ERR_INTERNAL_ERROR,
334-
error_message="operation failed: disk vdb not found")
336+
error_message="operation failed: disk vdb not found",
337+
supports_device_missing=False)
335338

336339
def test_detach_device_with_retry_second_detach_device_missing(self):
337-
self._test_detach_device_with_retry_second_detach_failure()
340+
self._test_detach_device_with_retry_second_detach_failure(
341+
error_code=fakelibvirt.VIR_ERR_DEVICE_MISSING,
342+
error_message="device not found: disk vdb not found",
343+
supports_device_missing=True)
338344

339345
def _test_detach_device_with_retry_first_detach_failure(
340-
self, error_code=fakelibvirt.VIR_ERR_DEVICE_MISSING,
341-
error_message="device not found: disk vdb not found"):
346+
self, error_code=None, error_message=None,
347+
supports_device_missing=False):
342348
# This simulates a persistent or live domain detach failing because the
343349
# device is not found during the first attempt to detach the device.
344350
# We should still attempt to detach the device from the live config if
@@ -369,7 +375,8 @@ def _test_detach_device_with_retry_first_detach_failure(
369375
# succeeds afterward
370376
self.domain.detachDeviceFlags.side_effect = [fake_exc, None]
371377
retry_detach = self.guest.detach_device_with_retry(get_config,
372-
fake_device, live=True, inc_sleep_time=.01, max_retry_count=3)
378+
fake_device, live=True, inc_sleep_time=.01, max_retry_count=3,
379+
supports_device_missing_error_code=supports_device_missing)
373380
# We should have tried to detach from the persistent domain
374381
self.domain.detachDeviceFlags.assert_called_once_with(
375382
"</xml>", flags=(fakelibvirt.VIR_DOMAIN_AFFECT_CONFIG |
@@ -385,22 +392,28 @@ def _test_detach_device_with_retry_first_detach_failure(
385392
def test_detach_device_with_retry_first_detach_operation_failed(self):
386393
self._test_detach_device_with_retry_first_detach_failure(
387394
error_code=fakelibvirt.VIR_ERR_OPERATION_FAILED,
388-
error_message="operation failed: disk vdb not found")
395+
error_message="operation failed: disk vdb not found",
396+
supports_device_missing=False)
389397

390398
# TODO(lyarwood): Remove this test once MIN_LIBVIRT_VERSION is >= 4.1.0
391399
def test_detach_device_with_retry_first_detach_internal_error(self):
392400
self._test_detach_device_with_retry_first_detach_failure(
393401
error_code=fakelibvirt.VIR_ERR_INTERNAL_ERROR,
394-
error_message="operation failed: disk vdb not found")
402+
error_message="operation failed: disk vdb not found",
403+
supports_device_missing=False)
395404

396405
# TODO(lyarwood): Remove this test once MIN_LIBVIRT_VERSION is >= 4.1.0
397406
def test_detach_device_with_retry_first_detach_invalid_arg(self):
398407
self._test_detach_device_with_retry_first_detach_failure(
399408
error_code=fakelibvirt.VIR_ERR_INVALID_ARG,
400-
error_message="invalid argument: no target device vdb")
409+
error_message="invalid argument: no target device vdb",
410+
supports_device_missing=False)
401411

402412
def test_detach_device_with_retry_first_detach_device_missing(self):
403-
self._test_detach_device_with_retry_first_detach_failure()
413+
self._test_detach_device_with_retry_first_detach_failure(
414+
error_code=fakelibvirt.VIR_ERR_DEVICE_MISSING,
415+
error_message="device not found: disk vdb not found",
416+
supports_device_missing=True)
404417

405418
def test_get_xml_desc(self):
406419
self.guest.get_xml_desc()

nova/virt/libvirt/driver.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ def repr_method(self):
278278
MIN_LIBVIRT_BLOCKDEV = (6, 0, 0)
279279
MIN_QEMU_BLOCKDEV = (4, 2, 0)
280280

281+
MIN_LIBVIRT_VIR_ERR_DEVICE_MISSING = (4, 1, 0)
282+
281283

282284
class LibvirtDriver(driver.ComputeDriver):
283285
def __init__(self, virtapi, read_only=False):
@@ -2004,9 +2006,11 @@ def detach_volume(self, context, connection_info, instance, mountpoint,
20042006
# detaching any attached encryptors or disconnecting the underlying
20052007
# volume in _disconnect_volume. Otherwise, the encryptor or volume
20062008
# driver may report that the volume is still in use.
2007-
wait_for_detach = guest.detach_device_with_retry(guest.get_disk,
2008-
disk_dev,
2009-
live=live)
2009+
supports_device_missing = self._host.has_min_version(
2010+
MIN_LIBVIRT_VIR_ERR_DEVICE_MISSING)
2011+
wait_for_detach = guest.detach_device_with_retry(
2012+
guest.get_disk, disk_dev, live=live,
2013+
supports_device_missing_error_code=supports_device_missing)
20102014
wait_for_detach()
20112015

20122016
except exception.InstanceNotFound:
@@ -2209,9 +2213,12 @@ def detach_interface(self, context, instance, vif):
22092213
live = state in (power_state.RUNNING, power_state.PAUSED)
22102214
# Now we are going to loop until the interface is detached or we
22112215
# timeout.
2216+
supports_device_missing = self._host.has_min_version(
2217+
MIN_LIBVIRT_VIR_ERR_DEVICE_MISSING)
22122218
wait_for_detach = guest.detach_device_with_retry(
22132219
guest.get_interface_by_cfg, cfg, live=live,
2214-
alternative_device_name=self.vif_driver.get_vif_devname(vif))
2220+
alternative_device_name=self.vif_driver.get_vif_devname(vif),
2221+
supports_device_missing_error_code=supports_device_missing)
22152222
wait_for_detach()
22162223
except exception.DeviceDetachFailed:
22172224
# We failed to detach the device even with the retry loop, so let's

nova/virt/libvirt/guest.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ def get_all_devices(self, devtype=None):
375375
def detach_device_with_retry(self, get_device_conf_func, device, live,
376376
max_retry_count=7, inc_sleep_time=2,
377377
max_sleep_time=30,
378-
alternative_device_name=None):
378+
alternative_device_name=None,
379+
supports_device_missing_error_code=False):
379380
"""Detaches a device from the guest. After the initial detach request,
380381
a function is returned which can be used to ensure the device is
381382
successfully removed from the guest domain (retrying the removal as
@@ -396,8 +397,16 @@ def detach_device_with_retry(self, get_device_conf_func, device, live,
396397
max_sleep_time will be used as the sleep time.
397398
:param alternative_device_name: This is an alternative identifier for
398399
the device if device is not an ID, used solely for error messages.
400+
:param supports_device_missing_error_code: does the installed version
401+
of libvirt provide the
402+
VIR_ERR_DEVICE_MISSING error
403+
code.
399404
"""
400405
alternative_device_name = alternative_device_name or device
406+
unplug_libvirt_error_codes = set([
407+
libvirt.VIR_ERR_OPERATION_FAILED,
408+
libvirt.VIR_ERR_INTERNAL_ERROR
409+
])
401410

402411
def _try_detach_device(conf, persistent=False, live=False):
403412
# Raise DeviceNotFound if the device isn't found during detach
@@ -414,9 +423,10 @@ def _try_detach_device(conf, persistent=False, live=False):
414423
# TODO(lyarwood): Remove libvirt.VIR_ERR_OPERATION_FAILED
415424
# and libvirt.VIR_ERR_INTERNAL_ERROR once
416425
# MIN_LIBVIRT_VERSION is >= 4.1.0
417-
if errcode in (libvirt.VIR_ERR_OPERATION_FAILED,
418-
libvirt.VIR_ERR_INTERNAL_ERROR,
419-
libvirt.VIR_ERR_DEVICE_MISSING):
426+
if supports_device_missing_error_code:
427+
unplug_libvirt_error_codes.add(
428+
libvirt.VIR_ERR_DEVICE_MISSING)
429+
if errcode in unplug_libvirt_error_codes:
420430
# TODO(lyarwood): Remove the following error message
421431
# check once we only care about VIR_ERR_DEVICE_MISSING
422432
errmsg = ex.get_error_message()

0 commit comments

Comments
 (0)