]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Implement snapshots-related features for Block Device Driver
authorYuriy Nesenenko <ynesenenko@mirantis.com>
Thu, 3 Dec 2015 17:47:16 +0000 (19:47 +0200)
committeryuriy_n <ynesenenko@mirantis.com>
Mon, 14 Dec 2015 15:13:59 +0000 (17:13 +0200)
This patch adds the create_snapshot, create_volume_from_snapshot
and delete_snapshot methods to Block Device driver.

Related blueprint: block-device-driver-minimum-features-set

Change-Id: If86dca417234ea2c58fbce4e30a1626b288de3f6

cinder/tests/unit/test_block_device.py
cinder/volume/drivers/block_device.py

index cd83c8b3089a813113fc6d4c8e18f59f5176c99a..ab7543c5bc1779ef3c0cd9546bfeb4ca38d7f420 100644 (file)
@@ -318,3 +318,100 @@ class TestBlockDeviceDriver(cinder.test.TestCase):
                                   self.drv.extend_volume, TEST_VOLUME, 2)
                 lp_mocked.assert_called_once_with(TEST_VOLUME)
                 mock_get_size.assert_called_once_with(['/dev/loop1'])
+
+    @mock.patch('cinder.volume.utils.copy_volume')
+    def test_create_snapshot(self, _copy_volume):
+        TEST_VOLUME = obj_volume.Volume(id=1,
+                                        name_id='1234',
+                                        size=1,
+                                        display_name='vol1',
+                                        status='available',
+                                        provider_location='/dev/loop1')
+        TEST_SNAP = obj_snap.Snapshot(id=1,
+                                      volume_id=1,
+                                      volume_size=1024,
+                                      provider_location='/dev/loop2',
+                                      volume=TEST_VOLUME)
+
+        with mock.patch.object(self.drv, 'find_appropriate_size_device',
+                               return_value='/dev/loop2') as fasd_mocked:
+            with mock.patch.object(self.drv, '_get_devices_sizes',
+                                   return_value={'/dev/loop2': 1024}) as \
+                    gds_mocked:
+                with mock.patch.object(self.drv,
+                                       '_update_provider_location') as \
+                        upl_mocked:
+                    volutils.copy_volume('/dev/loop1', fasd_mocked, 1024,
+                                         mock.sentinel,
+                                         execute=self.drv._execute)
+                    self.drv.create_snapshot(TEST_SNAP)
+                    fasd_mocked.assert_called_once_with(TEST_SNAP.volume_size)
+                    gds_mocked.assert_called_once_with(['/dev/loop2'])
+                    upl_mocked.assert_called_once_with(
+                        TEST_SNAP, '/dev/loop2')
+
+    def test_create_snapshot_with_not_available_volume(self):
+        TEST_VOLUME = obj_volume.Volume(id=1,
+                                        name_id='1234',
+                                        size=1,
+                                        display_name='vol1',
+                                        status='in use',
+                                        provider_location='/dev/loop1')
+        TEST_SNAP = obj_snap.Snapshot(id=1,
+                                      volume_id=1,
+                                      volume_size=1024,
+                                      provider_location='/dev/loop2',
+                                      volume=TEST_VOLUME)
+
+        self.assertRaises(cinder.exception.CinderException,
+                          self.drv.create_snapshot, TEST_SNAP)
+
+    @mock.patch('cinder.volume.utils.copy_volume')
+    def test_create_volume_from_snapshot(self, _copy_volume):
+        TEST_SNAP = obj_snap.Snapshot(volume_id=1,
+                                      volume_size=1024,
+                                      provider_location='/dev/loop1')
+        TEST_VOLUME = obj_volume.Volume(id=1,
+                                        name_id='1234',
+                                        size=1,
+                                        display_name='vol1',
+                                        provider_location='/dev/loop2')
+
+        with mock.patch.object(self.drv, 'find_appropriate_size_device',
+                               return_value='/dev/loop2') as fasd_mocked:
+            with mock.patch.object(self.drv, '_get_devices_sizes',
+                                   return_value={'/dev/loop2': 1024}) as \
+                    gds_mocked:
+                with mock.patch.object(self.drv,
+                                       '_update_provider_location') as \
+                        upl_mocked:
+                    volutils.copy_volume('/dev/loop1', fasd_mocked, 1024,
+                                         mock.sentinel,
+                                         execute=self.drv._execute)
+                    self.drv.create_volume_from_snapshot(
+                        TEST_VOLUME, TEST_SNAP)
+                    fasd_mocked.assert_called_once_with(
+                        TEST_SNAP.volume_size)
+                    gds_mocked.assert_called_once_with(['/dev/loop2'])
+                    upl_mocked.assert_called_once_with(
+                        TEST_VOLUME, '/dev/loop2')
+
+    @mock.patch('os.path.exists', return_value=True)
+    @mock.patch('cinder.volume.utils.clear_volume')
+    def test_delete_snapshot(self, _clear_volume, _exists):
+        TEST_SNAP = obj_snap.Snapshot(volume_id=1,
+                                      provider_location='/dev/loop1',
+                                      status='available')
+
+        with mock.patch.object(self.drv, 'local_path',
+                               return_value='/dev/loop1') as lp_mocked:
+            with mock.patch.object(self.drv, '_get_devices_sizes',
+                                   return_value={'/dev/loop1': 1}) as \
+                    gds_mocked:
+                volutils.clear_volume(gds_mocked, lp_mocked)
+                self.drv.delete_snapshot(TEST_SNAP)
+                lp_mocked.assert_called_once_with(TEST_SNAP)
+                gds_mocked.assert_called_once_with(['/dev/loop1'])
+
+        self.assertTrue(_exists.called)
+        self.assertTrue(_clear_volume.called)
index dfc7d58a3daf0d7bc27066dd8aba6ba30df3e7a0..8c3ecd4fd06e29fdbc435f5cc8460da458e36def 100644 (file)
@@ -229,6 +229,39 @@ class BlockDeviceDriver(driver.BaseVD, driver.LocalVD,
             LOG.error(msg, resource=volume)
             raise exception.CinderException(msg)
 
+    @utils.synchronized('block_device', external=True)
+    def create_snapshot(self, snapshot):
+        volume = snapshot.volume
+        if volume.status != 'available':
+            msg = _("Volume is not available.")
+            LOG.error(msg, resource=volume)
+            raise exception.CinderException(msg)
+
+        LOG.info(_LI('Creating volume snapshot: %s.'), snapshot.id)
+        device = self.find_appropriate_size_device(snapshot.volume_size)
+        dev_size = self._get_devices_sizes([device])
+        volutils.copy_volume(
+            self.local_path(volume), device,
+            dev_size[device],
+            self.configuration.volume_dd_blocksize,
+            execute=self._execute)
+        self._update_provider_location(snapshot, device)
+
+    def delete_snapshot(self, snapshot):
+        self._clear_block_device(snapshot)
+
+    @utils.synchronized('block_device', external=True)
+    def create_volume_from_snapshot(self, volume, snapshot):
+        LOG.info(_LI('Creating volume %s from snapshot.'), volume.id)
+        device = self.find_appropriate_size_device(snapshot.volume_size)
+        dev_size = self._get_devices_sizes([device])
+        volutils.copy_volume(
+            self.local_path(snapshot), device,
+            dev_size[device],
+            self.configuration.volume_dd_blocksize,
+            execute=self._execute)
+        self._update_provider_location(volume, device)
+
     # #######  Interface methods for DataPath (Target Driver) ########
 
     def ensure_export(self, context, volume):