mirror of
https://github.com/hansjone/netx.git
synced 2026-10-19 05:33:17 +08:00
UME: system time for alarm time_created, 48h inventory auto-sync, runtime pause/resume
- formatSystemTime for current alarm time_created\n- Background thread inventory_auto_sync (NETX_UME_SYNC_INVENTORY_AUTO_ENABLED, NETX_UME_SYNC_INVENTORY_EVERY_HOURS default 48)\n- Pause/resume for token_keepalive, alarms_current_auto_sync, inventory_auto_sync via POST /v1/ume/runtime/tasks/{task}/pause|resume\n- UmePage后台任务列操作 暂停/开始
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
edad7b24fa
commit
1741965de6
4 changed files with 129 additions and 5 deletions
|
|
@ -42,12 +42,13 @@ class Settings(BaseSettings):
|
||||||
ume_keepalive_renew_before_s: int = 900
|
ume_keepalive_renew_before_s: int = 900
|
||||||
ume_sync_alarms_current_enabled: bool = True
|
ume_sync_alarms_current_enabled: bool = True
|
||||||
ume_sync_alarms_current_interval_s: int = 300
|
ume_sync_alarms_current_interval_s: int = 300
|
||||||
|
ume_sync_inventory_auto_enabled: bool = True
|
||||||
|
ume_sync_inventory_every_hours: int = 48
|
||||||
ume_token_path: str = "/restconf/operations/zte-security:oauth_token"
|
ume_token_path: str = "/restconf/operations/zte-security:oauth_token"
|
||||||
ume_token_handshake_path: str = "/restconf/operations/zte-security:oauth_handshake"
|
ume_token_handshake_path: str = "/restconf/operations/zte-security:oauth_handshake"
|
||||||
ume_token_logout_path: str = "/restconf/operations/zte-security:oauth_token"
|
ume_token_logout_path: str = "/restconf/operations/zte-security:oauth_token"
|
||||||
ume_ne_path: str = "/restconf/data/zte-resources-module:network-elements"
|
ume_ne_path: str = "/restconf/data/zte-resources-module:network-elements"
|
||||||
ume_alarms_path: str = "/restconf/data/zte-alarms:alarms/alarm-list"
|
ume_alarms_path: str = "/restconf/data/zte-alarms:alarms/alarm-list"
|
||||||
ume_sync_inventory_every_hours: int = 24
|
|
||||||
ume_sync_alarms_history_every_hours: int = 24
|
ume_sync_alarms_history_every_hours: int = 24
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,10 @@ _SQL_FORBIDDEN_RE = re.compile(
|
||||||
_UME_RUNTIME_TASKS: dict[str, dict[str, Any]] = {
|
_UME_RUNTIME_TASKS: dict[str, dict[str, Any]] = {
|
||||||
"token_keepalive": {"task": "token_keepalive", "status": "init", "last_run_at": None, "last_error": ""},
|
"token_keepalive": {"task": "token_keepalive", "status": "init", "last_run_at": None, "last_error": ""},
|
||||||
"alarms_current_auto_sync": {"task": "alarms_current_auto_sync", "status": "init", "last_run_at": None, "last_error": ""},
|
"alarms_current_auto_sync": {"task": "alarms_current_auto_sync", "status": "init", "last_run_at": None, "last_error": ""},
|
||||||
|
"inventory_auto_sync": {"task": "inventory_auto_sync", "status": "init", "last_run_at": None, "last_error": ""},
|
||||||
}
|
}
|
||||||
|
_UME_RUNTIME_PAUSED: dict[str, bool] = {}
|
||||||
|
UME_KNOWN_RUNTIME_TASKS: tuple[str, ...] = tuple(_UME_RUNTIME_TASKS.keys())
|
||||||
_UME_RUNTIME_LOCK = threading.Lock()
|
_UME_RUNTIME_LOCK = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,15 +87,38 @@ def _set_runtime_task(task: str, *, status: str, last_run_at: datetime | None =
|
||||||
_UME_RUNTIME_TASKS[task] = item
|
_UME_RUNTIME_TASKS[task] = item
|
||||||
|
|
||||||
|
|
||||||
|
def _runtime_is_paused(task: str) -> bool:
|
||||||
|
with _UME_RUNTIME_LOCK:
|
||||||
|
return bool(_UME_RUNTIME_PAUSED.get(str(task or "").strip()))
|
||||||
|
|
||||||
|
|
||||||
|
def _runtime_pause_task(task: str) -> None:
|
||||||
|
tid = str(task or "").strip()
|
||||||
|
with _UME_RUNTIME_LOCK:
|
||||||
|
if tid not in _UME_RUNTIME_TASKS:
|
||||||
|
raise KeyError(tid)
|
||||||
|
_UME_RUNTIME_PAUSED[tid] = True
|
||||||
|
|
||||||
|
|
||||||
|
def _runtime_resume_task(task: str) -> None:
|
||||||
|
tid = str(task or "").strip()
|
||||||
|
with _UME_RUNTIME_LOCK:
|
||||||
|
_UME_RUNTIME_PAUSED[tid] = False
|
||||||
|
|
||||||
|
|
||||||
def _list_runtime_tasks() -> list[dict[str, Any]]:
|
def _list_runtime_tasks() -> list[dict[str, Any]]:
|
||||||
with _UME_RUNTIME_LOCK:
|
with _UME_RUNTIME_LOCK:
|
||||||
out: list[dict[str, Any]] = []
|
out: list[dict[str, Any]] = []
|
||||||
for v in _UME_RUNTIME_TASKS.values():
|
for v in _UME_RUNTIME_TASKS.values():
|
||||||
|
task_id = str(v.get("task") or "")
|
||||||
|
paused = bool(_UME_RUNTIME_PAUSED.get(task_id))
|
||||||
|
eff_status = "paused" if paused else str(v.get("status") or "unknown")
|
||||||
ts = _ensure_utc(v.get("last_run_at")) if isinstance(v.get("last_run_at"), datetime) else None
|
ts = _ensure_utc(v.get("last_run_at")) if isinstance(v.get("last_run_at"), datetime) else None
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
"task": str(v.get("task") or ""),
|
"task": task_id,
|
||||||
"status": str(v.get("status") or "unknown"),
|
"status": eff_status,
|
||||||
|
"paused": paused,
|
||||||
"last_run_at": ts.isoformat() if ts else None,
|
"last_run_at": ts.isoformat() if ts else None,
|
||||||
"last_error": str(v.get("last_error") or ""),
|
"last_error": str(v.get("last_error") or ""),
|
||||||
}
|
}
|
||||||
|
|
@ -353,6 +379,9 @@ def on_startup() -> None:
|
||||||
# Best-effort keepalive: if token exists, periodically handshake to extend TTL.
|
# Best-effort keepalive: if token exists, periodically handshake to extend TTL.
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
if _runtime_is_paused("token_keepalive"):
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
client = _ume_client()
|
client = _ume_client()
|
||||||
st = client.token_status()
|
st = client.token_status()
|
||||||
expires_in = int(st.get("expires_in_s") or 0)
|
expires_in = int(st.get("expires_in_s") or 0)
|
||||||
|
|
@ -375,6 +404,9 @@ def on_startup() -> None:
|
||||||
def _alarms_current_sync_loop() -> None:
|
def _alarms_current_sync_loop() -> None:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
if _runtime_is_paused("alarms_current_auto_sync"):
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
client = _ume_client()
|
client = _ume_client()
|
||||||
|
|
@ -400,6 +432,43 @@ def on_startup() -> None:
|
||||||
t2.start()
|
t2.start()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
try:
|
||||||
|
if bool(getattr(settings, "ume_sync_inventory_auto_enabled", True)):
|
||||||
|
hours = int(getattr(settings, "ume_sync_inventory_every_hours", 48) or 48)
|
||||||
|
hours = max(1, min(hours, 168))
|
||||||
|
interval_s = int(hours * 3600)
|
||||||
|
|
||||||
|
def _inventory_auto_sync_loop() -> None:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
if _runtime_is_paused("inventory_auto_sync"):
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
client = _ume_client()
|
||||||
|
sync_inventory_full(db, client, trigger_mode="schedule")
|
||||||
|
_set_runtime_task(
|
||||||
|
"inventory_auto_sync",
|
||||||
|
status="running",
|
||||||
|
last_run_at=datetime.now(timezone.utc),
|
||||||
|
last_error="",
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
except Exception as exc:
|
||||||
|
_set_runtime_task(
|
||||||
|
"inventory_auto_sync",
|
||||||
|
status="error",
|
||||||
|
last_run_at=datetime.now(timezone.utc),
|
||||||
|
last_error=str(exc)[:240],
|
||||||
|
)
|
||||||
|
time.sleep(interval_s)
|
||||||
|
|
||||||
|
t3 = threading.Thread(target=_inventory_auto_sync_loop, name="ume-inventory-auto-sync", daemon=True)
|
||||||
|
t3.start()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
|
|
@ -564,6 +633,26 @@ def ume_sync_status(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/v1/ume/runtime/tasks/{task}/pause")
|
||||||
|
def ume_runtime_task_pause(task: str) -> dict[str, Any]:
|
||||||
|
tid = str(task or "").strip()
|
||||||
|
if tid not in UME_KNOWN_RUNTIME_TASKS:
|
||||||
|
raise HTTPException(status_code=404, detail="unknown_runtime_task")
|
||||||
|
_runtime_pause_task(tid)
|
||||||
|
_set_runtime_task(tid, status="paused", last_error="")
|
||||||
|
return {"ok": True, "task": tid, "runtime_tasks": _list_runtime_tasks()}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/v1/ume/runtime/tasks/{task}/resume")
|
||||||
|
def ume_runtime_task_resume(task: str) -> dict[str, Any]:
|
||||||
|
tid = str(task or "").strip()
|
||||||
|
if tid not in UME_KNOWN_RUNTIME_TASKS:
|
||||||
|
raise HTTPException(status_code=404, detail="unknown_runtime_task")
|
||||||
|
_runtime_resume_task(tid)
|
||||||
|
_set_runtime_task(tid, status="running", last_error="")
|
||||||
|
return {"ok": True, "task": tid, "runtime_tasks": _list_runtime_tasks()}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/v1/ume/inventory/ne")
|
@app.get("/v1/ume/inventory/ne")
|
||||||
def ume_list_ne(
|
def ume_list_ne(
|
||||||
keyword: str | None = Query(default=None),
|
keyword: str | None = Query(default=None),
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,17 @@ export function UmePage() {
|
||||||
const runningTasks = (syncStatusQuery.data?.items || []).filter((x) => String(x.status || "").toLowerCase() === "running");
|
const runningTasks = (syncStatusQuery.data?.items || []).filter((x) => String(x.status || "").toLowerCase() === "running");
|
||||||
const runtimeTasks = syncStatusQuery.data?.runtime_tasks || [];
|
const runtimeTasks = syncStatusQuery.data?.runtime_tasks || [];
|
||||||
|
|
||||||
|
const runtimeTaskMutation = useMutation({
|
||||||
|
mutationFn: async (vars: { task: string; action: "pause" | "resume" }) =>
|
||||||
|
apiPost<{ ok: boolean }>(
|
||||||
|
`/v1/ume/runtime/tasks/${encodeURIComponent(vars.task)}/${vars.action}`,
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
onSuccess: async () => {
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ["umeSyncStatus"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="cards">
|
<section className="cards">
|
||||||
|
|
@ -205,6 +216,7 @@ export function UmePage() {
|
||||||
<th>status</th>
|
<th>status</th>
|
||||||
<th>last_run_at</th>
|
<th>last_run_at</th>
|
||||||
<th>last_error</th>
|
<th>last_error</th>
|
||||||
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -214,11 +226,32 @@ export function UmePage() {
|
||||||
<td>{x.status}</td>
|
<td>{x.status}</td>
|
||||||
<td>{x.last_run_at ? formatSystemTime(x.last_run_at) : "-"}</td>
|
<td>{x.last_run_at ? formatSystemTime(x.last_run_at) : "-"}</td>
|
||||||
<td>{x.last_error || "-"}</td>
|
<td>{x.last_error || "-"}</td>
|
||||||
|
<td>
|
||||||
|
{Boolean(x.paused) ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="link-btn"
|
||||||
|
disabled={runtimeTaskMutation.isPending}
|
||||||
|
onClick={() => runtimeTaskMutation.mutate({ task: x.task, action: "resume" })}
|
||||||
|
>
|
||||||
|
开始
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="link-btn"
|
||||||
|
disabled={runtimeTaskMutation.isPending}
|
||||||
|
onClick={() => runtimeTaskMutation.mutate({ task: x.task, action: "pause" })}
|
||||||
|
>
|
||||||
|
暂停
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
{!syncStatusQuery.isLoading && runtimeTasks.length === 0 && (
|
{!syncStatusQuery.isLoading && runtimeTasks.length === 0 && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={4}>暂无后台任务状态</td>
|
<td colSpan={5}>暂无后台任务状态</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
@ -462,7 +495,7 @@ export function UmePage() {
|
||||||
<tbody>
|
<tbody>
|
||||||
{(currentQuery.data?.items || []).map((x) => (
|
{(currentQuery.data?.items || []).map((x) => (
|
||||||
<tr key={x.alarm_key}>
|
<tr key={x.alarm_key}>
|
||||||
<td>{x.time_created}</td>
|
<td>{formatSystemTime(x.time_created)}</td>
|
||||||
<td>{x.perceived_severity}</td>
|
<td>{x.perceived_severity}</td>
|
||||||
<td>{x.ne_id}</td>
|
<td>{x.ne_id}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ export type UmeSyncStatusResponse = {
|
||||||
runtime_tasks?: Array<{
|
runtime_tasks?: Array<{
|
||||||
task: string;
|
task: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
paused?: boolean;
|
||||||
last_run_at?: string | null;
|
last_run_at?: string | null;
|
||||||
last_error?: string;
|
last_error?: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue