From f1a85066a563b7191fa5bbfec17d96400528df05 Mon Sep 17 00:00:00 2001 From: Fabian Wiesel Date: Thu, 13 Jul 2023 10:05:07 +0200 Subject: [PATCH] [vmwareapi] Implement trigger_crash_dump Just hook up the driver interface with the vsphere api call. Change-Id: I755d81a8f1d9bdc89788227dea5d55720567c362 --- .../unit/virt/vmwareapi/test_driver_api.py | 6 +++ .../tests/unit/virt/vmwareapi/test_vm_util.py | 37 +++++++++++++++++++ nova/tests/unit/virt/vmwareapi/test_vmops.py | 6 +++ nova/virt/vmwareapi/driver.py | 3 ++ nova/virt/vmwareapi/vm_util.py | 13 +++++++ nova/virt/vmwareapi/vmops.py | 3 ++ 6 files changed, 68 insertions(+) diff --git a/nova/tests/unit/virt/vmwareapi/test_driver_api.py b/nova/tests/unit/virt/vmwareapi/test_driver_api.py index ae6d9a0b05d..df89dee7028 100644 --- a/nova/tests/unit/virt/vmwareapi/test_driver_api.py +++ b/nova/tests/unit/virt/vmwareapi/test_driver_api.py @@ -1596,6 +1596,12 @@ def test_power_off(self, mock_update_cached_instances): info = self._get_info() self._check_vm_info(info, power_state.SHUTDOWN) + @mock.patch.object(vmops.VMwareVMOps, 'trigger_crash_dump') + def test_trigger_crash_dump(self, mock_trigger_crash_dump): + self._create_vm() + self.conn.trigger_crash_dump(self.instance) + mock_trigger_crash_dump.assert_called_once_with(self.instance) + def test_power_off_non_existent(self): self._create_instance() self.assertRaises(exception.InstanceNotFound, self.conn.power_off, diff --git a/nova/tests/unit/virt/vmwareapi/test_vm_util.py b/nova/tests/unit/virt/vmwareapi/test_vm_util.py index ff7f7316fad..f1096fd8c97 100644 --- a/nova/tests/unit/virt/vmwareapi/test_vm_util.py +++ b/nova/tests/unit/virt/vmwareapi/test_vm_util.py @@ -1813,6 +1813,43 @@ def test_power_off_instance_power_state_exception(self, fake_get_ref): fake_wait_for_task.assert_called_once_with('fake-task') self.assertFalse(fake_get_ref.called) + @mock.patch.object(vm_util, "get_vm_ref") + def test_trigger_crash_dump(self, fake_get_ref): + session = fake.FakeSession() + with test.nested( + mock.patch.object(session, '_call_method'), + ) as (fake_call_method,): + vm_util.trigger_crash_dump(session, self._instance, 'fake-vm-ref') + fake_call_method.assert_called_once_with(session.vim, + "SendNMI", + 'fake-vm-ref') + self.assertFalse(fake_get_ref.called) + + @mock.patch.object(vm_util, "get_vm_ref", return_value="fake-vm-ref") + def test_trigger_crash_dump_no_vm_ref(self, fake_get_ref): + session = fake.FakeSession() + with test.nested( + mock.patch.object(session, '_call_method') + ) as (fake_call_method,): + vm_util.trigger_crash_dump(session, self._instance) + fake_get_ref.assert_called_once_with(session, self._instance) + fake_call_method.assert_called_once_with(session.vim, + "SendNMI", + 'fake-vm-ref') + + @mock.patch.object(vm_util, "get_vm_ref") + def test_trigger_crash_dump_power_state_exception(self, fake_get_ref): + session = fake.FakeSession() + with test.nested( + mock.patch.object(session, '_call_method', + side_effect=vexc.InvalidPowerStateException), + ) as (fake_call_method, ): + vm_util.trigger_crash_dump(session, self._instance, 'fake-vm-ref') + fake_call_method.assert_called_once_with(session.vim, + "SendNMI", + 'fake-vm-ref') + self.assertFalse(fake_get_ref.called) + def test_get_vm_create_spec_updated_hw_version(self): extra_specs = vm_util.ExtraSpecs(hw_version='vmx-08') result = vm_util.get_vm_create_spec(fake.FakeFactory(), diff --git a/nova/tests/unit/virt/vmwareapi/test_vmops.py b/nova/tests/unit/virt/vmwareapi/test_vmops.py index feca5413dba..1cf56faff9e 100644 --- a/nova/tests/unit/virt/vmwareapi/test_vmops.py +++ b/nova/tests/unit/virt/vmwareapi/test_vmops.py @@ -4034,3 +4034,9 @@ def specced_flavor(specs): extra_specs = self._vmops._get_extra_specs( specced_flavor({'trait:CUSTOM_NUMASIZE_C48_M729': 'forbidden'})) self.assertEqual(extra_specs.numa_prefer_ht, '') + + @mock.patch.object(vm_util, 'trigger_crash_dump') + def test_trigger_crash_dump(self, mock_trigger_crash_dump): + self._vmops.trigger_crash_dump(self._instance) + mock_trigger_crash_dump.assert_called_once_with(self._session, + self._instance) diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py index fbc8fbbc4e5..7a4aeabd2ad 100644 --- a/nova/virt/vmwareapi/driver.py +++ b/nova/virt/vmwareapi/driver.py @@ -751,6 +751,9 @@ def power_on(self, context, instance, network_info, """Power on the specified instance.""" self._vmops.power_on(instance) + def trigger_crash_dump(self, instance): + self._vmops.trigger_crash_dump(instance) + def poll_rebooting_instances(self, timeout, instances): """Poll for rebooting instances.""" self._vmops.poll_rebooting_instances(timeout, instances) diff --git a/nova/virt/vmwareapi/vm_util.py b/nova/virt/vmwareapi/vm_util.py index 9c33b80ac69..388511542ff 100644 --- a/nova/virt/vmwareapi/vm_util.py +++ b/nova/virt/vmwareapi/vm_util.py @@ -2072,6 +2072,19 @@ def power_on_instance(session, instance, vm_ref=None): LOG.debug("VM already powered on", instance=instance) +def trigger_crash_dump(session, instance, vm_ref=None): + """Trigger a crashdump by sending an NMI""" + if vm_ref is None: + vm_ref = get_vm_ref(session, instance) + + LOG.debug("Sending NMI to VM", instance=instance) + try: + session._call_method(session.vim, "SendNMI", vm_ref) + LOG.debug("NMI sent to VM", instance=instance) + except vexc.InvalidPowerStateException: + LOG.info("VM is powered off", instance=instance) + + def _get_vm_port_indices(session, vm_ref): extra_config = session._call_method(vutil, 'get_object_property', diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 1b3eb1b68fe..7da6445c5da 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -2202,6 +2202,9 @@ def power_on(self, instance): vm_util.power_on_instance(self._session, instance) self.update_cached_instances() + def trigger_crash_dump(self, instance): + vm_util.trigger_crash_dump(self._session, instance) + def _update_instance_progress(self, context, instance, step, total_steps): """Update instance progress percent to reflect current step number """