Merge "image_utils: Detect missing device before calling qemu-img convert"
This commit is contained in:
@@ -51,6 +51,7 @@ from cinder.i18n import _
|
||||
from cinder.image import accelerator
|
||||
from cinder.image import glance
|
||||
import cinder.privsep.format_inspector
|
||||
import cinder.privsep.path
|
||||
from cinder import utils
|
||||
from cinder.volume import throttling
|
||||
from cinder.volume import volume_utils
|
||||
@@ -376,6 +377,16 @@ def check_qemu_img_version(minimum_version: str) -> None:
|
||||
raise exception.VolumeBackendAPIException(data=_msg)
|
||||
|
||||
|
||||
def _ensure_exists(dest_path: str) -> None:
|
||||
# Ensure that "dest" exists if it is in /dev/
|
||||
# This prevents qemu-img from creating a file in /dev/
|
||||
# if the block device has disappeared.
|
||||
if dest_path.startswith('/dev/'):
|
||||
if not cinder.privsep.path.exists(dest_path):
|
||||
raise exception.CinderException(
|
||||
"qemu-img convert destination %s does not exist" % dest_path)
|
||||
|
||||
|
||||
def _convert_image(
|
||||
prefix: tuple,
|
||||
source: str,
|
||||
@@ -443,6 +454,8 @@ def _convert_image(
|
||||
src_passphrase_file=src_passphrase_file,
|
||||
disable_sparse=disable_sparse)
|
||||
|
||||
_ensure_exists(dest)
|
||||
|
||||
start_time = timeutils.utcnow()
|
||||
|
||||
# If there is not enough space on the conversion partition, include
|
||||
|
||||
@@ -37,3 +37,8 @@ def symlink(src, dest):
|
||||
if not os.path.exists(src):
|
||||
raise exception.FileNotFound(file_path=src)
|
||||
os.symlink(src, dest)
|
||||
|
||||
|
||||
@cinder.privsep.sys_admin_pctxt.entrypoint
|
||||
def exists(path: str | os.PathLike) -> bool:
|
||||
return os.path.exists(path)
|
||||
|
||||
@@ -264,6 +264,8 @@ class TestQemuImgInfo(test.TestCase):
|
||||
|
||||
@ddt.ddt
|
||||
class TestConvertImage(test.TestCase):
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=True)
|
||||
@@ -296,6 +298,8 @@ class TestConvertImage(test.TestCase):
|
||||
'-O', out_format, source, dest,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=True)
|
||||
@@ -333,6 +337,8 @@ class TestConvertImage(test.TestCase):
|
||||
'-O', out_format, source, dest,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.volume.volume_utils.check_for_odirect_support',
|
||||
return_value=True)
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@@ -358,6 +364,8 @@ class TestConvertImage(test.TestCase):
|
||||
source, dest,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.volume.volume_utils.check_for_odirect_support',
|
||||
return_value=True)
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@@ -383,6 +391,8 @@ class TestConvertImage(test.TestCase):
|
||||
source, dest,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=True)
|
||||
@@ -403,6 +413,8 @@ class TestConvertImage(test.TestCase):
|
||||
'-O', out_format, '-t', 'none',
|
||||
source, dest, run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=False)
|
||||
@@ -422,6 +434,8 @@ class TestConvertImage(test.TestCase):
|
||||
source, dest, run_as_root=True)
|
||||
|
||||
@ddt.data(True, False)
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=False)
|
||||
@@ -446,6 +460,8 @@ class TestConvertImage(test.TestCase):
|
||||
mock_exec.assert_called_once_with(*exec_args,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('cinder.utils.is_blk_device', return_value=False)
|
||||
@@ -464,6 +480,8 @@ class TestConvertImage(test.TestCase):
|
||||
'-O', out_format, '-S', '0', source,
|
||||
dest, run_as_root=True)
|
||||
|
||||
@mock.patch('cinder.image.image_utils._ensure_exists',
|
||||
new=mock.MagicMock())
|
||||
@mock.patch('cinder.volume.volume_utils.check_for_odirect_support',
|
||||
return_value=True)
|
||||
@mock.patch('cinder.image.image_utils.qemu_img_info')
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
`Bug #2132083 <https://bugs.launchpad.net/cinder/+bug/2132083>`_: When
|
||||
creating a volume from an image, a storage connectivity failure or
|
||||
similar could result in the local block device node disappearing. In
|
||||
this situation, avoid writing the image to a file in /dev/ when calling
|
||||
qemu-img.
|
||||
Reference in New Issue
Block a user