]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Generic backup_volume and restore_backup functions
authorRonen Kat <ronenkat@il.ibm.com>
Wed, 7 Aug 2013 12:37:07 +0000 (15:37 +0300)
committerRonen Kat <ronenkat@il.ibm.com>
Tue, 20 Aug 2013 18:02:40 +0000 (21:02 +0300)
Add implementation for backup_volume and restore_backup to the
VolumeDriver class, which uses brick for attach and detach.
Add default NotImplmeneted implmentation for non block drivers:
remotefs, coraid, gpfs, scality, sheepdog and sm.

Change-Id: I291390a58608b14f0deea703fc5cec3e34b965cd

cinder/tests/test_volume.py
cinder/volume/driver.py
cinder/volume/drivers/gpfs.py
cinder/volume/drivers/nfs.py
cinder/volume/drivers/scality.py
cinder/volume/drivers/sheepdog.py
cinder/volume/drivers/xenapi/sm.py

index aa91f3d9e0cf98c870d9cc25b10a223fe205ec24..d7b3665e1dfb03092d427779245962f6e4172984 100644 (file)
@@ -29,6 +29,8 @@ import tempfile
 import mox
 from oslo.config import cfg
 
+from cinder.backup import driver as backup_driver
+from cinder.brick.initiator import connector as brick_conn
 from cinder.brick.iscsi import iscsi
 from cinder.brick.local_dev import lvm as brick_lvm
 from cinder import context
@@ -36,6 +38,7 @@ from cinder import db
 from cinder import exception
 from cinder.image import image_utils
 from cinder import keymgr
+from cinder.openstack.common import fileutils
 from cinder.openstack.common import importutils
 from cinder.openstack.common.notifier import api as notifier_api
 from cinder.openstack.common.notifier import test_notifier
@@ -47,6 +50,8 @@ from cinder.tests.brick.fake_lvm import FakeBrickLVM
 from cinder.tests import conf_fixture
 from cinder.tests.image import fake as fake_image
 from cinder.tests.keymgr import fake as fake_keymgr
+from cinder.tests import utils as tests_utils
+from cinder import utils
 import cinder.volume
 from cinder.volume import configuration as conf
 from cinder.volume import driver
@@ -55,7 +60,6 @@ from cinder.volume.flows import create_volume
 from cinder.volume import rpcapi as volume_rpcapi
 from cinder.volume import utils as volutils
 
-
 QUOTAS = quota.QUOTAS
 
 CONF = cfg.CONF
@@ -1735,6 +1739,78 @@ class DriverTestCase(test.TestCase):
             self.volume.delete_volume(self.context, volume_id)
 
 
