Virtualization phase 2: ISOs, New VM from ISO, the VGA console in the browser, nightly exports, mounts, instance sensors

- Library: an ISO shelf in virt/isos: upload (the Files uploader), fetch by URL with an
  optional sha256, import a file from the machine by copying it in, rename, delete (refused
  while attached), attach to a VM as a CD-ROM live or stopped with boot-first, eject, and
  New VM from ISO: an empty VM with the ISO as first boot device, Secure Boot on or off.
- The VGA console: Ham Lab asks Incus for the VM's SPICE console over its unix socket and
  bridges the display websocket to the browser, where spice-html5 (vendored from upstream
  b372aab, LGPL, loaded on demand) draws it; Ctrl+Alt+Del, fit-to-window.
- Exports: every instance as a tarball in virt/exports/<name>/, kept to a count, as a cron
  line in /etc/cron.d/hamlab-virt calling hamlab virt-export, settings and the last run
  as files beside the data; Export now per instance or for all.
- Disks tab per instance: the root disk, attached ISOs with Eject, bind mounts in and out
  (shift for containers, virtiofs for VMs).
- instance.* sensors (running, down, state, cpu, mem) and the VM-or-container-down alert
  default, offered once to installs seeded before it existed.
- cloud-init: Use my SSH keys writes a cloud-config from the account's public keys.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-10-09 22:21:57 -07:00
co-authored by Claude Fable 5.1
parent 66498a58c5
commit 43ebacdd92
46 changed files with 14493 additions and 25 deletions
+9
View File
@@ -902,3 +902,12 @@ pre.diff .add { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%,
.virt-form-row { display: flex; gap: 8px; }
.virt-form-row > label { flex: 1; min-width: 0; }
.virt .modal { max-width: 640px; }
.vga-back { align-items: stretch; padding: 10px; }
.vga { display: flex; flex-direction: column; gap: 6px; width: 100%; height: 100%; max-width: none; max-height: none; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 8px; }
.vga-bar { flex-shrink: 0; }
.vga-area { flex: 1; min-height: 0; overflow: auto; background: #000; border-radius: 6px; display: flex; align-items: flex-start; justify-content: flex-start; padding: 4px; }
.vga-area #spice-area { display: inline-block; }
.vga-area #spice-screen { display: inline-block; line-height: 0; }
.vga-area canvas { outline: none; }
.vga-msg { display: none; }
.vga-hint { flex-shrink: 0; }