Ansible tips’n’tricks: assessing your runtime environment

One thing that I frequently need to do is test for a certain condition, and fail if it is not met. After all, I want to write those playbooks in a safe way.

Here is an example: I need to ensure that my playbook only runs on Oracle Linux 7. How can I do this? Ansible offers a shell and a command module (make sure you read the notes in the command module documentation!), so I could simply write something testing for the output of, let’s say, /etc/os-release.

This is totally possible, although I believe it’s a bit messy and there is a more elegant way requiring far less coding. Ansible maintains a whole raft of variables it gathers when you run a playbook. Here is an example (I am again using the “debug” stdout_callback as described in my earlier blog posts):

[martin@controller ansible]$ ansible-playbook -i inventory.yml test.yml

PLAY [blogpost] ****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [server1]

TASK [say hello] ***************************************************************
ok: [server1] => {}

MSG:

hello world

PLAY RECAP *********************************************************************
server1                    : ok=2    changed=0    unreachable=0    failed=0   

When invoking the play “blogpost” (I have defined my host “server1” to be part of a group named “blogposts” in the local inventory file), I can see that the first task is to gather facts. This is unusual, since I haven’t defined any such task:

[martin@controller ansible]$ cat test.yml 
---
- hosts: blogpost

  tasks:
    - name: say hello
      debug:
        msg: hello world

Nevertheless, ansible gathers these facts, and they are most useful. But which of these exist, and what are their values? It is not too hard to find out. The following command invokes the setup module (using a local inventory mapping “server1” to group “blogpost”) specifically against host “server1”:

[martin@controller ansible]$ ansible -i inventory.yml -m setup server1 | head -n 25
server1 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.abc.24"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::5054:ff:fe91:abcd"
        ],
        "ansible_apparmor": {
            "status": "disabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "04/01/2014",
        "ansible_bios_version": "1.10.2-2.fc27",
        "ansible_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-4.1.12-94.3.9.el7uek.x86_64",
            "LANG": "en_US.UTF-8",
            "crashkernel": "auto",
            "quiet": true,
            "rd.lvm.lv": "rootvg/swaplv",
            "rhgb": true,
            "ro": true,
            "root": "/dev/mapper/rootvg-rootlv"
        },
        "ansible_date_time": {

There is a lot more output! You can query pretty much everything using these built-in variables. So what about our distribution? Let’s check the output of the setup module for a string containing the word “distribution”:

[martin@controller ansible]$ ansible -i inventory.yml -m setup server1 | grep -i distribution
        "ansible_distribution": "OracleLinux", 
        "ansible_distribution_file_parsed": true, 
        "ansible_distribution_file_path": "/etc/oracle-release", 
        "ansible_distribution_file_search_string": "Oracle Linux", 
        "ansible_distribution_file_variety": "OracleLinux", 
        "ansible_distribution_major_version": "7", 
        "ansible_distribution_release": "NA", 
        "ansible_distribution_version": "7.5", 

So it looks like there is something we could use here. I can re-write my playbook now to check for Oracle Linux 7 as follows:

[martin@controller ansible]$ cat test2.yml 
---
- hosts: blogpost

  tasks:
    - name: print debuginfo
      debug:
        var: "{{ item }}" 
      with_items:
        - ansible_distribution
        - ansible_distribution_major_version

    - name: ensure we run Oracle Linux 7
      fail:
        msg: This playbook only runs on Oracle Linux 7
      when: (ansible_distribution != "OracleLinux" and ansible_distribution_major_version|int != 7)

This does work all right when running against an Oracle Linux 7 system.

[martin@controller ansible]$ ansible-playbook -i inventory.yml test2.yml

PLAY [blogpost] ****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [server1]

TASK [print debuginfo] *********************************************************
ok: [server1] => (item=ansible_distribution) => {
    "ansible_distribution": "OracleLinux", 
    "item": "ansible_distribution"
}
ok: [server1] => (item=ansible_distribution_major_version) => {
    "ansible_distribution_major_version": "7", 
    "item": "ansible_distribution_major_version"
}

TASK [ensure we run Oracle Linux 7] ********************************************
skipping: [server1]

PLAY RECAP *********************************************************************
server1                    : ok=2    changed=0    unreachable=0    failed=0   

The “skipping” message indicates this task [ensure we run Oracle Linux 7] has not fired and we can proceed with life. Pointing this playbook to another environment not running Oracle Linux 7, the play fails:

[martin@controller ansible]$ ansible-playbook -i inventory.yml test2.yml

PLAY [blogpost] ****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [linuxdev]

TASK [print debuginfo] *********************************************************
ok: [linuxdev] => (item=ansible_distribution) => {
    "ansible_distribution": "Fedora", 
    "item": "ansible_distribution"
}
ok: [linuxdev] => (item=ansible_distribution_major_version) => {
    "ansible_distribution_major_version": "28", 
    "item": "ansible_distribution_major_version"
}

TASK [ensure we run Oracle Linux 7] ********************************************
fatal: [linuxdev]: FAILED! => {
    "changed": false
}

MSG:

This playbook only runs on Oracle Linux 7


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

So there we go! This example can easily be extended to check for other things, such as a combination of running UEK on Oracle Linux, etc.

Happy scripting!

Advertisements