From a6270cbaa1cd25cfeb62b70b61908f56dd02cbc3 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 19 Aug 2021 16:47:03 +1200 Subject: [PATCH 1/4] server side implementation of source selection --- po/POTFILES.in | 5 + subiquity/cmd/server.py | 2 + subiquity/common/apidef.py | 5 + subiquity/common/types.py | 16 +++ subiquity/models/source.py | 92 ++++++++++++++ subiquity/models/subiquity.py | 7 +- subiquity/models/tests/test_source.py | 117 ++++++++++++++++++ subiquity/server/controllers/__init__.py | 2 + subiquity/server/controllers/source.py | 83 +++++++++++++ .../server/controllers/tests/__init__.py | 1 + .../server/controllers/tests/test_source.py | 65 ++++++++++ subiquity/server/server.py | 10 +- subiquitycore/tests/util.py | 6 + 13 files changed, 406 insertions(+), 5 deletions(-) create mode 100644 subiquity/models/source.py create mode 100644 subiquity/models/tests/test_source.py create mode 100644 subiquity/server/controllers/source.py create mode 100644 subiquity/server/controllers/tests/__init__.py create mode 100644 subiquity/server/controllers/tests/test_source.py diff --git a/po/POTFILES.in b/po/POTFILES.in index 6efa40b2..387aa76b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -118,11 +118,13 @@ subiquity/models/mirror.py subiquity/models/network.py subiquity/models/proxy.py subiquity/models/snaplist.py +subiquity/models/source.py subiquity/models/ssh.py subiquity/models/subiquity.py subiquity/models/tests/__init__.py subiquity/models/tests/test_filesystem.py subiquity/models/tests/test_mirror.py +subiquity/models/tests/test_source.py subiquity/models/tests/test_subiquity.py subiquity/models/timezone.py subiquity/models/updates.py @@ -144,8 +146,11 @@ subiquity/server/controllers/refresh.py subiquity/server/controllers/reporting.py subiquity/server/controllers/shutdown.py subiquity/server/controllers/snaplist.py +subiquity/server/controllers/source.py subiquity/server/controllers/ssh.py +subiquity/server/controllers/tests/__init__.py subiquity/server/controllers/tests/test_keyboard.py +subiquity/server/controllers/tests/test_source.py subiquity/server/controllers/timezone.py subiquity/server/controllers/updates.py subiquity/server/controllers/userdata.py diff --git a/subiquity/cmd/server.py b/subiquity/cmd/server.py index 54244f4c..7e5c88d1 100644 --- a/subiquity/cmd/server.py +++ b/subiquity/cmd/server.py @@ -61,6 +61,8 @@ def make_server_args_parser(): '--snap-section', action='store', default='server', help=("Show snaps from this section of the store in the snap " "list screen.")) + parser.add_argument( + '--source-catalog', dest='source_catalog', action='store') return parser diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index e8400bc4..e1096ea2 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -41,6 +41,7 @@ from subiquity.common.types import ( SnapInfo, SnapListResponse, SnapSelection, + SourceSelectionAndSetting, SSHData, LiveSessionSSHInfo, StorageResponse, @@ -123,6 +124,10 @@ class API: class steps: def GET(index: Optional[str]) -> AnyStep: ... + class source: + def GET() -> SourceSelectionAndSetting: ... + def POST(source_id: str) -> None: ... + class zdev: def GET() -> List[ZdevInfo]: ... diff --git a/subiquity/common/types.py b/subiquity/common/types.py index 4000e459..db507887 100644 --- a/subiquity/common/types.py +++ b/subiquity/common/types.py @@ -168,6 +168,22 @@ class KeyboardSetup: layouts: List[KeyboardLayout] +@attr.s(auto_attribs=True) +class SourceSelection: + name: str + description: str + id: str + size: int + variant: str + default: bool + + +@attr.s(auto_attribs=True) +class SourceSelectionAndSetting: + sources: List[SourceSelection] + current_id: str + + @attr.s(auto_attribs=True) class ZdevInfo: id: str diff --git a/subiquity/models/source.py b/subiquity/models/source.py new file mode 100644 index 00000000..99df435e --- /dev/null +++ b/subiquity/models/source.py @@ -0,0 +1,92 @@ +# 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 . + +import logging +import os +import typing +import yaml + +import attr + +log = logging.getLogger('subiquity.models.source') + + +@attr.s(auto_attribs=True) +class CatalogEntry: + variant: str + id: str + name: typing.Dict[str, str] + description: typing.Dict[str, str] + path: str + size: int + type: str + default: bool = False + + +fake_entries = { + 'server': CatalogEntry( + variant='server', + id='synthesized', + name={'en': 'Ubuntu Server'}, + description={'en': 'the default'}, + path='/media/filesystem', + type='cp', + default=True, + size=2 << 30), + 'desktop': CatalogEntry( + variant='desktop', + id='synthesized', + name={'en': 'Ubuntu Desktop'}, + description={'en': 'the default'}, + path='/media/filesystem', + type='cp', + default=True, + size=5 << 30), + } + + +class SourceModel: + + def __init__(self): + self._dir = '/cdrom/casper' + self.current = fake_entries['server'] + self.sources = [self.current] + + def load_from_file(self, fp): + self._dir = os.path.dirname(fp.name) + self.sources = [] + self.current = None + entries = yaml.safe_load(fp) + for entry in entries: + kw = {} + for field in attr.fields(CatalogEntry): + if field.name in entry: + kw[field.name] = entry[field.name] + c = CatalogEntry(**kw) + self.sources.append(c) + if c.default: + self.current = c + log.debug("loaded %d sources from %r", len(self.sources), fp.name) + if self.current is None: + self.current = self.sources[0] + + def render(self): + path = os.path.join(self._dir, self.current.path) + scheme = self.current.type + return { + 'sources': { + 'ubuntu00': f'{scheme}://{path}' + }, + } diff --git a/subiquity/models/subiquity.py b/subiquity/models/subiquity.py index 99de443c..1bb43c81 100644 --- a/subiquity/models/subiquity.py +++ b/subiquity/models/subiquity.py @@ -39,6 +39,7 @@ from .mirror import MirrorModel from .network import NetworkModel from .proxy import ProxyModel from .snaplist import SnapListModel +from .source import SourceModel from .ssh import SSHModel from .timezone import TimeZoneModel from .updates import UpdatesModel @@ -116,6 +117,7 @@ class SubiquityModel: self.proxy = ProxyModel() self.snaplist = SnapListModel() self.ssh = SSHModel() + self.source = SourceModel() self.timezone = TimeZoneModel() self.updates = UpdatesModel() self.userdata = {} @@ -307,16 +309,13 @@ class SubiquityModel: config = { 'stages': stages, - 'sources': { - 'ubuntu00': 'cp:///media/filesystem' - }, - 'curthooks_commands': { '001-configure-apt': [ resource_path('bin/subiquity-configure-apt'), sys.executable, str(self.network.has_network).lower(), ], }, + 'grub': { 'terminal': 'unmodified', 'probe_additional_os': True diff --git a/subiquity/models/tests/test_source.py b/subiquity/models/tests/test_source.py new file mode 100644 index 00000000..1663fb52 --- /dev/null +++ b/subiquity/models/tests/test_source.py @@ -0,0 +1,117 @@ +# 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 . + +import os +import random +import shutil +import tempfile +import unittest +import yaml + +from subiquitycore.tests.util import random_string + +from subiquity.models.source import ( + SourceModel, + ) + + +def make_entry(**fields): + raw = { + 'name': { + 'en': random_string(), + }, + 'description': { + 'en': random_string(), + }, + 'id': random_string(), + 'type': random_string(), + 'variant': random_string(), + 'path': random_string(), + 'size': random.randint(1000000, 2000000), + } + for k, v in fields.items(): + raw[k] = v + return raw + + +class TestMirrorModel(unittest.TestCase): + + def tdir(self): + tdir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, tdir) + return tdir + + def write_and_load_entries(self, model, entries, dir=None): + if dir is None: + dir = self.tdir() + cat_path = os.path.join(dir, 'catalog.yaml') + with open(cat_path, 'w') as fp: + yaml.dump(entries, fp) + with open(cat_path) as fp: + model.load_from_file(fp) + + def test_initially_server(self): + model = SourceModel() + self.assertEqual(model.current.variant, 'server') + + def test_load_from_file_simple(self): + entry = make_entry(id='id1') + model = SourceModel() + self.write_and_load_entries(model, [entry]) + self.assertEqual(model.current.id, 'id1') + + def test_load_from_file_ignores_extra_keys(self): + entry = make_entry(id='id1', foobarbaz=random_string()) + model = SourceModel() + self.write_and_load_entries(model, [entry]) + self.assertEqual(model.current.id, 'id1') + + def test_default(self): + entries = [ + make_entry(id='id1'), + make_entry(id='id2', default=True), + make_entry(id='id3'), + ] + model = SourceModel() + self.write_and_load_entries(model, entries) + self.assertEqual(model.current.id, 'id2') + + def test_render_absolute(self): + entry = make_entry( + type='scheme', + path='/foo/bar/baz', + ) + model = SourceModel() + self.write_and_load_entries(model, [entry]) + self.assertEqual( + model.render(), {'sources': {'ubuntu00': 'scheme:///foo/bar/baz'}}) + + def test_render_relative(self): + dir = self.tdir() + entry = make_entry( + type='scheme', + path='foo/bar/baz', + ) + model = SourceModel() + self.write_and_load_entries(model, [entry], dir) + self.assertEqual( + model.render(), + {'sources': {'ubuntu00': f'scheme://{dir}/foo/bar/baz'}}) + + def test_render_initial(self): + model = SourceModel() + self.assertEqual( + model.render(), + {'sources': {'ubuntu00': 'cp:///media/filesystem'}}) diff --git a/subiquity/server/controllers/__init__.py b/subiquity/server/controllers/__init__.py index 3edbc7d4..d875238b 100644 --- a/subiquity/server/controllers/__init__.py +++ b/subiquity/server/controllers/__init__.py @@ -29,6 +29,7 @@ from .refresh import RefreshController from .reporting import ReportingController from .shutdown import ShutdownController from .snaplist import SnapListController +from .source import SourceController from .ssh import SSHController from .timezone import TimeZoneController from .updates import UpdatesController @@ -54,6 +55,7 @@ __all__ = [ 'ReportingController', 'ShutdownController', 'SnapListController', + 'SourceController', 'SSHController', 'TimeZoneController', 'UpdatesController', diff --git a/subiquity/server/controllers/source.py b/subiquity/server/controllers/source.py new file mode 100644 index 00000000..a9fe65e2 --- /dev/null +++ b/subiquity/server/controllers/source.py @@ -0,0 +1,83 @@ +# 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 . + +import os + +from subiquity.common.apidef import API +from subiquity.common.types import ( + SourceSelection, + SourceSelectionAndSetting, + ) +from subiquity.server.controller import SubiquityController + + +def _translate(d, lang): + if lang: + for lang in lang, lang.split('_', 1)[0]: + if lang in d: + return d[lang] + return _(d['en']) + + +def convert_source(source, lang): + return SourceSelection( + name=_translate(source.name, lang), + description=_translate(source.description, lang), + id=source.id, + size=source.size, + variant=source.variant, + default=source.default) + + +class SourceController(SubiquityController): + + model_name = "source" + + endpoint = API.source + + def start(self): + path = '/cdrom/casper/install-sources.yaml' + if self.app.opts.source_catalog is not None: + path = self.app.opts.source_catalog + if not os.path.exists(path): + return + with open(path) as fp: + self.model.load_from_file(fp) + + def interactive(self): + if len(self.model.sources) <= 1: + return False + return super().interactive() + + async def GET(self) -> SourceSelectionAndSetting: + cur_lang = self.app.base_model.locale.selected_language + cur_lang = cur_lang.rsplit('.', 1)[0] + + return SourceSelectionAndSetting( + [ + convert_source(source, cur_lang) + for source in self.model.sources + ], + self.model.current.id) + + def configured(self): + super().configured() + self.app.base_model.set_source_variant(self.model.current.variant) + + async def POST(self, source_id: str) -> None: + for source in self.model.sources: + if source.id == source_id: + self.model.current = source + self.configured() diff --git a/subiquity/server/controllers/tests/__init__.py b/subiquity/server/controllers/tests/__init__.py new file mode 100644 index 00000000..792d6005 --- /dev/null +++ b/subiquity/server/controllers/tests/__init__.py @@ -0,0 +1 @@ +# diff --git a/subiquity/server/controllers/tests/test_source.py b/subiquity/server/controllers/tests/test_source.py new file mode 100644 index 00000000..fbe03323 --- /dev/null +++ b/subiquity/server/controllers/tests/test_source.py @@ -0,0 +1,65 @@ +# 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 . + +import random +import unittest + +import attr + +from subiquitycore.tests.util import random_string + +from subiquity.models.source import CatalogEntry +from subiquity.server.controllers.source import convert_source + + +def make_entry(**kw): + fields = { + 'default': False, + 'name': {'en': random_string()}, + 'description': {'en': random_string()}, + 'size': random.randint(1000000, 2000000), + } + for field in attr.fields(CatalogEntry): + if field.name not in fields: + fields[field.name] = random_string() + for k, v in kw.items(): + fields[k] = v + return CatalogEntry(**fields) + + +class TestSubiquityModel(unittest.TestCase): + + def test_convert_source(self): + entry = make_entry() + source = convert_source(entry, 'C') + self.assertEqual(source.id, entry.id) + + def test_convert_translations(self): + entry = make_entry( + name={ + 'en': 'English', + 'fr': 'French', + 'fr_CA': 'French Canadian', + }) + self.assertEqual( + convert_source(entry, 'C').name, "English") + self.assertEqual( + convert_source(entry, 'en').name, "English") + self.assertEqual( + convert_source(entry, 'fr').name, "French") + self.assertEqual( + convert_source(entry, 'fr_CA').name, "French Canadian") + self.assertEqual( + convert_source(entry, 'fr_BE').name, "French") diff --git a/subiquity/server/server.py b/subiquity/server/server.py index e833367d..db326499 100644 --- a/subiquity/server/server.py +++ b/subiquity/server/server.py @@ -115,7 +115,13 @@ class MetaController: async def client_variant_POST(self, variant: str) -> None: if variant not in self.app.supported_variants: raise ValueError(f'unrecognized client variant {variant}') - self.app.base_model.set_source_variant(variant) + from subiquity.models.source import fake_entries + if variant in fake_entries: + if self.app.base_model.source.current.variant != variant: + self.app.base_model.source.current = fake_entries[variant] + self.app.controllers.Source.configured() + else: + self.app.base_model.set_source_variant(variant) async def ssh_info_GET(self) -> Optional[LiveSessionSSHInfo]: ips = [] @@ -169,6 +175,7 @@ INSTALL_MODEL_NAMES = ModelNames({ "mirror", "network", "proxy", + "source", }) POSTINSTALL_MODEL_NAMES = ModelNames({ @@ -213,6 +220,7 @@ class SubiquityServer(Application): "Kernel", "Keyboard", "Zdev", + "Source", "Network", "Proxy", "Mirror", diff --git a/subiquitycore/tests/util.py b/subiquitycore/tests/util.py index f69f6bc0..3deab2d8 100644 --- a/subiquitycore/tests/util.py +++ b/subiquitycore/tests/util.py @@ -14,7 +14,13 @@ # along with this program. If not, see . import asyncio +import random +import string def run_coro(coro): return asyncio.get_event_loop().run_until_complete(coro) + + +def random_string(): + return ''.join(random.choice(string.ascii_letters) for _ in range(8)) From 8c83ad05ccbec259d169e236b7b57377a470e736 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 19 Aug 2021 16:50:14 +1200 Subject: [PATCH 2/4] simple client side ui for source selection --- subiquity/client/client.py | 1 + subiquity/client/controllers/__init__.py | 2 + subiquity/client/controllers/source.py | 42 +++++++++++++++ subiquity/ui/views/source.py | 69 ++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 subiquity/client/controllers/source.py create mode 100644 subiquity/ui/views/source.py diff --git a/subiquity/client/client.py b/subiquity/client/client.py index ef3f8111..9a111052 100644 --- a/subiquity/client/client.py +++ b/subiquity/client/client.py @@ -101,6 +101,7 @@ class SubiquityClient(TuiApplication): "Welcome", "Refresh", "Keyboard", + "Source", "Zdev", "Network", "Proxy", diff --git a/subiquity/client/controllers/__init__.py b/subiquity/client/controllers/__init__.py index 66f0be3c..11abc3b2 100644 --- a/subiquity/client/controllers/__init__.py +++ b/subiquity/client/controllers/__init__.py @@ -24,6 +24,7 @@ from .proxy import ProxyController from .refresh import RefreshController from .serial import SerialController from .snaplist import SnapListController +from .source import SourceController from .ssh import SSHController from .welcome import WelcomeController from .zdev import ZdevController @@ -41,6 +42,7 @@ __all__ = [ 'RepeatedController', 'SerialController', 'SnapListController', + 'SourceController', 'SSHController', 'WelcomeController', 'ZdevController', diff --git a/subiquity/client/controllers/source.py b/subiquity/client/controllers/source.py new file mode 100644 index 00000000..c0ce37a7 --- /dev/null +++ b/subiquity/client/controllers/source.py @@ -0,0 +1,42 @@ +# 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 . + +import logging + +from subiquity.client.controller import SubiquityTuiController +from subiquity.ui.views.source import SourceView + +log = logging.getLogger('subiquity.client.controllers.source') + + +class SourceController(SubiquityTuiController): + + endpoint_name = 'source' + + async def make_ui(self): + sources = await self.endpoint.GET() + return SourceView(self, sources.sources, sources.current_id) + + def run_answers(self): + if 'source' in self.answers: + self.app.ui.body.form.source.value = self.answers['source'] + self.app.ui.body.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)) diff --git a/subiquity/ui/views/source.py b/subiquity/ui/views/source.py new file mode 100644 index 00000000..634c5d08 --- /dev/null +++ b/subiquity/ui/views/source.py @@ -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 . + +import logging +from urwid import connect_signal + +from subiquitycore.view import BaseView +from subiquitycore.ui.form import ( + Form, + RadioButtonField, +) + +log = logging.getLogger('subiquity.ui.views.source') + + +class SourceView(BaseView): + + title = _("Choose type of install") + + def __init__(self, controller, sources, current_id): + self.controller = controller + + group = [] + + ns = { + 'cancel_label': _("Back"), + } + initial = {} + + for default in True, False: + for source in sorted(sources, key=lambda s: s.id): + if source.default != default: + continue + ns[source.id] = RadioButtonField( + group, source.name, '\n' + source.description) + initial[source.id] = source.id == current_id + + SourceForm = type(Form)('SourceForm', (Form,), ns) + log.debug('%r %r', ns, current_id) + + self.form = SourceForm(initial=initial) + + connect_signal(self.form, 'submit', self.done) + connect_signal(self.form, 'cancel', self.cancel) + + excerpt = _("Choose the base for the installation.") + + super().__init__(self.form.as_screen(excerpt=excerpt)) + + def done(self, result): + log.debug("User input: {}".format(result.as_data())) + for k, v in result.as_data().items(): + if v: + self.controller.done(k) + + def cancel(self, result=None): + self.controller.cancel() From 6fd5eb6ee9422b6881a1e0e58561ef69fe649104 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 19 Aug 2021 16:50:45 +1200 Subject: [PATCH 3/4] do not need to POST to /meta/client_variant any more --- subiquity/client/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/subiquity/client/client.py b/subiquity/client/client.py index 9a111052..19610469 100644 --- a/subiquity/client/client.py +++ b/subiquity/client/client.py @@ -83,7 +83,7 @@ class SubiquityClient(TuiApplication): snapd_socket_path = '/run/snapd.socket' - variant = "server" + variant = None cmdline = ['snap', 'run', 'subiquity'] dryrun_cmdline_module = 'subiquity.cmd.tui' @@ -444,7 +444,8 @@ class SubiquityClient(TuiApplication): endpoint_names.append(c.endpoint_name) if endpoint_names: await self.client.meta.mark_configured.POST(endpoint_names) - await self.client.meta.client_variant.POST(self.variant) + if self.variant: + await self.client.meta.client_variant.POST(self.variant) self.controllers.index = index - 1 self.next_screen() From 5c66260369e90d8ad5f453f36b2a93901aefa661 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 19 Aug 2021 16:53:45 +1200 Subject: [PATCH 4/4] remove min_size from guided API, use source installed size instead --- subiquity/client/controllers/filesystem.py | 8 ++------ subiquity/common/apidef.py | 3 +-- subiquity/server/controllers/filesystem.py | 14 ++++++-------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/subiquity/client/controllers/filesystem.py b/subiquity/client/controllers/filesystem.py index b0ba3f88..33f494de 100644 --- a/subiquity/client/controllers/filesystem.py +++ b/subiquity/client/controllers/filesystem.py @@ -42,10 +42,6 @@ BIOS_GRUB_SIZE_BYTES = 1 * 1024 * 1024 # 1MiB PREP_GRUB_SIZE_BYTES = 8 * 1024 * 1024 # 8MiB UEFI_GRUB_SIZE_BYTES = 512 * 1024 * 1024 # 512MiB EFI partition -# Disks larger than this are considered sensible targets for guided -# installation. -MIN_SIZE_GUIDED = 6 * (1 << 30) - class FilesystemController(SubiquityTuiController, FilesystemManipulator): @@ -59,7 +55,7 @@ class FilesystemController(SubiquityTuiController, FilesystemManipulator): self.answers.setdefault('manual', []) async def make_ui(self): - status = await self.endpoint.guided.GET(MIN_SIZE_GUIDED) + status = await self.endpoint.guided.GET() if status.status == ProbeStatus.PROBING: self.app.aio_loop.create_task(self._wait_for_probing()) return SlowProbing(self) @@ -67,7 +63,7 @@ class FilesystemController(SubiquityTuiController, FilesystemManipulator): return self.make_guided_ui(status) async def _wait_for_probing(self): - status = await self.endpoint.guided.GET(MIN_SIZE_GUIDED, wait=True) + status = await self.endpoint.guided.GET(wait=True) if isinstance(self.ui.body, SlowProbing): self.ui.set_body(self.make_guided_ui(status)) diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index e1096ea2..f86c6911 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -213,8 +213,7 @@ class API: class storage: class guided: - def GET(min_size: int = None, wait: bool = False) \ - -> GuidedStorageResponse: + def GET(wait: bool = False) -> GuidedStorageResponse: pass def POST(choice: Optional[GuidedChoice]) \ diff --git a/subiquity/server/controllers/filesystem.py b/subiquity/server/controllers/filesystem.py index 304315d0..c0110c60 100644 --- a/subiquity/server/controllers/filesystem.py +++ b/subiquity/server/controllers/filesystem.py @@ -62,10 +62,6 @@ from subiquity.server.controller import ( log = logging.getLogger("subiquity.server.controller.filesystem") block_discover_log = logging.getLogger('block-discover') -# Disks larger than this are considered sensible targets for guided -# installation. -DEFAULT_MIN_SIZE_GUIDED = 6 * (1 << 30) - class FilesystemController(SubiquityController, FilesystemManipulator): @@ -217,13 +213,15 @@ class FilesystemController(SubiquityController, FilesystemManipulator): config, self.model._probe_data['blockdev'], is_probe_data=False) self.configured() - async def guided_GET(self, min_size: int = None, wait: bool = False) \ - -> GuidedStorageResponse: + async def guided_GET(self, wait: bool = False) -> GuidedStorageResponse: probe_resp = await self._probe_response(wait, GuidedStorageResponse) if probe_resp is not None: return probe_resp - if not min_size: - min_size = DEFAULT_MIN_SIZE_GUIDED + # This calculation is pretty much a hack and we should + # actually think about it at some point (like: maybe the + # source catalog should directly specify the minimum suitable + # size?) + min_size = 2*self.app.base_model.source.current.size + (1 << 30) disks = [] for raid in self.model._all(type='raid'): if not boot.can_be_boot_device(raid, with_reformatting=True):