Merge branch 'master' into still-better-i18n
This commit is contained in:
commit
7bcfcba99b
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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 = (
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue