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?
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.
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.
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.
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.
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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With