From fc392470739f5b3d7b1456a051a0462bb9bdbc3f Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 19 Aug 2021 13:11:04 +1200 Subject: [PATCH 1/4] rename "reboot" controller/endpoint to "shutdown" --- po/POTFILES.in | 12 +++++++++- subiquity/client/client.py | 2 +- subiquity/client/controllers/progress.py | 2 +- subiquity/common/apidef.py | 2 +- subiquity/server/controllers/__init__.py | 4 ++-- .../controllers/{reboot.py => shutdown.py} | 22 +++++++++---------- subiquity/server/server.py | 2 +- 7 files changed, 28 insertions(+), 18 deletions(-) rename subiquity/server/controllers/{reboot.py => shutdown.py} (86%) diff --git a/po/POTFILES.in b/po/POTFILES.in index 29032463..6efa40b2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -42,6 +42,7 @@ subiquity/common/filesystem/tests/test_actions.py subiquity/common/filesystem/tests/test_labels.py subiquity/common/filesystem/tests/test_manipulator.py subiquity/common/__init__.py +subiquity/common/resources.py subiquity/common/serialize.py subiquity/common/tests/__init__.py subiquity/common/tests/test_serialization.py @@ -72,8 +73,11 @@ subiquitycore/ssh.py subiquitycore/testing/__init__.py subiquitycore/testing/view_helpers.py subiquitycore/tests/__init__.py +subiquitycore/tests/mocks.py subiquitycore/tests/test_netplan.py +subiquitycore/tests/test_pubsub.py subiquitycore/tests/test_view.py +subiquitycore/tests/util.py subiquitycore/tuicontroller.py subiquitycore/tui.py subiquitycore/ui/actionmenu.py @@ -120,6 +124,7 @@ subiquity/models/tests/__init__.py subiquity/models/tests/test_filesystem.py subiquity/models/tests/test_mirror.py subiquity/models/tests/test_subiquity.py +subiquity/models/timezone.py subiquity/models/updates.py subiquity/server/controller.py subiquity/server/controllers/cmdlist.py @@ -135,21 +140,26 @@ 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/shutdown.py subiquity/server/controllers/snaplist.py subiquity/server/controllers/ssh.py subiquity/server/controllers/tests/test_keyboard.py +subiquity/server/controllers/timezone.py subiquity/server/controllers/updates.py subiquity/server/controllers/userdata.py subiquity/server/controllers/zdev.py subiquity/server/dryrun.py subiquity/server/errors.py +subiquity/server/geoip.py subiquity/server/__init__.py subiquity/server/server.py +subiquity/server/tests/__init__.py +subiquity/server/tests/test_geoip.py subiquity/tests/fakes.py subiquity/tests/__init__.py +subiquity/tests/test_timezonecontroller.py subiquity/ui/frame.py subiquity/ui/__init__.py subiquity/ui/mount.py diff --git a/subiquity/client/client.py b/subiquity/client/client.py index 0f8a6303..d513ce38 100644 --- a/subiquity/client/client.py +++ b/subiquity/client/client.py @@ -252,7 +252,7 @@ class SubiquityClient(TuiApplication): elif event['SUBIQUITY_EVENT_TYPE'] == 'finish': print('finish: ' + event["MESSAGE"]) context_name = event.get('SUBIQUITY_CONTEXT_NAME', '') - if context_name == 'subiquity/Reboot/reboot': + if context_name == 'subiquity/Shutdown/shutdown': self.exit() async def connect(self): diff --git a/subiquity/client/controllers/progress.py b/subiquity/client/controllers/progress.py index c3dbafe2..a173a0f5 100644 --- a/subiquity/client/controllers/progress.py +++ b/subiquity/client/controllers/progress.py @@ -65,7 +65,7 @@ class ProgressController(SubiquityTuiController): async def send_reboot_and_wait(self): try: - await self.app.client.reboot.POST() + await self.app.client.shutdown.POST() except aiohttp.ClientError: pass self.app.exit() diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index 9f564ecd..133d3d66 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -235,7 +235,7 @@ class API: def GET() -> TimeZoneInfo: ... def POST(tz: str): ... - class reboot: + class shutdown: def POST(): ... diff --git a/subiquity/server/controllers/__init__.py b/subiquity/server/controllers/__init__.py index a4498c11..3edbc7d4 100644 --- a/subiquity/server/controllers/__init__.py +++ b/subiquity/server/controllers/__init__.py @@ -25,9 +25,9 @@ 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 .shutdown import ShutdownController from .snaplist import SnapListController from .ssh import SSHController from .timezone import TimeZoneController @@ -50,9 +50,9 @@ __all__ = [ 'NetworkController', 'PackageController', 'ProxyController', - 'RebootController', 'RefreshController', 'ReportingController', + 'ShutdownController', 'SnapListController', 'SSHController', 'TimeZoneController', diff --git a/subiquity/server/controllers/reboot.py b/subiquity/server/controllers/shutdown.py similarity index 86% rename from subiquity/server/controllers/reboot.py rename to subiquity/server/controllers/shutdown.py index fe68a075..77159c8a 100644 --- a/subiquity/server/controllers/reboot.py +++ b/subiquity/server/controllers/shutdown.py @@ -29,19 +29,19 @@ from subiquity.server.controllers.install import ApplicationState log = logging.getLogger("subiquity.controllers.restart") -class RebootController(SubiquityController): +class ShutdownController(SubiquityController): - endpoint = API.reboot + endpoint = API.shutdown def __init__(self, app): super().__init__(app) - self.user_reboot_event = asyncio.Event() - self.rebooting_event = asyncio.Event() + self.user_shutdown_event = asyncio.Event() + self.shuttingdown_event = asyncio.Event() async def POST(self): self.app.controllers.Install.stop_uu() - self.user_reboot_event.set() - await self.rebooting_event.wait() + self.user_shutdown_event.set() + await self.shuttingdown_event.wait() def interactive(self): return self.app.interactive @@ -55,10 +55,10 @@ class RebootController(SubiquityController): 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() + await self.user_shutdown_event.wait() + self.shutdown() elif self.app.state == ApplicationState.DONE: - self.reboot() + self.shutdown() @with_context() async def copy_logs_to_target(self, context): @@ -81,8 +81,8 @@ class RebootController(SubiquityController): log.exception("saving journal failed") @with_context() - def reboot(self, context): - self.rebooting_event.set() + def shutdown(self, context): + self.shuttingdown_event.set() if self.opts.dry_run: self.app.exit() else: diff --git a/subiquity/server/server.py b/subiquity/server/server.py index e4743e7e..c4651e94 100644 --- a/subiquity/server/server.py +++ b/subiquity/server/server.py @@ -222,7 +222,7 @@ class SubiquityServer(Application): "Install", "Updates", "Late", - "Reboot", + "Shutdown", ] def make_model(self): From a2675b6ca6e80a227692c95dfa9a1378af6fc803 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 19 Aug 2021 14:12:32 +1200 Subject: [PATCH 2/4] add an immediate parameter to request immediate reboot --- subiquity/common/apidef.py | 2 +- subiquity/server/controllers/shutdown.py | 25 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index 133d3d66..e99bbe87 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -236,7 +236,7 @@ class API: def POST(tz: str): ... class shutdown: - def POST(): ... + def POST(immediate: bool = False): ... class LinkAction(enum.Enum): diff --git a/subiquity/server/controllers/shutdown.py b/subiquity/server/controllers/shutdown.py index 77159c8a..fb8ccf4e 100644 --- a/subiquity/server/controllers/shutdown.py +++ b/subiquity/server/controllers/shutdown.py @@ -35,25 +35,37 @@ class ShutdownController(SubiquityController): def __init__(self, app): super().__init__(app) + # user_shutdown_event is set when the user requests the shutdown. + # server_reboot_event is set when the server is ready for shutdown + # shuttingdown_event is set when the shutdown has begun (so don't + # depend on anything actually happening after it is set, it's all a bag + # of races from that point!) self.user_shutdown_event = asyncio.Event() + self.server_reboot_event = asyncio.Event() self.shuttingdown_event = asyncio.Event() - async def POST(self): + async def POST(self, immediate: bool = False): self.app.controllers.Install.stop_uu() self.user_shutdown_event.set() + if immediate: + self.server_reboot_event.set() await self.shuttingdown_event.wait() def interactive(self): return self.app.interactive def start(self): + self.app.aio_loop.create_task(self._wait_install()) self.app.aio_loop.create_task(self._run()) - async def _run(self): - Install = self.app.controllers.Install - await Install.install_task + async def _wait_install(self): + await self.app.controllers.Install.install_task await self.app.controllers.Late.run_event.wait() await self.copy_logs_to_target() + self.server_reboot_event.set() + + async def _run(self): + await self.server_reboot_event.wait() if self.app.interactive: await self.user_shutdown_event.wait() self.shutdown() @@ -86,6 +98,7 @@ class ShutdownController(SubiquityController): if self.opts.dry_run: self.app.exit() else: - if platform.machine() == 's390x': - run_command(["chreipl", "/target/boot"]) + if self.app.state == ApplicationState.DONE: + if platform.machine() == 's390x': + run_command(["chreipl", "/target/boot"]) run_command(["/sbin/reboot"]) From 7f1beb1d6449978a81acf61426eb47b45a7259be Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 19 Aug 2021 14:28:38 +1200 Subject: [PATCH 3/4] add a mode argument to shutdown to allow reboot or power off --- subiquity/client/controllers/progress.py | 7 +++++-- subiquity/common/apidef.py | 3 ++- subiquity/common/types.py | 5 +++++ subiquity/server/controllers/shutdown.py | 12 +++++++++--- subiquitycore/context.py | 2 +- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/subiquity/client/controllers/progress.py b/subiquity/client/controllers/progress.py index a173a0f5..fba00777 100644 --- a/subiquity/client/controllers/progress.py +++ b/subiquity/client/controllers/progress.py @@ -21,7 +21,10 @@ import aiohttp from subiquitycore.context import with_context from subiquity.client.controller import SubiquityTuiController -from subiquity.common.types import ApplicationState +from subiquity.common.types import ( + ApplicationState, + ShutdownMode, + ) from subiquity.ui.views.installprogress import ( InstallRunning, ProgressView, @@ -65,7 +68,7 @@ class ProgressController(SubiquityTuiController): async def send_reboot_and_wait(self): try: - await self.app.client.shutdown.POST() + await self.app.client.shutdown.POST(mode=ShutdownMode.REBOOT) except aiohttp.ClientError: pass self.app.exit() diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index e99bbe87..3bacac94 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -37,6 +37,7 @@ from subiquity.common.types import ( IdentityData, NetworkStatus, RefreshStatus, + ShutdownMode, SnapInfo, SnapListResponse, SnapSelection, @@ -236,7 +237,7 @@ class API: def POST(tz: str): ... class shutdown: - def POST(immediate: bool = False): ... + def POST(mode: ShutdownMode, immediate: bool = False): ... class LinkAction(enum.Enum): diff --git a/subiquity/common/types.py b/subiquity/common/types.py index 6bbc6250..8461b11d 100644 --- a/subiquity/common/types.py +++ b/subiquity/common/types.py @@ -330,3 +330,8 @@ class SnapListResponse: class TimeZoneInfo: timezone: str from_geoip: bool + + +class ShutdownMode(enum.Enum): + REBOOT = enum.auto() + POWEROFF = enum.auto() diff --git a/subiquity/server/controllers/shutdown.py b/subiquity/server/controllers/shutdown.py index fb8ccf4e..bf74f4cb 100644 --- a/subiquity/server/controllers/shutdown.py +++ b/subiquity/server/controllers/shutdown.py @@ -23,6 +23,7 @@ from subiquitycore.context import with_context from subiquitycore.utils import arun_command, run_command from subiquity.common.apidef import API +from subiquity.common.types import ShutdownMode from subiquity.server.controller import SubiquityController from subiquity.server.controllers.install import ApplicationState @@ -43,8 +44,10 @@ class ShutdownController(SubiquityController): self.user_shutdown_event = asyncio.Event() self.server_reboot_event = asyncio.Event() self.shuttingdown_event = asyncio.Event() + self.mode = ShutdownMode.REBOOT - async def POST(self, immediate: bool = False): + async def POST(self, mode: ShutdownMode, immediate: bool = False): + self.mode = mode self.app.controllers.Install.stop_uu() self.user_shutdown_event.set() if immediate: @@ -92,7 +95,7 @@ class ShutdownController(SubiquityController): except Exception: log.exception("saving journal failed") - @with_context() + @with_context(description='mode={self.mode.name}') def shutdown(self, context): self.shuttingdown_event.set() if self.opts.dry_run: @@ -101,4 +104,7 @@ class ShutdownController(SubiquityController): if self.app.state == ApplicationState.DONE: if platform.machine() == 's390x': run_command(["chreipl", "/target/boot"]) - run_command(["/sbin/reboot"]) + if self.mode == ShutdownMode.REBOOT: + run_command(["/sbin/reboot"]) + elif self.mode == ShutdownMode.POWEROFF: + run_command(["/sbin/poweroff"]) diff --git a/subiquitycore/context.py b/subiquitycore/context.py index 0ed9c399..954ec041 100644 --- a/subiquitycore/context.py +++ b/subiquitycore/context.py @@ -131,7 +131,7 @@ def with_context(name=None, description="", **context_kw): context = self.context kw['context'] = context.child( name=name.format(**kw), - description=description.format(**kw), + description=description.format(self=self, **kw), **context_kw) return kw From a9d7e46b5f48f1b9d504d10a4a206fae1a0e8f8f Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 19 Aug 2021 14:52:34 +1200 Subject: [PATCH 4/4] add autoinstall config to choose shutdown mode --- autoinstall-schema.json | 7 +++++++ subiquity/server/controllers/shutdown.py | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/autoinstall-schema.json b/autoinstall-schema.json index 1e78394d..1503dcb2 100644 --- a/autoinstall-schema.json +++ b/autoinstall-schema.json @@ -721,6 +721,13 @@ "type": "string" } } + }, + "shutdown": { + "type": "string", + "enum": [ + "reboot", + "poweroff" + ] } }, "required": [ diff --git a/subiquity/server/controllers/shutdown.py b/subiquity/server/controllers/shutdown.py index bf74f4cb..e82732b1 100644 --- a/subiquity/server/controllers/shutdown.py +++ b/subiquity/server/controllers/shutdown.py @@ -33,6 +33,11 @@ log = logging.getLogger("subiquity.controllers.restart") class ShutdownController(SubiquityController): endpoint = API.shutdown + autoinstall_key = 'shutdown' + autoinstall_schema = { + 'type': 'string', + 'enum': ['reboot', 'poweroff'] + } def __init__(self, app): super().__init__(app) @@ -46,6 +51,12 @@ class ShutdownController(SubiquityController): self.shuttingdown_event = asyncio.Event() self.mode = ShutdownMode.REBOOT + def load_autoinstall_data(self, data): + if data == 'reboot': + self.mode = ShutdownMode.REBOOT + elif data == 'poweroff': + self.mode = ShutdownMode.POWEROFF + async def POST(self, mode: ShutdownMode, immediate: bool = False): self.mode = mode self.app.controllers.Install.stop_uu()