VladoPortos
VladoPortos

Reputation: 603

Validate line with multiple config variables

I'm learning ansible and can't figure this out in simple terms with asnible.

I have a file that contains:

net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_memory=1 cgroup_enable=memory

This is one line and I would for example want to check if cgroup_memory is set to 1, if not set it to 1 and if the whole setting is missing append it to end of the line.

I know perfectly simple method how to do it with bash and sed, but I can't wrap my head around ansible "correct" way, without using lineinfile and replacing the whole line... as mentioned lineinfile is working with whole line so that would not be ideal I think.. next module I looked into is replace that could possibly work but I can't just look for "cgroup_memory=1" as it would look at "cgroup_memory=0" and evaluated as thats not "cgroup_memory=1" and I end up with cgroup_memory=1 cgroup_memory=0 in a file... somehow I need to extract the value, compare and replace the value ( keeping the whole line intact ) and if its not there append to the end of the line...

Here is bash version I want in ansible: https://pastebin.com/1TCz30Nq

This is where I got closest to what I need:

- name: "Check for cgroup_memory"
  shell: grep -v '^[[:space:]]*#' /tmp/cmdline.txt_old | grep -icP "cgroup_memory=-?\d+[[:space:]]" || true
  register: grep_value
  changed_when: False

- debug:
    var: grep_value

- name: "Variable Missing: Add cgroup_memory=1 to end of line"
  become: true
  replace: dest=/tmp/cmdline.txt_old regexp='(\s+)$' replace=' cgroup_memory=1'
  when: grep_value.stdout == "0"

- name: "Variable Exist: Set cgroup_memory=1"
  become: true
  replace: dest=/tmp/cmdline.txt_old regexp='cgroup_memory=\d+' replace='cgroup_memory=1'
  when: grep_value.stdout == "1"

How ever this does not ignore # lines so the replace will happily change the values in comments which is bad. Also this does not evaluate the value but I guess I could grep and split further after determining that its there.. I don't want to complain but ansible was hyped to me as perfect for configuration management and the first simple thing that I do a lot via shell scripts is not that simple... ( if I don't want to use shell module )

Upvotes: 0

Views: 321

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68144

The best option, IMHO, is blockinfile. Given the configuration file below

shell> cat test.cfg
line1
line2
net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_memory=1 cgroup_enable=memory
line4
line5

Mark the line first. blockinfile needs the markers to find the block (a line in this case). For example

    - lineinfile:
        path: test.cfg
        insertbefore: "^net.ifnames(.*)$"
        line: "# BEGIN ANSIBLE MANAGED BLOCK my_block_01"
    - lineinfile:
        path: test.cfg
        insertafter: "^net.ifnames(.*)$"
        line: "# END ANSIBLE MANAGED BLOCK my_block_01"

gives

shell> cat test.cfg
line1
line2
# BEGIN ANSIBLE MANAGED BLOCK my_block_01
net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait=fixrtc cgroup_memory=1 cgroup_enable=memory 
# END ANSIBLE MANAGED BLOCK my_block_01
line4
line5

The lineinfile tasks above are idempotent. Then create a variable with the configuration. For example

shell> cat net_cfg.yml 
net_cfg_01:
  - {key: 'net.ifnames', val: 0}
  - {key: 'dwc_otg.lpm_enable', val: 0}
  - {key: 'console', val: 'serial0,115200'}
  - {key: 'console', val: 'tty1'}
  - {key: 'root', val: 'LABEL=writable'}
  - {key: 'rootfstype', val: 'ext4'}
  - {key: 'elevator', val: 'deadline'}
  - {key: 'rootwait', val: 'fixrtc'}
  - {key: 'cgroup_memory', val: 1}
  - {key: 'cgroup_enable', val: 'memory'}

A list, instead of a simpler dictionary, must be used because the keys are not unique. Create a template. For example

shell> cat templates/net_cfg_01.j2
{% for i in net_cfg_01 %}{{i.key}}={{i.val}} {% endfor %}

Now you can include the variable and configure the line in the file

    - include_vars: net_cfg.yml
    - blockinfile:
        path: test.cfg
        block: "{{ lookup('template', 'net_cfg_01.j2') }}"
        marker: "# {mark} ANSIBLE MANAGED BLOCK my_block_01"

Notes

  • Fit the marker to your needs and to the comment format of the file
  • The template leaves an empty space before the end of the line. Use loop.last to fix it if necessary
  • All tasks are idempotent

How to use it

There are many options on how to use the above framework. For example, put configuration variables into the list

shell> cat net_cfg.yml
net_cfg_01:
  ... 
  - {key: 'cgroup_memory', val: {{ my_cgroup_memory }}}

Then the command below will make sure the file is configured properly

shell> ansible-playbook playbook.yml -e 'my_cgroup_memory=1'

Upvotes: 1

seshadri_c
seshadri_c

Reputation: 7340

One way to ensure that cgroup_memory will be set to 1 is to use the Ansible replace module. The replace module can match a regexp and replace it with what we want.

Example:

  tasks:
  - name: set cgroup_memory to 1
    replace:
      path: /path/to/file
      regexp: 'cgroup_memory=\d+'
      replace: 'cgroup_memory=1'

If there are more than one lines having this expression, this will replace all occurrences. We can use before or after parameters in the above task to target specific ones.

Or we can ensure that an entire line is present using the lineinfile module.

  tasks:
  - name: ensure line with cgroup_memory is present in file
    lineinfile:
      path: /path/to/file
      regexp: 'cgroup_memory=\d+'
      line: "net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_memory=1 cgroup_enable=memory"

Upvotes: 1

Related Questions