Merge "NFS driver: Use database format in initialize_connection"

This commit is contained in:
Zuul
2025-12-19 22:19:49 +00:00
committed by Gerrit Code Review
6 changed files with 97 additions and 37 deletions

View File

@@ -31,6 +31,7 @@ import os
import re
import shutil
import tempfile
import typing
from typing import ContextManager, Generator, Optional
import cryptography
@@ -163,20 +164,28 @@ def qemu_img_info(
path: str,
run_as_root: bool = True,
force_share: bool = False,
allow_qcow2_backing_file: bool = False) -> imageutils.QemuImgInfo:
allow_qcow2_backing_file: bool = False,
img_format: Optional[str] = None) -> imageutils.QemuImgInfo:
"""Return an object containing the parsed output from qemu-img info."""
format_name = cinder.privsep.format_inspector.get_format_if_safe(
path=path,
allow_qcow2_backing_file=allow_qcow2_backing_file)
if format_name is None:
LOG.warning('Image/Volume %s failed safety check', path)
# NOTE(danms): This is the same exception as would be raised
# by qemu_img_info() if the disk format was unreadable or
# otherwise unsuitable.
raise exception.Invalid(
reason=_('Image/Volume failed safety check'))
format_name = img_format
# NOTE(sfernand): In case we are trying to inspect a raw volume containing
# a qcow2 image, inspection will incorrectly report the inner (qcow2)
# format, instead of the expected outer (raw) format. To avoid that, we
# need skip format inspection if the Cinder volume is known to be raw.
if img_format != 'raw':
format_name = cinder.privsep.format_inspector.get_format_if_safe(
path=path,
allow_qcow2_backing_file=allow_qcow2_backing_file)
if format_name is None:
LOG.warning('Image/Volume %s failed safety check', path)
# NOTE(danms): This is the same exception as would be raised
# by qemu_img_info() if the disk format was unreadable or
# otherwise unsuitable.
raise exception.Invalid(
reason=_('Image/Volume failed safety check'))
format_name = typing.cast(str, format_name)
cmd = ['env', 'LC_ALL=C', 'qemu-img', 'info',
'-f', format_name, '--output=json']
if force_share:

View File

@@ -336,6 +336,13 @@ NFS_CONFIG4 = {'max_over_subscription_ratio': 20.0,
'nas_secure_file_permissions': 'false',
'nas_secure_file_operations': 'true'}
VOLUME_ADMIN_METADATA_QCOW2_FORMAT = {
'format': 'qcow2'
}
VOLUME_ADMIN_METADATA_RAW_FORMAT = {
'format', 'raw'
}
QEMU_IMG_INFO_OUT1 = """{
"filename": "%(volid)s",
"format": "raw",
@@ -1372,10 +1379,12 @@ class NfsDriverTestCase(test.TestCase):
mock_read_info_file.assert_called_once_with(info_path)
snap_info_call = mock.call(snap_path,
force_share=True, run_as_root=True,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
src_info_call = mock.call(src_vol_path,
force_share=True, run_as_root=True,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
self.assertEqual(2, mock_img_info.call_count)
mock_img_info.assert_has_calls([snap_info_call, src_info_call])
used_qcow = nfs_conf['nfs_qcow2_volumes']
@@ -1483,16 +1492,22 @@ class NfsDriverTestCase(test.TestCase):
new_volume,
fake_snap)
@ddt.data([NFS_CONFIG1, QEMU_IMG_INFO_OUT1],
[NFS_CONFIG2, QEMU_IMG_INFO_OUT2],
[NFS_CONFIG3, QEMU_IMG_INFO_OUT1],
[NFS_CONFIG4, QEMU_IMG_INFO_OUT2])
@ddt.data([NFS_CONFIG1, QEMU_IMG_INFO_OUT1, 'raw'],
[NFS_CONFIG2, QEMU_IMG_INFO_OUT2, 'qcow2'],
[NFS_CONFIG3, QEMU_IMG_INFO_OUT1, 'raw'],
[NFS_CONFIG4, QEMU_IMG_INFO_OUT2, 'qcow2'])
@ddt.unpack
def test_initialize_connection(self, nfs_confs, qemu_img_info):
@mock.patch('cinder.objects.volume.Volume.get_by_id')
@mock.patch('cinder.context.get_admin_context')
def test_initialize_connection(self, nfs_confs, qemu_img_info,
expected_format, mock_get_admin_context,
mock_volume_get_by_id):
self._set_driver(extra_confs=nfs_confs)
drv = self._driver
volume = self._simple_volume()
volume.admin_metadata = {'format': expected_format}
vol_dir = os.path.join(self.TEST_MNT_POINT_BASE,
drv._get_hash_str(volume.provider_location))
vol_path = os.path.join(vol_dir, volume.name)
@@ -1510,17 +1525,24 @@ class NfsDriverTestCase(test.TestCase):
mock_img_utils.assert_called_once_with(vol_path,
force_share=True,
run_as_root=True,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=expected_format)
self.assertEqual('nfs', conn_info['driver_volume_type'])
self.assertEqual(volume.name, conn_info['data']['name'])
self.assertEqual(self.TEST_MNT_POINT_BASE,
conn_info['mount_point_base'])
self.assertEqual(expected_format, conn_info['data']['format'])
@mock.patch.object(image_utils, 'qemu_img_info')
def test_initialize_connection_raise_exception(self, mock_img_info):
@mock.patch('cinder.objects.volume.Volume.get_by_id')
def test_initialize_connection_raise_exception(self, mock_get,
mock_img_info):
self._set_driver()
drv = self._driver
volume = self._simple_volume()
volume.admin_metadata = {}
mock_get.return_value = volume
qemu_img_output = """{
"filename": "%s",
@@ -1538,10 +1560,14 @@ class NfsDriverTestCase(test.TestCase):
None)
@mock.patch.object(image_utils, 'qemu_img_info')
def test_initialize_connection_raise_on_wrong_size(self, mock_img_info):
@mock.patch('cinder.objects.volume.Volume.get_by_id')
def test_initialize_connection_raise_on_wrong_size(self, mock_get,
mock_img_info):
self._set_driver()
drv = self._driver
volume = self._simple_volume()
volume.admin_metadata = {}
mock_get.return_value = volume
qemu_img_output = """{
"filename": "%s",

View File

@@ -273,7 +273,8 @@ class QuobyteDriverTestCase(test.TestCase):
mock_qemu_img_info.assert_called_with(mock.sentinel.image_path,
force_share=True,
run_as_root=True,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
@ddt.data(['/other_random_path', '/mnt'],
['/other_basedir/' + TEST_MNT_HASH + '/volume-' + VOLUME_UUID,
@@ -986,7 +987,8 @@ class QuobyteDriverTestCase(test.TestCase):
volume_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
image_utils.resize_image.assert_called_once_with(volume_path, 3)
def test_copy_volume_from_snapshot(self):
@@ -1034,7 +1036,8 @@ class QuobyteDriverTestCase(test.TestCase):
snap_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
(mock_convert.
assert_called_once_with(src_vol_path,
dest_vol_path,
@@ -1094,7 +1097,8 @@ class QuobyteDriverTestCase(test.TestCase):
snap_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
self.assertFalse(mock_convert.called,
("_convert_image was called but should not have been")
)
@@ -1160,7 +1164,8 @@ class QuobyteDriverTestCase(test.TestCase):
snap_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
(mock_convert.
assert_called_once_with(
src_vol_path,
@@ -1225,7 +1230,8 @@ class QuobyteDriverTestCase(test.TestCase):
snap_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
(mock_convert.
assert_called_once_with(
src_vol_path,
@@ -1298,7 +1304,8 @@ class QuobyteDriverTestCase(test.TestCase):
vol_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
self.assertEqual('raw', conn_info['data']['format'])
self.assertEqual('quobyte', conn_info['driver_volume_type'])
@@ -1351,7 +1358,8 @@ class QuobyteDriverTestCase(test.TestCase):
volume_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
mock_upload_volume.assert_called_once_with(
mock.ANY, mock.ANY, mock.ANY, upload_path, run_as_root=False,
store_id=None, base_image_ref=None, compress=True,
@@ -1406,7 +1414,8 @@ class QuobyteDriverTestCase(test.TestCase):
volume_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
mock_convert_image.assert_called_once_with(
volume_path, upload_path, 'raw', run_as_root=False)
mock_upload_volume.assert_called_once_with(
@@ -1465,7 +1474,8 @@ class QuobyteDriverTestCase(test.TestCase):
volume_path,
force_share=True,
run_as_root=False,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
mock_convert_image.assert_called_once_with(
volume_path, upload_path, 'raw', run_as_root=False)
mock_upload_volume.assert_called_once_with(

View File

@@ -676,7 +676,8 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
mock_qemu_img_info.assert_called_with(mock.sentinel.image_path,
force_share=False,
run_as_root=True,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=None)
@ddt.data([None, '/fake_basedir'],
['/fake_basedir/cb2016/fake_vol_name', '/fake_basedir'],

View File

@@ -141,8 +141,19 @@ class NfsDriver(remotefs.RemoteFSSnapDriverDistributed):
active_vol = self.get_active_image_from_info(volume)
volume_dir = self._local_volume_dir(volume)
path_to_vol = os.path.join(volume_dir, active_vol)
vol_format = None
admin_metadata = None
# admin context is required for admin_metadata
with volume.obj_as_admin():
admin_metadata = volume.admin_metadata
if admin_metadata and 'format' in admin_metadata:
vol_format = admin_metadata['format']
info = self._qemu_img_info(path_to_vol,
volume['name'])
volume['name'],
img_format=vol_format)
data = {'export': volume.provider_location,
'name': active_vol}
@@ -581,13 +592,14 @@ class NfsDriver(remotefs.RemoteFSSnapDriverDistributed):
self._delete(base_volume_path)
def _qemu_img_info(self, path, volume_name):
def _qemu_img_info(self, path, volume_name, img_format=None):
return super(NfsDriver, self)._qemu_img_info_base(
path,
volume_name,
self.configuration.nfs_mount_point_base,
force_share=True,
run_as_root=True)
run_as_root=True,
img_format=img_format)
def _check_snapshot_support(self, setup_checking=False):
"""Ensure snapshot support is enabled in config."""

View File

@@ -858,7 +858,8 @@ class RemoteFSSnapDriverBase(RemoteFSDriver):
basedir: str,
ext_bf_template=None,
force_share=False,
run_as_root=False) -> imageutils.QemuImgInfo:
run_as_root=False,
img_format=None) -> imageutils.QemuImgInfo:
"""Sanitize image_utils' qemu_img_info.
This code expects to deal only with relative filenames.
@@ -877,7 +878,8 @@ class RemoteFSSnapDriverBase(RemoteFSDriver):
info = image_utils.qemu_img_info(path,
force_share=force_share,
run_as_root=run_as_root,
allow_qcow2_backing_file=True)
allow_qcow2_backing_file=True,
img_format=img_format)
if info.image:
info.image = os.path.basename(info.image)
if info.backing_file: