Última actividad 21 hours ago

Revisión 9c4db30a38e17d3c8a75fc4d8882cc49dabe2a70

sysreport.py Sin formato
1#!/usr/bin/env python3
2"""
3sysreport.py — Genera un reporte completo del sistema en JSON.
4Usa jc para convertir la salida de comandos del sistema a JSON
5estructurado. Ideal para diagnosticar, auditar o pasar a una IA.
6
7Uso:
8 python3 sysreport.py # Guarda en /tmp/sysreport.json
9 python3 sysreport.py --stdout # Imprime por stdout
10 python3 sysreport.py --compact # Sin pretty-print
11 python3 sysreport.py --only discos # Solo una sección
12
13Dependencias:
14 - Python 3.10+
15 - jc (pip install jc)
16 - jq (sudo apt install jq)
17 - Los comandos del sistema: ps, df, systemctl, ss, free, uptime
18"""
19
20import argparse
21import json
22import subprocess
23import sys
24from datetime import datetime
25
26
27# ─── Secciones del reporte ────────────────────────────────────────────────────
28
29SECCIONES = {
30 "procesos": (["ps", "aux"], "ps"),
31 "discos": (["df", "-h"], "df"),
32 "servicios": (["systemctl", "list-units", "--no-legend"], "systemctl"),
33 "puertos": (["ss", "-tlnp"], "ss"),
34 "memoria": (["free", "-h"], "free"),
35 "uptime": (["uptime"], "uptime"),
36}
37
38
39def ejecutar_jc(comando: list, parser: str) -> list | dict:
40 """Ejecuta un comando y lo parsea con jc."""
41 try:
42 out = subprocess.run(comando, capture_output=True, text=True, timeout=10)
43 if out.returncode != 0:
44 return {"error": f"Comando falló: {' '.join(comando)}"}
45 # Parsear con jc
46 jc_out = subprocess.run(
47 ["jc", f"--{parser}"],
48 input=out.stdout,
49 capture_output=True,
50 text=True,
51 timeout=10,
52 )
53 if jc_out.returncode != 0:
54 return {"error": f"jc falló para {parser}: {jc_out.stderr[:200]}"}
55 return json.loads(jc_out.stdout)
56 except subprocess.TimeoutExpired:
57 return {"error": f"Timeout ejecutando {' '.join(comando)}"}
58 except json.JSONDecodeError as e:
59 return {"error": f"JSON inválido de jc --{parser}: {e}"}
60 except FileNotFoundError:
61 return {"error": f"jc no encontrado. Instala: pip install jc"}
62
63
64def generar_reporte(secciones: list[str] | None = None) -> dict:
65 """Genera el reporte del sistema."""
66 incluir = secciones if secciones else list(SECCIONES.keys())
67
68 reporte = {
69 "fecha": datetime.now().isoformat(),
70 "hostname": subprocess.run(
71 ["hostname"], capture_output=True, text=True, timeout=5
72 ).stdout.strip(),
73 }
74
75 for seccion in incluir:
76 if seccion in SECCIONES:
77 comando, parser = SECCIONES[seccion]
78 reporte[seccion] = ejecutar_jc(comando, parser)
79
80 return reporte
81
82
83# ─── Main ─────────────────────────────────────────────────────────────────────
84
85def main():
86 parser = argparse.ArgumentParser(
87 description="sysreport: reporte del sistema en JSON usando jc",
88 formatter_class=argparse.RawDescriptionHelpFormatter,
89 epilog="""\
90Ejemplos:
91 python3 sysreport.py # Guarda en /tmp/sysreport.json
92 python3 sysreport.py --stdout # Imprime por stdout
93 python3 sysreport.py --only procesos discos # Solo procesos y discos
94 python3 sysreport.py --stdout | jq '.discos' # Filtrar con jq
95 """,
96 )
97 parser.add_argument("--stdout", action="store_true", help="Imprimir por stdout en vez de guardar")
98 parser.add_argument("--compact", action="store_true", help="Sin pretty-print (más pequeño)")
99 parser.add_argument("--only", nargs="+", choices=list(SECCIONES.keys()),
100 help=f"Solo estas secciones: {', '.join(SECCIONES.keys())}")
101
102 args = parser.parse_args()
103
104 print("📡 Generando reporte del sistema...", file=sys.stderr)
105 reporte = generar_reporte(args.only)
106
107 indent = None if args.compact else 2
108
109 if args.stdout:
110 print(json.dumps(reporte, indent=indent, ensure_ascii=False))
111 else:
112 output_path = "/tmp/sysreport.json"
113 with open(output_path, "w") as f:
114 json.dump(reporte, f, indent=indent, ensure_ascii=False)
115 print(f"✅ Reporte guardado en {output_path}", file=sys.stderr)
116 print(f"📦 Tamaño: {len(json.dumps(reporte))} caracteres", file=sys.stderr)
117 print(f" Secciones: {', '.join(reporte.keys())}", file=sys.stderr)
118
119 # Mostrar resumen rápido
120 errores = [k for k, v in reporte.items() if isinstance(v, dict) and "error" in v]
121 if errores:
122 print(f"⚠️ Errores en: {', '.join(errores)}", file=sys.stderr)
123
124 # Contar elementos por sección
125 for k, v in reporte.items():
126 if isinstance(v, list):
127 print(f" {k}: {len(v)} elementos", file=sys.stderr)
128
129
130if __name__ == "__main__":
131 main()