P0:seed 磁盘 CIDATA ISO 挂进 VmConfig 第二块只读盘; restore provisioned 下复用钉死 known_hosts、缺失 fail-fast 绝不 keyscan;preflight 拦截 CHV_DISK_MODE=reuse(显式 实验二次确认 + 审计放行);SSH 目标选客机 IP 方案(显式 host > net 静态 ip > 显式转发宣称回环,三者皆无 fail-fast)。 P1:网关 extra_env 经 SSH env 前缀注入、worker 守卫收窄到 Local/CHV;取消/清理/看门狗接 CHV 通用路径;WarmPool acquire 内 resume;容量门持久化计数;磁盘配额总量+余量 与已存在磁盘校验重拷;API 按 Content-Length 验完整性; 快照 file:// 验落盘再 shutdown;execute 死端口归类 vm_not_running。 P2 全部排除未做(用户范围收窄令,留待以后一轨)。 测试:原 84 + 新增 29 = 113 项,mock,无真 VM。
105 lines
3.9 KiB
Python
105 lines
3.9 KiB
Python
"""Cloud Hypervisor 每任务磁盘供给(叶子模块)。
|
||
|
||
单任务单 VM 的文件系统独立性就靠这里:copy 模式每任务
|
||
复制一份基础镜像,两个任务绝不写同一文件;reuse 模式
|
||
直接返回基础镜像路径(仅单租户实验,生产拦截见执行器
|
||
preflight,须同时设 CHV_REUSE_SINGLE_TENANT_EXPERIMENT=1
|
||
并审计放行)。
|
||
|
||
feat-chv-fix(P1-5)配额口径:不再只比基础镜像大小。
|
||
check_disk_quota 改为三项:state_dir 已用总量 + 本次
|
||
拷贝(基础镜像大小)+ 宿主剩余空间必须容纳本次拷贝;
|
||
镜像不可读时不再静默跳过,报 disk_quota_unverified
|
||
(preflight 在 wake 路径上会先报 image_missing,这里
|
||
是直接调用者的第二道门)。provision_task_disk 对已存在
|
||
磁盘校验大小:与源不一致即重拷;源不可读即报失败,
|
||
绝不复用脏磁盘。
|
||
"""
|
||
|
||
import os
|
||
import shutil
|
||
|
||
from cloud.chv.errors import ChvFailure
|
||
|
||
|
||
def _dir_total_bytes(path):
|
||
total = 0
|
||
for root, _dirs, files in os.walk(path):
|
||
for name in files:
|
||
try:
|
||
total += os.path.getsize(os.path.join(root, name))
|
||
except OSError:
|
||
pass
|
||
return total
|
||
|
||
|
||
def check_disk_quota(rootfs, disk_gb, state_dir=None):
|
||
"""磁盘配额门(feat-chv-fix 口径):
|
||
|
||
- disk_gb 未设时不检查;
|
||
- 镜像不可读时报 disk_quota_unverified,不静默跳过;
|
||
- 镜像大小超过 disk_gb 即 disk_quota_exceeded;
|
||
- state_dir 已给时:已用总量 + 本次拷贝超过 disk_gb
|
||
即 disk_quota_exceeded;宿主剩余空间不足以容纳本次
|
||
拷贝即 disk_quota_exceeded(宿主余量校验)。
|
||
"""
|
||
if disk_gb is None or not rootfs:
|
||
return
|
||
try:
|
||
size = os.path.getsize(rootfs)
|
||
except OSError:
|
||
raise ChvFailure(
|
||
"disk_quota_unverified",
|
||
"磁盘镜像不可读,配额无法校验:%s" % rootfs)
|
||
if size > disk_gb * 1024 ** 3:
|
||
raise ChvFailure(
|
||
"disk_quota_exceeded",
|
||
"磁盘镜像 %d 字节超过配额 %.1f GB" % (size, disk_gb))
|
||
if state_dir and os.path.isdir(state_dir):
|
||
used = _dir_total_bytes(state_dir)
|
||
if used + size > disk_gb * 1024 ** 3:
|
||
raise ChvFailure(
|
||
"disk_quota_exceeded",
|
||
"磁盘总量 %d 字节 + 本次 %d 字节超过配额 %.1f GB"
|
||
% (used, size, disk_gb))
|
||
try:
|
||
free = shutil.disk_usage(state_dir).free
|
||
except OSError:
|
||
free = None
|
||
if free is not None and free < size:
|
||
raise ChvFailure(
|
||
"disk_quota_exceeded",
|
||
"宿主剩余空间 %d 字节不足以容纳本次磁盘拷贝 %d 字节"
|
||
% (free, size))
|
||
|
||
|
||
def provision_task_disk(state_dir, vm_name, rootfs, disk_mode):
|
||
"""每任务磁盘路径:copy 模式复制到
|
||
state_dir/<vm>/disk.raw;reuse 模式直接返回基础镜像路径。
|
||
|
||
feat-chv-fix:已存在磁盘须校验——大小与源不一致即
|
||
重拷(脏磁盘不复用);源不可读即 ChvFailure,绝不
|
||
静默返回旧磁盘。"""
|
||
if disk_mode == "reuse":
|
||
return rootfs
|
||
task_dir = os.path.join(state_dir, vm_name)
|
||
os.makedirs(task_dir, exist_ok=True)
|
||
task_disk = os.path.join(task_dir, "disk.raw")
|
||
if os.path.exists(task_disk):
|
||
try:
|
||
same = os.path.getsize(task_disk) == os.path.getsize(rootfs)
|
||
except OSError:
|
||
raise ChvFailure(
|
||
"disk_quota_unverified",
|
||
"任务磁盘或源镜像不可读,无法校验复用:%s" % task_disk)
|
||
if same:
|
||
return task_disk
|
||
os.remove(task_disk)
|
||
try:
|
||
shutil.copyfile(rootfs, task_disk)
|
||
except OSError as exc:
|
||
raise ChvFailure(
|
||
"disk_quota_unverified",
|
||
"任务磁盘拷贝失败:%s" % exc)
|
||
return task_disk
|