# Watching the lab guests
The lab is five Windows guests now. This is how to see all of them at once.
## The live wall
Leave the tab open. Tiles refresh every 5 s; click a tile for that guest full size.
Each tile carries the VM id, name, status and the capture timestamp. A guest that is
stopped, paused or unreachable shows a labelled `NO SIGNAL` tile with the monitor's
own error text, never a broken image.
Served by `vmwatch.service` on **spicy** (`/opt/vmwatch/vmwatch.py`, systemd, enabled
at boot). Source of truth is `tools/vmwatch.py` in this repo; redeploy with:
tools/vmwatch-install.sh spicy # install or update
tools/vmwatch-install.sh spicy --uninstall
Nothing was installed on spicy beyond that file and its unit — the service is Python 3
standard library only (spicy has no ImageMagick, no netpbm, no Pillow).
## The one-shot contact sheet
tools/vmshot.py # all guests -> dumps/vmshot/sheet-.png + latest.png
tools/vmshot.py --one 140 # one guest, full size
tools/vmshot.py 144 145 --cols 2
tools/vmshot.py --ssh # bypass the service, capture over SSH
`vmshot` pulls frames from the vmwatch service when it is reachable (~0.5 s, no extra
load on the guests) and falls back to SSH + `qm monitor` when it is not (~4 s). Output
lands in `dumps/vmshot/`, which is gitignored.
## Why this mechanism
Both tools capture with QEMU `screendump`, reached over each guest's
`/var/run/qemu-server/.qmp` socket — the same socket and command `qm monitor` uses.
* It needs **no guest agent, no guest network and nothing installed in the guest**.
* It does **not perturb the guest**. That is the point (method-rule 19: an instrument
that perturbs the thing it measures has already cost this campaign once). Reading
VM 140's screen is therefore *not* an experiment and does **not** take 140's
exclusivity lock.
* The campaign board already records that `qm monitor` screendump is more reliable
than the in-guest click helper's `shot`.
## Two things worth knowing
**Do not fork `qm` in a loop.** The first version of vmwatch shelled out to
`qm monitor` once per guest per 5 s tick. `qm` is a Perl program: that cost **~90 % of
a host core and a 728 MB cgroup peak**. Talking to the QMP socket directly — same
socket, same command, no fork — brought it to **0.33 CPU-seconds per 88 s and 23 MB
RSS**, roughly a 300× reduction. The fleet list comes from `/etc/pve/qemu-server/*.conf`
plus `query-status` for the same reason. `qm` remains the fallback path only.
**QEMU 11 on spicy dumps PNG natively** (`screendump -f png`), so no PPM
conversion is needed. `vmwatch.ppm_to_png` exists as a fallback for an older QEMU and
was checked against QEMU's own encoder on a real 1024×768 framebuffer: pixel-identical.
Guests are discovered dynamically by matching `sots-re` in the guest name, so clones
added or destroyed later appear and vanish on their own — nothing to edit.
Both tools are strictly read-only. Neither starts, stops, resets nor reconfigures a VM.