sots-re/guides/lab-screen-wall.md
alex 962cdb6981 tools: live screen wall + contact sheet for the five lab guests
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.
2026-09-08 16:53:04 -04:00

63 lines
3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Watching the lab guests
The lab is five Windows guests now. This is how to see all of them at once.
## The live wall
<http://192.168.3.201:8140/>
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-<ts>.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/<id>.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 <file> -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.