Insert lines in a file from an other file lines in Ansible - linux

I would copy lines from a file /tmp/test1 to a file /tmp/test2
the /tmp/test1 contains:
argument1
argument2
#test1
#test2
#test3
the /tmp/test2 contains:
argument1.1
argument2192
#example
#test2
#example1
So my main goal is to insert every line that doesn't exist in /tmp/test2 from file /tmp/test1
and that the line added must be added at the end of the last line which is containing the same begin of line: ^[[:alpha:]] and ^#, so /tmp/test2 should look like this:
argument1.1
argument2192
argument1
argument2
#example
#test2
#example1
#test1
#test3
I created this playbook but it doesn't do what i am looking for:
- name: check test1 content
command: cat /tmp/test1
register: tmp_content
- name: insert line
lineinfile:
path: /tmp/test2
line: '{{item}}'
insertafter: "^#*"
loop: "{{tmp_content.stdout_lines}}"

1) "Insert every line that doesn't exist in /tmp/test2 from file /tmp/test1"
2) "The line added must be added at the end of the last line which is containing the same beginning."
A: The task below does the job. If the first character is # the line is inserted at the end of the file. Otherwise, the line is inserted before the first line starting with #. The parameters insertafter and insertbefore may not be used together. The negative options omit make the parameters insertafter and insertbefore mutually exclusive
- name: insert line
lineinfile:
path: /tmp/test2
line: "{{ item }}"
insertafter: "{{ (item.0 == '#')|ternary('EOF', omit) }}"
insertbefore: "{{ (item.0 != '#')|ternary('^#.*$', omit) }}"
firstmatch: true
loop: "{{ tmp_content.stdout_lines }}"
Notes
Example of a complete playbook for testing
shell> cat pb.yml
- hosts: localhost
tasks:
- name: check test1 content
command: cat /tmp/test1
register: tmp_content
changed_when: false
- name: insert line
lineinfile:
path: /tmp/test2
line: "{{ item }}"
insertafter: "{{ (item.0 == '#')|ternary('EOF', omit) }}"
insertbefore: "{{ (item.0 != '#')|ternary('^#.*$', omit)}}"
firstmatch: true
loop: "{{ tmp_content.stdout_lines }}"
The playbook is idempotent. See the output of the diff_mode below
shell> ansible-playbook pb.yml --diff
...
TASK [insert line] *************************************************
--- before: /tmp/test2 (content)
+++ after: /tmp/test2 (content)
## -1,5 +1,6 ##
argument1.1
argument2192
+argument1
#example
#test2
#example1
changed: [localhost] => (item=argument1)
--- before: /tmp/test2 (content)
+++ after: /tmp/test2 (content)
## -1,6 +1,7 ##
argument1.1
argument2192
argument1
+argument2
#example
#test2
#example1
changed: [localhost] => (item=argument2)
--- before: /tmp/test2 (content)
+++ after: /tmp/test2 (content)
## -5,3 +5,4 ##
#example
#test2
#example1
+#test1
changed: [localhost] => (item=#test1)
ok: [localhost] => (item=#test2)
--- before: /tmp/test2 (content)
+++ after: /tmp/test2 (content)
## -6,3 +6,4 ##
#test2
#example1
#test1
+#test3
changed: [localhost] => (item=#test3)
Brute force option
Read the files
- command: cat /tmp/test1
register: test1
- command: cat /tmp/test2
register: test2
Declare the variables
l1_alpha: "{{ test1.stdout_lines|select('match', '^[^#].*$') }}"
l1_glyph: "{{ test1.stdout_lines|select('match', '^#.*$') }}"
l2_alpha: "{{ test2.stdout_lines|select('match', '^[^#].*$') }}"
l2_glyph: "{{ test2.stdout_lines|select('match', '^#.*$') }}"
l1_alpha_diff: "{{ l1_alpha|difference(l2_alpha) }}"
l1_glyph_diff: "{{ l1_glyph|difference(l2_glyph) }}"
result: "{{ l2_alpha + l1_alpha_diff + l2_glyph + l1_glyph_diff }}"
This gives the expected result
- debug:
msg: |
{% for line in result %}
{{ line }}
{% endfor %}
msg: |-
argument1.1
argument2192
argument1
argument2
#example
#test2
#example1
#test1
#test3
Write it to a file
- copy:
dest: /tmp/test2
content: |
{% for line in result %}
{{ line }}
{% endfor %}
gives
shell> cat /tmp/test2
argument1.1
argument2192
argument1
argument2
#example
#test2
#example1
#test1
#test3
Example of a complete playbook for testing
shell> cat pb.yml
- hosts: localhost
vars:
l1_alpha: "{{ test1.stdout_lines|select('match', '^[^#].*$') }}"
l1_glyph: "{{ test1.stdout_lines|select('match', '^#.*$') }}"
l2_alpha: "{{ test2.stdout_lines|select('match', '^[^#].*$') }}"
l2_glyph: "{{ test2.stdout_lines|select('match', '^#.*$') }}"
l1_alpha_diff: "{{ l1_alpha|difference(l2_alpha) }}"
l1_glyph_diff: "{{ l1_glyph|difference(l2_glyph) }}"
result: "{{ l2_alpha + l1_alpha_diff + l2_glyph + l1_glyph_diff }}"
tasks:
- command: cat /tmp/test1
register: test1
changed_when: false
- command: cat /tmp/test2
register: test2
changed_when: false
- copy:
dest: /tmp/test2
content: |
{% for line in result %}
{{ line }}
{% endfor %}
The playbook is idempotent.

Related

how to traverse through each line of a text file in ansible

I have to traverse through package list file which contains list of packages with their architecture. How can I feed those input to my playbook file? I found a way to get the package names alone but architecture version is not coming. This is my package_list file
nginx | x86_64
telnet| x86_64
openssh | i386
This is my playbook
- name: get contents of package.txt
command: cat "/root/packages.txt"
register: _packages
- name: get contents of architecture from packages.txt
command: cat "/root/packages.txt" | awk '{print $3}'
register: _arch
- name: Filter
theforeman.foreman.content_view_filter:
username: "admin"
password: "mypass"
server_url: "myhost"
name: "myfilter"
organization: "COT"
content_view: "M_view"
filter_type: "rpm"
architecture: "{{ _arch }}"
package_name: "{{ item }}"
inclusion: True
loop: "{{ _packages.stdout_lines }}"
loop: "{{ _arch.stdout_lines }}"
Any help would be appreciated
The required output is package name and architecture should be read from packages.txt file through ansible-playbook
try this playbook:
- name: Reproduce issue
hosts: localhost
gather_facts: no
tasks:
- name: get contents of package.txt
command: cat "/root/packages.txt"
register: _packages
- debug:
msg: "package: {{ line.0 }}, arch: {{ line.1 }}"
loop: "{{ _packages.stdout_lines }}"
vars:
line: "{{ item.split('|')|list }}"
result:
ok: [localhost] => (item=nginx | x86_64) => {
"msg": "package: nginx , arch: x86_64 "
}
ok: [localhost] => (item=telnet| x86_64) => {
"msg": "package: telnet, arch: x86_64 "
}
ok: [localhost] => (item=openssh | i386) => {
"msg": "package: openssh , arch: i386 "
}
for your case:
- name: Filter
theforeman.foreman.content_view_filter:
:
:
architecture: "{{ line.1 }}"
package_name: "{{ line.0 }}"
inclusion: True
loop: "{{ _packages.stdout_lines }}"
vars:
line: "{{ item.split('|')|list }}"
following the version of ansible you could write too line: "{{ item | split('|') | list }}"
You need to split up the line into the necessary values by filtering.
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Gahter package list
shell:
cmd: cat package.txt
register: _packages
- name: Show packages
debug:
msg: "Name: {{ item.split('|')[0] }}, Arch: {{ item.split('|')[1] }}"
loop: "{{ _packages.stdout_lines }}"
Further Documentation
Playbook filters - Manipulating strings
Jinja Template Designer - Filters
Further Q&A
Split string into list in Jinja?

Ansible - load multiplne local yml files and merge their content

my host_vars file has about 5k lines of yml code. So I would like to have separate yml files - one file per one service.
Simplified example:
user#test $ cat production/split_configs/a.example.net.yml
my_array:
- a.example.net
user#test $ cat production/split_configs/b.example.net.yml
my_array:
- b.example.net
user#test $ cat webhosts.yml
- hosts: myservers
pre_tasks:
- name: merge ansible arrays
tags: always
delegate_to: localhost
block:
- name: find config files
find:
paths: production/configs/
patterns: '*.yml'
register: find_results
- name: aaa
debug:
msg: "{{ find_results.files }}"
- name: bbb
debug:
msg: "{{ item.path }}"
with_items: "{{ find_results.files }}"
- name: ccc
debug:
msg: "{{ lookup('file', 'production/configs/a.example.net.yml') }}"
- name: ddd
debug:
msg: "{{ lookup('file', item.path) }}"
loop: "{{ find_results.files }}"
tasks:
- name: eee
debug:
msg: "{{ my_array }}"
The goal is to merge content of both arrays an print the merged content in task eee:
my_array:
- a.example.net
- b.example.net
Task aaa print informations about files (path, mode, uid, ...) - it works.
Tasks bbb, and ddd print nothing. I do not understand why.
Task ccc print content of file. But the path is written in playbook :-(
After load files I need to merge them. My idea is to use something like set_fact: my_array="{{ my_array + my_array }}" in task with with_items: "{{ find_results.files }}". Is it good idea? Or how better I can do it?
For example, the tasks below do the job
- include_vars:
file: "{{ item }}"
name: "my_prefix_{{ item|regex_replace('\\W', '_') }}"
with_fileglob: production/split_configs/*
- set_fact:
my_vars: "{{ my_vars|d({})|
combine(lookup('vars', item),
recursive=True,
list_merge='append') }}"
loop: "{{ q('varnames', 'my_prefix_.*') }}"
give
my_vars:
my_array:
- a.example.net
- b.example.net
You can use the simple cat commnd to merge the files into the one file and later include var file for example -
- raw: "cat production/split_configs/* >my_vars.yml"
- include_vars: file=my_vars.yml name=my_vars
will give you the result -
my_vars:
my_array:
- a.example.net
- b.example.net

How to rename a file in ansible

I need to rename hosts_example or hosts_Example to be named as hosts_real if any of the file exists
- name: Playbook to Standardize Hosts
hosts: test
vars:
destpath: /etc/hosts_real
filename: [ /etc/hosts_example,/etc/hosts_Example ]
tasks:
- name: Check if file exists
stat:
path: "{{ item }}"
with_items:
- "{{ filename }}"
register: check_file_name
- debug:
msg: "{{check_file_name}}"
- name: Rename file
command: mv "{{ item }}"{{destpath}}"
with_items:
- "{{ check_file_name.results }}"
when: item.stat.exists == true
I tried this am getting errors and not able to achieve the desired result
name: replace file
shell: mv /local/oracle/12.2/oldora.ora /local/oracle/12.2/tnsnames.ora
become: appuser
Works for us. Update your path in the shell command. I dont think the file module has a rename command. If you have a variable in your shell line put the whole line in quotes like:
shell: "mv {{ path_1 }} {{ path_2 }}"

Ansible aclling two diffrent file variables into single task looping through multiple files

Content of fil1
# cat file1
fostrain01.example.com
fostrain02.example.com
fostrain03.example.com
Content of file2
# cat fil2
ServerIPS 171.20.20.16 171.20.20.17 171.20.20.18 171.20.20.19 171.20.20.20
ServerIPS 171.20.20.21 171.20.20.22 171.20.20.23 171.20.20.24 171.20.20.25
In the below playbook, its replacing the contents on two lines one is hostname as str and in another line replacing the ip So, i have taken the two different task 1) Replace strings in file & 2) Replace ip in file to accomplish this calling the variable defined.
Playbook:
- name: Replace string in hosts file
hosts: all
gather_facts: false
vars:
files:
- /etc/file1
- /etc/file2
from_str: "fostrain01.example.com"
to_str: "dbfoxtrain01.example.com"
from_ip: "^(.*)171\\.20\\.20\\.18(.*)$"
to_ip: "\\g<1>172.20.20.18\\g<2>"
tasks:
- name: Replace strings in file
replace:
path: "{{ item}}"
regexp: "{{ from_str }}"
replace: "{{ to_str }}"
backup: yes
loop: "{{ files }}"
- name: Replace ip in file
replace:
path: "{{ item}}"
regexp: "{{ from_ip }}"
replace: "{{ to_ip }}"
backup: yes
loop: "{{ files }}"
Can we write the task as follows something where we don't really write to two different tasks, i tried but not getting how to loops through "{{ files }} with below approach.
tasks:
- name: Replace elements in file
replace:
path: "{{ item.path }}"
regexp: "{{ item.From }}"
replace: "{{ item.To }}"
backup: yes
loop:
# Replace the desired string
- { path: "{{ item }}", From: "{{ from_str }}", To: "{{ to_str }}" }
# Replace the desired ip
- { path: "{{ item}}", From: "{{ from_ip }}", To: "{{ to_ip}}" }
What is Desired Change:
task 1) Replace strings in file & task 2) Replace ip in file to be clubbed into One.
Expected Results:
# cat file1
dbfoxtrain01.example.com <-- Changed
fostrain02.example.com
fostrain03.example.com
# cat fil2
ServerIPS 171.20.20.16 171.20.20.17 172.20.20.18 171.20.20.19 171.20.20.20 <-- changed here ^172
ServerIPS 171.20.20.21 171.20.20.22 171.20.20.23 171.20.20.24 171.20.20.25
The task below does the job
- replace:
path: "{{ item.path }}"
regexp: "{{ item.from }}"
replace: "{{ item.to }}"
loop:
- path: file1
from: 'fostrain01\.example\.com'
to: 'dbfoxtrain01.example.com'
- path: file2
from: '171\.20\.20\.18'
to: '172.20.20.18'
Example
shell> tree .
.
├── file1
├── file1.orig
├── file2
├── file2.orig
└── test.yml
0 directories, 5 files
shell> cat file1
fostrain01.example.com
fostrain02.example.com
fostrain03.example.com
shell> cat file2
ServerIPS 171.20.20.16 171.20.20.17 171.20.20.18 171.20.20.19 171.20.20.20
ServerIPS 171.20.20.21 171.20.20.22 171.20.20.23 171.20.20.24 171.20.20.25
shell> cat test.yml
- hosts: localhost
gather_facts: false
tasks:
- replace:
path: "{{ item.path }}"
regexp: "{{ item.from }}"
replace: "{{ item.to }}"
loop:
- path: file1
from: 'fostrain01\.example\.com'
to: 'dbfoxtrain01.example.com'
- path: file2
from: '171\.20\.20\.18'
to: '172.20.20.18'
shell> ansible-playbook test.yml
PLAY [localhost] ************************************************
TASK [replace] **************************************************
changed: [localhost] => (item={'path': 'file1', 'from': 'fostrain01\\.example\\.com', 'to': 'dbfoxtrain01.example.com'})
changed: [localhost] => (item={'path': 'file2', 'from': '171\\.20\\.20\\.18', 'to': '172.20.20.18'})
PLAY RECAP ******************************************************
localhost: ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
shell> cat file1
dbfoxtrain01.example.com
fostrain02.example.com
fostrain03.example.com
shell> cat file2
ServerIPS 171.20.20.16 171.20.20.17 172.20.20.18 171.20.20.19 171.20.20.20
ServerIPS 171.20.20.21 171.20.20.22 171.20.20.23 171.20.20.24 171.20.20.25

Ansible playbook loop with with_items

I have to update sudoers.d multiple user files with few lines/commands using ansible playbook
users.yml
user1:
- Line1111
- Line2222
- Line3333
user2:
- Line4444
- Line5555
- Line6666
main.yml
- hosts: "{{ host_group }}"
vars_files:
- ../users.yml
tasks:
- name: Add user "user1" to sudoers.d
lineinfile:
path: /etc/sudoers.d/user1
line: '{{ item }}'
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ user1 }}"
The above one is working only for user1..
If I want to also include user2 --> How to change the file name : path: /etc/sudoers.d/user1
I tried below and its not working :
Passing below users as variable to main.yml while running
users:
- "user1"
- "user2"
- name: Add user "{{users}}" to sudoers.d
lineinfile:
path: /etc/sudoers.d/{{users}}
line: '{{ item }}'
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ users }}"
So, basically I want to pass in users to a variable {{users}} as user1 and user2 and wanted to use the lines for each user from users.yml and add it to respective user files (/etc/sudoers.d/user1 and /etc/sudoers.d/user2).
So /etc/sudoers.d/user1 should look like
Line1111
Line2222
Line3333
and /etc/sudoers.d/user2 should look like
Line4444
Line5555
Line6666
Try to add quotes:
users:
- "user1"
- "user2"
- name: "Add user {{users}} to sudoers.d"
lineinfile:
path: "/etc/sudoers.d/{{users}}"
line: "{{ item }}"
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ users }}"
As per Ansible Documentation on Using Variables:
YAML syntax requires that if you start a value with {{ foo }} you quote the whole line, since it wants to be sure you aren’t trying to start a YAML dictionary. This is covered on the YAML Syntax documentation.
This won’t work:
- hosts: app_servers
vars:
app_path: {{ base_path }}/22
Do it like this and you’ll be fine:
- hosts: app_servers
vars:
app_path: "{{ base_path }}/22"
cat users.yml
---
users:
- user1:
filename: user1sudoers
args:
- Line1111
- Line2222
- Line3333
- user2:
filename: user2sudoers
args:
- Line4444
- Line5555
- Line6666
I use template here, instead of lineinfile
---
cat sudoers.j2
{% if item.args is defined and item.args %}
{% for arg in item.args %}
{{ arg }}
{% endfor %}
{% endif %}
the task content
---
- hosts: localhost
vars_files: ./users.yml
tasks:
- name: sync sudoers.j2 to localhost
template:
src: sudoers.j2
dest: "/tmp/{{ item.filename }}"
loop: "{{ users_list }}"
when: "users_list is defined and users_list"
after run the task.yml, generate two files under /tmp directory.
cat /tmp/user1sudoers
Line1111
Line2222
Line3333
cat /tmp/user2sudoers
Line4444
Line5555
Line6666

Resources