Merge "SMBFS: Fix initialize connection issues caused by in-use images"

This commit is contained in:
Jenkins 2016-04-19 23:13:52 +00:00 committed by Gerrit Code Review
commit 7e0838fffc
4 changed files with 142 additions and 67 deletions

View File

@ -15,14 +15,17 @@
import copy
import os
import ddt
import mock
from cinder import exception
from cinder.image import image_utils
from cinder import test
from cinder import utils
from cinder.volume.drivers import remotefs
@ddt.ddt
class RemoteFsSnapDriverTestCase(test.TestCase):
_FAKE_CONTEXT = 'fake_context'
@ -356,3 +359,65 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
self._locked_volume_operation_test_helper(
func=synchronized_func,
expected_exception=exception.VolumeBackendAPIException)
@mock.patch.object(image_utils, 'qemu_img_info')
@mock.patch('os.path.basename')
def _test_qemu_img_info(self, mock_basename,
mock_qemu_img_info, backing_file, basedir,
valid_backing_file=True):
fake_vol_name = 'fake_vol_name'
mock_info = mock_qemu_img_info.return_value
mock_info.image = mock.sentinel.image_path
mock_info.backing_file = backing_file
self._driver._VALID_IMAGE_EXTENSIONS = ['vhd', 'vhdx', 'raw', 'qcow2']
mock_basename.side_effect = [mock.sentinel.image_basename,
mock.sentinel.backing_file_basename]
if valid_backing_file:
img_info = self._driver._qemu_img_info_base(
mock.sentinel.image_path, fake_vol_name, basedir)
self.assertEqual(mock_info, img_info)
self.assertEqual(mock.sentinel.image_basename,
mock_info.image)
expected_basename_calls = [mock.call(mock.sentinel.image_path)]
if backing_file:
self.assertEqual(mock.sentinel.backing_file_basename,
mock_info.backing_file)
expected_basename_calls.append(mock.call(backing_file))
mock_basename.assert_has_calls(expected_basename_calls)
else:
self.assertRaises(exception.RemoteFSException,
self._driver._qemu_img_info_base,
mock.sentinel.image_path,
fake_vol_name, basedir)
mock_qemu_img_info.assert_called_with(mock.sentinel.image_path)
@ddt.data([None, '/fake_basedir'],
['/fake_basedir/cb2016/fake_vol_name', '/fake_basedir'],
['/fake_basedir/cb2016/fake_vol_name.vhd', '/fake_basedir'],
['/fake_basedir/cb2016/fake_vol_name.404f-404',
'/fake_basedir'],
['/fake_basedir/cb2016/fake_vol_name.tmp-snap-404f-404',
'/fake_basedir'])
@ddt.unpack
def test_qemu_img_info_valid_backing_file(self, backing_file, basedir):
self._test_qemu_img_info(backing_file=backing_file,
basedir=basedir)
@ddt.data(['/other_random_path', '/fake_basedir'],
['/other_basedir/cb2016/fake_vol_name', '/fake_basedir'],
['/fake_basedir/invalid_hash/fake_vol_name', '/fake_basedir'],
['/fake_basedir/cb2016/invalid_vol_name', '/fake_basedir'],
['/fake_basedir/cb2016/fake_vol_name.info', '/fake_basedir'],
['/fake_basedir/cb2016/fake_vol_name-random-suffix',
'/fake_basedir'],
['/fake_basedir/cb2016/fake_vol_name.invalidext',
'/fake_basedir'])
@ddt.unpack
def test_qemu_img_info_invalid_backing_file(self, backing_file, basedir):
self._test_qemu_img_info(backing_file=backing_file,
basedir=basedir,
valid_backing_file=False)

View File

