Ansible 模块架构

如果你正在处理 ansible-core 代码、编写 Ansible 模块或开发 action 插件,你可能需要了解 Ansible 的程序流程是如何执行的。如果你只是在 playbook 中使用 Ansible 模块,则可以跳过本节。

模块类型

Ansible 在其代码库中支持几种不同的模块类型。其中一些是为了向后兼容性,而另一些是为了提高灵活性。

Action 插件

对于任何编写 playbook 的人来说,Action 插件看起来都像模块。大多数 Action 插件的使用文档都位于同名模块内。一些 Action 插件完成所有工作,而模块只提供文档。一些 Action 插件执行模块。 normal Action 插件执行没有特殊 Action 插件的模块。Action 插件始终在控制节点上执行。

一些 Action 插件在控制节点上完成所有工作。例如, debug Action 插件(打印用户可以看到的文本)和 assert Action 插件(测试 playbook 中的值是否满足特定条件)完全在控制节点上执行。

大多数 Action 插件在控制节点上设置一些值,然后在被管理的节点上调用一个实际的模块来处理这些值。例如, template Action 插件使用来自 playbook 环境的变量,从用户处获取值来在控制节点上的临时位置构造一个文件。然后,它将临时文件传输到远程系统上的临时文件。之后,它调用 copy 模块,该模块在远程系统上运行,将文件移动到其最终位置,设置文件权限等。

新式模块

所有与 Ansible 一起提供的模块都属于此类别。虽然你可以使用任何语言编写模块,但所有官方模块(与 Ansible 一起提供的)都使用 Python 或 PowerShell。

新式模块以某种方式在其内部嵌入了模块的参数。旧式模块必须将一个单独的文件复制到被管理的节点,这效率较低,因为它需要两个网络连接而不是只有一个。

Python

新式 Python 模块使用 Ansiballz 框架 来构建模块。这些模块使用来自 ansible.module_utils 的导入来引入样板模块代码,例如参数解析、将返回值格式化为 JSON 以及各种文件操作。

注意

在 Ansible 2.0.x 版本之前,官方 Python 模块使用 模块替换框架。对于模块作者来说, Ansiballz 框架 很大程度上是 模块替换框架 功能的超集,因此你通常不需要了解它们之间的区别。

PowerShell

新式 PowerShell 模块使用 模块替换框架 来构建模块。这些模块在发送到被管理的节点之前,会嵌入一个 PowerShell 代码库。

JSONARGS 模块

这些模块是脚本,在其主体中包含字符串 <<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>> 。此字符串将被替换为 JSON 格式的参数字符串。这些模块通常会将变量设置为该值,如下所示:

json_arguments = """<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>"""

展开后为:

json_arguments = """{"param1": "test's quotes", "param2": "\"To be or not to be\" - Hamlet"}"""

注意

Ansible 输出一个带有裸引号的 JSON 字符串。双引号用于引用字符串值,字符串值内部的双引号用反斜杠转义,单引号可以在字符串值内未转义地出现。要使用 JSONARGS,你的脚本语言必须能够处理这种类型的字符串。该示例使用 Python 的三引号字符串来实现这一点。其他脚本语言可能有类似的引号字符,不会与 JSON 中的任何引号混淆,或者它可能允许你定义自己的引号开始和结束字符。如果语言没有提供任何这些功能,则需要编写 非原生 JSON 模块旧式模块

这些模块通常使用 JSON 库解析 json_arguments 的内容,然后将它们用作代码中的原生变量。

非原生 WANT JSON 模块

如果模块在任何地方都包含字符串 WANT_JSON ,则 Ansible 会将其视为接受文件名作为其唯一命令行参数的非原生模块。文件名用于包含 JSON 字符串的临时文件,该字符串包含模块的参数。模块需要打开文件,读取和解析参数,对数据进行操作,并在退出之前将返回数据作为 JSON 编码的字典打印到 stdout。

这些类型的模块是自包含的实体。从 Ansible 2.1 开始,Ansible 只修改它们以更改 shebang 行(如果存在)。

