From edce19f6dffee337c19cc5c8d24cf93ae37ed1b4 Mon Sep 17 00:00:00 2001 From: "MENJO, Takashi" Date: Fri, 30 Oct 2015 17:30:10 +0900 Subject: [PATCH] Sheepdog: Fix a problem about multi backend Current Sheepdog driver does not support Cinder multi backend. It can connect at most one Sheepdog cluster defined in the [DEFAULT] section in the cinder.conf. This patch makes Sheepdog driver support Cinder multi backend. I think this should be treated as bug fix rather than enhancement (i.e. adding a new feature) because Cinder itself has been supported multi backend from Grizzly. You can use multi Sheepdog clusters (for example, one of them has low-performance SATA disk and another has high-performance SSD) with cinder.conf like as below: [DEFAULT] enabled_backends=sheep0,sheep1 [sheep0] volume_driver=cinder.volume.drivers.sheepdog.SheepdogDriver sheepdog_store_address=127.0.0.1 sheepdog_store_port=7000 volume_backend_name=sheep_SATA [sheep1] volume_driver=cinder.volume.drivers.sheepdog.SheepdogDriver sheepdog_store_address=127.0.0.1 sheepdog_store_port=7001 volume_backend_name=sheep_SSD DocImpact Closes-Bug: #1514353 Change-Id: I484430e52aa0661bf1cddf9939ad308e274317e2 --- cinder/tests/unit/test_sheepdog.py | 41 +++++++++++++++++++++++------- cinder/volume/drivers/sheepdog.py | 39 +++++++++++++++------------- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/cinder/tests/unit/test_sheepdog.py b/cinder/tests/unit/test_sheepdog.py index 3b909e61bae..36eaf5c78d4 100644 --- a/cinder/tests/unit/test_sheepdog.py +++ b/cinder/tests/unit/test_sheepdog.py @@ -284,9 +284,9 @@ class SheepdogIOWrapperTestCase(test.TestCase): self.snapshot_name = 'snapshot-bf452d80-068a-43d7-ba9f-196cf47bd0be' self.vdi_wrapper = sheepdog.SheepdogIOWrapper( - self.volume) + SHEEP_ADDR, SHEEP_PORT, self.volume) self.snapshot_wrapper = sheepdog.SheepdogIOWrapper( - self.volume, self.snapshot_name) + SHEEP_ADDR, SHEEP_PORT, self.volume, self.snapshot_name) self.execute = mock.MagicMock() self.mock_object(processutils, 'execute', self.execute) @@ -321,7 +321,8 @@ class SheepdogIOWrapperTestCase(test.TestCase): def test_read_vdi(self): self.vdi_wrapper.read() self.execute.assert_called_once_with( - 'dog', 'vdi', 'read', self.volume['name'], 0, process_input=None) + '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 @@ -334,7 +335,7 @@ class SheepdogIOWrapperTestCase(test.TestCase): self.vdi_wrapper.write(data) self.execute.assert_called_once_with( - 'dog', 'vdi', 'write', + '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()) @@ -347,8 +348,8 @@ class SheepdogIOWrapperTestCase(test.TestCase): def test_read_snapshot(self): self.snapshot_wrapper.read() self.execute.assert_called_once_with( - 'dog', 'vdi', 'read', '-s', self.snapshot_name, - self.volume['name'], 0, + 'dog', 'vdi', 'read', '-a', SHEEP_ADDR, '-p', SHEEP_PORT, + '-s', self.snapshot_name, self.volume['name'], 0, process_input=None) def test_seek(self): @@ -389,6 +390,8 @@ class SheepdogClientTestCase(test.TestCase): 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 @@ -1200,6 +1203,8 @@ class SheepdogDriverTestCase(test.TestCase): 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 @@ -1282,7 +1287,10 @@ class SheepdogDriverTestCase(test.TestCase): '-f', 'raw', '-t', 'none', '-O', 'raw', - 'sheepdog:%s' % fake_volume['name'], + 'sheepdog:%s:%s:%s' % ( + self._addr, + self._port, + fake_volume['name']), mock.ANY) fake_try_execute.assert_called_once_with(*expected_cmd) fake_image_service_update.assert_called_once_with( @@ -1371,8 +1379,10 @@ class SheepdogDriverTestCase(test.TestCase): image_location, image_meta, image_service) self.assertTrue(cloned) - self.assertEqual("sheepdog:%s" % - self.test_data.TEST_CLONED_VOLUME.name, + 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): @@ -1427,6 +1437,19 @@ class SheepdogDriverTestCase(test.TestCase): 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.assertDictMatch(expected, actual) + @mock.patch.object(sheepdog.SheepdogClient, 'resize') @mock.patch.object(sheepdog, 'LOG') def test_extend_volume(self, fake_logger, fake_execute): diff --git a/cinder/volume/drivers/sheepdog.py b/cinder/volume/drivers/sheepdog.py index b41c1b26e77..384367ea55c 100644 --- a/cinder/volume/drivers/sheepdog.py +++ b/cinder/volume/drivers/sheepdog.py @@ -338,7 +338,9 @@ class SheepdogClient(object): class SheepdogIOWrapper(io.RawIOBase): """File-like object with Sheepdog backend.""" - def __init__(self, volume, snapshot_name=None): + 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 @@ -369,7 +371,7 @@ class SheepdogIOWrapper(io.RawIOBase): ) % self._vdiname raise exception.VolumeDriverException(message=msg) - cmd = ['dog', 'vdi', 'read'] + 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)) @@ -386,7 +388,8 @@ class SheepdogIOWrapper(io.RawIOBase): raise exception.VolumeDriverException(message=msg) length = len(data) - cmd = ('dog', 'vdi', 'write', self._vdiname, self._offset, length) + cmd = ('dog', 'vdi', 'write', '-a', self._addr, '-p', self._port, + self._vdiname, self._offset, length) self._execute(cmd, data) self._offset += length return length @@ -437,8 +440,10 @@ class SheepdogDriver(driver.VolumeDriver): def __init__(self, *args, **kwargs): super(SheepdogDriver, self).__init__(*args, **kwargs) - self.client = SheepdogClient(CONF.sheepdog_store_address, - CONF.sheepdog_store_port) + self.configuration.append_config_values(sheepdog_opts) + self.addr = self.configuration.sheepdog_store_address + self.port = self.configuration.sheepdog_store_port + self.client = SheepdogClient(self.addr, self.port) self.stats_pattern = re.compile(r'[\w\s%]*Total\s(\d+)\s(\d+)*') self._stats = {} @@ -543,13 +548,7 @@ class SheepdogDriver(driver.VolumeDriver): # see volume/drivers/manager.py:_create_volume self.client.delete(volume.name) # convert and store into sheepdog - image_utils.convert_image( - tmp, - 'sheepdog:%(addr)s:%(port)d:%(name)s' % { - 'addr': CONF.sheepdog_store_address, - 'port': CONF.sheepdog_store_port, - 'name': volume['name']}, - 'raw') + image_utils.convert_image(tmp, self.local_path(volume), 'raw') self.client.resize(volume.name, volume.size) def copy_volume_to_image(self, context, volume, image_service, image_meta): @@ -564,7 +563,7 @@ class SheepdogDriver(driver.VolumeDriver): '-f', 'raw', '-t', 'none', '-O', 'raw', - 'sheepdog:%s' % volume['name'], + self.local_path(volume), tmp) self._try_execute(*cmd) @@ -580,7 +579,10 @@ class SheepdogDriver(driver.VolumeDriver): self.client.delete_snapshot(snapshot.volume_name, snapshot.name) def local_path(self, volume): - return "sheepdog:%s" % volume['name'] + return "sheepdog:%(addr)s:%(port)s:%(name)s" % { + 'addr': self.addr, + 'port': self.port, + 'name': volume['name']} def ensure_export(self, context, volume): """Safely and synchronously recreate an export for a logical volume.""" @@ -598,7 +600,9 @@ class SheepdogDriver(driver.VolumeDriver): return { 'driver_volume_type': 'sheepdog', 'data': { - 'name': volume['name'] + 'name': volume['name'], + 'hosts': [self.addr], + 'ports': ["%d" % self.port], } } @@ -667,12 +671,13 @@ class SheepdogDriver(driver.VolumeDriver): raise exception.SheepdogError(reason=msg) try: - sheepdog_fd = SheepdogIOWrapper(src_volume, temp_snapshot_name) + sheepdog_fd = SheepdogIOWrapper(self.addr, self.port, + src_volume, temp_snapshot_name) backup_service.backup(backup, sheepdog_fd) finally: self.client.delete_snapshot(src_volume.name, temp_snapshot_name) def restore_backup(self, context, backup, volume, backup_service): """Restore an existing backup to a new or existing volume.""" - sheepdog_fd = SheepdogIOWrapper(volume) + sheepdog_fd = SheepdogIOWrapper(self.addr, self.port, volume) backup_service.restore(backup, volume['id'], sheepdog_fd)