Files
awoooi/apps/api/src/services/playbook_seed_service.py
OG T c9efaa3740
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
fix(playbook-seed): 修正 get_db_context import 路徑(db.session → db.base)
seed 啟動時靜默失敗的根因:
  from src.db.session import get_db_context  ← 模組不存在
  from src.db.base import get_db_context     ← 正確路徑

此 bug 導致 yaml_rule playbooks 完全無法建立。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 23:49:56 +08:00

112 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Playbook Seed Service — 從 alert_rules.yaml 初始化 Playbook 資料
=================================================================
職責:
- 啟動時讀取 alert_rules.yaml
- 將每條規則轉換為 APPROVED Playbook 寫入 DB冪等已存在則跳過
- 確保自動修復鏈路有資料可用
呼叫方: main.py lifespan (asyncio.create_task — 非阻塞)
2026-04-10 Claude Sonnet 4.6 Asia/Taipei
"""
from __future__ import annotations
from pathlib import Path
import structlog
import yaml
logger = structlog.get_logger(__name__)
_RULES_PATH = Path(__file__).parent.parent.parent / "alert_rules.yaml"
async def seed_playbooks_from_rules() -> None:
"""從 alert_rules.yaml 匯入 APPROVED Playbook冪等"""
try:
if not _RULES_PATH.exists():
logger.warning("playbook_seed_rules_not_found", path=str(_RULES_PATH))
return
data = yaml.safe_load(_RULES_PATH.read_text())
rules = data.get("rules", [])
if not rules:
return
from src.models.playbook import (
ActionType, Playbook, PlaybookSource, PlaybookStatus,
RepairStep, RiskLevel, SymptomPattern,
)
from src.repositories.playbook_repository import get_playbook_repository
repo = get_playbook_repository()
# 取得現有 YAML_RULE playbook依 name 去重避免重複建立
# 2026-04-15 ogt: 不再用 list_playbooks舊格式 repair_steps 會 validation error
# 改用 raw SQL 只撈 name 欄位,更穩健
from src.db.base import get_db_context
from sqlalchemy import text as sa_text
async with get_db_context() as db:
rows = await db.execute(
sa_text("SELECT name FROM playbooks WHERE source = 'yaml_rule'")
)
existing_names = {r[0] for r in rows.fetchall()}
seeded = 0
for rule in rules:
rule_id = rule.get("id", "")
rule_name = rule.get("description", rule_id)
if rule_name in existing_names:
continue
resp = rule.get("response", {})
kubectl_cmd = resp.get("kubectl_command", "").strip()
if not kubectl_cmd:
continue
risk_str = resp.get("risk", "medium").lower()
risk_map = {"low": RiskLevel.LOW, "medium": RiskLevel.MEDIUM, "critical": RiskLevel.HIGH}
risk = risk_map.get(risk_str, RiskLevel.MEDIUM)
alertnames = rule.get("match", {}).get("alertname", [])
action_type = ActionType.KUBECTL
if kubectl_cmd.startswith("ssh"):
action_type = ActionType.SSH_COMMAND
playbook = Playbook(
name=rule.get("description", rule_id),
description=resp.get("description", rule.get("description", "")),
status=PlaybookStatus.APPROVED,
source=PlaybookSource.YAML_RULE,
symptom_pattern=SymptomPattern(
alert_names=alertnames,
affected_services=[],
severity_range=["P2", "P3"],
),
repair_steps=[
RepairStep(
step_number=1,
action_type=action_type,
command=kubectl_cmd,
expected_result=resp.get("action_title", ""),
risk_level=risk,
requires_approval=False,
)
],
ai_confidence=1.0,
approved_by="alert_rules_yaml",
)
try:
await repo.create(playbook)
seeded += 1
logger.info("playbook_seeded", rule_id=rule_id, name=playbook.name)
except Exception as e:
logger.warning("playbook_seed_failed", rule_id=rule_id, error=str(e))
logger.info("playbook_seed_complete", seeded=seeded, total=len(rules))
except Exception as e:
logger.error("playbook_seed_error", error=str(e))