Add basic implementation for third-party drivers GUI

[Michael Hudson Doyle]
 * Original patch

 [Olivier Gayot]
 * Make sure visible elements can be translated by using gettext _()
   construct.
 * Have GET /drivers provide the status of the checkbox (i.e. install
   drivers?) so we can restore the choice of the user when going back.
 * Store an object with an "install" boolean in the autoinstall schema ;
   instead of storing a boolean directly.

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-02-24 15:27:09 +01:00
parent d1d3f82be5
commit 2ec62051d4
15 changed files with 212 additions and 9 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",
"UbuntuAdvantage",
"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,79 @@
# 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 subiquitycore.tuicontroller import Skip
from subiquity.common.types import DriversPayload, DriversResponse
from subiquity.client.controller import SubiquityTuiController
from subiquity.ui.views.drivers import DriversView
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 response.drivers is not None and not response.drivers:
raise Skip
if response.drivers is None:
return DriversView(self, None, response.install)
else:
return DriversView(self, bool(response.drivers), response.install)
async def _wait_drivers(self) -> bool:
response: DriversResponse = await self.endpoint.GET(wait=True)
assert response.drivers is not None
return bool(response.drivers)
async def run_answers(self):
if 'install' not in self.answers:
return
import urwid
from subiquitycore.testing.view_helpers import (
click,
find_button_matching,
find_with_pred,
)
def text_finder(txt):
def pred(w):
return isinstance(w, urwid.Text) and txt in w.text
return pred
view = self.app.ui.body
while find_with_pred(view, text_finder('Looking for')):
await asyncio.sleep(0.2)
if find_with_pred(view, text_finder('No applicable')):
click(find_button_matching(view, "Continue"))
return
view.form.install.value = self.answers['install']
click(view.form.done_btn)
def cancel(self):
self.app.prev_screen()
def done(self, install):
log.debug("DriversController.done next_screen install=%s", install)
self.app.next_screen(
self.endpoint.POST(DriversPayload(install=install)))

View File

@ -0,0 +1,108 @@
# 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/>.
""" Install Path
Provides high level options for Ubuntu install
"""
import asyncio
import logging
from typing import 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):
cancel_label = _("Back")
install = BooleanField(_("Install the drivers?"))
class DriversView(BaseView):
title = _("Third-party drivers.")
form = None
def __init__(self, controller, has_drivers: Optional[bool],
install: bool) -> None:
self.controller = controller
if has_drivers is None:
self.make_waiting(install)
else:
self.make_main(install)
def make_waiting(self, install: bool):
self.spinner = Spinner(self.controller.app.aio_loop, style='dots')
self.spinner.start()
rows = [
Text(_("Looking for applicable third-party drivers...")),
Text(""),
self.spinner,
]
btn = ok_btn(_("Continue"), on_press=lambda sender: self.done(False))
self._w = screen(rows, [btn])
asyncio.create_task(self._wait(install))
async def _wait(self, install: bool):
has_drivers = await self.controller._wait_drivers()
self.spinner.stop()
if has_drivers:
self.make_main(install)
else:
self.make_no_drivers()
def make_no_drivers(self):
rows = [Text(_("No applicable third-party drivers were found."))]
btn = ok_btn(_("Continue"), on_press=lambda sender: self.done(False))
self._w = screen(rows, [btn])
def make_main(self, install: bool) -> None:
self.form = DriversForm(initial={'install': install})
excerpt = _(
"Third-party drivers were found. Do you want to install them?")
connect_signal(
self.form, 'submit',
lambda result: self.done(result.install.value))
connect_signal(self.form, 'cancel', self.cancel)
self._w = self.form.as_screen(excerpt=_(excerpt))
def done(self, result):
log.debug("User input: %r", result)
self.controller.done(result)
def cancel(self, result=None):
self.controller.cancel()