qemu_img_info: Don't autodetect source format

Use the format information provided by Glance.

Autodetection of formats is almost never what we want
to do: it introduces possibilities for dangerous
behavior and complexity, and we can avoid it here
because Glance gives us enough information to not need
it.

More directly for the encrypted image->volume case,
this prevents qemu-img from treating an encrypted image
as a "luks" container that it should decrypt, instead
treating it as raw data.

Change-Id: I4071927c491870626fe174b75ecaf8ef6da39cf5
This commit is contained in:
Eric Harney 2017-04-04 15:26:47 -04:00
parent df87d8901e
commit b1ebfa4f5d
2 changed files with 47 additions and 26 deletions

View File

@ -122,7 +122,8 @@ def check_qemu_img_version(minimum_version):
raise exception.VolumeBackendAPIException(data=_msg)
def _convert_image(prefix, source, dest, out_format, run_as_root=True):
def _convert_image(prefix, source, dest, out_format,
src_format=None, run_as_root=True):
"""Convert image to other format."""
cmd = prefix + ('qemu-img', 'convert',
@ -142,8 +143,12 @@ def _convert_image(prefix, source, dest, out_format, run_as_root=True):
dest,
'oflag=direct')):
cmd = prefix + ('qemu-img', 'convert',
'-t', 'none',
'-O', out_format, source, dest)
'-t', 'none')
if src_format is not None:
cmd += ('-f', src_format) # prevent detection of format
cmd += ('-O', out_format, source, dest)
start_time = timeutils.utcnow()
utils.execute(*cmd, run_as_root=run_as_root)
@ -177,13 +182,16 @@ def _convert_image(prefix, source, dest, out_format, run_as_root=True):
LOG.info(msg, {"sz": fsz_mb, "mbps": mbps})
def convert_image(source, dest, out_format, run_as_root=True, throttle=None):
def convert_image(source, dest, out_format, src_format=None,
run_as_root=True, throttle=None):
if not throttle:
throttle = throttling.Throttle.get_default()
with throttle.subcommand(source, dest) as throttle_cmd:
_convert_image(tuple(throttle_cmd['prefix']),
source, dest,
out_format, run_as_root=run_as_root)
out_format,
src_format=src_format,
run_as_root=run_as_root)
def resize_image(source, size, run_as_root=False):
@ -400,6 +408,7 @@ def fetch_to_volume_format(context, image_service,
# malicious.
LOG.debug("%s was %s, converting to %s ", image_id, fmt, volume_format)
convert_image(tmp, dest, volume_format,
src_format=image_meta['disk_format'],
run_as_root=run_as_root)
data = qemu_img_info(dest, run_as_root=run_as_root)

View File

@ -334,7 +334,7 @@ class TestVerifyImage(test.TestCase):
def test_kwargs(self, mock_fetch, mock_fileutils, mock_info,
mock_check_space):
ctxt = mock.sentinel.context
image_service = mock.Mock()
image_service = FakeImageService()
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
user_id = mock.sentinel.user_id
@ -656,6 +656,17 @@ class TestFetchToRaw(test.TestCase):
run_as_root=run_as_root)
class FakeImageService(object):
def __init__(self, db_driver=None, image_service=None):
self.temp_images = None
def show(self, context, image_id):
return {'size': 2 * units.Gi,
'disk_format': 'raw',
'container_format': 'bare',
'status': 'active'}
class TestFetchToVolumeFormat(test.TestCase):
@mock.patch('cinder.image.image_utils.check_available_space')
@mock.patch('cinder.image.image_utils.convert_image')
@ -673,7 +684,7 @@ class TestFetchToVolumeFormat(test.TestCase):
mock_check_space):
ctxt = mock.sentinel.context
ctxt.user_id = mock.sentinel.user_id
image_service = mock.Mock(temp_images=None)
image_service = FakeImageService()
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
volume_format = mock.sentinel.volume_format
@ -690,7 +701,6 @@ class TestFetchToVolumeFormat(test.TestCase):
volume_format, blocksize)
self.assertIsNone(output)
image_service.show.assert_called_once_with(ctxt, image_id)
mock_temp.assert_called_once_with()
mock_info.assert_has_calls([
mock.call(tmp, run_as_root=True),
@ -701,7 +711,8 @@ class TestFetchToVolumeFormat(test.TestCase):
self.assertFalse(mock_repl_xen.called)
self.assertFalse(mock_copy.called)
mock_convert.assert_called_once_with(tmp, dest, volume_format,
run_as_root=True)
run_as_root=True,
src_format='raw')
@mock.patch('cinder.image.image_utils.check_available_space')
@mock.patch('cinder.image.image_utils.convert_image')
@ -718,7 +729,7 @@ class TestFetchToVolumeFormat(test.TestCase):
mock_is_xen, mock_repl_xen, mock_copy, mock_convert,
mock_check_space):
ctxt = mock.sentinel.context
image_service = mock.Mock(temp_images=None)
image_service = FakeImageService()
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
volume_format = mock.sentinel.volume_format
@ -740,7 +751,6 @@ class TestFetchToVolumeFormat(test.TestCase):
run_as_root=run_as_root)
self.assertIsNone(output)
image_service.show.assert_called_once_with(ctxt, image_id)
mock_temp.assert_called_once_with()
mock_info.assert_has_calls([
mock.call(tmp, run_as_root=run_as_root),
@ -751,25 +761,27 @@ class TestFetchToVolumeFormat(test.TestCase):
self.assertFalse(mock_repl_xen.called)
self.assertFalse(mock_copy.called)
mock_convert.assert_called_once_with(tmp, dest, volume_format,
run_as_root=run_as_root)
run_as_root=run_as_root,
src_format='raw')
@mock.patch('cinder.image.image_utils.check_available_space')
@mock.patch('cinder.image.image_utils.check_available_space',
new=mock.Mock())
@mock.patch('cinder.image.image_utils.is_xenserver_format',
new=mock.Mock(return_value=False))
@mock.patch('cinder.image.image_utils.convert_image')
@mock.patch('cinder.image.image_utils.volume_utils.copy_volume')
@mock.patch(
'cinder.image.image_utils.replace_xenserver_image_with_coalesced_vhd')
@mock.patch('cinder.image.image_utils.is_xenserver_format',
return_value=False)
@mock.patch('cinder.image.image_utils.fetch')
@mock.patch('cinder.image.image_utils.qemu_img_info')
@mock.patch('cinder.image.image_utils.temporary_file')
@mock.patch('cinder.image.image_utils.CONF')
def test_temporary_images(self, mock_conf, mock_temp, mock_info,
mock_fetch, mock_is_xen, mock_repl_xen,
mock_copy, mock_convert, mock_check_space):
mock_fetch, mock_repl_xen,
mock_copy, mock_convert):
ctxt = mock.sentinel.context
ctxt.user_id = mock.sentinel.user_id
image_service = mock.Mock(temp_images=None)
image_service = FakeImageService()
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
volume_format = mock.sentinel.volume_format
@ -792,7 +804,6 @@ class TestFetchToVolumeFormat(test.TestCase):
blocksize)
self.assertIsNone(output)
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),
@ -804,7 +815,8 @@ class TestFetchToVolumeFormat(test.TestCase):
self.assertFalse(mock_repl_xen.called)
self.assertFalse(mock_copy.called)
mock_convert.assert_called_once_with(tmp, dest, volume_format,
run_as_root=True)
run_as_root=True,
src_format='raw')
@mock.patch('cinder.image.image_utils.convert_image')
@mock.patch('cinder.image.image_utils.volume_utils.copy_volume')
@ -1143,7 +1155,7 @@ class TestFetchToVolumeFormat(test.TestCase):
mock_check_space,
legacy_format_name=False):
ctxt = mock.sentinel.context
image_service = mock.Mock(temp_images=None)
image_service = FakeImageService()
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
volume_format = 'vhd'
@ -1172,7 +1184,6 @@ class TestFetchToVolumeFormat(test.TestCase):
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_temp.assert_called_once_with()
mock_info.assert_has_calls([
mock.call(tmp, run_as_root=run_as_root),
@ -1183,7 +1194,8 @@ class TestFetchToVolumeFormat(test.TestCase):
self.assertFalse(mock_repl_xen.called)
self.assertFalse(mock_copy.called)
mock_convert.assert_called_once_with(tmp, dest, volume_format,
run_as_root=run_as_root)
run_as_root=run_as_root,
src_format='raw')
def test_format_mismatch(self):
self._test_format_name_mismatch()
@ -1208,7 +1220,7 @@ class TestFetchToVolumeFormat(test.TestCase):
mock_fetch, mock_is_xen, mock_repl_xen,
mock_copy, mock_convert, mock_check_space):
ctxt = mock.sentinel.context
image_service = mock.Mock(temp_images=None)
image_service = FakeImageService()
image_id = mock.sentinel.image_id
dest = mock.sentinel.dest
volume_format = mock.sentinel.volume_format
@ -1230,7 +1242,6 @@ class TestFetchToVolumeFormat(test.TestCase):
run_as_root=run_as_root)
self.assertIsNone(output)
image_service.show.assert_called_once_with(ctxt, image_id)
mock_temp.assert_called_once_with()
mock_info.assert_has_calls([
mock.call(tmp, run_as_root=run_as_root),
@ -1241,7 +1252,8 @@ class TestFetchToVolumeFormat(test.TestCase):
mock_repl_xen.assert_called_once_with(tmp)
self.assertFalse(mock_copy.called)
mock_convert.assert_called_once_with(tmp, dest, volume_format,
run_as_root=run_as_root)
run_as_root=run_as_root,
src_format='raw')
@mock.patch('cinder.image.image_utils.fetch')
@mock.patch('cinder.image.image_utils.qemu_img_info',