From 45abc5ac700b259fca06c53dd157faa22d4615f6 Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Mon, 20 Jul 2026 08:26:56 -0600 Subject: [PATCH] feat: Support extra dracut settings for static IP Feature: Introduce the nbde_client_extra_dracut_settings variable. This is a list of strings written to /etc/dracut.conf.d/nbde_client.conf in addition to the implicit platform-specific __nbde_client_dracut_settings. Reason: NBDE clients with static IP addressing need custom early-boot network settings in dracut (for example kernel_cmdline with a static ip= and nameserver). Previously the role only wrote fixed platform settings, so users had to disable early boot or manage dracut config outside the role. Result: Users can pass extra dracut configuration lines via nbde_client_extra_dracut_settings (documented in README.md) while keeping the role's implicit settings. A new integration test verifies the static IP kernel_cmdline example is written to the conf file. Assisted-by: Cursor using models Cursor Grok 4.5, Composer 2.5, Sonnet 5 Signed-off-by: Rich Megginson --- README.md | 21 ++++- defaults/main.yml | 6 ++ tasks/main-clevis.yml | 1 + tasks/main.yml | 11 +++ templates/nbde_client.conf | 2 +- tests/tests_default_vars.yml | 1 + tests/tests_extra_dracut_settings.yml | 111 ++++++++++++++++++++++++++ 7 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 tests/tests_extra_dracut_settings.yml diff --git a/README.md b/README.md index f882bd0b..ad3e5621 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,9 @@ These are the variables that can be passed to the role: |----------|-------------|------| | `nbde_client_provider` | `clevis`| identifies the provider for the `nbde_client` role. We currently support `clevis`.| | `nbde_client_bindings` | | a list containing binding configurations, which include e.g. devices and slots. | -| `nbde_client_early_boot` | `true` | by default nbde_client will configure the initrd to unlock the volume. This may need to be disabled if the managed host is using static IP addressing, or if the volume should be unlocked by clevis-luks-askpass | +| `nbde_client_early_boot` | `true` | by default nbde_client will configure the initrd to unlock the volume. This may need to be disabled if the volume should be unlocked by clevis-luks-askpass. For managed hosts with static IP addressing, prefer `nbde_client_extra_dracut_settings` instead of disabling early boot. | | `nbde_client_secure_logging` | `true` | If true, suppress potentially sensitive output from tasks that handle credentials, secrets, and other sensitive data. Set to false for debugging issues with credential handling or secret management, but be aware this may expose sensitive information in logs. | +| `nbde_client_extra_dracut_settings` | `[]` | a list of extra dracut configuration lines written to `/etc/dracut.conf.d/nbde_client.conf` in addition to the role's implicit platform settings. Use this for static IP early-boot networking and other custom dracut options. Requires `nbde_client_early_boot: true` (the default); the role fails if early boot is disabled. | ### nbde_client_bindings @@ -57,6 +58,24 @@ nbde_client_bindings: - http://server2.example.com ``` +### nbde_client_extra_dracut_settings + +`nbde_client_extra_dracut_settings` is a list of strings. Each string is written +as a line in `/etc/dracut.conf.d/nbde_client.conf`, after the implicit +platform-specific settings from `__nbde_client_dracut_settings`. + +This is useful for NBDE clients that use static IP addressing and need network +configuration available during early boot. For example: + +```yaml +nbde_client_extra_dracut_settings: + - kernel_cmdline+=" ip=192.0.2.10::192.0.2.1:255.255.255.0::ens3:none nameserver=192.0.2.100 " +``` + +**Note:** You cannot set `nbde_client_early_boot: false` when using +`nbde_client_extra_dracut_settings`. Extra dracut settings are only written when +early boot is enabled. The role fails if both are set this way. + ## Example Playbooks ### Example 1: high availability diff --git a/defaults/main.yml b/defaults/main.yml index 220496dc..8b7040a7 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -23,4 +23,10 @@ nbde_client_early_boot: true nbde_client_bindings: [] nbde_client_secure_logging: true +# Extra lines written to /etc/dracut.conf.d/nbde_client.conf in addition to +# the platform-specific __nbde_client_dracut_settings. Each list item is a +# full dracut configuration line, for example: +# - kernel_cmdline+=" ip=192.0.2.10::192.0.2.1:255.255.255.0::ens3:none nameserver=192.0.2.100 " +nbde_client_extra_dracut_settings: [] + # vim:set ts=2 sw=2 et: diff --git a/tasks/main-clevis.yml b/tasks/main-clevis.yml index 2dc6a5e1..196cdab2 100644 --- a/tasks/main-clevis.yml +++ b/tasks/main-clevis.yml @@ -22,6 +22,7 @@ owner: root mode: '0444' when: nbde_client_early_boot | bool + notify: Handle nbde_client update initramfs - name: Check whether devices are at the desired state when: diff --git a/tasks/main.yml b/tasks/main.yml index e6462d91..79fa9da2 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,5 +1,16 @@ # SPDX-License-Identifier: MIT --- +- name: Fail when extra dracut settings are set but early boot is disabled + fail: + msg: >- + nbde_client_extra_dracut_settings requires nbde_client_early_boot + to be true. nbde_client_extra_dracut_settings has + {{ nbde_client_extra_dracut_settings | length }} entries, but + nbde_client_early_boot is false so they will not be written. + when: + - not nbde_client_early_boot + - nbde_client_extra_dracut_settings | length > 0 + # Set up internal variables. - name: Set version specific variables include_tasks: set_vars.yml diff --git a/templates/nbde_client.conf b/templates/nbde_client.conf index 620ca835..6c3b1e51 100644 --- a/templates/nbde_client.conf +++ b/templates/nbde_client.conf @@ -1,6 +1,6 @@ # nbde_client dracut config {{ ansible_managed | comment }} {{ "system_role:nbde_client" | comment(prefix="", postfix="") }} -{% for line in __nbde_client_dracut_settings %} +{% for line in __nbde_client_dracut_settings + nbde_client_extra_dracut_settings %} {{ line }} {% endfor %} diff --git a/tests/tests_default_vars.yml b/tests/tests_default_vars.yml index e1a4ed45..5a63a881 100644 --- a/tests/tests_default_vars.yml +++ b/tests/tests_default_vars.yml @@ -14,5 +14,6 @@ that: - nbde_client_provider is defined - nbde_client_bindings is defined + - nbde_client_extra_dracut_settings is defined # vim:set ts=2 sw=2 et: diff --git a/tests/tests_extra_dracut_settings.yml b/tests/tests_extra_dracut_settings.yml new file mode 100644 index 00000000..b10506d7 --- /dev/null +++ b/tests/tests_extra_dracut_settings.yml @@ -0,0 +1,111 @@ +# Generated by: Cursor using models Cursor Grok 4.5, Composer 2.5, Sonnet 5 +--- +- name: Test nbde_client_extra_dracut_settings + hosts: all + vars: + __nbde_client_test_extra_dracut_line: >- + kernel_cmdline+=" ip=192.0.2.10::192.0.2.1:255.255.255.0::ens3:none nameserver=192.0.2.100 " + nbde_client_extra_dracut_settings: + - "{{ __nbde_client_test_extra_dracut_line }}" + + tasks: + - name: Run the test + block: + - name: Use nbde_client role + include_tasks: tasks/run_role_with_clear_facts.yml + vars: + __sr_public: true + + - name: Read generated dracut config + slurp: + src: /etc/dracut.conf.d/nbde_client.conf + register: __nbde_client_dracut_conf + + - name: Decode dracut config content + set_fact: + __nbde_client_dracut_conf_content: "{{ + __nbde_client_dracut_conf.content | b64decode }}" + + - name: Verify extra dracut setting is present + assert: + that: + - __nbde_client_test_extra_dracut_line in + __nbde_client_dracut_conf_content + fail_msg: >- + Expected /etc/dracut.conf.d/nbde_client.conf to contain + {{ __nbde_client_test_extra_dracut_line }}. Content was: + {{ __nbde_client_dracut_conf_content }} + + - name: Verify implicit dracut settings are present + assert: + that: + - item in __nbde_client_dracut_conf_content + fail_msg: >- + Expected /etc/dracut.conf.d/nbde_client.conf to contain + implicit setting {{ item }}. Content was: + {{ __nbde_client_dracut_conf_content }} + loop: "{{ __nbde_client_dracut_settings }}" + + - name: Use nbde_client role again for idempotency + include_tasks: tasks/run_role_with_clear_facts.yml + vars: + __sr_public: true + + - name: Re-read generated dracut config + slurp: + src: /etc/dracut.conf.d/nbde_client.conf + register: __nbde_client_dracut_conf_again + + - name: Verify dracut config is unchanged after second run + assert: + that: + - __nbde_client_dracut_conf.content == + __nbde_client_dracut_conf_again.content + fail_msg: Dracut config changed on second role run + + - name: Run the role with early boot disabled + vars: + nbde_client_early_boot: false + nbde_client_extra_dracut_settings: + - >- + kernel_cmdline+=" ip=192.0.2.10::192.0.2.1:255.255.255.0::ens3:none + nameserver=192.0.2.100 " + block: + - name: Use nbde_client role with conflicting settings + include_tasks: tasks/run_role_with_clear_facts.yml + + - name: Fail if the role did not reject conflicting settings + fail: + msg: >- + Role should fail when nbde_client_early_boot is false and + nbde_client_extra_dracut_settings is set + + rescue: + - name: Assert the early boot conflict error message + assert: + that: + - >- + 'nbde_client_extra_dracut_settings requires + nbde_client_early_boot' in ansible_failed_result.msg + fail_msg: >- + Unexpected failure: + {{ ansible_failed_result.msg | default(ansible_failed_result) }} + + always: + - name: Clean up generated dracut config + file: + path: /etc/dracut.conf.d/nbde_client.conf + state: absent + tags: + - tests::cleanup + + - name: Run the role with no settings to clean up + include_tasks: tasks/run_role_with_clear_facts.yml + vars: + nbde_client_extra_dracut_settings: [] + nbde_client_early_boot: true + nbde_client_bindings: [] + tags: + - tests::cleanup + +# vim:set ts=2 sw=2 et: