This commit is contained in:
1 parent
98e92a8264
commit
8ccf5a7009
28 files changed
+2836
-422
No files matched your search
@@ -8,14 +8,14 @@ templ Header() {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta name="description" content="vCTP dashboard and API endpoint"/>
|
||||
<meta name="color-scheme" content="light"/>
|
||||
<meta name="theme-color" content="#1b61c9"/>
|
||||
<meta name="theme-color" content="#195fc8"/>
|
||||
<title>vCTP API</title>
|
||||
<link rel="icon" href="/favicon.ico"/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/>
|
||||
<link rel="icon" href={ "/favicon.ico?v=" + version.Value }/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href={ "/favicon-16x16.png?v=" + version.Value }/>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href={ "/favicon-32x32.png?v=" + version.Value }/>
|
||||
<script src="/assets/js/htmx@v2.0.2.min.js"></script>
|
||||
<script src={ "/assets/js/web3-charts.js?v=" + version.Value }></script>
|
||||
<link href={ "/assets/css/output@" + version.Value + ".css" } rel="stylesheet"/>
|
||||
<link href="/assets/css/web3.css" rel="stylesheet"/>
|
||||
<link href={ "/assets/css/web3.css?v=" + version.Value } rel="stylesheet"/>
|
||||
</head>
|
||||
}
|
||||
@@ -31,33 +31,85 @@ func Header() templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"description\" content=\"vCTP dashboard and API endpoint\"><meta name=\"color-scheme\" content=\"light\"><meta name=\"theme-color\" content=\"#1b61c9\"><title>vCTP API</title><link rel=\"icon\" href=\"/favicon.ico\"><link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\"><link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\"><script src=\"/assets/js/htmx@v2.0.2.min.js\"></script><script src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"description\" content=\"vCTP dashboard and API endpoint\"><meta name=\"color-scheme\" content=\"light\"><meta name=\"theme-color\" content=\"#195fc8\"><title>vCTP API</title><link rel=\"icon\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("/assets/js/web3-charts.js?v=" + version.Value)
|
||||
var templ_7745c5c3_Var2 templ.SafeURL
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs("/favicon.ico?v=" + version.Value)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 17, Col: 62}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 13, Col: 59}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"></script><link href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.SafeURL
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs("/assets/css/output@" + version.Value + ".css")
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs("/favicon-16x16.png?v=" + version.Value)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 18, Col: 61}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 14, Col: 96}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" rel=\"stylesheet\"><link href=\"/assets/css/web3.css\" rel=\"stylesheet\"></head>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\"><link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.SafeURL
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs("/favicon-32x32.png?v=" + version.Value)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 15, Col: 96}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"><script src=\"/assets/js/htmx@v2.0.2.min.js\"></script><script src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("/assets/js/web3-charts.js?v=" + version.Value)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 17, Col: 62}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"></script><link href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs("/assets/css/output@" + version.Value + ".css")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 18, Col: 61}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" rel=\"stylesheet\"><link href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 templ.SafeURL
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs("/assets/css/web3.css?v=" + version.Value)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 19, Col: 56}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\" rel=\"stylesheet\"></head>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -12,6 +12,20 @@ type SegmentedLink struct {
|
||||
Class string
|
||||
}
|
||||
|
||||
func actionLinkClass(class string) string {
|
||||
if class == "" {
|
||||
return "web2-button"
|
||||
}
|
||||
return class
|
||||
}
|
||||
|
||||
func segmentedLinkClass(class string) string {
|
||||
if class == "" {
|
||||
return "web3-button"
|
||||
}
|
||||
return class
|
||||
}
|
||||
|
||||
templ PageHeader(pill string, title string, subtitle string, actions []ActionLink) {
|
||||
<div class="web2-page-head-row">
|
||||
<div class="web2-head-copy">
|
||||
@@ -26,7 +40,7 @@ templ PageHeader(pill string, title string, subtitle string, actions []ActionLin
|
||||
if len(actions) > 0 {
|
||||
<div class="web2-actions">
|
||||
for _, action := range actions {
|
||||
<a class={ action.Class } href={ action.Href }>{ action.Label }</a>
|
||||
<a class={ actionLinkClass(action.Class) } href={ action.Href }>{ action.Label }</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -37,7 +51,7 @@ templ SegmentedActions(actions []SegmentedLink) {
|
||||
if len(actions) > 0 {
|
||||
<div class="web3-button-group">
|
||||
for _, action := range actions {
|
||||
<a class={ action.Class } href={ action.Href }>{ action.Label }</a>
|
||||
<a class={ segmentedLinkClass(action.Class) } href={ action.Href }>{ action.Label }</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -45,7 +59,7 @@ templ SegmentedActions(actions []SegmentedLink) {
|
||||
|
||||
templ SectionHead(title string, badge string) {
|
||||
<div class="web2-section-head">
|
||||
<h2>{ title }</h2>
|
||||
<h2 class="web2-section-title">{ title }</h2>
|
||||
if badge != "" {
|
||||
<span class="web2-badge">{ badge }</span>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package views
|
||||
|
||||
import "vctp/components/core"
|
||||
import (
|
||||
"strings"
|
||||
"vctp/components/core"
|
||||
)
|
||||
|
||||
type BuildInfo struct {
|
||||
BuildTime string
|
||||
@@ -8,6 +11,14 @@ type BuildInfo struct {
|
||||
GoVersion string
|
||||
}
|
||||
|
||||
func truncateSHA(sha string) string {
|
||||
trimmed := strings.TrimSpace(sha)
|
||||
if len(trimmed) <= 14 {
|
||||
return trimmed
|
||||
}
|
||||
return trimmed[:14] + "..."
|
||||
}
|
||||
|
||||
templ Index(info BuildInfo) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@@ -41,15 +52,15 @@ templ Index(info BuildInfo) {
|
||||
</div>
|
||||
<div class="web2-card">
|
||||
<p class="web2-kpi-label">SHA1 Version</p>
|
||||
<p class="web2-kpi-value">{ info.SHA1Ver }</p>
|
||||
<p class="web2-kpi-value web2-kpi-value-mono web2-kpi-truncate" title={ info.SHA1Ver }>{ truncateSHA(info.SHA1Ver) }</p>
|
||||
</div>
|
||||
<div class="web2-card">
|
||||
<p class="web2-kpi-label">Go Runtime</p>
|
||||
<p class="web2-kpi-value">{ info.GoVersion }</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="grid gap-6 lg:grid-cols-3">
|
||||
<div class="web2-card">
|
||||
<section class="web2-index-sections">
|
||||
<div class="web2-card web2-card-overview web2-index-overview">
|
||||
<h2 class="mb-2">Overview</h2>
|
||||
<p class="web2-page-subtitle">
|
||||
vCTP is a vSphere Chargeback Tracking Platform.
|
||||
@@ -61,7 +72,7 @@ templ Index(info BuildInfo) {
|
||||
Use <code class="web2-code">/api/auth/me</code> to inspect active claims and roles during integration and diagnostics.
|
||||
</p>
|
||||
</div>
|
||||
<div class="web2-card">
|
||||
<div class="web2-card web2-card-featured web2-index-featured">
|
||||
<h2 class="mb-2">Snapshots and Reports</h2>
|
||||
<div class="web2-paragraphs web2-page-subtitle">
|
||||
<p>Hourly snapshots capture inventory per vCenter (concurrency via <code class="web2-code">hourly_snapshot_concurrency</code>), then daily and monthly summaries are derived from those snapshots.</p>
|
||||
@@ -76,7 +87,7 @@ templ Index(info BuildInfo) {
|
||||
<p>Monthly aggregation reports include a Daily Totals sheet with full-day interval labels (YYYY-MM-DD to YYYY-MM-DD) and prorated totals.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="web2-card">
|
||||
<div class="web2-card web2-index-wide">
|
||||
<h2 class="mb-2">Prorating and Aggregation</h2>
|
||||
<div class="web2-paragraphs web2-page-subtitle">
|
||||
<p><code class="web2-code">SamplesPresent</code> is the count of snapshots in which the VM appears; <code class="web2-code">TotalSamples</code> is the count of unique snapshot times for that vCenter/day.</p>
|
||||
|
||||
@@ -8,7 +8,10 @@ package views
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "vctp/components/core"
|
||||
import (
|
||||
"strings"
|
||||
"vctp/components/core"
|
||||
)
|
||||
|
||||
type BuildInfo struct {
|
||||
BuildTime string
|
||||
@@ -16,6 +19,14 @@ type BuildInfo struct {
|
||||
GoVersion string
|
||||
}
|
||||
|
||||
func truncateSHA(sha string) string {
|
||||
trimmed := strings.TrimSpace(sha)
|
||||
if len(trimmed) <= 14 {
|
||||
return trimmed
|
||||
}
|
||||
return trimmed[:14] + "..."
|
||||
}
|
||||
|
||||
func Index(info BuildInfo) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
@@ -52,39 +63,52 @@ func Index(info BuildInfo) templ.Component {
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(info.BuildTime)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/index.templ`, Line: 40, Col: 48}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/index.templ`, Line: 51, Col: 48}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</p></div><div class=\"web2-card\"><p class=\"web2-kpi-label\">SHA1 Version</p><p class=\"web2-kpi-value\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</p></div><div class=\"web2-card\"><p class=\"web2-kpi-label\">SHA1 Version</p><p class=\"web2-kpi-value web2-kpi-value-mono web2-kpi-truncate\" title=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(info.SHA1Ver)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/index.templ`, Line: 44, Col: 46}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/index.templ`, Line: 55, Col: 90}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</p></div><div class=\"web2-card\"><p class=\"web2-kpi-label\">Go Runtime</p><p class=\"web2-kpi-value\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(info.GoVersion)
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(truncateSHA(info.SHA1Ver))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/index.templ`, Line: 48, Col: 48}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/index.templ`, Line: 55, Col: 120}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</p></div></section><section class=\"grid gap-6 lg:grid-cols-3\"><div class=\"web2-card\"><h2 class=\"mb-2\">Overview</h2><p class=\"web2-page-subtitle\">vCTP is a vSphere Chargeback Tracking Platform.</p><p class=\"web2-page-subtitle\">Use fast vCenter totals views (Daily Aggregated and Hourly Detail 45d) and VM Trace views (Hourly Detail and Daily Aggregated) to move between long-range trends and granular timelines.</p><p class=\"web2-page-subtitle\">Use <code class=\"web2-code\">/api/auth/me</code> to inspect active claims and roles during integration and diagnostics.</p></div><div class=\"web2-card\"><h2 class=\"mb-2\">Snapshots and Reports</h2><div class=\"web2-paragraphs web2-page-subtitle\"><p>Hourly snapshots capture inventory per vCenter (concurrency via <code class=\"web2-code\">hourly_snapshot_concurrency</code>), then daily and monthly summaries are derived from those snapshots.</p><p><strong>Hourly tracks:</strong> VM identity (<code class=\"web2-code\">InventoryId</code>, <code class=\"web2-code\">Name</code>, <code class=\"web2-code\">VmId</code>, <code class=\"web2-code\">VmUuid</code>, <code class=\"web2-code\">Vcenter</code>, <code class=\"web2-code\">EventKey</code>, <code class=\"web2-code\">CloudId</code>), lifecycle (<code class=\"web2-code\">CreationTime</code>, <code class=\"web2-code\">DeletionTime</code>, <code class=\"web2-code\">SnapshotTime</code>), placement (<code class=\"web2-code\">Datacenter</code>, <code class=\"web2-code\">Cluster</code>, <code class=\"web2-code\">Folder</code>, <code class=\"web2-code\">ResourcePool</code>), and sizing/state (<code class=\"web2-code\">VcpuCount</code>, <code class=\"web2-code\">RamGB</code>, <code class=\"web2-code\">ProvisionedDisk</code>, <code class=\"web2-code\">PoweredOn</code>, <code class=\"web2-code\">IsTemplate</code>, <code class=\"web2-code\">SrmPlaceholder</code>).</p><p><strong>Daily tracks:</strong> <code class=\"web2-code\">SamplesPresent</code>, <code class=\"web2-code\">TotalSamples</code>, <code class=\"web2-code\">AvgIsPresent</code>, <code class=\"web2-code\">AvgVcpuCount</code>, <code class=\"web2-code\">AvgRamGB</code>, <code class=\"web2-code\">AvgProvisionedDisk</code>, <code class=\"web2-code\">PoolTinPct</code>, <code class=\"web2-code\">PoolBronzePct</code>, <code class=\"web2-code\">PoolSilverPct</code>, <code class=\"web2-code\">PoolGoldPct</code>, plus chargeback totals columns <code class=\"web2-code\">Tin</code>, <code class=\"web2-code\">Bronze</code>, <code class=\"web2-code\">Silver</code>, <code class=\"web2-code\">Gold</code>.</p><p><strong>Monthly tracks:</strong> the same daily aggregate fields, with monthly values weighted by per-day sample volume so partial-day VMs and config changes stay proportional.</p><p>Snapshots are registered in <code class=\"web2-code\">snapshot_registry</code> so regeneration via <code class=\"web2-code\">/api/snapshots/aggregate</code> can locate the correct tables (fallback scanning is also supported).</p><p>vCenter totals pages are accelerated by compact cache tables: <code class=\"web2-code\">vcenter_latest_totals</code> and <code class=\"web2-code\">vcenter_aggregate_totals</code>.</p><p>VM Trace daily mode uses the <code class=\"web2-code\">vm_daily_rollup</code> cache when available, and falls back to daily summary tables if needed.</p><p>Reports (XLSX with totals/charts) are generated automatically after hourly, daily, and monthly jobs and written to a reports directory.</p><p>Hourly totals are interval-based: each row represents <code class=\"web2-code\">[HH:00, HH+1:00)</code> and uses the first snapshot at or after the hour end (including cross-day snapshots) to prorate VM presence.</p><p>Monthly aggregation reports include a Daily Totals sheet with full-day interval labels (YYYY-MM-DD to YYYY-MM-DD) and prorated totals.</p></div></div><div class=\"web2-card\"><h2 class=\"mb-2\">Prorating and Aggregation</h2><div class=\"web2-paragraphs web2-page-subtitle\"><p><code class=\"web2-code\">SamplesPresent</code> is the count of snapshots in which the VM appears; <code class=\"web2-code\">TotalSamples</code> is the count of unique snapshot times for that vCenter/day.</p><p><code class=\"web2-code\">AvgIsPresent = SamplesPresent / TotalSamples</code> (0 when <code class=\"web2-code\">TotalSamples</code> is 0).</p><p>Daily <code class=\"web2-code\">AvgVcpuCount</code>, <code class=\"web2-code\">AvgRamGB</code>, and <code class=\"web2-code\">AvgProvisionedDisk</code> are per-sample sums divided by <code class=\"web2-code\">TotalSamples</code> (time-weighted).</p><p>Daily pool percentages (<code class=\"web2-code\">PoolTinPct</code>/<code class=\"web2-code\">PoolBronzePct</code>/<code class=\"web2-code\">PoolSilverPct</code>/<code class=\"web2-code\">PoolGoldPct</code>) use pool-hit counts divided by <code class=\"web2-code\">SamplesPresent</code>.</p><p>Monthly aggregation converts each day intoLine truncated
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</p></div><div class=\"web2-card\"><p class=\"web2-kpi-label\">Go Runtime</p><p class=\"web2-kpi-value\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(info.GoVersion)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/index.templ`, Line: 59, Col: 48}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</p></div></section><section class=\"web2-index-sections\"><div class=\"web2-card web2-card-overview web2-index-overview\"><h2 class=\"mb-2\">Overview</h2><p class=\"web2-page-subtitle\">vCTP is a vSphere Chargeback Tracking Platform.</p><p class=\"web2-page-subtitle\">Use fast vCenter totals views (Daily Aggregated and Hourly Detail 45d) and VM Trace views (Hourly Detail and Daily Aggregated) to move between long-range trends and granular timelines.</p><p class=\"web2-page-subtitle\">Use <code class=\"web2-code\">/api/auth/me</code> to inspect active claims and roles during integration and diagnostics.</p></div><div class=\"web2-card web2-card-featured web2-index-featured\"><h2 class=\"mb-2\">Snapshots and Reports</h2><div class=\"web2-paragraphs web2-page-subtitle\"><p>Hourly snapshots capture inventory per vCenter (concurrency via <code class=\"web2-code\">hourly_snapshot_concurrency</code>), then daily and monthly summaries are derived from those snapshots.</p><p><strong>Hourly tracks:</strong> VM identity (<code class=\"web2-code\">InventoryId</code>, <code class=\"web2-code\">Name</code>, <code class=\"web2-code\">VmId</code>, <code class=\"web2-code\">VmUuid</code>, <code class=\"web2-code\">Vcenter</code>, <code class=\"web2-code\">EventKey</code>, <code class=\"web2-code\">CloudId</code>), lifecycle (<code class=\"web2-code\">CreationTime</code>, <code class=\"web2-code\">DeletionTime</code>, <code class=\"web2-code\">SnapshotTime</code>), placement (<code class=\"web2-code\">Datacenter</code>, <code class=\"web2-code\">Cluster</code>, <code class=\"web2-code\">Folder</code>, <code class=\"web2-code\">ResourcePool</code>), and sizing/state (<code class=\"web2-code\">VcpuCount</code>, <code class=\"web2-code\">RamGB</code>, <code class=\"web2-code\">ProvisionedDisk</code>, <code class=\"web2-code\">PoweredOn</code>, <code class=\"web2-code\">IsTemplate</code>, <code class=\"web2-code\">SrmPlaceholder</code>).</p><p><strong>Daily tracks:</strong> <code class=\"web2-code\">SamplesPresent</code>, <code class=\"web2-code\">TotalSamples</code>, <code class=\"web2-code\">AvgIsPresent</code>, <code class=\"web2-code\">AvgVcpuCount</code>, <code class=\"web2-code\">AvgRamGB</code>, <code class=\"web2-code\">AvgProvisionedDisk</code>, <code class=\"web2-code\">PoolTinPct</code>, <code class=\"web2-code\">PoolBronzePct</code>, <code class=\"web2-code\">PoolSilverPct</code>, <code class=\"web2-code\">PoolGoldPct</code>, plus chargeback totals columns <code class=\"web2-code\">Tin</code>, <code class=\"web2-code\">Bronze</code>, <code class=\"web2-code\">Silver</code>, <code class=\"web2-code\">Gold</code>.</p><p><strong>Monthly tracks:</strong> the same daily aggregate fields, with monthly values weighted by per-day sample volume so partial-day VMs and config changes stay proportional.</p><p>Snapshots are registered in <code class=\"web2-code\">snapshot_registry</code> so regeneration via <code class=\"web2-code\">/api/snapshots/aggregate</code> can locate the correct tables (fallback scanning is also supported).</p><p>vCenter totals pages are accelerated by compact cache tables: <code class=\"web2-code\">vcenter_latest_totals</code> and <code class=\"web2-code\">vcenter_aggregate_totals</code>.</p><p>VM Trace daily mode uses the <code class=\"web2-code\">vm_daily_rollup</code> cache when available, and falls back to daily summary tables if needed.</p><p>Reports (XLSX with totals/charts) are generated automatically after hourly, daily, and monthly jobs and written to a reports directory.</p><p>Hourly totals are interval-based: each row represents <code class=\"web2-code\">[HH:00, HH+1:00)</code> and uses the first snapshot at or after the hour end (including cross-day snapshots) to prorate VM presence.</p><p>Monthly aggregation reports include a Daily Totals sheet with full-day interval labels (YYYY-MM-DD to YYYY-MM-DD) and prorated totals.</p></div></div><div class=\"web2-card web2-index-wide\"><h2 class=\"mb-2\">Prorating and Aggregation</h2><div class=\"web2-paragraphs web2-page-subtitle\"><p><code class=\"web2-code\">SamplesPresent</code> is the count of snapshots in which the VM appears; <code class=\"web2-code\">TotalSamples</code> is the count of unique snapshot times for that vCenter/day.</p><p><code class=\"web2-code\">AvgIsPresent = SamplesPresent / TotalSamples</code> (0 when <code class=\"web2-code\">TotalSamples</code> is 0).</p><p>Daily <code class=\"web2-code\">AvgVcpuCount</code>, <code class=\"web2-code\">AvgRamGB</code>, and <code class=\"web2-code\">AvgProvisionedDisk</code> are per-sample sums divided by <code class=\"web2-code\">TotalSamples</code> (time-weighted).</p><p>Daily pool percentages (<code class=\"web2-code\">PoolTinPct</code>/<code class=\"web2-code\">PoolBronzePct</code>/<code class=\"web2-code\">PoolSilverPct</code>/<code class=\"web2-code\">PoolGoldPct</code>) use pool-hit counts divided by <code claLine truncated
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -92,7 +116,7 @@ func Index(info BuildInfo) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user