Merge "libvirt: handle DiskNotFound during update_available_resource" into stable/pike
This commit is contained in:
commit
779fbf838c
|
@ -13449,6 +13449,50 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertEqual(0, drvr._get_disk_over_committed_size_total())
|
||||
|
||||
@mock.patch('nova.virt.libvirt.host.Host.list_instance_domains')
|
||||
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.'
|
||||
'_get_instance_disk_info_from_config',
|
||||
side_effect=exception.DiskNotFound(location='/opt/stack/foo'))
|
||||
@mock.patch('nova.objects.BlockDeviceMappingList.bdms_by_instance_uuid',
|
||||
return_value=objects.BlockDeviceMappingList())
|
||||
@mock.patch('nova.objects.InstanceList.get_by_filters',
|
||||
return_value=objects.InstanceList(objects=[
|
||||
objects.Instance(uuid=uuids.instance,
|
||||
task_state=task_states.DELETING)]))
|
||||
def test_disk_over_committed_size_total_disk_not_found_ignore(
|
||||
self, mock_get, mock_bdms, mock_get_disk_info, mock_list_domains):
|
||||
"""Tests that we handle DiskNotFound gracefully for an instance that
|
||||
is undergoing a task_state transition.
|
||||
"""
|
||||
mock_dom = mock.Mock()
|
||||
mock_dom.XMLDesc.return_value = "<domain/>"
|
||||
mock_dom.UUIDString.return_value = uuids.instance
|
||||
mock_list_domains.return_value = [mock_dom]
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertEqual(0, drvr._get_disk_over_committed_size_total())
|
||||
|
||||
@mock.patch('nova.virt.libvirt.host.Host.list_instance_domains')
|
||||
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.'
|
||||
'_get_instance_disk_info_from_config',
|
||||
side_effect=exception.DiskNotFound(location='/opt/stack/foo'))
|
||||
@mock.patch('nova.objects.BlockDeviceMappingList.bdms_by_instance_uuid',
|
||||
return_value=objects.BlockDeviceMappingList())
|
||||
@mock.patch('nova.objects.InstanceList.get_by_filters',
|
||||
return_value=objects.InstanceList(objects=[
|
||||
objects.Instance(uuid=uuids.instance, task_state=None)]))
|
||||
def test_disk_over_committed_size_total_disk_not_found_reraise(
|
||||
self, mock_get, mock_bdms, mock_get_disk_info, mock_list_domains):
|
||||
"""Tests that we handle DiskNotFound gracefully for an instance that
|
||||
is NOT undergoing a task_state transition and the error is re-raised.
|
||||
"""
|
||||
mock_dom = mock.Mock()
|
||||
mock_dom.XMLDesc.return_value = "<domain/>"
|
||||
mock_dom.UUIDString.return_value = uuids.instance
|
||||
mock_list_domains.return_value = [mock_dom]
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertRaises(exception.DiskNotFound,
|
||||
drvr._get_disk_over_committed_size_total)
|
||||
|
||||
def test_cpu_info(self):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class QemuTestCase(test.NoDBTestCase):
|
|||
|
||||
@mock.patch.object(os.path, 'exists', return_value=True)
|
||||
def test_qemu_info_with_errors(self, path_exists):
|
||||
self.assertRaises(exception.InvalidDiskInfo,
|
||||
self.assertRaises(exception.DiskNotFound,
|
||||
images.qemu_img_info,
|
||||
'/fake/path')
|
||||
|
||||
|
@ -65,6 +65,24 @@ class QemuTestCase(test.NoDBTestCase):
|
|||
'/fake/path')
|
||||
self.assertIn('qemu-img aborted by prlimits', six.text_type(exc))
|
||||
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch.object(os.path, 'exists', return_value=True)
|
||||
def test_qemu_img_info_with_disk_not_found(self, exists, mocked_execute):
|
||||
"""Tests that the initial os.path.exists check passes but the qemu-img
|
||||
command fails because the path is gone by the time the command runs.
|
||||
"""
|
||||
path = '/opt/stack/data/nova/instances/some-uuid/disk'
|
||||
stderr = (u"qemu-img: Could not open "
|
||||
"'/opt/stack/data/nova/instances/some-uuid/disk': "
|
||||
"Could not open '/opt/stack/data/nova/instances/some-uuid/"
|
||||
"disk': No such file or directory\n")
|
||||
mocked_execute.side_effect = (
|
||||
processutils.ProcessExecutionError(
|
||||
exit_code=1, stderr=stderr))
|
||||
self.assertRaises(exception.DiskNotFound, images.qemu_img_info, path)
|
||||
exists.assert_called_once_with(path)
|
||||
mocked_execute.assert_called_once()
|
||||
|
||||
@mock.patch.object(images, 'convert_image',
|
||||
side_effect=exception.ImageUnacceptable)
|
||||
@mock.patch.object(images, 'qemu_img_info')
|
||||
|
|
|
@ -72,10 +72,15 @@ def qemu_img_info(path, format=None):
|
|||
cmd = cmd + ('--force-share',)
|
||||
out, err = utils.execute(*cmd, prlimit=QEMU_IMG_LIMITS)
|
||||
except processutils.ProcessExecutionError as exp:
|
||||
# this means we hit prlimits, make the exception more specific
|
||||
if exp.exit_code == -9:
|
||||
# this means we hit prlimits, make the exception more specific
|
||||
msg = (_("qemu-img aborted by prlimits when inspecting "
|
||||
"%(path)s : %(exp)s") % {'path': path, 'exp': exp})
|
||||
elif exp.exit_code == 1 and 'No such file or directory' in exp.stderr:
|
||||
# The os.path.exists check above can race so this is a simple
|
||||
# best effort at catching that type of failure and raising a more
|
||||
# specific error.
|
||||
raise exception.DiskNotFound(location=path)
|
||||
else:
|
||||
msg = (_("qemu-img failed to execute on %(path)s : %(exp)s") %
|
||||
{'path': path, 'exp': exp})
|
||||
|
|
|
@ -7511,6 +7511,24 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
'by concurrent operations such as resize. '
|
||||
'Error: %(error)s',
|
||||
{'i_name': guest.name, 'error': e})
|
||||
except exception.DiskNotFound:
|
||||
with excutils.save_and_reraise_exception() as err_ctxt:
|
||||
# If the instance is undergoing a task state transition,
|
||||
# like moving to another host or is being deleted, we
|
||||
# should ignore this instance and move on.
|
||||
if guest.uuid in local_instances:
|
||||
inst = local_instances[guest.uuid]
|
||||
if inst.task_state is not None:
|
||||
LOG.info('Periodic task is updating the host '
|
||||
'stats; it is trying to get disk info '
|
||||
'for %(i_name)s, but the backing disk '
|
||||
'was removed by a concurrent operation '
|
||||
'(task_state=%(task_state)s)',
|
||||
{'i_name': guest.name,
|
||||
'task_state': inst.task_state},
|
||||
instance=inst)
|
||||
err_ctxt.reraise = False
|
||||
|
||||
# NOTE(gtt116): give other tasks a chance.
|
||||
greenthread.sleep(0)
|
||||
return disk_over_committed_size
|
||||
|
|
Loading…
Reference in New Issue