From: LisaLi Date: Thu, 24 Dec 2015 03:16:26 +0000 (+0800) Subject: Add restore_volume_id in backup X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=4c83280125cc7ce15dc65b700494b2cc4491b4bd;p=openstack-build%2Fcinder-build.git 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 --- diff --git a/cinder/backup/api.py b/cinder/backup/api.py index c39004222..3be3f6960 100644 --- a/cinder/backup/api.py +++ b/cinder/backup/api.py @@ -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'}) diff --git a/cinder/db/sqlalchemy/migrate_repo/versions/064_add_restore_volume_id_to_backups.py b/cinder/db/sqlalchemy/migrate_repo/versions/064_add_restore_volume_id_to_backups.py new file mode 100644 index 000000000..370a53512 --- /dev/null +++ b/cinder/db/sqlalchemy/migrate_repo/versions/064_add_restore_volume_id_to_backups.py @@ -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) diff --git a/cinder/db/sqlalchemy/models.py b/cinder/db/sqlalchemy/models.py index 6e42bf8f5..aa182e020 100644 --- a/cinder/db/sqlalchemy/models.py +++ b/cinder/db/sqlalchemy/models.py @@ -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): diff --git a/cinder/objects/backup.py b/cinder/objects/backup.py index 6193d118a..826388280 100644 --- a/cinder/objects/backup.py +++ b/cinder/objects/backup.py @@ -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'] diff --git a/cinder/objects/base.py b/cinder/objects/base.py index 24f775a1d..90174fb6d 100644 --- a/cinder/objects/base.py +++ b/cinder/objects/base.py @@ -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): diff --git a/cinder/tests/unit/objects/test_backup.py b/cinder/tests/unit/objects/test_backup.py index 4b9a48bc7..808f553fe 100644 --- a/cinder/tests/unit/objects/test_backup.py +++ b/cinder/tests/unit/objects/test_backup.py @@ -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, diff --git a/cinder/tests/unit/objects/test_objects.py b/cinder/tests/unit/objects/test_objects.py index a5b89e010..15ddd9e55 100644 --- a/cinder/tests/unit/objects/test_objects.py +++ b/cinder/tests/unit/objects/test_objects.py @@ -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', diff --git a/cinder/tests/unit/test_backup.py b/cinder/tests/unit/test_backup.py index 707fc59be..d6661dfb4 100644 --- a/cinder/tests/unit/test_backup.py +++ b/cinder/tests/unit/test_backup.py @@ -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) diff --git a/cinder/tests/unit/test_db_api.py b/cinder/tests/unit/test_db_api.py index ed3215378..1a3e92bba 100644 --- a/cinder/tests/unit/test_db_api.py +++ b/cinder/tests/unit/test_db_api.py @@ -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 diff --git a/cinder/tests/unit/test_migrations.py b/cinder/tests/unit/test_migrations.py index ac692a00b..253c45f17 100644 --- a/cinder/tests/unit/test_migrations.py +++ b/cinder/tests/unit/test_migrations.py @@ -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)