Merge "libvirt: Support boot from volume stable device instance rescue"

This commit is contained in:
Zuul 2020-04-11 11:31:51 +00:00 committed by Gerrit Code Review
commit 1d0f934cbb
3 changed files with 145 additions and 1 deletions

View File

@ -527,6 +527,7 @@ class ProviderUsageBaseTestCase(test.TestCase, InstanceHelperMixin):
os_traits.COMPUTE_IMAGE_TYPE_ISO, os_traits.COMPUTE_IMAGE_TYPE_ISO,
os_traits.COMPUTE_IMAGE_TYPE_QCOW2, os_traits.COMPUTE_IMAGE_TYPE_QCOW2,
os_traits.COMPUTE_IMAGE_TYPE_RAW, os_traits.COMPUTE_IMAGE_TYPE_RAW,
os_traits.COMPUTE_RESCUE_BFV,
] ]
]) ])

View File

@ -22823,6 +22823,141 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
query = "devices/disk[source/@file = '%s']/boot/@order" % disk_path query = "devices/disk[source/@file = '%s']/boot/@order" % disk_path
self.assertEqual('1', domain.xpath(query)[0]) self.assertEqual('1', domain.xpath(query)[0])
def test_supports_bfv_rescue_capability(self):
"""Assert that the supports_bfv_rescue capability is set"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
self.assertTrue(drvr.capabilities.get('supports_bfv_rescue'))
def test_rescue_stable_device_bfv_without_instance_image_ref(self):
"""Assert that image_meta is fetched from the bdms for bfv instances"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
# Set instance.image_ref to None for this BFV instance
instance = self._create_instance({'config_drive': str(True)})
instance.image_ref = None
rescue_image_meta = objects.ImageMeta.from_dict(
{'id': uuids.rescue_image_id,
'name': 'rescue',
'properties': {'hw_rescue_device': 'disk',
'hw_rescue_bus': 'virtio'}})
bdm = objects.BlockDeviceMapping(self.context,
**fake_block_device.FakeDbBlockDeviceDict({
'id': 1,
'image_id': uuids.bdm_image_id,
'source_type': 'image',
'destination_type': 'volume',
'device_name': '/dev/vda',
'boot_index': 0}))
bdms = driver_block_device.convert_images([bdm])
block_device_info = {'root_device_name': '/dev/vda',
'ephemerals': [],
'swap': None,
'block_device_mapping': bdms}
network_info = _fake_network_info(self)
disk_info = {'mapping': {}}
with test.nested(
mock.patch.object(drvr, '_create_domain'),
mock.patch.object(drvr, '_destroy'),
mock.patch.object(drvr, '_get_guest_xml'),
mock.patch.object(drvr, '_create_image'),
mock.patch.object(drvr, '_get_existing_domain_xml'),
mock.patch.object(libvirt_utils, 'write_to_file'),
mock.patch.object(libvirt_utils, 'get_instance_path'),
mock.patch('nova.virt.libvirt.blockinfo.get_disk_info'),
mock.patch('nova.image.glance.API.get'),
mock.patch('nova.objects.image_meta.ImageMeta.from_dict')
) as (mock_create, mock_destroy, mock_get_guest_xml, mock_create_image,
mock_get_existing_xml, mock_write, mock_inst_path,
mock_get_disk_info, mock_image_get, mock_from_dict):
self.flags(virt_type='kvm', group='libvirt')
mock_image_get.return_value = mock.sentinel.bdm_image_meta_dict
mock_from_dict.return_value = mock.sentinel.bdm_image_meta
mock_get_disk_info.return_value = disk_info
drvr.rescue(self.context, instance, network_info,
rescue_image_meta, mock.sentinel.rescue_password,
block_device_info)
# Assert that we fetch image metadata from Glance using the image
# uuid stashed in the BDM and build an image_meta object using the
# returned dict.
mock_image_get.assert_called_once_with(
self.context, uuids.bdm_image_id)
mock_from_dict.assert_called_once_with(
mock.sentinel.bdm_image_meta_dict)
# Assert that get_disk_info is then called using this object
mock_get_disk_info.assert_called_once_with(
'kvm', instance, mock.sentinel.bdm_image_meta, rescue=True,
block_device_info=block_device_info,
rescue_image_meta=rescue_image_meta)
# Assert that this object is also used when building guest XML
mock_get_guest_xml.assert_called_once_with(
self.context, instance, network_info, disk_info,
mock.sentinel.bdm_image_meta, rescue=mock.ANY, mdevs=mock.ANY,
block_device_info=block_device_info)
def test_rescue_stable_device_bfv(self):
"""Assert the disk layout when rescuing BFV instances"""
# NOTE(lyarwood): instance.image_ref is left in place here to allow us
# to reuse the _test_rescue test method as we only care about the
# eventual disk layout and not how we get the image_meta in this test.
instance = self._create_instance({'config_drive': str(True)})
# Set ephemeral_gb to 0 to avoid any disk.local disks for being used
instance.ephemeral_gb = 0
inst_image_meta_dict = {'id': uuids.image_id, 'name': 'fake'}
rescue_image_meta_dict = {
'id': uuids.rescue_image_id,
'name': 'rescue',
'properties': {'hw_rescue_device': 'disk',
'hw_rescue_bus': 'virtio'}}
conn_info = {
'driver_volume_type': 'iscsi',
'data': {'device_path': '/dev/sdb'}}
bdm = objects.BlockDeviceMapping(
self.context,
**fake_block_device.FakeDbBlockDeviceDict({
'id': 1,
'source_type': 'volume',
'destination_type': 'volume',
'device_name': '/dev/vda'}))
bdms = driver_block_device.convert_volumes([bdm])
block_device_info = {'root_device_name': '/dev/vda',
'ephemerals': [],
'swap': None,
'block_device_mapping': bdms}
bdm = block_device_info['block_device_mapping'][0]
bdm['connection_info'] = conn_info
backend, domain = self._test_rescue(
instance,
image_meta_dict=rescue_image_meta_dict,
instance_image_meta_dict=inst_image_meta_dict,
block_device_info=block_device_info)
# Assert that we created the expected set of disks, and no others
self.assertEqual(['disk.rescue', 'kernel.rescue', 'ramdisk.rescue'],
sorted(backend.created_disks.keys()))
# Assert that the original disks are presented first with the rescue
# disk attached as the final device in the domain.
expected_disk_paths = [backend.disks['disk.config'].path,
'/dev/sdb', backend.disks['disk.rescue'].path]
query = 'devices/disk/source/@*[name()="file" or name()="dev"]'
disk_paths = domain.xpath(query)
self.assertEqual(expected_disk_paths, disk_paths)
# Assert that the disk.rescue device has a boot order of 1
disk_path = backend.disks['disk.rescue'].path
query = "devices/disk[source/@file = '%s']/boot/@order" % disk_path
self.assertEqual('1', domain.xpath(query)[0])
@mock.patch.object(libvirt_utils, 'get_instance_path') @mock.patch.object(libvirt_utils, 'get_instance_path')
@mock.patch.object(libvirt_utils, 'load_file') @mock.patch.object(libvirt_utils, 'load_file')
@mock.patch.object(host.Host, '_get_domain') @mock.patch.object(host.Host, '_get_domain')

