diff --git a/nova/api/openstack/compute/migrations.py b/nova/api/openstack/compute/migrations.py index bdb4fae2a8..db0c2f131a 100644 --- a/nova/api/openstack/compute/migrations.py +++ b/nova/api/openstack/compute/migrations.py @@ -50,6 +50,7 @@ class MigrationsController(wsgi.Controller): del obj['deleted'] del obj['deleted_at'] del obj['hidden'] + del obj['uuid'] if 'memory_total' in obj: for key in detail_keys: del obj[key] diff --git a/nova/objects/migration.py b/nova/objects/migration.py index b7e989dbc5..d77848609d 100644 --- a/nova/objects/migration.py +++ b/nova/objects/migration.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_db import exception as db_exc +from oslo_utils import uuidutils from oslo_utils import versionutils from nova import db @@ -38,10 +40,12 @@ class Migration(base.NovaPersistentObject, base.NovaObject, # Version 1.2: Added migration_type and hidden # Version 1.3: Added get_by_id_and_instance() # Version 1.4: Added migration progress detail - VERSION = '1.4' + # Version 1.5: Added uuid + VERSION = '1.5' fields = { 'id': fields.IntegerField(), + 'uuid': fields.UUIDField(), 'source_compute': fields.StringField(nullable=True), 'dest_compute': fields.StringField(nullable=True), 'source_node': fields.StringField(nullable=True), @@ -69,10 +73,13 @@ class Migration(base.NovaPersistentObject, base.NovaObject, value = db_migration[key] if key == 'migration_type' and value is None: value = determine_migration_type(db_migration) + elif key == 'uuid' and value is None: + continue migration[key] = value migration._context = context migration.obj_reset_changes() + migration._ensure_uuid() return migration def obj_make_compatible(self, primitive, target_version): @@ -90,6 +97,9 @@ class Migration(base.NovaPersistentObject, base.NovaObject, del primitive['disk_total'] del primitive['disk_processed'] del primitive['disk_remaining'] + if target_version < (1, 5): + if 'uuid' in primitive: + del primitive['uuid'] def obj_load_attr(self, attrname): if attrname == 'migration_type': @@ -101,6 +111,19 @@ class Migration(base.NovaPersistentObject, base.NovaObject, else: super(Migration, self).obj_load_attr(attrname) + def _ensure_uuid(self): + if 'uuid' in self: + return + + self.uuid = uuidutils.generate_uuid() + try: + self.save() + except db_exc.DBDuplicateEntry: + # NOTE(danms) We raced to generate a uuid for this, + # so fetch the winner and use that uuid + fresh = self.__class__.get_by_id(self.context, self.id) + self.uuid = fresh.uuid + @base.remotable_classmethod def get_by_id(cls, context, migration_id): db_migration = db.migration_get(context, migration_id) @@ -123,6 +146,8 @@ class Migration(base.NovaPersistentObject, base.NovaObject, if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') + if 'uuid' not in self: + self.uuid = uuidutils.generate_uuid() updates = self.obj_get_changes() if 'migration_type' not in updates: raise exception.ObjectActionError( diff --git a/nova/tests/functional/api_sample_tests/test_migrations.py b/nova/tests/functional/api_sample_tests/test_migrations.py index 957d29a736..c3dcc24f78 100644 --- a/nova/tests/functional/api_sample_tests/test_migrations.py +++ b/nova/tests/functional/api_sample_tests/test_migrations.py @@ -18,6 +18,7 @@ import datetime from nova import context from nova import objects from nova.tests.functional.api_sample_tests import api_sample_base +from nova.tests import uuidsentinel as uuids # NOTE(ShaoHe Feng) here I can not use uuidsentinel, it generate a random @@ -44,7 +45,8 @@ def _stub_migrations(stub_self, context, filters): 'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'deleted_at': None, - 'deleted': False + 'deleted': False, + 'uuid': uuids.migration1, }, { 'id': 5678, @@ -62,7 +64,8 @@ def _stub_migrations(stub_self, context, filters): 'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'deleted_at': None, - 'deleted': False + 'deleted': False, + 'uuid': uuids.migration2, } ] return fake_migrations diff --git a/nova/tests/unit/api/openstack/compute/test_migrations.py b/nova/tests/unit/api/openstack/compute/test_migrations.py index 44b227a3a0..0d08b7780c 100644 --- a/nova/tests/unit/api/openstack/compute/test_migrations.py +++ b/nova/tests/unit/api/openstack/compute/test_migrations.py @@ -50,7 +50,8 @@ fake_migrations = [ 'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'deleted_at': None, - 'deleted': False + 'deleted': False, + 'uuid': uuids.migration1, }, # non in-progress live migration { @@ -75,7 +76,8 @@ fake_migrations = [ 'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'deleted_at': None, - 'deleted': False + 'deleted': False, + 'uuid': uuids.migration2, }, # in-progress resize { @@ -100,7 +102,8 @@ fake_migrations = [ 'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'deleted_at': None, - 'deleted': False + 'deleted': False, + 'uuid': uuids.migration3, }, # non in-progress resize { @@ -125,7 +128,8 @@ fake_migrations = [ 'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'deleted_at': None, - 'deleted': False + 'deleted': False, + 'uuid': uuids.migration4, } ] diff --git a/nova/tests/unit/api/openstack/compute/test_server_migrations.py b/nova/tests/unit/api/openstack/compute/test_server_migrations.py index 5611928324..4a335eca68 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_migrations.py +++ b/nova/tests/unit/api/openstack/compute/test_server_migrations.py @@ -52,7 +52,8 @@ fake_migrations = [ 'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'deleted_at': None, - 'deleted': False + 'deleted': False, + 'uuid': uuids.migration1, }, { 'id': 5678, @@ -76,7 +77,8 @@ fake_migrations = [ 'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'deleted_at': None, - 'deleted': False + 'deleted': False, + 'uuid': uuids.migration2, } ] diff --git a/nova/tests/unit/compute/test_compute_api.py b/nova/tests/unit/compute/test_compute_api.py index 78419f3343..ebf468884f 100644 --- a/nova/tests/unit/compute/test_compute_api.py +++ b/nova/tests/unit/compute/test_compute_api.py @@ -3391,6 +3391,7 @@ class _ComputeAPIUnitTestMixIn(object): 'dest_node': None, 'dest_host': None, 'old_instance_type_id': None, 'new_instance_type_id': None, + 'uuid': uuids.migration, 'instance_uuid': uuids.instance_2, 'status': None, 'migration_type': 'evacuation', 'memory_total': None, 'memory_processed': None, 'memory_remaining': None, diff --git a/nova/tests/unit/objects/test_migration.py b/nova/tests/unit/objects/test_migration.py index 5dc5530b03..c1ee821c3c 100644 --- a/nova/tests/unit/objects/test_migration.py +++ b/nova/tests/unit/objects/test_migration.py @@ -35,6 +35,7 @@ def fake_db_migration(**updates): 'deleted_at': None, 'deleted': False, 'id': 123, + 'uuid': uuidsentinel.migration, 'source_compute': 'compute-source', 'dest_compute': 'compute-dest', 'source_node': 'node-source', @@ -103,11 +104,14 @@ class _TestMigrationObject(object): mig = migration.Migration(context=ctxt) mig.source_compute = 'foo' mig.migration_type = 'resize' + mig.uuid = uuidsentinel.migration mig.create() self.assertEqual(fake_migration['dest_compute'], mig.dest_compute) + self.assertIn('uuid', mig) mock_create.assert_called_once_with(ctxt, {'source_compute': 'foo', - 'migration_type': 'resize'}) + 'migration_type': 'resize', + 'uuid': uuidsentinel.migration}) @mock.patch.object(db, 'migration_create') def test_recreate_fails(self, mock_create): @@ -117,11 +121,13 @@ class _TestMigrationObject(object): mig = migration.Migration(context=ctxt) mig.source_compute = 'foo' mig.migration_type = 'resize' + mig.uuid = uuidsentinel.migration mig.create() self.assertRaises(exception.ObjectActionError, mig.create) mock_create.assert_called_once_with(ctxt, {'source_compute': 'foo', - 'migration_type': 'resize'}) + 'migration_type': 'resize', + 'uuid': uuidsentinel.migration}) def test_create_fails_migration_type(self): ctxt = context.get_admin_context() @@ -249,6 +255,25 @@ class _TestMigrationObject(object): migration = objects.Migration.get_by_id_and_instance(ctxt, '1', '1') self.compare_obj(migration, fake_migration) + def test_create_uuid_on_load(self): + values = {'source_compute': 'src', + 'dest_compute': 'dst', + 'source_node': 'srcnode', + 'dest_node': 'dstnode', + 'instance_uuid': 'fake', + 'status': 'faking', + 'migration_type': 'migration', + 'created_at': None, + 'deleted_at': None, + 'updated_at': None} + db_mig = db.migration_create(self.context, values) + mig = objects.Migration.get_by_id(self.context, db_mig.id) + self.assertIn('uuid', mig) + uuid = mig.uuid + # Make sure that it was saved and we get the same one back + mig = objects.Migration.get_by_id(self.context, db_mig.id) + self.assertEqual(uuid, mig.uuid) + class TestMigrationObject(test_objects._LocalTest, _TestMigrationObject): diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index 6b6bcca425..39fc80055f 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1126,7 +1126,7 @@ object_data = { 'KeyPair': '1.4-1244e8d1b103cc69d038ed78ab3a8cc6', 'KeyPairList': '1.3-94aad3ac5c938eef4b5e83da0212f506', 'MemoryDiagnostics': '1.0-2c995ae0f2223bb0f8e523c5cc0b83da', - 'Migration': '1.4-17979b9f2ae7f28d97043a220b2a8350', + 'Migration': '1.5-48bebaada664ee15bc23b35b2b814d75', 'MigrationContext': '1.1-9fb17b0b521370957a884636499df52d', 'MigrationList': '1.3-55595bfc1a299a5962614d0821a3567e', 'MonitorMetric': '1.1-53b1db7c4ae2c531db79761e7acc52ba',