diff --git a/cinder/opts.py b/cinder/opts.py index 93fbc98b5a8..2bf2ce5774b 100644 --- a/cinder/opts.py +++ b/cinder/opts.py @@ -167,7 +167,6 @@ from cinder.volume.drivers import remotefs as cinder_volume_drivers_remotefs from cinder.volume.drivers.san.hp import hpmsa_common as \ cinder_volume_drivers_san_hp_hpmsacommon from cinder.volume.drivers.san import san as cinder_volume_drivers_san_san -from cinder.volume.drivers import scality as cinder_volume_drivers_scality from cinder.volume.drivers import sheepdog as cinder_volume_drivers_sheepdog from cinder.volume.drivers import smbfs as cinder_volume_drivers_smbfs from cinder.volume.drivers import solidfire as cinder_volume_drivers_solidfire @@ -359,7 +358,6 @@ def list_opts(): cinder_volume_drivers_san_hp_hpmsacommon.common_opts, cinder_volume_drivers_san_hp_hpmsacommon.iscsi_opts, cinder_volume_drivers_san_san.san_opts, - cinder_volume_drivers_scality.volume_opts, cinder_volume_drivers_sheepdog.sheepdog_opts, cinder_volume_drivers_smbfs.volume_opts, cinder_volume_drivers_solidfire.sf_opts, diff --git a/cinder/tests/unit/volume/drivers/test_scality.py b/cinder/tests/unit/volume/drivers/test_scality.py deleted file mode 100644 index 34484336985..00000000000 --- a/cinder/tests/unit/volume/drivers/test_scality.py +++ /dev/null @@ -1,392 +0,0 @@ -# Copyright (c) 2015 Scality -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Unit tests for the Scality SOFS Volume Driver. -""" -import errno -import os - -import mock -from oslo_utils import imageutils -from six.moves import urllib - -from cinder import context -from cinder import exception -from cinder import test -from cinder.tests.unit import fake_snapshot -from cinder.tests.unit import fake_volume -from cinder.volume import configuration as conf -import cinder.volume.drivers.scality as driver - -_FAKE_VOLUME_ID = 'a79d463e-1fd5-11e5-a6ff-5b81bfee8544' -_FAKE_VOLUME_NAME = 'volume-a79d463e-1fd5-11e5-a6ff-5b81bfee8544' - -_FAKE_SNAPSHOT_ID = 'ae3d6da2-1fd5-11e5-967f-1b8cf3b401ab' -_FAKE_SNAPSHOT_NAME = 'snapshot-ae3d6da2-1fd5-11e5-967f-1b8cf3b401ab' - -_FAKE_BACKUP = {'id': '914849d2-2585-11e5-be54-d70ca0c343d6', - 'volume_id': _FAKE_VOLUME_ID} - -_FAKE_MNT_POINT = '/tmp' -_FAKE_SOFS_CONFIG = '/etc/sfused.conf' -_FAKE_VOLUME_DIR = 'cinder/volumes' -_FAKE_VOL_BASEDIR = os.path.join(_FAKE_MNT_POINT, _FAKE_VOLUME_DIR, '00') -_FAKE_VOL_PATH = os.path.join(_FAKE_VOL_BASEDIR, _FAKE_VOLUME_NAME) -_FAKE_SNAP_PATH = os.path.join(_FAKE_VOL_BASEDIR, _FAKE_SNAPSHOT_NAME) - -_FAKE_MOUNTS_TABLE = [['tmpfs /dev/shm\n'], - ['fuse ' + _FAKE_MNT_POINT + '\n']] - - -class ScalityDriverTestCase(test.TestCase): - """Test case for the Scality driver.""" - - def setUp(self): - super(ScalityDriverTestCase, self).setUp() - - self.cfg = mock.Mock(spec=conf.Configuration) - self.cfg.scality_sofs_mount_point = _FAKE_MNT_POINT - self.cfg.scality_sofs_config = _FAKE_SOFS_CONFIG - self.cfg.scality_sofs_volume_dir = _FAKE_VOLUME_DIR - - self.drv = driver.ScalityDriver(configuration=self.cfg) - self.drv.db = mock.Mock() - - self.context = context.get_admin_context() - - def _simple_volume(self, **kwargs): - updates = {'display_name': _FAKE_VOLUME_NAME, - 'id': _FAKE_VOLUME_ID, - 'provider_location': 'fake_share'} - updates.update(kwargs) - - return fake_volume.fake_volume_obj(self.context, **updates) - - def _simple_snapshot(self, **kwargs): - updates = {'id': _FAKE_SNAPSHOT_ID, - 'display_name': _FAKE_SNAPSHOT_NAME, - 'status': 'available', - 'provider_location': None, - 'volume_size': 1} - - updates.update(kwargs) - snapshot = fake_snapshot.fake_snapshot_obj(self.context, **updates) - volume = self._simple_volume() - snapshot.volume = volume - - return snapshot - - @mock.patch.object(driver.urllib.request, 'urlopen') - @mock.patch('os.access') - def test_check_for_setup_error(self, mock_os_access, mock_urlopen): - self.drv.check_for_setup_error() - - mock_urlopen.assert_called_once_with('file://%s' % _FAKE_SOFS_CONFIG, - timeout=5) - mock_os_access.assert_called_once_with('/sbin/mount.sofs', os.X_OK) - - def test_check_for_setup_error_with_no_sofs_config(self): - self.cfg.scality_sofs_config = '' - - self.drv = driver.ScalityDriver(configuration=self.cfg) - - self.assertRaises(exception.VolumeBackendAPIException, - self.drv.check_for_setup_error) - exec_patcher = mock.patch.object(self.drv, '_execute', - mock.MagicMock()) - exec_patcher.start() - self.addCleanup(exec_patcher.stop) - - @mock.patch.object(driver.urllib.request, 'urlopen') - def test_check_for_setup_error_with_urlerror(self, mock_urlopen): - # Add a Unicode char to be sure that the exception is properly - # handled even if it contains Unicode chars - mock_urlopen.side_effect = urllib.error.URLError(u'\u9535') - self.assertRaises(exception.VolumeBackendAPIException, - self.drv.check_for_setup_error) - - @mock.patch.object(driver.urllib.request, 'urlopen') - def test_check_for_setup_error_with_httperror(self, mock_urlopen): - mock_urlopen.side_effect = urllib.error.HTTPError(*[None] * 5) - self.assertRaises(exception.VolumeBackendAPIException, - self.drv.check_for_setup_error) - - @mock.patch.object(driver.urllib.request, 'urlopen', mock.Mock()) - @mock.patch('os.access') - def test_check_for_setup_error_with_no_mountsofs(self, mock_os_access): - mock_os_access.return_value = False - self.assertRaises(exception.VolumeBackendAPIException, - self.drv.check_for_setup_error) - mock_os_access.assert_called_once_with('/sbin/mount.sofs', os.X_OK) - - def test_load_shares_config(self): - self.assertEqual({}, self.drv.shares) - self.drv._load_shares_config() - self.assertEqual({_FAKE_VOLUME_DIR: None}, self.drv.shares) - - def test_get_mount_point_for_share(self): - self.assertEqual(_FAKE_VOL_BASEDIR, - self.drv._get_mount_point_for_share()) - - @mock.patch("cinder.volume.utils.read_proc_mounts") - @mock.patch("oslo_concurrency.processutils.execute") - def test_ensure_share_mounted_when_mount_failed(self, mock_execute, - mock_read_proc_mounts): - mock_read_proc_mounts.return_value = ['tmpfs /dev/shm\n'] - self.assertRaises(exception.VolumeBackendAPIException, - self.drv._ensure_share_mounted) - self.assertEqual(2, mock_read_proc_mounts.call_count) - self.assertEqual(1, mock_execute.call_count) - - @mock.patch("cinder.volume.utils.read_proc_mounts") - @mock.patch("oslo_concurrency.processutils.execute") - @mock.patch("oslo_utils.fileutils.ensure_tree") - @mock.patch("os.symlink") - def test_ensure_shares_mounted(self, mock_symlink, mock_ensure_tree, - mock_execute, mock_read_proc_mounts): - self.assertEqual([], self.drv._mounted_shares) - - mock_read_proc_mounts.side_effect = _FAKE_MOUNTS_TABLE - - self.drv._ensure_shares_mounted() - - self.assertEqual([_FAKE_VOLUME_DIR], self.drv._mounted_shares) - self.assertEqual(2, mock_read_proc_mounts.call_count) - mock_symlink.assert_called_once_with('.', _FAKE_VOL_BASEDIR) - self.assertEqual(2, mock_ensure_tree.call_count) - self.assertEqual(1, mock_execute.call_count) - expected_args = ('mount', '-t', 'sofs', _FAKE_SOFS_CONFIG, - _FAKE_MNT_POINT) - self.assertEqual(expected_args, mock_execute.call_args[0]) - - @mock.patch("cinder.volume.utils.read_proc_mounts") - @mock.patch("oslo_concurrency.processutils.execute") - @mock.patch("oslo_utils.fileutils.ensure_tree", mock.Mock()) - @mock.patch("os.symlink", mock.Mock()) - def test_ensure_shares_mounted_when_sofs_mounted(self, mock_execute, - mock_read_proc_mounts): - mock_read_proc_mounts.return_value = _FAKE_MOUNTS_TABLE[1] - - self.drv._ensure_shares_mounted() - - # Because SOFS is mounted from the beginning, we shouldn't read - # /proc/mounts more than once. - mock_read_proc_mounts.assert_called_once_with() - self.assertFalse(mock_execute.called) - - def test_find_share_when_no_shares_mounted(self): - self.assertRaises(exception.RemoteFSNoSharesMounted, - self.drv._find_share, 'ignored') - - @mock.patch("cinder.volume.utils.read_proc_mounts") - @mock.patch("oslo_concurrency.processutils.execute") - @mock.patch("oslo_utils.fileutils.ensure_tree") - @mock.patch("os.symlink") - def test_find_share(self, mock_symlink, mock_ensure_tree, mock_execute, - mock_read_proc_mounts): - mock_read_proc_mounts.side_effect = _FAKE_MOUNTS_TABLE - - self.drv._ensure_shares_mounted() - - self.assertEqual(_FAKE_VOLUME_DIR, self.drv._find_share('ignored')) - self.assertEqual(2, mock_read_proc_mounts.call_count) - self.assertEqual(1, mock_execute.call_count) - - expected_args = ('mount', '-t', 'sofs', _FAKE_SOFS_CONFIG, - _FAKE_MNT_POINT) - self.assertEqual(expected_args, mock_execute.call_args[0]) - - mock_symlink.assert_called_once_with('.', _FAKE_VOL_BASEDIR) - - self.assertEqual(mock_ensure_tree.call_args_list, - [mock.call(_FAKE_MNT_POINT), - mock.call(os.path.join(_FAKE_MNT_POINT, - _FAKE_VOLUME_DIR))]) - - def test_get_volume_stats(self): - with mock.patch.object(self.cfg, 'safe_get') as mock_safe_get: - mock_safe_get.return_value = 'fake_backend_name' - stats = self.drv.get_volume_stats() - self.assertEqual(self.drv.VERSION, stats['driver_version']) - self.assertEqual(mock_safe_get.return_value, - stats['volume_backend_name']) - mock_safe_get.assert_called_once_with('volume_backend_name') - - @mock.patch("cinder.image.image_utils.qemu_img_info") - def test_initialize_connection(self, mock_qemu_img_info): - info = imageutils.QemuImgInfo() - info.file_format = 'raw' - info.image = _FAKE_VOLUME_NAME - mock_qemu_img_info.return_value = info - - volume = self._simple_volume() - with mock.patch.object(self.drv, 'get_active_image_from_info') as \ - mock_get_active_image_from_info: - - mock_get_active_image_from_info.return_value = _FAKE_VOLUME_NAME - conn_info = self.drv.initialize_connection(volume, None) - - expected_conn_info = { - 'driver_volume_type': driver.ScalityDriver.driver_volume_type, - 'mount_point_base': _FAKE_MNT_POINT, - 'data': { - 'export': volume.provider_location, - 'name': volume.name, - 'sofs_path': 'cinder/volumes/00/' + volume.name, - 'format': 'raw' - } - } - self.assertEqual(expected_conn_info, conn_info) - mock_get_active_image_from_info.assert_called_once_with(volume) - mock_qemu_img_info.assert_called_once_with(_FAKE_VOL_PATH) - - @mock.patch("cinder.image.image_utils.resize_image") - @mock.patch("cinder.image.image_utils.qemu_img_info") - def test_extend_volume(self, mock_qemu_img_info, mock_resize_image): - info = imageutils.QemuImgInfo() - info.file_format = 'raw' - mock_qemu_img_info.return_value = info - - self.drv.extend_volume(self._simple_volume(), 2) - - mock_qemu_img_info.assert_called_once_with(_FAKE_VOL_PATH) - - mock_resize_image.assert_called_once_with(_FAKE_VOL_PATH, 2) - - @mock.patch("cinder.image.image_utils.qemu_img_info") - def test_extend_volume_with_invalid_format(self, mock_qemu_img_info): - info = imageutils.QemuImgInfo() - info.file_format = 'vmdk' - mock_qemu_img_info.return_value = info - - self.assertRaises(exception.InvalidVolume, - self.drv.extend_volume, self._simple_volume(), 2) - - @mock.patch("cinder.image.image_utils.resize_image") - @mock.patch("cinder.image.image_utils.convert_image") - def test_copy_volume_from_snapshot_with_ioerror(self, mock_convert_image, - mock_resize_image): - with mock.patch.object(self.drv, '_read_info_file') as \ - mock_read_info_file, \ - mock.patch.object(self.drv, '_set_rw_permissions_for_all') as \ - mock_set_rw_permissions: - mock_read_info_file.side_effect = IOError(errno.ENOENT, '') - self.drv._copy_volume_from_snapshot(self._simple_snapshot(), - self._simple_volume(), 1) - - mock_read_info_file.assert_called_once_with("%s.info" % _FAKE_VOL_PATH) - mock_convert_image.assert_called_once_with(_FAKE_SNAP_PATH, - _FAKE_VOL_PATH, 'raw', - run_as_root=True) - mock_set_rw_permissions.assert_called_once_with(_FAKE_VOL_PATH) - mock_resize_image.assert_called_once_with(_FAKE_VOL_PATH, 1) - - @mock.patch("cinder.image.image_utils.resize_image") - @mock.patch("cinder.image.image_utils.convert_image") - @mock.patch("cinder.image.image_utils.qemu_img_info") - def test_copy_volume_from_snapshot(self, mock_qemu_img_info, - mock_convert_image, mock_resize_image): - - new_volume = self._simple_volume( - display_name='volume-3fa63b02-1fe5-11e5-b492-abf97a8fb23b', - id='3fa63b02-1fe5-11e5-b492-abf97a8fb23b', - provider_location='fake_share') - new_vol_path = os.path.join(_FAKE_VOL_BASEDIR, new_volume.name) - - info = imageutils.QemuImgInfo() - info.file_format = 'raw' - info.backing_file = _FAKE_VOL_PATH - mock_qemu_img_info.return_value = info - - with mock.patch.object(self.drv, '_read_info_file') as \ - mock_read_info_file, \ - mock.patch.object(self.drv, '_set_rw_permissions_for_all') as \ - mock_set_rw_permissions: - self.drv._copy_volume_from_snapshot(self._simple_snapshot(), - new_volume, 1) - - mock_read_info_file.assert_called_once_with("%s.info" % _FAKE_VOL_PATH) - mock_convert_image.assert_called_once_with(_FAKE_VOL_PATH, - new_vol_path, 'raw', - run_as_root=True) - mock_set_rw_permissions.assert_called_once_with(new_vol_path) - mock_resize_image.assert_called_once_with(new_vol_path, 1) - - @mock.patch("cinder.image.image_utils.qemu_img_info") - @mock.patch("cinder.utils.temporary_chown") - @mock.patch("six.moves.builtins.open") - def test_backup_volume(self, mock_open, mock_temporary_chown, - mock_qemu_img_info): - """Backup a volume with no snapshots.""" - - info = imageutils.QemuImgInfo() - info.file_format = 'raw' - mock_qemu_img_info.return_value = info - - backup = {'volume_id': _FAKE_VOLUME_ID} - mock_backup_service = mock.MagicMock() - self.drv.db.volume_get.return_value = self._simple_volume() - - self.drv.backup_volume(context, backup, mock_backup_service) - - mock_qemu_img_info.assert_called_once_with(_FAKE_VOL_PATH) - mock_temporary_chown.assert_called_once_with(_FAKE_VOL_PATH) - mock_open.assert_called_once_with(_FAKE_VOL_PATH) - mock_backup_service.backup.assert_called_once_with( - backup, mock_open().__enter__()) - - @mock.patch("cinder.image.image_utils.qemu_img_info") - def test_backup_volume_with_non_raw_volume(self, mock_qemu_img_info): - - info = imageutils.QemuImgInfo() - info.file_format = 'qcow2' - mock_qemu_img_info.return_value = info - - self.drv.db.volume_get.return_value = self._simple_volume() - - self.assertRaises(exception.InvalidVolume, self.drv.backup_volume, - context, _FAKE_BACKUP, mock.MagicMock()) - - mock_qemu_img_info.assert_called_once_with(_FAKE_VOL_PATH) - - @mock.patch("cinder.image.image_utils.qemu_img_info") - def test_backup_volume_with_backing_file(self, mock_qemu_img_info): - - info = imageutils.QemuImgInfo() - info.file_format = 'raw' - info.backing_file = 'fake.img' - mock_qemu_img_info.return_value = info - - backup = {'volume_id': _FAKE_VOLUME_ID} - self.drv.db.volume_get.return_value = self._simple_volume() - - self.assertRaises(exception.InvalidVolume, self.drv.backup_volume, - context, backup, mock.MagicMock()) - - mock_qemu_img_info.assert_called_once_with(_FAKE_VOL_PATH) - - @mock.patch("cinder.utils.temporary_chown") - @mock.patch("six.moves.builtins.open") - def test_restore_bakup(self, mock_open, mock_temporary_chown): - mock_backup_service = mock.MagicMock() - - self.drv.restore_backup(context, _FAKE_BACKUP, self._simple_volume(), - mock_backup_service) - - mock_temporary_chown.assert_called_once_with(_FAKE_VOL_PATH) - mock_open.assert_called_once_with(_FAKE_VOL_PATH, 'wb') - mock_backup_service.restore.assert_called_once_with( - _FAKE_BACKUP, _FAKE_VOLUME_ID, mock_open().__enter__()) diff --git a/cinder/volume/drivers/scality.py b/cinder/volume/drivers/scality.py deleted file mode 100644 index 42c8f1ed047..00000000000 --- a/cinder/volume/drivers/scality.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) 2015 Scality -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Scality SOFS Volume Driver. -""" - - -import errno -import os - -from oslo_concurrency import lockutils -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import fileutils -import six -from six.moves import urllib - -from cinder import exception -from cinder.i18n import _, _LI -from cinder.image import image_utils -from cinder import interface -from cinder import utils -from cinder.volume.drivers import remotefs as remotefs_drv -from cinder.volume import utils as volume_utils - - -LOG = logging.getLogger(__name__) - -volume_opts = [ - cfg.StrOpt('scality_sofs_config', - help='Path or URL to Scality SOFS configuration file'), - cfg.StrOpt('scality_sofs_mount_point', - default='$state_path/scality', - help='Base dir where Scality SOFS shall be mounted'), - cfg.StrOpt('scality_sofs_volume_dir', - default='cinder/volumes', - help='Path from Scality SOFS root to volume dir'), -] - -CONF = cfg.CONF -CONF.register_opts(volume_opts) - - -@interface.volumedriver -class ScalityDriver(remotefs_drv.RemoteFSSnapDriver): - """Scality SOFS cinder driver. - - Creates sparse files on SOFS for hypervisors to use as block - devices. - """ - - driver_volume_type = 'scality' - driver_prefix = 'scality_sofs' - volume_backend_name = 'Scality_SOFS' - VERSION = '2.0.0' - - # ThirdPartySystems wiki page - CI_WIKI_NAME = "Scality_CI" - - # TODO(smcginnis) Either remove this if CI requirements are met, or - # remove this driver in the Ocata release per normal deprecation - SUPPORTED = False - - def __init__(self, *args, **kwargs): - super(ScalityDriver, self).__init__(*args, **kwargs) - self.configuration.append_config_values(volume_opts) - - self.sofs_mount_point = self.configuration.scality_sofs_mount_point - self.sofs_config = self.configuration.scality_sofs_config - self.sofs_rel_volume_dir = self.configuration.scality_sofs_volume_dir - self.sofs_abs_volume_dir = os.path.join(self.sofs_mount_point, - self.sofs_rel_volume_dir) - - # The following config flag is used by RemoteFSDriver._do_create_volume - # We want to use sparse file (ftruncated) without exposing this - # as a config switch to customers. - self.configuration.scality_sofs_sparsed_volumes = True - - def check_for_setup_error(self): - """Sanity checks before attempting to mount SOFS.""" - - # config is mandatory - if not self.sofs_config: - msg = _("Value required for 'scality_sofs_config'") - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - # config can be a file path or a URL, check it - config = self.sofs_config - if urllib.parse.urlparse(self.sofs_config).scheme == '': - # turn local path into URL - config = 'file://%s' % self.sofs_config - try: - urllib.request.urlopen(config, timeout=5).close() - except (urllib.error.URLError, urllib.error.HTTPError) as e: - msg = _("Can't access 'scality_sofs_config'" - ": %s") % six.text_type(e) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - # mount.sofs must be installed - if not os.access('/sbin/mount.sofs', os.X_OK): - msg = _("Cannot execute /sbin/mount.sofs") - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - def _load_shares_config(self, share_file=None): - self.shares[self.sofs_rel_volume_dir] = None - - def _get_mount_point_for_share(self, share=None): - # The _qemu_img_info_base() method from the RemoteFSSnapDriver class - # expects files (volume) to be inside a subdir of the mount point. - # So we have to append a dummy subdir. - return self.sofs_abs_volume_dir + "/00" - - def _sofs_is_mounted(self): - """Check if SOFS is already mounted at the expected location.""" - mount_path = self.sofs_mount_point.rstrip('/') - for mount in volume_utils.read_proc_mounts(): - parts = mount.split() - if (parts[0].endswith('fuse') and - parts[1].rstrip('/') == mount_path): - return True - return False - - @lockutils.synchronized('mount-sofs', 'cinder-sofs', external=True) - def _ensure_share_mounted(self, share=None): - """Mount SOFS if need be.""" - fileutils.ensure_tree(self.sofs_mount_point) - - if not self._sofs_is_mounted(): - self._execute('mount', '-t', 'sofs', self.sofs_config, - self.sofs_mount_point, run_as_root=True) - # Check whether the mount command succeeded - if not self._sofs_is_mounted(): - msg = _("Cannot mount Scality SOFS, check syslog for errors") - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - fileutils.ensure_tree(self.sofs_abs_volume_dir) - - # We symlink the '00' subdir to its parent dir to maintain - # compatibility with previous version of this driver. - try: - os.symlink(".", self._get_mount_point_for_share()) - except OSError as exc: - if exc.errno == errno.EEXIST: - if not os.path.islink(self._get_mount_point_for_share()): - raise - else: - raise - - def _ensure_shares_mounted(self): - self._ensure_share_mounted() - self._mounted_shares = [self.sofs_rel_volume_dir] - - def _find_share(self, volume_size_for): - try: - return self._mounted_shares[0] - except IndexError: - raise exception.RemoteFSNoSharesMounted() - - def get_volume_stats(self, refresh=False): - """Return the current state of the volume service.""" - stats = { - 'vendor_name': 'Scality', - 'driver_version': self.VERSION, - 'storage_protocol': 'scality', - 'total_capacity_gb': 'infinite', - 'free_capacity_gb': 'infinite', - 'reserved_percentage': 0, - } - backend_name = self.configuration.safe_get('volume_backend_name') - stats['volume_backend_name'] = backend_name or self.volume_backend_name - return stats - - @remotefs_drv.locked_volume_id_operation - def initialize_connection(self, volume, connector): - """Allow connection to connector and return connection info.""" - - # Find active qcow2 file - active_file = self.get_active_image_from_info(volume) - path = '%s/%s' % (self._get_mount_point_for_share(), active_file) - sofs_rel_path = os.path.join(self.sofs_rel_volume_dir, "00", - volume.name) - - data = {'export': volume.provider_location, - 'name': active_file, - 'sofs_path': sofs_rel_path} - - # Test file for raw vs. qcow2 format - info = self._qemu_img_info(path, volume.name) - data['format'] = info.file_format - if data['format'] not in ['raw', 'qcow2']: - msg = _('%s must be a valid raw or qcow2 image.') % path - raise exception.InvalidVolume(msg) - - return { - 'driver_volume_type': self.driver_volume_type, - 'data': data, - 'mount_point_base': self.sofs_mount_point - } - - def _qemu_img_info(self, path, volume_name): - return super(ScalityDriver, self)._qemu_img_info_base( - path, volume_name, self.sofs_abs_volume_dir) - - @remotefs_drv.locked_volume_id_operation - def extend_volume(self, volume, size_gb): - volume_path = self.local_path(volume) - - info = self._qemu_img_info(volume_path, volume.name) - backing_fmt = info.file_format - - if backing_fmt not in ['raw', 'qcow2']: - msg = _('Unrecognized backing format: %s') - raise exception.InvalidVolume(msg % backing_fmt) - - # qemu-img can resize both raw and qcow2 files - image_utils.resize_image(volume_path, size_gb) - - def _copy_volume_from_snapshot(self, snapshot, volume, volume_size): - """Copy data from snapshot to destination volume. - - This is done with a qemu-img convert to raw/qcow2 from the snapshot - qcow2. - """ - - info_path = self._local_path_volume_info(snapshot.volume) - - # For BC compat' with version < 2 of this driver - try: - snap_info = self._read_info_file(info_path) - except IOError as exc: - if exc.errno != errno.ENOENT: - raise - else: - path_to_snap_img = self.local_path(snapshot) - else: - vol_path = self._local_volume_dir(snapshot.volume) - - forward_file = snap_info[snapshot.id] - forward_path = os.path.join(vol_path, forward_file) - - # Find the file which backs this file, which represents the point - # when this snapshot was created. - img_info = self._qemu_img_info(forward_path, - snapshot.volume.name) - - path_to_snap_img = os.path.join(vol_path, img_info.backing_file) - - LOG.debug("will copy from snapshot at %s", path_to_snap_img) - - path_to_new_vol = self.local_path(volume) - out_format = 'raw' - image_utils.convert_image(path_to_snap_img, - path_to_new_vol, - out_format, - run_as_root=self._execute_as_root) - - self._set_rw_permissions_for_all(path_to_new_vol) - - image_utils.resize_image(path_to_new_vol, volume_size) - - def backup_volume(self, context, backup, backup_service): - """Create a new backup from an existing volume.""" - volume = self.db.volume_get(context, backup['volume_id']) - volume_local_path = self.local_path(volume) - LOG.info(_LI('Begin backup of volume %s.'), volume.name) - - qemu_img_info = image_utils.qemu_img_info(volume_local_path) - if qemu_img_info.file_format != 'raw': - msg = _('Backup is only supported for raw-formatted ' - 'SOFS volumes.') - raise exception.InvalidVolume(msg) - - if qemu_img_info.backing_file is not None: - msg = _('Backup is only supported for SOFS volumes ' - 'without backing file.') - raise exception.InvalidVolume(msg) - - with utils.temporary_chown(volume_local_path): - with open(volume_local_path) as volume_file: - backup_service.backup(backup, volume_file) - - def restore_backup(self, context, backup, volume, backup_service): - """Restore an existing backup to a new or existing volume.""" - LOG.info(_LI('Restoring backup %(backup)s to volume %(volume)s.'), - {'backup': backup['id'], 'volume': volume.name}) - volume_local_path = self.local_path(volume) - with utils.temporary_chown(volume_local_path): - with open(volume_local_path, 'wb') as volume_file: - backup_service.restore(backup, volume.id, volume_file) diff --git a/releasenotes/notes/remove-scality-fa209aae9748a1f3.yaml b/releasenotes/notes/remove-scality-fa209aae9748a1f3.yaml new file mode 100644 index 00000000000..f91e8015e4b --- /dev/null +++ b/releasenotes/notes/remove-scality-fa209aae9748a1f3.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - The Scality backend volume driver was marked as not + supported in the previous release and has now been + removed.