推荐Ansible InventoryPlugin

这是2021年樱互联网Advent Calendar的第16篇文章。

简而言之

在生成Ansible清单的动态方法中,人们通常会使用“动态清单”,但还有一种叫做“清单插件”的替代方法。

有时候,使用库存插件可以更简洁地构建剧本,尽管与动态库存相比,库存插件的机制稍微复杂一些。

立刻試試看

作为一个例子,让我们尝试将以下Excel文件(book1.xlsx)用作Ansible的清单。
从左到右,这些是主机名、IP地址和用途等图像。

book1.png

请指定库存插件位置。

为了方便操作,我们会设置一个目录来引用默认的库插件。

[defaults]
inventory_plugins=./plugins/inventory

默认值的详细信息请参阅以下文件。

创建库存文件

用 ansible-playbook 命令的 -i 选项来指定创建清单文件。
在插件中写下将要创建的清单插件名称,并在文件中定义要加载的Excel文件。

plugin: xlsx
files:
  - "book1.xlsx"

创建库存插件

这是一个很简单但有效的脚本,它可以读取Excel文件并将内容添加到库存中。

import openpyxl
from ansible.plugins.inventory import BaseInventoryPlugin


class InventoryModule(BaseInventoryPlugin):
    NAME = "xlsx"

    def verify_file(self, path):
        valid = False
        if super(InventoryModule, self).verify_file(path):
            if path.endswith(".yml"):
                valid = True
                return valid
        return True

    def _load_xlsx(self, path):
        inventory_data = self.loader.load_from_file(path, cache=False)
        nodes = []
        for f in inventory_data.get("files"):
            wb = openpyxl.load_workbook(f)
            ws = wb["Sheet1"]
            for row in ws.rows:
                nodes.append(
                    {
                        "hostname": row[0].value,
                        "address": row[1].value,
                        "role": row[2].value,
                    }
                )
        return nodes

    def parse(self, inventory, loader, path, cache=True):
        super(InventoryModule, self).parse(inventory, loader, path, cache)

        xlsx_data = self._load_xlsx(path)

        for data in xlsx_data:
            self.inventory.add_host(data["hostname"])
            self.inventory.set_variable(
                data["hostname"], "ansible_host", data["address"]
            )
            self.inventory.add_group(data["role"])
            self._populate_host_vars([data["hostname"]], {}, data["role"])
            self.inventory.add_child("all", data["role"])

详细内容省略。如果要自行制作,请参考以下存储库。

试一试

我成功地将Excel文件的内容作为库存处理,一切顺利!

$ ansible-inventory -i xlsx.yml --list
{
    "_meta": {
        "hostvars": {
            "node1": {
                "ansible_host": "192.0.2.1"
            },
            "node2": {
                "ansible_host": "192.0.2.2"
            },
            "node3": {
                "ansible_host": "192.0.2.3"
            }
        }
    },
    "all": {
        "children": [
            "db",
            "ungrouped",
            "web"
        ]
    },
    "db": {
        "hosts": [
            "node2",
            "node3"
        ]
    },
    "web": {
        "hosts": [
            "node1"
        ]
    }
}

這是額外的禮物。

book2.png

只需将其添加到库存文件中即可。

plugin: xlsx
files:
  - "book1.xlsx"
  - "book2.xlsx"
$ ansible-inventory -i xlsx.yml --list
{
    "_meta": {
        "hostvars": {
            "node1": {
                "ansible_host": "192.0.2.1"
            },
            "node2": {
                "ansible_host": "192.0.2.2"
            },
            "node3": {
                "ansible_host": "192.0.2.3"
            },
            "node4": {
                "ansible_host": "192.0.2.4"
            },
            "node5": {
                "ansible_host": "192.0.2.5"
            },
            "node6": {
                "ansible_host": "192.0.2.6"
            }
        }
    },
    "all": {
        "children": [
            "db",
            "ungrouped",
            "web"
        ]
    },
    "db": {
        "hosts": [
            "node2",
            "node3",
            "node5",
            "node6"
        ]
    },
    "web": {
        "hosts": [
            "node1",
            "node4"
        ]
    }
}

顺便提一下,创建的文件目录结构如下所示。

$ tree
.
├── ansible.cfg
├── book1.xlsx
├── book2.xlsx
├── plugins
│   └── inventory
│       └── xlsx.py
└── xlsx.yml

作为结束。

在使用动态清单时,我认为主要使用环境变量作为提供的参数。而对于清单插件的情况,则可以在清单文件中定义任意数量的选项,这样可以获得更高的自由度,构建更加灵活的清单。如果对于动态清单感到困惑,请务必尝试一下。