feat: implement resource resizing for VMs with storage management

- Added KnownHostsManager to handle cleanup of SSH known_hosts entries upon VM deletion.
- Introduced disk_size_bytes method in ImageStore to read logical disk capacity.
- Enhanced LifecycleService to manage VM resource resizing, including CPU, RAM, and storage.
- Updated application configuration to support known_hosts_files for SSH management.
- Modified dashboard to include resource resizing options in the UI.
- Improved error handling and validation for resource inputs in the dashboard.
- Removed obsolete .pyc files from the repository.
This commit is contained in:
2026-09-21 20:37:52 +05:30
parent fb80ce7c8a
commit 4a46685a4c
40 changed files with 1048 additions and 63 deletions
+44 -8
View File
@@ -3,7 +3,7 @@
`uvm` is a small, local Firecracker microVM command-line manager for Ubuntu.
It creates one Firecracker process per VM, attaches it to a host TAP device,
and keeps a private disk and local registry for named VMs. The CLI supports
create, start, stop, restart, delete, inspection, SSH, logs, diagnostics, and
create, start, stop, restart, resize, delete, inspection, SSH, logs, diagnostics, and
enforced private-IPv4 TCP port allowlists.
This directory is a self-contained project. All UVM code, tests, packaging
@@ -40,7 +40,7 @@ The project is an application, not a reusable Python SDK.
- Linux 5.3 or newer with native bridge conntrack and safe pidfd support
- An x86_64 or ARM64 (`aarch64`) host, including NVIDIA DGX Spark
- Working host networking, `iproute2`, `iptables`, `nftables`, `conntrack`,
`e2fsprogs`, `util-linux`, `openssl`, and `openssh-client`
`coreutils`, `e2fsprogs`, `util-linux`, `openssl`, and `openssh-client`
- An STP-disabled software bridge; no offload path that bypasses bridge hooks
`uvm install` installs the required host packages. It validates `/dev/kvm`
@@ -89,7 +89,7 @@ sudo env UVM_ALLOW_UNVERIFIED_DOWNLOADS=1 ./uvm.py install
An unverified manifest is not accepted as publisher trust in a later strict
run. Reinstall with trusted digests to establish a verified local manifest.
To boot VMs from an unverified local-development install, retain
`UVM_ALLOW_UNVERIFIED_DOWNLOADS=1` on later `create`, `start`, and `restart`
`UVM_ALLOW_UNVERIFIED_DOWNLOADS=1` on later `create`, `start`, `restart`, and `resize`
commands and on the API server process.
## Commands
@@ -100,12 +100,12 @@ These are separate operation examples, not a script to run from top to bottom.
# Install after supplying the trusted checksum variables shown above.
# A bare `install` command fails under the default strict integrity policy.
# Create a named VM: 1 CPU, 512 MiB RAM, root/root, and private TCP/22.
# Create a named VM: 1 CPU, 512 MiB RAM, 30 GiB storage, root/root, and private TCP/22.
# Use a unique non-default password for anything beyond a throwaway VM.
sudo ./uvm.py create --name web
# Choose resources, a private guest IP, and additional service ports.
sudo ./uvm.py create --name app --cpu 1 --ram 1G \
sudo ./uvm.py create --name app --cpu 1 --ram 1G --storage 50G \
--guest-ip 10.42.0.10 --ports 80:HTTP,443:HTTPS
# Read a password from one line of stdin, rather than a --password argument.
@@ -133,6 +133,10 @@ sudo ./uvm.py stop web --timeout 5
sudo ./uvm.py start web
sudo ./uvm.py restart web app --timeout 10
# Stop, change resources, and boot the same VM with its existing identity/data.
sudo ./uvm.py resize app --cpu 2 --ram 4G --storage 80G
sudo ./uvm.py resize app --ram 2G
# Preview bulk operations before confirming them.
sudo ./uvm.py stop --all --dry-run --json
sudo ./uvm.py stop --all -y
@@ -150,6 +154,28 @@ sudo ./uvm.py delete web -y
start. It is a finite non-negative SIGTERM wait in seconds, default `5`, not
a total command deadline or a guest shutdown timeout.
### Storage and Resizing
New VMs default to a **30 GiB private disk**, which is also the minimum.
`--storage` accepts values such as `30G`, `50G`, or `1T`; bare numbers use GiB.
As with RAM, `G`/`GB`/`GiB` are binary units. Capacities round up to whole MiB
and are returned as `storage_mib`. Disks are sparse: the configured capacity
does not immediately reserve that much physical host space.
`resize VM` accepts any combination of `--cpu`, `--ram`, and `--storage`, plus
`--timeout` and `--json`. It stops the VM, resizes the ext4 disk when needed,
updates the resource settings, and boots it. A previously stopped VM also boots
at the end. VM ID, IP/MAC, username/password, port rules, guest files, and SSH
host keys are retained. The dashboard exposes the same operation under
**Resize resources**; the API uses `POST /vms/{identifier}/resize`.
Storage can grow or shrink, provided ext4 can fit its existing data into the
requested capacity. Disk changes are prepared on a temporary sparse copy and
published after verification. Failed resizes retain a `pending_resize` target
for retry; `start` on the stopped VM completes that target before booting.
Older VMs report `storage_mib: null` until their first resize, which retains
their current capacity or grows it to the 30 GiB minimum when storage is omitted.
Stop sends SIGTERM and, if needed, SIGKILL to the Firecracker VMM. It is **not
a guest-coordinated shutdown**. Start boots the retained disk without resetting
guest data, SSH host keys, or credentials; it does not restore guest memory.
@@ -248,11 +274,12 @@ The server exposes these routes:
| `GET` | `/diagnostics` | Run host readiness checks for the dashboard/system view |
| `POST` | `/install` | Install or refresh host assets; body: `{ "force": false }` |
| `GET` | `/vms` | List VM records and observed status |
| `POST` | `/vms` | Create; supports `cpu`, `ram`, `guest_ip`, `username`, `password`, `name`, `ports`, `no_default_ports` |
| `POST` | `/vms` | Create; supports `cpu`, `ram`, `storage`, `guest_ip`, `username`, `password`, `name`, `ports`, `no_default_ports` |
| `POST` | `/vms/actions` | Run a fixed snapshot of selected or all VM lifecycle actions |
| `GET` | `/vms/{identifier}` | Inspect by exact ID, name, or guest IP |
| `POST` | `/vms/{identifier}/start` | Boot a retained private disk |
| `POST` | `/vms/{identifier}/restart` | Stop then boot a VM; optional `timeout` query parameter |
| `POST` | `/vms/{identifier}/resize` | Stop, resize CPU/RAM/storage, and boot; optional `timeout` query parameter |
| `POST` | `/vms/{identifier}/stop` | Stop a VM while retaining its private disk; optional `timeout` query parameter |
| `DELETE` | `/vms/{identifier}` | Delete a VM and release its resources; optional `timeout` query parameter |
| `POST` | `/vms/{vm_id}/ports` | Set, open, or close private port rules for an exact VM ID |
@@ -260,7 +287,9 @@ The server exposes these routes:
| `GET` | `/vms/{identifier}/logs` | Read a bounded, non-following tail of a VM log |
API port rules are objects such as `{"port": 80, "protocol": "HTTP"}`;
`no_default_ports` is a boolean, default `false`. Port changes use
`no_default_ports` is a boolean, default `false`. Create also accepts `storage`,
defaulting to `"30G"`; resize accepts optional `cpu`, `ram`, and `storage` fields.
Port changes use
`{"mode":"set"|"open"|"close", "ports":[...]}`. A legacy VM must be
stopped and use `set` to establish a managed policy. Responses include `name`,
`ports`, `policy_status`, and `operation`, but never passwords. See
@@ -379,7 +408,7 @@ The default data directory is `/var/lib/uvm`:
└── state.json # Root-only VM inventory and guest credentials
```
The default registry is `/var/lib/uvm/state.json`, schema version `2`. Writes
The default registry is `/var/lib/uvm/state.json`, schema version `3`. Writes
are locked and atomic. Lifecycle, port-policy, and installation mutations
share an operation lock; bulk operations use a fixed snapshot of VM IDs.
@@ -404,6 +433,11 @@ time, running binary, exact API-socket command line, and protected paths all
match. Missing or ambiguous evidence fails closed: UVM will not guess which
process to signal or release a disk while process exit is unconfirmed.
Schema 3 adds disk capacity and durable resize targets. Older registries migrate
on the next write; a recorded pending resize is completed before a stopped VM
can boot. A boot failure after a successful resize retains the new allocation
for a later start.
## Configuration
Host and API settings can be overridden through environment variables:
@@ -467,6 +501,8 @@ dependencies are installed.
When `nft` and `conntrack` are installed, the suite also checks their command
parsers using deliberately invalid final commands/options that prevent mutations.
With e2fsprogs installed, offline disk tests grow and shrink temporary sparse ext4
images and check that data and credential/key fixtures survive; no VM is booted.
Opt-in packet checks run **only in disposable isolated user/network
namespaces**, which may require privileges and the installed network tools: