guardia-itsm/routers/infra_native.py

246 lines
11 KiB
Python

"""
GUARDiA 클라우드 네이티브 인프라 — Gen6
eBPF 계측·Wasm 엣지·서비스 메시·이벤트 소싱·시크릿 관리·멀티런타임
"""
import uuid
from datetime import datetime
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel
router = APIRouter(prefix="/api/infra", tags=["Cloud Native Infra"])
_ebpf_probes: Dict[str, Dict] = {}
_wasm_modules: Dict[str, Dict] = {}
_mesh_services: Dict[str, Dict] = {}
_events: List[Dict] = []
_secrets: Dict[str, Dict] = {}
_runtimes: Dict[str, Dict] = {}
class EBPFProbe(BaseModel):
name: str; program_type: str = "kprobe" # kprobe|tracepoint|xdp|tc
target: str; filter_expr: str = ""; owner: str = "platform"
class WasmModule(BaseModel):
name: str; wasm_binary_url: str = ""
runtime: str = "wasmtime"; memory_mb: int = 64
env: Dict[str, str] = {}
class MeshService(BaseModel):
service: str; version: str = "v1"
protocol: str = "http2"; mtls: bool = True
circuit_breaker: bool = True; retries: int = 3
class EventCreate(BaseModel):
aggregate_id: str; aggregate_type: str
event_type: str; payload: Dict[str, Any] = {}
correlation_id: Optional[str] = None
class SecretCreate(BaseModel):
name: str; value: str; engine: str = "vault" # vault|k8s|env
rotate_days: int = 90; owner: str = ""
class RuntimeCreate(BaseModel):
name: str; runtime_type: str = "wasmtime" # wasmtime|spin|containerd|gvisor
config: Dict[str, Any] = {}
# ── eBPF 계측 ─────────────────────────────────────────────────────────────
@router.post("/ebpf/probes")
async def create_ebpf_probe(probe: EBPFProbe):
pid = f"EBPF-{uuid.uuid4().hex[:8].upper()}"
_ebpf_probes[pid] = {**probe.model_dump(), "id": pid, "status": "attached",
"created_at": datetime.utcnow().isoformat(), "events_captured": 0}
return _ebpf_probes[pid]
@router.get("/ebpf/probes")
async def list_ebpf_probes():
probes = list(_ebpf_probes.values()) or [
{"id": "EBPF-SYS001", "name": "syscall_monitor", "type": "kprobe", "status": "attached"},
{"id": "EBPF-NET001", "name": "network_flow", "type": "xdp", "status": "attached"},
]
return {"probes": probes, "total": len(probes)}
@router.get("/ebpf/probes/{pid}/metrics")
async def ebpf_probe_metrics(pid: str):
return {"probe_id": pid, "events_per_sec": 1240, "latency_p99_us": 45,
"cpu_overhead_pct": 0.3, "ts": datetime.utcnow().isoformat()}
@router.delete("/ebpf/probes/{pid}")
async def detach_ebpf_probe(pid: str):
_ebpf_probes.pop(pid, None); return {"detached": pid}
@router.get("/ebpf/trace")
async def live_trace(program: str = "syscall", duration_sec: int = 5):
return {"program": program, "duration_sec": duration_sec,
"trace": [
{"ts": datetime.utcnow().isoformat(), "pid": 1234, "comm": "guardia-api", "event": "tcp_connect", "latency_ns": 4500},
{"ts": datetime.utcnow().isoformat(), "pid": 1234, "comm": "guardia-api", "event": "sys_read", "latency_ns": 120},
]}
@router.get("/ebpf/topology")
async def network_topology():
return {"nodes": [
{"id": "guardia-itsm", "type": "service", "ip": "10.0.1.10"},
{"id": "guardia-manager", "type": "service", "ip": "10.0.1.11"},
{"id": "postgres", "type": "database", "ip": "10.0.1.20"},
], "edges": [
{"from": "guardia-itsm", "to": "postgres", "protocol": "tcp", "port": 5432},
{"from": "guardia-manager", "to": "guardia-itsm", "protocol": "tcp", "port": 8001},
], "captured_by": "eBPF XDP"}
# ── Wasm 엣지 모듈 ───────────────────────────────────────────────────────
@router.post("/wasm/modules")
async def deploy_wasm(module: WasmModule):
mid = f"WASM-{uuid.uuid4().hex[:8].upper()}"
_wasm_modules[mid] = {**module.model_dump(), "id": mid, "status": "running",
"deployed_at": datetime.utcnow().isoformat()}
return _wasm_modules[mid]
@router.get("/wasm/modules")
async def list_wasm():
modules = list(_wasm_modules.values()) or [
{"id": "WASM-EDGE01", "name": "request-validator", "runtime": "wasmtime", "status": "running"},
]
return {"modules": modules, "total": len(modules)}
@router.get("/wasm/modules/{mid}/logs")
async def wasm_logs(mid: str, lines: int = 50):
return {"module_id": mid, "logs": [
f"[2026-06-06T00:00:00Z] Module {mid} started",
f"[2026-06-06T00:00:01Z] Processed 1240 requests",
][-lines:]}
@router.post("/wasm/modules/{mid}/invoke")
async def invoke_wasm(mid: str, input: Dict[str, Any] = {}):
m = _wasm_modules.get(mid)
if not m: raise HTTPException(404)
return {"module_id": mid, "input": input, "output": {"result": "ok", "processed": True},
"exec_time_ms": 1.2, "ts": datetime.utcnow().isoformat()}
# ── 서비스 메시 ────────────────────────────────────────────────────────────
@router.post("/mesh/services")
async def register_mesh_service(svc: MeshService):
sid = f"MESH-{uuid.uuid4().hex[:8].upper()}"
_mesh_services[sid] = {**svc.model_dump(), "id": sid, "status": "enrolled",
"enrolled_at": datetime.utcnow().isoformat()}
return _mesh_services[sid]
@router.get("/mesh/services")
async def list_mesh_services():
svcs = list(_mesh_services.values()) or [
{"service": "guardia-itsm", "mtls": True, "status": "enrolled"},
{"service": "guardia-manager", "mtls": True, "status": "enrolled"},
]
return {"services": svcs, "total": len(svcs)}
@router.get("/mesh/traffic")
async def mesh_traffic():
return {"services": [
{"from": "guardia-manager", "to": "guardia-itsm", "rps": 142, "error_rate": 0.1, "p99_ms": 45},
{"from": "guardia-itsm", "to": "postgres", "rps": 520, "error_rate": 0.0, "p99_ms": 12},
]}
@router.get("/mesh/policies")
async def mesh_policies():
return {"policies": [
{"type": "circuit_breaker", "service": "guardia-itsm", "threshold": 50, "window_sec": 10},
{"type": "retry", "service": "guardia-manager", "max_attempts": 3, "backoff_ms": 100},
]}
@router.post("/mesh/policies")
async def create_mesh_policy(service: str, policy_type: str, rules: Dict[str, Any] = {}):
return {"id": f"POL-{uuid.uuid4().hex[:8].upper()}", "service": service,
"type": policy_type, "rules": rules, "applied": True,
"ts": datetime.utcnow().isoformat()}
# ── 이벤트 소싱 ────────────────────────────────────────────────────────────
@router.post("/events/publish")
async def publish_event(event: EventCreate):
eid = f"EVT-{uuid.uuid4().hex[:8].upper()}"
record = {**event.model_dump(), "id": eid, "sequence": len(_events) + 1,
"published_at": datetime.utcnow().isoformat()}
_events.append(record)
return record
@router.get("/events/stream")
async def get_event_stream(aggregate_id: Optional[str] = None,
event_type: Optional[str] = None, limit: int = 100):
evts = _events
if aggregate_id: evts = [e for e in evts if e["aggregate_id"] == aggregate_id]
if event_type: evts = [e for e in evts if e["event_type"] == event_type]
return {"events": evts[-limit:], "total": len(evts)}
@router.get("/events/replay/{aggregate_id}")
async def replay_events(aggregate_id: str, from_sequence: int = 0):
evts = [e for e in _events if e["aggregate_id"] == aggregate_id
and e.get("sequence", 0) >= from_sequence]
return {"aggregate_id": aggregate_id, "events": evts, "replayed": len(evts)}
@router.get("/events/projections")
async def list_projections():
return {"projections": [
{"name": "sr-read-model", "last_event": len(_events), "status": "up-to-date"},
{"name": "server-state", "last_event": len(_events), "status": "up-to-date"},
]}
# ── 시크릿 관리 ────────────────────────────────────────────────────────────
@router.post("/secrets")
async def create_secret(secret: SecretCreate):
sid = f"SEC-{uuid.uuid4().hex[:8].upper()}"
_secrets[sid] = {"id": sid, "name": secret.name, "engine": secret.engine,
"rotate_days": secret.rotate_days, "owner": secret.owner,
"value": "***ENCRYPTED***",
"created_at": datetime.utcnow().isoformat()}
return {k: v for k, v in _secrets[sid].items() if k != "value"}
@router.get("/secrets")
async def list_secrets():
return {"secrets": [{k: v for k, v in s.items() if k != "value"}
for s in _secrets.values()]}
@router.post("/secrets/{name}/rotate")
async def rotate_secret(name: str):
return {"name": name, "rotated": True, "new_version": f"v{uuid.uuid4().hex[:4]}",
"ts": datetime.utcnow().isoformat()}
@router.get("/secrets/{name}/audit")
async def secret_audit(name: str):
return {"name": name, "access_log": [
{"user": "guardia-itsm", "action": "read", "ts": datetime.utcnow().isoformat()},
], "rotation_history": [{"version": "v1", "ts": datetime.utcnow().isoformat()}]}
# ── 멀티 런타임 관리 ──────────────────────────────────────────────────────
@router.post("/runtimes")
async def create_runtime(rt: RuntimeCreate):
rid = f"RT-{uuid.uuid4().hex[:8].upper()}"
_runtimes[rid] = {**rt.model_dump(), "id": rid, "status": "ready",
"created_at": datetime.utcnow().isoformat()}
return _runtimes[rid]
@router.get("/runtimes")
async def list_runtimes():
rts = list(_runtimes.values()) or [
{"id": "RT-WASM01", "name": "wasmtime-edge", "type": "wasmtime", "status": "ready"},
{"id": "RT-CONT01", "name": "containerd-shim", "type": "containerd", "status": "ready"},
]
return {"runtimes": rts, "total": len(rts)}
@router.get("/runtimes/{rid}/stats")
async def runtime_stats(rid: str):
return {"runtime_id": rid, "cpu_cores": 4, "memory_used_mb": 512,
"modules_running": len(_wasm_modules), "uptime_sec": 86400,
"ts": datetime.utcnow().isoformat()}
# ── 클라우드 네이티브 상태 ────────────────────────────────────────────────
@router.get("/native/health")
async def native_health():
return {"status": "healthy", "ebpf_probes": len(_ebpf_probes),
"wasm_modules": len(_wasm_modules), "mesh_services": len(_mesh_services),
"events_stored": len(_events), "secrets": len(_secrets), "runtimes": len(_runtimes)}
@router.get("/native/overview")
async def native_overview():
return {"gen": 6, "capabilities": ["eBPF", "Wasm Edge", "Service Mesh", "Event Sourcing",
"Secret Manager", "Multi-Runtime"],
"maturity": "production", "last_updated": datetime.utcnow().isoformat()}