View File

@ -333,6 +333,7 @@ class LibvirtDriver(driver.ComputeDriver):
"supports_image_type_ploop": requires_ploop_image, "supports_image_type_ploop": requires_ploop_image,
"supports_pcpus": True, "supports_pcpus": True,
"supports_accelerators": True, "supports_accelerators": True,
"supports_bfv_rescue": True,
} }
super(LibvirtDriver, self).__init__(virtapi) super(LibvirtDriver, self).__init__(virtapi)
@ -3482,7 +3483,14 @@ class LibvirtDriver(driver.ComputeDriver):
image_meta = objects.ImageMeta.from_image_ref( image_meta = objects.ImageMeta.from_image_ref(
context, self._image_api, instance.image_ref) context, self._image_api, instance.image_ref)
else: else:
image_meta = objects.ImageMeta.from_dict({}) # NOTE(lyarwood): If instance.image_ref isn't set attempt to
# lookup the original image_meta from the bdms. This will
# return an empty dict if no valid image_meta is found.
image_meta_dict = utils.get_bdm_image_metadata(
context, self._image_api, self._volume_api,
block_device_info['block_device_mapping'],
legacy_bdm=False)
image_meta = objects.ImageMeta.from_dict(image_meta_dict)
else: else:
LOG.info("Attempting an unstable device rescue", instance=instance) LOG.info("Attempting an unstable device rescue", instance=instance)