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.