Need a random number for your Ansible playbook? But want to be idempotent on subsequent runs? There is an answer!
Let's say you want to register cron
jobs on a bunch of servers and don't want it to start on the same time. You can use:
minutes: "{{ 60 | random }}"
but this will generate random number during each playbook execution, giving you unnecessary changed
state for tasks.
Update for Ansible 2.3:
As of Ansible version 2.3, it’s also possible to initialize the random number generator from a seed. This way, you can create random-but-idempotent numbers:
"{{ 59 |random(seed=inventory_hostname) }} * * * * root /script/from/cron"
For previous Ansible versions:
But you can craft a pseudo-random number based on any variable/fact you want. For example, you can choose inventory_hostname
to make this number different between servers but the same on subsequent playbook runs:
minutes: "{{ ( inventory_hostname | hash | list | map('int',0,16) | sum ) % 60 }}"
Magic explained:
- we take
inventory_hostname
string (e.g."myserver"
) - make a
hash
from it ("c3a7a35a28dcce27daad3a7a90caad99b967a904"
) - split it into array of characters (
["c","3","a",...]
)
where every character is a hexadecimal digit - apply
int
filter withbase=16
to every character to convert it to number0..15
([12,3,10,...]
) sum
all numbers (334
)- limit our pseudo-random number by taking the remainder of division
% 60
(34
)
So your cron task may look like:
cron:
name: myjob
job: myscript.sh
minute: "{{ ( inventory_hostname | hash | list | map('int',0,16) | sum ) % 60 }}"
hour: "{{ (( inventory_hostname | hash | list | map('int',0,16) | sum ) % 2) + 6 }}"
This will start myscript.sh
at some random time between 6:00
and 7:59
and this time will be idempotent on subsequent playbook runs.