Unable to default omit when synchronize attributes are multiline in Ansible - ansible

Below Ansible works fine when final_file_perm is defined:
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts: "{{ final_file_perm | default(omit) }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
However, my requirement is to have multiple multiline rsync_opts on new line like below:
rsync_opts:
- "--chmod=F0775"
- "--chmod=D0775"
So I tried the following:
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts:
- "{{ final_file_perm | default(omit) }}"
- "{{ final_folder_perm | default(omit) }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
The above does not work and give me the following error:
TASK [Copying from "remhost" to this ansible server.] *********************
failed: [remhost] (item=/u/files/inst.zip) => {"changed": false, "cmd": "/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/share/centrifydc/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 --out-format=<<CHANGED>>%i %n%L remuser#remhost:/u/files/inst.zip /web/playbooks/filecopy/tmpfiles/124/", "item": "/u/files/inst.zip", "msg": "Unexpected remote arg: remuser#remhost:/u/files/inst.zip\nrsync error: syntax or usage error (code 1) at main.c(1344) [sender=3.1.2]\n", "rc": 1}
to retry, use: --limit #/web/playbooks/filecopy/copyfiles.retry
As you can see ansible synchronize module is not liking __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 right after -o UserKnownHostsFile=/dev/null
I also tried the following:
rsync_opts: [ '{{ final_file_perm | default(omit) }}', '{{ final_folder_perm | default(omit) }}' ]
But, I get similar error as shared above.
Note: this works fine when final_file_perm and final_folder_perm are defined. Errors only when they are undefined and I wish to omit them.
Can you please propose a solution where I can use default(omit) for attributes on multi newlines?

Then you will want to build up the rsync_opts list conditionally, rather than having what amounts to None items in the list
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts: "{{ my_rsync_opts }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
vars:
my_rsync_opts: >-
{{ [] +
([final_file_perm] if final_file_perm|d("") else []) +
([final_folder_perm] if final_folder_perm|d("") else []) }}

Related

Loop through the children host group ansible is failing

My inventory file is having below host groups:
[uat1]
123.11.23.22 ansible_user="xxx"
[OS_uat2]
123.45.6.7 ansible_user="yyy"
[uat1_childs:children]
uat1
OS_uat2
I am having the vars file which is having param for below hosts. I am running a playbook to run a shell command. I am passing some parameters with the playbook. I am passing deployment_environment as uat1_childs. This is giving me error. Playbook is:
- name: play to ping test
gather_facts: false
hosts: "{{ deployment_environment }}"
ignore_unreachable: yes
vars_files:
- r_params.yml
vars:
package: "{{ package }}"
tasks:
- set_fact:
env_param: "{{ deployment_environment }}"
- name: ping test
ping:
data: pong
- name: Deploy Services on "{{ deployment_environment }}"
shell: cd "{{ env_select[env_param].script_path }}"; sh "{{ env_select[env_param].script_path }}/deploy.sh" "param1" "param2" "{{ env_select[env_param].repo }}" "{{ artifact_version }}" "{{ env_select[env_param].ENV }}" "{{ arti_username }}" "{{ arti_pass }}" "{{ deployer }}" "{{ package }}" "{{ env_select[env_param].deployment_path }}"
when: (package == "abc")
with_items: "{{ groups[{{ 'deployment_environment' }}] }}"
This is giving me error as:
fatal: [123.11.23.22]: FAILED! =>
{
"msg": "'dict object' has no attribute 'deployment_environment'"
}
fatal: [123.45.6.7]: FAILED! =>
{
"msg": "'dict object' has no attribute 'deployment_environment'"
}
I tried removing apostrophe in with items, still it is giving me error. Cant identify how to run the task in all children host group.

Ansible loop dict items

