Files
pulse/pkg/reporting/reporting.go
T
rcourtman e6c0c4d385 fix(reporting): resolve unified resource IDs to metrics targets and render honest report subjects
Performance reports were structurally disconnected from the v6 ID
space: the UI (and any API caller working from /api/state) addresses
resources by canonical unified ID, while the metrics store is keyed by
each platform's native source ID (the resource's metricsTarget). The
engine queried the store with the unified ID verbatim, so every report
rendered 'Data Points: 0' regardless of how much history existed, and
covers showed raw hash IDs a report reader cannot map to a machine.

- MetricReportRequest gains MetricsResourceID: handlers resolve the
  unified ID through the tenant monitor's resource store (new
  Monitor.MetricsTargetForResource accessor; the registry computes
  targets on demand, they are not persisted on snapshot structs) and
  the engine uses it for store queries only. Recovery points and
  Patrol findings stay keyed by the unified ID.
- Legacy snapshot models and their alerts are keyed by the metrics
  target ID, so enrichment now matches either ID space and resource
  names/status resolve again on covers, headers, and fleet rows.
- Fleet summaries mirror the single-report guard: zero data points
  across the fleet renders a muted NO DATA card instead of a green
  HEALTHY 'All systems operating normally' - false reassurance is the
  worst failure mode for a client-facing stability report.
- Em dashes in PDF-bound literals become hyphens; fpdf core fonts are
  cp1252 and rendered them as mojibake.
2026-06-10 17:10:30 +01:00

191 lines
6.0 KiB
Go

package reporting
import (
"time"
)
// ReportFormat represents the output format of a report
type ReportFormat string
const (
FormatCSV ReportFormat = "csv"
FormatPDF ReportFormat = "pdf"
)
// MetricReportRequest defines the parameters for generating a report
type MetricReportRequest struct {
ResourceType string
ResourceID string
MetricType string // Optional, if empty all metrics for the resource are included
Start time.Time
End time.Time
Format ReportFormat
Title string
Branding ReportBranding
// MetricsResourceID overrides ResourceID for metrics-store queries.
// The v6 API addresses resources by canonical unified ID while the
// metrics store is keyed by each platform's native source ID (the
// resource's metricsTarget); recovery points and Patrol findings stay
// keyed by the unified ID. Handlers set this from the resource's
// metricsTarget so the store query can match without changing the
// identity the rest of the report pipeline sees.
MetricsResourceID string
// Optional enrichment data (populated by handler from monitor state)
Resource *ResourceInfo // Details about the resource being reported on
Alerts []AlertInfo // Active and recently resolved alerts for this resource
Backups []BackupInfo // Backup information for VMs/containers
Storage []StorageInfo // Storage pools (for nodes)
Disks []DiskInfo // Physical disk health (for nodes)
// Optional narrative interpretation. When Narrator is non-nil the
// engine builds a NarrativeInput from the queried report data and asks
// it to produce the executive summary; on error or nil it falls back to
// the heuristic narrator. Findings are passed through to NarrativeInput
// so a narrator can reference Patrol activity in the period.
Narrator Narrator
FindingsProvider FindingsProvider
}
// ResourceInfo contains details about the resource being reported on
type ResourceInfo struct {
Name string
DisplayName string
Status string
Host string // URL for nodes
Node string // Parent node for VMs/containers
Instance string // Proxmox instance name
Uptime int64
KernelVersion string
PVEVersion string
OSName string
OSVersion string
IPAddresses []string
CPUModel string
CPUCores int
CPUSockets int
MemoryTotal int64
DiskTotal int64
LoadAverage []float64
Temperature *float64 // CPU temp if available
Tags []string
ClusterName string
IsCluster bool
}
// AlertInfo contains alert information for the report
type AlertInfo struct {
Type string
Level string // warning, critical
Message string
Value float64
Threshold float64
StartTime time.Time
ResolvedTime *time.Time // nil if still active
Acknowledged bool
}
// BackupInfo contains backup information for VMs/containers
type BackupInfo struct {
Type string // vzdump, pbs
Storage string
Timestamp time.Time
Size int64
Verified bool
Protected bool
VolID string
NextBackup *time.Time
}
// StorageInfo contains storage pool information
type StorageInfo struct {
Name string
Type string // lvm, zfs, dir, nfs, etc.
Status string
Total int64
Used int64
Available int64
UsagePerc float64
Content string // images, rootdir, backup, etc.
ZFSHealth string // For ZFS pools
ZFSErrors int // Checksum/read/write errors
}
// DiskInfo contains physical disk health information
type DiskInfo struct {
Device string
Model string
Serial string
Type string // nvme, ssd, hdd
Size int64
Health string // PASSED, FAILED, UNKNOWN
Temperature int // Celsius
WearLevel int // 0-100, percentage of life REMAINING (100 = healthy, 0 = end of life, -1 = unknown)
}
// MultiReportRequest defines the parameters for generating a multi-resource report.
type MultiReportRequest struct {
Resources []MetricReportRequest // One per resource, each with enrichment
Format ReportFormat
Start time.Time
End time.Time
Title string
MetricType string
Branding ReportBranding
// Optional fleet-level narrative interpretation. When FleetNarrator is
// non-nil the engine builds a FleetNarrativeInput from the queried
// per-resource report data and asks it to produce the cross-resource
// summary; on error or nil it falls back to the heuristic fleet
// narrator. FindingsProvider, when set, is consulted per-resource so
// patrol findings can flow into per-resource narratives.
FleetNarrator FleetNarrator
Narrator Narrator
FindingsProvider FindingsProvider
}
// MultiReportData holds the data for multi-resource report generation.
type MultiReportData struct {
Title string
Start time.Time
End time.Time
GeneratedAt time.Time
Resources []*ReportData // Reuse existing ReportData per resource
TotalPoints int
Brand *ReportBrand
// Fleet-level narrative interpretation, populated by the engine when
// the request supplies a FleetNarrator (or always populated with the
// heuristic fallback). The renderer prefers this over recomputing
// observations inline.
FleetNarrative *FleetNarrative
}
// Engine defines the interface for report generation.
// This allows the enterprise version to provide PDF/CSV generation.
//
// NarrativeFor and FleetNarrativeFor return the structured narrative
// without rendering, for callers that want the synthesis layer in a
// non-PDF form (Pulse Assistant tool calls, programmatic consumers).
type Engine interface {
Generate(req MetricReportRequest) (data []byte, contentType string, err error)
GenerateMulti(req MultiReportRequest) (data []byte, contentType string, err error)
NarrativeFor(req MetricReportRequest) (*Narrative, error)
FleetNarrativeFor(req MultiReportRequest) (*FleetNarrative, error)
}
var (
globalEngine Engine
)
// SetEngine sets the global report engine.
func SetEngine(e Engine) {
globalEngine = e
}
// GetEngine returns the current global report engine.
func GetEngine() Engine {
return globalEngine
}