From 5df97016b4c00d5a2699d56d184f237225d01b96 Mon Sep 17 00:00:00 2001 From: Lee Yarwood Date: Wed, 26 Jan 2022 17:51:27 +0000 Subject: [PATCH] block_device: Add DriverImageBlockDevice to block_device_info Change-Id: I17e0758e3b77caebd4d142664a8367ab4601ebdf --- doc/source/reference/block-device-structs.rst | 9 +- nova/compute/manager.py | 5 ++ nova/tests/unit/compute/test_compute.py | 29 +++--- .../tests/unit/virt/libvirt/test_blockinfo.py | 89 +++++++++++++++---- nova/tests/unit/virt/libvirt/test_driver.py | 5 +- nova/tests/unit/virt/test_block_device.py | 54 +++++++++-- nova/virt/block_device.py | 50 +++++++++-- nova/virt/driver.py | 11 +++ nova/virt/libvirt/blockinfo.py | 72 ++++++++------- 9 files changed, 241 insertions(+), 83 deletions(-) diff --git a/doc/source/reference/block-device-structs.rst b/doc/source/reference/block-device-structs.rst index 1b8636c5378b..8c2508539f6d 100644 --- a/doc/source/reference/block-device-structs.rst +++ b/doc/source/reference/block-device-structs.rst @@ -71,6 +71,8 @@ called ``block_device_info``, and is generated by ``root_device_name`` Hypervisor's notion of the root device's name +``image`` + An image backed disk if used ``ephemerals`` A list of all ephemeral disks ``block_device_mapping`` @@ -105,13 +107,6 @@ persist data to the BDM object in the DB. In other contexts this filtering will not have happened, and ``block_device_mapping`` will contain all volumes. -.. note:: - - Unlike BDMs, ``block_device_info`` does not currently represent all - disks that an instance might have. Significantly, it will not contain any - representation of an image-backed local disk, i.e. the root disk of a - typical instance which isn't boot-from-volume. Other representations used - by the libvirt driver explicitly reconstruct this missing disk. libvirt driver specific BDM data structures =========================================== diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 9f8479a30e40..fc720e2429ee 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -2025,6 +2025,7 @@ class ComputeManager(manager.Manager): ephemerals = [] swap = [] block_device_mapping = [] + image = [] for device in block_devices: if block_device.new_format_is_ephemeral(device): @@ -2036,8 +2037,12 @@ class ComputeManager(manager.Manager): if driver_block_device.is_block_device_mapping(device): block_device_mapping.append(device) + if driver_block_device.is_local_image(device): + image.append(device) + self._default_device_names_for_instance(instance, root_device_name, + image, ephemerals, swap, block_device_mapping) diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index 035116699107..43fcdf501bc9 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -1389,13 +1389,14 @@ class ComputeVolumeTestCase(BaseTestCase): @mock.patch.object(nova.virt.block_device, 'convert_snapshots') @mock.patch.object(nova.virt.block_device, 'convert_volumes') @mock.patch.object(nova.virt.block_device, 'convert_ephemerals') + @mock.patch.object(nova.virt.block_device, 'convert_local_images') @mock.patch.object(nova.virt.block_device, 'convert_swap') @mock.patch.object(nova.virt.block_device, 'attach_block_devices') def test_prep_block_device_with_blanks(self, attach_block_devices, - convert_swap, convert_ephemerals, - convert_volumes, convert_snapshots, - convert_images, convert_blanks, - get_swap): + convert_swap, convert_local_images, + convert_ephemerals, convert_volumes, + convert_snapshots, convert_images, + convert_blanks, get_swap): instance = self._create_fake_instance_obj() instance['root_device_name'] = '/dev/vda' root_volume = objects.BlockDeviceMapping( @@ -1426,6 +1427,7 @@ class ComputeVolumeTestCase(BaseTestCase): return bdm convert_swap.return_value = [] + convert_local_images.return_value = [] convert_ephemerals.return_value = [] convert_volumes.return_value = [blank_volume1, blank_volume2] convert_snapshots.return_value = [] @@ -1438,6 +1440,7 @@ class ComputeVolumeTestCase(BaseTestCase): 'root_device_name': '/dev/vda', 'swap': [], 'ephemerals': [], + 'image': [], 'block_device_mapping': bdms } @@ -1452,6 +1455,7 @@ class ComputeVolumeTestCase(BaseTestCase): self.assertIsNotNone(bdm.device_name) convert_swap.assert_called_once_with(bdms) + convert_local_images.assert_called_once_with(bdms) convert_ephemerals.assert_called_once_with(bdms) bdm_args = tuple(bdms) convert_volumes.assert_called_once_with(bdm_args) @@ -3212,6 +3216,7 @@ class ComputeTestCase(BaseTestCase, expected = { 'swap': None, 'ephemerals': [], + 'image': [], 'root_device_name': None, 'block_device_mapping': driver_bdms } @@ -3240,6 +3245,7 @@ class ComputeTestCase(BaseTestCase, expected = { 'swap': None, 'ephemerals': [], + 'image': [], 'root_device_name': None, 'block_device_mapping': driver_bdms } @@ -3318,6 +3324,7 @@ class ComputeTestCase(BaseTestCase, 'size': 2 } ], + 'image': [], 'block_device_mapping': [], 'root_device_name': None } @@ -6108,7 +6115,7 @@ class ComputeTestCase(BaseTestCase, mock_pre.assert_called_once_with( test.MatchType(nova.context.RequestContext), test.MatchType(objects.Instance), - {'swap': None, 'ephemerals': [], + {'swap': None, 'ephemerals': [], 'image': [], 'root_device_name': None, 'block_device_mapping': []}, mock.ANY, mock.ANY, mock.ANY) @@ -6474,7 +6481,7 @@ class ComputeTestCase(BaseTestCase, self.assertEqual(2, mock_notify.call_count) post_live_migration.assert_has_calls([ mock.call(c, instance, {'swap': None, 'ephemerals': [], - 'root_device_name': None, + 'image': [], 'root_device_name': None, 'block_device_mapping': []}, migrate_data)]) migrate_instance_start.assert_has_calls([ @@ -6705,7 +6712,7 @@ class ComputeTestCase(BaseTestCase, mock_setup.assert_called_once_with(c, instance, self.compute.host, teardown=True) mock_rollback.assert_called_once_with(c, instance, [], - {'swap': None, 'ephemerals': [], + {'swap': None, 'ephemerals': [], 'image': [], 'root_device_name': None, 'block_device_mapping': []}, destroy_disks=True, migrate_data=None) @@ -8134,7 +8141,7 @@ class ComputeTestCase(BaseTestCase, self.compute._default_block_device_names(instance, {}, bdms) self.assertEqual('/dev/vda', instance.root_device_name) - mock_def.assert_called_once_with(instance, '/dev/vda', [], [], + mock_def.assert_called_once_with(instance, '/dev/vda', [], [], [], [bdm for bdm in bdms]) @mock.patch.object(objects.BlockDeviceMapping, 'save') @@ -8148,7 +8155,7 @@ class ComputeTestCase(BaseTestCase, self.compute._default_block_device_names(instance, {}, bdms) - mock_def.assert_called_once_with(instance, '/dev/vda', [], [], + mock_def.assert_called_once_with(instance, '/dev/vda', [], [], [], [bdm for bdm in bdms]) @mock.patch.object(objects.Instance, 'save') @@ -8170,7 +8177,7 @@ class ComputeTestCase(BaseTestCase, self.assertEqual('/dev/vda', instance.root_device_name) mock_default_dev.assert_called_once_with(instance, mock.ANY, bdms[0]) mock_default_name.assert_called_once_with(instance, '/dev/vda', [], [], - [bdm for bdm in bdms]) + [], [bdm for bdm in bdms]) def test_default_block_device_names_with_blank_volumes(self): instance = self._create_fake_instance_obj() @@ -8230,7 +8237,7 @@ class ComputeTestCase(BaseTestCase, self.assertEqual('/dev/vda', instance.root_device_name) self.assertTrue(object_save.called) default_device_names.assert_called_once_with(instance, - '/dev/vda', [bdms[-2]], [bdms[-1]], + '/dev/vda', [], [bdms[-2]], [bdms[-1]], [bdm for bdm in bdms[:-2]]) def test_reserve_block_device_name(self): diff --git a/nova/tests/unit/virt/libvirt/test_blockinfo.py b/nova/tests/unit/virt/libvirt/test_blockinfo.py index 3da827dce8e6..9505dbe31ca5 100644 --- a/nova/tests/unit/virt/libvirt/test_blockinfo.py +++ b/nova/tests/unit/virt/libvirt/test_blockinfo.py @@ -74,6 +74,7 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): def _test_block_device_info(self, with_eph=True, with_swap=True, with_bdms=True): swap = {'device_name': '/dev/vdb', 'swap_size': 1} + image = [{'device_type': 'disk', 'boot_index': 0}] ephemerals = [{'device_type': 'disk', 'guest_format': 'ext4', 'device_name': '/dev/vdc1', 'size': 10}, {'disk_bus': 'ide', 'guest_format': None, @@ -84,6 +85,7 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): 'device_path': 'fake_device'}] return {'root_device_name': '/dev/vda', 'swap': swap if with_swap else {}, + 'image': image, 'ephemerals': ephemerals if with_eph else [], 'block_device_mapping': block_device_mapping if with_bdms else []} @@ -178,11 +180,16 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): instance_ref = objects.Instance(**self.test_instance) image_meta = objects.ImageMeta.from_dict(self.test_image_meta) + block_device_info = { + 'image': [ + {'device_type': 'disk', 'boot_index': 0}, + ] + } with mock.patch.object(instance_ref, 'get_flavor', return_value=instance_ref.flavor) as get_flavor: - mapping = blockinfo.get_disk_mapping("kvm", instance_ref, - "virtio", "ide", - image_meta) + mapping = blockinfo.get_disk_mapping( + "kvm", instance_ref, "virtio", "ide", image_meta, + block_device_info=block_device_info) # Since there was no block_device_info passed to get_disk_mapping we # expect to get the swap info from the flavor in the instance. get_flavor.assert_called_once_with() @@ -202,7 +209,8 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): instance_ref = objects.Instance(**self.test_instance) image_meta = objects.ImageMeta.from_dict(self.test_image_meta) block_device_info = { - 'root_device_name': '/dev/sda' + 'root_device_name': '/dev/sda', + 'image': [{'device_type': 'disk', 'boot_index': 0}], } mapping = blockinfo.get_disk_mapping("kvm", instance_ref, @@ -490,9 +498,12 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): instance_ref = objects.Instance(**self.test_instance) image_meta = objects.ImageMeta.from_dict(self.test_image_meta) - mapping = blockinfo.get_disk_mapping("lxc", instance_ref, - "lxc", "lxc", - image_meta) + block_device_info = { + 'image': [{'device_type': 'disk', 'boot_index': 0}], + } + mapping = blockinfo.get_disk_mapping( + "lxc", instance_ref, "lxc", "lxc", image_meta, + block_device_info=block_device_info) expect = { 'disk': {'bus': 'lxc', 'dev': None, 'type': 'disk', 'boot_index': '1'}, @@ -527,9 +538,14 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): instance_ref.flavor.swap = 5 image_meta = objects.ImageMeta.from_dict(self.test_image_meta) - mapping = blockinfo.get_disk_mapping("kvm", instance_ref, - "virtio", "ide", - image_meta) + block_device_info = { + 'image': [ + {'device_type': 'disk', 'boot_index': 0}, + ] + } + mapping = blockinfo.get_disk_mapping( + "kvm", instance_ref, "virtio", "ide", image_meta, + block_device_info=block_device_info) expect = { 'disk': {'bus': 'virtio', 'dev': 'vda', @@ -549,6 +565,7 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): instance_ref.ephemeral_gb = 0 block_dev_info = {'swap': None, 'root_device_name': u'/dev/vda', + 'image': [], 'ephemerals': [], 'block_device_mapping': [{'boot_index': None, 'mount_device': u'/dev/vdb', @@ -591,8 +608,14 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): instance_ref = objects.Instance(**self.test_instance) image_meta = objects.ImageMeta.from_dict(self.test_image_meta) + block_device_info = { + 'image': [ + {'device_type': 'disk', 'boot_index': 0}, + ] + } mapping = blockinfo.get_disk_mapping( - "kvm", instance_ref, "virtio", "ide", image_meta) + "kvm", instance_ref, "virtio", "ide", image_meta, + block_device_info=block_device_info) # Pick the first drive letter on the bus that is available # as the config drive. Delete the last device hardcode as @@ -647,8 +670,14 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): instance_ref = objects.Instance(**self.test_instance) image_meta = objects.ImageMeta.from_dict(self.test_image_meta) + block_device_info = { + 'image': [ + {'device_type': 'disk', 'boot_index': 0}, + ] + } mapping = blockinfo.get_disk_mapping( - "kvm", instance_ref, "virtio", "ide", image_meta) + "kvm", instance_ref, "virtio", "ide", image_meta, + block_device_info=block_device_info) expect = { 'disk': { @@ -697,9 +726,14 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): instance_ref = objects.Instance(**self.test_instance) image_meta = objects.ImageMeta.from_dict(self.test_image_meta) - mapping = blockinfo.get_disk_mapping("kvm", instance_ref, - "virtio", "ide", - image_meta) + block_device_info = { + 'image': [ + {'device_type': 'disk', 'boot_index': 0}, + ] + } + mapping = blockinfo.get_disk_mapping( + "kvm", instance_ref, "virtio", "ide", image_meta, + block_device_info=block_device_info) expect = { 'disk': {'bus': 'virtio', 'dev': 'vda', @@ -718,6 +752,9 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): image_meta = objects.ImageMeta.from_dict(self.test_image_meta) block_device_info = { + 'image': [ + {'device_type': 'disk', 'boot_index': 0}, + ], 'ephemerals': [ {'device_type': 'disk', 'guest_format': 'ext4', 'device_name': '/dev/vdb', 'size': 10}, @@ -754,6 +791,8 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): block_device_info = { 'swap': {'device_name': '/dev/vdb', 'swap_size': 10}, + 'image': [{'device_type': 'disk', + 'boot_index': 0}], } mapping = blockinfo.get_disk_mapping("kvm", instance_ref, "virtio", "ide", @@ -775,6 +814,7 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): image_meta = objects.ImageMeta.from_dict(self.test_image_meta) block_device_info = { + 'image': [], 'block_device_mapping': [ {'connection_info': "fake", 'mount_device': "/dev/vda", @@ -803,6 +843,7 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): image_meta = {} block_device_info = { + 'image': [], 'block_device_mapping': [ {'connection_info': None, 'mount_device': None, @@ -858,6 +899,7 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): image_meta = objects.ImageMeta.from_dict(self.test_image_meta) block_device_info = { + 'image': [], 'block_device_mapping': [ {'connection_info': "fake", 'mount_device': "/dev/vda", @@ -899,6 +941,7 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): 'root_device_name': '/dev/vdf', 'swap': {'device_name': '/dev/vdy', 'swap_size': 10}, + 'image': [{'device_type': 'disk', 'boot_index': 0}], 'ephemerals': [ {'device_type': 'disk', 'guest_format': 'ext4', 'device_name': '/dev/vdb', 'size': 10}, @@ -940,6 +983,8 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): 'swap': {'device_name': '/dev/vdb', 'device_type': 'really_lame_type', 'swap_size': 10}, + 'image': [{'device_name': '/dev/vda', + 'device_type': 'disk'}], 'ephemerals': [{'disk_bus': 'no_such_bus', 'device_type': 'yeah_right', 'device_name': '/dev/vdc', 'size': 10}], @@ -951,6 +996,8 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): } expected_swap = {'device_name': '/dev/vdb', 'disk_bus': 'virtio', 'device_type': 'disk', 'swap_size': 10} + expected_image = {'device_name': '/dev/vda', 'device_type': 'disk', + 'disk_bus': 'virtio'} expected_ephemeral = {'disk_bus': 'virtio', 'device_type': 'disk', 'device_name': '/dev/vdc', 'size': 10} @@ -970,6 +1017,7 @@ class LibvirtBlockInfoTest(test.NoDBTestCase): self.assertFalse(get_flavor_mock.called) self.assertEqual(expected_swap, block_device_info['swap']) + self.assertEqual(expected_image, block_device_info['image'][0]) self.assertEqual(expected_ephemeral, block_device_info['ephemerals'][0]) self.assertEqual(expected_bdm, @@ -1441,6 +1489,15 @@ class DefaultDeviceNamesTestCase(test.NoDBTestCase): 'destination_type': 'volume', 'boot_index': -1}))] + self.image = [ + objects.BlockDeviceMapping(self.context, + **fake_block_device.FakeDbBlockDeviceDict( + {'id': 6, 'instance_uuid': uuids.instance, + 'source_type': 'image', + 'destination_type': 'local', + 'device_type': 'disk', + 'boot_index': 0}))] + def tearDown(self): super(DefaultDeviceNamesTestCase, self).tearDown() for patcher in self.patchers: @@ -1450,7 +1507,7 @@ class DefaultDeviceNamesTestCase(test.NoDBTestCase): 'nova.virt.libvirt.utils.get_arch', return_value=obj_fields.Architecture.X86_64) def _test_default_device_names(self, eph, swap, bdm, mock_get_arch): - bdms = eph + swap + bdm + bdms = self.image + eph + swap + bdm bdi = driver.get_block_device_info(self.instance, bdms) blockinfo.default_device_names(self.virt_type, self.context, diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 7f73e45fd76c..30af4e3e2ff8 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -20410,7 +20410,7 @@ class LibvirtConnTestCase(test.NoDBTestCase, **fake_block_device.FakeDbBlockDeviceDict( {'id': 0, 'source_type': 'volume', 'destination_type': 'volume', - 'device_name': '/dev/sda'})) + 'device_name': '/dev/sda', 'boot_index': 0})) info = {'block_device_mapping': driver_block_device.convert_volumes( [bdm])} info['block_device_mapping'][0]['connection_info'] = conn_info @@ -24941,7 +24941,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin): 'id': 1, 'source_type': 'volume', 'destination_type': 'volume', - 'device_name': '/dev/vda'})) + 'device_name': '/dev/vda', + 'boot_index': 0})) bdms = driver_block_device.convert_volumes([bdm]) block_device_info = {'root_device_name': '/dev/vda', 'ephemerals': [], diff --git a/nova/tests/unit/virt/test_block_device.py b/nova/tests/unit/virt/test_block_device.py index 79dbc44bf176..fe74744ec5c0 100644 --- a/nova/tests/unit/virt/test_block_device.py +++ b/nova/tests/unit/virt/test_block_device.py @@ -49,7 +49,8 @@ class TestDriverBlockDevice(test.NoDBTestCase): 'volume': driver_block_device.DriverVolumeBlockDevice, 'volsnapshot': driver_block_device.DriverVolSnapshotBlockDevice, 'volimage': driver_block_device.DriverVolImageBlockDevice, - 'volblank': driver_block_device.DriverVolBlankBlockDevice + 'volblank': driver_block_device.DriverVolBlankBlockDevice, + 'image': driver_block_device.DriverImageBlockDevice, } swap_bdm_dict = block_device.BlockDeviceDict( @@ -210,6 +211,27 @@ class TestDriverBlockDevice(test.NoDBTestCase): 'boot_index': -1, 'volume_type': None} + image_bdm_dict = block_device.BlockDeviceDict( + {'id': 7, 'instance_uuid': uuids.instance, + 'device_name': '/dev/vda', + 'source_type': 'image', + 'destination_type': 'local', + 'disk_bus': 'virtio', + 'device_type': 'disk', + 'guest_format': 'ext4', + 'boot_index': 0, + 'image_id': 'fake-image-id-1', + 'volume_size': 5}) + + image_driver_bdm = { + 'device_name': '/dev/vda', + 'device_type': 'disk', + 'guest_format': 'ext4', + 'disk_bus': 'virtio', + 'boot_index': 0, + 'image_id': 'fake-image-id-1', + 'size': 5} + def setUp(self): super(TestDriverBlockDevice, self).setUp() self.volume_api = mock.MagicMock(autospec=cinder.API) @@ -219,6 +241,8 @@ class TestDriverBlockDevice(test.NoDBTestCase): # create bdm objects for testing self.swap_bdm = fake_block_device.fake_bdm_object( self.context, self.swap_bdm_dict) + self.image_bdm = fake_block_device.fake_bdm_object( + self.context, self.image_bdm_dict) self.ephemeral_bdm = fake_block_device.fake_bdm_object( self.context, self.ephemeral_bdm_dict) self.volume_bdm = fake_block_device.fake_bdm_object( @@ -337,6 +361,10 @@ class TestDriverBlockDevice(test.NoDBTestCase): if field == 'attachment_id': # Must set UUID values on UUID fields. fake_value = ATTACHMENT_ID + elif isinstance(test_bdm._bdm_obj.fields[fld], + fields.UUIDField): + # Generically handle other UUID fields. + fake_value = uuids.fake_value else: fake_value = 'fake_changed_value' test_bdm[field] = fake_value @@ -377,6 +405,20 @@ class TestDriverBlockDevice(test.NoDBTestCase): def test_driver_swap_default_size(self): self._test_driver_default_size('swap') + def test_driver_image_block_device(self): + self._test_driver_device("image") + + def test_driver_image_default_size(self): + self._test_driver_default_size('image') + + def test_driver_image_block_device_destination_not_local(self): + self._test_driver_device('image') + bdm = self.image_bdm_dict.copy() + bdm['destination_type'] = 'volume' + self.assertRaises(driver_block_device._InvalidType, + self.driver_classes['image'], + fake_block_device.fake_bdm_object(self.context, bdm)) + def test_driver_ephemeral_block_device(self): self._test_driver_device("ephemeral") @@ -406,7 +448,7 @@ class TestDriverBlockDevice(test.NoDBTestCase): self.assertEqual(test_bdm.volume_size, 3) self.assertEqual('fake-snapshot-id-1', test_bdm.get('snapshot_id')) - def test_driver_image_block_device(self): + def test_driver_volume_image_block_device(self): self._test_driver_device('volimage') test_bdm = self.driver_classes['volimage']( @@ -416,7 +458,7 @@ class TestDriverBlockDevice(test.NoDBTestCase): self.assertEqual(test_bdm.volume_size, 1) self.assertEqual('fake-image-id-1', test_bdm.get('image_id')) - def test_driver_image_block_device_destination_local(self): + def test_driver_volume_image_block_device_destination_local(self): self._test_driver_device('volimage') bdm = self.volimage_bdm_dict.copy() bdm['destination_type'] = 'local' @@ -1263,12 +1305,8 @@ class TestDriverBlockDevice(test.NoDBTestCase): def test_is_implemented(self): for bdm in (self.volimage_bdm, self.volume_bdm, self.swap_bdm, - self.ephemeral_bdm, self.volsnapshot_bdm): + self.ephemeral_bdm, self.volsnapshot_bdm, self.image_bdm): self.assertTrue(driver_block_device.is_implemented(bdm)) - local_image = self.volimage_bdm_dict.copy() - local_image['destination_type'] = 'local' - self.assertFalse(driver_block_device.is_implemented( - fake_block_device.fake_bdm_object(self.context, local_image))) def test_is_block_device_mapping(self): test_swap = self.driver_classes['swap'](self.swap_bdm) diff --git a/nova/virt/block_device.py b/nova/virt/block_device.py index 4a4170317462..df354ec65d30 100644 --- a/nova/virt/block_device.py +++ b/nova/virt/block_device.py @@ -227,6 +227,36 @@ class DriverSwapBlockDevice(DriverBlockDevice): }) +class DriverImageBlockDevice(DriverBlockDevice): + _valid_source = 'image' + _proxy_as_attr_inherited = set(['image_id']) + _new_only_fields = set([ + 'disk_bus', + 'device_type', + 'guest_format', + 'boot_index', + ]) + _fields = set([ + 'device_name', + 'size']) | _new_only_fields + _legacy_fields = ( + _fields - _new_only_fields | set(['num', 'virtual_name'])) + + def _transform(self): + if (not self._bdm_obj.get('source_type') == 'image' or + not self._bdm_obj.get('destination_type') == 'local'): + raise _InvalidType + self.update({ + 'device_name': self._bdm_obj.device_name, + 'size': self._bdm_obj.volume_size or 0, + 'disk_bus': self._bdm_obj.disk_bus, + 'device_type': self._bdm_obj.device_type, + 'guest_format': self._bdm_obj.guest_format, + 'image_id': self._bdm_obj.image_id, + 'boot_index': 0, + }) + + class DriverEphemeralBlockDevice(DriverBlockDevice): _new_only_fields = set(['disk_bus', 'device_type', 'guest_format']) _fields = set(['device_name', 'size']) | _new_only_fields @@ -802,15 +832,15 @@ def _convert_block_devices(device_type, block_device_mapping): convert_swap = functools.partial(_convert_block_devices, DriverSwapBlockDevice) +convert_local_images = functools.partial(_convert_block_devices, + DriverImageBlockDevice) convert_ephemerals = functools.partial(_convert_block_devices, DriverEphemeralBlockDevice) - convert_volumes = functools.partial(_convert_block_devices, DriverVolumeBlockDevice) - convert_snapshots = functools.partial(_convert_block_devices, DriverVolSnapshotBlockDevice) @@ -897,9 +927,15 @@ def get_swap(transformed_list): return None -_IMPLEMENTED_CLASSES = (DriverSwapBlockDevice, DriverEphemeralBlockDevice, - DriverVolumeBlockDevice, DriverVolSnapshotBlockDevice, - DriverVolImageBlockDevice, DriverVolBlankBlockDevice) +_IMPLEMENTED_CLASSES = ( + DriverSwapBlockDevice, + DriverEphemeralBlockDevice, + DriverVolumeBlockDevice, + DriverVolSnapshotBlockDevice, + DriverVolImageBlockDevice, + DriverVolBlankBlockDevice, + DriverImageBlockDevice +) def is_implemented(bdm): @@ -912,6 +948,10 @@ def is_implemented(bdm): return False +def is_local_image(bdm): + return bdm.source_type == 'image' and bdm.destination_type == 'local' + + def is_block_device_mapping(bdm): return (bdm.source_type in ('image', 'volume', 'snapshot', 'blank') and bdm.destination_type == 'volume' and diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 23d0b54f1887..301d93b14816 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -44,6 +44,7 @@ def get_block_device_info(instance, block_device_mapping): of a dict containing the following keys: - root_device_name: device name of the root disk + - image: An instance of DriverImageBlockDevice or None - ephemerals: a (potentially empty) list of DriverEphemeralBlockDevice instances - swap: An instance of DriverSwapBlockDevice or None @@ -54,6 +55,8 @@ def get_block_device_info(instance, block_device_mapping): from nova.virt import block_device as virt_block_device return { 'root_device_name': instance.root_device_name, + 'image': virt_block_device.convert_local_images( + block_device_mapping), 'ephemerals': virt_block_device.convert_ephemerals( block_device_mapping), 'block_device_mapping': @@ -79,6 +82,14 @@ def swap_is_usable(swap): return swap and swap['device_name'] and swap['swap_size'] > 0 +def block_device_info_get_image(block_device_info): + block_device_info = block_device_info or {} + # get_disk_mapping() supports block_device_info=None and thus requires that + # we return a list here. + image = block_device_info.get('image') or [] + return image + + def block_device_info_get_ephemerals(block_device_info): block_device_info = block_device_info or {} ephemerals = block_device_info.get('ephemerals') or [] diff --git a/nova/virt/libvirt/blockinfo.py b/nova/virt/libvirt/blockinfo.py index f86a9c461c87..9a4aa671be33 100644 --- a/nova/virt/libvirt/blockinfo.py +++ b/nova/virt/libvirt/blockinfo.py @@ -414,13 +414,7 @@ def get_device_name(bdm): def get_root_info(instance, virt_type, image_meta, root_bdm, disk_bus, cdrom_bus, root_device_name=None): - # NOTE (ndipanov): This is a hack to avoid considering an image - # BDM with local target, as we don't support them - # yet. Only applies when passed non-driver format - no_root_bdm = (not root_bdm or ( - root_bdm.get('source_type') == 'image' and - root_bdm.get('destination_type') == 'local')) - if no_root_bdm: + if root_bdm is None: # NOTE(mriedem): In case the image_meta object was constructed from # an empty dict, like in the case of evacuate, we have to first check # if disk_format is set on the ImageMeta object. @@ -452,10 +446,13 @@ def default_device_names(virt_type, context, instance, block_device_info, image_meta): get_disk_info(virt_type, instance, image_meta, block_device_info) - for driver_bdm in itertools.chain(block_device_info['ephemerals'], - [block_device_info['swap']] if - block_device_info['swap'] else [], - block_device_info['block_device_mapping']): + for driver_bdm in itertools.chain( + block_device_info['image'], + block_device_info['ephemerals'], + [block_device_info['swap']] if + block_device_info['swap'] else [], + block_device_info['block_device_mapping'] + ): driver_bdm.save() @@ -563,41 +560,48 @@ def _get_disk_mapping(virt_type, instance, disk_bus, cdrom_bus, image_meta, :returns: Disk mapping for the given instance. """ mapping = {} - pre_assigned_device_names = \ - [block_device.strip_dev(get_device_name(bdm)) for bdm in itertools.chain( + + driver_bdms = itertools.chain( + driver.block_device_info_get_image(block_device_info), driver.block_device_info_get_ephemerals(block_device_info), [driver.block_device_info_get_swap(block_device_info)], - driver.block_device_info_get_mapping(block_device_info)) - if get_device_name(bdm)] + driver.block_device_info_get_mapping(block_device_info) + ) - # NOTE (ndipanov): root_bdm can be None when we boot from image - # as there is no driver representation of local targeted images - # and they will not be in block_device_info list. - root_bdm = block_device.get_root_bdm( - driver.block_device_info_get_mapping(block_device_info)) + pre_assigned_device_names = [ + block_device.strip_dev(get_device_name(bdm)) + for bdm in driver_bdms if get_device_name(bdm) + ] + # Try to find the root driver bdm, either an image based disk or volume + root_bdm = None + if any(driver.block_device_info_get_image(block_device_info)): + root_bdm = driver.block_device_info_get_image(block_device_info)[0] + elif driver.block_device_info_get_mapping(block_device_info): + root_bdm = block_device.get_root_bdm( + driver.block_device_info_get_mapping(block_device_info)) root_device_name = block_device.strip_dev( driver.block_device_info_get_root_device(block_device_info)) root_info = get_root_info( instance, virt_type, image_meta, root_bdm, disk_bus, cdrom_bus, root_device_name) - mapping['root'] = root_info - # NOTE (ndipanov): This implicitly relies on image->local BDMs not - # being considered in the driver layer - so missing - # bdm with boot_index 0 means - use image, unless it was - # overridden. This can happen when using legacy syntax and - # no root_device_name is set on the instance. - if not root_bdm and not block_device.volume_in_mapping(root_info['dev'], - block_device_info): - mapping['disk'] = root_info - elif root_bdm: - # NOTE (ft): If device name is not set in root bdm, root_info has a - # generated one. We have to copy device name to root bdm to prevent its - # second generation in loop through bdms. If device name is already - # set, nothing is changed. + + # NOTE (ft): If device name is not set in root bdm, root_info has a + # generated one. We have to copy device name to root bdm to prevent its + # second generation in loop through bdms. If device name is already + # set, nothing is changed. + # NOTE(melwitt): root_bdm can be None in the case of a ISO root device, for + # example. + if root_bdm: update_bdm(root_bdm, root_info) + if ( + driver.block_device_info_get_image(block_device_info) or + root_bdm is None + ): + mapping['disk'] = root_info + default_eph = get_default_ephemeral_info(instance, disk_bus, block_device_info, mapping) if default_eph: