mirror of
https://github.com/andrey271192/vps_monitoring.git
synced 2026-09-20 11:55:34 +00:00
fix(keenetic): non-blocking refresh-all and longer timeouts
Run refresh-all in background so API stays responsive during 24-router poll. Cap per-device poll at 75s, add 2s gap between routers, bump KeenDNS connect/read timeouts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,8 +22,11 @@ router = APIRouter(prefix="/api/keenetic", tags=["keenetic"])
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
keenetic_metrics: Dict[str, dict] = {}
|
keenetic_metrics: Dict[str, dict] = {}
|
||||||
|
_refresh_all_running = False
|
||||||
|
|
||||||
KEENETIC_FILE = DATA_DIR / "keenetic.json"
|
KEENETIC_FILE = DATA_DIR / "keenetic.json"
|
||||||
|
DEVICE_REFRESH_TIMEOUT = 75
|
||||||
|
REFRESH_ALL_GAP_SEC = 2
|
||||||
|
|
||||||
|
|
||||||
def _load_keenetic():
|
def _load_keenetic():
|
||||||
@@ -73,8 +76,19 @@ async def _refresh_device(dev: dict) -> dict:
|
|||||||
client = _client_for_device(dev)
|
client = _client_for_device(dev)
|
||||||
try:
|
try:
|
||||||
cached = keenetic_metrics.get(name)
|
cached = keenetic_metrics.get(name)
|
||||||
metrics = await client.collect_metrics(cached_info=cached)
|
try:
|
||||||
metrics["last_updated"] = datetime.now().isoformat()
|
metrics = await asyncio.wait_for(
|
||||||
|
client.collect_metrics(cached_info=cached),
|
||||||
|
timeout=DEVICE_REFRESH_TIMEOUT,
|
||||||
|
)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
metrics = {
|
||||||
|
"online": False,
|
||||||
|
"error": "Poll timeout",
|
||||||
|
"last_updated": datetime.now().isoformat(),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
metrics["last_updated"] = datetime.now().isoformat()
|
||||||
keenetic_metrics[name] = metrics
|
keenetic_metrics[name] = metrics
|
||||||
return metrics
|
return metrics
|
||||||
finally:
|
finally:
|
||||||
@@ -283,18 +297,45 @@ async def keenetic_refresh(name: str, request: Request, user: str = Depends(requ
|
|||||||
return {"status": "error", "detail": str(e)}
|
return {"status": "error", "detail": str(e)}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/refresh-all")
|
async def _refresh_all_devices() -> list:
|
||||||
async def keenetic_refresh_all(request: Request, user: str = Depends(require_auth)):
|
global _refresh_all_running
|
||||||
devices = _load_keenetic()
|
devices = _load_keenetic()
|
||||||
results = []
|
results = []
|
||||||
for dev in devices:
|
try:
|
||||||
try:
|
for i, dev in enumerate(devices):
|
||||||
metrics = await _refresh_device(dev)
|
try:
|
||||||
results.append({"name": dev["name"], "online": metrics["online"],
|
metrics = await _refresh_device(dev)
|
||||||
"error": metrics.get("error", "")})
|
results.append({
|
||||||
except Exception as e:
|
"name": dev["name"],
|
||||||
results.append({"name": dev["name"], "online": False, "error": str(e)})
|
"online": metrics["online"],
|
||||||
return {"status": "ok", "results": results}
|
"error": metrics.get("error", ""),
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
results.append({"name": dev["name"], "online": False, "error": str(e)})
|
||||||
|
if i + 1 < len(devices):
|
||||||
|
await asyncio.sleep(REFRESH_ALL_GAP_SEC)
|
||||||
|
finally:
|
||||||
|
_refresh_all_running = False
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/refresh-all")
|
||||||
|
async def keenetic_refresh_all(request: Request, user: str = Depends(require_auth)):
|
||||||
|
global _refresh_all_running
|
||||||
|
if _refresh_all_running:
|
||||||
|
return {"status": "ok", "message": "refresh already running"}
|
||||||
|
|
||||||
|
devices = _load_keenetic()
|
||||||
|
if not devices:
|
||||||
|
return {"status": "ok", "results": []}
|
||||||
|
|
||||||
|
_refresh_all_running = True
|
||||||
|
asyncio.create_task(_refresh_all_devices())
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"message": f"refresh started for {len(devices)} routers",
|
||||||
|
"count": len(devices),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/detail/{name}")
|
@router.get("/detail/{name}")
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def client_timeout_for(host: str) -> aiohttp.ClientTimeout:
|
|||||||
"""Timeouts tuned for KeenDNS vs direct IP."""
|
"""Timeouts tuned for KeenDNS vs direct IP."""
|
||||||
if is_public_ip_host(host):
|
if is_public_ip_host(host):
|
||||||
return aiohttp.ClientTimeout(total=20, connect=8, sock_read=12)
|
return aiohttp.ClientTimeout(total=20, connect=8, sock_read=12)
|
||||||
return aiohttp.ClientTimeout(total=45, connect=15, sock_read=30)
|
return aiohttp.ClientTimeout(total=60, connect=20, sock_read=40)
|
||||||
|
|
||||||
|
|
||||||
def build_api_base_url(host: str, web_url: str = "") -> str:
|
def build_api_base_url(host: str, web_url: str = "") -> str:
|
||||||
@@ -214,7 +214,7 @@ class KeeneticClient:
|
|||||||
)
|
)
|
||||||
await self._reset_session()
|
await self._reset_session()
|
||||||
if attempt + 1 < AUTH_RETRIES:
|
if attempt + 1 < AUTH_RETRIES:
|
||||||
await asyncio.sleep(1.5 * (attempt + 1))
|
await asyncio.sleep(2.0 * (attempt + 1))
|
||||||
continue
|
continue
|
||||||
logger.error(f"Keenetic auth timeout @ {self.base_url}")
|
logger.error(f"Keenetic auth timeout @ {self.base_url}")
|
||||||
return False
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user