Ansible: Accumulate mulitple process output - ansible

I have the following playbook:
---
- hosts: localhost
gather_facts: false
vars:
db_process:
- mysqld
- db2sys
- oracle*
- pmon
tasks:
- shell: ps -ef | grep '{{ item }}' | grep -v grep | wc -l ; pgrep -x '{{ item }}' | wc -l
loop: "{{ db_process }}"
changed_when: false
register: ps_out
- debug:
msg: "{{ item }}"
loop: "{{ ps_out.results }}"
- name: combine output
set_fact:
db_process_status: '{% if item.stdout_lines[0]|int == 0 and item.stdout_lines[1]|int == 0 %}pass{% else %}fail{% endif %}'
db_process_message: '{{ item.item }} {% if item.stdout_lines[0]|int == 0 and item.stdout_lines[1]|int == 0 %}Not Found{% else %}Found{% endif %}'
loop: "{{ ps_out.results }}"
After this task, i have to combine all the result of db_process_message into a proper JSON format.
Instead of grouping, Is there some way to accumulate the output across multiple process and register that to a variable?
Expected output:
[
"mysqld Not Found",
"db2sys Not Found",
"oracle Not Found",
"pmon Not Found"
]

In your set_fact task, you are overwritting in each iteration over the loop variable the values of db_process_status and db_process_message. Instead of that, you need to populate two lists, one for each variable, so that you keep their values from each loop run.
here is the task that can do it, your code was moved to the var1 and var2 lines, and the new code is appending these values to two lists:
task:
- name: combine output
set_fact:
db_process_status: '{{ db_process_status|default([]) + [var1] }}'
db_process_message: '{{ db_process_message|default([]) + [var2] }}'
vars:
var1: '{% if item.stdout_lines[0]|int == 0 and item.stdout_lines[1]|int == 0 %}pass{% else %}fail{% endif %}'
var2: '{{ item.item }} {% if item.stdout_lines[0]|int == 0 and item.stdout_lines[1]|int == 0 %}Not Found{% else %}Found{% endif %}'
loop: "{{ ps_out.results }}"
- name: print db_process_status
debug:
var: db_process_status
- name: print db_process_message
debug:
var: db_process_message
sample output (i added sshd to demonstrate it picks it up as running):
TASK [print db_process_status] ****************************************************************************************************************************************************************************************
ok: [localhost] => {
"db_process_status": [
"pass",
"pass",
"pass",
"pass",
"fail"
]
}
TASK [print db_process_message] ***************************************************************************************************************************************************************************************
ok: [localhost] => {
"db_process_message": [
"mysqld Not Found",
"db2sys Not Found",
"oracle* Not Found",
"pmon Not Found",
"sshd Found"
]
}

Related

Loop over list and evaluate element property in Ansible

I have a list with shell lines that I want to execute on inventory hosts so I can determine if the database is working. For the test purposes I have 1 server with PostgreSQL and 1 with MySQL.
This is my playbook so far:
- name: Check db statuses
shell: "{{ item }}"
loop:
- ps -fp $(pgrep -u postgres) | grep /usr/lib/postgresql
- ps -fp $(pgrep -u mysql) | grep mysqld
register: http
ignore_errors: yes
changed_when: item.failed == false
this is failing with:
{
"http": {
"failed": true,
"msg": "The conditional check 'item.failed == false' failed. The error was: error while evaluating conditional (item.failed == false): 'ansible.parsing.yaml.objects.AnsibleUnicode object' has no attribute 'failed'"
}
}
I want to assign only the item.failed==false result in the register variable (http) but ignore the failed ones.
You can't select what will be registered in a loop. Instead, you'll have to evaluate the registered results in the next task(s), e.g.
- hosts: localhost
tasks:
- command: "{{ item }}"
loop:
- /bin/true
- /bin/false
register: http
ignore_errors: true
- debug:
msg: "{{ item.item }} failed: {{ item.failed }}"
loop: "{{ http.results }}"
loop_control:
label: "{{ item.cmd }}"
gives
ok: [localhost] => (item=['/bin/true']) =>
msg: '/bin/true failed: False'
ok: [localhost] => (item=['/bin/false']) =>
msg: '/bin/false failed: True'

Iterate Over 2 dictionary in ansible

I Have 2 dictionary:
- Test1:
1: pass
2: fail
3: pass
- Test2:
1.1.1.1: val1
2.2.2.2: val2
3.3.3.3: val3
Condition is when Test1.value contians fail
- name: test
debug:
msg: "{{item.1.value}} {{item.1.key}} {{item.0.key}} {{item.0.value}}"
with_together:
- "{{Test1}}"
- "{{Test2}}"
when: item.0.value == "fail"
This is not working as expected unable to get both key and value of 2 dict in one loop
In when statement you must to use item.0 or item.1 to evaluate the condition. And I recommend you use a list in with_together loop and if you are using a variable you have to use braces {{ variable }} .
Try as below:
- name: test
debug:
msg: "{{item.1 }}"
with_together:
- "{{ Test1.values() | list }}"
- "{{ Test2.values() | list }}"
when: item.0 == "fail"
You'll get
TASK [test] *******************************************************************************************************************************************************************************************************
skipping: [127.0.0.1] => (item=['pass', 'val1'])
ok: [127.0.0.1] => (item=['fail', 'val2']) => {
"msg": "val2"
}
skipping: [127.0.0.1] => (item=['pass', 'val3'])
I achieved this by :
converting dict to list using filter -> |list
since
both dict of same size I was able to get data of both dict in single loop:
- name: test
debug:
msg: "{{item.0}} {{item.1}} {{item.2}} {{item.3}}"
with_together:
- "{{ Test1.values() | list }}"
- "{{ Test2.values() | list }}"
- "{{ Test1.keys() | list }}"
- "{{ Test2.keys() | list }}"
when: item.0 == "fail"

Conditional set_fact doesn't evaluate

I'm looking to set a variable based on the outcome of the following evaluation: ({{ found_files.files|length > 0 }}), however, it doesn't appear to be evaluated fully. What could be the issue?
Desired outcome:found_files2: /dev/null
Outcome:
TASK [debug] ********************************************************************************************************************************************************************************
ok: [localhost] => {
"found_files2": "{'files': [], 'changed': False, 'msg': '', 'matched': 0, 'examined': 0, 'failed': False} if (False) else '/dev/null'"
}
Playbook:
---
- hosts: localhost
gather_facts: no
vars:
backup_dir: /home/user1/projects/ansible/modules/backups/router2
tasks:
- name: get files in backups/<router name>/
delegate_to: localhost
find:
paths: "{{ backup_dir }}"
register: found_files
- set_fact:
found_files2: "{{ found_files }} if ({{ found_files.files|length > 0 }}) else '/dev/null'"
- debug: var=found_files2
The correct syntax is below
- set_fact:
found_files2: "{{ found_files if (found_files.files|length > 0) else '/dev/null' }}"
The filter ternary gives the same result
- set_fact:
found_files2: "{{ (found_files.files|length > 0)|ternary(found_files, '/dev/null') }}"

Ansible escape string \

Trying to add \ before . on a list of IPs in Ansible.
Example:
"msg": "192.168.5.0"
Expected output:
"msg": "192\.168\.5\.0"
I tried this with no luck.
---
- hosts: localhost
vars:
ip: "{{ ansible_default_ipv4.network }}"
tasks:
- debug: msg="{{ ip | regex_replace('\.', '\\.')}}"
Output:
ok: [localhost] => {
"msg": "192\\.168\\.5\\.0"
}
---
- hosts: localhost
vars:
ip: "{{ ansible_default_ipv4.network }}"
tasks:
- debug: msg="{{ ip | regex_replace('.', '\.')}}"
Output:
ok: [localhost] => {
"msg": "192\\.168\\.5\\.0"
}
The function regex_replace works as you expect. The double backslashes you see are the evaluation of the string by debug. Display, for example, the length of the string
- set_fact:
ip2: "{{ ip | regex_replace(myregex, myreplace) }}"
vars:
myregex: '\.'
myreplace: '\.'
- debug:
msg: "length of {{ ip2 }} is {{ ip2|length }}"
give
"msg": "length of 192\\.168\\.5\\.0 is 14"
It's also possible to write the string to a file. For example the template
shell> cat test.txt.j2
{{ ip2 }}
and the task
- template:
src: test.txt.j2
dest: test.txt
give
shell> cat test.txt
192\.168\.5\.0

ansible ssh to json_query response values in loop

Team, I have response from json_query which is a dict key:value and i would like to iterate over all values and run ssh command for each value
Below gets me list of all nodes
- name: "Fetch all nodes from clusters using K8s facts"
k8s_facts:
kubeconfig: $WORKSPACE
kind: Node
verify_ssl: no
register: node_list
- debug:
var: node_list | json_query(query)
vars:
query: 'resources[].{node_name: metadata.name, nodeType: metadata.labels.nodeType}'
TASK [3_validations_on_ssh : debug]
ok: [target1] => {
"node_list | json_query(query)": [
{
"nodeType": null,
"node_name": "host1"
},
{
"nodeType": "gpu",
"node_name": "host2"
},
{
"nodeType": "gpu",
"node_name": "host3"
}
]
}
playbook to write: parse node_name and use that in ssh command for all hosts 1-3
- name: "Loop on all nodeNames and ssh."
shell: ssh -F ~/.ssh/ssh_config bouncer#{{ item }}.internal.sshproxy.net "name -a"
register: ssh_result_per_host
failed_when: ssh_result_per_host.rc != 0
with_item: {{ for items in query.node_name }}
- debug:
var: ssh_result_per_host.stdout_lines
error output:
> The offending line appears to be:
failed_when: ssh_result_per_host.rc != 0
with_item: {{ for items in query.node_name }}
^ here
solution 2 also fails when i do loop:
shell: ssh -F ~/.ssh/ssh_config bouncer#{{ item.metadata.name }}.sshproxy.internal.net "name -a"
loop: "{{ node_list.resources }}"
loop_control:
label: "{{ item.metadata.name }}"
output sol 2:
failed: [target1] (item=host1) => {"msg": "Invalid options for debug: shell"}
failed: [target1] (item=host2) => {"msg": "Invalid options for debug: shell"}
fatal: [target1]: FAILED! => {"msg": "All items completed"}
To expand on Ash's correct answer:
- name: "Fetch all nodes from clusters using K8s facts"
k8s_facts:
kubeconfig: $WORKSPACE
kind: Node
verify_ssl: no
register: node_list
- set_fact:
k8s_node_names: '{{ node_list | json_query(query) | map(attribute="node_name") | list }}'
vars:
query: 'resources[].{node_name: metadata.name, nodeType: metadata.labels.nodeType}'
- name: "Loop on all nodeNames and ssh."
shell: ssh -F ~/.ssh/ssh_config bouncer#{{ item }}.internal.sshproxy.net "name -a"
register: ssh_result_per_host
with_items: '{{ k8s_node_names }}'
Separately, unless you quite literally just want to run that one ssh command, the way ansible thinks about that problem is via add_host::
- hosts: localhost
connection: local
gather_facts: no
tasks:
# ... as before, to generate the "k8s_node_names" list
- add_host:
hostname: '{{ item }}.internal.sshproxy.net'
groups:
- the_k8s_nodes
ansible_ssh_username: bouncer
# whatever other per-host variables you want
with_items: '{{ k8s_node_names }}'
# now, run the playbook against those nodes
- hosts: the_k8s_nodes
tasks:
- name: run "name -a" on the host
command: name -a
register: whatever
This is a contrived example, because if you actually wanted just to get the list of kubernetes nodes and use those in a playbook, you use would a dynamic inventory script
It is not with_item it is with_items
You cannot use with_item: {{ for items in query.node_name }}
Save the values to a variable and use the variable in with_items: {{ new_variable }}

Resources