guanghu-ice-heart/server-tools/persona-compute-pool/scheduler.py

37 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Deterministic first slice of the Guanghu persona compute-pool scheduler."""
from __future__ import annotations
import argparse, json, uuid
from pathlib import Path
ROOT=Path(__file__).resolve().parents[2]
MAP=ROOT/"routing/guanghu-compute-pool-map.json"
def load_map(): return json.loads(MAP.read_text(encoding="utf-8"))
def select(task: dict, pool: dict|None=None):
pool=pool or load_map(); min_cpu=int(task.get("min_cpu_cores",1)); min_mem=int(task.get("min_memory_mb",256)); kind=task.get("kind","GENERAL")
candidates=[]
for node in pool["nodes"]:
if node["state"] != "ACTIVE_WORKER": continue
if node["cpu_cores"] < min_cpu or node["memory_mb"] < min_mem: continue
if kind == "WINDOWS_BUILD" and "Windows" not in node["os"]: continue
candidates.append(node)
candidates.sort(key=lambda n:(n["cpu_cores"], n["memory_mb"], n["disk_free_gb"]))
if not candidates: return {"outcome":"NO_ELIGIBLE_WORKER","reason":"WORKER_HEARTBEAT_OR_TRANSPORT_NOT_READY","authority_granted":False}
node=candidates[0]
return {"outcome":"LEASE_REQUESTED","lease_id":f"LEASE-{uuid.uuid4()}","node_id":node["node_id"],"ttl_seconds":pool["lease_rules"]["default_ttl_seconds"],"preemptible":pool["lease_rules"]["preemptible"],"authority_granted":False}
def audit(pool=None):
pool=pool or load_map(); errors=[]
if any(n["node_id"]=="JD-FD-PRIMARY" for n in pool["nodes"]): errors.append("MOTHER_NODE_MUST_NOT_BE_WORKER")
if pool.get("controller") != "QY-LH-MAIN-PROD-01": errors.append("CONTROLLER_MISMATCH")
if not all(n["state"] in {"DISCOVERED_OPT_IN_PENDING_AGENT","ACTIVE_WORKER","PAUSED","REVOKED"} for n in pool["nodes"]): errors.append("NODE_STATE_INVALID")
return {"outcome":"PASS" if not errors else "FAIL","errors":errors,"node_count":len(pool["nodes"]),"mother_node_excluded":"JD-FD-PRIMARY" not in [n["node_id"] for n in pool["nodes"]],"authority_granted":False}
def main():
parser=argparse.ArgumentParser(); parser.add_argument("command",choices=["audit","select"]); parser.add_argument("--task",type=Path); args=parser.parse_args()
result=audit() if args.command=="audit" else select(json.loads(args.task.read_text(encoding="utf-8")))
print(json.dumps(result,ensure_ascii=False,indent=2)); return 0 if result["outcome"] in {"PASS","LEASE_REQUESTED","NO_ELIGIBLE_WORKER"} else 2
if __name__=="__main__": raise SystemExit(main())