a )g]@sddlmZmZmZeZdZdZdZddl Z ddl Z ddl Z ddl m Z ddlmZmZmZdd Zd d Zd d ZddZddZedkredS))absolute_importdivisionprint_functiona --- module: lineinfile short_description: Manage lines in text files description: - This module ensures a particular line is in a file, or replace an existing line using a back-referenced regular expression. - This is primarily useful when you want to change a single line in a file only. - See the M(ansible.builtin.replace) module if you want to change multiple, similar lines or check M(ansible.builtin.blockinfile) if you want to insert/update/remove a block of lines in a file. For other cases, see the M(ansible.builtin.copy) or M(ansible.builtin.template) modules. version_added: "0.7" options: path: description: - The file to modify. - Before Ansible 2.3 this option was only usable as I(dest), I(destfile) and I(name). type: path required: true aliases: [ dest, destfile, name ] regexp: description: - The regular expression to look for in every line of the file. - For C(state=present), the pattern to replace if found. Only the last line found will be replaced. - For C(state=absent), the pattern of the line(s) to remove. - If the regular expression is not matched, the line will be added to the file in keeping with C(insertbefore) or C(insertafter) settings. - When modifying a line the regexp should typically match both the initial state of the line as well as its state after replacement by C(line) to ensure idempotence. - Uses Python regular expressions. See U(https://docs.python.org/3/library/re.html). type: str aliases: [ regex ] version_added: '1.7' search_string: description: - The literal string to look for in every line of the file. This does not have to match the entire line. - For C(state=present), the line to replace if the string is found in the file. Only the last line found will be replaced. - For C(state=absent), the line(s) to remove if the string is in the line. - If the literal expression is not matched, the line will be added to the file in keeping with C(insertbefore) or C(insertafter) settings. - Mutually exclusive with C(backrefs) and C(regexp). type: str version_added: '2.11' state: description: - Whether the line should be there or not. type: str choices: [ absent, present ] default: present line: description: - The line to insert/replace into the file. - Required for C(state=present). - If C(backrefs) is set, may contain backreferences that will get expanded with the C(regexp) capture groups if the regexp matches. type: str aliases: [ value ] backrefs: description: - Used with C(state=present). - If set, C(line) can contain backreferences (both positional and named) that will get populated if the C(regexp) matches. - This parameter changes the operation of the module slightly; C(insertbefore) and C(insertafter) will be ignored, and if the C(regexp) does not match anywhere in the file, the file will be left unchanged. - If the C(regexp) does match, the last matching line will be replaced by the expanded line parameter. - Mutually exclusive with C(search_string). type: bool default: no version_added: "1.1" insertafter: description: - Used with C(state=present). - If specified, the line will be inserted after the last match of specified regular expression. - If the first match is required, use(firstmatch=yes). - A special value is available; C(EOF) for inserting the line at the end of the file. - If specified regular expression has no matches, EOF will be used instead. - If C(insertbefore) is set, default value C(EOF) will be ignored. - If regular expressions are passed to both C(regexp) and C(insertafter), C(insertafter) is only honored if no match for C(regexp) is found. - May not be used with C(backrefs) or C(insertbefore). type: str choices: [ EOF, '*regex*' ] default: EOF insertbefore: description: - Used with C(state=present). - If specified, the line will be inserted before the last match of specified regular expression. - If the first match is required, use C(firstmatch=yes). - A value is available; C(BOF) for inserting the line at the beginning of the file. - If specified regular expression has no matches, the line will be inserted at the end of the file. - If regular expressions are passed to both C(regexp) and C(insertbefore), C(insertbefore) is only honored if no match for C(regexp) is found. - May not be used with C(backrefs) or C(insertafter). type: str choices: [ BOF, '*regex*' ] version_added: "1.1" create: description: - Used with C(state=present). - If specified, the file will be created if it does not already exist. - By default it will fail if the file is missing. type: bool default: no backup: description: - Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it incorrectly. type: bool default: no firstmatch: description: - Used with C(insertafter) or C(insertbefore). - If set, C(insertafter) and C(insertbefore) will work with the first line that matches the given regular expression. type: bool default: no version_added: "2.5" others: description: - All arguments accepted by the M(ansible.builtin.file) module also work here. type: str extends_documentation_fragment: - action_common_attributes - action_common_attributes.files - files - validate attributes: check_mode: support: full diff_mode: support: full platform: platforms: posix safe_file_operations: support: full vault: support: none notes: - As of Ansible 2.3, the I(dest) option has been changed to I(path) as default, but I(dest) still works as well. seealso: - module: ansible.builtin.blockinfile - module: ansible.builtin.copy - module: ansible.builtin.file - module: ansible.builtin.replace - module: ansible.builtin.template - module: community.windows.win_lineinfile author: - Daniel Hokka Zakrissoni (@dhozac) - Ahti Kitsik (@ahtik) - Jose Angel Munoz (@imjoseangel) a6 # NOTE: Before 2.3, option 'dest', 'destfile' or 'name' was used instead of 'path' - name: Ensure SELinux is set to enforcing mode ansible.builtin.lineinfile: path: /etc/selinux/config regexp: '^SELINUX=' line: SELINUX=enforcing - name: Make sure group wheel is not in the sudoers configuration ansible.builtin.lineinfile: path: /etc/sudoers state: absent regexp: '^%wheel' - name: Replace a localhost entry with our own ansible.builtin.lineinfile: path: /etc/hosts regexp: '^127\.0\.0\.1' line: 127.0.0.1 localhost owner: root group: root mode: '0644' - name: Replace a localhost entry searching for a literal string to avoid escaping ansible.builtin.lineinfile: path: /etc/hosts search_string: '127.0.0.1' line: 127.0.0.1 localhost owner: root group: root mode: '0644' - name: Ensure the default Apache port is 8080 ansible.builtin.lineinfile: path: /etc/httpd/conf/httpd.conf regexp: '^Listen ' insertafter: '^#Listen ' line: Listen 8080 - name: Ensure php extension matches new pattern ansible.builtin.lineinfile: path: /etc/httpd/conf/httpd.conf search_string: '' insertafter: '^\t\n' line: ' ' - name: Ensure we have our own comment added to /etc/services ansible.builtin.lineinfile: path: /etc/services regexp: '^# port for http' insertbefore: '^www.*80/tcp' line: '# port for http by default' - name: Add a line to a file if the file does not exist, without passing regexp ansible.builtin.lineinfile: path: /tmp/testfile line: 192.168.1.99 foo.lab.net foo create: yes # NOTE: Yaml requires escaping backslashes in double quotes but not in single quotes - name: Ensure the JBoss memory settings are exactly as needed ansible.builtin.lineinfile: path: /opt/jboss-as/bin/standalone.conf regexp: '^(.*)Xms(\d+)m(.*)$' line: '\1Xms${xms}m\3' backrefs: yes # NOTE: Fully quoted because of the ': ' on the line. See the Gotchas in the YAML docs. - name: Validate the sudoers file before saving ansible.builtin.lineinfile: path: /etc/sudoers state: present regexp: '^%ADMIN ALL=' line: '%ADMIN ALL=(ALL) NOPASSWD: ALL' validate: /usr/sbin/visudo -cf %s # See https://docs.python.org/3/library/re.html for further details on syntax - name: Use backrefs with alternative group syntax to avoid conflicts with variable values ansible.builtin.lineinfile: path: /tmp/config regexp: ^(host=).* line: \g<1>{{ hostname }} backrefs: yes #N) AnsibleModule)to_bytes to_nativeto_textc Cstj|jd\}}t|d}||Wdn1s>0Y|jdd}| }|rd|vrx|jd|d| t ||dd\}} } |d k}|d kr|jd || fd|r|j |t tj t |dddd|jd d dS) N)dirwbvalidatez%szvalidate must contain %%s: %smsgsurrogate_or_stricterrorsrz"failed to validate: rc:%s error:%s unsafe_writes)r)tempfileZmkstempZtmpdirosfdopen writelinesparamsget fail_jsonZ run_commandrZ atomic_moverpathrealpath) moduleb_linesdestZtmpfdZtmpfilefr Zvalidrcouterrr#>/usr/lib/python3.9/site-packages/ansible/modules/lineinfile.py write_changess&(r%cCs<||j}|j|d|dr4|r(|d7}d}|d7}||fS)NF)diffz and Tz,ownership, perms or SE linux context changed)Zload_file_common_argumentsrZset_fs_attributes_if_different)rchangedmessager&Z file_argsr#r#r$check_file_attrss r)c ! Csxddd|d|d} t|dd} tj| s|sD|jdd|dtj| } | rtj| s|jszt| Wn@ty}z(|jd t | t |fd WYd}~n d}~00g}n2t | d }| }Wdn1s0Y|j rt d || d <|dur$tt|dd}|dvrBtt|dd}n"|dvr`tt|dd}nd}ddg}d}d}t|dd}|durt|D]2\}}||}|r||d<|}| rqƐq|durt|D]8\}}t|dd|v}|r||d<|}| rqq|st|D]v\}}||dkrF||d<d}nN|dur ||r |rz|d|d<| rzq|r ||d<| r qq d}d}ttjdd}|ddkrr| r|r||}n|}||s||7}|||fdkrH|sH|r|dkr|r>|ddddvr>|d||d<t||dkr||ddd|kr|||d}d}n4||dd|krF||d||d}d}n|rn|dkrn|ddkr||dd|krF||d||d}d}n8||ddd|krn||d||d}d}n&||d|kr|||d<d}d}n<| r|n2|dks|dkr|d||d}d}n|dks|ddkr|r|ddddvr|||||d}d}n|r|ddkrt||dkr\||ddd|kr|||d}d}n4|||ddkr||d||d}d}n||d||d}d}|j rt d || d<d}|r|js|rtj| r||}t||||jr,tj| s,|j|||| di}t||||\}}d||d <d||d!<| |g} |j|||| ddS)"N %s (content)beforeafter before_header after_headerrrizDestination %s does not exist !r rzError creating %s (%s)r rbr-)NBOFEOF)Nr4Fr TNNNr5)  z line addedr4z line replaceds r.)r'rbackupr&%s (file attributes)r/r0)rrrexistsrdirname check_modemakedirs Exceptionr open readlines_diffrjoinrecompile enumeratesearchrstriplinesepexpandendswithlenappendinsert backup_localr% exit_jsonr))!rrregexp search_stringline insertafter insertbeforecreater<backrefs firstmatchr&b_destZ b_destpatherrZbre_mZbre_insindexmatchZexact_line_matchb_linelineno b_cur_line match_foundrr'Z b_linesepZ b_new_line backupdest attr_diffdifflistr#r#r$present(s   2 &                   rgcst|dd}tj|s&|jdddd}ddd|d|d}t|d } | } Wdn1sh0Y|jrtd | |d <durt tddgt|ddfd d fdd| D} t dk} |jrtd | |d<d} | r,|j s,|r ||} t|| || r>dt }i} t|| || \}} d|| d<d|| d<|| g}|j| t || |ddS)NrrFzfile not present)r'rr*r+r,r2r3r-csPdur|}n(dur.tdd|v}n|dk}|rJ|| S)Nrrr7)rJrrKrP)rbrc)r`bre_cfoundrTrUr#r$matchers  zabsent..matchercsg|]}|r|qSr#r#).0l)rjr#r$ r3zabsent..rr.z%s line(s) removedr=r/r0)r'rirr<r&)rrrr>rSrCrDrErrFrGrHrOr@rRr%r))rrrTrUrVr<r\rr&rrr'rdrerfr#)r`rhrirjrTrUr$absentsF   &        rncCs tttddgddtddddgdtdd gd tdd tdd gd tdd tdd td ddtd ddtd ddtd ddtdd d ddgddgddggddd}|j}|d}|d}|d}|d}|d}|d}|d}|d} d||fvr&d} d} |dkrd} | d7} || | t|d d!} tj| rR|jd"d#|d$|d%dkr|r||dur||jd&d'| dur|jd(d'|d|d} }| dur|durd)}t ||||| || |||| n.||| fd*kr|jd+d't ||||| |dS),NrT)rZdestfilename)typerequiredaliasesstrrgrn)rpdefaultchoicesZregex)rprr)rpvalueboolF)rprt) rstaterTrUrVrWrXrZrYr<r[r rXrWrTrUrZ)Z argument_specZmutually_exclusiveZadd_file_common_argsZsupports_check_moderYr<r[rVr*zThe %s is an empty string, which will match every line in the file. This may have unintended consequences, such as replacing the last line in the file rather than appending.z search stringzregular expressionzT If this is desired, use '^' to match every line in the file and avoid this warning.rrzPath %s is a directory !r1rxz%regexp is required with backrefs=truer z#line is required with state=presentr5r9zCone of line, search_string, or regexp is required with state=absent) rdictrwarnrrrisdirrrgrn)rrrYr<rZrr[rTrUrVrZ param_nameb_pathZins_befZins_aftr#r#r$main:sj              r~__main__)Z __future__rrrrpZ __metaclass__Z DOCUMENTATIONZEXAMPLESZRETURNrrGrZansible.module_utils.basicrZansible.module_utils._textrrr r%r)rgrnr~__name__r#r#r#r$s$U  V=C