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:
commit
ad6cd6e383
|
@ -56,4 +56,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -32,5 +32,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
|
||||
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -35,3 +35,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -79,3 +79,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -66,3 +66,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -40,3 +40,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -107,3 +107,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -66,3 +66,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -39,5 +39,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
|
||||
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -39,5 +39,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
|
||||
|
||||
Drivers:
|
||||
install: no
|
||||
|
|
|
@ -31,5 +31,5 @@ SnapList:
|
|||
classic: false
|
||||
InstallProgress:
|
||||
reboot: yes
|
||||
|
||||
|
||||
Drivers:
|
||||
install: yes
|
||||
|
|
|
@ -113,6 +113,7 @@ class SubiquityClient(TuiApplication):
|
|||
"Identity",
|
||||
"UbuntuPro",
|
||||
"SSH",
|
||||
"Drivers",
|
||||
"SnapList",
|
||||
"Progress",
|
||||
]
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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)))
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue