最后活跃于 20 hours ago

atareao's Avatar atareao 修订了这个 Gist 20 hours ago. 转到此修订

1 file changed, 131 insertions

sysreport.py(文件已创建)

@@ -0,0 +1,131 @@
1 + #!/usr/bin/env python3
2 + """
3 + sysreport.py — Genera un reporte completo del sistema en JSON.
4 + Usa jc para convertir la salida de comandos del sistema a JSON
5 + estructurado. Ideal para diagnosticar, auditar o pasar a una IA.
6 +
7 + Uso:
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 +
13 + Dependencias:
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 +
20 + import argparse
21 + import json
22 + import subprocess
23 + import sys
24 + from datetime import datetime
25 +
26 +
27 + # ─── Secciones del reporte ────────────────────────────────────────────────────
28 +
29 + SECCIONES = {
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 +
39 + def 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 +
64 + def 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 +
85 + def main():
86 + parser = argparse.ArgumentParser(
87 + description="sysreport: reporte del sistema en JSON usando jc",
88 + formatter_class=argparse.RawDescriptionHelpFormatter,
89 + epilog="""\
90 + Ejemplos:
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 +
130 + if __name__ == "__main__":
131 + main()
上一页 下一页