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()