Fix asyncio.Lock usage for Python 3.9+

'with (yield from alock):' is incompatible with Python 3.9+.

Change it to 'async with alock:', and then change the affected functions
to 'async def'.

This makes the test suite pass again in a Fedora 33 VM.

QubesOS/qubes-issues#2738
This commit is contained in:
Rusty Bird 2021-02-11 11:17:41 +00:00
parent 7a91af9b2c
commit a7fe59449a
No known key found for this signature in database
GPG Key ID: 469D78F47AAF2ADF
4 changed files with 59 additions and 67 deletions

View File

@ -1191,13 +1191,12 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
@qubes.api.method('admin.vm.Remove', no_payload=True, @qubes.api.method('admin.vm.Remove', no_payload=True,
scope='global', write=True) scope='global', write=True)
@asyncio.coroutine async def vm_remove(self):
def vm_remove(self):
self.enforce(not self.arg) self.enforce(not self.arg)
self.fire_event_for_permission() self.fire_event_for_permission()
with (yield from self.dest.startup_lock): async with self.dest.startup_lock:
if not self.dest.is_halted(): if not self.dest.is_halted():
raise qubes.exc.QubesVMNotHaltedError(self.dest) raise qubes.exc.QubesVMNotHaltedError(self.dest)
@ -1207,7 +1206,7 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
del self.app.domains[self.dest] del self.app.domains[self.dest]
try: try:
yield from self.dest.remove_from_disk() await self.dest.remove_from_disk()
except: # pylint: disable=bare-except except: # pylint: disable=bare-except
self.app.log.exception('Error while removing VM \'%s\' files', self.app.log.exception('Error while removing VM \'%s\' files',
self.dest.name) self.dest.name)

View File

@ -168,11 +168,10 @@ class Volume:
>>>def start(self): >>>def start(self):
>>> pass >>> pass
''' '''
@asyncio.coroutine
@functools.wraps(method) @functools.wraps(method)
def wrapper(self, *args, **kwargs): async def wrapper(self, *args, **kwargs):
with (yield from self._lock): # pylint: disable=protected-access async with self._lock: # pylint: disable=protected-access
return (yield from method(self, *args, **kwargs)) return await method(self, *args, **kwargs)
return wrapper return wrapper
def create(self): def create(self):

View File

@ -242,15 +242,14 @@ class CallbackPool(qubes.storage.Pool):
cmd = self._cb_conf.get('cmd') cmd = self._cb_conf.get('cmd')
return bool(cmd and cmd != '-') return bool(cmd and cmd != '-')
@asyncio.coroutine async def _init(self, callback=True):
def _init(self, callback=True):
''' Late storage initialization on first use for e.g. decryption on first usage request. ''' Late storage initialization on first use for e.g. decryption on first usage request.
:param callback: Whether to trigger the `pre_sinit` callback or not. :param callback: Whether to trigger the `pre_sinit` callback or not.
''' '''
with (yield from self._cb_init_lock): async with self._cb_init_lock:
if self._cb_requires_init: if self._cb_requires_init:
if callback: if callback:
yield from self._callback('pre_sinit') await self._callback('pre_sinit')
self._cb_requires_init = False self._cb_requires_init = False
@asyncio.coroutine @asyncio.coroutine

View File

@ -1060,12 +1060,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
# methods for changing domain state # methods for changing domain state
# #
@asyncio.coroutine async def _ensure_shutdown_handled(self):
def _ensure_shutdown_handled(self):
"""Make sure previous shutdown is fully handled. """Make sure previous shutdown is fully handled.
MUST NOT be called when domain is running. MUST NOT be called when domain is running.
""" """
with (yield from self._domain_stopped_lock): async with self._domain_stopped_lock:
# Don't accept any new stopped event's till a new VM has been # Don't accept any new stopped event's till a new VM has been
# created. If we didn't received any stopped event or it wasn't # created. If we didn't received any stopped event or it wasn't
# handled yet we will handle this in the next lines. # handled yet we will handle this in the next lines.
@ -1086,11 +1085,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
# twice if an exception gets thrown. # twice if an exception gets thrown.
self._domain_stopped_event_handled = True self._domain_stopped_event_handled = True
yield from self.fire_event_async('domain-stopped') await self.fire_event_async('domain-stopped')
yield from self.fire_event_async('domain-shutdown') await self.fire_event_async('domain-shutdown')
@asyncio.coroutine async def start(self, start_guid=True, notify_function=None,
def start(self, start_guid=True, notify_function=None,
mem_required=None): mem_required=None):
"""Start domain """Start domain
@ -1099,7 +1097,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
:param int mem_required: FIXME :param int mem_required: FIXME
""" """
with (yield from self.startup_lock): async with self.startup_lock:
# check if domain wasn't removed in the meantime # check if domain wasn't removed in the meantime
if self not in self.app.domains: if self not in self.app.domains:
raise qubes.exc.QubesVMNotFoundError(self.name) raise qubes.exc.QubesVMNotFoundError(self.name)
@ -1108,19 +1106,19 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
if self.get_power_state() != 'Halted': if self.get_power_state() != 'Halted':
return self return self
yield from self._ensure_shutdown_handled() await self._ensure_shutdown_handled()
self.log.info('Starting {}'.format(self.name)) self.log.info('Starting {}'.format(self.name))
try: try:
yield from self.fire_event_async('domain-pre-start', await self.fire_event_async('domain-pre-start',
pre_event=True, pre_event=True,
start_guid=start_guid, start_guid=start_guid,
mem_required=mem_required) mem_required=mem_required)
except Exception as exc: except Exception as exc:
self.log.error('Start failed: %s', str(exc)) self.log.error('Start failed: %s', str(exc))
yield from self.fire_event_async('domain-start-failed', await self.fire_event_async('domain-start-failed',
reason=str(exc)) reason=str(exc))
raise raise
qmemman_client = None qmemman_client = None
@ -1135,26 +1133,26 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
if self.virt_mode == 'pvh' and not self.kernel: if self.virt_mode == 'pvh' and not self.kernel:
raise qubes.exc.QubesException( raise qubes.exc.QubesException(
'virt_mode PVH require kernel to be set') 'virt_mode PVH require kernel to be set')
yield from self.storage.verify() await self.storage.verify()
if self.netvm is not None: if self.netvm is not None:
# pylint: disable = no-member # pylint: disable = no-member
if self.netvm.qid != 0: if self.netvm.qid != 0:
if not self.netvm.is_running(): if not self.netvm.is_running():
yield from self.netvm.start( await self.netvm.start(
start_guid=start_guid, start_guid=start_guid,
notify_function=notify_function) notify_function=notify_function)
qmemman_client = yield from asyncio.get_event_loop(). \ qmemman_client = await asyncio.get_event_loop(). \
run_in_executor(None, self.request_memory, mem_required) run_in_executor(None, self.request_memory, mem_required)
yield from self.storage.start() await self.storage.start()
except Exception as exc: except Exception as exc:
self.log.error('Start failed: %s', str(exc)) self.log.error('Start failed: %s', str(exc))
# let anyone receiving domain-pre-start know that startup failed # let anyone receiving domain-pre-start know that startup failed
yield from self.fire_event_async('domain-start-failed', await self.fire_event_async('domain-start-failed',
reason=str(exc)) reason=str(exc))
if qmemman_client: if qmemman_client:
qmemman_client.close() qmemman_client.close()
raise raise
@ -1179,16 +1177,16 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
'Failed to start an HVM qube with PCI devices assigned ' 'Failed to start an HVM qube with PCI devices assigned '
'- hardware does not support IOMMU/VT-d/AMD-Vi') '- hardware does not support IOMMU/VT-d/AMD-Vi')
self.log.error('Start failed: %s', str(exc)) self.log.error('Start failed: %s', str(exc))
yield from self.fire_event_async('domain-start-failed', await self.fire_event_async('domain-start-failed',
reason=str(exc)) reason=str(exc))
yield from self.storage.stop() await self.storage.stop()
raise exc raise exc
except Exception as exc: except Exception as exc:
self.log.error('Start failed: %s', str(exc)) self.log.error('Start failed: %s', str(exc))
# let anyone receiving domain-pre-start know that startup failed # let anyone receiving domain-pre-start know that startup failed
yield from self.fire_event_async('domain-start-failed', await self.fire_event_async('domain-start-failed',
reason=str(exc)) reason=str(exc))
yield from self.storage.stop() await self.storage.stop()
raise raise
finally: finally:
@ -1199,21 +1197,21 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
self._domain_stopped_event_handled = False self._domain_stopped_event_handled = False
try: try:
yield from self.fire_event_async('domain-spawn', await self.fire_event_async('domain-spawn',
start_guid=start_guid) start_guid=start_guid)
self.log.info('Setting Qubes DB info for the VM') self.log.info('Setting Qubes DB info for the VM')
yield from self.start_qubesdb() await self.start_qubesdb()
self.create_qdb_entries() self.create_qdb_entries()
self.start_qdb_watch() self.start_qdb_watch()
self.log.warning('Activating the {} VM'.format(self.name)) self.log.warning('Activating the {} VM'.format(self.name))
self.libvirt_domain.resume() self.libvirt_domain.resume()
yield from self.start_qrexec_daemon() await self.start_qrexec_daemon()
yield from self.fire_event_async('domain-start', await self.fire_event_async('domain-start',
start_guid=start_guid) start_guid=start_guid)
except Exception as exc: # pylint: disable=bare-except except Exception as exc: # pylint: disable=bare-except
self.log.error('Start failed: %s', str(exc)) self.log.error('Start failed: %s', str(exc))
@ -1221,13 +1219,13 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
# raised in self._kill_locked(), because the vm is not # raised in self._kill_locked(), because the vm is not
# running or paused # running or paused
try: try:
yield from self._kill_locked() await self._kill_locked()
except qubes.exc.QubesVMNotStartedError: except qubes.exc.QubesVMNotStartedError:
pass pass
# let anyone receiving domain-pre-start know that startup failed # let anyone receiving domain-pre-start know that startup failed
yield from self.fire_event_async('domain-start-failed', await self.fire_event_async('domain-start-failed',
reason=str(exc)) reason=str(exc))
raise raise
return self return self
@ -1255,9 +1253,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
self._domain_stopped_future = \ self._domain_stopped_future = \
asyncio.ensure_future(self._domain_stopped_coro()) asyncio.ensure_future(self._domain_stopped_coro())
@asyncio.coroutine async def _domain_stopped_coro(self):
def _domain_stopped_coro(self): async with self._domain_stopped_lock:
with (yield from self._domain_stopped_lock):
assert not self._domain_stopped_event_handled assert not self._domain_stopped_event_handled
# Set this immediately such that we don't generate events twice if # Set this immediately such that we don't generate events twice if
@ -1265,9 +1262,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
self._domain_stopped_event_handled = True self._domain_stopped_event_handled = True
while self.get_power_state() == 'Dying': while self.get_power_state() == 'Dying':
yield from asyncio.sleep(0.25) await asyncio.sleep(0.25)
yield from self.fire_event_async('domain-stopped') await self.fire_event_async('domain-stopped')
yield from self.fire_event_async('domain-shutdown') await self.fire_event_async('domain-shutdown')
@qubes.events.handler('domain-stopped') @qubes.events.handler('domain-stopped')
@asyncio.coroutine @asyncio.coroutine
@ -1282,8 +1279,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
self.fire_event('property-reset:stubdom_xid', name='stubdom_xid') self.fire_event('property-reset:stubdom_xid', name='stubdom_xid')
self.fire_event('property-reset:start_time', name='start_time') self.fire_event('property-reset:start_time', name='start_time')
@asyncio.coroutine async def shutdown(self, force=False, wait=False, timeout=None):
def shutdown(self, force=False, wait=False, timeout=None):
"""Shutdown domain. """Shutdown domain.
:param force: ignored :param force: ignored
@ -1298,8 +1294,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
raise qubes.exc.QubesVMNotStartedError(self) raise qubes.exc.QubesVMNotStartedError(self)
try: try:
yield from self.fire_event_async('domain-pre-shutdown', await self.fire_event_async('domain-pre-shutdown',
pre_event=True, force=force) pre_event=True, force=force)
self.libvirt_domain.shutdown() self.libvirt_domain.shutdown()
@ -1307,23 +1303,22 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
if timeout is None: if timeout is None:
timeout = self.shutdown_timeout timeout = self.shutdown_timeout
while timeout > 0 and not self.is_halted(): while timeout > 0 and not self.is_halted():
yield from asyncio.sleep(0.25) await asyncio.sleep(0.25)
timeout -= 0.25 timeout -= 0.25
with (yield from self.startup_lock): async with self.startup_lock:
if self.is_halted(): if self.is_halted():
# make sure all shutdown tasks are completed # make sure all shutdown tasks are completed
yield from self._ensure_shutdown_handled() await self._ensure_shutdown_handled()
else: else:
raise qubes.exc.QubesVMShutdownTimeoutError(self) raise qubes.exc.QubesVMShutdownTimeoutError(self)
except Exception as ex: except Exception as ex:
yield from self.fire_event_async('domain-shutdown-failed', await self.fire_event_async('domain-shutdown-failed',
reason=str(ex)) reason=str(ex))
raise raise
return self return self
@asyncio.coroutine async def kill(self):
def kill(self):
"""Forcefully shutdown (destroy) domain. """Forcefully shutdown (destroy) domain.
:raises qubes.exc.QubesVMNotStartedError: \ :raises qubes.exc.QubesVMNotStartedError: \
@ -1333,8 +1328,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
if not self.is_running() and not self.is_paused(): if not self.is_running() and not self.is_paused():
raise qubes.exc.QubesVMNotStartedError(self) raise qubes.exc.QubesVMNotStartedError(self)
with (yield from self.startup_lock): async with self.startup_lock:
yield from self._kill_locked() await self._kill_locked()
return self return self