Skip to content

Files

Latest commit

f87fb9e · Dec 29, 2021

History

History
1067 lines (771 loc) · 62.1 KB

File metadata and controls

1067 lines (771 loc) · 62.1 KB

五、消费和创建模块

在这本书里,我们几乎经常提到并使用 Ansible 模块。我们已经将这些视为“黑盒”——也就是说,我们刚刚接受了它们的存在,并且它们将以某种记录的方式工作。然而,Ansible 的一大优点是它是一个开源产品,因此,您不仅可以查看和修改它的源代码,还可以编写自己的附加内容。迄今为止,Ansible 几乎有成千上万个模块,处理从复制文件和安装软件包等简单命令,到配置高度复杂和定制的网络设备的一切。这一大堆模块是出于解决 Ansible 问题的真正需要,Ansible 每个版本包含的模块数量每次都在增加。

迟早,你会遇到一个特定的功能,它不存在于任何当前的 Ansible 模块中。当然,您可以尝试在功能上填补这个空白,要么编写自己的模块,要么将现有模块的增强功能贡献回 Ansible 项目,让其他人都从中受益。在本章中,您将学习创建自己的模块的基础知识,以及如果您愿意,如何将代码贡献回上游的 Ansible 项目。

具体而言,在本章中,您将涵盖以下主题:

  • 使用命令行执行多个模块
  • 查看模块索引
  • 从命令行访问模块文档
  • 模块返回值
  • 开发定制模块

我们开始吧!

技术要求

本章假设您已经使用 Ansible 设置了您的控制主机,详见第 1 章ansi ble 入门,并且正在使用最新的可用版本-本章中的示例已使用 Ansible 2.9 进行了测试。本章还假设您至少还有一台要测试的主机。理想情况下,这应该是基于 Linux 的。虽然我们将在本章中给出主机名的具体示例,但是您可以用自己的主机名和/或 IP 地址来替换它们。如何做到这一点的细节将在适当的地方提供。

本章将介绍的模块开发工作假设您的计算机上存在 Python 2 或 Python 3 开发环境,并且您正在运行 Linux、FreeBSD 或 macOS。如果需要额外的 Python 模块,它们的安装将被记录下来。构建模块文档的任务对 Python 3.5 或更高版本有一些非常具体的要求,因此如果您希望尝试这样做,您需要安装一个合适的 Python 环境。

本章的代码包可在此处获得:https://github . com/packt publishing/Ansible-2-cook book/tree/master/Chapter % 205

使用命令行执行多个模块

因为这一章是关于模块和如何创建它们的,让我们回顾一下如何使用模块。我们在整本书中都这样做了,但是我们没有注意到与它们如何工作相关的一些细节。我们还没有讨论的一个关键问题是 Ansible 引擎如何与其模块对话,反之亦然,所以让我们现在来探讨一下这个问题。

和以往一样,在使用 Ansible 命令时,我们需要一个清单来运行我们的命令。在本章中,由于我们的重点是模块本身,我们将使用一个非常简单的小清单,如下所示:

[frontends]
frt01.example.com

[appservers]
app01.example.com

现在,对于我们总结的第一部分,您可以通过临时命令非常容易地运行一个模块,并使用-m开关告诉 Ansible 您想要运行哪个模块。因此,您可以运行的最简单的命令之一是 Ansible ping命令,如下所示:

$ ansible -i hosts appservers -m ping 

现在,有一件事我们以前没有关注过,那就是 Ansible 和它的模块之间的通信;但是,让我们检查一下前面命令的输出:

$ ansible -i hosts appservers -m ping
app01.example.com | SUCCESS => {
 "ansible_facts": {
 "discovered_interpreter_python": "/usr/bin/python"
 },
 "changed": false,
 "ping": "pong"
}

你注意到输出的结构了吗——大括号、冒号和逗号?是的,Ansible 使用 JSON 格式的数据与它的模块对话,模块也在 JSON 中向 Ansible 报告它们的数据。事实上,前面的输出是ping模块返回给 Ansible 引擎的 JSON 格式数据的子集。

当然,我们从来不需要担心这个,因为我们使用命令行上的key=value对或者剧本和角色中的 YAML 来处理模块。因此,JSON 对我们是屏蔽的,但是当我们在本章后面进入模块开发的世界时,这是一个需要记住的重要事实。

Ansible 模块就像高级编程语言中的函数一样,它们将一个定义良好的参数列表作为输入,执行它们的功能,然后提供一组输出数据,这些数据也是定义良好且有文档记录的。我们将在本章后面更详细地讨论这一点。当然,前面的命令不包含任何参数,所以这是通过 Ansible 调用模块的最简单的可能方式。

现在,让我们运行另一个命令,该命令接受一个参数并将该数据传递给模块:

$ ansible -i hosts appservers -m command -a "/bin/echo 'hello modules'"

在这种情况下,我们向命令模块提供了一个字符串作为参数,Ansible 反过来又将其转换为 JSON,并在调用时传递给命令模块。运行此临时命令时,您将看到类似以下内容的输出:

$  ansible -i hosts appservers -m command -a "/bin/echo 'hello modules'"
app01.example.com | CHANGED | rc=0 >>
hello modules

在这种情况下,输出数据似乎不是 JSON 格式的;然而,当您运行一个模块时,Ansible 打印到终端的只是每个模块返回的数据的一个子集——例如,我们命令中的CHANGED状态和rc=0退出代码都以 JSON 格式的数据结构传递回 Ansible——这只是对我们隐藏的。

这一点不需要太费力,但设置一个背景很重要。正是在这种背景下,我们将在本章中展开讨论,因此请记住以下要点:

  • Ansible 及其模块之间的通信是通过 JSON 格式的数据结构完成的。
  • 模块接受控制它们如何工作的输入数据(参数)。
  • 模块总是返回数据——至少是模块执行的状态(例如,changedokfailed)。

当然,在您开始编写自己的模块之前,检查一个能够执行您需要的所有(或部分)功能的模块是否已经存在是有意义的。我们将在下一节探讨这一点。

查看模块索引

如前一节所述,Ansible 提供了数千个模块,使开发行动手册并在多台主机上运行变得快速简单。当有这么多模块的时候,你如何开始寻找合适的模块呢?幸运的是,Ansible 文档提供了一个组织良好的模块分类列表,您可以参考该列表来找到您想要的模块——这可以在这里找到:https://docs . Ansible . com/Ansible/latest/modules/modules _ by _ category . html

让我们假设您想看看是否有一个本地的 Ansible 模块可以帮助您配置和管理您的亚马逊网络服务 S3 桶。这是一个相当精确、定义明确的需求,所以让我们以一种合乎逻辑的方式来处理这个问题:

  1. 如前所述,首先在网络浏览器中打开分类模块索引:
