Team,
how can I avoid this huge output and just get the value I want from every item in the results list?
I just want the item value that is hostname to be shown and only the line from stdout for every host.
- name: "Mount count on GPU Nodes"
shell: "mount | grep -Ec '/dev/sd.*\\<csi' | awk '{ print $0,\"mounts found on $HOSTNAME\"($0>64? \" that are more than 64.\" : \".\") }'"
register: mounts_count
changed_when: false
failed_when:
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-gpu-node'] }}"
- name: Check if csi related mounts are present on gpu nodes
assert:
that:
- item.stdout is search('64')
fail_msg: " mounts are present on this node"
success_msg: "mounts are not present on this node"
loop: "{{ mounts_count.results }}"
loop_control:
label: "{{ item.item }}"
ignore_errors: yes
sample output
TASK [team-services-pre-install-checks : Check if csi related mounts are present on gpu nodes] ***
Wednesday 18 December 2019 22:47:17 +0000 (0:00:07.012) 0:00:09.110 ****
failed: [localhost] (item=node1) => {
"ansible_loop_var": "item",
"assertion": "item.stdout is search('0')",
"changed": false,
"evaluated_to": false,
"item": {
"ansible_loop_var": "item",
"changed": false,
"cmd": "mount | grep -Ec '/dev/sd.*\\<csi' | awk '{ print $0,\"mounts found on hostname\"($0>64? \" that are more than 64.\" : \".\") }'",
"delta": "0:00:00.102618",
"end": "2019-12-18 22:47:12.566399",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "mount | grep -Ec '/dev/sd.*\\<csi' | awk '{ print $0,\"mounts found on hostname\"($0>64? \" that are more than 64.\" : \".\") }'",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "node1",
"rc": 0,
"start": "2019-12-18 22:47:12.463781",
"stderr": "",
"stderr_lines": [],
"stdout": "1 mounts found on hostname.",
"stdout_lines": [
"1 mounts found on hostname."
]
},
"msg": " mounts are present on this node"
}
ok: [localhost] => (item=node2) => {
"ansible_loop_var": "item",
"changed": false,
"item": {
"ansible_loop_var": "item",
"changed": false,
"cmd": "mount | grep -Ec '/dev/sd.*\\<csi' | awk '{ print $0,\"mounts found on hostname\"($0>64? \" that are more than 64.\" : \".\") }'",
"delta": "0:00:00.109244",
"end": "2019-12-18 22:47:14.303305",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "mount | grep -Ec '/dev/sd.*\\<csi' | awk '{ print $0,\"mounts found on hostname\"($0>64? \" that are more than 64.\" : \".\") }'",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "node2",
"rc": 0,
"start": "2019-12-18 22:47:14.194061",
"stderr": "",
"stderr_lines": [],
"stdout": "0 mounts found on hostname.",
"stdout_lines": [
"0 mounts found on hostname."
]
},
"msg": "mounts are not present on this node"
}
expected output: below is just a manual sample i made but any thing that shows hostname and stdout_line is good enough.
"item": "node1"
0 mounts found on hostname.
"item": "node2"
0 mounts found on hostname.
Ansible isn't really designed to produce "nice" output on the console. If you want information produced in a specific format, you are better off generating a file from a template.
That said, the assert module is designed primarily as a debugging tool and generates verbose output. I think you could get the behavior you want using the fail module instead:
- name: Check if csi related mounts are present on gpu nodes
fail:
msg: " mounts are present on this node"
when: item.stdout is not search('64')
loop: "{{ mounts_count.results }}"
loop_control:
label: "{{ item.item }}"
ignore_errors: yes
This will simply "skip" items for which the when condition is false.
Related
I have a task in ansible that greps a list of emails in a loop. I am trying to get the "stdout_lines" output of this.
vars:
emails:
- test#email.com
tasks:
- name: search
register: found
shell: "grep -i {{item}} ~/file"
with_items: "{{emails}}"
debug:
msg: "{{found.results}}"
The above displays the following:
"msg": {
"changed": true,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "grep -i test#email.com ~/file",
"delta": "0:00:00.003060",
"end": "2020-10-19 12:52:29.930458",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "grep -i test#email.com ~/x",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "test#email.com",
"rc": 0,
"start": "2020-10-19 12:52:29.927398",
"stderr": "",
"stderr_lines": [],
"stdout": "test#email.com line 1\ntest#email.com line2",
"stdout_lines": [
"test#email.com line 1",
"test#email.com line 2"
]
}
]
}
And when I try
debug:
msg: "{{found.results.stdout_lines}}"
I get the error
fatal: [127.0.0.1]: FAILED! => {"msg": "The task includes an option
with an undefined variable. The error was: 'list object' has no
attribute 'stdout_lines'
I should also note that I run into issues when I have more than one email in the list, but one problem at at time.
When you run an Ansible module in a loop, like you have done here with the shell module, the results key is a list (docs).
When you use register with a loop, the data structure placed in the variable will contain a results attribute that is a list of all responses from the module. This differs from the data structure returned when using register without a loop.
Since found.results is a list of objects it doesn't have a stdout_lines key itself, rather each of its items do. Depending on what you need you could access the key on the first item:
found.results[0].stdout_lines
You could loop over each item again and do something with each:
- name: Do something with output
example:
var: "{{ item }}"
loop: found.results
You could run the map filter over it, for example to concatenate the lines in each stdout_lines item:
found.results | map(attribute='stdout_lines') | join('\n')
I am running the deployment concurrently on a number of hosts. As can be expected, the output moves quickly during runtime and it is hard to track at what state each task ends. When I get to the end of the playbook I can see which host have failed which is great. However, I need to scroll through pages and pages of output in order to find out on which task did a certain host fail.
Is there a way to have a print out at the end of the playbook saying for example:
"Host 1 failed on Task 1/cmd"
Don't know if this fits your issue exactly, but you can help yourself with a little exception handling like this:
---
- hosts: localhost
any_errors_fatal: true
tasks:
- block:
- name: "Fail a command"
shell: |
rm someNonExistentFile
rescue:
- debug:
msg: "{{ ansible_failed_result }}"
#- fail:
# msg: "Playbook run failed. Aborting..."
# uncomment this failed section to actually fail a deployment after writing the error message
The variable ansible_failed_result contains something like this:
TASK [debug] ************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"changed": true,
"cmd": "rm someNonExistentFile\n",
"delta": "0:00:00.036509",
"end": "2019-10-31 12:06:09.579806",
"failed": true,
"invocation": {
"module_args": {
"_raw_params": "rm someNonExistentFile\n",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"msg": "non-zero return code",
"rc": 1,
"start": "2019-10-31 12:06:09.543297",
"stderr": "rm: cannot remove ‘someNonExistentFile’: No such file or directory",
"stderr_lines": [
"rm: cannot remove ‘someNonExistentFile’: No such file or directory"
],
"stdout": "",
"stdout_lines": [],
"warnings": [
"Consider using the file module with state=absent rather than running 'rm'. If you need to use command because file is insufficient you can add 'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message."
]
}
}
I mostly use stderr when applicable. Otherwise I use "{{ ansible_failed_result | to_nice_json }}".
hth
I have written the following ansible playbook to find the disk failure on the raid
- name: checking raid status
shell: "cat /proc/mdstat | grep nvme"
register: "array_check"
- debug:
msg: "{{ array_check.stdout_lines }}"
Following is the output I got
"msg": [
"md0 : active raid1 nvme0n1p1[0] nvme1n1p1[1]",
"md2 : active raid1 nvme1n1p3[1](F) nvme0n1p3[0]",
"md1 : active raid1 nvme1n1p2[1] nvme0n1p2[0]"
]
I want to extract the disk name which is failed from the register variable array_check.
How do I do this in the ansible? Can I use set_fact module in ansible? Can I use grep, awk, sed command on the register variable array_check
This is the playbook I am using to check the health status of a drive using smartctl
- name: checking the smartctl logs
shell: "smartctl -H /dev/{{ item }}"
with_items:
- nvme0
- nvme1
And I am facing the following error
(item=nvme0) => {"changed": true, "cmd": "smartctl -H /dev/nvme0", "delta": "0:00:00.090760", "end": "2019-09-05 11:21:17.035173", "failed": true, "item": "nvme0", "rc": 127, "start": "2019-09-05 11:21:16.944413", "stderr": "/bin/sh: 1: smartctl: not found", "stdout": "", "stdout_lines": [], "warnings": []}
(item=nvme1) => {"changed": true, "cmd": "smartctl -H /dev/nvme1", "delta": "0:00:00.086596", "end": "2019-09-05 11:21:17.654036", "failed": true, "item": "nvme1", "rc": 127, "start": "2019-09-05 11:21:17.567440", "stderr": "/bin/sh: 1: smartctl: not found", "stdout": "", "stdout_lines": [], "warnings": []}
The desired output should be something like this,
=== START OF SMART DATA SECTION ===
SMART overall-health self-assessment test result: PASSED
Below is the complete playbook including the logic to execute multiple commands in a single task using with_items,
---
- hosts: raid_host
remote_user: ansible
become: yes
become_method: sudo
tasks:
- name: checking raid status
shell: "cat /proc/mdstat | grep 'F' | cut -d' ' -f6 | cut -d'[' -f1"
register: "array_check"
- debug:
msg: "{{ array_check.stdout_lines }}"
- name: checking the samrtctl logs for the drive
shell: "/usr/sbin/smartctl -H /dev/{{ item }} | tail -2|awk -F' ' '{print $6}'"
with_items:
- "nvme0"
- "nvme1"
register: "smartctl_status"
I have a problem for manipulating data in Ansible.
My main goal is to purge ADRCI log.
In a first time, i'm listing the databases that are launched with users Oracle and Grid with the shell module
- name: Generate List of databases started with user oracle
shell: "ps -ef | grep smon | grep -v grep | grep oracle | awk '{print $NF}' | awk -F '_' '{print $NF}'"
register: list_oracle_bases
Then i want to get the homes to purge with an other shell command :
- name: Get databases homes
register: get_homes
environment:
ORACLE_SID: "{{ item }}"
ORAENV_ASK: "NO"
become: true
become_user: oracle
become_method: su
shell: ". oraenv 1>/dev/null 2>&- && adrci exec='show homes' | grep -v 'ADR Homes:'"
with_items:
- "{{ list_oracle_bases.stdout_lines }}"
And here comes my problem i've got this result for {{ get_homes }}
ok: [host1] => {
"msg": {
"changed": true,
"msg": "All items completed",
"results": [
{
"_ansible_ignore_errors": null,
"_ansible_item_label": "ANSIBLE1",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": true,
"cmd": ". oraenv 1>/dev/null 2>&- && adrci exec='show homes' | grep -v 'ADR Homes:'",
"delta": "0:00:00.028970",
"end": "2019-03-18 11:29:29.708709",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": ". oraenv 1>/dev/null 2>&- && adrci exec='show homes' | grep -v 'ADR Homes:'",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": "ANSIBLE1",
"rc": 0,
"start": "2019-03-18 11:29:29.679739",
"stderr": "",
"stderr_lines": [],
"stdout": "diag/rdbms/ansible1/ANSIBLE1",
"stdout_lines": [
"diag/rdbms/ansible1/ANSIBLE1"
]
}
]
}
}
I tried to get get_homes.results.stdout_lines but i've got the following error when i want to display it in a debug statement.
FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'stdout_lines'\n\nThe error appears to have been in '/tmp/sylvain/roles/test/tasks/query.yml': line 59, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:\n ^ here\n"}
What is the correct way to get that stdout_lines ("diag/rdbms/ansible1/ANSIBLE1") in a variable.
Regards
Sylvain
results is an array, because you have created it in a loop (with_items). The correct syntax is:
get_homes.results[0].stdout_lines
for the first and so on for the rest.
In order to get the list of home directories you have to filter the results array by the attribute you need. And in the end you have to flatten the array of arrays. Here is a mcve:
---
- name: filter
hosts: localhost
connection: local
tasks:
- shell: |-
echo ~{{ item }}
with_items:
- man
- lp
- mail
register: get_homes
ignore_errors: true
changed_when: false
- debug: var=get_homes
- set_fact:
homes: >-
{{ get_homes.results | map(attribute='stdout_lines') | list | flatten }}
- debug: var=homes
I am using a package specific command for which there is no existing module. The output (stdout) of the command is either 0 or 1. I'm trying assign the value of stdout to a variable for later use in the playbook. However, I cannot seem to isolate the stdout, which is clearly being captured.
ok: [hostname1] => {
"dmstate": {
"changed": true,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": true,
"cmd": "/usr/bin/ssh -q hostname1 \"/opt/REDACTED/bin/dmctl -s localhost:8426/dmbroker get DomainManager::hostname1-ampm1::state\"",
"delta": "0:00:01.808716",
"end": "2017-09-06 13:28:04.853221",
"invocation": {
"module_args": {
"_raw_params": "/usr/bin/ssh -q hostname1 \"/opt/REDACTED/bin/dmctl -s localhost:8426/dmbroker get DomainManager::hostname1-ampm1::state\"",
"_uses_shell": true,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"warn": true
}
},
"item": "hostname1-ampm1",
"rc": 0,
"start": "2017-09-06 13:28:03.044505",
"stderr": "",
"stderr_lines": [],
"stdout": "0",
"stdout_lines": [
"0"
]
}
]
}
}
What I tried was using set_fact: to isolate and search the dmstate.results, but I cannot figure out the correct syntax.
tasks:
- name: check status
shell: '/usr/bin/ssh -q {{ inventory_hostname }} "/opt/REDACTED/bin/dmctl -s localhost:8426/dmbroker get DomainManager::{{ item }}::state" '
register: dmstate
with_items: "{{ myprocess }}"
- name: find stdout value
set_fact: stdout_value="{{ item.stdout }}"
when: item.dmstate.results.stdout == "stdout"
with_items: dmstate.results
- name: show value of stdout
debug: var=stdout_value
The results of this are here:
fatal: [hostname1]: FAILED! => {"failed": true, "msg": "The conditional check 'item.dmstate.results.stdout == \"stdout\"' failed. The error was: error while evaluating conditional (item.dmstate.results.stdout == \"stdout\"): 'AnsibleUnsafeText' object has no attribute 'dmstate'\n\n
Once you are using with_items, item contains the inner value, so item.dmstate.results.stdout has no sense, you have to use item.stdout (like you do in the set_fact).
Even with this fix, your item will be skipped every time because you are testing the stdout value against the "stdout" string, but you say that stdout is only "0" or "1".
I think you could simply remove the when clause.
Another problem: each item will overwrite the value of stdout_value, so at the end you will only get the last value.