dev_1.0.0_yuyr_2:重新提交 PR,增加 master/agent 以及系统集成测试 (#17)
Reviewed-on: #17 Reviewed-by: sundapeng <sundp@mail.zgclab.edu.cn> Reviewed-by: xuxt <xuxt@zgclab.edu.cn>
This commit was merged in pull request #17.
This commit is contained in:
90
src/master/app/scheduler.py
Normal file
90
src/master/app/scheduler.py
Normal file
@@ -0,0 +1,90 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import threading
|
||||
from typing import Optional
|
||||
|
||||
from .config import AppConfig
|
||||
from .storage import Storage
|
||||
from .util import atomic_write_json, parse_iso, to_iso, utcnow
|
||||
|
||||
|
||||
class StatusScheduler:
|
||||
def __init__(self, storage: Storage, config: AppConfig, logger: Optional[logging.Logger] = None) -> None:
|
||||
self._storage = storage
|
||||
self._config = config
|
||||
self._logger = logger or logging.getLogger("argus.master.scheduler")
|
||||
self._stop_event = threading.Event()
|
||||
self._thread = threading.Thread(target=self._run, name="status-scheduler", daemon=True)
|
||||
self._nodes_json_lock = threading.Lock()
|
||||
self._pending_nodes_json = threading.Event()
|
||||
|
||||
def start(self) -> None:
|
||||
"""启动后台线程,定期刷新节点状态与 nodes.json。"""
|
||||
if not self._thread.is_alive():
|
||||
self._logger.info("Starting scheduler thread")
|
||||
self._thread.start()
|
||||
|
||||
def stop(self) -> None:
|
||||
self._stop_event.set()
|
||||
self._pending_nodes_json.set()
|
||||
self._thread.join(timeout=5)
|
||||
|
||||
def trigger_nodes_json_refresh(self) -> None:
|
||||
self._pending_nodes_json.set()
|
||||
|
||||
def generate_nodes_json(self) -> None:
|
||||
with self._nodes_json_lock:
|
||||
online_nodes = self._storage.get_online_nodes()
|
||||
atomic_write_json(self._config.metric_nodes_json_path, online_nodes)
|
||||
self._logger.info("nodes.json updated", extra={"count": len(online_nodes)})
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# internal loop
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _run(self) -> None:
|
||||
# 确保启动时 nodes.json 会立即生成
|
||||
self._pending_nodes_json.set()
|
||||
while not self._stop_event.is_set():
|
||||
changed = self._reconcile_statuses()
|
||||
if changed or self._pending_nodes_json.is_set():
|
||||
try:
|
||||
self.generate_nodes_json()
|
||||
finally:
|
||||
self._pending_nodes_json.clear()
|
||||
self._stop_event.wait(self._config.scheduler_interval_seconds)
|
||||
|
||||
def _reconcile_statuses(self) -> bool:
|
||||
"""根据 last_report 与当前时间对比,决定是否切换状态。"""
|
||||
any_status_changed = False
|
||||
now = utcnow()
|
||||
rows = self._storage.fetch_nodes_for_scheduler()
|
||||
for row in rows:
|
||||
node_id = row["id"]
|
||||
last_report_iso = row["last_report"]
|
||||
current_status = row["status"]
|
||||
last_report_dt = parse_iso(last_report_iso)
|
||||
if last_report_dt is None:
|
||||
# No report yet; treat as initialized until report arrives
|
||||
continue
|
||||
delta_seconds = (now - last_report_dt).total_seconds()
|
||||
new_status = current_status
|
||||
if delta_seconds > self._config.offline_threshold_seconds:
|
||||
new_status = "offline"
|
||||
elif delta_seconds <= self._config.online_threshold_seconds:
|
||||
new_status = "online"
|
||||
# Between thresholds: keep current status (sticky)
|
||||
if new_status != current_status:
|
||||
any_status_changed = True
|
||||
self._logger.info(
|
||||
"Updating node status",
|
||||
extra={
|
||||
"node_id": node_id,
|
||||
"previous": current_status,
|
||||
"new": new_status,
|
||||
"delta_seconds": delta_seconds,
|
||||
},
|
||||
)
|
||||
self._storage.update_status(node_id, new_status, last_updated_iso=to_iso(now))
|
||||
return any_status_changed
|
||||
Reference in New Issue
Block a user