scm-harmonic-cmts-admin/routers/leaf_options.py

114 lines
4.7 KiB
Python
Raw Permalink 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.

# --- routers/leaf_options.py ---
from fastapi import APIRouter, BackgroundTasks, HTTPException, Body
from pydantic import BaseModel
from typing import List
import json, os, asyncio, time, re
# 🌟 引入新的 asyncssh 版本爬蟲
from cmts_scraper import sync_cmts_leaves_async
from shared import CMTS_DEVICE
router = APIRouter(prefix="/api/v1", tags=["Options"])
CACHE_FILE = "cmts_leaf_options_cache.json"
scraper_semaphore = asyncio.Semaphore(1)
class SyncOptionsRequest(BaseModel):
leaf_paths: List[str]
async def run_scraper_task_async(leaf_paths: List[str]):
if scraper_semaphore.locked():
print("⚠️ [Debug] 爬蟲正在執行中,本次請求被忽略")
return
async with scraper_semaphore:
try:
print(f"🚀 [Debug] 背景爬蟲開始啟動,準備抓取: {leaf_paths}")
# 🌟 雙重防護:確保傳給 CMTS 的是空白分隔,並在後端也徹底拔除 [0], [1] 等虛擬索引
cmts_query_paths = []
for p in leaf_paths:
clean_p = p.replace("::", " ")
clean_p = re.sub(r"\s*\[\d+\]", "", clean_p) # 拔除虛擬索引
cmts_query_paths.append(clean_p)
# 去除重複的路徑 (例如 alias [0] 和 alias [1] 淨化後都會變成 alias)
cmts_query_paths = list(dict.fromkeys(cmts_query_paths))
print(f"⏳ [Debug] 正在連線 CMTS 執行 sync_cmts_leaves_async...")
# 🌟 核心修改:直接 await 非同步爬蟲,不再需要 Netmiko 與 to_thread
# 從共用的 CMTS_DEVICE 字典中提取連線資訊
result = await sync_cmts_leaves_async(
host=CMTS_DEVICE.get("host"),
username=CMTS_DEVICE.get("username"),
password=CMTS_DEVICE.get("password"),
leaf_paths=cmts_query_paths
)
print(f"✅ [Debug] CMTS 抓取完成,結果: {result['status']}")
if result["status"] == "success":
cache_data = {}
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, "r", encoding="utf-8") as f:
cache_data = json.load(f)
current_timestamp = int(time.time())
# 存入快取
for cmts_path, data in result["data"].items():
data["updated_at"] = current_timestamp
cache_data[cmts_path] = data
with open(CACHE_FILE, "w", encoding="utf-8") as f:
json.dump(cache_data, f, indent=4, ensure_ascii=False)
print("💾 [Debug] 快取檔案寫入成功!")
else:
error_msg = result.get('message', '沒有提供錯誤訊息')
print(f"❌ [Debug] 抓取失敗CMTS 拒絕了我們!原因: {error_msg}")
except Exception as e:
print(f"💥 [Debug] 背景爬蟲發生嚴重錯誤: {e}")
@router.get("/cmts-leaf-options")
async def get_leaf_options():
if not os.path.exists(CACHE_FILE): return {}
with open(CACHE_FILE, "r", encoding="utf-8") as f:
return json.load(f)
@router.post("/cmts-leaf-options/sync")
async def sync_leaf_options(request: SyncOptionsRequest, background_tasks: BackgroundTasks):
if not request.leaf_paths: raise HTTPException(status_code=400)
background_tasks.add_task(run_scraper_task_async, request.leaf_paths)
return {"status": "processing"}
# 🌟 新增:清除特定選項快取的 API (使用您原本定義的 CACHE_FILE)
@router.post("/clear_cache")
async def clear_specific_cache(paths_to_clear: list = Body(...)):
cleared_count = 0
# 1. 檢查快取檔案是否存在
if not os.path.exists(CACHE_FILE):
return {"status": "success", "cleared_count": 0, "message": "快取檔案不存在"}
try:
# 2. 讀取現有的 JSON 快取資料
with open(CACHE_FILE, "r", encoding="utf-8") as f:
cache_data = json.load(f)
# 3. 逐一比對並刪除指定的路徑
for path in paths_to_clear:
if path in cache_data:
del cache_data[path]
cleared_count += 1
# 4. 如果有刪除資料,將更新後的內容寫回 JSON 檔案
if cleared_count > 0:
with open(CACHE_FILE, "w", encoding="utf-8") as f:
json.dump(cache_data, f, ensure_ascii=False, indent=4)
return {"status": "success", "cleared_count": cleared_count}
except Exception as e:
return {"status": "error", "message": f"清除快取時發生錯誤: {str(e)}"}