Merge pull request #1066 from canonical/wsl_oobe_conf_calls

DEENG-38/DEENG-39 - Moving to WSL Configuration and remove old command call
This commit is contained in:
Didier Roche 2021-10-07 09:35:02 +02:00 committed by GitHub
commit e367e7b4b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 462 additions and 426 deletions

View File

@ -70,16 +70,16 @@
"wslconfbase": {
"type": "object",
"properties": {
"custom_path": {
"automount_root": {
"type": "string"
},
"custom_mount_opt": {
"automount_options": {
"type": "string"
},
"gen_host": {
"network_generatehosts": {
"type": "boolean"
},
"gen_resolvconf": {
"network_generateresolvconf": {
"type": "boolean"
}
},
@ -100,22 +100,22 @@
"gui_followwintheme": {
"type": "boolean"
},
"legacy_gui": {
"interop_guiintegration": {
"type": "boolean"
},
"legacy_audio": {
"interop_audiointegration": {
"type": "boolean"
},
"adv_ip_detect": {
"interop_advancedipdetection": {
"type": "boolean"
},
"wsl_motd_news": {
"motd_wslnewsenabled": {
"type": "boolean"
},
"automount": {
"automount_enabled": {
"type": "boolean"
},
"mountfstab": {
"automount_mountfstab": {
"type": "boolean"
}
},

View File

@ -6,9 +6,9 @@ WSLIdentity:
# ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
WSLConfigurationBase:
custom_path: '/custom_mnt_path'
custom_mount_opt: 'opt1 opt2 opt3'
gen_host: false
gen_resolvconf: false
automount_root: '/custom_mnt_path'
automount_options: 'metadata'
network_generatehosts: false
network_generateresolvconf: false
Summary:
reboot: yes

View File

@ -3,17 +3,15 @@ early-commands:
- echo a
- ["sleep", "1"]
- echo a
Welcome:
lang: en_US
locale: en_US
identity:
realname: Ubuntu
username: ubuntu
# ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
WSLConfigurationBase:
custom_path: '/custom_mnt_path'
custom_mount_opt: 'opt1 opt2 opt3'
gen_host: false
gen_resolvconf: false
Summary:
reboot: yes
wslconfbase:
automount_root: '/custom_mnt_path'
automount_options: 'metadata'
network_generatehosts: false
network_generateresolvconf: false
shutdown: 'reboot'

View File

@ -379,21 +379,21 @@ class ShutdownMode(enum.Enum):
@attr.s(auto_attribs=True)
class WSLConfigurationBase:
custom_path: str = attr.ib(default='/mnt/')
custom_mount_opt: str = ''
gen_host: bool = attr.ib(default=True)
gen_resolvconf: bool = attr.ib(default=True)
automount_root: str = attr.ib(default='/mnt/')
automount_options: str = ''
network_generatehosts: bool = attr.ib(default=True)
network_generateresolvconf: bool = attr.ib(default=True)
@attr.s(auto_attribs=True)
class WSLConfigurationAdvanced:
gui_theme: str = attr.ib(default='default')
gui_followwintheme: bool = attr.ib(default=True)
legacy_gui: bool = attr.ib(default=False)
legacy_audio: bool = attr.ib(default=False)
adv_ip_detect: bool = attr.ib(default=False)
wsl_motd_news: bool = attr.ib(default=True)
automount: bool = attr.ib(default=True)
mountfstab: bool = attr.ib(default=True)
interop_guiintegration: bool = attr.ib(default=False)
interop_audiointegration: bool = attr.ib(default=False)
interop_advancedipdetection: bool = attr.ib(default=False)
motd_wslnewsenabled: bool = attr.ib(default=True)
automount_enabled: bool = attr.ib(default=True)
automount_mountfstab: bool = attr.ib(default=True)
interop_enabled: bool = attr.ib(default=True)
interop_appendwindowspath: bool = attr.ib(default=True)

View File

@ -33,22 +33,12 @@ class WSLConfigurationAdvancedController(SubiquityTuiController):
def run_answers(self):
if all(elem in self.answers for elem in
['interop_enabled', 'interop_appendwindowspath',
'gui_theme', 'gui_followwintheme', 'legacy_gui',
'legacy_audio', 'adv_ip_detect',
'wsl_motd_news', 'automount', 'mountfstab']):
reconfiguration = WSLConfigurationAdvanced(
interop_enabled=self.answers['interop_enabled'],
interop_appendwindowspath=self
.answers['interop_appendwindowspath'],
gui_theme=self.answers['gui_theme'],
gui_followwintheme=self.answers['gui_followwintheme'],
legacy_gui=self.answers['legacy_gui'],
legacy_audio=self.answers['legacy_audio'],
adv_ip_detect=self.answers['adv_ip_detect'],
wsl_motd_news=self.answers['wsl_motd_news'],
automount=self.answers['automount'],
mountfstab=self.answers['mountfstab']
)
'gui_theme', 'gui_followwintheme', 'interop_guiintegration',
'interop_audiointegration', 'interop_advancedipdetection',
'motd_wslnewsenabled', 'automount_enabled',
'automount_mountfstab']):
reconfiguration = WSLConfigurationAdvanced(**self.answers)
self.done(reconfiguration)
def done(self, reconf_data):

View File

@ -16,13 +16,9 @@ class WSLConfigurationBaseController(SubiquityTuiController):
def run_answers(self):
if all(elem in self.answers for elem in
['custom_path', 'custom_mount_opt',
'gen_host', 'gen_resolvconf']):
configuration = WSLConfigurationBase(
custom_path=self.answers['custom_path'],
custom_mount_opt=self.answers['custom_mount_opt'],
gen_host=self.answers['gen_host'],
gen_resolvconf=self.answers['gen_resolvconf'])
['automount_root', 'automount_options',
'network_generatehosts', 'network_generateresolvconf']):
configuration = WSLConfigurationBase(**self.answers)
self.done(configuration)
def done(self, configuration_data):

View File

@ -0,0 +1,169 @@
#!/usr/bin/env python3
# Copyright 2015-2021 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
# original code from ubuntuwslctl.core.loader
# Copyright (C) 2021 Canonical Ltd.
import os
import logging
from configparser import ConfigParser
log = logging.getLogger("system_setup.common.wsl_conf")
config_base_default = {
"wsl": {
"automount": {
"root": "/mnt/",
"options": ""
},
"network": {
"generatehosts": "true",
"generateresolvconf": "true"
}
}
}
config_adv_default = {
"wsl": {
"automount": {
"enabled": "true",
"mountfstab": "true"
},
"interop": {
"enabled": "true",
"appendwindowspath": "true"
}
},
"ubuntu": {
"GUI": {
"theme": "default",
"followwintheme": "false"
},
"Interop": {
"guiintegration": "false",
"audiointegration": "false",
"advancedipdetection": "false"
},
"Motd": {
"wslnewsenabled": "true"
}
}
}
conf_type_to_file = {
"wsl": "/etc/wsl.conf",
"ubuntu": "/etc/ubuntu-wsl.conf"
}
def wsl_config_loader(data, config_ref, id):
"""
Loads the configuration from the given file type,
section and reference config.
:param data: dict, the data to load into
:param pathname: string, the path to the file to load
:param id: string, the name of the section to load
"""
pathname = conf_type_to_file[id]
if not os.path.exists(pathname):
return data
config = ConfigParser()
config.read(pathname)
for conf_sec in config:
if conf_sec in config_ref[id]:
conf_sec_list = config[conf_sec]
for conf_item in conf_sec_list:
if conf_item in config_ref[id][conf_sec]:
data[conf_sec.lower()
+ "_" + conf_item.lower()] = \
conf_sec_list[conf_item]
return data
def default_loader(is_advanced=False):
"""
This will load the default WSL config for the given type.
:param is_advanced: boolean, True if it is WSLConfigurationAdvanced,
else is WSLConfigurationBase
"""
data = {}
conf_ref = config_adv_default if is_advanced else config_base_default
data = wsl_config_loader(data, conf_ref, "wsl")
if is_advanced:
data = \
wsl_config_loader(data, conf_ref, "ubuntu")
return data
# TODO: remove dryrun and add root param once we write the option to .subiquity
def wsl_config_update(config_class, is_dry_run):
"""
This update the configuration file for the given class.zzd
:param config_class: WSLConfigurationBase or WSLConfigurationAdvanced
:param is_dry_run: boolean, True if it is a dry run
"""
if is_dry_run:
log.debug("mimicking setting config %s", config_class)
temp_conf_default = {}
temp_confname = config_class.__str__()
if temp_confname.startswith("WSLConfigurationBase"):
temp_conf_default = config_base_default
elif temp_confname.startswith("WSLConfigurationAdvanced"):
temp_conf_default = config_adv_default
else:
raise TypeError("Invalid type name.")
if is_dry_run:
return
# update the config file
for config_type in temp_conf_default:
config_sections = temp_conf_default[config_type]
config = ConfigParser()
config.BasicInterpolcation = None
conf_file = conf_type_to_file[config_type]
config.read(conf_file)
for config_section in config_sections:
config_settings = config_sections[config_section]
for config_setting in config_settings:
config_default_value = config_settings[config_setting]
config_api_name = \
config_section.lower() + "_" + config_setting.lower()
config_value = config_class.__dict__[config_api_name]
# if the value for the setting is defualt value, drop it
if config_default_value == config_value:
if config_setting in config[config_section]:
config.remove_option(config_section, config_setting)
# drop the section if it become empty
if config[config_section] == {}:
config.remove_section(config_section)
else:
if config_section not in config:
config.add_section(config_section)
if isinstance(config_value, bool):
config_value = str(config_value).lower()
config[config_section][config_setting] = config_value
with open(conf_file + ".new", 'w+') as configfile:
config.write(configfile)
os.rename(conf_file + ".new", conf_file)

View File

@ -20,39 +20,6 @@ import subprocess
log = logging.getLogger("subiquity.system_setup.common.wsl_utils")
config_ref = {
"wsl": {
"automount": {
"enabled": "automount",
"mountfstab": "mountfstab",
"root": "custom_path",
"options": "custom_mount_opt",
},
"network": {
"generatehosts": "gen_host",
"generateresolvconf": "gen_resolvconf",
},
"interop": {
"enabled": "interop_enabled",
"appendwindowspath": "interop_appendwindowspath",
}
},
"ubuntu": {
"GUI": {
"theme": "gui_theme",
"followwintheme": "gui_followwintheme",
},
"Interop": {
"guiintegration": "legacy_gui",
"audiointegration": "legacy_audio",
"advancedipdetection": "adv_ip_detect",
},
"Motd": {
"wslnewsenabled": "wsl_motd_news",
}
}
}
def is_reconfigure(is_dryrun):
if is_dryrun and \
@ -92,3 +59,31 @@ def get_windows_locale():
except OSError as e:
log.info(windows_locale_failed_msg + e.strerror)
return None
def get_userandgroups():
from subiquity.common.resources import resource_path
from subiquitycore.utils import run_command
users_and_groups_path = resource_path('users-and-groups')
if os.path.exists(users_and_groups_path):
groups = open(users_and_groups_path).read().split()
else:
groups = ['admin']
groups.append('sudo')
command = ['getent', 'group']
cp = run_command(command, check=True)
target_groups = set()
for line in cp.stdout.splitlines():
target_groups.add(line.split(':')[0])
groups = [group for group in groups if group in target_groups]
oneline_usergroups = ",".join(groups)
return oneline_usergroups
def convert_if_bool(value):
if value.lower() in ('true', 'false'):
return value.lower() == 'true'
return value

View File

@ -14,24 +14,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import subprocess
import attr
from subiquitycore.utils import run_command
log = logging.getLogger('subiquity.models.wsl_configuration_advanced')
log = logging.getLogger('system_setup.models.wsl_configuration_advanced')
@attr.s
class WSLConfigurationAdvanced(object):
gui_theme = attr.ib()
gui_followwintheme = attr.ib()
legacy_gui = attr.ib()
legacy_audio = attr.ib()
adv_ip_detect = attr.ib()
wsl_motd_news = attr.ib()
automount = attr.ib()
mountfstab = attr.ib()
interop_guiintegration = attr.ib()
interop_audiointegration = attr.ib()
interop_advancedipdetection = attr.ib()
motd_wslnewsenabled = attr.ib()
automount_enabled = attr.ib()
automount_mountfstab = attr.ib()
interop_enabled = attr.ib()
interop_appendwindowspath = attr.ib()
@ -42,67 +39,10 @@ class WSLConfigurationAdvancedModel(object):
def __init__(self):
self._wslconfadvanced = None
# TODO WSL: Load settings from system
def apply_settings(self, result, is_dry_run=False):
d = {}
# TODO: placholder settings; should be dynamically assgined using
# ubuntu-wsl-integration
d['interop_enabled'] = result.interop_enabled
d['interop_appendwindowspath'] = result.interop_appendwindowspath
d['gui_theme'] = result.gui_theme
d['gui_followwintheme'] = result.gui_followwintheme
d['legacy_gui'] = result.legacy_gui
d['legacy_audio'] = result.legacy_audio
d['adv_ip_detect'] = result.adv_ip_detect
d['wsl_motd_news'] = result.wsl_motd_news
d['automount'] = result.automount
d['mountfstab'] = result.mountfstab
def apply_settings(self, result):
d = result.__dict__
self._wslconfadvanced = WSLConfigurationAdvanced(**d)
# TODO WSL: Drop all calls of ubuntuwsl here and ensure the data
# are passed to the app model
if not is_dry_run:
# reset to keep everything as refreshed as new
run_command(["/usr/bin/ubuntuwsl", "reset", "-y"],
stdout=subprocess.DEVNULL)
# set the settings
# TODO: placholder settings; should be dynamically generated using
# ubuntu-wsl-integration
run_command(["/usr/bin/ubuntuwsl", "update",
"WSL.automount.enabled", result.automount],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"WSL.automount.mountfstab", result.mountfstab],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"WSL.interop.enabled",
result.interop_enabled],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"WSL.interop.appendwindowspath",
result.interop_appendwindowspath],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"ubuntu.GUI.followwintheme",
result.gui_followwintheme],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"ubuntu.GUI.theme", result.gui_theme],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"ubuntu.Interop.guiintergration", result.legacy_gui],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"ubuntu.Interop.audiointegration",
result.legacy_audio],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"ubuntu.Interop.advancedipdetection",
result.adv_ip_detect],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"ubuntu.Motd.wslnewsenabled", result.wsl_motd_news],
stdout=subprocess.DEVNULL)
@property
def wslconfadvanced(self):

