Merge "Support rescuing an instance with shares"

This commit is contained in:
Zuul
2024-11-29 22:44:42 +00:00
committed by Gerrit Code Review
11 changed files with 185 additions and 40 deletions
+5 -1
View File
@@ -5038,6 +5038,8 @@ class ComputeManager(manager.Manager):
block_device_info = self._get_instance_block_device_info(
context, instance, bdms=bdms)
share_info = self._get_share_info(context, instance)
extra_usage_info = {'rescue_image_name':
self._get_image_name(rescue_image_meta)}
self._notify_about_instance_usage(context, instance,
@@ -5050,9 +5052,11 @@ class ComputeManager(manager.Manager):
try:
self._power_off_instance(context, instance, clean_shutdown)
self._mount_all_shares(context, instance, share_info)
self.driver.rescue(context, instance, network_info,
rescue_image_meta, admin_password,
block_device_info)
block_device_info, share_info)
except Exception as e:
LOG.exception("Error trying to Rescue Instance",
instance=instance)
+124 -13
View File
@@ -2417,7 +2417,7 @@ class ComputeTestCase(BaseTestCase,
'unrescued': False}
def fake_rescue(self, context, instance_ref, network_info, image_meta,
rescue_password, block_device_info):
rescue_password, block_device_info, share_info):
called['rescued'] = True
self.stub_out('nova.virt.fake.FakeDriver.rescue', fake_rescue)
@@ -2448,7 +2448,7 @@ class ComputeTestCase(BaseTestCase,
def test_rescue_notifications(self, mock_context, mock_notify):
# Ensure notifications on instance rescue.
def fake_rescue(self, context, instance_ref, network_info, image_meta,
rescue_password, block_device_info):
rescue_password, block_device_info, share_info):
pass
self.stub_out('nova.virt.fake.FakeDriver.rescue', fake_rescue)
@@ -2545,13 +2545,14 @@ class ComputeTestCase(BaseTestCase,
self.compute.terminate_instance(self.context, instance, [])
@mock.patch('nova.compute.manager.ComputeManager._get_share_info')
@mock.patch.object(nova.compute.manager.ComputeManager,
'_get_instance_block_device_info')
@mock.patch.object(fake.FakeDriver, 'power_off')
@mock.patch.object(fake.FakeDriver, 'rescue')
@mock.patch.object(compute_manager.ComputeManager, '_get_rescue_image')
def test_rescue_handle_err(self, mock_get, mock_rescue, mock_power_off,
mock_get_block_info):
mock_get_block_info, mock_get_share_info):
# If the driver fails to rescue, instance state should got to ERROR
# and the exception should be converted to InstanceNotRescuable
inst_obj = self._create_fake_instance_obj()
@@ -2562,6 +2563,9 @@ class ComputeTestCase(BaseTestCase,
expected_message = ('Instance %s cannot be rescued: '
'Driver Error: Try again later' % inst_obj.uuid)
share_info = objects.ShareMappingList()
mock_get_share_info.return_value = share_info
with testtools.ExpectedException(
exception.InstanceNotRescuable, expected_message):
self.compute.rescue_instance(
@@ -2573,18 +2577,25 @@ class ComputeTestCase(BaseTestCase,
mock_get.assert_called_once_with(mock.ANY, inst_obj, mock.ANY)
mock_rescue.assert_called_once_with(mock.ANY, inst_obj, [],
mock.ANY, 'password',
mock.sentinel.block_device_info)
mock.sentinel.block_device_info,
share_info)
@mock.patch.object(nova.virt.fake.FakeDriver, "mount_share")
@mock.patch('nova.compute.manager.ComputeManager._get_share_info')
@mock.patch.object(nova.compute.manager.ComputeManager,
'_get_instance_block_device_info')
@mock.patch.object(image_api.API, "get")
@mock.patch.object(fake.FakeDriver, 'power_off')
@mock.patch.object(nova.virt.fake.FakeDriver, "rescue")
def test_rescue_with_image_specified(self, mock_rescue, mock_power_off,
mock_image_get, mock_get_block_info):
mock_image_get, mock_get_block_info, mock_get_share_info,
mock_drv_mount):
image_ref = uuids.image_instance
rescue_image_meta = {}
params = {"task_state": task_states.RESCUING}
share_info = objects.ShareMappingList()
mock_get_share_info.return_value = share_info
instance = self._create_fake_instance_obj(params=params)
ctxt = context.get_admin_context()
@@ -2599,24 +2610,30 @@ class ComputeTestCase(BaseTestCase,
clean_shutdown=True)
mock_image_get.assert_called_with(ctxt, image_ref)
mock_drv_mount.assert_not_called()
mock_rescue.assert_called_with(ctxt, instance, [],
test.MatchType(objects.ImageMeta),
'password',
mock.sentinel.block_device_info)
mock.sentinel.block_device_info,
share_info)
self.compute.terminate_instance(ctxt, instance, [])
@mock.patch.object(nova.virt.fake.FakeDriver, "mount_share")
@mock.patch('nova.compute.manager.ComputeManager._get_share_info')
@mock.patch.object(nova.compute.manager.ComputeManager,
'_get_instance_block_device_info')
@mock.patch.object(image_api.API, "get")
@mock.patch.object(fake.FakeDriver, 'power_off')
@mock.patch.object(nova.virt.fake.FakeDriver, "rescue")
def test_rescue_with_base_image_when_image_not_specified(self,
mock_rescue, mock_power_off, mock_image_get, mock_get_block_info):
image_ref = FAKE_IMAGE_REF
system_meta = {"image_base_image_ref": image_ref}
def test_rescue_with_image_specified_and_share(
self, mock_rescue, mock_power_off, mock_image_get, mock_get_block_info,
mock_get_share_info, mock_drv_mount):
image_ref = uuids.image_instance
rescue_image_meta = {}
params = {"task_state": task_states.RESCUING,
"system_metadata": system_meta}
params = {"task_state": task_states.RESCUING}
share_info = self.fake_share_info()
mock_get_share_info.return_value = share_info
instance = self._create_fake_instance_obj(params=params)
ctxt = context.get_admin_context()
@@ -2626,6 +2643,99 @@ class ComputeTestCase(BaseTestCase,
mock_get_block_info.return_value = mock.sentinel.block_device_info
mock_image_get.return_value = rescue_image_meta
mock_get_share_info.return_value = share_info
self.compute.rescue_instance(mock_context, instance=instance,
rescue_password="password", rescue_image_ref=image_ref,
clean_shutdown=True)
mock_image_get.assert_called_with(ctxt, image_ref)
mock_drv_mount.assert_called_with(ctxt, instance, share_info[0])
mock_rescue.assert_called_with(
ctxt,
instance,
[],
test.MatchType(objects.ImageMeta),
"password",
mock.sentinel.block_device_info,
share_info,
)
self.compute.terminate_instance(ctxt, instance, [])
@mock.patch('nova.objects.instance_fault.InstanceFault.create')
@mock.patch.object(nova.virt.fake.FakeDriver, "mount_share")
@mock.patch('nova.compute.manager.ComputeManager._get_share_info')
@mock.patch.object(nova.compute.manager.ComputeManager,
'_get_instance_block_device_info')
@mock.patch.object(image_api.API, "get")
@mock.patch.object(fake.FakeDriver, 'power_off')
@mock.patch.object(nova.virt.fake.FakeDriver, "rescue")
def test_rescue_with_image_specified_and_mount_error(
self, mock_rescue, mock_power_off, mock_image_get, mock_get_block_info,
mock_get_share_info, mock_drv_mount, mock_db_fault):
image_ref = uuids.image_instance
rescue_image_meta = {}
params = {"task_state": task_states.RESCUING}
share_info = self.fake_share_info()
mock_get_share_info.return_value = share_info
instance = self._create_fake_instance_obj(params=params)
ctxt = context.get_admin_context()
mock_context = mock.Mock()
mock_context.elevated.return_value = ctxt
mock_get_block_info.return_value = mock.sentinel.block_device_info
mock_image_get.return_value = rescue_image_meta
mock_drv_mount.side_effect = exception.ShareMountError(
share_id=share_info[0].share_id,
server_id=instance.uuid,
reason="fake_reason",
)
self.assertRaises(
exception.InstanceNotRescuable,
self.compute.rescue_instance,
mock_context,
instance=instance,
rescue_password="password",
rescue_image_ref=image_ref,
clean_shutdown=True,
)
self.assertEqual(instance.vm_state, 'error')
self.compute.terminate_instance(ctxt, instance, [])
@mock.patch('nova.compute.manager.ComputeManager._get_share_info')
@mock.patch.object(nova.compute.manager.ComputeManager,
'_get_instance_block_device_info')
@mock.patch.object(image_api.API, "get")
@mock.patch.object(fake.FakeDriver, 'power_off')
@mock.patch.object(nova.virt.fake.FakeDriver, "rescue")
def test_rescue_with_base_image_when_image_not_specified(self,
mock_rescue, mock_power_off, mock_image_get, mock_get_block_info,
mock_get_share_info):
image_ref = FAKE_IMAGE_REF
system_meta = {"image_base_image_ref": image_ref}
rescue_image_meta = {}
params = {"task_state": task_states.RESCUING,
"system_metadata": system_meta}
share_info = objects.ShareMappingList()
mock_get_share_info.return_value = share_info
instance = self._create_fake_instance_obj(params=params)
ctxt = context.get_admin_context()
mock_context = mock.Mock()
mock_context.elevated.return_value = ctxt
mock_get_block_info.return_value = mock.sentinel.block_device_info
mock_image_get.return_value = rescue_image_meta
share_info = objects.ShareMappingList()
mock_get_share_info.return_value = share_info
self.compute.rescue_instance(mock_context, instance=instance,
rescue_password="password",
rescue_image_ref=None,
@@ -2636,7 +2746,8 @@ class ComputeTestCase(BaseTestCase,
mock_rescue.assert_called_with(ctxt, instance, [],
test.MatchType(objects.ImageMeta),
'password',
mock.sentinel.block_device_info)
mock.sentinel.block_device_info,
share_info)
self.compute.terminate_instance(self.context, instance, [])
def test_power_on(self):
+9 -3
View File
@@ -5706,6 +5706,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase,
fake_nw_info = network_model.NetworkInfo()
rescue_image_meta = objects.ImageMeta.from_dict(
{'id': uuids.image_id, 'name': uuids.image_name})
with test.nested(
mock.patch.object(self.context, 'elevated',
return_value=self.context),
@@ -5724,13 +5725,18 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase,
mock.patch.object(compute_utils, 'notify_usage_exists'),
mock.patch.object(self.compute, '_get_power_state',
return_value=power_state.RUNNING),
mock.patch.object(instance, 'save')
mock.patch.object(instance, 'save'),
mock.patch('nova.compute.manager.ComputeManager._get_share_info')
) as (
elevated_context, get_nw_info, get_rescue_image,
get_bdm_list, get_block_info, notify_instance_usage,
power_off_instance, driver_rescue, notify_usage_exists,
get_power_state, instance_save
get_power_state, instance_save, mock_get_share_info
):
share_info = objects.ShareMappingList()
mock_get_share_info.return_value = share_info
self.compute.rescue_instance(
self.context, instance, rescue_password='verybadpass',
rescue_image_ref=None, clean_shutdown=clean_shutdown)
@@ -5765,7 +5771,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase,
driver_rescue.assert_called_once_with(
self.context, instance, fake_nw_info, rescue_image_meta,
'verybadpass', mock.sentinel.block_device_info)
'verybadpass', mock.sentinel.block_device_info, share_info)
notify_usage_exists.assert_called_once_with(self.compute.notifier,
self.context, instance, 'fake-mini', current_period=True)
+9 -4
View File
@@ -2645,9 +2645,11 @@ class IronicDriverSyncTestCase(IronicDriverTestCase):
fake_looping_call = FakeLoopingCall()
mock_looping.return_value = fake_looping_call
share_info = objects.ShareMappingList()
instance = fake_instance.fake_instance_obj(self.ctx, node=node.id)
self.driver.rescue(self.ctx, instance, None, None, 'xyz', None)
self.driver.rescue(self.ctx, instance, None, None, 'xyz', None,
share_info)
self.mock_conn.set_node_provision_state.assert_called_once_with(
node.id, 'rescue', rescue_password='xyz',
)
@@ -2660,12 +2662,13 @@ class IronicDriverSyncTestCase(IronicDriverTestCase):
mock_looping.return_value = fake_looping_call
self.mock_conn.set_node_provision_state.side_effect = \
sdk_exc.BadRequestException()
share_info = objects.ShareMappingList()
instance = fake_instance.fake_instance_obj(self.ctx, node=node.id)
self.assertRaises(
exception.InstanceRescueFailure,
self.driver.rescue,
self.ctx, instance, None, None, 'xyz', None,
self.ctx, instance, None, None, 'xyz', None, share_info
)
self.mock_conn.set_node_provision_state.assert_called_once_with(
node.id, 'rescue', rescue_password='xyz',
@@ -2680,11 +2683,12 @@ class IronicDriverSyncTestCase(IronicDriverTestCase):
fake_validate.side_effect = exception.InstanceNotFound(
instance_id='fake',
)
share_info = objects.ShareMappingList()
self.assertRaises(
exception.InstanceRescueFailure,
self.driver.rescue,
self.ctx, instance, None, None, 'xyz', None,
self.ctx, instance, None, None, 'xyz', None, share_info
)
@mock.patch.object(ironic_driver.IronicDriver,
@@ -2695,12 +2699,13 @@ class IronicDriverSyncTestCase(IronicDriverTestCase):
last_error='rescue failed')
fake_validate.return_value = node
share_info = objects.ShareMappingList()
instance = fake_instance.fake_instance_obj(self.ctx, node=node.id)
self.assertRaises(
exception.InstanceRescueFailure,
self.driver.rescue,
self.ctx, instance, None, None, 'xyz', None,
self.ctx, instance, None, None, 'xyz', None, share_info
)
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
+18 -10
View File
@@ -26207,7 +26207,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
self, instance, mock_instance_metadata, mock_supports_direct_io,
mock_build_device_metadata, mock_set_host_enabled, mock_get_mdev,
mock_get_image_meta_by_ref, image_meta_dict=None, exists=None,
instance_image_meta_dict=None, block_device_info=None,
instance_image_meta_dict=None, block_device_info=None, share_info=None
):
self.flags(instances_path=self.useFixture(fixtures.TempDir()).path)
@@ -26246,9 +26246,11 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
mock.patch.object(self.drvr, '_connect_volume'),
) as (mock_create_guest, mock_connect_volume):
share_info = objects.ShareMappingList()
self.drvr.rescue(self.context, instance,
network_info, image_meta, rescue_password,
block_device_info)
block_device_info, share_info)
self.assertTrue(mock_create_guest.called)
@@ -26378,9 +26380,10 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
# Assert that InstanceNotRescuable is raised for lxc virt_type
self.flags(virt_type='lxc', group='libvirt')
share_info = objects.ShareMappingList()
self.assertRaises(exception.InstanceNotRescuable, self.drvr.rescue,
self.context, instance, network_info,
rescue_image_meta, None, None)
rescue_image_meta, None, None, share_info)
def test_rescue_stable_device(self):
# Assert the imagebackend behaviour and domain device layout
@@ -26452,12 +26455,15 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
'block_device_mapping': bdms}
bdm = block_device_info['block_device_mapping'][0]
bdm['connection_info'] = conn_info
share_info = objects.ShareMappingList()
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)
instance,
image_meta_dict=rescue_image_meta_dict,
instance_image_meta_dict=inst_image_meta_dict,
block_device_info=block_device_info,
share_info=share_info,
)
# Assert that we created the expected set of disks, and no others
self.assertEqual(['disk.rescue', 'kernel.rescue', 'ramdisk.rescue'],
@@ -26526,16 +26532,18 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
) as (
mock_create, mock_destroy, mock_get_guest_xml, mock_create_image,
mock_get_existing_xml, mock_inst_path, mock_get_disk_info,
mock_image_get, mock_from_dict, mock_open,
mock_image_get, mock_from_dict, mock_open
):
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
share_info = nova.objects.share_mapping.ShareMappingList()
drvr.rescue(self.context, instance, network_info,
rescue_image_meta, mock.sentinel.rescue_password,
block_device_info)
block_device_info, share_info=share_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
@@ -26555,7 +26563,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
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)
block_device_info=block_device_info, share_info=share_info)
def test_rescue_stable_device_bfv(self):
"""Assert the disk layout when rescuing BFV instances"""
+4 -2
View File
@@ -284,8 +284,9 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase):
def test_rescue(self):
image_meta = objects.ImageMeta.from_dict({})
instance_ref, network_info = self._get_running_instance()
share_info = objects.ShareMappingList()
self.connection.rescue(self.ctxt, instance_ref, network_info,
image_meta, '', None)
image_meta, '', None, share_info)
@catch_notimplementederror
def test_unrescue_unrescued_instance(self):
@@ -297,8 +298,9 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase):
def test_unrescue_rescued_instance(self, mock_unlink):
image_meta = objects.ImageMeta.from_dict({})
instance_ref, network_info = self._get_running_instance()
share_info = objects.ShareMappingList()
self.connection.rescue(self.ctxt, instance_ref, network_info,
image_meta, '', None)
image_meta, '', None, share_info)
self.connection.unrescue(self.ctxt, instance_ref)
@catch_notimplementederror
+3 -1
View File
@@ -1016,7 +1016,7 @@ class ComputeDriver(object):
raise NotImplementedError()
def rescue(self, context, instance, network_info, image_meta,
rescue_password, block_device_info):
rescue_password, block_device_info, share_info):
"""Rescue the specified instance.
:param nova.context.RequestContext context:
@@ -1030,6 +1030,8 @@ class ComputeDriver(object):
:param rescue_password: new root password to set for rescue.
:param dict block_device_info:
The block device mapping of the instance.
:param nova.objects.share_mapping.ShareMapingList share_info
list of share_mapping
"""
raise NotImplementedError()
+1 -1
View File
@@ -245,7 +245,7 @@ class FakeDriver(driver.ComputeDriver):
pass
def rescue(self, context, instance, network_info, image_meta,
rescue_password, block_device_info):
rescue_password, block_device_info, share_info):
pass
def unrescue(
+3 -1
View File
@@ -2150,7 +2150,7 @@ class IronicDriver(virt_driver.ComputeDriver):
raise exception.IronicAPIVersionNotAvailable(version=version)
def rescue(self, context, instance, network_info, image_meta,
rescue_password, block_device_info):
rescue_password, block_device_info, share_info):
"""Rescue the specified instance.
:param nova.context.RequestContext context:
@@ -2165,6 +2165,8 @@ class IronicDriver(virt_driver.ComputeDriver):
:param rescue_password: new root password to set for rescue.
:param dict block_device_info:
The block device mapping of the instance.
:param nova.objects.share_mapping.ShareMapingList share_info
optional list of share_mapping
:raise InstanceRescueFailure if rescue fails.
"""
LOG.debug('Rescue called for instance', instance=instance)
+8 -3
View File
@@ -4465,7 +4465,7 @@ class LibvirtDriver(driver.ComputeDriver):
)
def rescue(self, context, instance, network_info, image_meta,
rescue_password, block_device_info):
rescue_password, block_device_info, share_info):
"""Loads a VM using rescue images.
A rescue is normally performed when something goes wrong with the
@@ -4496,9 +4496,13 @@ class LibvirtDriver(driver.ComputeDriver):
:param rescue_password: new root password to set for rescue.
:param dict block_device_info:
The block device mapping of the instance.
:param nova.objects.ShareMappingList share_info:
list of share_mapping
"""
instance_dir = libvirt_utils.get_instance_path(instance)
unrescue_xml = self._get_existing_domain_xml(instance, network_info)
unrescue_xml = self._get_existing_domain_xml(
instance, network_info, share_info=share_info)
unrescue_xml_path = os.path.join(instance_dir, 'unrescue.xml')
with open(unrescue_xml_path, 'w') as f:
f.write(unrescue_xml)
@@ -4584,7 +4588,8 @@ class LibvirtDriver(driver.ComputeDriver):
xml = self._get_guest_xml(context, instance, network_info, disk_info,
image_meta, rescue=rescue_images,
mdevs=mdevs,
block_device_info=block_device_info)
block_device_info=block_device_info,
share_info=share_info)
self._destroy(instance)
self._create_guest(
context, xml, instance, post_xml_callback=gen_confdrive,
+1 -1
View File
@@ -656,7 +656,7 @@ class VMwareVCDriver(driver.ComputeDriver):
self._vmops.resume(instance)
def rescue(self, context, instance, network_info, image_meta,
rescue_password, block_device_info):
rescue_password, block_device_info, share_info):
"""Rescue the specified instance."""
self._vmops.rescue(context, instance, network_info, image_meta)