0

How to update a line in /etc/default/grub using Ansible from ->

GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet" 

to

GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet console=ttyS0,1100"

I tried the following -

      - name: Updating of GRUB_CMDLINE_LINUX
        lineinfile:
          path: "/etc/default/grub"
          regexp: "^(.*GRUB_CMDLINE_LINUX.*)$"
          line: '\1 console=ttyS0,1100"'

but it just added console=ttyS0,1100" to the end of sentence leading to an extra " (from the existing sentence).

quiet" console=ttyS0,1100" 

Any help?

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
  • 2
    Please don't blindly apply tags, this has nothing to do with Python or Linux-specific APIs. – Ulrich Eckhardt Jan 02 '23 at 23:29
  • Just for the record, there is an other similar question about [How to configure GRUB2 Boot Loader Settings via Ansible](https://stackoverflow.com/questions/73530296/) with two answers. – U880D Jan 04 '23 at 08:21

2 Answers2

0

Put the data into a list. For example,

  grub_add:
    - {key: GRUB_CMDLINE_LINUX, value: 'console=ttyS0,1100'}

Given the file for testing

shell> cat grub
GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet"

The task below does the job

    - lineinfile:
        backrefs: true
        path: '{{ playbook_dir }}/grub'
        regexp: '^\s*{{ item.key }}\s*="(?!.*{{ item.value }})(.*)"$'
        line: '{{ item.key }}="\1 {{ item.value }}"'
      loop: "{{ grub_add }}"
  • You have to enable backrefs to reference the capture group in line.

  • The regexp will match if the value is not present. See the item 'negative lookahead assertion' below

^ ....................... the beginning of the string
\s* ..................... 0 or more whitespace
{{ item.key }} .......... the key
\s* ..................... 0 or more whitespace
=" ...................... it is what you think it is
(?!.*{{ item.value }}) .. negative lookahead assertion; matches if the value is not present
(.*)" ................... the capture group followed by the double-quote
$ ....................... the end of the string
  • The value will be appended to the line if not already present.

  • Use Single-Quoted Style. You don't have to escape the special characters.

Run the play with --check --diff options

TASK [lineinfile] ****************************************************************************
--- before: /export/scratch/tmp7/test-130/grub (content)
+++ after: /export/scratch/tmp7/test-130/grub (content)
@@ -1 +1 @@
-GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet"
+GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet console=ttyS0,1100"

changed: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'console=ttyS0,1100'})

The task is idempotent. There will be no changes when you repeat it.


Example of the project for testing

shell> tree .
.
├── ansible.cfg
├── grub
├── grub.orig
├── hosts
└── pb.yml
shell> cat grub
GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet"

Example of a complete playbook. Test adding all parameters to the list

shell> cat pb.yml
- hosts: localhost

  vars:

    grub_add:
      - {key: GRUB_CMDLINE_LINUX, value: 'audit=1'}
      - {key: GRUB_CMDLINE_LINUX, value: 'crashkernel=auto'}
      - {key: GRUB_CMDLINE_LINUX, value: 'rhgb'}
      - {key: GRUB_CMDLINE_LINUX, value: 'quiet'}
      - {key: GRUB_CMDLINE_LINUX, value: 'console=ttyS0,1100'}

  tasks:

    - lineinfile:
        backrefs: true
        path: '{{ playbook_dir }}/grub'
        regexp: '^\s*{{ item.key }}\s*="(?!.*{{ item.value }})(.*)"$'
        line: '{{ item.key }}="\1 {{ item.value }}"'
      loop: "{{ grub_add }}"

gives running with --check --diff options

shell> ansible-playbook pb.yml -CD

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

TASK [lineinfile] ****************************************************************************
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'audit=1'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'crashkernel=auto'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'rhgb'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'quiet'})
--- before: /export/scratch/tmp7/test-130/grub (content)
+++ after: /export/scratch/tmp7/test-130/grub (content)
@@ -1 +1 @@
-GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet"
+GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet console=ttyS0,1100"

changed: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'console=ttyS0,1100'})

PLAY RECAP ***********************************************************************************
localhost: ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

gives

shell> ansible-playbook pb.yml

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

TASK [lineinfile] ****************************************************************************
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'audit=1'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'crashkernel=auto'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'rhgb'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'quiet'})
changed: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'console=ttyS0,1100'})

PLAY RECAP ***********************************************************************************
localhost: ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

The result

shell> cat grub
GRUB_CMDLINE_LINUX="audit=1 crashkernel=auto rhgb quiet console=ttyS0,1100"

The playbook is idempotent

shell> ansible-playbook pb.yml

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

TASK [lineinfile] ****************************************************************************
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'audit=1'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'crashkernel=auto'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'rhgb'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'quiet'})
ok: [localhost] => (item={'key': 'GRUB_CMDLINE_LINUX', 'value': 'console=ttyS0,1100'})

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

In order to get at the commandline, use this regex to digest to the input:

^GRUB_CMDLINE_LINUX="(.*)"$

This now gives you the actual value in \1, so you can write the output as:

GRUB_CMDLINE_LINUX="\1 console=ttyS0,1100"

Of course, you have to escape the double quotes properly for the format of the file you have there.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55