utils/vmbox/lib/last_event.py
Krzysztof kuhy Rudnicki 694c013b6b
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file length / file-length (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
palette drift / palette (push) Has been cancelled
shared gate tests / gate unit tests (push) Has been cancelled
vmbox: UEFI sandboxes and 'vm wake' RTC-wake emulation for poweroff/hibernate; worktree copies skip gitignored files
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-01 12:29:54 +02:00

75 lines
2.1 KiB
Python
Executable File

#!/usr/bin/env python3
"""Classify the last stop recorded in a vmbox QMP event log (events.jsonl).
Prints exactly one of:
guest-shutdown the guest powered itself off (ACPI S5)
hibernate the guest hibernated: QEMU emits SUSPEND_DISK right before
the SHUTDOWN when the guest enters ACPI S4
guest-reset the guest rebooted (-no-reboot turns that into a SHUTDOWN)
panic GUEST_PANICKED (pvpanic)
host a SHUTDOWN the guest did not initiate
<reason> any other SHUTDOWN reason, verbatim
none no stop at all
Usage: last_event.py <events.jsonl>
"""
from __future__ import annotations
import json
import sys
from collections.abc import Iterable
from typing import Any
def _events(lines: Iterable[str]) -> Iterable[dict[str, Any]]:
for raw in lines:
line = raw.strip()
if not line:
continue
try:
msg = json.loads(line)
except json.JSONDecodeError:
continue
if isinstance(msg, dict):
yield msg
def classify(lines: Iterable[str]) -> str:
"""Return the kind of the LAST stop in the log (see module docstring)."""
kind = "none"
suspend_disk = False
for msg in _events(lines):
event = msg.get("event", "")
if event == "GUEST_PANICKED":
kind = "panic"
elif event == "SUSPEND_DISK":
# Only meaningful for the SHUTDOWN that follows it.
suspend_disk = True
elif event == "SHUTDOWN":
data = msg.get("data", {})
reason = data.get("reason", "")
if not reason:
reason = "guest-shutdown" if data.get("guest") else "host"
kind = (
"hibernate" if suspend_disk and reason == "guest-shutdown" else reason
)
suspend_disk = False
return kind
def main() -> int:
if len(sys.argv) != 2:
print(__doc__, file=sys.stderr)
return 2
try:
with open(sys.argv[1], encoding="utf-8") as fh:
print(classify(fh))
except FileNotFoundError:
print("none")
return 0
if __name__ == "__main__":
sys.exit(main())