diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4e6616bdef4d..9b1a201541d7 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -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) diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index 995380e52df9..2f391109e3df 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -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): diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index 52a0a82ea3f5..ebe9f1cc10c7 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -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) diff --git a/nova/tests/unit/virt/ironic/test_driver.py b/nova/tests/unit/virt/ironic/test_driver.py index 95ee0c00808f..d28b4c9b1f66 100644 --- a/nova/tests/unit/virt/ironic/test_driver.py +++ b/nova/tests/unit/virt/ironic/test_driver.py @@ -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') diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index f7463e2a335c..3fe5dae5842f 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -26217,7 +26217,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) @@ -26256,9 +26256,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) @@ -26388,9 +26390,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 @@ -26462,12 +26465,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'], @@ -26536,16 +26542,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 @@ -26565,7 +26573,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""" diff --git a/nova/tests/unit/virt/test_virt_drivers.py b/nova/tests/unit/virt/test_virt_drivers.py index 3370c4841f9c..b724130a2c45 100644 --- a/nova/tests/unit/virt/test_virt_drivers.py +++ b/nova/tests/unit/virt/test_virt_drivers.py @@ -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 diff --git a/nova/virt/driver.py b/nova/virt/driver.py index f90c1b58b3be..a268d435d114 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -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() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index aadf2b97d325..3cc8e926078f 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -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( diff --git a/nova/virt/ironic/driver.py b/nova/virt/ironic/driver.py index 8a95f949373e..7cd01de49f39 100644 --- a/nova/virt/ironic/driver.py +++ b/nova/virt/ironic/driver.py @@ -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) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 5b64404ff933..2ab419cdbe0a 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -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, diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py index 5988ce469f38..7ed562502039 100644 --- a/nova/virt/vmwareapi/driver.py +++ b/nova/virt/vmwareapi/driver.py @@ -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)