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: