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
lineinfile is not idempotent #58923.
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.
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'
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:
It is the second case that tackles idempotence by avoiding matching an entry for "127.0.0.1" if one already exists.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With