我想要将Ansible执行结果输出到默认输出 + JSON文件中
我想要以JSON格式处理Ansible的执行结果!但是控制台输出不太理想!
一般情况下,Ansible会将执行结果逐步输出到控制台,如下所示:
$ ansible-playbook -i hosts playbook.yml
PLAY [all] ******************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************
ok: [web_server]
ok: [db_server]
....
PLAY RECAP ******************************************************************************************************
db_server : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web_server : ok=8 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
如果想要在其他工具中处理此执行结果,我认为会需要结构化数据(如JSON)。(或者,您可以尝试直接解析控制台输出,但会相当困难。)
如果出现这种情况,您只需要将 ANSIBLE_STDOUT_CALLBACK=json 作为环境变量提供即可,但是在执行播放书的最后,会一下子输出一大段长长的JSON。
$ ANSIBLE_STDOUT_CALLBACK=json ansible-playbook -i hosts playbook.yml
{
"custom_stats": {},
"global_custom_stats": {},
"plays": [
{
"play": {
"duration": {
"end": "2022-05-26T04:22:39.631342Z",
"start": "2022-05-26T04:22:37.698472Z"
},
... (4200行ののち)
"stats": {
"db_server": {
"changed": 0,
"failures": 0,
"ignored": 0,
"ok": 5,
"rescued": 0,
"skipped": 0,
"unreachable": 0
},
"web_server": {
"changed": 0,
"failures": 0,
"ignored": 0,
"ok": 8,
"rescued": 0,
"skipped": 0,
"unreachable": 0
}
}
}
# 上記が ansible-playbook が実行終了のタイミングでドバっと一気に出る
现在无法确定处理进行到什么地方。而且,简直是一眼望不清。
并非如此,在将JSON输出到文件的同时,不会有一种方便的方法将标准输出设置为默认输出方法。
如果希望自由地输出Ansible的执行结果,只需自己创建一个自定义的回调插件即可。
要解决这个问题,你需要自己创建一个定制的回调插件来自定义Ansible的控制台输出。
Ansible是使用Python编写的,您可以通过编写自己的Python脚本来自由地向Ansible添加插件。
对于这个项目而言,可以按照如下方式创建一个专用插件(假设为json_export)。
[defaults]
# ./callback_plugins/json_export.py に自作callbackプラグインを置く
# (ansible-playbook に与える引数のプレイブックからの相対パスになる)
callback_plugins = ./callback_plugins
callbacks_enabled = json_export
# デフォルトのcallbackプラグインの指定
# 別に ANSIBLE_STDOUT_CALLBACK=json_export のように実行時に環境変数として指定してもいい
stdout_callback = json_export
import os
import json
from ansible.parsing.ajson import AnsibleJSONEncoder
from ansible.plugins.callback.default import CallbackModule as Base
DOCUMENTATION = '''
name: json_export
type: stdout
short_description: Also export results into a JSON file if specified.
version_added: historical
description:
- Export results into a JSON file (specified at JSON_EXPORT_PATH).
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout in configuration
'''
# 基本的にはデフォルトの出力が欲しいので、デフォルトのcallbackモジュールを拡張したものを作成する
class CallbackModule(Base):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'json_export'
def __init__(self):
super(CallbackModule, self).__init__()
self._results = []
self._servers = {}
self._output_to = os.getenv('JSON_EXPORT_PATH', '')
# v2_* メソッドをオーバーライドしてデフォルトの動作に処理を追加していく
def v2_playbook_on_task_start(self, task, is_conditional):
super(CallbackModule, self).v2_playbook_on_task_start(task, is_conditional)
self._results.append({ 'name': task.get_name(), 'items': [] })
def v2_runner_on_ok(self, result):
super(CallbackModule, self).v2_runner_on_ok(result)
self._append_result('ok', result)
def v2_runner_on_failed(self, result, ignore_errors=False):
super(CallbackModule, self).v2_runner_on_failed(result)
self._append_result('failed', result)
def v2_runner_on_skipped(self, result):
super(CallbackModule, self).v2_runner_on_skipped(result)
self._append_result('skipped', result)
def v2_runner_on_unreachable(self, result):
super(CallbackModule, self).v2_runner_on_skipped(result)
self._append_result('unreachable', result)
def _append_result(self, result_type, result):
lastResults = self._results[-1]['items']
host_name = result._host.get_name()
rawResult = result._result
essentialResult = {}
for key in ["changed", "start", "end", "stdout", "stdout_lines", "stderr", "stderr_lines"]:
if key in rawResult:
essentialResult[key] = rawResult[key]
lastResults.append({
'host_name': host_name,
'type': result_type,
'result': essentialResult
})
self._servers[host_name] = True
# 実行結果出力時(実行の最後)にJSONファイルを出力する
def v2_playbook_on_stats(self, stats):
super(CallbackModule, self).v2_playbook_on_stats(stats)
if self._output_to:
with open(self._output_to, "w") as f:
json.dump({
'results': self._results,
'host_names': list(self._servers.keys())
}, f, cls=AnsibleJSONEncoder, indent=2, ensure_ascii=False, sort_keys=True)
只需要在ansible.cfg中添加插件的搜索路径和允许的插件名称,然后将其配置为运行时要使用的插件。
如果您在上述自制插件中提供了所需的JSON_EXPORT_PATH,那么在执行结束时会生成一个JSON文件。
$ JSON_EXPORT_PATH=ansible_result.json ansible-playbook -i hosts playbook.yml
PLAY [all] *************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************
ok: [web_server]
ok: [db_server]
...
PLAY RECAP *************************************************************************************************************************
db_server : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web_server : ok=8 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
{
"host_names": [
"db_server",
"web_server"
],
"results": [
{
"items": [
{
"host_name": "db_server",
"result": {
"changed": false
},
"type": "ok"
},
...
总结
如果您想要自由定制化执行结果的输出,您可以创建一个回调插件来实现任何功能。
可以利用这个方法,例如,可以将Ansible的所有执行结果强制性地上传到审计用的存储库中。