简体   繁体   中英

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

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.

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'

The doc says:

insertafter: ... If regular expressions are passed to both regexp and insertafter, insertafter is only honored if no match for regexp is found.

The regex in the task expands to

regex: ^mysite\.local

This regex is not found because there is no line that begins with "mysite.local". Hence insertafter is honored and "line" is inserted after 127.0.1.1 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM