diff --git a/nova/compute/manager.py b/nova/compute/manager.py index eb982464e89e..812b8024bc3a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -4150,6 +4150,11 @@ class ComputeManager(manager.Manager): rescue_image_meta = self._get_rescue_image(context, instance, rescue_image_ref) + bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( + context, instance.uuid) + block_device_info = self._get_instance_block_device_info( + context, instance, bdms=bdms) + extra_usage_info = {'rescue_image_name': self._get_image_name(rescue_image_meta)} self._notify_about_instance_usage(context, instance, @@ -4162,9 +4167,9 @@ class ComputeManager(manager.Manager): try: self._power_off_instance(context, instance, clean_shutdown) - self.driver.rescue(context, instance, - network_info, - rescue_image_meta, admin_password) + self.driver.rescue(context, instance, network_info, + rescue_image_meta, admin_password, + block_device_info) except Exception as e: LOG.exception("Error trying to Rescue Instance", instance=instance) diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index 917a0aeec6d1..8ac5459f5f6e 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -2277,7 +2277,7 @@ class ComputeTestCase(BaseTestCase, 'unrescued': False} def fake_rescue(self, context, instance_ref, network_info, image_meta, - rescue_password): + rescue_password, block_device_info): called['rescued'] = True self.stub_out('nova.virt.fake.FakeDriver.rescue', fake_rescue) @@ -2309,7 +2309,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): + rescue_password, block_device_info): pass self.stub_out('nova.virt.fake.FakeDriver.rescue', fake_rescue) @@ -2406,14 +2406,18 @@ class ComputeTestCase(BaseTestCase, self.compute.terminate_instance(self.context, instance, []) + @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): + def test_rescue_handle_err(self, mock_get, mock_rescue, mock_power_off, + mock_get_block_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() mock_get.return_value = objects.ImageMeta.from_dict({}) + mock_get_block_info.return_value = mock.sentinel.block_device_info mock_rescue.side_effect = RuntimeError("Try again later") expected_message = ('Instance %s cannot be rescued: ' @@ -2429,13 +2433,16 @@ class ComputeTestCase(BaseTestCase, self.assertEqual(vm_states.ERROR, inst_obj.vm_state) 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.ANY, 'password', + mock.sentinel.block_device_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_image_get, mock_get_block_info): image_ref = uuids.image_instance rescue_image_meta = {} params = {"task_state": task_states.RESCUING} @@ -2445,6 +2452,7 @@ class ComputeTestCase(BaseTestCase, 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 self.compute.rescue_instance(mock_context, instance=instance, @@ -2454,14 +2462,17 @@ class ComputeTestCase(BaseTestCase, mock_image_get.assert_called_with(ctxt, image_ref) mock_rescue.assert_called_with(ctxt, instance, [], test.MatchType(objects.ImageMeta), - 'password') + 'password', + mock.sentinel.block_device_info) self.compute.terminate_instance(ctxt, instance, []) + @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_rescue, mock_power_off, mock_image_get, mock_get_block_info): image_ref = FAKE_IMAGE_REF system_meta = {"image_base_image_ref": image_ref} rescue_image_meta = {} @@ -2473,6 +2484,7 @@ class ComputeTestCase(BaseTestCase, 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 self.compute.rescue_instance(mock_context, instance=instance, @@ -2484,7 +2496,8 @@ class ComputeTestCase(BaseTestCase, mock_rescue.assert_called_with(ctxt, instance, [], test.MatchType(objects.ImageMeta), - 'password') + 'password', + mock.sentinel.block_device_info) self.compute.terminate_instance(self.context, instance, []) def test_power_on(self): diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index 4f5a90004146..85b09a93b95f 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -4301,6 +4301,11 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase, return_value=fake_nw_info), mock.patch.object(self.compute, '_get_rescue_image', return_value=rescue_image_meta), + mock.patch.object(objects.BlockDeviceMappingList, + 'get_by_instance_uuid', + return_value=mock.sentinel.bdms), + mock.patch.object(self.compute, '_get_instance_block_device_info', + return_value=mock.sentinel.block_device_info), mock.patch.object(self.compute, '_notify_about_instance_usage'), mock.patch.object(self.compute, '_power_off_instance'), mock.patch.object(self.compute.driver, 'rescue'), @@ -4310,8 +4315,9 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase, mock.patch.object(instance, 'save') ) as ( elevated_context, get_nw_info, get_rescue_image, - notify_instance_usage, power_off_instance, driver_rescue, - notify_usage_exists, get_power_state, instance_save + get_bdm_list, get_block_info, notify_instance_usage, + power_off_instance, driver_rescue, notify_usage_exists, + get_power_state, instance_save ): self.compute.rescue_instance( self.context, instance, rescue_password='verybadpass', @@ -4327,6 +4333,9 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase, get_nw_info.assert_called_once_with(self.context, instance) get_rescue_image.assert_called_once_with( self.context, instance, None) + get_bdm_list.assert_called_once_with(self.context, instance.uuid) + get_block_info.assert_called_once_with(self.context, instance, + bdms=mock.sentinel.bdms) extra_usage_info = {'rescue_image_name': uuids.image_name} notify_calls = [ @@ -4344,7 +4353,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase, driver_rescue.assert_called_once_with( self.context, instance, fake_nw_info, rescue_image_meta, - 'verybadpass') + 'verybadpass', mock.sentinel.block_device_info) notify_usage_exists.assert_called_once_with(self.compute.notifier, self.context, instance, 'fake-mini', current_period=True) diff --git a/nova/tests/unit/virt/ironic/test_driver.py b/nova/tests/unit/virt/ironic/test_driver.py index a381e97c37e2..ef8b696af0ed 100644 --- a/nova/tests/unit/virt/ironic/test_driver.py +++ b/nova/tests/unit/virt/ironic/test_driver.py @@ -3004,7 +3004,7 @@ class IronicDriverSyncTestCase(IronicDriverTestCase): instance = fake_instance.fake_instance_obj(self.ctx, node=node.uuid) - self.driver.rescue(self.ctx, instance, None, None, 'xyz') + self.driver.rescue(self.ctx, instance, None, None, 'xyz', None) mock_sps.assert_called_once_with(node.uuid, 'rescue', rescue_password='xyz') @@ -3021,7 +3021,7 @@ class IronicDriverSyncTestCase(IronicDriverTestCase): self.assertRaises(exception.InstanceRescueFailure, self.driver.rescue, - self.ctx, instance, None, None, 'xyz') + self.ctx, instance, None, None, 'xyz', None) @mock.patch.object(ironic_driver.IronicDriver, '_validate_instance_and_node') @@ -3035,7 +3035,7 @@ class IronicDriverSyncTestCase(IronicDriverTestCase): self.assertRaises(exception.InstanceRescueFailure, self.driver.rescue, - self.ctx, instance, None, None, 'xyz') + self.ctx, instance, None, None, 'xyz', None) @mock.patch.object(ironic_driver.IronicDriver, '_validate_instance_and_node') @@ -3051,7 +3051,7 @@ class IronicDriverSyncTestCase(IronicDriverTestCase): self.assertRaises(exception.InstanceRescueFailure, self.driver.rescue, - self.ctx, instance, None, None, 'xyz') + self.ctx, instance, None, None, 'xyz', None) @mock.patch.object(loopingcall, 'FixedIntervalLoopingCall') @mock.patch.object(FAKE_CLIENT.node, 'set_provision_state') diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 7d7ab94d0ca2..c23760efabfa 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -22576,7 +22576,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin): self.drvr, '_create_domain', side_effect=fake_create_domain) as mock_create_domain: self.drvr.rescue(self.context, instance, - network_info, image_meta, rescue_password) + network_info, image_meta, rescue_password, None) self.assertTrue(mock_create_domain.called) diff --git a/nova/tests/unit/virt/test_virt_drivers.py b/nova/tests/unit/virt/test_virt_drivers.py index c0c433aca7d8..52a0a310fc45 100644 --- a/nova/tests/unit/virt/test_virt_drivers.py +++ b/nova/tests/unit/virt/test_virt_drivers.py @@ -298,7 +298,7 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): image_meta = objects.ImageMeta.from_dict({}) instance_ref, network_info = self._get_running_instance() self.connection.rescue(self.ctxt, instance_ref, network_info, - image_meta, '') + image_meta, '', None) @catch_notimplementederror @mock.patch('os.unlink') @@ -313,7 +313,7 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): image_meta = objects.ImageMeta.from_dict({}) instance_ref, network_info = self._get_running_instance() self.connection.rescue(self.ctxt, instance_ref, network_info, - image_meta, '') + image_meta, '', None) self.connection.unrescue(instance_ref, network_info) @catch_notimplementederror diff --git a/nova/tests/unit/virt/xenapi/test_xenapi.py b/nova/tests/unit/virt/xenapi/test_xenapi.py index 75d0f5821c4a..e47a6e97d477 100644 --- a/nova/tests/unit/virt/xenapi/test_xenapi.py +++ b/nova/tests/unit/virt/xenapi/test_xenapi.py @@ -1315,7 +1315,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase, {'id': IMAGE_VHD, 'disk_format': 'vhd', 'properties': {'vm_mode': 'xen'}}) - conn.rescue(self.context, instance, [], image_meta, '') + conn.rescue(self.context, instance, [], image_meta, '', None) vm = xenapi_fake.get_record('VM', vm_ref) rescue_name = "%s-rescue" % vm["name_label"] @@ -1351,7 +1351,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase, self.conn._vmops, '_start', side_effect=test.TestingException('Start Error')): self.assertRaises(test.TestingException, self.conn.rescue, - self.context, instance, [], image_meta, '') + self.context, instance, [], image_meta, '', []) # confirm original disk still exists: vdi_ref2, vdi_rec2 = vm_utils.get_vdi_for_vm_safely(session, diff --git a/nova/virt/driver.py b/nova/virt/driver.py index f1deacad9499..484e42da1d5f 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -850,7 +850,7 @@ class ComputeDriver(object): raise NotImplementedError() def rescue(self, context, instance, network_info, image_meta, - rescue_password): + rescue_password, block_device_info): """Rescue the specified instance. :param nova.context.RequestContext context: @@ -862,6 +862,8 @@ class ComputeDriver(object): :param nova.objects.ImageMeta image_meta: The metadata of the image of the instance. :param rescue_password: new root password to set for rescue. + :param dict block_device_info: + The block device mapping of the instance. """ raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index d0b811045e65..2c668af8755e 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -238,7 +238,7 @@ class FakeDriver(driver.ComputeDriver): pass def rescue(self, context, instance, network_info, image_meta, - rescue_password): + rescue_password, block_device_info): pass def unrescue(self, instance, network_info): diff --git a/nova/virt/hyperv/driver.py b/nova/virt/hyperv/driver.py index e47a962d2aba..2d34aa9e07cb 100644 --- a/nova/virt/hyperv/driver.py +++ b/nova/virt/hyperv/driver.py @@ -357,7 +357,7 @@ class HyperVDriver(driver.ComputeDriver): return self._vmops.detach_interface(instance, vif) def rescue(self, context, instance, network_info, image_meta, - rescue_password): + rescue_password, block_device_info): self._vmops.rescue_instance(context, instance, network_info, image_meta, rescue_password) diff --git a/nova/virt/ironic/driver.py b/nova/virt/ironic/driver.py index f8bf2d70c70a..c1ad4996c3fc 100644 --- a/nova/virt/ironic/driver.py +++ b/nova/virt/ironic/driver.py @@ -2120,7 +2120,7 @@ class IronicDriver(virt_driver.ComputeDriver): version=max_version) def rescue(self, context, instance, network_info, image_meta, - rescue_password): + rescue_password, block_device_info): """Rescue the specified instance. :param nova.context.RequestContext context: @@ -2133,6 +2133,8 @@ class IronicDriver(virt_driver.ComputeDriver): :param nova.objects.ImageMeta image_meta: The metadata of the image of the instance. Ignored by this driver. :param rescue_password: new root password to set for rescue. + :param dict block_device_info: + The block device mapping of the instance. :raise InstanceRescueFailure if rescue fails. """ LOG.debug('Rescue called for instance', instance=instance) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 89cc3ddced9f..c5e9fbb58e3f 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -3409,7 +3409,7 @@ class LibvirtDriver(driver.ComputeDriver): self._hard_reboot(context, instance, network_info, block_device_info) def rescue(self, context, instance, network_info, image_meta, - rescue_password): + rescue_password, block_device_info): """Loads a VM using rescue images. A rescue is normally performed when something goes wrong with the diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py index 3357c018a24b..1509e7e147cf 100644 --- a/nova/virt/vmwareapi/driver.py +++ b/nova/virt/vmwareapi/driver.py @@ -646,7 +646,7 @@ class VMwareVCDriver(driver.ComputeDriver): self._vmops.resume(instance) def rescue(self, context, instance, network_info, image_meta, - rescue_password): + rescue_password, block_device_info): """Rescue the specified instance.""" self._vmops.rescue(context, instance, network_info, image_meta) diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py index cf44b8272934..afa25834361c 100644 --- a/nova/virt/xenapi/driver.py +++ b/nova/virt/xenapi/driver.py @@ -313,7 +313,7 @@ class XenAPIDriver(driver.ComputeDriver): self._vmops.resume(instance) def rescue(self, context, instance, network_info, image_meta, - rescue_password): + rescue_password, block_device_info): """Rescue the specified instance.""" self._vmops.rescue(context, instance, network_info, image_meta, rescue_password)