Add vCenter reference cache tables and update related functions
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-02-13 14:45:13 +11:00
parent 5cd8f9c2a1
commit 18be1fbe06
4 changed files with 420 additions and 45 deletions

View File

@@ -30,6 +30,11 @@ type ColumnDef struct {
Type string
}
type VcenterHostCacheEntry struct {
Cluster string
Datacenter string
}
type ensureOnceKey struct {
dbConn *sqlx.DB
name string
@@ -1107,6 +1112,243 @@ CREATE TABLE IF NOT EXISTS vm_renames (
return nil
}
// EnsureVcenterReferenceCacheTables creates lookup caches for vCenter object references.
func EnsureVcenterReferenceCacheTables(ctx context.Context, dbConn *sqlx.DB) error {
return ensureOncePerDB(dbConn, "vcenter_reference_cache_tables", func() error {
ddls := []string{
`
CREATE TABLE IF NOT EXISTS vcenter_folder_cache (
"Vcenter" TEXT NOT NULL,
"FolderRef" TEXT NOT NULL,
"FolderPath" TEXT NOT NULL,
"LastSeen" BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY ("Vcenter","FolderRef")
)`,
`
CREATE TABLE IF NOT EXISTS vcenter_resource_pool_cache (
"Vcenter" TEXT NOT NULL,
"ResourcePoolRef" TEXT NOT NULL,
"ResourcePoolName" TEXT NOT NULL,
"LastSeen" BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY ("Vcenter","ResourcePoolRef")
)`,
`
CREATE TABLE IF NOT EXISTS vcenter_host_cache (
"Vcenter" TEXT NOT NULL,
"HostRef" TEXT NOT NULL,
"Cluster" TEXT,
"Datacenter" TEXT,
"LastSeen" BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY ("Vcenter","HostRef")
)`,
}
for _, ddl := range ddls {
if _, err := execLog(ctx, dbConn, ddl); err != nil {
return err
}
}
indexes := []string{
`CREATE INDEX IF NOT EXISTS vcenter_folder_cache_vcenter_idx ON vcenter_folder_cache ("Vcenter")`,
`CREATE INDEX IF NOT EXISTS vcenter_resource_pool_cache_vcenter_idx ON vcenter_resource_pool_cache ("Vcenter")`,
`CREATE INDEX IF NOT EXISTS vcenter_host_cache_vcenter_idx ON vcenter_host_cache ("Vcenter")`,
}
for _, idx := range indexes {
if _, err := execLog(ctx, dbConn, idx); err != nil {
return err
}
}
return nil
})
}
func LoadVcenterFolderCache(ctx context.Context, dbConn *sqlx.DB, vcenter string) (map[string]string, error) {
cache := make(map[string]string)
vcenter = strings.TrimSpace(vcenter)
if vcenter == "" {
return cache, nil
}
if err := EnsureVcenterReferenceCacheTables(ctx, dbConn); err != nil {
return nil, err
}
query := dbConn.Rebind(`
SELECT "FolderRef","FolderPath"
FROM vcenter_folder_cache
WHERE "Vcenter" = ?
`)
rows, err := dbConn.QueryxContext(ctx, query, vcenter)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var ref string
var pathValue string
if err := rows.Scan(&ref, &pathValue); err != nil {
return nil, err
}
ref = strings.TrimSpace(ref)
pathValue = strings.TrimSpace(pathValue)
if ref == "" || pathValue == "" {
continue
}
cache[ref] = pathValue
}
return cache, rows.Err()
}
func LoadVcenterResourcePoolCache(ctx context.Context, dbConn *sqlx.DB, vcenter string) (map[string]string, error) {
cache := make(map[string]string)
vcenter = strings.TrimSpace(vcenter)
if vcenter == "" {
return cache, nil
}
if err := EnsureVcenterReferenceCacheTables(ctx, dbConn); err != nil {
return nil, err
}
query := dbConn.Rebind(`
SELECT "ResourcePoolRef","ResourcePoolName"
FROM vcenter_resource_pool_cache
WHERE "Vcenter" = ?
`)
rows, err := dbConn.QueryxContext(ctx, query, vcenter)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var ref string
var name string
if err := rows.Scan(&ref, &name); err != nil {
return nil, err
}
ref = strings.TrimSpace(ref)
name = strings.TrimSpace(name)
if ref == "" || name == "" {
continue
}
cache[ref] = name
}
return cache, rows.Err()
}
func LoadVcenterHostCache(ctx context.Context, dbConn *sqlx.DB, vcenter string) (map[string]VcenterHostCacheEntry, error) {
cache := make(map[string]VcenterHostCacheEntry)
vcenter = strings.TrimSpace(vcenter)
if vcenter == "" {
return cache, nil
}
if err := EnsureVcenterReferenceCacheTables(ctx, dbConn); err != nil {
return nil, err
}
query := dbConn.Rebind(`
SELECT "HostRef","Cluster","Datacenter"
FROM vcenter_host_cache
WHERE "Vcenter" = ?
`)
rows, err := dbConn.QueryxContext(ctx, query, vcenter)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var ref string
var cluster sql.NullString
var datacenter sql.NullString
if err := rows.Scan(&ref, &cluster, &datacenter); err != nil {
return nil, err
}
ref = strings.TrimSpace(ref)
if ref == "" {
continue
}
cache[ref] = VcenterHostCacheEntry{
Cluster: strings.TrimSpace(cluster.String),
Datacenter: strings.TrimSpace(datacenter.String),
}
}
return cache, rows.Err()
}
func UpsertVcenterFolderCache(ctx context.Context, dbConn *sqlx.DB, vcenter, folderRef, folderPath string, lastSeen int64) error {
vcenter = strings.TrimSpace(vcenter)
folderRef = strings.TrimSpace(folderRef)
folderPath = strings.TrimSpace(folderPath)
if vcenter == "" || folderRef == "" || folderPath == "" {
return nil
}
if err := EnsureVcenterReferenceCacheTables(ctx, dbConn); err != nil {
return err
}
query := dbConn.Rebind(`
INSERT INTO vcenter_folder_cache ("Vcenter","FolderRef","FolderPath","LastSeen")
VALUES (?, ?, ?, ?)
ON CONFLICT ("Vcenter","FolderRef") DO UPDATE SET
"FolderPath" = EXCLUDED."FolderPath",
"LastSeen" = CASE
WHEN EXCLUDED."LastSeen" > vcenter_folder_cache."LastSeen" THEN EXCLUDED."LastSeen"
ELSE vcenter_folder_cache."LastSeen"
END
`)
_, err := execLog(ctx, dbConn, query, vcenter, folderRef, folderPath, lastSeen)
return err
}
func UpsertVcenterResourcePoolCache(ctx context.Context, dbConn *sqlx.DB, vcenter, resourcePoolRef, resourcePoolName string, lastSeen int64) error {
vcenter = strings.TrimSpace(vcenter)
resourcePoolRef = strings.TrimSpace(resourcePoolRef)
resourcePoolName = strings.TrimSpace(resourcePoolName)
if vcenter == "" || resourcePoolRef == "" || resourcePoolName == "" {
return nil
}
if err := EnsureVcenterReferenceCacheTables(ctx, dbConn); err != nil {
return err
}
query := dbConn.Rebind(`
INSERT INTO vcenter_resource_pool_cache ("Vcenter","ResourcePoolRef","ResourcePoolName","LastSeen")
VALUES (?, ?, ?, ?)
ON CONFLICT ("Vcenter","ResourcePoolRef") DO UPDATE SET
"ResourcePoolName" = EXCLUDED."ResourcePoolName",
"LastSeen" = CASE
WHEN EXCLUDED."LastSeen" > vcenter_resource_pool_cache."LastSeen" THEN EXCLUDED."LastSeen"
ELSE vcenter_resource_pool_cache."LastSeen"
END
`)
_, err := execLog(ctx, dbConn, query, vcenter, resourcePoolRef, resourcePoolName, lastSeen)
return err
}
func UpsertVcenterHostCache(ctx context.Context, dbConn *sqlx.DB, vcenter, hostRef, cluster, datacenter string, lastSeen int64) error {
vcenter = strings.TrimSpace(vcenter)
hostRef = strings.TrimSpace(hostRef)
cluster = strings.TrimSpace(cluster)
datacenter = strings.TrimSpace(datacenter)
if vcenter == "" || hostRef == "" {
return nil
}
if cluster == "" && datacenter == "" {
return nil
}
if err := EnsureVcenterReferenceCacheTables(ctx, dbConn); err != nil {
return err
}
query := dbConn.Rebind(`
INSERT INTO vcenter_host_cache ("Vcenter","HostRef","Cluster","Datacenter","LastSeen")
VALUES (?, ?, ?, ?, ?)
ON CONFLICT ("Vcenter","HostRef") DO UPDATE SET
"Cluster" = COALESCE(NULLIF(EXCLUDED."Cluster", ''), vcenter_host_cache."Cluster"),
"Datacenter" = COALESCE(NULLIF(EXCLUDED."Datacenter", ''), vcenter_host_cache."Datacenter"),
"LastSeen" = CASE
WHEN EXCLUDED."LastSeen" > vcenter_host_cache."LastSeen" THEN EXCLUDED."LastSeen"
ELSE vcenter_host_cache."LastSeen"
END
`)
_, err := execLog(ctx, dbConn, query, vcenter, hostRef, cluster, datacenter, lastSeen)
return err
}
// UpsertVmIdentity updates/creates the identity record and records rename events.
func UpsertVmIdentity(ctx context.Context, dbConn *sqlx.DB, vcenter string, vmId, vmUuid sql.NullString, name string, cluster sql.NullString, snapshotTime time.Time) error {
keyVmID := strings.TrimSpace(vmId.String)

View File

@@ -180,6 +180,8 @@ func ensureDestinationImportTable(ctx context.Context, destination *sqlx.DB, tab
return EnsureVcenterLatestTotalsTable(ctx, destination)
case tableName == "vcenter_aggregate_totals":
return EnsureVcenterAggregateTotalsTable(ctx, destination)
case tableName == "vcenter_folder_cache", tableName == "vcenter_resource_pool_cache", tableName == "vcenter_host_cache":
return EnsureVcenterReferenceCacheTables(ctx, destination)
default:
return fmt.Errorf("source table %q does not exist in postgres and cannot be auto-created", tableName)
}