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
InstallProgress:
reboot: yes
Drivers:
install: no

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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