From 9c59fa59db876f92b8c6011b94b43cbc2cba73d1 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 20 Dec 2019 14:09:21 +1300 Subject: [PATCH] general machinery for autoinstalls --- subiquity/controller.py | 44 ++++++++++++++++++- subiquity/controllers/refresh.py | 3 ++ .../controllers/tests/test_filesystem.py | 1 + subiquity/core.py | 25 +++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/subiquity/controller.py b/subiquity/controller.py index 515fcac5..78c597d9 100644 --- a/subiquity/controller.py +++ b/subiquity/controller.py @@ -26,8 +26,35 @@ log = logging.getLogger("subiquity.controller") class SubiquityController(BaseController): - def deserialize(self, state): - self.configured() + autoinstall_key = None + autoinstall_default = None + + def __init__(self, app): + super().__init__(app) + self.autoinstall_applied = False + if app.autoinstall_config: + self.load_autoinstall_data( + app.autoinstall_config.get( + self.autoinstall_key, + self.autoinstall_default)) + + def load_autoinstall_data(self, data): + """Load autoinstall data. + + This is called if there is an autoinstall happening. This + controller may not have any data, and this controller may still + be interactive. + """ + pass + + async def apply_autoinstall_config(self): + """Apply autoinstall configuration. + + This is only called for a non-interactive controller. It should + block until the configuration has been applied. (self.configured() + is called after this is done). + """ + pass def interactive(self): if not self.app.autoinstall_config: @@ -44,6 +71,9 @@ class SubiquityController(BaseController): if self.model_name is not None: self.app.base_model.configured(self.model_name) + def deserialize(self, state): + self.configured() + class NoUIController(SubiquityController): @@ -59,5 +89,15 @@ class NoUIController(SubiquityController): class RepeatedController(RepeatedController): + def __init__(self, orig, index): + super().__init__(orig, index) + self.autoinstall_applied = False + + async def apply_autoinstall_config(self): + await self.orig.apply_autoinstall_config(self.index) + + def configured(self): + self.orig.configured() + def interactive(self): return self.orig.interactive() diff --git a/subiquity/controllers/refresh.py b/subiquity/controllers/refresh.py index 3cda245e..ae555139 100644 --- a/subiquity/controllers/refresh.py +++ b/subiquity/controllers/refresh.py @@ -65,6 +65,9 @@ class RefreshController(SubiquityController): self.check_for_update, propagate_errors=False) self.check_task.start_sync() + async def apply_autoinstall_config(self, index=1): + pass + @property def check_state(self): task = self.check_task.task diff --git a/subiquity/controllers/tests/test_filesystem.py b/subiquity/controllers/tests/test_filesystem.py index ee1ec592..4104eeea 100644 --- a/subiquity/controllers/tests/test_filesystem.py +++ b/subiquity/controllers/tests/test_filesystem.py @@ -37,6 +37,7 @@ class Thing: class MiniApplication: ui = signal = loop = None project = "mini" + autoinstall_config = {} answers = {} opts = Thing() opts.dry_run = True diff --git a/subiquity/core.py b/subiquity/core.py index 16c11cef..c80f4626 100644 --- a/subiquity/core.py +++ b/subiquity/core.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import asyncio import logging import os import platform @@ -26,6 +27,7 @@ from subiquitycore.async_helpers import ( run_in_thread, schedule_task, ) +from subiquitycore.controller import Skip from subiquitycore.core import Application from subiquitycore.utils import run_command @@ -165,6 +167,29 @@ class Subiquity(Application): self.show_error_report(report) return + def select_screen(self, new): + if new.interactive(): + super().select_screen(new) + return + elif self.autoinstall_config and not new.autoinstall_applied: + schedule_task(self._apply(new)) + else: + raise Skip + + async def _apply(self, controller): + with controller.context.child("apply_autoinstall_config"): + try: + await controller.apply_autoinstall_config() + except BaseException: + logging.exception( + "%s.apply_autoinstall_config failed", controller.name) + # Obviously need to something better here. + await asyncio.sleep(1800) + raise + controller.autoinstall_applied = True + controller.configured() + self.next_screen() + def _network_change(self): self.signal.emit_signal('snapd-network-change')