b9eac14ef9
Add an optional --vmtest-web flag that starts an HTTP server showing a live dashboard for vmtest runs. The dashboard includes: - Step progress tracker showing all test phases (compile, image prep, QEMU launch, agent connect, tailscale up, test-specific steps) with status icons and elapsed times - Per-VM "virtual monitor" cards showing serial console output streamed in realtime via WebSocket - Per-NIC DHCP status (supporting multi-homed VMs like subnet routers) - Per-node Tailscale status (hidden for non-tailnet VMs) - Test status badge (Running/Passed/Failed) with live elapsed timer - Event log showing all lifecycle events chronologically Architecture follows the existing util/eventbus HTMX+WebSocket pattern: the server pushes HTML fragments with hx-swap-oob attributes over a WebSocket, and HTMX routes them to the correct DOM elements by ID. Key components: - vmstatus.go: Step tracker (Begin/End lifecycle), EventBus (pub/sub with history for late joiners), VMEvent types, NodeStatus tracking - web.go: HTTP server, WebSocket handler, template loading, ANSI-to-HTML conversion via robert-nix/ansihtml, deterministic port selection - assets/: HTML templates, CSS, HTMX library (copied from eventbus) - vnet/vnet.go: DHCP event callback on Server for observing DHCP lifecycle - qemu.go: Console log file tailing with manual offset-based reading Usage: go test ./tstest/natlab/vmtest/ --run-vm-tests --vmtest-web=:0 -v When using :0, a deterministic port based on the test name is tried first so re-runs get the same URL, falling back to OS-assigned on conflict. Updates #13038 Change-Id: I45281347b3d7af78ed9f4ff896033984f84dcb4d Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
40 lines
1.5 KiB
HTML
40 lines
1.5 KiB
HTML
{{if eq .Type "test_status"}}
|
|
<span class="test-status test-{{.Message}}" id="test-status" hx-swap-oob="outerHTML">{{.Message}} ({{.Detail}})</span>
|
|
{{end}}
|
|
|
|
{{if eq .Type "step_changed"}}
|
|
<div class="step step-{{.Step.Status}}" id="step-{{.Step.Index}}" hx-swap-oob="outerHTML">
|
|
<span class="step-icon">{{.Step.Status.Icon}}</span>
|
|
<span class="step-name">{{.Step.Name}}</span>
|
|
<span class="step-time">{{formatDuration .Step.Elapsed}}</span>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if eq .Type "console_output"}}
|
|
<div id="console-{{.NodeName}}" hx-swap-oob="beforeend">{{ansi .Message}}
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if eq .Type "dhcp_discover"}}
|
|
<span id="dhcp-{{.NodeName}}-{{.NIC}}" hx-swap-oob="innerHTML">Discover sent</span>
|
|
{{end}}
|
|
|
|
{{if eq .Type "dhcp_offer"}}
|
|
<span id="dhcp-{{.NodeName}}-{{.NIC}}" hx-swap-oob="innerHTML">Offered {{.Detail}}</span>
|
|
{{end}}
|
|
|
|
{{if eq .Type "dhcp_request"}}
|
|
<span id="dhcp-{{.NodeName}}-{{.NIC}}" hx-swap-oob="innerHTML">Requesting {{.Detail}}</span>
|
|
{{end}}
|
|
|
|
{{if eq .Type "dhcp_ack"}}
|
|
<span id="dhcp-{{.NodeName}}-{{.NIC}}" hx-swap-oob="innerHTML">Got {{.Detail}}</span>
|
|
{{end}}
|
|
|
|
{{if eq .Type "tailscale"}}
|
|
<span id="ts-{{.NodeName}}" hx-swap-oob="innerHTML">{{.Detail}}</span>
|
|
{{end}}
|
|
|
|
<div id="events" hx-swap-oob="beforeend"><div class="event event-{{.Type}}"><span class="event-time">{{.Time.Format "15:04:05.000"}}</span> {{if .NodeName}}<span class="event-node">[{{.NodeName}}]</span> {{end}}<span class="event-msg">{{.Message}}</span>{{if .Detail}} <span class="event-detail">{{.Detail}}</span>{{end}}</div>
|
|
</div>
|