Merge branch 'master' into still-better-i18n

This commit is contained in:
Michael Hudson-Doyle 2020-05-21 11:15:32 +12:00
commit 7bcfcba99b
16 changed files with 97 additions and 54 deletions

View File

@ -17,6 +17,9 @@ if [ -e /run/snapd-recovery-chooser-triggered ]; then
# when recovery chooser is invoked it takes over the terminal # when recovery chooser is invoked it takes over the terminal
exec /usr/lib/snapd/snap-recovery-chooser exec /usr/lib/snapd/snap-recovery-chooser
fi fi
if [ -e /var/lib/console-conf/complete ]; then
exit 0
fi
fi fi
if grep -q 'snapd_recovery_mode=install' /proc/cmdline ; then if grep -q 'snapd_recovery_mode=install' /proc/cmdline ; then

View File

@ -10,7 +10,8 @@ After=core18.start-snapd.service core.start-snapd.service
After=snapd.recovery-chooser-trigger.service After=snapd.recovery-chooser-trigger.service
IgnoreOnIsolate=yes IgnoreOnIsolate=yes
ConditionPathExists=/dev/tty0 ConditionPathExists=/dev/tty0
ConditionPathExists=!/var/lib/console-conf/complete ConditionPathExists=|!/var/lib/console-conf/complete
ConditionPathExists=|/run/snapd-recovery-chooser-triggered
StartLimitInterval=0 StartLimitInterval=0
[Service] [Service]

View File

@ -161,6 +161,7 @@ class FilesystemController(SubiquityController):
"block probing failed restricted=%s", restricted) "block probing failed restricted=%s", restricted)
report = self.app.make_apport_report( report = self.app.make_apport_report(
kind, "block probing", interrupt=False) kind, "block probing", interrupt=False)
if report is not None:
self._crash_reports[restricted] = report self._crash_reports[restricted] = report
continue continue
break break

View File

@ -153,6 +153,7 @@ class InstallProgressController(SubiquityController):
self.progress_view.set_status(('info_error', self.progress_view.set_status(('info_error',
_("An error has occurred"))) _("An error has occurred")))
self.start_ui() self.start_ui()
if crash_report is not None:
self.progress_view.show_error(crash_report) self.progress_view.show_error(crash_report)
def logged_command(self, cmd): def logged_command(self, cmd):

View File

@ -35,7 +35,7 @@ class KeyboardController(SubiquityController):
'properties': { 'properties': {
'layout': {'type': 'string'}, 'layout': {'type': 'string'},
'variant': {'type': 'string'}, 'variant': {'type': 'string'},
'toggle': {'type': 'string'}, 'toggle': {'type': ['string', 'null']},
}, },
'required': ['layout'], 'required': ['layout'],
'additionalProperties': False, 'additionalProperties': False,

View File

@ -274,6 +274,7 @@ class Subiquity(Application):
report = self.make_apport_report( report = self.make_apport_report(
ErrorReportKind.UI, "Installer UI", interrupt=False, ErrorReportKind.UI, "Installer UI", interrupt=False,
wait=True) wait=True)
if report is not None:
print("report saved to {path}".format(report.path)) print("report saved to {path}".format(report.path))
except Exception: except Exception:
print("report generation failed") print("report generation failed")
@ -457,6 +458,9 @@ class Subiquity(Application):
self._apport_data.append((key, value)) self._apport_data.append((key, value))
def make_apport_report(self, kind, thing, *, interrupt, wait=False, **kw): def make_apport_report(self, kind, thing, *, interrupt, wait=False, **kw):
if not self.opts.dry_run and not os.path.exists('/cdrom/.disk/info'):
return None
log.debug("generating crash report") log.debug("generating crash report")
try: try:
@ -507,7 +511,7 @@ class Subiquity(Application):
self.add_global_overlay(ErrorReportStretchy(self, report)) self.add_global_overlay(ErrorReportStretchy(self, report))
def make_autoinstall(self): def make_autoinstall(self):
config = {} config = {'version': 1}
for controller in self.controllers.instances: for controller in self.controllers.instances:
controller_conf = controller.make_autoinstall() controller_conf = controller.make_autoinstall()
if controller_conf: if controller_conf:

View File

