diff --git a/bin/console-conf-wrapper b/bin/console-conf-wrapper
index 609e9fc6..11ddb168 100755
--- a/bin/console-conf-wrapper
+++ b/bin/console-conf-wrapper
@@ -17,6 +17,9 @@ if [ -e /run/snapd-recovery-chooser-triggered ]; then
# when recovery chooser is invoked it takes over the terminal
exec /usr/lib/snapd/snap-recovery-chooser
fi
+ if [ -e /var/lib/console-conf/complete ]; then
+ exit 0
+ fi
fi
if grep -q 'snapd_recovery_mode=install' /proc/cmdline ; then
diff --git a/debian/console-conf.console-conf@.service b/debian/console-conf.console-conf@.service
index bec8e483..373222f4 100644
--- a/debian/console-conf.console-conf@.service
+++ b/debian/console-conf.console-conf@.service
@@ -10,7 +10,8 @@ After=core18.start-snapd.service core.start-snapd.service
After=snapd.recovery-chooser-trigger.service
IgnoreOnIsolate=yes
ConditionPathExists=/dev/tty0
-ConditionPathExists=!/var/lib/console-conf/complete
+ConditionPathExists=|!/var/lib/console-conf/complete
+ConditionPathExists=|/run/snapd-recovery-chooser-triggered
StartLimitInterval=0
[Service]
diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py
index 8f6f38c6..9c1a8928 100644
--- a/subiquity/controllers/filesystem.py
+++ b/subiquity/controllers/filesystem.py
@@ -161,7 +161,8 @@ class FilesystemController(SubiquityController):
"block probing failed restricted=%s", restricted)
report = self.app.make_apport_report(
kind, "block probing", interrupt=False)
- self._crash_reports[restricted] = report
+ if report is not None:
+ self._crash_reports[restricted] = report
continue
break
diff --git a/subiquity/controllers/installprogress.py b/subiquity/controllers/installprogress.py
index 8f392037..a26e617d 100644
--- a/subiquity/controllers/installprogress.py
+++ b/subiquity/controllers/installprogress.py
@@ -153,7 +153,8 @@ class InstallProgressController(SubiquityController):
self.progress_view.set_status(('info_error',
_("An error has occurred")))
self.start_ui()
- self.progress_view.show_error(crash_report)
+ if crash_report is not None:
+ self.progress_view.show_error(crash_report)
def logged_command(self, cmd):
return ['systemd-cat', '--level-prefix=false',
diff --git a/subiquity/controllers/keyboard.py b/subiquity/controllers/keyboard.py
index 4ce319cb..1a9310e5 100644
--- a/subiquity/controllers/keyboard.py
+++ b/subiquity/controllers/keyboard.py
@@ -35,7 +35,7 @@ class KeyboardController(SubiquityController):
'properties': {
'layout': {'type': 'string'},
'variant': {'type': 'string'},
- 'toggle': {'type': 'string'},
+ 'toggle': {'type': ['string', 'null']},
},
'required': ['layout'],
'additionalProperties': False,
diff --git a/subiquity/core.py b/subiquity/core.py
index d5723e02..bb886ad0 100644
--- a/subiquity/core.py
+++ b/subiquity/core.py
@@ -274,7 +274,8 @@ class Subiquity(Application):
report = self.make_apport_report(
ErrorReportKind.UI, "Installer UI", interrupt=False,
wait=True)
- print("report saved to {path}".format(report.path))
+ if report is not None:
+ print("report saved to {path}".format(report.path))
except Exception:
print("report generation failed")
traceback.print_exc()
@@ -457,6 +458,9 @@ class Subiquity(Application):
self._apport_data.append((key, value))
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")
try:
@@ -507,7 +511,7 @@ class Subiquity(Application):
self.add_global_overlay(ErrorReportStretchy(self, report))
def make_autoinstall(self):
- config = {}
+ config = {'version': 1}
for controller in self.controllers.instances:
controller_conf = controller.make_autoinstall()
if controller_conf:
diff --git a/subiquity/models/filesystem.py b/subiquity/models/filesystem.py
index bfee70b4..3ffe0535 100644
--- a/subiquity/models/filesystem.py
+++ b/subiquity/models/filesystem.py
@@ -24,6 +24,7 @@ import math
import os
import pathlib
import platform
+import tempfile
from curtin import storage_config
from curtin.util import human2bytes
@@ -158,7 +159,7 @@ raidlevels = [
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:
r[str(n)] = r[n] = r["raid"+str(n)]
r["stripe"] = r["raid0"]
@@ -391,7 +392,7 @@ def asdict(inst):
continue
m = getattr(inst, 'serialize_' + field.name, None)
if m:
- r[field.name] = m()
+ r.update(m())
else:
v = getattr(inst, field.name)
if v is not None:
@@ -968,7 +969,7 @@ class Partition(_Formattable):
return self._fs._available()
def serialize_number(self):
- return self._number
+ return {'number': self._number}
@property
def _number(self):
@@ -1021,7 +1022,7 @@ class Raid(_Device):
# Surprisingly, the order of devices passed to mdadm --create
# matters (see get_raid_size) so we sort devices here the same
# 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(
backlink="_constructed_device", default=attr.Factory(set))
@@ -1161,7 +1162,7 @@ class LVM_LogicalVolume(_Formattable):
preserve = attr.ib(default=False)
def serialize_size(self):
- return "{}B".format(self.size)
+ return {'size': "{}B".format(self.size)}
def available(self):
if self._constructed_device is not None:
@@ -1209,6 +1210,16 @@ class DM_Crypt:
volume = attributes.ref(backlink="_constructed_device") # _Formattable
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)
preserve = attr.ib(default=False)
diff --git a/subiquity/models/keyboard.py b/subiquity/models/keyboard.py
index 0e6649db..288c2359 100644
--- a/subiquity/models/keyboard.py
+++ b/subiquity/models/keyboard.py
@@ -135,7 +135,7 @@ class KeyboardSetting:
# Non-latin keyboard layouts that are handled in a uniform way
standard_non_latin_layouts = set(
('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',
'mal', 'np', 'ori', 'pk', 'ru', 'scc', 'sy', 'syr', 'tel', 'th',
'tj', 'tam', 'tib', 'ua', 'ug', 'uz')
diff --git a/subiquity/models/subiquity.py b/subiquity/models/subiquity.py
index 7ed94c3f..43814794 100644
--- a/subiquity/models/subiquity.py
+++ b/subiquity/models/subiquity.py
@@ -149,9 +149,10 @@ class SubiquityModel:
'mode': 'off',
},
'locale': self.locale.selected_language + '.UTF-8',
- 'preserve_hostname': True,
'resize_rootfs': False,
}
+ if self.identity.hostname is not None:
+ config['preserve_hostname'] = True
user = self.identity.user
if user:
users_and_groups_path = (
diff --git a/subiquity/ui/views/filesystem/guided.py b/subiquity/ui/views/filesystem/guided.py
index 1457ae50..9721b2be 100644
--- a/subiquity/ui/views/filesystem/guided.py
+++ b/subiquity/ui/views/filesystem/guided.py
@@ -86,7 +86,7 @@ class GuidedChoiceForm(SubForm):
lvm_options = SubFormField(LVMOptionsForm, "", help=NO_HELP)
def __init__(self, parent):
- super().__init__(parent)
+ super().__init__(parent, initial={'use_lvm': True})
options = []
tables = []
initial = -1
diff --git a/subiquitycore/controllers/network.py b/subiquitycore/controllers/network.py
index c540856c..94524458 100644
--- a/subiquitycore/controllers/network.py
+++ b/subiquitycore/controllers/network.py
@@ -29,7 +29,6 @@ from subiquitycore.file_util import write_file
from subiquitycore.models.network import (
BondParameters,
NetDevAction,
- sanitize_config,
)
from subiquitycore import netplan
from subiquitycore.ui.views.network import (
@@ -342,7 +341,9 @@ class NetworkController(BaseController):
config = self.model.render_config()
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):
if p == self.netplan_path:
diff --git a/subiquitycore/models/network.py b/subiquitycore/models/network.py
index 001d85d7..6fb60556 100644
--- a/subiquitycore/models/network.py
+++ b/subiquitycore/models/network.py
@@ -13,7 +13,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-import copy
import enum
import ipaddress
import logging
@@ -49,27 +48,6 @@ class NetDevAction(enum.Enum):
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'] = ''
-
-
-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:
# Just a place to hang various data about how bonds can be
# configured.
@@ -176,6 +154,21 @@ class NetworkDev(object):
def supports_action(self, action):
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
def ifindex(self):
if self.info is not None:
@@ -298,7 +291,7 @@ class NetworkModel(object):
dev.config = config
log.debug("new_link %s %s with config %s",
ifindex, link.name,
- sanitize_interface_config(dev.config))
+ netplan.sanitize_interface_config(dev.config))
self.devices_by_name[link.name] = dev
return dev
diff --git a/subiquitycore/netplan.py b/subiquitycore/netplan.py
index aac23143..ed42a07b 100644
--- a/subiquitycore/netplan.py
+++ b/subiquitycore/netplan.py
@@ -8,6 +8,27 @@ import yaml
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'] = ''
+
+
+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:
"""A NetplanConfig represents the network config for a system.
@@ -85,7 +106,9 @@ class _PhysicalDevice:
self.match_mac = match.get('macaddress')
self.match_driver = match.get('driver')
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):
if self.match_name is not None:
@@ -107,7 +130,9 @@ class _VirtualDevice:
def __init__(self, name, config):
self.name = name
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):
diff --git a/subiquitycore/ssh.py b/subiquitycore/ssh.py
index a312e693..fa3a5eab 100644
--- a/subiquitycore/ssh.py
+++ b/subiquitycore/ssh.py
@@ -84,10 +84,10 @@ def get_ips_standalone():
prober.probe_network()
links = prober.get_results()['network']['links']
ips = []
- for l in sorted(links, key=lambda l: l['netlink_data']['name']):
- if l['type'] in NETDEV_IGNORED_IFACE_TYPES:
+ for link in sorted(links, key=lambda link: link['netlink_data']['name']):
+ if link['type'] in NETDEV_IGNORED_IFACE_TYPES:
continue
- for addr in l['addresses']:
+ for addr in link['addresses']:
if addr['scope'] == "global":
ips.append(addr['address'].split('/')[0])
return ips
diff --git a/subiquitycore/ui/form.py b/subiquitycore/ui/form.py
index c1153ee5..0384aad4 100644
--- a/subiquitycore/ui/form.py
+++ b/subiquitycore/ui/form.py
@@ -555,6 +555,6 @@ class SubFormField(FormField):
class SubForm(Form):
- def __init__(self, parent):
+ def __init__(self, parent, **kw):
self.parent = parent
- super().__init__()
+ super().__init__(**kw)
diff --git a/subiquitycore/ui/views/network_configure_wlan_interface.py b/subiquitycore/ui/views/network_configure_wlan_interface.py
index 66b1e6ed..69a2bdbd 100644
--- a/subiquitycore/ui/views/network_configure_wlan_interface.py
+++ b/subiquitycore/ui/views/network_configure_wlan_interface.py
@@ -74,10 +74,11 @@ class NetworkConfigureWLANStretchy(Stretchy):
connect_signal(self.form, 'submit', self.done)
connect_signal(self.form, 'cancel', self.cancel)
- if self.device.configured_ssid is not None:
- self.form.ssid.value = self.device.configured_ssid
- if self.device.configured_wifi_psk is not None:
- self.form.psk.value = self.device.configured_wifi_psk
+ ssid, psk = self.device.configured_ssid
+ if ssid:
+ self.form.ssid.value = ssid
+ if psk:
+ self.form.psk.value = psk
self.ssid_row = self.form.ssid._table
self.psk_row = self.form.psk._table
@@ -95,7 +96,8 @@ class NetworkConfigureWLANStretchy(Stretchy):
def show_ssid_list(self, sender):
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):
fp = self.inputs.focus_position - 1
@@ -109,13 +111,13 @@ class NetworkConfigureWLANStretchy(Stretchy):
self.error.set_text("%s" % (r,))
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",
on_press=self.show_ssid_list)
else:
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)
else:
scan_btn = disabled(menu_btn("Scanning for networks"))
@@ -146,7 +148,7 @@ class NetworkConfigureWLANStretchy(Stretchy):
for obj in self._build_iface_inputs()]
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
# the first time...
self.device.dhcp4 = True