在 Galaxy 上将角色迁移到集合中的角色

您可以将任何现有的独立角色迁移到一个集合中,并将该集合托管在 Galaxy 上。借助 Ansible 集合,您可以在一个可重用自动化的单个内聚单元中分发多个角色。在集合内部,您可以跨集合中的所有角色共享自定义插件,而不是在每个角色的 library/` 目录中复制它们。

如果您想将角色作为认证的 Ansible 内容分发,则必须将角色迁移到集合。

注意

如果您想将集合导入到 Galaxy,则需要一个 Galaxy 命名空间

有关集合的详细信息,请参阅 开发集合

比较独立角色和集合角色

独立角色具有以下目录结构

  role/
  ├── defaults
  ├── files
  ├── handlers
  ├── library
  ├── meta
  ├── module_utils
  ├── [*_plugins]
  ├── tasks
  ├── templates
  ├── tests
  └── vars

当您迁移到基于集合的角色时,上面突出显示的目录将发生更改。集合目录结构包括一个 roles/ 目录

mynamespace/
└── mycollection/
  ├── docs/
  ├── galaxy.yml
  ├── plugins/
     ├── modules/
        └── module1.py
     ├── inventory/
     └── .../
  ├── README.md
  ├── roles/
     ├── role1/
     ├── role2/
     └── .../
  ├── playbooks/
     ├── files/
     ├── vars/
     ├── templates/
     └── tasks/
  └── tests/

当您将角色迁移到集合中时,您需要使用完全限定集合名称(FQCN)来使用角色和插件。FQCN 是集合 namespace、集合 name 以及您引用的内容项的组合。

因此,例如,在上面的集合中,访问 role1 的 FQCN 将是

mynamespace.mycollection.role1

一个集合可以在 roles/ 目录中包含一个或多个角色,这些角色几乎与独立角色相同,除了您需要将插件移出各个角色,并在某些地方使用 FQCN,如下一节所述。

注意

在独立角色中,某些插件目录在其复数意义上引用了插件类型;在集合中,情况并非如此。

将角色迁移到集合

要从不包含插件的独立角色迁移到集合角色

  1. 创建一个本地 ansible_collections 目录,并将 cd 切换到此新目录。

  2. 创建一个集合。如果您想将此集合导入到 Ansible Galaxy,则需要一个 Galaxy 命名空间

$ ansible-galaxy collection init mynamespace.mycollection

这将创建集合目录结构。

  1. 将独立角色目录复制到集合的 roles/ 子目录中。集合中的角色名称不能包含连字符。将任何此类角色重命名为使用下划线代替。

$ mkdir mynamespace/mycollection/roles/my_role/
$ cp -r /path/to/standalone/role/mynamespace/my_role/\* mynamespace/mycollection/roles/my_role/
  1. 更新 galaxy.yml 以包含任何角色依赖项。

  2. 更新集合 README.md 文件以添加指向任何角色 README.md 文件的链接。

将包含插件的角色迁移到集合

要从具有插件的独立角色迁移到集合角色

  1. 创建一个本地 ansible_collections 目录,并将 cd 切换到此新目录。

  2. 创建一个集合。如果您想将此集合导入到 Ansible Galaxy,则需要一个 Galaxy 命名空间

$ ansible-galaxy collection init mynamespace.mycollection

这将创建集合目录结构。

  1. 将独立角色目录复制到集合的 roles/ 子目录中。集合中的角色名称不能包含连字符。将任何此类角色重命名为使用下划线代替。

$ mkdir mynamespace/mycollection/roles/my_role/
$ cp -r /path/to/standalone/role/mynamespace/my_role/\* mynamespace/mycollection/roles/my_role/
  1. 将任何模块移动到 plugins/modules/ 目录。

$ mv -r mynamespace/mycollection/roles/my_role/library/\* mynamespace/mycollection/plugins/modules/
  1. 将任何其他插件移动到相应的 plugins/PLUGINTYPE/ 目录。有关可能需要的其他步骤,请参阅 将其他角色插件迁移到集合

  2. 更新 galaxy.yml 以包含任何角色依赖项。

  3. 更新集合 README.md 文件以添加指向任何角色 README.md 文件的链接。

  4. 更改对角色的任何引用以使用 FQCN

---
- name: example role by FQCN
  hosts: some_host_pattern
  tasks:
    - name: import FQCN role from a collection
      import_role:
        name: mynamespace.mycollection.my_role

您可以选择使用 collections 关键字来简化此操作

---
- name: example role by FQCN
  hosts: some_host_pattern
  collections:
    - mynamespace.mycollection
  tasks:
    - name: import role from a collection
      import_role:
        name: my_role

将其他角色插件迁移到集合

要将其他角色插件迁移到集合

  1. 将每个非模块插件移动到相应的 plugins/PLUGINTYPE/ 目录。mynamespace/mycollection/plugins/README.md 文件解释了集合可以在可选创建的子目录中包含的插件类型。

$ mv -r mynamespace/mycollection/roles/my_role/filter_plugins/\* mynamespace/mycollection/plugins/filter/
  1. 更新文档以使用 FQCN。使用 doc_fragments 的插件需要使用 FQCN(例如,mydocfrag 变为 mynamespace.mycollection.mydocfrag)。

  2. 更新集合中使用的相对导入以句点开头。例如,./filename../asdfu/filestuff 有效,但是必须将同一目录中的 filename 更新为 ./filename

如果您有自定义的 module_utils 或从 __init__.py 导入,则还必须

  1. 更改自定义 module_utils 的 Python 命名空间以使用 FQCN 以及 ansible_collections 约定。请参阅 更新 module_utils

  2. 更改从 __init__.py 导入的方式。请参阅 从 __init__.py 导入

更新 module_utils

如果您的任何自定义模块使用自定义模块实用程序,则一旦您迁移到集合,您就无法在顶层 ansible.module_utils Python 命名空间中寻址该模块实用程序。Ansible 不会将集合中的内容合并到 Ansible 内部 Python 命名空间中。当您将自定义内容迁移到集合时,请更新任何引用自定义模块实用程序的 Python 导入语句。有关详细信息,请参阅 集合中的 module_utils

在集合中使用 module_utils 进行编码时,Python 导入语句需要考虑 FQCN 以及 ansible_collections 约定。生成的 Python 导入看起来类似于以下示例

from ansible_collections.{namespace}.{collectionname}.plugins.module_utils.{util} import {something}

注意

您需要在更改路径和使用子类化插件的命名名称时遵循相同的规则。

以下示例代码片段显示了使用默认 Ansible module_utils 和集合提供的 module_utils 的 Python 和 PowerShell 模块。在此示例中,命名空间为 ansible_example,集合为 community

在 Python 示例中,module_utilshelperFQCNansible_example.community.plugins.module_utils.helper

 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.common.text.converters import to_text
 from ansible.module_utils.six.moves.urllib.parse import urlencode
 from ansible.module_utils.six.moves.urllib.error import HTTPError
 from ansible_collections.ansible_example.community.plugins.module_utils.helper import HelperRequest

 argspec = dict(
         name=dict(required=True, type='str'),
         state=dict(choices=['present', 'absent'], required=True),
 )

 module = AnsibleModule(
         argument_spec=argspec,
         supports_check_mode=True
 )

 _request = HelperRequest(
       module,
         headers={"Content-Type": "application/json"},
      data=data
)

在 PowerShell 示例中,module_utilshypervFQCNansible_example.community.plugins.module_utils.hyperv

#!powershell
#AnsibleRequires -CSharpUtil Ansible.Basic
#AnsibleRequires -PowerShell ansible_collections.ansible_example.community.plugins.module_utils.hyperv

$spec = @{
        name = @{ required = $true; type = "str" }
      state = @{ required = $true; choices = @("present", "absent") }
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)

Invoke-HyperVFunction -Name $module.Params.name

$module.ExitJson()

从 __init__.py 导入

由于 CPython 解释器进行导入的方式,以及 Ansible 插件加载器的工作方式,如果您的自定义嵌入式模块或插件需要从 __init__.py 文件导入某些内容,则该文件也将成为您的集合的一部分。您可以从独立角色中创建内容,也可以在 Python 导入语句中使用文件名。以下示例是一个 __init__.py 文件,它是名为 ansible_example.community 的集合中的回调插件的一部分。

from ansible_collections.ansible_example.community.plugins.callback.__init__ import CustomBaseClass

示例:将带有插件的独立角色迁移到集合

在此示例中,我们有一个名为 my-standalone-role.webapp 的独立角色,用于模拟名称中包含破折号的独立角色(这在集合中无效)。此独立角色在其 library/ 目录中包含一个名为 manage_webserver 的自定义模块。

my-standalone-role.webapp
├── defaults
├── files
├── handlers
├── library
├── meta
├── tasks
├── templates
├── tests
└── vars
  1. 创建一个新的集合,例如 acme.webserver

$ ansible-galaxy collection init acme.webserver
- Collection acme.webserver was created successfully
$ tree acme -d 1
acme
└── webserver
       ├── docs
       ├── plugins
       └── roles
  1. 在集合内部创建 webapp 角色,并将所有内容从独立角色复制过来

$ mkdir acme/webserver/roles/webapp
$ cp my-standalone-role.webapp/* acme/webserver/roles/webapp/
  1. manage_webserver 模块移动到其在 acme/webserver/plugins/modules/ 中的新位置

$ cp my-standalone-role.webapp/library/manage_webserver.py acme/webserver/plugins/modules/manage.py

注意

此示例将原始源文件 manage_webserver.py 更改为目标文件 manage.py。这是可选的,但 FQCNwebserver 上下文提供为 acme.webserver.manage

  1. 在角色中的 tasks/ 文件中(例如,my-standalone-role.webapp/tasks/main.yml)和任何使用原始模块名称的地方,将 manage_webserver 更改为 acme.webserver.manage

注意

只有在更改了原始模块名称时才需要进行此名称更改,但这说明了由 FQCN 引用的内容可以提供上下文,从而可以使模块和插件名称更短。如果您希望独立于角色使用这些模块,请保留原始命名约定。用户可以在他们的 playbook 中添加 collections 关键字。通常,角色是一个抽象层,用户不会独立使用角色的组件。

示例:在下游 RPM 中支持独立的和迁移的集合角色

一个独立的角色可以与其集合角色对应部分共存(例如,作为产品支持生命周期的一部分)。这应该只在过渡期间进行,但这两种角色可以共存于下游的软件包中,例如 RPM。例如,RHEL 系统角色可以与 RHEL 系统角色集合的示例共存,并提供与下游 RPM 的现有向后兼容性。

本节将逐步介绍如何在下游 RPM 中创建这种共存,并且需要 Ansible 2.9.0 或更高版本。

要将角色作为独立角色和集合角色交付

  1. 将集合放在 /usr/share/ansible/collections/ansible_collections/ 中。

  2. 将集合内部的角色内容复制到一个以独立角色命名的目录中,并将独立角色放在 /usr/share/ansible/roles/ 中。

先前在独立角色中使用的所有捆绑模块和插件现在都通过 FQCN 引用,因此即使它们不再嵌入,也可以从集合内容中找到它们。这是一个示例,说明集合内部的内容是一个唯一的实体,不必绑定到角色或其他任何东西。您可以选择创建两个单独的集合:一个用于模块和插件,另一个用于要迁移到的独立角色。该角色必须使用模块和插件作为 FQCN

以下是一个示例 RPM spec 文件,该文件使用此示例内容完成此操作

Name: acme-ansible-content
Summary: Ansible Collection for deploying and configuring ACME webapp
Version: 1.0.0
Release: 1%{?dist}
License: GPLv3+
Source0: acme-webserver-1.0.0.tar.gz

Url: https://github.com/acme/webserver-ansible-collection
BuildArch: noarch

%global roleprefix my-standalone-role.
%global collection_namespace acme
%global collection_name webserver

%global collection_dir %{_datadir}/ansible/collections/ansible_collections/%{collection_namespace}/%{collection_name}

%description
Ansible Collection and standalone role (for backward compatibility and migration) to deploy, configure, and manage the ACME webapp software.

%prep
%setup -qc

%build

%install

mkdir -p %{buildroot}/%{collection_dir}
cp -r ./* %{buildroot}/%{collection_dir}/

mkdir -p %{buildroot}/%{_datadir}/ansible/roles
for role in %{buildroot}/%{collection_dir}/roles/*
  do
         cp -pR ${role} %{buildroot}/%{_datadir}/ansible/roles/%{roleprefix}$(basename ${role})

         mkdir -p %{buildroot}/%{_pkgdocdir}/$(basename ${role})
         for docfile in README.md COPYING LICENSE
          do
      if [ -f ${role}/${docfile} ]
          then
              cp -p ${role}/${docfile} %{buildroot}/%{_pkgdocdir}/$(basename ${role})/${docfile}
      fi
         done
done


%files
%dir %{_datadir}/ansible
%dir %{_datadir}/ansible/roles
%dir %{_datadir}/ansible/collections
%dir %{_datadir}/ansible/collections/ansible_collections
%{_datadir}/ansible/roles/
%doc %{_pkgdocdir}/*/README.md
%doc %{_datadir}/ansible/roles/%{roleprefix}*/README.md
%{collection_dir}
%doc %{collection_dir}/roles/*/README.md
%license %{_pkgdocdir}/*/COPYING
%license %{_pkgdocdir}/*/LICENSE

使用 ansible.legacy 从基于集合的角色访问本地自定义模块

集合中的某些角色使用 本地自定义模块,这些模块不属于集合本身。如果自定义模块短名称与集合模块名称之间存在冲突,则需要指定任务调用哪个模块。您可以更新任务,将 local_module_name 更改为 ansible.legacy.local_module_name,以确保您使用的是自定义模块。