I am using ansible 2.9 and trying to use ansible loop instead of with_dict. I am getting below error. I need to pass the values at the task, I can't keep them in variable or load them at the beginning. do I need to switch back to with_dict?
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: expected token 'end of print statement', got ':'. String: {{ key:/var/tmp/h vaule:/tmp/h|dict2items }}"}
- name: copy module
copy:
src: "{{ item.key }}"
dest: "{{ item.value}}"
loop:
- "{{ key: /var/tmp/a value:/tmp/h |dict2items }}"
- "{{ key: /var/tmp/b value:/tmp/z |dict2items }}"
You don't need dict2items here. Fix the syntax. For example, test it first
- hosts: localhost
tasks:
- debug:
msg: >-
src: {{ item.key }}
dest: {{ item.value}}
loop:
- {key: /var/tmp/a, value: /tmp/h}
- {key: /var/tmp/b, value: /tmp/z}
gives (abridged)
msg: 'src: /var/tmp/a dest: /tmp/h'
msg: 'src: /var/tmp/b dest: /tmp/z'
If this is what you want to copy the files
- name: copy module
copy:
src: "{{ item.key }}"
dest: "{{ item.value }}"
loop:
- {key: /var/tmp/a, value: /tmp/h}
- {key: /var/tmp/b, value: /tmp/z}
You'd need dict2items if the structure was a dictionary. For example,
dirs:
/var/tmp/a: /tmp/h
/var/tmp/b: /tmp/z
Then the task below would give the same result
- debug:
msg: >-
src: {{ item.key }}
dest: {{ item.value}}
loop: "{{ dirs|dict2items }}"

In Ansible loop, test existence of files from registered results

I have several files that I need to backup in different directories. I have tried the code below and not working for me.
vars:
file_vars:
- {name: /file1}
- {name: /etc/file2}
- {name: /etc/file/file3}
tasks:
- name: "Checking if config files exists"
stat:
path: "{{ item.name }}"
with_items: "{{ file_vars }}"
register: stat_result
- name: Backup Files
copy: src={{ item.name }} dest={{ item.name }}{{ ansible_date_time.date }}.bak
with_items: "{{ file_vars }}"
remote_src: yes
when: stat_result.stat.exists == True
The problem is the condition
when: stat_result.stat.exists == True
There is no attribute stat_result.stat. Instead, the attribute stat_result.results is a list of the results from the loop. It's possible to create a dictionary of files and their statuses. For example
- set_fact:
files_stats: "{{ dict(my_files|zip(my_stats)) }}"
vars:
my_files: "{{ stat_result.results|json_query('[].item.name') }}"
my_stats: "{{ stat_result.results|json_query('[].stat.exists') }}"
Then simply use this dictionary in the condition
when: files_stats[item.name]
Below is a shorter version which creates the dictionary more efficiently
- set_fact:
files_stats: "{{ dict(stat_result.results|
json_query('[].[item.name, stat.exists]')) }}"
Please try using below worked for me:
---
- name: Copy files
hosts: localhost
become: yes
become_user: root
vars_files:
- files.yml
tasks:
- name: "Checking if config files exists"
stat:
path: "{{ item }}"
with_items: "{{ files }}"
register: stat_result
- name: Ansible
debug:
msg: "{{ stat_result }}"
- name: Backup Files
copy:
src: "{{ item }}"
dest: "{{ item.bak }}"
with_items: "{{ files }}"
when: stat_result == "True"
and files.yml will look like:
---
files:
- /tmp/file1
- /tmp/file2
you can check you playbook syntax using below command:
ansible-playbook copy.yml --syntax-check
Also you do dry run your playbook before actual execution.
ansible-playbook -i localhost copy.yml --check

Ansible write result command to local file with loop

