move reboot controller (the last one!) to new world

This commit is contained in:
Michael Hudson-Doyle 2020-10-12 14:18:59 +13:00
parent 4367f8f97c
commit 303c0d75d6
8 changed files with 37 additions and 187 deletions

View File

@ -40,9 +40,6 @@ subiquity/common/tests/__init__.py
subiquity/common/tests/test_filesystem.py subiquity/common/tests/test_filesystem.py
subiquity/common/tests/test_keyboard.py subiquity/common/tests/test_keyboard.py
subiquity/common/types.py subiquity/common/types.py
subiquity/controller.py
subiquity/controllers/__init__.py
subiquity/controllers/reboot.py
subiquitycore/async_helpers.py subiquitycore/async_helpers.py
subiquitycore/contextlib38.py subiquitycore/contextlib38.py
subiquitycore/context.py subiquitycore/context.py
@ -129,6 +126,7 @@ subiquity/server/controllers/mirror.py
subiquity/server/controllers/network.py subiquity/server/controllers/network.py
subiquity/server/controllers/package.py subiquity/server/controllers/package.py
subiquity/server/controllers/proxy.py subiquity/server/controllers/proxy.py
subiquity/server/controllers/reboot.py
subiquity/server/controllers/refresh.py subiquity/server/controllers/refresh.py
subiquity/server/controllers/reporting.py subiquity/server/controllers/reporting.py
subiquity/server/controllers/snaplist.py subiquity/server/controllers/snaplist.py

View File

@ -186,6 +186,9 @@ class API:
class status: class status:
def GET(cur: Optional[InstallState] = None) -> InstallStatus: ... def GET(cur: Optional[InstallState] = None) -> InstallStatus: ...
class reboot:
def POST(): ...
class LinkAction(enum.Enum): class LinkAction(enum.Enum):
NEW = enum.auto() NEW = enum.auto()

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
from .reboot import RebootController
__all__ = [
'RebootController',
]

View File

@ -24,6 +24,7 @@ from .mirror import MirrorController
from .network import NetworkController from .network import NetworkController
from .package import PackageController from .package import PackageController
from .proxy import ProxyController from .proxy import ProxyController
from .reboot import RebootController
from .refresh import RefreshController from .refresh import RefreshController
from .reporting import ReportingController from .reporting import ReportingController
from .snaplist import SnapListController from .snaplist import SnapListController
@ -45,6 +46,7 @@ __all__ = [
'NetworkController', 'NetworkController',
'PackageController', 'PackageController',
'ProxyController', 'ProxyController',
'RebootController',
'RefreshController', 'RefreshController',
'ReportingController', 'ReportingController',
'SnapListController', 'SnapListController',

View File

@ -13,28 +13,49 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
import logging import logging
import os import os
import platform import platform
import subprocess import subprocess
from subiquitycore.async_helpers import schedule_task
from subiquitycore.context import with_context from subiquitycore.context import with_context
from subiquitycore.utils import arun_command, run_command 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") log = logging.getLogger("subiquity.controllers.restart")
class RebootController(SubiquityTuiController): class RebootController(SubiquityController):
endpoint = API.reboot
def __init__(self, app): def __init__(self, app):
super().__init__(app) super().__init__(app)
self.context.set('hidden', True) self.user_reboot_event = asyncio.Event()
self.rebooting_event = asyncio.Event()
def interactive(self): async def POST(self):
return self.app.interactive() 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() @with_context()
async def copy_logs_to_target(self, context): async def copy_logs_to_target(self, context):
@ -56,27 +77,12 @@ class RebootController(SubiquityTuiController):
except Exception: except Exception:
log.exception("saving journal failed") log.exception("saving journal failed")
def reboot(self): @with_context()
def reboot(self, context):
self.rebooting_event.set()
if self.opts.dry_run: if self.opts.dry_run:
self.app.exit() self.app.exit()
else: else:
if platform.machine() == 's390x': if platform.machine() == 's390x':
run_command(["chreipl", "/target/boot"]) run_command(["chreipl", "/target/boot"])
run_command(["/sbin/reboot"]) 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

View File

@ -131,6 +131,7 @@ class SubiquityServer(Application):
"SnapList", "SnapList",
"Install", "Install",
"Late", "Late",
"Reboot",
] ]
def make_model(self): def make_model(self):

View File

@ -36,8 +36,6 @@ class SubiquityUI(SubiquityCoreUI):
return super().keypress(size, key) return super().keypress(size, key)
def set_body(self, widget): def set_body(self, widget):
if widget is self.body:
return
super().set_body(widget) super().set_body(widget)
if isinstance(widget, BaseView): if isinstance(widget, BaseView):
for overlay in self.app.global_overlays: for overlay in self.app.global_overlays: