The lab went from one Windows guest to five and there was no way to see what
they were all doing without issuing a QEMU screendump per guest by hand.
- tools/vmwatch.py: always-on HTTP service serving an auto-refreshing wall of
live guest screens. Runs on spicy as vmwatch.service; browse it at
http://192.168.3.201:8140/. Click a tile for that guest full size. Guests are
discovered from /etc/pve/qemu-server by matching sots-re, so clones appear and
vanish on their own. A stopped, paused or unreachable guest gets a labelled
placeholder tile carrying the monitor's own error, never a broken image or a
500. Python 3 stdlib only.
- tools/vmwatch-install.sh: install/update/uninstall the unit on the host.
- tools/vmshot.py: one-shot contact sheet, and --one <id> for a full-size grab.
Pulls frames from the vmwatch service when it is up (0.5s) and falls back to
ssh + qm monitor when it is not (4s).
- guides/lab-screen-wall.md: how to use both, and why.
Capture goes over each guest's QMP socket rather than forking qm: qm is a Perl
program, and one fork per guest per tick cost ~90% of a host core and a 728 MB
cgroup peak. Direct QMP is 0.33 CPU-seconds per 88s and 23 MB RSS. QEMU 11 here
dumps PNG natively; the fallback PPM encoder was verified pixel-identical to
QEMU's own on a real framebuffer.
Read-only throughout: screendump does not perturb the guest (method-rule 19),
so reading VM 140's screen is not an experiment and does not take its lock.