create vm in inventory when receiving modify event for vm we dont know about
Some checks failed
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 failing
continuous-integration/drone Build is passing

This commit is contained in:
2024-10-15 16:06:54 +11:00
parent 309db2f1a6
commit f86ec3d615

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -81,7 +82,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
configChanges = h.processConfigChanges(event.CloudEvent.Data.ConfigChanges.Modified) configChanges = h.processConfigChanges(event.CloudEvent.Data.ConfigChanges.Modified)
//prettyPrint(configChanges) //prettyPrint(configChanges)
var found = false var changeFound = false
// Only interested in vCPU or ram changes currently // Only interested in vCPU or ram changes currently
for _, change := range configChanges { for _, change := range configChanges {
//fmt.Printf("Type: %s, New Value: %s\n", change["type"], change["newValue"]) //fmt.Printf("Type: %s, New Value: %s\n", change["type"], change["newValue"])
@@ -91,7 +92,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
h.Logger.Error("Unable to convert new value to int64", "new_value", change["newValue"]) h.Logger.Error("Unable to convert new value to int64", "new_value", change["newValue"])
} else { } else {
found = true changeFound = true
params.NewVcpus = sql.NullInt64{Int64: i, Valid: i > 0} params.NewVcpus = sql.NullInt64{Int64: i, Valid: i > 0}
params.UpdateType = "reconfigure" params.UpdateType = "reconfigure"
} }
@@ -100,12 +101,12 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
h.Logger.Error("Unable to convert new value to int64", "new_value", change["newValue"]) h.Logger.Error("Unable to convert new value to int64", "new_value", change["newValue"])
} else { } else {
found = true changeFound = true
params.NewRam = sql.NullInt64{Int64: i, Valid: i > 0} params.NewRam = sql.NullInt64{Int64: i, Valid: i > 0}
params.UpdateType = "reconfigure" params.UpdateType = "reconfigure"
} }
case "config.managedBy": // This changes when a VM becomes a placeholder or vice versa case "config.managedBy": // This changes when a VM becomes a placeholder or vice versa
found = true changeFound = true
params.UpdateType = "srm" params.UpdateType = "srm"
if change["newValue"] == "(extensionKey = \"com.vmware.vcDr\", type = \"placeholderVm\")" { if change["newValue"] == "(extensionKey = \"com.vmware.vcDr\", type = \"placeholderVm\")" {
@@ -159,7 +160,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
if strings.ToLower(matches[1]) == strings.ToLower(event.CloudEvent.Data.VM.Name) { if strings.ToLower(matches[1]) == strings.ToLower(event.CloudEvent.Data.VM.Name) {
h.Logger.Debug("This disk belongs to this VM") h.Logger.Debug("This disk belongs to this VM")
found = true changeFound = true
diskChangeFound = true diskChangeFound = true
// don't need to keep searching through the rest of the backing devices in this VM // don't need to keep searching through the rest of the backing devices in this VM
@@ -183,7 +184,17 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
} }
// Only create a database record if we found one of the config changes we were interested in // Only create a database record if we found one of the config changes we were interested in
if found { if changeFound {
// Parse the datetime string to a time.Time object
eventTime, err := time.Parse(time.RFC3339, event.CloudEvent.Data.CreatedTime)
if err != nil {
h.Logger.Warn("unable to convert cloud event time to timestamp", "error", err)
unixTimestamp = time.Now().Unix()
} else {
// Convert to Unix timestamp
unixTimestamp = eventTime.Unix()
}
// lookup Iid from Inventory table for this VM // lookup Iid from Inventory table for this VM
// also figure out what to do if we didn't find an entry for this VM in the Inventory table. Create one? // also figure out what to do if we didn't find an entry for this VM in the Inventory table. Create one?
h.Logger.Debug("Checking inventory table for VM record") h.Logger.Debug("Checking inventory table for VM record")
@@ -194,6 +205,37 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
invResult, err := h.Database.Queries().GetInventoryVmId(ctx, invParams) invResult, err := h.Database.Queries().GetInventoryVmId(ctx, invParams)
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) {
// TODO Add a record to the inventory table for this VM
h.Logger.Info("Received VM modify event for a VM not currently in the inventory. Adding to inventory")
iid, err := h.AddVmToInventory(event, ctx, unixTimestamp)
if err != nil {
h.Logger.Error("Received error adding VM to inventory", "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Valid request but experienced error adding vm id '%s' in datacenter name '%s' to inventory table : %s",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err),
})
return
}
if iid > 0 {
params.InventoryId = sql.NullInt64{Int64: iid, Valid: iid > 0}
} else {
h.Logger.Error("Received zero for inventory id when adding VM to inventory")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Valid request but received zero result when adding vm id '%s' in datacenter name '%s' to inventory table",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name),
})
return
}
} else {
h.Logger.Error("unable to find existing inventory record for this VM", "error", err) h.Logger.Error("unable to find existing inventory record for this VM", "error", err)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@@ -203,6 +245,8 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err), event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err),
}) })
return return
}
} else { } else {
params.InventoryId = sql.NullInt64{Int64: invResult.Iid, Valid: invResult.Iid > 0} params.InventoryId = sql.NullInt64{Int64: invResult.Iid, Valid: invResult.Iid > 0}
} }
@@ -219,16 +263,6 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
return return
} }
// Parse the datetime string to a time.Time object
eventTime, err := time.Parse(time.RFC3339, event.CloudEvent.Data.CreatedTime)
if err != nil {
h.Logger.Warn("unable to convert cloud event time to timestamp", "error", err)
unixTimestamp = time.Now().Unix()
} else {
// Convert to Unix timestamp
unixTimestamp = eventTime.Unix()
}
// populate other parameters for the Update database record // populate other parameters for the Update database record
params.Name = sql.NullString{String: event.CloudEvent.Data.VM.Name, Valid: event.CloudEvent.Data.VM.Name != ""} params.Name = sql.NullString{String: event.CloudEvent.Data.VM.Name, Valid: event.CloudEvent.Data.VM.Name != ""}
params.RawChangeString = []byte(event.CloudEvent.Data.ConfigChanges.Modified) params.RawChangeString = []byte(event.CloudEvent.Data.ConfigChanges.Modified)
@@ -356,3 +390,172 @@ func (h *Handler) calculateNewDiskSize(event models.CloudEventReceived) float64
return diskSize return diskSize
} }
// AddVmToInventory adds a vm from a received cloudevent and returns the inventoryid and any error message
func (h *Handler) AddVmToInventory(evt models.CloudEventReceived, ctx context.Context, unixTimestamp int64) (int64, error) {
var (
numVcpus int32
numRam int32
totalDiskGB float64
srmPlaceholder string
foundVm bool
isTemplate string
poweredOn string
folderPath string
rpName string
vmUuid string
)
//c.Logger.Debug("connecting to vcenter")
vc := vcenter.New(h.Logger, h.VcCreds)
vc.Login(evt.CloudEvent.Source)
//datacenter = evt.DatacenterName.String
vmObject, err := vc.FindVMByIDWithDatacenter(evt.CloudEvent.Data.VM.VM.Value, evt.CloudEvent.Data.Datacenter.Datacenter.Value)
if err != nil {
h.Logger.Error("Can't locate vm in vCenter", "vmID", evt.CloudEvent.Data.VM.VM.Value, "error", err)
return 0, err
} else if vmObject == nil {
h.Logger.Debug("didn't find VM", "vm_id", evt.CloudEvent.Data.VM.VM.Value)
return 0, nil
}
//c.Logger.Debug("found VM")
srmPlaceholder = "FALSE" // Default assumption
//prettyPrint(vmObject)
// calculate VM properties we want to store
if vmObject.Config != nil {
numRam = vmObject.Config.Hardware.MemoryMB
numVcpus = vmObject.Config.Hardware.NumCPU
vmUuid = vmObject.Config.Uuid
var totalDiskBytes int64
// Calculate the total disk allocated in GB
for _, device := range vmObject.Config.Hardware.Device {
if disk, ok := device.(*types.VirtualDisk); ok {
// Print the filename of the backing device
if _, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
//c.Logger.Debug("Adding disk", "size_bytes", disk.CapacityInBytes, "backing_file", backing.FileName)
} else {
//c.Logger.Debug("Adding disk, unknown backing type", "size_bytes", disk.CapacityInBytes)
}
totalDiskBytes += disk.CapacityInBytes
//totalDiskGB += float64(disk.CapacityInBytes / 1024 / 1024 / 1024) // Convert from bytes to GB
}
}
totalDiskGB = float64(totalDiskBytes / 1024 / 1024 / 1024)
h.Logger.Debug("Converted total disk size", "bytes", totalDiskBytes, "GB", totalDiskGB)
// Determine if the VM is a normal VM or an SRM placeholder
if vmObject.Config.ManagedBy != nil && vmObject.Config.ManagedBy.ExtensionKey == "com.vmware.vcDr" {
if vmObject.Config.ManagedBy.Type == "placeholderVm" {
h.Logger.Debug("VM is a placeholder")
srmPlaceholder = "TRUE"
} else {
h.Logger.Debug("VM is managed by SRM but not a placeholder", "details", vmObject.Config.ManagedBy)
}
}
if vmObject.Config.Template {
isTemplate = "TRUE"
} else {
isTemplate = "FALSE"
}
// Retrieve the full folder path of the VM
folderPath, err = vc.GetVMFolderPath(*vmObject)
if err != nil {
h.Logger.Error("failed to get vm folder path", "error", err)
folderPath = ""
} else {
h.Logger.Debug("Found vm folder path", "folder_path", folderPath)
}
// Retrieve the resource pool of the VM
rpName, _ = vc.GetVmResourcePool(*vmObject)
foundVm = true
} else {
h.Logger.Error("Empty VM config")
}
//c.Logger.Debug("VM has runtime data", "power_state", vmObject.Runtime.PowerState)
if vmObject.Runtime.PowerState == "poweredOff" {
poweredOn = "FALSE"
} else {
poweredOn = "TRUE"
}
err = vc.Logout()
if err != nil {
h.Logger.Error("unable to logout of vcenter", "error", err)
}
if foundVm {
e := evt.CloudEvent
h.Logger.Debug("Adding to Inventory table", "vm_name", e.Data.VM.Name, "vcpus", numVcpus, "ram", numRam, "dc", e.Data.Datacenter.Datacenter.Value)
insertParams := queries.CreateInventoryParams{
Name: e.Data.VM.Name,
Vcenter: evt.CloudEvent.Source,
CloudId: sql.NullString{String: e.ID, Valid: e.ID != ""},
EventKey: sql.NullString{String: strconv.Itoa(e.Data.Key), Valid: e.Data.Key > 0},
VmId: sql.NullString{String: e.Data.VM.VM.Value, Valid: e.Data.VM.VM.Value != ""},
Datacenter: sql.NullString{String: e.Data.Datacenter.Name, Valid: e.Data.Datacenter.Name != ""},
Cluster: sql.NullString{String: e.Data.ComputeResource.Name, Valid: e.Data.ComputeResource.Name != ""},
CreationTime: sql.NullInt64{Int64: unixTimestamp, Valid: unixTimestamp > 0},
InitialVcpus: sql.NullInt64{Int64: int64(numVcpus), Valid: numVcpus > 0},
InitialRam: sql.NullInt64{Int64: int64(numRam), Valid: numRam > 0},
ProvisionedDisk: sql.NullFloat64{Float64: totalDiskGB, Valid: totalDiskGB > 0},
Folder: sql.NullString{String: folderPath, Valid: folderPath != ""},
ResourcePool: sql.NullString{String: rpName, Valid: rpName != ""},
VmUuid: sql.NullString{String: vmUuid, Valid: vmUuid != ""},
SrmPlaceholder: srmPlaceholder,
IsTemplate: isTemplate,
PoweredOn: poweredOn,
}
/*
params := queries.CreateInventoryParams{
Name: vmObject.Name,
Vcenter: evt.Source,
CloudId: sql.NullString{String: evt.CloudId, Valid: evt.CloudId != ""},
EventKey: sql.NullString{String: evt.EventKey.String, Valid: evt.EventKey.Valid},
VmId: sql.NullString{String: evt.VmId.String, Valid: evt.VmId.Valid},
Datacenter: sql.NullString{String: evt.DatacenterName.String, Valid: evt.DatacenterName.Valid},
Cluster: sql.NullString{String: evt.ComputeResourceName.String, Valid: evt.ComputeResourceName.Valid},
CreationTime: sql.NullInt64{Int64: evt.EventTime.Int64, Valid: evt.EventTime.Valid},
InitialVcpus: sql.NullInt64{Int64: int64(numVcpus), Valid: numVcpus > 0},
InitialRam: sql.NullInt64{Int64: int64(numRam), Valid: numRam > 0},
ProvisionedDisk: sql.NullFloat64{Float64: totalDiskGB, Valid: totalDiskGB > 0},
Folder: sql.NullString{String: folderPath, Valid: folderPath != ""},
ResourcePool: sql.NullString{String: rpName, Valid: rpName != ""},
VmUuid: sql.NullString{String: vmUuid, Valid: vmUuid != ""},
SrmPlaceholder: srmPlaceholder,
IsTemplate: isTemplate,
PoweredOn: poweredOn,
}
*/
//c.Logger.Debug("database params", "params", params)
// Insert the new inventory record into the database
record, err := h.Database.Queries().CreateInventory(ctx, insertParams)
if err != nil {
h.Logger.Error("unable to perform database insert", "error", err)
return 0, err
} else {
//c.Logger.Debug("created database record", "insert_result", result)
return record.Iid, nil
}
} else {
h.Logger.Debug("Not adding to Inventory due to missing vcenter config property", "vm_name", evt.CloudEvent.Data.VM.Name)
}
return 0, nil
}