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

@@ -49,13 +49,14 @@ type Inventory struct {
}
type Updates struct {
Uid int64
InventoryId sql.NullInt64
UpdateTime sql.NullInt64
UpdateType string
NewVcpus sql.NullInt64
NewRam sql.NullInt64
NewResourcePool sql.NullString
EventKey sql.NullString
EventId sql.NullString
Uid int64
InventoryId sql.NullInt64
UpdateTime sql.NullInt64
UpdateType string
NewVcpus sql.NullInt64
NewRam sql.NullInt64
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,22 +146,23 @@ 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 {
InventoryId sql.NullInt64
EventKey sql.NullString
EventId sql.NullString
UpdateTime sql.NullInt64
UpdateType string
NewVcpus sql.NullInt64
NewRam sql.NullInt64
NewResourcePool sql.NullString
InventoryId sql.NullInt64
EventKey sql.NullString
EventId sql.NullString
UpdateTime sql.NullInt64
UpdateType string
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"`
}