Using Testinfra with Ansible
Tags: ansible, testing, python
Published: 2019-11-10 06:38:31

Testinfra which tests infrastructure works really well with Ansible.
I posted about Testinfra previously so please check if you like.

{% link https://dev.to/koh_sh/play-with-testinfra-which-tests-states-of-infrastructure-1bl2 %}

## Sharing Inventory

Testinfra can refer to Inventories of Ansible.
So you don't have to re-define hosts information for Ansible and tests too.

https://testinfra.readthedocs.io/en/latest/backends.html#ansible

```
$ py.test --hosts='ansible://all' # tests all inventory hosts
$ py.test --hosts='ansible://host1,ansible://host2'
$ py.test --hosts='ansible://web*'
$ py.test --force-ansible --hosts='ansible://all'
$ py.test --hosts='ansible://host?force_ansible=True'
```

If the inventory file is not specified with `ansible.cfg`, you can specify with `--ansible-inventory=ANSIBLE_INVENTORY` in command lines.

## Executing modules of Ansible

You can execute modules of Ansible while testing.

https://testinfra.readthedocs.io/en/latest/modules.html#ansible

```
[koh@kohs-MBP] ~/vag_test
% ipython
Python 3.7.3 (default, May  1 2019, 16:07:48)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import testinfra

In [2]: host = testinfra.get_host("ansible://Vag1")

In [3]: host.ansible("command", "echo foo", check=False)
Out[3]:
{'ansible_facts': {'discovered_interpreter_python': '/usr/bin/python'},
 'changed': True,
 'cmd': ['echo', 'foo'],
 'delta': '0:00:00.004734',
 'end': '2019-10-05 15:07:42.251008',
 'rc': 0,
 'start': '2019-10-05 15:07:42.246274',
 'stderr': '',
 'stderr_lines': [],
 'stdout': 'foo',
 'stdout_lines': ['foo']}

In [4]:
```
This is an example of executing command module of Ansible with Testinfra.  
As a result of executing a module, you can get like `changed`, `rc` or `stdout` same as Ansible.

`setup` module would fit well with testing.

```
[koh@kohs-MBP] ~/vag_test
% cat test_ansible.py
def test_dns(host):
    nameservers = host.ansible("setup")["ansible_facts"]["ansible_dns"]["nameservers"]
    assert '10.1.1.1' in nameservers
[koh@kohs-MBP] ~/vag_test
% 
[koh@kohs-MBP] ~/vag_test
% py.test -v test_ansible.py --hosts='ansible://Vag1'
======================================== test session starts ========================================
platform darwin -- Python 3.7.3, pytest-4.4.1, py-1.8.0, pluggy-0.9.0 -- /Users/koh/.pyenv/versions/3.7.3/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/koh/vag_test, inifile: pytest.ini
plugins: xonsh-0.8.12, testinfra-3.0.5
collected 1 item

test_ansible.py::test_dns[ansible://Vag1] FAILED                                              [100%]

============================================= FAILURES ==============================================
_____________________________________ test_dns[ansible://Vag1] ______________________________________

host = <testinfra.host.Host object at 0x10e9be748>

    def test_dns(host):
        nameservers = host.ansible("setup")["ansible_facts"]["ansible_dns"]["nameservers"]
>       assert '10.1.1.1' in nameservers
E       AssertionError: assert '10.1.1.1' in ['10.0.2.3']

test_ansible.py:3: AssertionError
===================================== 1 failed in 2.17 seconds ======================================
zsh: exit 1     py.test -v test_ansible.py --hosts='ansible://Vag1'
[koh@kohs-MBP] ~/vag_test
%
```

This is an example of checking nameserver of resolv.conf with setup module.

## Refering Variable

You can refer to variables of Ansible.
`host_vars`, `group_vars` and magic variables like `inventory_hostname` can be referred from Testinfra.
Variables defined with `include_vars` in Playbooks cannot be referred.

```
[koh@kohs-MBP] ~/vag_test
% ipython
Python 3.7.3 (default, May  1 2019, 16:07:48)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import testinfra

In [2]: host = testinfra.get_host("ansible://Vag1")

In [3]: host.ansible.get_variables()
Out[3]:
{'aaa': 'bbb',
 'foo': 'bar',
 'hoge': 'fuga',
 'inventory_hostname': 'Vag1',
 'group_names': ['Vag'],
 'groups': {'Vag': ['Vag1', 'Vag2', 'Vag3'], 'all': ['Vag1', 'Vag2', 'Vag3']}}

In [4]:
```

## Conclusion

As currently, Molecule use Testinfra as the default test tool, Testinfra works really well with Ansible.
Testinfra can be used with Docker or Kubernetes too, so I would keep trying them out.