Search This Blog

Saturday, April 18, 2015

The Cassandra, the Ansible, a pipe and a complicate command

I've been working on Cassandra ring downsizing (not a fanny task). To make things a bit easier I've been using Ansible for configuration management. In theory you don't need to change the value of 'initial_token' in Cassandra configuration, and everything else stays the same during ring resize. Therefore, Ansible is not really necessary, but I believe it's good to have consistence in your configuration.

Cassandra role

In first place I added a variable with the Cassandra ring size as a max_nodes  to its role.

  - {role: cassandra, max_nodes: 13}

This value is used to create a Ansible local fact. To learn more on local facts please read this Curtis Collicutt article, which is the best for my script. Please note that the following code is a Jinja template not ready script.

#!/bin/sh
NODE=$(hostname |grep -o -P "\d+")
TOKEN=$(echo '2^127/{{ max_nodes }}' | bc)
cat <
{
    "node_number": $NODE,
    "token": $TOKEN
}
EOF


The above script print onto standard output a json with two variables.
  • node_number - is created based on hostname. The number a hostname correspond to a ring position (i.e. cassandra-4 is forth node).
  • token - is 'a main part' of token value calculation. The rest is done in Cassandra configuration template:
    initial_token: {{ '%d' % ( ansible_local.cassandra.token *  (ansible_local.cassandra.node_number - 1)) }}

That's sounds complicated and it is. As often there is a historical explanation to that situation. Initially, the whole calculation was done in the template. It worked for 16 node ring, but not for 15. It didn't work, because division two integral  number (2**127/15) results in float one in jinja what lead to rounding error!

Ansible commands

As a bonus two example of using Ansible to run a bit more complicated command on many hosts.
  1. Reads token position from configuration file and initialize node move in a screen session. Please note escape character in front of '$'.
    ansible \
     -i inventory/ec.py \
     -m shell \
     -a "screen -d -m \
       nodetool -h localhost move \
       \$(awk '/initial_token/ {print \$2}' \
         /etc/cassandra/default.conf/cassandra.yaml)"\
    tag_pool_cassandra

  2. Much simpler. Checks that screen runs. AFAIK you have to use shell module if you want to use UNIX pipe.
    ansible \
     -i inventory/ec2.py \
     -m shell \
     -a "ps -ef| grep SCREEN"\
    tag_pool_cassandra