fix(web): VM uptime pinned near 0s while a build was running

uptime_sec came from the clone directory's own mtime, but a running VM's
hypervisor can rewrite lock files under that same directory at any point,
resetting the mtime and pinning "up" near 0s for the VM's entire life --
worst exactly while a build is actively running, since that's when the VM
is busiest. The clone name already carries its own creation timestamp
(_make_clone_name, parsed back via parse_clone_name), set once and never
disturbed after -- prefer it, falling back to the old mtime read only for
a name that doesn't parse.
This commit is contained in:
2026-07-26 02:38:40 +02:00
parent 567f9da619
commit 3da992119b
2 changed files with 39 additions and 5 deletions
+14 -5
View File
@@ -204,11 +204,20 @@ def list_live_vms(backend: VmBackend, config: Config) -> list[LiveVm]:
uptime_sec: int | None = None
if under_base:
try:
mtime = datetime.fromtimestamp(vmx.parent.stat().st_mtime, tz=UTC)
uptime_sec = int((now - mtime).total_seconds())
except OSError:
uptime_sec = None
created = parse_clone_name(name).started
if created is not None:
# The clone name's own timestamp, set once at creation and
# never touched again -- unlike the directory's mtime, which
# a running VM's hypervisor can reset at any point (lock
# files rewritten under it), silently pinning uptime near 0
# for the VM's entire life.
uptime_sec = int((now - created.replace(tzinfo=UTC)).total_seconds())
else:
try:
mtime = datetime.fromtimestamp(vmx.parent.stat().st_mtime, tz=UTC)
uptime_sec = int((now - mtime).total_seconds())
except OSError:
uptime_sec = None
is_orphan = False
if under_base and uptime_sec is not None:
+25
View File
@@ -321,6 +321,31 @@ def test_list_live_vms_outside_build_vms_not_orphan(tmp_path: Path) -> None:
assert vm.uptime_sec is None
def test_list_live_vms_uptime_uses_clone_name_timestamp_not_directory_mtime(
tmp_path: Path,
) -> None:
"""The clone directory's own mtime can be disturbed by the hypervisor
while the VM is running (VMware rewrites lock files under it) -- the
timestamp already baked into the clone's own name at creation time can't
be, so it must win over the filesystem mtime."""
build_vms = tmp_path / "build-vms"
build_vms.mkdir()
two_hours_ago = datetime.now(UTC) - timedelta(hours=2)
name = f"Clone_412-1_{two_hours_ago:%Y%m%d_%H%M%S}_linux"
clone_dir, vmx = _make_clone_dir(build_vms, name)
# Something touched the directory moments ago -- its mtime says "just
# now" even though the clone itself is 2h old.
_age_dir(clone_dir, hours=0)
backend = _FakeLiveBackend([VmHandle(identifier=str(vmx), name=name)])
config = _make_config(build_vms)
result = list_live_vms(backend, config) # type: ignore[arg-type]
assert len(result) == 1
vm = result[0]
assert vm.uptime_sec is not None and vm.uptime_sec >= 7000
def test_list_live_vms_get_ip_none(tmp_path: Path) -> None:
build_vms = tmp_path / "build-vms"
build_vms.mkdir()