Fix hanging when network is the last postinst model configured

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 <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-01-20 11:22:22 +01:00
parent efea0c5391
commit 37f286ac93
1 changed files with 20 additions and 15 deletions

View File

@ -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: