diff --git a/linux/systemd/qubes-core.service b/linux/systemd/qubes-core.service index 34ce40fde..3786250e4 100644 --- a/linux/systemd/qubes-core.service +++ b/linux/systemd/qubes-core.service @@ -1,6 +1,6 @@ [Unit] Description=Qubes Dom0 startup setup -After=qubes-db-dom0.service libvirtd.service xenconsoled.service qubesd.service qubes-qmemman.service +After=qubes-db-dom0.service libvirtd.service virtxend.socket xenconsoled.service qubesd.service qubes-qmemman.service # Cover legacy init.d script [Service] diff --git a/qubes/app.py b/qubes/app.py index fd65eb9fb..4031f8386 100644 --- a/qubes/app.py +++ b/qubes/app.py @@ -92,6 +92,10 @@ def _reconnect_if_dead(self): self._vm = self._connection._conn.lookupByUUID(self._vm.UUID()) return is_dead + def close(self): + self._vm = None + self._connection = None + def __getattr__(self, attrname): attr = getattr(self._vm, attrname) if not isinstance(attr, collections.abc.Callable): @@ -100,12 +104,14 @@ def __getattr__(self, attrname): @functools.wraps(attr) def wrapper(*args, **kwargs): try: - return attr(*args, **kwargs) + return getattr(self._vm, attrname)(*args, **kwargs) except libvirt.libvirtError: if self._reconnect_if_dead(): return getattr(self._vm, attrname)(*args, **kwargs) raise + del wrapper.__wrapped__ + del attr return wrapper @@ -531,15 +537,7 @@ def __delitem__(self, key): if not vm.is_halted(): raise qubes.exc.QubesVMNotHaltedError(vm) self.app.fire_event('domain-pre-delete', pre_event=True, vm=vm) - try: - if vm.libvirt_domain: - vm.libvirt_domain.undefine() - # pylint: disable=protected-access - vm._libvirt_domain = None - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: - # already undefined - pass + vm.libvirt_undefine() del self._dict[vm.qid] self.app.fire_event('domain-delete', vm=vm) if getattr(vm, 'dispid', None): diff --git a/qubes/tests/init.py b/qubes/tests/init.py index fa4cc66c6..e52605032 100644 --- a/qubes/tests/init.py +++ b/qubes/tests/init.py @@ -351,6 +351,9 @@ def is_halted(self): def get_power_state(self): return "Halted" + def libvirt_undefine(self): + pass + class TestApp(qubes.tests.TestEmitter): pass diff --git a/qubes/tests/integ/basic.py b/qubes/tests/integ/basic.py index c659f4d74..1f574dd3e 100644 --- a/qubes/tests/integ/basic.py +++ b/qubes/tests/integ/basic.py @@ -285,7 +285,7 @@ def test_140_libvirt_events_reconnect(self): self.loop.run_until_complete(self.vm.create_on_disk()) self.loop.run_until_complete(self.vm.start()) p = self.loop.run_until_complete(asyncio.create_subprocess_exec( - 'systemctl', 'restart', 'libvirtd')) + 'systemctl', 'restart', 'virtxend')) self.loop.run_until_complete(p.communicate()) # check if events still works self.domain_paused_received = False @@ -302,7 +302,7 @@ def test_141_libvirt_objects_reconnect(self): # make sure libvirt object is cached self.app.domains[0].libvirt_domain.isActive() p = self.loop.run_until_complete(asyncio.create_subprocess_exec( - 'systemctl', 'restart', 'libvirtd')) + 'systemctl', 'restart', 'virtxend')) self.loop.run_until_complete(p.communicate()) # trigger reconnect with self.assertNotRaises(libvirt.libvirtError): diff --git a/qubes/tests/integ/vm_qrexec_gui.py b/qubes/tests/integ/vm_qrexec_gui.py index c90ce0b6f..a91b258ce 100644 --- a/qubes/tests/integ/vm_qrexec_gui.py +++ b/qubes/tests/integ/vm_qrexec_gui.py @@ -536,6 +536,8 @@ async def _test_300_bug_1028_gui_memory_pinning(self): # exclude from memory balancing self.testvm1.features['service.meminfo-writer'] = False + # prevent Whonix startup notifications from interfering + self.testvm1.features['service.whonix-tor-disable'] = True await self.testvm1.start() await self.wait_for_session(self.testvm1) diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index f4571ffc2..299908e76 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -1930,6 +1930,19 @@ async def clone_disk_files(self, src, pool=None, pools=None, ): # fire hooks await self.fire_event_async('domain-clone-files', src=src) + def libvirt_undefine(self): + """Undefine domain object in libvirt""" + try: + if self.libvirt_domain: + self.libvirt_domain.undefine() + except libvirt.libvirtError as e: + if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: + # already undefined + pass + if self._libvirt_domain is not None: + self._libvirt_domain.close() + self._libvirt_domain = None + # # methods for querying domain state #