package tasks import ( "context" "database/sql" "log/slog" "time" "vctp/db/queries" "vctp/internal/vcenter" "github.com/vmware/govmomi/vim25/types" ) // use gocron to check events in the Events table func (c *CronTask) RunVmCheck(ctx context.Context, logger *slog.Logger) error { var ( numVcpus int32 numRam int32 totalDiskGB float64 srmPlaceholder string foundVm bool isTemplate string poweredOn string folderPath string ) dateCmp := time.Now().AddDate(0, 0, -1).Unix() logger.Debug("Started Events processing", "time", time.Now(), "since", dateCmp) // Query events table events, err := c.Database.Queries().ListUnprocessedEvents(ctx, sql.NullInt64{Int64: dateCmp, Valid: dateCmp > 0}) if err != nil { logger.Error("Unable to query for unprocessed events", "error", err) return nil // TODO - what to do with this error? } else { logger.Debug("Successfully queried for unprocessed events", "count", len(events)) } for _, evt := range events { logger.Debug("Checking event", "event", evt) // TODO - get a list of unique vcenters, then process each event in batches // to avoid doing unnecessary login/logout of vcenter c.Logger.Debug("connecting to vcenter") vc := vcenter.New(c.Logger, c.VcCreds) vc.Login(evt.Source) //datacenter = evt.DatacenterName.String vmObject, err := vc.FindVMByIDWithDatacenter(evt.VmId.String, evt.DatacenterId.String) if err != nil { c.Logger.Error("Can't locate vm in vCenter", "vmID", evt.VmId.String, "error", err) } else if vmObject == nil { c.Logger.Debug("didn't find VM", "vm_id", evt.VmId.String) numRam = 0 numVcpus = 0 totalDiskGB = 0 isTemplate = "FALSE" folderPath = "" } else { c.Logger.Debug("found VM") srmPlaceholder = "FALSE" // Default assumption //prettyPrint(vmObject) // calculate VM properties we want to store if vmObject.Vm.Config != nil { numRam = vmObject.Vm.Config.Hardware.MemoryMB numVcpus = vmObject.Vm.Config.Hardware.NumCPU var totalDiskBytes int64 // Calculate the total disk allocated in GB for _, device := range vmObject.Vm.Config.Hardware.Device { if disk, ok := device.(*types.VirtualDisk); ok { // Print the filename of the backing device if backing, 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) c.Logger.Debug("Converted total disk size", "bytes", totalDiskBytes, "GB", totalDiskGB) // Determine if the VM is a normal VM or an SRM placeholder if vmObject.Vm.Config.ManagedBy != nil && vmObject.Vm.Config.ManagedBy.Type == "com.vmware.vcDr" { c.Logger.Debug("VM ManagedBy indicates managed by SRM") srmPlaceholder = "TRUE" } if vmObject.Vm.Config.Template { isTemplate = "TRUE" } else { isTemplate = "FALSE" } // Retrieve the full folder path of the VM folderPath, err = vc.GetVMFolderPath(vmObject.Vm) if err != nil { c.Logger.Error("failed to get vm folder path", "error", err) folderPath = "" } else { c.Logger.Debug("Found vm folder path", "folder_path", folderPath) } foundVm = true } else { c.Logger.Error("Empty VM config") } c.Logger.Debug("VM has runtime data", "power_state", vmObject.Vm.Runtime.PowerState) if vmObject.Vm.Runtime.PowerState == "poweredOff" { poweredOn = "FALSE" } else { poweredOn = "TRUE" } } err = vc.Logout() if err != nil { c.Logger.Error("unable to logout of vcenter", "error", err) } if foundVm { c.Logger.Debug("Adding to Inventory table", "vm_name", evt.VmName.String, "vcpus", numVcpus, "ram", numRam, "dc", evt.DatacenterId.String) params := queries.CreateInventoryParams{ Name: vmObject.Vm.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: vmObject.ResourcePool, Valid: vmObject.ResourcePool != ""}, SrmPlaceholder: srmPlaceholder, IsTemplate: isTemplate, PoweredOn: poweredOn, } c.Logger.Debug("database params", "params", params) // Insert the new inventory record into the database result, err := c.Database.Queries().CreateInventory(ctx, params) if err != nil { c.Logger.Error("unable to perform database insert", "error", err) } else { c.Logger.Debug("created database record", "insert_result", result) // mark this event as processed err = c.Database.Queries().UpdateEventsProcessed(ctx, evt.Eid) if err != nil { c.Logger.Error("Unable to mark this event as processed", "event_id", evt.Eid, "error", err) } else { c.Logger.Debug("Marked event as processed", "event_id", evt.Eid) } } } else { c.Logger.Debug("Not adding to Inventory due to missing vcenter config property", "vm_name", evt.VmName.String) } } //fmt.Printf("processing at %s", time.Now()) return nil }