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
This commit is contained in:
parent
f211c150cb
commit
15962a4d13
@ -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())
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user