feat(inventory): deep hardware capture + per-probe substeps + verbose logs
CI / Lint + build + test (push) Successful in 1m35s
Release / release (push) Successful in 9m34s

Extend Inventory stage from a one-liner summary to a per-probe substep
emitter with ~20-30 narrative log lines per run.

- spec: per-DIMM memory (slot/size/speed/manufacturer/part_number),
  richer CPU (vendor/stepping/physical_cores/flags), disk
  model/transport/rotational, NIC driver/pci_addr, GPU vram/pci/driver,
  new System/Baseboard/PSU/OS top-level sections. All fields omitempty
  so existing expected-spec YAML and artifacts stay compatible.
- spec.Diff: new diffDIMMs/diffSystem/diffBaseboard/diffPSU/diffOS
  helpers; extended diffDisks/diffNICs/diffGPUs for new fields. GPU
  diff gains PCIAddr-pinned matching alongside count-by-model.
- agent/probes/inventory: CPU (/proc/cpuinfo extended), Memory
  (dmidecode -t 17 multi-block), Disks (+model/transport/rotational),
  NICs (+driver/pci from sysfs), GPUs (VRAM from lspci -vv),
  new System/Baseboard (dmidecode -t system/baseboard), PSU
  (dmidecode -t 39), OS (/proc/sys/kernel/osrelease + /etc/os-release).
  All probes accept a Logger and emit per-finding info/warn lines.
- agent/probes/firmware: parseDmidecodeAllSections for multi-block
  fixtures (memory / PSU).
- agent/runner: Inventory case becomes 9 substep rows (CPU / Memory /
  Disks / NICs / GPUs / System / Baseboard / PSU / OS) with per-probe
  start/complete timestamps.
- report: new Inventory HTML section between Stages and Firmware;
  resolveReporting loads the inventory.json artifact.
- agent/tests/fakes/dmidecode: dispatches on -t flag to serve bios /
  memory / system / baseboard / 39 fixtures for unit tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-19 22:21:17 -04:00
