Fase 0 complete: extract all business logic, auth, database, and 176 endpoints from monolithic app.py into dedicated modules. Structure: - app.py: FastAPI hub (CORS, routers, startup/shutdown) - models.py: 13 Pydantic request models - utils.py: shared utilities (embed status, upload validation, process registries) - config.py: constants, env vars, model catalogs - auth/: crypto, jwt_auth, oidc, rate_limit - database/: db(), init_db(), schema DDL - services/: genai, compliance, chat, terraform, embeddings, cis_reports, mcp - routes/: 13 APIRouter modules (auth, users, oci_config, oci_explorer, genai, mcp, adb, embeddings, reports, chat, terraform, settings, cis_engine) Also: README updated with OCIR auth instructions for manual docker run. 86 tests passing.
81 lines
4.1 KiB
Python
81 lines
4.1 KiB
Python
"""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])
|