Merge "Separate out routine for getting qemu_img_info" into stable/newton

This commit is contained in:
Jenkins 2017-05-04 16:56:17 +00:00 committed by Gerrit Code Review
commit 08bfc7d817
2 changed files with 151 additions and 44 deletions

View File

@ -205,35 +205,78 @@ def fetch(context, image_service, image_id, path, _user_id, _project_id):
LOG.info(msg, {"sz": fsz_mb, "mbps": mbps})
def get_qemu_data(image_id, has_meta, disk_format_raw, dest, run_as_root):
# We may be on a system that doesn't have qemu-img installed. That
# is ok if we are working with a RAW image. This logic checks to see
# if qemu-img is installed. If not we make sure the image is RAW and
# throw an exception if not. Otherwise we stop before needing
# qemu-img. Systems with qemu-img will always progress through the
# whole function.
try:
# Use the empty tmp file to make sure qemu_img_info works.
data = qemu_img_info(dest, run_as_root=run_as_root)
# There are a lot of cases that can cause a process execution
# error, but until we do more work to separate out the various
# cases we'll keep the general catch here
except processutils.ProcessExecutionError:
data = None
if has_meta:
if not disk_format_raw:
raise exception.ImageUnacceptable(
reason=_("qemu-img is not installed and image is of "
"type %s. Only RAW images can be used if "
"qemu-img is not installed.") %
disk_format_raw,
image_id=image_id)
else:
raise exception.ImageUnacceptable(
reason=_("qemu-img is not installed and the disk "
"format is not specified. Only RAW images "
"can be used if qemu-img is not installed."),
image_id=image_id)
return data
def fetch_verify_image(context, image_service, image_id, dest,
user_id=None, project_id=None, size=None,
run_as_root=True):
fetch(context, image_service, image_id, dest,
None, None)
image_meta = image_service.show(context, image_id)
with fileutils.remove_path_on_error(dest):
data = qemu_img_info(dest, run_as_root=run_as_root)
fmt = data.file_format
if fmt is None:
raise exception.ImageUnacceptable(
reason=_("'qemu-img info' parsing failed."),
image_id=image_id)
has_meta = False if not image_meta else True
try:
format_raw = True if image_meta['disk_format'] == 'raw' else False
except TypeError:
format_raw = False
data = get_qemu_data(image_id, has_meta, format_raw,
dest, run_as_root)
# We can only really do verification of the image if we have
# qemu data to use
if data is not None:
fmt = data.file_format
if fmt is None:
raise exception.ImageUnacceptable(
reason=_("'qemu-img info' parsing failed."),
image_id=image_id)
backing_file = data.backing_file
if backing_file is not None:
raise exception.ImageUnacceptable(
image_id=image_id,
reason=(_("fmt=%(fmt)s backed by: %(backing_file)s") %
{'fmt': fmt, 'backing_file': backing_file}))
backing_file = data.backing_file
if backing_file is not None:
raise exception.ImageUnacceptable(
image_id=image_id,
reason=(_("fmt=%(fmt)s backed by: %(backing_file)s") %
{'fmt': fmt, 'backing_file': backing_file}))
# NOTE(xqueralt): If the image virtual size doesn't fit in the
# requested volume there is no point on resizing it because it will
# generate an unusable image.
if size is not None and data.virtual_size > size:
params = {'image_size': data.virtual_size, 'volume_size': size}
reason = _("Size is %(image_size)dGB and doesn't fit in a "
"volume of size %(volume_size)dGB.") % params
raise exception.ImageUnacceptable(image_id=image_id, reason=reason)
# NOTE(xqueralt): If the image virtual size doesn't fit in the
# requested volume there is no point on resizing it because it will
# generate an unusable image.
if size is not None and data.virtual_size > size:
params = {'image_size': data.virtual_size, 'volume_size': size}
reason = _("Size is %(image_size)dGB and doesn't fit in a "
"volume of size %(volume_size)dGB.") % params
raise exception.ImageUnacceptable(image_id=image_id,
reason=reason)
def fetch_to_vhd(context, image_service,
@ -264,31 +307,15 @@ def fetch_to_volume_format(context, image_service,
# Unfortunately it seems that you can't pipe to 'qemu-img convert' because
# it seeks. Maybe we can think of something for a future version.
with temporary_file() as tmp:
# We may be on a system that doesn't have qemu-img installed. That
# is ok if we are working with a RAW image. This logic checks to see
# if qemu-img is installed. If not we make sure the image is RAW and
# throw an exception if not. Otherwise we stop before needing
# qemu-img. Systems with qemu-img will always progress through the
# whole function.
has_meta = False if not image_meta else True
try:
# Use the empty tmp file to make sure qemu_img_info works.
qemu_img_info(tmp, run_as_root=run_as_root)
except processutils.ProcessExecutionError:
format_raw = True if image_meta['disk_format'] == 'raw' else False
except TypeError:
format_raw = False
data = get_qemu_data(image_id, has_meta, format_raw,
tmp, run_as_root)
if data is None:
qemu_img = False
if image_meta:
if image_meta['disk_format'] != 'raw':
raise exception.ImageUnacceptable(
reason=_("qemu-img is not installed and image is of "
"type %s. Only RAW images can be used if "
"qemu-img is not installed.") %
image_meta['disk_format'],
image_id=image_id)
else:
raise exception.ImageUnacceptable(
reason=_("qemu-img is not installed and the disk "
"format is not specified. Only RAW images "
"can be used if qemu-img is not installed."),
image_id=image_id)
tmp_images = TemporaryImages.for_image_service(image_service)
tmp_image = tmp_images.get(context, image_id)

View File

@ -757,7 +757,7 @@ class TestFetchToVolumeFormat(test.TestCase):
blocksize)
self.assertIsNone(output)
image_service.show.assert_called_once_with(ctxt, image_id)
self.assertEqual(2, image_service.show.call_count)
self.assertEqual(2, mock_temp.call_count)
mock_info.assert_has_calls([
mock.call(tmp, run_as_root=True),
@ -1160,6 +1160,86 @@ class TestFetchToVolumeFormat(test.TestCase):
mock_convert.assert_called_once_with(tmp, dest, volume_format,
run_as_root=run_as_root)
@mock.patch('cinder.image.image_utils.fetch')
@mock.patch('cinder.image.image_utils.qemu_img_info',
side_effect=processutils.ProcessExecutionError)
@mock.patch('cinder.image.image_utils.temporary_file')
@mock.patch('cinder.image.image_utils.CONF')
def test_no_qemu_img_fetch_verify_image(self, mock_conf,
mock_temp, mock_info,
mock_fetch):
ctxt = mock.sentinel.context
image_service = mock.Mock(temp_images=None)
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
ctxt.user_id = user_id = mock.sentinel.user_id
project_id = mock.sentinel.project_id
size = 4321
run_as_root = mock.sentinel.run_as_root
image_service.show.return_value = {'disk_format': 'raw',
'size': 41126400}
image_utils.fetch_verify_image(
ctxt, image_service, image_id, dest,
user_id=user_id, project_id=project_id, size=size,
run_as_root=run_as_root)
image_service.show.assert_called_once_with(ctxt, image_id)
mock_info.assert_called_once_with(dest, run_as_root=run_as_root)
mock_fetch.assert_called_once_with(ctxt, image_service, image_id,
dest, None, None)
@mock.patch('cinder.image.image_utils.qemu_img_info',
side_effect=processutils.ProcessExecutionError)
@mock.patch('cinder.image.image_utils.temporary_file')
@mock.patch('cinder.image.image_utils.CONF')
def test_get_qemu_data_returns_none(self, mock_conf, mock_temp, mock_info):
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
run_as_root = mock.sentinel.run_as_root
disk_format_raw = True
has_meta = True
output = image_utils.get_qemu_data(image_id, has_meta,
disk_format_raw, dest,
run_as_root=run_as_root)
self.assertIsNone(output)
@mock.patch('cinder.image.image_utils.qemu_img_info',
side_effect=processutils.ProcessExecutionError)
@mock.patch('cinder.image.image_utils.temporary_file')
@mock.patch('cinder.image.image_utils.CONF')
def test_get_qemu_data_with_image_meta_exception(self, mock_conf,
mock_temp, mock_info):
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
run_as_root = mock.sentinel.run_as_root
disk_format_raw = False
has_meta = True
self.assertRaises(
exception.ImageUnacceptable,
image_utils.get_qemu_data, image_id, has_meta, disk_format_raw,
dest, run_as_root=run_as_root)
@mock.patch('cinder.image.image_utils.qemu_img_info',
side_effect=processutils.ProcessExecutionError)
@mock.patch('cinder.image.image_utils.temporary_file')
@mock.patch('cinder.image.image_utils.CONF')
def test_get_qemu_data_without_image_meta_except(self, mock_conf,
mock_temp, mock_info):
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
run_as_root = mock.sentinel.run_as_root
disk_format_raw = False
has_meta = False
self.assertRaises(
exception.ImageUnacceptable,
image_utils.get_qemu_data, image_id, has_meta, disk_format_raw,
dest, run_as_root=run_as_root)
class TestXenserverUtils(test.TestCase):
@mock.patch('cinder.image.image_utils.is_xenserver_format')