v0.7.0 — the three bugs a v0.6.3 user reported, and four things they asked for#4
Merged
Conversation
…sked for Snapshot names containing a space could be created and then never removed. Create and delete used different validators: create only refused / \ and ., delete ran the name through validName(), which rejects spaces. Measured against a live v0.6.3 — "nospace-x" reaches virsh and comes back 500 "no domain snapshot with matching name", "with space" never gets there and comes back 400 "invalid snapshot name". Revert had the same gate. Delete and revert no longer apply a character allowlist. They resolve the name against virsh snapshot-list and pass through what libvirt actually has, which cannot strand a name we failed to anticipate and hands virsh a string virsh gave us. Snapshots already stranded on v0.6.3 become deletable with no migration. Create accepts single spaces; overlay filenames are derived separately (fileStem) so a snapshot named "before Windows update" does not put spaces into a --diskspec file= path. Every VM showed 100 % memory. balloon.current is the balloon's target size, not consumption — with an uninflated balloon it equals balloon.maximum, so "4.0 GB / 4.0 GB" was two names for the same number. The in-guest figures exist only once libvirt is collecting them, which nothing was doing. The daemon now arms dommemstat --period 5 --live on demand (live only, rate limited, self-healing after a VM restart) and reports balloon.available, unused, usable and rss. The card shows used, available-to-apps and total side by side; deliberately not a second "used" derived from usable, because Linux subtracts the low watermark from MemAvailable and it can sit below MemFree (measured here: 3.47 GiB usable vs 3.59 GiB unused of 3.63 GiB), so that figure would exceed the first and read as a contradiction. Where a guest reports nothing, the card says "Memory (allocated)" and shows no percentage rather than inventing one. The snapshot storage target vanished for a shut-off VM. That was correct — such a VM gets an internal snapshot inside its own qcow2 and there is no target — but hiding the row said "broken", not "not applicable". The row stays, disabled, and explains itself. Requested and added: power controls on the Autostart tab (start, ACPI shut down, reboot, resume, confirmed force-off), which warn when the VM's watchdog would restart it within 30 s; backup and snapshot target directories remembered on the appliance in settings.json rather than in one browser; and a short in-page history sparkline per metric. Not added: the interactive folder picker. It needs an endpoint that enumerates the host filesystem over the gateway, and that surface deserves its own pass rather than a bugfix release. Also, unprompted: the PCIe tab listed 41 devices on a ZimaCube of which 23 were bridges that can never be passed through, plus entries with no IOMMU group, which VFIO requires. Both are hidden behind a toggle now — 41 down to 17 here. The IOMMU-group rule only applies when at least one device has a group, so a host with IOMMU disabled in firmware still sees its devices and gets told why none of them qualify. And the reason this took two deploys to verify: index.html carried a hand-maintained ?v=0.6.3 on its script and stylesheet. Bumping VERSION did not touch it, so the daemon reported 0.7.0 while every browser kept running 0.6.3's app.js — and I read that stale copy as evidence my fix had not landed. build.sh now stamps the URL from VERSION plus a hash of each asset, so it changes whenever the bytes do; a version alone would not have caught the second iteration of the same release. Verified on 192.168.1.147 (ZimaOS 1.7.0-beta1) through the browser UI, not just the API: snapshot "zvme test 070" created and deleted through the form and the Delete button; the storage row visible and annotated for a shut-off VM; memory reading 1 % — 40.6 MB used / 3.5 GB avail. to apps / 3.6 GB total where it read 100 % — 4.0 GB / 4.0 GB an hour earlier; five sparklines drawing; PCIe list 41 -> 17 with the toggle restoring all 41. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tering Spotted while re-shooting the README screenshots: with Fedora selected, the snapshot tab still showed /DATA/AppData/zima-vm-extras/snapshots/bde975b9 — the Mint VM looked at a moment earlier. renderSnapshotsForCurrentVM only filled the field `if (!extDirInput.value)`, so once anything was in it the per-VM subdirectory never changed again. An external snapshot of one VM would have written its overlay and memory files into another VM's directory. Not destructive, since the filenames carry the snapshot name, but wrong and confusing to clean up. The field now recomposes when it still holds a value this code put there, and is left alone when the operator typed their own path. Picking a storage target recomposes for the currently selected VM too, without stealing focus, since that path also ends in the VM name. PCIe: a ZimaCube lists 41 devices, 23 of them bridges that isPassable() already refused, plus entries with no IOMMU group, which VFIO requires. They drowned the ones that can actually be passed. Both classes are hidden behind a "Show bridges & non-passable" toggle — 41 down to 17 here. Pinned devices stay visible regardless. The IOMMU-group rule only applies when at least one device has a group: on a host with IOMMU disabled in firmware, nothing has one, and filtering on that alone would have emptied the list and hidden the actual cause. That case now says so instead. Metrics memory shows both figures that exist rather than one: used (available - unused), the guest's own MemAvailable, and the total. No second "used" is derived from usable — Linux subtracts the low watermark from MemAvailable, so it can sit below MemFree (3.47 vs 3.59 GiB of 3.63 on the test guest) and the derived figure would exceed the first one. Screenshots: dashboard.png re-shot for v0.7.0 — the old one predates the power controls and still showed v0.6.3 — and metrics.png and pcie-passthrough.png added, since neither tab had an image and both changed in this release. usb-passthrough.png, vnc-security.png and tpm-secure-boot.png are unchanged apart from the version badge in the header and were left as they are. build.sh now stamps ?v= from VERSION plus a hash of each asset. A version alone was not enough: two builds of 0.7.0 with different app.js produced the same URL, so a browser that had already loaded the page kept the old script. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adopts the idea behind #1 — making the console's listen address and port settable — into the existing VNC tab rather than alongside it. #1 adds its own internal/vnc package and /api/vnc/ handler, paths that the VNC work in v0.5.1 already occupies, so it cannot merge as it stands. Four things are different from that PR, all of them following from what this tab is for. No console goes onto all interfaces without a password. #1 validates only that listen_address is one of two strings. Here every path that sets 0.0.0.0 ends with a password on the console; asking for it without one is refused with "a console on all interfaces needs a password". The reverse is deliberately allowed: restricting a console to 127.0.0.1 is protective in itself and needs no password, so tightening is never gated behind remembering one. The <listen> child element is rewritten, not just the attribute. libvirt keeps the address in both places and honours the child; editing only the attribute would have reported success and changed nothing. A child of type='network' is replaced wholesale rather than patched, since it would otherwise keep overriding the address. This is the same class of trap as the --security-info masking documented at the top of virsh/vnc.go. The address is pinned and reconciled like the password. ZVM regenerates <graphics> with listen='::' on every re-save, which would quietly put a restricted console back on the LAN. The card has three states, not two. `virsh define` writes the persistent config and the running qemu keeps the socket it started with, so a changed address shows "restart required" with both the configured and the live address instead of a green "local only" over a console still on the LAN. One bug caught while building: an entry that pins only an address has no password, and the reconciler would have called SetVNCPassword(vm, "") — after which the config still has no password, so the next pass would repair it again, once a minute, forever. The password repair is now skipped for such entries, with a test. Verified on 192.168.1.147 by measuring the socket rather than the config, and from the LAN rather than from the host. After restricting one VM and starting it: qemu gets `-vnc 127.0.0.1:0` where the untouched VMs have `[::]:1` and `[::]:2`; `ss` shows 127.0.0.1:5900 against *:5901 and *:5902; and a TCP probe from another machine reaches 5901 and 5902 but not 5900. libvirt applies the address to ZVM's websocket port as well — 127.0.0.1:5700 against *:5701 and *:5702 — so both surfaces close together, which was worth checking before calling anything "local only". The guard was exercised through the button and again straight at the API, bypassing the button, and refused both times. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Community feedback on v0.6.3 (forum thread), verified against a live v0.6.3 on ZimaOS 1.7.0-beta1 before fixing and re-verified in the browser afterwards.
Reported bugs
Snapshot names containing a space could be created and then never removed. Create and delete used different validators. Measured on the running v0.6.3:
Revert had the same gate, so those snapshots were stuck entirely. Delete and revert now resolve the name against
virsh snapshot-listinstead of filtering characters — snapshots already stranded on v0.6.3 become removable with no migration step. Overlay filenames are derived separately, so a snapshot namedbefore Windows updatedoes not put spaces into a--diskspec file=path.Every VM showed 100 % memory. It divided
balloon.currentbyballoon.maximum, butballoon.currentis the balloon's target size — with an uninflated balloon those are the same number. The daemon now enables the guest's balloon statistics on demand (dommemstat --period 5 --live, rate limited, self-healing after a VM restart) and the card shows used · available-to-apps · total. Where a guest reports nothing it says "Memory (allocated)" and shows no percentage rather than inventing one.The snapshot storage target vanished for a shut-off VM. Correct behaviour — such a VM gets an internal snapshot inside its own qcow2 — but hiding the row read as "broken". It stays, disabled, and explains itself.
Found while fixing
The external target directory did not follow the VM picker. With VM B selected the field still held VM A's path, so B's overlay files would have been written into A's directory.
Requested features
Power controls on the Autostart tab (start · shut down · reboot · resume · confirmed force-off), warning when the VM's watchdog would restart it anyway. Backup and snapshot target directories remembered on the appliance rather than in one browser. A short in-page history sparkline per metric.
Not included: the interactive folder picker. It needs an endpoint that enumerates the host filesystem through the gateway, and that surface deserves its own pass.
Unprompted
The PCIe tab listed 41 devices on a ZimaCube, 23 of them bridges, plus entries with no IOMMU group. Both are hidden behind a toggle now — 41 down to 17. The IOMMU-group rule only applies when at least one device has a group, so a host with IOMMU disabled in firmware still sees its devices and is told why none qualify.
build.shstamps?v=from VERSION plus a per-asset hash. The hand-maintained?v=0.6.3had gone stale, so the daemon reported 0.7.0 while browsers kept running 0.6.3'sapp.js.Verified
On 192.168.1.147 (ZimaOS 1.7.0-beta1), through the UI rather than the API: snapshot
zvme test 070created and deleted via the form and the Delete button; storage row visible and annotated for a shut-off VM; memory reading1 % — 40.6 MB used · 3.5 GB avail. to apps · 3.6 GB totalwhere it read100 % — 4.0 GB / 4.0 GB; five sparklines drawing; PCIe list 41 -> 17 with the toggle restoring all 41.go vet,go testand the build are green.Note on the open PR
This branch touches
internal/handlers,internal/virshand the frontend, and addsinternal/settings. #1 addsinternal/vnc/{store,vnc}.go— paths now occupied by the VNC work that landed in v0.5.1 — and its own/api/vnc/handler, so it already conflicts withmainindependently of this branch.🤖 Generated with Claude Code
Added after review: VNC reachability (the idea behind #1)
The listen address and port are now settable in the existing VNC tab, as a second "Reachability" card. Four things differ from #1:
0.0.0.0ends with a password on the console. Tightening to127.0.0.1is deliberately allowed without one — restricting a console is protective in itself.<listen>child element is rewritten, not just the attribute. libvirt keeps the address in both places and honours the child; editing only the attribute reports success and changes nothing.<graphics>withlisten='::'on every re-save.Verified by measuring the socket rather than the config, and from the LAN rather than from the host:
-vnc 127.0.0.1:0[::]:1,[::]:2ssVNC127.0.0.1:5900*:5901,*:5902sswebsocket127.0.0.1:5700*:5701,*:5702libvirt applies the address to ZVM's websocket port too, so both surfaces close together — worth checking before calling anything "local only". The guard was exercised through the button and again straight at the API, bypassing the button, and refused both times.
#1 cannot merge as it stands regardless of this branch: it adds
internal/vnc/{store,vnc,vnc_test}.goas new files, and those paths have been occupied since v0.5.1.