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:
1 parent
aebc6238a6
commit
c371bbdf1e
9 files changed
+2364
-11
No files matched your search
+22
-6
@@ -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;"
|
||||
|
||||
Reference in new issue
Block a user