0

Is there a way to add a string to the middle of a line?

Here is the present line:

GRUB_DEFAULT="root=/dev/sda"

Should be like this:

GRUB_DEFAULT="numa_balancing=disable root=/dev/sda"
phanaz
  • 1,188
  • 8
  • 17
Sash
  • 11
  • 3
  • 1
    You can do this using the [`regex_replace`](https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#searching-strings-with-regular-expressions) filter. – larsks Aug 29 '22 at 14:27
  • 1
    What I are the rules for your replacement? Could you offer some kind of pseudo-code? If the line is strictly `GRUB_DEFAULT="root=/dev/sda"`, then re-assign it to `GRUB_DEFAULT="numa_balancing=disable root=/dev/sda"`, with a simple [`replace`](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/replace_module.html). – β.εηοιτ.βε Aug 29 '22 at 14:41

2 Answers2

1
For example, given the file for testing
shell> cat /tmp/grub 
GRUB_DEFAULT="root=/dev/sda"
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=2
GRUB_CMDLINE_LINUX="quiet splash net.ifnames=1 biosdevname=0 ipv6.disable=1"

WARNING: The solution below works for space-delimited items only!

Read the file to registered variable out

    - command:
        cmd: "cat {{ grub_file }}"
      register: out

Use filter community.general.jc and parse the file. Put the declarations below as appropriate, for example, to the playbook vars

    grub_file: /tmp/grub
    grub_ini: "{{ out.stdout|community.general.jc('ini') }}"
    grub_keys: "{{ grub_ini.keys()|list }}"
    grub_vals: "{{ grub_ini.values()|map('split', ' ') }}"
    grub_orig: "{{ dict(grub_keys|zip(grub_vals)) }}"

gives

  grub_orig:
    grub_cmdline_linux:
    - quiet
    - splash
    - net.ifnames=1
    - biosdevname=0
    - ipv6.disable=1
    grub_default:
    - root=/dev/sda
    grub_timeout:
    - '2'
    grub_timeout_style:
    - hidden

Declare the changes you want to make. For example, create the below dictionary also in the playbook vars

    grub_update:
      GRUB_DEFAULT:
        - "numa_balancing=disable"

Now, the module lineinfile below iterates the keys of the dictionary grub_update, concatenates unique items, and updates the configuration lines

    - lineinfile:
        path: "{{ grub_file }}"
        regexp: '^{{ item }}.*$'
        line: '{{ item }}="{{ line }}"'
      loop: "{{ grub_update.keys()|list }}"
      vars:
        line: "{{ (grub_update[item] + grub_orig[item|lower]|d([]))|
                  unique|
                  join(' ') }}"

gives

shell> cat /tmp/grub 
GRUB_DEFAULT="numa_balancing=disable root=/dev/sda"
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=2
GRUB_CMDLINE_LINUX="quiet splash net.ifnames=1 biosdevname=0 ipv6.disable=1"

The task is idempotent.


Example of a complete playbook for testing

shell> cat pb.yml
- hosts: localhost

  vars:

    grub_file: /tmp/grub
    grub_ini: "{{ out.stdout|community.general.jc('ini') }}"
    grub_keys: "{{ grub_ini.keys()|list }}"
    grub_vals: "{{ grub_ini.values()|map('split', ' ') }}"
    grub_orig: "{{ dict(grub_keys|zip(grub_vals)) }}"

    grub_update:
      GRUB_DEFAULT:
        - "numa_balancing=disable"

  tasks:

    - command:
        cmd: "cat {{ grub_file }}"
      register: out

    - debug:
        var: grub_orig
      when: debug|d(false)|bool
    - debug:
        var: line
      loop: "{{ grub_update.keys()|list }}"
      vars:
        line: "{{ (grub_update[item] + grub_orig[item|lower]|d([]))|
                   unique|
                   join(' ') }}"
      when: debug|d(false)|bool

    - lineinfile:
        path: "{{ grub_file }}"
        regexp: '^{{ item }}.*$'
        line: '{{ item }}="{{ line }}"'
      loop: "{{ grub_update.keys()|list }}"
      vars:
        line: "{{ (grub_update[item] + grub_orig[item|lower]|d([]))|
                  unique|
                  join(' ') }}"

gives

shell> ansible-playbook pb.yml -e debug=true --diff

PLAY [localhost] *****************************************************************************

TASK [command] *******************************************************************************
changed: [localhost]

TASK [debug] *********************************************************************************
ok: [localhost] => 
  grub_orig:
    grub_cmdline_linux:
    - quiet
    - splash
    - net.ifnames=1
    - biosdevname=0
    - ipv6.disable=1
    grub_default:
    - root=/dev/sda
    grub_timeout:
    - '2'
    grub_timeout_style:
    - hidden

TASK [debug] *********************************************************************************
ok: [localhost] => (item=GRUB_DEFAULT) => 
  ansible_loop_var: item
  item: GRUB_DEFAULT
  line: numa_balancing=disable root=/dev/sda

TASK [lineinfile] ****************************************************************************
--- before: /tmp/grub (content)
+++ after: /tmp/grub (content)
@@ -1,4 +1,4 @@
-GRUB_DEFAULT="root=/dev/sda"
+GRUB_DEFAULT="numa_balancing=disable root=/dev/sda"
 GRUB_TIMEOUT_STYLE=hidden
 GRUB_TIMEOUT=2
 GRUB_CMDLINE_LINUX="quiet splash net.ifnames=1 biosdevname=0 ipv6.disable=1"

changed: [localhost] => (item=GRUB_DEFAULT)

PLAY RECAP ***********************************************************************************
localhost: ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
0

As mentioned within the comments, to change the configuration you could use an approach like

- name: Set grub default in /etc/sysconfig/grub
  replace:
    path: "/etc/sysconfig/grub"
    regexp: 'GRUB_DEFAULT="root=/dev/sda"'
    replace: 'GRUB_DEFAULT="numa_balancing=disable root=/dev/sda"'
    backup: true

Please take note that to reflect the change some more steps and a reboot may be necessary.

- name: Rebuild boot image to reflect the change
  shell: 
    cmd: grub2-mkconfig -o /boot/efi/EFI/{{ DISTRIBUTION }}/grub.cfg
  register: grub2_mkconfig

- debug: 
    msg: "{{ grub2_mkconfig.stderr_lines }}"
  when: not ansible_check_mode

Further Q&A

which shows the usage of Ansible for GRUB bootloader configuration.

U880D
  • 8,601
  • 6
  • 24
  • 40