Generalized asyncio.gather pattern
Currently, and mainly in the master controller, the exception handling of a task list is insufficient. The usual pattern looks like this:
for play in provision_playbook:
subplay_tasks.append(asyncio.create_task(self._ansible_subplay_executor(play)))
await asyncio.gather(*subplay_tasks)
If a subplay task throws an exception, other subplay tasks remain on the asyncio.ioloop-stack. We need a generalized pattern to handle such exceptions. A specialized implementation for the shown example would look like this:
for play in provision_playbook:
subplay_tasks.append(asyncio.create_task(self._ansible_subplay_executor(play)))
result = await asyncio.gather(*subplay_tasks, return_exceptions=True)
for r, p in zip(result, provision_playbook):
if isinstance(r, Exception):
_log.error("Error in provisioning thrown by Ansible: %s", r)
raise FailReply(f"Error in provisioning of subplay: {p}")