This commit is contained in:
@@ -35,6 +35,7 @@ func (h *Handler) VmTrace(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var entries []views.VmTraceEntry
|
||||
chart := views.VmTraceChart{}
|
||||
diagnostics := views.VmTraceDiagnostics{}
|
||||
queryLabel := firstNonEmpty(vmID, vmUUID, name)
|
||||
displayQuery := ""
|
||||
if queryLabel != "" {
|
||||
@@ -47,10 +48,11 @@ func (h *Handler) VmTrace(w http.ResponseWriter, r *http.Request) {
|
||||
// Only fetch data when a query is provided; otherwise render empty page with form.
|
||||
if vmID != "" || vmUUID != "" || name != "" {
|
||||
h.Logger.Info("vm trace request", "view", viewType, "vm_id", vmID, "vm_uuid", vmUUID, "name", name)
|
||||
lifecycle, lifeErr := db.FetchVmLifecycle(ctx, h.Database.DB(), vmID, vmUUID, name)
|
||||
lifecycle, lifecycleDiag, lifeErr := db.FetchVmLifecycleWithDiagnostics(ctx, h.Database.DB(), vmID, vmUUID, name)
|
||||
if lifeErr != nil {
|
||||
h.Logger.Warn("failed to fetch VM lifecycle", "error", lifeErr)
|
||||
}
|
||||
diagnostics = buildVmTraceDiagnostics(lifecycleDiag)
|
||||
var (
|
||||
rows []db.VmTraceRow
|
||||
err error
|
||||
@@ -114,7 +116,7 @@ func (h *Handler) VmTrace(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := views.VmTracePage(queryLabel, displayQuery, vmID, vmUUID, name, creationLabel, deletionLabel, creationApprox, entries, chart, meta).Render(ctx, w); err != nil {
|
||||
if err := views.VmTracePage(queryLabel, displayQuery, vmID, vmUUID, name, creationLabel, deletionLabel, creationApprox, entries, chart, meta, diagnostics).Render(ctx, w); err != nil {
|
||||
http.Error(w, "Failed to render template", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
@@ -155,6 +157,65 @@ func buildVmTraceMeta(viewType, vmID, vmUUID, name string) views.VmTraceMeta {
|
||||
return meta
|
||||
}
|
||||
|
||||
func buildVmTraceDiagnostics(diag db.VmLifecycleDiagnostics) views.VmTraceDiagnostics {
|
||||
lines := make([]views.VmTraceDiagnosticLine, 0, 24)
|
||||
lookup := "-"
|
||||
if diag.LookupField != "" {
|
||||
lookup = diag.LookupField + "=" + diag.LookupValue
|
||||
}
|
||||
lines = append(lines, views.VmTraceDiagnosticLine{Label: "Lookup", Value: lookup})
|
||||
lines = append(lines, views.VmTraceDiagnosticLine{Label: "Final First Seen", Value: formatDiagUnix(diag.FinalLifecycle.FirstSeen)})
|
||||
lines = append(lines, views.VmTraceDiagnosticLine{Label: "Final Last Seen", Value: formatDiagUnix(diag.FinalLifecycle.LastSeen)})
|
||||
lines = append(lines, views.VmTraceDiagnosticLine{Label: "Final Creation Time", Value: formatDiagUnix(diag.FinalLifecycle.CreationTime)})
|
||||
lines = append(lines, views.VmTraceDiagnosticLine{Label: "Final Creation Approx", Value: fmt.Sprintf("%t", diag.FinalLifecycle.CreationApprox)})
|
||||
lines = append(lines, views.VmTraceDiagnosticLine{Label: "Final Deletion Time", Value: formatDiagUnix(diag.FinalLifecycle.DeletionTime)})
|
||||
|
||||
lines = append(lines, summarizeLifecycleSource(diag.HourlyCache)...)
|
||||
lines = append(lines, summarizeLifecycleSource(diag.LifecycleCache)...)
|
||||
lines = append(lines, summarizeLifecycleSource(diag.SnapshotFallback)...)
|
||||
|
||||
return views.VmTraceDiagnostics{
|
||||
Visible: len(lines) > 0,
|
||||
Lines: lines,
|
||||
}
|
||||
}
|
||||
|
||||
func summarizeLifecycleSource(src db.VmLifecycleSourceDiagnostics) []views.VmTraceDiagnosticLine {
|
||||
prefix := src.Source
|
||||
if strings.TrimSpace(prefix) == "" {
|
||||
prefix = "source"
|
||||
}
|
||||
out := []views.VmTraceDiagnosticLine{
|
||||
{Label: prefix + " used", Value: fmt.Sprintf("%t", src.Used)},
|
||||
{Label: prefix + " error", Value: defaultString(src.Error, "-")},
|
||||
{Label: prefix + " matched rows", Value: fmt.Sprintf("%d", src.MatchedRows)},
|
||||
{Label: prefix + " first seen", Value: formatDiagUnix(src.FirstSeen)},
|
||||
{Label: prefix + " last seen", Value: formatDiagUnix(src.LastSeen)},
|
||||
{Label: prefix + " creation", Value: formatDiagUnix(src.CreationTime)},
|
||||
{Label: prefix + " creation approx", Value: fmt.Sprintf("%t", src.CreationApprox)},
|
||||
{Label: prefix + " deletion rows", Value: fmt.Sprintf("%d", src.DeletionRows)},
|
||||
{Label: prefix + " deletion min", Value: formatDiagUnix(src.DeletionMin)},
|
||||
{Label: prefix + " deletion max", Value: formatDiagUnix(src.DeletionMax)},
|
||||
{Label: prefix + " selected deletion", Value: formatDiagUnix(src.SelectedDeletionTime)},
|
||||
{Label: prefix + " stale deletion ignored", Value: fmt.Sprintf("%t", src.StaleDeletionIgnored)},
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func formatDiagUnix(ts int64) string {
|
||||
if ts <= 0 {
|
||||
return "-"
|
||||
}
|
||||
return fmt.Sprintf("%s (%d)", time.Unix(ts, 0).Local().Format("2006-01-02 15:04:05"), ts)
|
||||
}
|
||||
|
||||
func defaultString(value string, fallback string) string {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fallback
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func buildVmTraceChart(entries []views.VmTraceEntry) views.VmTraceChart {
|
||||
if len(entries) == 0 {
|
||||
return views.VmTraceChart{}
|
||||
|
||||
Reference in New Issue
Block a user