diff --git a/cinder/api/contrib/volume_actions.py b/cinder/api/contrib/volume_actions.py index 99b1c540d3f..6b1d0f57c48 100644 --- a/cinder/api/contrib/volume_actions.py +++ b/cinder/api/contrib/volume_actions.py @@ -250,6 +250,8 @@ class VolumeActionsController(wsgi.Controller): image_utils.VALID_DISK_FORMATS) } raise webob.exc.HTTPBadRequest(explanation=msg) + if disk_format == "parallels": + disk_format = "ploop" image_metadata = {"container_format": params.get( "container_format", "bare"), diff --git a/cinder/image/image_utils.py b/cinder/image/image_utils.py index a6e35420584..8c5988f653e 100644 --- a/cinder/image/image_utils.py +++ b/cinder/image/image_utils.py @@ -468,9 +468,12 @@ def upload_volume(context, image_service, image_meta, volume_path, % {'fmt': fmt, 'backing_file': backing_file}) out_format = image_meta['disk_format'] - # qemu-img accepts 'vpc' as argument for vhd format + # qemu-img accepts 'vpc' as argument for 'vhd 'format and 'parallels' + # as argument for 'ploop'. if out_format == 'vhd': out_format = 'vpc' + if out_format == 'ploop': + out_format = 'parallels' convert_image(volume_path, tmp, out_format, run_as_root=run_as_root) diff --git a/cinder/tests/unit/api/contrib/test_volume_actions.py b/cinder/tests/unit/api/contrib/test_volume_actions.py index 55eea53abd2..9b282106f0e 100644 --- a/cinder/tests/unit/api/contrib/test_volume_actions.py +++ b/cinder/tests/unit/api/contrib/test_volume_actions.py @@ -923,6 +923,26 @@ class VolumeImageActionsTest(test.TestCase): id, body) + @mock.patch.object(volume_api.API, "copy_volume_to_image") + def test_copy_volume_to_image_disk_format_parallels(self, + mock_copy_to_image): + volume = utils.create_volume(self.context, metadata={'test': 'test'}) + + img = {"container_format": 'bare', + "disk_format": 'parallels', + "image_name": 'image_name'} + body = {"os-volume_upload_image": img} + req = fakes.HTTPRequest.blank('/v3/%s/volumes/%s/action' % + (fake.PROJECT_ID, volume.id)) + + image_metadata = {'container_format': 'bare', + 'disk_format': 'ploop', + 'name': 'image_name'} + self.controller._volume_upload_image(req, volume.id, body) + + mock_copy_to_image.assert_called_once_with( + req.environ['cinder.context'], volume, image_metadata, False) + @mock.patch.object(volume_api.API, 'get', fake_volume_get_obj) @mock.patch.object(volume_api.API, 'copy_volume_to_image', side_effect=ValueError) diff --git a/cinder/tests/unit/test_image_utils.py b/cinder/tests/unit/test_image_utils.py index 3d62cd60502..bf0737dc937 100644 --- a/cinder/tests/unit/test_image_utils.py +++ b/cinder/tests/unit/test_image_utils.py @@ -15,6 +15,7 @@ # under the License. """Unit tests for image utils.""" +import ddt import errno import math @@ -466,23 +467,27 @@ class TestTemporaryDir(test.TestCase): self.assertEqual(output, mock_tempdir.return_value) +@ddt.ddt class TestUploadVolume(test.TestCase): + @ddt.data((mock.sentinel.disk_format, mock.sentinel.disk_format), + ('ploop', 'parallels')) @mock.patch('cinder.image.image_utils.CONF') @mock.patch('six.moves.builtins.open') @mock.patch('cinder.image.image_utils.qemu_img_info') @mock.patch('cinder.image.image_utils.convert_image') @mock.patch('cinder.image.image_utils.temporary_file') @mock.patch('cinder.image.image_utils.os') - def test_diff_format(self, mock_os, mock_temp, mock_convert, mock_info, - mock_open, mock_conf): + def test_diff_format(self, image_format, mock_os, mock_temp, mock_convert, + mock_info, mock_open, mock_conf): + input_format, output_format = image_format ctxt = mock.sentinel.context image_service = mock.Mock() image_meta = {'id': 'test_id', - 'disk_format': mock.sentinel.disk_format} + 'disk_format': input_format} volume_path = mock.sentinel.volume_path mock_os.name = 'posix' data = mock_info.return_value - data.file_format = mock.sentinel.disk_format + data.file_format = output_format data.backing_file = None temp_file = mock_temp.return_value.__enter__.return_value @@ -492,7 +497,7 @@ class TestUploadVolume(test.TestCase): self.assertIsNone(output) mock_convert.assert_called_once_with(volume_path, temp_file, - mock.sentinel.disk_format, + output_format, run_as_root=True) mock_info.assert_called_with(temp_file, run_as_root=True) self.assertEqual(2, mock_info.call_count)