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-11-08 09:39:12 +00:00
1 parent 8b2686548c
commit 250c511bce
10 files changed
+2364 -11

No files matched your search

+18 -4
View File
@@ -517,6 +517,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;"
@@ -1592,7 +1595,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"
@@ -3742,16 +3745,19 @@ class CloudControlPlaneHandler(OAuthMixin, IdentityMemoryMixin, ProjectMixin, Au
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)。
# 未指定时用 agent 定义的 executor(若有),再由下面
# 的校验与环境继承处理。
executor = body.get("executor")
if executor is None and definition is not None:
executor = definition.defaults.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
@@ -4009,6 +4015,10 @@ class CloudControlPlaneHandler(OAuthMixin, IdentityMemoryMixin, ProjectMixin, Au
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(
@@ -5445,6 +5455,10 @@ class CloudControlPlaneHandler(OAuthMixin, IdentityMemoryMixin, ProjectMixin, Au
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;"