Developing a virtual machine for Erlang/OTP using Ansible

0
7828

This seventh article in the DevOps series is a tutorial on how to create a test virtual machine (VM) to compile, build, and test Erlang/OTP from its source code. You can then adapt the method to create different VMs for various Erlang releases.

Erlang is a programming language designed by Ericsson primarily for soft real-time systems. The Open Telecom Platform (OTP) consists of libraries, applications and tools to be used with Erlang to implement services that require high availability. In this article, we will create a test virtual machine (VM) to compile, build, and test Erlang/OTP from its source code. This allows you to create VMs with different Erlang release versions for testing.

The Erlang programming language was developed by Joe Armstrong, Robert Virding and Mike Williams in 1986 and released as free and open source software in 1998. It was initially designed to work with telecom switches, but is widely used today in large scale, distributed systems. Erlang is a concurrent and functional programming language, and is released under the Apache License 2.0.

Setting it up

A CentOS 6.8 virtual machine (VM) running on KVM is used for the installation. Internet access should be available from the guest machine. The VM should have at least 2GB of RAM allotted to build the Erlang/OTP documentation. The Ansible version used on the host (Parabola GNU/Linux-libre x86_64) is 2.3.0.0. The ansible/ folder contains the following files:

ansible/inventory/kvm/inventory
 
ansible/playbooks/configuration/erlang.yml

The IP address of the guest CentOS 6.8 VM is added to the inventory file as shown below:

erlang ansible_host=192.168.122.150 ansible_connection=ssh ansible_user=bravo ansible_password=password

An entry for the erlang host is also added to the /etc/hosts file as indicated below:

192.168.122.150 erlang

A ‘bravo’ user account is created on the test VM, and is added to the ‘wheel’ group. The /etc/sudoers file also has the following line uncommented, so that the ‘bravo’ user will be able to execute sudo commands:

## Allows people in group wheel to run all commands
 
%wheel ALL=(ALL) ALL

We can obtain the Erlang/OTP sources from a stable tarball, or clone the Git repository. The steps involved in both these cases are discussed below.

Building from the source tarball

The Erlang/OTP stable releases are available at http://www.erlang.org/downloads. The build process is divided into many steps, and we shall go through each one of them. The version of Erlang/OTP can be passed as an argument to the playbook. Its default value is the release 19.0, and is defined in the variable section of the playbook as shown below:

vars:
 
ERL_VERSION: “otp_src_{{ version | default(‘19.0’) }}”
 
ERL_DIR: “{{ ansible_env.HOME }}/installs/erlang
 
ERL_TOP: “{{ ERL_DIR }}/{{ ERL_VERSION }}”
 
TEST_SERVER_DIR: “{{ ERL_TOP }}/release/tests/test_server

The ERL_DIR variable represents the directory where the tarball will be downloaded, and the ERL_TOP variable refers to the top-level directory location containing the source code. The path to the test directory from where the tests will be invoked is given by the TEST_SERVER_DIR variable.

Erlang/OTP has mandatory and optional package dependencies. Let’s first update the software package repository, and then install the required dependencies as indicated below:

tasks:
 
- name: Update the software package repository
 
become: true
 
yum:
 
name: ‘*’
 
update_cache: yes
 
- name: Install dependencies
 
become: true
 
package:
 
name: “{{ item }}”
 
state: latest
 
with_items:
 
- wget
 
- make
 
- gcc
 
- perl
 
- m4
 
- ncurses-devel
 
- sed
 
- libxslt
 
- fop

The Erlang/OTP sources are written using the ‘C’ programming language. The GNU C Compiler (GCC) and GNU Make are used to compile the source code. The ‘libxslt’ and ‘fop’ packages are required to generate the documentation. The build directory is then created, the source tarball is downloaded and it is extracted to the directory mentioned in ERL_DIR.

- name: Create destination directory
 
file: path=”{{ ERL_DIR }}” state=directory
 
- name: Download and extract Erlang source tarball
 
unarchive:
 
src: “http://erlang.org/download/{{ ERL_VERSION }}.tar.gz”
 
dest: “{{ ERL_DIR }}”
 
remote_src: yes

The ‘configure’ script is available in the sources, and it is used to generate the Makefile based on the installed software. The ‘make’ command will build the binaries from the source code.

- name: Build the project
 
command: “{{ item }} chdir={{ ERL_TOP }}”
 
with_items:
 
- ./configure
 
- make
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”

After the ‘make’ command finishes, the ‘bin’ folder in the top-level sources directory will contain the Erlang ‘erl’ interpreter. The Makefile also has targets to run tests to verify the built binaries. We are remotely invoking the test execution from Ansible and hence -noshell -noinput are passed as arguments to the Erlang interpreter, as shown in the .yaml file.

- name: Prepare tests
 
command: “{{ item }} chdir={{ ERL_TOP }}”
 
with_items:
 
- make release_tests
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”
 
- name: Execute tests
 
shell: “cd {{ TEST_SERVER_DIR }} && {{ ERL_TOP }}/bin/erl -noshell -noinput -s ts install -s ts smoke_test batch -s init stop”

