sysreport.py
· 4.9 KiB · Python
Исходник
#!/usr/bin/env python3
"""
sysreport.py — Genera un reporte completo del sistema en JSON.
Usa jc para convertir la salida de comandos del sistema a JSON
estructurado. Ideal para diagnosticar, auditar o pasar a una IA.
Uso:
python3 sysreport.py # Guarda en /tmp/sysreport.json
python3 sysreport.py --stdout # Imprime por stdout
python3 sysreport.py --compact # Sin pretty-print
python3 sysreport.py --only discos # Solo una sección
Dependencias:
- Python 3.10+
- jc (pip install jc)
- jq (sudo apt install jq)
- Los comandos del sistema: ps, df, systemctl, ss, free, uptime
"""
import argparse
import json
import subprocess
import sys
from datetime import datetime
# ─── Secciones del reporte ────────────────────────────────────────────────────
SECCIONES = {
"procesos": (["ps", "aux"], "ps"),
"discos": (["df", "-h"], "df"),
"servicios": (["systemctl", "list-units", "--no-legend"], "systemctl"),
"puertos": (["ss", "-tlnp"], "ss"),
"memoria": (["free", "-h"], "free"),
"uptime": (["uptime"], "uptime"),
}
def ejecutar_jc(comando: list, parser: str) -> list | dict:
"""Ejecuta un comando y lo parsea con jc."""
try:
out = subprocess.run(comando, capture_output=True, text=True, timeout=10)
if out.returncode != 0:
return {"error": f"Comando falló: {' '.join(comando)}"}
# Parsear con jc
jc_out = subprocess.run(
["jc", f"--{parser}"],
input=out.stdout,
capture_output=True,
text=True,
timeout=10,
)
if jc_out.returncode != 0:
return {"error": f"jc falló para {parser}: {jc_out.stderr[:200]}"}
return json.loads(jc_out.stdout)
except subprocess.TimeoutExpired:
return {"error": f"Timeout ejecutando {' '.join(comando)}"}
except json.JSONDecodeError as e:
return {"error": f"JSON inválido de jc --{parser}: {e}"}
except FileNotFoundError:
return {"error": f"jc no encontrado. Instala: pip install jc"}
def generar_reporte(secciones: list[str] | None = None) -> dict:
"""Genera el reporte del sistema."""
incluir = secciones if secciones else list(SECCIONES.keys())
reporte = {
"fecha": datetime.now().isoformat(),
"hostname": subprocess.run(
["hostname"], capture_output=True, text=True, timeout=5
).stdout.strip(),
}
for seccion in incluir:
if seccion in SECCIONES:
comando, parser = SECCIONES[seccion]
reporte[seccion] = ejecutar_jc(comando, parser)
return reporte
# ─── Main ─────────────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description="sysreport: reporte del sistema en JSON usando jc",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""\
Ejemplos:
python3 sysreport.py # Guarda en /tmp/sysreport.json
python3 sysreport.py --stdout # Imprime por stdout
python3 sysreport.py --only procesos discos # Solo procesos y discos
python3 sysreport.py --stdout | jq '.discos' # Filtrar con jq
""",
)
parser.add_argument("--stdout", action="store_true", help="Imprimir por stdout en vez de guardar")
parser.add_argument("--compact", action="store_true", help="Sin pretty-print (más pequeño)")
parser.add_argument("--only", nargs="+", choices=list(SECCIONES.keys()),
help=f"Solo estas secciones: {', '.join(SECCIONES.keys())}")
args = parser.parse_args()
print("📡 Generando reporte del sistema...", file=sys.stderr)
reporte = generar_reporte(args.only)
indent = None if args.compact else 2
if args.stdout:
print(json.dumps(reporte, indent=indent, ensure_ascii=False))
else:
output_path = "/tmp/sysreport.json"
with open(output_path, "w") as f:
json.dump(reporte, f, indent=indent, ensure_ascii=False)
print(f"✅ Reporte guardado en {output_path}", file=sys.stderr)
print(f"📦 Tamaño: {len(json.dumps(reporte))} caracteres", file=sys.stderr)
print(f" Secciones: {', '.join(reporte.keys())}", file=sys.stderr)
# Mostrar resumen rápido
errores = [k for k, v in reporte.items() if isinstance(v, dict) and "error" in v]
if errores:
print(f"⚠️ Errores en: {', '.join(errores)}", file=sys.stderr)
# Contar elementos por sección
for k, v in reporte.items():
if isinstance(v, list):
print(f" {k}: {len(v)} elementos", file=sys.stderr)
if __name__ == "__main__":
main()
| 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() |