Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ansible lineinfile duplication using insertafter

I am trying to add an entry into my /etc/hosts file using ansibles lineinfile. I want the logic to be if it finds the entry 127.0.0.1 mysite.local then do nothing otherwise insert it after the line 127.0.1.1

127.0.0.1   localhost
127.0.1.1   mypc
127.0.0.1      mysite.local

I have the insert after part working but it appears the actual regex search is failing to find the existing entry so I keep getting duplication of the insertion of 127.0.0.1 mysite.local

The docs do say;

When modifying a line the regexp should typically match both the initial state of the line as well as its state after replacement by line to ensure idempotence.

But I'm not sure how that applies to my regex. Currently my play is;

- name: Add the site to hosts
  lineinfile:
    path: /etc/hosts
    # Escape special chars
    regex: "^{{ domain|regex_escape() }}"
    line: "127.0.0.1      {{ domain }}"
    insertafter: '127\.0\.1\.1'
    firstmatch: yes
  become: yes

where domain is mysite.local.

I have looked at this answer but I'm pretty sure I cannot use backrefs since the docs state;

This flag changes the operation of the module slightly; insertbefore and insertafter will be ignored, and if the regexp doesn't match anywhere in the file, the file will be left unchanged.

I have tried;

regex: '127\.0\.0\.1\s+?{{ domain|regex_escape() }}'

With no luck either

like image 440
myol Avatar asked Nov 18 '18 05:11

myol


People also ask

Is Lineinfile Idempotent?

lineinfile is not idempotent #58923.

How do you modify a line in a file using Ansible?

To modify a line, you need to use the Ansible backrefs parameter along with the regexp parameter. This should be used with state=present. If the regexp does not match any line, then the file is not changed. If the regexp matches a line or multiple lines, then the last matched line will be replaced.


2 Answers

It seems that firstmatch: yes was breaking things. It work for me with following task (I replaced space with tab for fancy look but spaces work as well):

  - name: Add the site to hosts
    lineinfile:
      path: /etc/hosts
      # Escape special chars
      regexp: "{{ domain|regex_escape() }}"
      line: "127.0.0.1{{ '\t' }}{{ domain }}"
      insertafter: '127\.0\.1\.1'
like image 190
Vladimir Avatar answered Oct 23 '22 22:10

Vladimir


According to this link, lineinfile scans the file and applies the regex one line at a time, meaning you cannot use a regex that looks through the whole file. I am unfamiliar with the lineinfile tool, but if you can use the "replace" tool used in the link above then you can use the following Python regex to match as you need:

\A((?:(?!127\.0\.0\.1\s)[\s\S])*?)(?:\Z|127\.0\.0\.1\s+(?!{{ domain|regex_escape() }})\S+\n|(127\.0\.1\.1\s+\S+(?![\s\S]*\n127\.0\.0\.1\s)\n))

With the substitution: "\1\2127.0.0.1 {{ domain }}\n"

The non-capturing group handles three distinct cases:

  1. Case 1: 127.0.1.1 and 127.0.0.1 don't exist so insert at end
  2. Case 2: 127.0.0.1 exists with a different host so replace the entry
  3. Case 3: 127.0.1.1 exists so insert after it

It is the second case that tackles idempotence by avoiding matching an entry for "127.0.0.1" if one already exists.

like image 26
jaytea Avatar answered Oct 23 '22 23:10

jaytea