You need to verify that the tests have passed successfully by checking the $ERL_TOP/release/tests/test_server/index.html page in a browser. A screenshot of the test results is shown in Figure 1.

The built executables and libraries can then be installed on the system using the make install command. By default, the install directory is /usr/local.

- name: Install
 
command: “{{ item }} chdir={{ ERL_TOP }}”
 
with_items:
 
- make install
 
become: true
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”

The documentation can also be generated and installed as shown below:

- name: Make docs
 
shell: “cd {{ ERL_TOP }} && make docs”
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”
 
FOP_HOME: “{{ ERL_TOP }}/fop
 
FOP_OPTS: “-Xmx2048m”
 
- name: Install docs
 
become: true
 
shell: “cd {{ ERL_TOP }} && make install-docs”
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”

The total available RAM (2GB) is specified in the FOP_OPTS environment variable. The complete playbook to download, compile, execute the tests, and also generate the documentation is given below:

---
 
- name: Setup Erlang build
 
hosts: erlang
 
gather_facts: true
 
tags: [release]
 
vars:
 
ERL_VERSION: “otp_src_{{ version | default(‘19.0’) }}”
 
ERL_DIR: “{{ ansible_env.HOME }}/installs/erlang
 
ERL_TOP: “{{ ERL_DIR }}/{{ ERL_VERSION }}”
 
TEST_SERVER_DIR: “{{ ERL_TOP }}/release/tests/test_server
 
tasks:
 
- name: Update the software package repository
 
become: true
 
yum:
 
name: ‘*’
 
update_cache: yes
 
- name: Install dependencies
 
become: true
 
package:
 
name: “{{ item }}”
 
state: latest
 
with_items:
 
- wget
 
- make
 
- gcc
 
- perl
 
- m4
 
- ncurses-devel
 
- sed
 
- libxslt
 
- fop
 
- name: Create destination directory
 
file: path=”{{ ERL_DIR }}” state=directory
 
- name: Download and extract Erlang source tarball
 
unarchive:
 
src: “http://erlang.org/download/{{ ERL_VERSION }}.tar.gz”
 
dest: “{{ ERL_DIR }}”
 
remote_src: yes
 
- name: Build the project
 
command: “{{ item }} chdir={{ ERL_TOP }}”
 
with_items:
 
- ./configure
 
- make
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”
 
- name: Prepare tests
 
command: “{{ item }} chdir={{ ERL_TOP }}”
 
with_items:
 
- make release_tests
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”
 
- name: Execute tests
 
shell: “cd {{ TEST_SERVER_DIR }} && {{ ERL_TOP }}/bin/erl -noshell -noinput -s ts install -s ts smoke_test batch -s init stop”
 
- name: Install
 
command: “{{ item }} chdir={{ ERL_TOP }}”
 
with_items:
 
- make install
 
become: true
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”
 
- name: Make docs
 
shell: “cd {{ ERL_TOP }} && make docs”
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”
 
FOP_HOME: “{{ ERL_TOP }}/fop
 
FOP_OPTS: “-Xmx2048m”
 
- name: Install docs
 
become: true
 
shell: “cd {{ ERL_TOP }} && make install-docs”
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”

The playbook can be invoked as follows:

$ ansible-playbook -i inventory/kvm/inventory playbooks/configuration/erlang.yml -e “version=19.0” --tags “release” -K

Building from the Git repository

We can build the Erlang/OTP sources from the Git repository too. The complete playbook is given below for reference:

- name: Setup Erlang Git build
 
hosts: erlang
 
gather_facts: true
 
tags: [git]
 
vars:
 
GIT_VERSION: “otp”
 
ERL_DIR: “{{ ansible_env.HOME }}/installs/erlang
 
ERL_TOP: “{{ ERL_DIR }}/{{ GIT_VERSION }}”
 
TEST_SERVER_DIR: “{{ ERL_TOP }}/release/tests/test_server
 
tasks:
 
- name: Update the software package repository
 
become: true
 
yum:
 
name: ‘*’
 
update_cache: yes
 
- name: Install dependencies
 
become: true
 
package:
 
name: “{{ item }}”
 
state: latest
 
with_items:
 
- wget
 
- make
 
- gcc
 
- perl
 
- m4
 
- ncurses-devel
 
- sed
 
- libxslt
 
- fop
 
- git
 
- autoconf
 
- name: Create destination directory
 
file: path=”{{ ERL_DIR }}” state=directory
 
- name: Clone the repository
 
git:
 
repo: “https://github.com/erlang/otp.git”
 
dest: “{{ ERL_DIR }}/otp
 
- name: Build the project
 
command: “{{ item }} chdir={{ ERL_TOP }}”
 
with_items:
 
- ./otp_build autoconf
 
- ./configure
 
- make
 
environment:
 
ERL_TOP: “{{ ERL_TOP }}”

The ‘git’ and ‘autoconf’ software packages are required for downloading and building the sources from the Git repository. The Ansible Git module is used to clone the remote repository. The source directory provides an otp_build script to create the configure script. You can invoke the above playbook as follows:

$ ansible-playbook -i inventory/kvm/inventory playbooks/configuration/erlang.yml --tags “git” -K