Ansible: shell command returns blank is fatal - shell

I'm trying to run a simple tail command with Ansible. Iterate over 3 strings until all are not found. If found loop over the tail command until all are not found.
- name: Tail the logs for string
shell: "tail -10 /path/to/log/file.log | egrep 'STRING1|STRING2|STRING3'"
register: tail
until: "'STRING1' and 'STRING2' and 'STRING3' not in tail.stdout_lines"
retries: 10
delay: 5
When I run the above task and there's nothing to return, it exits with a fatal error. Even though this is the success case.
fatal: [testserver]: FAILED! => {"changed": true, "cmd": "tail -10 /path/to/log/file.log | egrep 'STRING1|STRING2|STRING3'", "delta": "0:00:00.012770", "end": "2016-09-07 07:44:35.684238", "failed": true, "invocation": {"module_args": {"_raw_params": "tail -10 /path/to/log/file.log | egrep 'STRING1|STRING2|STRING3'", "_uses_shell": true, "chdir": null, "creates": null, "executable": null, "removes": null, "warn": true}, "module_name": "command"}, "rc": 1, "start": "2016-09-07 07:44:35.671468", "stderr": "", "stdout": "", "stdout_lines": [], "warnings": []}
I am unsure why it ends in a fatal state.

grep returns nonzero when it doesn't match anything. If you want to override its exit status:
shell: "tail -10 /path/to/log/file.log | egrep 'STRING1|STRING2|STRING3' ||:"

After resolving the tail command thanks the advice from #charles-duffy the issue remained with the loop condition.
Here is the correct condition:
until: '"STRING1" not in tail.stdout and "STRING2" not in tail.stdout and "STRING3" not in tail.stdout'
I also used tail.stdout instead of tail.stdout_lines
Thanks

Related

ansible: debug only output from tasks?

When i use like ansible-playbook -vvvv, it shows all stdout for all running tasks. Though what it also shows, is noise that shows how each command is run through SSH. Is there a way to use verbosity to just show tasks stdout without any other noise?
Currently it shows like this:
<myhost> ESTABLISH SSH CONNECTION FOR USER: root
<myhost> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=30m -o PreferredAuthentications=publickey -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/tmp/ansible-ssh-%h-%p-%r myhost '/bin/sh -c '"'"'rm -f -r /var/tmp/ansible-tmp-1333333.89364-1154635-13434444/ > /dev/null 2>&1 && sleep 0'"'"''
<myhost> (0, b'', b'OpenSSH_8.2p1 Ubuntu-4ubuntu0.4, OpenSSL 1.1.1f 31 Mar 2020\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files\r\ndebug1: /etc/ssh/ssh_config line 21: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 1136353\r\ndebug3: mux_client_request_session: session request sent\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n')
changed: [dev] => {
"changed": true,
"cmd": [
"/opt/odoo/.local/bin/docker-compose",
"rm",
"-fsv",
"odoo"
],
"delta": "0:00:03.287055",
"end": "2022-01-25 08:09:25.105235",
"invocation": {
"module_args": {
"_raw_params": "/opt/odoo/.local/bin/docker-compose rm -fsv odoo",
"_uses_shell": false,
"argv": null,
"chdir": "/opt/odoo/app",
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": false
}
},
"msg": "",
"rc": 0,
"start": "2022-01-25 08:09:21.818180",
"stderr": "Stopping app_odoo_1 ... \r\nStopping app_odoo_1 ... done\r\nRemoving app_odoo_1 ... \r\nRemoving app_odoo_1 ... done",
"stderr_lines": [
"Stopping app_odoo_1 ... ",
"Stopping app_odoo_1 ... done",
"Removing app_odoo_1 ... ",
"Removing app_odoo_1 ... done"
],
"stdout": "Going to remove app_odoo_1",
"stdout_lines": [
"Going to remove app_odoo_1"
]
}
Is there a way to just keep JSON part without explicitly specifying debug arg for each task?
When using Ansible in Verbose Mode like ansible-playbook -vvvv, it shows how each command is run through SSH and it also shows all stdout for all running tasks.
verbose mode (-vvv for more, -vvvv to enable connection debugging)
This is so far the intended behavior.
Is there a way to use verbosity to just show tasks stdout without any other noise?
Even the parameters -v or -vv producing a kind of noise. This is also the intended behavior since the feature is for debugging functionality outside of tasks. See in example source query vvv or source query verbose.
Is there a way to just keep JSON part without explicitly specifying debug arg for each task?
I am not aware of any feature like that.
Currently I assume it would be necessary to use the debug_module. Or using the Playbook Debugger to Debugging tasks. With that it would be possible to get the stdout of a task only.
Further Readings
How to debug Ansible issues?
Debugging modules
Aside from Ansible there might be a way of filtering the output in example via | grep -A2 stdout or awk 'p; / =>/ {p=1}', sed '0,/ => /d' and than | jq -M -r '.stdout', respective | jq -M -r '.stdout_lines'.
Thanks to
Delete everything before pattern including pattern using awk or sed
Using jq to fetch key value from JSON output

shell script gives incorrect output when invoked using Ansible shell module

I have check.sh script that I wish to run on the target nodes:
cat check.sh
str=`echo $1 | sed -e 's#[\][\]n# #g'`
echo $str>check.row
It is suppose to replace \n with a single white space from the argument and save it in check.row file.
When I run it manually on the target server i get good output results as shown below:
bash -x ./check.sh '/fin/app/01/scripts\\n/fin/app/01/sql'
++ echo '/fin/app/01/scripts\\n/fin/app/01/sql'
++ sed -e 's#[\][\]n# #g'
+ str='/fin/app/01/scripts /fin/app/01/sql'
+ echo /fin/app/01/scripts /fin/app/01/sql
The check.row generated looks good as below:
[user1#remotehost1 ~]$ cat check.row
/fin/app/01/scripts /fin/app/01/sql
However, when i run the same using ansible shell or command module I do not get the expected results.
Below is my playbook:
tasks:
- copy:
src: "{{ playbook_dir }}/files/check.sh"
dest: "~/"
mode: 0754
- set_fact:
install_dir: "{{ hostvars[\'localhost\'][\'command_result\'].stdout.split('\t')[2] }}"
- shell: "bash -x ~/check.sh '{{ install_dir }}' > ~/check_rollback.log"
See ansible's debug output below:
changed: [10.8.44.55] => {
"changed": true,
"cmd": "bash -x ~/check.sh '/fin/app/01/scripts\\n/fin/app/01/sql' > ~/check_rollback.log",
"delta": "0:00:00.118943",
"end": "2019-09-04 10:50:16.503745",
"invocation": {
"module_args": {
"_raw_params": "bash -x ~/check.sh '/fin/app/01/scripts\\n/fin/app/01/sql' > ~/check_rollback.log",
"_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
}
},
"rc": 0,
"start": "2019-09-04 10:50:16.384802",
"stderr": "++ echo '/fin/app/01/scripts\\n/fin/app/01/sql'\n++ sed -e 's#[\\][\\]n# #g'\n+ str='/fin/app/01/scripts\\n/fin/app/01/sql'\n+ echo '/fin/app/01/scripts\\n/fin/app/01/sql'",
"stderr_lines": [
"++ echo '/fin/app/01/scripts\\n/fin/app/01/sql'",
"++ sed -e 's#[\\][\\]n# #g'",
"+ str='/fin/app/01/scripts\\n/fin/app/01/sql'",
"+ echo '/fin/app/01/scripts\\n/fin/app/01/sql'"
],
"stdout": "",
"stdout_lines": [] }
And here is the check.row file output from ansible's run:
[user1#remotehost1 ~]$ cat check.row
/fin/app/01/scripts\\n/fin/app/01/sql
As you can instead of single whitespace it is now printing \n.
I am on the latest version of ansible.
One can replicate this issue easily. Can you please suggest why am I getting this issue and how to fix this?
First of all, you are using the shell module in which only the shell command is specified, you have incorrectly used bash in it.
shell: "bash -x ~/check.sh '{{ install_dir }}' > ~/check_rollback.log"
Secondly, as you can see your task has resulted in an error as seen in your attached output.
Stdout is empty and we can see an error in stderr.
Thirdly, If you want to use bash you can use command module, as shown below,
- command: "bash -x ~/check.sh '{{ install_dir }}' > ~/check_rollback.log"
I also suggest the following changes in your check.sh script,
#!/bin/bash
echo $1 # You can check the value that is passed to the script
str=$(echo "$1" | sed -e 's/\\n/ /g') # Use quotes around your variable
echo "$str" > check.row
And it is working fine.

ansible diff ignore order of lines

The diff command entered directly form Ubuntu provides the the expected output, But when its entered via playbook it gives the following errors:
ubuntu#ubuntu:~$ diff <(sort CSR1_route.txt) <(sort CSR2_route.txt) >> delta1.txt ubuntu#ubuntu:~$ more delta1.txt 13c13 < O 2.2.2.2 [110/2] via 192.168.255.134, 01:36:31, GigabitEthernet1
---
> O 1.2.2.2 [110/2] via 192.168.255.134, 01:36:31, GigabitEthernet1
- name: testing_diff shell: diff <(sort CSR1_route.txt) <(sort CSR2_route.txt) >> delta4.txt
fatal: [CSR1]: FAILED! => {
"changed": true,
"cmd": "diff <(sort CSR1_route.txt) <(sort CSR2_route.txt) >> delta4.txt",
"delta": "0:00:00.001697",
"end": "2018-12-05 16:34:59.665114",
"invocation": {
"module_args": {
"_raw_params": "diff <(sort CSR1_route.txt) <(sort CSR2_route.txt) >> delta4.txt",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"msg": "non-zero return code",
"rc": 2,
"start": "2018-12-05 16:34:59.663417",
"stderr": "/bin/sh: 1: Syntax error: \"(\" unexpected",
"stderr_lines": [
"/bin/sh: 1: Syntax error: \"(\" unexpected"
],
"stdout": "",
"stdout_lines": []
Solution 1 -
- name: testing_diff
shell: |
diff file1 file2 >> delta4.txt
Solution 2 -
- name: testing_diff
shell: |
diff file1 file2 >> delta4.txt
args:
executable: /bin/bash
Solution 3 -
If not try the command module.

How to pass multiple executable in ansible within a shell module?

I am trying to pass a prompt y in ansible when it executes command.
When i do manually on server it asks for a prompt.
The issue is for command to run i need to pass the executable /bin/bash
command: source /etc/profile.d/tableau_server.sh && tsm pending-changes apply for expect command to run i need to pass /usr/bin/expect .
My question, how can i pass 2 executable in ansible such that for command it uses /bin/bash and for expect prompt it should use /usr/bin/expect and the error is because i am using source, what is an alternative i can use?
Update: I dont know why but i am not able to pass --ignore-prompt , It gives an error
ubuntu#ip-xx-xxx-xx-xx:~$ tsm pending-changes apply --ignore-prompt
Unrecognized option: --ignore-prompt
Please help me with a solution!
ubuntu#ip-xx-xxx-xx-xx:~$ tsm pending-changes apply
This operation will perform a server restart. Are you sure you wish to continue?
(y/n):
My ansible script:
shell: |
source /etc/profile.d/tableau_server.sh && tsm pending-changes apply
expect "This operation will perform a server restart. Are you sure you wish to continue?\n(y/n):"
send "y\n"
exit 0
args:
executable: /usr/bin/expect
args:
executable: /bin/bash/expect
when: inventory_hostname == "xx.xxx.xx.xx"
ERROR:
changed: [xx.xxx.xxx.xx] => {
"changed": true,
"cmd": "source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\n expect \"This operation will perform a server restart. Are you sure you wish to continue?\\n(y/n):\"\n send \"y\\n\"\n exit 0",
"delta": "0:00:00.034824",
"end": "2018-08-20 17:29:41.457700",
"invocation": {
"module_args": {
"_raw_params": "source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\n expect \"This operation will perform a server restart. Are you sure you wish to continue?\\n(y/n):\"\n send \"y\\n\"\n exit 0",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": "/usr/bin/expect",
"removes": null,
"stdin": null,
"warn": true
}
},
"rc": 0,
"start": "2018-08-20 17:29:41.422876",
"stderr": "wrong # args: should be \"source ?-encoding name? fileName\"\n while executing\n\"source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\"",
"stderr_lines": [
"wrong # args: should be \"source ?-encoding name? fileName\"",
" while executing",
"\"source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\""
],
"stdout": "",
"stdout_lines": []
I would say you are doing far too much with bash commands and '&&' inside command, none of this feels idempotent.
Can I recommend going back to the drawing board with this. I would recommend creating the command using the 'creates' parameter so it can tell if it needs to run.
https://docs.ansible.com/ansible/2.6/modules/command_module.html
Or alternatively check before hand which will then see if the command needs running using register.
In this instance of your issue with the:
tsm pending-changes apply
should support as per https://onlinehelp.tableau.com/current/server-linux/en-us/cli_pending-changes.htm
tsm pending-changes apply --ignore-prompt
which will then not prompt for a yes and will not need the expect module.
I solved my issue by passing an -r option.
- name: Initialize and Start Tableau Server
shell: source /etc/profile.d/tableau_server.sh && tsm pending-changes apply -r -u ubuntu -p '{{ tableau_server_admin_password }}'
args:
executable: /bin/bash
when: inventory_hostname == "xx.xxx.xx.xx"

Chaining a star sign in Ansible playbook

Lately, I've started to use Ansible, and i need some help with a playbook.
I'm trying to get a device details as a parameter, and then chain a star sign for it, in order to run a shell command. For example, if the device is /etc/sda
, the shell command will be ls -l /dev/sda*.
- name: Get Hitachi Devices device details
shell: lsscsi | grep HITACHI | awk '{print $6}'
register: hitachiDevice
- name: Check if the volume is partitioned
shell: ls -l "{{ hitachiDevice.rc }}"* | wc -l
failed_when: hitachiDevice.rc != 1
Here is the error i get after running the script:
{
"changed": true,
"cmd": "ls -l \"0\"* | wc -l",
"delta": "0:00:00.027338",
"end": "2016-08-10 14:15:12.200415",
"failed": true,
"failed_when_result": true,
"rc": 0,
"start": "2016-08-10 14:15:12.173077",
"stderr": "ls: cannot access 0*: No such file or directory",
"stdout": "0",
"stdout_lines": [
"0"
],
"warnings": []
}
Any one know what is the issue and how can i fix it?
hitachiDevice.rc contains result code of previous command.
I bet it is always 0 if grep is successful.
So you next command will almost always will be ls -l 0* | wc -l which gives you the error
ls: cannot access 0*: No such file or directory
I think you need something like hitachiDevice.stdout, depending on what you want to achieve.

Resources