From: JordanP Date: Tue, 18 Nov 2014 13:34:39 +0000 (+0100) Subject: Scality SOFS: implement volume backup and restore X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=23227a5c637f78905976627ecbefffa4f1d12184;p=openstack-build%2Fcinder-build.git Scality SOFS: implement volume backup and restore Add support in Scality SOFS driver for volume backup interface to backup or restore a volume using backup service specified in cinder.conf. Change-Id: I6b4b132c7aec9d0e9e558fd30036cf95d80698d1 --- diff --git a/cinder/tests/test_scality.py b/cinder/tests/test_scality.py index b00185621..b9d1529b9 100644 --- a/cinder/tests/test_scality.py +++ b/cinder/tests/test_scality.py @@ -27,6 +27,8 @@ from oslo.utils import units from cinder import context from cinder import exception from cinder.image import image_utils +from cinder.openstack.common import fileutils +from cinder.openstack.common import imageutils from cinder import test from cinder import utils from cinder.volume import configuration as conf @@ -286,3 +288,59 @@ class ScalityDriverTestCase(test.TestCase): self.mox.ReplayAll() self._driver.extend_volume(self.TEST_VOLUME, self.TEST_NEWSIZE) + + def test_backup_volume(self): + self.mox.StubOutWithMock(self._driver, 'db') + self.mox.StubOutWithMock(self._driver.db, 'volume_get') + + volume = {'id': '2', 'name': self.TEST_VOLNAME} + self._driver.db.volume_get(context, volume['id']).AndReturn(volume) + + info = imageutils.QemuImgInfo() + info.file_format = 'raw' + self.mox.StubOutWithMock(image_utils, 'qemu_img_info') + image_utils.qemu_img_info(self.TEST_VOLPATH).AndReturn(info) + + self.mox.StubOutWithMock(utils, 'temporary_chown') + mock_tempchown = self.mox.CreateMockAnything() + utils.temporary_chown(self.TEST_VOLPATH).AndReturn(mock_tempchown) + mock_tempchown.__enter__() + mock_tempchown.__exit__(None, None, None) + + self.mox.StubOutWithMock(fileutils, 'file_open') + mock_fileopen = self.mox.CreateMockAnything() + fileutils.file_open(self.TEST_VOLPATH).AndReturn(mock_fileopen) + mock_fileopen.__enter__() + mock_fileopen.__exit__(None, None, None) + + backup = {'volume_id': volume['id']} + mock_servicebackup = self.mox.CreateMockAnything() + mock_servicebackup.backup(backup, mox_lib.IgnoreArg()) + + self.mox.ReplayAll() + self._driver.backup_volume(context, backup, mock_servicebackup) + self.mox.VerifyAll() + + def test_restore_backup(self): + volume = {'id': '2', 'name': self.TEST_VOLNAME} + + self.mox.StubOutWithMock(utils, 'temporary_chown') + mock_tempchown = self.mox.CreateMockAnything() + utils.temporary_chown(self.TEST_VOLPATH).AndReturn(mock_tempchown) + mock_tempchown.__enter__() + mock_tempchown.__exit__(None, None, None) + + self.mox.StubOutWithMock(fileutils, 'file_open') + mock_fileopen = self.mox.CreateMockAnything() + fileutils.file_open(self.TEST_VOLPATH, 'wb').AndReturn(mock_fileopen) + mock_fileopen.__enter__() + mock_fileopen.__exit__(None, None, None) + + backup = {'id': 123, 'volume_id': volume['id']} + mock_servicebackup = self.mox.CreateMockAnything() + mock_servicebackup.restore(backup, volume['id'], mox_lib.IgnoreArg()) + + self.mox.ReplayAll() + self._driver.restore_backup(context, backup, volume, + mock_servicebackup) + self.mox.VerifyAll() diff --git a/cinder/volume/drivers/scality.py b/cinder/volume/drivers/scality.py index 282ac3a1b..583fa3f4a 100644 --- a/cinder/volume/drivers/scality.py +++ b/cinder/volume/drivers/scality.py @@ -26,9 +26,11 @@ from oslo.utils import units import six.moves.urllib.parse as urlparse from cinder import exception -from cinder.i18n import _ +from cinder.i18n import _, _LI from cinder.image import image_utils +from cinder.openstack.common import fileutils from cinder.openstack.common import log as logging +from cinder import utils from cinder.volume import driver @@ -283,8 +285,30 @@ class ScalityDriver(driver.VolumeDriver): 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']) + volume_local_path = self.local_path(volume) + LOG.info(_LI('Begin backup of volume %s.') % volume['name']) + + qemu_img_info = image_utils.qemu_img_info(volume_local_path) + if qemu_img_info.file_format != 'raw': + msg = _('Backup is only supported for raw-formatted ' + 'SOFS volumes.') + raise exception.InvalidVolume(msg) + + if qemu_img_info.backing_file is not None: + msg = _('Backup is only supported for SOFS volumes ' + 'without backing file.') + raise exception.InvalidVolume(msg) + + with utils.temporary_chown(volume_local_path): + with fileutils.file_open(volume_local_path) as volume_file: + backup_service.backup(backup, volume_file) def restore_backup(self, context, backup, volume, backup_service): """Restore an existing backup to a new or existing volume.""" - raise NotImplementedError() + LOG.info(_LI('Restoring backup %(backup)s to volume %(volume)s.') % + {'backup': backup['id'], 'volume': volume['name']}) + volume_local_path = self.local_path(volume) + with utils.temporary_chown(volume_local_path): + with fileutils.file_open(volume_local_path, 'wb') as volume_file: + backup_service.restore(backup, volume['id'], volume_file)