diff --git a/server/handler/vmModifyEvent.go b/server/handler/vmModifyEvent.go index a28de4f..5c8a333 100644 --- a/server/handler/vmModifyEvent.go +++ b/server/handler/vmModifyEvent.go @@ -25,6 +25,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) { var unixTimestamp int64 re := regexp.MustCompile(`/([^/]+)/[^/]+\.vmdk$`) + ctx := context.Background() reqBody, err := io.ReadAll(r.Body) if err != nil { @@ -142,7 +143,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) { VmId: sql.NullString{String: event.CloudEvent.Data.VM.VM.Value, Valid: event.CloudEvent.Data.VM.VM.Value != ""}, DatacenterName: sql.NullString{String: event.CloudEvent.Data.Datacenter.Name, Valid: event.CloudEvent.Data.Datacenter.Name != ""}, } - invResult, err := h.Database.Queries().GetInventoryVmId(context.Background(), invParams) + invResult, err := h.Database.Queries().GetInventoryVmId(ctx, invParams) if err != nil { h.Logger.Error("unable to find existing inventory record for this VM", "error", err) @@ -168,7 +169,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) { params.UserName = sql.NullString{String: event.CloudEvent.Data.UserName, Valid: event.CloudEvent.Data.UserName != ""} // Create the Update database record - result, err := h.Database.Queries().CreateUpdate(context.Background(), params) + result, err := h.Database.Queries().CreateUpdate(ctx, params) if err != nil { h.Logger.Error("unable to perform database insert", "error", err) w.WriteHeader(http.StatusInternalServerError) diff --git a/server/handler/vmMoveEvent.go b/server/handler/vmMoveEvent.go new file mode 100644 index 0000000..852e75c --- /dev/null +++ b/server/handler/vmMoveEvent.go @@ -0,0 +1,106 @@ +package handler + +import ( + "context" + "database/sql" + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" + "time" + "vctp/db/queries" + models "vctp/server/models" +) + +func (h *Handler) VmMoveEvent(w http.ResponseWriter, r *http.Request) { + params := queries.CreateUpdateParams{} + var unixTimestamp int64 + + ctx := context.Background() + + reqBody, err := io.ReadAll(r.Body) + if err != nil { + h.Logger.Error("Invalid data received", "error", err) + fmt.Fprintf(w, "Invalid data received") + w.WriteHeader(http.StatusInternalServerError) + return + } else { + //h.Logger.Debug("received input data", "length", len(reqBody)) + } + + // Decode the JSON body into CloudEventReceived struct + var event models.CloudEventReceived + if err := json.Unmarshal(reqBody, &event); err != nil { + h.Logger.Error("unable to unmarshal json", "error", err) + prettyPrint(reqBody) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(map[string]string{ + "status": "ERROR", + "message": fmt.Sprintf("Unable to unmarshal JSON in request body: '%s'", err), + }) + return + } else { + h.Logger.Debug("successfully decoded JSON") + //prettyPrint(event) + } + + if event.CloudEvent.Data.OldParent == nil || event.CloudEvent.Data.NewParent == nil { + h.Logger.Error("No resource pool data found in cloud event") + prettyPrint(event) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(map[string]string{ + "status": "ERROR", + "message": fmt.Sprintf("CloudEvent missing resource pool data"), + }) + return + } + + h.Logger.Debug("Checking inventory table for VM record") + invParams := queries.GetInventoryVmIdParams{ + VmId: sql.NullString{String: event.CloudEvent.Data.VM.VM.Value, Valid: event.CloudEvent.Data.VM.VM.Value != ""}, + DatacenterName: sql.NullString{String: event.CloudEvent.Data.Datacenter.Name, Valid: event.CloudEvent.Data.Datacenter.Name != ""}, + } + invResult, err := h.Database.Queries().GetInventoryVmId(ctx, invParams) + + if err != nil { + h.Logger.Error("unable to find existing inventory record for this VM", "error", err) + // TODO - how to handle? + } else { + params.InventoryId = sql.NullInt64{Int64: invResult.Iid, Valid: invResult.Iid > 0} + } + + // 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 + params.NewResourcePool = sql.NullString{String: event.CloudEvent.Data.NewParent.Name, Valid: event.CloudEvent.Data.NewParent.Name != ""} + params.UpdateType = "move" + 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.UserName = sql.NullString{String: event.CloudEvent.Data.UserName, Valid: event.CloudEvent.Data.UserName != ""} + + // Create the Update database record + result, err := h.Database.Queries().CreateUpdate(ctx, params) + if err != nil { + h.Logger.Error("unable to perform database insert", "error", err) + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "Error : %v\n", err) + return + } else { + h.Logger.Debug("created database record", "insert_result", result) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "Processed update event: %v\n", result) + return + } +} diff --git a/server/models/models.go b/server/models/models.go index 69cae0d..db54eb2 100644 --- a/server/models/models.go +++ b/server/models/models.go @@ -39,18 +39,14 @@ type CloudEventReceived struct { } `json:"Host"` Name string `json:"Name"` } `json:"Host"` - Key int `json:"Key"` - Net interface{} `json:"Net"` - SrcTemplate struct { - Name string `json:"Name"` - VM struct { - Type string `json:"Type"` - Value string `json:"Value"` - } `json:"Vm"` - } `json:"SrcTemplate"` - Template bool `json:"Template"` - UserName string `json:"UserName"` - VM struct { + Key int `json:"Key"` + Net interface{} `json:"Net"` + NewParent *CloudEventResourcePool `json:"NewParent"` + OldParent *CloudEventResourcePool `json:"OldParent"` + SrcTemplate *CloudEventVm `json:"SrcTemplate"` + Template bool `json:"Template"` + UserName string `json:"UserName"` + VM struct { Name string `json:"Name"` VM struct { Type string `json:"Type"` @@ -63,6 +59,22 @@ type CloudEventReceived struct { } `json:"cloudEvent"` } +type CloudEventResourcePool struct { + Name string `json:"Name"` + ResourcePool struct { + Type string `json:"Type"` + Value string `json:"Value"` + } `json:"ResourcePool"` +} + +type CloudEventVm struct { + Name string `json:"Name"` + VM struct { + Type string `json:"Type"` + Value string `json:"Value"` + } `json:"Vm"` +} + type ImportReceived struct { Name string `json:"Name"` Vcenter string `json:"Vcenter"` diff --git a/server/router/router.go b/server/router/router.go index 6b8dfaa..dd97362 100644 --- a/server/router/router.go +++ b/server/router/router.go @@ -24,6 +24,7 @@ func New(logger *slog.Logger, database db.Database, buildTime string, sha1ver st mux.HandleFunc("/", h.Home) mux.HandleFunc("/api/event/vm/create", h.VmCreateEvent) mux.HandleFunc("/api/event/vm/modify", h.VmModifyEvent) + mux.HandleFunc("/api/event/vm/move", h.VmMoveEvent) mux.HandleFunc("/api/event/vm/delete", h.VmDeleteEvent) mux.HandleFunc("/api/import/vm", h.VmImport) // Use this when we need to manually remove a VM from the database to clean up