예제 #1
0
파일: image_utils.py 프로젝트: xcnix/cinder
def upload_volume(context, image_service, image_meta, volume_path,
                  volume_format='raw', run_as_root=True, compress=True):
    image_id = image_meta['id']
    if image_meta.get('container_format') != 'compressed':
        if (image_meta['disk_format'] == volume_format):
            LOG.debug("%s was %s, no need to convert to %s",
                      image_id, volume_format, image_meta['disk_format'])
            if os.name == 'nt' or os.access(volume_path, os.R_OK):
                with open(volume_path, 'rb') as image_file:
                    image_service.update(context, image_id, {},
                                         tpool.Proxy(image_file))
            else:
                with utils.temporary_chown(volume_path):
                    with open(volume_path, 'rb') as image_file:
                        image_service.update(context, image_id, {},
                                             tpool.Proxy(image_file))
            return

    with temporary_file() as tmp:
        LOG.debug("%s was %s, converting to %s",
                  image_id, volume_format, image_meta['disk_format'])

        data = qemu_img_info(volume_path, run_as_root=run_as_root)
        backing_file = data.backing_file
        fmt = data.file_format
        if backing_file is not None:
            # Disallow backing files as a security measure.
            # This prevents a user from writing an image header into a raw
            # volume with a backing file pointing to data they wish to
            # access.
            raise exception.ImageUnacceptable(
                image_id=image_id,
                reason=_("fmt=%(fmt)s backed by:%(backing_file)s")
                % {'fmt': fmt, 'backing_file': backing_file})

        out_format = fixup_disk_format(image_meta['disk_format'])
        convert_image(volume_path, tmp, out_format,
                      run_as_root=run_as_root,
                      compress=compress)

        data = qemu_img_info(tmp, run_as_root=run_as_root)
        if data.file_format != out_format:
            raise exception.ImageUnacceptable(
                image_id=image_id,
                reason=_("Converted to %(f1)s, but format is now %(f2)s") %
                {'f1': out_format, 'f2': data.file_format})

        # NOTE(ZhengMa): This is used to do image compression on image
        # uploading with 'compressed' container_format.
        # Compress file 'tmp' in-place
        if image_meta.get('container_format') == 'compressed':
            LOG.debug("Container_format set to 'compressed', compressing "
                      "image before uploading.")
            accel = accelerator.ImageAccel(tmp, tmp)
            accel.compress_img(run_as_root=run_as_root)
        with open(tmp, 'rb') as image_file:
            image_service.update(context, image_id, {},
                                 tpool.Proxy(image_file))
예제 #2
0
    def test_compress_img_engine_ready(self, mock_accel_engine_ready,
                                       mock_get_engine):
        source = mock.sentinel.source
        dest = mock.sentinel.dest
        run_as_root = mock.sentinel.run_as_root

        mock_engine = mock.Mock(spec=fakeEngine)
        mock_get_engine.return_value = mock_engine
        accel = accelerator.ImageAccel(source, dest)

        accel.compress_img(run_as_root=run_as_root)
        mock_engine.compress_img.assert_called()
예제 #3
0
    def test_decompress_img_prefer_qat_without_gzip(self, mock_gzip_exist,
                                                    mock_qat_exist, mock_exec):
        source = 'fake_path'
        dest = 'fake_path'

        accel = accelerator.ImageAccel(source, dest)
        accel.decompress_img(run_as_root=True)

        expected = [
            mock.call('mv', source, source + '.gz', run_as_root=True),
            mock.call('qzip', '-d', source + '.gz', run_as_root=True)
        ]

        mock_exec.assert_has_calls(expected)
예제 #4
0
    def test_compress_img_prefer_qat_when_available(self, mock_gzip_exist,
                                                    mock_qat_exist, mock_exec):

        source = 'fake_path'
        dest = 'fake_path'

        accel = accelerator.ImageAccel(source, dest)
        accel.compress_img(run_as_root=True)

        expected = [
            mock.call('qzip', '-k', dest, '-o', dest, run_as_root=True),
            mock.call('mv', dest + '.gz', dest, run_as_root=True)
        ]

        mock_exec.assert_has_calls(expected)