@ -24,6 +24,7 @@ import math
import os import os
import pathlib import pathlib
import platform import platform
import tempfile
from curtin import storage_config from curtin import storage_config
from curtin.util import human2bytes from curtin.util import human2bytes
@ -158,7 +159,7 @@ raidlevels = [
def _raidlevels_by_value(): def _raidlevels_by_value():
r = {l.value: l for l in raidlevels} r = {level.value: level for level in raidlevels}
for n in 0, 1, 5, 6, 10: for n in 0, 1, 5, 6, 10:
r[str(n)] = r[n] = r["raid"+str(n)] r[str(n)] = r[n] = r["raid"+str(n)]
r["stripe"] = r["raid0"] r["stripe"] = r["raid0"]
@ -391,7 +392,7 @@ def asdict(inst):
continue continue
m = getattr(inst, 'serialize_' + field.name, None) m = getattr(inst, 'serialize_' + field.name, None)
if m: if m:
r[field.name] = m() r.update(m())
else: else:
v = getattr(inst, field.name) v = getattr(inst, field.name)
if v is not None: if v is not None:
@ -968,7 +969,7 @@ class Partition(_Formattable):
return self._fs._available() return self._fs._available()
def serialize_number(self): def serialize_number(self):
return self._number return {'number': self._number}
@property @property
def _number(self): def _number(self):
@ -1021,7 +1022,7 @@ class Raid(_Device):
# Surprisingly, the order of devices passed to mdadm --create # Surprisingly, the order of devices passed to mdadm --create
# matters (see get_raid_size) so we sort devices here the same # matters (see get_raid_size) so we sort devices here the same
# way get_raid_size does. # way get_raid_size does.
return [d.id for d in raid_device_sort(self.devices)] return {'devices': [d.id for d in raid_device_sort(self.devices)]}
spare_devices = attributes.reflist( spare_devices = attributes.reflist(
backlink="_constructed_device", default=attr.Factory(set)) backlink="_constructed_device", default=attr.Factory(set))
@ -1161,7 +1162,7 @@ class LVM_LogicalVolume(_Formattable):
preserve = attr.ib(default=False) preserve = attr.ib(default=False)
def serialize_size(self): def serialize_size(self):
return "{}B".format(self.size) return {'size': "{}B".format(self.size)}
def available(self): def available(self):
if self._constructed_device is not None: if self._constructed_device is not None:
@ -1209,6 +1210,16 @@ class DM_Crypt:
volume = attributes.ref(backlink="_constructed_device") # _Formattable volume = attributes.ref(backlink="_constructed_device") # _Formattable
key = attr.ib(metadata={'redact': True}) key = attr.ib(metadata={'redact': True})
def serialize_key(self):
if self.key:
f = tempfile.NamedTemporaryFile(
prefix='luks-key-', mode='w', delete=False)
f.write(self.key)
f.close()
return {'keyfile': f.name}
else:
return {}
dm_name = attr.ib(default=None) dm_name = attr.ib(default=None)
preserve = attr.ib(default=False) preserve = attr.ib(default=False)

View File

@ -135,7 +135,7 @@ class KeyboardSetting:
# Non-latin keyboard layouts that are handled in a uniform way # Non-latin keyboard layouts that are handled in a uniform way
standard_non_latin_layouts = set( standard_non_latin_layouts = set(
('af', 'am', 'ara', 'ben', 'bd', 'bg', 'bt', 'by', 'et', 'ge', ('af', 'am', 'ara', 'ben', 'bd', 'bg', 'bt', 'by', 'et', 'ge',
'gh', 'gr', 'guj', 'guru', 'il', ''in'', 'iq', 'ir', 'iku', 'kan', 'gh', 'gr', 'guj', 'guru', 'il', 'in', 'iq', 'ir', 'iku', 'kan',
'kh', 'kz', 'la', 'lao', 'lk', 'kg', 'ma', 'mk', 'mm', 'mn', 'mv', 'kh', 'kz', 'la', 'lao', 'lk', 'kg', 'ma', 'mk', 'mm', 'mn', 'mv',
'mal', 'np', 'ori', 'pk', 'ru', 'scc', 'sy', 'syr', 'tel', 'th', 'mal', 'np', 'ori', 'pk', 'ru', 'scc', 'sy', 'syr', 'tel', 'th',
'tj', 'tam', 'tib', 'ua', 'ug', 'uz') 'tj', 'tam', 'tib', 'ua', 'ug', 'uz')

View File

@ -149,9 +149,10 @@ class SubiquityModel:
'mode': 'off', 'mode': 'off',
}, },
'locale': self.locale.selected_language + '.UTF-8', 'locale': self.locale.selected_language + '.UTF-8',
'preserve_hostname': True,
'resize_rootfs': False, 'resize_rootfs': False,
} }
if self.identity.hostname is not None:
config['preserve_hostname'] = True
user = self.identity.user user = self.identity.user
if user: if user:
users_and_groups_path = ( users_and_groups_path = (

View File

@ -86,7 +86,7 @@ class GuidedChoiceForm(SubForm):
lvm_options = SubFormField(LVMOptionsForm, "", help=NO_HELP) lvm_options = SubFormField(LVMOptionsForm, "", help=NO_HELP)
def __init__(self, parent): def __init__(self, parent):
super().__init__(parent) super().__init__(parent, initial={'use_lvm': True})
options = [] options = []
tables = [] tables = []
initial = -1 initial = -1

View File

@ -29,7 +29,6 @@ from subiquitycore.file_util import write_file
from subiquitycore.models.network import ( from subiquitycore.models.network import (
BondParameters, BondParameters,
NetDevAction, NetDevAction,
sanitize_config,
) )
from subiquitycore import netplan from subiquitycore import netplan
from subiquitycore.ui.views.network import ( from subiquitycore.ui.views.network import (
@ -342,7 +341,9 @@ class NetworkController(BaseController):
config = self.model.render_config() config = self.model.render_config()
log.debug("network config: \n%s", log.debug("network config: \n%s",
yaml.dump(sanitize_config(config), default_flow_style=False)) yaml.dump(
netplan.sanitize_config(config),
default_flow_style=False))
for p in netplan.configs_in_root(self.root, masked=True): for p in netplan.configs_in_root(self.root, masked=True):
if p == self.netplan_path: if p == self.netplan_path:

View File

@ -13,7 +13,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import copy
import enum import enum
import ipaddress import ipaddress
import logging import logging
@ -49,27 +48,6 @@ class NetDevAction(enum.Enum):
return pgettext(type(self).__name__, self.value) return pgettext(type(self).__name__, self.value)
def _sanitize_inteface_config(iface_config):
for ap, ap_config in iface_config.get('access-points', {}).items():
if 'password' in ap_config:
ap_config['password'] = '<REDACTED>'
def sanitize_interface_config(iface_config):
iface_config = copy.deepcopy(iface_config)
_sanitize_inteface_config(iface_config)
return iface_config
def sanitize_config(config):
"""Return a copy of config with passwords redacted."""
config = copy.deepcopy(config)
interfaces = config.get('network', {}).get('wifis', {}).items()
for iface, iface_config in interfaces:
_sanitize_inteface_config(iface_config)
return config
class BondParameters: class BondParameters:
# Just a place to hang various data about how bonds can be # Just a place to hang various data about how bonds can be
# configured. # configured.
@ -176,6 +154,21 @@ class NetworkDev(object):
def supports_action(self, action): def supports_action(self, action):
return getattr(self, "_supports_" + action.name) return getattr(self, "_supports_" + action.name)
@property
def configured_ssid(self):
for ssid, settings in self.config.get('access-points', {}).items():
psk = settings.get('password')
return ssid, psk
return None, None
def set_ssid_psk(self, ssid, psk):
aps = self.config.setdefault('access-points', {})
aps.clear()
if ssid is not None:
aps[ssid] = {}
if psk is not None:
aps[ssid]['password'] = psk
@property @property
def ifindex(self): def ifindex(self):
if self.info is not None: if self.info is not None:
@ -298,7 +291,7 @@ class NetworkModel(object):
dev.config = config dev.config = config
log.debug("new_link %s %s with config %s", log.debug("new_link %s %s with config %s",
ifindex, link.name, ifindex, link.name,
sanitize_interface_config(dev.config)) netplan.sanitize_interface_config(dev.config))
self.devices_by_name[link.name] = dev self.devices_by_name[link.name] = dev
return dev return dev

View File

@ -8,6 +8,27 @@ import yaml
log = logging.getLogger("subiquitycore.netplan") log = logging.getLogger("subiquitycore.netplan")
def _sanitize_inteface_config(iface_config):
for ap, ap_config in iface_config.get('access-points', {}).items():
if 'password' in ap_config:
ap_config['password'] = '<REDACTED>'
def sanitize_interface_config(iface_config):
iface_config = copy.deepcopy(iface_config)
_sanitize_inteface_config(iface_config)
return iface_config
def sanitize_config(config):
"""Return a copy of config with passwords redacted."""
config = copy.deepcopy(config)
interfaces = config.get('network', {}).get('wifis', {}).items()
for iface, iface_config in interfaces:
_sanitize_inteface_config(iface_config)
return config
class Config: class Config:
"""A NetplanConfig represents the network config for a system. """A NetplanConfig represents the network config for a system.
@ -85,7 +106,9 @@ class _PhysicalDevice:
self.match_mac = match.get('macaddress') self.match_mac = match.get('macaddress')
self.match_driver = match.get('driver') self.match_driver = match.get('driver')
self.config = config self.config = config
log.debug("config for %s = %s" % (name, self.config)) log.debug(
"config for %s = %s" % (
name, sanitize_interface_config(self.config)))
def matches_link(self, link): def matches_link(self, link):
if self.match_name is not None: if self.match_name is not None:
@ -107,7 +130,9 @@ class _VirtualDevice:
def __init__(self, name, config): def __init__(self, name, config):
self.name = name self.name = name
self.config = config self.config = config
log.debug("config for %s = %s" % (name, self.config)) log.debug(
"config for %s = %s" % (
name, sanitize_interface_config(self.config)))
def configs_in_root(root, masked=False): def configs_in_root(root, masked=False):

View File

@ -84,10 +84,10 @@ def get_ips_standalone():
prober.probe_network() prober.probe_network()
links = prober.get_results()['network']['links'] links = prober.get_results()['network']['links']
ips = [] ips = []
for l in sorted(links, key=lambda l: l['netlink_data']['name']): for link in sorted(links, key=lambda link: link['netlink_data']['name']):
if l['type'] in NETDEV_IGNORED_IFACE_TYPES: if link['type'] in NETDEV_IGNORED_IFACE_TYPES:
continue continue
for addr in l['addresses']: for addr in link['addresses']:
if addr['scope'] == "global": if addr['scope'] == "global":
ips.append(addr['address'].split('/')[0]) ips.append(addr['address'].split('/')[0])
return ips return ips

View File

@ -555,6 +555,6 @@ class SubFormField(FormField):
class SubForm(Form): class SubForm(Form):
def __init__(self, parent): def __init__(self, parent, **kw):
self.parent = parent self.parent = parent
super().__init__() super().__init__(**kw)

View File

@ -74,10 +74,11 @@ class NetworkConfigureWLANStretchy(Stretchy):
connect_signal(self.form, 'submit', self.done) connect_signal(self.form, 'submit', self.done)
connect_signal(self.form, 'cancel', self.cancel) connect_signal(self.form, 'cancel', self.cancel)
if self.device.configured_ssid is not None: ssid, psk = self.device.configured_ssid
self.form.ssid.value = self.device.configured_ssid if ssid:
if self.device.configured_wifi_psk is not None: self.form.ssid.value = ssid
self.form.psk.value = self.device.configured_wifi_psk if psk:
self.form.psk.value = psk
self.ssid_row = self.form.ssid._table self.ssid_row = self.form.ssid._table
self.psk_row = self.form.psk._table self.psk_row = self.form.psk._table
@ -95,7 +96,8 @@ class NetworkConfigureWLANStretchy(Stretchy):
def show_ssid_list(self, sender): def show_ssid_list(self, sender):
self.parent.show_overlay( self.parent.show_overlay(
NetworkList(self, self.device.actual_ssids), width=60) NetworkList(
self, self.device.info.wlan['visible_ssids']), width=60)
def start_scan(self, sender): def start_scan(self, sender):
fp = self.inputs.focus_position - 1 fp = self.inputs.focus_position - 1
@ -109,13 +111,13 @@ class NetworkConfigureWLANStretchy(Stretchy):
self.error.set_text("%s" % (r,)) self.error.set_text("%s" % (r,))
def _build_iface_inputs(self): def _build_iface_inputs(self):
if len(self.device.actual_ssids) > 0: if len(self.device.info.wlan['visible_ssids']) > 0:
networks_btn = menu_btn("Choose a visible network", networks_btn = menu_btn("Choose a visible network",
on_press=self.show_ssid_list) on_press=self.show_ssid_list)
else: else:
networks_btn = disabled(menu_btn("No visible networks")) networks_btn = disabled(menu_btn("No visible networks"))
if not self.device.scan_state: if not self.device.info.wlan['scan_state']:
scan_btn = menu_btn("Scan for networks", on_press=self.start_scan) scan_btn = menu_btn("Scan for networks", on_press=self.start_scan)
else: else:
scan_btn = disabled(menu_btn("Scanning for networks")) scan_btn = disabled(menu_btn("Scanning for networks"))
@ -146,7 +148,7 @@ class NetworkConfigureWLANStretchy(Stretchy):
for obj in self._build_iface_inputs()] for obj in self._build_iface_inputs()]
def done(self, sender): def done(self, sender):
if self.device.configured_ssid is None and self.form.ssid.value: if self.device.configured_ssid[0] is None and self.form.ssid.value:
# Turn DHCP4 on by default when specifying an SSID for # Turn DHCP4 on by default when specifying an SSID for
# the first time... # the first time...
self.device.dhcp4 = True self.device.dhcp4 = True