"""CIS report execution — subprocess management.""" import os, json, uuid, asyncio from pathlib import Path from datetime import datetime from config import OCI_DIR, REPORTS, log from database import db from auth.jwt_auth import _audit, _config_log from utils import running_reports, classify_report_file async def _exec_report(rid, cfg, regions, level=2, obp=False, raw=False, redact_output=False): rdir = REPORTS / rid; rdir.mkdir(parents=True, exist_ok=True) config_path = str(OCI_DIR / cfg["id"] / "config") try: cmd = ["python3", "-u", "/app/cis_reports.py", "-c", config_path, "--report-directory", str(rdir), "--level", str(level), "--print-to-screen", "True", "--report-summary-json"] if regions: cmd += ["--regions", ",".join(regions)] if obp: cmd.append("--obp") if raw: cmd.append("--raw") if redact_output: cmd.append("--redact-output") proc = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) running_reports[rid] = proc with db() as c: c.execute("UPDATE reports SET worker_pid=? WHERE id=?", (proc.pid, rid)) progress_lines = [] try: while True: line = await proc.stdout.readline() if not line: break text = line.decode(errors="replace").strip() if text: progress_lines.append(text) with db() as c: c.execute("UPDATE reports SET progress=? WHERE id=?", ("\n".join(progress_lines[-50:]), rid)) await proc.wait() finally: running_reports.pop(rid, None) stderr_data = await proc.stderr.read() # Check if cancelled with db() as c: cur_status = c.execute("SELECT status FROM reports WHERE id=?", (rid,)).fetchone() if cur_status and cur_status["status"] == "cancelled": return if proc.returncode == 0: # Scan output directory for all generated files html_path = None; json_path = None with db() as c: for fpath in rdir.iterdir(): if fpath.is_file(): fname = fpath.name ftype = fpath.suffix.lstrip(".") category = classify_report_file(fname) fsize = fpath.stat().st_size c.execute("INSERT INTO report_files (id,report_id,file_name,file_path,file_type,file_category,file_size) VALUES (?,?,?,?,?,?,?)", (str(uuid.uuid4()), rid, fname, str(fpath), ftype, category, fsize)) if "summary_report" in fname and fname.endswith(".html"): html_path = str(fpath) elif "summary_report" in fname and fname.endswith(".json"): json_path = str(fpath) c.execute("UPDATE reports SET status='completed',progress=?,html_path=?,json_path=?,completed_at=datetime('now') WHERE id=?", ("\n".join(progress_lines), html_path, json_path, rid)) _config_log("oci", cfg["id"], cfg["tenancy_name"], "success", "report", f"Report completed: {rid}") else: err = (stderr_data.decode(errors="replace") if stderr_data else "Unknown")[:2000] with db() as c: c.execute("UPDATE reports SET status='failed',progress=?,error_msg=?,completed_at=datetime('now') WHERE id=?", ("\n".join(progress_lines), err, rid)) _config_log("oci", cfg["id"], cfg["tenancy_name"], "error", "report", err) except Exception as e: running_reports.pop(rid, None) with db() as c: c.execute("UPDATE reports SET status='failed',error_msg=?,completed_at=datetime('now') WHERE id=?", (str(e)[:2000], rid)) _config_log("oci", cfg["id"], cfg["tenancy_name"], "error", "report", str(e)[:2000])