Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Ansible variables in testinfra

Using TestInfra with Ansible backend for testing purposes. Everything goes fine except using Ansible itself while running tests

test.py

import pytest
def test_zabbix_agent_package(host):
    package = host.package("zabbix-agent")
    assert package.is_installed
    package_version = host.ansible("debug", "msg={{ zabbix_agent_version }}")["msg"]
    (...)

where zabbix_agent_version is an Ansible variable from group_vars. It can be obtained by running this playbook

- hosts: all
  become: true
  tasks:
  - name: debug
    debug: msg={{ zabbix_agent_version }}

command executing tests

pytest --connection=ansible --ansible-inventory=inventory  --hosts=$hosts -v test.py

ansible.cfg

[defaults]
timeout = 10
host_key_checking = False
library=library/
retry_files_enabled = False
roles_path=roles/
pipelining=true
ConnectTimeout=60
remote_user=deploy
private_key_file=/opt/jenkins/.ssh/deploy

the output I get is

self = <ansible>, module_name = 'debug', module_args = 'msg={{ zabbix_agent_version }}', check = True, kwargs = {}
result = {'failed': True, 'msg': "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'zabbix_agent_version' is undefined"}

    def __call__(self, module_name, module_args=None, check=True, **kwargs):
        if not self._host.backend.HAS_RUN_ANSIBLE:
            raise RuntimeError((
                "Ansible module is only available with ansible "
                "connection backend"))
        result = self._host.backend.run_ansible(
            module_name, module_args, check=check, **kwargs)
        if result.get("failed", False) is True:
>           raise AnsibleException(result)
E           AnsibleException: Unexpected error: {'failed': True,
E            'msg': u"the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'zabbix_agent_version' is undefined"}

/usr/lib/python2.7/site-packages/testinfra/modules/ansible.py:70: AnsibleException

Any idea why Ansible can't see this variable when running testinfra's Ansible module while it can see it while running Ansible alone?

like image 944
FRC Avatar asked Oct 17 '17 14:10

FRC


People also ask

How do you use Ansible variables in playbook?

Define Ansible Variables at Playbook Runtime Variables can also be defined when executing a playbook by passing the variables on the command line using the --extra-vars or -e argument. The variable is enclosed in a single-quoted string inside a pair of single curly braces.

How do you manage variables in Ansible?

Ansible uses variables to manage differences between systems. With Ansible, you can execute tasks and playbooks on multiple different systems with a single command. To represent the variations among those different systems, you can create variables with standard YAML syntax, including lists and dictionaries.

How do you write Ansible variables?

Ansible has a strict set of rules to create valid variable names. Variable names can contain only letters, numbers, and underscores and must start with a letter or underscore. Some strings are reserved for other purposes and aren't valid variable names, such as Python Keywords or Playbook Keywords.

Where can variables be defined for use in Ansible?

Automation with Ansible Playbooks Variable in playbooks are very similar to using variables in any programming language. It helps you to use and assign a value to a variable and use that anywhere in the playbook. One can put conditions around the value of the variables and accordingly use them in the playbook.


2 Answers

If zabbix_agent_version is a variable set using group_vars, then it seems as if you should be accessing it using host.ansible.get_variables() rather than running debug task. In any case, both should work. If I have, in my current directory:

test_myvar.py
group_vars/
  all.yml

And in group_vars/all.yml I have:

myvar: value

And in test_myvar.py I have:

def test_myvar_using_get_variables(host):
    all_variables = host.ansible.get_variables()
    assert 'myvar' in all_variables
    assert all_variables['myvar'] == 'myvalue'


def test_myvar_using_debug_var(host):
    result = host.ansible("debug", "var=myvar")
    assert 'myvar' in result
    assert result['myvar'] == 'myvalue'


def test_myvar_using_debug_msg(host):
    result = host.ansible("debug", "msg={{ myvar }}")
    assert 'msg' in result
    assert result['msg'] == 'myvalue'

Then all tests pass:

$ py.test --connection=ansible --ansible-inventory=hosts -v 
test_myvar.py 
============================= test session starts ==============================
platform linux2 -- Python 2.7.13, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- /home/lars/env/common/bin/python2
cachedir: .cache
rootdir: /home/lars/tmp/testinfra, inifile:
plugins: testinfra-1.8.1.dev2
collected 3 items                                                               

test_myvar.py::test_myvar_using_get_variables[ansible://localhost] PASSED
test_myvar.py::test_myvar_using_debug_var[ansible://localhost] PASSED
test_myvar.py::test_myvar_using_debug_msg[ansible://localhost] PASSED

=========================== 3 passed in 1.77 seconds ===========================

Can you confirm that the layout of our files (in particular, the location of your group_vars directory relative to the your tests) matches what I've shown here?

like image 100
larsks Avatar answered Oct 14 '22 04:10

larsks


I chased an answer to this for days. Here's what finally worked for me. Essentially you are using testinfra's Ansible module to access the include_vars function of Ansible.

import pytest

@pytest.fixture()
def AnsibleVars(host):
ansible_vars = host.ansible(
    "include_vars", "file=./group_vars/all/vars.yml")
return ansible_vars["ansible_facts"]

Then in my tests, I included the function as a parameter:

def test_something(host, AnsibleVars):

This solution was taken partially from https://github.com/metacloud/molecule/issues/151

I had an interesting issue where I was trying to include the variables from my main playbook and I was receiving an error of "must be stored as a dictionary/hash" when including the playbook.yml file. Separating the variables out into the group_vars/all/vars.yml file resolved that error.

like image 32
tjfo Avatar answered Oct 14 '22 04:10

tjfo