enhance vm trace page
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-01-16 14:52:15 +11:00
parent d55916766b
commit 7273961cfc
4 changed files with 152 additions and 57 deletions

View File

@@ -717,6 +717,14 @@ type VmTraceRow struct {
DeletionTime sql.NullInt64 `db:"DeletionTime"`
}
// VmLifecycle captures observed lifecycle times from hourly snapshots.
type VmLifecycle struct {
CreationTime int64
FirstSeen int64
LastSeen int64
DeletionTime int64
}
// FetchVmTrace returns combined hourly snapshot records for a VM (by id/uuid/name) ordered by snapshot time.
// To avoid SQLite's UNION term limits, this iterates tables one by one and merges in-memory.
func FetchVmTrace(ctx context.Context, dbConn *sqlx.DB, vmID, vmUUID, name string) ([]VmTraceRow, error) {
@@ -777,6 +785,70 @@ WHERE ("VmId" = ? OR "VmUuid" = ? OR lower("Name") = lower(?))
return rows, nil
}
// FetchVmLifecycle walks hourly snapshots to determine lifecycle bounds for a VM.
func FetchVmLifecycle(ctx context.Context, dbConn *sqlx.DB, vmID, vmUUID, name string) (VmLifecycle, error) {
var lifecycle VmLifecycle
var tables []struct {
TableName string `db:"table_name"`
SnapshotTime int64 `db:"snapshot_time"`
}
if err := selectLog(ctx, dbConn, &tables, `
SELECT table_name, snapshot_time
FROM snapshot_registry
WHERE snapshot_type = 'hourly'
ORDER BY snapshot_time
`); err != nil {
return lifecycle, err
}
driver := strings.ToLower(dbConn.DriverName())
minCreation := int64(0)
for _, t := range tables {
if err := ValidateTableName(t.TableName); err != nil {
continue
}
// Probe this table for the VM.
query := fmt.Sprintf(`
SELECT MIN(NULLIF("CreationTime",0)) AS min_creation, COUNT(1) AS cnt
FROM %s
WHERE ("VmId" = ? OR "VmUuid" = ? OR lower("Name") = lower(?))
`, t.TableName)
args := []interface{}{vmID, vmUUID, name}
if driver != "sqlite" {
query = strings.Replace(query, "?", "$1", 1)
query = strings.Replace(query, "?", "$2", 1)
query = strings.Replace(query, "?", "$3", 1)
}
var probe struct {
MinCreation sql.NullInt64 `db:"min_creation"`
Cnt int64 `db:"cnt"`
}
if err := getLog(ctx, dbConn, &probe, query, args...); err != nil {
continue
}
if probe.Cnt > 0 {
if lifecycle.FirstSeen == 0 {
lifecycle.FirstSeen = t.SnapshotTime
}
lifecycle.LastSeen = t.SnapshotTime
if probe.MinCreation.Valid {
if minCreation == 0 || probe.MinCreation.Int64 < minCreation {
minCreation = probe.MinCreation.Int64
}
}
} else if lifecycle.LastSeen > 0 && lifecycle.DeletionTime == 0 && t.SnapshotTime > lifecycle.LastSeen {
lifecycle.DeletionTime = t.SnapshotTime
break
}
}
if minCreation > 0 {
lifecycle.CreationTime = minCreation
} else if lifecycle.FirstSeen > 0 {
lifecycle.CreationTime = lifecycle.FirstSeen
}
return lifecycle, nil
}
// SyncVcenterTotalsFromSnapshots backfills vcenter_totals using hourly snapshot tables in snapshot_registry.
func SyncVcenterTotalsFromSnapshots(ctx context.Context, dbConn *sqlx.DB) error {
if err := EnsureVcenterTotalsTable(ctx, dbConn); err != nil {