From 37f286ac93ff57cfd06af80df3c702dfafa64e34 Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Thu, 20 Jan 2022 11:22:22 +0100 Subject: [PATCH] Fix hanging when network is the last postinst model configured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running an autoinstallation with only “network” as an interactive section, the installation hanged after the following steps: finalizing installation running 'curtin hook' curtin command hook executing late commands where in normal conditions, the next step is: final system configuration Also, querying the meta/status endpoint at this point shows that the app is in POST_WAIT state. The problem is: the network model is declared as both an “install” model and a “postinstall” model (it is the only one we have AFAIK) when calling .configured() for a given model, we only trigger /at max/ one event. Either: * install_event (if the model is an “install” model - and it is the last one configured) * postinstall_event (if the model is a “postinstall” model but not an “install” model - and it is the last one configured) * no event (as in most cases) So when we call .configured() for the network model, we /can/ trigger the install_event but can never trigger the postinstall event. Therefore Subiquity, will wait forever until something triggers the postinstall_event Fixed by accepting to trigger the install_event and the postinstall_event in a single call to .configured. Signed-off-by: Olivier Gayot --- subiquity/models/subiquity.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/subiquity/models/subiquity.py b/subiquity/models/subiquity.py index 0f056f11..18c1cefb 100644 --- a/subiquity/models/subiquity.py +++ b/subiquity/models/subiquity.py @@ -18,6 +18,7 @@ from collections import OrderedDict import functools import logging import os +from typing import Set import uuid import yaml @@ -190,23 +191,27 @@ class SubiquityModel: self._postinstall_event.set() def _configured(self, model_name): + """ Add the model to the set of models that have been configured. If + there is no more model to configure in the relevant section(s) (i.e., + INSTALL or POSTINSTALL), we trigger the associated event(s). """ + def log_and_trigger(stage: str, names: Set[str], + event: asyncio.Event) -> None: + unconfigured = names - self._configured_names + log.debug( + "model %s for %s stage is configured, to go %s", + model_name, stage, unconfigured) + if not unconfigured: + event.set() + self._configured_names.add(model_name) if model_name in self._cur_install_model_names: - stage = 'install' - names = self._cur_install_model_names - event = self._install_event - elif model_name in self._cur_postinstall_model_names: - stage = 'postinstall' - names = self._cur_postinstall_model_names - event = self._postinstall_event - else: - return - unconfigured = names - self._configured_names - log.debug( - "model %s for %s stage is configured, to go %s", - model_name, stage, unconfigured) - if not unconfigured: - event.set() + log_and_trigger(stage="install", + names=self._cur_install_model_names, + event=self._install_event) + if model_name in self._cur_postinstall_model_names: + log_and_trigger(stage="postinstall", + names=self._cur_postinstall_model_names, + event=self._postinstall_event) async def wait_install(self): if len(self._cur_install_model_names) == 0: