Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions webapp/backend/pkg/models/collector/smart.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,17 @@ type SmartInfo struct {
ScsiVersion string `json:"scsi_version"`
ScsiGrownDefectList int64 `json:"scsi_grown_defect_list"`
ScsiErrorCounterLog ScsiErrorCounterLog `json:"scsi_error_counter_log"`

ScsiEnvironmentalReports map[string]ScsiTemperatureData `json:"scsi_environmental_reports"`
}

type ScsiTemperatureData struct {
ParameterCode int `json:"parameter_code"`
Current int64 `json:"current"`
LifetimeMaximum int64 `json:"lifetime_maximum"`
LifetimeMinimum int64 `json:"lifetime_minimum"`
MaximumSincePowerOn int64 `json:"maximum_since_power_on"`
MinimumSincePowerOn int64 `json:"minimum_since_power_on"`
}

// Capacity finds the total capacity of the device in bytes, or 0 if unknown.
Expand Down
30 changes: 21 additions & 9 deletions webapp/backend/pkg/models/measurements/smart.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package measurements

import (
"fmt"
"github.com/analogj/scrutiny/webapp/backend/pkg"
"github.com/analogj/scrutiny/webapp/backend/pkg/models/collector"
"github.com/analogj/scrutiny/webapp/backend/pkg/thresholds"
"log"
"strconv"
"strings"
"time"

"github.com/analogj/scrutiny/webapp/backend/pkg"
"github.com/analogj/scrutiny/webapp/backend/pkg/models/collector"
"github.com/analogj/scrutiny/webapp/backend/pkg/thresholds"
)

type Smart struct {
Expand Down Expand Up @@ -100,7 +101,7 @@ func NewSmartFromInfluxDB(attrs map[string]interface{}) (*Smart, error) {
return &sm, nil
}

//Parse Collector SMART data results and create Smart object (and associated SmartAtaAttribute entries)
// Parse Collector SMART data results and create Smart object (and associated SmartAtaAttribute entries)
func (sm *Smart) FromCollectorSmartInfo(wwn string, info collector.SmartInfo) error {
sm.DeviceWWN = wwn
sm.Date = time.Unix(info.LocalTime.TimeT, 0)
Expand All @@ -121,13 +122,13 @@ func (sm *Smart) FromCollectorSmartInfo(wwn string, info collector.SmartInfo) er
} else if sm.DeviceProtocol == pkg.DeviceProtocolNvme {
sm.ProcessNvmeSmartInfo(info.NvmeSmartHealthInformationLog)
} else if sm.DeviceProtocol == pkg.DeviceProtocolScsi {
sm.ProcessScsiSmartInfo(info.ScsiGrownDefectList, info.ScsiErrorCounterLog)
sm.ProcessScsiSmartInfo(info.ScsiGrownDefectList, info.ScsiErrorCounterLog, info.ScsiEnvironmentalReports)
}

return nil
}

//generate SmartAtaAttribute entries from Scrutiny Collector Smart data.
// generate SmartAtaAttribute entries from Scrutiny Collector Smart data.
func (sm *Smart) ProcessAtaSmartInfo(tableItems []collector.AtaSmartAttributesTableItem) {
for _, collectorAttr := range tableItems {
attrModel := SmartAtaAttribute{
Expand Down Expand Up @@ -155,7 +156,7 @@ func (sm *Smart) ProcessAtaSmartInfo(tableItems []collector.AtaSmartAttributesTa
}
}

//generate SmartNvmeAttribute entries from Scrutiny Collector Smart data.
// generate SmartNvmeAttribute entries from Scrutiny Collector Smart data.
func (sm *Smart) ProcessNvmeSmartInfo(nvmeSmartHealthInformationLog collector.NvmeSmartHealthInformationLog) {

sm.Attributes = map[string]SmartAttribute{
Expand Down Expand Up @@ -185,9 +186,11 @@ func (sm *Smart) ProcessNvmeSmartInfo(nvmeSmartHealthInformationLog collector.Nv
}
}

//generate SmartScsiAttribute entries from Scrutiny Collector Smart data.
func (sm *Smart) ProcessScsiSmartInfo(defectGrownList int64, scsiErrorCounterLog collector.ScsiErrorCounterLog) {
// generate SmartScsiAttribute entries from Scrutiny Collector Smart data.
func (sm *Smart) ProcessScsiSmartInfo(defectGrownList int64, scsiErrorCounterLog collector.ScsiErrorCounterLog, temperature map[string]collector.ScsiTemperatureData) {
sm.Attributes = map[string]SmartAttribute{
"temperature": (&SmartNvmeAttribute{AttributeId: "temperature", Value: getScsiTemperature(temperature), Threshold: -1}).PopulateAttributeStatus(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be SmartScsiAttribute? Unsure since the attributes seem similar properties.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like it might be parsing it as unknown attribute name

passed	Unknown Attribute Name
	27 		
Unknown
Type
Value
Worst/Thresh
Failure %
Scrutiny
27
--
--
Normalized
27
--/
--
Raw
--
--```

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah .... so I do not think this will work as intended. This seems to store the value into InfluxDB as metrics -> smart -> attr.temperature.value -> device_wwn -> device_protocol but the web app seems to be missing the bits to tie into everything which is why it is still showing as infinite.

It gets the data from Influx here

https://github.com/AnalogJ/scrutiny/blob/master/webapp/backend/pkg/web/handler/get_device_details.go#L28

and since this is a SCSI drive it will popolate the metadata

https://github.com/AnalogJ/scrutiny/blob/master/webapp/backend/pkg/web/handler/get_device_details.go#L41

and I think the web is temp from the smart attribute

,,0,2025-11-30T20:54:37.872729924Z,2025-11-30T21:54:37.872729924Z,2025-11-30T21:10:20Z,0,attr.temperature.status,smart,SCSI,xxxxxxxxx
,,1,2025-11-30T20:54:37.872729924Z,2025-11-30T21:54:37.872729924Z,2025-11-30T21:10:20Z,28,attr.temperature.value,smart,SCSI,xxxxxxxxx
,,2,2025-11-30T20:54:37.872729924Z,2025-11-30T21:54:37.872729924Z,2025-11-30T21:10:20Z,1,power_on_hours,smart,SCSI,xxxxxxxxx
,,3,2025-11-30T20:54:37.872729924Z,2025-11-30T21:54:37.872729924Z,2025-11-30T21:10:20Z,0,temp,smart,SCSI,xxxxxx

(here is the influx data for the device I removed the WWN deice id)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for testing this, can you point out how should I change it?


"scsi_grown_defect_list": (&SmartScsiAttribute{AttributeId: "scsi_grown_defect_list", Value: defectGrownList, Threshold: 0}).PopulateAttributeStatus(),
"read_errors_corrected_by_eccfast": (&SmartScsiAttribute{AttributeId: "read_errors_corrected_by_eccfast", Value: scsiErrorCounterLog.Read.ErrorsCorrectedByEccfast, Threshold: -1}).PopulateAttributeStatus(),
"read_errors_corrected_by_eccdelayed": (&SmartScsiAttribute{AttributeId: "read_errors_corrected_by_eccdelayed", Value: scsiErrorCounterLog.Read.ErrorsCorrectedByEccdelayed, Threshold: -1}).PopulateAttributeStatus(),
Expand All @@ -210,3 +213,12 @@ func (sm *Smart) ProcessScsiSmartInfo(defectGrownList int64, scsiErrorCounterLog
}
}
}

func getScsiTemperature(s map[string]collector.ScsiTemperatureData) int64 {
temp, ok := s["temperature_1"]
if !ok {
return 0
}

return temp.Current
}