Files
codex-app-server/cloud/chv/api_client.py
T
host fde7bf4c3e feat: CHV 执行器优化(拆模块/去重复/SSH 钉死/池持久化/中途死亡分类)
- 拆模块:CHV 从 cloud/sandbox.py 巨类拆到 cloud/chv/(executor/
  pool/config/api_client/ssh_transport/disk/errors),sandbox 只留
  兼容 re-export,旧导入路径与新路径是同一对象
- 去重复:goal 解析、host 顶层参数、runner 超时抽成共享 helper,
  Local 与 CHV 在矩阵输入下结论全等(Docker goal-resume 变体
  有意保留内联版,见 PR 描述)
- 安全:SSH host key 按 VM 钉死(state_dir/<vm>/known_hosts,
  ssh-keyscan 首次捕获 TOFU),废除 no + /dev/null;CHV_SSH_USER
  空串与 CHV_SSH_PORT 越界直接拒绝,不静默 fallback
- execute 语义:只读已运行 VM,未运行报 vm_not_running,不为
  status 起 VM
- 预热池:registry 落盘、重启载入只当孤儿、reconcile 清理、
  损坏 quarantine;条目仍一次性,绝不复用脏 VM
- 长任务:VM 中途死亡判 interrupted(断点可续跑);中途心跳
  经评估不做(无客内进度信号可采)
- #215 未合并:只列复用清单,不复制模块

测试:原 42 项 + 新 24 项全过;相关旧套件与 cloud 全量 1496 项
全过(skipped=4)。验证仍是 mock + 干跑 + 单元测试,未做真机、
压测、安全审计;堆叠于 #216 之上,base 为 feat-cloud-hypervisor。
2026-10-30 19:45:33 +00:00

75 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Cloud Hypervisor REST API 传输(叶子模块,不依赖
cloud.sandbox,避免循环导入)。
只用标准库 raw HTTP over Unix socket,无第三方依赖。
"""
import json
import os
import socket
def _unix_socket_api(socket_path, method, path, payload=None):
"""Cloud Hypervisor REST API 的默认传输:标准库 raw HTTP
over Unix socket(无第三方依赖)。返回 (status, body),
body 能解析 JSON 时为 dict,否则为原文本。socket 不存在
或连接失败时 raise RuntimeError(调用方据此判 api 超时
/失败,不编造 200)。"""
if not os.path.exists(socket_path):
raise RuntimeError("api socket 不存在:%s" % socket_path)
body = b""
if payload is not None:
body = json.dumps(payload).encode("utf-8")
req_lines = ["%s /api/v1%s HTTP/1.1" % (method, path),
"Host: localhost",
"Accept: application/json",
"Content-Type: application/json",
"Content-Length: %d" % len(body),
"Connection: close", "", ""]
raw = "\r\n".join(req_lines).encode("utf-8") + body
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(5)
try:
sock.connect(socket_path)
sock.sendall(raw)
chunks = []
while True:
try:
data = sock.recv(65536)
except socket.timeout:
break
if not data:
break
chunks.append(data)
finally:
sock.close()
text = b"".join(chunks).decode("utf-8", errors="replace")
status = 0
if text.startswith("HTTP/"):
try:
status = int(text.split()[1])
except (IndexError, ValueError):
status = 0
body_text = text.split("\r\n\r\n", 1)[1] if "\r\n\r\n" in text \
else ""
try:
parsed = json.loads(body_text) if body_text.strip() else {}
except ValueError:
parsed = body_text
return status, parsed
def normalize_api_result(result):
"""API 调用归一化:fake api_client 可返回 (status, body)
或 {"status":..., "body":...} 或带 .status 的对象,本
函数统一成 (status:int, body)。"""
if isinstance(result, tuple) and len(result) == 2:
return int(result[0]), result[1]
if isinstance(result, dict):
status = result.get("status", result.get("status_code"))
body = result.get("body", result)
return (int(status) if status is not None else 0), body
status = getattr(result, "status", None) \
or getattr(result, "status_code", None)
return (int(status) if status is not None else 0), result