View File

@ -14,20 +14,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import subprocess
import attr
from subiquitycore.utils import run_command
log = logging.getLogger('subiquity.models.wsl_configuration_base')
log = logging.getLogger('system_setup.models.wsl_configuration_base')
@attr.s
class WSLConfigurationBase(object):
custom_path = attr.ib()
custom_mount_opt = attr.ib()
gen_host = attr.ib()
gen_resolvconf = attr.ib()
automount_root = attr.ib()
automount_options = attr.ib()
network_generatehosts = attr.ib()
network_generateresolvconf = attr.ib()
class WSLConfigurationBaseModel(object):
@ -38,33 +35,9 @@ class WSLConfigurationBaseModel(object):
self._wslconfbase = None
# TODO WSL: Load settings from system
def apply_settings(self, result, is_dry_run=False):
d = {}
d['custom_path'] = result.custom_path
d['custom_mount_opt'] = result.custom_mount_opt
d['gen_host'] = result.gen_host
d['gen_resolvconf'] = result.gen_resolvconf
def apply_settings(self, result):
d = result.__dict__
self._wslconfbase = WSLConfigurationBase(**d)
# TODO WSL: Drop all calls of ubuntuwsl here and ensure the data
# are passed to the app model
if not is_dry_run:
# reset to keep everything as refreshed as new
run_command(["/usr/bin/ubuntuwsl", "reset", "-y"],
stdout=subprocess.DEVNULL)
# set the settings
run_command(["/usr/bin/ubuntuwsl", "update",
"WSL.automount.root", result.custom_path],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"WSL.automount.options", result.custom_mount_opt],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"WSL.network.generatehosts", result.gen_host],
stdout=subprocess.DEVNULL)
run_command(["/usr/bin/ubuntuwsl", "update",
"WSL.network.generateresolvconf",
result.gen_resolvconf],
stdout=subprocess.DEVNULL)
@property
def wslconfbase(self):

View File

@ -15,16 +15,13 @@
import logging
from subiquitycore.context import with_context
from subiquity.common.errorreport import ErrorReportKind
from subiquity.server.controller import (
SubiquityController,
)
from subiquity.common.types import (
ApplicationState,
)
from subiquity.common.types import ApplicationState
from subiquity.server.controller import SubiquityController
from subiquitycore.context import with_context
from subiquitycore.utils import run_command
from system_setup.common.wsl_conf import wsl_config_update
from system_setup.common.wsl_utils import get_userandgroups
log = logging.getLogger("system_setup.server.controllers.configure")
@ -69,6 +66,40 @@ class ConfigureController(SubiquityController):
self.app.update_state(ApplicationState.POST_RUNNING)
dryrun = self.app.opts.dry_run
variant = self.app.variant
if variant == "wsl_setup":
wsl_id = self.model.identity.user
if dryrun:
log.debug("mimicking creating user %s",
wsl_id.username)
else:
create_user_act = \
run_command(["/usr/sbin/useradd", "-m", "-s",
"/bin/bash", "-p",
wsl_id.password,
wsl_id.username])
if create_user_act.returncode != 0:
raise Exception("Failed to create user %s: %s"
% (wsl_id.username,
create_user_act.stderr))
log.debug("created user %s", wsl_id.username)
assign_grp_act = \
run_command(["/usr/sbin/usermod", "-a",
"-c", wsl_id.realname,
"-G", get_userandgroups(),
wsl_id.username])
if assign_grp_act.returncode != 0:
raise Exception(("Failed to assign group"
" to user %s: %s")
% (wsl_id.username,
assign_grp_act.stderr))
else:
wsl_config_update(self.model.wslconfadvanced.wslconfadvanced,
dryrun)
wsl_config_update(self.model.wslconfbase.wslconfbase, dryrun)
self.app.update_state(ApplicationState.DONE)
except Exception:
kw = {}

View File

@ -16,15 +16,14 @@
import logging
import attr
from os import path
import configparser
from subiquitycore.context import with_context
from subiquity.common.apidef import API
from subiquity.common.types import WSLConfigurationAdvanced
from subiquity.server.controller import SubiquityController
from system_setup.common.wsl_utils import config_ref
from system_setup.common.wsl_conf import default_loader
from system_setup.common.wsl_utils import convert_if_bool
log = logging.getLogger(
'system_setup.server.controllers.wsl_configuration_advanced')
@ -42,103 +41,32 @@ class WSLConfigurationAdvancedController(SubiquityController):
'interop_appendwindowspath': {'type': 'boolean'},
'gui_theme': {'type': 'string'},
'gui_followwintheme': {'type': 'boolean'},
'legacy_gui': {'type': 'boolean'},
'legacy_audio': {'type': 'boolean'},
'adv_ip_detect': {'type': 'boolean'},
'wsl_motd_news': {'type': 'boolean'},
'automount': {'type': 'boolean'},
'mountfstab': {'type': 'boolean'}
'interop_guiintegration': {'type': 'boolean'},
'interop_audiointegration': {'type': 'boolean'},
'interop_advancedipdetection': {'type': 'boolean'},
'motd_wslnewsenabled': {'type': 'boolean'},
'automount_enabled': {'type': 'boolean'},
'automount_mountfstab': {'type': 'boolean'}
},
'additionalProperties': False,
}
# this is a temporary simplified reference. The future complete reference
# should use the default.json in `ubuntu-wsl-integration`.
config_ref = {
"wsl": {
"automount": {
"enabled": "automount",
"mountfstab": "mountfstab",
},
"interop": {
"enabled": "interop_enabled",
"appendwindowspath": "interop_appendwindowspath",
}
},
"ubuntu": {
"GUI": {
"theme": "gui_theme",
"followwintheme": "gui_followwintheme",
},
"Interop": {
"guiintegration": "legacy_gui",
"audiointegration": "legacy_audio",
"advancedipdetection": "adv_ip_detect",
},
"Motd": {
"wslnewsenabled": "wsl_motd_news",
}
}
}
def __init__(self, app):
super().__init__(app)
# load the config file
data = {}
if path.exists('/etc/wsl.conf'):
wslconfig = configparser.ConfigParser()
wslconfig.read('/etc/wsl.conf')
for conf_sec in wslconfig:
if conf_sec in config_ref['wsl']:
conf_sec_list = wslconfig[conf_sec]
for conf_item in conf_sec_list:
if conf_item in config_ref['wsl'][conf_sec]:
data[config_ref['wsl'][conf_sec][conf_item]] = \
conf_sec_list[conf_item]
if path.exists('/etc/ubuntu-wsl.conf'):
ubuntuconfig = configparser.ConfigParser()
ubuntuconfig.read('/etc/ubuntu-wsl.conf')
for conf_sec in ubuntuconfig:
if conf_sec in self.config_ref['ubuntu']:
conf_sec_list = ubuntuconfig[conf_sec]
for conf_item in conf_sec_list:
if conf_item in config_ref['ubuntu'][conf_sec]:
data[config_ref['ubuntu'][conf_sec][conf_item]] = \
conf_sec_list[conf_item]
data = default_loader(is_advanced=True)
if data:
def bool_converter(x):
return x.lower() == 'true'
reconf_data = WSLConfigurationAdvanced(
interop_enabled=bool_converter(data['interop_enabled']),
interop_appendwindowspath=bool_converter(
data['interop_appendwindowspath']),
gui_theme=data['gui_theme'],
gui_followwintheme=bool_converter(data['gui_followwintheme']),
legacy_gui=bool_converter(data['legacy_gui']),
legacy_audio=bool_converter(data['legacy_audio']),
adv_ip_detect=bool_converter(data['adv_ip_detect']),
wsl_motd_news=bool_converter(data['wsl_motd_news']),
automount=bool_converter(data['automount']),
mountfstab=bool_converter(data['mountfstab']),
)
self.model.apply_settings(reconf_data, self.opts.dry_run)
proc_data = \
{key: convert_if_bool(value) for (key, value) in data.items()}
reconf_data = WSLConfigurationAdvanced(**proc_data)
self.model.apply_settings(reconf_data)
def load_autoinstall_data(self, data):
if data is not None:
reconf_data = WSLConfigurationAdvanced(
interop_enabled=data['interop_enabled'],
interop_appendwindowspath=data['interop_appendwindowspath'],
gui_theme=data['gui_theme'],
gui_followwintheme=data['gui_followwintheme'],
legacy_gui=data['legacy_gui'],
legacy_audio=data['legacy_audio'],
adv_ip_detect=data['adv_ip_detect'],
wsl_motd_news=data['wsl_motd_news'],
automount=data['automount'],
mountfstab=data['mountfstab']
)
self.model.apply_settings(reconf_data, self.opts.dry_run)
reconf_data = WSLConfigurationAdvanced(**data)
self.model.apply_settings(reconf_data)
@with_context()
async def apply_autoinstall_config(self, context=None):
@ -151,20 +79,28 @@ class WSLConfigurationAdvancedController(SubiquityController):
async def GET(self) -> WSLConfigurationAdvanced:
data = WSLConfigurationAdvanced()
if self.model.wslconfadvanced is not None:
data.interop_enabled = self.model.wslconfadvanced.interop_enabled
data.interop_enabled = \
self.model.wslconfadvanced.interop_enabled
data.interop_appendwindowspath = \
self.model.wslconfadvanced.interop_appendwindowspath
data.gui_theme = self.model.wslconfadvanced.gui_theme
data.gui_theme = \
self.model.wslconfadvanced.gui_theme
data.gui_followwintheme = \
self.model.wslconfadvanced.gui_followwintheme
data.legacy_gui = self.model.wslconfadvanced.legacy_gui
data.legacy_audio = self.model.wslconfadvanced.legacy_audio
data.adv_ip_detect = self.model.wslconfadvanced.adv_ip_detect
data.wsl_motd_news = self.model.wslconfadvanced.wsl_motd_news
data.automount = self.model.wslconfadvanced.automount
data.mountfstab = self.model.wslconfadvanced.mountfstab
data.interop_guiintegration = \
self.model.wslconfadvanced.interop_guiintegration
data.interop_audiointegration = \
self.model.wslconfadvanced.interop_audiointegration
data.interop_advancedipdetection = \
self.model.wslconfadvanced.interop_advancedipdetection
data.motd_wslnewsenabled = \
self.model.wslconfadvanced.motd_wslnewsenabled
data.automount_enabled = \
self.model.wslconfadvanced.automount_enabled
data.automount_mountfstab = \
self.model.wslconfadvanced.automount_mountfstab
return data
async def POST(self, data: WSLConfigurationAdvanced):
self.model.apply_settings(data, self.opts.dry_run)
self.model.apply_settings(data)
await self.configured()

View File

@ -13,10 +13,7 @@
# 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/>.
import configparser
import logging
from os import path
import attr
from subiquitycore.context import with_context
@ -25,7 +22,8 @@ from subiquity.common.apidef import API
from subiquity.common.types import WSLConfigurationBase
from subiquity.server.controller import SubiquityController
from system_setup.common.wsl_utils import config_ref
from system_setup.common.wsl_conf import default_loader
from system_setup.common.wsl_utils import convert_if_bool
log = logging.getLogger('system_setup.server' +
'.controllers.wsl_configuration_base')
@ -39,10 +37,10 @@ class WSLConfigurationBaseController(SubiquityController):
autoinstall_schema = {
'type': 'object',
'properties': {
'custom_path': {'type': 'string'},
'custom_mount_opt': {'type': 'string'},
'gen_host': {'type': 'boolean'},
'gen_resolvconf': {'type': 'boolean'},
'automount_root': {'type': 'string'},
'automount_options': {'type': 'string'},
'network_generatehosts': {'type': 'boolean'},
'network_generateresolvconf': {'type': 'boolean'},
},
'additionalProperties': False,
}
@ -51,38 +49,18 @@ class WSLConfigurationBaseController(SubiquityController):
super().__init__(app)
# load the config file
data = {}
data = default_loader()
if path.exists('/etc/wsl.conf'):
wslconfig = configparser.ConfigParser()
wslconfig.read('/etc/wsl.conf')
for conf_sec in wslconfig:
if conf_sec in config_ref['wsl']:
conf_sec_list = wslconfig[conf_sec]
for conf_item in conf_sec_list:
if conf_item in config_ref['wsl'][conf_sec]:
data[config_ref['wsl'][conf_sec][conf_item]] = \
conf_sec_list[conf_item]
if data:
def bool_converter(x):
return x.lower() == 'true'
conf_data = WSLConfigurationBase(
custom_path=data['custom_path'],
custom_mount_opt=data['custom_mount_opt'],
gen_host=bool_converter(data['gen_host']),
gen_resolvconf=bool_converter(data['gen_resolvconf']),
)
self.model.apply_settings(conf_data, self.opts.dry_run)
proc_data = \
{key: convert_if_bool(value) for (key, value) in data.items()}
conf_data = WSLConfigurationBase(**proc_data)
self.model.apply_settings(conf_data)
def load_autoinstall_data(self, data):
if data is not None:
identity_data = WSLConfigurationBase(
custom_path=data['custom_path'],
custom_mount_opt=data['custom_mount_opt'],
gen_host=data['gen_host'],
gen_resolvconf=data['gen_resolvconf'],
)
self.model.apply_settings(identity_data, self.opts.dry_run)
identity_data = WSLConfigurationBase(**data)
self.model.apply_settings(identity_data)
@with_context()
async def apply_autoinstall_config(self, context=None):
@ -95,12 +73,14 @@ class WSLConfigurationBaseController(SubiquityController):
async def GET(self) -> WSLConfigurationBase:
data = WSLConfigurationBase()
if self.model.wslconfbase is not None:
data.custom_path = self.model.wslconfbase.custom_path
data.custom_mount_opt = self.model.wslconfbase.custom_mount_opt
data.gen_host = self.model.wslconfbase.gen_host
data.gen_resolvconf = self.model.wslconfbase.gen_resolvconf
data.automount_root = self.model.wslconfbase.automount_root
data.automount_options = self.model.wslconfbase.automount_options
data.network_generatehosts = \
self.model.wslconfbase.network_generatehosts
data.network_generateresolvconf = \
self.model.wslconfbase.network_generateresolvconf
return data
async def POST(self, data: WSLConfigurationBase):
self.model.apply_settings(data, self.opts.dry_run)
self.model.apply_settings(data)
await self.configured()

View File

@ -42,55 +42,53 @@ class WSLConfigurationAdvancedForm(Form):
def __init__(self, initial):
super().__init__(initial=initial)
automount = BooleanField(_("Enable Auto-Mount"),
help=_("Whether the Auto-Mount freature is"
" enabled. This feature allows you "
"to mount Windows drive in WSL"))
mountfstab = BooleanField(_("Mount `/etc/fstab`"),
help=_("Whether `/etc/fstab` will be mounted."
" The configuration file `/etc/fstab` "
"contains the necessary information to"
" automate the process of mounting "
"partitions. "))
interop_enabled = BooleanField(_("Enable Interop"),
help=_("Whether the interoperability is"
" enabled"))
interop_appendwindowspath = BooleanField(_("Append Windows Path"),
help=_("Whether Windows Path "
"will be append in the"
" PATH environment "
"variable in WSL."))
gui_theme = ChoiceField(_("GUI Theme"),
help=_("This option changes the Ubuntu theme."),
choices=["default", "light", "dark"])
gui_followwintheme = BooleanField(_("Follow Windows Theme"),
help=_("This option manages whether the"
" Ubuntu theme follows the "
"Windows theme; that is, when "
"Windows uses dark theme, "
"Ubuntu also uses dark theme."
" Requires WSL interoperability"
" enabled. "))
legacy_gui = BooleanField(_("Legacy GUI Integration"),
help=_("This option enables the Legacy GUI "
"Integration on Windows 10. Requires"
" a Third-party X Server."))
legacy_audio = BooleanField(_("Legacy Audio Integration"),
help=_("This option enables the Legacy "
"Audio Integration on Windows 10. "
"Requires PulseAudio for "
"Windows Installed."))
adv_ip_detect = BooleanField(_("Advanced IP Detection"),
help=_("This option enables advanced "
"detection of IP by Windows "
"IPv4 Address which is more "
"reliable to use with WSL2. "
"Requires WSL interoperability"
" enabled."))
wsl_motd_news = BooleanField(_("Enable WSL News"),
help=_("This options allows you to control"
" your MOTD News. Toggling it on "
"allows you to see the MOTD."))
automount_enabled = \
BooleanField(_("Enable Auto-Mount"),
help=_("Whether the Auto-Mount freature is enabled. "
"This feature allows you to mount Windows drive"
" in WSL."))
automount_mountfstab = \
BooleanField(_("Mount `/etc/fstab`"),
help=_("Whether `/etc/fstab` will be mounted. The "
"configuration file `/etc/fstab` contains "
"the necessary information to automate the"
" process of mounting partitions. "))
interop_enabled = \
BooleanField(_("Enable Interop"),
help=_("Whether the interoperability is enabled"))
interop_appendwindowspath = \
BooleanField(_("Append Windows Path"),
help=_("Whether Windows Path will be append in the"
" PATH environment variable in WSL."))
gui_theme = \
ChoiceField(_("GUI Theme"),
help=_("This option changes the Ubuntu theme."),
choices=["default", "light", "dark"])
gui_followwintheme = \
BooleanField(_("Follow Windows Theme"),
help=_("This option manages whether the Ubuntu theme"
" follows the Windows theme; that is, when "
"Windows uses dark theme, Ubuntu also uses dark"
" theme. Requires WSL interoperability enabled. "))
interop_guiintegration = \
BooleanField(_("Legacy GUI Integration"),
help=_("This option enables the Legacy GUI Integration "
"on Windows 10. Requires a Third-party X Server."))
interop_audiointegration = \
BooleanField(_("Legacy Audio Integration"),
help=_("This option enables the Legacy Audio Integration "
"on Windows 10. Requires PulseAudio for Windows "
"Installed."))
interop_advancedipdetection = \
BooleanField(_("Advanced IP Detection"),
help=_("This option enables advanced detection of IP by "
"Windows IPv4 Address which is more reliable to "
"use with WSL2. Requires WSL interoperability "
"enabled."))
motd_wslnewsenabled = \
BooleanField(_("Enable WSL News"),
help=_("This option allows you to control your MOTD News."
" Toggling it on allows you to see the MOTD."))
class WSLConfigurationAdvancedView(BaseView):
@ -102,17 +100,26 @@ class WSLConfigurationAdvancedView(BaseView):
self.controller = controller
initial = {
'interop_enabled': configuration_data.interop_enabled,
'interop_enabled':
configuration_data.interop_enabled,
'interop_appendwindowspath':
configuration_data.interop_appendwindowspath,
'gui_theme': configuration_data.gui_theme,
'gui_followwintheme': configuration_data.gui_followwintheme,
'legacy_gui': configuration_data.legacy_gui,
'legacy_audio': configuration_data.legacy_audio,
'adv_ip_detect': configuration_data.adv_ip_detect,
'wsl_motd_news': configuration_data.wsl_motd_news,
'automount': configuration_data.automount,
'mountfstab': configuration_data.mountfstab,
'gui_theme':
configuration_data.gui_theme,
'gui_followwintheme':
configuration_data.gui_followwintheme,
'interop_guiintegration':
configuration_data.interop_guiintegration,
'interop_audiointegration':
configuration_data.interop_audiointegration,
'interop_advancedipdetection':
configuration_data.interop_advancedipdetection,
'motd_wslnewsenabled':
configuration_data.motd_wslnewsenabled,
'automount_enabled':
configuration_data.automount_enabled,
'automount_mountfstab':
configuration_data.automount_mountfstab,
}
self.form = WSLConfigurationAdvancedForm(initial=initial)
@ -128,15 +135,24 @@ class WSLConfigurationAdvancedView(BaseView):
def done(self, result):
self.controller.done(WSLConfigurationAdvanced(
interop_enabled=self.form.interop_enabled.value,
interop_enabled=self.form
.interop_enabled.value,
interop_appendwindowspath=self.form
.interop_appendwindowspath.value,
gui_theme=self.form.gui_theme.value,
gui_followwintheme=self.form.gui_followwintheme.value,
legacy_gui=self.form.legacy_gui.value,
legacy_audio=self.form.legacy_audio.value,
adv_ip_detect=self.form.adv_ip_detect.value,
wsl_motd_news=self.form.wsl_motd_news.value,
automount=self.form.automount.value,
mountfstab=self.form.mountfstab.value,
gui_theme=self.form
.gui_theme.value,
gui_followwintheme=self.form
.gui_followwintheme.value,
interop_guiintegration=self.form
.interop_guiintegration.value,
interop_audiointegration=self.form
.interop_audiointegration.value,
interop_advancedipdetection=self.form
.interop_advancedipdetection.value,
motd_wslnewsenabled=self.form
.motd_wslnewsenabled.value,
automount_enabled=self.form
.automount_enabled.value,
automount_mountfstab=self.form
.automount_mountfstab.value,
))

View File

@ -41,24 +41,28 @@ class WSLConfBaseForm(Form):
def __init__(self, initial):
super().__init__(initial=initial)
custom_path = MountField(_("Mount Location"),
help=_("Location for the automount"))
custom_mount_opt = StringField(_("Mount Option"),
help=_("Mount option passed "
"for the automount"))
gen_host = BooleanField(_("Enable Host Generation"), help=_(
"Selecting enables /etc/host re-generation at every start"))
gen_resolvconf = BooleanField(_("Enable resolv.conf Generation"), help=_(
"Selecting enables /etc/resolv.conf re-generation at every start"))
automount_root = MountField(_("Mount Location"),
help=_("Location for the automount"))
automount_options = StringField(_("Mount Option"),
help=_("Mount option passed "
"for the automount"))
network_generatehosts = \
BooleanField(_("Enable Host Generation"),
help=_("Selecting enables /etc/host re-generation at"
" every start"))
network_generateresolvconf = \
BooleanField(_("Enable resolv.conf Generation"),
help=_("Selecting enables /etc/resolv.conf re-generation"
" at every start"))
def validate_custom_path(self):
p = self.custom_path.value
def validate_automount_root(self):
p = self.automount_root.value
if p != "" and (re.fullmatch(r"(/[^/ ]*)+/?", p) is None):
return _("Mount location must be a absolute UNIX path"
" without space.")
def validate_custom_mount_opt(self):
o = self.custom_mount_opt.value
def validate_automount_options(self):
o = self.automount_options.value
# filesystem independent mount option
fsimo = [r"async", r"(no)?atime", r"(no)?auto",
r"(fs|def|root)?context=\w+", r"(no)?dev", r"(no)?diratime",
@ -99,10 +103,14 @@ class WSLConfigurationBaseView(BaseView):
self.controller = controller
initial = {
'custom_path': configuration_data.custom_path,
'custom_mount_opt': configuration_data.custom_mount_opt,
'gen_host': configuration_data.gen_host,
'gen_resolvconf': configuration_data.gen_resolvconf,
'automount_root':
configuration_data.automount_root,
'automount_options':
configuration_data.automount_options,
'network_generatehosts':
configuration_data.network_generatehosts,
'network_generateresolvconf':
configuration_data.network_generateresolvconf,
}
self.form = WSLConfBaseForm(initial=initial)
@ -118,8 +126,12 @@ class WSLConfigurationBaseView(BaseView):
def done(self, result):
self.controller.done(WSLConfigurationBase(
custom_path=self.form.custom_path.value,
custom_mount_opt=self.form.custom_mount_opt.value,
gen_host=self.form.gen_host.value,
gen_resolvconf=self.form.gen_resolvconf.value
automount_root=self.form
.automount_root.value,
automount_options=self.form
.automount_options.value,
network_generatehosts=self.form
.network_generatehosts.value,
network_generateresolvconf=self.form
.network_generateresolvconf.value
))