Fix the bug of OSError when convert image
When I try to convert a image use image_utils.convert_image() method, an error occurred like this : ''' OSError: [Errno 2] No such file or directory: 'sheepdog:10.133.17.61:7000:volume-a0a70f9b-a50e-4369-885f-c41a894c9fe5' ''' The reason is that in some cluster storage systems, like ceph/sheepdog, QEMU can access an image directly via their private protocol, and there’s no need to map an image as a block device on the host. In this case, the qemu-img convert command may like: #qemu-img convert -O raw sheepdog:Ip:port:image_name temp_file #qemu-img convert -O raw rbd:pool_name/image_name temp_file The source path may be 'sheepdog:Ip:port:image_name' or 'rbd:pool_name/image_name', it doesn't exist in OS. So, when it runs the os.stat(source) in image_utils.convert_image(source,dest,out_format) method, an OSError would be raised. We can use qemu_img_info method instead to resolve this problem, because the 'qemu-img info' command can always get the image size info which has support qemu-img tool. Here we capture a ValueError just in case, but it only need to give a warning message, because the image has been successfully converted. Change-Id: I5fd1e51840972a67053b85a76f8e001fa8148ad7 Closes-Bug: #1514442
This commit is contained in:
parent
a6f92ceaf0
commit
53073d1921
|
@ -126,7 +126,17 @@ def _convert_image(prefix, source, dest, out_format, run_as_root=True):
|
|||
# some incredible event this is 0 (cirros image?) don't barf
|
||||
if duration < 1:
|
||||
duration = 1
|
||||
fsz_mb = os.stat(source).st_size / units.Mi
|
||||
try:
|
||||
image_size = qemu_img_info(source, run_as_root=True).virtual_size
|
||||
except ValueError as e:
|
||||
msg = _LI("The image was successfully converted, but image size "
|
||||
"is unavailable. src %(src)s, dest %(dest)s. %(error)s")
|
||||
LOG.info(msg, {"src": source,
|
||||
"dest": dest,
|
||||
"error": e})
|
||||
return
|
||||
|
||||
fsz_mb = image_size / units.Mi
|
||||
mbps = (fsz_mb / duration)
|
||||
msg = ("Image conversion details: src %(src)s, size %(sz).2f MB, "
|
||||
"duration %(duration).2f sec, destination %(dest)s")
|
||||
|
|
|
@ -114,15 +114,15 @@ class TestQemuImgInfo(test.TestCase):
|
|||
|
||||
|
||||
class TestConvertImage(test.TestCase):
|
||||
@mock.patch('cinder.image.image_utils.os.stat')
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=True)
|
||||
def test_defaults_block_dev(self, mock_isblk, mock_exec,
|
||||
mock_stat):
|
||||
def test_defaults_block_dev_with_size_info(self, mock_isblk,
|
||||
mock_exec, mock_info):
|
||||
source = mock.sentinel.source
|
||||
dest = mock.sentinel.dest
|
||||
out_format = mock.sentinel.out_format
|
||||
mock_stat.return_value.st_size = 1048576
|
||||
mock_info.return_value.virtual_size = 1048576
|
||||
throttle = throttling.Throttle(prefix=['cgcmd'])
|
||||
|
||||
with mock.patch('cinder.volume.utils.check_for_odirect_support',
|
||||
|
@ -146,17 +146,75 @@ class TestConvertImage(test.TestCase):
|
|||
'-O', out_format, source, dest,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.volume.utils.check_for_odirect_support',
|
||||
return_value=True)
|
||||
@mock.patch('cinder.image.image_utils.os.stat')
|
||||
@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_defaults_not_block_dev(self, mock_isblk, mock_exec,
|
||||
mock_stat, mock_odirect):
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=True)
|
||||
def test_defaults_block_dev_without_size_info(self, mock_isblk,
|
||||
mock_exec,
|
||||
mock_info):
|
||||
source = mock.sentinel.source
|
||||
dest = mock.sentinel.dest
|
||||
out_format = mock.sentinel.out_format
|
||||
mock_stat.return_value.st_size = 1048576
|
||||
mock_info.side_effect = ValueError
|
||||
throttle = throttling.Throttle(prefix=['cgcmd'])
|
||||
|
||||
with mock.patch('cinder.volume.utils.check_for_odirect_support',
|
||||
return_value=True):
|
||||
output = image_utils.convert_image(source, dest, out_format,
|
||||
throttle=throttle)
|
||||
|
||||
mock_info.assert_called_once_with(source, run_as_root=True)
|
||||
self.assertIsNone(output)
|
||||
mock_exec.assert_called_once_with('cgcmd', 'qemu-img', 'convert',
|
||||
'-t', 'none', '-O', out_format,
|
||||
source, dest, run_as_root=True)
|
||||
|
||||
mock_exec.reset_mock()
|
||||
|
||||
with mock.patch('cinder.volume.utils.check_for_odirect_support',
|
||||
return_value=False):
|
||||
output = image_utils.convert_image(source, dest, out_format)
|
||||
|
||||
self.assertIsNone(output)
|
||||
mock_exec.assert_called_once_with('qemu-img', 'convert',
|
||||
'-O', out_format, source, dest,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.volume.utils.check_for_odirect_support',
|
||||
return_value=True)
|
||||
@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_defaults_not_block_dev_with_size_info(self, mock_isblk,
|
||||
mock_exec,
|
||||
mock_info,
|
||||
mock_odirect):
|
||||
source = mock.sentinel.source
|
||||
dest = mock.sentinel.dest
|
||||
out_format = mock.sentinel.out_format
|
||||
mock_info.return_value.virtual_size = 1048576
|
||||
|
||||
output = image_utils.convert_image(source, dest, out_format)
|
||||
|
||||
self.assertIsNone(output)
|
||||
mock_exec.assert_called_once_with('qemu-img', 'convert', '-O',
|
||||
out_format, source, dest,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.volume.utils.check_for_odirect_support',
|
||||
return_value=True)
|
||||
@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_defaults_not_block_dev_without_size_info(self,
|
||||
mock_isblk,
|
||||
mock_exec,
|
||||
mock_info,
|
||||
mock_odirect):
|
||||
source = mock.sentinel.source
|
||||
dest = mock.sentinel.dest
|
||||
out_format = mock.sentinel.out_format
|
||||
mock_info.side_effect = ValueError
|
||||
|
||||
output = image_utils.convert_image(source, dest, out_format)
|
||||
|
||||
|
|
Loading…
Reference in New Issue