feat: Implement UVM Dashboard with frontend and backend integration
- Added dashboard.js for frontend functionality, including VM management and system health monitoring. - Created dashboard route to serve the dashboard HTML. - Introduced diagnostics route for token-protected host diagnostics. - Enhanced VMs router with bulk actions, VM log retrieval, and port management. - Updated lifecycle service to read VM log tails with truncation handling. - Integrated static file serving for dashboard assets in the FastAPI server. - Redirect root URL to the dashboard for improved user experience.
This commit is contained in:
@@ -602,11 +602,12 @@ inventing identity fields in `state.json`.
|
||||
|
||||
## 10. Use the FastAPI Server
|
||||
|
||||
The API uses the same local lifecycle services, but exposes only the existing
|
||||
health/install/create/list/inspect/stop/delete endpoints. There are no new
|
||||
start/restart, port-management, or bulk HTTP endpoints; SSH, logs, and doctor
|
||||
also remain CLI-only. The server does not autostart VMs or reconcile rules,
|
||||
and is not safe to expose directly to the public internet.
|
||||
The API uses the same local lifecycle services and exposes health, installation,
|
||||
VM lifecycle, diagnostics, bounded VM-log tails, and managed private-port
|
||||
controls. The local dashboard at `/dashboard` uses those same endpoints. There
|
||||
API also supports fixed-target bulk actions; interactive SSH remains CLI-only.
|
||||
The server does not autostart VMs or reconcile rules, and is not safe to expose
|
||||
directly to the public internet.
|
||||
|
||||
### Start a Local Server
|
||||
|
||||
@@ -620,15 +621,18 @@ sudo env \
|
||||
./uvm.py --serve --host 127.0.0.1 --port 8000
|
||||
```
|
||||
|
||||
Liveness is at `http://127.0.0.1:8000/health`; OpenAPI docs are at
|
||||
Liveness is at `http://127.0.0.1:8000/health`; the dashboard is at
|
||||
`http://127.0.0.1:8000/dashboard`; OpenAPI docs are at
|
||||
`http://127.0.0.1:8000/docs`. The default bind is `127.0.0.1:8000`.
|
||||
|
||||
The API token is required even for a loopback server. All VM and installation
|
||||
routes require it. FastAPI's informational `/docs`, `/redoc`, and
|
||||
`/openapi.json` endpoints are also available without a token; they do not
|
||||
perform host mutations. Tokens must be visible ASCII with no whitespace.
|
||||
Use a protected environment file or secret manager rather than recording a
|
||||
real token in shell history; do not share tokens or guest passwords in reports.
|
||||
The API token is required even for a loopback server. All VM, diagnostics, and
|
||||
installation routes require it. The dashboard shell, liveness route, and FastAPI's
|
||||
informational `/docs`, `/redoc`, and `/openapi.json` endpoints are available
|
||||
without a token; they do not perform host mutations. The dashboard keeps an
|
||||
entered token only for the active browser session. Tokens must be visible ASCII
|
||||
with no whitespace. Use a protected environment file or secret manager rather
|
||||
than recording a real token in shell history; do not share tokens or guest
|
||||
passwords in reports.
|
||||
|
||||
### Start a TLS Server
|
||||
|
||||
@@ -675,16 +679,26 @@ curl --cacert /path/to/ca.pem \
|
||||
|
||||
| Method | Path | Operation |
|
||||
|---|---|---|
|
||||
| `GET` | `/` | Redirect to `/dashboard` |
|
||||
| `GET` | `/dashboard` | Serve the local management dashboard |
|
||||
| `GET` | `/health` | Unauthenticated liveness |
|
||||
| `GET` | `/diagnostics` | Run host readiness checks |
|
||||
| `POST` | `/install` | Install/refresh with a `force` boolean |
|
||||
| `GET` | `/vms` | List VM responses |
|
||||
| `POST` | `/vms` | Create and boot, HTTP 201 on success |
|
||||
| `POST` | `/vms/actions` | Run selected/all VM lifecycle actions from a fixed snapshot |
|
||||
| `GET` | `/vms/{identifier}` | Inspect by exact ID, name, or guest IP |
|
||||
| `POST` | `/vms/{identifier}/stop` | Stop and retain disk, using the default TERM wait |
|
||||
| `DELETE` | `/vms/{identifier}` | Delete VM and private disk |
|
||||
| `POST` | `/vms/{identifier}/start` | Start a retained VM |
|
||||
| `POST` | `/vms/{identifier}/restart` | Restart a VM; accepts `timeout` query parameter |
|
||||
| `POST` | `/vms/{identifier}/stop` | Stop and retain disk; accepts `timeout` query parameter |
|
||||
| `DELETE` | `/vms/{identifier}` | Delete VM and private disk; accepts `timeout` query parameter |
|
||||
| `POST` | `/vms/{vm_id}/ports` | Set, open, or close private ports for an exact VM ID |
|
||||
| `POST` | `/vms/{vm_id}/ports/sync` | Reapply the policy for an exact VM ID |
|
||||
| `GET` | `/vms/{identifier}/logs` | Read a bounded, non-following log tail |
|
||||
|
||||
These stop/delete endpoints do not expose the CLI timeout, dry-run, or bulk
|
||||
flags and do not prompt for confirmation.
|
||||
Individual lifecycle requests do not expose a CLI dry-run flag and do not prompt
|
||||
for confirmation. `timeout` is an optional finite, non-negative query parameter
|
||||
on restart, stop, and delete; it has the same SIGTERM wait semantics as the CLI.
|
||||
|
||||
### Health, List, and Inspect
|
||||
|
||||
@@ -739,9 +753,19 @@ The response never includes the password. Omitting credentials still uses
|
||||
labels return HTTP `422`. Example passwords above are placeholders; real
|
||||
secrets in curl arguments can enter shell history and process listings.
|
||||
|
||||
### Stop and Delete
|
||||
### Start, Restart, Stop, and Delete
|
||||
|
||||
```sh
|
||||
# Start a retained private disk.
|
||||
curl -X POST \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
"$UVM_API_URL/vms/api-web/start"
|
||||
|
||||
# Restart with an explicit SIGTERM wait.
|
||||
curl -X POST \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
"$UVM_API_URL/vms/api-web/restart?timeout=10"
|
||||
|
||||
curl -X POST \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
"$UVM_API_URL/vms/api-web/stop"
|
||||
@@ -753,8 +777,95 @@ curl -X DELETE \
|
||||
```
|
||||
|
||||
Stop returns the VM response with `status: "stopped"`; delete returns its
|
||||
exact `id` and `status: "terminated"`. Start the retained disk through the
|
||||
local CLI, not a new HTTP route.
|
||||
exact `id` and `status: "terminated"`. Start and restart return the updated VM
|
||||
response. Start preserves the retained private disk and does not restore guest
|
||||
memory.
|
||||
|
||||
### Bulk Lifecycle Actions
|
||||
|
||||
The dashboard batch toolbar uses this endpoint for selected VMs. The server
|
||||
resolves the selected identifiers into a fixed ID snapshot before it invokes the
|
||||
existing serialized bulk lifecycle service.
|
||||
|
||||
```sh
|
||||
# Restart two named VMs with a shared TERM wait.
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
-d '{"action":"restart","identifiers":["api-web","worker"],"timeout":10}' \
|
||||
"$UVM_API_URL/vms/actions"
|
||||
|
||||
# Sync managed policies for every VM currently in the registry.
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
-d '{"action":"ports-sync","all_vms":true}' \
|
||||
"$UVM_API_URL/vms/actions"
|
||||
```
|
||||
|
||||
Valid actions are `start`, `stop`, `restart`, `delete`, and `ports-sync`.
|
||||
Provide `identifiers` or `all_vms: true`, but not both. The response contains
|
||||
one `{id, action, outcome, error}` object per selected VM. HTTP bulk requests
|
||||
do not prompt for confirmation or provide a dry-run; require an explicit
|
||||
operator confirmation in a caller before destructive use.
|
||||
|
||||
### Manage Private Ports
|
||||
|
||||
The port mutation and sync routes require an **exact VM ID** returned by create
|
||||
or list. They deliberately do not resolve a name or guest IP, preventing a
|
||||
later name/IP reuse from changing the wrong policy. Port rules are strict JSON
|
||||
objects, not CLI colon strings.
|
||||
|
||||
```sh
|
||||
# Replace the full managed TCP allowlist.
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
-d '{"mode":"set","ports":[{"port":22,"protocol":"TCP"},{"port":443,"protocol":"HTTPS"}]}' \
|
||||
"$UVM_API_URL/vms/<vm-id>/ports"
|
||||
|
||||
# Add or remove one or more rules without replacing the other rules.
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
-d '{"mode":"open","ports":[{"port":80,"protocol":"HTTP"}]}' \
|
||||
"$UVM_API_URL/vms/<vm-id>/ports"
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
-d '{"mode":"close","ports":[{"port":80,"protocol":"HTTP"}]}' \
|
||||
"$UVM_API_URL/vms/<vm-id>/ports"
|
||||
|
||||
# Reapply a policy after an external firewall change.
|
||||
curl -X POST \
|
||||
-H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
"$UVM_API_URL/vms/<vm-id>/ports/sync"
|
||||
```
|
||||
|
||||
`set` can use an empty `ports` array to close every managed service. A legacy
|
||||
VM with `ports: null` must be stopped and use `set` before it can start or use
|
||||
the other policy operations. `open`, `close`, and `sync` reject a legacy policy.
|
||||
Mutating a live policy is subject to the same containment and confirmation rules
|
||||
as the CLI.
|
||||
|
||||
### Diagnostics and Log Tails
|
||||
|
||||
The dashboard's System view calls the protected diagnostics endpoint. It runs
|
||||
the same host-readiness checks as `uvm doctor`, but does not repair a host or
|
||||
change VM state.
|
||||
|
||||
```sh
|
||||
curl -H "X-UVM-Token: $UVM_API_TOKEN" "$UVM_API_URL/diagnostics"
|
||||
|
||||
# Read the final 200 lines without opening a streaming follow request.
|
||||
curl -H "X-UVM-Token: $UVM_API_TOKEN" \
|
||||
"$UVM_API_URL/vms/api-web/logs?tail=200"
|
||||
```
|
||||
|
||||
Log responses have `content` and `truncated` fields. `tail` is an integer from
|
||||
0 through 1000, defaulting to 100. Reads are limited to the final 256 KiB of a
|
||||
log; `truncated: true` means an oversized line or the read bound prevented a
|
||||
complete requested tail. Use `uvm logs VM --follow` for continuous output.
|
||||
|
||||
### Install or Refresh Through the API
|
||||
|
||||
|
||||
Reference in New Issue
Block a user