Merge branch 'master' into mwhudson/snap-screen

This commit is contained in:
Michael Hudson-Doyle 2018-05-22 22:47:36 +12:00
commit 919b292800
34 changed files with 168 additions and 165 deletions

View File

@ -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

View File

@ -14,3 +14,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Console-Conf """
import subiquitycore.i18n

View File

@ -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 = []

View File

@ -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',

View File

@ -16,3 +16,5 @@
""" Subiquity """
__version__ = "0.0.5"
import subiquitycore.i18n

View File

@ -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):

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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'])

View File

@ -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:

View File

@ -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:

View File

@ -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("")),

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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()))

View File

@ -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)

View File

@ -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()))

View File

@ -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("")),

View File

@ -16,3 +16,5 @@
""" SubiquityCore """
__version__ = "0.0.5"
import subiquitycore.i18n

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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):

View File

@ -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("")),

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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',