Ansible 模块架构

如果您正在处理 ansible-core 代码,编写 Ansible 模块或开发操作插件,您可能需要了解 Ansible 的程序流程是如何执行的。如果您只是在 Playbook 中使用 Ansible 模块,则可以跳过此部分。

模块类型

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

操作插件

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

一些操作插件在控制节点上完成所有工作。例如,debug 操作插件(打印文本供用户查看)和 assert 操作插件(测试 Playbook 中的值是否满足某些条件)完全在控制节点上执行。

大多数操作插件在控制节点上设置一些值,然后在受管节点上调用一个实际的模块来使用这些值。例如,template 操作插件从用户获取值,以使用来自 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 行(如果存在)。

另请参阅

Ansible for Rubyists 存储库中可以找到用 Ruby 编写的非原生模块的示例。

二进制模块

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

二进制模块以与需要 JSON 的模块相同的方式接收参数并将数据返回给 Ansible。

另请参阅

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

旧式模块

旧式模块类似于需要 JSON 的模块,不同之处在于它们接收的文件包含其参数的 key=value 对,而不是 JSON。当模块没有任何表明它是其他类型之一的标记时,Ansible 会将其判断为旧式模块。

模块的执行方式

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

执行器/任务执行器

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 编码的模块参数的 Python repr。 对 JSON 字符串使用 repr 可以安全地将其嵌入到 Python 文件中。 在 Ansiballz 框架下的新式 Python 模块中,最好通过实例化 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 中指定的 syslog_facility,或者适用于此主机的任何 ansible_syslog_facility 清单变量所替换。在新式 Python 模块中,此项已略有更改。如果确实需要访问它,则应实例化一个 AnsibleModule,然后使用 AnsibleModule._syslog_facility 进行访问。它不再是实际的 syslog 功能,现在是 syslog 功能的名称。有关详细信息,请参阅内部参数文档

Ansiballz 框架

Ansible 2.1 中采用了 Ansiballz 框架,并将其用于所有新式 Python 模块。与模块替换器不同,Ansiballz 使用 ansible/module_utils 中内容的真实 Python 导入,而不仅仅是预处理模块。它通过构建一个 zip 文件来实现此目的,该 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 -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 元素:字符串

此参数为模块提供应具有特殊 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 facility。大多数模块应该只使用 AnsibleModule.log() 函数,该函数将使用此参数。如果模块必须单独使用此参数,它应该实例化 AnsibleModule 方法,然后从 AnsibleModule._syslog_facility 中检索 syslog facility 的名称。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_string_conversion_action

此参数提供有关在将用户指定的模块参数的值转换为字符串后模块应执行的操作的说明。有关更多详细信息,请参阅 STRING_CONVERSION_ACTION 配置选项。

_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 字符串,并将该字符串打印到其 stdout。正常的操作插件接收 JSON 字符串,将其解析为 Python 字典,然后将其返回给执行器。

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

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

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

特殊注意事项

管道传输

Ansible 可以通过两种方式将模块传输到远程机器:

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

  • 或者,它可以使用所谓的管道传输,通过将模块管道传输到远程解释器的 stdin 中来执行该模块。

目前,管道传输仅适用于用 Python 编写的模块,因为 Ansible 只知道 Python 支持这种操作模式。支持管道传输意味着模块有效负载在通过网络发送之前所采用的任何格式都必须可以通过 Python 通过 stdin 执行。

为什么通过 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:

type='list' 时,elementstype 结合使用。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 示例中,它将允许定义 module.params['top_level']['second_level'],即使用户在调用模块时未提供 top_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:

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

required_together

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

required_one_of

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

required_if

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

required_by

如果指定了 optionsrequired_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 (<在此处插入模块名称>) 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)