summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormaxnet <bos@je-eigen-domein.nl>2022-01-12 15:37:32 +0100
committerGitHub <noreply@github.com>2022-01-12 08:37:32 -0600
commit5147e8d4764e368ab8ccea7433b02e4ff9d6c901 (patch)
treeb16949cebf383364cd26a8c2fef95bd11e4e2ca7
parent42b938e8ff4c50833ff7b8f5acc1d9ab3f43ab18 (diff)
downloadcloud-init-git-5147e8d4764e368ab8ccea7433b02e4ff9d6c901.tar.gz
Add new config module to set keyboard layout (#1176)
Adds a new module to allow setting keyboard layout, for use-cases in which cloud-init is used to configure OS images meant for physical computers instead of the cloud. This initial release only implements support for Linux distributions that allow layout to be set through systemd's localectl. LP: #1951593
-rw-r--r--cloudinit/config/cc_keyboard.py125
-rwxr-xr-xcloudinit/distros/__init__.py15
-rw-r--r--cloudinit/distros/debian.py7
-rw-r--r--config/cloud.cfg.tmpl1
-rw-r--r--doc/rtd/topics/modules.rst1
-rw-r--r--tests/integration_tests/modules/test_keyboard.py17
-rw-r--r--tests/unittests/config/test_schema.py1
-rw-r--r--tools/.github-cla-signers1
8 files changed, 168 insertions, 0 deletions
diff --git a/cloudinit/config/cc_keyboard.py b/cloudinit/config/cc_keyboard.py
new file mode 100644
index 00000000..17eb9a54
--- /dev/null
+++ b/cloudinit/config/cc_keyboard.py
@@ -0,0 +1,125 @@
+# Copyright (c) 2022 Floris Bos
+#
+# Author: Floris Bos <bos@je-eigen-domein.nl>
+#
+# This file is part of cloud-init. See LICENSE file for license information.
+
+"""keyboard: set keyboard layout"""
+
+from textwrap import dedent
+
+from cloudinit import distros
+from cloudinit import log as logging
+from cloudinit.config.schema import get_meta_doc, validate_cloudconfig_schema
+from cloudinit.settings import PER_INSTANCE
+
+frequency = PER_INSTANCE
+
+# FIXME: setting keyboard layout should be supported by all OSes.
+# But currently only implemented for Linux distributions that use systemd.
+osfamilies = ["arch", "debian", "redhat", "suse"]
+distros = distros.Distro.expand_osfamily(osfamilies)
+
+DEFAULT_KEYBOARD_MODEL = "pc105"
+
+meta = {
+ "id": "cc_keyboard",
+ "name": "Keyboard",
+ "title": "Set keyboard layout",
+ "description": dedent(
+ """\
+ Handle keyboard configuration.
+ """
+ ),
+ "distros": distros,
+ "examples": [
+ dedent(
+ """\
+ # Set keyboard layout to "us"
+ keyboard:
+ layout: us
+ """
+ ),
+ dedent(
+ """\
+ # Set specific keyboard layout, model, variant, options
+ keyboard:
+ layout: de
+ model: pc105
+ variant: nodeadkeys
+ options: compose:rwin
+ """
+ ),
+ ],
+ "frequency": frequency,
+}
+
+
+schema = {
+ "type": "object",
+ "properties": {
+ "keyboard": {
+ "type": "object",
+ "properties": {
+ "layout": {
+ "type": "string",
+ "description": dedent(
+ """\
+ Required. Keyboard layout. Corresponds to XKBLAYOUT.
+ """
+ ),
+ },
+ "model": {
+ "type": "string",
+ "default": DEFAULT_KEYBOARD_MODEL,
+ "description": dedent(
+ """\
+ Optional. Keyboard model. Corresponds to XKBMODEL.
+ """
+ ),
+ },
+ "variant": {
+ "type": "string",
+ "description": dedent(
+ """\
+ Optional. Keyboard variant. Corresponds to XKBVARIANT.
+ """
+ ),
+ },
+ "options": {
+ "type": "string",
+ "description": dedent(
+ """\
+ Optional. Keyboard options. Corresponds to XKBOPTIONS.
+ """
+ ),
+ },
+ },
+ "required": ["layout"],
+ "additionalProperties": False,
+ }
+ },
+}
+
+__doc__ = get_meta_doc(meta, schema)
+
+LOG = logging.getLogger(__name__)
+
+
+def handle(name, cfg, cloud, log, args):
+ if "keyboard" not in cfg:
+ LOG.debug(
+ "Skipping module named %s, no 'keyboard' section found", name
+ )
+ return
+ validate_cloudconfig_schema(cfg, schema)
+ kb_cfg = cfg["keyboard"]
+ layout = kb_cfg["layout"]
+ model = kb_cfg.get("model", DEFAULT_KEYBOARD_MODEL)
+ variant = kb_cfg.get("variant", "")
+ options = kb_cfg.get("options", "")
+ LOG.debug("Setting keyboard layout to '%s'", layout)
+ cloud.distro.set_keymap(layout, model, variant, options)
+
+
+# vi: ts=4 expandtab
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index de000b52..a261c16e 100755
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -881,6 +881,21 @@ class Distro(persistence.CloudInitPickleMixin, metaclass=abc.ABCMeta):
cmd = list(init_cmd) + list(cmds[action])
return subp.subp(cmd, capture=True)
+ def set_keymap(self, layout, model, variant, options):
+ if self.uses_systemd():
+ subp.subp(
+ [
+ "localectl",
+ "set-x11-keymap",
+ layout,
+ model,
+ variant,
+ options,
+ ]
+ )
+ else:
+ raise NotImplementedError()
+
def _apply_hostname_transformations_to_url(url: str, transformations: list):
"""
diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py
index 9effa0a0..6dc1ad40 100644
--- a/cloudinit/distros/debian.py
+++ b/cloudinit/distros/debian.py
@@ -300,6 +300,13 @@ class Distro(distros.Distro):
def get_primary_arch(self):
return util.get_dpkg_architecture()
+ def set_keymap(self, layout, model, variant, options):
+ # Let localectl take care of updating /etc/default/keyboard
+ distros.Distro.set_keymap(self, layout, model, variant, options)
+ # Workaround for localectl not applying new settings instantly
+ # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=926037
+ self.manage_service("restart", "console-setup")
+
def _get_wrapper_prefix(cmd, mode):
if isinstance(cmd, str):
diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl
index 741b23d5..37c223f3 100644
--- a/config/cloud.cfg.tmpl
+++ b/config/cloud.cfg.tmpl
@@ -107,6 +107,7 @@ cloud_config_modules:
{% endif %}
{% if variant not in ["photon"] %}
- ssh-import-id
+ - keyboard
- locale
{% endif %}
- set-passwords
diff --git a/doc/rtd/topics/modules.rst b/doc/rtd/topics/modules.rst
index 3ca6b9e3..8ee4fe57 100644
--- a/doc/rtd/topics/modules.rst
+++ b/doc/rtd/topics/modules.rst
@@ -23,6 +23,7 @@ Modules
.. automodule:: cloudinit.config.cc_growpart
.. automodule:: cloudinit.config.cc_grub_dpkg
.. automodule:: cloudinit.config.cc_install_hotplug
+.. automodule:: cloudinit.config.cc_keyboard
.. automodule:: cloudinit.config.cc_keys_to_console
.. automodule:: cloudinit.config.cc_landscape
.. automodule:: cloudinit.config.cc_locale
diff --git a/tests/integration_tests/modules/test_keyboard.py b/tests/integration_tests/modules/test_keyboard.py
new file mode 100644
index 00000000..7db35014
--- /dev/null
+++ b/tests/integration_tests/modules/test_keyboard.py
@@ -0,0 +1,17 @@
+import pytest
+
+USER_DATA = """\
+#cloud-config
+keyboard:
+ layout: de
+ model: pc105
+ variant: nodeadkeys
+ options: compose:rwin
+"""
+
+
+class TestKeyboard:
+ @pytest.mark.user_data(USER_DATA)
+ def test_keyboard(self, client):
+ lc = client.execute("localectl")
+ assert "X11 Layout: de" in lc
diff --git a/tests/unittests/config/test_schema.py b/tests/unittests/config/test_schema.py
index 822efe5a..93206bdd 100644
--- a/tests/unittests/config/test_schema.py
+++ b/tests/unittests/config/test_schema.py
@@ -87,6 +87,7 @@ class GetSchemaTest(CiTestCase):
"cc_apk_configure",
"cc_apt_configure",
"cc_bootcmd",
+ "cc_keyboard",
"cc_locale",
"cc_ntp",
"cc_resizefs",
diff --git a/tools/.github-cla-signers b/tools/.github-cla-signers
index e4b64b0d..62e6017a 100644
--- a/tools/.github-cla-signers
+++ b/tools/.github-cla-signers
@@ -52,6 +52,7 @@ mamercad
manuelisimo
marlluslustosa
matthewruffell
+maxnet
mitechie
nazunalika
nicolasbock