From 51af2ed27cde518a0bc6c9ac274886a4584ee0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Tue, 8 Oct 2019 23:35:24 +0200 Subject: [PATCH] vm: add domain-shutdown-failed event Similar to domain-start-failed, add an event fired when domain-pre-shutdown was fired but the actual operation failed. Note it might not catch all the cases, as shutdown() may be called with wait=False, which means it won't wait fot the actual shutdown. In that case, timeout won't result in domain-shutdown-failed event. QubesOS/qubes-issues#5380 --- qubes/vm/qubesvm.py | 48 +++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index b225e112..a3e3e4a7 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -303,6 +303,19 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): :param event: Event name (``'domain-pre-shutdown'``) :param force: If the shutdown is to be forceful + .. event:: domain-shutdown-failed (subject, event, reason) + + Fired when ``domain-pre-shutdown`` event was sent, but the actual + shutdown operation failed. It can be caused by other + ``domain-pre-shutdown`` handler blocking the operation with an + exception, or a shutdown timeout. + + Handler for this event can be asynchronous (a coroutine). + + :param subject: Event emitter (the qube object) + :param event: Event name (``'domain-shutdown-failed'``) + :param reason: Error message + .. event:: domain-cmd-pre-run (subject, event, start_guid) Fired at the beginning of :py:meth:`run_service` method. @@ -1178,23 +1191,28 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): if self.is_halted(): raise qubes.exc.QubesVMNotStartedError(self) - yield from self.fire_event_async('domain-pre-shutdown', pre_event=True, - force=force) + try: + yield from self.fire_event_async('domain-pre-shutdown', + pre_event=True, force=force) - self.libvirt_domain.shutdown() + self.libvirt_domain.shutdown() - if wait: - if timeout is None: - timeout = self.shutdown_timeout - while timeout > 0 and not self.is_halted(): - yield from asyncio.sleep(0.25) - timeout -= 0.25 - with (yield from self.startup_lock): - if self.is_halted(): - # make sure all shutdown tasks are completed - yield from self._ensure_shutdown_handled() - else: - raise qubes.exc.QubesVMShutdownTimeoutError(self) + if wait: + if timeout is None: + timeout = self.shutdown_timeout + while timeout > 0 and not self.is_halted(): + yield from asyncio.sleep(0.25) + timeout -= 0.25 + with (yield from self.startup_lock): + if self.is_halted(): + # make sure all shutdown tasks are completed + yield from self._ensure_shutdown_handled() + else: + raise qubes.exc.QubesVMShutdownTimeoutError(self) + except Exception as ex: + yield from self.fire_event_async('domain-shutdown-failed', + reason=str(ex)) + raise return self