Ansible 模块架构

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

模块类型

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

操作插件

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

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

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

新式模块

所有随 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 的内容,然后将其作为本机变量在整个代码中使用。

非原生 JSON 模块

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

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

另请参阅

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

二进制模块

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

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

另请参阅

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

旧式模块

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

模块的执行方式

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

执行器/task_executor

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

The normal action plugin

The normal action plugin 在远程主机上执行模块。 它是实际在受管机器上执行模块的大部分工作的首要协调者。

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

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

  • 它会与其他插件(连接、shell、become、其他动作插件)协作,在远程机器上创建任何临时文件并在完成后进行清理。

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

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

许多此功能来自 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 框架

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 理解标准输入中的脚本,但它不理解 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 中,模块的其他代码可以从这里访问。

警告

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

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

注意

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

内部参数

无论是 模块替换器框架 还是 Ansiballz 框架,都会向 Ansible 模块发送额外的参数,除了用户在剧本中指定的参数之外。这些额外的参数是内部参数,它们有助于实现全局 Ansible 功能。模块通常不需要明确地知道这些参数,因为这些功能是在 ansible.module_utils.basic 中实现的。但是,某些功能需要模块的支持,并且了解一些内部参数是有用的。

本节中的内部参数是全局的。如果你需要向自定义模块添加本地内部参数,为该特定模块创建一个操作插件。请参阅 复制操作插件 中的 _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。你还可以使用剧本中的 diff 关键字,或相关的环境变量访问此参数。有关详细信息,请参阅 剧本关键字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_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 将返回数据中的所有字符串标记为 Unsafe,按原样发出字符串中的任何 Jinja2 模板,而不是由 Jinja2 展开。

通过 ActionPlugin._execute_module() 调用模块返回的字符串会由普通操作插件自动标记为 Unsafe。如果另一个操作插件通过其他方式从模块检索信息,它必须自行将其返回数据标记为 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 允许您定义为参数接受的值的类型。的默认值为 typestr。可能的值是

  • str

  • list

  • dict

  • bool

  • int

  • float

  • path

  • raw

  • jsonarg

  • json

  • bytes

  • bits

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

元素:

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

默认:

``default`` 选项允许为参数设置一个默认值,用于在未向模块提供参数的情况下。如果未指定,则默认值为 ``None``。

回退:

``fallback`` 接受一个 ``tuple``,其中第一个参数是一个可调用对象(函数),该函数将用于根据第二个参数执行查找。第二个参数是一个可被调用对象接受的值列表。

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

示例

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

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

必需:

``required`` 接受一个布尔值,可以是 ``True`` 或 ``False``,表示参数是否必需。如果未指定,``required`` 默认值为 ``False``。不应将其与 ``default`` 结合使用。

不记录:

``no_log`` 接受一个布尔值,可以是 ``True`` 或 ``False``,显式地指示参数值是否应该在日志和输出中被屏蔽。

注意

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

别名:

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

选项:

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

应用默认值:

``apply_defaults`` 与 ``options`` 协同工作,并允许即使未提供顶级参数,也可以应用子选项的 ``default``。

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

在版本中移除:

``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`` 指示在该日期之后,ansible-core 的次要版本或集合的主要版本将不再包含弃用的参数。与 ``removed_in_version`` 互斥,必须与 ``removed_from_collection`` 一起使用。

示例

option = {
  'type': 'str',
  'removed_at_date': '2020-12-31',
  'removed_from_collection': 'testns.testcol',
},
从集合中移除:

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

弃用别名:

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

名称:

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

版本:

ansible-core 或集合的版本,将在此版本中删除此别名。必须指定 ``version`` 或 ``date``。

日期:

ansible-core 的次要版本或集合的主要版本在该日期之后将不再包含此别名。必须指定 ``version`` 或 ``date``。

集合名称:

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

示例

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',
    },
  ],
},
互斥:

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

共同必需:

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

其中之一必需:

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

如果必需:

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

被...必需:

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

上下文:

版本 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'),
  ],
)
互斥:

必须是字符串序列(列表或元组)的序列。每个字符串序列都是一个互斥选项名称列表。如果同时指定了一个列表中的多个选项,Ansible 将使模块失败并显示错误。

示例

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

在此示例中,选项 ``path`` 和 ``content`` 不能同时指定。同样,选项 ``repository_url`` 和 ``repository_filename`` 也不能同时指定。但指定 ``path`` 和 ``repository_url`` 是可以接受的。

为了确保准确地指定两个(或多个)选项中的一个,将 ``mutually_exclusive`` 与 ``required_one_of`` 结合使用。

共同必需:

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

示例

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

在这个例子中,如果指定了选项 file_pathfile_hash 中的一个,如果另一个没有指定,Ansible 将使用错误终止模块。

其中之一必需:

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

示例

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

在这个例子中,必须至少指定 pathcontent 中的一个。如果都没有指定,执行将失败。显式允许同时指定两者;为了防止这种情况,可以将 required_one_ofmutually_exclusive 结合使用。

如果必需:

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

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

('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={
  '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)