All checks were successful
continuous-integration/drone/push Build is passing
174 lines
7.7 KiB
Plaintext
174 lines
7.7 KiB
Plaintext
package views
|
|
|
|
import (
|
|
"fmt"
|
|
"vctp/components/core"
|
|
)
|
|
|
|
type VmTraceEntry struct {
|
|
Snapshot string
|
|
RawTime int64
|
|
Name string
|
|
VmId string
|
|
VmUuid string
|
|
Vcenter string
|
|
ResourcePool string
|
|
VcpuCount int64
|
|
RamGB int64
|
|
ProvisionedDisk float64
|
|
CreationTime string
|
|
DeletionTime string
|
|
}
|
|
|
|
type VmTraceChart struct {
|
|
PointsVcpu string
|
|
PointsRam string
|
|
PointsTin string
|
|
PointsBronze string
|
|
PointsSilver string
|
|
PointsGold string
|
|
Width int
|
|
Height int
|
|
GridX []float64
|
|
GridY []float64
|
|
XTicks []ChartTick
|
|
YTicks []ChartTick
|
|
}
|
|
|
|
templ VmTracePage(query string, display_query string, vm_id string, vm_uuid string, vm_name string, creationLabel string, deletionLabel string, creationApprox bool, entries []VmTraceEntry, chart VmTraceChart) {
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
@core.Header()
|
|
<body class="flex flex-col min-h-screen web2-bg">
|
|
<main class="flex-grow web2-shell space-y-8 max-w-screen-2xl mx-auto" style="max-width: 1400px;">
|
|
<section class="web2-header">
|
|
<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
|
<div>
|
|
<div class="web2-pill">VM Trace</div>
|
|
<h1 class="mt-3 text-4xl font-bold">Snapshot history{display_query}</h1>
|
|
<p class="mt-2 text-sm text-slate-600">Timeline of vCPU, RAM, and resource pool changes across snapshots.</p>
|
|
</div>
|
|
<div class="flex gap-3 flex-wrap">
|
|
<a class="web2-button" href="/">Dashboard</a>
|
|
</div>
|
|
</div>
|
|
<form method="get" action="/vm/trace" class="mt-4 grid gap-3 md:grid-cols-3">
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-sm text-slate-600" for="vm_id">VM ID</label>
|
|
<input class="web2-card border border-slate-200 px-3 py-2 rounded" type="text" id="vm_id" name="vm_id" value={vm_id} placeholder="vm-12345"/>
|
|
</div>
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-sm text-slate-600" for="vm_uuid">VM UUID</label>
|
|
<input class="web2-card border border-slate-200 px-3 py-2 rounded" type="text" id="vm_uuid" name="vm_uuid" value={vm_uuid} placeholder="uuid..."/>
|
|
</div>
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-sm text-slate-600" for="name">Name</label>
|
|
<input class="web2-card border border-slate-200 px-3 py-2 rounded" type="text" id="name" name="name" value={vm_name} placeholder="VM name"/>
|
|
</div>
|
|
<div class="md:col-span-3 flex gap-2">
|
|
<button class="web3-button active" type="submit">Load VM Trace</button>
|
|
<a class="web3-button" href="/vm/trace">Clear</a>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="web2-card">
|
|
<div class="flex items-center justify-between gap-3 mb-4 flex-wrap">
|
|
<h2 class="text-lg font-semibold">Snapshot Timeline</h2>
|
|
<span class="web2-badge">{len(entries)} samples</span>
|
|
</div>
|
|
if chart.PointsVcpu != "" {
|
|
<div class="mb-6 overflow-auto">
|
|
<svg width="100%" height="360" viewBox={"0 0 " + fmt.Sprintf("%d", chart.Width) + " 320"} role="img" aria-label="VM timeline">
|
|
<rect x="40" y="10" width={fmt.Sprintf("%d", chart.Width-60)} height={fmt.Sprintf("%d", chart.Height)} fill="white" stroke="#e2e8f0"></rect>
|
|
<g stroke="#e2e8f0" stroke-width="1" stroke-dasharray="2,4">
|
|
for _, y := range chart.GridY {
|
|
<line x1="40" y1={fmt.Sprintf("%.1f", y)} x2={fmt.Sprintf("%d", chart.Width-20)} y2={fmt.Sprintf("%.1f", y)} />
|
|
}
|
|
for _, x := range chart.GridX {
|
|
<line x1={fmt.Sprintf("%.1f", x)} y1="10" x2={fmt.Sprintf("%.1f", x)} y2={fmt.Sprintf("%d", chart.Height+10)} />
|
|
}
|
|
</g>
|
|
<line x1="40" y1={fmt.Sprintf("%d", chart.Height+10)} x2={fmt.Sprintf("%d", chart.Width-20)} y2={fmt.Sprintf("%d", chart.Height+10)} stroke="#94a3b8" stroke-width="1.5"></line>
|
|
<line x1="40" y1="10" x2="40" y2={fmt.Sprintf("%d", chart.Height+10)} stroke="#94a3b8" stroke-width="1.5"></line>
|
|
<polyline points={chart.PointsVcpu} fill="none" stroke="#2563eb" stroke-width="2.5"></polyline>
|
|
<polyline points={chart.PointsRam} fill="none" stroke="#16a34a" stroke-width="2.5"></polyline>
|
|
<polyline points={chart.PointsTin} fill="none" stroke="#0ea5e9" stroke-width="1.5" stroke-dasharray="4,4"></polyline>
|
|
<polyline points={chart.PointsBronze} fill="none" stroke="#a855f7" stroke-width="1.5" stroke-dasharray="4,4"></polyline>
|
|
<polyline points={chart.PointsSilver} fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4,4"></polyline>
|
|
<polyline points={chart.PointsGold} fill="none" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="4,4"></polyline>
|
|
<g font-size="10" fill="#475569" text-anchor="end">
|
|
for _, tick := range chart.YTicks {
|
|
<text x="36" y={fmt.Sprintf("%.1f", tick.Pos+3)}>{tick.Label}</text>
|
|
}
|
|
</g>
|
|
<g font-size="10" fill="#475569" text-anchor="middle">
|
|
for _, tick := range chart.XTicks {
|
|
<text x={fmt.Sprintf("%.1f", tick.Pos)} y={fmt.Sprintf("%d", chart.Height+24)}>{tick.Label}</text>
|
|
}
|
|
</g>
|
|
<g font-size="12" fill="#475569" transform={"translate(40 " + fmt.Sprintf("%d", chart.Height+50) + ")"}>
|
|
<rect x="0" y="0" width="14" height="8" fill="#2563eb"></rect><text x="22" y="12">vCPU</text>
|
|
<rect x="90" y="0" width="14" height="8" fill="#16a34a"></rect><text x="112" y="12">RAM (GB)</text>
|
|
<rect x="200" y="0" width="14" height="8" fill="#0ea5e9"></rect><text x="222" y="12">Tin</text>
|
|
<rect x="260" y="0" width="14" height="8" fill="#a855f7"></rect><text x="282" y="12">Bronze</text>
|
|
<rect x="340" y="0" width="14" height="8" fill="#94a3b8"></rect><text x="362" y="12">Silver</text>
|
|
<rect x="420" y="0" width="14" height="8" fill="#f59e0b"></rect><text x="442" y="12">Gold</text>
|
|
</g>
|
|
<text x="15" y="20" transform={"rotate(-90 15 20)"} font-size="12" fill="#475569">Resources / Pool</text>
|
|
<text x={fmt.Sprintf("%d", chart.Width/2)} y={fmt.Sprintf("%d", chart.Height+70)} font-size="12" fill="#475569">Snapshots (oldest left, newest right)</text>
|
|
</svg>
|
|
</div>
|
|
}
|
|
<div class="grid gap-3 md:grid-cols-2 mb-4">
|
|
<div class="web2-card">
|
|
<p class="text-xs uppercase tracking-[0.15em] text-slate-500">Creation time</p>
|
|
<p class="mt-2 text-base font-semibold text-slate-800">{creationLabel}</p>
|
|
if creationApprox {
|
|
<p class="text-xs text-slate-500 mt-1">Approximate (earliest snapshot)</p>
|
|
}
|
|
</div>
|
|
<div class="web2-card">
|
|
<p class="text-xs uppercase tracking-[0.15em] text-slate-500">Deletion time</p>
|
|
<p class="mt-2 text-base font-semibold text-slate-800">{deletionLabel}</p>
|
|
</div>
|
|
</div>
|
|
<div class="overflow-hidden border border-slate-200 rounded">
|
|
<table class="web2-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Snapshot</th>
|
|
<th>VM Name</th>
|
|
<th>VmId</th>
|
|
<th>VmUuid</th>
|
|
<th>Vcenter</th>
|
|
<th>Resource Pool</th>
|
|
<th class="text-right">vCPUs</th>
|
|
<th class="text-right">RAM (GB)</th>
|
|
<th class="text-right">Disk</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
for _, e := range entries {
|
|
<tr>
|
|
<td>{e.Snapshot}</td>
|
|
<td>{e.Name}</td>
|
|
<td>{e.VmId}</td>
|
|
<td>{e.VmUuid}</td>
|
|
<td>{e.Vcenter}</td>
|
|
<td>{e.ResourcePool}</td>
|
|
<td class="text-right">{e.VcpuCount}</td>
|
|
<td class="text-right">{e.RamGB}</td>
|
|
<td class="text-right">{fmt.Sprintf("%.1f", e.ProvisionedDisk)}</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</body>
|
|
@core.Footer()
|
|
</html>
|
|
}
|