+class GenericVolumeDriverTestCase(DriverTestCase):
+    """Test case for VolumeDriver."""
+    driver_name = "cinder.tests.fake_driver.LoggingVolumeDriver"
+
+    def test_backup_volume(self):
+        vol = tests_utils.create_volume(self.context)
+        backup = {'volume_id': vol['id']}
+        properties = {}
+        attach_info = {'device': {'path': '/dev/null'}}
+        backup_service = self.mox.CreateMock(backup_driver.BackupDriver)
+        root_helper = 'sudo cinder-rootwrap None'
+        self.mox.StubOutWithMock(self.volume.driver.db, 'volume_get')
+        self.mox.StubOutWithMock(cinder.brick.initiator.connector,
+                                 'get_connector_properties')
+        self.mox.StubOutWithMock(self.volume.driver, '_attach_volume')
+        self.mox.StubOutWithMock(os, 'getuid')
+        self.mox.StubOutWithMock(utils, 'execute')
+        self.mox.StubOutWithMock(fileutils, 'file_open')
+        self.mox.StubOutWithMock(self.volume.driver, '_detach_volume')
+        self.mox.StubOutWithMock(self.volume.driver, 'terminate_connection')
+
+        self.volume.driver.db.volume_get(self.context, vol['id']).\
+            AndReturn(vol)
+        cinder.brick.initiator.connector.\
+            get_connector_properties(root_helper).AndReturn(properties)
+        self.volume.driver._attach_volume(self.context, vol, properties).\
+            AndReturn(attach_info)
+        os.getuid()
+        utils.execute('chown', None, '/dev/null', run_as_root=True)
+        f = fileutils.file_open('/dev/null').AndReturn(file('/dev/null'))
+        backup_service.backup(backup, f)
+        utils.execute('chown', 0, '/dev/null', run_as_root=True)
+        self.volume.driver._detach_volume(attach_info)
+        self.volume.driver.terminate_connection(vol, properties)
+        self.mox.ReplayAll()
+        self.volume.driver.backup_volume(self.context, backup, backup_service)
+        self.mox.UnsetStubs()
+
+    def test_restore_backup(self):
+        vol = tests_utils.create_volume(self.context)
+        backup = {'volume_id': vol['id'],
+                  'id': 'backup-for-%s' % vol['id']}
+        properties = {}
+        attach_info = {'device': {'path': '/dev/null'}}
+        root_helper = 'sudo cinder-rootwrap None'
+        backup_service = self.mox.CreateMock(backup_driver.BackupDriver)
+        self.mox.StubOutWithMock(cinder.brick.initiator.connector,
+                                 'get_connector_properties')
+        self.mox.StubOutWithMock(self.volume.driver, '_attach_volume')
+        self.mox.StubOutWithMock(os, 'getuid')
+        self.mox.StubOutWithMock(utils, 'execute')
+        self.mox.StubOutWithMock(fileutils, 'file_open')
+        self.mox.StubOutWithMock(self.volume.driver, '_detach_volume')
+        self.mox.StubOutWithMock(self.volume.driver, 'terminate_connection')
+
+        cinder.brick.initiator.connector.\
+            get_connector_properties(root_helper).AndReturn(properties)
+        self.volume.driver._attach_volume(self.context, vol, properties).\
+            AndReturn(attach_info)
+        os.getuid()
+        utils.execute('chown', None, '/dev/null', run_as_root=True)
+        f = fileutils.file_open('/dev/null', 'wb').AndReturn(file('/dev/null'))
+        backup_service.restore(backup, vol['id'], f)
+        utils.execute('chown', 0, '/dev/null', run_as_root=True)
+        self.volume.driver._detach_volume(attach_info)
+        self.volume.driver.terminate_connection(vol, properties)
+        self.mox.ReplayAll()
+        self.volume.driver.restore_backup(self.context, backup, vol,
+                                          backup_service)
+        self.mox.UnsetStubs()
+
+
 class LVMISCSIVolumeDriverTestCase(DriverTestCase):
     """Test case for VolumeDriver"""
     driver_name = "cinder.volume.drivers.lvm.LVMISCSIDriver"
index 587ca578bfe73b5fbd9a246447ac4e58d933175a..e45fd919098ad20fe7c4bc0a4a582f8167844ad5 100644 (file)
@@ -29,6 +29,7 @@ from cinder.brick.initiator import connector as initiator
 from cinder import exception
 from cinder.image import image_utils
 from cinder.openstack.common import excutils
+from cinder.openstack.common import fileutils
 from cinder.openstack.common import log as logging
 from cinder import utils
 from cinder.volume import rpcapi as volume_rpcapi
@@ -372,11 +373,45 @@ class VolumeDriver(object):
 
     def backup_volume(self, context, backup, backup_service):
         """Create a new backup from an existing volume."""
-        raise NotImplementedError()
+        volume = self.db.volume_get(context, backup['volume_id'])
+
+        LOG.debug(_('Creating a new backup for volume %s.') %
+                  volume['name'])
+
+        root_helper = 'sudo cinder-rootwrap %s' % CONF.rootwrap_config
+        properties = initiator.get_connector_properties(root_helper)
+        attach_info = self._attach_volume(context, volume, properties)
+
+        try:
+            volume_path = attach_info['device']['path']
+            with utils.temporary_chown(volume_path):
+                with fileutils.file_open(volume_path) as volume_file:
+                    backup_service.backup(backup, volume_file)
+
+        finally:
+            self._detach_volume(attach_info)
+            self.terminate_connection(volume, properties)
 
     def restore_backup(self, context, backup, volume, backup_service):
         """Restore an existing backup to a new or existing volume."""