https://docs.ansible.com/ansible/latest/modules/modules_by_category.html
  1. 现在,我们知道亚马逊网络服务几乎肯定会出现在Cloud模块类别中,所以让我们在浏览器中打开它。
  2. 这个页面上列出的模块即使不是成千上万,也有几百个!所以,让我们使用浏览器中的查找功能( Ctrl + F )来查看s3关键词是否出现在任何地方:

我们很幸运——确实如此,页面下方还有几个列表:

我们现在有了一个可供使用的模块清单——当然,还有几个,所以我们仍然需要确定我们的行动手册需要哪一个(或哪些)。从前面的简短描述中可以看出,这将取决于您的预期任务。

  1. 简短的描述应该足以为您提供一些关于该模块是否适合您的需求的线索。一旦你有了想法,你可以点击相应的文档链接来查看关于该模块以及如何使用它的更多细节:

如您所见,每个模块的文档页面都提供了大量信息,包括更长的描述。如果您向下滚动页面,您将看到您可以为模块提供的可能参数的列表,一些如何使用它们的实际示例,以及一些关于模块输出的详细信息。另外,请注意上一张截图中的需求部分——有些模块,尤其是与云相关的模块,在工作之前需要额外的 Python 模块,如果您试图从剧本中运行aws_s3模块,而没有在 Python 2.6 或更高版本上安装botoboto3botocore模块,您只会收到一个错误。

所有模块在被接受为 Ansible 项目的一部分之前,都必须创建这样的文档,所以如果您打算提交自己的模块,您必须记住这一点。这也是 Ansible 受欢迎的原因之一——它具有易于维护和记录良好的标准,是自动化的完美社区平台。然而,官方的 Ansible 网站并不是唯一可以获得文档的地方,因为它甚至可以在命令行上获得。在下一节中,我们将了解如何通过该路径检索文档。

从命令行访问模块文档

如前一节所讨论的,Ansible 项目以其文档为荣,使这些文档易于访问是项目本身的一个重要部分。现在,假设您正在处理一项可执行的任务(在剧本、角色甚至是临时命令中),并且您处于一个数据中心环境中,在该环境中,您只能访问正在处理的机器的外壳。您如何访问 Ansible 文档?

幸运的是,我们还没有讨论的 Ansible 安装的一部分是ansible-doc工具,它是与熟悉的ansibleansible-playbook可执行文件一起作为标准安装的。ansible-doc命令包括一个完整的(基于文本的)文档库,用于您安装的 Ansible 版本附带的所有模块。这意味着,您使用模块所需的信息唾手可得,即使您身处数据中心且没有可用的互联网连接!

以下是一些示例,向您展示如何与ansible-doc工具交互:

  • 只需发出以下命令,您就可以列出 Ansible 控制机器上的所有文档模块:
$ ansible-doc -l 

您应该会看到类似如下的输出:

fortios_router_community_list          Configure community lists in Fortinet's FortiOS ...
azure_rm_devtestlab_info               Get Azure DevTest Lab facts
ecs_taskdefinition                     register a task definition in ecs
avi_alertscriptconfig                  Module for setup of AlertScriptConfig Avi RESTfu...
tower_receive                          Receive assets from Ansible Tower
netapp_e_iscsi_target                  NetApp E-Series manage iSCSI target configuratio...
azure_rm_acs                           Manage an Azure Container Service(ACS) instance
fortios_log_syslogd2_filter            Filters for remote system server in Fortinet's F...
junos_rpc                              Runs an arbitrary RPC over NetConf on an Juniper...
na_elementsw_vlan                      NetApp Element Software Manage VLAN
pn_ospf                                CLI command to add/remove ospf protocol to a vRo...
pn_snmp_vacm                           CLI command to create/modify/delete snmp-vacm
cp_mgmt_service_sctp                   Manages service-sctp objects on Check Point over...
onyx_ospf                              Manage OSPF protocol on Mellanox ONYX network de.

输出有很多页,这只是显示你有多少模块!事实上,你可以数一数:

$ ansible-doc -l | wc -l
3387

没错——ansi ble 2 . 9 . 6 附带了 3387 个模块!

  • 和以前一样,可以使用自己喜欢的 shell 工具搜索特定的模块来处理索引;例如,您可以将s3设置为grep来查找所有与 S3 相关的模块,就像我们在上一节的网络浏览器中交互式地做的那样:
$ ansible-doc -l | grep s3
s3_bucket_notification                    Creates, upda...
purefb_s3user                             Create or del...
purefb_s3acc                              Create or del...
aws_s3_cors                               Manage CORS f...
s3_sync                                   Efficiently u...
s3_logging                                Manage loggin...
s3_website                                Configure an ...
s3_bucket                                 Manage S3 buc...
s3_lifecycle                              Manage s3 buc...
aws_s3_bucket_info                        Lists S3 buck...
aws_s3                                    manage object...

  • 现在,我们可以轻松地查找我们感兴趣的模块的特定文档。假设我们想了解更多关于aws_s3模块的信息,就像我们在网站上做的那样,只需运行以下命令:
$ ansible-doc aws_s3

这将产生类似如下的输出:

$ ansible-doc aws_s3
> AWS_S3 (/usr/lib/python2.7/site-packages/ansible/modules/cloud/amazon/aws_s

 This module allows the user to manage S3 buckets and the
 objects within them. Includes support for creating and
 deleting both objects and buckets, retrieving objects as files
 or strings and generating download links. This module has a
 dependency on boto3 and botocore.

 * This module is maintained by The Ansible Core Team
 * note: This module has a corresponding action plugin.

OPTIONS (= is mandatory):

- aws_access_key
 AWS access key id. If not set then the value of the
 AWS_ACCESS_KEY environment variable is used.
 (Aliases: ec2_access_key, access_key)[Default: (null)]
 type: str
....

虽然格式有些不同,ansible-doc向我们介绍了模块,提供了我们可以传递的所有参数(OPTIONS)的列表,当我们向下滚动时,甚至给出了一些工作示例和可能的返回值。我们将在下一节探讨返回值的主题,因为它们对于理解很重要,尤其是当我们接近开发我们自己的模块的主题时。

模块返回值

正如我们在本章前面所讨论的,Ansible 模块将它们的结果作为结构化数据返回,在 JSON 中进行幕后格式化。在前面的示例中,您遇到了这个返回数据,既有退出代码的形式,也有我们使用register关键字在 Ansible 变量中捕获任务结果的形式。在本节中,我们将探讨如何发现 Ansible 模块的返回值,以便我们以后可以在行动手册中使用它们,例如条件处理(参见第 4 章行动手册和角色)。

为了节省空间,我们将选择一个最简单的 Ansible 模块来处理返回值——T0 模块。

不用多说,让我们使用上一节中学习的ansible-doc工具,看看这个模块的返回值是什么:

$ ansible-doc ping

如果您滚动到前面命令输出的底部,您应该会看到如下内容:

$ ansible-doc ping
...

RETURN VALUES:

ping:
 description: value provided with the data parameter
 returned: success
 type: str
 sample: pong

因此,我们可以看到ping模块将只返回一个值,这被称为pingdescription告诉我们应该期望这个特定的返回值包含什么,而returned字段告诉我们它将只在success上返回(如果它将在其他条件下返回,这些将在这里列出)。type返回值是一个字符串(用str表示),虽然您可以使用提供给ping模块的参数来更改该值,但默认返回值(因此为sample)是pong

现在,让我们看看实际情况。例如,这些返回值中没有任何内容可以告诉我们模块是否成功运行以及是否有任何更改;然而,我们知道这些是关于每个模块运行的基本信息。

让我们把一个非常简单的剧本放在一起。我们将在没有参数的情况下运行ping模块,使用register关键字捕获返回值,然后使用debug模块将返回值转储到终端:

---
- name: Simple play to demonstrate a return value
  hosts: localhost

  tasks:
    - name: Perform a simple module based task
      ping:
      register: pingresult

    - name: Display the result
      debug:
        var: pingresult

现在,让我们看看当我们运行这个剧本时会发生什么:

$ ansible-playbook retval.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

PLAY [Simple play to demonstrate a return value] *******************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Perform a simple module based task] **************************************
ok: [localhost]

TASK [Display the result] ******************************************************
ok: [localhost] => {
 "pingresult": {
 "changed": false,
 "failed": false,
 "ping": "pong"
 }
}

PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

请注意,ping模块确实返回了一个名为ping的值,它包含了pong字符串(因为 ping 成功了)。但是,您可以看到,事实上,Ansible 文档中没有列出另外两个返回值。这些伴随着每一个单独的任务运行,因此是隐式的——也就是说,您可以假设它们将在从每个模块返回的数据中。如果模块运行导致目标主机发生变化,changed返回值将被设置为true,而如果模块运行由于某种原因失败,failed返回值将被设置为true

使用debug模块打印模块运行的输出是一个非常有用的技巧,如果你想收集更多关于模块的信息,它是如何工作的,以及返回什么样的数据。至此,我们已经介绍了使用模块的所有基础知识,因此在下一节中,我们将开始开发我们自己的(简单的)模块。

开发定制模块

既然我们已经熟悉了模块,如何调用它们,如何解释它们的结果,以及如何找到关于它们的文档,我们就可以开始编写自己的简单模块了。虽然这不包括 Ansible 附带的许多模块的深层和复杂功能,但希望这能给你足够的信息,让你在构建自己的更复杂的模块时有信心继续。

需要注意的一点是,Ansible 是用 Python 编写的,因此它的模块也是如此。因此,您将需要用 Python 编写您的模块,并且要开始开发您自己的模块,您将需要确保您已经安装了 Python 和一些必要的工具。如果您已经在您的开发机器上运行了 Ansible,您可能已经安装了所需的包,但是如果您从零开始,您将需要安装 Python、Python 包管理器(pip)以及其他一些开发包。操作系统之间的具体过程会有很大的不同,但下面是一些让您入门的示例:

  • 在 Fedora 上,您可以运行以下命令来安装所需的软件包:
$ sudo dnf install python python-devel
  • 同样,在 CentOS 上,您将运行以下命令来安装所需的包:
$ sudo yum install python python-devel
  • 在 Ubuntu 上,您可以运行以下命令来安装您需要的包:
$ sudo apt-get update
$ sudo apt-get install python-pip python-dev build-essential 
  • 如果您正在使用 macOS,并且正在使用家酿包装系统,以下命令将安装您需要的软件包:
$ sudo brew install python

一旦您安装了所需的包,您将需要将 Ansible Git 存储库克隆到您的本地机器上,因为其中有一些有价值的脚本,我们将在模块开发过程中稍后需要它们。使用以下命令将 Ansible 存储库克隆到开发计算机上的当前目录:

$ git clone https://github.com/ansible/ansible.git

最后(尽管是可选的),在虚拟环境中开发您的 Ansible 模块是一个很好的做法,因为这意味着您需要安装的任何 Python 包都可以在这里安装,而不是在您的全局系统 Python 模块中安装。以不受控制的方式为整个系统安装模块有时会导致兼容性问题,甚至损坏本地工具,因此尽管这不是必需的步骤,但强烈建议这样做。

为您的 Python 模块开发工作创建虚拟环境的确切命令将取决于您运行的操作系统和您使用的 Python 版本。有关更多信息,您应该参考您的 Linux 发行版的文档;但是,在 CentOS 7.7 上使用默认的 Python 2.7.5 测试了以下命令,以便在您刚刚从 GitHub 克隆的 Ansible 源代码目录内创建一个名为moduledev的虚拟环境:

$ cd ansible
$  python -m virtualenv moduledev
New python executable in /home/james/ansible/moduledev/bin/python
Installing setuptools, pip, wheel...done.

随着开发环境的建立,让我们开始编写第一个模块。本模块将非常简单,因为围绕如何编写大量 Python 代码进行深入讨论超出了本书的范围。但是,我们将编写一些代码,可以使用 Python 库中的函数在目标机器上本地复制文件。

显然,这与现有的模块功能有很大的重叠,但它将作为一个很好的简洁的例子,说明如何编写一个简单的 Python 程序,使 Ansible 能够将其用作模块。现在,让我们开始编写第一个模块:

  1. 在首选编辑器中,创建一个名为(例如)remote_filecopy.py的新文件:
$ vi remote_filecopy.py
  1. 以一个符号开始,表示这个模块应该用 Python 来执行:
#!/usr/bin/python
  1. 虽然不是强制性的,但在新模块的标题中添加版权信息以及您的详细信息是一种好的做法。通过这样做,任何使用它的人都将理解他们可以使用、修改或重新分发它的条款。这里给出的文本只是一个例子;您应该调查适合自己的各种许可证,并确定哪种许可证最适合您的模块:
# Copyright: (c) 2018, Jesse Keating <jesse.keating@example.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
  1. 在版权部分之后立即添加包含metadata_versionstatussupported_by信息的 Ansible 元数据部分也是一种好的做法。请注意,metadata_version字段代表 Ansible 元数据版本(在撰写本文时,应为1.1),与您的模块版本无关,也与您正在使用的 Ansible 版本无关。以下代码中建议的值对于刚入门的人来说是合适的,但是如果您的模块被官方 Ansible 源代码接受,它们可能会改变:
ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'community'}
  1. 还记得ansible-doc和 Ansible 文档网站上的优秀文档吗?所有这些都是从您添加到该文件的特殊部分自动生成的。让我们从在模块中添加以下代码开始:
DOCUMENTATION = '''
---
module: remote_filecopy
version_added: "2.9"
short_description: Copy a file on the remote host
description:
  - The remote_copy module copies a file on the remote host from a given source to a provided destination.
options:
  source:
    description:
      - Path to a file on the source file on the remote host
    required: True
  dest:
    description:
      - Path to the destination on the remote host for the copy
    required: True
author:
- Jesse Keating (@omgjlk)
'''

特别注意author字典——为了通过语法检查以包含在官方的 Ansible 代码库中,作者的名字应该在括号中加上他们的 GitHub ID。如果您不这样做,您的模块仍然可以工作,但是它不会通过我们稍后执行的测试。

Notice how the documentation is in YAML format, enclosed between triple single quotes? The fields listed should be common to just about all modules, but naturally, if your module takes different options, you would specify these so that they match your module.

  1. 您将在文档中找到的示例也是从该文件生成的–它们在DOCUMENTATION之后有自己的特殊文档部分,并且应该提供关于如何使用您的模块创建任务的实际示例,如下例所示:
EXAMPLES = '''
   # Example from Ansible Playbooks
   - name: backup a config file
     remote_copy:
       source: /etc/herp/derp.conf
       dest: /root/herp-derp.conf.bak
'''
  1. 您的模块返回给 Ansible 的数据也应该记录在它自己的部分。我们的示例模块将返回以下值:
RETURN = '''
source:
  description: source file used for the copy
  returned: success
  type: str
  sample: "/path/to/file.name"
dest:
  description: destination of the copy
  returned: success
  type: str
  sample: "/path/to/destination.file"
gid:
  description: group ID of destination target
  returned: success
  type: int
  sample: 502
group:
  description: group name of destination target
  returned: success
  type: str
  sample: "users"
uid:
  description: owner ID of destination target
  returned: success
  type: int
  sample: 502
owner:
  description: owner name of destination target
  returned: success
  type: str
  sample: "fred"
mode:
  description: permissions of the destination target
  returned: success
  type: int
  sample: 0644
size:
  description: size of destination target
  returned: success
  type: int
  sample: 20
state:
  description: state of destination target
  returned: success
  type: str
  sample: "file"
'''
  1. 在我们完成文档部分之后,我们应该立即导入我们将要使用的任何 Python 模块。这里,我们将包括shutil模块,该模块将用于执行我们的文件复制:
import shutil
  1. 既然我们已经建立了模块头和文档,我们就可以开始处理代码了。现在,您可以看到在每个 Ansible 模块的文档中投入了多少精力!我们的模块应该从定义一个main函数开始,在这个函数中我们将创建一个AnsibleModule类型的对象,并使用一个argument_spec字典来获取调用该模块的选项:
 def main():
       module = AnsibleModule(
           argument_spec = dict(
               source=dict(required=True, type='str'),
               dest=dict(required=True, type='str')
           ) 
       )
  1. 在这个阶段,我们拥有了编写模块功能代码所需的一切——甚至包括调用它的选项。因此,我们可以基于提供的参数,使用 Python shutil模块来执行本地文件复制:
       shutil.copy(module.params['source'],
                   module.params['dest'])
  1. 至此,我们已经执行了模块设计要完成的任务。然而,公平地说,我们还没有完成——我们需要干净地退出模块,并将我们的返回值提供给 Ansible。通常,此时,您会编写一些条件逻辑来检测模块是否成功,以及它是否在目标主机上执行了更改。然而,为了简单起见,我们每次都简单地以changed状态退出——扩展这个逻辑并使返回状态更有意义是留给您的练习:
      module.exit_json(changed=True)

module.exit_json方法来自于我们之前创建的AnsibleModule——记住,我们说过知道数据是使用 JSON 来回传递的很重要!

  1. 当我们接近模块代码的结尾时,我们现在必须告诉 Python 它可以从哪里导入AnsibleModule对象。这可以通过下面一行代码来完成:
   from ansible.module_utils.basic import *
  1. 现在是模块的最后两行代码——这是我们告诉模块它应该在启动时运行main功能的地方:
   if __name__ == '__main__':
       main()

就是这样——通过一系列有据可查的步骤,您可以用 Python 编写自己的 Ansible 模块。当然,下一步是测试它,在我们实际在 Ansible 中测试它之前,让我们看看是否可以在 shell 中手动运行它。当然,为了让模块认为它是在 Ansible 中运行的,我们必须生成一些——你猜对了——JSON 格式的参数。创建一个包含以下内容的文件来提供参数:

{
    "ANSIBLE_MODULE_ARGS": {
        "source": "/tmp/foo",
        "dest": "/tmp/bar"
    }
}

有了这个 JSON 的小片段,您可以直接用 Python 执行您的模块。如果您还没有这样做,您将需要如下设置您的 Ansible 开发环境。请注意,我们还手动创建了源文件/tmp/foo,这样我们的模块就可以真正执行文件复制:

$ touch /tmp/foo
$ . moduledev/bin/activate
(moduledev) $ . hacking/env-setup
running egg_info
creating lib/ansible_base.egg-info
writing requirements to lib/ansible_base.egg-info/requires.txt
writing lib/ansible_base.egg-info/PKG-INFO
writing top-level names to lib/ansible_base.egg-info/top_level.txt
writing dependency_links to lib/ansible_base.egg-info/dependency_links.txt
writing manifest file 'lib/ansible_base.egg-info/SOURCES.txt'
reading manifest file 'lib/ansible_base.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no files found matching 'SYMLINK_CACHE.json'
warning: no previously-included files found matching 'docs/docsite/rst_warnings'
warning: no previously-included files matching '*' found under directory 'docs/docsite/_build'
warning: no previously-included files matching '*.pyc' found under directory 'docs/docsite/_extensions'
warning: no previously-included files matching '*.pyo' found under directory 'docs/docsite/_extensions'
warning: no files found matching '*.ps1' under directory 'lib/ansible/modules/windows'
warning: no files found matching '*.psm1' under directory 'test/support'
writing manifest file 'lib/ansible_base.egg-info/SOURCES.txt'

Setting up Ansible to run out of checkout...

PATH=/home/james/ansible/bin:/home/james/ansible/moduledev/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/james/bin
PYTHONPATH=/home/james/ansible/lib
MANPATH=/home/james/ansible/docs/man:/usr/local/share/man:/usr/share/man

Remember, you may wish to specify your host file with -i

Done!

现在,您终于准备好第一次运行模块了。您可以按如下方式进行:

(moduledev) $ python remote_filecopy.py args.json
{"invocation": {"module_args": {"dest": "/tmp/bar", "source": "/tmp/foo"}}, "changed": true}

(moduledev) $ ls -l /tmp/bar
-rw-r--r-- 1 root root 0 Apr 16 16:24 /tmp/bar

成功!正如我们在本章前面所讨论的那样,您的模块可以工作,它既吸收又产生 JSON 数据。当然,您的模块还有更多需要添加的内容——我们没有处理模块的failedok返回,也不支持检查模式。然而,我们已经有了一个飞速的开端,如果你想了解更多关于 Ansible 模块的信息并充实你的功能,你可以在这里找到更多的细节:https://docs . Ansible . com/Ansible/latest/dev _ guide/developing _ modules _ general . html

请注意,在测试您的模块时,在 JSON 文件中创建参数并不直观,尽管正如我们所看到的,它确实工作得很好。幸运的是,在剧本中运行我们的 Ansible 模块很容易!默认情况下,Ansible 将检查 playbook 目录中名为library/的子目录,并将从这里运行引用的模块。因此,我们可以创建以下内容:

$ cd ~
$ mkdir testplaybook
$ cd testplaybook
$ mkdir library
$ cp ~/ansible/moduledev/remote_filecopy.py library/

现在,在此行动手册目录中创建一个简单的清单文件,就像我们之前所做的那样,并添加一个包含以下内容的行动手册:

---
- name: Playbook to test custom module
  hosts: all

  tasks:
    - name: Test the custom module
      remote_filecopy:
        source: /tmp/foo
        dest: /tmp/bar
      register: testresult

    - name: Print the test result data
      debug:
        var: testresult

为了清楚起见,您的最终目录结构应该如下所示:

testplaybook
├── hosts
├── library
│   └── remote_filecopy.py
└── testplaybook.yml

现在,尝试以通常的方式运行剧本,看看会发生什么:

$ ansible-playbook -i hosts testplaybook.yml

PLAY [Playbook to test custom module] ******************************************

TASK [Gathering Facts] *********************************************************
ok: [frt01.example.com]
ok: [app01.example.com]

TASK [Test the custom module] **************************************************
changed: [app01.example.com]
changed: [frt01.example.com]

TASK [Print the test result data] **********************************************
ok: [app01.example.com] => {
 "testresult": {
 "changed": true,
 "failed": false
 }
}
ok: [frt01.example.com] => {
 "testresult": {
 "changed": true,
 "failed": false
 }
}

PLAY RECAP *********************************************************************
app01.example.com : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
frt01.example.com : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

成功!您不仅在本地测试了您的 Python 代码,而且还在 Ansible 剧本中的两台远程服务器上成功运行了它。这真的很容易,这证明了开始扩展您的 Ansible 模块以满足您自己的定制需求是多么简单。

尽管成功运行了这段代码,但我们还没有检查文档,也没有从 Ansible 测试它的操作。在我们更详细地讨论这些问题之前,在下一节中,我们将看看模块开发中的一些常见陷阱以及如何避免它们。

避免常见的陷阱

至关重要的是,您的模块经过深思熟虑,能够优雅地处理错误情况——总有一天,人们会依靠您的模块在可能成千上万台服务器上自动完成一项任务,因此他们最不希望的就是花费大量时间调试错误,尤其是那些本可以被优雅地捕获或处理的琐碎错误。在这一节中,我们将专门研究错误处理以及如何做到这一点,以便行动手册仍然可以正常运行和退出。

在我们开始之前的一个总体指导是,就像文档在 Ansible 中受到高度关注一样,您的错误消息也应该受到高度关注。它们应该有意义并且容易解释,你应该避开无意义的字符串,比如Error!

所以,现在,如果我们删除我们试图复制的源文件,然后用相同的参数重新运行我们的模块,我想你会同意输出既不漂亮也没有意义,除非你碰巧是一个坚定的 Python 开发人员:

(moduledev) $ rm -f /tmp/foo
(moduledev) $ python remote_filecopy.py args.json
Traceback (most recent call last):
 File "remote_filecopy.py", line 99, in <module>
 main()
 File "remote_filecopy.py", line 93, in main
 module.params['dest'])
 File "/usr/lib64/python2.7/shutil.py", line 119, in copy
 copyfile(src, dst)
 File "/usr/lib64/python2.7/shutil.py", line 82, in copyfile
 with open(src, 'rb') as fsrc:
IOError: [Errno 2] No such file or directory: '/tmp/foo'

毫无疑问,我们可以做得更好。让我们复制我们的模块,并在其中添加一些代码。首先将shutil.copy行代码替换为如下内容:

    try:
       shutil.copy(module.params['source'], module.params['dest'])
    except:
       module.fail_json(msg="Failed to copy file")

这是 Python 中一些令人难以置信的基本异常处理,但它所做的是允许代码尝试shutil.copy任务。但是,如果失败并引发异常,我们将使用module.fail_json调用干净地退出,而不是使用回溯退出。这将告诉 Ansible 模块失败,并干净地发送回 JSON 格式的错误消息。自然,我们可以做很多事情来改进错误信息;例如,我们可以从shutil模块中获取准确的错误信息,并将其传递回 Ansible,但这仍然是留给您完成的练习。

现在,当我们尝试使用不存在的源文件运行模块时,我们将看到以下格式清晰的 JSON 输出:

(moduledev) $ rm -f /tmp/foo
(moduledev) $ python better_remote_filecopy.py args.json

{"msg": "Failed to copy file", "failed": true, "invocation": {"module_args": {"dest": "/tmp/bar", "source": "/tmp/foo"}}}

但是,如果复制成功,该模块仍以与以前相同的方式工作:

(moduledev) $ touch /tmp/foo
(moduledev) $ python better_remote_filecopy.py args.json

{"invocation": {"module_args": {"dest": "/tmp/bar", "source": "/tmp/foo"}}, "changed": true}

通过对我们的代码进行这种简单的更改,我们现在可以干净优雅地处理文件复制操作的失败,并向用户报告一些更有意义的事情,而不是使用回溯。模块中异常处理和处理的一些附加指针如下:

  • 快速失败–出错后不要试图继续处理。
  • 使用各种模块 JSON 返回函数返回最有意义的错误消息。
  • 如果有任何方法可以避免,千万不要返回追溯。
  • 试着让错误在模块和它所做的事情的上下文中有意义(例如,对于我们的模块来说,File copy errorFile error更有意义——我想你会很容易地想出更好的错误消息)。
  • 不要用错误轰炸用户;相反,试着专注于报告最有意义的,尤其是当你的模块代码很复杂的时候。

这就完成了我们对 Ansible 模块中错误处理的简单而实用的介绍。在下一节中,我们将返回到我们模块中包含的文档,包括如何将其构建为 HTML 文档,以便它可以在 Ansible 网站上运行(事实上,如果您的模块被 Ansible 源代码接受,这正是网络文档的生成方式)。

测试和记录您的模块

正如我们在本章前面所讨论的,我们已经在记录我们的模块方面做了大量的工作。然而,我们如何才能看到它,我们如何才能检查它是否正确编译成如果它被接受为 Ansible 源代码的一部分,将在 Ansible 网站上显示的 HTML?

在我们真正开始查看我们的文档之前,我们应该使用一个名为ansible-test的工具,它是在 2.9 版本中新添加的。该工具可以对我们的模块代码执行健全性检查,以确保我们的文档符合 Ansible 项目团队要求的所有标准,并且代码结构正确(例如,Python import语句应该总是在文档块之后)。让我们开始吧:

  1. 要运行健全性测试,假设您已经克隆了正式的存储库,请更改到这个目录并设置您的环境。请注意,如果您的标准 Python 二进制不是 Python 3,ansible-test工具将不会运行,因此您应该确保安装了 Python 3,并在必要时设置一个虚拟环境,以确保您正在使用 Python 3。这可以通过以下方式实现:
$ cd ansible$ python 3 -m venv venv
$ . venv/bin/activate
(venv) $ source hacking/env-setup
running egg_info
creating lib/ansible.egg-info
writing lib/ansible.egg-info/PKG-INFO
writing dependency_links to lib/ansible.egg-info/dependency_links.txt
writing requirements to lib/ansible.egg-info/requires.txt
writing top-level names to lib/ansible.egg-info/top_level.txt
writing manifest file 'lib/ansible.egg-info/SOURCES.txt'
reading manifest file 'lib/ansible.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no files found matching 'SYMLINK_CACHE.json'
writing manifest file 'lib/ansible.egg-info/SOURCES.txt'

Setting up Ansible to run out of checkout...

PATH=/home/james/ansible/bin:/home/james/ansible/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/james/bin
PYTHONPATH=/home/james/ansible/lib
MANPATH=/home/james/ansible/docs/man:/usr/local/share/man:/usr/share/man

Remember, you may wish to specify your host file with -i

Done!
  1. 接下来,使用pip安装 Python 需求,以便运行ansible-test工具:
(venv) $ pip install -r test/runner/requirements/sanity.txt
  1. 现在,假设您已经将模块代码复制到源代码树中的适当位置(这里显示了一个复制命令示例),您可以按如下方式运行健全性测试:
(venv) $ cp ~/moduledev/remote_filecopy.py ./lib/ansible/modules/files/
(venv) $ ansible-test sanity --test validate-modules remote_filecopy
Sanity check using validate-modules
WARNING: Cannot perform module comparison against the base branch. Base branch not detected when running locally.
WARNING: Reviewing previous 1 warning(s):
WARNING: Cannot perform module comparison against the base branch. Base branch not detected when running locally.

从前面的输出中,您可以看到,除了一个与我们没有可比较的基本分支相关的警告之外,我们在本章前面开发的模块代码已经通过了所有测试。如果您对文档有问题(例如,作者姓名格式不正确),这将被视为错误。

现在我们已经通过了ansible-test的健全性检查,让我们通过使用ansible-doc命令来看看文档是否看起来正确。这很容易做到。首先,退出你的虚拟环境,如果你还在里面的话,换到你之前从 GitHub 克隆的 Ansible 源代码目录。现在,您可以手动告诉ansible-doc在哪里寻找模块,而不是默认路径。这意味着您可以运行以下命令:

$ cd ~/ansible
$ ansible-doc -M moduledev/ remote_filecopy

应该向您呈现我们之前创建的文档的文本呈现,这里显示了第一页的示例,让您了解它应该是什么样子:

> REMOTE_FILECOPY (/home/james/ansible/moduledev/remote_filecopy.py)

 The remote_copy module copies a file on the remote host from a
 given source to a provided destination.

 * This module is maintained by The Ansible Community
OPTIONS (= is mandatory):

= dest
 Path to the destination on the remote host for the copy

= source
 Path to a file on the source file on the remote host

太棒了!因此,我们已经可以使用ansible-doc访问我们的模块文档,并且确实确认它在文本模式下正确呈现。然而,我们如何着手构建 HTML 版本呢?幸运的是,这有一个明确的过程,我们将在这里概述:

  1. lib/ansible/modules/下,您会发现模块所在的一系列分类目录–我们的目录最适合在files类别下,因此请将其复制到此位置,为接下来的构建过程做准备:
$ cp moduledev/remote_filecopy.py lib/ansible/modules/files/
  1. 作为文档创建过程的下一步,转到docs/docsite/目录:
$ cd docs/docsite/
  1. 构建一个基于文档的 Python 文件。使用以下命令进行操作:
$ MODULES=hello_module make webdocs

现在,理论上,制作 Ansible 的文档应该这么简单;然而,不幸的是,在编写本文时,Ansible v2.9.6 的源代码拒绝构建webdocs。这无疑将在适当的时候得到解决,因为在撰写本文时,文档构建脚本正在移植到 Python 3。为了让make webdocs命令运行,我必须克隆 Ansible v2.8.10 的源代码作为起点。

即使在这种环境下,在 CentOS 7 上,make webdocs命令也会失败,除非您有一些非常具体的 Python 3 要求。这些都没有很好的记录,但是从测试中,我可以告诉你 Sphinx v2.4.4 是有效的。CentOS 7 提供的版本太旧并且失败了,而 Python 模块存储库中可用的最新版本(在编写本文时为 v3.0.1)与构建过程不兼容并且失败了。

一旦我开始从 Ansible v2.8.10 源代码树中工作,我必须确保我已经从我的 Python 3 环境中删除了任何先前存在的sphinx模块(您需要 Python 3.5 或更高版本才能在本地构建文档-如果您的节点上没有安装这些模块,请在继续之前执行此操作),然后运行以下命令:

$ pip3 uninstall sphinx
$ pip3 install sphinx==2.4.4
$ pip3 install sphinx-notfound-page

有了这些,您将能够成功运行make webdocs来构建您的文档。您将看到数页输出。成功的运行应该以如下所示的输出结束:

generating indices... genindex py-modindexdone
writing additional pages... search/home/james/ansible/docs/docsite/_themes/sphinx_rtd_theme/search.html:21: RemovedInSphinx30Warning: To modify script_files in the theme is deprecated. Please insert a <script> tag directly in your theme instead.
 {% endblock %}
 opensearchdone
copying images... [100%] dev_guide/style_guiimg/thenvsthan.jpg
copying downloadable files... [ 50%] network/getting_started/sample_files/first_copying downloadable files... [100%] network/getting_started/sample_files/first_playbook_ext.yml
copying static files... ... done
copying extra files... done
dumping search index in English (code: en)... done
dumping object inventory... done
build succeeded, 35 warnings.

The HTML pages are in _build/html.
make[1]: Leaving directory `/home/james/ansible/docs/docsite'

现在,请注意在这个过程的最后,make命令告诉我们在哪里可以找到编译的文档。如果您查看这里,您会发现以下内容:

$ find /home/james/ansible/docs/docsite -name remote_filecopy*
/home/james/ansible/docs/docsite/rst/modules/remote_filecopy_module.rst
/home/james/ansible/docs/docsite/_build/html/modules/remote_filecopy_module.html
/home/james/ansible/docs/docsite/_build/doctrees/modules/remote_filecopy_module.doctree

尝试在您的网络浏览器中打开 HTML 文件–您应该会看到该页面呈现得就像官方 Ansible 项目文档中的一个文档页面一样!这使您能够检查您的文档构建是否正确,并且在要查看的上下文中看起来和读起来是否良好。它还让您有信心,当您向 Ansible 项目提交代码时(如果您正在这样做的话),您提交的东西符合 Ansible 的文档质量标准。

More information on building the documentation locally is provided here: https://docs.ansible.com/ansible/latest/community/documentation_contributions.html#building-the-documentation-locally. Although this is an excellent document, it does not currently reflect the compatibility issues around Sphinx, nor the build issues regarding Ansible 2.9. Hopefully, however, it will give you all of the other pointers you need to get going with your documentation.

当前构建文档的过程对所支持的环境有点繁琐;然而,希望这是将在适当时候得到解决的事情。与此同时,本节中概述的流程为您提供了一个经过测试的工作流程。

模块清单

除了我们到目前为止已经介绍过的指针和良好实践之外,您还应该在模块代码中坚持一些东西,以产生一些被认为是可能包含在 Ansible 中的高标准的东西。下面的列表并不详尽,但会让您对作为模块开发人员应该遵循的实践有一个很好的了解:

  • 尽可能多地测试你的模块,不管是成功的还是导致错误的。您可以使用 JSON 数据测试它们,就像我们在本章中所做的那样,或者在测试手册中使用它们。

  • 尽量将您的 Python 需求保持在最低限度。有时候,没有办法避免需要额外的 Python 依赖项(比如 AWS 特定模块的boto需求),但是一般来说,你能用的越少越好。

  • 不要为您的模块缓存数据 Ansible 在不同主机上的执行策略意味着您不太可能从这样做中获得好的结果。期望在每次运行时收集您需要的所有数据。

  • 模块应该是一个单独的 Python 文件——它们不应该分布在多个文件中。

  • 在提交模块代码时,请确保您调查并运行了 Ansible 集成测试。更多相关信息请点击这里:https://docs . ansi ble . com/ansi ble/latest/dev _ guide/testing _ integration . html

  • 确保您在模块代码的适当位置包含异常处理,正如我们在本章中所做的那样,以防止出现问题。

  • 除非绝对无法避免,否则不要在 Windows 模块中使用PSCustomObjects

有了从本章获得的信息,您应该拥有开始创建自己的模块所需的一切。您可能不会决定将它们提交给 Ansible 项目,当然也没有这样做的要求。然而,即使你没有,遵循本章中概述的实践将确保你构建一个高质量的模块,而不管它的目标受众是什么。最后,在您确实想将源代码提交给 Ansible 项目的基础上,在下一节中,我们将看看如何通过对 Ansible 项目的请求来实现这一点。

向上游投稿–提交 GitHub 拉取请求

当你在你的模块上努力工作并彻底测试和记录它时,你可能会觉得是时候将它提交给 Ansible 项目进行包含了。这样做意味着在官方的 Ansible 存储库中创建一个拉请求。尽管使用 GitHub 的复杂性超出了本书的范围,但我们将为您提供一个实际上重点突出的基本过程的概要。

Following the process outlined here will generate a real request against the Ansible project on GitHub so that the code you are committing can be merged with their code. Do not follow this process unless you genuinely have a new module that is ready for submission to the Ansible codebase.

要将您的模块作为 Ansible 存储库的拉取请求提交,您需要分叉官方 Ansible 存储库的devel分支。为此,请从您的网络浏览器登录到您的 GitHub 帐户(或者创建一个帐户,如果您还没有的话),然后导航到下面截图中显示的网址。单击右上角的“分叉”。提醒一下,官方 Ansible 源代码库 URL 是https://github.com/ansible/ansible.git:

现在您已经将存储库转移到您自己的帐户,我们将遍历您需要运行的命令,以便向其中添加您的模块代码。然后,我们将向您展示如何创建所需的拉取请求(也称为 PRs ),以便您可以将新模块与上游的 Ansible 项目合并:

  1. 克隆刚刚分叉到本地机器的devel分支。使用类似于下面的命令,但是一定要用与您自己的 GitHub 帐户相匹配的 URL 来替换该 URL:
$ git clone https://github.com/danieloh30/ansible.git
  1. 将您的模块代码复制到适当的模块目录中–以下代码中给出的copy命令只是一个示例,为您提供了如何操作的线索,但实际上,您应该为您的模块选择适当的类别子目录,因为它不一定适合files类别。一旦添加了 Python 文件,执行git add让 Git 知道新文件,然后用有意义的提交消息提交它。一些示例命令如下:
$ cd ansible
$ cp ~/ansible-development/moduledev/remote_filecopy.py ./lib/ansible/modules/files/
$ git add lib/ansible/modules/files/remote_filecopy.py
$ git commit -m 'Added tested version of remote_filecopy.py for pull request creation'
  1. 现在,请确保使用以下命令将代码推送到分叉的存储库中:
$ git push
  1. 在您的网络浏览器中返回 GitHub,导航到拉取请求页面,如下所示。单击“新建请求”按钮:

按照 GitHub 网站的指导,完成拉取请求的创建过程。一旦您成功提交了请求,您应该能够导航到官方 Ansible 源代码存储库中的请求列表,并在那里找到您的请求。此处显示了一个请求列表示例,供您参考:

这张截图拍摄时,有将近 31,000 个关闭的拉取请求和将近 1,700 个开放的审查!当你读这本书的时候,肯定会有更多的书,证明 Ansible 是多么依赖开源社区来持续发展和增长。想想吧——你可能是其中的一员!如果您的请求需要很长时间才能得到审查,请不要惊慌——这只是需要审查和处理的请求数量的一个方面。您总是可以通过将您的模块代码添加到本地library/目录来本地使用它,正如我们前面演示的那样,这样您的拉取请求的处理速度就不会妨碍您使用 Ansible 的工作。关于在本地工作时将插件代码放在哪里的更多细节可以在这里找到:https://docs . ansi ble . com/ansi ble/latest/dev _ guide/developing _ local . html

除了为定制模块创建拉取请求之外,还有很多方法可以为 Ansible 项目做出贡献。以下是一些您可以为项目做出贡献的其他方式的示例:

  • 查看 Ansible 的文档并报告您在其中发现的任何错误(一个是在创建第 4 章行动手册和角色时提交的)
  • 创建一个本地的 Ansible meeting up 来传播你关于 ansi ble 的知识。如果你足够幸运,已经有一个在你的领域,考虑定期参加它。
  • 通过社交媒体传播 Ansible 的知识和意识,并提供适当的账户参考和标签;例如,@ansible#ansible等等。

这就完成了我们学习如何创建模块的旅程,从研究模块操作理论的第一步,一直到将您的新模块代码贡献给 GitHub 上的官方 Ansible 项目。我们希望您已经发现此旅程内容丰富且有价值,并希望它增强您与 Ansible 合作的能力,并在需要时扩展其功能。

摘要

模块是 Ansible 的命脉,没有它们,Ansible 就无法在各种系统中出色地完成所有复杂多样的任务。由于是一个开源项目,自己扩展 Ansible 的功能非常容易,在这一章中,我们探讨了如何利用一点 Python 知识从头开始编写自己的定制模块。在撰写本文时,Ansible 的功能非常丰富,但是这种易于定制和扩展的特性使得 Ansible 的潜力几乎是无限的,尤其是考虑到 Python 作为一种编程语言的强大和流行。

在本章中,我们首先回顾了如何使用命令行执行多个模块。然后,我们探讨了查询当前模块索引的过程,以及如何获取模块的文档来评估它们是否适合我们的需求,而不管我们是否有活跃的互联网连接。然后,我们探索了模块数据及其 JSON 格式,最后开始了一段旅程,我们将简单的定制模块的代码放在一起。如果您愿意的话,这为您将来创建自己的模块提供了基础。

在下一章中,我们将探索使用和创建另一个核心 Ansible 特性(称为插件)的过程。

发现插件类型

Ansible 的代码一直被设计成模块化的——事实上,这是它的核心优势之一。无论是通过使用模块来执行任务,还是通过插件(我们将很快看到),Ansible 的模块化设计都使它能够像本书迄今为止所展示的那样通用和强大。与模块一样,Ansible 插件都是用 Python 编写的,并被期望以某种明确定义的格式接收和返回数据(稍后将详细介绍)。Ansible 的插件在它们的功能中通常是不可见的,因为你很少在你的命令或剧本中叫它们的名字,但是它们负责 Ansible 必须提供的一些最重要的功能,包括 SSH 连接、解析清单文件的能力(INI 格式、YAML 或其他),以及对你的数据运行jinja2过滤器的能力。

和以往一样,在继续下一步之前,让我们验证您的测试机器上是否存在适当安装的 Ansible 版本:

$  ansible-doc --version
ansible-doc 2.9.6
 config file = /etc/ansible/ansible.cfg
 configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
 ansible python module location = /usr/lib/python2.7/site-packages/ansible
 executable location = /usr/bin/ansible-doc
 python version = 2.7.5 (default, Aug 7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

记录插件的工作和记录模块的工作一样多,你会很高兴知道在https://docs . ansi ble . com/ansi ble/latest/plugins/plugins . html上有一个插件索引。

你也可以像我们之前一样使用ansible-doc命令,只是你也需要添加-t开关。插件总是被放在一个适当的类别中,因为它们的功能在不同的类别中是完全不同的。如果您没有使用ansible-doc指定-t开关,您最终会指定ansible-doc -t模块,该模块会返回可用模块的列表。

在撰写本文时,可以在 Ansible 中找到以下插件类别:

  • become:负责让 Ansible 获得超级用户访问权限(例如通过sudo)

  • cache:负责缓存从后端系统检索到的事实,以提高自动化性能

  • callback:允许您在响应事件时添加新的行为,例如,更改 Ansible playbook 运行的输出中打印数据的格式

  • cliconf:为各种网络设备的命令行界面提供抽象,给 Ansible 一个标准的操作界面

  • connection:提供从 Ansible 到远程系统的连接(例如,通过 SSH、WinRM、Docker 等)

  • httpapi:告诉 Ansible 如何与远程系统的 API 交互(例如,对于 Fortinet 防火墙)

  • inventory:为 Ansible 提供解析各种静态和动态库存格式的能力

  • lookup:允许 Ansible 从外部来源查找数据(例如,通过读取平面文本文件)

  • netconf:为 Ansible 提供抽象,使其能够与支持 NETCONF 的网络设备一起工作

  • shell:为 Ansible 提供了在不同系统上使用不同外壳的能力(例如,Windows 上的powershell与 Linux 上的sh

  • strategy:为 Ansible 提供不同执行策略的插件(比如我们在第四章剧本和角色看到的调试策略)

  • vars:为 Ansible 提供从特定来源获取变量的能力,例如我们在第 3 章定义库存中探索的host_varsgroup_vars目录

我们将把探索 Ansible 网站上的插件文档留给您来完成。但是,如果您想使用ansible-doc工具探索各种插件,您需要运行以下命令:

  1. 要使用ansible-doc命令列出给定类别中所有可用的插件,您可以运行以下命令:
$ ansible-doc -t connection -l

这将返回连接插件的文本索引,类似于我们在查看模块文档时看到的。这里显示了索引输出的前几行:

kubectl           Execute tasks in pods running on Kubernetes
napalm            Provides persistent connection using NAPALM
qubes             Interact with an existing QubesOS AppVM
libvirt_lxc       Run tasks in lxc containers via libvirt
funcd             Use funcd to connect to target
chroot            Interact with local chroot
psrp              Run tasks over Microsoft PowerShell Remoting Protocol
zone              Run tasks in a zone instance
winrm             Run tasks over Microsoft's WinRM
paramiko_ssh      Run tasks via python ssh (paramiko)
  1. 然后,您可以浏览给定插件的文档。例如,如果我们想了解paramiko_ssh插件,我们可以发出以下命令:
$ ansible-doc -t connection paramiko_ssh

您会发现插件文档采用了非常熟悉的格式,类似于我们在第 5 章消费和创建模块中看到的模块:

> PARAMIKO (/usr/lib/python2.7/site-packages/ansible/plugins/connection/param

 Use the python ssh implementation (Paramiko) to connect to
 targets The paramiko transport is provided because many
 distributions, in particular EL6 and before do not support
 ControlPersist in their SSH implementations. This is needed on
 the Ansible control machine to be reasonably efficient with
 connections. Thus paramiko is faster for most users on these
 platforms. Users with ControlPersist capability can consider
 using -c ssh or configuring the transport in the configuration
 file. This plugin also borrows a lot of settings from the ssh
 plugin as they both cover the same protocol.

 * This module is maintained by The Ansible Community
OPTIONS (= is mandatory):

- host_key_auto_add
 TODO: write it
 [Default: (null)]
 set_via:
 env:
 - name: ANSIBLE_PARAMIKO_HOST_KEY_AUTO_ADD
 ini:
 - key: host_key_auto_add

感谢所有的辛勤工作和努力来记录 Ansible 的每个领域,你可以很容易地找到 Ansible 包含的插件以及如何使用它们。到目前为止,我们已经看到插件的文档和模块一样完整。在本章的下一节中,我们将深入探讨如何找到 Ansible 发行版附带的插件代码。

问题

  1. 哪个命令行可以作为参数传递给模块?

A) ansible dbservers -m command "/bin/echo 'hello modules'"

B) ansible dbservers -m command -d "/bin/echo 'hello modules'"

C) ansible dbservers -z command -a "/bin/echo 'hello modules'"

D) ansible dbservers -m command -a "/bin/echo 'hello modules'"

E) ansible dbservers -a "/bin/echo 'hello modules'"

  1. 当您创建自定义模块并解决异常时,不建议采用以下哪种做法?

a)简单地设计一个定制模块,如果可以避免的话,永远不要向用户提供追溯。

b)快速使您的模块代码失败,并验证您是否提供了有用且可理解的异常消息。

c)仅显示最相关异常的错误消息,而不是所有可能的错误。

d)确保您的模块文档相关且易于理解。

e)删除导致错误的行动手册,然后从头开始重新创建。

  1. 对或错:要为 Ansible 上游项目做出贡献,您需要向devel分支提交代码。

真的吗

假的

进一步阅读