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
+168
View File
@@ -0,0 +1,168 @@
body
{
background-color: #999999;
color: #000000;
margin: 0;
padding: 0;
font-family: "Lucida Grande", "Lucida Sans Unicode", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
font-size: 12pt;
line-height: 1.5em;
}
* { margin: 0; }
button
{
cursor: pointer;
color: #000000;
background-color: #999999;
border: 1px solid #4a4a4a;
min-width: 150px;
border-radius: 10px;
background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#24414e));
background: -moz-linear-gradient(top, #fff, #24414e);
-ms-transform: translate(15%, -8%);
transform: translate(15%, -8%);
font-size: .90em;
font-weight: bolder;
padding: 0px 0px 3px 0px;
margin-top: 5px;
}
button:focus
{
outline: none;
}
button:hover
{
background-color: #24414e
}
button:active
{
background-color: #24414e;
transform: translate(14.85%, -7%);
}
.SidenavClosed
{
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #bbbbbb;
box-shadow: 1px 0px #00000040;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
}
.SidenavOpen
{
height: 100%;
width: 200px !important;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #bbbbbb;
box-shadow: 1px 0px #00000040;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
}
#Sidenav label
{
color: #000000;
margin-left: 3%;
text-shadow: 1px 1px 0px rgba(175, 210, 220, 0.8);
position: absolute;
font-size: .9em;
}
#Sidenav input
{
padding: 3px;
background-color: #fAfAfA;
border: 1px inset #999999;
outline: none;
float: right;
margin-right: 3%;
-moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
position: relative;
max-width: 110px;
}
#Sidenav .closebtn
{
position: absolute;
top: 10px;
right: 10px;
font-size: 25px;
margin-left: 50px;
cursor: pointer;
color: #000000;
}
canvas
{
border: 1px solid #000000;
}
#login
{
background: #bbbbbb;
z-index: 1;
padding-top: 2px;
border-bottom: 2px solid #00000040;
}
#login p
{
margin-left: 27%;
display: inline;
font-size: large;
}
#spice-area
{
height: 100%;
width: 100%;
margin-left: auto;
margin-right: auto;
}
.spice-screen
{
min-height: 600px;
height: 100%;
text-align: center;
padding: 0;
background-color: #999999;
}
.spice-message
{
width: 90%;
height: 40%;
overflow: auto;
margin-left: auto;
margin-right: auto;
padding: 10px;
font-size: 8pt;
line-height: 1.1em;
font-family: 'Andale Mono', monospace;
background-color: #fAfAfA;
border: 1px inset #999999;
border-radius: 3px;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.spice-message p {
margin-bottom: 0em;
margin-top: 0em;
}
.spice-message-warning {
color: orange;
}
.spice-message-error {
color: red;
}