From 697949c1d2645e5bc36baf89ee8dc0de44097dde Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 16 Mar 2021 12:49:03 +1300 Subject: [PATCH] move code aroud now only the server calls set_keyboard --- po/POTFILES.in | 3 +- subiquity/common/keyboard.py | 83 ------------------- subiquity/models/keyboard.py | 57 +++++++++++-- subiquity/server/controllers/keyboard.py | 25 ++++-- .../controllers}/tests/test_keyboard.py | 22 +++-- 5 files changed, 87 insertions(+), 103 deletions(-) delete mode 100644 subiquity/common/keyboard.py rename subiquity/{common => server/controllers}/tests/test_keyboard.py (74%) diff --git a/po/POTFILES.in b/po/POTFILES.in index 2e736f61..fdb59f17 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -34,11 +34,9 @@ subiquity/common/api/tests/test_server.py subiquity/common/errorreport.py subiquity/common/filesystem.py subiquity/common/__init__.py -subiquity/common/keyboard.py subiquity/common/serialize.py subiquity/common/tests/__init__.py subiquity/common/tests/test_filesystem.py -subiquity/common/tests/test_keyboard.py subiquity/common/tests/test_serialization.py subiquity/common/types.py subiquitycore/async_helpers.py @@ -132,6 +130,7 @@ subiquity/server/controllers/refresh.py subiquity/server/controllers/reporting.py subiquity/server/controllers/snaplist.py subiquity/server/controllers/ssh.py +subiquity/server/controllers/tests/test_keyboard.py subiquity/server/controllers/userdata.py subiquity/server/controllers/zdev.py subiquity/server/dryrun.py diff --git a/subiquity/common/keyboard.py b/subiquity/common/keyboard.py deleted file mode 100644 index 3a934699..00000000 --- a/subiquity/common/keyboard.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2020 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 . - -import os -import re - -from subiquitycore.utils import arun_command - -from subiquity.common.types import KeyboardSetting - - -etc_default_keyboard_template = """\ -# KEYBOARD CONFIGURATION FILE - -# Consult the keyboard(5) manual page. - -XKBMODEL="pc105" -XKBLAYOUT="{layout}" -XKBVARIANT="{variant}" -XKBOPTIONS="{options}" - -BACKSPACE="guess" -""" - - -def from_config_file(config_file): - with open(config_file) as fp: - content = fp.read() - - def optval(opt, default): - match = re.search(r'(?m)^\s*%s=(.*)$' % (opt,), content) - if match: - r = match.group(1).strip('"') - if r != '': - return r - return default - - XKBLAYOUT = optval("XKBLAYOUT", "us") - XKBVARIANT = optval("XKBVARIANT", "") - XKBOPTIONS = optval("XKBOPTIONS", "") - toggle = None - for option in XKBOPTIONS.split(','): - if option.startswith('grp:'): - toggle = option[4:] - return KeyboardSetting(layout=XKBLAYOUT, variant=XKBVARIANT, toggle=toggle) - - -def render(setting): - options = "" - if setting.toggle: - options = "grp:" + setting.toggle - return etc_default_keyboard_template.format( - layout=setting.layout, - variant=setting.variant, - options=options) - - -async def set_keyboard(root, setting, dry_run): - path = os.path.join(root, 'etc', 'default', 'keyboard') - os.makedirs(os.path.dirname(path), exist_ok=True) - with open(path, 'w') as fp: - fp.write(render(setting)) - cmds = [ - ['setupcon', '--save', '--force', '--keyboard-only'], - ['/snap/bin/subiquity.subiquity-loadkeys'], - ] - if dry_run: - scale = os.environ.get('SUBIQUITY_REPLAY_TIMESCALE', "1") - cmds = [['sleep', str(1/float(scale))]] - for cmd in cmds: - await arun_command(cmd) diff --git a/subiquity/models/keyboard.py b/subiquity/models/keyboard.py index 8330bc3a..cdfade80 100644 --- a/subiquity/models/keyboard.py +++ b/subiquity/models/keyboard.py @@ -14,30 +14,75 @@ # along with this program. If not, see . import logging +import re import os -from subiquity.common.keyboard import from_config_file, render from subiquity.common.types import KeyboardSetting log = logging.getLogger("subiquity.models.keyboard") +etc_default_keyboard_template = """\ +# KEYBOARD CONFIGURATION FILE + +# Consult the keyboard(5) manual page. + +XKBMODEL="pc105" +XKBLAYOUT="{layout}" +XKBVARIANT="{variant}" +XKBOPTIONS="{options}" + +BACKSPACE="guess" +""" + + +def from_config_file(config_file): + with open(config_file) as fp: + content = fp.read() + + def optval(opt, default): + match = re.search(r'(?m)^\s*%s=(.*)$' % (opt,), content) + if match: + r = match.group(1).strip('"') + if r != '': + return r + return default + + XKBLAYOUT = optval("XKBLAYOUT", "us") + XKBVARIANT = optval("XKBVARIANT", "") + XKBOPTIONS = optval("XKBOPTIONS", "") + toggle = None + for option in XKBOPTIONS.split(','): + if option.startswith('grp:'): + toggle = option[4:] + return KeyboardSetting(layout=XKBLAYOUT, variant=XKBVARIANT, toggle=toggle) + + class KeyboardModel: def __init__(self, root): - self.root = root - config_path = os.path.join(self.root, 'etc', 'default', 'keyboard') - if os.path.exists(config_path): - self.setting = from_config_file(config_path) + self.config_path = os.path.join( + root, 'etc', 'default', 'keyboard') + if os.path.exists(self.config_path): + self.setting = from_config_file(self.config_path) else: self.setting = KeyboardSetting(layout='us') + def render_config_file(self): + options = "" + if self.setting.toggle: + options = "grp:" + self.setting.toggle + return etc_default_keyboard_template.format( + layout=self.setting.layout, + variant=self.setting.variant, + options=options) + def render(self): return { 'write_files': { 'etc_default_keyboard': { 'path': 'etc/default/keyboard', - 'content': render(self.setting), + 'content': self.render_config_file(), 'permissions': 0o644, }, }, diff --git a/subiquity/server/controllers/keyboard.py b/subiquity/server/controllers/keyboard.py index 2988514c..14652510 100644 --- a/subiquity/server/controllers/keyboard.py +++ b/subiquity/server/controllers/keyboard.py @@ -20,9 +20,9 @@ import os import attr from subiquitycore.context import with_context +from subiquitycore.utils import arun_command from subiquity.common.apidef import API -from subiquity.common.keyboard import set_keyboard from subiquity.common.serialize import Serializer from subiquity.common.types import ( AnyStep, @@ -146,12 +146,26 @@ class KeyboardController(SubiquityController): @with_context() async def apply_autoinstall_config(self, context): if self.needs_set_keyboard: - await set_keyboard( - self.app.root, self.model.setting, self.opts.dry_run) + await self.set_keyboard() def make_autoinstall(self): return attr.asdict(self.model.setting) + async def set_keyboard(self): + path = self.model.config_path + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, 'w') as fp: + fp.write(self.model.render_config_file()) + cmds = [ + ['setupcon', '--save', '--force', '--keyboard-only'], + ['/snap/bin/subiquity.subiquity-loadkeys'], + ] + if self.opts.dry_run: + scale = os.environ.get('SUBIQUITY_REPLAY_TIMESCALE', "1") + cmds = [['sleep', str(1/float(scale))]] + for cmd in cmds: + await arun_command(cmd) + async def GET(self) -> KeyboardSetting: return for_ui(self.model.setting) @@ -160,9 +174,8 @@ class KeyboardController(SubiquityController): if new is not None: data = KeyboardSetting(new[0], new[1], data.toggle) if data != self.model.setting: - await set_keyboard( - self.app.root, data, self.opts.dry_run) - self.model.setting = data + self.model.setting = data + await self.set_keyboard() self.configured() async def needs_toggle_GET(self, layout_code: str, diff --git a/subiquity/common/tests/test_keyboard.py b/subiquity/server/controllers/tests/test_keyboard.py similarity index 74% rename from subiquity/common/tests/test_keyboard.py rename to subiquity/server/controllers/tests/test_keyboard.py index e312ab39..3bf56763 100644 --- a/subiquity/common/tests/test_keyboard.py +++ b/subiquity/server/controllers/tests/test_keyboard.py @@ -18,13 +18,19 @@ import os import tempfile import unittest -from subiquity.common.keyboard import ( - from_config_file, - set_keyboard, +from subiquity.models.keyboard import ( + KeyboardModel, + ) +from subiquity.server.controllers.keyboard import ( + KeyboardController, ) from subiquity.common.types import KeyboardSetting +class opts: + dry_run = True + + class TestSubiquityModel(unittest.TestCase): def test_write_config(self): @@ -38,9 +44,13 @@ class TestSubiquityModel(unittest.TestCase): os.environ['SUBIQUITY_REPLAY_TIMESCALE'] = '100' with tempfile.TemporaryDirectory() as tmpdir: new_setting = KeyboardSetting('fr', 'azerty') - await set_keyboard(tmpdir, new_setting, True) - read_setting = from_config_file( - os.path.join(tmpdir, 'etc', 'default', 'keyboard')) + model = KeyboardModel(tmpdir) + model.setting = new_setting + c = object.__new__(KeyboardController) + c.opts = opts + c.model = model + await c.set_keyboard() + read_setting = KeyboardModel(tmpdir).setting self.assertEqual(new_setting, read_setting) loop.run_until_complete(t()) loop.close()