Ansible working with multiple loops in conditions - ansible

how can I add a condition to ansible task that is based on a loop, when the task itself also based on a loop?
For example, here's my code:
- hosts: all
gather_facts: False
vars:
current_version: 826
versions:
- 805
- 821
- 824
- 826
tasks:
- name: First Task
find:
paths: /Users/tomer/projects/personal/ansible/test
patterns: snapshot*
register: files
when:
- current_version == item
loop: "{{versions}}"
- name: Second task
set_fact:
test_work: "{{ true if item > 0 else false}}"
loop:
- "{{ files | json_query('results[*].matched') }}"
So far, this is working as expected.
The first task is looking for any file with the name snapshot if the current_version is matching one of the versions in the list.
The second task iterates over the dictionary result from the first task and based on each item it is setting the fact. (In my case, only one item has this attribute).
I wanted to run the second task, only when the first task did run, however, the changed status is always false, so this condition is not useful.
I wanted to add the same condition of current_version == item but I can't use item twice here.
Any idea how to achieve that?

The find command is not really going to change anything, it just queries the file system without doing any modification, so it will indeed always give you a false.
On the other hand, you can definitely use the skipped field of the item.
This said, I would simplify the loop on your set_fact, because there is no real need to use json_query here.
This task would do the job perfectly fine:
- set_fact:
test_work: "{{ item.matched > 0 }}"
loop: "{{ files.results }}"
when: item is not skipped
Another extra tip is to not do things like
true if condition_that_evaluates_to_true else false
But rather do right away
condition_that_evaluates_to_true
Here would be a made up example playbook
- hosts: all
gather_facts: no
tasks:
- find:
path: /tmp
pattern: dummy*
when: item == current_version
register: files
loop: "{{ versions }}"
vars:
current_version: 826
versions:
- 805
- 821
- 824
- 826
- debug:
msg: "{{ item.matched > 0 }}"
loop: "{{ files.results }}"
when: item is not skipped
loop_control:
label: "{{ item.item }}"
This would yield the result
PLAY [all] *******************************************************************************************************
TASK [find] ******************************************************************************************************
skipping: [localhost] => (item=805)
skipping: [localhost] => (item=821)
skipping: [localhost] => (item=824)
ok: [localhost] => (item=826)
TASK [debug] *****************************************************************************************************
skipping: [localhost] => (item=805)
skipping: [localhost] => (item=821)
skipping: [localhost] => (item=824)
ok: [localhost] => (item=826) => {
"msg": true
}
PLAY RECAP *******************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Related

compare value with list of int in ansible

