capture new disk size
This commit is contained in:
9
db/migrations/20240916045259_updates_disk.sql
Normal file
9
db/migrations/20240916045259_updates_disk.sql
Normal 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
|
@@ -49,13 +49,14 @@ type Inventory struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Updates struct {
|
type Updates struct {
|
||||||
Uid int64
|
Uid int64
|
||||||
InventoryId sql.NullInt64
|
InventoryId sql.NullInt64
|
||||||
UpdateTime sql.NullInt64
|
UpdateTime sql.NullInt64
|
||||||
UpdateType string
|
UpdateType string
|
||||||
NewVcpus sql.NullInt64
|
NewVcpus sql.NullInt64
|
||||||
NewRam sql.NullInt64
|
NewRam sql.NullInt64
|
||||||
NewResourcePool sql.NullString
|
NewResourcePool sql.NullString
|
||||||
EventKey sql.NullString
|
EventKey sql.NullString
|
||||||
EventId sql.NullString
|
EventId sql.NullString
|
||||||
|
NewProvisionedDisk sql.NullFloat64
|
||||||
}
|
}
|
||||||
|
@@ -29,9 +29,9 @@ WHERE "VmId" = sqlc.arg('vmId') AND "Datacenter" = sqlc.arg('datacenterName');
|
|||||||
|
|
||||||
-- name: CreateUpdate :one
|
-- name: CreateUpdate :one
|
||||||
INSERT INTO "Updates" (
|
INSERT INTO "Updates" (
|
||||||
"InventoryId", "EventKey", "EventId", "UpdateTime", "UpdateType", "NewVcpus", "NewRam", "NewResourcePool"
|
"InventoryId", "EventKey", "EventId", "UpdateTime", "UpdateType", "NewVcpus", "NewRam", "NewResourcePool", "NewProvisionedDisk"
|
||||||
) VALUES(
|
) VALUES(
|
||||||
?, ?, ?, ?, ?, ?, ?, ?
|
?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||||
)
|
)
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
|
@@ -146,22 +146,23 @@ func (q *Queries) CreateInventory(ctx context.Context, arg CreateInventoryParams
|
|||||||
|
|
||||||
const createUpdate = `-- name: CreateUpdate :one
|
const createUpdate = `-- name: CreateUpdate :one
|
||||||
INSERT INTO "Updates" (
|
INSERT INTO "Updates" (
|
||||||
"InventoryId", "EventKey", "EventId", "UpdateTime", "UpdateType", "NewVcpus", "NewRam", "NewResourcePool"
|
"InventoryId", "EventKey", "EventId", "UpdateTime", "UpdateType", "NewVcpus", "NewRam", "NewResourcePool", "NewProvisionedDisk"
|
||||||
) VALUES(
|
) 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 {
|
type CreateUpdateParams struct {
|
||||||
InventoryId sql.NullInt64
|
InventoryId sql.NullInt64
|
||||||
EventKey sql.NullString
|
EventKey sql.NullString
|
||||||
EventId sql.NullString
|
EventId sql.NullString
|
||||||
UpdateTime sql.NullInt64
|
UpdateTime sql.NullInt64
|
||||||
UpdateType string
|
UpdateType string
|
||||||
NewVcpus sql.NullInt64
|
NewVcpus sql.NullInt64
|
||||||
NewRam sql.NullInt64
|
NewRam sql.NullInt64
|
||||||
NewResourcePool sql.NullString
|
NewResourcePool sql.NullString
|
||||||
|
NewProvisionedDisk sql.NullFloat64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateUpdate(ctx context.Context, arg CreateUpdateParams) (Updates, error) {
|
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.NewVcpus,
|
||||||
arg.NewRam,
|
arg.NewRam,
|
||||||
arg.NewResourcePool,
|
arg.NewResourcePool,
|
||||||
|
arg.NewProvisionedDisk,
|
||||||
)
|
)
|
||||||
var i Updates
|
var i Updates
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
@@ -186,6 +188,7 @@ func (q *Queries) CreateUpdate(ctx context.Context, arg CreateUpdateParams) (Upd
|
|||||||
&i.NewResourcePool,
|
&i.NewResourcePool,
|
||||||
&i.EventKey,
|
&i.EventKey,
|
||||||
&i.EventId,
|
&i.EventId,
|
||||||
|
&i.NewProvisionedDisk,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
@@ -32,11 +32,11 @@ func (h *Handler) VmDelete(w http.ResponseWriter, r *http.Request) {
|
|||||||
var event models.CloudEventReceived
|
var event models.CloudEventReceived
|
||||||
if err := json.Unmarshal(reqBody, &event); err != nil {
|
if err := json.Unmarshal(reqBody, &event); err != nil {
|
||||||
h.Logger.Error("unable to decode json", "error", err)
|
h.Logger.Error("unable to decode json", "error", err)
|
||||||
|
prettyPrint(event)
|
||||||
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
|
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
h.Logger.Debug("successfully decoded JSON")
|
h.Logger.Debug("successfully decoded JSON")
|
||||||
prettyPrint(event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the event CreatedTime to update the DeletionTime column in the VM inventory table
|
// Use the event CreatedTime to update the DeletionTime column in the VM inventory table
|
||||||
|
@@ -12,7 +12,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"vctp/db/queries"
|
"vctp/db/queries"
|
||||||
|
"vctp/internal/vcenter"
|
||||||
models "vctp/server/models"
|
models "vctp/server/models"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VmModify receives the CloudEvent for a VM modification or move
|
// 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 {
|
} else {
|
||||||
found = true
|
found = true
|
||||||
params.NewVcpus = sql.NullInt64{Int64: i, Valid: i > 0}
|
params.NewVcpus = sql.NullInt64{Int64: i, Valid: i > 0}
|
||||||
|
params.UpdateType = "reconfigure"
|
||||||
}
|
}
|
||||||
case "config.hardware.memoryMB":
|
case "config.hardware.memoryMB":
|
||||||
i, err := strconv.ParseInt(change["newValue"], 10, 64)
|
i, err := strconv.ParseInt(change["newValue"], 10, 64)
|
||||||
@@ -73,6 +77,7 @@ func (h *Handler) VmModify(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
found = true
|
found = true
|
||||||
params.NewRam = sql.NullInt64{Int64: i, Valid: i > 0}
|
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
|
// TODO - recalculate total disk size
|
||||||
h.Logger.Debug("Detected config change for VM disk")
|
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.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.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.UpdateTime = sql.NullInt64{Int64: unixTimestamp, Valid: unixTimestamp > 0}
|
||||||
params.UpdateType = "reconfigure"
|
|
||||||
|
|
||||||
// Create the Update database record
|
// Create the Update database record
|
||||||
result, err := h.Database.Queries().CreateUpdate(context.Background(), params)
|
result, err := h.Database.Queries().CreateUpdate(context.Background(), params)
|
||||||
@@ -135,7 +143,7 @@ func (h *Handler) VmModify(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusAccepted)
|
||||||
fmt.Fprintf(w, "Processed update event but no config changes were of interest\n")
|
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
|
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
|
||||||
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CloudEventReceived struct {
|
type CloudEventReceived struct {
|
||||||
@@ -66,3 +67,136 @@ type CloudEventReceived struct {
|
|||||||
type ConfigChangesReceived struct {
|
type ConfigChangesReceived struct {
|
||||||
Modified string `json:"modified"`
|
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"`
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user