From 303c0d75d681933b73a0756ce81310ac9a9acd8d Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 12 Oct 2020 14:18:59 +1300 Subject: [PATCH] move reboot controller (the last one!) to new world --- po/POTFILES.in | 4 +- subiquity/common/apidef.py | 3 + subiquity/controller.py | 138 ------------------- subiquity/controllers/__init__.py | 20 --- subiquity/server/controllers/__init__.py | 2 + subiquity/{ => server}/controllers/reboot.py | 54 ++++---- subiquity/server/server.py | 1 + subiquity/ui/frame.py | 2 - 8 files changed, 37 insertions(+), 187 deletions(-) delete mode 100644 subiquity/controller.py delete mode 100644 subiquity/controllers/__init__.py rename subiquity/{ => server}/controllers/reboot.py (68%) diff --git a/po/POTFILES.in b/po/POTFILES.in index 7601b8d7..4fa2ed51 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -40,9 +40,6 @@ subiquity/common/tests/__init__.py subiquity/common/tests/test_filesystem.py subiquity/common/tests/test_keyboard.py subiquity/common/types.py -subiquity/controller.py -subiquity/controllers/__init__.py -subiquity/controllers/reboot.py subiquitycore/async_helpers.py subiquitycore/contextlib38.py subiquitycore/context.py @@ -129,6 +126,7 @@ subiquity/server/controllers/mirror.py subiquity/server/controllers/network.py subiquity/server/controllers/package.py subiquity/server/controllers/proxy.py +subiquity/server/controllers/reboot.py subiquity/server/controllers/refresh.py subiquity/server/controllers/reporting.py subiquity/server/controllers/snaplist.py diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index 52858932..cf5acef5 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -186,6 +186,9 @@ class API: class status: def GET(cur: Optional[InstallState] = None) -> InstallStatus: ... + class reboot: + def POST(): ... + class LinkAction(enum.Enum): NEW = enum.auto() diff --git a/subiquity/controller.py b/subiquity/controller.py deleted file mode 100644 index 701dcc77..00000000 --- a/subiquity/controller.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright 2019 Canonical, Ltd. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -import json -import logging -import os - -import jsonschema - -from subiquitycore.context import with_context -from subiquitycore.controller import ( - BaseController, - ) -from subiquitycore.tuicontroller import ( - RepeatedController, - TuiController, - ) - -log = logging.getLogger("subiquity.controller") - - -class Confirm(Exception): - pass - - -class SubiquityController(BaseController): - - autoinstall_key = None - autoinstall_schema = None - autoinstall_default = None - - def __init__(self, app): - super().__init__(app) - self.autoinstall_applied = False - self.context.set('controller', self) - - def interactive(self): - return False - - def setup_autoinstall(self): - if self.app.autoinstall_config: - with self.context.child("load_autoinstall_data"): - ai_data = self.app.autoinstall_config.get( - self.autoinstall_key, - self.autoinstall_default) - if ai_data is not None and self.autoinstall_schema is not None: - jsonschema.validate(ai_data, self.autoinstall_schema) - self.load_autoinstall_data(ai_data) - - 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 - - @with_context() - async def apply_autoinstall_config(self, context): - """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 configured(self): - """Let the world know that this controller's model is now configured. - """ - with open(self.app.state_path('states', self.name), 'w') as fp: - json.dump(self.serialize(), fp) - if self.model_name is not None: - self.app.base_model.configured(self.model_name) - - def load_state(self): - state_path = self.app.state_path('states', self.name) - if not os.path.exists(state_path): - return - with open(state_path) as fp: - self.deserialize(json.load(fp)) - - def deserialize(self, state): - pass - - def make_autoinstall(self): - return {} - - -class SubiquityTuiController(SubiquityController, TuiController): - - def interactive(self): - if not self.app.autoinstall_config: - return True - i_sections = self.app.autoinstall_config.get( - 'interactive-sections', []) - return '*' in i_sections or self.autoinstall_key in i_sections - - -class RepeatedController(RepeatedController): - - autoinstall_key = None - autoinstall_schema = None - - def __init__(self, orig, index): - super().__init__(orig, index) - self.autoinstall_applied = False - - def setup_autoinstall(self): - pass - - async def apply_autoinstall_config(self): - await self.orig.apply_autoinstall_config(index=self.index) - - def configured(self): - self.orig.configured() - - def interactive(self): - return self.orig.interactive() - - def make_autoinstall(self): - return {} - - def load_state(self): - pass diff --git a/subiquity/controllers/__init__.py b/subiquity/controllers/__init__.py deleted file mode 100644 index e81cd12a..00000000 --- a/subiquity/controllers/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2020 Canonical, Ltd. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -from .reboot import RebootController - -__all__ = [ - 'RebootController', -] diff --git a/subiquity/server/controllers/__init__.py b/subiquity/server/controllers/__init__.py index fefe6b4f..fde735e4 100644 --- a/subiquity/server/controllers/__init__.py +++ b/subiquity/server/controllers/__init__.py @@ -24,6 +24,7 @@ from .mirror import MirrorController from .network import NetworkController from .package import PackageController from .proxy import ProxyController +from .reboot import RebootController from .refresh import RefreshController from .reporting import ReportingController from .snaplist import SnapListController @@ -45,6 +46,7 @@ __all__ = [ 'NetworkController', 'PackageController', 'ProxyController', + 'RebootController', 'RefreshController', 'ReportingController', 'SnapListController', diff --git a/subiquity/controllers/reboot.py b/subiquity/server/controllers/reboot.py similarity index 68% rename from subiquity/controllers/reboot.py rename to subiquity/server/controllers/reboot.py index b048c6ae..7fc7b089 100644 --- a/subiquity/controllers/reboot.py +++ b/subiquity/server/controllers/reboot.py @@ -13,28 +13,49 @@ # 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 import subprocess -from subiquitycore.async_helpers import schedule_task from subiquitycore.context import with_context from subiquitycore.utils import arun_command, run_command -from subiquity.controller import SubiquityTuiController +from subiquity.common.apidef import API +from subiquity.server.controller import SubiquityController +from subiquity.server.controllers.install import InstallState log = logging.getLogger("subiquity.controllers.restart") -class RebootController(SubiquityTuiController): +class RebootController(SubiquityController): + + endpoint = API.reboot def __init__(self, app): super().__init__(app) - self.context.set('hidden', True) + self.user_reboot_event = asyncio.Event() + self.rebooting_event = asyncio.Event() - def interactive(self): - return self.app.interactive() + async def POST(self): + self.app.controllers.Install.stop_uu() + self.user_reboot_event.set() + await self.rebooting_event.wait() + + def start(self): + self.app.aio_loop.create_task(self._run()) + + async def _run(self): + Install = self.app.controllers.Install + await Install.install_task + await self.app.controllers.Late.run_event.wait() + await self.copy_logs_to_target() + if self.app.interactive(): + await self.user_reboot_event.wait() + self.reboot() + elif Install.install_state == InstallState.DONE: + self.reboot() @with_context() async def copy_logs_to_target(self, context): @@ -56,27 +77,12 @@ class RebootController(SubiquityTuiController): except Exception: log.exception("saving journal failed") - def reboot(self): + @with_context() + def reboot(self, context): + self.rebooting_event.set() if self.opts.dry_run: self.app.exit() else: if platform.machine() == 's390x': run_command(["chreipl", "/target/boot"]) run_command(["/sbin/reboot"]) - - @with_context() - async def apply_autoinstall_config(self, context): - await self.copy_logs_to_target(context=context) - self.reboot() - - async def _run(self): - await self.copy_logs_to_target() - await self.app.controllers.InstallProgress.reboot_clicked.wait() - self.reboot() - - def make_ui(self): - schedule_task(self._run()) - return self.ui.body - - def cancel(self): - pass diff --git a/subiquity/server/server.py b/subiquity/server/server.py index 7e82f05d..2f2f7e86 100644 --- a/subiquity/server/server.py +++ b/subiquity/server/server.py @@ -131,6 +131,7 @@ class SubiquityServer(Application): "SnapList", "Install", "Late", + "Reboot", ] def make_model(self): diff --git a/subiquity/ui/frame.py b/subiquity/ui/frame.py index 798d917e..8314f546 100644 --- a/subiquity/ui/frame.py +++ b/subiquity/ui/frame.py @@ -36,8 +36,6 @@ class SubiquityUI(SubiquityCoreUI): return super().keypress(size, key) def set_body(self, widget): - if widget is self.body: - return super().set_body(widget) if isinstance(widget, BaseView): for overlay in self.app.global_overlays: