From 15962a4d13334b2265120e67ab4222b119e95c3e Mon Sep 17 00:00:00 2001 From: Stephen Mulcahy Date: Mon, 4 Mar 2013 16:10:34 +0000 Subject: [PATCH] swift backup service checks version during restore Modified swift backup service to check metadata version during restore and raise an error if the backup version isn't a version that the service knows how to handle. The versions which can be handled are described in a dictionary mapping versions to methods which can handle them. This will facilitate graceful handling of newer backup formats by the swift backup service when we introduce changes. Fixes bug: 1136174 Change-Id: Id7d05848fd448ce21f641e5cd6945477702cbe38 --- cinder/backup/services/swift.py | 55 +++++++++++++++--------- cinder/tests/backup/fake_swift_client.py | 5 ++- cinder/tests/test_backup_swift.py | 11 +++++ 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/cinder/backup/services/swift.py b/cinder/backup/services/swift.py index 8ed568aba..f88202ab9 100644 --- a/cinder/backup/services/swift.py +++ b/cinder/backup/services/swift.py @@ -78,6 +78,7 @@ class SwiftBackupService(base.Base): """Provides backup, restore and delete of backup objects within Swift.""" SERVICE_VERSION = '1.0.0' + SERVICE_VERSION_MAPPING = {'1.0.0': '_restore_v1'} def _get_compressor(self, algorithm): try: @@ -194,11 +195,10 @@ class SwiftBackupService(base.Base): (resp, body) = self.conn.get_object(container, filename) metadata = json.loads(body) LOG.debug(_('_read_metadata finished (%s)') % metadata) - return metadata['objects'] + return metadata def backup(self, backup, volume_file): - """Backup the given volume to swift using the given backup metadata. - """ + """Backup the given volume to swift using the given backup metadata.""" backup_id = backup['id'] volume_id = backup['volume_id'] volume = self.db.volume_get(self.context, volume_id) @@ -275,32 +275,20 @@ class SwiftBackupService(base.Base): object_id}) LOG.debug(_('backup %s finished.') % backup_id) - def restore(self, backup, volume_id, volume_file): - """Restore the given volume backup from swift. - """ + def _restore_v1(self, backup, volume_id, metadata, volume_file): + """Restore a v1 swift volume backup from swift.""" backup_id = backup['id'] + LOG.debug(_('v1 swift volume backup restore of %s started'), backup_id) container = backup['container'] - volume = self.db.volume_get(self.context, volume_id) - volume_size = volume['size'] - backup_size = backup['size'] - - object_prefix = backup['service_metadata'] - LOG.debug(_('starting restore of backup %(object_prefix)s from swift' - ' container: %(container)s, to volume %(volume_id)s, ' - 'backup: %(backup_id)s') % locals()) - swift_object_names = self._generate_object_names(backup) - try: - metadata_objects = self._read_metadata(backup) - except socket.error as err: - raise exception.SwiftConnectionFailed(reason=str(err)) + metadata_objects = metadata['objects'] metadata_object_names = [] for metadata_object in metadata_objects: metadata_object_names.extend(metadata_object.keys()) LOG.debug(_('metadata_object_names = %s') % metadata_object_names) prune_list = [self._metadata_filename(backup)] swift_object_names = [swift_object_name for swift_object_name in - swift_object_names if swift_object_name - not in prune_list] + self._generate_object_names(backup) + if swift_object_name not in prune_list] if sorted(swift_object_names) != sorted(metadata_object_names): err = _('restore_backup aborted, actual swift object list in ' 'swift does not match object list stored in metadata') @@ -332,6 +320,31 @@ class SwiftBackupService(base.Base): # threads can run, allowing for among other things the service # status to be updated eventlet.sleep(0) + LOG.debug(_('v1 swift volume backup restore of %s finished'), + backup_id) + + def restore(self, backup, volume_id, volume_file): + """Restore the given volume backup from swift.""" + backup_id = backup['id'] + container = backup['container'] + object_prefix = backup['service_metadata'] + LOG.debug(_('starting restore of backup %(object_prefix)s from swift' + ' container: %(container)s, to volume %(volume_id)s, ' + 'backup: %(backup_id)s') % locals()) + try: + metadata = self._read_metadata(backup) + except socket.error as err: + raise exception.SwiftConnectionFailed(reason=str(err)) + metadata_version = metadata['version'] + LOG.debug(_('Restoring swift backup version %s'), metadata_version) + try: + restore_func = getattr(self, self.SERVICE_VERSION_MAPPING.get( + metadata_version)) + except TypeError: + err = (_('No support to restore swift backup version %s') + % metadata_version) + raise exception.InvalidBackup(reason=err) + restore_func(backup, volume_id, metadata, volume_file) LOG.debug(_('restore %(backup_id)s to %(volume_id)s finished.') % locals()) diff --git a/cinder/tests/backup/fake_swift_client.py b/cinder/tests/backup/fake_swift_client.py index 0093f1194..ff7424679 100644 --- a/cinder/tests/backup/fake_swift_client.py +++ b/cinder/tests/backup/fake_swift_client.py @@ -76,7 +76,10 @@ class FakeSwiftConnection(object): if 'metadata' in name: fake_object_header = None metadata = {} - metadata['version'] = '1.0.0' + if container == 'unsupported_version': + metadata['version'] = '9.9.9' + else: + metadata['version'] = '1.0.0' metadata['backup_id'] = 123 metadata['volume_id'] = 123 metadata['backup_name'] = 'fake backup' diff --git a/cinder/tests/test_backup_swift.py b/cinder/tests/test_backup_swift.py index d5dc56475..013641729 100644 --- a/cinder/tests/test_backup_swift.py +++ b/cinder/tests/test_backup_swift.py @@ -161,6 +161,17 @@ class BackupSwiftTestCase(test.TestCase): service.restore, backup, '1234-5678-1234-8888', volume_file) + def test_restore_unsupported_version(self): + container_name = 'unsupported_version' + self._create_backup_db_entry(container=container_name) + service = SwiftBackupService(self.ctxt) + + with tempfile.NamedTemporaryFile() as volume_file: + backup = db.backup_get(self.ctxt, 123) + self.assertRaises(exception.InvalidBackup, + service.restore, + backup, '1234-5678-1234-8888', volume_file) + def test_delete(self): self._create_backup_db_entry() service = SwiftBackupService(self.ctxt) -- 2.45.2