Conflict of variable name *packages* with anisble_facts.packages
I have a repository with Ansible playbooks. In one playbook, gather-installed.yml, I use package_facts_module together with ansible-cmdb. In next following playbook, which I use to install software, I have a variable of type dict defined: packages:
packages:
yum:
- epel-release
- yum-utils
...
pip:
- ruamel.yaml
...
which describes packages to install/uninstall with yum and pip.
Here is head of my role/task from packages.yml playbook:
---
- name: Display yum packages to install
debug: var=packages.yum
- name: Install yum packages
yum:
name: "{{ packages.yum }}"
state: present
gather-installed.yml:
- name: Gather installed packages
ansible.builtin.package_facts:
manager: auto
and my ansible.cfg:
[defaults]
fact_caching = jsonfile
fact_caching_connection = cache
It works if cache directory is empty. If I inovke the playbook gather-installed.yml with package_facts_module I cannot use the packages.yml playbook. A reference to the packages variable actually refers to ansible_facts.packages:
$ ansible-playbook playbooks/packages.yml -e 'target=node1'
Executing playbook packages.yml
- node1 on hosts: node1 -
Display yum packages to install...
node1 ok: {
"changed": false,
"packages.yum": [
{
"arch": "noarch",
"epoch": null,
"name": "yum",
"release": "168.el7.centos",
"source": "rpm",
"version": "3.4.3"
}
]
}
...
If I change the key yum into yum_install:
$ ansible-playbook playbooks/packages.yml -e 'target=node1'
Executing playbook packages.yml
- node1 on hosts: node1 -
Display yum packages to install...
node1 ok: {
"changed": false,
"packages.yum_install": "VARIABLE IS NOT DEFINED!"
}
Install yum packages...
node1 failed | msg: The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'yum_install'
I don't understand why I cannot use the packages variable defined in defaults/main.yml. I can rename that variable but I want to understand this issue.
Earlier I didn't have a problem because I used python library to obtain a list installed packages from OS, which was inserted in custom_facts:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rpm
def _check_installed(module):
ts = rpm.TransactionSet()
mi = ts.dbMatch()
results = {}
for h in mi:
name = h['name']
version = h['version']
release = h['release']
arch = h['arch']
if results.has_key(name):
value = results[name]
value.append(("%s-%s.%s" % (version,release,arch)))
results[name] = value
else:
results[name] = [("%s-%s.%s" % (version,release,arch))]
custom_facts = { "Packages installed": results }
return { 'ansible_facts': { 'custom_facts': custom_facts } }
def main():
module = AnsibleModule(
argument_spec = dict(
),
supports_check_mode = True,
)
data = _check_installed(module)
module.exit_json(**data)
from ansible.module_utils.basic import *
main()
From package_facts_module:
Facts returned by this module are added/updated in the hostvars host facts and can be referenced by name just like any other host fact. They do not need to be registered in order to use them.
but I don't use either ansible_facts.packages or hostvars[target].ansible_facts.packages.
inject_facts_as_vars currently defaults to true
; while facts from the setup
module are guaranteed to be prefixed with ansible_
, this is not the case for all modules. The fact returned by the package_facts
module does not have this prefix, so it will clobber an existing packages
variable unless this setting is turned off.
Disabling fact injection by default is something the developers plan to do, but they first need to implement a way to trigger a warning when a deprecated variable is used, so that deprecation warnings can be targeted to people who are relying on this functionality. In the meantime, anyone who's not actually using the injected vars is free to disable it and prevent random modules from polluting their top-level variable namespace.