模块格式和文档

如果您想将您的模块贡献给大多数 Ansible 集合,您必须使用 Python 编写模块并遵循下面描述的标准格式。(除非您正在编写 Windows 模块,在这种情况下,请应用 Windows 指南。)除了遵循此格式外,您还应该查看我们的提交清单编程技巧维护 Python 2 和 Python 3 兼容性的策略,以及有关 测试 的信息,然后再打开 Pull Request。

每个用 Python 编写的 Ansible 模块都必须以特定顺序开始包含七个标准部分,然后是代码。这些部分按顺序排列为:

注意

为什么导入不先出现?

敏锐的 Python 程序员可能会注意到,与 PEP 8 的建议相反,我们没有将 imports 放在文件的顶部。这是因为 DOCUMENTATIONRETURN 部分不是模块代码本身使用的;它们本质上是文件的额外文档字符串。导入放在这些特殊变量之后的原因与 PEP 8 将导入放在介绍性注释和文档字符串之后的原因相同。这使代码的活动部分保持在一起,并将纯粹的信息部分分开。排除 E402 的决定是基于可读性的(这正是 PEP 8 的目的)。模块中的文档字符串与模块级别的文档字符串更相似,而不是代码,并且永远不会被模块本身使用。将导入放在此文档下方并更靠近代码,以一致的方式合并和分组所有相关的代码,以提高可读性、调试和理解。

警告

小心复制旧模块!

一些较旧的 Ansible 模块在文件底部有 imports、带有完整 GPL 前缀的 Copyright 通知和/或 DOCUMENTATION 字段的顺序错误。这些是需要更新的遗留文件 - 不要将它们复制到新模块中。随着时间的推移,我们正在更新和更正较旧的模块。请遵循此页面上的指南!

注意

对于非 Python 模块,您仍然需要创建一个 .py 文件以用于文档目的。从 ansible-core 2.14 开始,您可以选择创建一个 .yml 文件,该文件具有相同的数据结构,但使用纯 YAML。使用 YAML 文件,可以通过删除 Python 引号并将 = 替换为 : 来轻松使用下面的示例,例如 DOCUMENTATION = r''' ... '''DOCUMENTATION: ... 并删除结束引号。 相邻的 YAML 文档文件

Python Shebang & UTF-8 编码

使用 #!/usr/bin/python 开始您的 Ansible 模块 - 此 “shebang” 允许 ansible_python_interpreter 工作。在 shebang 之后立即使用 # -*- coding: utf-8 -*- 来明确该文件是 UTF-8 编码的。

警告

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

  • 在 shebang 中向解释器传递参数不起作用(例如,#!/usr/bin/env python)。

注意

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

注意

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

ANSIBLE_METADATA 块

由于我们已迁移到集合,因此我们已弃用 METADATA 功能,模块不再需要它,但如果存在它也不会破坏任何东西。

DOCUMENTATION 块

在 shebang、UTF-8 编码、版权行和许可部分之后是 DOCUMENTATION 块。Ansible 的在线模块文档是从每个模块源代码中的 DOCUMENTATION 块生成的。DOCUMENTATION 块必须是有效的 YAML。您可能会发现,在将 DOCUMENTATION 字符串包含在 Python 文件中之前,先在具有 YAML 语法突出显示的编辑器 中开始编写它会更容易。您可以先将我们的 示例文档字符串 复制到您的模块文件中并进行修改。如果您在 YAML 中遇到语法问题,您可以在 YAML Lint 网站上对其进行验证。

模块文档应简要准确地定义每个模块和选项的作用,以及它如何在底层系统中与其他模块协同工作。文档应该为广泛的受众编写——既能被专家阅读,也能被非专家阅读。
  • 描述应始终以大写字母开头并以句点结尾。一致性总是有帮助的。

  • 验证文档和模块规范字典中的参数是否相同。

  • 对于密码/密钥参数,应设置 no_log=True

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

  • 如果某个选项仅在某些情况下是必需的,请描述这些条件。例如,“当 I(state=present) 时为必需。”

  • 如果您的模块允许 check_mode,请在文档中反映此事实。

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

下面描述了每个文档字段。在提交模块文档之前,请在命令行和 HTML 中对其进行测试。

  • 只要您的模块文件是本地可用的,您就可以使用 ansible-doc -t module my_module_name 在命令行查看您的模块文档。任何解析错误都将很明显 - 您可以通过向命令添加 -vvv 来查看详细信息。

  • 您还应该测试模块文档的 HTML 输出

文档字段

DOCUMENTATION 块中的所有字段均为小写。除非另有说明,否则所有字段均为必填项

module:
  • 模块的名称。

  • 必须与文件名相同,不带 .py 扩展名。

short_description:
  • 集合索引页面和 ansible-doc -l 上显示的简短描述。

  • short_descriptionansible-doc -l 显示,没有任何类别分组,因此它需要足够的细节来解释模块的用途,而无需考虑它所在的目录结构的上下文。

  • description: 不同,short_description 不应带有尾随句点/句号。

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

  • 必须以完整的句子书写,换句话说,带有大写字母和句点/句号。

  • 不应提及模块名称。

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

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

version_added:
  • 添加模块的 Ansible 版本。

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

  • 在集合中,这必须是模块添加到其中的集合版本,而不是 Ansible 版本。例如,version_added: 1.0.0

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

  • 如果有多个作者,请使用多行列表。

  • 不要使用引号,因为 YAML 不应该需要它。

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

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

  • 如果您的模块有选项(换句话说,接受参数),则应彻底记录每个选项。对于每个模块选项,请包含

option-name:
  • 声明式操作(不是 CRUD),侧重于最终状态,例如 online:,而不是 is_online:

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

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

  • (没有明确的字段 option-name。此条目是关于 options 字典中选项的。)

description:
  • 详细说明此选项的作用。它应该以完整的句子书写。

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

  • 不应列出可能的值(这是 choices: 的用途,尽管它应该解释如果这些值不明显则它们的作用)。

  • 如果某个选项仅在某些情况下是必需的,请描述这些条件。例如,“当 I(state=present) 时为必需。”

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

required:
  • 仅当 true 时才需要。

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

default:
  • 如果 required 为 false/缺失,则可以指定 default(如果缺失则假定为“null”)。

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

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

  • 如果该选项是布尔值,则可以使用 Ansible 识别的任何布尔值(例如 true/falseyes/no)。为了与 ansible-lint 保持一致性和兼容性,请将布尔值记录为 true/false

choices:
  • 选项值的列表。

  • 如果为空,则应省略。

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

  • 如果参数是 type='bool',则此字段应设置为 type: bool,并且不应指定 choices

  • 如果参数是 type='list',则应指定 elements

elements:
  • 指定当 type='list' 时列表元素的类型。

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

  • 通常不需要。

version_added:
  • 仅当此选项在初始 Ansible 版本之后扩展时才需要,换句话说,这大于顶级 version_added 字段。

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

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

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

  • 包括最低版本。

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

  • 在 Ansible 2.10 及更高版本中,对模块的引用必须使用 FQCN 或 ansible.builtin 来引用 ansible-core 中的模块。

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

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

    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 和其他可能特定于属性的字段。

description:
  • 一个字符串或字符串列表。每个字符串为一段。该描述是必需的。

  • 对该属性作用的解释。应使用完整的句子编写。

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

  • 描述支持可能无法按用户预期工作的情况。

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

support:
  • 以下值之一:fullnonepartialN/A。这是必需的。

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

membership:
  • 一个字符串或字符串列表。

  • 只能用于属性 action_group,并且对于该属性必须始终指定。

  • 列出此模块或操作所属的操作组。

platforms:
  • 一个字符串或字符串列表。

  • 只能用于属性 platform,并且对于该属性必须始终指定。

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

version_added:
  • 仅当此属性的支持是在创建模块/插件之后扩展时才需要,换句话说,此值大于顶层 version_added 字段。

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

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

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

  • 关于 check_modediff 的信息应 在此处列出,而是应在 attributes 中提及。

在模块文档中进行链接

您可以使用一些预定义的宏,从您的模块文档链接到其他模块文档、docs.ansible.com 上的其他资源以及 Internet 上的其他资源。这些宏的正确格式为

  • L() 用于带有标题的链接。例如:请参阅 L(Ansible 自动化平台,https://ansible.org.cn/products/automation-platform)。 自 Ansible 2.10 起,请勿使用 L() 来链接 Ansible 文档和集合文档之间的相对链接。

  • U() 用于 URL。例如:请参阅 U(https://ansible.org.cn/products/automation-platform) 以获取概述。

  • R() 用于带有标题的交叉引用(在 Ansible 2.10 中添加)。例如:请参阅 R(Cisco IOS 平台指南,ios_platform_options)。使用 RST 锚点进行交叉引用。有关详细信息,请参阅添加锚点

  • M() 用于模块名称。例如:另请参阅 M(ansible.builtin.yum) M(community.general.apt_rpm)必须 使用 FQCN,短名称将创建断开的链接;对于 ansible-core 中的模块,请使用 ansible.builtin

  • P() 用于插件名称。例如:另请参阅 P(ansible.builtin.file#lookup) P(community.general.json_query#filter)。这也可以引用角色:P(community.sops.install#role)。自 ansible-core 2.15 起支持此功能。必须使用 FQCN;对于 ansible-core 中的插件,请使用 ansible.builtin

注意

对于集合内模块和文档之间的链接,您可以使用上述任何选项。对于集合外部的链接,如果可用,请使用 R()。否则,请使用带有完整 URL(非相对链接)的 U()L()。对于模块,请使用带有 FQCN 或 ansible.builtinM(),如示例所示。如果您正在创建自己的文档站点,则需要使用intersphinx 扩展R()M() 转换为正确的链接。

注意

要引用集合中的一组模块,请使用 R()。当集合不是正确的粒度时,请使用 C(..)

  • 有关管理 kubernetes 集群的信息, 请参阅 R(kubernetes.core 集合, plugins_in_kubernetes.core)

  • C(win_*) 模块(分布在多个集合中) 允许您管理 windows 主机的各个方面。

注意

由于它更突出,请使用 seealso 进行一般引用,而不是使用 notes 或向描述添加链接。

模块文档中的语义标记

您可以使用语义标记来突出显示选项名称、选项值和环境变量。标记处理器以统一的方式格式化这些突出显示的术语。通过语义标记,我们可以修改输出的外观,而无需更改底层代码。

语义标记的正确格式如下

  • O() 用于选项名称,无论是单独提及还是与值一起提及。例如:如果 O(state=present),则为必填项。 O(force) 一起使用以要求安全访问。

  • V() 用于单独提及的选项值。例如:可能的值包括 V(monospace) V(pretty)。

  • RV() 用于返回值名称,无论是单独提及还是与值一起提及。例如:如果发生更改,则模块返回 RV(changed=true)。使用 RV(stdout) 返回值进行标准输出。

  • E() 用于环境变量。例如:如果未设置,则将使用环境变量 E(ACME_PASSWORD)。

这些格式化函数的参数可以使用反斜杠进行转义:V(foo(bar="a\\b"\), baz) 结果为格式化值 foo(bar="a\b"), baz)

使用 O()RV() 的规则非常严格。您必须遵循语法规则,以便文档渲染器可以为选项和返回值创建超链接。

允许的语法如下

  • 要引用当前插件/模块的选项,或当前角色(在角色入口点文档内)的入口点,请使用 O(option)O(option=name)

  • 要从角色文档内部引用另一个入口点 entrypoint 的选项,请使用 O(entrypoint:option)O(entrypoint:option=name)。文档渲染器可以忽略入口点信息,将其转换为指向该入口点的链接,甚至直接指向该入口点的选项。

  • 要引用类型为 type另一个插件/模块 plugin.fqcn.name 的选项,请使用 O(plugin.fqcn.name#type:option)O(plugin.fqcn.name#type:option=name)。对于模块,请使用 type=module。文档渲染器可以忽略 FQCN 和插件类型,将其转换为指向该插件的链接,甚至直接指向该插件的选项。

  • 要引用另一个角色 role.fqcn.name 的入口点 entrypoint 的选项,请使用 O(role.fqcn.name#role:entrypoint:option)O(role.fqcn.name#role:entrypoint:option=name)。文档渲染器可以忽略 FQCN 和入口点信息,将其转换为指向该入口点的链接,甚至直接指向该入口点的选项。

  • 要引用不存在的选项(例如,在早期版本中删除的选项),请使用 O(ignore:option)O(ignore:option=name)ignore: 部分不会由文档渲染向用户显示。

选项名称可以通过列出以点分隔的选项路径来引用子选项。例如,如果您有一个名为 foo 的选项,它有一个名为 bar 的子选项,那么您必须使用 O(foo.bar) 来引用该子选项。您可以添加数组指示符,如 O(foo[].bar) 甚至 O(foo[-1].bar) 来指示特定的列表元素。为了确定选项的真实名称,会忽略 [] 对之间的所有内容。例如,O(foo[foo | length - 1].bar[]) 会产生与 O(foo.bar) 相同的链接,但会显示文本 foo[foo | length - 1].bar[],而不是 foo.bar

同样的语法也可以用于 RV(),只不过这些将引用返回值名称而不是选项名称;例如 RV(ansible.builtin.service_facts#module:ansible_facts.services) 引用的是 ansible_facts.services 这个由 ansible.builtin.service_facts 模块 返回的事实。

模块文档中的格式宏

虽然可以使用标准的 Ansible 格式宏来控制模块文档中其他术语的外观,但您应该谨慎使用。

可能的宏包括以下这些

  • C() 用于 monospace(代码)文本。例如:此模块的功能类似于 Unix 命令 C(foo)。

  • B() 用于粗体文本。

  • I() 用于斜体文本。

  • HORIZONTALLINE 用于水平线(<hr> html 标签),以分隔长描述。

请注意,C()B()I() 不允许转义,因此不能包含值 ),因为它总是结束格式化序列。如果需要在 C() 内使用 ),我们建议使用 V() 代替;请参阅上面关于语义标记的部分。

文档片段

如果您正在编写多个相关的模块,它们可能共享通用的文档,例如身份验证详细信息、文件模式设置、notes:seealso: 条目。您可以将这些信息保存为 doc_fragment 插件,并在每个模块的文档中使用,而不是在每个模块的 DOCUMENTATION 块中重复该信息。在 Ansible 中,共享文档片段包含在 lib/ansible/plugins/doc_fragments/ 或集合中的等效目录中的 ModuleDocFragment 类中。要包含文档片段,请在模块文档中添加 extends_documentation_fragment: FRAGMENT_NAME。对 FRAGMENT_NAME 使用完全限定的集合名称(例如,kubernetes.core.k8s_auth_options)。

如果模块将以与导入该片段的现有模块相同的行为方式实现那里记录的所有接口,则模块应该只使用 doc 片段中的项。目标是,从 doc 片段导入的项在另一个导入该 doc 片段的模块中使用时,其行为将完全相同。

默认情况下,只有 doc 片段的 DOCUMENTATION 属性才会插入到模块文档中。可以在 doc 片段中定义其他属性,以便仅导入 doc 片段的某些部分或根据需要混合和匹配。如果某个属性在 doc 片段和模块中都定义了,则模块值会覆盖 doc 片段的值。

以下是一个名为 example_fragment.py 的示例 doc 片段

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 开始,您可以通过在剧本或角色的相邻位置使用 doc_fragments 目录来拥有用户提供的 doc_fragments,就像任何其他插件一样。

例如,所有 AWS 模块都应包括

extends_documentation_fragment:
- aws
- ec2

在集合中使用文档片段 介绍了如何在集合中合并文档片段。

EXAMPLES 块

在 shebang、UTF-8 编码、版权行、许可部分和 DOCUMENTATION 块之后是 EXAMPLES 块。在此处,您以多行纯文本 YAML 格式向用户展示模块如何在实际示例中工作。最好的示例是用户可以复制并粘贴到 playbook 中的示例。每次更改模块时,都应查看并更新示例。

按照 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

如果示例使用布尔选项,请使用 yes/no 值。由于文档将布尔值生成为 yes/no,因此示例也使用这些值,这使得模块文档更加一致。

如果您的模块返回经常需要的事实,那么提供如何使用它们的示例可能会有所帮助。

RETURN 块

在 shebang、UTF-8 编码、版权行、许可部分、DOCUMENTATIONEXAMPLES 块之后是 RETURN 块。此部分记录模块返回的信息,供其他模块使用。

如果您的模块不返回任何内容(除了标准返回值),则模块的此部分应读取:RETURN = r''' # '''。否则,对于每个返回的值,请提供以下字段。除非另有说明,否则所有字段都是必需的。

return name:

返回字段的名称。

description:

详细描述此值表示的内容。首字母大写且带有尾随点。

returned:

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

type:

数据类型。

elements:

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

sample:

一个或多个示例。

version_added:

仅当此返回值是在 Ansible 初始版本之后扩展时才需要,换句话说,这大于顶级 version_added 字段。这是一个字符串,而不是浮点数,例如,version_added: '2.3'

contains:

可选。要描述嵌套返回值,请设置 type: dicttype: 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 导入

在 shebang、UTF-8 编码、版权声明、许可证以及 DOCUMENTATIONEXAMPLESRETURN 部分之后,您最终可以添加 Python 导入语句。所有模块都必须使用以下形式的 Python 导入:

from module_utils.basic import AnsibleModule

不再允许使用“通配符”导入,例如 from module_utils.basic import *

测试模块文档

要在本地测试 Ansible 文档,请按照说明操作。要在集合中测试文档,请参阅 使用 antsibull-docs 构建文档站点