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:
@@ -204,11 +204,20 @@ def list_live_vms(backend: VmBackend, config: Config) -> list[LiveVm]:
|
|||||||
|
|
||||||
uptime_sec: int | None = None
|
uptime_sec: int | None = None
|
||||||
if under_base:
|
if under_base:
|
||||||
try:
|
created = parse_clone_name(name).started
|
||||||
mtime = datetime.fromtimestamp(vmx.parent.stat().st_mtime, tz=UTC)
|
if created is not None:
|
||||||
uptime_sec = int((now - mtime).total_seconds())
|
# The clone name's own timestamp, set once at creation and
|
||||||
except OSError:
|
# never touched again -- unlike the directory's mtime, which
|
||||||
uptime_sec = None
|
# 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
|
is_orphan = False
|
||||||
if under_base and uptime_sec is not None:
|
if under_base and uptime_sec is not None:
|
||||||
|
|||||||
@@ -321,6 +321,31 @@ def test_list_live_vms_outside_build_vms_not_orphan(tmp_path: Path) -> None:
|
|||||||
assert vm.uptime_sec is 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:
|
def test_list_live_vms_get_ip_none(tmp_path: Path) -> None:
|
||||||
build_vms = tmp_path / "build-vms"
|
build_vms = tmp_path / "build-vms"
|
||||||
build_vms.mkdir()
|
build_vms.mkdir()
|
||||||
|
|||||||
Reference in New Issue
Block a user