capture new disk size
Some checks are pending
CI / Lint (push) Waiting to run
CI / Test (push) Waiting to run
CI / End-to-End (push) Waiting to run
CI / Publish Docker (push) Blocked by required conditions
continuous-integration/drone/push Build is passing

This commit is contained in:
2024-09-16 15:04:31 +10:00
parent fb4a7a790d
commit c122e775a3
7 changed files with 212 additions and 25 deletions

View File

@@ -0,0 +1,9 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE "Updates" ADD COLUMN NewProvisionedDisk REAL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE "Updates" DROP COLUMN NewProvisionedDisk;
-- +goose StatementEnd

View File

@@ -58,4 +58,5 @@ type Updates struct {
NewResourcePool sql.NullString
EventKey sql.NullString
EventId sql.NullString
NewProvisionedDisk sql.NullFloat64
}

View File

@@ -29,9 +29,9 @@ WHERE "VmId" = sqlc.arg('vmId') AND "Datacenter" = sqlc.arg('datacenterName');
-- name: CreateUpdate :one
INSERT INTO "Updates" (
"InventoryId", "EventKey", "EventId", "UpdateTime", "UpdateType", "NewVcpus", "NewRam", "NewResourcePool"
"InventoryId", "EventKey", "EventId", "UpdateTime", "UpdateType", "NewVcpus", "NewRam", "NewResourcePool", "NewProvisionedDisk"
) VALUES(
?, ?, ?, ?, ?, ?, ?, ?
?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING *;

View File

@@ -146,11 +146,11 @@ func (q *Queries) CreateInventory(ctx context.Context, arg CreateInventoryParams
const createUpdate = `-- name: CreateUpdate :one
INSERT INTO "Updates" (
"InventoryId", "EventKey", "EventId", "UpdateTime", "UpdateType", "NewVcpus", "NewRam", "NewResourcePool"
"InventoryId", "EventKey", "EventId", "UpdateTime", "UpdateType", "NewVcpus", "NewRam", "NewResourcePool", "NewProvisionedDisk"
) VALUES(
?, ?, ?, ?, ?, ?, ?, ?
?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING Uid, InventoryId, UpdateTime, UpdateType, NewVcpus, NewRam, NewResourcePool, EventKey, EventId
RETURNING Uid, InventoryId, UpdateTime, UpdateType, NewVcpus, NewRam, NewResourcePool, EventKey, EventId, NewProvisionedDisk
`
type CreateUpdateParams struct {
@@ -162,6 +162,7 @@ type CreateUpdateParams struct {
NewVcpus sql.NullInt64
NewRam sql.NullInt64
NewResourcePool sql.NullString
NewProvisionedDisk sql.NullFloat64
}
func (q *Queries) CreateUpdate(ctx context.Context, arg CreateUpdateParams) (Updates, error) {
@@ -174,6 +175,7 @@ func (q *Queries) CreateUpdate(ctx context.Context, arg CreateUpdateParams) (Upd
arg.NewVcpus,
arg.NewRam,
arg.NewResourcePool,
arg.NewProvisionedDisk,
)
var i Updates
err := row.Scan(
@@ -186,6 +188,7 @@ func (q *Queries) CreateUpdate(ctx context.Context, arg CreateUpdateParams) (Upd
&i.NewResourcePool,
&i.EventKey,
&i.EventId,
&i.NewProvisionedDisk,
)
return i, err
}

View File

@@ -32,11 +32,11 @@ func (h *Handler) VmDelete(w http.ResponseWriter, r *http.Request) {
var event models.CloudEventReceived
if err := json.Unmarshal(reqBody, &event); err != nil {
h.Logger.Error("unable to decode json", "error", err)
prettyPrint(event)
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
return
} else {
h.Logger.Debug("successfully decoded JSON")
prettyPrint(event)
}
// Use the event CreatedTime to update the DeletionTime column in the VM inventory table

View File

@@ -12,7 +12,10 @@ import (
"strings"
"time"
"vctp/db/queries"
"vctp/internal/vcenter"
models "vctp/server/models"
"github.com/vmware/govmomi/vim25/types"
)
// VmModify receives the CloudEvent for a VM modification or move
@@ -65,6 +68,7 @@ func (h *Handler) VmModify(w http.ResponseWriter, r *http.Request) {
} else {
found = true
params.NewVcpus = sql.NullInt64{Int64: i, Valid: i > 0}
params.UpdateType = "reconfigure"
}
case "config.hardware.memoryMB":
i, err := strconv.ParseInt(change["newValue"], 10, 64)
@@ -73,6 +77,7 @@ func (h *Handler) VmModify(w http.ResponseWriter, r *http.Request) {
} else {
found = true
params.NewRam = sql.NullInt64{Int64: i, Valid: i > 0}
params.UpdateType = "reconfigure"
}
}
@@ -82,6 +87,10 @@ func (h *Handler) VmModify(w http.ResponseWriter, r *http.Request) {
// TODO - recalculate total disk size
h.Logger.Debug("Detected config change for VM disk")
found = true
params.UpdateType = "diskchange"
diskSize := h.calculateNewDiskSize(event)
params.NewProvisionedDisk = sql.NullFloat64{Float64: diskSize, Valid: diskSize > 0}
}
}
@@ -117,7 +126,6 @@ func (h *Handler) VmModify(w http.ResponseWriter, r *http.Request) {
params.EventId = sql.NullString{String: event.CloudEvent.ID, Valid: event.CloudEvent.ID != ""}
params.EventKey = sql.NullString{String: strconv.Itoa(event.CloudEvent.Data.Key), Valid: event.CloudEvent.Data.Key > 0}
params.UpdateTime = sql.NullInt64{Int64: unixTimestamp, Valid: unixTimestamp > 0}
params.UpdateType = "reconfigure"
// Create the Update database record
result, err := h.Database.Queries().CreateUpdate(context.Background(), params)
@@ -135,7 +143,7 @@ func (h *Handler) VmModify(w http.ResponseWriter, r *http.Request) {
} else {
w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, "Processed update event but no config changes were of interest\n")
prettyPrint(configChanges)
prettyPrint(event.CloudEvent.Data.ConfigSpec)
}
}
}
@@ -179,3 +187,35 @@ func (h *Handler) processConfigChanges(configChanges string) []map[string]string
return result
}
func (h *Handler) calculateNewDiskSize(event models.CloudEventReceived) float64 {
var diskSize float64
h.Logger.Debug("connecting to vcenter")
vc := vcenter.New(h.Logger)
vc.Login(event.CloudEvent.Source)
vmObject, err := vc.FindVMByIDWithDatacenter(event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Datacenter.Value)
if err != nil {
h.Logger.Error("Can't locate vm in vCenter", "vmID", event.CloudEvent.Data.VM.VM.Value, "error", err)
} else {
if vmObject.Vm.Config != nil {
h.Logger.Debug("Found VM with config, calculating new total disk size", "vmID", event.CloudEvent.Data.VM.VM.Value)
// Calculate the total disk allocated in GB
for _, device := range vmObject.Vm.Config.Hardware.Device {
if disk, ok := device.(*types.VirtualDisk); ok {
diskSize += float64(disk.CapacityInBytes / 1024 / 1024 / 1024) // Convert from bytes to GB
}
}
}
}
err = vc.Logout()
if err != nil {
h.Logger.Error("unable to logout of vcenter", "error", err)
}
h.Logger.Debug("Calculated new disk size", "value", diskSize)
return diskSize
}

View File

@@ -2,6 +2,7 @@ package models
import (
"encoding/json"
"time"
)
type CloudEventReceived struct {
@@ -66,3 +67,136 @@ type CloudEventReceived struct {
type ConfigChangesReceived struct {
Modified string `json:"modified"`
}
type ConfigSpec struct {
AlternateGuestName string `json:"AlternateGuestName"`
Annotation string `json:"Annotation"`
BootOptions any `json:"BootOptions"`
ChangeTrackingEnabled any `json:"ChangeTrackingEnabled"`
ChangeVersion string `json:"ChangeVersion"`
ConsolePreferences any `json:"ConsolePreferences"`
CPUAffinity any `json:"CpuAffinity"`
CPUAllocation any `json:"CpuAllocation"`
CPUFeatureMask any `json:"CpuFeatureMask"`
CPUHotAddEnabled any `json:"CpuHotAddEnabled"`
CPUHotRemoveEnabled any `json:"CpuHotRemoveEnabled"`
CreateDate time.Time `json:"CreateDate"`
Crypto any `json:"Crypto"`
DeviceChange []struct {
Backing any `json:"Backing"`
Device struct {
Backing struct {
BackingObjectID string `json:"BackingObjectId"`
ChangeID string `json:"ChangeId"`
ContentID string `json:"ContentId"`
Datastore struct {
Type string `json:"Type"`
Value string `json:"Value"`
} `json:"Datastore"`
DeltaDiskFormat string `json:"DeltaDiskFormat"`
DeltaDiskFormatVariant string `json:"DeltaDiskFormatVariant"`
DeltaGrainSize int `json:"DeltaGrainSize"`
DigestEnabled any `json:"DigestEnabled"`
DiskMode string `json:"DiskMode"`
EagerlyScrub bool `json:"EagerlyScrub"`
FileName string `json:"FileName"`
KeyID any `json:"KeyId"`
Parent any `json:"Parent"`
Sharing string `json:"Sharing"`
Split any `json:"Split"`
ThinProvisioned bool `json:"ThinProvisioned"`
UUID string `json:"Uuid"`
WriteThrough any `json:"WriteThrough"`
} `json:"Backing"`
CapacityInBytes int `json:"CapacityInBytes"`
CapacityInKB int `json:"CapacityInKB"`
Connectable any `json:"Connectable"`
ControllerKey int `json:"ControllerKey"`
DeviceInfo any `json:"DeviceInfo"`
DiskObjectID string `json:"DiskObjectId"`
Iofilter any `json:"Iofilter"`
Key int `json:"Key"`
NativeUnmanagedLinkedClone any `json:"NativeUnmanagedLinkedClone"`
Shares any `json:"Shares"`
SlotInfo any `json:"SlotInfo"`
StorageIOAllocation struct {
Limit int `json:"Limit"`
Reservation any `json:"Reservation"`
Shares struct {
Level string `json:"Level"`
Shares int `json:"Shares"`
} `json:"Shares"`
} `json:"StorageIOAllocation"`
UnitNumber int `json:"UnitNumber"`
VDiskID any `json:"VDiskId"`
VFlashCacheConfigInfo any `json:"VFlashCacheConfigInfo"`
} `json:"Device"`
FileOperation string `json:"FileOperation"`
Operation string `json:"Operation"`
Profile []struct {
ProfileData struct {
ExtensionKey string `json:"ExtensionKey"`
ObjectData time.Time `json:"ObjectData"`
} `json:"ProfileData"`
ProfileID string `json:"ProfileId"`
ProfileParams any `json:"ProfileParams"`
ReplicationSpec any `json:"ReplicationSpec"`
} `json:"Profile"`
} `json:"DeviceChange"`
ExtraConfig any `json:"ExtraConfig"`
Files struct {
FtMetadataDirectory string `json:"FtMetadataDirectory"`
LogDirectory string `json:"LogDirectory"`
SnapshotDirectory string `json:"SnapshotDirectory"`
SuspendDirectory string `json:"SuspendDirectory"`
VMPathName string `json:"VmPathName"`
} `json:"Files"`
Firmware string `json:"Firmware"`
Flags any `json:"Flags"`
FtInfo any `json:"FtInfo"`
GuestAutoLockEnabled any `json:"GuestAutoLockEnabled"`
GuestID string `json:"GuestId"`
GuestMonitoringModeInfo any `json:"GuestMonitoringModeInfo"`
InstanceUUID string `json:"InstanceUuid"`
LatencySensitivity any `json:"LatencySensitivity"`
LocationID string `json:"LocationId"`
ManagedBy any `json:"ManagedBy"`
MaxMksConnections int `json:"MaxMksConnections"`
MemoryAffinity any `json:"MemoryAffinity"`
MemoryAllocation any `json:"MemoryAllocation"`
MemoryHotAddEnabled any `json:"MemoryHotAddEnabled"`
MemoryMB int `json:"MemoryMB"`
MemoryReservationLockedToMax any `json:"MemoryReservationLockedToMax"`
MessageBusTunnelEnabled any `json:"MessageBusTunnelEnabled"`
MigrateEncryption string `json:"MigrateEncryption"`
Name string `json:"Name"`
NestedHVEnabled any `json:"NestedHVEnabled"`
NetworkShaper any `json:"NetworkShaper"`
NpivDesiredNodeWwns int `json:"NpivDesiredNodeWwns"`
NpivDesiredPortWwns int `json:"NpivDesiredPortWwns"`
NpivNodeWorldWideName any `json:"NpivNodeWorldWideName"`
NpivOnNonRdmDisks any `json:"NpivOnNonRdmDisks"`
NpivPortWorldWideName any `json:"NpivPortWorldWideName"`
NpivTemporaryDisabled any `json:"NpivTemporaryDisabled"`
NpivWorldWideNameOp string `json:"NpivWorldWideNameOp"`
NpivWorldWideNameType string `json:"NpivWorldWideNameType"`
NumCPUs int `json:"NumCPUs"`
NumCoresPerSocket int `json:"NumCoresPerSocket"`
PowerOpInfo any `json:"PowerOpInfo"`
RepConfig any `json:"RepConfig"`
ScheduledHardwareUpgradeInfo any `json:"ScheduledHardwareUpgradeInfo"`
SevEnabled any `json:"SevEnabled"`
SgxInfo any `json:"SgxInfo"`
SwapPlacement string `json:"SwapPlacement"`
Tools any `json:"Tools"`
UUID string `json:"Uuid"`
VAppConfig any `json:"VAppConfig"`
VAppConfigRemoved any `json:"VAppConfigRemoved"`
VAssertsEnabled any `json:"VAssertsEnabled"`
VPMCEnabled any `json:"VPMCEnabled"`
VcpuConfig any `json:"VcpuConfig"`
Version string `json:"Version"`
VirtualICH7MPresent any `json:"VirtualICH7MPresent"`
VirtualSMCPresent any `json:"VirtualSMCPresent"`
VMProfile any `json:"VmProfile"`
}