引言
Ansible是一种强大的自动化工具,可以简化IT基础架构的管理和部署。它提供了各种各样的插件,以便用户可以轻松地扩展其功能。其中,自定义插件是Ansible的一个重要特性,可以满足用户不同的需求,例如自定义Action、Module、Filter、Lookup、Callback等插件。
在Ansible中,Inventory是由主机清单和组成员组成的。主机清单是一个文本文件,其中列出了所有托管主机的信息,包括主机名、IP地址、用户名、密码等。组成员是一组主机,可以按功能、环境、角色等方式组织在一起。动态Inventory插件允许用户在运行时动态地生成主机清单,而不是使用静态文本文件。。自定义Inventory插件可以是各种类型,例如脚本、API、数据库等。最简单的方式可以通过脚本的方式来做Inventory的解析,实际上,最开始我也是尝试用脚本来搞定的。但是遇到一个问题,因为项目的ssh端口号这些不是协议规定的22端口号,需要通过ansible_ssh_port
等变量去纠正,但是发现用脚本来解析时会遇到这些变量没有实际生效的问题,因此研究了一下开发插件来解析的方式。相比于使用脚本来解析,插件还有一些高级特性,例如支持缓存等功能。
本文将重点介绍笔者开发一个从MongoDB
读取inventory
的插件的开发过程及途中遇到的问题。
开发过程
数据准备
MongoDB中创建一个名为hosts
的表(这个可以自定义,跟下面的配置里面的collection对应起来就行),插入如下的示例数据:
{
"name" : "server1",
"vars" : {
"ansible_ssh_host" : "1.1.1.1",
"ansible_ssh_port" : "12345",
"ansible_ssh_user" : "user",
"ansible_ssh_private_key_file" : "/path/to/private/key",
"ansible_ssh_common_args" : "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
}
}
配置部分
对应的inventory文件mongodb.yaml
(这里需要指定一个固定的yml文件来让ansible识别使用哪个插件来读取配置以及获取必要的参数):
# mongodb.yaml
plugin: mongodb_inventory
mongodb_uri: mongodb://localhost:27017
database: ansible
collection: hosts
ansible.cfg
文件里面需要指明一下去哪个目录下搜索插件:
[defaults]
inventory_plugins = /root/inventory_plugins
插件开发
mongodb_inventory.py
代码
# mongodb_inventory.py
from ansible.plugins.inventory import BaseInventoryPlugin
try:
from pymongo import MongoClient
except ImportError:
raise Exception("The 'pymongo' python module is required.")
DOCUMENTATION = '''
name: mongodb_inventory
plugin_type: inventory
short_description: Returns Ansible inventory from MongoDB
options:
mongodb_uri:
description: MongoDB URI
required: True
database:
description: Database name
required: True
collection:
description: Collection name
required: True
requirements:
- pymongo
'''
class InventoryModule(BaseInventoryPlugin):
NAME = 'mongodb_inventory'
def verify_file(self, path):
return path.endswith(('mongodb.yaml', 'mongodb.yml'))
def parse(self, inventory, loader, path, cache=True):
super(InventoryModule, self).parse(inventory, loader, path)
self._read_config_data(path)
client = MongoClient(self.get_option('mongodb_uri'))
db = client[self.get_option('database')]
collection = db[self.get_option('collection')]
results = collection.find()
for item in results:
host_name = item.get('host')
group_name = item.get('group', 'ungrouped')
host_vars = item.get('vars', {})
self.inventory.add_host(host_name)
self.inventory.add_group(group_name)
self.inventory.add_host(host_name, group_name)
for var, value in host_vars.items():
self.inventory.set_variable(host_name, var, value)
使用示例
ansible -i mongodb.yaml all -m ping
如果能正常执行ping脚本,那么说明我们的插件开发以及ok了。至此,这个小插件就开发完成了。通过这个插件,我们就可以将inventory数据定义在mongodb里面了。如果别的接口或者数据库也是类似的思路,只是里面的verify和parse函数稍作调整即可。
小结
个人觉得这里比较关键的三个点是:
- 要了解如
inventory
插件的生效机制,需要通过ansible.cfg
(也可以在默认插件文件存放插件文件,但是个人觉得不太好做版本管理)来指定对应的插件搜索路径; - 了解插件开发的范式,需要一个
yml
文件作为inventory
文件来告诉ansible
使用哪个插件来解析inventory,并且需要设置DOCUMENT
变量值才能让插件正常的解析inventory
文件里面自定义的参数; - 最简单的插件需要实现
verify_file
和parse
两个函数,一个是做合法性校验的,另一个则是添加host并设置对应的变量;
踩的几个坑
问题1
运行命令时遇到 specifies unknown plugin 错误,比如这样:
Failed to parse /root/mongodb.yml with auto plugin: inventory config '/root/mongodb.yml' specifies unknown plugin 'mongodb_inventory'
解决:
该错误表示 Ansible 无法找到名为 mongodb_inventory
的库存插件。这可能是因为插件文件未放置在正确的位置,或者 Ansible 的配置未正确指向插件的位置。
以下是解决此问题的步骤:
- 确保
mongodb_inventory.py
文件已放置在正确的目录中:Ansible 的库存插件需要放置在ansible/plugins/inventory/
目录中,或者你可以创建一个自定义的目录并将其添加到ansible.cfg
文件的inventory_plugins
配置项中。 - 确保
mongodb_inventory.py
文件有执行权限。你可以通过运行以下命令来设置执行权限:
chmod +x mongodb_inventory.py
- 检查你的
ansible.cfg
文件,并确保inventory_plugins
配置项包含了你的插件文件mongodb_inventory.py
所在的目录。例如:
[defaults]
inventory_plugins = /path/to/your/inventory_plugins_directory
- 确保你的 Ansible 版本支持库存插件。库存插件是在 Ansible 2.4 版本中引入的,因此如果你的 Ansible 版本较旧,你可能需要升级你的 Ansible 版本。
如果你仍然遇到问题,你可能需要检查你的插件代码是否有误,或者检查 MongoDB 的连接信息是否正确。
问题2
命名已经在yml文件里面定义了对应的参数,但是运行报错:
mongodb_inventory setting: mongodb_uri ) was not defined in configuration
解决:
插件里面需要有 DOCUMENTATION
变量定义, 比如这样:
DOCUMENTATION = '''
name: mongodb_inventory
plugin_type: inventory
short_description: Returns Ansible inventory from MongoDB
options:
mongodb_uri:
description: MongoDB URI
required: True
database:
description: Database name
required: True
collection:
description: Collection name
required: True
requirements:
- pymongo
'''