另见

用 ruby 编写的非原生模块示例位于 Ansible for Rubyists 代码库中。

二进制模块

从 Ansible 2.2 开始,模块也可以是小型的二进制程序。Ansible 不会执行任何使其可移植到不同系统的操作,因此它们可能特定于编译它们的系统,或者需要其他二进制运行时依赖项。尽管有这些缺点,如果这是访问某些资源的唯一方法,你可能必须针对特定二进制库编译自定义模块。

二进制模块与其参数和返回值的方式与 WANT JSON 模块 相同。

另见

一个用 go 编写的 二进制模块 示例。

旧式模块

旧式模块类似于 WANT JSON 模块,除了它们接受的文件包含 key=value 对作为其参数,而不是 JSON。当模块没有任何其他类型的标记时,Ansible 会将其判断为旧式模块。

模块如何执行

当用户使用ansibleansible-playbook时,他们会指定一个要执行的任务。这个任务通常是模块的名称以及要传递给模块的多个参数。Ansible会以各种方式处理这些值,然后最终在远程机器上执行。

执行器/task_executor

TaskExecutor接收从剧本(或在/usr/bin/ansible命令行情况下)解析的模块名称和参数。它使用名称来确定它查看的是模块还是动作插件。如果它是一个模块,它会加载普通动作插件,并将名称、变量以及关于任务和剧本的其他信息传递给该动作插件以进行进一步处理。

normal动作插件

normal动作插件在远程主机上执行模块。它是实际在被管理机器上执行模块的大部分工作的首要协调者。

  • 它为任务加载合适的连接插件,然后根据需要进行传输或执行以创建到该主机的连接。

  • 它将任何内部Ansible属性添加到模块的参数中(例如,将no_log传递给模块的那些属性)。

  • 它与其他插件(连接、shell、become、其他动作插件)一起在远程机器上创建任何临时文件,并在之后清理。

  • 它将模块和模块参数推送到远程主机,尽管下一节中描述的module_common代码决定了这些参数将采用哪种格式。

  • 它处理关于模块的任何特殊情况(例如,异步执行,或围绕必须与Python模块具有相同名称的Windows模块的复杂情况,以便其他动作插件可以从内部调用模块)。

许多功能来自BaseAction类,它位于plugins/action/__init__.py中。它使用ConnectionShell对象来完成其工作。

注意

当使用async:参数运行任务时,Ansible使用async动作插件而不是normal动作插件来调用它。该程序流程目前没有记录。请阅读源代码以了解其工作原理。

执行器/module_common.py

executor/module_common.py中的代码组装要发送到被管理节点的模块。首先读取模块,然后检查其类型。

