Merge "Compress images uploaded to Glance"
This commit is contained in:
commit
80a46a80ed
@ -57,7 +57,12 @@ image_opts = [
|
||||
cfg.StrOpt('image_conversion_dir',
|
||||
default='$state_path/conversion',
|
||||
help='Directory used for temporary storage '
|
||||
'during image conversion'), ]
|
||||
'during image conversion'),
|
||||
cfg.BoolOpt('image_compress_on_upload',
|
||||
default=True,
|
||||
help='When possible, compress images uploaded '
|
||||
'to the image service'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(image_opts)
|
||||
@ -79,6 +84,8 @@ QEMU_IMG_VERSION = None
|
||||
QEMU_IMG_MIN_FORCE_SHARE_VERSION = [2, 10, 0]
|
||||
QEMU_IMG_MIN_CONVERT_LUKS_VERSION = '2.10'
|
||||
|
||||
COMPRESSIBLE_IMAGE_FORMATS = ('qcow2')
|
||||
|
||||
|
||||
def fixup_disk_format(disk_format):
|
||||
"""Return the format to be provided to qemu-img convert."""
|
||||
@ -141,7 +148,8 @@ def qemu_img_supports_force_share():
|
||||
|
||||
def _get_qemu_convert_cmd(src, dest, out_format, src_format=None,
|
||||
out_subformat=None, cache_mode=None,
|
||||
prefix=None, cipher_spec=None, passphrase_file=None):
|
||||
prefix=None, cipher_spec=None,
|
||||
passphrase_file=None, compress=False):
|
||||
|
||||
if out_format == 'vhd':
|
||||
# qemu-img still uses the legacy vpc name
|
||||
@ -155,6 +163,10 @@ def _get_qemu_convert_cmd(src, dest, out_format, src_format=None,
|
||||
if cache_mode:
|
||||
cmd += ('-t', cache_mode)
|
||||
|
||||
if CONF.image_compress_on_upload and compress:
|
||||
if out_format in COMPRESSIBLE_IMAGE_FORMATS:
|
||||
cmd += ('-c',)
|
||||
|
||||
if out_subformat:
|
||||
cmd += ('-o', 'subformat=%s' % out_subformat)
|
||||
|
||||
@ -206,8 +218,21 @@ def check_qemu_img_version(minimum_version):
|
||||
|
||||
def _convert_image(prefix, source, dest, out_format,
|
||||
out_subformat=None, src_format=None,
|
||||
run_as_root=True, cipher_spec=None, passphrase_file=None):
|
||||
"""Convert image to other format."""
|
||||
run_as_root=True, cipher_spec=None,
|
||||
passphrase_file=None, compress=False):
|
||||
"""Convert image to other format.
|
||||
|
||||
:param prefix: command prefix, i.e. cgexec for throttling
|
||||
:param source: source filename
|
||||
:param dest: destination filename
|
||||
:param out_format: output image format of qemu-img
|
||||
:param out_subformat: output image subformat
|
||||
:param src_format: source image format
|
||||
:param run_as_root: run qemu-img as root
|
||||
:param cipher_spec: encryption details
|
||||
:param passphrase_file: filename containing luks passphrase
|
||||
:param compress: compress w/ qemu-img when possible (best effort)
|
||||
"""
|
||||
|
||||
# Check whether O_DIRECT is supported and set '-t none' if it is
|
||||
# This is needed to ensure that all data hit the device before
|
||||
@ -234,7 +259,8 @@ def _convert_image(prefix, source, dest, out_format,
|
||||
cache_mode=cache_mode,
|
||||
prefix=prefix,
|
||||
cipher_spec=cipher_spec,
|
||||
passphrase_file=passphrase_file)
|
||||
passphrase_file=passphrase_file,
|
||||
compress=compress)
|
||||
|
||||
start_time = timeutils.utcnow()
|
||||
|
||||
@ -286,7 +312,8 @@ def _convert_image(prefix, source, dest, out_format,
|
||||
|
||||
def convert_image(source, dest, out_format, out_subformat=None,
|
||||
src_format=None, run_as_root=True, throttle=None,
|
||||
cipher_spec=None, passphrase_file=None):
|
||||
cipher_spec=None, passphrase_file=None,
|
||||
compress=False):
|
||||
if not throttle:
|
||||
throttle = throttling.Throttle.get_default()
|
||||
with throttle.subcommand(source, dest) as throttle_cmd:
|
||||
@ -297,7 +324,8 @@ def convert_image(source, dest, out_format, out_subformat=None,
|
||||
src_format=src_format,
|
||||
run_as_root=run_as_root,
|
||||
cipher_spec=cipher_spec,
|
||||
passphrase_file=passphrase_file)
|
||||
passphrase_file=passphrase_file,
|
||||
compress=compress)
|
||||
|
||||
|
||||
def resize_image(source, size, run_as_root=False):
|
||||
@ -641,7 +669,8 @@ def upload_volume(context, image_service, image_meta, volume_path,
|
||||
|
||||
out_format = fixup_disk_format(image_meta['disk_format'])
|
||||
convert_image(volume_path, tmp, out_format,
|
||||
run_as_root=run_as_root)
|
||||
run_as_root=run_as_root,
|
||||
compress=True)
|
||||
|
||||
data = qemu_img_info(tmp, run_as_root=run_as_root)
|
||||
if data.file_format != out_format:
|
||||
|
@ -124,6 +124,7 @@ class TestQemuImgInfo(test.TestCase):
|
||||
current_version=[1, 8])
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestConvertImage(test.TestCase):
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@ -280,6 +281,31 @@ class TestConvertImage(test.TestCase):
|
||||
'-O', 'vpc',
|
||||
source, dest, run_as_root=True)
|
||||
|
||||
@ddt.data(True, False)
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=False)
|
||||
def test_convert_to_qcow2(self,
|
||||
compress_option,
|
||||
mock_isblk, mock_exec, mock_info):
|
||||
self.override_config('image_compress_on_upload', compress_option)
|
||||
source = mock.sentinel.source
|
||||
dest = mock.sentinel.dest
|
||||
out_format = 'qcow2'
|
||||
mock_info.return_value.virtual_size = 1048576
|
||||
|
||||
image_utils.convert_image(source,
|
||||
dest,
|
||||
out_format,
|
||||
compress=True)
|
||||
|
||||
exec_args = ['qemu-img', 'convert', '-O', 'qcow2']
|
||||
if compress_option:
|
||||
exec_args.append('-c')
|
||||
exec_args.extend((source, dest))
|
||||
mock_exec.assert_called_once_with(*exec_args,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.image.image_utils.CONF')
|
||||
@mock.patch('cinder.volume.utils.check_for_odirect_support',
|
||||
return_value=True)
|
||||
@ -727,7 +753,8 @@ class TestUploadVolume(test.TestCase):
|
||||
mock_convert.assert_called_once_with(volume_path,
|
||||
temp_file,
|
||||
output_format,
|
||||
run_as_root=True)
|
||||
run_as_root=True,
|
||||
compress=True)
|
||||
mock_info.assert_called_with(temp_file, run_as_root=True)
|
||||
self.assertEqual(2, mock_info.call_count)
|
||||
mock_open.assert_called_once_with(temp_file, 'rb')
|
||||
@ -823,7 +850,8 @@ class TestUploadVolume(test.TestCase):
|
||||
mock_convert.assert_called_once_with(volume_path,
|
||||
temp_file,
|
||||
mock.sentinel.disk_format,
|
||||
run_as_root=True)
|
||||
run_as_root=True,
|
||||
compress=True)
|
||||
mock_info.assert_called_with(temp_file, run_as_root=True)
|
||||
self.assertEqual(2, mock_info.call_count)
|
||||
self.assertFalse(image_service.update.called)
|
||||
|
@ -896,7 +896,8 @@ class BaseVD(object):
|
||||
image_utils.upload_volume(context,
|
||||
image_service,
|
||||
image_meta,
|
||||
attach_info['device']['path'])
|
||||
attach_info['device']['path'],
|
||||
compress=True)
|
||||
finally:
|
||||
# Since attached volume was not used for writing we can force
|
||||
# detach it
|
||||
|
7
releasenotes/notes/compress-images-fed3e354d94b0845.yaml
Normal file
7
releasenotes/notes/compress-images-fed3e354d94b0845.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
When uploading qcow2 images to Glance, image data will be compressed. This
|
||||
will generally result in less data transferred to Glance at the expense of
|
||||
higher CPU usage. This behavior is controlled by the
|
||||
"image_compress_on_upload" boolean option, which defaults to True.
|
Loading…
x
Reference in New Issue
Block a user