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