diff --git a/bin/subiquity-tui b/bin/subiquity-tui index c34df3d5..7b7601da 100755 --- a/bin/subiquity-tui +++ b/bin/subiquity-tui @@ -20,7 +20,6 @@ import os import signal import sys -from subiquitycore.i18n import * from subiquitycore.log import setup_logger from subiquitycore import __version__ as VERSION from subiquitycore.core import ApplicationError diff --git a/console_conf/__init__.py b/console_conf/__init__.py index ef34c23e..49c8381d 100644 --- a/console_conf/__init__.py +++ b/console_conf/__init__.py @@ -14,3 +14,5 @@ # along with this program. If not, see . """ Console-Conf """ + +import subiquitycore.i18n diff --git a/console_conf/controllers/identity.py b/console_conf/controllers/identity.py index 713fe953..5bc3e0bb 100644 --- a/console_conf/controllers/identity.py +++ b/console_conf/controllers/identity.py @@ -55,7 +55,7 @@ def host_key_fingerprints(): log.debug("sshd -T failed %r", config['err']) return [] keyfiles = [] - for line in config.output.splitlines(): + for line in config.stdout.splitlines(): if line.startswith('hostkey '): keyfiles.append(line.split(None, 1)[1]) info = [] diff --git a/setup.py b/setup.py index ee6422f4..db6cb123 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,11 @@ from DistUtilsExtra.command import build_i18n import os import sys -import subiquitycore +with open(os.path.join(os.path.dirname(__file__), 'subiquitycore', '__init__.py')) as init: + lines = [line for line in init if 'i18n' not in line] + ns = {} + exec('\n'.join(lines), ns) + version = ns['__version__'] if sys.argv[-1] == 'clean': print("Cleaning up ...") @@ -37,7 +41,7 @@ if sys.argv[-1] == 'clean': sys.exit() setup(name='subiquity', - version=subiquitycore.__version__, + version=version, description="Ubuntu Server Installer", long_description=__doc__, author='Canonical Engineering', diff --git a/subiquity/__init__.py b/subiquity/__init__.py index 6e7ce7e1..51ec1fe1 100644 --- a/subiquity/__init__.py +++ b/subiquity/__init__.py @@ -16,3 +16,5 @@ """ Subiquity """ __version__ = "0.0.5" + +import subiquitycore.i18n diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py index d53994b7..f2056e2a 100644 --- a/subiquity/controllers/filesystem.py +++ b/subiquity/controllers/filesystem.py @@ -55,10 +55,6 @@ class FilesystemController(BaseController): self.model.probe() # probe before we complete def default(self): - title = _("Filesystem setup") - footer = (_("Choose guided or manual partitioning")) - self.ui.set_header(title) - self.ui.set_footer(footer) self.ui.set_body(GuidedFilesystemView(self)) if self.answers['guided']: self.guided() @@ -66,19 +62,11 @@ class FilesystemController(BaseController): self.manual() def manual(self): - title = _("Filesystem setup") - footer = (_("Select available disks to format and mount")) - self.ui.set_header(title) - self.ui.set_footer(footer) self.ui.set_body(FilesystemView(self.model, self)) if self.answers['guided']: self.finish() def guided(self): - title = _("Filesystem setup") - footer = (_("Choose the installation target")) - self.ui.set_header(title) - self.ui.set_footer(footer) v = GuidedDiskSelectionView(self.model, self) self.ui.set_body(v) if self.answers['guided']: @@ -94,14 +82,6 @@ class FilesystemController(BaseController): def cancel(self): self.signal.emit_signal('prev-screen') - def filesystem_error(self, error_fname): - title = _("Filesystem error") - footer = (_("Error while installing Ubuntu")) - error_msg = _("Failed to obtain write permissions to /tmp") - self.ui.set_header(title) - self.ui.set_footer(footer) - self.ui.set_body(ErrorView(self.signal, error_msg)) - def finish(self): # start curtin install in background self.signal.emit_signal('installprogress:filesystem-config-done') @@ -111,26 +91,17 @@ class FilesystemController(BaseController): # Filesystem/Disk partition ----------------------------------------------- def partition_disk(self, disk): log.debug("In disk partition view, using {} as the disk.".format(disk.label)) - title = (_("Partition, format, and mount {}").format(disk.label)) - footer = (_("Partition the disk, or format the entire device " - "without partitions")) - self.ui.set_header(title) - self.ui.set_footer(footer) dp_view = DiskPartitionView(self.model, self, disk) self.ui.set_body(dp_view) def add_disk_partition(self, disk): log.debug("Adding partition to {}".format(disk)) - footer = _("Select whole disk, or partition, to format and mount.") - self.ui.set_footer(footer) adp_view = PartitionView(self.model, self, disk) self.ui.set_body(adp_view) def edit_partition(self, disk, partition): log.debug("Editing partition {}".format(partition)) - footer = _("Edit partition details format and mount.") - self.ui.set_footer(footer) adp_view = PartitionView(self.model, self, disk, partition) self.ui.set_body(adp_view) @@ -295,23 +266,11 @@ class FilesystemController(BaseController): def format_entire(self, disk): log.debug("format_entire {}".format(disk.label)) - header = (_("Format and/or mount {}").format(disk.label)) - footer = _("Format or mount whole disk.") - self.ui.set_header(header) - self.ui.set_footer(footer) afv_view = FormatEntireView(self.model, self, disk, lambda : self.partition_disk(disk)) self.ui.set_body(afv_view) def format_mount_partition(self, partition): log.debug("format_mount_partition {}".format(partition)) - if partition.fs() is not None: - header = (_("Mount partition {} of {}").format(partition._number, partition.device.label)) - footer = _("Mount partition.") - else: - header = (_("Format and mount partition {} of {}").format(partition._number, partition.device.label)) - footer = _("Format and mount partition.") - self.ui.set_header(header) - self.ui.set_footer(footer) afv_view = FormatEntireView(self.model, self, partition, self.manual) self.ui.set_body(afv_view) @@ -380,8 +339,6 @@ class FilesystemController(BaseController): result = template.format(**dinfo) log.debug('calling DiskInfoView()') disk_info_view = DiskInfoView(self.model, self, disk, result) - footer = _('Select next or previous disks with n and p') - self.ui.set_footer(footer) self.ui.set_body(disk_info_view) def is_uefi(self): diff --git a/subiquity/controllers/identity.py b/subiquity/controllers/identity.py index 770cc282..981378ac 100644 --- a/subiquity/controllers/identity.py +++ b/subiquity/controllers/identity.py @@ -36,9 +36,6 @@ class IdentityController(BaseController): self.answers = self.all_answers.get('Identity', {}) def default(self): - title = _("Profile setup") - excerpt = _("Enter the username and password (or ssh identity) you will use to log in to the system.") - self.ui.set_header(title, excerpt) self.ui.set_body(IdentityView(self.model, self, self.opts)) if 'realname' in self.answers and \ 'username' in self.answers and \ @@ -70,6 +67,8 @@ class IdentityController(BaseController): def _bg_fetch_ssh_keys(self, user_spec, proc, ssh_import_id): stdout, stderr = proc.communicate() + stdout = stdout.decode('utf-8', errors='replace') + stderr = stderr.decode('utf-8', errors='replace') log.debug("ssh-import-id exited with code %s", proc.returncode) if proc != self._fetching_proc: log.debug("_fetch_ssh_keys cancelled") diff --git a/subiquity/controllers/installpath.py b/subiquity/controllers/installpath.py index 9474a321..00c88ebf 100644 --- a/subiquity/controllers/installpath.py +++ b/subiquity/controllers/installpath.py @@ -32,16 +32,6 @@ class InstallpathController(BaseController): self.answers = self.all_answers.get("Installpath", {}) def installpath(self): - title = "Ubuntu %s"%(lsb_release.get_distro_information()['RELEASE'],) - excerpt = _("Welcome to Ubuntu! The world's favourite platform " - "for clouds, clusters, and amazing internet things. " - "This is the installer for Ubuntu on servers and " - "internet devices.") - footer = _("Use UP, DOWN arrow keys, and ENTER, to " - "navigate options") - - self.ui.set_header(title, excerpt) - self.ui.set_footer(footer) self.ui.set_body(InstallpathView(self.model, self)) if 'path' in self.answers: path = self.answers['path'] @@ -75,9 +65,7 @@ class InstallpathController(BaseController): "individually, MAAS turns your bare metal into an elastic " "cloud-like resource. \n\nFor further details, see https://maas.io/." ) - self.ui.set_header(title, excerpt) - self.ui.set_footer("") - self.ui.set_body(MAASView(self.model, self)) + self.ui.set_body(MAASView(self.model, self, title, excerpt)) def install_maas_rack(self): # show cack questions, seed model @@ -89,9 +77,7 @@ class InstallpathController(BaseController): "region controller." ) - self.ui.set_header(title, excerpt) - self.ui.set_footer("") - self.ui.set_body(MAASView(self.model, self)) + self.ui.set_body(MAASView(self.model, self, title, excerpt)) def setup_maas(self, result): self.model.update(result) diff --git a/subiquity/controllers/installprogress.py b/subiquity/controllers/installprogress.py index a2273782..d2500a4f 100644 --- a/subiquity/controllers/installprogress.py +++ b/subiquity/controllers/installprogress.py @@ -398,14 +398,14 @@ class InstallProgressController(BaseController): def default(self): self.progress_view_showing = True - self.ui.set_body(self.progress_view) if self.install_state == InstallState.RUNNING: - self.ui.set_header(_("Installing system")) - self.ui.set_footer(_("Thank you for using Ubuntu!")) + self.progress_view.title = _("Installing system") + self.progress_view.footer = _("Thank you for using Ubuntu!") elif self.install_state == InstallState.DONE: - self.ui.set_header(_("Install complete!")) - self.ui.set_footer(_("Thank you for using Ubuntu!")) + self.progress_view.title = _("Install complete!") + self.progress_view.footer = _("Thank you for using Ubuntu!") elif self.install_state == InstallState.ERROR: - self.ui.set_header(_('An error occurred during installation')) - self.ui.set_footer(_('Please report this error in Launchpad')) + self.progress_view.title = _('An error occurred during installation') + self.progress_view.footer = _('Please report this error in Launchpad') + self.ui.set_body(self.progress_view) diff --git a/subiquity/controllers/keyboard.py b/subiquity/controllers/keyboard.py index 2d58df62..f307f374 100644 --- a/subiquity/controllers/keyboard.py +++ b/subiquity/controllers/keyboard.py @@ -45,14 +45,6 @@ class KeyboardController(BaseController): def default(self): if self.model.current_lang is None: self.model.load_language('C') - title = _("Keyboard configuration") - if self.opts.run_on_serial: - excerpt = _('Please select the layout of the keyboard directly attached to the system, if any.') - else: - excerpt = _('Please select your keyboard layout below, or select "Identify keyboard" to detect your layout automatically.') - footer = _("Use UP, DOWN and ENTER keys to select your keyboard.") - self.ui.set_header(title, excerpt) - self.ui.set_footer(footer) view = KeyboardView(self.model, self, self.opts) self.ui.set_body(view) if 'layout' in self.answers: diff --git a/subiquity/controllers/proxy.py b/subiquity/controllers/proxy.py index 4ea7d6c3..458d983e 100644 --- a/subiquity/controllers/proxy.py +++ b/subiquity/controllers/proxy.py @@ -31,9 +31,6 @@ class ProxyController(BaseController): self.answers = self.all_answers.get('Proxy', {}) def default(self): - title = _("Configure proxy") - excerpt = _("If this system requires a proxy to connect to the internet, enter its details here.") - self.ui.set_header(title, excerpt) self.ui.set_body(ProxyView(self.model, self)) if 'proxy' in self.answers: self.done(self.answers['proxy']) diff --git a/subiquity/controllers/welcome.py b/subiquity/controllers/welcome.py index 9da87176..fd032d79 100644 --- a/subiquity/controllers/welcome.py +++ b/subiquity/controllers/welcome.py @@ -30,11 +30,6 @@ class WelcomeController(BaseController): log.debug("Welcome: answers=%s", self.answers) def default(self): - title = "Willkommen! Bienvenue! Welcome! Добро пожаловать! Welkom!" - excerpt = _("Please choose your preferred language") - footer = _("Use UP, DOWN and ENTER keys to select your language.") - self.ui.set_header(title, excerpt) - self.ui.set_footer(footer) view = WelcomeView(self.model, self) self.ui.set_body(view) if 'lang' in self.answers: diff --git a/subiquity/ui/views/filesystem/disk_info.py b/subiquity/ui/views/filesystem/disk_info.py index 694b709f..90a9042b 100644 --- a/subiquity/ui/views/filesystem/disk_info.py +++ b/subiquity/ui/views/filesystem/disk_info.py @@ -26,11 +26,15 @@ log = logging.getLogger('subiquity.ui.filesystem.disk_info') class DiskInfoView(BaseView): + + footer = _('Select next or previous disks with n and p') + def __init__(self, model, controller, disk, hdinfo): log.debug('DiskInfoView: {}'.format(disk)) self.model = model self.controller = controller self.disk = disk + self.title = _("Information on {}").format(disk.label) hdinfo = hdinfo.split("\n") body = [] for h in hdinfo: diff --git a/subiquity/ui/views/filesystem/disk_partition.py b/subiquity/ui/views/filesystem/disk_partition.py index 522a4cb0..7837dfa9 100644 --- a/subiquity/ui/views/filesystem/disk_partition.py +++ b/subiquity/ui/views/filesystem/disk_partition.py @@ -28,10 +28,14 @@ log = logging.getLogger('subiquity.ui.filesystem.disk_partition') class DiskPartitionView(BaseView): + footer = (_("Partition the disk, or format the entire device " + "without partitions")) + def __init__(self, model, controller, disk): self.model = model self.controller = controller self.disk = disk + self.title = _("Partition, format, and mount {}").format(disk.label) self.body = Pile([ ('pack', Text("")), diff --git a/subiquity/ui/views/filesystem/filesystem.py b/subiquity/ui/views/filesystem/filesystem.py index a2c4a851..0b37c890 100644 --- a/subiquity/ui/views/filesystem/filesystem.py +++ b/subiquity/ui/views/filesystem/filesystem.py @@ -75,6 +75,9 @@ class FilesystemConfirmation(Stretchy): class FilesystemView(BaseView): + title = _("Filesystem setup") + footer = _("Select available disks to format and mount") + def __init__(self, model, controller): log.debug('FileSystemView init start()') self.model = model @@ -99,7 +102,7 @@ class FilesystemView(BaseView): #] self.lb = Padding.center_95(ListBox(body)) - self.footer = Pile([ + bottom = Pile([ Text(""), self._build_buttons(), Text(""), @@ -107,7 +110,7 @@ class FilesystemView(BaseView): self.frame = Pile([ ('pack', Text("")), self.lb, - ('pack', self.footer)]) + ('pack', bottom)]) if self.model.can_install(): self.frame.focus_position = 2 super().__init__(self.frame) diff --git a/subiquity/ui/views/filesystem/guided.py b/subiquity/ui/views/filesystem/guided.py index 9dd45742..b32520ce 100644 --- a/subiquity/ui/views/filesystem/guided.py +++ b/subiquity/ui/views/filesystem/guided.py @@ -43,6 +43,9 @@ review and modify the results.""") class GuidedFilesystemView(BaseView): + title = _("Filesystem setup") + footer = _("Choose guided or manual partitioning") + def __init__(self, controller): self.controller = controller guided = ok_btn(_("Use An Entire Disk"), on_press=self.guided) @@ -68,6 +71,9 @@ class GuidedFilesystemView(BaseView): class GuidedDiskSelectionView(BaseView): + title = _("Filesystem setup") + footer = (_("Choose the installation target")) + def __init__(self, model, controller): self.model = model self.controller = controller diff --git a/subiquity/ui/views/filesystem/partition.py b/subiquity/ui/views/filesystem/partition.py index 748b00ac..56292d50 100644 --- a/subiquity/ui/views/filesystem/partition.py +++ b/subiquity/ui/views/filesystem/partition.py @@ -35,6 +35,7 @@ from subiquitycore.view import BaseView from subiquity.models.filesystem import ( align_up, + Disk, FilesystemModel, HUMAN_UNITS, dehumanize_size, @@ -185,11 +186,13 @@ class PartitionView(PartitionFormatView): self.controller = controller self.disk = disk self.partition = partition + self.title = _("Partition, format, and mount {}").format(disk.label) max_size = disk.free initial = {} if partition is None: label = _("Create") + self.footer = _("Enter partition details, format and mount.") else: max_size += partition.size initial['size'] = humanize_size(partition.size) @@ -197,6 +200,7 @@ class PartitionView(PartitionFormatView): label = None initial['mount'] = None else: + self.footer = _("Edit partition details, format and mount.") label = _("Save") super().__init__(max_size, partition, initial, lambda : self.controller.partition_disk(disk), focus_buttons=label is None) if label is not None: @@ -247,10 +251,18 @@ class PartitionView(PartitionFormatView): class FormatEntireView(PartitionFormatView): + def __init__(self, model, controller, volume, back): self.model = model self.controller = controller self.volume = volume + if isinstance(volume, Disk): + self.title = _("Format and/or mount {}").format(disk.label) + self.footer = _("Format or mount whole disk.") + else: + self.title = _("Partition, format, and mount {}").format(volume.device.label) + self.footer = _("Edit partition details, format and mount.") + super().__init__(None, volume, {}, back) def done(self, form): diff --git a/subiquity/ui/views/identity.py b/subiquity/ui/views/identity.py index d5d74b83..f64b056f 100644 --- a/subiquity/ui/views/identity.py +++ b/subiquity/ui/views/identity.py @@ -29,6 +29,9 @@ from subiquitycore.ui.buttons import ( ok_btn, other_btn, ) +from subiquitycore.ui.container import ( + ListBox, + ) from subiquitycore.ui.interactive import ( PasswordEditor, StringEditor, @@ -257,6 +260,9 @@ class FetchingSSHKeysFailed(Stretchy): self.parent.remove_overlay() class IdentityView(BaseView): + title = _("Profile setup") + excerpt = _("Enter the username and password (or ssh identity) you will use to log in to the system.") + def __init__(self, model, controller, opts): self.model = model self.controller = controller @@ -270,12 +276,14 @@ class IdentityView(BaseView): connect_signal(self.form.ssh_import_id.widget, 'select', self._select_ssh_import_id) self.form.import_username.enabled = False + + self.form_rows = ListBox(self.form.as_rows()) super().__init__( screen( - self.form.as_rows(), - button_pile([self.form.done_btn]), + self.form_rows, + [self.form.done_btn], + excerpt=_(self.excerpt), focus_buttons=False)) - self.form_rows = self._w[1] def _check_password(self, sender, new_text): password = self.form.password.value diff --git a/subiquity/ui/views/installpath.py b/subiquity/ui/views/installpath.py index f41e282b..4dbd4367 100644 --- a/subiquity/ui/views/installpath.py +++ b/subiquity/ui/views/installpath.py @@ -21,11 +21,13 @@ Provides high level options for Ubuntu install import binascii import logging import re -from urwid import connect_signal, Text + +import lsb_release + +from urwid import connect_signal from subiquitycore.ui.buttons import back_btn, forward_btn -from subiquitycore.ui.utils import Padding, button_pile -from subiquitycore.ui.container import ListBox, Pile +from subiquitycore.ui.utils import screen from subiquitycore.view import BaseView from subiquitycore.ui.interactive import ( PasswordEditor, @@ -43,19 +45,24 @@ log = logging.getLogger('subiquity.installpath') class InstallpathView(BaseView): + title = "Ubuntu {}" + + excerpt = _("Welcome to Ubuntu! The world's favourite platform " + "for clouds, clusters, and amazing internet things. " + "This is the installer for Ubuntu on servers and " + "internet devices.") + footer = _("Use UP, DOWN arrow keys, and ENTER, to " + "navigate options") + def __init__(self, model, controller): + self.title = self.title.format(lsb_release.get_distro_information()['RELEASE']) self.model = model self.controller = controller self.items = [] back = back_btn(_("Back"), on_press=self.cancel) - self.body = [ - ('pack', Text("")), - Padding.center_79(self._build_choices()), - ('pack', Text("")), - ('pack', button_pile([back])), - ('pack', Text("")), - ] - super().__init__(Pile(self.body)) + super().__init__(screen( + self._build_choices(), [back], + focus_buttons=False, excerpt=_(self.excerpt))) def _build_choices(self): choices = [] @@ -64,7 +71,7 @@ class InstallpathView(BaseView): choices.append( forward_btn( label=label, on_press=self.confirm, user_arg=path)) - return ListBox(choices) + return choices def confirm(self, sender, path): self.controller.choose_path(path) @@ -149,11 +156,12 @@ class RackForm(Form): class MAASView(BaseView): - def __init__(self, model, controller): + def __init__(self, model, controller, title, excerpt): self.model = model self.controller = controller self.signal = controller.signal self.items = [] + self.title = title if self.model.path == "maas_region": self.form = RegionForm() @@ -165,7 +173,7 @@ class MAASView(BaseView): connect_signal(self.form, 'submit', self.done) connect_signal(self.form, 'cancel', self.cancel) - super().__init__(self.form.as_screen(focus_buttons=False)) + super().__init__(self.form.as_screen(focus_buttons=False, excerpt=excerpt)) def done(self, result): log.debug("User input: {}".format(result.as_data())) diff --git a/subiquity/ui/views/keyboard.py b/subiquity/ui/views/keyboard.py index 41a06192..01da3bf0 100644 --- a/subiquity/ui/views/keyboard.py +++ b/subiquity/ui/views/keyboard.py @@ -356,6 +356,9 @@ class KeyboardForm(Form): class KeyboardView(BaseView): + title = _("Keyboard configuration") + footer = _("Use UP, DOWN and ENTER keys to select your keyboard.") + def __init__(self, model, controller, opts): self.model = model self.controller = controller @@ -378,6 +381,11 @@ class KeyboardView(BaseView): # Don't crash on pre-existing invalid config. pass + if self.opts.run_on_serial: + excerpt = _('Please select the layout of the keyboard directly attached to the system, if any.') + else: + excerpt = _('Please select your keyboard layout below, or select "Identify keyboard" to detect your layout automatically.') + lb_contents = self.form.as_rows() if not self.opts.run_on_serial: lb_contents.extend([ @@ -385,7 +393,7 @@ class KeyboardView(BaseView): button_pile([ other_btn(label=_("Identify keyboard"), on_press=self.detect)]), ]) - super().__init__(screen(lb_contents, self.form.buttons)) + super().__init__(screen(lb_contents, self.form.buttons, excerpt=excerpt)) def detect(self, sender): detector = Detector(self) diff --git a/subiquity/ui/views/proxy.py b/subiquity/ui/views/proxy.py index 18c6fa85..dbbaec3d 100644 --- a/subiquity/ui/views/proxy.py +++ b/subiquity/ui/views/proxy.py @@ -45,6 +45,9 @@ class ProxyForm(Form): class ProxyView(BaseView): + title = _("Configure proxy") + excerpt = _("If this system requires a proxy to connect to the internet, enter its details here.") + def __init__(self, model, controller): self.model = model self.controller = controller @@ -54,7 +57,7 @@ class ProxyView(BaseView): connect_signal(self.form, 'submit', self.done) connect_signal(self.form, 'cancel', self.cancel) - super().__init__(self.form.as_screen()) + super().__init__(self.form.as_screen(excerpt=_(self.excerpt))) def done(self, result): log.debug("User input: {}".format(result.as_data())) diff --git a/subiquity/ui/views/welcome.py b/subiquity/ui/views/welcome.py index 7c4713ff..cded8da2 100644 --- a/subiquity/ui/views/welcome.py +++ b/subiquity/ui/views/welcome.py @@ -30,11 +30,15 @@ log = logging.getLogger("subiquity.views.welcome") class WelcomeView(BaseView): + title = "Willkommen! Bienvenue! Welcome! Добро пожаловать! Welkom!" + footer = _("Use UP, DOWN and ENTER keys to select your language.") def __init__(self, model, controller): self.model = model self.controller = controller super().__init__(Pile([ + ('pack', Text("")), + ('pack', Padding.center_79(Text(_("Please choose your preferred language")))), ('pack', Text("")), Padding.center_50(self._build_model_inputs()), ('pack', Text("")), diff --git a/subiquitycore/__init__.py b/subiquitycore/__init__.py index fd227128..04f0fe56 100644 --- a/subiquitycore/__init__.py +++ b/subiquitycore/__init__.py @@ -16,3 +16,5 @@ """ SubiquityCore """ __version__ = "0.0.5" + +import subiquitycore.i18n diff --git a/subiquitycore/controllers/network.py b/subiquitycore/controllers/network.py index 550d22f2..2e90ad4c 100644 --- a/subiquitycore/controllers/network.py +++ b/subiquitycore/controllers/network.py @@ -225,13 +225,6 @@ class NetworkController(BaseController, TaskWatcher): self.signal.emit_signal('prev-screen') def default(self): - title = _("Network connections") - excerpt = _("Configure at least one interface this server can use to talk to " - "other machines, and which preferably provides sufficient access for " - "updates.") - footer = _("Select an interface to configure it or select Done to continue") - self.ui.set_header(title, excerpt) - self.ui.set_footer(footer) self.ui.set_body(NetworkView(self.model, self)) if self.answers.get('accept-default', False): self.network_finish(self.model.render()) @@ -315,35 +308,27 @@ class NetworkController(BaseController, TaskWatcher): self.signal.emit_signal('next-screen') def set_default_v4_route(self): - self.ui.set_header("Default route") + #self.ui.set_header("Default route") self.ui.set_body(NetworkSetDefaultRouteView(self.model, socket.AF_INET, self)) def set_default_v6_route(self): - self.ui.set_header("Default route") + #self.ui.set_header("Default route") self.ui.set_body(NetworkSetDefaultRouteView(self.model, socket.AF_INET6, self)) def bond_interfaces(self): - self.ui.set_header("Bond interfaces") + #self.ui.set_header("Bond interfaces") self.ui.set_body(NetworkBondInterfacesView(self.model, self)) def network_configure_interface(self, iface): - self.ui.set_header(_("Network interface {}").format(iface)) - self.ui.set_footer("") self.ui.set_body(NetworkConfigureInterfaceView(self.model, self, iface)) def network_configure_ipv4_interface(self, iface): - self.ui.set_header(_("Network interface {} manual IPv4 configuration").format(iface)) - self.ui.set_footer("") self.ui.set_body(NetworkConfigureIPv4InterfaceView(self.model, self, iface)) def network_configure_wlan_interface(self, iface): - self.ui.set_header(_("Network interface {} WIFI configuration").format(iface)) - self.ui.set_footer("") self.ui.set_body(NetworkConfigureWLANView(self.model, self, iface)) def network_configure_ipv6_interface(self, iface): - self.ui.set_header(_("Network interface {} manual IPv6 configuration").format(iface)) - self.ui.set_footer("") self.ui.set_body(NetworkConfigureIPv6InterfaceView(self.model, self, iface)) def install_network_driver(self): diff --git a/subiquitycore/ui/anchors.py b/subiquitycore/ui/anchors.py index ef7135b9..04f8d293 100644 --- a/subiquitycore/ui/anchors.py +++ b/subiquitycore/ui/anchors.py @@ -27,19 +27,14 @@ class Header(WidgetWrap): :returns: Header() """ - def __init__(self, title=None, excerpt=None): - widgets = [Text("")] - if title is not None: - widgets.append( - Padding.center_79(Text(title))) - widgets.append(Text("")) - widgets = [Color.frame_header(Pile(widgets))] - if excerpt is not None: - widgets.extend([ - Text(""), - Padding.center_79(Text(excerpt)), - ]) - super().__init__(Pile(widgets)) + def __init__(self, title): + super().__init__( + Color.frame_header( + Pile([ + Text(""), + Padding.center_79(Text(title)), + Text(""), + ]))) class StepsProgressBar(ProgressBar): diff --git a/subiquitycore/ui/form.py b/subiquitycore/ui/form.py index 6f1a048c..5bab9d13 100644 --- a/subiquitycore/ui/form.py +++ b/subiquitycore/ui/form.py @@ -28,7 +28,6 @@ from urwid import ( WidgetWrap, ) -from subiquitycore.i18n import * from subiquitycore.ui.buttons import cancel_btn, done_btn from subiquitycore.ui.container import Columns, Pile from subiquitycore.ui.interactive import ( @@ -362,8 +361,10 @@ class Form(object, metaclass=MetaForm): del rows[-1:] return rows - def as_screen(self, focus_buttons=True): - return screen(self.as_rows(), self.buttons, focus_buttons=focus_buttons) + def as_screen(self, focus_buttons=True, excerpt=None): + return screen( + self.as_rows(), self.buttons, + focus_buttons=focus_buttons, excerpt=excerpt) def validated(self): in_error = False diff --git a/subiquitycore/ui/frame.py b/subiquitycore/ui/frame.py index 2afb06ef..39255751 100644 --- a/subiquitycore/ui/frame.py +++ b/subiquitycore/ui/frame.py @@ -27,7 +27,7 @@ log = logging.getLogger('subiquitycore.ui.frame') class SubiquityUI(WidgetWrap): def __init__(self): - self.header = Header() + self.header = Header("") self.body = Body() self.footer = Footer("", 0, 1) self.frame = Frame(self.body, header=self.header, footer=self.footer) @@ -38,11 +38,13 @@ class SubiquityUI(WidgetWrap): def keypress(self, size, key): return super().keypress(size, key) - def set_header(self, title=None, excerpt=None): - self.frame.header = Header(title, excerpt) + def set_header(self, title=None): + self.frame.header = Header(title) def set_footer(self, message): self.frame.footer = Footer(message, self.progress_current, self.progress_completion) def set_body(self, widget): + self.set_header(_(widget.title)) self.frame.body = widget + self.set_footer(_(widget.footer)) diff --git a/subiquitycore/ui/utils.py b/subiquitycore/ui/utils.py index 4d3fe802..ffc7cb6a 100644 --- a/subiquitycore/ui/utils.py +++ b/subiquitycore/ui/utils.py @@ -208,6 +208,8 @@ def screen(rows, buttons, focus_buttons=True, excerpt=None): The commonest screen layout in subiquity is: + [ 1 line padding (optional) ] + excerpt (optional) [ 1 line padding ] Listbox() [ 1 line padding ] @@ -218,21 +220,23 @@ def screen(rows, buttons, focus_buttons=True, excerpt=None): """ if isinstance(rows, list): rows = ListBox(rows) - body = [] + if isinstance(buttons, list): + buttons = button_pile(buttons) + excerpt_rows = [] if excerpt is not None: - body = [ + excerpt_rows = [ ('pack', Text("")), - ('pack', Padding.center_79(Text(excerpt))), + ('pack', Text(excerpt)), ] - body.extend([ + body = [ ('pack', Text("")), - Padding.center_79(rows), + rows, ('pack', Text("")), ('pack', buttons), ('pack', Text("")), - ]) - screen = Pile(body) + ] + pile = Pile(excerpt_rows + body) if focus_buttons: - screen.focus_position = 3 + 2*bool(excerpt) - return screen + pile.focus_position = len(excerpt_rows) + 3 + return Padding.center_79(pile) diff --git a/subiquitycore/ui/views/network.py b/subiquitycore/ui/views/network.py index 707debc1..0bfcb083 100644 --- a/subiquitycore/ui/views/network.py +++ b/subiquitycore/ui/views/network.py @@ -107,6 +107,12 @@ def _build_gateway_ip_info_for_version(dev, version): class NetworkView(BaseView): + title = _("Network connections") + excerpt = _("Configure at least one interface this server can use to talk to " + "other machines, and which preferably provides sufficient access for " + "updates.") + footer = _("Select an interface to configure it or select Done to continue") + def __init__(self, model, controller): self.model = model self.controller = controller @@ -117,17 +123,19 @@ class NetworkView(BaseView): Padding.center_79(self.additional_options), Padding.line_break(""), ]) - self.footer = Pile([ + self.bottom = Pile([ Text(""), self._build_buttons(), Text(""), ]) self.error_showing = False self.frame = Pile([ + ('pack', Text("")), + ('pack', Padding.center_79(Text(_(self.excerpt)))), ('pack', Text("")), Padding.center_90(self.listbox), - ('pack', self.footer)]) - self.frame.focus_position = 2 + ('pack', self.bottom)]) + self.frame.focus_position = 4 super().__init__(self.frame) def _build_buttons(self): @@ -251,9 +259,9 @@ class NetworkView(BaseView): def show_network_error(self, action, info=None): self.error_showing = True - self.footer.contents[0:0] = [ - (Text(""), self.footer.options()), - (Color.info_error(self.error), self.footer.options()), + self.bottom.contents[0:0] = [ + (Text(""), self.bottom.options()), + (Color.info_error(self.error), self.bottom.options()), ] if action == 'stop-networkd': exc = info[0] @@ -274,7 +282,7 @@ class NetworkView(BaseView): def done(self, result): if self.error_showing: - self.footer.contents[0:2] = [] + self.bottom.contents[0:2] = [] self.controller.network_finish(self.model.render()) def cancel(self, button=None): diff --git a/subiquitycore/ui/views/network_configure_interface.py b/subiquitycore/ui/views/network_configure_interface.py index 84faf10c..565e0e39 100644 --- a/subiquitycore/ui/views/network_configure_interface.py +++ b/subiquitycore/ui/views/network_configure_interface.py @@ -28,10 +28,12 @@ log = logging.getLogger('subiquitycore.network.network_configure_interface') choice_btn = _stylized_button("", "", "menu") class NetworkConfigureInterfaceView(BaseView): + def __init__(self, model, controller, name): self.model = model self.controller = controller self.dev = self.model.get_netdev_by_name(name) + self.title = _("Network interface {}").format(name) self._build_widgets() super().__init__(Pile([ ('pack', Text("")), diff --git a/subiquitycore/ui/views/network_configure_manual_interface.py b/subiquitycore/ui/views/network_configure_manual_interface.py index bf401539..f1d159f4 100644 --- a/subiquitycore/ui/views/network_configure_manual_interface.py +++ b/subiquitycore/ui/views/network_configure_manual_interface.py @@ -114,6 +114,7 @@ class BaseNetworkConfigureManualView(BaseView): self.model = model self.controller = controller self.dev = self.model.get_netdev_by_name(name) + self.title = _("Network interface {} manual IPv{} configuration").format(name, self.ip_version) self.is_gateway = False self.form = NetworkConfigForm(self.ip_version) connect_signal(self.form, 'submit', self.done) diff --git a/subiquitycore/ui/views/network_configure_wlan_interface.py b/subiquitycore/ui/views/network_configure_wlan_interface.py index 5a178611..1e95e1ef 100644 --- a/subiquitycore/ui/views/network_configure_wlan_interface.py +++ b/subiquitycore/ui/views/network_configure_wlan_interface.py @@ -54,6 +54,7 @@ class NetworkConfigureWLANView(BaseView): self.model = model self.controller = controller self.dev = self.model.get_netdev_by_name(name) + self.title = _("Network interface {} WIFI configuration").format(name) self.form = WLANForm() diff --git a/subiquitycore/utils.py b/subiquitycore/utils.py index 054c5580..d8c7e8d1 100644 --- a/subiquitycore/utils.py +++ b/subiquitycore/utils.py @@ -110,9 +110,16 @@ def run_command(cmd, *, input=None, stdout=subprocess.PIPE, stderr=subprocess.PI """ if input is None: kw['stdin'] = subprocess.DEVNULL + else: + input = input.encode(encoding) log.debug("run_command called: %s", cmd) try: - cp = subprocess.run(cmd, input=input, stdout=stdout, stderr=stderr, encoding=encoding, env=_clean_env(env), **kw) + cp = subprocess.run(cmd, input=input, stdout=stdout, stderr=stderr, env=_clean_env(env), **kw) + if encoding: + if isinstance(cp.stdout, bytes): + cp.stdout = cp.stdout.decode(encoding) + if isinstance(cp.stderr, bytes): + cp.stderr = cp.stderr.decode(encoding) except subprocess.CalledProcessError as e: log.debug("run_command %s", str(e)) raise @@ -127,7 +134,7 @@ def start_command(cmd, *, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stde We never ever want a subprocess to inherit our file descriptors! """ log.debug('start_command called: %s', cmd) - return subprocess.Popen(cmd, stdin=stdin, stdout=stdout, stderr=stderr, encoding=encoding, env=_clean_env(env), **kw) + return subprocess.Popen(cmd, stdin=stdin, stdout=stdout, stderr=stderr, env=_clean_env(env), **kw) # FIXME: replace with passlib and update package deps diff --git a/subiquitycore/view.py b/subiquitycore/view.py index 49b0eb33..0dbb62f3 100644 --- a/subiquitycore/view.py +++ b/subiquitycore/view.py @@ -25,6 +25,8 @@ from urwid import Columns, Overlay, Pile, Text, WidgetWrap class BaseView(WidgetWrap): + footer = "" + def show_overlay(self, overlay_widget, **kw): args = dict( align='center',