Can ansible ad-hoc tolerate some hosts failures? - ansible

I know ansible playbooks can set max_fail_percentage to allow the playbook to progress if at least that percentage of the hosts succeeded. However, I wanted to run an ad-hoc command that succeded (exit status 0) if at least a percentage of the hosts executed without errors. Is it possible?

If you have a playbook that affects say 10 hosts and at some point during execution it fails on 1 host, Ansible will simply continue (if you don't set max_fail_percentage at all) on all other hosts. This is default behaviour, generally playbooks will stop executing any more steps on a host that has a failure.
This is mentioned also in Ansible docs: Ansible - max_failure_percentage
This behaviour is exactly the same for ad hoc commands.
Test, test, test...
EDIT:
Just Ansible will not do this, however you can override exit status by piping Ansible's output to for example perl one-liner and exit with a different code there, it's quite ugly but works :)
See example below, it exits with 0 only if > 65% of hosts succeeded, otherwise exit code is 2.
In order to catch failures and parse them somehow you need to redirect STDERR to STDOUT from ansible command (thus 2>&1 at the end of the Ansible command, Perl will not see it otherwise)
$ ansible all -i provisioning/vagrant-inventory -u vagrant --private-key=~/.vagrant.d/insecure_private_key -m ping 2>&1 | perl -pe 'BEGIN { $failed=0; $success=0;} END { $exit_code=( $success/($success+$failed) ) > 0.65 ? 0 : 2; exit $exit_code;} $failed++ if /\| FAILED/i; $success++ if /\| success/i;'
192.168.111.210 | success >> {
"changed": false,
"ping": "pong"
}
192.168.111.200 | success >> {
"changed": false,
"ping": "pong"
}
192.168.111.211 | FAILED => SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh
$ echo $?
0

Related

How do I check the succesfull retry with separate command in ansible?

Ansible 2.9.6
There is standard way to use retry in Ansible.
- name: run my command
register: result
retries: 5
delay: 60
until: result.rc == 0
shell:
cmd: >
mycommand.sh
until is a passive check here.
How can I do the check with the separate command? Like "retry command A several times until command B return 0"
Of cause I may put both commands inside shell execution "commandA ; commandB" and I will get exit status of the second one for the result.rc. But is any Ansible way to do this?
The ansible way would point towards retries over a block or include that contains a command task for each script. However, that's not supported. You can use a modified version of the workaround described here, though, but it starts to get complicated. Therefore, you may prefer to take Zeitounator's suggestion.

Filter/extract a whole line from my stdout in ansible

I am running my shell script from ansible playbook using command module. My playbook prints some messages & error messages like(ansible console output):
rc: 1
start: '2020-04-30 10:42:44.165313'
stderr: ''
stderr_lines: <omitted>
stdout: |-
User verified
Ping test okay
ERROR!!! Unable to connect to machine..Aborted...:Error741
I captured the command output in register variable(output_1). Now I want to extract 'ERROR' message line from above output. I used regex_search(output_1.stdout | regex_search('Error741')) but that is giving me exact word(say Error741) not whole line.
My expected output:-
"ERROR!!! Unable to connect to machine.. exiting....:Error741"
You can modify the regex like this.
{{ output_1.stdout | regex_search('.*Error741') }}
With the stdout text in your example, this regex will return below line.
ERROR!!! Unable to connect to machine..Aborted...:Error741
Also check more details on regex filter here.

Is there any option to list groups in Ansible?

As far as i know, ansible has an option named --list-hosts for listing hosts. Is there any option for listing host groups? Or any other way to come through?
You can simply inspect the groups variable using the debug module:
ansible localhost -m debug -a 'var=groups.keys()'
The above is using groups.keys() to get just the list of groups. You could drop the .keys() part to see group membership as well:
ansible localhost -m debug -a 'var=groups'
Listing groups
Using tools built-in to Ansible + jq, this gives you something reasonably close:
ansible-inventory --list | jq "keys"
An advantage of this approach vs manually parsing inventory files is that it fully utilizes your ansible.cfg files (which can point to one or multiple inventory files, a directory of inventory files, etc...).
Example output on my machine (local_redis_all is a locally defined Ansible group):
[
"_meta",
"all",
"local_redis_all",
]
If you prefer it in plain-text form, use an approach like ansible-inventory --list | jq -r "keys | .[]". It will give you an output like this:
_meta
all
local_redis_all
Listing hosts
This was not part of the original question, but including it here anyway since it might be useful to some of my readers. Use the following command for JSON output (note: the command actually outputs a JSON array for each group, I haven't yet figured out a way to flatten these with jq - suggestions are welcome):
ansible-inventory --list | jq ".[].hosts | map(.)?
This gives you an output similar to this:
[
"redis-01",
"redis-02",
"redis-03"
]
Likewise, in raw text format (one host per line): ansible-inventory --list | jq -r ".[].hosts | .[]?"
This gives you an output like this:
redis-01
redis-02
redis-03
Note:- For New Ansible Users
Ansible has some special internal variables which are also known as Magic Variables.
From this link you will get full list of magic variables Magic Variables
There is magic variable called "groups" which hold the inventory group information.
we can access the value of any variable ( both user defined and Internal ) using an ansible module called debug .
I am Using Separate Inventory File
$
$ ansible -i inventory.ini all -m debug -a "var=groups"
$
centos-client.ansible.lab | SUCCESS => {
"groups": {
"all": [
"centos-client.ansible.lab",
"ubuntu-client.ansible.lab"
],
"centos": [
"centos-client.ansible.lab"
],
"ubuntu": [
"ubuntu-client.ansible.lab"
],
"ungrouped": []
}
}
ubuntu-client.ansible.lab | SUCCESS => {
"groups": {
"all": [
"centos-client.ansible.lab",
"ubuntu-client.ansible.lab"
],
"centos": [
"centos-client.ansible.lab"
],
"ubuntu": [
"ubuntu-client.ansible.lab"
],
"ungrouped": []
}
}
Method #1 - Using Ansible
If you just want a list of the groups within a given inventory file you can use the magic variables as mentioned in a couple of the other answers.
In this case you can use the groups magic variable and specifically just show the keys() in this hash (keys + values). The keys are all the names of the groups.
NOTE: By targeting the localhost we force this command to just run against a single host when processing the inventory file.
$ ansible -i inventory/rhvh localhost -m debug -a 'var=groups.keys()'
localhost | SUCCESS => {
"groups.keys()": "dict_keys(['all', 'ungrouped', 'dc1-rhvh', 'dc2-rhvh', 'dc3-rhvh', 'dc4-rhvh', 'dc5-rhvh', 'rhvh', 'dc1', 'dc2', 'dc3', 'dc4', 'dc5', 'production'])"
}
Method #2 - using grep & sed
You could of course just grep the contents of your inventory file too:
$ grep -E '^\[' inventory/rhvh
[dc1-rhvh]
[dc2-rhvh]
[dc3-rhvh]
[dc4-rhvh]
[dc5-rhvh]
[rhvh:children]
[dc1:children]
[dc2:children]
[dc3:children]
[dc4:children]
[dc5:children]
[production:children]
You're on the hook though for teasing out of the 2nd method's output or you can use sed to do it:
$ grep -E '^\[' inventory/rhvh | sed 's/:children//'
[dc1-rhvh]
[dc2-rhvh]
[dc3-rhvh]
[dc4-rhvh]
[dc5-rhvh]
[rhvh]
[dc1]
[dc2]
[dc3]
[dc4]
[dc5]
If you're not sure if the host will actually be in the inventory you can use:
ansible -i hosts/ localhost -m debug -a 'var=groups'
-i where your inventory files are kept
-m enable module debug.
-a module arguments.
It will output the group / hosts just once and not for every host in your inventory.
Same goes for just getting the list of groups in the inventory:
ansible -i hosts/ localhost -m debug -a 'var=groups.keys()'
another way to see your hosts is just to press tab after the ansible keyword.
Something like that?
cat ~/inventory/* | grep "\[.*\]"

SH - How can I make a script read an output and follow a condition based on that output?

I have a KVM virtual machine that is shutting down by itself at random times. Until I fix it I have to keep it up and running.
I know if the virtual machine is running or not by executing the command virsh dominfo kvm110. Output:
[root#dal01 ~]# virsh dominfo kvm110
Id: 54
Name: kvm110
UUID: ea136d63-4806-4d8c-a9b3-7b9f412552c3
OS Type: hvm
State: running
CPU(s): 7
CPU time: 885.4s
Max memory: 10485760 KiB
Used memory: 10485760 KiB
Persistent: no
Autostart: disable
Managed save: no
Security model: none
Security DOI: 0
I want to make a script that executes the above command every second, reads the line State: running and then do the following based on the 2 possible alternatives, running and not running. I have no coding experience in SH or Bash, but I'd imagine that the script would be something simple as this:
def check():
if "status" = "running" :
running = true
print ("VM is running")
else:
print ("VM is NOT running") + print time.localtime() #prints timestamp
run command "virsh start kvm110"
check()
I didn't include the part where the script runs virsh dominfo kvm110 and parses the line State: out of it because I don't know how to do that.
Could someone help me out?
Use grep. If it matches its argument, it exits successfully (code 0), otherwise unsuccessfully (code 1, or 2 for an error). You can test whether a command succeeded or failed with an if statement:
if virsh dominfo kvm110 | grep -q '^State: *running'
then
echo "VM is running"
else
echo "VM is NOT running, " $(date) #prints timestamp
fi
The -q means "quiet". It tells grep not to print out the matching text.

How should script output be formatted for Ansible reporting?

I'm using Ansible v1.3 to run a bash script on a group of servers. I'm trying to get my output to work with what Ansible is expecting to format the output correctly but I'm missing something.
I've read somewhere (can't find the link!) that if script output is formatted as JSON, Ansible will pick it up and include it in the output.
So in the script, the very last thing I do is this:
cat <<EOF
{
"value" : $value
}
EOF
I call my script like this:
ansible target_hosts -m script -a script.sh
And the output I get is like this:
X.X.X.X | success >> {
"rc": 0,
"stderr": "",
"stdout": "value=96\r\n"
}
I'm expecting to see something like this:
X.X.X.X | success >> {
"rc": 0,
"stderr": "",
"stdout": "",
"value": "96"
}
What am I missing?
The problem is that you are running your module as a script. Create a library folder and put your script there. After that, you can run your script with:
ansible target_hosts -m script.sh
I case of doubt, take a look at: http://jpmens.net/2012/07/05/shell-scripts-as-ansible-modules/
Note: Don't forget to include a #!/bin/bash in the top of the file, or ansible will fail with a message like target_host | FAILED => module is missing interpreter line

Resources