diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index 75d252b1ae37..e84d4ef1f7b8 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -56,6 +56,7 @@ from nova import exception from nova.i18n import _ from nova import objects from nova.objects import aggregate as aggregate_obj +from nova.objects import block_device as block_device_obj from nova.objects import build_request as build_request_obj from nova.objects import host_mapping as host_mapping_obj from nova.objects import instance as instance_obj @@ -436,6 +437,8 @@ class DbCommands(object): quotas_obj.migrate_quota_classes_to_api_db, # Added in Queens sa_db.migration_migrate_to_uuid, + # Added in Queens + block_device_obj.BlockDeviceMapping.populate_uuids, ) def __init__(self): diff --git a/nova/objects/block_device.py b/nova/objects/block_device.py index 866109d47bdf..340128c512a5 100644 --- a/nova/objects/block_device.py +++ b/nova/objects/block_device.py @@ -104,6 +104,22 @@ class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject, if target_version < (1, 17) and 'tag' in primitive: del primitive['tag'] + @classmethod + def populate_uuids(cls, context, count): + @db_api.pick_context_manager_reader + def get_bdms_no_uuid(context): + return context.session.query(db_models.BlockDeviceMapping).\ + filter_by(uuid=None).limit(count).all() + + db_bdms = get_bdms_no_uuid(context) + + done = 0 + for db_bdm in db_bdms: + cls._create_uuid(context, db_bdm['id']) + done += 1 + + return done, done + @staticmethod @oslo_db_api.wrap_db_retry(max_retries=1, retry_on_deadlock=True) def _create_uuid(context, bdm_id): diff --git a/nova/tests/unit/objects/test_block_device.py b/nova/tests/unit/objects/test_block_device.py index fd6681589452..574384383e63 100644 --- a/nova/tests/unit/objects/test_block_device.py +++ b/nova/tests/unit/objects/test_block_device.py @@ -483,6 +483,40 @@ class TestBlockDeviceMappingUUIDMigration(test.TestCase): self.assertEqual(uuid1, uuid2) + def _assert_online_migration(self, expected_total, expected_done, + limit=10): + total, done = objects.BlockDeviceMapping.populate_uuids( + self.context, limit) + self.assertEqual(expected_total, total) + self.assertEqual(expected_done, done) + + def test_online_migration(self): + self._assert_online_migration(0, 0) + + # Create 2 BDMs, one with a uuid and one without + self._create_legacy_bdm(self.context) + db_api.block_device_mapping_create(self.context, + {'uuid': uuids.bdm2, 'instance_uuid': uuids.instance_uuid}, + legacy=False) + + # Run the online migration. We should find 1 and update 1 + self._assert_online_migration(1, 1) + + # Fetch the BDMs and check we didn't modify the uuid of bdm2 + bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( + self.context, uuids.instance_uuid) + bdm_uuids = [bdm.uuid for bdm in bdms] + self.assertIn(uuids.bdm2, bdm_uuids) + self.assertNotIn(None, bdm_uuids) + + # Run the online migration again to see nothing was processed + self._assert_online_migration(0, 0) + + # Test that we don't migrate more than the limit + for i in range(0, 3): + self._create_legacy_bdm(self.context) + self._assert_online_migration(2, 2, limit=2) + class TestBlockDeviceMappingObject(test_objects._LocalTest, _TestBlockDeviceMappingObject):