예제 #5
0
    def test_decompress_img_qat_accel_not_exist_gzip_exist(
            self, mock_gzip_exist, mock_qat_exist, mock_exec):
        source = 'fake_path'
        dest = 'fake_path'

        accel = accelerator.ImageAccel(source, dest)
        accel.decompress_img(run_as_root=True)

        not_called = mock.call('qzip', '-d', source + '.gz', run_as_root=True)

        self.assertNotIn(not_called, mock_exec.call_args_list)

        expected = [
            mock.call('mv', source, source + '.gz', run_as_root=True),
            mock.call('gzip', '-d', source + '.gz', run_as_root=True)
        ]

        mock_exec.assert_has_calls(expected)
예제 #6
0
def fetch_to_volume_format(context,
                           image_service,
                           image_id,
                           dest,
                           volume_format,
                           blocksize,
                           volume_subformat=None,
                           user_id=None,
                           project_id=None,
                           size=None,
                           run_as_root=True):
    qemu_img = True
    image_meta = image_service.show(context, image_id)

    allow_image_compression = CONF.allow_compression_on_image_upload
    if image_meta and (image_meta.get('container_format') == 'compressed'):
        if allow_image_compression is False:
            compression_param = {
                'container_format': image_meta.get('container_format')
            }
            raise exception.ImageUnacceptable(
                image_id=image_id,
                reason=_("Image compression disallowed, "
                         "but container_format is "
                         "%(container_format)s.") % compression_param)

    # NOTE(avishay): I'm not crazy about creating temp files which may be
    # large and cause disk full errors which would confuse users.
    # 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:
        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, tmp, run_as_root)
        if data is None:
            qemu_img = False

        tmp_images = TemporaryImages.for_image_service(image_service)
        tmp_image = tmp_images.get(context, image_id)
        if tmp_image:
            tmp = tmp_image
        else:
            fetch(context, image_service, image_id, tmp, user_id, project_id)

        if is_xenserver_format(image_meta):
            replace_xenserver_image_with_coalesced_vhd(tmp)

        if not qemu_img:
            # qemu-img is not installed but we do have a RAW image.  As a
            # result we only need to copy the image to the destination and then
            # return.
            LOG.debug(
                'Copying image from %(tmp)s to volume %(dest)s - '
                'size: %(size)s', {
                    'tmp': tmp,
                    'dest': dest,
                    'size': image_meta['size']
                })
            image_size_m = math.ceil(float(image_meta['size']) / units.Mi)
            volume_utils.copy_volume(tmp, dest, image_size_m, blocksize)
            return

        data = qemu_img_info(tmp, run_as_root=run_as_root)

        # 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:
            check_virtual_size(data.virtual_size, size, image_id)

        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,
                })

        # NOTE(ZhengMa): This is used to do image decompression on image
        # downloading with 'compressed' container_format. It is a
        # transparent level between original image downloaded from
        # Glance and Cinder image service. So the source file path is
        # the same with destination file path.
        if image_meta.get('container_format') == 'compressed':
            LOG.debug("Found image with compressed container format")
            if not accelerator.is_gzip_compressed(tmp):
                raise exception.ImageUnacceptable(
                    image_id=image_id,
                    reason=_("Unsupported compressed image format found. "
                             "Only gzip is supported currently"))
            accel = accelerator.ImageAccel(tmp, tmp)
            accel.decompress_img(run_as_root=run_as_root)

        # NOTE(jdg): I'm using qemu-img convert to write
        # to the volume regardless if it *needs* conversion or not
        # TODO(avishay): We can speed this up by checking if the image is raw
        # and if so, writing directly to the device. However, we need to keep
        # check via 'qemu-img info' that what we copied was in fact a raw
        # image and not a different format with a backing file, which may be
        # malicious.
        disk_format = fixup_disk_format(image_meta['disk_format'])
        LOG.debug("%s was %s, converting to %s", image_id, fmt, volume_format)

        convert_image(tmp,
                      dest,
                      volume_format,
                      out_subformat=volume_subformat,
                      src_format=disk_format,
                      run_as_root=run_as_root)