在组装步骤之后,对所有具有shebang行(#!)的模块进行最终修改。Ansible检查shebang行中的解释器是否具有使用ansible_$X_interpreter清单变量配置的特定路径。如果存在,Ansible会将该路径替换为模块中给出的解释器路径。之后,Ansible将完整的模块数据和模块类型返回给普通动作,继续执行模块。

组装框架

Ansible支持两种组装框架:Ansiballz和较旧的模块替换器。

模块替换器框架

模块替换器框架是实现新式模块的原始框架,并且仍然用于PowerShell模块。它本质上是一个预处理器(对于熟悉该编程语言的人来说,就像C预处理器一样)。它直接替换模块文件中的特定子字符串模式。有两种类型的替换:

  • 仅在模块文件中发生的替换。这些是模块可以利用其获得有用的样板或访问参数的公共替换字符串。

    • from ansible.module_utils.MOD_LIB_NAME import *ansible/module_utils/MOD_LIB_NAME.py的内容替换。这只能与新式Python模块一起使用。

    • #<<INCLUDE_ANSIBLE_MODULE_COMMON>> 等效于 from ansible.module_utils.basic import *,也应该只适用于新式Python模块。

    • # POWERSHELL_COMMON 替换 ansible/module_utils/powershell.ps1 的内容。它应该只与新式Powershell模块一起使用。

  • ansible.module_utils代码使用的替换。这些是内部替换模式。它们可以在上述公共替换中内部使用,但不应直接由模块使用。

    • "<<ANSIBLE_VERSION>>"被Ansible版本替换。在新式Python模块Ansiballz框架下,更好的方法是实例化一个AnsibleModule,然后从:attr:AnsibleModule.ansible_version访问版本。

    • "<<INCLUDE_ANSIBLE_MODULE_COMPLEX_ARGS>>"被替换为一个字符串,该字符串是JSON编码的模块参数的Pythonrepr。对JSON字符串使用repr使其可以安全地嵌入到Python文件中。在新式Python模块的Ansiballz框架下,最好通过实例化AnsibleModule然后使用AnsibleModule.params来访问它。

    • <<SELINUX_SPECIAL_FILESYSTEMS>>替换一个字符串,该字符串是一个逗号分隔的字符串,其中包含在SELinux中具有文件系统相关的安全上下文的各个文件系统。在新式Python模块中,如果您确实需要这个,您应该实例化一个AnsibleModule,然后使用AnsibleModule._selinux_special_fs来访问它。该变量也已从逗号分隔的文件系统名称字符串更改为实际的文件系统名称的Python列表。

    • <<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>将模块参数替换为JSON字符串。必须注意正确地引用该字符串,因为JSON数据可能包含引号。在新式Python模块中不会替换此模式,因为它们可以通过其他方式获取模块参数。

    • 字符串syslog.LOG_USER在出现的地方都会被ansible.cfg或任何适用于此主机的ansible_syslog_facility清单变量中命名的syslog_facility替换。在新式Python模块中,这一点略有变化。如果您确实需要访问它,则应实例化一个AnsibleModule,然后使用AnsibleModule._syslog_facility来访问它。它不再是实际的syslog工具,而是syslog工具的名称。有关详细信息,请参阅内部参数文档

Ansiballz框架

Ansiballz 框架在 Ansible 2.1 中被采用,并用于所有新型 Python 模块。与模块替换器不同,Ansiballz 使用 ansible/module_utils 中的真实 Python 导入,而不是仅仅预处理模块。它是通过构建一个 zip 文件来实现的——该文件包含模块文件、模块导入的 ansible/module_utils 中的文件,以及一些用于传递模块参数的样板代码。然后对 zip 文件进行 Base64 编码,并将其包装在一个小的 Python 脚本中,该脚本解码 Base64 编码并将 zip 文件放置到被管理节点上的临时目录中。然后,它只从 zip 文件中提取 Ansible 模块脚本,并将其也放置在临时目录中。然后,它设置 PYTHONPATH 以查找 zip 文件中的 Python 模块,并将 Ansible 模块作为特殊名称 __main__ 导入。将其导入为 __main__ 会使 Python 认为它正在执行脚本而不是仅仅导入模块。这允许 Ansible 在远程机器上的单个 Python 副本中运行包装器脚本和模块代码。

注意

  • Ansible 将 zip 文件包装在 Python 脚本中有两个原因:

    • 为了与 Python 2.6 兼容,Python 2.6 具有功能较弱的 Python -m 命令行开关版本。

    • 以便管道能够正常工作。管道需要将 Python 模块传递到远程节点上的 Python 解释器。Python 理解 stdin 上的脚本,但不理解 zip 文件。

  • 在 Ansible 2.7 之前,模块由第二个 Python 解释器执行,而不是在同一个进程中执行。一旦放弃 Python 2.4 支持,就进行了此更改以加快模块执行速度。

在 Ansiballz 中,从 ansible.module_utils 包导入 Python 模块的任何操作都会触发将该 Python 文件包含到 zip 文件中。模块中的 #<<INCLUDE_ANSIBLE_MODULE_COMMON>> 实例将转换为 from ansible.module_utils.basic import *,然后 ansible/module-utils/basic.py 将包含在 zip 文件中。从 module_utils 包含的文件本身也会被扫描,以查找从 module_utils 导入的其他 Python 模块,以便也包含在 zip 文件中。

传递参数

这两个框架传递参数的方式不同:

  • 模块替换器框架 中,模块参数被转换为 JSON 字符串,并被替换到组合的模块文件中。

  • Ansiballz 框架 中,JSON 字符串是包装 zip 文件的脚本的一部分。在包装器脚本将 Ansible 模块导入为 __main__ 之前,它使用变量值修补 basic.py 中的私有变量 _ANSIBLE_ARGS。当 ansible.module_utils.basic.AnsibleModule 实例化时,它会解析此字符串并将参数放入 AnsibleModule.params,模块的其他代码可以在其中访问它。

警告

如果您正在编写模块,请记住,我们传递参数的方式是一个内部实现细节:它过去已经改变过,并且一旦通用 module_utils 代码的更改允许 Ansible 模块不再使用 ansible.module_utils.basic.AnsibleModule,它将再次改变。不要依赖内部全局变量 _ANSIBLE_ARGS

需要在实例化 AnsibleModule 之前解析参数的非常动态的自定义模块可以使用 _load_params 来检索这些参数。尽管如果需要支持代码更改,_load_params 可能会以破坏性的方式发生变化,但它可能比我们传递参数的方式或内部全局变量更稳定。

注意

在 Ansible 2.7 之前,Ansible 模块在第二个 Python 解释器中调用,然后参数通过脚本的 stdin 传递给脚本。

内部参数

模块替换器框架Ansiballz 框架 都向 Ansible 模块发送除用户在 playbook 中指定的参数之外的其他参数。这些附加参数是帮助实现全局 Ansible 功能的内部参数。模块通常不需要明确了解这些参数,因为这些功能是在 ansible.module_utils.basic 中实现的。但是,某些功能需要模块的支持,并且了解一些内部参数是有用的。

本节中的内部参数是全局的。如果您需要向自定义模块添加本地内部参数,请为该特定模块创建一个操作插件。有关示例,请参阅 copy 操作插件 中的 _original_basename

_ansible_no_log

类型:bool

当任务或剧集中的参数指定 no_log 时,将其设置为 True。任何调用 AnsibleModule.log() 函数的模块都会自动处理此操作。如果您有一个实现自身日志记录的模块,则需要检查 _ansible_no_log 的值。要在模块中访问 _ansible_no_log,请实例化 AnsibleModule 实用程序,然后检查 AnsibleModule.no_log 的值。

注意

在模块的 argument_spec 中指定的 no_log 由不同的机制处理。

_ansible_debug

类型:bool

操作详细日志记录和模块执行的外部命令的日志记录。如果模块使用 AnsibleModule.debug() 函数而不是 AnsibleModule.log() 函数,则只有在将 _ansible_debug 参数设置为 True 时才会记录消息。要在模块中访问 _ansible_debug,请实例化 AnsibleModule 实用程序并访问 AnsibleModule._debug。更多详细信息,请参阅 DEFAULT_DEBUG

_ansible_diff

类型:bool

使用此参数,您可以配置模块以显示将应用于模板文件的更改的统一差异。要在模块中访问 _ansible_diff,请实例化 AnsibleModule 实用程序并访问 AnsibleModule._diff。您也可以使用 playbook 中的 diff 关键字或相关的环境变量来访问此参数。更多详细信息,请参阅 Playbook 关键字DIFF_ALWAYS 配置选项。

_ansible_verbosity

类型:int

您可以使用此参数来控制日志记录中详细程度的级别(0 表示无)。

_ansible_selinux_special_fs

类型:list 元素:strings

此参数为模块提供应具有特殊 SELinux 上下文的系统名称。它们由操作文件的 AnsibleModule 方法(更改属性、移动和复制)使用。

大多数模块可以使用内置的 AnsibleModule 方法来操作文件。要在需要了解这些特殊上下文文件系统的模块中访问,请实例化 AnsibleModule 并检查 AnsibleModule._selinux_special_fs 中的列表。

此参数替换了 ansible.module_utils.basic.SELINUX_SPECIAL_FS(来自 模块替换框架)。在模块替换框架中,该参数格式化为用逗号分隔的文件系统名称字符串。在 Ansiballz 框架下,它是一个列表。您可以使用相应的环境变量访问 _ansible_selinux_special_fs。更多详细信息,请参见 DEFAULT_SELINUX_SPECIAL_FS 配置选项。

2.1 版本新增。

_ansible_syslog_facility

此参数控制模块记录到哪个 syslog 设施。大多数模块应该只使用 AnsibleModule.log() 函数,该函数将随后使用此参数。如果模块必须自行使用此参数,则应实例化 AnsibleModule 方法,然后从 AnsibleModule._syslog_facility 中检索 syslog 设施的名称。Ansiballz 代码不如 模块替换框架 代码优雅。

# Old module_replacer way
import syslog
syslog.openlog(NAME, 0, syslog.LOG_USER)

# New Ansiballz way
import syslog
facility_name = module._syslog_facility
facility = getattr(syslog, facility_name, syslog.LOG_USER)
syslog.openlog(NAME, 0, facility)

更多详细信息,请参见 DEFAULT_SYSLOG_FACILITY 配置选项。

2.1 版本新增。

_ansible_version

此参数将 Ansible 的版本传递给模块。要访问它,模块应实例化 AnsibleModule 方法,然后从 AnsibleModule.ansible_version 中检索版本。这替换了 ansible.module_utils.basic.ANSIBLE_VERSION(来自 模块替换框架)。

2.1 版本新增。

_ansible_module_name

类型:str

此参数向模块传递有关其名称的信息。更多详细信息,请参见配置选项 DEFAULT_MODULE_NAME

_ansible_keep_remote_files

类型:bool

此参数提供说明,如果模块需要保留远程文件,则必须准备好。更多详细信息,请参见 DEFAULT_KEEP_REMOTE_FILES 配置选项。

_ansible_socket

此参数为模块提供用于持久连接的套接字。该参数使用 PERSISTENT_CONTROL_PATH_DIR 配置选项创建。

_ansible_shell_executable

类型:bool

此参数确保模块使用指定的 shell 可执行文件。更多详细信息,请参见远程主机环境参数 ansible_shell_executable

_ansible_tmpdir

类型:str

此参数向模块提供说明,所有命令都必须使用指定的临时目录(如果已创建)。操作插件设计此临时目录。

模块可以通过使用公共 tmpdir 属性来访问此参数。如果操作插件未设置参数,则 tmpdir 属性将创建一个临时目录。

目录名是随机生成的,目录的根目录由以下一项确定:

因此,使用 ansible.cfg 配置文件激活或自定义此设置并不能保证您可以控制完整的值。

_ansible_remote_tmp

如果操作插件未设置 _ansible_tmpdir,则模块的 tmpdir 属性在此目录中创建一个随机化的目录名。更多详细信息,请参见 shell 插件的 remote_tmp 参数。

模块返回值和不安全字符串

在模块执行结束时,它会将其想要返回的数据格式化为 JSON 字符串,并将该字符串打印到其标准输出。正常的操作插件接收 JSON 字符串,将其解析为 Python 字典,并将其返回给执行程序。

如果 Ansible 对每个字符串返回值进行模板化,它将容易受到具有受管理节点访问权限的用户的攻击。如果一个不怀好意的用户将恶意代码伪装成 Ansible 返回值字符串,并且这些字符串随后在控制节点上进行模板化,则 Ansible 可能会执行任意代码。为了防止这种情况,Ansible 将返回数据中的所有字符串标记为 Unsafe,按字面意思发出字符串中的任何 Jinja2 模板,而不是由 Jinja2 展开。

通过 ActionPlugin._execute_module() 调用模块返回的字符串会自动被正常的操作插件标记为 Unsafe。如果另一个操作插件通过其他方式从模块检索信息,则它必须自行将其返回数据标记为 Unsafe

如果代码编写不佳的操作插件未能将其结果标记为“不安全”,则 Ansible 在将结果返回给执行程序时会再次审核结果,并将所有字符串标记为 Unsafe。正常的操作插件使用结果数据作为参数来保护自身及其调用的任何其他代码。执行程序内部的检查保护所有其他操作插件的输出,确保 Ansible 随后运行的任务也不会从这些结果中模板化任何内容。

特殊注意事项

流水线

Ansible 可以通过两种方式之一将模块传输到远程计算机:

  • 它可以将模块写入远程主机上的临时文件,然后使用与远程主机的第二个连接来使用模块所需的解释器执行它。

  • 或者,它可以使用所谓的流水线通过将其管道输送到远程解释器的 stdin 来执行模块。

目前,流水线仅适用于用 Python 编写的模块,因为 Ansible 只知道 Python 支持这种操作模式。支持流水线意味着模块有效负载在通过网络发送之前的任何格式都必须能够通过 stdin 由 Python 执行。

为什么要通过 stdin 传递参数?

选择通过 stdin 传递参数的原因如下:

  • 当与 ANSIBLE_PIPELINING 结合使用时,这可以防止模块的参数暂时保存在远程机器上的磁盘上。这使得远程机器上的恶意用户更难(但并非不可能)窃取参数中可能存在的任何敏感信息。

  • 命令行参数是不安全的,因为大多数系统允许非特权用户读取进程的完整命令行。

  • 环境变量通常比命令行更安全,但某些系统限制环境的总大小。如果我们达到此限制,这可能会导致参数被截断。

AnsibleModule

参数规范

提供给 AnsibleModuleargument_spec 定义了模块支持的参数,以及它们的类型、默认值等等。

示例 argument_spec

module = AnsibleModule(argument_spec=dict(
    top_level=dict(
        type='dict',
        options=dict(
            second_level=dict(
                default=True,
                type='bool',
            )
        )
    )
))

本节将讨论参数的行为属性。

type:

type 允许您定义为参数接受的值的类型。type 的默认值为 str。可能的值为:

  • str

  • list

  • dict

  • bool

  • int

  • float

  • path

  • raw

  • jsonarg

  • json

  • bytes

  • bits

raw 类型不执行类型验证或类型转换,并保持传递值的类型。

elements:

elementstype='list' 时与 type 结合使用。elements 然后可以定义为 elements='int' 或任何其他类型,表示指定列表的每个元素都应为该类型。

default:

default 选项允许为参数未提供给模块的情况设置参数的默认值。未指定时,默认值为 None

fallback:

fallback 接受一个 tuple,其中第一个参数是一个可调用对象(函数),它将用于基于第二个参数执行查找。第二个参数是要由可调用对象接受的值列表。

最常用的可调用函数是 env_fallback,它允许在未提供参数时,可选地使用环境变量。

示例

username=dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME']))
choices:

choices 接受一个参数将接受的选择列表。choices 的类型应与 type 匹配。

required:

required 接受一个布尔值,TrueFalse,指示参数是否必填。未指定时,required 默认值为 False。这不能与 default 结合使用。

no_log:

no_log 接受一个布尔值,TrueFalse,显式指示是否应在日志和输出中屏蔽参数值。

注意

如果没有 no_log,如果参数名称似乎表明参数值是密码或通行短语(例如“admin_password”),则会显示警告,并且该值将在日志中被屏蔽,但在输出中不会被屏蔽。要禁用对不包含敏感信息的参数的警告和屏蔽,请将 no_log 设置为 False

aliases:

aliases 接受参数的替代参数名称列表,例如参数为 name 但模块接受 aliases=['pkg'] 以允许 pkgname 交换使用。使用别名可能会使模块接口混乱,因此我们建议仅在必要时添加它们。如果您正在更新参数名称以修复错别字或改进接口,请考虑将旧名称移至 deprecated_aliases,而不是无限期地保留它们。

options:

options 实现创建子参数规范的功能,其中顶级参数的子选项也使用本节中讨论的属性进行验证。本节开头的示例演示了 options 的用法。typeelements 在这种情况下应为 dict

apply_defaults:

apply_defaultsoptions 配合使用,允许即使未提供顶级参数,也可以应用子选项的 default

在本节开头的 argument_spec 示例中,即使用户在调用模块时未提供 top_level,它也允许定义 module.params['top_level']['second_level']

removed_in_version:

removed_in_version 指示将在哪个版本的 ansible-core 或集合中删除弃用的参数。与 removed_at_date 互斥,必须与 removed_from_collection 一起使用。

示例

option = {
  'type': 'str',
  'removed_in_version': '2.0.0',
  'removed_from_collection': 'testns.testcol',
},
removed_at_date:

removed_at_date 指示将在此日期之后的小版本 ansible-core 版本或大版本集合版本中删除弃用的参数。与 removed_in_version 互斥,必须与 removed_from_collection 一起使用。

示例

option = {
  'type': 'str',
  'removed_at_date': '2020-12-31',
  'removed_from_collection': 'testns.testcol',
},
removed_from_collection:

指定哪个集合(或 ansible-core)弃用此弃用的参数。对于 ansible-core,指定 ansible.builtin,或集合的名称(格式 foo.bar)。必须与 removed_in_versionremoved_at_date 一起使用。

deprecated_aliases:

弃用此参数的别名。必须包含一个字典列表或元组,其中包含以下一些键

name:

要弃用的别名名称。(必填。)

version:

将在哪个版本的 ansible-core 或集合中删除此别名。必须指定 versiondate

date:

ansible-core 的小版本或集合的大版本将不再包含此别名的日期。必须指定 versiondate

collection_name:

指定哪个集合(或 ansible-core)弃用此弃用的别名。对于 ansible-core,指定 ansible.builtin,或集合的名称(格式 foo.bar)。必须与 versiondate 一起使用。

示例

option = {
  'type': 'str',
  'aliases': ['foo', 'bar'],
  'deprecated_aliases': [
    {
      'name': 'foo',
      'version': '2.0.0',
      'collection_name': 'testns.testcol',
    },
    {
      'name': 'foo',
      'date': '2020-12-31',
      'collection_name': 'testns.testcol',
    },
  ],
},
mutually_exclusive:

如果指定了 options,则 mutually_exclusive 指的是 options 中描述的子选项,其行为方式与 模块选项之间的依赖关系 中相同。

required_together:

如果指定了 options,则 required_together 指的是 options 中描述的子选项,其行为方式与 模块选项之间的依赖关系 中相同。

required_one_of:

如果指定了 options,则 required_one_of 指的是 options 中描述的子选项,其行为方式与 模块选项之间的依赖关系 中相同。

required_if:

如果指定了 options,则 required_if 指的是 options 中描述的子选项,其行为方式与 模块选项之间的依赖关系 中相同。

required_by:

如果指定了 options,则 required_by 指的是 options 中描述的子选项,其行为方式与 模块选项之间的依赖关系 中相同。

context:

2.17 版中的新功能。

您可以将 context 键的值设置为自定义内容的字典。这允许您在参数规范中提供其他上下文。核心引擎不会验证或使用提供的内容。

示例

option = {
    'type': 'str',
    'context': {
        'disposition': '/properties/apiType',
    },
    'choices': ['http', 'soap'],
}

模块选项之间的依赖关系

以下是 AnsibleModule() 的可选参数

module = AnsibleModule(
  argument_spec,
  mutually_exclusive=[
    ('path', 'content'),
  ],
  required_one_of=[
    ('path', 'content'),
  ],
)
mutually_exclusive:

必须是字符串序列(列表或元组)的序列。每个字符串序列都是选项名称列表,它们是互斥的。如果同时指定了列表中的多个选项,Ansible 将会报错。

示例

mutually_exclusive=[
  ('path', 'content'),
  ('repository_url', 'repository_filename'),
],

在此示例中,不能同时指定选项 pathcontent。同样,不能同时指定选项 repository_urlrepository_filename。但是,允许同时指定 pathrepository_url

要确保精确地指定两个(或更多)选项中的一个,请将 mutually_exclusiverequired_one_of 结合使用。

required_together:

必须是字符串序列(列表或元组)的序列。每个字符串序列都是必须一起指定的选项名称列表。如果指定了这些选项中的至少一个,则来自同一序列的其他选项都必须存在。

示例

required_together=[
  ('file_path', 'file_hash'),
],

在此示例中,如果指定了选项 file_pathfile_hash 中的一个,则如果另一个未指定,Ansible 将会报错。

required_one_of:

必须是字符串序列(列表或元组)的序列。每个字符串序列都是选项名称列表,其中必须至少指定一个。如果没有指定这些选项中的任何一个,Ansible 将会报错。

示例

required_one_of=[
  ('path', 'content'),
],

在此示例中,必须至少指定 pathcontent 中的一个。如果没有指定,则执行将失败。明确允许同时指定两者;要防止这种情况,请将 required_one_ofmutually_exclusive 结合使用。

required_if:

必须是序列的序列。每个内部序列描述一个条件依赖关系。每个序列必须包含三个或四个值。前两个值是选项的名称和选项的值,用于描述条件。序列的后续元素仅在该名称的选项具有此确切值时才需要。

如果您希望在满足条件时指定选项名称列表中的所有选项,请使用以下其中一种形式

('option_name', option_value, ('option_a', 'option_b', ...)),
('option_name', option_value, ('option_a', 'option_b', ...), False),

如果您希望在满足条件时指定选项名称列表中的至少一个选项,请使用以下形式

('option_name', option_value, ('option_a', 'option_b', ...), True),

示例

required_if=[
  ('state', 'present', ('path', 'content'), True),
  ('force', True, ('force_reason', 'force_code')),
],

在这个例子中,如果用户指定了state=present,则必须提供pathcontent选项中的至少一个(或两者)。为了确保只能指定一个,请将required_ifmutually_exclusive结合使用。

另一方面,如果force(布尔参数)设置为trueyes等等,则必须同时指定force_reasonforce_code

required_by:

必须是一个字典,将选项名称映射到选项名称序列。如果字典键中的选项名称已指定,则它映射到的选项名称也必须全部指定。请注意,您可以指定单个选项名称,而不是选项名称序列。

示例

required_by={
  'force': 'force_reason',
  'path': ('mode', 'owner', 'group'),
},

在这个例子中,如果指定了force,则还必须指定force_reason。此外,如果指定了path,则还必须指定三个选项modeownergroup

声明检查模式支持

要声明模块支持检查模式,请向AnsibleModule()调用提供supports_check_mode=True

module = AnsibleModule(argument_spec, supports_check_mode=True)

模块可以通过检查布尔值module.check_mode来确定它是在检查模式下调用的。如果其值为True,则模块必须注意不要进行任何修改。

如果指定了supports_check_mode=False(这是默认值),则模块将在检查模式下以skipped=True和消息remote module (<insert module name here>) does not support check mode退出。

添加文件选项

要声明模块应添加对所有常见文件选项的支持,请向AnsibleModule()调用提供add_file_common_args=True

module = AnsibleModule(argument_spec, add_file_common_args=True)

您可以在此处找到所有文件选项的列表。建议在这种情况下,使您的DOCUMENTATION扩展文档片段ansible.builtin.files(参见文档片段),以确保所有这些字段都被正确记录。

辅助函数module.load_file_common_arguments()module.set_fs_attributes_if_different()可用于为您处理这些参数

argument_spec = {
  'path': {
    'type': 'str',
    'required': True,
  },
}

module = AnsibleModule(argument_spec, add_file_common_args=True)
changed = False

# TODO do something with module.params['path'], like update its contents

# Ensure that module.params['path'] satisfies the file options supplied by the user
file_args = module.load_file_common_arguments(module.params)
changed = module.set_fs_attributes_if_different(file_args, changed)

module.exit_json(changed=changed)