Want to compare values with list for int and display msg values greater than 15
tasks:
- name: Create a List variable and print it
set_fact:
Continents: ["10","20"]
- name: set fatc
set_fact:
int_list: "{{ Continents|map('int')|list }}"
- debug:
msg: "{{greater than 15}}"
when: "{{int_list}}" > 15
Getting error as below:
The offending line appears to be:
msg: "{{ list }}"
when: "{{int_list}}" > 15
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
Expected Output:
greater than 15
If your goal is to show only those integers in the list that are greater than 15, you can use the select filter:
- hosts: localhost
gather_facts: false
tasks:
- name: Create a list of integers
set_fact:
int_list: [10, 20]
- name: Find integers greater than 15
debug:
msg: "{{ item }}"
loop: "{{ int_list | select('>', 15) }}"
The output of this playbook is:
PLAY [localhost] ***************************************************************
TASK [Create a list of integers] ***********************************************
ok: [localhost]
TASK [Find integers greater than 15] *******************************************
ok: [localhost] => (item=20) => {
"msg": 20
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You can achieve the same with two tasks. Change your list to integers. Make the debug task to loop through the Continents variable.
- hosts: localhost
tasks:
- name: Create a variable with a list of integers
set_fact:
Continents: [10, 20]
- debug:
msg: "greater than 15"
loop: "{{ Continents }}"
when: item > 15 ##item becomes each value of the Continents variable. So, 10, 20 and so on.
Gives:
skipping: [localhost] => (item=10)
ok: [localhost] => (item=20) => {
"msg": "greater than 15"
}

Play recap and ignored=1 in Ansible

I am checking whether all services related to a docker-compose.yml are running on a system. The code snippet shown below.
---
- name:
shell: docker-compose ps -q "{{ item }}"
register: result
ignore_errors: yes
The code above works as expected. I have to ignore errors otherwise Ansible will not complete. The following result shows ignored=1
PLAY RECAP *******************************************************************************************************
192.168.50.219 : ok=38 changed=12 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
If this completes successfully I want to run a subsequent playbook but don't know how to specify ignored=1 correctly.
---
- name:
include_tasks: do_other_things.yml
when: ignored is false
How do I get the result from PLAY RECAP into something I can test with?
A better idea than trying to cope with the error returned by docker compose ps when the container does not exist would be to use the purposed module: docker_container_info to achieve the same.
Given the playbook:
- hosts: localhost
gather_facts: no
tasks:
- docker_container_info:
name: "{{ item }}"
register: containers
loop:
- node1 # exists
- node404 # does not exists
- debug:
msg: "`{{ item.item }}` is not started"
loop: "{{ containers.results }}"
loop_control:
label: "{{ item.item }}"
when: not item.exists
This would yield:
TASK [docker_container_info] *************************************************
ok: [localhost] => (item=node1)
ok: [localhost] => (item=node404)
TASK [debug] *****************************************************************
skipping: [localhost] => (item=node1)
ok: [localhost] => (item=node404) =>
msg: `node404` is not started

Ansible loop giving warning found a duplicate dict key (when). Using last defined value only

I am trying to iterate over an array and assign the value to variables hooks_enabled, workflow_artifact_id, workflow_version, one by one in every iteration and perform a specific task (currently debug, later change to Helm install command).
Code:
---
- name: Executing Ansible Playbook
hosts: localhost
become: yes
become_user: someuser
pre_tasks:
- include_vars: global_vars.yaml
- name: Print some debug information
set_fact:
all_vars: |
Content of vars
--------------------------------
{{ vars | to_nice_json }}
tasks:
- name: Iterate over an array
set_fact:
hooks_enabled: '{{ array_item1_hooks_enabled }}'
workflow_artifact_id: '{{ array_item1_workflow_artifact_id }}'
workflow_version: '{{ array_item1_workflow_version }}'
when: "item == 'array_item1'"
set_fact:
hooks_enabled: '{{ array_item2_hooks_enabled }}'
workflow_artifact_id: '{{ array_item2_workflow_artifact_id }}'
workflow_version: '{{ array_item2_workflow_version }}'
when: "item == 'array_item2'"
with_items: "{{ array}}"
# Change debug with helm install command
- debug:
msg: " id= '{{ workflow_artifact_id }}'"
The issue I am facing is, only the last when is considered and others are skipped
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[WARNING]: While constructing a mapping from /c/ansible-test/second.yaml, line 16, column 7, found a duplicate dict key (set_fact). Using last defined value only.
[WARNING]: While constructing a mapping from /c/ansible-test/second.yaml, line 16, column 7, found a duplicate dict key (when). Using last defined value only.
PLAY [Executing Ansible Playbook] *********************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
ok: [localhost]
TASK [include_vars] ***********************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Print some debug information] *******************************************************************************************************************************************************************************
ok: [localhost]
TASK [Iterate over an array] **************************************************************************************************************************************************************************************
skipping: [localhost] => (item=array_item1)
ok: [localhost] => (item=array_item2)
skipping: [localhost] => (item=array_item3)
skipping: [localhost] => (item=array_item4)
skipping: [localhost] => (item=array_item5)
TASK [debug] ******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": " id= 'algorithm-Workflow'"
}
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
How do I modify the block to enable all the when statement execute and later use helm install command to take the variables one by one.
I would go with a dynamic variable construction using the vars lookup.
Something along the lines of:
- set_fact:
hooks_enabled: "{{ lookup('vars', item ~ '_hooks_enabled') }}"
workflow_artifact_id: "{{ lookup('vars', item ~ '_workflow_artifact_id') }}"
workflow_version: "{{ lookup('vars', item ~ '_workflow_version') }}"
when: "item in ['array_item1', 'array_item2']"
with_items: "{{ array }}"

Or condition is not working with set_facts in my playbook

I created one playbook to run the tasks based on the test case so I have created like below
Here when I pass the ansible-playbook playbook.yml -e stage=1, it's skipping all the tasks, and when I debug the test_case* values I could see both are in a false state, So can some help me to work this code.
---
- name: test
hosts: localhost
tasks:
- name: setting the level
set_fact:
test_case_1: "{{ stage == 1 }}"
test_case_2: "{{ stage == 1 or stage == 2 }}"
- name: "running ls command"
shell: "ls -l"
register: testing
when:
- test_case_1 == true
- debug:
msg: "{{ testing.stdout_lines }}"
when:
- test_case_1 == true
- name: "kickoff"
shell: "df -Th"
register: kick
when:
- test_case_2 == true
- name: "printing kickoff"
debug:
msg: "{{ kick.stdout_lines }}"
when:
- test_case_2 == true
Below is the error results which I am getting
[root#server ~]# ansible-playbook playbook.yml -e stage=1
PLAY [test] ***********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [localhost]
TASK [setting the level] **********************************************************************************************
ok: [localhost]
TASK [running ls command] *********************************************************************************************
skipping: [localhost]
TASK [debug] **********************************************************************************************************
skipping: [localhost]
TASK [kickoff] ********************************************************************************************************
skipping: [localhost]
TASK [printing kickoff] ***********************************************************************************************
skipping: [localhost]
PLAY RECAP ************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0
[root#server ~]#
expected results should be, it should execute all the tasks from the play.
Your problem is that you are performing an integer comparison (stage == 1), but when you provide a value on the command line via -e stage=1, you are setting a string value.
You probably want to case the value of stage to an integer using the int filter.
---
- name: test
hosts: localhost
tasks:
- name: setting the level
set_fact:
test_case_1: "{{ stage|int == 1 }}"
test_case_2: "{{ stage|int == 1 or stage|int == 2 }}"
With this change, things seem to work as expected.
Unrelated to your question, but you could rewrite the second test like this:
{{ stage|int in [1, 2] }}
That simplifies things a bit.

Get all first level folders of archive module run

I'm using the archive module to zip some folders and archives.
I need to list all first level folders inside my zip file.
Is there any Jinja or python function to do this using the registered output of the archive module?
My test, so far are:
- name: zip folder
archive:
path:
- /tmp/test/*
dest: /tmp/zipfile.zip
format: zip
mode: "0755"
force_archive: true
register: module_result
Printing module_result, I get:
ok: [host1] =>
msg:
ansible_facts:
discovered_interpreter_python: /usr/bin/python
archived:
- /tmp/test/cfg/1.txt
- /tmp/test/cfg/2.txt
- /tmp/test/cfg/3.txt
- /tmp/test/folder1/folder2/4.txt
- /tmp/test/folder1/folder2/5.txt
Using another task to show my attempts:
- name: list folders
debug:
msg: "{{ item.lstrip(module_result.arcroot).split('/')[0] }}"
# msg: "{{ item }}"
with_items: "{{module_result.archived}}"
I'm trying to cut the first part of the url, using module_result.arcroot on every item that module outputs me and then, a split of the rest of the path, but I get a wrong result. And the worst part is that I'm getting too many times the same folder name.
And the result:
cfg
cfg
cfg
folder1
folder1
My expected result would be:
cfg
folder1
Is this possible?
If your goal is to achieve an unicity on the folders of first level, then the unique Jinja filter is what you are looking for.
Since this filter acts on lists, you would then need to, first, register your results as a list, via the module set_fact.
- name: Making a list out of the files first folder
set_fact:
folders: "{{ folders | default([]) + [(item.lstrip(faked_module_result_arcroot).split('/')[0])] }}"
with_items: "{{ faked_module_result_archived }}"
Then just use the Jinja filter:
- debug:
msg: "{{ folders | unique }}"
Fully working playbook:
---
- hosts: localhost
connection: local
vars:
faked_module_result_archived:
- /tmp/test/cfg/1.txt
- /tmp/test/cfg/2.txt
- /tmp/test/cfg/3.txt
- /tmp/test/folder1/folder2/4.txt
- /tmp/test/folder1/folder2/5.txt
faked_module_result_arcroot: /tmp/test/
tasks:
- name: Making an list out of the files first folder
set_fact:
folders: "{{ folders | default([]) + [(item.lstrip(faked_module_result_arcroot).split('/')[0])] }}"
with_items: "{{ faked_module_result_archived }}"
- debug:
msg: "{{ folders | unique }}"
Would output
PLAY [localhost] ************************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************************
ok: [localhost]
TASK [Making an list out of the files first folder] ************************************************************************************************************************
ok: [localhost] => (item=/tmp/test/cfg/1.txt)
ok: [localhost] => (item=/tmp/test/cfg/2.txt)
ok: [localhost] => (item=/tmp/test/cfg/3.txt)
ok: [localhost] => (item=/tmp/test/folder1/folder2/4.txt)
ok: [localhost] => (item=/tmp/test/folder1/folder2/5.txt)
TASK [debug] ****************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"cfg",
"folder1"
]
}
PLAY RECAP ******************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Your example works great! I could get some tips and tricks to customize mi script, but for some reason, I'm am getting a strange lstrip behavior when I run the adaptation.
Here is my code:
- name: zip a folder
archive:
path:
- /tmp/test/*
dest: /tmp/{{ path_example | basename }}-files.zip
exclude_path:
- /tmp/test/another_folder
format: zip
mode: "0755"
force_archive: true
register: module_result
- name: First, lstrip result
set_fact:
lstrip_result: "{{ lstrip_result | default([]) [(item.lstrip(module_result.arcroot))] }}"
with_items: "{{ module_result.archived }}"
After run "Archive" task:
ok: [desa01] => changed=false
ansible_facts:
discovered_interpreter_python: /usr/bin/python
archived:
- /tmp/test/cfg/1.txt
- /tmp/test/cfg/2.txt
- /tmp/test/cfg/3.txt
- /tmp/test/folder1/folder2/4.txt
- /tmp/test/folder1/folder2/5.txt
arcroot: /tmp/test/
dest: /tmp/lelo/test-files.zip
expanded_exclude_paths:
- /tmp/test/another_folder
After run "First, lstrip result" task:
TASK [task2: First remove arcroot using lstrip ] ********************************************************************************************************
ok: [desa01] => (item=/tmp/test/cfg/1.txt) => changed=false
ansible_facts:
lstrip_result:
- cfg/1.txt
ansible_loop_var: item
item: /tmp/test/cfg/1.txt
ok: [desa01] => (item=/tmp/test/cfg/2.txt) => changed=false
ansible_facts:
lstrip_result:
- cfg/1.txt
- cfg/2.txt
ansible_loop_var: item
item: /tmp/test/cfg/2.txt
ok: [desa01] => (item=/tmp/test/cfg/3.txt) => changed=false
ansible_facts:
lstrip_result:
- cfg/1.txt
- cfg/2.txt
- cfg/3.txt
ansible_loop_var: item
item: /tmp/test/cfg/3.txt
ok: [desa01] => (item=/tmp/test/folder1/folder2/4.txt) => changed=false
ansible_facts:
lstrip_result:
- cfg/1.txt
- cfg/2.txt
- cfg/3.txt
- st/folder1/folder2/4.txt
ansible_loop_var: item
item: /tmp/test/folder1/folder2/4.txt
ok: [desa01] => (item=/tmp/test/folder1/folder2/5.txt) => changed=false
ansible_facts:
lstrip_result:
- cfg/1.txt
- cfg/2.txt
- cfg/3.txt
- st/folder1/folder2/4.txt
- st/folder1/folder2/4.txt
ansible_loop_var: item
item: /tmp/test/folder1/folder2/5.txt
Here is my problem, with folder cfg everything it is ok, but with folder test, lstrip function is like striping 2 chars more than expected and the result is:
st/folder...
instead of:
test/folder...
Someone gets an idea of this strange lstrip out??
EDIT
lstrip doc says:
Definition and Usage
The lstrip() method removes any leading characters (space is the default leading character to remove)
That's why my output was bad striped, becouse some chars where repited in the string.
I replace lstrip with replace
- name: First, lstrip result
set_fact:
lstrip_result: "{{ lstrip_result | default([]) [(item.lstrip(module_result.arcroot))] }}"
with_items: "{{ module_result.archived }}"

Resources