ansible nodetool: GitHub File Free Download

README

An Ansible module to interact with Erlang nodes via Erlang RPC.

Overview

If your architecture includes one or more Erlang nodes and you use
Ansible to orchestrate them, you may find this Ansible module helpful.

Minimum Requirements

  • Ansible 2.0.0.2
  • Erlang/OTP 17.0

Installation

Clone the ansible-nodetool repository:

git clone https://github.com/robertoaloi/ansible-nodetool.git /path/to/ansible-nodetool

Then, you need to tell Ansible where to find the new module.
You can do this by
appending the repository path to the library value in your
~/.ansible.cfg file. You can find the library value under the
defaults group. If you do not have a defaults group in your
~/.ansible.cfg file (or if you do not have a ~/.ansible.cfg file
at all)
add one. You can find more information about configuring Ansible
here.

[defaults]
library = CURRENT_PATH:/path/to/ansible-nodetool

Alternatively, you can specify the -M option when invoking a
playbook. Example:

$ ansible-playbook -M /path/to/ansible-nodetool my_playbook.yml

Or when running an ad-hoc
command
. Example:

$ ansible -m nodetool \
         -M /path/to/ansible-nodetool \
         -a 'node=alice@localhost cookie=secret action=ping' \
         localhost

Parameters

 node:
     description: The remote Erlang node
     required:    true
 action:
     description: The action to be performed
     choices:     [getpid, ping, stop, restart, reboot, eval]
     required:    true
 nametype:
     description: Nametype to be used
     choices:     [longnames, shortnames]
     required:    false
     default:     shortnames
 cookie:
     description: Erlang Cookie to be used for the connection
     required:    false
 timeout:
     description: Timeout (in ms) for the actions
     required:    false
     default:     60000

Usage

The ansible-nodetool module is typically used from an Ansible
playbook.

To try things out, start a sample Erlang node named
‘alice’ and with a ‘secret’ cookie:

$ erl -sname alice@localhost -setcookie secret

You can now ping the node using the following playbook:

---

- hosts: localhost
  tasks:
  - name: "Ping the 'alice' Erlang node"
    nodetool:
      action: ping
      cookie: secret
      node:   alice@{{ inventory_hostname_short }}

Example:

$ ansible-playbook ping.yml

PLAY
***************************************************************************

TASK [setup]
*******************************************************************
ok: [localhost]

TASK [Ping the 'alice' Erlang node]
********************************************
changed: [localhost]

PLAY RECAP
*********************************************************************
localhost                  : ok=2    changed=1    unreachable=0
failed=0

But the ansible-nodetool module is not only about pinging nodes.
You can also evaluate custom Erlang expressions on a remote Erlang
node using the eval action.

The following playbook gets the list of running applications in an
Erlang node, registers the result into an Ansible variable and prints
the result:

---

- hosts: localhost
  tasks:
  - name: "Return a list of running applications"
    nodetool:
      action:  eval
      command: application:which_applications()
      cookie:  secret
      node:    alice@{{ inventory_hostname_short }}
    register: applications
  - debug:
      msg: "{{ applications.stdout_lines }}"

Let’s see it in action:

$ ansible-playbook applications.yml

PLAY
***************************************************************************

TASK [setup]
*******************************************************************
ok: [localhost]

TASK [Return a list of running applications]
***********************************
changed: [localhost]

TASK [debug]
*******************************************************************
ok: [localhost] => {
  "msg": [
    "[{sasl,\"SASL  CXC 138 11\",\"2.4.1\"},",
    " {stdlib,\"ERTS  CXC 138 10\",\"2.4\"},",
    " {kernel,\"ERTS  CXC 138 10\",\"3.2.0.1\"}]"
  ]
}

PLAY RECAP
*********************************************************************
localhost                  : ok=3    changed=1    unreachable=0
failed=0

Another available action is getpid, which returns the process
identifier of the current Erlang emulator:

---

- hosts: localhost
  tasks:
  - name: "Return the process identifier of the current Erlang emulator"
    nodetool:
      action: getpid
      cookie: secret
      node:   alice@{{ inventory_hostname_short }}
    register: pid
  - debug:
      msg: "{{ pid.stdout }}"

There are also other available actions, such as stop, restart or
reboot to control a remote Erlang node.

Result

As part of the Ansible result, the ansible-nodetool provides a JSON structure
which contains the following fields:

FIELD DESCRIPTION
rc The Erlang RPC return code (0 => success, 1 => failure)
stdout The return value of the Erlang RPC
stdout_lines The return value of the Erlang RPC call, split in lines
remote_output The stdout on the remote Erlang node, captured via group leader

I have seen many people trying to assert the return value of the
Erlang RPC in complicated ways. Remember that, if you want to verify
that the return value of a given Erlang expression corresponds to a
specific value you can simply use the pattern matching
operator
for
this. No need to find complicated Ansible-based solutions!

Example:

---

- hosts: localhost
  tasks:
  - name: "Ensure that the reverse of 'foo' is 'oof'"
    nodetool:
      action:  eval
      command: "\"oof\"=lists:reverse(\"foo\")"
      cookie:  secret
      node:    alice@{{ inventory_hostname_short }}

Notice the usage of the pattern matching operator (=) above and the
presence of backslashes to escape the double quotes.

Idempotence

Idempotence is a crucial principle in Ansible. Given the nature of the
Ansible nodetool module, which allow the operator to perform custom
Erlang expressions via the eval action, idempotence cannot be
guaranteed by the module. It is up to the user to avoid violating it.

Let’s consider the following example:

---

# THIS PLAYBOOK IS NOT IDEMPOTENT!!!
# RUNNING IT TWICE WOULD CAUSE A FAILURE!!!
- hosts: localhost
  tasks:
  - name: "Start the SASL application"
    nodetool:
      action:  eval
      command: ok=application:start(sasl).
      cookie:  secret
      node:    alice@{{ inventory_hostname_short }}

The above playbook expects the application:start/1 function to
return ok. This will only happen if the application is not already started.
The following is a much better approach:

---

- hosts: localhost
  tasks:
  - name: "Start the SASL application"
    nodetool:
      action:  eval
      command: ok=application:ensure_started(sasl)
      cookie:  secret
      node:    alice@{{ inventory_hostname_short }}

The application:ensure_started/1 function will only start the
application if it is not already started. Much better!

Credits

The work is based on the nodetool escript from the Yaws project:

https://github.com/klacke/yaws

Contributors

A big thanks to:

  • Amir Moulavi
  • Fabricio Leotti

For their precious contributions.

Go to GitHub File