]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Sheepdog: Fix a problem about multi backend
authorMENJO, Takashi <menjo.takashi@lab.ntt.co.jp>
Fri, 30 Oct 2015 08:30:10 +0000 (17:30 +0900)
committerMENJO, Takashi <menjo.takashi@lab.ntt.co.jp>
Fri, 13 Nov 2015 07:57:42 +0000 (16:57 +0900)
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
cinder/volume/drivers/sheepdog.py

index 3b909e61bae4fd641c1b652e6961080bd8a6df35..36eaf5c78d4baea7aca3da98de17a3381155a8bc 100644 (file)
@@ -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):
index b41c1b26e776caf34dd7a0a424621316ed85d546..384367ea55c0efc7b943502cd407ec1a624aafcf 100644 (file)
@@ -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)