前言
api 接口用以实现在程序端调用 ansible 控制远程主机,是平台化的必要步骤。
ansible开发api接口
ansible api封装
下面是官方的一个api,创建ansibleApi.py,并将代码保存到该文件里。
import json
import shutil
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
import ansible.constants as C
class ResultCallback(CallbackBase):
"""一个简单的回调插件 """
def v2_runner_on_ok(self, result, **kwargs):
"""打印结果的json表示形式 """
host = result._host
print(json.dumps({host.name: result._result}, indent=4))
# 构建一个解析器,将相关参数传入
Options = namedtuple('Options', [
'connection',
'module_path',
'forks',
'become',
'become_method',
'become_user',
'check',
'diff'])
options = Options( connection='local',
module_path=['/to/mymodules'],
forks=10,
become=None,
become_method=None,
become_user=None,
check=False,
diff=False)
# 初始化所需对象
loader = DataLoader() # 负责查找和读取yAML、json和ini文件
passwords = dict(vault_pass='secret')
# 实例化 ResultCallback,处理返回的结果。
results_callback = ResultCallback()
# 创建 inventory,定义需要执行的主机组
inventory = InventoryManager(loader=loader, sources='localhost,')
# 合并所有不同来源的参数
variable_manager = VariableManager(loader=loader, inventory=inventory)
# 创建数据结构来加载任务
play_source = dict(
name = "Ansible Play",
hosts = 'localhost',
gather_facts = 'no',
tasks = [
dict(action=dict(module='shell', args='ls'), register='shell_out'),
dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
]
)
# 创建play对象,使用 .load 初始化
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
# 实例化任务队列管理器,它负责设置所有对象,以便在主机列表和任务上迭代
tqm = None
try:
tqm = TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=passwords,
stdout_callback=results_callback, # 使用自定义回调函数
)
result = tqm.run(play) # 执行任务
finally:
# 清理子进程等相关数据
if tqm is not None:
tqm.cleanup()
# 删除临时目录
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
从官方提供的代码看,我们只需要传递执行主机和执行任务即可。这里传递的执行主机是sources='localhost,',执行的任务是
tasks = [
dict(action=dict(module='shell', args='ls'), register='shell_out'),
dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
]
上面代码执行了两个 ansible 任务,第一个调用 shell 模块执行了 ls,第二个调用 debug 模块打印了第一个任务的输出结果。
至此,根据官方的api例子,我们可以封装成可调用的函数,将两个api整合到一起,代码如下:
import json
import shutil
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader # 用于读取yaml,json格式的文件
from ansible.vars.manager import VariableManager # 用于管理变量的类,包括主机,组,扩展等变量
from ansible.inventory.manager import InventoryManager # 用于创建和管理inventory,导入inventory文件
from ansible.playbook.play import Play # ad-hoc 存储着角色列表,任务,处理代码块
from ansible.executor.task_queue_manager import TaskQueueManager # ad-hoc ansible底层用到的任务队列
from ansible.plugins.callback import CallbackBase # 回调基类,用来定义回调事件,比如返回失败成功等信息
from ansible.executor.playbook_executor import PlaybookExecutor # 执行playbook
from ansible.utils.ssh_functions import check_for_controlpersist
import ansible.constants as C
import redis
import datetime
import logging, logging.handlers
ansible_remote_user = 'root'
class ResultCallback(CallbackBase):
"""一个简单的回调插件 """
def v2_runner_on_ok(self, result, **kwargs):
"""打印结果的 json 表示形式 """
host = result._host
print(json.dumps({host.name: result._result}, indent=4, ensure_ascii=False))
# 执行ansible任务
def AnsibleApi(hosts, tasks, sources, extra_vars={}):
'''调用时需要传入 3 个参数,分别是 执行主机列表、任务列表、inventory 文件'''
Options = namedtuple('Options',
['remote_user',
'connection',
'module_path',
'forks',
'become',
'become_method',
'become_user',
'check',
'diff'])
options = Options(remote_user=ansible_remote_user,
connection='paramiko',
module_path=['/to/mymodules'],
forks=10,
become=None,
become_method=None,
become_user=None,
check=False,
diff=False)
loader = DataLoader()
passwords = dict(vault_pass='secret')
inventory = InventoryManager(loader=loader, sources=sources)
variable_manager = VariableManager(loader=loader, inventory=inventory)
variable_manager.extra_vars=extra_vars
play_source = dict(name = "Ansible Play",hosts = hosts,gather_facts = 'no',tasks = tasks)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
tqm = None
try:
tqm = TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=passwords,
stdout_callback=ResultCallback(),
)
result = tqm.run(play)
finally:
# 最后删除ansible产生的临时目录
# 这个临时目录会在 ~/.ansible/tmp/ 目录下
if tqm is not None:
tqm.cleanup()
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
class MyPlaybookExecutor(PlaybookExecutor):
'''重写 PlaybookExecutor,实现调用 TaskQueueManager 时可以加载自定义的 ResultCallback'''
def __init__(self, playbooks, inventory, variable_manager, loader, options, passwords):
self._playbooks = playbooks
self._inventory = inventory
self._variable_manager = variable_manager
self._loader = loader
self._options = options
self.passwords = passwords
self._unreachable_hosts = dict()
if options.listhosts or options.listtasks or options.listtags or options.syntax:
self._tqm = None
else:
self._tqm = TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=self.passwords,
stdout_callback=ResultCallback()
)
# 运行此命令以缓存默认的ansible ssh可执行文件支持控制持久化,
# 我们暂时无法进行此缓存使用它的地方(在task_executor中),
# 因此将在每个任务之后丢弃。
check_for_controlpersist(C.ANSIBLE_SSH_EXECUTABLE)
# 执行ansible-playbook任务
def AnsiblePlaybookApi(playbooks, sources, extra_vars={}):
Options = namedtuple('Options', [
'remote_user',
'connection',
'module_path',
'forks',
'become',
'become_method',
'become_user',
'check',
'diff',
'listhosts',
'listtasks',
'listtags',
'syntax',
])
options = Options(
remote_user=ansible_remote_user,
connection='paramiko',
module_path=['/to/mymodules'],
forks=10,
become=None,
become_method=None,
become_user=None,
check=False,
diff=False,
listhosts=None,
listtasks=None,
listtags=None,
syntax=None
)
loader = DataLoader()
passwords = dict(vault_pass='secret')
inventory = InventoryManager(loader=loader, sources=sources)
variable_manager = VariableManager(loader=loader, inventory=inventory)
variable_manager.extra_vars=extra_vars
pb = MyPlaybookExecutor(playbooks=playbooks,
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=passwords)
result = pb.run()
if __name__ == '__main__':
# 主机资产信息放在了scripts目录下的inventory文件里。
sources = 'scripts/inventory'
extra_vars = {'content': '这个参数从外部传入'} # 字典形式传入额外参数
# 测试 ansible_api
tasks = []
tasks.append(dict(action=dict(module='debug', args=dict(msg='{{ content}}'))))
AnsibleApi("localhost", tasks, sources, extra_vars)
# 测试 ansible-playbook_api
playbooks = ['playbooks/test_debug.yml',]
AnsiblePlaybookApi(playbooks, sources, extra_vars)
在测试ansible-playbook_api的时候,传进去的test_debug.yml文件内容为:
- hosts: localhost
tasks:
- name: Debug
debug:
msg: '{{ content }}'
运行python ansibleApi.py,输出的结果最后可以看到:
{
"localhost": {
"msg": "这个参数从外部传入",
"_ansible_no_log": false,
"changed": false,
"_ansible_verbose_always": true
}
}