模块格式与文档

在大多数情况下,如果您希望向 Ansible 集合(Collection)贡献模块,则应使用 Python 编写模块并遵循下文所述的标准格式。如果您正在编写 Windows 模块,则应遵循 Windows 开发指南

在提交合并请求(Pull Request)之前,除了遵循这些准则外,还请查阅并遵守以下章节中概述的做法:

每个用 Python 编写的 Ansible 模块都必须以特定顺序的七个标准部分开始,随后才是代码。这些部分依次为:

如果您好奇为什么 imports(导入)没有放在文件顶部,请参阅 Python 导入 一节。

如果您在较旧的 Ansible 模块中看到任何差异,请提交一个合并请求,进行符合这些准则的修改。

非 Python 模块文档

对于非 Python 语言编写的模块,有两种处理文档的方法:

  • 方法一:创建一个包含本文档中所述文档相关部分的 .py 文件。

  • 方法二:创建一个纯 YAML 格式且具有相同数据结构的 .yml 文件。

    • 使用 YAML 文件时,可以通过删除 Python 引号并将 = 替换为 : 来轻松使用下方的示例,例如将 DOCUMENTATION = r''' ... ''' 转换为 DOCUMENTATION: ... 并移除结尾引号。详情请参考 相邻的 YAML 文档文件

Python shebang 与 UTF-8 编码

  1. Ansible 模块开头请使用 #!/usr/bin/python shebang,以便 ansible_python_interpreter 能够正常工作。

  • 如果您使用其他脚本语言开发模块,请相应调整解释器(#!/usr/bin/<interpreter>),以便 ansible_<interpreter>_interpreter 能为该特定语言工作。

  • 二进制模块不需要 shebang 或解释器。

  • 请勿使用 #!/usr/bin/env,因为它会将 env 作为解释器,并绕过 ansible_<interpreter>_interpreter 逻辑。

  • 在 shebang 中向解释器传递参数是无效的;例如 #!/usr/bin/env python

  1. 在 shebang 之后紧跟 # -*- coding: utf-8 -*-,以明确文件采用 UTF-8 编码。

DOCUMENTATION 代码块

在提交模块文档之前,请在 命令行及 HTML 环境中进行测试。

在 shebang、UTF-8 编码、版权行和许可部分之后是 DOCUMENTATION 代码块。Ansible 的在线模块文档是由每个模块源代码中的 DOCUMENTATION 块生成的。

DOCUMENTATION 块必须是有效的 YAML。为了方便起见:

在编写模块文档时,请考虑以下声明:

  • 模块文档应简明准确地定义每个模块及其选项的功能,以及它们如何与底层系统进行交互。

  • 模块文档应面向广大受众,无论是专家还是非专家都能轻松理解。

  • 描述应始终以大写字母开头,并以句号结尾。保持一致性总是有益的。

  • 对于密码和密钥参数,应设置 no_log=True,并且任何示例密码、密钥或哈希值应以 EXAMPLE 开头,以确保不泄露真实密码等信息。

  • 对于那些看起来包含敏感信息但不包含秘密信息(如“password_length”)的参数,请设置 no_log=False 以禁用警告消息。

  • 如果某个选项仅在特定条件下才需要,请描述这些条件;例如,“Required when I(state=present)”。

  • 如果您的模块支持 check_mode,请在文档中反映这一点。

  • 为了创建清晰、简洁、一致且有用的文档,请遵循 风格指南

每个文档字段的描述如下。

文档字段

  • DOCUMENTATION 块中的所有字段均为小写。

  • 除非另有说明,所有字段均为必填。

module:
  • 模块的名称。

  • 必须与文件名相同(不包含 .py 后缀)。

简短描述:
  • 简短描述,显示在 集合索引 页面和 ansible-doc -l 中。

  • short_descriptionansible-doc -l 显示,没有任何分类分组,因此需要提供足够的详细信息来解释模块的用途,而无需依赖其所属目录结构的上下文。

  • description: 不同,short_description 不得有结尾的句号。

  • 您可以在此字段中使用 Ansible 标记

描述:
  • 详细描述(通常为两句或更多句子)。

  • 每个句子必须完整:以大写字母开头,以句号结尾。

  • 不应提及模块名称。

  • 尽量利用多个条目,而不是使用一个长段落。

  • 除非 YAML 要求,否则不得引用完整值。

  • 您可以在此字段中使用 Ansible 标记

添加版本:
  • 这是一个字符串,不是浮点数,应该加上引号以避免错误。

  • 对于 ansible.builtin.* 模块(包含在 ansible-core 中),它是 ansible-core 的版本,例如 version_added: '2.18'

  • 在集合中,当模块被添加时,它必须是集合的版本(而非 Ansible 版本),例如 version_added: '1.0.0'

作者:
  • 模块作者的名称,格式为 First Last (@GitHubID)

  • 如果作者多于一名,请使用多行列表。

  • 除非 YAML 要求,否则不要使用引号。

deprecated:
options:
  • 选项通常被称为“参数”或“arguments”。由于文档字段被称为 options,我们将使用该术语。

  • 如果模块没有选项(例如它是 _facts 模块),则只需一行:options: {}

  • 如果模块有选项(即接受参数),请彻底记录它们。对于每个模块选项,包括:

选项名称:
  • 将其命名为描述性操作(而非 CRUD),重点关注最终状态,例如 online:,而不是 is_online:

  • 确保名称与模块其余部分以及同一类别中的其他模块保持一致。

  • 如有疑问,请查看其他模块以找到用于相同目的的选项名称,我们希望为用户提供一致性。

  • 没有明确的 option-name 字段。此条目是指 options 字典中选项的“键”。

描述:
  • 对该选项功能的详细解释。以完整句子书写,以大写字母开头,以句号结尾。

  • 第一个条目是对选项本身的描述;随后的条目详细说明其用法、依赖项或可能值的格式。

  • 不要列出所有可能的值(这是 choices: 字段的作用,尽管如果值不明显,它应该解释这些值的作用)。

  • 如果某个选项仅在特定条件下需要,请描述这些条件。例如,“Required when O(state=present)”。

  • 互斥的选项必须在每个选项的最后一句中记录。

  • 您可以在此字段中使用 Ansible 标记

required:
  • 仅当为 true 时需要。

  • 如果缺失,我们假定该选项不是必需的。

default:
  • 如果 requiredfalse 或缺失,可以指定 default(如果缺失则默认为 null)。

  • 确保文档中的默认值与代码中的默认值匹配。

  • 默认字段不得作为描述的一部分列出,除非它需要额外的信息或条件。

  • 如果选项是布尔值,您可以使用 Ansible 识别的任何布尔值(如 true/falseyes/no)。为了与 ansible-lint 的一致性和兼容性,文档中请使用 true/false

choices:
  • 选项值的列表。

  • 如果是空的,请不要使用它。

type:
  • 指定选项接受的数据类型,必须与 argument_spec 字典匹配。

  • 如果参数为 type='bool',请将其设置为 type: bool 且不要指定 choices

  • 如果参数为 type='list',请指定 elements

elements:
  • 如果 type='list',则指定列表元素的数据类型。

aliases:
  • 可选名称别名列表。

  • 通常不需要,且不推荐使用,以确保模块用法的一致性。

添加版本:
  • 仅在选项在初始模块发布后添加时才需要;即大于顶层(模块级)的 version_added 字段。

  • 这是一个字符串,不是浮点数,例如对于 ansible-core 中的模块,这可能是 version_added: '2.18'

  • 在集合中,这必须是添加该选项时的集合版本,而不是 Ansible 版本。例如,version_added: '1.0.0'

suboptions:
requirements:
  • 需求列表(如果适用)。

  • 包括最低版本要求。

  • 您可以在此字段中使用 Ansible 标记

seealso:
  • 指向其他模块、文档或互联网资源的引用列表。

  • 由于其更突出,对于常规引用,请使用 seealso,而不是 notes 或在模块 description 中添加链接。

  • 对模块的引用必须使用 FQCN,对于 ansible-core 中的模块使用 ansible.builtin

  • 自 ansible-core 2.15 起支持插件引用。

  • 您可以在 descriptionname 字段中使用 Ansible 标记

  • 引用可以是以下格式之一:

    seealso:
    
    # Reference by module name
    - module: cisco.aci.aci_tenant
    
    # Reference by module name, including description
    - module: cisco.aci.aci_tenant
      description: ACI module to create tenants on a Cisco ACI fabric.
    
    # Reference by plugin name
    - plugin: ansible.builtin.file
      plugin_type: lookup
    
    # Reference by plugin name, including description
    - plugin: ansible.builtin.file
      plugin_type: lookup
      description: You can use the ansible.builtin.file lookup to read files on the control node.
    
    # Reference by rST documentation anchor
    - ref: aci_guide
      description: Detailed information on how to manage your ACI infrastructure using Ansible.
    
    # Reference by rST documentation anchor (with custom title)
    - ref: The official Ansible ACI guide <aci_guide>
      description: Detailed information on how to manage your ACI infrastructure using Ansible.
    
    # Reference by Internet resource
    - name: APIC Management Information Model reference
      description: Complete reference of the APIC object model.
      link: https://developer.cisco.com/docs/apic-mim-ref/
    
  • 如果您使用 ref: 链接到一个未关联标题的锚点,您必须为 ref 添加标题,链接才能正常工作。

attributes:
  • 将属性名称映射到描述该属性的字典的字典。

  • 通常属性由文档片段提供,例如 ansible.builtin.action_common_attributes 及其子片段。模块和插件使用适当的文档片段,并填充 supportdetails 以及潜在的特定属性字段。

描述:
  • 必需。

  • 字符串或字符串列表。每个字符串为一个段落。

  • 对此属性功能的解释。应以完整句子书写。

  • 您可以在此字段中使用 Ansible 标记

details:
  • 通常是可选的,但如果 supportpartial,则必须提供。

  • 字符串或字符串列表。每个字符串为一个段落。

  • 描述支持功能为何可能无法按用户预期工作。

  • 您可以在此字段中使用 Ansible 标记

support:
  • 必需。

  • 必须是 fullnonepartialN/A 之一。

  • 指明该模块或插件是否支持此属性。

membership:
  • 只能为 action_group 属性提供。

  • 列出该模块或动作所属的动作组。

  • 字符串或字符串列表。

platforms:
  • 只能用于 platform 属性。

  • 列出模块或动作支持的平台。

  • 字符串或字符串列表。

添加版本:
  • 仅在属性的支持功能在模块/插件创建后扩展时才需要;即大于顶层(模块级)的 version_added 字段。

  • 这是一个字符串,而不是浮点数,例如 version_added: '2.3'

  • 在集合中,这必须是添加该属性支持时的集合版本,而非 Ansible 版本。例如,version_added: '1.0.0'

notes:
  • 任何不适合上述任何部分的重要信息的详细说明。

  • 不要在 notes 下列出 check_modediff 信息。请改用 attributes 字段。

  • 由于其更突出,请使用 seealso 进行常规引用,而不是使用 notes

  • 您可以在此字段中使用 Ansible 标记

文档片段

如果您正在编写多个相关模块,它们可能会共享通用文档,例如选项、身份验证详细信息、文件模式设置、notes:seealso: 条目。与其在每个模块的 DOCUMENTATION 块中重复该信息,不如将其保存为 doc_fragment 插件,然后在每个模块的文档中包含它。

在 Ansible 中,共享文档片段包含在 lib/ansible/plugins/doc_fragments/ 下的 ModuleDocFragment 类中,或者在集合的 plugins/doc_fragments 目录中。要包含文档片段,请在模块文档中添加 extends_documentation_fragment: FRAGMENT_NAME。对于 FRAGMENT_NAME,请使用完全限定集合名称(例如 kubernetes.core.k8s_auth_options)。

模块仅在能够以与导入该片段的现有模块相同的方式实现文档化接口时,才应使用文档片段中的项。其目标是确保从文档片段导入的项在导入该片段的其他模块中使用时,行为完全相同。

默认情况下,仅文档片段中的 DOCUMENTATION 属性会插入到模块文档中。可以在文档片段中定义其他属性,以便仅导入特定部分。如果文档片段和模块中都定义了某个属性,模块的值将覆盖文档片段的值。

以下是一个名为 example_fragment.py 的文档片段示例。

class ModuleDocFragment(object):
    # Standard documentation
    DOCUMENTATION = r'''
    options:
      # options here
    '''

    # Additional section
    OTHER = r'''
    options:
      # other options here
    '''

要将 OTHER 的内容插入到模块中:

extends_documentation_fragment: example_fragment.other

或者两者都使用:

extends_documentation_fragment:
  - example_fragment
  - example_fragment.other

2.8 版本新增。

自 Ansible 2.8 起,您可以像使用任何其他插件一样,通过在 play 或角色旁边使用 doc_fragments 目录来提供用户定义的 doc_fragments。

例如,所有 AWS 模块都应包含:

extends_documentation_fragment:
- aws
- ec2

在集合中使用文档片段 描述了如何将文档片段整合到集合中。

EXAMPLES 代码块

DOCUMENTATION 块之后紧跟 EXAMPLES 块。在这里,您可以通过多行纯文本 YAML 格式的真实示例向用户展示您的模块如何工作。最好的示例是用户可以直接复制并粘贴到 playbook 中的代码。请在每次修改模块时审查并更新您的示例。

如果模块有集成测试,请将您想要添加的示例包含在集成测试中,以确保其有效。

最佳做法包括:

  • 每个示例应包含一行 name:

EXAMPLES = r'''
- name: Ensure foo is installed
  namespace.collection.modulename:
    name: foo
    state: present
'''
  • name: 行应首字母大写,且不包含结尾的点。

  • 在模块名称中使用完全限定集合名称(FQCN),如上例所示。

    • 对于 ansible-core 中的模块,使用 ansible.builtin. 标识符,例如 ansible.builtin.debug

  • 如果您的示例使用布尔选项,请使用 true/false 值。由于文档将布尔值生成为 true/false,因此在示例中使用这些值会使模块文档更加一致。

  • 如果您的模块返回经常需要的事实(facts),请考虑添加如何使用它们的示例。

RETURN 代码块

EXAMPLES 块之后是 RETURN 块。本节记录了模块返回给其他模块使用的信息。

如果您的模块没有任何返回(ansible-core 的标准返回除外),请将其指定为 RETURN = r''' # '''。否则,对于每个返回的值,请提供以下字段。除非另有说明,所有字段均为必填。

return name:

返回字段的名称。

描述:

该值代表什么的详细描述。首字母大写并以句号结尾。您可以在此字段中使用 Ansible 标记

returned:

返回该值的时间,例如 alwayschangedsuccess。这是一个字符串,可以包含任何人类可读的内容。

type:

数据类型。

elements:

如果 type='list',则指定列表元素的数据类型。

sample:

一个或多个示例。

添加版本:

仅在返回项在初始模块发布后扩展时才需要;即大于顶层(模块级)的 version_added 字段。这是一个字符串,而不是浮点数,例如 version_added: '2.3'

contains:

可选。为了描述嵌套的返回值,请设置 type: dict,或 type: list/elements: dict,或者如果确实需要,使用 type: complex,并为每个子字段重复上述元素。

这里有两个 RETURN 部分示例,一个包含三个简单字段,另一个包含一个复杂的嵌套字段。

RETURN = r'''
dest:
    description: Destination file/path.
    returned: success
    type: str
    sample: /path/to/file.txt
src:
    description: Source file used for the copy on the target machine.
    returned: changed
    type: str
    sample: /home/httpd/.ansible/tmp/ansible-tmp-1423796390.97-147729857856000/source
md5sum:
    description: MD5 checksum of the file after running copy.
    returned: when supported
    type: str
    sample: 2a5aeecc61dc98c4d780b14b330e3282
'''

RETURN = r'''
packages:
    description: Information about package requirements.
    returned: success
    type: dict
    contains:
        missing:
            description: Packages that are missing from the system.
            returned: success
            type: list
            elements: str
            sample:
                - libmysqlclient-dev
                - libxml2-dev
        badversion:
            description: Packages that are installed but at bad versions.
            returned: success
            type: list
            elements: dict
            sample:
                - package: libxml2-dev
                  version: 2.9.4+dfsg1-2
                  constraint: ">= 3.0"
'''

Python 导入

RETURN 块之后立即添加 Python 导入。所有模块必须使用以下形式的 Python 导入:

from module_utils.basic import AnsibleModule

不再允许使用类似 from module_utils.basic import * 的“通配符”导入。

注意

为什么导入不是放在第一位?

由于 DOCUMENTATIONEXAMPLESRETURN 块本质上是文件的额外文档字符串,模块代码本身并不直接使用它们,因此 import 语句被放置在这些特殊变量之后。将导入放置在更靠近功能代码的位置有助于整合相关元素,从而提高可读性、调试效率和整体理解力。

测试模块文档