Totals for {vcenter}
-{meta.TypeLabel} snapshots of VM count, vCPU, and RAM over time.
-Totals for { vcenter }
+{ meta.TypeLabel } snapshots of VM count, vCPU, and RAM over time.
{ meta.TypeLabel } Snapshots
+ { len(entries) } records +| {entry.Snapshot} | -{entry.VmCount} | -{entry.VcpuTotal} | -{entry.RamTotalGB} | +{ entry.Snapshot } | +{ entry.VmCount } | +{ entry.VcpuTotal } | +{ entry.RamTotalGB } |
| Snapshot Time | VMs | vCPUs | RAM (GB) |
|---|---|---|---|
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var31 string
- templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs("0 0 " + fmt.Sprintf("%d", chart.Width) + " " + fmt.Sprintf("%d", chart.Height+70))
+ templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Snapshot)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 211, Col: 160}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 217, Col: 30}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\" role=\"img\" aria-label=\"Totals over time\">| ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var32 string
- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Width-60))
+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(entry.VmCount)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 217, Col: 68}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 218, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\" height=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, " | ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var33 string
- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height))
+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(entry.VcpuTotal)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 217, Col: 109}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 219, Col: 50}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\" fill=\"white\" stroke=\"#e2e8f0\"> | ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, y := range chart.GridY {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, " | |
| Snapshot Time | VMs | vCPUs | RAM (GB) |
|---|---|---|---|
| ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var56 string - templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Snapshot) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 271, Col: 29} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var57 string - templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(entry.VmCount) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 272, Col: 47} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var58 string - templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(entry.VcpuTotal) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 273, Col: 49} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var59 string - templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(entry.RamTotalGB) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/snapshots.templ`, Line: 274, Col: 50} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, " |
Snapshot Timeline
{len(entries)} samplesSnapshot history")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "VM TraceSnapshot history")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(display_query)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 48, Col: 74}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 37, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
@@ -92,7 +81,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(vm_id)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 58, Col: 123}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 47, Col: 123}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@@ -105,7 +94,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(vm_uuid)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 62, Col: 129}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 51, Col: 129}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
@@ -118,7 +107,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(vm_name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 66, Col: 123}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 55, Col: 123}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
@@ -131,7 +120,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(len(entries))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 78, Col: 44}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 67, Col: 44}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
@@ -141,576 +130,189 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- if chart.PointsVcpu != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var8 string
- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Width-60))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 83, Col: 68}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "Creation time
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var8 string
+ templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(creationLabel)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 86, Col: 76}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if creationApprox {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "Approximate (earliest snapshot)
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" height=\"")
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "Deletion time
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var9 string
+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(deletionLabel)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 93, Col: 76}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
Snapshot VM Name VmId VmUuid Vcenter Resource Pool vCPUs RAM (GB) Disk ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, e := range entries {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var9 string
- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height))
+ var templ_7745c5c3_Var10 string
+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(e.Snapshot)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 83, Col: 109}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 114, Col: 25}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" fill=\"white\" stroke=\"#e2e8f0\"> ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, y := range chart.GridY {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
+ var templ_7745c5c3_Var11 string
+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 115, Col: 21}
}
- for _, x := range chart.GridX {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var12 string
+ templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmId)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 116, Col: 21}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var13 string
+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmUuid)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 117, Col: 23}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var14 string
+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(e.Vcenter)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 118, Col: 24}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var15 string
+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(e.ResourcePool)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 119, Col: 29}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height+10))
+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(e.VcpuCount)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 60}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 120, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" x2=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Width-20))
+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(e.RamGB)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 99}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 121, Col: 41}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" y2=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
- templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height+10))
+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", e.ProvisionedDisk))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 139}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 122, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" stroke=\"#94a3b8\" stroke-width=\"1.5\"> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, tick := range chart.YTicks {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var27 string
- templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(tick.Label)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 102, Col: 70}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, tick := range chart.XTicks {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var30 string
- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(tick.Label)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 107, Col: 100}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, " vCPU RAM (GB) Tin Bronze Silver Gold Resources / Pool Snapshots (oldest left, newest right) ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "Creation time
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var35 string
- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(creationLabel)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 126, Col: 76}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if creationApprox {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "Approximate (earliest snapshot)
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "Deletion time
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var36 string
- templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(deletionLabel)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 133, Col: 76}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "
Snapshot VM Name VmId VmUuid Vcenter Resource Pool vCPUs RAM (GB) Disk ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, e := range entries {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var37 string
- templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(e.Snapshot)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 154, Col: 25}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var38 string
- templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 155, Col: 21}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var39 string
- templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmId)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 156, Col: 21}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var40 string
- templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmUuid)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 157, Col: 23}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var41 string
- templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(e.Vcenter)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 158, Col: 24}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var42 string
- templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(e.ResourcePool)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 159, Col: 29}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var43 string
- templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(e.VcpuCount)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 160, Col: 45}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var44 string
- templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(e.RamGB)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 161, Col: 41}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var45 string
- templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", e.ProvisionedDisk))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 162, Col: 72}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -718,7 +320,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/dist/assets/css/web3.css b/dist/assets/css/web3.css
index bcfe36e..147a79d 100644
--- a/dist/assets/css/web3.css
+++ b/dist/assets/css/web3.css
@@ -17,6 +17,9 @@ body {
margin: 0 auto;
padding: 2rem 1.5rem 4rem;
}
+.web2-shell-wide {
+ max-width: 1400px;
+}
.web2-header {
background: var(--web2-card);
border: 1px solid var(--web2-border);
@@ -176,3 +179,68 @@ body {
color: var(--web2-muted);
background: #f8fafc;
}
+.web3-chart-frame {
+ position: relative;
+ min-width: 760px;
+ width: 100%;
+}
+.web3-chart-canvas {
+ display: block;
+ width: 100%;
+ height: 360px;
+ background: #ffffff;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+}
+.web3-chart-tooltip {
+ position: absolute;
+ left: 0;
+ top: 0;
+ opacity: 0;
+ pointer-events: none;
+ background: rgba(15, 23, 42, 0.95);
+ color: #f8fafc;
+ padding: 0.55rem 0.65rem;
+ border-radius: 6px;
+ font-size: 0.75rem;
+ line-height: 1.35;
+ min-width: 170px;
+ box-shadow: 0 10px 30px rgba(2, 6, 23, 0.25);
+ z-index: 20;
+ transition: opacity 0.08s linear;
+}
+.web3-chart-tooltip.visible {
+ opacity: 1;
+}
+.web3-chart-tooltip-title {
+ font-weight: 700;
+ color: #e2e8f0;
+ margin-bottom: 0.35rem;
+}
+.web3-chart-tooltip-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.65rem;
+}
+.web3-chart-tooltip-label {
+ display: inline-flex;
+ align-items: center;
+ color: #cbd5e1;
+}
+.web3-chart-tooltip-value {
+ font-weight: 700;
+ color: #f8fafc;
+}
+.web3-chart-tooltip-swatch {
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 999px;
+ margin-right: 0.35rem;
+}
+@media (max-width: 900px) {
+ .web3-chart-frame {
+ min-width: 640px;
+ }
+}
diff --git a/dist/assets/js/web3-charts.js b/dist/assets/js/web3-charts.js
new file mode 100644
index 0000000..87f36dc
--- /dev/null
+++ b/dist/assets/js/web3-charts.js
@@ -0,0 +1,521 @@
+(function () {
+ "use strict";
+
+ function clamp(value, min, max) {
+ if (value < min) {
+ return min;
+ }
+ if (value > max) {
+ return max;
+ }
+ return value;
+ }
+
+ function toNumber(value) {
+ var num = Number(value);
+ return Number.isFinite(num) ? num : null;
+ }
+
+ function escapeHTML(value) {
+ return String(value)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ }
+
+ function formatValue(value, format) {
+ if (value === null || value === undefined || Number.isNaN(value)) {
+ return "-";
+ }
+ switch (format) {
+ case "int":
+ return String(Math.round(value));
+ case "float1":
+ return Number(value).toFixed(1);
+ case "float2":
+ return Number(value).toFixed(2);
+ default:
+ return String(value);
+ }
+ }
+
+ function pickTickIndices(total, desired) {
+ if (total <= 0) {
+ return [];
+ }
+ if (total === 1) {
+ return [0];
+ }
+ var target = Math.max(2, Math.min(total, desired || 6));
+ if (target >= total) {
+ var all = [];
+ for (var i = 0; i < total; i++) {
+ all.push(i);
+ }
+ return all;
+ }
+ var indices = [0];
+ var step = (total - 1) / (target - 1);
+ for (var j = 1; j < target - 1; j++) {
+ indices.push(Math.round(j * step));
+ }
+ indices.push(total - 1);
+ var seen = {};
+ var deduped = [];
+ for (var k = 0; k < indices.length; k++) {
+ var idx = indices[k];
+ if (!seen[idx]) {
+ seen[idx] = true;
+ deduped.push(idx);
+ }
+ }
+ deduped.sort(function (a, b) {
+ return a - b;
+ });
+ return deduped;
+ }
+
+ function getPlotBounds(width, height) {
+ return {
+ left: 52,
+ top: 16,
+ right: width - 20,
+ bottom: height - 78,
+ };
+ }
+
+ function buildScales(config, plot) {
+ var maxY = 0;
+ for (var i = 0; i < config.series.length; i++) {
+ var values = config.series[i].values || [];
+ for (var j = 0; j < values.length; j++) {
+ var value = toNumber(values[j]);
+ if (value !== null && value > maxY) {
+ maxY = value;
+ }
+ }
+ }
+ if (maxY <= 0) {
+ maxY = 1;
+ }
+ var count = config.labels.length;
+ var xSpan = plot.right - plot.left;
+ var ySpan = plot.bottom - plot.top;
+
+ return {
+ maxY: maxY,
+ xForIndex: function (index) {
+ if (count <= 1) {
+ return plot.left;
+ }
+ return plot.left + (index / (count - 1)) * xSpan;
+ },
+ yForValue: function (value) {
+ var numeric = toNumber(value);
+ if (numeric === null) {
+ return null;
+ }
+ return plot.bottom - (numeric / maxY) * ySpan;
+ },
+ };
+ }
+
+ function drawGrid(ctx, plot, config, scales) {
+ ctx.save();
+ ctx.strokeStyle = "#e2e8f0";
+ ctx.lineWidth = 1;
+ ctx.setLineDash([2, 4]);
+
+ var yTickCount = Math.max(2, config.yTicks || 5);
+ for (var i = 0; i < yTickCount; i++) {
+ var yRatio = i / (yTickCount - 1);
+ var y = plot.top + yRatio * (plot.bottom - plot.top);
+ ctx.beginPath();
+ ctx.moveTo(plot.left, y);
+ ctx.lineTo(plot.right, y);
+ ctx.stroke();
+ }
+
+ var xIndices = pickTickIndices(config.labels.length, config.xTicks || 6);
+ for (var j = 0; j < xIndices.length; j++) {
+ var x = scales.xForIndex(xIndices[j]);
+ ctx.beginPath();
+ ctx.moveTo(x, plot.top);
+ ctx.lineTo(x, plot.bottom);
+ ctx.stroke();
+ }
+ ctx.restore();
+ }
+
+ function drawAxes(ctx, plot) {
+ ctx.save();
+ ctx.strokeStyle = "#94a3b8";
+ ctx.lineWidth = 1.5;
+ ctx.setLineDash([]);
+
+ ctx.beginPath();
+ ctx.moveTo(plot.left, plot.bottom);
+ ctx.lineTo(plot.right, plot.bottom);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(plot.left, plot.top);
+ ctx.lineTo(plot.left, plot.bottom);
+ ctx.stroke();
+ ctx.restore();
+ }
+
+ function drawLabels(ctx, plot, config, scales) {
+ ctx.save();
+ ctx.fillStyle = "#475569";
+ ctx.font = "10px sans-serif";
+
+ var yTickCount = Math.max(2, config.yTicks || 5);
+ for (var i = 0; i < yTickCount; i++) {
+ var ratio = i / (yTickCount - 1);
+ var y = plot.top + ratio * (plot.bottom - plot.top);
+ var value = scales.maxY * (1 - ratio);
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ ctx.fillText(formatValue(value, "int"), plot.left - 8, y);
+ }
+
+ var xIndices = pickTickIndices(config.labels.length, config.xTicks || 6);
+ for (var j = 0; j < xIndices.length; j++) {
+ var idx = xIndices[j];
+ var tick = (config.tickLabels && config.tickLabels[idx]) || config.labels[idx] || "";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "top";
+ ctx.fillText(tick, scales.xForIndex(idx), plot.bottom + 12);
+ }
+
+ if (config.yLabel) {
+ ctx.save();
+ ctx.translate(16, plot.top + (plot.bottom-plot.top)/2);
+ ctx.rotate(-Math.PI / 2);
+ ctx.textAlign = "center";
+ ctx.textBaseline = "top";
+ ctx.font = "12px sans-serif";
+ ctx.fillText(config.yLabel, 0, 0);
+ ctx.restore();
+ }
+
+ if (config.xLabel) {
+ ctx.textAlign = "center";
+ ctx.textBaseline = "top";
+ ctx.font = "12px sans-serif";
+ ctx.fillText(config.xLabel, plot.left + (plot.right-plot.left)/2, plot.bottom + 48);
+ }
+ ctx.restore();
+ }
+
+ function drawSeries(ctx, plot, config, scales) {
+ for (var i = 0; i < config.series.length; i++) {
+ var series = config.series[i];
+ var values = series.values || [];
+ if (!values.length) {
+ continue;
+ }
+ ctx.save();
+ ctx.strokeStyle = series.color || "#2563eb";
+ ctx.lineWidth = series.lineWidth || 2.5;
+ ctx.setLineDash(Array.isArray(series.dash) ? series.dash : []);
+ ctx.beginPath();
+ var moved = false;
+ for (var j = 0; j < values.length; j++) {
+ var y = scales.yForValue(values[j]);
+ if (y === null) {
+ continue;
+ }
+ var x = scales.xForIndex(j);
+ if (!moved) {
+ ctx.moveTo(x, y);
+ moved = true;
+ } else {
+ ctx.lineTo(x, y);
+ }
+ }
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+
+ function drawLegend(ctx, config, width, height) {
+ var x = 52;
+ var y = height - 32;
+
+ ctx.save();
+ ctx.font = "12px sans-serif";
+ ctx.textBaseline = "middle";
+ for (var i = 0; i < config.series.length; i++) {
+ var series = config.series[i];
+ var label = series.name || "Series";
+ ctx.strokeStyle = series.color || "#2563eb";
+ ctx.fillStyle = "#475569";
+ ctx.lineWidth = series.lineWidth || 2.5;
+ ctx.setLineDash(Array.isArray(series.dash) ? series.dash : []);
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + 16, y);
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.fillText(label, x + 22, y);
+ x += 22 + ctx.measureText(label).width + 18;
+ if (x > width - 160) {
+ x = 52;
+ y += 18;
+ }
+ }
+ ctx.restore();
+ }
+
+ function updateTooltip(state, config) {
+ if (!state.tooltip) {
+ return;
+ }
+ if (state.hoverIndex === null) {
+ state.tooltip.classList.remove("visible");
+ state.tooltip.setAttribute("aria-hidden", "true");
+ return;
+ }
+
+ var idx = state.hoverIndex;
+ var rows = [];
+ rows.push('' + escapeHTML(config.labels[idx] || "") + "");
+ for (var i = 0; i < config.series.length; i++) {
+ var series = config.series[i];
+ if (series.tooltipHidden) {
+ continue;
+ }
+ var values = series.values || [];
+ var value = toNumber(values[idx]);
+ var valueLabel = formatValue(value, series.tooltipFormat || "int");
+ rows.push(
+ '' +
+ '' +
+ escapeHTML(series.name || "Series") +
+ "" +
+ '' + escapeHTML(valueLabel) + "" +
+ ""
+ );
+ }
+
+ var hoverRows = config.hoverRows || [];
+ for (var j = 0; j < hoverRows.length; j++) {
+ var hover = hoverRows[j];
+ var values = hover.values || [];
+ var label = values[idx] || "-";
+ rows.push(
+ '' +
+ '' + escapeHTML(hover.name || "Value") + "" +
+ '' + escapeHTML(label) + "" +
+ ""
+ );
+ }
+
+ state.tooltip.innerHTML = rows.join("");
+ state.tooltip.classList.add("visible");
+ state.tooltip.setAttribute("aria-hidden", "false");
+
+ var box = state.wrapper.getBoundingClientRect();
+ var tooltipBox = state.tooltip.getBoundingClientRect();
+ var left = clamp(state.mouseX + 14, 4, box.width - tooltipBox.width - 4);
+ var top = clamp(state.mouseY + 14, 4, box.height - tooltipBox.height - 4);
+ state.tooltip.style.left = left + "px";
+ state.tooltip.style.top = top + "px";
+ }
+
+ function drawHover(ctx, plot, config, scales, hoverIndex) {
+ if (hoverIndex === null) {
+ return;
+ }
+ var x = scales.xForIndex(hoverIndex);
+ ctx.save();
+ ctx.strokeStyle = "#94a3b8";
+ ctx.lineWidth = 1;
+ ctx.setLineDash([3, 4]);
+ ctx.beginPath();
+ ctx.moveTo(x, plot.top);
+ ctx.lineTo(x, plot.bottom);
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ for (var i = 0; i < config.series.length; i++) {
+ var series = config.series[i];
+ var values = series.values || [];
+ var y = scales.yForValue(values[hoverIndex]);
+ if (y === null) {
+ continue;
+ }
+ ctx.fillStyle = "#ffffff";
+ ctx.strokeStyle = series.color || "#2563eb";
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.arc(x, y, 3.5, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.stroke();
+ }
+ ctx.restore();
+ }
+
+ function renderLineChart(options) {
+ if (!options || !options.canvasId || !options.config) {
+ return;
+ }
+ var canvas = document.getElementById(options.canvasId);
+ if (!canvas) {
+ return;
+ }
+ var config = options.config;
+ if (!Array.isArray(config.labels) || config.labels.length === 0 || !Array.isArray(config.series) || config.series.length === 0) {
+ return;
+ }
+ var wrapper = canvas.parentElement;
+ var tooltip = options.tooltipId ? document.getElementById(options.tooltipId) : null;
+ var ctx = canvas.getContext("2d");
+ if (!ctx) {
+ return;
+ }
+
+ var state = {
+ canvas: canvas,
+ wrapper: wrapper,
+ tooltip: tooltip,
+ hoverIndex: null,
+ mouseX: 0,
+ mouseY: 0,
+ scales: null,
+ plot: null,
+ cssWidth: 0,
+ cssHeight: config.height || 360,
+ };
+
+ function redraw() {
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ var dpr = window.devicePixelRatio || 1;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ ctx.fillStyle = "#ffffff";
+ ctx.fillRect(0, 0, state.cssWidth, state.cssHeight);
+
+ drawGrid(ctx, state.plot, config, state.scales);
+ drawAxes(ctx, state.plot);
+ drawSeries(ctx, state.plot, config, state.scales);
+ drawLabels(ctx, state.plot, config, state.scales);
+ drawLegend(ctx, config, state.cssWidth, state.cssHeight);
+ drawHover(ctx, state.plot, config, state.scales, state.hoverIndex);
+ updateTooltip(state, config);
+ }
+
+ function resize() {
+ var rect = canvas.getBoundingClientRect();
+ var width = Math.max(320, Math.floor(rect.width));
+ var height = config.height || 360;
+ var dpr = window.devicePixelRatio || 1;
+
+ canvas.width = Math.round(width * dpr);
+ canvas.height = Math.round(height * dpr);
+ canvas.style.height = height + "px";
+
+ state.cssWidth = width;
+ state.cssHeight = height;
+ state.plot = getPlotBounds(width, height);
+ state.scales = buildScales(config, state.plot);
+ redraw();
+ }
+
+ canvas.addEventListener("mousemove", function (event) {
+ var rect = canvas.getBoundingClientRect();
+ var x = event.clientX - rect.left;
+ var y = event.clientY - rect.top;
+ state.mouseX = x;
+ state.mouseY = y;
+
+ if (!state.plot || config.labels.length === 0) {
+ return;
+ }
+ if (x < state.plot.left || x > state.plot.right || y < state.plot.top || y > state.plot.bottom) {
+ state.hoverIndex = null;
+ redraw();
+ return;
+ }
+
+ var ratio = (x - state.plot.left) / (state.plot.right - state.plot.left);
+ var idx = Math.round(ratio * (config.labels.length - 1));
+ state.hoverIndex = clamp(idx, 0, config.labels.length - 1);
+ redraw();
+ });
+
+ canvas.addEventListener("mouseleave", function () {
+ state.hoverIndex = null;
+ redraw();
+ });
+
+ window.addEventListener("resize", resize);
+ if (window.ResizeObserver) {
+ var observer = new ResizeObserver(function () {
+ resize();
+ });
+ observer.observe(wrapper);
+ }
+
+ resize();
+ }
+
+ function renderFromScript(options) {
+ if (!options || !options.configId) {
+ return;
+ }
+ var configNode = document.getElementById(options.configId);
+ if (!configNode) {
+ return;
+ }
+ var payload = configNode.textContent || "";
+ if (!payload.trim()) {
+ return;
+ }
+ try {
+ var config = JSON.parse(payload);
+ renderLineChart({
+ canvasId: options.canvasId,
+ tooltipId: options.tooltipId,
+ config: config,
+ });
+ } catch (error) {
+ // Leave page functional even when chart config is malformed.
+ }
+ }
+
+ function renderFromDataset(options) {
+ if (!options || !options.canvasId) {
+ return;
+ }
+ var canvas = document.getElementById(options.canvasId);
+ if (!canvas) {
+ return;
+ }
+ var payload = canvas.dataset.chartConfig || "";
+ if (!payload.trim()) {
+ return;
+ }
+ try {
+ var config = JSON.parse(payload);
+ renderLineChart({
+ canvasId: options.canvasId,
+ tooltipId: options.tooltipId,
+ config: config,
+ });
+ } catch (error) {
+ // Leave page functional even when chart config is malformed.
+ }
+ }
+
+ window.Web3Charts = {
+ renderLineChart: renderLineChart,
+ renderFromScript: renderFromScript,
+ renderFromDataset: renderFromDataset,
+ };
+})();
diff --git a/server/handler/chart_builders_test.go b/server/handler/chart_builders_test.go
new file mode 100644
index 0000000..65069e0
--- /dev/null
+++ b/server/handler/chart_builders_test.go
@@ -0,0 +1,101 @@
+package handler
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+ "vctp/components/views"
+)
+
+func TestBuildVcenterChartEncodesClientConfig(t *testing.T) {
+ entries := []views.VcenterTotalsEntry{
+ {
+ RawTime: 2_000,
+ VmCount: 30,
+ VcpuTotal: 80,
+ RamTotalGB: 120,
+ },
+ {
+ RawTime: 1_000,
+ VmCount: 20,
+ VcpuTotal: 60,
+ RamTotalGB: 90,
+ },
+ }
+
+ chart := buildVcenterChart(entries)
+ if chart.ConfigJSON == "" {
+ t.Fatal("expected config json for non-empty vcenter chart")
+ }
+
+ var cfg lineChartConfig
+ if err := json.Unmarshal([]byte(chart.ConfigJSON), &cfg); err != nil {
+ t.Fatalf("failed to decode chart config json: %v", err)
+ }
+
+ if len(cfg.Labels) != 2 {
+ t.Fatalf("expected 2 labels, got %d", len(cfg.Labels))
+ }
+ expectedFirst := time.Unix(1_000, 0).Local().Format("2006-01-02 15:04:05")
+ if cfg.Labels[0] != expectedFirst {
+ t.Fatalf("expected oldest label first %q, got %q", expectedFirst, cfg.Labels[0])
+ }
+ if len(cfg.Series) != 3 {
+ t.Fatalf("expected 3 series, got %d", len(cfg.Series))
+ }
+ if cfg.Series[0].Values[0] != 20 {
+ t.Fatalf("expected first VM value 20, got %v", cfg.Series[0].Values[0])
+ }
+}
+
+func TestBuildVmTraceChartEncodesPoolState(t *testing.T) {
+ entries := []views.VmTraceEntry{
+ {
+ RawTime: 1_000,
+ ResourcePool: "Tin",
+ VcpuCount: 4,
+ RamGB: 16,
+ },
+ {
+ RawTime: 2_000,
+ ResourcePool: "Gold",
+ VcpuCount: 8,
+ RamGB: 24,
+ },
+ }
+
+ chart := buildVmTraceChart(entries)
+ if chart.ConfigJSON == "" {
+ t.Fatal("expected config json for non-empty vm trace chart")
+ }
+
+ var cfg lineChartConfig
+ if err := json.Unmarshal([]byte(chart.ConfigJSON), &cfg); err != nil {
+ t.Fatalf("failed to decode vm trace chart config: %v", err)
+ }
+
+ if len(cfg.Series) != 6 {
+ t.Fatalf("expected 6 series, got %d", len(cfg.Series))
+ }
+ if len(cfg.HoverRows) != 1 || cfg.HoverRows[0].Name != "Resource Pool" {
+ t.Fatalf("expected resource pool hover row, got %#v", cfg.HoverRows)
+ }
+ if cfg.HoverRows[0].Values[0] != "Tin" || cfg.HoverRows[0].Values[1] != "Gold" {
+ t.Fatalf("unexpected hover row values: %#v", cfg.HoverRows[0].Values)
+ }
+ if cfg.Series[2].Values[0] == 0 || cfg.Series[2].Values[1] != 0 {
+ t.Fatalf("tin series should be active only for first point: %#v", cfg.Series[2].Values)
+ }
+ if cfg.Series[5].Values[0] != 0 || cfg.Series[5].Values[1] == 0 {
+ t.Fatalf("gold series should be active only for second point: %#v", cfg.Series[5].Values)
+ }
+}
+
+func TestBuildChartsEmptyInput(t *testing.T) {
+ if chart := buildVcenterChart(nil); chart.ConfigJSON != "" {
+ t.Fatalf("expected empty config for empty vcenter input, got %q", chart.ConfigJSON)
+ }
+ if chart := buildVmTraceChart(nil); chart.ConfigJSON != "" {
+ t.Fatalf("expected empty config for empty vm trace input, got %q", chart.ConfigJSON)
+ }
+}
diff --git a/server/handler/chart_config.go b/server/handler/chart_config.go
new file mode 100644
index 0000000..110087c
--- /dev/null
+++ b/server/handler/chart_config.go
@@ -0,0 +1,41 @@
+package handler
+
+import "encoding/json"
+
+type lineChartConfig struct {
+ Height int `json:"height,omitempty"`
+ XTicks int `json:"xTicks,omitempty"`
+ YTicks int `json:"yTicks,omitempty"`
+ YLabel string `json:"yLabel,omitempty"`
+ XLabel string `json:"xLabel,omitempty"`
+ Labels []string `json:"labels"`
+ TickLabels []string `json:"tickLabels,omitempty"`
+ Series []lineChartSeries `json:"series"`
+ HoverRows []lineChartHoverRow `json:"hoverRows,omitempty"`
+}
+
+type lineChartSeries struct {
+ Name string `json:"name"`
+ Color string `json:"color"`
+ Values []float64 `json:"values"`
+ Dash []float64 `json:"dash,omitempty"`
+ LineWidth float64 `json:"lineWidth,omitempty"`
+ TooltipFormat string `json:"tooltipFormat,omitempty"`
+ TooltipHidden bool `json:"tooltipHidden,omitempty"`
+}
+
+type lineChartHoverRow struct {
+ Name string `json:"name"`
+ Values []string `json:"values"`
+}
+
+func encodeLineChartConfig(cfg lineChartConfig) string {
+ if len(cfg.Labels) == 0 || len(cfg.Series) == 0 {
+ return ""
+ }
+ out, err := json.Marshal(cfg)
+ if err != nil {
+ return ""
+ }
+ return string(out)
+}
diff --git a/server/handler/vcenters.go b/server/handler/vcenters.go
index ad5ab78..c48f01f 100644
--- a/server/handler/vcenters.go
+++ b/server/handler/vcenters.go
@@ -141,91 +141,54 @@ func buildVcenterChart(entries []views.VcenterTotalsEntry) views.VcenterChartDat
plot = append(plot, entries[i])
}
- width := 1200.0
- height := 260.0
- plotWidth := width - 60.0
- startX := 40.0
- maxVal := float64(0)
+ labels := make([]string, 0, len(plot))
+ tickLabels := make([]string, 0, len(plot))
+ vmValues := make([]float64, 0, len(plot))
+ vcpuValues := make([]float64, 0, len(plot))
+ ramValues := make([]float64, 0, len(plot))
+
for _, e := range plot {
- if float64(e.VmCount) > maxVal {
- maxVal = float64(e.VmCount)
- }
- if float64(e.VcpuTotal) > maxVal {
- maxVal = float64(e.VcpuTotal)
- }
- if float64(e.RamTotalGB) > maxVal {
- maxVal = float64(e.RamTotalGB)
- }
+ t := time.Unix(e.RawTime, 0).Local()
+ labels = append(labels, t.Format("2006-01-02 15:04:05"))
+ tickLabels = append(tickLabels, t.Format("01-02 15:04"))
+ vmValues = append(vmValues, float64(e.VmCount))
+ vcpuValues = append(vcpuValues, float64(e.VcpuTotal))
+ ramValues = append(ramValues, float64(e.RamTotalGB))
}
- if maxVal == 0 {
- maxVal = 1
- }
- stepX := plotWidth
- if len(plot) > 1 {
- stepX = plotWidth / float64(len(plot)-1)
- }
- pointsVm := ""
- pointsVcpu := ""
- pointsRam := ""
- for i, e := range plot {
- x := startX + float64(i)*stepX
- yVm := 10 + (1-(float64(e.VmCount)/maxVal))*height
- yVcpu := 10 + (1-(float64(e.VcpuTotal)/maxVal))*height
- yRam := 10 + (1-(float64(e.RamTotalGB)/maxVal))*height
- if i == 0 {
- pointsVm = fmt.Sprintf("%.1f,%.1f", x, yVm)
- pointsVcpu = fmt.Sprintf("%.1f,%.1f", x, yVcpu)
- pointsRam = fmt.Sprintf("%.1f,%.1f", x, yRam)
- } else {
- pointsVm = pointsVm + " " + fmt.Sprintf("%.1f,%.1f", x, yVm)
- pointsVcpu = pointsVcpu + " " + fmt.Sprintf("%.1f,%.1f", x, yVcpu)
- pointsRam = pointsRam + " " + fmt.Sprintf("%.1f,%.1f", x, yRam)
- }
- }
- gridX := []float64{}
- if len(plot) > 1 {
- for i := 0; i < len(plot); i++ {
- gridX = append(gridX, startX+float64(i)*stepX)
- }
- }
- gridY := []float64{}
- for i := 0; i <= 4; i++ {
- gridY = append(gridY, 10+float64(i)*(height/4))
- }
- yTicks := []views.ChartTick{}
- for i := 0; i <= 4; i++ {
- val := maxVal * float64(4-i) / 4
- pos := 10 + float64(i)*(height/4)
- yTicks = append(yTicks, views.ChartTick{Pos: pos, Label: fmt.Sprintf("%.0f", val)})
- }
- xTicks := []views.ChartTick{}
- maxTicks := 6
- stepIdx := 1
- if len(plot) > 1 {
- stepIdx = (len(plot)-1)/maxTicks + 1
- }
- for idx := 0; idx < len(plot); idx += stepIdx {
- x := startX + float64(idx)*stepX
- label := time.Unix(plot[idx].RawTime, 0).Local().Format("01-02 15:04")
- xTicks = append(xTicks, views.ChartTick{Pos: x, Label: label})
- }
- if len(plot) > 1 {
- lastIdx := len(plot) - 1
- xLast := startX + float64(lastIdx)*stepX
- labelLast := time.Unix(plot[lastIdx].RawTime, 0).Local().Format("01-02 15:04")
- if len(xTicks) == 0 || xTicks[len(xTicks)-1].Pos != xLast {
- xTicks = append(xTicks, views.ChartTick{Pos: xLast, Label: labelLast})
- }
+
+ cfg := lineChartConfig{
+ Height: 360,
+ XTicks: 6,
+ YTicks: 5,
+ YLabel: "Totals",
+ XLabel: "Snapshots (oldest left, newest right)",
+ Labels: labels,
+ TickLabels: tickLabels,
+ Series: []lineChartSeries{
+ {
+ Name: "VMs",
+ Color: "#2563eb",
+ Values: vmValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ {
+ Name: "vCPU",
+ Color: "#16a34a",
+ Values: vcpuValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ {
+ Name: "RAM (GB)",
+ Color: "#ea580c",
+ Values: ramValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ },
}
return views.VcenterChartData{
- PointsVm: pointsVm,
- PointsVcpu: pointsVcpu,
- PointsRam: pointsRam,
- Width: int(width),
- Height: int(height),
- GridX: gridX,
- GridY: gridY,
- YTicks: yTicks,
- XTicks: xTicks,
+ ConfigJSON: encodeLineChartConfig(cfg),
}
}
diff --git a/server/handler/vmTrace.go b/server/handler/vmTrace.go
index ca53858..eb5d11c 100644
--- a/server/handler/vmTrace.go
+++ b/server/handler/vmTrace.go
@@ -108,114 +108,135 @@ func buildVmTraceChart(entries []views.VmTraceEntry) views.VmTraceChart {
if len(entries) == 0 {
return views.VmTraceChart{}
}
- width := 1200.0
- height := 220.0
- plotWidth := width - 60.0
- startX := 40.0
- maxVal := float64(0)
+ maxResource := float64(0)
for _, e := range entries {
- if float64(e.VcpuCount) > maxVal {
- maxVal = float64(e.VcpuCount)
+ if float64(e.VcpuCount) > maxResource {
+ maxResource = float64(e.VcpuCount)
}
- if float64(e.RamGB) > maxVal {
- maxVal = float64(e.RamGB)
+ if float64(e.RamGB) > maxResource {
+ maxResource = float64(e.RamGB)
}
}
- if maxVal == 0 {
- maxVal = 1
+ if maxResource == 0 {
+ maxResource = 1
}
- stepX := plotWidth
- if len(entries) > 1 {
- stepX = plotWidth / float64(len(entries)-1)
- }
- scale := height / maxVal
- var ptsVcpu, ptsRam, ptsTin, ptsBronze, ptsSilver, ptsGold string
- appendPt := func(s string, x, y float64) string {
- if s == "" {
- return fmt.Sprintf("%.1f,%.1f", x, y)
+
+ tinLevel := maxResource
+ bronzeLevel := maxResource * 0.9
+ silverLevel := maxResource * 0.8
+ goldLevel := maxResource * 0.7
+
+ labels := make([]string, 0, len(entries))
+ tickLabels := make([]string, 0, len(entries))
+ vcpuValues := make([]float64, 0, len(entries))
+ ramValues := make([]float64, 0, len(entries))
+ tinValues := make([]float64, 0, len(entries))
+ bronzeValues := make([]float64, 0, len(entries))
+ silverValues := make([]float64, 0, len(entries))
+ goldValues := make([]float64, 0, len(entries))
+ poolNames := make([]string, 0, len(entries))
+
+ for _, e := range entries {
+ t := time.Unix(e.RawTime, 0).Local()
+ labels = append(labels, t.Format("2006-01-02 15:04:05"))
+ tickLabels = append(tickLabels, t.Format("01-02 15:04"))
+ vcpuValues = append(vcpuValues, float64(e.VcpuCount))
+ ramValues = append(ramValues, float64(e.RamGB))
+
+ pool := strings.TrimSpace(e.ResourcePool)
+ if pool == "" {
+ pool = "Unknown"
}
- return s + " " + fmt.Sprintf("%.1f,%.1f", x, y)
- }
- for i, e := range entries {
- x := startX + float64(i)*stepX
- yVcpu := 10 + height - float64(e.VcpuCount)*scale
- yRam := 10 + height - float64(e.RamGB)*scale
- ptsVcpu = appendPt(ptsVcpu, x, yVcpu)
- ptsRam = appendPt(ptsRam, x, yRam)
- poolY := map[string]float64{
- "tin": 10 + height - scale*maxVal,
- "bronze": 10 + height - scale*maxVal*0.9,
- "silver": 10 + height - scale*maxVal*0.8,
- "gold": 10 + height - scale*maxVal*0.7,
- }
- lower := strings.ToLower(e.ResourcePool)
+ poolNames = append(poolNames, pool)
+
+ lower := strings.ToLower(pool)
if lower == "tin" {
- ptsTin = appendPt(ptsTin, x, poolY["tin"])
+ tinValues = append(tinValues, tinLevel)
} else {
- ptsTin = appendPt(ptsTin, x, 10+height)
+ tinValues = append(tinValues, 0)
}
if lower == "bronze" {
- ptsBronze = appendPt(ptsBronze, x, poolY["bronze"])
+ bronzeValues = append(bronzeValues, bronzeLevel)
} else {
- ptsBronze = appendPt(ptsBronze, x, 10+height)
+ bronzeValues = append(bronzeValues, 0)
}
if lower == "silver" {
- ptsSilver = appendPt(ptsSilver, x, poolY["silver"])
+ silverValues = append(silverValues, silverLevel)
} else {
- ptsSilver = appendPt(ptsSilver, x, 10+height)
+ silverValues = append(silverValues, 0)
}
if lower == "gold" {
- ptsGold = appendPt(ptsGold, x, poolY["gold"])
+ goldValues = append(goldValues, goldLevel)
} else {
- ptsGold = appendPt(ptsGold, x, 10+height)
+ goldValues = append(goldValues, 0)
}
}
- gridY := []float64{}
- for i := 0; i <= 4; i++ {
- gridY = append(gridY, 10+float64(i)*(height/4))
- }
- gridX := []float64{}
- for i := 0; i < len(entries); i++ {
- gridX = append(gridX, startX+float64(i)*stepX)
- }
- yTicks := []views.ChartTick{}
- for i := 0; i <= 4; i++ {
- val := maxVal * float64(4-i) / 4
- pos := 10 + float64(i)*(height/4)
- yTicks = append(yTicks, views.ChartTick{Pos: pos, Label: fmt.Sprintf("%.0f", val)})
- }
- xTicks := []views.ChartTick{}
- maxTicks := 8
- stepIdx := 1
- if len(entries) > 1 {
- stepIdx = (len(entries)-1)/maxTicks + 1
- }
- for idx := 0; idx < len(entries); idx += stepIdx {
- x := startX + float64(idx)*stepX
- label := time.Unix(entries[idx].RawTime, 0).Local().Format("01-02 15:04")
- xTicks = append(xTicks, views.ChartTick{Pos: x, Label: label})
- }
- if len(entries) > 1 {
- lastIdx := len(entries) - 1
- xLast := startX + float64(lastIdx)*stepX
- labelLast := time.Unix(entries[lastIdx].RawTime, 0).Local().Format("01-02 15:04")
- if len(xTicks) == 0 || xTicks[len(xTicks)-1].Pos != xLast {
- xTicks = append(xTicks, views.ChartTick{Pos: xLast, Label: labelLast})
- }
+
+ cfg := lineChartConfig{
+ Height: 360,
+ XTicks: 8,
+ YTicks: 5,
+ YLabel: "Resources / Pool",
+ XLabel: "Snapshots (oldest left, newest right)",
+ Labels: labels,
+ TickLabels: tickLabels,
+ Series: []lineChartSeries{
+ {
+ Name: "vCPU",
+ Color: "#2563eb",
+ Values: vcpuValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ {
+ Name: "RAM (GB)",
+ Color: "#16a34a",
+ Values: ramValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ {
+ Name: "Tin",
+ Color: "#0ea5e9",
+ Values: tinValues,
+ Dash: []float64{4, 4},
+ LineWidth: 1.5,
+ TooltipHidden: true,
+ },
+ {
+ Name: "Bronze",
+ Color: "#a855f7",
+ Values: bronzeValues,
+ Dash: []float64{4, 4},
+ LineWidth: 1.5,
+ TooltipHidden: true,
+ },
+ {
+ Name: "Silver",
+ Color: "#94a3b8",
+ Values: silverValues,
+ Dash: []float64{4, 4},
+ LineWidth: 1.5,
+ TooltipHidden: true,
+ },
+ {
+ Name: "Gold",
+ Color: "#f59e0b",
+ Values: goldValues,
+ Dash: []float64{4, 4},
+ LineWidth: 1.5,
+ TooltipHidden: true,
+ },
+ },
+ HoverRows: []lineChartHoverRow{
+ {
+ Name: "Resource Pool",
+ Values: poolNames,
+ },
+ },
}
return views.VmTraceChart{
- PointsVcpu: ptsVcpu,
- PointsRam: ptsRam,
- PointsTin: ptsTin,
- PointsBronze: ptsBronze,
- PointsSilver: ptsSilver,
- PointsGold: ptsGold,
- Width: int(width),
- Height: int(height),
- GridX: gridX,
- GridY: gridY,
- XTicks: xTicks,
- YTicks: yTicks,
+ ConfigJSON: encodeLineChartConfig(cfg),
}
}
Snapshot history")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(display_query)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 48, Col: 74}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 37, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
@@ -92,7 +81,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(vm_id)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 58, Col: 123}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 47, Col: 123}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@@ -105,7 +94,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(vm_uuid)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 62, Col: 129}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 51, Col: 129}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
@@ -118,7 +107,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(vm_name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 66, Col: 123}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 55, Col: 123}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
@@ -131,7 +120,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(len(entries))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 78, Col: 44}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 67, Col: 44}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
@@ -141,576 +130,189 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- if chart.PointsVcpu != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var8 string
- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Width-60))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 83, Col: 68}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "Creation time
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var8 string
+ templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(creationLabel)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 86, Col: 76}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if creationApprox {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "Approximate (earliest snapshot)
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" height=\"")
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "Deletion time
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var9 string
+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(deletionLabel)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 93, Col: 76}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
Snapshot VM Name VmId VmUuid Vcenter Resource Pool vCPUs RAM (GB) Disk ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, e := range entries {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var9 string
- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height))
+ var templ_7745c5c3_Var10 string
+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(e.Snapshot)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 83, Col: 109}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 114, Col: 25}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" fill=\"white\" stroke=\"#e2e8f0\"> ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, y := range chart.GridY {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
+ var templ_7745c5c3_Var11 string
+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 115, Col: 21}
}
- for _, x := range chart.GridX {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var12 string
+ templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmId)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 116, Col: 21}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var13 string
+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmUuid)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 117, Col: 23}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var14 string
+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(e.Vcenter)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 118, Col: 24}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var15 string
+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(e.ResourcePool)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 119, Col: 29}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height+10))
+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(e.VcpuCount)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 60}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 120, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" x2=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Width-20))
+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(e.RamGB)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 99}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 121, Col: 41}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" y2=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
- templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height+10))
+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", e.ProvisionedDisk))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 139}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 122, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" stroke=\"#94a3b8\" stroke-width=\"1.5\"> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, tick := range chart.YTicks {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var27 string
- templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(tick.Label)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 102, Col: 70}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, tick := range chart.XTicks {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var30 string
- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(tick.Label)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 107, Col: 100}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, " vCPU RAM (GB) Tin Bronze Silver Gold Resources / Pool Snapshots (oldest left, newest right) ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "Creation time
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var35 string
- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(creationLabel)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 126, Col: 76}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if creationApprox {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "Approximate (earliest snapshot)
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "Deletion time
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var36 string
- templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(deletionLabel)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 133, Col: 76}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "
Snapshot VM Name VmId VmUuid Vcenter Resource Pool vCPUs RAM (GB) Disk ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, e := range entries {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var37 string
- templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(e.Snapshot)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 154, Col: 25}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var38 string
- templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 155, Col: 21}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var39 string
- templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmId)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 156, Col: 21}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var40 string
- templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmUuid)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 157, Col: 23}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var41 string
- templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(e.Vcenter)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 158, Col: 24}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var42 string
- templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(e.ResourcePool)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 159, Col: 29}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var43 string
- templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(e.VcpuCount)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 160, Col: 45}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var44 string
- templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(e.RamGB)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 161, Col: 41}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var45 string
- templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", e.ProvisionedDisk))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 162, Col: 72}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -718,7 +320,7 @@ func VmTracePage(query string, display_query string, vm_id string, vm_uuid strin
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/dist/assets/css/web3.css b/dist/assets/css/web3.css
index bcfe36e..147a79d 100644
--- a/dist/assets/css/web3.css
+++ b/dist/assets/css/web3.css
@@ -17,6 +17,9 @@ body {
margin: 0 auto;
padding: 2rem 1.5rem 4rem;
}
+.web2-shell-wide {
+ max-width: 1400px;
+}
.web2-header {
background: var(--web2-card);
border: 1px solid var(--web2-border);
@@ -176,3 +179,68 @@ body {
color: var(--web2-muted);
background: #f8fafc;
}
+.web3-chart-frame {
+ position: relative;
+ min-width: 760px;
+ width: 100%;
+}
+.web3-chart-canvas {
+ display: block;
+ width: 100%;
+ height: 360px;
+ background: #ffffff;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+}
+.web3-chart-tooltip {
+ position: absolute;
+ left: 0;
+ top: 0;
+ opacity: 0;
+ pointer-events: none;
+ background: rgba(15, 23, 42, 0.95);
+ color: #f8fafc;
+ padding: 0.55rem 0.65rem;
+ border-radius: 6px;
+ font-size: 0.75rem;
+ line-height: 1.35;
+ min-width: 170px;
+ box-shadow: 0 10px 30px rgba(2, 6, 23, 0.25);
+ z-index: 20;
+ transition: opacity 0.08s linear;
+}
+.web3-chart-tooltip.visible {
+ opacity: 1;
+}
+.web3-chart-tooltip-title {
+ font-weight: 700;
+ color: #e2e8f0;
+ margin-bottom: 0.35rem;
+}
+.web3-chart-tooltip-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.65rem;
+}
+.web3-chart-tooltip-label {
+ display: inline-flex;
+ align-items: center;
+ color: #cbd5e1;
+}
+.web3-chart-tooltip-value {
+ font-weight: 700;
+ color: #f8fafc;
+}
+.web3-chart-tooltip-swatch {
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 999px;
+ margin-right: 0.35rem;
+}
+@media (max-width: 900px) {
+ .web3-chart-frame {
+ min-width: 640px;
+ }
+}
diff --git a/dist/assets/js/web3-charts.js b/dist/assets/js/web3-charts.js
new file mode 100644
index 0000000..87f36dc
--- /dev/null
+++ b/dist/assets/js/web3-charts.js
@@ -0,0 +1,521 @@
+(function () {
+ "use strict";
+
+ function clamp(value, min, max) {
+ if (value < min) {
+ return min;
+ }
+ if (value > max) {
+ return max;
+ }
+ return value;
+ }
+
+ function toNumber(value) {
+ var num = Number(value);
+ return Number.isFinite(num) ? num : null;
+ }
+
+ function escapeHTML(value) {
+ return String(value)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ }
+
+ function formatValue(value, format) {
+ if (value === null || value === undefined || Number.isNaN(value)) {
+ return "-";
+ }
+ switch (format) {
+ case "int":
+ return String(Math.round(value));
+ case "float1":
+ return Number(value).toFixed(1);
+ case "float2":
+ return Number(value).toFixed(2);
+ default:
+ return String(value);
+ }
+ }
+
+ function pickTickIndices(total, desired) {
+ if (total <= 0) {
+ return [];
+ }
+ if (total === 1) {
+ return [0];
+ }
+ var target = Math.max(2, Math.min(total, desired || 6));
+ if (target >= total) {
+ var all = [];
+ for (var i = 0; i < total; i++) {
+ all.push(i);
+ }
+ return all;
+ }
+ var indices = [0];
+ var step = (total - 1) / (target - 1);
+ for (var j = 1; j < target - 1; j++) {
+ indices.push(Math.round(j * step));
+ }
+ indices.push(total - 1);
+ var seen = {};
+ var deduped = [];
+ for (var k = 0; k < indices.length; k++) {
+ var idx = indices[k];
+ if (!seen[idx]) {
+ seen[idx] = true;
+ deduped.push(idx);
+ }
+ }
+ deduped.sort(function (a, b) {
+ return a - b;
+ });
+ return deduped;
+ }
+
+ function getPlotBounds(width, height) {
+ return {
+ left: 52,
+ top: 16,
+ right: width - 20,
+ bottom: height - 78,
+ };
+ }
+
+ function buildScales(config, plot) {
+ var maxY = 0;
+ for (var i = 0; i < config.series.length; i++) {
+ var values = config.series[i].values || [];
+ for (var j = 0; j < values.length; j++) {
+ var value = toNumber(values[j]);
+ if (value !== null && value > maxY) {
+ maxY = value;
+ }
+ }
+ }
+ if (maxY <= 0) {
+ maxY = 1;
+ }
+ var count = config.labels.length;
+ var xSpan = plot.right - plot.left;
+ var ySpan = plot.bottom - plot.top;
+
+ return {
+ maxY: maxY,
+ xForIndex: function (index) {
+ if (count <= 1) {
+ return plot.left;
+ }
+ return plot.left + (index / (count - 1)) * xSpan;
+ },
+ yForValue: function (value) {
+ var numeric = toNumber(value);
+ if (numeric === null) {
+ return null;
+ }
+ return plot.bottom - (numeric / maxY) * ySpan;
+ },
+ };
+ }
+
+ function drawGrid(ctx, plot, config, scales) {
+ ctx.save();
+ ctx.strokeStyle = "#e2e8f0";
+ ctx.lineWidth = 1;
+ ctx.setLineDash([2, 4]);
+
+ var yTickCount = Math.max(2, config.yTicks || 5);
+ for (var i = 0; i < yTickCount; i++) {
+ var yRatio = i / (yTickCount - 1);
+ var y = plot.top + yRatio * (plot.bottom - plot.top);
+ ctx.beginPath();
+ ctx.moveTo(plot.left, y);
+ ctx.lineTo(plot.right, y);
+ ctx.stroke();
+ }
+
+ var xIndices = pickTickIndices(config.labels.length, config.xTicks || 6);
+ for (var j = 0; j < xIndices.length; j++) {
+ var x = scales.xForIndex(xIndices[j]);
+ ctx.beginPath();
+ ctx.moveTo(x, plot.top);
+ ctx.lineTo(x, plot.bottom);
+ ctx.stroke();
+ }
+ ctx.restore();
+ }
+
+ function drawAxes(ctx, plot) {
+ ctx.save();
+ ctx.strokeStyle = "#94a3b8";
+ ctx.lineWidth = 1.5;
+ ctx.setLineDash([]);
+
+ ctx.beginPath();
+ ctx.moveTo(plot.left, plot.bottom);
+ ctx.lineTo(plot.right, plot.bottom);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(plot.left, plot.top);
+ ctx.lineTo(plot.left, plot.bottom);
+ ctx.stroke();
+ ctx.restore();
+ }
+
+ function drawLabels(ctx, plot, config, scales) {
+ ctx.save();
+ ctx.fillStyle = "#475569";
+ ctx.font = "10px sans-serif";
+
+ var yTickCount = Math.max(2, config.yTicks || 5);
+ for (var i = 0; i < yTickCount; i++) {
+ var ratio = i / (yTickCount - 1);
+ var y = plot.top + ratio * (plot.bottom - plot.top);
+ var value = scales.maxY * (1 - ratio);
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ ctx.fillText(formatValue(value, "int"), plot.left - 8, y);
+ }
+
+ var xIndices = pickTickIndices(config.labels.length, config.xTicks || 6);
+ for (var j = 0; j < xIndices.length; j++) {
+ var idx = xIndices[j];
+ var tick = (config.tickLabels && config.tickLabels[idx]) || config.labels[idx] || "";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "top";
+ ctx.fillText(tick, scales.xForIndex(idx), plot.bottom + 12);
+ }
+
+ if (config.yLabel) {
+ ctx.save();
+ ctx.translate(16, plot.top + (plot.bottom-plot.top)/2);
+ ctx.rotate(-Math.PI / 2);
+ ctx.textAlign = "center";
+ ctx.textBaseline = "top";
+ ctx.font = "12px sans-serif";
+ ctx.fillText(config.yLabel, 0, 0);
+ ctx.restore();
+ }
+
+ if (config.xLabel) {
+ ctx.textAlign = "center";
+ ctx.textBaseline = "top";
+ ctx.font = "12px sans-serif";
+ ctx.fillText(config.xLabel, plot.left + (plot.right-plot.left)/2, plot.bottom + 48);
+ }
+ ctx.restore();
+ }
+
+ function drawSeries(ctx, plot, config, scales) {
+ for (var i = 0; i < config.series.length; i++) {
+ var series = config.series[i];
+ var values = series.values || [];
+ if (!values.length) {
+ continue;
+ }
+ ctx.save();
+ ctx.strokeStyle = series.color || "#2563eb";
+ ctx.lineWidth = series.lineWidth || 2.5;
+ ctx.setLineDash(Array.isArray(series.dash) ? series.dash : []);
+ ctx.beginPath();
+ var moved = false;
+ for (var j = 0; j < values.length; j++) {
+ var y = scales.yForValue(values[j]);
+ if (y === null) {
+ continue;
+ }
+ var x = scales.xForIndex(j);
+ if (!moved) {
+ ctx.moveTo(x, y);
+ moved = true;
+ } else {
+ ctx.lineTo(x, y);
+ }
+ }
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+
+ function drawLegend(ctx, config, width, height) {
+ var x = 52;
+ var y = height - 32;
+
+ ctx.save();
+ ctx.font = "12px sans-serif";
+ ctx.textBaseline = "middle";
+ for (var i = 0; i < config.series.length; i++) {
+ var series = config.series[i];
+ var label = series.name || "Series";
+ ctx.strokeStyle = series.color || "#2563eb";
+ ctx.fillStyle = "#475569";
+ ctx.lineWidth = series.lineWidth || 2.5;
+ ctx.setLineDash(Array.isArray(series.dash) ? series.dash : []);
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + 16, y);
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.fillText(label, x + 22, y);
+ x += 22 + ctx.measureText(label).width + 18;
+ if (x > width - 160) {
+ x = 52;
+ y += 18;
+ }
+ }
+ ctx.restore();
+ }
+
+ function updateTooltip(state, config) {
+ if (!state.tooltip) {
+ return;
+ }
+ if (state.hoverIndex === null) {
+ state.tooltip.classList.remove("visible");
+ state.tooltip.setAttribute("aria-hidden", "true");
+ return;
+ }
+
+ var idx = state.hoverIndex;
+ var rows = [];
+ rows.push('' + escapeHTML(config.labels[idx] || "") + "");
+ for (var i = 0; i < config.series.length; i++) {
+ var series = config.series[i];
+ if (series.tooltipHidden) {
+ continue;
+ }
+ var values = series.values || [];
+ var value = toNumber(values[idx]);
+ var valueLabel = formatValue(value, series.tooltipFormat || "int");
+ rows.push(
+ '' +
+ '' +
+ escapeHTML(series.name || "Series") +
+ "" +
+ '' + escapeHTML(valueLabel) + "" +
+ ""
+ );
+ }
+
+ var hoverRows = config.hoverRows || [];
+ for (var j = 0; j < hoverRows.length; j++) {
+ var hover = hoverRows[j];
+ var values = hover.values || [];
+ var label = values[idx] || "-";
+ rows.push(
+ '' +
+ '' + escapeHTML(hover.name || "Value") + "" +
+ '' + escapeHTML(label) + "" +
+ ""
+ );
+ }
+
+ state.tooltip.innerHTML = rows.join("");
+ state.tooltip.classList.add("visible");
+ state.tooltip.setAttribute("aria-hidden", "false");
+
+ var box = state.wrapper.getBoundingClientRect();
+ var tooltipBox = state.tooltip.getBoundingClientRect();
+ var left = clamp(state.mouseX + 14, 4, box.width - tooltipBox.width - 4);
+ var top = clamp(state.mouseY + 14, 4, box.height - tooltipBox.height - 4);
+ state.tooltip.style.left = left + "px";
+ state.tooltip.style.top = top + "px";
+ }
+
+ function drawHover(ctx, plot, config, scales, hoverIndex) {
+ if (hoverIndex === null) {
+ return;
+ }
+ var x = scales.xForIndex(hoverIndex);
+ ctx.save();
+ ctx.strokeStyle = "#94a3b8";
+ ctx.lineWidth = 1;
+ ctx.setLineDash([3, 4]);
+ ctx.beginPath();
+ ctx.moveTo(x, plot.top);
+ ctx.lineTo(x, plot.bottom);
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ for (var i = 0; i < config.series.length; i++) {
+ var series = config.series[i];
+ var values = series.values || [];
+ var y = scales.yForValue(values[hoverIndex]);
+ if (y === null) {
+ continue;
+ }
+ ctx.fillStyle = "#ffffff";
+ ctx.strokeStyle = series.color || "#2563eb";
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.arc(x, y, 3.5, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.stroke();
+ }
+ ctx.restore();
+ }
+
+ function renderLineChart(options) {
+ if (!options || !options.canvasId || !options.config) {
+ return;
+ }
+ var canvas = document.getElementById(options.canvasId);
+ if (!canvas) {
+ return;
+ }
+ var config = options.config;
+ if (!Array.isArray(config.labels) || config.labels.length === 0 || !Array.isArray(config.series) || config.series.length === 0) {
+ return;
+ }
+ var wrapper = canvas.parentElement;
+ var tooltip = options.tooltipId ? document.getElementById(options.tooltipId) : null;
+ var ctx = canvas.getContext("2d");
+ if (!ctx) {
+ return;
+ }
+
+ var state = {
+ canvas: canvas,
+ wrapper: wrapper,
+ tooltip: tooltip,
+ hoverIndex: null,
+ mouseX: 0,
+ mouseY: 0,
+ scales: null,
+ plot: null,
+ cssWidth: 0,
+ cssHeight: config.height || 360,
+ };
+
+ function redraw() {
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ var dpr = window.devicePixelRatio || 1;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ ctx.fillStyle = "#ffffff";
+ ctx.fillRect(0, 0, state.cssWidth, state.cssHeight);
+
+ drawGrid(ctx, state.plot, config, state.scales);
+ drawAxes(ctx, state.plot);
+ drawSeries(ctx, state.plot, config, state.scales);
+ drawLabels(ctx, state.plot, config, state.scales);
+ drawLegend(ctx, config, state.cssWidth, state.cssHeight);
+ drawHover(ctx, state.plot, config, state.scales, state.hoverIndex);
+ updateTooltip(state, config);
+ }
+
+ function resize() {
+ var rect = canvas.getBoundingClientRect();
+ var width = Math.max(320, Math.floor(rect.width));
+ var height = config.height || 360;
+ var dpr = window.devicePixelRatio || 1;
+
+ canvas.width = Math.round(width * dpr);
+ canvas.height = Math.round(height * dpr);
+ canvas.style.height = height + "px";
+
+ state.cssWidth = width;
+ state.cssHeight = height;
+ state.plot = getPlotBounds(width, height);
+ state.scales = buildScales(config, state.plot);
+ redraw();
+ }
+
+ canvas.addEventListener("mousemove", function (event) {
+ var rect = canvas.getBoundingClientRect();
+ var x = event.clientX - rect.left;
+ var y = event.clientY - rect.top;
+ state.mouseX = x;
+ state.mouseY = y;
+
+ if (!state.plot || config.labels.length === 0) {
+ return;
+ }
+ if (x < state.plot.left || x > state.plot.right || y < state.plot.top || y > state.plot.bottom) {
+ state.hoverIndex = null;
+ redraw();
+ return;
+ }
+
+ var ratio = (x - state.plot.left) / (state.plot.right - state.plot.left);
+ var idx = Math.round(ratio * (config.labels.length - 1));
+ state.hoverIndex = clamp(idx, 0, config.labels.length - 1);
+ redraw();
+ });
+
+ canvas.addEventListener("mouseleave", function () {
+ state.hoverIndex = null;
+ redraw();
+ });
+
+ window.addEventListener("resize", resize);
+ if (window.ResizeObserver) {
+ var observer = new ResizeObserver(function () {
+ resize();
+ });
+ observer.observe(wrapper);
+ }
+
+ resize();
+ }
+
+ function renderFromScript(options) {
+ if (!options || !options.configId) {
+ return;
+ }
+ var configNode = document.getElementById(options.configId);
+ if (!configNode) {
+ return;
+ }
+ var payload = configNode.textContent || "";
+ if (!payload.trim()) {
+ return;
+ }
+ try {
+ var config = JSON.parse(payload);
+ renderLineChart({
+ canvasId: options.canvasId,
+ tooltipId: options.tooltipId,
+ config: config,
+ });
+ } catch (error) {
+ // Leave page functional even when chart config is malformed.
+ }
+ }
+
+ function renderFromDataset(options) {
+ if (!options || !options.canvasId) {
+ return;
+ }
+ var canvas = document.getElementById(options.canvasId);
+ if (!canvas) {
+ return;
+ }
+ var payload = canvas.dataset.chartConfig || "";
+ if (!payload.trim()) {
+ return;
+ }
+ try {
+ var config = JSON.parse(payload);
+ renderLineChart({
+ canvasId: options.canvasId,
+ tooltipId: options.tooltipId,
+ config: config,
+ });
+ } catch (error) {
+ // Leave page functional even when chart config is malformed.
+ }
+ }
+
+ window.Web3Charts = {
+ renderLineChart: renderLineChart,
+ renderFromScript: renderFromScript,
+ renderFromDataset: renderFromDataset,
+ };
+})();
diff --git a/server/handler/chart_builders_test.go b/server/handler/chart_builders_test.go
new file mode 100644
index 0000000..65069e0
--- /dev/null
+++ b/server/handler/chart_builders_test.go
@@ -0,0 +1,101 @@
+package handler
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+ "vctp/components/views"
+)
+
+func TestBuildVcenterChartEncodesClientConfig(t *testing.T) {
+ entries := []views.VcenterTotalsEntry{
+ {
+ RawTime: 2_000,
+ VmCount: 30,
+ VcpuTotal: 80,
+ RamTotalGB: 120,
+ },
+ {
+ RawTime: 1_000,
+ VmCount: 20,
+ VcpuTotal: 60,
+ RamTotalGB: 90,
+ },
+ }
+
+ chart := buildVcenterChart(entries)
+ if chart.ConfigJSON == "" {
+ t.Fatal("expected config json for non-empty vcenter chart")
+ }
+
+ var cfg lineChartConfig
+ if err := json.Unmarshal([]byte(chart.ConfigJSON), &cfg); err != nil {
+ t.Fatalf("failed to decode chart config json: %v", err)
+ }
+
+ if len(cfg.Labels) != 2 {
+ t.Fatalf("expected 2 labels, got %d", len(cfg.Labels))
+ }
+ expectedFirst := time.Unix(1_000, 0).Local().Format("2006-01-02 15:04:05")
+ if cfg.Labels[0] != expectedFirst {
+ t.Fatalf("expected oldest label first %q, got %q", expectedFirst, cfg.Labels[0])
+ }
+ if len(cfg.Series) != 3 {
+ t.Fatalf("expected 3 series, got %d", len(cfg.Series))
+ }
+ if cfg.Series[0].Values[0] != 20 {
+ t.Fatalf("expected first VM value 20, got %v", cfg.Series[0].Values[0])
+ }
+}
+
+func TestBuildVmTraceChartEncodesPoolState(t *testing.T) {
+ entries := []views.VmTraceEntry{
+ {
+ RawTime: 1_000,
+ ResourcePool: "Tin",
+ VcpuCount: 4,
+ RamGB: 16,
+ },
+ {
+ RawTime: 2_000,
+ ResourcePool: "Gold",
+ VcpuCount: 8,
+ RamGB: 24,
+ },
+ }
+
+ chart := buildVmTraceChart(entries)
+ if chart.ConfigJSON == "" {
+ t.Fatal("expected config json for non-empty vm trace chart")
+ }
+
+ var cfg lineChartConfig
+ if err := json.Unmarshal([]byte(chart.ConfigJSON), &cfg); err != nil {
+ t.Fatalf("failed to decode vm trace chart config: %v", err)
+ }
+
+ if len(cfg.Series) != 6 {
+ t.Fatalf("expected 6 series, got %d", len(cfg.Series))
+ }
+ if len(cfg.HoverRows) != 1 || cfg.HoverRows[0].Name != "Resource Pool" {
+ t.Fatalf("expected resource pool hover row, got %#v", cfg.HoverRows)
+ }
+ if cfg.HoverRows[0].Values[0] != "Tin" || cfg.HoverRows[0].Values[1] != "Gold" {
+ t.Fatalf("unexpected hover row values: %#v", cfg.HoverRows[0].Values)
+ }
+ if cfg.Series[2].Values[0] == 0 || cfg.Series[2].Values[1] != 0 {
+ t.Fatalf("tin series should be active only for first point: %#v", cfg.Series[2].Values)
+ }
+ if cfg.Series[5].Values[0] != 0 || cfg.Series[5].Values[1] == 0 {
+ t.Fatalf("gold series should be active only for second point: %#v", cfg.Series[5].Values)
+ }
+}
+
+func TestBuildChartsEmptyInput(t *testing.T) {
+ if chart := buildVcenterChart(nil); chart.ConfigJSON != "" {
+ t.Fatalf("expected empty config for empty vcenter input, got %q", chart.ConfigJSON)
+ }
+ if chart := buildVmTraceChart(nil); chart.ConfigJSON != "" {
+ t.Fatalf("expected empty config for empty vm trace input, got %q", chart.ConfigJSON)
+ }
+}
diff --git a/server/handler/chart_config.go b/server/handler/chart_config.go
new file mode 100644
index 0000000..110087c
--- /dev/null
+++ b/server/handler/chart_config.go
@@ -0,0 +1,41 @@
+package handler
+
+import "encoding/json"
+
+type lineChartConfig struct {
+ Height int `json:"height,omitempty"`
+ XTicks int `json:"xTicks,omitempty"`
+ YTicks int `json:"yTicks,omitempty"`
+ YLabel string `json:"yLabel,omitempty"`
+ XLabel string `json:"xLabel,omitempty"`
+ Labels []string `json:"labels"`
+ TickLabels []string `json:"tickLabels,omitempty"`
+ Series []lineChartSeries `json:"series"`
+ HoverRows []lineChartHoverRow `json:"hoverRows,omitempty"`
+}
+
+type lineChartSeries struct {
+ Name string `json:"name"`
+ Color string `json:"color"`
+ Values []float64 `json:"values"`
+ Dash []float64 `json:"dash,omitempty"`
+ LineWidth float64 `json:"lineWidth,omitempty"`
+ TooltipFormat string `json:"tooltipFormat,omitempty"`
+ TooltipHidden bool `json:"tooltipHidden,omitempty"`
+}
+
+type lineChartHoverRow struct {
+ Name string `json:"name"`
+ Values []string `json:"values"`
+}
+
+func encodeLineChartConfig(cfg lineChartConfig) string {
+ if len(cfg.Labels) == 0 || len(cfg.Series) == 0 {
+ return ""
+ }
+ out, err := json.Marshal(cfg)
+ if err != nil {
+ return ""
+ }
+ return string(out)
+}
diff --git a/server/handler/vcenters.go b/server/handler/vcenters.go
index ad5ab78..c48f01f 100644
--- a/server/handler/vcenters.go
+++ b/server/handler/vcenters.go
@@ -141,91 +141,54 @@ func buildVcenterChart(entries []views.VcenterTotalsEntry) views.VcenterChartDat
plot = append(plot, entries[i])
}
- width := 1200.0
- height := 260.0
- plotWidth := width - 60.0
- startX := 40.0
- maxVal := float64(0)
+ labels := make([]string, 0, len(plot))
+ tickLabels := make([]string, 0, len(plot))
+ vmValues := make([]float64, 0, len(plot))
+ vcpuValues := make([]float64, 0, len(plot))
+ ramValues := make([]float64, 0, len(plot))
+
for _, e := range plot {
- if float64(e.VmCount) > maxVal {
- maxVal = float64(e.VmCount)
- }
- if float64(e.VcpuTotal) > maxVal {
- maxVal = float64(e.VcpuTotal)
- }
- if float64(e.RamTotalGB) > maxVal {
- maxVal = float64(e.RamTotalGB)
- }
+ t := time.Unix(e.RawTime, 0).Local()
+ labels = append(labels, t.Format("2006-01-02 15:04:05"))
+ tickLabels = append(tickLabels, t.Format("01-02 15:04"))
+ vmValues = append(vmValues, float64(e.VmCount))
+ vcpuValues = append(vcpuValues, float64(e.VcpuTotal))
+ ramValues = append(ramValues, float64(e.RamTotalGB))
}
- if maxVal == 0 {
- maxVal = 1
- }
- stepX := plotWidth
- if len(plot) > 1 {
- stepX = plotWidth / float64(len(plot)-1)
- }
- pointsVm := ""
- pointsVcpu := ""
- pointsRam := ""
- for i, e := range plot {
- x := startX + float64(i)*stepX
- yVm := 10 + (1-(float64(e.VmCount)/maxVal))*height
- yVcpu := 10 + (1-(float64(e.VcpuTotal)/maxVal))*height
- yRam := 10 + (1-(float64(e.RamTotalGB)/maxVal))*height
- if i == 0 {
- pointsVm = fmt.Sprintf("%.1f,%.1f", x, yVm)
- pointsVcpu = fmt.Sprintf("%.1f,%.1f", x, yVcpu)
- pointsRam = fmt.Sprintf("%.1f,%.1f", x, yRam)
- } else {
- pointsVm = pointsVm + " " + fmt.Sprintf("%.1f,%.1f", x, yVm)
- pointsVcpu = pointsVcpu + " " + fmt.Sprintf("%.1f,%.1f", x, yVcpu)
- pointsRam = pointsRam + " " + fmt.Sprintf("%.1f,%.1f", x, yRam)
- }
- }
- gridX := []float64{}
- if len(plot) > 1 {
- for i := 0; i < len(plot); i++ {
- gridX = append(gridX, startX+float64(i)*stepX)
- }
- }
- gridY := []float64{}
- for i := 0; i <= 4; i++ {
- gridY = append(gridY, 10+float64(i)*(height/4))
- }
- yTicks := []views.ChartTick{}
- for i := 0; i <= 4; i++ {
- val := maxVal * float64(4-i) / 4
- pos := 10 + float64(i)*(height/4)
- yTicks = append(yTicks, views.ChartTick{Pos: pos, Label: fmt.Sprintf("%.0f", val)})
- }
- xTicks := []views.ChartTick{}
- maxTicks := 6
- stepIdx := 1
- if len(plot) > 1 {
- stepIdx = (len(plot)-1)/maxTicks + 1
- }
- for idx := 0; idx < len(plot); idx += stepIdx {
- x := startX + float64(idx)*stepX
- label := time.Unix(plot[idx].RawTime, 0).Local().Format("01-02 15:04")
- xTicks = append(xTicks, views.ChartTick{Pos: x, Label: label})
- }
- if len(plot) > 1 {
- lastIdx := len(plot) - 1
- xLast := startX + float64(lastIdx)*stepX
- labelLast := time.Unix(plot[lastIdx].RawTime, 0).Local().Format("01-02 15:04")
- if len(xTicks) == 0 || xTicks[len(xTicks)-1].Pos != xLast {
- xTicks = append(xTicks, views.ChartTick{Pos: xLast, Label: labelLast})
- }
+
+ cfg := lineChartConfig{
+ Height: 360,
+ XTicks: 6,
+ YTicks: 5,
+ YLabel: "Totals",
+ XLabel: "Snapshots (oldest left, newest right)",
+ Labels: labels,
+ TickLabels: tickLabels,
+ Series: []lineChartSeries{
+ {
+ Name: "VMs",
+ Color: "#2563eb",
+ Values: vmValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ {
+ Name: "vCPU",
+ Color: "#16a34a",
+ Values: vcpuValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ {
+ Name: "RAM (GB)",
+ Color: "#ea580c",
+ Values: ramValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ },
}
return views.VcenterChartData{
- PointsVm: pointsVm,
- PointsVcpu: pointsVcpu,
- PointsRam: pointsRam,
- Width: int(width),
- Height: int(height),
- GridX: gridX,
- GridY: gridY,
- YTicks: yTicks,
- XTicks: xTicks,
+ ConfigJSON: encodeLineChartConfig(cfg),
}
}
diff --git a/server/handler/vmTrace.go b/server/handler/vmTrace.go
index ca53858..eb5d11c 100644
--- a/server/handler/vmTrace.go
+++ b/server/handler/vmTrace.go
@@ -108,114 +108,135 @@ func buildVmTraceChart(entries []views.VmTraceEntry) views.VmTraceChart {
if len(entries) == 0 {
return views.VmTraceChart{}
}
- width := 1200.0
- height := 220.0
- plotWidth := width - 60.0
- startX := 40.0
- maxVal := float64(0)
+ maxResource := float64(0)
for _, e := range entries {
- if float64(e.VcpuCount) > maxVal {
- maxVal = float64(e.VcpuCount)
+ if float64(e.VcpuCount) > maxResource {
+ maxResource = float64(e.VcpuCount)
}
- if float64(e.RamGB) > maxVal {
- maxVal = float64(e.RamGB)
+ if float64(e.RamGB) > maxResource {
+ maxResource = float64(e.RamGB)
}
}
- if maxVal == 0 {
- maxVal = 1
+ if maxResource == 0 {
+ maxResource = 1
}
- stepX := plotWidth
- if len(entries) > 1 {
- stepX = plotWidth / float64(len(entries)-1)
- }
- scale := height / maxVal
- var ptsVcpu, ptsRam, ptsTin, ptsBronze, ptsSilver, ptsGold string
- appendPt := func(s string, x, y float64) string {
- if s == "" {
- return fmt.Sprintf("%.1f,%.1f", x, y)
+
+ tinLevel := maxResource
+ bronzeLevel := maxResource * 0.9
+ silverLevel := maxResource * 0.8
+ goldLevel := maxResource * 0.7
+
+ labels := make([]string, 0, len(entries))
+ tickLabels := make([]string, 0, len(entries))
+ vcpuValues := make([]float64, 0, len(entries))
+ ramValues := make([]float64, 0, len(entries))
+ tinValues := make([]float64, 0, len(entries))
+ bronzeValues := make([]float64, 0, len(entries))
+ silverValues := make([]float64, 0, len(entries))
+ goldValues := make([]float64, 0, len(entries))
+ poolNames := make([]string, 0, len(entries))
+
+ for _, e := range entries {
+ t := time.Unix(e.RawTime, 0).Local()
+ labels = append(labels, t.Format("2006-01-02 15:04:05"))
+ tickLabels = append(tickLabels, t.Format("01-02 15:04"))
+ vcpuValues = append(vcpuValues, float64(e.VcpuCount))
+ ramValues = append(ramValues, float64(e.RamGB))
+
+ pool := strings.TrimSpace(e.ResourcePool)
+ if pool == "" {
+ pool = "Unknown"
}
- return s + " " + fmt.Sprintf("%.1f,%.1f", x, y)
- }
- for i, e := range entries {
- x := startX + float64(i)*stepX
- yVcpu := 10 + height - float64(e.VcpuCount)*scale
- yRam := 10 + height - float64(e.RamGB)*scale
- ptsVcpu = appendPt(ptsVcpu, x, yVcpu)
- ptsRam = appendPt(ptsRam, x, yRam)
- poolY := map[string]float64{
- "tin": 10 + height - scale*maxVal,
- "bronze": 10 + height - scale*maxVal*0.9,
- "silver": 10 + height - scale*maxVal*0.8,
- "gold": 10 + height - scale*maxVal*0.7,
- }
- lower := strings.ToLower(e.ResourcePool)
+ poolNames = append(poolNames, pool)
+
+ lower := strings.ToLower(pool)
if lower == "tin" {
- ptsTin = appendPt(ptsTin, x, poolY["tin"])
+ tinValues = append(tinValues, tinLevel)
} else {
- ptsTin = appendPt(ptsTin, x, 10+height)
+ tinValues = append(tinValues, 0)
}
if lower == "bronze" {
- ptsBronze = appendPt(ptsBronze, x, poolY["bronze"])
+ bronzeValues = append(bronzeValues, bronzeLevel)
} else {
- ptsBronze = appendPt(ptsBronze, x, 10+height)
+ bronzeValues = append(bronzeValues, 0)
}
if lower == "silver" {
- ptsSilver = appendPt(ptsSilver, x, poolY["silver"])
+ silverValues = append(silverValues, silverLevel)
} else {
- ptsSilver = appendPt(ptsSilver, x, 10+height)
+ silverValues = append(silverValues, 0)
}
if lower == "gold" {
- ptsGold = appendPt(ptsGold, x, poolY["gold"])
+ goldValues = append(goldValues, goldLevel)
} else {
- ptsGold = appendPt(ptsGold, x, 10+height)
+ goldValues = append(goldValues, 0)
}
}
- gridY := []float64{}
- for i := 0; i <= 4; i++ {
- gridY = append(gridY, 10+float64(i)*(height/4))
- }
- gridX := []float64{}
- for i := 0; i < len(entries); i++ {
- gridX = append(gridX, startX+float64(i)*stepX)
- }
- yTicks := []views.ChartTick{}
- for i := 0; i <= 4; i++ {
- val := maxVal * float64(4-i) / 4
- pos := 10 + float64(i)*(height/4)
- yTicks = append(yTicks, views.ChartTick{Pos: pos, Label: fmt.Sprintf("%.0f", val)})
- }
- xTicks := []views.ChartTick{}
- maxTicks := 8
- stepIdx := 1
- if len(entries) > 1 {
- stepIdx = (len(entries)-1)/maxTicks + 1
- }
- for idx := 0; idx < len(entries); idx += stepIdx {
- x := startX + float64(idx)*stepX
- label := time.Unix(entries[idx].RawTime, 0).Local().Format("01-02 15:04")
- xTicks = append(xTicks, views.ChartTick{Pos: x, Label: label})
- }
- if len(entries) > 1 {
- lastIdx := len(entries) - 1
- xLast := startX + float64(lastIdx)*stepX
- labelLast := time.Unix(entries[lastIdx].RawTime, 0).Local().Format("01-02 15:04")
- if len(xTicks) == 0 || xTicks[len(xTicks)-1].Pos != xLast {
- xTicks = append(xTicks, views.ChartTick{Pos: xLast, Label: labelLast})
- }
+
+ cfg := lineChartConfig{
+ Height: 360,
+ XTicks: 8,
+ YTicks: 5,
+ YLabel: "Resources / Pool",
+ XLabel: "Snapshots (oldest left, newest right)",
+ Labels: labels,
+ TickLabels: tickLabels,
+ Series: []lineChartSeries{
+ {
+ Name: "vCPU",
+ Color: "#2563eb",
+ Values: vcpuValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ {
+ Name: "RAM (GB)",
+ Color: "#16a34a",
+ Values: ramValues,
+ TooltipFormat: "int",
+ LineWidth: 2.5,
+ },
+ {
+ Name: "Tin",
+ Color: "#0ea5e9",
+ Values: tinValues,
+ Dash: []float64{4, 4},
+ LineWidth: 1.5,
+ TooltipHidden: true,
+ },
+ {
+ Name: "Bronze",
+ Color: "#a855f7",
+ Values: bronzeValues,
+ Dash: []float64{4, 4},
+ LineWidth: 1.5,
+ TooltipHidden: true,
+ },
+ {
+ Name: "Silver",
+ Color: "#94a3b8",
+ Values: silverValues,
+ Dash: []float64{4, 4},
+ LineWidth: 1.5,
+ TooltipHidden: true,
+ },
+ {
+ Name: "Gold",
+ Color: "#f59e0b",
+ Values: goldValues,
+ Dash: []float64{4, 4},
+ LineWidth: 1.5,
+ TooltipHidden: true,
+ },
+ },
+ HoverRows: []lineChartHoverRow{
+ {
+ Name: "Resource Pool",
+ Values: poolNames,
+ },
+ },
}
return views.VmTraceChart{
- PointsVcpu: ptsVcpu,
- PointsRam: ptsRam,
- PointsTin: ptsTin,
- PointsBronze: ptsBronze,
- PointsSilver: ptsSilver,
- PointsGold: ptsGold,
- Width: int(width),
- Height: int(height),
- GridX: gridX,
- GridY: gridY,
- XTicks: xTicks,
- YTicks: yTicks,
+ ConfigJSON: encodeLineChartConfig(cfg),
}
}
Creation time
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(creationLabel) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 86, Col: 76} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if creationApprox { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "Approximate (earliest snapshot)
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" height=\"") + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "Deletion time
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(deletionLabel) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 93, Col: 76} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
| Snapshot | VM Name | VmId | VmUuid | Vcenter | Resource Pool | vCPUs | RAM (GB) | Disk |
|---|---|---|---|---|---|---|---|---|
| ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var9 string
- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height))
+ var templ_7745c5c3_Var10 string
+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(e.Snapshot)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 83, Col: 109}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 114, Col: 25}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" fill=\"white\" stroke=\"#e2e8f0\"> | ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, y := range chart.GridY {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "| ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var12 string
+ templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmId)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 116, Col: 21}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " | ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var13 string
+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmUuid)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 117, Col: 23}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " | ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var14 string
+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(e.Vcenter)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 118, Col: 24}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " | ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var15 string
+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(e.ResourcePool)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 119, Col: 29}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " | ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height+10))
+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(e.VcpuCount)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 60}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 120, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" x2=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, " | ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Width-20))
+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(e.RamGB)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 99}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 121, Col: 41}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" y2=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " | ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
- templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", chart.Height+10))
+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", e.ProvisionedDisk))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 92, Col: 139}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 122, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" stroke=\"#94a3b8\" stroke-width=\"1.5\"> | |
| Snapshot | VM Name | VmId | VmUuid | Vcenter | Resource Pool | vCPUs | RAM (GB) | Disk |
|---|---|---|---|---|---|---|---|---|
| ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var37 string - templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(e.Snapshot) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 154, Col: 25} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var38 string - templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 155, Col: 21} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var39 string - templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmId) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 156, Col: 21} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var40 string - templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(e.VmUuid) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 157, Col: 23} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var41 string - templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(e.Vcenter) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 158, Col: 24} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var42 string - templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(e.ResourcePool) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 159, Col: 29} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var43 string - templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(e.VcpuCount) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 160, Col: 45} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var44 string - templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(e.RamGB) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 161, Col: 41} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, " | ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var45 string - templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", e.ProvisionedDisk)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/vm_trace.templ`, Line: 162, Col: 72} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, " |