PolarSPARC |
Introduction to Ansible - Part 2
Bhaskar S | 07/04/2018 |
Overview
In Part 1 of the series, we touched on a brief overview of Ansible, basic terminology, installation and setup of the environment, and some basic ad-hoc task execution using Ansible.
In this part, we will get our hands dirty with Ansible Playbooks.
An Ansible playbook, written in yaml format, is a collection of one or more plays, which in turn consists of one or more tasks, which helps manage the configuration of the servers defined in the inventory file.
Hands-on with Ansible Playbooks
Create and save a playbook file called playbook-0.yaml, with the following contents, in the directory /home/alice/Ansible:
The following is a pictorial representation of the above playbook playbook-0.yaml.
The above playbook file defines two plays, named, Play-1 and Play-2 respectively. The first play (Play-1) is targeted at host host1 and the second play (Play-2) is targeted at host host2.
The first play (Play-1) uses the module debug to display a message on the standartd output, while the second play (Play-2) uses the module ping to ping the host.
To test the above playbook playbook-0.yaml, execute the following command:
$ ansible-playbook -i hosts playbook-0.yaml
The following would be a typical output:
_______________ < PLAY [Play-1] > --------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ________________________ < TASK [Gathering Facts] > ------------------------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ok: [192.168.100.11] __________________________ < TASK [Display a message] > -------------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ok: [192.168.100.11] => { "msg": "Hello, welcome to Ansible Playbooks" } _______________ < PLAY [Play-2] > --------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ________________________ < TASK [Gathering Facts] > ------------------------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ok: [192.168.100.12] ___________________ < TASK [Ping hosts] > ------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ok: [192.168.100.12] ____________ < PLAY RECAP > ------------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || 192.168.100.11 : ok=2 changed=0 unreachable=0 failed=0 192.168.100.12 : ok=2 changed=0 unreachable=0 failed=0
WOW !!! Whats with the cowsay display ??? They are distracting the results.
To disable the cowsay display, we need to create an Ansible configuration file called ansible.cfg in the directory /home/alice/Ansible, with the following contents:
The nocows = yes option turns off displaying the cowsay.
With the inventory = hosts option, one does not need to specify the inventory file on the command line.
Execute the playbook playbook-0.yaml once again with the following command:
$ ansible-playbook playbook-0.yaml
The following would be a typical output:
PLAY [Play-1] ********************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************* ok: [192.168.100.11] TASK [Display a message] *********************************************************************************************************** ok: [192.168.100.11] => { "msg": "Hello, welcome to Ansible Playbooks" } PLAY [Play-2] ********************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************* ok: [192.168.100.12] TASK [Ping hosts] ****************************************************************************************************************** ok: [192.168.100.12] PLAY RECAP ************************************************************************************************************************* 192.168.100.11 : ok=2 changed=0 unreachable=0 failed=0 192.168.100.12 : ok=2 changed=0 unreachable=0 failed=0
Looks much better now.
Before executing the first task in each play, Ansible connects to the target host and gathers various facts such as, the cpu, the memory, the storage disks, the operating system, the networking, etc., and stores them in pre-defined variables.
From the Output.2, the fact gathering is indicated by the lines starting with TASK [Gathering Facts].
Create and save a playbook file called playbook-1.yaml, with the following contents, in the directory /home/alice/Ansible:
The above playbook file defines just one play with 3 tasks, which is targeted at the hosts in the group myhosts.
The first task uses the module command to execute a command on the target remote host(s). The command module takes a command name followed by an optional list of space delimited arguments. In this example, we are invoking the whoami command.
Invoking the command whoami results in the display of the username of the currently logged in user on the standard output. How do we capture this value ???
This is where the register clause comes into play. The results of the command execution (which is a set of values) are captured in the variable called user. The username is in one of the returned values called stdout.
The other two tasks use the module debug to display Ansible gathered facts. Notice the use of the syntax {{ variable }} to display variables.
To test the above playbook playbook-1.yaml, execute the following command:
$ ansible-playbook -v playbook-1.yaml
Notice the use of the verbose flag -v when executing the playbook above to illustrate the fact that the results from the command module execution is a set of values.
The following would be a typical output:
Using /home/bswamina/MyProjects/Ansible/ansible.cfg as config file PLAY [Simple playbook with 3 tasks] ************************************************************************************************ TASK [Gathering Facts] ************************************************************************************************************* ok: [192.168.100.11] ok: [192.168.100.12] TASK [Find who am I] *************************************************************************************************************** changed: [192.168.100.11] => {"changed": true, "cmd": ["whoami"], "delta": "0:00:00.016586", "end": "2018-07-04 14:49:44.588500", "rc": 0, "start": "2018-07-04 14:49:44.571914", "stderr": "", "stderr_lines": [], "stdout": "vagrant", "stdout_lines": ["vagrant"]} changed: [192.168.100.12] => {"changed": true, "cmd": ["whoami"], "delta": "0:00:00.014920", "end": "2018-07-04 14:49:43.801986", "rc": 0, "start": "2018-07-04 14:49:43.787066", "stderr": "", "stderr_lines": [], "stdout": "vagrant", "stdout_lines": ["vagrant"]} TASK [Display some facts] ********************************************************************************************************** ok: [192.168.100.11] => { "msg": "OS Family - Debian, User - vagrant}" } ok: [192.168.100.12] => { "msg": "OS Family - Debian, User - vagrant}" } TASK [Display some more facts] ***************************************************************************************************** ok: [192.168.100.11] => { "msg": [ "Host Name: host1", "Operating System: Ubuntu 16.04.4 LTS", "Processor: ['0', 'AuthenticAMD', 'AMD FX(tm)-8350 Eight-Core Processor']", "Total Memory: 2000" ] } ok: [192.168.100.12] => { "msg": [ "Host Name: host2", "Operating System: Ubuntu 16.04.4 LTS", "Processor: ['0', 'AuthenticAMD', 'AMD FX(tm)-8350 Eight-Core Processor']", "Total Memory: 2000" ] } PLAY RECAP ************************************************************************************************************************* 192.168.100.11 : ok=4 changed=1 unreachable=0 failed=0 192.168.100.12 : ok=4 changed=1 unreachable=0 failed=0
The following illustration highlights the set of values returned by the command module execution from the playbook playbook-1.yaml.
Ansible fact gathering is under-the-hood an additional task performed before the first task is executed. If one will not be using or referring any of the pre-defined Ansible variables from the fact gathering, then one can disable fact gathering as an optimization.
Create and save a playbook file called playbook-2.yaml, with the following contents, in the directory /home/alice/Ansible:
The above playbook file defines just one play with 3 tasks, which is targeted at the hosts in the group myhosts.
To test the above playbook playbook-2.yaml, execute the following command:
$ ansible-playbook playbook-2.yaml
The following would be a typical output:
PLAY [Playbook with 3 tasks] ******************************************************************************************************* TASK [Execute free] **************************************************************************************************************** changed: [192.168.100.11] changed: [192.168.100.12] TASK [Display free info] *********************************************************************************************************** ok: [192.168.100.11] => { "msg": "Memory - total used free shared buff/cache available\nMem: 2.0G 52M 1.6G 3.1M 328M 1.8G\nSwap: 0B 0B 0B}" } ok: [192.168.100.12] => { "msg": "Memory - total used free shared buff/cache available\nMem: 2.0G 52M 1.6G 3.1M 328M 1.8G\nSwap: 0B 0B 0B}" } TASK [Display OS family] *********************************************************************************************************** fatal: [192.168.100.11]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible_os_family' is undefined\n\nThe error appears to have been in '/home/alice/Ansible/playbook-2.yaml': line 13, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Display OS family\n ^ here\n"} fatal: [192.168.100.12]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible_os_family' is undefined\n\nThe error appears to have been in '/home/alice/Ansible/playbook-2.yaml': line 13, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Display OS family\n ^ here\n"} to retry, use: --limit @/home/alice/Ansible/playbook-2.retry PLAY RECAP ************************************************************************************************************************* 192.168.100.11 : ok=2 changed=1 unreachable=0 failed=1 192.168.100.12 : ok=2 changed=1 unreachable=0 failed=1
As is evident from the Output.4 above, the 3rd task in the playbook playbook-2.yaml failed to execute as the pre-defined variable ansible_os_family is undefined. This is because we disabled fact gathering by specifying the clause gather_facts: no in the playbook.
Also, from the Output.4 above, the line to retry, use: --limit @/home/alice/Ansible/playbook-2.retry is interesting. If we look in the directory /home/alice/Ansible, we will see a file named playbook-2.retry. This file contains the names of the remote target host(s) on which the playbook playbook-2.yaml failed to execute.
To disable the creation of the retry file, modify the Ansible configuration file ansible.cfg located in the directory /home/alice/Ansible, with the following contents:
Is there a way to check if a variable is defined in Ansible before referencing it ???
This is where the when clause comes in handy for performing conditional checks prior to executing a task.
Create and save a playbook file called playbook-3.yaml, with the following contents, in the directory /home/alice/Ansible:
To test the above playbook playbook-3.yaml, execute the following command:
$ ansible-playbook playbook-3.yaml
The following would be a typical output:
PLAY [Playbook with 3 tasks] ******************************************************************************************************* TASK [Execute free] **************************************************************************************************************** changed: [192.168.100.11] changed: [192.168.100.12] TASK [Display free info] *********************************************************************************************************** ok: [192.168.100.11] => { "msg": "Memory - total used free shared buff/cache available\nMem: 2.0G 52M 1.7G 3.1M 191M 1.8G\nSwap: 0B 0B 0B}" } ok: [192.168.100.12] => { "msg": "Memory - total used free shared buff/cache available\nMem: 2.0G 52M 1.7G 3.1M 191M 1.8G\nSwap: 0B 0B 0B}" } TASK [Display OS family] *********************************************************************************************************** skipping: [192.168.100.11] skipping: [192.168.100.12] PLAY RECAP ************************************************************************************************************************* 192.168.100.11 : ok=2 changed=1 unreachable=0 failed=0 192.168.100.12 : ok=2 changed=1 unreachable=0 failed=0
References