-        raise NotImplementedError()
+        LOG.debug(_('Restoring backup %(backup)s to '
+                    'volume %(volume)s.') %
+                  {'backup': backup['id'],
+                   'volume': volume['name']})
+
+        root_helper = 'sudo cinder-rootwrap %s' % CONF.rootwrap_config
+        properties = initiator.get_connector_properties(root_helper)
+        attach_info = self._attach_volume(context, volume, properties)
+
+        try:
+            volume_path = attach_info['device']['path']
+            with utils.temporary_chown(volume_path):
+                with fileutils.file_open(volume_path, 'wb') as volume_file:
+                    backup_service.restore(backup, volume['id'], volume_file)
+
+        finally:
+            self._detach_volume(attach_info)
+            self.terminate_connection(volume, properties)
 
     def clear_download(self, context, volume):
         """Clean up after an interrupted image copy."""
index b0c76b46a3d7dc7ee7838b050b3fc6b52b4ca145..1ccbb11e69ac839b47e5498a34f5b20e76207057 100644 (file)
@@ -427,6 +427,14 @@ class GPFSDriver(driver.VolumeDriver):
                                   image_meta,
                                   self.local_path(volume))
 
+    def backup_volume(self, context, backup, backup_service):
+        """Create a new backup from an existing volume."""
+        raise NotImplementedError()
+
+    def restore_backup(self, context, backup, volume, backup_service):
+        """Restore an existing backup to a new or existing volume."""
+        raise NotImplementedError()
+
     def _mkfs(self, volume, fs, label=None):
         if fs == 'swap':
             cmd = ['mkswap']
index 98bda30645073b6b1b54c3fc35ffd5bc9d068b64..8e339f32f767a133d557a01f66fad5dab611175e 100644 (file)
@@ -340,6 +340,14 @@ class RemoteFsDriver(driver.VolumeDriver):
     def _ensure_share_mounted(self, nfs_share):
         raise NotImplementedError()
 
+    def backup_volume(self, context, backup, backup_service):
+        """Create a new backup from an existing volume."""
+        raise NotImplementedError()
+
+    def restore_backup(self, context, backup, volume, backup_service):
+        """Restore an existing backup to a new or existing volume."""
+        raise NotImplementedError()
+
 
 class NfsDriver(RemoteFsDriver):
     """NFS based cinder driver. Creates file on NFS share for using it
index 31e75bd2642fd7ca2fa4dff5d40712ce8f8c9291..10ee48192abc32d4e56de4bda1b1260398dfd205 100644 (file)
@@ -272,3 +272,11 @@ class ScalityDriver(driver.VolumeDriver):
         """Extend an existing volume."""
         self._create_file(self.local_path(volume),
                           self._size_bytes(new_size))
+
+    def backup_volume(self, context, backup, backup_service):
+        """Create a new backup from an existing volume."""
+        raise NotImplementedError()
+
+    def restore_backup(self, context, backup, volume, backup_service):
+        """Restore an existing backup to a new or existing volume."""
+        raise NotImplementedError()
index b3bda9c7df377898ea823041a807fdd5ac688502..92033c3244b8092156be8803c61a6d44cf917946 100644 (file)
@@ -198,3 +198,11 @@ class SheepdogDriver(driver.VolumeDriver):
 
         LOG.debug(_("Extend volume from %(old_size) to %(new_size)"),
                   {'old_size': old_size, 'new_size': new_size})
+
+    def backup_volume(self, context, backup, backup_service):
+        """Create a new backup from an existing volume."""
+        raise NotImplementedError()
+
+    def restore_backup(self, context, backup, volume, backup_service):
+        """Restore an existing backup to a new or existing volume."""
+        raise NotImplementedError()
index 7df2b1b9f58e92c44c0585501eeee1a3716e0e66..c2f53777d92a2415d10a3ac6f89d29817759f2cc 100644 (file)
@@ -261,3 +261,11 @@ class XenAPINFSDriver(driver.VolumeDriver):
             self._stats = data
 
         return self._stats
+
+    def backup_volume(self, context, backup, backup_service):
+        """Create a new backup from an existing volume."""
+        raise NotImplementedError()
+
+    def restore_backup(self, context, backup, volume, backup_service):
+        """Restore an existing backup to a new or existing volume."""
+        raise NotImplementedError()