变量名称 *packages* 与 anisble_facts.packages 的冲突

Conflict of variable name *packages* with anisble_facts.packages

我有一个包含 Ansible 剧本的存储库。在一本剧本中,收集-installed.yml,我使用package_facts_module together with ansible-cmdb。 在我用来安装软件的下一个剧本中,我定义了一个 dict 类型的变量:packages:

packages:
  yum:
    - epel-release
    - yum-utils
...
  pip:
    - ruamel.yaml
...

用 yum 和 pip 描述 install/uninstall 的软件包。

这是 packages.yml 剧本中我的 role/task 的负责人:

---
- name: Display yum packages to install
  debug: var=packages.yum

- name: Install yum packages
  yum:
    name: "{{ packages.yum }}"
    state: present

收集-installed.yml:

- name: Gather installed packages
  ansible.builtin.package_facts:
    manager: auto

和我的 ansible.cfg:

[defaults]
fact_caching            = jsonfile
fact_caching_connection = cache

缓存目录为空时有效。如果我用 package_facts_module inovke 剧本 gather-installed.yml 我不能使用 packages.yml 剧本。 对 packages 变量的引用实际上是指 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"
        }
    ]
}
...

如果我将密钥 yum 更改为 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'

我不明白为什么我不能使用 defaults/main.yml 中定义的 packages 变量。我可以重命名该变量,但我想了解这个问题。

早些时候我没有遇到问题,因为我使用 python 库从 OS 获取已安装的软件包列表,它被插入 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()

来自 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.

但我不使用 ansible_facts.packageshostvars[target].ansible_facts.packages.

inject_facts_as_vars目前默认为true;虽然来自 setup 模块的事实保证以 ansible_ 为前缀,但并非所有模块都是如此。 package_facts 模块返回的事实没有此前缀,因此除非关闭此设置,否则它将破坏现有的 packages 变量。

默认情况下禁用事实注入是开发人员计划做的事情,但他们首先需要实现一种方法来在使用已弃用的变量时触发警告,以便弃用警告可以针对那些依赖的人这个功能。与此同时,任何没有实际使用注入变量的人都可以自由禁用它并防止随机模块污染它们的顶级变量命名空间。