Ansible:从自定义模块中访问 host/group 变量

Ansible: Access host/group vars from within custom module

有没有一种方法可以从 custom written module 中访问 host/group 变量?我想避免将所有必需的变量作为模块参数传递。

我的模块是用 Python 编写的,我使用样板文件。我检查了几乎所有可用的变量,但它们没有存储在任何地方:

def main():
    pprint(dir())
    pprint(globals())
    pprint(locals())
    for name in vars().keys():
        print(name)

现在我唯一的希望是可以通过未记录的模块 utils 以某种方式访问​​它们。

我猜这是不可能的,因为模块在目标机器上运行,并且可能 facts/host/group 变量没有随模块一起传输...

编辑:现在找到了 module utils,但看起来不太乐观。

我觉得你的想法一针见血:

I guess it is not possible, since the module runs on the target machine and probably the facts/host/group vars are not transferred along with the module...

但是,话虽如此,如果您确实需要这样做,那么这样做的方式可能会有些混乱。从 Ansible 1.8 开始,您可以设置 fact caching, which uses redis to cache facts between runs of plays. Since redis is pretty easy to use and has clients for most popular programming languages,您可以让您的模块查询 redis 服务器以获取您需要的任何信息。这并不是最干净的方法,但它可能会起作用。

Is there a way how one can access host/group vars from within a custom written module?

不是内置的。

您必须自己以一种或另一种方式传递它们:

  • 模块参数。
  • 序列化到本地文件系统(使用 pickleyaml.dump()json 或 ...)并发送文件。
  • 您可以想出任何其他创新点子。

不幸的是,你不能按原样发送整个 host/groupvar 文件,因为你必须实现变量 scope/precedence 未定义的 ansible 解析算法(它不是 Zen philosophy of ansible 来定义这些琐碎的事情 :P).

--编辑--

我看到他们现在有 some precedence defined

Ansible does apply variable precedence, and you might have a use for it. Here is the order of precedence from least to greatest (the last listed variables override all other variables):

  • command line values (for example, -u my_user, these are not variables)
  • role defaults (defined in role/defaults/main.yml) 1
  • inventory file or script group vars 2
  • inventory group_vars/all 3
  • playbook group_vars/all 3
  • inventory group_vars/* 3
  • playbook group_vars/* 3
  • inventory file or script host vars 2
  • inventory host_vars/* 3
  • playbook host_vars/* 3
  • host facts / cached set_facts 4
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (defined in role/vars/main.yml)
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • include_vars
  • set_facts / registered vars
  • role (and include_role) params
  • include params
  • extra vars (for example, -e "user=my_user")(always win precedence)

In general, Ansible gives precedence to variables that were defined more recently, more actively, and with more explicit scope. Variables in the defaults folder inside a role are easily overridden. Anything in the vars directory of the role overrides previous versions of that variable in the namespace. Host and/or inventory variables override role defaults, but explicit includes such as the vars directory or an include_vars task override inventory variables.

Ansible merges different variables set in inventory so that more specific settings override more generic settings. For example, ansible_ssh_user specified as a group_var is overridden by ansible_user specified as a host_var. For details about the precedence of variables set in inventory, see How variables are merged.

Footnotes

1 Tasks in each role see their own role’s defaults. Tasks defined outside of a role see the last role’s defaults.

2(1,2) Variables defined in inventory file or provided by dynamic inventory.

3(1,2,3,4,5,6) Includes vars added by ‘vars plugins’ as well as host_vars and group_vars which are added by the default vars plugin shipped with Ansible.

4 When created with set_facts’s cacheable option, variables have the high precedence in the play, but are the same as a host facts precedence when they come from the cache.

根据您在 your answer here, I did manage to read host_vars and local play vars through a custom Action Plugin 中的建议。

我发布这个答案是为了完整起见,并给出一个明确的例子说明如何使用这个方法,尽管你最初提出了这个想法:)

注意 - 就功能齐全的插件而言,此示例不完整。它只是展示了如何访问变量。

from ansible.template import is_template
from ansible.plugins.action import ActionBase


class ActionModule(ActionBase):

    def run(self, tmp=None, task_vars=None):

        # some boilerplate ...
        
        # init
        result = super(ActionModule, self).run(tmp, task_vars)

        # more boilerplate ...

        # check the arguments passed to the task, where if missing, return None
        self._task.args.get('<TASK ARGUMENT NAME>', None)
        # or
        # check if the play has vars defined
        task_vars['vars']['<ARGUMENT NAME>']
        # or
        # check if the host vars has something defined
        task_vars['hostvars']['<HOST NAME FORM HOSTVARS>']['<ARGUMENT NAME>']

        # again boilerplate...

        # build arguments to pass to the module
        some_module_args = dict(
            arg1=arg1,
            arg2=arg2
        )

        # call the module with the above arguments...

如果您的 playbook 变量带有 jinja 2 模板,您可以在插件中解析这些模板,如下所示:

from ansible.template import is_template

# check if the variable is a template through 'is_template'
if is_template(var, self._templar.environment):
    # access the internal `_templar` object to resolve the template
    resolved_arg = self._templar.template(var_arg)

一些警告:

    1. 如果您在剧本中定义了如下变量
# things ...
#
      vars:
        - pkcs12_path: '{{ pkcs12_full_path }}'
        - pkcs12_pass: '{{ pkcs12_password }}'

变量 pkcs12_path 不得 匹配 host_vars 名称。

例如,如果你有 pkcs12_path: '{{ pkcs12_path }}',那么用上面的代码解析模板将导致递归异常......这对某些人来说可能是显而易见的,但对我来说令人惊讶的是 host_vars变量和剧本变量必须不能同名。

    1. 您也可以通过 task_vars['<ARG_NAME>'] 访问变量,但我不确定它是从哪里读取的。此外,它不如从 task_vars['vars']['<ARG_NAME>'] 或 hostvars.
    2. 获取变量那么明确

PS - 在撰写本文时,示例遵循 Ansible 考虑的动作插件的基本结构。将来,run 方法可能会更改其签名...