I've write ansible-playbook to collect the result from many network devices. Below playbook is working fine. But if I have to collect result with lot of commands. Let say 20 commands, I've to create the many task to write the results into file in my playbook.
For now, I manually create the tasks to write to logs into file. Below is example with 3 commands.
- name: run multiple commands and evaluate the output
hosts: <<network-host>>
gather_facts: no
connection: local
vars:
datetime: "{{ lookup('pipe', 'date +%Y%m%d%H') }}"
backup_dir: "/backup/"
cli:
host: "{{ ansible_host }}"
username: <<username>>
password: <<password>>
tasks:
- sros_command:
commands:
- show version
- show system information
- show port
provider: "{{ cli }}"
register: result
- name: Writing output
local_action:
module: lineinfile
dest: "{{ backup_dir }}/{{ inventory_hostname }}-{{ datetime }}.txt"
line: "{{ inventory_hostname }}:# show version\n{{ result.stdout[0] }}"
create: yes
changed_when: False
- name: Writing output
local_action:
module: lineinfile
dest: "{{ backup_dir }}/{{ inventory_hostname }}-{{ datetime }}.txt"
line: "{{ inventory_hostname }}:# show system information\n{{ result.stdout[1] }}"
create: yes
changed_when: False
- name: Writing output
local_action:
module: lineinfile
dest: "{{ backup_dir }}/{{ inventory_hostname }}-{{ datetime }}.txt"
line: "{{ inventory_hostname }}:# show port\n{{ cmd_result.stdout[2] }}"
create: yes
changed_when: False
Is it possible to loop commands and result within one task?
Please kindly advice.
Thanks
try this one task alone in place above three tasks..
- name: Writing output
local_action:
module: lineinfile
dest: "{{ backup_dir }}/{{ inventory_hostname }}-{{ datetime }}.txt"
line: "{{ inventory_hostname }}:# show {{ item.command }}\n{{ cmd_result.stdout{{ item.outnum }} }}"
create: yes
changed_when: False
with_items:
- { command: version, outnum: [0] }
- { command: system information, outnum: [1] }
- { command: port, outnum: [2] }
Below playbook is worked for me
- name: Writing output
local_action:
module: lineinfile
dest: "{{ backup_dir }}/{{ inventory_hostname }}-{{ datetime }}.txt"
line: "{{ inventory_hostname }}:# show {{ item.command }}\n{{ item.cmdoutput}}"
create: yes
changed_when: False
with_items:
- { command: "version", cmdoutput: "{{ cmd_result.stdout[0] }}" }
- { command: "system information", cmdoutput: "{{ cmd_result.stdout[1] }}" }
- { command: "port", cmdoutput: "{{ cmd_result.stdout[2] }}" }

Ansible : Loop over two register variables using with_items

I have stored checksum of local and remote files in two different variables. Now I want to compare those checksum and fail if they don't match. Below is code.
- name: Get cksum some of files copied locally
stat:
path : "{{ item.src }}/{{ item.file }}"
checksum_algorithm: sha1
delegate_to: localhost
with_items: "{{ files }}"
register: local_files
- name: Get cksum of remote files
stat:
path : "{{ item.dest }}/{{ item.file }}_{{ item.package }}_NEW"
checksum_algorithm: sha1
with_items: "{{ files }}"
register: remote_files
- name : Compare local and remote cksums. Fail if not matched
debug:
msg="Checksum don't match"
failed_when: item[0].results.stat.checksum != item[1].results.stat.checksum
with_items:
- "{{ local_files.results }}"
- "{{ remote_files.results }}"
When I run this, I get below error.
FAILED! => {"failed": true, "msg": "The conditional check 'item[0].results.stat.checksum != item[1].results.stat.checksum' failed. The error was: error while evaluating conditional (item[0].results.stat.checksum != item[1].results.stat.checksum): dict object has no element 0"}
How can I correct it to compare checksum?
Do this:
- name: Get cksum some of files copied locally
stat:
path : "{{ item.src }}/{{ item.file }}"
delegate_to: localhost
with_items: "{{ files }}"
register: local_files
- name: Get cksum of remote files
stat:
path : "{{ item.dest }}/{{ item.file }}"
with_items: "{{ files }}"
register: remote_files
- name: Compare local and remote cksums. Fail if not matched
fail:
msg: "Checksum don't match"
when: item[0].stat.checksum != item[1].stat.checksum
with_together:
- "{{ local_files.results }}"
- "{{ remote_files.results }}"

Resources