import type { Worker } from "./types"; import { workerAgeText } from "./client"; // CHV 可用性纯逻辑:不发请求、不拼 URL,只从 ctx 已有的 // /workers 数据推断。非管理员的 /workers 只返回聚合数 // (workers_total/online/offline),没有按执行器的分项, // 此时 CHV 在线数是未知,绝不能把未知写成 0,也绝不能 // 把聚合在线数当成 CHV 在线数。 export function isWorkerAlive(w: Worker): boolean { if (w.alive !== undefined && w.alive !== null) return !!w.alive; const info = workerAgeText(w); return info.age !== null && info.age <= 120; } export function countChvWorkers(workers: Worker[]): { online: number; total: number } { const chv = workers.filter((w) => w.executor === "cloud-hypervisor"); return { online: chv.filter(isWorkerAlive).length, total: chv.length }; } export type ChvAvailability = | { state: "known"; online: number; total: number } | { state: "unknown"; reason: "not-loaded" | "aggregate-only" | "load-failed" }; export function chvAvailability( workers: Worker[] | null, workersSummary: string, workersError: string, ): ChvAvailability { if (workers) return { state: "known", ...countChvWorkers(workers) }; if (workersError) return { state: "unknown", reason: "load-failed" }; if (workersSummary.includes("无 worker 注册")) return { state: "known", online: 0, total: 0 }; if (workersSummary.includes("完整 worker 清单仅全局管理员可见")) { return { state: "unknown", reason: "aggregate-only" }; } return { state: "unknown", reason: "not-loaded" }; } // 排队原因提示:只在“已知零 CHV worker 在线 + CHV 任务排队”时 // 给出,文案明确写“可能原因”,且不编造后端未返回的 // VM 状态/IP/快照。未知时返回空串(不提示、不猜测)。 export function chvQueueHint( executor: string | undefined | null, status: string | undefined | null, availability: ChvAvailability, ): string { if (executor !== "cloud-hypervisor" || status !== "queued") return ""; if (availability.state === "known" && availability.online === 0) { return "可能原因:当前无 cloud-hypervisor worker 在线,任务在排队(基于 /workers 推断;VM 状态/IP/快照等后端未返回字段不展示)。请先确认 KVM 宿主上的 CHV worker 已起,worker 上线后可认领。"; } return ""; }