How to have each node get a different variable from an array

Hi,

I am creating a playbook that creates a valid cassandra.yaml file for each node in a new cluster using a jinja2 template. What this file needs is a different token for each node, but I do not know how to do this with Ansible.

So the part of the template in question looks like this:

initial_token: {{ token }}

In this example, I have 6 nodes. I can generate the tokens for each using this script:

$ ./tokenbuilder.py 6
{
“0”: {
“0”: 0,
“1”: 28356863910078205288614550619314017621,
“2”: 56713727820156410577229101238628035242,
“3”: 85070591730234615865843651857942052864,
“4”: 113427455640312821154458202477256070485,
“5”: 141784319550391026443072753096570088106
}
}

I believe there is a way to get the output of that script into a variable in Ansible so that I can access it by {{ tokens[0] }}. (Any tips appreciated).

In any case, I can use a playbook that has

vars:

tokens:

0: 0

1: 28356863910078205288614550619314017621

2: 56713727820156410577229101238628035242

tasks:

  • name: Copy over the templated cassandra.yaml config file
    template: src=templates/cassandra.yaml.j2 dest=/etc/cassandra/cassandra.yaml

to do the same thing.

The question is, how can I then get each of the 6 hosts to use one of these array values in the template? So that when the template is evaluated:

host1 has token = tokens[0] = 0
host2 has token = tokens[1] = 28356863910078205288614550619314017621
host3 has token = tokens[2] = 56713727820156410577229101238628035242
host4 has token = tokens[3] = 85070591730234615865843651857942052864
host5 has token = tokens[4] = 113427455640312821154458202477256070485

host6 has token = tokens[5] = 141784319550391026443072753096570088106

All hosts are part of the same group in the inventory. Is there a trick to get the index of a host in a group?

I would really appreciate anyone pointing me in the right direction.

Thanks,
Peter

you can call a task that calls the script and outputs a token per
host, register the variable and then use that in the template.

Perhaps, but how?

How does ansible know which “host number in the group” it is running on?

This isn’t serial, all 6 hosts have the ansible playbook running simultaneously, so it is not a matter of maintaining a stack between each host where I pop off each token for each host.

To be clear, the tokens aren’t random, they are calculated based on the number of hosts (in this example, 6).

Hey Pete,

I am an newbie, so there might be better ways to do it, how about set the vars as
vars:
: 1234
: 456456
etc…

in in your template referece it as
token = {{ ansible_hostname }}

or maybe
set a custom facter in each host ‘hostnumber’ and then in template evaluate this value and set token accordingly

Regards,
Benno Joy

I like Benno's solution of the hash table, though you could also try
something like this to derive the token from the hostname:

- shell: echo $hostname | md5sum -t
  register: host_token

And then you could just loop over the hosts in your template::

{% for host in groups['groupname'] %}
    {{ hostvars[host]['host_token'] }}
{% endfor %}

Thanks for the suggestions. I believe both of those will work.

However, both suggestions push variables for one template in one playbook far up the stack and into the inventory. I am looking for a way to encapsulate all of this inside just the playbook itself without extra dependencies.

The other issue is that I don’t have named hosts (I am using the AWS EC2 inventory plugin). The hostnames are not unique and unpredictable.

To have a solution that is both encapsulated, and dynamic, I have come up with some Ansible/Jinja trickery.

Using this yaml file to store the pre-computed tokens for clusters of size 1 to 10 nodes: https://gist.github.com/pas256/5111146

I can use this in the my cassandra.yaml.j2 template:

Cassandra storage config YAML

{% set seeds = %}

{% for host in groups[‘tag_Name_cassandra’] -%}

{# This is in an if block even though it is just a variable assignment because

the only other way to append to an array is with the ‘do’ Jinja2 extension

-#}

{% if seeds.append(hostvars[host][‘ansible_eth0’][‘ipv4’][‘address’]) -%}{% endif -%}

{% endfor -%}

{% set num_hosts = seeds|length -%}

{% for ip in seeds|sort -%}

{% if ansible_eth0[“ipv4”][“address”] == ip -%}

initial_token: {{ initial_tokens[num_hosts][loop.index0] }}

{% endif -%}

{% endfor -%}

What I am doing is building an array of all nodes in the group, where the array entry is the ip address. Then further down the template, I can loop though that array looking for the IP address of the hosts that is running the template, and use the loop index as the index into the initial_tokens variable. To ensure consistency, the array is always sorted.

Not sure if there is a simpler way of doing this, but is does solve both my concerns, and has all the logic in the 1 place.

Thanks for sharing this… just saved me quite a bit of frustration.

I was trying something along these lines (https://groups.google.com/d/msg/ansible-project/YTF6Up3kaKw/xHASvMROhegJ) to get a list of private IPs for a MySQL cluster config, and have ended up with:

Hello,

Replying to an old thread but this is relevant to what I am trying to do.

I have defined the tokens as a dictionary in the vars_file as below.

tokens:
192.168.56.1: 0
192.168.56.2: 56713727820156410577229101238628035242
192.168.56.3: 113427455640312821154458202477256070485

192.168.56.x hosts are coming in from inventory file
and in the cassandra.yaml I have the following
initial_token: {{ tokens[‘{{ inventory_hostname}}’] }}

tokens[“{{ inventory_hostname}}”] is evaluated but not {{ tokens[‘{{ inventory_hostname}}’] }}

How do I get it done with jinja2? Where am I going wrong? Any help is much appreciated.

-SR