@@ -94,8 +94,8 @@ func writeNVMeReport(b *strings.Builder, outputs map[string][]byte) {
nel := uint64 ( sl . NumErrLogEntries )
// data_units are in 1000 × 512-byte sectors = 512,000 bytes each
dataRead := floa t64( sl . DataUnitsRead ) * 512000 / 1e9
dataW ritten := floa t64( sl . DataUnitsWritten ) * 512000 / 1e9
readBytes := uin t64( sl . DataUnitsRead ) * 512000
w rittenBytes := uin t64( sl . DataUnitsWritten ) * 512000
writeSectionHeader ( b , "Health" )
writeField ( b , "Temperature" , fmt . Sprintf ( "%d °C" , tempC ) )
@@ -107,8 +107,8 @@ func writeNVMeReport(b *strings.Builder, outputs map[string][]byte) {
writeField ( b , "Power On Hours" , fmt . Sprintf ( "%s h" , formatUint ( poh ) ) )
writeField ( b , "Power Cycles" , formatUint ( pc ) )
writeField ( b , "Unsafe Shutdowns" , formatUint ( us ) )
writeField ( b , "Data Written" , fmt . Sprintf ( "%.1f GB" , dataWritten ) )
writeField ( b , "Data Read" , fmt . Sprintf ( "%.1f GB" , dataRead ) )
writeField ( b , "Data Written" , formatBytesHuman ( float64 ( writtenBytes ) ) )
writeField ( b , "Data Read" , formatBytesHuman ( float64 ( readBytes ) ) )
writeSectionHeader ( b , "Errors" )
writeField ( b , "Media Errors" , formatUint ( me ) )
@@ -118,18 +118,22 @@ func writeNVMeReport(b *strings.Builder, outputs map[string][]byte) {
if capacityBytes == 0 {
capacityBytes = uint64 ( ctrl . NVMCapacity )
}
writeResourceSection ( b , resourceInfo {
ri := resourceInfo {
powerOnHours : poh ,
writtenBytes : uint64 ( sl . DataUnitsWritten ) * 512000 ,
read Bytes: uint64 ( sl . DataUnitsRead ) * 512000 ,
powerCycles : pc ,
written Bytes: writtenBytes ,
readBytes : readBytes ,
capacityBytes : capacityBytes ,
} )
}
writeResourceSection ( b , ri )
if selfTest := outputs [ "nvme-device-self-test" ] ; len ( selfTest ) > 0 {
writeSectionHeader ( b , "Self-Test" )
result := parseSelfTestResult ( string ( selfTest ) )
writeField ( b , "Result" , result )
}
writeConclusionSection ( b , ri )
}
// ── SATA / SAS (smartctl) ────────────────────────────────────────────────────
@@ -202,13 +206,15 @@ func writeSATAReport(b *strings.Builder, outputs map[string][]byte) {
}
}
var poh , writtenLBAs , readLBAs uint64
var poh , pc , writtenLBAs , readLBAs uint64
var readValue int
hasReadValue := false
for _ , a := range attrs {
switch a . ID {
case 9 : // Power_On_Hours
poh = parseLeadingUint ( a . Raw )
case 12 : // Power_Cycle_Count
pc = parseLeadingUint ( a . Raw )
case 241 : // Total_LBAs_Written
writtenLBAs = parseLeadingUint ( a . Raw )
case 242 : // Total_LBAs_Read
@@ -218,14 +224,16 @@ func writeSATAReport(b *strings.Builder, outputs map[string][]byte) {
}
}
const sataSectorBytes = 512
writeResourceSection ( b , resourceInfo {
ri := resourceInfo {
powerOnHours : poh ,
powerCycles : pc ,
writtenBytes : writtenLBAs * sataSectorBytes ,
readBytes : readLBAs * sataSectorBytes ,
capacityBytes : capacityBytes ,
readPercent : 100 - readValue ,
hasReadPercent : hasReadValue ,
} )
}
writeResourceSection ( b , ri )
selfTest := outputs [ "smartctl-self-test-status" ]
if len ( selfTest ) == 0 {
@@ -236,6 +244,8 @@ func writeSATAReport(b *strings.Builder, outputs map[string][]byte) {
result := parseSelfTestResult ( string ( selfTest ) )
writeField ( b , "Result" , result )
}
writeConclusionSection ( b , ri )
}
func parseSMARTAttrs ( text string ) [ ] smartAttr {
@@ -331,6 +341,7 @@ const (
type resourceInfo struct {
powerOnHours uint64
powerCycles uint64
writtenBytes uint64
readBytes uint64
capacityBytes uint64
@@ -363,6 +374,70 @@ func writeResourceSection(b *strings.Builder, r resourceInfo) {
}
}
// ── Conclusion (new-vs-used verdict) ────────────────────────────────────────
// Thresholds for treating a drive as "new": less than one full drive-write
// (110% of capacity, headroom for provisioning/overprovisioning rounding),
// less than a bit over two full drive-reads (210% of capacity), under a
// week of power-on time, and under 30 power cycles. Any one violation is
// enough to call the drive used — these are deliberately loose bounds, not
// a wear/endurance judgment (see -- Resource -- for that).
const (
newDiskMaxWrittenFrac = 1.10
newDiskMaxReadFrac = 2.10
newDiskMaxUptimeHours = 7 * 24
newDiskMaxPowerCycles = 30
)
func writeConclusionSection ( b * strings . Builder , r resourceInfo ) {
writeSectionHeader ( b , "Conclusion" )
var reasons , notes [ ] string
isNew := true
if r . capacityBytes > 0 {
writtenFrac := float64 ( r . writtenBytes ) / float64 ( r . capacityBytes )
readFrac := float64 ( r . readBytes ) / float64 ( r . capacityBytes )
if writtenFrac >= newDiskMaxWrittenFrac {
isNew = false
reasons = append ( reasons , fmt . Sprintf (
"data written %s (%s of capacity)" ,
formatBytesHuman ( float64 ( r . writtenBytes ) ) , formatPercent ( writtenFrac * 100 ) ) )
}
if readFrac >= newDiskMaxReadFrac {
isNew = false
reasons = append ( reasons , fmt . Sprintf (
"data read %s (%s of capacity)" ,
formatBytesHuman ( float64 ( r . readBytes ) ) , formatPercent ( readFrac * 100 ) ) )
}
} else {
notes = append ( notes , "capacity unknown — write/read criteria not evaluated" )
}
if r . powerOnHours >= newDiskMaxUptimeHours {
isNew = false
reasons = append ( reasons , fmt . Sprintf ( "uptime %s" , formatHoursHuman ( r . powerOnHours ) ) )
}
if r . powerCycles >= newDiskMaxPowerCycles {
isNew = false
reasons = append ( reasons , fmt . Sprintf ( "power cycles %s" , formatUint ( r . powerCycles ) ) )
}
if isNew {
writeField ( b , "Disk Condition" , "NEW" )
} else {
writeField ( b , "Disk Condition" , "USED" )
b . WriteString ( " Reason:\n" )
for _ , reason := range reasons {
fmt . Fprintf ( b , " - %s\n" , reason )
}
}
for _ , note := range notes {
fmt . Fprintf ( b , " Note: %s\n" , note )
}
}
// progressBar renders a fixed-width pseudographic bar, e.g. "[######------]".
func progressBar ( frac float64 , width int ) string {
if math . IsNaN ( frac ) || frac < 0 {