Merge pull request #1196 from ogayot/FR-2077

Add client-side controller and view to install third-party drivers on server installer
This commit is contained in:
Olivier Gayot 2022-03-17 10:38:48 +01:00 committed by GitHub
commit ad6cd6e383
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 232 additions and 14 deletions

View File

@ -56,4 +56,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -32,5 +32,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -35,3 +35,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -79,3 +79,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -66,3 +66,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -40,3 +40,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -107,3 +107,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -66,3 +66,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -39,5 +39,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -39,5 +39,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: no

View File

@ -31,5 +31,5 @@ SnapList:
classic: false classic: false
InstallProgress: InstallProgress:
reboot: yes reboot: yes
Drivers:
install: yes

View File

@ -113,6 +113,7 @@ class SubiquityClient(TuiApplication):
"Identity", "Identity",
"UbuntuPro", "UbuntuPro",
"SSH", "SSH",
"Drivers",
"SnapList", "SnapList",
"Progress", "Progress",
] ]

View File

@ -14,6 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from subiquitycore.tuicontroller import RepeatedController from subiquitycore.tuicontroller import RepeatedController
from .drivers import DriversController
from .filesystem import FilesystemController from .filesystem import FilesystemController
from .identity import IdentityController from .identity import IdentityController
from .keyboard import KeyboardController from .keyboard import KeyboardController
@ -32,6 +33,7 @@ from .zdev import ZdevController
# see SubiquityClient.controllers for another list # see SubiquityClient.controllers for another list
__all__ = [ __all__ = [
'DriversController',
'FilesystemController', 'FilesystemController',
'IdentityController', 'IdentityController',
'KeyboardController', 'KeyboardController',

View File

@ -0,0 +1,69 @@
# Copyright 2021 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 asyncio
import logging
from typing import List
from subiquitycore.tuicontroller import Skip
from subiquity.common.types import DriversPayload, DriversResponse
from subiquity.client.controller import SubiquityTuiController
from subiquity.ui.views.drivers import DriversView, DriversViewStatus
log = logging.getLogger('subiquity.client.controllers.drivers')
class DriversController(SubiquityTuiController):
endpoint_name = 'drivers'
async def make_ui(self) -> DriversView:
response: DriversResponse = await self.endpoint.GET()
if not response.drivers and response.drivers is not None:
raise Skip
return DriversView(self, response.drivers, response.install)
async def _wait_drivers(self) -> List[str]:
response: DriversResponse = await self.endpoint.GET(wait=True)
assert response.drivers is not None
return response.drivers
async def run_answers(self):
if 'install' not in self.answers:
return
from subiquitycore.testing.view_helpers import (
click,
)
view = self.app.ui.body
while view.status == DriversViewStatus.WAITING:
await asyncio.sleep(0.2)
if view.status == DriversViewStatus.NO_DRIVERS:
click(view.cont_btn.base_widget)
return
view.form.install.value = self.answers['install']
click(view.form.done_btn.base_widget)
def cancel(self) -> None:
self.app.prev_screen()
def done(self, install: bool) -> None:
log.debug("DriversController.done next_screen install=%s", install)
self.app.next_screen(
self.endpoint.POST(DriversPayload(install=install)))

View File

@ -88,11 +88,6 @@ class DriversController(SubiquityController):
log.debug("Available drivers to install: %s", self.drivers) log.debug("Available drivers to install: %s", self.drivers)
if not self.drivers: if not self.drivers:
await self.configured() await self.configured()
else:
# TODO Remove this once we have the GUI controller.
await self.POST(data=DriversPayload(
install=True,
))
async def GET(self, wait: bool = False) -> DriversResponse: async def GET(self, wait: bool = False) -> DriversResponse:
if wait: if wait:

View File

@ -0,0 +1,138 @@
# Copyright 2021 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/>.
""" Module defining the view for third-party drivers installation.
"""
import asyncio
from enum import auto, Enum
import logging
from typing import List, Optional
from urwid import (
connect_signal,
Text,
)
from subiquitycore.ui.buttons import ok_btn
from subiquitycore.ui.form import (
Form,
BooleanField,
)
from subiquitycore.ui.spinner import Spinner
from subiquitycore.ui.utils import screen
from subiquitycore.view import BaseView
log = logging.getLogger('subiquity.ui.views.drivers')
class DriversForm(Form):
""" Form that shows a checkbox to configure whether we want to install the
available drivers or not. """
cancel_label = _("Back")
install = BooleanField(_("Install the drivers"))
class DriversViewStatus(Enum):
WAITING = auto()
NO_DRIVERS = auto()
MAIN = auto()
class DriversView(BaseView):
title = _("Third-party drivers")
form = None
def __init__(self, controller, drivers: Optional[List[str]],
install: bool) -> None:
self.controller = controller
if drivers is None:
self.make_waiting(install)
else:
self.make_main(install, drivers)
def make_waiting(self, install: bool) -> None:
""" Change the view into a spinner and start waiting for drivers
asynchronously. """
self.spinner = Spinner(self.controller.app.aio_loop, style='dots')
self.spinner.start()
rows = [
Text(_("Looking for applicable third-party drivers...")),
Text(""),
self.spinner,
]
self.cont_btn = ok_btn(
_("Continue"),
on_press=lambda sender: self.done(False))
self._w = screen(rows, [self.cont_btn])
asyncio.create_task(self._wait(install))
self.status = DriversViewStatus.WAITING
async def _wait(self, install: bool) -> None:
""" Wait until the "list" of drivers is available and change the view
accordingly. """
drivers = await self.controller._wait_drivers()
self.spinner.stop()
if drivers:
self.make_main(install, drivers)
else:
self.make_no_drivers()
def make_no_drivers(self) -> None:
""" Change the view into an information page that shows that no
third-party drivers are available for installation. """
rows = [Text(_("No applicable third-party drivers were found."))]
self.cont_btn = ok_btn(
_("Continue"),
on_press=lambda sender: self.done(False))
self._w = screen(rows, [self.cont_btn])
self.status = DriversViewStatus.NO_DRIVERS
def make_main(self, install: bool, drivers: List[str]) -> None:
""" Change the view to display the drivers form. """
self.form = DriversForm(initial={'install': install})
excerpt = _(
"The following third-party drivers were found. "
"Do you want to install them?")
def on_cancel(_: DriversForm) -> None:
self.cancel()
connect_signal(
self.form, 'submit',
lambda result: self.done(result.install.value))
connect_signal(self.form, 'cancel', on_cancel)
rows = [Text(f"* {driver}") for driver in drivers]
rows.append(Text(""))
rows.extend(self.form.as_rows())
self._w = screen(rows, self.form.buttons, excerpt=excerpt)
self.status = DriversViewStatus.MAIN
def done(self, install: bool) -> None:
log.debug("User input: %r", install)
self.controller.done(install)
def cancel(self) -> None:
self.controller.cancel()