86fc10b7b9
Added functions (PopulateAttributeStatus) to ensure that NVME and SCSI drives set the status for SMART attributes. Moved Status populating fucntion into the *Attribute files, so they are closer to the code they actually interact with. Fix frontend to correctly display status, thresh and Ideal for NVMe and SCSI ddrives.
112 lines
3.9 KiB
Go
112 lines
3.9 KiB
Go
package db
|
|
|
|
import (
|
|
"github.com/analogj/scrutiny/webapp/backend/pkg/metadata"
|
|
"github.com/jinzhu/gorm"
|
|
"strings"
|
|
)
|
|
|
|
const SmartAttributeStatusPassed = "passed"
|
|
const SmartAttributeStatusFailed = "failed"
|
|
const SmartAttributeStatusWarning = "warn"
|
|
|
|
type SmartAtaAttribute struct {
|
|
gorm.Model
|
|
|
|
SmartId int `json:"smart_id"`
|
|
Smart Device `json:"-" gorm:"foreignkey:SmartId"` // use SmartId as foreign key
|
|
|
|
AttributeId int `json:"attribute_id"`
|
|
Name string `json:"name"`
|
|
Value int `json:"value"`
|
|
Worst int `json:"worst"`
|
|
Threshold int `json:"thresh"`
|
|
RawValue int64 `json:"raw_value"`
|
|
RawString string `json:"raw_string"`
|
|
WhenFailed string `json:"when_failed"`
|
|
|
|
TransformedValue int64 `json:"transformed_value"`
|
|
Status string `gorm:"-" json:"status,omitempty"`
|
|
StatusReason string `gorm:"-" json:"status_reason,omitempty"`
|
|
FailureRate float64 `gorm:"-" json:"failure_rate,omitempty"`
|
|
History []SmartAtaAttribute `gorm:"-" json:"history,omitempty"`
|
|
}
|
|
|
|
//populate attribute status, using SMART Thresholds & Observed Metadata
|
|
func (sa *SmartAtaAttribute) PopulateAttributeStatus() {
|
|
if strings.ToUpper(sa.WhenFailed) == SmartWhenFailedFailingNow {
|
|
//this attribute has previously failed
|
|
sa.Status = SmartAttributeStatusFailed
|
|
sa.StatusReason = "Attribute is failing manufacturer SMART threshold"
|
|
|
|
} else if strings.ToUpper(sa.WhenFailed) == SmartWhenFailedInThePast {
|
|
sa.Status = SmartAttributeStatusWarning
|
|
sa.StatusReason = "Attribute has previously failed manufacturer SMART threshold"
|
|
}
|
|
|
|
if smartMetadata, ok := metadata.AtaMetadata[sa.AttributeId]; ok {
|
|
sa.MetadataObservedThresholdStatus(smartMetadata)
|
|
}
|
|
|
|
//check if status is blank, set to "passed"
|
|
if len(sa.Status) == 0 {
|
|
sa.Status = SmartAttributeStatusPassed
|
|
}
|
|
}
|
|
|
|
// compare the attribute (raw, normalized, transformed) value to observed thresholds, and update status if necessary
|
|
func (sa *SmartAtaAttribute) MetadataObservedThresholdStatus(smartMetadata metadata.AtaAttributeMetadata) {
|
|
//TODO: multiple rules
|
|
// try to predict the failure rates for observed thresholds that have 0 failure rate and error bars.
|
|
// - if the attribute is critical
|
|
// - the failure rate is over 10 - set to failed
|
|
// - the attribute does not match any threshold, set to warn
|
|
// - if the attribute is not critical
|
|
// - if failure rate is above 20 - set to failed
|
|
// - if failure rate is above 10 but below 20 - set to warn
|
|
|
|
//update the smart attribute status based on Observed thresholds.
|
|
var value int64
|
|
if smartMetadata.DisplayType == metadata.AtaSmartAttributeDisplayTypeNormalized {
|
|
value = int64(sa.Value)
|
|
} else if smartMetadata.DisplayType == metadata.AtaSmartAttributeDisplayTypeTransformed {
|
|
value = sa.TransformedValue
|
|
} else {
|
|
value = sa.RawValue
|
|
}
|
|
|
|
for _, obsThresh := range smartMetadata.ObservedThresholds {
|
|
|
|
//check if "value" is in this bucket
|
|
if ((obsThresh.Low == obsThresh.High) && value == obsThresh.Low) ||
|
|
(obsThresh.Low < value && value <= obsThresh.High) {
|
|
sa.FailureRate = obsThresh.AnnualFailureRate
|
|
|
|
if smartMetadata.Critical {
|
|
if obsThresh.AnnualFailureRate >= 0.10 {
|
|
sa.Status = SmartAttributeStatusFailed
|
|
sa.StatusReason = "Observed Failure Rate for Critical Attribute is greater than 10%"
|
|
}
|
|
} else {
|
|
if obsThresh.AnnualFailureRate >= 0.20 {
|
|
sa.Status = SmartAttributeStatusFailed
|
|
sa.StatusReason = "Observed Failure Rate for Attribute is greater than 20%"
|
|
} else if obsThresh.AnnualFailureRate >= 0.10 {
|
|
sa.Status = SmartAttributeStatusWarning
|
|
sa.StatusReason = "Observed Failure Rate for Attribute is greater than 10%"
|
|
}
|
|
}
|
|
|
|
//we've found the correct bucket, we can drop out of this loop
|
|
return
|
|
}
|
|
}
|
|
// no bucket found
|
|
if smartMetadata.Critical {
|
|
sa.Status = SmartAttributeStatusWarning
|
|
sa.StatusReason = "Could not determine Observed Failure Rate for Critical Attribute"
|
|
}
|
|
|
|
return
|
|
}
|