From 0835b0862eb65998433a2dfb35f7489cf2f9badf Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Thu, 19 Dec 2019 07:25:08 -0600 Subject: [PATCH] Remove Sheepdog Driver The Sheepdog project is no longer active and the driver was marked as unsupported in the Train release. This completes the deprecation process and removes the driver. Change-Id: Ie94ae59a29473ca906c6df58a2681714049b16a1 Signed-off-by: Sean McGinnis --- cinder/opts.py | 2 - .../unit/volume/drivers/test_sheepdog.py | 1396 ----------------- cinder/volume/drivers/sheepdog.py | 661 -------- cinder/volume/manager.py | 2 +- .../block-storage/drivers/sheepdog-driver.rst | 51 - doc/source/reference/support-matrix.ini | 14 - doc/source/reference/support-matrix.rst | 3 +- ...epdog-driver-removal-b63d12460e886c33.yaml | 6 + 8 files changed, 9 insertions(+), 2126 deletions(-) delete mode 100644 cinder/tests/unit/volume/drivers/test_sheepdog.py delete mode 100644 cinder/volume/drivers/sheepdog.py delete mode 100644 doc/source/configuration/block-storage/drivers/sheepdog-driver.rst create mode 100644 releasenotes/notes/sheepdog-driver-removal-b63d12460e886c33.yaml diff --git a/cinder/opts.py b/cinder/opts.py index 1965d2b18b2..31c22291338 100644 --- a/cinder/opts.py +++ b/cinder/opts.py @@ -141,7 +141,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 sheepdog as cinder_volume_drivers_sheepdog from cinder.volume.drivers import solidfire as cinder_volume_drivers_solidfire from cinder.volume.drivers import storpool as cinder_volume_drivers_storpool from cinder.volume.drivers.stx import common as \ @@ -346,7 +345,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_sheepdog.sheepdog_opts, cinder_volume_drivers_solidfire.sf_opts, cinder_volume_drivers_storpool.storpool_opts, cinder_volume_drivers_stx_common.common_opts, diff --git a/cinder/tests/unit/volume/drivers/test_sheepdog.py b/cinder/tests/unit/volume/drivers/test_sheepdog.py deleted file mode 100644 index 0e7ea499e46..00000000000 --- a/cinder/tests/unit/volume/drivers/test_sheepdog.py +++ /dev/null @@ -1,1396 +0,0 @@ - -# Copyright (c) 2013 Zelin.io -# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. -# 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. - - -import contextlib -import errno - -import mock -from oslo_concurrency import processutils -from oslo_utils import importutils -from oslo_utils import units - -from cinder import context -from cinder import exception -from cinder.i18n import _ -from cinder.image import image_utils -from cinder import test -from cinder.tests.unit import fake_constants as fake -from cinder.tests.unit import fake_snapshot -from cinder.tests.unit import fake_volume -from cinder import utils -from cinder.volume import configuration as conf -from cinder.volume.drivers import sheepdog - -SHEEP_ADDR = '127.0.0.1' -SHEEP_PORT = 7000 - - -class SheepdogDriverTestDataGenerator(object): - def __init__(self): - self.TEST_VOLUME = self._make_fake_volume(self.TEST_VOL_DATA) - self.TEST_CLONED_VOLUME = self._make_fake_volume( - self.TEST_CLONED_VOL_DATA) - self.TEST_SNAPSHOT = self._make_fake_snapshot( - self.TEST_SNAPSHOT_DATA, self.TEST_VOLUME) - - def sheepdog_cmd_error(self, cmd, exit_code, stdout, stderr): - return (('(Command: %(cmd)s) ' - '(Return Code: %(exit_code)s) ' - '(Stdout: %(stdout)s) ' - '(Stderr: %(stderr)s)') % - {'cmd': cmd, - 'exit_code': exit_code, - 'stdout': stdout.replace('\n', '\\n'), - 'stderr': stderr.replace('\n', '\\n')}) - - def _make_fake_volume(self, volume_data): - return fake_volume.fake_volume_obj(context.get_admin_context(), - **volume_data) - - def _make_fake_snapshot(self, snapshot_data, src_volume): - snapshot_obj = fake_snapshot.fake_snapshot_obj( - context.get_admin_context(), **snapshot_data) - snapshot_obj.volume = src_volume - return snapshot_obj - - def cmd_dog_vdi_create(self, name, size): - return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'create', name, - '%sG' % size, '-a', SHEEP_ADDR, '-p', SHEEP_PORT) - - def cmd_dog_vdi_delete(self, name): - return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'delete', name, - '-a', SHEEP_ADDR, '-p', SHEEP_PORT) - - def cmd_dog_vdi_create_snapshot(self, vdiname, snapname): - return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'snapshot', '-s', - snapname, '-a', SHEEP_ADDR, '-p', SHEEP_PORT, vdiname) - - def cmd_dog_vdi_delete_snapshot(self, vdiname, snapname): - return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'delete', '-s', - snapname, '-a', SHEEP_ADDR, '-p', SHEEP_PORT, vdiname) - - def cmd_qemuimg_vdi_clone(self, src_vdiname, src_snapname, dst_vdiname, - size): - return ('env', 'LC_ALL=C', 'LANG=C', 'qemu-img', 'create', '-b', - 'sheepdog:%(addr)s:%(port)s:%(src_vdiname)s:%(src_snapname)s' % - {'addr': SHEEP_ADDR, 'port': SHEEP_PORT, - 'src_vdiname': src_vdiname, 'src_snapname': src_snapname}, - 'sheepdog:%(addr)s:%(port)s:%(dst_vdiname)s' % - {'addr': SHEEP_ADDR, 'port': SHEEP_PORT, - 'dst_vdiname': dst_vdiname}, '%sG' % size) - - def cmd_dog_vdi_resize(self, name, size): - return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'resize', name, - size, '-a', SHEEP_ADDR, '-p', SHEEP_PORT) - - def cmd_dog_vdi_list(self, name): - return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'list', name, - '-r', '-a', SHEEP_ADDR, '-p', SHEEP_PORT) - - def cmd_dog_node_info(self): - return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'node', 'info', - '-a', SHEEP_ADDR, '-p', SHEEP_PORT, '-r') - - def cmd_dog_node_list(self): - return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'node', 'list', - '-a', SHEEP_ADDR, '-p', SHEEP_PORT, '-r') - - CMD_DOG_CLUSTER_INFO = ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'cluster', - 'info', '-a', SHEEP_ADDR, '-p', SHEEP_PORT) - - TEST_VOL_DATA = { - 'size': 1, - 'id': fake.VOLUME_ID, - 'provider_auth': None, - 'host': 'host@backendsec#unit_test_pool', - 'project_id': fake.PROJECT_ID, - 'provider_location': 'location', - 'display_name': 'vol1', - 'display_description': 'unit test volume', - 'volume_type_id': None, - 'consistencygroup_id': None, - } - - TEST_CLONED_VOL_DATA = { - 'size': 2, - 'id': fake.VOLUME2_ID, - 'provider_auth': None, - 'host': 'host@backendsec#unit_test_pool', - 'project_id': fake.PROJECT_ID, - 'provider_location': 'location', - 'display_name': 'vol3', - 'display_description': 'unit test cloned volume', - 'volume_type_id': None, - 'consistencygroup_id': None, - } - - TEST_SNAPSHOT_DATA = { - 'id': fake.SNAPSHOT_ID, - } - - COLLIE_NODE_INFO = """ -0 107287605248 3623897354 3% -Total 107287605248 3623897354 3% 54760833024 -""" - - COLLIE_NODE_LIST = """ -0 127.0.0.1:7000 128 1 -""" - - COLLIE_VDI_LIST = """ -= testvolume 0 0 0 0 1467037106 fd32fc 3 -""" - - COLLIE_CLUSTER_INFO_0_5 = """\ -Cluster status: running - -Cluster created at Tue Jun 25 19:51:41 2013 - -Epoch Time Version -2013-06-25 19:51:41 1 [127.0.0.1:7000, 127.0.0.1:7001, 127.0.0.1:7002] -""" - - COLLIE_CLUSTER_INFO_0_6 = """\ -Cluster status: running, auto-recovery enabled - -Cluster created at Tue Jun 25 19:51:41 2013 - -Epoch Time Version -2013-06-25 19:51:41 1 [127.0.0.1:7000, 127.0.0.1:7001, 127.0.0.1:7002] -""" - - DOG_CLUSTER_RUNNING = """\ -Cluster status: running, auto-recovery enabled - -Cluster created at Thu Jun 18 17:24:56 2015 - -Epoch Time Version [Host:Port:V-Nodes,,,] -2015-06-18 17:24:56 1 [127.0.0.1:7000:128, 127.0.0.1:7001:128,\ - 127.0.0.1:7002:128] -""" - - DOG_CLUSTER_INFO_TO_BE_FORMATTED = """\ -Cluster status: Waiting for cluster to be formatted -""" - - DOG_CLUSTER_INFO_WAITING_OTHER_NODES = """\ -Cluster status: Waiting for other nodes to join cluster - -Cluster created at Thu Jun 18 17:24:56 2015 - -Epoch Time Version [Host:Port:V-Nodes,,,] -2015-06-18 17:24:56 1 [127.0.0.1:7000:128, 127.0.0.1:7001:128] -""" - - DOG_CLUSTER_INFO_SHUTTING_DOWN = """\ -Cluster status: System is shutting down -""" - - DOG_VDI_CREATE_VDI_ALREADY_EXISTS = """\ -Failed to create VDI %(vdiname)s: VDI exists already -""" - - DOG_VDI_SNAPSHOT_VDI_NOT_FOUND = """\ -Failed to create snapshot for volume-00000000-0000-0000-0000-000000000001: \ -No VDI found -""" - - DOG_VDI_SNAPSHOT_ALREADY_EXISTED = """\ -Failed to create snapshot for volume-00000000-0000-0000-0000-000000000001, \ -maybe snapshot id (0) or tag (snapshot-00000000-0000-0000-0000-000000000002) \ -is existed -""" - - DOG_VDI_SNAPSHOT_TAG_NOT_FOUND = """\ -Failed to open VDI volume-00000000-0000-0000-0000-000000000001 \ -(snapshot id: 0 snapshot tag: snapshot-00000000-0000-0000-0000-000000000002): \ -Failed to find requested tag -""" - - DOG_VDI_SNAPSHOT_VOLUME_NOT_FOUND = """\ -Failed to open VDI volume-00000000-0000-0000-0000-000000000001 \ -(snapshot id: 0 snapshot tag: snapshot-00000000-0000-0000-0000-000000000002): \ -No VDI found -""" - - DOG_VDI_RESIZE_SIZE_SHRINK = """\ -Shrinking VDIs is not implemented -""" - - DOG_VDI_RESIZE_TOO_LARGE = """\ -New VDI size is too large. This volume's max size is 4398046511104 -""" - - DOG_COMMAND_ERROR_VDI_NOT_EXISTS = """\ -Failed to open VDI %(vdiname)s (snapshot id: 0 snapshot tag: ): No VDI found -""" - - DOG_COMMAND_ERROR_FAIL_TO_CONNECT = """\ -failed to connect to 127.0.0.1:7000: Connection refused -failed to connect to 127.0.0.1:7000: Connection refused -Failed to get node list -""" - - QEMU_IMG_VDI_ALREADY_EXISTS = """\ -qemu-img: sheepdog:volume-00000000-0000-0000-0000-000000000001: \ -VDI exists already, -""" - - QEMU_IMG_VDI_NOT_FOUND = """\ -qemu-img: sheepdog:volume-00000000-0000-0000-0000-000000000003: \ -cannot get vdi info, No vdi found, \ -volume-00000000-0000-0000-0000-000000000001 \ -snapshot-00000000-0000-0000-0000-000000000002 -""" - - QEMU_IMG_SNAPSHOT_NOT_FOUND = """\ -qemu-img: sheepdog:volume-00000000-0000-0000-0000-000000000003: \ -cannot get vdi info, Failed to find the requested tag, \ -volume-00000000-0000-0000-0000-000000000001 \ -snapshot-00000000-0000-0000-0000-000000000002 -""" - - QEMU_IMG_SIZE_TOO_LARGE = """\ -qemu-img: sheepdog:volume-00000000-0000-0000-0000-000000000001: \ -An image is too large. The maximum image size is 4096GB -""" - - QEMU_IMG_FAILED_TO_CONNECT = """\ -qemu-img: sheepdog::volume-00000000-0000-0000-0000-000000000001: \ -Failed to connect socket: Connection refused -""" - - -class FakeImageService(object): - def download(self, context, image_id, path): - pass - - -class SheepdogIOWrapperTestCase(test.TestCase): - def setUp(self): - super(SheepdogIOWrapperTestCase, self).setUp() - self.volume = {'name': 'volume-2f9b2ff5-987b-4412-a91c-23caaf0d5aff'} - self.snapshot_name = 'snapshot-bf452d80-068a-43d7-ba9f-196cf47bd0be' - - self.vdi_wrapper = sheepdog.SheepdogIOWrapper( - SHEEP_ADDR, SHEEP_PORT, self.volume) - self.snapshot_wrapper = sheepdog.SheepdogIOWrapper( - SHEEP_ADDR, SHEEP_PORT, self.volume, self.snapshot_name) - - self.execute = mock.MagicMock() - self.mock_object(processutils, 'execute', self.execute) - - def test_init(self): - self.assertEqual(self.volume['name'], self.vdi_wrapper._vdiname) - self.assertIsNone(self.vdi_wrapper._snapshot_name) - self.assertEqual(0, self.vdi_wrapper._offset) - - self.assertEqual(self.snapshot_name, - self.snapshot_wrapper._snapshot_name) - - def test_execute(self): - cmd = ('cmd1', 'arg1') - data = 'data1' - - self.vdi_wrapper._execute(cmd, data) - - self.execute.assert_called_once_with(*cmd, process_input=data) - - def test_execute_error(self): - cmd = ('cmd1', 'arg1') - data = 'data1' - self.mock_object(processutils, 'execute', - mock.MagicMock(side_effect=OSError)) - - args = (cmd, data) - self.assertRaises(exception.VolumeDriverException, - self.vdi_wrapper._execute, - *args) - - def test_read_vdi(self): - self.vdi_wrapper.read() - self.execute.assert_called_once_with( - 'dog', 'vdi', 'read', '-a', SHEEP_ADDR, '-p', SHEEP_PORT, - self.volume['name'], 0, process_input=None) - - def test_read_vdi_invalid(self): - self.vdi_wrapper._valid = False - self.assertRaises(exception.VolumeDriverException, - self.vdi_wrapper.read) - - def test_write_vdi(self): - data = 'data1' - - self.vdi_wrapper.write(data) - - self.execute.assert_called_once_with( - 'dog', 'vdi', 'write', '-a', SHEEP_ADDR, '-p', SHEEP_PORT, - self.volume['name'], 0, len(data), - process_input=data) - self.assertEqual(len(data), self.vdi_wrapper.tell()) - - def test_write_vdi_invalid(self): - self.vdi_wrapper._valid = False - self.assertRaises(exception.VolumeDriverException, - self.vdi_wrapper.write, 'dummy_data') - - def test_read_snapshot(self): - self.snapshot_wrapper.read() - self.execute.assert_called_once_with( - 'dog', 'vdi', 'read', '-a', SHEEP_ADDR, '-p', SHEEP_PORT, - '-s', self.snapshot_name, self.volume['name'], 0, - process_input=None) - - def test_seek(self): - self.vdi_wrapper.seek(12345) - self.assertEqual(12345, self.vdi_wrapper.tell()) - - self.vdi_wrapper.seek(-2345, whence=1) - self.assertEqual(10000, self.vdi_wrapper.tell()) - - # This results in negative offset. - self.assertRaises(IOError, self.vdi_wrapper.seek, -20000, whence=1) - - def test_seek_invalid(self): - seek_num = 12345 - self.vdi_wrapper._valid = False - self.assertRaises(exception.VolumeDriverException, - self.vdi_wrapper.seek, seek_num) - - def test_flush(self): - # flush does nothing. - self.vdi_wrapper.flush() - self.assertFalse(self.execute.called) - - def test_fileno(self): - self.assertRaises(IOError, self.vdi_wrapper.fileno) - - -class SheepdogClientTestCase(test.TestCase): - def setUp(self): - super(SheepdogClientTestCase, self).setUp() - self._cfg = conf.Configuration(None) - self._cfg.sheepdog_store_address = SHEEP_ADDR - self._cfg.sheepdog_store_port = SHEEP_PORT - self.driver = sheepdog.SheepdogDriver(configuration=self._cfg) - db_driver = self.driver.configuration.db_driver - self.db = importutils.import_module(db_driver) - self.driver.db = self.db - self.driver.do_setup(None) - self.test_data = SheepdogDriverTestDataGenerator() - node_list = [SHEEP_ADDR] - self.client = sheepdog.SheepdogClient(node_list, SHEEP_PORT) - self._addr = SHEEP_ADDR - self._port = SHEEP_PORT - self._vdiname = self.test_data.TEST_VOLUME.name - self._vdisize = self.test_data.TEST_VOLUME.size - self._src_vdiname = self.test_data.TEST_SNAPSHOT.volume_name - self._snapname = self.test_data.TEST_SNAPSHOT.name - self._dst_vdiname = self.test_data.TEST_CLONED_VOLUME.name - self._dst_vdisize = self.test_data.TEST_CLONED_VOLUME.size - - @mock.patch.object(utils, 'execute') - def test_run_dog_success(self, fake_execute): - args = ('cluster', 'info') - expected_cmd = self.test_data.CMD_DOG_CLUSTER_INFO - fake_execute.return_value = ('', '') - self.client._run_dog(*args) - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_dog_command_not_found(self, fake_logger, fake_execute): - args = ('cluster', 'info') - expected_msg = 'No such file or directory' - expected_errno = errno.ENOENT - fake_execute.side_effect = OSError(expected_errno, expected_msg) - self.assertRaises(OSError, self.client._run_dog, *args) - self.assertTrue(fake_logger.error.called) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_dog_operation_not_permitted(self, fake_logger, fake_execute): - args = ('cluster', 'info') - expected_msg = 'Operation not permitted' - expected_errno = errno.EPERM - fake_execute.side_effect = OSError(expected_errno, expected_msg) - self.assertRaises(OSError, self.client._run_dog, *args) - self.assertTrue(fake_logger.error.called) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_dog_fail_to_connect(self, fake_logger, fake_execute): - args = ('cluster', 'info') - cmd = self.test_data.CMD_DOG_CLUSTER_INFO - exit_code = 2 - stdout = 'stdout dummy' - stderr = self.test_data.DOG_COMMAND_ERROR_FAIL_TO_CONNECT - expected_reason = (_('Failed to connect to sheep daemon. ' - 'addr: %(addr)s, port: %(port)s'), - {'addr': SHEEP_ADDR, 'port': SHEEP_PORT}) - fake_execute.side_effect = processutils.ProcessExecutionError( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - ex = self.assertRaises(sheepdog.SheepdogError, - self.client._run_dog, *args) - self.assertEqual(expected_reason, ex.kwargs['reason']) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_dog_fail_to_connect_bugcase(self, fake_logger, fake_execute): - # NOTE(zhangsong): Sheepdog's bug case. - # details are written to Sheepdog driver code. - args = ('node', 'list') - stdout = '' - stderr = self.test_data.DOG_COMMAND_ERROR_FAIL_TO_CONNECT - expected_reason = (_('Failed to connect to sheep daemon. ' - 'addr: %(addr)s, port: %(port)s'), - {'addr': SHEEP_ADDR, 'port': SHEEP_PORT}) - fake_execute.return_value = (stdout, stderr) - ex = self.assertRaises(sheepdog.SheepdogError, - self.client._run_dog, *args) - self.assertEqual(expected_reason, ex.kwargs['reason']) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_dog_unknown_error(self, fake_logger, fake_execute): - args = ('cluster', 'info') - cmd = self.test_data.CMD_DOG_CLUSTER_INFO - exit_code = 1 - stdout = 'stdout dummy' - stderr = 'stderr dummy' - expected_msg = self.test_data.sheepdog_cmd_error( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - fake_execute.side_effect = processutils.ProcessExecutionError( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client._run_dog, *args) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(utils, 'execute') - def test_run_qemu_img_success(self, fake_execute): - # multiple part of args match the prefix and - # volume name is matched the prefix unfortunately - expected_cmd = ('env', 'LC_ALL=C', 'LANG=C', - 'qemu-img', 'create', '-b', - 'sheepdog:%(addr)s:%(port)s:sheepdog:snap' % - {'addr': SHEEP_ADDR, 'port': SHEEP_PORT}, - 'sheepdog:%(addr)s:%(port)s:clone' % - {'addr': SHEEP_ADDR, 'port': SHEEP_PORT}, '10G') - fake_execute.return_value = ('', '') - self.client._run_qemu_img('create', '-b', 'sheepdog:sheepdog:snap', - 'sheepdog:clone', '10G') - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_qemu_img_command_not_found(self, fake_logger, fake_execute): - args = ('create', 'dummy') - expected_msg = 'No such file or directory' - expected_errno = errno.ENOENT - fake_execute.side_effect = OSError(expected_errno, expected_msg) - self.assertRaises(OSError, self.client._run_qemu_img, *args) - self.assertTrue(fake_logger.error.called) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_qemu_img_unknown_os_error(self, fake_logger, fake_execute): - args = ('create', 'dummy') - expected_msg = 'unknown' - expected_errno = errno.EPERM - fake_execute.side_effect = OSError(expected_errno, expected_msg) - self.assertRaises(OSError, self.client._run_qemu_img, *args) - self.assertTrue(fake_logger.error.called) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_qemu_img_fail_to_connect(self, fake_logger, fake_execute): - args = ('create', 'dummy') - cmd = ('qemu-img', 'create', 'dummy') - exit_code = 1 - stdout = 'stdout dummy' - stderr = self.test_data.QEMU_IMG_FAILED_TO_CONNECT - expected_reason = (_('Failed to connect to sheep daemon. ' - 'addr: %(addr)s, port: %(port)s'), - {'addr': SHEEP_ADDR, 'port': SHEEP_PORT}) - fake_execute.side_effect = processutils.ProcessExecutionError( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - ex = self.assertRaises(sheepdog.SheepdogError, - self.client._run_qemu_img, *args) - self.assertEqual(expected_reason, ex.kwargs['reason']) - - @mock.patch.object(utils, 'execute') - @mock.patch.object(sheepdog, 'LOG') - def test_run_qemu_img_unknown_execution_error(self, fake_logger, - fake_execute): - args = ('create', 'dummy') - cmd = ('qemu-img', 'create', 'dummy') - exit_code = 1 - stdout = 'stdout dummy' - stderr = 'stderr dummy' - expected_msg = self.test_data.sheepdog_cmd_error( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - fake_execute.side_effect = processutils.ProcessExecutionError( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client._run_qemu_img, *args) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_check_cluster_status_success(self, fake_logger, fake_execute): - stdout = self.test_data.DOG_CLUSTER_RUNNING - stderr = '' - expected_cmd = ('cluster', 'info') - fake_execute.return_value = (stdout, stderr) - self.client.check_cluster_status() - fake_execute.assert_called_once_with(*expected_cmd) - self.assertTrue(fake_logger.debug.called) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_check_cluster_status_v0_5(self, fake_execute): - stdout = self.test_data.COLLIE_CLUSTER_INFO_0_5 - stderr = '' - fake_execute.return_value = (stdout, stderr) - self.client.check_cluster_status() - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_check_cluster_status_v0_6(self, fake_execute): - stdout = self.test_data.COLLIE_CLUSTER_INFO_0_6 - stderr = '' - fake_execute.return_value = (stdout, stderr) - self.client.check_cluster_status() - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_check_cluster_status_not_formatted(self, fake_logger, - fake_execute): - stdout = self.test_data.DOG_CLUSTER_INFO_TO_BE_FORMATTED - stderr = '' - expected_reason = _('Cluster is not formatted. ' - 'You should probably perform ' - '"dog cluster format".') - fake_execute.return_value = (stdout, stderr) - ex = self.assertRaises(sheepdog.SheepdogError, - self.client.check_cluster_status) - self.assertEqual(expected_reason, ex.kwargs['reason']) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_check_cluster_status_waiting_to_join_cluster(self, fake_logger, - fake_execute): - stdout = self.test_data.DOG_CLUSTER_INFO_WAITING_OTHER_NODES - stderr = '' - expected_reason = _('Waiting for all nodes to join cluster. ' - 'Ensure all sheep daemons are running.') - fake_execute.return_value = (stdout, stderr) - ex = self.assertRaises(sheepdog.SheepdogError, - self.client.check_cluster_status) - self.assertEqual(expected_reason, ex.kwargs['reason']) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_check_cluster_status_shutting_down(self, fake_logger, - fake_execute): - stdout = self.test_data.DOG_CLUSTER_INFO_SHUTTING_DOWN - stderr = '' - expected_reason = _('Invalid sheepdog cluster status.') - fake_execute.return_value = (stdout, stderr) - ex = self.assertRaises(sheepdog.SheepdogError, - self.client.check_cluster_status) - self.assertEqual(expected_reason, ex.kwargs['reason']) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_check_cluster_status_unknown_error(self, fake_logger, - fake_execute): - cmd = self.test_data.CMD_DOG_CLUSTER_INFO - exit_code = 2 - stdout = 'stdout_dummy' - stderr = 'stdout_dummy' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.check_cluster_status) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_create_success(self, fake_execute): - expected_cmd = ('vdi', 'create', self._vdiname, '%sG' % self._vdisize) - fake_execute.return_value = ('', '') - self.client.create(self._vdiname, self._vdisize) - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_create_vdi_already_exists(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_vdi_create(self._vdiname, self._vdisize) - exit_code = 1 - stdout = '' - stderr = (self.test_data.DOG_VDI_CREATE_VDI_ALREADY_EXISTS % - {'vdiname': self._vdiname}) - expected_msg = self.test_data.sheepdog_cmd_error( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, self.client.create, - self._vdiname, self._vdisize) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_create_unknown_error(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_vdi_create(self._vdiname, self._vdisize) - exit_code = 1 - stdout = 'stdout_dummy' - stderr = 'stderr_dummy' - expected_msg = self.test_data.sheepdog_cmd_error( - cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, self.client.create, - self._vdiname, self._vdisize) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_delete_success(self, fake_execute): - expected_cmd = ('vdi', 'delete', self._vdiname) - fake_execute.return_value = ('', '') - self.client.delete(self._vdiname) - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_delete_vdi_not_found(self, fake_logger, fake_execute): - stdout = '' - stderr = (self.test_data.DOG_COMMAND_ERROR_VDI_NOT_EXISTS % - {'vdiname': self._vdiname}) - fake_execute.return_value = (stdout, stderr) - self.client.delete(self._vdiname) - self.assertTrue(fake_logger.warning.called) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_delete_unknown_error(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_vdi_delete(self._vdiname) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = 'stderr_dummy' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.delete, self._vdiname) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_create_snapshot_success(self, fake_execute): - args = (self._src_vdiname, self._snapname) - expected_cmd = ('vdi', 'snapshot', '-s', self._snapname, - self._src_vdiname) - fake_execute.return_value = ('', '') - self.client.create_snapshot(*args) - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_create_snapshot_vdi_not_found(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname) - cmd = self.test_data.cmd_dog_vdi_create_snapshot(*args) - exit_code = 1 - stdout = '' - stderr = self.test_data.DOG_VDI_SNAPSHOT_VDI_NOT_FOUND - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.create_snapshot, *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_create_snapshot_snap_name_already_used(self, fake_logger, - fake_execute): - args = (self._src_vdiname, self._snapname) - cmd = self.test_data.cmd_dog_vdi_create_snapshot(*args) - exit_code = 1 - stdout = 'stdout_dummy' - stderr = self.test_data.DOG_VDI_SNAPSHOT_ALREADY_EXISTED - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.create_snapshot, *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_create_snapshot_unknown_error(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname) - cmd = self.test_data.cmd_dog_vdi_create_snapshot(*args) - exit_code = 1 - stdout = 'stdout_dummy' - stderr = 'unknown_error' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.create_snapshot, *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_delete_snapshot_success(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname) - expected_cmd = ('vdi', 'delete', '-s', self._snapname, - self._src_vdiname) - fake_execute.return_value = ('', '') - self.client.delete_snapshot(*args) - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_delete_snapshot_not_found(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname) - stdout = '' - stderr = self.test_data.DOG_VDI_SNAPSHOT_TAG_NOT_FOUND - fake_execute.return_value = (stdout, stderr) - self.client.delete_snapshot(*args) - self.assertTrue(fake_logger.warning.called) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_delete_snapshot_vdi_not_found(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname) - stdout = '' - stderr = self.test_data.DOG_VDI_SNAPSHOT_VOLUME_NOT_FOUND - fake_execute.return_value = (stdout, stderr) - self.client.delete_snapshot(*args) - self.assertTrue(fake_logger.warning.called) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_delete_snapshot_unknown_error(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname) - cmd = self.test_data.cmd_dog_vdi_delete_snapshot(*args) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = 'unknown_error' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.delete_snapshot, *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img') - def test_clone_success(self, fake_execute): - args = (self._src_vdiname, self._snapname, - self._dst_vdiname, self._dst_vdisize) - src_volume = 'sheepdog:%(src_vdiname)s:%(snapname)s' % { - 'src_vdiname': self._src_vdiname, 'snapname': self._snapname} - dst_volume = 'sheepdog:%s' % self._dst_vdiname - expected_cmd = ('create', '-b', src_volume, dst_volume, - '%sG' % self._dst_vdisize) - fake_execute.return_code = ("", "") - self.client.clone(*args) - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img') - @mock.patch.object(sheepdog, 'LOG') - def test_clone_dst_vdi_already_exists(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname, - self._dst_vdiname, self._dst_vdisize) - cmd = self.test_data.cmd_qemuimg_vdi_clone(*args) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = self.test_data.QEMU_IMG_VDI_ALREADY_EXISTS - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, self.client.clone, - *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img') - @mock.patch.object(sheepdog, 'LOG') - def test_clone_src_vdi_not_found(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname, - self._dst_vdiname, self._dst_vdisize) - cmd = self.test_data.cmd_qemuimg_vdi_clone(*args) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = self.test_data.QEMU_IMG_VDI_NOT_FOUND - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, self.client.clone, - *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img') - @mock.patch.object(sheepdog, 'LOG') - def test_clone_src_snapshot_not_found(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname, - self._dst_vdiname, self._dst_vdisize) - cmd = self.test_data.cmd_qemuimg_vdi_clone(*args) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = self.test_data.QEMU_IMG_SNAPSHOT_NOT_FOUND - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, self.client.clone, - *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img') - @mock.patch.object(sheepdog, 'LOG') - def test_clone_too_large_volume_size(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname, - self._dst_vdiname, self._dst_vdisize) - cmd = self.test_data.cmd_qemuimg_vdi_clone(*args) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = self.test_data.QEMU_IMG_SIZE_TOO_LARGE - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, self.client.clone, - *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img') - @mock.patch.object(sheepdog, 'LOG') - def test_clone_unknown_error(self, fake_logger, fake_execute): - args = (self._src_vdiname, self._snapname, - self._dst_vdiname, self._dst_vdisize) - cmd = self.test_data.cmd_qemuimg_vdi_clone(*args) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = 'stderr_dummy' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, self.client.clone, - *args) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_resize_success(self, fake_execute): - expected_cmd = ('vdi', 'resize', self._vdiname, 10 * 1024 ** 3) - fake_execute.return_value = ('', '') - self.client.resize(self._vdiname, 10) - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_resize_vdi_not_found(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 10 * 1024 ** 3) - exit_code = 1 - stdout = 'stdout_dummy' - stderr = (self.test_data.DOG_COMMAND_ERROR_VDI_NOT_EXISTS % - {'vdiname': self._vdiname}) - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.resize, self._vdiname, 1) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_resize_shrinking_not_supported(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 1 * 1024 ** 3) - exit_code = 1 - stdout = 'stdout_dummy' - stderr = self.test_data.DOG_VDI_RESIZE_SIZE_SHRINK - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.resize, self._vdiname, 1) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_resize_too_large_size(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 5 * 1024 ** 4) - exit_code = 64 - stdout = 'stdout_dummy' - stderr = self.test_data.DOG_VDI_RESIZE_TOO_LARGE - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.resize, self._vdiname, 5120) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_resize_unknown_error(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 10 * 1024 ** 3) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = 'stderr_dummy' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.resize, self._vdiname, 10) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_get_volume_stats_success(self, fake_execute): - expected_cmd = ('node', 'info', '-r') - fake_execute.return_value = (self.test_data.COLLIE_NODE_INFO, '') - self.client.get_volume_stats() - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_get_volume_stats_unknown_error(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_node_info() - exit_code = 2 - stdout = 'stdout_dummy' - stderr = 'stderr_dummy' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.get_volume_stats) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_get_vdi_info_success(self, fake_execute): - - expected_cmd = ('vdi', 'list', self._vdiname, '-r') - fake_execute.return_value = (self.test_data.COLLIE_VDI_LIST, '') - self.client.get_vdi_info(self._vdiname) - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_get_vdi_info_unknown_error(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_vdi_list(self._vdiname) - exit_code = 2 - stdout = 'stdout_dummy' - stderr = 'stderr_dummy' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.get_vdi_info, self._vdiname) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_update_node_list_success(self, fake_execute): - expected_cmd = ('node', 'list', '-r') - fake_execute.return_value = (self.test_data.COLLIE_NODE_LIST, '') - self.client.update_node_list() - fake_execute.assert_called_once_with(*expected_cmd) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - @mock.patch.object(sheepdog, 'LOG') - def test_update_node_list_unknown_error(self, fake_logger, fake_execute): - cmd = self.test_data.cmd_dog_node_list() - exit_code = 2 - stdout = 'stdout_dummy' - stderr = 'stderr_dummy' - expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd, - exit_code=exit_code, - stdout=stdout, - stderr=stderr) - fake_execute.side_effect = sheepdog.SheepdogCmdError( - cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'), - stderr=stderr.replace('\n', '\\n')) - ex = self.assertRaises(sheepdog.SheepdogCmdError, - self.client.update_node_list) - self.assertTrue(fake_logger.error.called) - self.assertEqual(expected_msg, ex.msg) - - -class SheepdogDriverTestCase(test.TestCase): - def setUp(self): - super(SheepdogDriverTestCase, self).setUp() - self._cfg = conf.Configuration(None) - self._cfg.sheepdog_store_address = SHEEP_ADDR - self._cfg.sheepdog_store_port = SHEEP_PORT - self.driver = sheepdog.SheepdogDriver(configuration=self._cfg) - db_driver = self.driver.configuration.db_driver - self.db = importutils.import_module(db_driver) - self.driver.db = self.db - self.driver.do_setup(None) - self.test_data = SheepdogDriverTestDataGenerator() - self.client = self.driver.client - self._addr = SHEEP_ADDR - self._port = SHEEP_PORT - self._vdiname = self.test_data.TEST_VOLUME.name - self._vdisize = self.test_data.TEST_VOLUME.size - self._src_vdiname = self.test_data.TEST_SNAPSHOT.volume_name - self._snapname = self.test_data.TEST_SNAPSHOT.name - self._dst_vdiname = self.test_data.TEST_CLONED_VOLUME.name - self._dst_vdisize = self.test_data.TEST_CLONED_VOLUME.size - - @mock.patch.object(sheepdog.SheepdogClient, 'update_node_list') - @mock.patch.object(sheepdog.SheepdogClient, 'check_cluster_status') - def test_check_for_setup_error(self, fake_check, fake_update): - self.driver.check_for_setup_error() - fake_check.assert_called_once_with() - fake_update.assert_called_once_with() - - @mock.patch.object(sheepdog.SheepdogClient, 'create') - def test_create_volume(self, fake_execute): - self.driver.create_volume(self.test_data.TEST_VOLUME) - fake_execute.assert_called_once_with(self._vdiname, self._vdisize) - - @mock.patch.object(sheepdog.SheepdogClient, 'delete') - def test_delete_volume(self, fake_execute): - self.driver.delete_volume(self.test_data.TEST_VOLUME) - fake_execute.assert_called_once_with(self._vdiname) - - @mock.patch.object(sheepdog.SheepdogClient, 'get_volume_stats') - def test_update_volume_stats(self, fake_execute): - fake_execute.return_value = self.test_data.COLLIE_NODE_INFO - expected = dict( - volume_backend_name='sheepdog', - vendor_name='Open Source', - driver_version=self.driver.VERSION, - storage_protocol='sheepdog', - total_capacity_gb=float(107287605248) / units.Gi, - free_capacity_gb=float(107287605248 - 3623897354) / units.Gi, - reserved_percentage=0, - QoS_support=False) - actual = self.driver.get_volume_stats(True) - self.assertDictEqual(expected, actual) - - @mock.patch.object(sheepdog.SheepdogClient, '_run_dog') - def test_copy_image_to_volume(self, fake_run_dog): - @contextlib.contextmanager - def fake_temp_file(): - class FakeTmp(object): - def __init__(self, name): - self.name = name - yield FakeTmp('test').name - - def fake_try_execute(obj, *command, **kwargs): - return True - - self.mock_object(image_utils, 'temporary_file', fake_temp_file) - self.mock_object(image_utils, 'fetch_verify_image', - return_value=None) - self.mock_object(image_utils, 'convert_image', - return_value=None) - self.mock_object(sheepdog.SheepdogDriver, '_try_execute', - fake_try_execute) - fake_run_dog.return_value = ('fake_stdout', 'fake_stderr') - self.driver.copy_image_to_volume(None, self.test_data.TEST_VOLUME, - FakeImageService(), None) - - @mock.patch('six.moves.builtins.open') - @mock.patch('cinder.image.image_utils.temporary_file') - def test_copy_volume_to_image(self, mock_temp, mock_open): - fake_context = {} - fake_volume = {'name': 'volume-00000001'} - fake_image_service = mock.Mock() - fake_image_meta = {'id': '10958016-e196-42e3-9e7f-5d8927ae3099'} - temp_file = mock_temp.return_value.__enter__.return_value - - patch = mock.patch.object - with patch(self.driver, '_try_execute') as fake_try_execute: - self.driver.copy_volume_to_image(fake_context, - fake_volume, - fake_image_service, - fake_image_meta) - - expected_cmd = ('qemu-img', - 'convert', - '-f', 'raw', - '-t', 'none', - '-O', 'raw', - 'sheepdog:%s:%s:%s' % ( - self._addr, - self._port, - fake_volume['name']), - mock.ANY) - mock_open.assert_called_once_with(temp_file, 'rb') - fake_try_execute.assert_called_once_with(*expected_cmd) - fake_image_service.update.assert_called_once_with( - fake_context, fake_image_meta['id'], mock.ANY, mock.ANY) - - @mock.patch('six.moves.builtins.open') - @mock.patch('cinder.image.image_utils.temporary_file') - def test_copy_volume_to_image_nonexistent_volume(self, mock_temp, - mock_open): - fake_context = {} - fake_volume = { - 'name': 'nonexistent-volume-82c4539e-c2a5-11e4-a293-0aa186c60fe0'} - fake_image_service = mock.Mock() - fake_image_meta = {'id': '10958016-e196-42e3-9e7f-5d8927ae3099'} - - patch = mock.patch.object - with patch(self.driver, '_try_execute') as fake_try_execute: - fake_try_execute.side_effect = ( - processutils.ProcessExecutionError) - args = (fake_context, fake_volume, fake_image_service, - fake_image_meta) - expected_cmd = ('qemu-img', - 'convert', - '-f', 'raw', - '-t', 'none', - '-O', 'raw', - 'sheepdog:%s:%s:%s' % ( - self._addr, - self._port, - fake_volume['name']), - mock.ANY) - - self.assertRaises(processutils.ProcessExecutionError, - self.driver.copy_volume_to_image, - *args) - - fake_try_execute.assert_called_once_with(*expected_cmd) - mock_open.assert_not_called() - fake_image_service.update.assert_not_called() - - @mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot') - @mock.patch.object(sheepdog.SheepdogClient, 'clone') - @mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot') - def test_create_cloned_volume(self, fake_delete_snapshot, - fake_clone, fake_create_snapshot): - src_vol = self.test_data.TEST_VOLUME - cloned_vol = self.test_data.TEST_CLONED_VOLUME - - self.driver.create_cloned_volume(cloned_vol, src_vol) - snapshot_name = 'tmp-snap-%s-%s' % (src_vol.name, cloned_vol.id) - fake_create_snapshot.assert_called_once_with(src_vol.name, - snapshot_name) - fake_clone.assert_called_once_with(src_vol.name, snapshot_name, - cloned_vol.name, cloned_vol.size) - fake_delete_snapshot.assert_called_once_with(src_vol.name, - snapshot_name) - - @mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot') - @mock.patch.object(sheepdog.SheepdogClient, 'clone') - @mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot') - @mock.patch.object(sheepdog, 'LOG') - def test_create_cloned_volume_failure(self, fake_logger, - fake_delete_snapshot, - fake_clone, fake_create_snapshot): - src_vol = self.test_data.TEST_VOLUME - cloned_vol = self.test_data.TEST_CLONED_VOLUME - snapshot_name = 'tmp-snap-%s-%s' % (src_vol.name, cloned_vol.id) - - fake_clone.side_effect = sheepdog.SheepdogCmdError( - cmd='dummy', exit_code=1, stdout='dummy', stderr='dummy') - self.assertRaises(sheepdog.SheepdogCmdError, - self.driver.create_cloned_volume, - cloned_vol, src_vol) - fake_delete_snapshot.assert_called_once_with(src_vol.name, - snapshot_name) - self.assertTrue(fake_logger.error.called) - - @mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot') - def test_create_snapshot(self, fake_create_snapshot): - snapshot = self.test_data.TEST_SNAPSHOT - self.driver.create_snapshot(snapshot) - fake_create_snapshot.assert_called_once_with(snapshot.volume_name, - snapshot.name) - - @mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot') - def test_delete_snapshot(self, fake_delete_snapshot): - snapshot = self.test_data.TEST_SNAPSHOT - self.driver.delete_snapshot(snapshot) - fake_delete_snapshot.assert_called_once_with(snapshot.volume_name, - snapshot.name) - - def test_clone_image_success(self): - context = {} - image_id = "caa4ffd0-fake-fake-fake-f8631a807f5a" - image_location = ('sheepdog://192.168.1.111:7000:%s' % image_id, None) - image_meta = {'id': image_id, 'size': 1, 'disk_format': 'raw'} - image_service = '' - - patch = mock.patch.object - with patch(self.driver, '_is_cloneable', return_value=True): - with patch(self.driver, 'create_cloned_volume'): - with patch(self.client, 'resize'): - model_updated, cloned = self.driver.clone_image( - context, self.test_data.TEST_CLONED_VOLUME, - image_location, image_meta, image_service) - - self.assertTrue(cloned) - self.assertEqual("sheepdog:%s:%s:%s" % ( - self._addr, - self._port, - self.test_data.TEST_CLONED_VOLUME.name), - model_updated['provider_location']) - - def test_clone_image_failure(self): - context = {} - fake_vol = {} - image_location = ('image_location', None) - image_meta = {} - image_service = '' - - with mock.patch.object(self.driver, '_is_cloneable', - lambda *args: False): - result = self.driver.clone_image( - context, fake_vol, image_location, image_meta, image_service) - self.assertEqual(({}, False), result) - - def test_is_cloneable(self): - uuid = '87f1b01c-f46c-4537-bd5d-23962f5f4316' - location = 'sheepdog://127.0.0.1:7000:%s' % uuid - image_meta = {'id': uuid, 'size': 1, 'disk_format': 'raw'} - invalid_image_meta = {'id': uuid, 'size': 1, 'disk_format': 'iso'} - - with mock.patch.object(self.client, 'get_vdi_info') as fake_execute: - fake_execute.return_value = self.test_data.COLLIE_VDI_LIST - self.assertTrue( - self.driver._is_cloneable(location, image_meta)) - - # Test for invalid location - self.assertFalse( - self.driver._is_cloneable('invalid-location', image_meta)) - - # Test for image not exist in sheepdog cluster - fake_execute.return_value = '' - self.assertFalse( - self.driver._is_cloneable(location, image_meta)) - - # Test for invalid image meta - self.assertFalse( - self.driver._is_cloneable(location, invalid_image_meta)) - - def test_create_volume_from_snapshot(self): - dst_volume = self.test_data.TEST_CLONED_VOLUME - snapshot = self.test_data.TEST_SNAPSHOT - with mock.patch.object(self.client, 'clone') as fake_execute: - self.driver.create_volume_from_snapshot(dst_volume, snapshot) - fake_execute.assert_called_once_with(self._src_vdiname, - self._snapname, - self._dst_vdiname, - self._dst_vdisize) - - def test_initialize_connection(self): - fake_volume = self.test_data.TEST_VOLUME - expected = { - 'driver_volume_type': 'sheepdog', - 'data': { - 'name': fake_volume.name, - 'hosts': ["127.0.0.1"], - 'ports': ["7000"], - } - } - actual = self.driver.initialize_connection(fake_volume, None) - self.assertDictEqual(expected, actual) - - @mock.patch.object(sheepdog.SheepdogClient, 'resize') - @mock.patch.object(sheepdog, 'LOG') - def test_extend_volume(self, fake_logger, fake_execute): - self.driver.extend_volume(self.test_data.TEST_VOLUME, 10) - fake_execute.assert_called_once_with(self._vdiname, 10) - self.assertTrue(fake_logger.debug.called) diff --git a/cinder/volume/drivers/sheepdog.py b/cinder/volume/drivers/sheepdog.py deleted file mode 100644 index 4b5c1306657..00000000000 --- a/cinder/volume/drivers/sheepdog.py +++ /dev/null @@ -1,661 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# Copyright (c) 2013 Zelin.io -# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. -# 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. - -""" -SheepDog Volume Driver. - -""" -import errno -import eventlet -import io -import random -import re - -from oslo_concurrency import processutils -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import excutils -from oslo_utils import units - -from cinder import exception -from cinder.i18n import _ -from cinder.image import image_utils -from cinder import interface -from cinder import utils -from cinder.volume import configuration -from cinder.volume import driver - - -LOG = logging.getLogger(__name__) - -sheepdog_opts = [ - cfg.StrOpt('sheepdog_store_address', - default='127.0.0.1', - help=('IP address of sheep daemon.')), - cfg.PortOpt('sheepdog_store_port', - default=7000, - help=('Port of sheep daemon.')) -] - -CONF = cfg.CONF -CONF.import_opt("image_conversion_dir", "cinder.image.image_utils") -CONF.register_opts(sheepdog_opts, group=configuration.SHARED_CONF_GROUP) - - -class SheepdogError(exception.VolumeBackendAPIException): - message = _("An error has occurred in SheepdogDriver. " - "(Reason: %(reason)s)") - - -class SheepdogCmdError(SheepdogError): - message = _("(Command: %(cmd)s) " - "(Return Code: %(exit_code)s) " - "(Stdout: %(stdout)s) " - "(Stderr: %(stderr)s)") - - -class SheepdogClient(object): - """Sheepdog command executor.""" - - QEMU_SHEEPDOG_PREFIX = 'sheepdog:' - DOG_RESP_CONNECTION_ERROR = 'failed to connect to' - DOG_RESP_CLUSTER_RUNNING = 'Cluster status: running' - DOG_RESP_CLUSTER_NOT_FORMATTED = ('Cluster status: ' - 'Waiting for cluster to be formatted') - DOG_RESP_CLUSTER_WAITING = ('Cluster status: ' - 'Waiting for other nodes to join cluster') - DOG_RESP_VDI_ALREADY_EXISTS = ': VDI exists already' - DOG_RESP_VDI_NOT_FOUND = ': No VDI found' - DOG_RESP_VDI_SHRINK_NOT_SUPPORT = 'Shrinking VDIs is not implemented' - DOG_RESP_VDI_SIZE_TOO_LARGE = 'New VDI size is too large' - DOG_RESP_SNAPSHOT_VDI_NOT_FOUND = ': No VDI found' - DOG_RESP_SNAPSHOT_NOT_FOUND = ': Failed to find requested tag' - DOG_RESP_SNAPSHOT_EXISTED = 'tag (%(snapname)s) is existed' - QEMU_IMG_RESP_CONNECTION_ERROR = ('Failed to connect socket: ' - 'Connection refused') - QEMU_IMG_RESP_ALREADY_EXISTS = ': VDI exists already' - QEMU_IMG_RESP_SNAPSHOT_NOT_FOUND = 'Failed to find the requested tag' - QEMU_IMG_RESP_VDI_NOT_FOUND = 'No vdi found' - QEMU_IMG_RESP_SIZE_TOO_LARGE = 'An image is too large.' - - def __init__(self, node_list, port): - self.node_list = node_list - self.port = port - - def get_addr(self): - """Get a random node in sheepdog cluster.""" - return self.node_list[random.randint(0, len(self.node_list) - 1)] - - def local_path(self, volume): - """Return a sheepdog location path.""" - return "sheepdog:%(addr)s:%(port)s:%(name)s" % { - 'addr': self.get_addr(), - 'port': self.port, - 'name': volume['name']} - - def _run_dog(self, command, subcommand, *params): - """Execute dog command wrapper.""" - addr = self.get_addr() - cmd = ('env', 'LC_ALL=C', 'LANG=C', 'dog', command, subcommand, - '-a', addr, '-p', self.port) + params - try: - (_stdout, _stderr) = utils.execute(*cmd) - if _stderr.startswith(self.DOG_RESP_CONNECTION_ERROR): - # NOTE(tishizaki) - # Dog command does not return error_code although - # dog command cannot connect to sheep process. - # That is a Sheepdog's bug. - # To avoid a Sheepdog's bug, now we need to check stderr. - # If Sheepdog has been fixed, this check logic is needed - # by old Sheepdog users. - reason = (_('Failed to connect to sheep daemon. ' - 'addr: %(addr)s, port: %(port)s'), - {'addr': addr, 'port': self.port}) - raise SheepdogError(reason=reason) - return (_stdout, _stderr) - except OSError as e: - with excutils.save_and_reraise_exception(): - if e.errno == errno.ENOENT: - msg = 'Sheepdog is not installed. OSError: command is %s.' - else: - msg = 'OSError: command is %s.' - LOG.error(msg, cmd) - except processutils.ProcessExecutionError as e: - _stderr = e.stderr - if _stderr.startswith(self.DOG_RESP_CONNECTION_ERROR): - reason = (_('Failed to connect to sheep daemon. ' - 'addr: %(addr)s, port: %(port)s'), - {'addr': addr, 'port': self.port}) - raise SheepdogError(reason=reason) - raise SheepdogCmdError( - cmd=e.cmd, - exit_code=e.exit_code, - stdout=e.stdout.replace('\n', '\\n'), - stderr=e.stderr.replace('\n', '\\n')) - - def _run_qemu_img(self, command, *params): - """Executes qemu-img command wrapper.""" - addr = self.get_addr() - cmd = ['env', 'LC_ALL=C', 'LANG=C', 'qemu-img', command] - for param in params: - if param.startswith(self.QEMU_SHEEPDOG_PREFIX): - # replace 'sheepdog:vdiname[:snapshotname]' to - # 'sheepdog:addr:port:vdiname[:snapshotname]' - param = param.replace(self.QEMU_SHEEPDOG_PREFIX, - '%(prefix)s%(addr)s:%(port)s:' % - {'prefix': self.QEMU_SHEEPDOG_PREFIX, - 'addr': addr, 'port': self.port}, - 1) - cmd.append(param) - try: - return utils.execute(*cmd) - except OSError as e: - with excutils.save_and_reraise_exception(): - if e.errno == errno.ENOENT: - msg = ('Qemu-img is not installed. OSError: command is ' - '%(cmd)s.') - else: - msg = 'OSError: command is %(cmd)s.' - LOG.error(msg, {'cmd': tuple(cmd)}) - except processutils.ProcessExecutionError as e: - _stderr = e.stderr - if self.QEMU_IMG_RESP_CONNECTION_ERROR in _stderr: - reason = (_('Failed to connect to sheep daemon. ' - 'addr: %(addr)s, port: %(port)s'), - {'addr': addr, 'port': self.port}) - raise SheepdogError(reason=reason) - raise SheepdogCmdError( - cmd=e.cmd, - exit_code=e.exit_code, - stdout=e.stdout.replace('\n', '\\n'), - stderr=e.stderr.replace('\n', '\\n')) - - def check_cluster_status(self): - try: - (_stdout, _stderr) = self._run_dog('cluster', 'info') - except SheepdogCmdError as e: - cmd = e.kwargs['cmd'] - with excutils.save_and_reraise_exception(): - LOG.error('Failed to check cluster status.' - '(command: %s)', cmd) - - if _stdout.startswith(self.DOG_RESP_CLUSTER_RUNNING): - LOG.debug('Sheepdog cluster is running.') - return - - reason = _('Invalid sheepdog cluster status.') - if _stdout.startswith(self.DOG_RESP_CLUSTER_NOT_FORMATTED): - reason = _('Cluster is not formatted. ' - 'You should probably perform "dog cluster format".') - elif _stdout.startswith(self.DOG_RESP_CLUSTER_WAITING): - reason = _('Waiting for all nodes to join cluster. ' - 'Ensure all sheep daemons are running.') - raise SheepdogError(reason=reason) - - def create(self, vdiname, size): - try: - self._run_dog('vdi', 'create', vdiname, '%sG' % size) - except SheepdogCmdError as e: - _stderr = e.kwargs['stderr'] - with excutils.save_and_reraise_exception(): - if _stderr.rstrip('\\n').endswith( - self.DOG_RESP_VDI_ALREADY_EXISTS): - LOG.error('Volume already exists. %s', vdiname) - else: - LOG.error('Failed to create volume. %s', vdiname) - - def delete(self, vdiname): - try: - (_stdout, _stderr) = self._run_dog('vdi', 'delete', vdiname) - if _stderr.rstrip().endswith(self.DOG_RESP_VDI_NOT_FOUND): - LOG.warning('Volume not found. %s', vdiname) - except SheepdogCmdError as e: - _stderr = e.kwargs['stderr'] - with excutils.save_and_reraise_exception(): - LOG.error('Failed to delete volume. %s', vdiname) - - def create_snapshot(self, vdiname, snapname): - try: - self._run_dog('vdi', 'snapshot', '-s', snapname, vdiname) - except SheepdogCmdError as e: - cmd = e.kwargs['cmd'] - _stderr = e.kwargs['stderr'] - with excutils.save_and_reraise_exception(): - if _stderr.rstrip('\\n').endswith( - self.DOG_RESP_SNAPSHOT_VDI_NOT_FOUND): - LOG.error('Volume "%s" not found. Please check the ' - 'results of "dog vdi list".', - vdiname) - elif _stderr.rstrip('\\n').endswith( - self.DOG_RESP_SNAPSHOT_EXISTED % - {'snapname': snapname}): - LOG.error('Snapshot "%s" already exists.', snapname) - else: - LOG.error('Failed to create snapshot. (command: %s)', - cmd) - - def delete_snapshot(self, vdiname, snapname): - try: - (_stdout, _stderr) = self._run_dog('vdi', 'delete', '-s', - snapname, vdiname) - if _stderr.rstrip().endswith(self.DOG_RESP_SNAPSHOT_NOT_FOUND): - LOG.warning('Snapshot "%s" not found.', snapname) - elif _stderr.rstrip().endswith(self.DOG_RESP_VDI_NOT_FOUND): - LOG.warning('Volume "%s" not found.', vdiname) - except SheepdogCmdError as e: - cmd = e.kwargs['cmd'] - _stderr = e.kwargs['stderr'] - with excutils.save_and_reraise_exception(): - LOG.error('Failed to delete snapshot. (command: %s)', - cmd) - - def clone(self, src_vdiname, src_snapname, dst_vdiname, size): - try: - self._run_qemu_img('create', '-b', - 'sheepdog:%(src_vdiname)s:%(src_snapname)s' % - {'src_vdiname': src_vdiname, - 'src_snapname': src_snapname}, - 'sheepdog:%s' % dst_vdiname, '%sG' % size) - except SheepdogCmdError as e: - cmd = e.kwargs['cmd'] - _stderr = e.kwargs['stderr'] - with excutils.save_and_reraise_exception(): - if self.QEMU_IMG_RESP_ALREADY_EXISTS in _stderr: - LOG.error('Clone volume "%s" already exists. ' - 'Please check the results of "dog vdi list".', - dst_vdiname) - elif self.QEMU_IMG_RESP_VDI_NOT_FOUND in _stderr: - LOG.error('Src Volume "%s" not found. ' - 'Please check the results of "dog vdi list".', - src_vdiname) - elif self.QEMU_IMG_RESP_SNAPSHOT_NOT_FOUND in _stderr: - LOG.error('Snapshot "%s" not found. ' - 'Please check the results of "dog vdi list".', - src_snapname) - elif self.QEMU_IMG_RESP_SIZE_TOO_LARGE in _stderr: - LOG.error('Volume size "%sG" is too large.', size) - else: - LOG.error('Failed to clone volume.(command: %s)', cmd) - - def resize(self, vdiname, size): - size = int(size) * units.Gi - try: - (_stdout, _stderr) = self._run_dog('vdi', 'resize', vdiname, size) - except SheepdogCmdError as e: - _stderr = e.kwargs['stderr'] - with excutils.save_and_reraise_exception(): - if _stderr.rstrip('\\n').endswith( - self.DOG_RESP_VDI_NOT_FOUND): - LOG.error('Failed to resize vdi. vdi not found. %s', - vdiname) - elif _stderr.startswith(self.DOG_RESP_VDI_SHRINK_NOT_SUPPORT): - LOG.error('Failed to resize vdi. ' - 'Shrinking vdi not supported. ' - 'vdi: %(vdiname)s new size: %(size)s', - {'vdiname': vdiname, 'size': size}) - elif _stderr.startswith(self.DOG_RESP_VDI_SIZE_TOO_LARGE): - LOG.error('Failed to resize vdi. ' - 'Too large volume size. ' - 'vdi: %(vdiname)s new size: %(size)s', - {'vdiname': vdiname, 'size': size}) - else: - LOG.error('Failed to resize vdi. ' - 'vdi: %(vdiname)s new size: %(size)s', - {'vdiname': vdiname, 'size': size}) - - def get_volume_stats(self): - try: - (_stdout, _stderr) = self._run_dog('node', 'info', '-r') - except SheepdogCmdError as e: - with excutils.save_and_reraise_exception(): - LOG.error('Failed to get volume status. %s', e) - return _stdout - - def get_vdi_info(self, vdiname): - # Get info of the specified vdi. - try: - (_stdout, _stderr) = self._run_dog('vdi', 'list', vdiname, '-r') - except SheepdogCmdError as e: - with excutils.save_and_reraise_exception(): - LOG.error('Failed to get vdi info. %s', e) - return _stdout - - def update_node_list(self): - try: - (_stdout, _stderr) = self._run_dog('node', 'list', '-r') - except SheepdogCmdError as e: - with excutils.save_and_reraise_exception(): - LOG.error('Failed to get node list. %s', e) - node_list = [] - stdout = _stdout.strip('\n') - for line in stdout.split('\n'): - node_list.append(line.split()[1].split(':')[0]) - self.node_list = node_list - - -class SheepdogIOWrapper(io.RawIOBase): - """File-like object with Sheepdog backend.""" - - def __init__(self, addr, port, volume, snapshot_name=None): - self._addr = addr - self._port = port - self._vdiname = volume['name'] - self._snapshot_name = snapshot_name - self._offset = 0 - # SheepdogIOWrapper instance becomes invalid if a write error occurs. - self._valid = True - - def _execute(self, cmd, data=None): - try: - # NOTE(yamada-h): processutils.execute causes busy waiting - # under eventlet. - # To avoid wasting CPU resources, it should not be used for - # the command which takes long time to execute. - # For workaround, we replace a subprocess module with - # the original one while only executing a read/write command. - _processutils_subprocess = processutils.subprocess - processutils.subprocess = eventlet.patcher.original('subprocess') - return processutils.execute(*cmd, process_input=data)[0] - except (processutils.ProcessExecutionError, OSError): - self._valid = False - msg = _('Sheepdog I/O Error, command was: "%s".') % ' '.join(cmd) - raise exception.VolumeDriverException(message=msg) - finally: - processutils.subprocess = _processutils_subprocess - - def read(self, length=None): - if not self._valid: - msg = _('An error occurred while reading volume "%s".' - ) % self._vdiname - raise exception.VolumeDriverException(message=msg) - - cmd = ['dog', 'vdi', 'read', '-a', self._addr, '-p', self._port] - if self._snapshot_name: - cmd.extend(('-s', self._snapshot_name)) - cmd.extend((self._vdiname, self._offset)) - if length: - cmd.append(length) - data = self._execute(cmd) - self._offset += len(data) - return data - - def write(self, data): - if not self._valid: - msg = _('An error occurred while writing to volume "%s".' - ) % self._vdiname - raise exception.VolumeDriverException(message=msg) - - length = len(data) - cmd = ('dog', 'vdi', 'write', '-a', self._addr, '-p', self._port, - self._vdiname, self._offset, length) - self._execute(cmd, data) - self._offset += length - return length - - def seek(self, offset, whence=0): - if not self._valid: - msg = _('An error occurred while seeking for volume "%s".' - ) % self._vdiname - raise exception.VolumeDriverException(message=msg) - - if whence == 0: - # SEEK_SET or 0 - start of the stream (the default); - # offset should be zero or positive - new_offset = offset - elif whence == 1: - # SEEK_CUR or 1 - current stream position; offset may be negative - new_offset = self._offset + offset - else: - # SEEK_END or 2 - end of the stream; offset is usually negative - # TODO(yamada-h): Support SEEK_END - raise IOError(_("Invalid argument - whence=%s not supported.") % - whence) - - if new_offset < 0: - raise IOError(_("Invalid argument - negative seek offset.")) - - self._offset = new_offset - - def tell(self): - return self._offset - - def flush(self): - pass - - def fileno(self): - """Sheepdog does not have support for fileno so we raise IOError. - - Raising IOError is recommended way to notify caller that interface is - not supported - see http://docs.python.org/2/library/io.html#io.IOBase - """ - raise IOError(_("fileno is not supported by SheepdogIOWrapper")) - - -@interface.volumedriver -class SheepdogDriver(driver.VolumeDriver): - """Executes commands relating to Sheepdog Volumes.""" - - VERSION = "1.0.0" - - # ThirdPartySystems wiki page - CI_WIKI_NAME = "Cinder_Jenkins" - - # TODO(jsbryant) Remove this driver in the 'U' release as it is no - # longer maintained. - SUPPORTED = False - - def __init__(self, *args, **kwargs): - super(SheepdogDriver, self).__init__(*args, **kwargs) - self.configuration.append_config_values(sheepdog_opts) - addr = self.configuration.sheepdog_store_address - self.port = self.configuration.sheepdog_store_port - self.stats_pattern = re.compile(r'[\w\s%]*Total\s(\d+)\s(\d+)*') - self._stats = {} - self.node_list = [addr] - self.client = SheepdogClient(self.node_list, self.port) - - @staticmethod - def get_driver_options(): - return sheepdog_opts - - def check_for_setup_error(self): - """Check cluster status and update node list.""" - self.client.check_cluster_status() - self.client.update_node_list() - - def _is_cloneable(self, image_location, image_meta): - """Check the image can be clone or not.""" - if image_location is None: - return False - - prefix = 'sheepdog://' - if not image_location.startswith(prefix): - LOG.debug("Image is not stored in sheepdog.") - return False - - if image_meta['disk_format'] != 'raw': - LOG.debug("Image clone requires image format to be " - "'raw' but image %s(%s) is '%s'.", - image_location, - image_meta['id'], - image_meta['disk_format']) - return False - - # check whether volume is stored in sheepdog - # The image location would be like - # "sheepdog://192.168.10.2:7000:Alice" - (ip, port, name) = image_location[len(prefix):].split(":", 2) - - stdout = self.client.get_vdi_info(name) - # Dog command return 0 and has a null output if the volume not exists - if stdout: - return True - else: - LOG.debug("Can not find vdi %(image)s, is not cloneable", - {'image': name}) - return False - - def clone_image(self, context, volume, - image_location, image_meta, - image_service): - """Create a volume efficiently from an existing image.""" - image_location = image_location[0] if image_location else None - if not self._is_cloneable(image_location, image_meta): - return {}, False - - volume_ref = {'name': image_meta['id'], 'size': image_meta['size']} - self.create_cloned_volume(volume, volume_ref) - self.client.resize(volume.name, volume.size) - - vol_path = self.client.local_path(volume) - return {'provider_location': vol_path}, True - - def create_cloned_volume(self, volume, src_vref): - """Clone a sheepdog volume from another volume.""" - snapshot_name = 'tmp-snap-%s-%s' % (src_vref['name'], volume.id) - snapshot = { - 'name': snapshot_name, - 'volume_name': src_vref['name'], - 'volume_size': src_vref['size'], - } - - self.client.create_snapshot(snapshot['volume_name'], snapshot_name) - - try: - self.client.clone(snapshot['volume_name'], snapshot_name, - volume.name, volume.size) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.error('Failed to create cloned volume %s.', - volume.name) - finally: - # Delete temp Snapshot - self.client.delete_snapshot(snapshot['volume_name'], snapshot_name) - - def create_volume(self, volume): - """Create a sheepdog volume.""" - self.client.create(volume.name, volume.size) - - def create_volume_from_snapshot(self, volume, snapshot): - """Create a sheepdog volume from a snapshot.""" - self.client.clone(snapshot.volume_name, snapshot.name, - volume.name, volume.size) - - def delete_volume(self, volume): - """Delete a logical volume.""" - self.client.delete(volume.name) - - def copy_image_to_volume(self, context, volume, image_service, image_id): - with image_utils.temporary_file() as tmp: - # (wenhao): we don't need to convert to raw for sheepdog. - image_utils.fetch_verify_image(context, image_service, - image_id, tmp) - - # remove the image created by import before this function. - # see volume/drivers/manager.py:_create_volume - self.client.delete(volume.name) - # convert and store into sheepdog - image_utils.convert_image(tmp, self.client.local_path(volume), - 'raw') - self.client.resize(volume.name, volume.size) - - def copy_volume_to_image(self, context, volume, image_service, image_meta): - """Copy the volume to the specified image.""" - image_id = image_meta['id'] - with image_utils.temporary_file() as tmp: - # image_utils.convert_image doesn't support "sheepdog:" source, - # so we use the qemu-img directly. - # Sheepdog volume is always raw-formatted. - cmd = ('qemu-img', - 'convert', - '-f', 'raw', - '-t', 'none', - '-O', 'raw', - self.client.local_path(volume), - tmp) - self._try_execute(*cmd) - - with open(tmp, 'rb') as image_file: - image_service.update(context, image_id, {}, image_file) - - def create_snapshot(self, snapshot): - """Create a sheepdog snapshot.""" - self.client.create_snapshot(snapshot.volume_name, snapshot.name) - - def delete_snapshot(self, snapshot): - """Delete a sheepdog snapshot.""" - self.client.delete_snapshot(snapshot.volume_name, snapshot.name) - - def ensure_export(self, context, volume): - """Safely and synchronously recreate an export for a logical volume.""" - pass - - def create_export(self, context, volume, connector): - """Export a volume.""" - pass - - def remove_export(self, context, volume): - """Remove an export for a logical volume.""" - pass - - def initialize_connection(self, volume, connector): - return { - 'driver_volume_type': 'sheepdog', - 'data': { - 'name': volume['name'], - 'hosts': [self.client.get_addr()], - 'ports': ["%d" % self.port], - } - } - - def terminate_connection(self, volume, connector, **kwargs): - pass - - def _update_volume_stats(self): - stats = {} - - backend_name = "sheepdog" - if self.configuration: - backend_name = self.configuration.safe_get('volume_backend_name') - stats["volume_backend_name"] = backend_name or 'sheepdog' - stats['vendor_name'] = 'Open Source' - stats['driver_version'] = self.VERSION - stats['storage_protocol'] = 'sheepdog' - stats['total_capacity_gb'] = 'unknown' - stats['free_capacity_gb'] = 'unknown' - stats['reserved_percentage'] = 0 - stats['QoS_support'] = False - - stdout = self.client.get_volume_stats() - m = self.stats_pattern.match(stdout) - total = float(m.group(1)) - used = float(m.group(2)) - stats['total_capacity_gb'] = total / units.Gi - stats['free_capacity_gb'] = (total - used) / units.Gi - - self._stats = stats - - def get_volume_stats(self, refresh=False): - if refresh: - self._update_volume_stats() - return self._stats - - def extend_volume(self, volume, new_size): - """Extend an Existing Volume.""" - self.client.resize(volume.name, new_size) - LOG.debug('Extend volume from %(old_size)s GB to %(new_size)s GB.', - {'old_size': volume.size, 'new_size': new_size}) diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index b965b081ce4..0a6f25e6829 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -1801,7 +1801,7 @@ class VolumeManager(manager.CleanableManager, driver_volume_type: a string to identify the type of volume. This can be used by the calling code to determine the strategy for connecting to the - volume. This could be 'iscsi', 'rbd', 'sheepdog', etc. + volume. This could be 'iscsi', 'rbd', etc. data: this is the data that the calling code will use to connect to the diff --git a/doc/source/configuration/block-storage/drivers/sheepdog-driver.rst b/doc/source/configuration/block-storage/drivers/sheepdog-driver.rst deleted file mode 100644 index 96dc4eb9b4c..00000000000 --- a/doc/source/configuration/block-storage/drivers/sheepdog-driver.rst +++ /dev/null @@ -1,51 +0,0 @@ -=============== -Sheepdog driver -=============== - -Sheepdog is an open-source distributed storage system that provides a -virtual storage pool utilizing internal disk of commodity servers. - -Sheepdog scales to several hundred nodes, and has powerful virtual disk -management features like snapshotting, cloning, rollback, and thin -provisioning. - -More information can be found on `Sheepdog -Project `__. - -This driver enables the use of Sheepdog through Qemu/KVM. - -Supported operations -~~~~~~~~~~~~~~~~~~~~ - -Sheepdog driver supports these operations: - -- Create, delete, attach, and detach volumes. - -- Create, list, and delete volume snapshots. - -- Create a volume from a snapshot. - -- Copy an image to a volume. - -- Copy a volume to an image. - -- Clone a volume. - -- Extend a volume. - -Configuration -~~~~~~~~~~~~~ - -Set the following option in the ``cinder.conf`` file: - -.. code-block:: ini - - volume_driver = cinder.volume.drivers.sheepdog.SheepdogDriver - -The following table contains the configuration options supported by the -Sheepdog driver: - -.. config-table:: - :config-target: Sheepdog - - cinder.volume.drivers.sheepdog diff --git a/doc/source/reference/support-matrix.ini b/doc/source/reference/support-matrix.ini index 7d90a5235f9..245dcb41f9d 100644 --- a/doc/source/reference/support-matrix.ini +++ b/doc/source/reference/support-matrix.ini @@ -153,9 +153,6 @@ title=RBD (Ceph) Storage Driver (RBD) [driver.seagate] title=Seagate Driver (iSCSI, FC) -[driver.sheepdog] -title=Sheepdog Storage Driver (sheepdog) - [driver.storpool] title=StorPool Storage Driver (storpool) @@ -239,7 +236,6 @@ driver.qnap=complete driver.quobyte=complete driver.rbd=complete driver.seagate=complete -driver.sheepdog=missing driver.storpool=complete driver.synology=complete driver.vrtsaccess=missing @@ -301,7 +297,6 @@ driver.qnap=complete driver.quobyte=complete driver.rbd=complete driver.seagate=complete -driver.sheepdog=complete driver.storpool=complete driver.synology=complete driver.vrtsaccess=complete @@ -363,7 +358,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=missing driver.seagate=missing -driver.sheepdog=missing driver.storpool=missing driver.synology=missing driver.vrtsaccess=missing @@ -428,7 +422,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=missing driver.seagate=missing -driver.sheepdog=missing driver.storpool=missing driver.synology=missing driver.vrtsaccess=missing @@ -492,7 +485,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=complete driver.seagate=missing -driver.sheepdog=missing driver.storpool=complete driver.synology=missing driver.vrtsaccess=missing @@ -557,7 +549,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=missing driver.seagate=missing -driver.sheepdog=missing driver.storpool=missing driver.synology=missing driver.vrtsaccess=missing @@ -621,7 +612,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=complete driver.seagate=missing -driver.sheepdog=missing driver.storpool=complete driver.synology=missing driver.vrtsaccess=missing @@ -686,7 +676,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=missing driver.seagate=missing -driver.sheepdog=missing driver.storpool=complete driver.synology=missing driver.vrtsaccess=missing @@ -751,7 +740,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=complete driver.seagate=complete -driver.sheepdog=missing driver.storpool=complete driver.synology=missing driver.vrtsaccess=missing @@ -813,7 +801,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=missing driver.seagate=missing -driver.sheepdog=missing driver.storpool=missing driver.synology=missing driver.vrtsaccess=missing @@ -879,7 +866,6 @@ driver.qnap=missing driver.quobyte=missing driver.rbd=complete driver.seagate=missing -driver.sheepdog=missing driver.storpool=missing driver.synology=missing driver.vrtsaccess=missing diff --git a/doc/source/reference/support-matrix.rst b/doc/source/reference/support-matrix.rst index 965e85f5edd..edfa25d15bd 100644 --- a/doc/source/reference/support-matrix.rst +++ b/doc/source/reference/support-matrix.rst @@ -88,5 +88,6 @@ release. * Ussuri * Huawei FusionStorage Driver - * ProphetStor Flexvisor Driver * Nimble Storage Driver + * ProphetStor Flexvisor Driver + * Sheepdog Driver diff --git a/releasenotes/notes/sheepdog-driver-removal-b63d12460e886c33.yaml b/releasenotes/notes/sheepdog-driver-removal-b63d12460e886c33.yaml new file mode 100644 index 00000000000..c88fdb633ee --- /dev/null +++ b/releasenotes/notes/sheepdog-driver-removal-b63d12460e886c33.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The Sheepdog driver was marked unsupported in the Train release and has + now been removed. All data on Sheepdog backends should be migrated to a + supported backend before upgrading your Cinder installation.