parent 481b67fb69
commit 8acef92a60
10 changed files with 1715 additions and 148 deletions
+142
View File
@@ -18,6 +18,7 @@ import (
"time"
"vetting/internal/model"
"vetting/internal/spec"
)
// Data is the payload fed to the HTML template. Callers assemble it
@@ -30,6 +31,7 @@ type Data struct {
SpecDiffs []model.SpecDiff
Aggregates []Aggregate // flattened measurement summary; see Aggregate
Firmware []FirmwareSnapshot // captured firmware versions, empty if none
Inventory *spec.Inventory // captured inventory, nil if artifact missing
}
// FirmwareSnapshot is the report-facing view of one firmware row.
@@ -206,6 +208,146 @@ const htmlTemplate = `<!doctype html>
</table>
</section>
{{if .Inventory}}
<section>
<h2>Inventory</h2>
{{with .Inventory.System}}{{if or .Manufacturer .ProductName .SerialNumber .UUID}}
<h3>System</h3>
<table>
{{if .Manufacturer}}<tr><th>Manufacturer</th><td>{{.Manufacturer}}</td></tr>{{end}}
{{if .ProductName}}<tr><th>Product</th><td>{{.ProductName}}</td></tr>{{end}}
{{if .SerialNumber}}<tr><th>Serial</th><td><code>{{.SerialNumber}}</code></td></tr>{{end}}
{{if .UUID}}<tr><th>UUID</th><td><code>{{.UUID}}</code></td></tr>{{end}}
</table>
{{end}}{{end}}
{{with .Inventory.Baseboard}}{{if or .Manufacturer .ProductName .SerialNumber}}
<h3>Baseboard</h3>
<table>
{{if .Manufacturer}}<tr><th>Manufacturer</th><td>{{.Manufacturer}}</td></tr>{{end}}
{{if .ProductName}}<tr><th>Product</th><td>{{.ProductName}}</td></tr>{{end}}
{{if .SerialNumber}}<tr><th>Serial</th><td><code>{{.SerialNumber}}</code></td></tr>{{end}}
</table>
{{end}}{{end}}
{{with .Inventory.CPU}}{{if .Model}}
<h3>CPU</h3>
<table>
<tr><th>Model</th><td>{{.Model}}</td></tr>
{{if .Vendor}}<tr><th>Vendor</th><td>{{.Vendor}}</td></tr>{{end}}
{{if .PhysicalCores}}<tr><th>Physical cores</th><td>{{.PhysicalCores}}</td></tr>{{end}}
{{if .LogicalCores}}<tr><th>Logical cores</th><td>{{.LogicalCores}}</td></tr>{{end}}
{{if .Stepping}}<tr><th>Stepping</th><td>{{.Stepping}}</td></tr>{{end}}
{{if .Flags}}<tr><th>Flags</th><td>{{range $i, $f := .Flags}}{{if $i}}, {{end}}<code>{{$f}}</code>{{end}}</td></tr>{{end}}
</table>
{{end}}{{end}}
{{with .Inventory.Memory}}{{if or .TotalGiB .Modules}}
<h3>Memory</h3>
<table>
<tr><th>Total</th><td>{{.TotalGiB}} GiB</td></tr>
</table>
{{if .Modules}}
<table>
<thead><tr><th>Slot</th><th>Size</th><th>Speed</th><th>Manufacturer</th><th>Part number</th><th>Populated</th></tr></thead>
<tbody>
{{range .Modules}}
<tr>
<td><code>{{.Slot}}</code></td>
<td>{{if .SizeGB}}{{.SizeGB}} GiB{{else}}{{end}}</td>
<td>{{if .SpeedMTS}}{{.SpeedMTS}} MT/s{{else}}{{end}}</td>
<td>{{.Manufacturer}}</td>
<td><code>{{.PartNumber}}</code></td>
<td>{{if .Populated}}<span class="pass">yes</span>{{else}}<span class="skip">no</span>{{end}}</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
{{end}}{{end}}
{{if .Inventory.Disks}}
<h3>Disks</h3>
<table>
<thead><tr><th>Serial</th><th>Model</th><th>Size</th><th>Transport</th><th>Rotational</th></tr></thead>
<tbody>
{{range .Inventory.Disks}}
<tr>
<td><code>{{.Serial}}</code></td>
<td>{{.Model}}</td>
<td>{{.SizeGB}} GB</td>
<td>{{.Transport}}</td>
<td>{{if .Rotational}}yes{{else}}no{{end}}</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
{{if .Inventory.NICs}}
<h3>NICs</h3>
<table>
<thead><tr><th>MAC</th><th>Speed</th><th>Driver</th><th>PCI</th></tr></thead>
<tbody>
{{range .Inventory.NICs}}
<tr>
<td><code>{{.MAC}}</code></td>
<td>{{if .SpeedGbps}}{{.SpeedGbps}} Gbps{{else}}{{end}}</td>
<td>{{.Driver}}</td>
<td><code>{{.PCIAddr}}</code></td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
{{if .Inventory.GPUs}}
<h3>GPUs</h3>
<table>
<thead><tr><th>Model</th><th>VRAM</th><th>PCI</th><th>Driver</th></tr></thead>
<tbody>
{{range .Inventory.GPUs}}
<tr>
<td>{{.Model}}</td>
<td>{{if .VRAMGiB}}{{.VRAMGiB}} GiB{{else}}{{end}}</td>
<td><code>{{.PCIAddr}}</code></td>
<td>{{.Driver}}</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
{{if .Inventory.PSU}}
<h3>Power supplies</h3>
<table>
<thead><tr><th>Slot</th><th>Manufacturer</th><th>Model</th><th>Max watts</th><th>Status</th></tr></thead>
<tbody>
{{range .Inventory.PSU}}
<tr>
<td>{{.Slot}}</td>
<td>{{.Manufacturer}}</td>
<td>{{.Model}}</td>
<td>{{if .MaxWatts}}{{.MaxWatts}} W{{else}}{{end}}</td>
<td>{{.Status}}</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
{{with .Inventory.OS}}{{if or .Kernel .Distribution .Version}}
<h3>OS</h3>
<table>
{{if .Kernel}}<tr><th>Kernel</th><td><code>{{.Kernel}}</code></td></tr>{{end}}
{{if .Distribution}}<tr><th>Distribution</th><td>{{.Distribution}}</td></tr>{{end}}
{{if .Version}}<tr><th>Version</th><td>{{.Version}}</td></tr>{{end}}
</table>
{{end}}{{end}}
</section>
{{end}}
<section>
<h2>Firmware ({{len .Firmware}})</h2>
{{if .Firmware}}