feat: Cloud Hypervisor 执行器生产级 v1(cloud-hypervisor)

单任务单 VM:API 唤醒(preflight→vmm.ping→vm.create→vm.boot→SSH 就绪)、
暂停/快照/休眠等价/关闭/回收、预热快照池、每任务磁盘 copy、容量与磁盘配额门、
受控 egress fail-closed、VM 事件审计与分类失败;worker/server/autoscaler 接线
(executor=cloud-hypervisor,任务内 LLM 不可自选);默认关闭,不进生产路由。
未做真实 VM 验证:生产主机与本 VM 均无 /dev/kvm、无嵌套虚拟化,验证为 mock +
干跑命令 + 单元测试;真 VM、快照恢复、压测、安全审计见
docs/chv-production-readiness.md。CI 账单故障仍是硬门禁,本提交只到 PR OPEN。
This commit is contained in:
host committed 2026-10-30 18:54:16 +00:00
1 parent aebc6238a6
commit c371bbdf1e
9 files changed
+2364 -11

No files matched your search

+22 -6
View File
@@ -494,6 +494,9 @@ def _task_worktree_for_files(server, task):
"""
if task.get("executor") == "cloud":
return None, (409, "cloud task worktrees are not on this host")
if task.get("executor") == "cloud-hypervisor":
return None, (409, "cloud-hypervisor task worktrees are "
"inside the VM, not on this host")
worktree_host = task.get("worktree_host")
if worktree_host and worktree_host != socket.gethostname():
return None, (409, "task worktree is on host %s;"
@@ -1550,7 +1553,7 @@ def _default_executor():
raw = raw.strip()
if raw == "":
return None
if raw in ("local", "docker", "cloud"):
if raw in ("local", "docker", "cloud", "cloud-hypervisor"):
return raw
return "docker"
@@ -2492,9 +2495,11 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
self._send_json(400, {"error": "name too long"})
return
executor = body.get("executor")
if executor is not None and executor not in ("local", "docker"):
if executor is not None and executor not in (
"local", "docker", "cloud-hypervisor"):
self._send_json(400, {
"error": "executor must be local or docker"})
"error": "executor must be local, docker or "
"cloud-hypervisor"})
return
try:
branch = _validate_env_branch(body.get("branch"))
@@ -4248,12 +4253,15 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
self._send_json(400, {"error": "priority must be an integer"})
return
# executor 可选:None 表示任意 worker 可认领;指定时只能是
# local/docker/cloud,非法值 400 且不建任务。
# local/docker/cloud/cloud-hypervisor,非法值 400 且不建任务。
# cloud-hypervisor 是创建层可见的调度选择(任务内 LLM
# 绝不能自选它,见 sandbox.py 该类 docstring)。
executor = body.get("executor")
if executor is not None and executor not in (
"local", "docker", "cloud"):
"local", "docker", "cloud", "cloud-hypervisor"):
self._send_json(400, {
"error": "executor must be local, docker or cloud"})
"error": "executor must be local, docker, cloud or "
"cloud-hypervisor"})
return
# 任务级选项:model 不做白名单(中转目录会变,下拉只是
# 建议值);effort 必须是 codex 的 effort 档位;approval
@@ -4481,6 +4489,10 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
self._send_json(400, {
"error": "附件不支持 cloud 执行器"})
return
if executor == "cloud-hypervisor":
self._send_json(400, {
"error": "附件不支持 cloud-hypervisor 执行器"})
return
conn0 = self._db()
try:
brow = conn0.execute(
@@ -5893,6 +5905,10 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
if executor == "cloud":
return (409, "thread ops are local-executor only;"
" cloud task threads are not on this host")
if executor == "cloud-hypervisor":
return (409, "thread ops are local-executor only;"
" cloud-hypervisor task threads live inside"
" the VM, not on this host")
worktree_host = task.get("worktree_host")
if worktree_host and worktree_host != socket.gethostname():
return (409, "task worktree is on host %s;"