"""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//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