From 39fc6c6aea6192ae005194e282cfe4749e0db07d Mon Sep 17 00:00:00 2001 From: Ronen Kat Date: Wed, 7 Aug 2013 15:37:07 +0300 Subject: [PATCH] Generic backup_volume and restore_backup functions 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 | 78 +++++++++++++++++++++++++++++- cinder/volume/driver.py | 39 ++++++++++++++- cinder/volume/drivers/gpfs.py | 8 +++ cinder/volume/drivers/nfs.py | 8 +++ cinder/volume/drivers/scality.py | 8 +++ cinder/volume/drivers/sheepdog.py | 8 +++ cinder/volume/drivers/xenapi/sm.py | 8 +++ 7 files changed, 154 insertions(+), 3 deletions(-) diff --git a/cinder/tests/test_volume.py b/cinder/tests/test_volume.py index aa91f3d9e..d7b3665e1 100644 --- a/cinder/tests/test_volume.py +++ b/cinder/tests/test_volume.py @@ -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" diff --git a/cinder/volume/driver.py b/cinder/volume/driver.py index 587ca578b..e45fd9190 100644 --- a/cinder/volume/driver.py +++ b/cinder/volume/driver.py @@ -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.""" diff --git a/cinder/volume/drivers/gpfs.py b/cinder/volume/drivers/gpfs.py index b0c76b46a..1ccbb11e6 100644 --- a/cinder/volume/drivers/gpfs.py +++ b/cinder/volume/drivers/gpfs.py @@ -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'] diff --git a/cinder/volume/drivers/nfs.py b/cinder/volume/drivers/nfs.py index 98bda3064..8e339f32f 100644 --- a/cinder/volume/drivers/nfs.py +++ b/cinder/volume/drivers/nfs.py @@ -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 diff --git a/cinder/volume/drivers/scality.py b/cinder/volume/drivers/scality.py index 31e75bd26..10ee48192 100644 --- a/cinder/volume/drivers/scality.py +++ b/cinder/volume/drivers/scality.py @@ -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() diff --git a/cinder/volume/drivers/sheepdog.py b/cinder/volume/drivers/sheepdog.py index b3bda9c7d..92033c324 100644 --- a/cinder/volume/drivers/sheepdog.py +++ b/cinder/volume/drivers/sheepdog.py @@ -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() diff --git a/cinder/volume/drivers/xenapi/sm.py b/cinder/volume/drivers/xenapi/sm.py index 7df2b1b9f..c2f53777d 100644 --- a/cinder/volume/drivers/xenapi/sm.py +++ b/cinder/volume/drivers/xenapi/sm.py @@ -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() -- 2.45.2