diff --git a/nova/api/openstack/compute/contrib/migrations.py b/nova/api/openstack/compute/contrib/migrations.py index 72e7cdbdd40b..91a5493c77c7 100644 --- a/nova/api/openstack/compute/contrib/migrations.py +++ b/nova/api/openstack/compute/contrib/migrations.py @@ -32,9 +32,12 @@ def output(migrations_obj): primitive objects with the only necessary fields. """ objects = obj_base.obj_to_primitive(migrations_obj) + objects = [x for x in objects if not x['hidden']] for obj in objects: del obj['deleted'] del obj['deleted_at'] + del obj['migration_type'] + del obj['hidden'] return objects diff --git a/nova/api/openstack/compute/plugins/v3/migrations.py b/nova/api/openstack/compute/plugins/v3/migrations.py index 11ec6ba58a75..521bb211d944 100644 --- a/nova/api/openstack/compute/plugins/v3/migrations.py +++ b/nova/api/openstack/compute/plugins/v3/migrations.py @@ -30,9 +30,12 @@ def output(migrations_obj): primitive objects with the only necessary fields. """ objects = obj_base.obj_to_primitive(migrations_obj) + objects = [x for x in objects if not x['hidden']] for obj in objects: del obj['deleted'] del obj['deleted_at'] + del obj['migration_type'] + del obj['hidden'] return objects diff --git a/nova/objects/migration.py b/nova/objects/migration.py index ddd9b993a582..7bf48d6b501e 100644 --- a/nova/objects/migration.py +++ b/nova/objects/migration.py @@ -17,6 +17,14 @@ from nova import exception from nova import objects from nova.objects import base from nova.objects import fields +from nova import utils + + +def _determine_migration_type(migration): + if migration['old_instance_type_id'] != migration['new_instance_type_id']: + return 'resize' + else: + return 'migration' # TODO(berrange): Remove NovaObjectDictCompat @@ -24,7 +32,8 @@ class Migration(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: String attributes updated to support unicode - VERSION = '1.1' + # Version 1.2: Added migration_type and hidden + VERSION = '1.2' fields = { 'id': fields.IntegerField(), @@ -37,16 +46,42 @@ class Migration(base.NovaPersistentObject, base.NovaObject, 'new_instance_type_id': fields.IntegerField(nullable=True), 'instance_uuid': fields.StringField(nullable=True), 'status': fields.StringField(nullable=True), + 'migration_type': fields.EnumField(['migration', 'resize', + 'live-migration', 'evacuate'], + nullable=False), + 'hidden': fields.BooleanField(nullable=False, default=False), } @staticmethod def _from_db_object(context, migration, db_migration): for key in migration.fields: - migration[key] = db_migration[key] + value = db_migration[key] + if key == 'migration_type' and value is None: + value = _determine_migration_type(db_migration) + migration[key] = value + migration._context = context migration.obj_reset_changes() return migration + def obj_make_compatible(self, primitive, target_version): + super(Migration, self).obj_make_compatible(primitive, target_version) + target_version = utils.convert_version_to_tuple(target_version) + if target_version < (1, 2): + if 'migration_type' in primitive: + del primitive['migration_type'] + del primitive['hidden'] + + def obj_load_attr(self, attrname): + if attrname == 'migration_type': + # NOTE(danms): The only reason we'd need to load this is if + # some older node sent us one. So, guess the type. + self.migration_type = _determine_migration_type(self) + elif attrname == 'hidden': + self.hidden = False + else: + super(Migration, self).obj_load_attr(attrname) + @base.remotable_classmethod def get_by_id(cls, context, migration_id): db_migration = db.migration_get(context, migration_id) @@ -84,7 +119,8 @@ class MigrationList(base.ObjectListBase, base.NovaObject): # Version 1.0: Initial version # Migration <= 1.1 # Version 1.1: Added use_slave to get_unconfirmed_by_dest_compute - VERSION = '1.1' + # Version 1.2: Migration version 1.2 + VERSION = '1.2' fields = { 'objects': fields.ListOfObjectsField('Migration'), @@ -93,6 +129,7 @@ class MigrationList(base.ObjectListBase, base.NovaObject): '1.0': '1.1', # NOTE(danms): Migration was at 1.1 before we added this '1.1': '1.1', + '1.2': '1.2', } @base.remotable_classmethod diff --git a/nova/tests/functional/v3/test_migrations.py b/nova/tests/functional/v3/test_migrations.py index bf4c6961f407..67f08390f5c5 100644 --- a/nova/tests/functional/v3/test_migrations.py +++ b/nova/tests/functional/v3/test_migrations.py @@ -53,6 +53,8 @@ class MigrationsSamplesJsonTest(api_sample_base.ApiSampleTestBaseV3): 'instance_uuid': 'instance_id_123', 'old_instance_type_id': 1, 'new_instance_type_id': 2, + 'migration_type': 'resize', + 'hidden': False, 'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'deleted_at': None, @@ -69,6 +71,8 @@ class MigrationsSamplesJsonTest(api_sample_base.ApiSampleTestBaseV3): 'instance_uuid': 'instance_id_456', 'old_instance_type_id': 5, 'new_instance_type_id': 6, + 'migration_type': 'resize', + 'hidden': False, 'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'deleted_at': None, diff --git a/nova/tests/unit/api/openstack/compute/contrib/test_migrations.py b/nova/tests/unit/api/openstack/compute/contrib/test_migrations.py index 49efb0db84f9..f383252066fd 100644 --- a/nova/tests/unit/api/openstack/compute/contrib/test_migrations.py +++ b/nova/tests/unit/api/openstack/compute/contrib/test_migrations.py @@ -38,6 +38,8 @@ fake_migrations = [ 'instance_uuid': 'instance_id_123', 'old_instance_type_id': 1, 'new_instance_type_id': 2, + 'migration_type': 'resize', + 'hidden': False, 'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2), 'deleted_at': None, @@ -54,6 +56,8 @@ fake_migrations = [ 'instance_uuid': 'instance_id_456', 'old_instance_type_id': 5, 'new_instance_type_id': 6, + 'migration_type': 'resize', + 'hidden': False, 'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2), 'deleted_at': None, diff --git a/nova/tests/unit/objects/test_migration.py b/nova/tests/unit/objects/test_migration.py index 327aac890f27..4cee5c7401f8 100644 --- a/nova/tests/unit/objects/test_migration.py +++ b/nova/tests/unit/objects/test_migration.py @@ -12,11 +12,13 @@ # License for the specific language governing permissions and limitations # under the License. +import mock from oslo_utils import timeutils from nova import context from nova import db from nova import exception +from nova import objects from nova.objects import migration from nova.tests.unit import fake_instance from nova.tests.unit.objects import test_objects @@ -41,6 +43,8 @@ def fake_db_migration(**updates): 'new_instance_type_id': 84, 'instance_uuid': 'fake-uuid', 'status': 'migrating', + 'migration_type': 'resize', + 'hidden': False, } if updates: @@ -173,6 +177,36 @@ class _TestMigrationObject(object): for index, db_migration in enumerate(db_migrations): self.compare_obj(migrations[index], db_migration) + def test_migrate_old_resize_record(self): + db_migration = dict(fake_db_migration(), migration_type=None) + with mock.patch('nova.db.migration_get') as fake_get: + fake_get.return_value = db_migration + mig = objects.Migration.get_by_id(context.get_admin_context(), 1) + self.assertTrue(mig.obj_attr_is_set('migration_type')) + self.assertEqual('resize', mig.migration_type) + + def test_migrate_old_migration_record(self): + db_migration = dict( + fake_db_migration(), migration_type=None, + old_instance_type_id=1, new_instance_type_id=1) + with mock.patch('nova.db.migration_get') as fake_get: + fake_get.return_value = db_migration + mig = objects.Migration.get_by_id(context.get_admin_context(), 1) + self.assertTrue(mig.obj_attr_is_set('migration_type')) + self.assertEqual('migration', mig.migration_type) + + def test_migrate_unset_type_resize(self): + mig = objects.Migration(old_instance_type_id=1, + new_instance_type_id=2) + self.assertEqual('resize', mig.migration_type) + self.assertTrue(mig.obj_attr_is_set('migration_type')) + + def test_migrate_unset_type_migration(self): + mig = objects.Migration(old_instance_type_id=1, + new_instance_type_id=1) + self.assertEqual('migration', mig.migration_type) + self.assertTrue(mig.obj_attr_is_set('migration_type')) + 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 cb3d6deb3bdb..b9d9b1cf289b 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1151,8 +1151,8 @@ object_data = { 'InstancePCIRequests': '1.1-4825b599f000538991fdc9972a92c2c6', 'KeyPair': '1.3-2d7c9ccade5532f7cd185110a9367e6a', 'KeyPairList': '1.2-41b7c9ab5fd2a216be4bbce011a55eff', - 'Migration': '1.1-dc2db9e6e625bd3444a5a114438b298d', - 'MigrationList': '1.1-45a973ee70500f799da67491edabc5d4', + 'Migration': '1.2-0554a9f061ec0e9fefe43773bc426fcf', + 'MigrationList': '1.2-e772d7d6ae0581ec72042d50c6bdf6ec', 'MyObj': '1.6-fce707f79d6fee00f0ebbac98816a380', 'MyOwnedObject': '1.0-0f3d6c028543d7f3715d121db5b8e298', 'NUMACell': '1.2-cb9c3b08cc1c418d021492f788d04173',