Add vCenter reference cache tables and update related functions
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
242
db/helpers.go
242
db/helpers.go
@@ -30,6 +30,11 @@ type ColumnDef struct {
|
|||||||
Type string
|
Type string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VcenterHostCacheEntry struct {
|
||||||
|
Cluster string
|
||||||
|
Datacenter string
|
||||||
|
}
|
||||||
|
|
||||||
type ensureOnceKey struct {
|
type ensureOnceKey struct {
|
||||||
dbConn *sqlx.DB
|
dbConn *sqlx.DB
|
||||||
name string
|
name string
|
||||||
@@ -1107,6 +1112,243 @@ CREATE TABLE IF NOT EXISTS vm_renames (
|
|||||||
return nil
|
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.
|
// 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 {
|
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)
|
keyVmID := strings.TrimSpace(vmId.String)
|
||||||
|
|||||||
@@ -180,6 +180,8 @@ func ensureDestinationImportTable(ctx context.Context, destination *sqlx.DB, tab
|
|||||||
return EnsureVcenterLatestTotalsTable(ctx, destination)
|
return EnsureVcenterLatestTotalsTable(ctx, destination)
|
||||||
case tableName == "vcenter_aggregate_totals":
|
case tableName == "vcenter_aggregate_totals":
|
||||||
return EnsureVcenterAggregateTotalsTable(ctx, destination)
|
return EnsureVcenterAggregateTotalsTable(ctx, destination)
|
||||||
|
case tableName == "vcenter_folder_cache", tableName == "vcenter_resource_pool_cache", tableName == "vcenter_host_cache":
|
||||||
|
return EnsureVcenterReferenceCacheTables(ctx, destination)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("source table %q does not exist in postgres and cannot be auto-created", tableName)
|
return fmt.Errorf("source table %q does not exist in postgres and cannot be auto-created", tableName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -682,7 +682,7 @@ func (c *CronTask) generateReport(ctx context.Context, tableName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func snapshotFromVM(vmObject *mo.VirtualMachine, vc *vcenter.Vcenter, snapshotTime time.Time, inv *queries.Inventory, hostLookup map[string]vcenter.HostLookup, folderLookup vcenter.FolderLookup, rpLookup map[string]string) (InventorySnapshotRow, error) {
|
func snapshotFromVM(ctx context.Context, dbConn *sqlx.DB, vmObject *mo.VirtualMachine, vc *vcenter.Vcenter, snapshotTime time.Time, inv *queries.Inventory, hostLookup map[string]vcenter.HostLookup, folderLookup vcenter.FolderLookup, rpLookup map[string]string) (InventorySnapshotRow, error) {
|
||||||
if vmObject == nil {
|
if vmObject == nil {
|
||||||
return InventorySnapshotRow{}, fmt.Errorf("missing VM object")
|
return InventorySnapshotRow{}, fmt.Errorf("missing VM object")
|
||||||
}
|
}
|
||||||
@@ -772,23 +772,93 @@ func snapshotFromVM(vmObject *mo.VirtualMachine, vc *vcenter.Vcenter, snapshotTi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if row.ResourcePool.String == "" && vmObject.ResourcePool != nil {
|
if row.ResourcePool.String == "" && vmObject.ResourcePool != nil {
|
||||||
|
resourcePoolRef := vmObject.ResourcePool.Value
|
||||||
if rpLookup != nil {
|
if rpLookup != nil {
|
||||||
if rpName, ok := rpLookup[vmObject.ResourcePool.Value]; ok {
|
if rpName, ok := rpLookup[resourcePoolRef]; ok {
|
||||||
row.ResourcePool = sql.NullString{String: normalizeResourcePool(rpName), Valid: rpName != ""}
|
rpName = normalizeResourcePool(rpName)
|
||||||
|
row.ResourcePool = sql.NullString{String: rpName, Valid: rpName != ""}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if row.ResourcePool.String == "" {
|
||||||
|
rpName, err := vc.GetResourcePoolNameByRef(*vmObject.ResourcePool)
|
||||||
|
if err != nil {
|
||||||
|
if vc.Logger != nil {
|
||||||
|
vc.Logger.Warn("failed to resolve resource pool from vcenter", "vm_id", vmObject.Reference().Value, "resource_pool_ref", resourcePoolRef, "error", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rpName = normalizeResourcePool(rpName)
|
||||||
|
row.ResourcePool = sql.NullString{String: rpName, Valid: rpName != ""}
|
||||||
|
if rpLookup != nil && rpName != "" {
|
||||||
|
rpLookup[resourcePoolRef] = rpName
|
||||||
|
}
|
||||||
|
if dbConn != nil && rpName != "" {
|
||||||
|
if err := db.UpsertVcenterResourcePoolCache(ctx, dbConn, vc.Vurl, resourcePoolRef, rpName, snapshotTime.Unix()); err != nil && vc.Logger != nil {
|
||||||
|
vc.Logger.Warn("failed to persist resource pool cache", "vcenter", vc.Vurl, "resource_pool_ref", resourcePoolRef, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if row.Folder.String == "" {
|
if row.Folder.String == "" {
|
||||||
if folderPath, ok := vc.GetVMFolderPathFromLookup(*vmObject, folderLookup); ok {
|
folderRef := ""
|
||||||
|
if vmObject.Parent != nil {
|
||||||
|
folderRef = vmObject.Parent.Value
|
||||||
|
}
|
||||||
|
if folderRef != "" && folderLookup != nil {
|
||||||
|
if folderPath, ok := folderLookup[folderRef]; ok {
|
||||||
row.Folder = sql.NullString{String: folderPath, Valid: folderPath != ""}
|
row.Folder = sql.NullString{String: folderPath, Valid: folderPath != ""}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if row.Folder.String == "" {
|
||||||
|
folderPath, err := vc.GetVMFolderPath(*vmObject)
|
||||||
|
if err != nil {
|
||||||
|
if vc.Logger != nil {
|
||||||
|
vc.Logger.Warn("failed to resolve folder path from vcenter", "vm_id", vmObject.Reference().Value, "error", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Unable to resolve folder path from lookup; leave empty.
|
row.Folder = sql.NullString{String: folderPath, Valid: folderPath != ""}
|
||||||
|
if folderLookup != nil && folderRef != "" && folderPath != "" {
|
||||||
|
folderLookup[folderRef] = folderPath
|
||||||
|
}
|
||||||
|
if dbConn != nil && folderRef != "" && folderPath != "" {
|
||||||
|
if err := db.UpsertVcenterFolderCache(ctx, dbConn, vc.Vurl, folderRef, folderPath, snapshotTime.Unix()); err != nil && vc.Logger != nil {
|
||||||
|
vc.Logger.Warn("failed to persist folder cache", "vcenter", vc.Vurl, "folder_ref", folderRef, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if vmObject.Runtime.Host != nil && hostLookup != nil {
|
hostRef := ""
|
||||||
if lookup, ok := hostLookup[vmObject.Runtime.Host.Value]; ok {
|
if vmObject.Runtime.Host != nil {
|
||||||
|
hostRef = vmObject.Runtime.Host.Value
|
||||||
|
}
|
||||||
|
if hostRef != "" {
|
||||||
|
lookup, found := vcenter.HostLookup{}, false
|
||||||
|
if hostLookup != nil {
|
||||||
|
lookup, found = hostLookup[hostRef]
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
resolved, err := vc.GetHostLookupByRef(*vmObject.Runtime.Host)
|
||||||
|
if err != nil {
|
||||||
|
if vc.Logger != nil {
|
||||||
|
vc.Logger.Warn("failed to resolve host lookup from vcenter", "vm_id", vmObject.Reference().Value, "host_ref", hostRef, "error", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lookup = resolved
|
||||||
|
found = true
|
||||||
|
if hostLookup != nil {
|
||||||
|
hostLookup[hostRef] = resolved
|
||||||
|
}
|
||||||
|
if dbConn != nil && (resolved.Cluster != "" || resolved.Datacenter != "") {
|
||||||
|
if err := db.UpsertVcenterHostCache(ctx, dbConn, vc.Vurl, hostRef, resolved.Cluster, resolved.Datacenter, snapshotTime.Unix()); err != nil && vc.Logger != nil {
|
||||||
|
vc.Logger.Warn("failed to persist host cache", "vcenter", vc.Vurl, "host_ref", hostRef, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
if row.Cluster.String == "" && lookup.Cluster != "" {
|
if row.Cluster.String == "" && lookup.Cluster != "" {
|
||||||
row.Cluster = sql.NullString{String: lookup.Cluster, Valid: true}
|
row.Cluster = sql.NullString{String: lookup.Cluster, Valid: true}
|
||||||
}
|
}
|
||||||
@@ -801,6 +871,15 @@ func snapshotFromVM(vmObject *mo.VirtualMachine, vc *vcenter.Vcenter, snapshotTi
|
|||||||
if row.Cluster.String == "" && vmObject.Runtime.Host != nil {
|
if row.Cluster.String == "" && vmObject.Runtime.Host != nil {
|
||||||
if clusterName, err := vc.GetClusterFromHost(vmObject.Runtime.Host); err == nil {
|
if clusterName, err := vc.GetClusterFromHost(vmObject.Runtime.Host); err == nil {
|
||||||
row.Cluster = sql.NullString{String: clusterName, Valid: clusterName != ""}
|
row.Cluster = sql.NullString{String: clusterName, Valid: clusterName != ""}
|
||||||
|
if dbConn != nil && hostRef != "" && clusterName != "" {
|
||||||
|
dc := ""
|
||||||
|
if row.Datacenter.Valid {
|
||||||
|
dc = row.Datacenter.String
|
||||||
|
}
|
||||||
|
if err := db.UpsertVcenterHostCache(ctx, dbConn, vc.Vurl, hostRef, clusterName, dc, snapshotTime.Unix()); err != nil && vc.Logger != nil {
|
||||||
|
vc.Logger.Warn("failed to persist host cluster cache", "vcenter", vc.Vurl, "host_ref", hostRef, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if vc.Logger != nil {
|
} else if vc.Logger != nil {
|
||||||
vc.Logger.Warn("failed to resolve cluster from host", "vm_id", vmObject.Reference().Value, "error", err)
|
vc.Logger.Warn("failed to resolve cluster from host", "vm_id", vmObject.Reference().Value, "error", err)
|
||||||
}
|
}
|
||||||
@@ -809,6 +888,15 @@ func snapshotFromVM(vmObject *mo.VirtualMachine, vc *vcenter.Vcenter, snapshotTi
|
|||||||
if row.Datacenter.String == "" {
|
if row.Datacenter.String == "" {
|
||||||
if dcName, err := vc.GetDatacenterForVM(*vmObject); err == nil {
|
if dcName, err := vc.GetDatacenterForVM(*vmObject); err == nil {
|
||||||
row.Datacenter = sql.NullString{String: dcName, Valid: dcName != ""}
|
row.Datacenter = sql.NullString{String: dcName, Valid: dcName != ""}
|
||||||
|
if dbConn != nil && hostRef != "" && dcName != "" {
|
||||||
|
clusterName := ""
|
||||||
|
if row.Cluster.Valid {
|
||||||
|
clusterName = row.Cluster.String
|
||||||
|
}
|
||||||
|
if err := db.UpsertVcenterHostCache(ctx, dbConn, vc.Vurl, hostRef, clusterName, dcName, snapshotTime.Unix()); err != nil && vc.Logger != nil {
|
||||||
|
vc.Logger.Warn("failed to persist host datacenter cache", "vcenter", vc.Vurl, "host_ref", hostRef, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,7 +1046,7 @@ func (c *CronTask) buildPresentSnapshots(ctx context.Context, dbConn *sqlx.DB, v
|
|||||||
inv = &existingCopy
|
inv = &existingCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
row, err := snapshotFromVM(&vm, vc, startTime, inv, hostLookup, folderLookup, rpLookup)
|
row, err := snapshotFromVM(ctx, dbConn, &vm, vc, startTime, inv, hostLookup, folderLookup, rpLookup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("unable to build snapshot for VM", "vm_id", vm.Reference().Value, "error", err)
|
log.Error("unable to build snapshot for VM", "vm_id", vm.Reference().Value, "error", err)
|
||||||
continue
|
continue
|
||||||
@@ -1025,27 +1113,40 @@ func (c *CronTask) initVcenterResources(ctx context.Context, log *slog.Logger, u
|
|||||||
if err := db.EnsureVmIdentityTables(ctx, c.Database.DB()); err != nil {
|
if err := db.EnsureVmIdentityTables(ctx, c.Database.DB()); err != nil {
|
||||||
log.Warn("failed to ensure vm identity tables", "error", err)
|
log.Warn("failed to ensure vm identity tables", "error", err)
|
||||||
}
|
}
|
||||||
|
if err := db.EnsureVcenterReferenceCacheTables(ctx, c.Database.DB()); err != nil {
|
||||||
|
log.Warn("failed to ensure vcenter reference cache tables", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
hostLookup, err := vc.BuildHostLookup()
|
hostLookup := make(map[string]vcenter.HostLookup)
|
||||||
|
cachedHosts, err := db.LoadVcenterHostCache(ctx, c.Database.DB(), url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("failed to build host lookup", "url", url, "error", err)
|
log.Warn("failed to load host lookup cache", "url", url, "error", err)
|
||||||
hostLookup = nil
|
|
||||||
} else {
|
} else {
|
||||||
log.Debug("built host lookup", "url", url, "hosts", len(hostLookup))
|
for hostRef, entry := range cachedHosts {
|
||||||
|
hostLookup[hostRef] = vcenter.HostLookup{
|
||||||
|
Cluster: entry.Cluster,
|
||||||
|
Datacenter: entry.Datacenter,
|
||||||
}
|
}
|
||||||
folderLookup, err := vc.BuildFolderPathLookup()
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("failed to build folder lookup", "url", url, "error", err)
|
|
||||||
folderLookup = nil
|
|
||||||
} else {
|
|
||||||
log.Debug("built folder lookup", "url", url, "folders", len(folderLookup))
|
|
||||||
}
|
}
|
||||||
rpLookup, err := vc.BuildResourcePoolLookup()
|
log.Debug("loaded host lookup cache", "url", url, "hosts", len(hostLookup))
|
||||||
|
}
|
||||||
|
|
||||||
|
folderLookup := make(map[string]string)
|
||||||
|
cachedFolders, err := db.LoadVcenterFolderCache(ctx, c.Database.DB(), url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("failed to build resource pool lookup", "url", url, "error", err)
|
log.Warn("failed to load folder lookup cache", "url", url, "error", err)
|
||||||
rpLookup = nil
|
|
||||||
} else {
|
} else {
|
||||||
log.Debug("built resource pool lookup", "url", url, "pools", len(rpLookup))
|
folderLookup = cachedFolders
|
||||||
|
log.Debug("loaded folder lookup cache", "url", url, "folders", len(folderLookup))
|
||||||
|
}
|
||||||
|
|
||||||
|
rpLookup := make(map[string]string)
|
||||||
|
cachedPools, err := db.LoadVcenterResourcePoolCache(ctx, c.Database.DB(), url)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("failed to load resource pool cache", "url", url, "error", err)
|
||||||
|
} else {
|
||||||
|
rpLookup = cachedPools
|
||||||
|
log.Debug("loaded resource pool cache", "url", url, "pools", len(rpLookup))
|
||||||
}
|
}
|
||||||
|
|
||||||
res.vms = vms
|
res.vms = vms
|
||||||
|
|||||||
@@ -429,20 +429,17 @@ func (v *Vcenter) findVmDeletionEvents(ctx context.Context, begin, end time.Time
|
|||||||
if len(events) == 0 {
|
if len(events) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
pageCount++
|
||||||
processEvents(events)
|
processEvents(events)
|
||||||
if candidateSet != nil && foundCandidates >= len(candidateSet) {
|
if candidateSet != nil && foundCandidates >= len(candidateSet) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
pageCount++
|
|
||||||
if pageCount >= maxEventPages {
|
if pageCount >= maxEventPages {
|
||||||
if v.Logger != nil {
|
if v.Logger != nil {
|
||||||
v.Logger.Warn("vcenter deletion events truncated", "vcenter", v.Vurl, "label", label, "pages", pageCount, "page_size", eventPageSize, "window_start_utc", beginUTC, "window_end_utc", endUTC)
|
v.Logger.Warn("vcenter deletion events truncated", "vcenter", v.Vurl, "label", label, "pages", pageCount, "page_size", eventPageSize, "window_start_utc", beginUTC, "window_end_utc", endUTC)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if len(events) < int(eventPageSize) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -454,6 +451,7 @@ func (v *Vcenter) findVmDeletionEvents(ctx context.Context, begin, end time.Time
|
|||||||
BeginTime: &beginUTC,
|
BeginTime: &beginUTC,
|
||||||
EndTime: &endUTC,
|
EndTime: &endUTC,
|
||||||
},
|
},
|
||||||
|
MaxCount: eventPageSize,
|
||||||
DisableFullMessage: &disableFullMessage,
|
DisableFullMessage: &disableFullMessage,
|
||||||
EventTypeId: []string{
|
EventTypeId: []string{
|
||||||
"VmRemovedEvent",
|
"VmRemovedEvent",
|
||||||
@@ -477,6 +475,7 @@ func (v *Vcenter) findVmDeletionEvents(ctx context.Context, begin, end time.Time
|
|||||||
BeginTime: &beginUTC,
|
BeginTime: &beginUTC,
|
||||||
EndTime: &endUTC,
|
EndTime: &endUTC,
|
||||||
},
|
},
|
||||||
|
MaxCount: eventPageSize,
|
||||||
DisableFullMessage: &disableFullMessage,
|
DisableFullMessage: &disableFullMessage,
|
||||||
}
|
}
|
||||||
fc, err := mgr.CreateCollectorForEvents(ctx, fallbackFilter)
|
fc, err := mgr.CreateCollectorForEvents(ctx, fallbackFilter)
|
||||||
@@ -673,31 +672,55 @@ func (v *Vcenter) GetHostSystemObject(hostRef types.ManagedObjectReference) (*mo
|
|||||||
return &hs, nil
|
return &hs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Vcenter) GetHostLookupByRef(hostRef types.ManagedObjectReference) (HostLookup, error) {
|
||||||
|
lookup := HostLookup{}
|
||||||
|
|
||||||
|
var host mo.HostSystem
|
||||||
|
if err := v.client.RetrieveOne(v.ctx, hostRef, []string{"parent"}, &host); err != nil {
|
||||||
|
return lookup, fmt.Errorf("failed to retrieve host parent: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if host.Parent != nil && (host.Parent.Type == "ClusterComputeResource" || host.Parent.Type == "ComputeResource") {
|
||||||
|
var moCompute mo.ComputeResource
|
||||||
|
if err := v.client.RetrieveOne(v.ctx, *host.Parent, []string{"name"}, &moCompute); err != nil {
|
||||||
|
if v.Logger != nil {
|
||||||
|
v.Logger.Warn("failed to resolve cluster/compute name from host parent", "host_ref", hostRef.Value, "error", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lookup.Cluster = moCompute.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entities, err := mo.Ancestors(v.ctx, v.client.Client, v.client.ServiceContent.PropertyCollector, hostRef)
|
||||||
|
if err != nil {
|
||||||
|
if v.Logger != nil {
|
||||||
|
v.Logger.Warn("failed to resolve datacenter from host ancestors", "host_ref", hostRef.Value, "error", err)
|
||||||
|
}
|
||||||
|
return lookup, nil
|
||||||
|
}
|
||||||
|
for _, entity := range entities {
|
||||||
|
if entity.Self.Type != "Datacenter" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lookup.Datacenter = entity.Name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookup, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Function to find the cluster or compute resource from a host reference
|
// Function to find the cluster or compute resource from a host reference
|
||||||
func (v *Vcenter) GetClusterFromHost(hostRef *types.ManagedObjectReference) (string, error) {
|
func (v *Vcenter) GetClusterFromHost(hostRef *types.ManagedObjectReference) (string, error) {
|
||||||
if hostRef == nil {
|
if hostRef == nil {
|
||||||
v.Logger.Warn("nil hostRef passed to GetClusterFromHost")
|
v.Logger.Warn("nil hostRef passed to GetClusterFromHost")
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
var host mo.HostSystem
|
lookup, err := v.GetHostLookupByRef(*hostRef)
|
||||||
err := v.client.RetrieveOne(v.ctx, *hostRef, []string{"parent"}, &host)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to retrieve host parent: %w", err)
|
v.Logger.Error("cant get host lookup", "error", err)
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
return lookup.Cluster, nil
|
||||||
if host.Parent == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
if host.Parent.Type != "ClusterComputeResource" && host.Parent.Type != "ComputeResource" {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var moCompute mo.ComputeResource
|
|
||||||
if err := v.client.RetrieveOne(v.ctx, *host.Parent, []string{"name"}, &moCompute); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to retrieve compute resource: %w", err)
|
|
||||||
}
|
|
||||||
v.Logger.Debug("vm host parent compute resource resolved", "name", moCompute.Name, "host_ref", hostRef.Value)
|
|
||||||
return moCompute.Name, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to determine the datacenter a VM belongs to
|
// Function to determine the datacenter a VM belongs to
|
||||||
@@ -784,12 +807,19 @@ func (v *Vcenter) FindVMByIDWithDatacenter(vmID string, dcID string) (*mo.Virtua
|
|||||||
return &vm, nil
|
return &vm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Vcenter) GetResourcePoolNameByRef(resourcePoolRef types.ManagedObjectReference) (string, error) {
|
||||||
|
var pool mo.ResourcePool
|
||||||
|
if err := v.client.RetrieveOne(v.ctx, resourcePoolRef, []string{"name"}, &pool); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to retrieve resource pool: %w", err)
|
||||||
|
}
|
||||||
|
return pool.Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to retrieve the resource pool for the VM
|
// Helper function to retrieve the resource pool for the VM
|
||||||
func (v *Vcenter) GetVmResourcePool(vm mo.VirtualMachine) (string, error) {
|
func (v *Vcenter) GetVmResourcePool(vm mo.VirtualMachine) (string, error) {
|
||||||
var resourcePool string
|
var resourcePool string
|
||||||
if vm.ResourcePool != nil {
|
if vm.ResourcePool != nil {
|
||||||
rp := object.NewResourcePool(v.client.Client, *vm.ResourcePool)
|
rpName, err := v.GetResourcePoolNameByRef(*vm.ResourcePool)
|
||||||
rpName, err := rp.ObjectName(v.ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Logger.Error("failed to get resource pool name", "error", err)
|
v.Logger.Error("failed to get resource pool name", "error", err)
|
||||||
return resourcePool, err
|
return resourcePool, err
|
||||||
|
|||||||
Reference in New Issue
Block a user