Browse Source

events: add helper for waiting for just VM shutdown

Wrap setting events handling machinery for just this purpose in a single
function, to not duplicate it all over the code.
Marek Marczykowski-Górecki 7 years ago
parent
commit
b35588368f
2 changed files with 77 additions and 0 deletions
  1. 73 0
      qubesadmin/events/utils.py
  2. 4 0
      qubesadmin/exc.py

+ 73 - 0
qubesadmin/events/utils.py

@@ -0,0 +1,73 @@
+# -*- encoding: utf8 -*-
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2017 Marek Marczykowski-Górecki
+#                               <marmarek@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program; if not, see <http://www.gnu.org/licenses/>.
+
+''' Utilities for common events-based actions '''
+
+import asyncio
+import functools
+
+import qubesadmin.events
+import qubesadmin.exc
+
+
+
+class Interrupt(Exception):
+    '''Interrupt events processing'''
+
+
+def interrupt_on_vm_shutdown(vm, subject, event):
+    '''Interrupt events processing when given VM was shutdown'''
+    # pylint: disable=unused-argument
+    if event == 'connection-established':
+        if vm.is_halted():
+            raise Interrupt
+    elif event == 'domain-shutdown' and vm == subject:
+        raise Interrupt
+
+
+def wait_for_domain_shutdown(vm, timeout):
+    ''' Helper function to wait for domain shutdown.
+
+    This function wait for domain shutdown, but do not initiate the shutdown
+    itself.
+
+    Note: you need to close event loop after calling this function.
+
+    :param vm: QubesVM object to wait for shutdown on
+    :param timeout: Timeout in seconds, use 0 for no timeout
+    '''
+    events = qubesadmin.events.EventsDispatcher(vm.app)
+    loop = asyncio.get_event_loop()
+    events.add_handler('domain-shutdown',
+        functools.partial(interrupt_on_vm_shutdown, vm))
+    events.add_handler('connection-established',
+        functools.partial(interrupt_on_vm_shutdown, vm))
+    events_task = asyncio.ensure_future(events.listen_for_events(),
+        loop=loop)
+    if timeout:
+        # pylint: disable=no-member
+        loop.call_later(timeout, events_task.cancel)
+    try:
+        loop.run_until_complete(events_task)
+    except asyncio.CancelledError:
+        raise qubesadmin.exc.QubesVMShutdownTimeout(
+            'VM %s shutdown timeout expired', vm.name)
+    except Interrupt:
+        pass

+ 4 - 0
qubesadmin/exc.py

@@ -77,6 +77,10 @@ class QubesVMNotHaltedError(QubesVMError):
     '''
 
 
+class QubesVMShutdownTimeout(QubesVMError):
+    ''' Domain shutdown haven't completed in expected timeframe'''
+
+
 class QubesNoTemplateError(QubesVMError):
     '''Cannot start domain, because there is no template'''