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