Files
awoooi/apps/api/src/services/playbook_seed_service.py
OG T f33d514391
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
fix(auto_repair): playbook_seed_service — 從 alert_rules.yaml 初始化 APPROVED Playbook
根本原因: playbooks 表空 → NO_MATCH → 永遠走審批,從不自動修復
修復: startup 時從 alert_rules.yaml seed APPROVED Playbook(冪等)
確保自動修復鏈路有規則可用

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 21:52:38 +08:00

105 lines
3.7 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()
# 取得現有 playbook source_ids避免重複建立
existing = await repo.list_playbooks(status=PlaybookStatus.APPROVED, limit=500)
existing_sources = {p.source for p in existing if p.source}
seeded = 0
for rule in rules:
rule_id = rule.get("id", "")
source_key = f"alert_rule:{rule_id}"
if source_key in existing_sources:
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=source_key,
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))