Add restore_volume_id in backup

This patch is to add restore_volume_id in backup object.
When restoring a volume from a backup, it saves the
volume in backup object.

Currently volume service and backup service are in same host.
When backup service starts, it does cleanup tasks on both
backups and volumes on current host.

But with bp scalable-backup-service, backup service and
volume services can run on different hosts. When doing cleanup
tasks, we need to find out backing-up and restoring volumes
related to the backups on current host. Backing-up volumes can
be found with field backup.volume_id. Restoring volumes are found
by new field backup.restore_volume_id.

Change-Id: I757be7a5e47fc366c181400587b5a61fe3709a0b
Partial-Implements: bp scalable-backup-service
Co-Authored-By: Tom Barron <tpb@dyncloud.net>
This commit is contained in:
LisaLi 2015-12-24 11:16:26 +08:00
parent 54991b1086
commit 4c83280125
10 changed files with 60 additions and 5 deletions

View File

@ -365,8 +365,9 @@ class API(base.Base):
# Setting the status here rather than setting at start and unrolling
# for each error condition, it should be a very small window
backup.status = fields.BackupStatus.RESTORING
backup.restore_volume_id = volume.id
backup.save()
volume_host = volume_utils.extract_host(volume['host'], 'host')
volume_host = volume_utils.extract_host(volume.host, 'host')
self.db.volume_update(context, volume_id, {'status':
'restoring-backup'})

View File

@ -0,0 +1,26 @@
# Copyright (c) 2015 Intel Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from sqlalchemy import Column, MetaData, String, Table
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
backups = Table('backups', meta, autoload=True)
restore_volume_id = Column('restore_volume_id', String(length=36))
backups.create_column(restore_volume_id)

View File

@ -527,6 +527,7 @@ class Backup(BASE, CinderBase):
num_dependent_backups = Column(Integer)
snapshot_id = Column(String(36))
data_timestamp = Column(DateTime)
restore_volume_id = Column(String(36))
@validates('fail_reason')
def validate_fail_reason(self, key, fail_reason):

View File

@ -38,7 +38,8 @@ class Backup(base.CinderPersistentObject, base.CinderObject,
# is_incremental and has_dependent_backups.
# Version 1.2: Add new field snapshot_id and data_timestamp.
# Version 1.3: Changed 'status' field to use BackupStatusField
VERSION = '1.3'
# Version 1.4: Add restore_volume_id
VERSION = '1.4'
fields = {
'id': fields.UUIDField(),
@ -70,6 +71,7 @@ class Backup(base.CinderPersistentObject, base.CinderObject,
'num_dependent_backups': fields.IntegerField(),
'snapshot_id': fields.StringField(nullable=True),
'data_timestamp': fields.DateTimeField(nullable=True),
'restore_volume_id': fields.StringField(nullable=True),
}
obj_extra_fields = ['name', 'is_incremental', 'has_dependent_backups']

View File

@ -97,6 +97,7 @@ OBJ_VERSIONS.add('1.0', {'Backup': '1.3', 'BackupImport': '1.3',
'ConsistencyGroupList': '1.1', 'Service': '1.1',
'Volume': '1.3', 'VolumeTypeList': '1.1'})
OBJ_VERSIONS.add('1.1', {'Service': '1.2', 'ServiceList': '1.1'})
OBJ_VERSIONS.add('1.2', {'Backup': '1.4', 'BackupImport': '1.4'})
class CinderObjectRegistry(base.VersionedObjectRegistry):

View File

@ -37,6 +37,7 @@ fake_backup = {
'temp_snapshot_id': None,
'snapshot_id': None,
'data_timestamp': None,
'restore_volume_id': None,
}
@ -94,6 +95,11 @@ class TestBackup(test_objects.BaseObjectsTestCase):
snapshot_id='2')
self.assertEqual('2', backup.snapshot_id)
def test_obj_field_restore_volume_id(self):
backup = objects.Backup(context=self.context,
restore_volume_id='2')
self.assertEqual('2', backup.restore_volume_id)
def test_import_record(self):
utils.replace_obj_loader(self, objects.Backup)
backup = objects.Backup(context=self.context, id=1, parent_id=None,

View File

@ -21,8 +21,8 @@ from cinder import test
# NOTE: The hashes in this list should only be changed if they come with a
# corresponding version bump in the affected objects.
object_data = {
'Backup': '1.3-2e63492190bbbc85c0e5bea328cd38f7',
'BackupImport': '1.3-2e63492190bbbc85c0e5bea328cd38f7',
'Backup': '1.4-1002c50b6e31938583c95c4c4889286c',
'BackupImport': '1.4-1002c50b6e31938583c95c4c4889286c',
'BackupList': '1.0-24591dabe26d920ce0756fe64cd5f3aa',
'CGSnapshot': '1.0-190da2a2aa9457edc771d888f7d225c4',
'CGSnapshotList': '1.0-e8c3f4078cd0ee23487b34d173eec776',

View File

@ -1283,3 +1283,15 @@ class BackupAPITestCase(BaseBackupTest):
description="test backup description",
volume_id=volume_id,
container='volumebackups')
@mock.patch('cinder.backup.rpcapi.BackupAPI.restore_backup')
def test_restore_volume(self,
mock_rpcapi_restore):
ctxt = context.RequestContext('fake', 'fake')
volume_id = self._create_volume_db_entry(status='available',
size=1)
backup = self._create_backup_db_entry(size=1,
status='available')
self.api.restore(ctxt, backup.id, volume_id)
backup = objects.Backup.get_by_id(ctxt, backup.id)
self.assertEqual(volume_id, backup.restore_volume_id)

View File

@ -1903,7 +1903,8 @@ class DBAPIBackupTestCase(BaseTest):
'temp_volume_id': 'temp_volume_id',
'temp_snapshot_id': 'temp_snapshot_id',
'num_dependent_backups': 0,
'snapshot_id': 'snapshot_id', }
'snapshot_id': 'snapshot_id',
'restore_volume_id': 'restore_volume_id'}
if one:
return base_values

View File

@ -725,6 +725,11 @@ class MigrationsMixin(test_migrations.WalkVersionsMixin):
self.assertIsInstance(volume_type_projects.c.id.type,
self.INTEGER_TYPE)
def _check_064(self, engine, data):
backups = db_utils.get_table(engine, 'backups')
self.assertIsInstance(backups.c.restore_volume_id.type,
self.VARCHAR_TYPE)
def test_walk_versions(self):
self.walk_versions(False, False)