import re import sys from collections import defaultdict def parse_file(file_path): recap_results = [] failed_tasks = defaultdict(list) with open(file_path, "r", encoding="utf-8", errors="ignore") as f: lines = f.readlines() current_task = None for i, line in enumerate(lines): # ------------------------- # 1. Detect TASK name # ------------------------- task_match = re.search(r"TASK \[(.*?)\]", line) if task_match: current_task = task_match.group(1) # ------------------------- # 2. Detect FAILED lines # ------------------------- fail_match = re.search(r"fatal: \[(.*?)\]: FAILED!", line) if fail_match: host = fail_match.group(1) # Try to grab error message from same or next lines error_msg = "" # Look ahead a few lines to capture reason for j in range(i, min(i + 5, len(lines))): if "msg" in lines[j] or "stderr" in lines[j]: error_msg += lines[j].strip() + " " if not error_msg: error_msg = "Unknown error" failed_tasks[host].append({ "task": current_task, "error": error_msg.strip() }) # ------------------------- # 3. Parse PLAY RECAP lines # ------------------------- if "ok=" in line and "failed=" in line and "unreachable=" in line: host_match = re.search(r"(?:\d{2}:\d{2}:\d{2}\s+)?(\S+)\s+:", line) if not host_match: continue host = host_match.group(1) def get_value(field): match = re.search(rf"{field}=(\d+)", line) return int(match.group(1)) if match else 0 ok = get_value("ok") changed = get_value("changed") unreachable = get_value("unreachable") failed = get_value("failed") status = ( "UNREACHABLE" if unreachable > 0 else "FAILED" if failed > 0 else "OK" ) recap_results.append({ "host": host, "ok": ok, "changed": changed, "unreachable": unreachable, "failed": failed, "status": status }) return recap_results, failed_tasks def print_report(results, failed_tasks): unreachable_hosts = [] failed_hosts = [] print("\n=== FULL HOST REPORT ===") for r in results: print(f"{r['host']} -> {r['status']} " f"(ok={r['ok']}, changed={r['changed']}, " f"failed={r['failed']}, unreachable={r['unreachable']})") if r["status"] == "UNREACHABLE": unreachable_hosts.append(r["host"]) elif r["status"] == "FAILED": failed_hosts.append(r["host"]) print("\n=== SUMMARY ===") print("\nUNREACHABLE HOSTS:") print("\n".join(unreachable_hosts) if unreachable_hosts else " None") print("\nFAILED HOSTS:") print("\n".join(failed_hosts) if failed_hosts else " None") # ------------------------- # NEW SECTION # ------------------------- print("\n=== FAILED TASK DETAILS ===") if not failed_tasks: print(" No failed tasks detected") return for host, tasks in failed_tasks.items(): print(f"\n{host}") for t in tasks: print(f" Task: {t['task']}") print(f" Error: {t['error']}") if __name__ == "__main__": results, failed_tasks = parse_file(sys.argv[1]) print_report(results, failed_tasks)