Pulse/internal/api/reporting_inventory_handlers.go

91 lines
2.8 KiB
Go

package api
import (
"fmt"
"net/http"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
"github.com/rcourtman/pulse-go-rewrite/pkg/reporting"
)
// HandleExportVMInventory exports the current VM inventory as spreadsheet-friendly CSV.
func (h *ReportingHandlers) HandleExportVMInventory(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
definition := reporting.DescribeVMInventoryExport()
format := reporting.ReportFormat(r.URL.Query().Get("format"))
if format == "" {
format = definition.Format
}
if !definition.SupportsFormat(format) {
writeErrorResponse(w, http.StatusBadRequest, "invalid_format", definition.InvalidFormatError(), nil)
return
}
snapshot := emptyReportingEnrichmentSnapshot()
if h != nil {
if liveSnapshot, ok := h.getReportingEnrichmentSnapshot(r.Context(), GetOrgID(r.Context())); ok {
snapshot = liveSnapshot
}
}
generatedAt := time.Now().UTC()
data, err := reporting.GenerateVMInventoryCSV(reporting.VMInventoryData{
GeneratedAt: generatedAt,
Rows: buildVMInventoryRows(snapshot.Resources),
})
if err != nil {
writeErrorResponse(w, http.StatusInternalServerError, "generation_failed", "Failed to generate VM inventory export", nil)
return
}
w.Header().Set("Content-Type", "text/csv; charset=utf-8")
filename := definition.AttachmentFilename(generatedAt)
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
_, _ = w.Write(data)
}
func buildVMInventoryRows(resources []unifiedresources.Resource) []reporting.VMInventoryRow {
rows := make([]reporting.VMInventoryRow, 0, len(resources))
for i := range resources {
resource := &resources[i]
if resource.Type != unifiedresources.ResourceTypeVM {
continue
}
view := unifiedresources.NewVMView(resource)
diskAllocated, diskUsed := resolveVMInventoryDiskUsage(view)
rows = append(rows, reporting.VMInventoryRow{
ResourceID: view.ID(),
Instance: view.Instance(),
Node: view.Node(),
Pool: view.Pool(),
VMID: view.VMID(),
Name: view.Name(),
Status: string(view.Status()),
CPUCores: view.CPUs(),
MemoryAllocatedBytes: view.MemoryTotal(),
DiskAllocatedBytes: diskAllocated,
DiskUsedBytes: diskUsed,
DiskStatusReason: view.DiskStatusReason(),
})
}
return rows
}
func resolveVMInventoryDiskUsage(view unifiedresources.VMView) (allocated int64, used int64) {
allocated = view.DiskTotal()
used = view.DiskUsed()
if allocated > 0 || used > 0 {
return allocated, used
}
for _, disk := range view.Disks() {
allocated += disk.Total
used += disk.Used
}
return allocated, used
}