76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
from __future__ import (absolute_import, division, print_function)
|
|
__metaclass__ = type
|
|
|
|
DOCUMENTATION = '''
|
|
name: json_logger
|
|
type: notification
|
|
short_description: Appends one JSON line per playbook run to ansible_runs.jsonl
|
|
description:
|
|
- Records playbook execution results as newline-delimited JSON.
|
|
'''
|
|
|
|
import json
|
|
import os
|
|
import time
|
|
from datetime import datetime
|
|
|
|
from ansible.plugins.callback import CallbackBase
|
|
|
|
CALLBACK_TYPE = 'notification'
|
|
CALLBACK_NAME = 'json_logger'
|
|
CALLBACK_NEEDS_WHITELIST = False
|
|
|
|
_LOG_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ansible_runs.jsonl')
|
|
|
|
|
|
class CallbackModule(CallbackBase):
|
|
|
|
def __init__(self):
|
|
super(CallbackModule, self).__init__()
|
|
self._playbook_name = None
|
|
self._start_time = None
|
|
self._tasks = []
|
|
|
|
def v2_playbook_on_start(self, playbook):
|
|
self._playbook_name = os.path.basename(playbook._file_name)
|
|
self._start_time = time.time()
|
|
self._tasks = []
|
|
|
|
def v2_playbook_on_task_start(self, task, is_conditional):
|
|
name = task.get_name()
|
|
if name and name not in self._tasks:
|
|
self._tasks.append(name)
|
|
|
|
def v2_playbook_on_stats(self, stats):
|
|
duration = round(time.time() - self._start_time, 1) if self._start_time else 0.0
|
|
|
|
hosts_data = {}
|
|
overall_failed = False
|
|
for host in sorted(stats.processed.keys()):
|
|
s = stats.summarize(host)
|
|
hosts_data[host] = {
|
|
'ok': s['ok'],
|
|
'changed': s['changed'],
|
|
'failed': s['failures'],
|
|
'unreachable': s['unreachable'],
|
|
'skipped': s['skipped'],
|
|
}
|
|
if s['failures'] > 0 or s['unreachable'] > 0:
|
|
overall_failed = True
|
|
|
|
record = {
|
|
'ts': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S'),
|
|
'playbook': self._playbook_name or 'unknown',
|
|
'user': os.environ.get('USER', os.environ.get('LOGNAME', 'unknown')),
|
|
'duration': duration,
|
|
'status': 'failed' if overall_failed else 'success',
|
|
'hosts': hosts_data,
|
|
'tasks': self._tasks,
|
|
}
|
|
|
|
try:
|
|
with open(_LOG_PATH, 'a') as f:
|
|
f.write(json.dumps(record) + '\n')
|
|
except Exception as e:
|
|
self._display.warning(f'json_logger: could not write to {_LOG_PATH}: {e}')
|