From c76a8f23e34a2b04e368dff94eced6805a7635ce Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Tue, 29 Mar 2022 15:18:56 +0200 Subject: [PATCH] source: give the option to search for third-party drivers The source screen now includes a checkbox that makes Subiquity search for third-party drivers in a later stage. Signed-off-by: Olivier Gayot --- subiquity/client/controllers/source.py | 21 ++++++++++---- subiquity/common/apidef.py | 2 +- subiquity/common/types.py | 1 + subiquity/models/source.py | 1 + subiquity/server/controllers/source.py | 6 ++-- subiquity/tests/api/test_api.py | 2 ++ subiquity/ui/views/source.py | 38 ++++++++++++++++++++++---- 7 files changed, 57 insertions(+), 14 deletions(-) diff --git a/subiquity/client/controllers/source.py b/subiquity/client/controllers/source.py index 7c212d10..8d3d17de 100644 --- a/subiquity/client/controllers/source.py +++ b/subiquity/client/controllers/source.py @@ -27,18 +27,27 @@ class SourceController(SubiquityTuiController): async def make_ui(self): sources = await self.endpoint.GET() - return SourceView(self, sources.sources, sources.current_id) + return SourceView(self, + sources.sources, + sources.current_id, + sources.search_drivers) def run_answers(self): + form = self.app.ui.body.form + if "search_drivers" in self.answers: + form.search_drivers.value = self.answers["search_drivers"] if 'source' in self.answers: wanted_id = self.answers['source'] - for bf in self.app.ui.body.form._fields: + for bf in form._fields: + if bf is form.search_drivers: + continue bf.value = bf.field.name == wanted_id - self.app.ui.body.form._click_done(None) + form._click_done(None) def cancel(self): self.app.prev_screen() - def done(self, source_id): - log.debug("SourceController.done source_id=%s", source_id) - self.app.next_screen(self.endpoint.POST(source_id)) + def done(self, source_id, search_drivers: bool): + log.debug("SourceController.done source_id=%s, search_drivers=%s", + source_id, search_drivers) + self.app.next_screen(self.endpoint.POST(source_id, search_drivers)) diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index d5e5bb49..49bc8618 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -145,7 +145,7 @@ class API: class source: def GET() -> SourceSelectionAndSetting: ... - def POST(source_id: str) -> None: ... + def POST(source_id: str, search_drivers: bool) -> None: ... class zdev: def GET() -> List[ZdevInfo]: ... diff --git a/subiquity/common/types.py b/subiquity/common/types.py index 290c98cf..ab6ad68d 100644 --- a/subiquity/common/types.py +++ b/subiquity/common/types.py @@ -183,6 +183,7 @@ class SourceSelection: class SourceSelectionAndSetting: sources: List[SourceSelection] current_id: str + search_drivers: bool @attr.s(auto_attribs=True) diff --git a/subiquity/models/source.py b/subiquity/models/source.py index 1d766dbc..e1df107c 100644 --- a/subiquity/models/source.py +++ b/subiquity/models/source.py @@ -68,6 +68,7 @@ class SourceModel: self.current = fake_entries['server'] self.sources = [self.current] self.lang = None + self.search_drivers = False def load_from_file(self, fp): self._dir = os.path.dirname(fp.name) diff --git a/subiquity/server/controllers/source.py b/subiquity/server/controllers/source.py index 385ed3cf..af3681d3 100644 --- a/subiquity/server/controllers/source.py +++ b/subiquity/server/controllers/source.py @@ -86,7 +86,8 @@ class SourceController(SubiquityController): convert_source(source, cur_lang) for source in self.model.sources ], - self.model.current.id) + self.model.current.id, + search_drivers=self.model.search_drivers) async def configured(self): if self._handler is not None: @@ -100,7 +101,8 @@ class SourceController(SubiquityController): await super().configured() self.app.base_model.set_source_variant(self.model.current.variant) - async def POST(self, source_id: str) -> None: + async def POST(self, source_id: str, search_drivers: bool) -> None: + self.model.search_drivers = search_drivers for source in self.model.sources: if source.id == source_id: self.model.current = source diff --git a/subiquity/tests/api/test_api.py b/subiquity/tests/api/test_api.py index 00788e85..94d66cbe 100755 --- a/subiquity/tests/api/test_api.py +++ b/subiquity/tests/api/test_api.py @@ -948,6 +948,8 @@ class TestCancel(TestAPI): async def test_cancel_drivers(self): with patch.dict(os.environ, {'SUBIQUITY_DEBUG': 'has-drivers'}): async with start_server('examples/simple.json') as inst: + await inst.post('/source', source_id="dummy", + search_drivers=True) # /drivers?wait=true is expected to block until APT is # configured. # Let's make sure we cancel it. diff --git a/subiquity/ui/views/source.py b/subiquity/ui/views/source.py index 634c5d08..8687d4e3 100644 --- a/subiquity/ui/views/source.py +++ b/subiquity/ui/views/source.py @@ -14,13 +14,21 @@ # along with this program. If not, see . import logging -from urwid import connect_signal +from typing import List + +from urwid import ( + connect_signal, + Text, +) from subiquitycore.view import BaseView +from subiquitycore.ui.container import ListBox from subiquitycore.ui.form import ( + BooleanField, Form, RadioButtonField, ) +from subiquitycore.ui.utils import screen log = logging.getLogger('subiquity.ui.views.source') @@ -29,10 +37,10 @@ class SourceView(BaseView): title = _("Choose type of install") - def __init__(self, controller, sources, current_id): + def __init__(self, controller, sources, current_id, search_drivers: bool): self.controller = controller - group = [] + group: List[RadioButtonField] = [] ns = { 'cancel_label': _("Back"), @@ -47,6 +55,12 @@ class SourceView(BaseView): group, source.name, '\n' + source.description) initial[source.id] = source.id == current_id + ns["search_drivers"] = BooleanField( + _("Search for third-party drivers"), "\n" + + _("This software is subject to license terms included with its " + "documentation. Some is proprietary.")) + initial["search_drivers"] = search_drivers + SourceForm = type(Form)('SourceForm', (Form,), ns) log.debug('%r %r', ns, current_id) @@ -57,13 +71,27 @@ class SourceView(BaseView): excerpt = _("Choose the base for the installation.") - super().__init__(self.form.as_screen(excerpt=excerpt)) + # NOTE Hack to insert the "Additional options" text between two fields + # of the form. + rows = self.form.as_rows() + rows.insert(-2, Text("")) + rows.insert(-2, Text("Additional options")) + + super().__init__( + screen( + ListBox(rows), + self.form.buttons, + excerpt=excerpt, + focus_buttons=True)) def done(self, result): log.debug("User input: {}".format(result.as_data())) + search_drivers = result.as_data()["search_drivers"] for k, v in result.as_data().items(): + if k == "search_drivers": + continue if v: - self.controller.done(k) + self.controller.done(k, search_drivers=search_drivers) def cancel(self, result=None): self.controller.cancel()