Skip to content

feat(scsi): record SAS background scan, lifetime temperatures and wear counters - #1045

Merged
Starosdev merged 1 commit into
developfrom
feat/1024-scsi-lifetime-counters
Oct 28, 2026
Merged

Starosdev merged 1 commit into
developfrom
feat/1024-scsi-lifetime-counters

Conversation

@Starosdev

@Starosdev Starosdev commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

Three more SCSI log pages reach the attribute table and its history: Background Medium Scan status (0x15), the Environmental Reports lifetime temperature extremes, and the Start-Stop Cycle Counter (0x0e). Two of the three were already decoded and had no consumer at all.

This is the third of three stacked PRs for #1024.

Base is feat/1024-sas-phy-counters, not develop. Review only the top commit.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • CI/CD or infrastructure change

Related Issues

Related to #1024, delivering groups 2 and 3. Still no closing keyword, because group 5 is not implemented; see below.

Changes Made

  • Background Medium Scan: scans performed, medium scans performed, the numeric status code and the scan progress percentage. The drive runs this scan on its own schedule, independently of Scrutiny's self-tests, so the scan counts are how a user sees whether it is still checking its own media; a count that stops rising means scanning is disabled or never completing. None of the four is treated as a verdict, because the errors a scan finds are counted by the verify error counters.
  • Environmental Reports: lifetime and since-power-on temperature maxima and minima, one attribute set per reported sensor. The drive tracks these itself, so they cover the whole life of the drive rather than the window Scrutiny has been watching, and they survive any gap in collection. Humidity sensors of the same log page are left alone.
  • Start-Stop Cycle Counter: start/stop and load/unload cycles. Each accumulated counter comes with the count the manufacturer rates the drive for, so the rated count becomes the attribute's threshold and the UI reads as "44 of 50000". A drive that reports a counter without its rated limit gets no threshold rather than a zero one it would always exceed.
  • Every field of that page is decoded as a pointer. smartctl emits each parameter only when the drive reports it, so a drive can report start/stop cycles and omit load/unload entirely; a value type would turn that absence into a permanently-zero attribute and 7 InfluxDB fields of noise.
  • Documents the verify, background scan and start-stop fields in the OpenAPI collector schema, which had not been updated for any of the three PRs.
  • Drops the temperature parameter of ProcessScsiSmartInfo. It was never read, and the migration path it serves reconstructs attributes from pre-InfluxDB records that carry the grown defect count and the read/write error counters only.

Design decisions worth a reviewer's attention

  • Passing the rated cycle count is not reported as a failure. Drives routinely outlive their rated cycle count, and the ATA equivalents of these counters (attributes 4, 193 and 225) are not failed either. The threshold still renders, so the wear figure is visible; a user who wants an alarm can set a fail_above override. An earlier revision made these critical and failable, which turned an old-but-working drive red and sent a notification under the default notify filter.
  • Every temperature row names its sensor, including the first. Suppressing "(Sensor 1)" made the first row of a multi-sensor drive read as a drive-wide figure.
  • The background scan status string rides in the status reason. An attribute value is an int64 and cannot hold it, but StatusReason is already persisted, is carried through fn: last downsampling, and reaches the API response. Being precise about the UI, since an earlier draft of this body overstated it: the web UI surfaces that field as the tooltip on the attribute's status, not as a column of its own, and it is not graphable. A user override on this attribute also replaces the reason with the override's own. So group 2's four fields are all delivered; the string is the least prominent of them. The alternative was not recording the drive's own wording at all.
  • Nothing here is delta-evaluated. These are state and wear figures, not error counters, so a static value carries no false alarm to suppress.

Not implemented

Issue group 5, the General Statistics and Performance log page, is deliberately absent. Several of its counters read zero on the reporting user's own drives, and its logical-block counts duplicate the workload signal the read/write gigabytes-processed attributes already provide. The reporter says the same in the issue. Worth its own change if wanted.

Testing

  • I have tested this locally
  • I have added/updated tests that prove my fix/feature works
  • All existing tests pass

The Go test suite was not run locally, by operator policy on this machine; CI is its first run. Checked locally:

  • go build ./webapp/... and go vet ./webapp/backend/pkg/...: clean.
  • golangci-lint v2.14.0, the version CI pins: --new-from-rev against this branch's own base from the real worktree path, plus a full-module run cross-checked against the diff's line ranges. Zero findings on changed lines.
  • New tests: all three log pages end to end against a realistic fixture, per-sensor metadata naming for sensors 1 and 2, and a drive used past its rated start/stop count, which pins that it stays passed while recording the rating as the threshold.
  • New fixture smart-scsi-sas-full.json: the PHY fixture plus the three new pages, values taken from the smartctl-ultrastar-sas-samples.json attachment on the issue.
  • Also new: a drive that supports the start-stop page but reports only some of its counters, which must produce no attribute for the absent one; and the other half of that decision, a counter the drive reports as zero, which must still produce an attribute.
  • TESTING.md's fixture inventory gains rows for all three new fixtures across the stack.

Cost of the full stack

A fully-featured SAS HDD now reports 42 attributes where it reported 16, which is 298 InfluxDB fields per measurement point rather than 116. Downsampling aggregates every SMART field with fn: last, so the added state and wear figures keep their last observed value rather than being averaged. Nothing in the repo caps or chokes on that; the growth is linear in attributes and bounded by device count.

Checklist

  • My code follows the project's code style
  • I have performed a self-review of my code
  • I have commented my code where necessary (particularly complex areas)
  • I have updated the documentation if needed
  • My changes generate no new warnings
  • No console.log, debug statements, or commented-out code

Known wart, recorded rather than fixed

The two wear counters put a rated specification into the attribute threshold, a field whose only other meaning on SCSI is a failure boundary. The web UI's default attribute filter admits any attribute whose value is below its threshold (detail.component.ts), so a healthy wear counter appears in the default view. The same clause admits any attribute with a negative value, since "no threshold" is -1, so a drive stored below freezing also shows its lifetime temperature minimum there. The rows are correct and raise nothing, but separating "rated limit" from "fail boundary" is the real fix and belongs in its own change. The test comment now says this rather than claiming the opposite.

Additional Notes

Review: required (merging to develop publishes the testing image through deploy-testing.yml, with no separate deploy step).

Merge last of the three.

@Starosdev
Starosdev force-pushed the feat/1024-sas-phy-counters branch from 71a4427 to dd0dfe9 Compare October 28, 2026 11:16
@Starosdev
Starosdev force-pushed the feat/1024-scsi-lifetime-counters branch from b9ea63a to f82b327 Compare October 28, 2026 11:19
@Starosdev
Starosdev force-pushed the feat/1024-sas-phy-counters branch from dd0dfe9 to 5b0e033 Compare October 28, 2026 13:14
@Starosdev
Starosdev force-pushed the feat/1024-scsi-lifetime-counters branch from f82b327 to 6de95da Compare October 28, 2026 13:15
@Starosdev
Starosdev force-pushed the feat/1024-sas-phy-counters branch from 5b0e033 to 8199ac1 Compare October 28, 2026 15:00
…r counters (#1024)

Three more SCSI log pages reach the attribute table and its history.

Background Medium Scan (log page 0x15) was already decoded but had no consumer
at all. The drive runs this scan on its own schedule, independently of
Scrutiny's self-tests, so the scan counts are how a user sees whether it is
still checking its own media; a count that stops rising means scanning is
disabled or never completing. The status code and the progress percentage are
recorded alongside it, and none of the four is treated as a verdict, because
the errors a scan finds are counted by the verify error counters instead. The
page's human-readable status string cannot be an attribute value, which is an
int64, so it rides in the status reason of the status attribute: that field is
already persisted, is carried through downsampling by fn: last, and reaches the
API response. The web UI surfaces it as the tooltip on the attribute's status
rather than as a column of its own, and a user override on that attribute
replaces the reason with the override's own. Both are the cost of borrowing an
existing field rather than adding one, and the alternative was not recording
the drive's own wording at all.

Environmental Reports lifetime temperature extremes were likewise decoded and
only the current temperature was used. The drive tracks its own lifetime and
since-power-on maxima and minima, so they cover the whole life of the drive
rather than the window Scrutiny has been watching, and they survive any gap in
collection. One attribute set per reported sensor, keyed by the sensor key
smartctl used, and every row names its sensor, because a drive can report more
than one and an unnamed row would read as a drive-wide figure. Humidity sensors
of the same log page are left alone by an allow-list on the sensor key.

Start-Stop Cycle Counter (log page 0x0e) is new. It reports each accumulated
mechanical wear counter together with the count the manufacturer rates the
drive for, so the rated count becomes the attribute's threshold and the UI
reads as "44 of 50000 start/stop cycles". Passing the rating is not reported as
a failure: drives routinely outlive their rated cycle count, and the ATA
equivalents of these counters (attributes 4, 193 and 225) are not failed
either. A user who wants to be told can set a fail_above override.

Every field of that page is decoded as a pointer, because smartctl emits each
parameter only when the drive reports it: a drive can report start/stop cycles
and omit load/unload entirely, and a value type would turn that absence into a
permanently-zero attribute. A counter reported without its rated limit gets no
threshold rather than a zero one it would always exceed.

The lifetime temperature IDs are per sensor, so like the SAS PHY counters they
are resolved through ScsiMetadataFor rather than listed in the static metadata
map.

Nothing here is delta-evaluated: these are state and wear figures, not error
counters, so a static value carries no false alarm to suppress.

A fully-featured SAS HDD now reports 42 attributes where it reported 16, which
is 298 InfluxDB fields per measurement point rather than 116. Downsampling
aggregates every SMART field with fn: last, so the added state and wear figures
keep their last observed value rather than being averaged.

Known wart, recorded rather than fixed here: the two wear counters put a rated
specification into the attribute threshold, a field whose only other meaning on
SCSI is a failure boundary. The web UI's default attribute filter admits any
attribute whose value is below its threshold, so a healthy wear counter shows
there. The same clause admits any attribute with a negative value, because "no
threshold" is -1, so a drive stored below freezing also shows its lifetime
temperature minimum there. The rows are correct and raise nothing; separating
"rated limit" from "fail boundary" is the real fix.

One group of the issue remains unimplemented: the General Statistics and
Performance log page. Several of its counters read zero on the reporting user's
own drives, and its logical-block counts duplicate the workload signal the
read/write gigabytes-processed attributes already provide.

Also drops the temperature parameter of ProcessScsiSmartInfo. It was never
read, and the migration path it serves reconstructs attributes from
pre-InfluxDB records that carry the grown defect count and the read/write error
counters only, so every later SCSI log page is absent there by definition.
Documents the verify, background scan and start-stop fields in the OpenAPI
collector schema, which had not been updated for any of the three slices.
@Starosdev
Starosdev force-pushed the feat/1024-scsi-lifetime-counters branch from 6de95da to 9ab98be Compare October 28, 2026 15:22
@Starosdev
Starosdev changed the base branch from feat/1024-sas-phy-counters to develop October 28, 2026 15:22
@Starosdev
Starosdev merged commit 034e394 into develop Oct 28, 2026
11 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant