Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

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

2,806 views
Skip to first unread message

Peter Sankauskas

unread,
Mar 6, 2013, 6:40:14 PM3/6/13
to ansible...@googlegroups.com
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



Brian Coca

unread,
Mar 6, 2013, 6:50:41 PM3/6/13
to ansible...@googlegroups.com
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.

--
Brian Coca
Stultorum infinitus est numerus
0110000101110010011001010110111000100111011101000010000001111001011011110111010100100000011100110110110101100001011100100111010000100001
Pedo mellon a minno

Peter Sankauskas

unread,
Mar 6, 2013, 6:58:13 PM3/6/13
to ansible...@googlegroups.com
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).

benno joy

unread,
Mar 6, 2013, 9:13:18 PM3/6/13
to ansible...@googlegroups.com
Hey Pete,

I am an newbie, so there might be better ways to do it, how about set the vars as
vars:
<hostname>: 1234
<hostname>: 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



--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Michael DeHaan

unread,
Mar 7, 2013, 12:14:39 AM3/7/13
to ansible...@googlegroups.com
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 %}

Peter Sankauskas

unread,
Mar 7, 2013, 4:23:53 PM3/7/13
to ansible...@googlegroups.com
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.

Message has been deleted

Mark Casey

unread,
Feb 7, 2014, 10:58:36 AM2/7/14
to ansible...@googlegroups.com
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:

[mysqld]
{% set ip_list = [] %}
{% for host in groups['db'] -%}

{#   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 ip_list.append(hostvars[host]['ansible_eth0']['ipv4']['address']) -%}{% endif -%}
{% endfor -%}

datadir=/var/lib/mysql
user=mysql
# Path to Galera library
wsrep_provider=/usr/lib/libgalera_smm.so
# Cluster connection URL contains the IPs of all nodes
wsrep_cluster_address=gcomm://{{ ip_list|join(',') }}

skr...@gmail.com

unread,
Oct 6, 2014, 11:12:20 AM10/6/14
to ansible...@googlegroups.com
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.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
Reply all
Reply to author
Forward
0 new messages