This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user