@ -94,6 +94,8 @@ class SmbFsTestCase(test.TestCase):
self._smbfs_driver._alloc_info_file_path = (
self._FAKE_ALLOCATION_DATA_PATH)
self.addCleanup(mock.patch.stopall)
def _get_fake_allocation_data(self):
return {self._FAKE_SHARE_HASH: {
'total_allocated': self._FAKE_TOTAL_ALLOCATED}}
@ -242,51 +244,49 @@ class SmbFsTestCase(test.TestCase):
fake_config.smbfs_used_ratio = 1.1
self._test_setup(config=fake_config)
def _test_create_volume(self, volume_exists=False, volume_format=None):
fake_method = mock.MagicMock()
@mock.patch('os.path.exists')
def _test_create_volume(self, mock_exists, volume_exists=False,
volume_format=None, use_sparsed_file=False):
mock.patch.multiple(smbfs.SmbfsDriver,
_create_windows_image=mock.DEFAULT,
_create_regular_file=mock.DEFAULT,
_create_qcow2_file=mock.DEFAULT,
_create_sparsed_file=mock.DEFAULT,
get_volume_format=mock.DEFAULT,
local_path=mock.DEFAULT,
_set_rw_permissions_for_all=mock.DEFAULT).start()
self._smbfs_driver.configuration = copy.copy(self._FAKE_SMBFS_CONFIG)
self._smbfs_driver._set_rw_permissions_for_all = mock.MagicMock()
fake_set_permissions = self._smbfs_driver._set_rw_permissions_for_all
self._smbfs_driver.get_volume_format = mock.MagicMock()
self._smbfs_driver.configuration.smbfs_sparsed_volumes = (
use_sparsed_file)
windows_image_format = False
fake_vol_path = self._FAKE_VOLUME_PATH
self._smbfs_driver.get_volume_format.return_value = volume_format
self._smbfs_driver.local_path.return_value = mock.sentinel.vol_path
mock_exists.return_value = volume_exists
if volume_format:
if volume_format in ('vhd', 'vhdx'):
windows_image_format = volume_format
if volume_format == 'vhd':
windows_image_format = 'vpc'
method = '_create_windows_image'
fake_vol_path += '.' + volume_format
else:
method = '_create_%s_file' % volume_format
if volume_format == 'sparsed':
self._smbfs_driver.configuration.smbfs_sparsed_volumes = (
True)
if volume_exists:
self.assertRaises(exception.InvalidVolume,
self._smbfs_driver._do_create_volume,
self._FAKE_VOLUME)
return
self._smbfs_driver._do_create_volume(self._FAKE_VOLUME)
expected_create_args = [mock.sentinel.vol_path,
self._FAKE_VOLUME['size']]
if volume_format in [self._smbfs_driver._DISK_FORMAT_VHDX,
self._smbfs_driver._DISK_FORMAT_VHD]:
expected_create_args.append(volume_format)
exp_create_method = self._smbfs_driver._create_windows_image
else:
method = '_create_regular_file'
setattr(self._smbfs_driver, method, fake_method)
with mock.patch('os.path.exists', new=lambda x: volume_exists):
if volume_exists:
self.assertRaises(exception.InvalidVolume,
self._smbfs_driver._do_create_volume,
self._FAKE_VOLUME)
return
self._smbfs_driver._do_create_volume(self._FAKE_VOLUME)
if windows_image_format:
fake_method.assert_called_once_with(
fake_vol_path,
self._FAKE_VOLUME['size'],
windows_image_format)
if volume_format == self._smbfs_driver._DISK_FORMAT_QCOW2:
exp_create_method = self._smbfs_driver._create_qcow2_file
elif use_sparsed_file:
exp_create_method = self._smbfs_driver._create_sparsed_file
else:
fake_method.assert_called_once_with(
fake_vol_path, self._FAKE_VOLUME['size'])
fake_set_permissions.assert_called_once_with(fake_vol_path)
exp_create_method = self._smbfs_driver._create_regular_file
exp_create_method.assert_called_once_with(*expected_create_args)
mock_set_permissions = self._smbfs_driver._set_rw_permissions_for_all
mock_set_permissions.assert_called_once_with(mock.sentinel.vol_path)
def test_create_existing_volume(self):
self._test_create_volume(volume_exists=True)
@ -298,7 +298,8 @@ class SmbFsTestCase(test.TestCase):
self._test_create_volume(volume_format='qcow2')
def test_create_sparsed(self):
self._test_create_volume(volume_format='sparsed')
self._test_create_volume(volume_format='raw',
use_sparsed_file=True)
def test_create_regular(self):
self._test_create_volume()
@ -392,14 +393,12 @@ class SmbFsTestCase(test.TestCase):
@mock.patch.object(smbfs.SmbfsDriver, '_lookup_local_volume_path')
@mock.patch.object(smbfs.SmbfsDriver, 'get_volume_format')
def _test_get_volume_path(self, mock_get_volume_format, mock_lookup_volume,
mock_get_path_template, volume_exists=True,
volume_format='raw'):
mock_get_path_template, volume_exists=True):
drv = self._smbfs_driver
mock_get_path_template.return_value = self._FAKE_VOLUME_PATH
volume_format = 'raw'
expected_vol_path = self._FAKE_VOLUME_PATH
if volume_format in (drv._DISK_FORMAT_VHD, drv._DISK_FORMAT_VHDX):
expected_vol_path += '.' + volume_format
expected_vol_path = self._FAKE_VOLUME_PATH + '.' + volume_format
mock_lookup_volume.return_value = (
expected_vol_path if volume_exists else None)
@ -416,12 +415,9 @@ class SmbFsTestCase(test.TestCase):
def test_get_existing_volume_path(self):
self._test_get_volume_path()
def test_get_new_raw_volume_path(self):
def test_get_new_volume_path(self):
self._test_get_volume_path(volume_exists=False)
def test_get_new_vhd_volume_path(self):
self._test_get_volume_path(volume_exists=False, volume_format='vhd')
@mock.patch.object(smbfs.SmbfsDriver, '_local_volume_dir')
def test_get_local_volume_path_template(self, mock_get_local_dir):
mock_get_local_dir.return_value = self._FAKE_MNT_POINT
@ -437,8 +433,11 @@ class SmbFsTestCase(test.TestCase):
ret_val = self._smbfs_driver._lookup_local_volume_path(
self._FAKE_VOLUME_PATH)
extensions = [''] + [
".%s" % ext
for ext in self._smbfs_driver._SUPPORTED_IMAGE_FORMATS]
possible_paths = [self._FAKE_VOLUME_PATH + ext
for ext in ('', '.vhd', '.vhdx')]
for ext in extensions]
mock_exists.assert_has_calls(
[mock.call(path) for path in possible_paths])
self.assertEqual(expected_path, ret_val)
@ -495,11 +494,11 @@ class SmbFsTestCase(test.TestCase):
self._smbfs_driver._get_mount_point_base = mock.Mock(
return_value=self._FAKE_MNT_BASE)
self._smbfs_driver.shares = {self._FAKE_SHARE: self._FAKE_SHARE_OPTS}
self._smbfs_driver._qemu_img_info = mock.Mock(
return_value=mock.Mock(file_format='raw'))
self._smbfs_driver.get_volume_format = mock.Mock(
return_value=mock.sentinel.format)
fake_data = {'export': self._FAKE_SHARE,
'format': 'raw',
'format': mock.sentinel.format,
'name': self._FAKE_VOLUME_NAME,
'options': self._FAKE_SHARE_OPTS}
expected = {

View File

@ -631,6 +631,8 @@ class RemoteFSSnapDriver(RemoteFSDriver, driver.SnapshotVD):
_local_volume_dir(self, volume)
"""
_VALID_IMAGE_EXTENSIONS = []
def __init__(self, *args, **kwargs):
self._remotefsclient = None
self.base = None
@ -689,11 +691,18 @@ class RemoteFSSnapDriver(RemoteFSDriver, driver.SnapshotVD):
if info.image:
info.image = os.path.basename(info.image)
if info.backing_file:
if self._VALID_IMAGE_EXTENSIONS:
valid_ext = r'(\.(%s))?' % '|'.join(
self._VALID_IMAGE_EXTENSIONS)
else:
valid_ext = ''
backing_file_template = \
"(%(basedir)s/[0-9a-f]+/)?%" \
"(volname)s(.(tmp-snap-)?[0-9a-f-]+)?$" % {
"(volname)s(.(tmp-snap-)?[0-9a-f-]+)?%(valid_ext)s$" % {
'basedir': basedir,
'volname': volume_name
'volname': volume_name,
'valid_ext': valid_ext,
}
if not re.match(backing_file_template, info.backing_file):
msg = _("File %(path)s has invalid backing file "
@ -1326,7 +1335,7 @@ class RemoteFSSnapDriver(RemoteFSDriver, driver.SnapshotVD):
def _delete_snapshot_online(self, context, snapshot, info):
# Update info over the course of this method
# active file never changes
info_path = self._local_path_volume(snapshot['volume']) + '.info'
info_path = self._local_path_volume_info(snapshot['volume'])
snap_info = self._read_info_file(info_path)
if info['active_file'] == info['snapshot_file']:

View File

@ -114,6 +114,10 @@ class SmbfsDriver(remotefs_drv.RemoteFSSnapDriver):
_DISK_FORMAT_RAW = 'raw'
_DISK_FORMAT_QCOW2 = 'qcow2'
_SUPPORTED_IMAGE_FORMATS = [_DISK_FORMAT_RAW, _DISK_FORMAT_QCOW2,
_DISK_FORMAT_VHD, _DISK_FORMAT_VHDX]
_VALID_IMAGE_EXTENSIONS = _SUPPORTED_IMAGE_FORMATS
def __init__(self, execute=putils.execute, *args, **kwargs):
self._remotefsclient = None
super(SmbfsDriver, self).__init__(*args, **kwargs)
@ -143,10 +147,7 @@ class SmbfsDriver(remotefs_drv.RemoteFSSnapDriver):
"""
# Find active image
active_file = self.get_active_image_from_info(volume)
active_file_path = os.path.join(self._local_volume_dir(volume),
active_file)
info = self._qemu_img_info(active_file_path, volume['name'])
fmt = info.file_format
fmt = self.get_volume_format(volume)
data = {'export': volume['provider_location'],
'format': fmt,
@ -251,10 +252,7 @@ class SmbfsDriver(remotefs_drv.RemoteFSSnapDriver):
# The image does not exist, so retrieve the volume format
# in order to build the path.
fmt = self.get_volume_format(volume)
if fmt in (self._DISK_FORMAT_VHD, self._DISK_FORMAT_VHDX):
volume_path = volume_path_template + '.' + fmt
else:
volume_path = volume_path_template
volume_path = volume_path_template + '.' + fmt
return volume_path
def _get_local_volume_path_template(self, volume):
@ -263,7 +261,7 @@ class SmbfsDriver(remotefs_drv.RemoteFSSnapDriver):
return local_path_template
def _lookup_local_volume_path(self, volume_path_template):
for ext in ['', self._DISK_FORMAT_VHD, self._DISK_FORMAT_VHDX]:
for ext in [''] + self._SUPPORTED_IMAGE_FORMATS:
volume_path = (volume_path_template + '.' + ext
if ext else volume_path_template)
if os.path.exists(volume_path):
@ -283,8 +281,12 @@ class SmbfsDriver(remotefs_drv.RemoteFSSnapDriver):
volume_path = self._lookup_local_volume_path(volume_path_template)
if volume_path:
info = self._qemu_img_info(volume_path, volume['name'])
volume_format = info.file_format
ext = os.path.splitext(volume_path)[1].strip('.').lower()
if ext in self._SUPPORTED_IMAGE_FORMATS:
volume_format = ext
else:
info = self._qemu_img_info(volume_path, volume['name'])
volume_format = info.file_format
else:
volume_format = (
self._get_volume_format_spec(volume) or