jumpserver/apps/ops/ansible/callback.py

169 lines
5.3 KiB
Python

import os
from collections import defaultdict
from functools import reduce
class DefaultCallback:
STATUS_MAPPER = {
'successful': 'success',
'failure': 'failed',
'failed': 'failed',
'running': 'running',
'pending': 'pending',
'timeout': 'timeout',
'unknown': 'unknown'
}
def __init__(self):
self.result = dict(
ok=defaultdict(dict),
failures=defaultdict(dict),
dark=defaultdict(dict),
skipped=defaultdict(dict),
ignored=defaultdict(dict),
)
self.summary = dict(
ok=[],
failures={},
dark={},
skipped=[],
)
self.status = 'running'
self.finished = False
self.local_pid = 0
self.private_data_dir = None
@property
def host_results(self):
results = {}
for state, hosts in self.result.items():
for host, items in hosts.items():
results[host] = items
return results
def is_success(self):
return self.status != 'success'
def event_handler(self, data, **kwargs):
event = data.get('event', None)
if not event:
return
pid = data.get('pid', None)
if pid:
self.write_pid(pid)
event_data = data.get('event_data', {})
host = event_data.get('remote_addr', '')
task = event_data.get('task', '')
res = event_data.get('res', {})
handler = getattr(self, event, self.on_any)
handler(event_data, host=host, task=task, res=res)
def runner_on_ok(self, event_data, host=None, task=None, res=None):
detail = {
'action': event_data.get('task_action', ''),
'res': res,
'rc': res.get('rc', 0),
'stdout': res.get('stdout', ''),
}
self.result['ok'][host][task] = detail
def runner_on_skipped(self, event_data, host=None, task=None, **kwargs):
detail = {
'action': event_data.get('task_action', ''),
'res': {},
'rc': 0,
}
self.result['skipped'][host][task] = detail
def runner_on_failed(self, event_data, host=None, task=None, res=None, **kwargs):
detail = {
'action': event_data.get('task_action', ''),
'res': res,
'rc': res.get('rc', 0),
'stdout': res.get('stdout', ''),
'stderr': ';'.join([res.get('stderr', ''), res.get('msg', '')]).strip(';')
}
ignore_errors = event_data.get('ignore_errors', False)
error_key = 'ignored' if ignore_errors else 'failures'
self.result[error_key][host][task] = detail
def runner_on_unreachable(self, event_data, host=None, task=None, res=None, **kwargs):
detail = {
'action': event_data.get('task_action', ''),
'res': res,
'rc': 255,
'stderr': ';'.join([res.get('stderr', ''), res.get('msg', '')]).strip(';')
}
self.result['dark'][host][task] = detail
def runner_on_start(self, event_data, **kwargs):
pass
def runner_retry(self, event_data, **kwargs):
pass
def runner_on_file_diff(self, event_data, **kwargs):
pass
def runner_item_on_failed(self, event_data, **kwargs):
pass
def runner_item_on_skipped(self, event_data, **kwargs):
pass
def playbook_on_play_start(self, event_data, **kwargs):
pass
def playbook_on_stats(self, event_data, **kwargs):
error_func = lambda err, task_detail: err + f"{task_detail[0]}: {task_detail[1]['stderr']};"
for tp in ['dark', 'failures']:
for host, tasks in self.result[tp].items():
error = reduce(error_func, tasks.items(), '').strip(';')
self.summary[tp][host] = error
failures = list(self.result['failures'].keys())
dark_or_failures = list(self.result['dark'].keys()) + failures
for host, tasks in self.result.get('ignored', {}).items():
ignore_errors = reduce(error_func, tasks.items(), '').strip(';')
if host in failures:
self.summary['failures'][host] += ignore_errors
self.summary['ok'] = list(set(self.result['ok'].keys()) - set(dark_or_failures))
self.summary['skipped'] = list(set(self.result['skipped'].keys()) - set(dark_or_failures))
def playbook_on_include(self, event_data, **kwargs):
pass
def playbook_on_notify(self, event_data, **kwargs):
pass
def playbook_on_vars_prompt(self, event_data, **kwargs):
pass
def playbook_on_handler_task_start(self, event_data, **kwargs):
pass
def playbook_on_no_hosts_matched(self, event_data, **kwargs):
pass
def playbook_on_no_hosts_remaining(self, event_data, **kwargs):
pass
def warning(self, event_data, **kwargs):
pass
def on_any(self, event_data, **kwargs):
pass
def status_handler(self, data, **kwargs):
status = data.get('status', '')
self.status = self.STATUS_MAPPER.get(status, 'unknown')
self.private_data_dir = data.get("private_data_dir", None)
def write_pid(self, pid):
pid_filepath = os.path.join(self.private_data_dir, 'local.pid')
with open(pid_filepath, 'w') as f:
f.write(str(pid))