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

非原生需要 JSON 模块

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

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

另请参阅

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

二进制模块

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

二进制模块以与 需要 JSON 模块 相同的方式将其参数和返回数据传递给 Ansible。

另请参阅

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

旧式模块

旧式模块类似于 需要 JSON 模块 ,除了它们获取的文件包含其参数的 key=value 对,而不是 JSON 。当模块没有任何表明它是其他类型的标记时,Ansible 会将其视为旧式模块。

模块如何执行

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

执行程序/任务执行程序

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

普通动作插件

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

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

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

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

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

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

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

注意

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

Executor/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 文件中。在新式 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 在出现的地方都被替换为 syslog_facility,该字符串在 ansible.cfg 或任何适用于此主机的 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 理解 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 中,模块的其他代码可以从中访问这些参数。

警告

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

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

注意

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

内部参数

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

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

_ansible_no_log

类型:bool

只要任务或 playbook 中的参数指定了 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_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 字符串,并将字符串打印到其标准输出。正常的操作插件接收 JSON 字符串,将其解析为 Python 字典,并将其返回给执行器。

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

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

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

特殊注意事项

流水线

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

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

  • 或者它可以使用称为流水线的方法,通过将其管道传输到远程解释器的标准输入来执行模块。

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

为什么要通过标准输入传递参数?

选择通过标准输入传递参数的原因如下

  • 当与 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 示例中,它将允许定义 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:

如果指定了 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)