diff --git a/webapp/backend/pkg/constants.go b/webapp/backend/pkg/constants.go index 83ab99b..abccc06 100644 --- a/webapp/backend/pkg/constants.go +++ b/webapp/backend/pkg/constants.go @@ -4,9 +4,9 @@ const DeviceProtocolAta = "ATA" const DeviceProtocolScsi = "SCSI" const DeviceProtocolNvme = "NVMe" -const SmartAttributeStatusPassed = "passed" -const SmartAttributeStatusFailed = "failed" -const SmartAttributeStatusWarning = "warn" +const SmartAttributeStatusPassed = 0 +const SmartAttributeStatusFailed = 1 +const SmartAttributeStatusWarning = 2 const SmartWhenFailedFailingNow = "FAILING_NOW" const SmartWhenFailedInThePast = "IN_THE_PAST" diff --git a/webapp/backend/pkg/database/scrutiny_repository.go b/webapp/backend/pkg/database/scrutiny_repository.go index bac613b..0ff4a36 100644 --- a/webapp/backend/pkg/database/scrutiny_repository.go +++ b/webapp/backend/pkg/database/scrutiny_repository.go @@ -74,47 +74,20 @@ func NewScrutinyRepository(appConfig config.Interface, globalLogger logrus.Field // we will initialize with a predetermined username & password, that you should change. // metrics bucket will have a retention period of 8 days (since it will be down-sampled once a week) - // in hours (24hours * 8 days) = 192 + // in seconds (60seconds * 60minutes * 24hours * 15 days) = 1_296_000 (see EnsureBucket() function) onboardingResponse, err := client.Setup( backgroundContext, appConfig.GetString("web.influxdb.init_username"), appConfig.GetString("web.influxdb.init_password"), appConfig.GetString("web.influxdb.org"), appConfig.GetString("web.influxdb.bucket"), - 192) + 0) if err != nil { return nil, err } appConfig.Set("web.influxdb.token", *onboardingResponse.Auth.Token) //todo: determine if we should write the config file out here. - - orgId, err := client.OrganizationsAPI().FindOrganizationByID(backgroundContext, appConfig.GetString("web.influxdb.org")) - if err != nil { - return nil, err - } - - //create buckets (used for downsampling) - - // metrics_weekly bucket will have a retention period of 8+1 weeks (since it will be down-sampled once a month) - // in seconds (60seconds * 60minutes * 24hours * 7 days * 9 weeks) = 5_443_200 - _, err = client.BucketsAPI().CreateBucketWithName(backgroundContext, orgId, fmt.Sprintf("%s_weekly", appConfig.GetString("web.influxdb.bucket")), domain.RetentionRule{EverySeconds: 5_443_200}) - if err != nil { - return nil, err - } - - // metrics_monthly bucket will have a retention period of 24+1 months (since it will be down-sampled once a year) - // in seconds (60seconds * 60minutes * 24hours * 7 days * (52 + 52 + 4)weeks) = 65_318_400 - _, err = client.BucketsAPI().CreateBucketWithName(backgroundContext, orgId, fmt.Sprintf("%s_monthly", appConfig.GetString("web.influxdb.bucket")), domain.RetentionRule{EverySeconds: 65_318_400}) - if err != nil { - return nil, err - } - - // metrics_yearly bucket will have an infinite retention period - _, err = client.BucketsAPI().CreateBucketWithName(backgroundContext, orgId, fmt.Sprintf("%s_yearly", appConfig.GetString("web.influxdb.bucket"))) - if err != nil { - return nil, err - } } // Use blocking write client for writes to desired bucket @@ -139,8 +112,20 @@ func NewScrutinyRepository(appConfig config.Interface, globalLogger logrus.Field influxTaskApi: taskAPI, gormClient: database, } + + orgInfo, err := client.OrganizationsAPI().FindOrganizationByName(backgroundContext, appConfig.GetString("web.influxdb.org")) + if err != nil { + return nil, err + } + + // Initialize Buckets (if necessary) + err = deviceRepo.EnsureBuckets(backgroundContext, orgInfo) + if err != nil { + return nil, err + } + // Initialize Background Tasks - err = deviceRepo.InitTasks(backgroundContext) + err = deviceRepo.EnsureTasks(backgroundContext, *orgInfo.Id) if err != nil { return nil, err } @@ -164,32 +149,81 @@ func (sr *scrutinyRepository) Close() error { return nil } +func (sr *scrutinyRepository) EnsureBuckets(ctx context.Context, org *domain.Organization) error { + + mainBucket := sr.appConfig.GetString("web.influxdb.bucket") + if foundMainBucket, foundErr := sr.influxClient.BucketsAPI().FindBucketByName(ctx, mainBucket); foundErr != nil { + // metrics bucket will have a retention period of (14+1) 15 days (since it will be down-sampled once a week) + // in seconds (60seconds * 60minutes * 24hours * 15 days) = 1_296_000 + _, err := sr.influxClient.BucketsAPI().CreateBucketWithName(ctx, org, mainBucket, domain.RetentionRule{EverySeconds: 1_296_000}) + if err != nil { + return err + } + } else { + //correctly set the retention period for the main bucket (cant do it during creation) + foundMainBucket.RetentionRules = domain.RetentionRules{domain.RetentionRule{EverySeconds: 1_296_000}} + sr.influxClient.BucketsAPI().UpdateBucket(ctx, foundMainBucket) + } + + //create buckets (used for downsampling) + weeklyBucket := fmt.Sprintf("%s_weekly", sr.appConfig.GetString("web.influxdb.bucket")) + if _, foundErr := sr.influxClient.BucketsAPI().FindBucketByName(ctx, weeklyBucket); foundErr != nil { + // metrics_weekly bucket will have a retention period of 8+1 weeks (since it will be down-sampled once a month) + // in seconds (60seconds * 60minutes * 24hours * 7 days * 9 weeks) = 5_443_200 + _, err := sr.influxClient.BucketsAPI().CreateBucketWithName(ctx, org, weeklyBucket, domain.RetentionRule{EverySeconds: 5_443_200}) + if err != nil { + return err + } + } + + monthlyBucket := fmt.Sprintf("%s_monthly", sr.appConfig.GetString("web.influxdb.bucket")) + if _, foundErr := sr.influxClient.BucketsAPI().FindBucketByName(ctx, monthlyBucket); foundErr != nil { + // metrics_monthly bucket will have a retention period of 24+1 months (since it will be down-sampled once a year) + // in seconds (60seconds * 60minutes * 24hours * 7 days * (52 + 52 + 4)weeks) = 65_318_400 + _, err := sr.influxClient.BucketsAPI().CreateBucketWithName(ctx, org, monthlyBucket, domain.RetentionRule{EverySeconds: 65_318_400}) + if err != nil { + return err + } + } + + yearlyBucket := fmt.Sprintf("%s_yearly", sr.appConfig.GetString("web.influxdb.bucket")) + if _, foundErr := sr.influxClient.BucketsAPI().FindBucketByName(ctx, yearlyBucket); foundErr != nil { + // metrics_yearly bucket will have an infinite retention period + _, err := sr.influxClient.BucketsAPI().CreateBucketWithName(ctx, org, yearlyBucket) + if err != nil { + return err + } + } + + return nil +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Tasks //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -func (sr *scrutinyRepository) InitTasks(ctx context.Context) error { +func (sr *scrutinyRepository) EnsureTasks(ctx context.Context, orgID string) error { weeklyTaskName := "tsk-weekly-aggr" - if _, missingTask := sr.influxTaskApi.GetTaskByID(ctx, weeklyTaskName); missingTask != nil { + if found, findErr := sr.influxTaskApi.FindTasks(ctx, &api.TaskFilter{Name: weeklyTaskName}); findErr == nil && len(found) == 0 { //weekly on Sunday at 1:00am - _, err := sr.influxTaskApi.CreateTaskWithCron(ctx, weeklyTaskName, sr.DownsampleScript("weekly"), "0 1 * * 0", sr.appConfig.GetString("web.influxdb.org")) + _, err := sr.influxTaskApi.CreateTaskWithCron(ctx, weeklyTaskName, sr.DownsampleScript("weekly"), "0 1 * * 0", orgID) if err != nil { return err } } monthlyTaskName := "tsk-monthly-aggr" - if _, missingTask := sr.influxTaskApi.GetTaskByID(ctx, monthlyTaskName); missingTask != nil { + if found, findErr := sr.influxTaskApi.FindTasks(ctx, &api.TaskFilter{Name: monthlyTaskName}); findErr == nil && len(found) == 0 { //monthly on first day of the month at 1:30am - _, err := sr.influxTaskApi.CreateTaskWithCron(ctx, monthlyTaskName, sr.DownsampleScript("monthly"), "30 1 1 * *", sr.appConfig.GetString("web.influxdb.org")) + _, err := sr.influxTaskApi.CreateTaskWithCron(ctx, monthlyTaskName, sr.DownsampleScript("monthly"), "30 1 1 * *", orgID) if err != nil { return err } } - yearlyTaskName := "tsk-monthly-aggr" - if _, missingTask := sr.influxTaskApi.GetTaskByID(ctx, yearlyTaskName); missingTask != nil { + yearlyTaskName := "tsk-yearly-aggr" + if found, findErr := sr.influxTaskApi.FindTasks(ctx, &api.TaskFilter{Name: yearlyTaskName}); findErr == nil && len(found) == 0 { //yearly on the first day of the year at 2:00am - _, err := sr.influxTaskApi.CreateTaskWithCron(ctx, yearlyTaskName, sr.DownsampleScript("yearly"), "0 2 1 1 *", sr.appConfig.GetString("web.influxdb.org")) + _, err := sr.influxTaskApi.CreateTaskWithCron(ctx, yearlyTaskName, sr.DownsampleScript("yearly"), "0 2 1 1 *", orgID) if err != nil { return err } @@ -235,7 +269,7 @@ func (sr *scrutinyRepository) DownsampleScript(aggregationType string) string { smart_data = from(bucket: sourceBucket) |> range(start: rangeStart, stop: rangeEnd) |> filter(fn: (r) => r["_measurement"] == "smart" ) - |> filter(fn: (r) => r["_field"] !~ /(raw_string|_measurement|device_protocol|device_wwn|attribute_id|name|status|when_failed)/) + |> filter(fn: (r) => r["_field"] !~ /(raw_string|_measurement|device_protocol|device_wwn|attribute_id|when_failed)/) |> last() |> yield(name: "last") diff --git a/webapp/backend/pkg/models/measurements/smart.go b/webapp/backend/pkg/models/measurements/smart.go index 315bd11..d0c3a39 100644 --- a/webapp/backend/pkg/models/measurements/smart.go +++ b/webapp/backend/pkg/models/measurements/smart.go @@ -140,7 +140,6 @@ func (sm *Smart) ProcessAtaSmartInfo(info collector.SmartInfo) { for _, collectorAttr := range info.AtaSmartAttributes.Table { attrModel := SmartAtaAttribute{ AttributeId: collectorAttr.ID, - Name: collectorAttr.Name, Value: collectorAttr.Value, Worst: collectorAttr.Worst, Threshold: collectorAttr.Thresh, @@ -151,7 +150,6 @@ func (sm *Smart) ProcessAtaSmartInfo(info collector.SmartInfo) { //now that we've parsed the data from the smartctl response, lets match it against our metadata rules and add additional Scrutiny specific data. if smartMetadata, ok := thresholds.AtaMetadata[collectorAttr.ID]; ok { - attrModel.Name = smartMetadata.DisplayName if smartMetadata.Transform != nil { attrModel.TransformedValue = smartMetadata.Transform(attrModel.Value, attrModel.RawValue, attrModel.RawString) } @@ -168,22 +166,22 @@ func (sm *Smart) ProcessAtaSmartInfo(info collector.SmartInfo) { func (sm *Smart) ProcessNvmeSmartInfo(info collector.SmartInfo) { sm.Attributes = map[string]SmartAttribute{ - "critical_warning": (&SmartNvmeAttribute{AttributeId: "critical_warning", Name: "Critical Warning", Value: info.NvmeSmartHealthInformationLog.CriticalWarning, Threshold: 0}).PopulateAttributeStatus(), - "temperature": (&SmartNvmeAttribute{AttributeId: "temperature", Name: "Temperature", Value: info.NvmeSmartHealthInformationLog.Temperature, Threshold: -1}).PopulateAttributeStatus(), - "available_spare": (&SmartNvmeAttribute{AttributeId: "available_spare", Name: "Available Spare", Value: info.NvmeSmartHealthInformationLog.AvailableSpare, Threshold: info.NvmeSmartHealthInformationLog.AvailableSpareThreshold}).PopulateAttributeStatus(), - "percentage_used": (&SmartNvmeAttribute{AttributeId: "percentage_used", Name: "Percentage Used", Value: info.NvmeSmartHealthInformationLog.PercentageUsed, Threshold: 100}).PopulateAttributeStatus(), - "data_units_read": (&SmartNvmeAttribute{AttributeId: "data_units_read", Name: "Data Units Read", Value: info.NvmeSmartHealthInformationLog.DataUnitsRead, Threshold: -1}).PopulateAttributeStatus(), - "data_units_written": (&SmartNvmeAttribute{AttributeId: "data_units_written", Name: "Data Units Written", Value: info.NvmeSmartHealthInformationLog.DataUnitsWritten, Threshold: -1}).PopulateAttributeStatus(), - "host_reads": (&SmartNvmeAttribute{AttributeId: "host_reads", Name: "Host Reads", Value: info.NvmeSmartHealthInformationLog.HostReads, Threshold: -1}).PopulateAttributeStatus(), - "host_writes": (&SmartNvmeAttribute{AttributeId: "host_writes", Name: "Host Writes", Value: info.NvmeSmartHealthInformationLog.HostWrites, Threshold: -1}).PopulateAttributeStatus(), - "controller_busy_time": (&SmartNvmeAttribute{AttributeId: "controller_busy_time", Name: "Controller Busy Time", Value: info.NvmeSmartHealthInformationLog.ControllerBusyTime, Threshold: -1}).PopulateAttributeStatus(), - "power_cycles": (&SmartNvmeAttribute{AttributeId: "power_cycles", Name: "Power Cycles", Value: info.NvmeSmartHealthInformationLog.PowerCycles, Threshold: -1}).PopulateAttributeStatus(), - "power_on_hours": (&SmartNvmeAttribute{AttributeId: "power_on_hours", Name: "Power on Hours", Value: info.NvmeSmartHealthInformationLog.PowerOnHours, Threshold: -1}).PopulateAttributeStatus(), - "unsafe_shutdowns": (&SmartNvmeAttribute{AttributeId: "unsafe_shutdowns", Name: "Unsafe Shutdowns", Value: info.NvmeSmartHealthInformationLog.UnsafeShutdowns, Threshold: -1}).PopulateAttributeStatus(), - "media_errors": (&SmartNvmeAttribute{AttributeId: "media_errors", Name: "Media Errors", Value: info.NvmeSmartHealthInformationLog.MediaErrors, Threshold: 0}).PopulateAttributeStatus(), - "num_err_log_entries": (&SmartNvmeAttribute{AttributeId: "num_err_log_entries", Name: "Numb Err Log Entries", Value: info.NvmeSmartHealthInformationLog.NumErrLogEntries, Threshold: 0}).PopulateAttributeStatus(), - "warning_temp_time": (&SmartNvmeAttribute{AttributeId: "warning_temp_time", Name: "Warning Temp Time", Value: info.NvmeSmartHealthInformationLog.WarningTempTime, Threshold: -1}).PopulateAttributeStatus(), - "critical_comp_time": (&SmartNvmeAttribute{AttributeId: "critical_comp_time", Name: "Critical CompTime", Value: info.NvmeSmartHealthInformationLog.CriticalCompTime, Threshold: -1}).PopulateAttributeStatus(), + "critical_warning": (&SmartNvmeAttribute{AttributeId: "critical_warning", Value: info.NvmeSmartHealthInformationLog.CriticalWarning, Threshold: 0}).PopulateAttributeStatus(), + "temperature": (&SmartNvmeAttribute{AttributeId: "temperature", Value: info.NvmeSmartHealthInformationLog.Temperature, Threshold: -1}).PopulateAttributeStatus(), + "available_spare": (&SmartNvmeAttribute{AttributeId: "available_spare", Value: info.NvmeSmartHealthInformationLog.AvailableSpare, Threshold: info.NvmeSmartHealthInformationLog.AvailableSpareThreshold}).PopulateAttributeStatus(), + "percentage_used": (&SmartNvmeAttribute{AttributeId: "percentage_used", Value: info.NvmeSmartHealthInformationLog.PercentageUsed, Threshold: 100}).PopulateAttributeStatus(), + "data_units_read": (&SmartNvmeAttribute{AttributeId: "data_units_read", Value: info.NvmeSmartHealthInformationLog.DataUnitsRead, Threshold: -1}).PopulateAttributeStatus(), + "data_units_written": (&SmartNvmeAttribute{AttributeId: "data_units_written", Value: info.NvmeSmartHealthInformationLog.DataUnitsWritten, Threshold: -1}).PopulateAttributeStatus(), + "host_reads": (&SmartNvmeAttribute{AttributeId: "host_reads", Value: info.NvmeSmartHealthInformationLog.HostReads, Threshold: -1}).PopulateAttributeStatus(), + "host_writes": (&SmartNvmeAttribute{AttributeId: "host_writes", Value: info.NvmeSmartHealthInformationLog.HostWrites, Threshold: -1}).PopulateAttributeStatus(), + "controller_busy_time": (&SmartNvmeAttribute{AttributeId: "controller_busy_time", Value: info.NvmeSmartHealthInformationLog.ControllerBusyTime, Threshold: -1}).PopulateAttributeStatus(), + "power_cycles": (&SmartNvmeAttribute{AttributeId: "power_cycles", Value: info.NvmeSmartHealthInformationLog.PowerCycles, Threshold: -1}).PopulateAttributeStatus(), + "power_on_hours": (&SmartNvmeAttribute{AttributeId: "power_on_hours", Value: info.NvmeSmartHealthInformationLog.PowerOnHours, Threshold: -1}).PopulateAttributeStatus(), + "unsafe_shutdowns": (&SmartNvmeAttribute{AttributeId: "unsafe_shutdowns", Value: info.NvmeSmartHealthInformationLog.UnsafeShutdowns, Threshold: -1}).PopulateAttributeStatus(), + "media_errors": (&SmartNvmeAttribute{AttributeId: "media_errors", Value: info.NvmeSmartHealthInformationLog.MediaErrors, Threshold: 0}).PopulateAttributeStatus(), + "num_err_log_entries": (&SmartNvmeAttribute{AttributeId: "num_err_log_entries", Value: info.NvmeSmartHealthInformationLog.NumErrLogEntries, Threshold: 0}).PopulateAttributeStatus(), + "warning_temp_time": (&SmartNvmeAttribute{AttributeId: "warning_temp_time", Value: info.NvmeSmartHealthInformationLog.WarningTempTime, Threshold: -1}).PopulateAttributeStatus(), + "critical_comp_time": (&SmartNvmeAttribute{AttributeId: "critical_comp_time", Value: info.NvmeSmartHealthInformationLog.CriticalCompTime, Threshold: -1}).PopulateAttributeStatus(), } //find analyzed attribute status @@ -197,19 +195,19 @@ func (sm *Smart) ProcessNvmeSmartInfo(info collector.SmartInfo) { //generate SmartScsiAttribute entries from Scrutiny Collector Smart data. func (sm *Smart) ProcessScsiSmartInfo(info collector.SmartInfo) { sm.Attributes = map[string]SmartAttribute{ - "scsi_grown_defect_list": (&SmartScsiAttribute{AttributeId: "scsi_grown_defect_list", Name: "Grown Defect List", Value: info.ScsiGrownDefectList, Threshold: 0}).PopulateAttributeStatus(), - "read_errors_corrected_by_eccfast": (&SmartScsiAttribute{AttributeId: "read_errors_corrected_by_eccfast", Name: "Read Errors Corrected by ECC Fast", Value: info.ScsiErrorCounterLog.Read.ErrorsCorrectedByEccfast, Threshold: -1}).PopulateAttributeStatus(), - "read_errors_corrected_by_eccdelayed": (&SmartScsiAttribute{AttributeId: "read_errors_corrected_by_eccdelayed", Name: "Read Errors Corrected by ECC Delayed", Value: info.ScsiErrorCounterLog.Read.ErrorsCorrectedByEccdelayed, Threshold: -1}).PopulateAttributeStatus(), - "read_errors_corrected_by_rereads_rewrites": (&SmartScsiAttribute{AttributeId: "read_errors_corrected_by_rereads_rewrites", Name: "Read Errors Corrected by ReReads/ReWrites", Value: info.ScsiErrorCounterLog.Read.ErrorsCorrectedByRereadsRewrites, Threshold: 0}).PopulateAttributeStatus(), - "read_total_errors_corrected": (&SmartScsiAttribute{AttributeId: "read_total_errors_corrected", Name: "Read Total Errors Corrected", Value: info.ScsiErrorCounterLog.Read.TotalErrorsCorrected, Threshold: -1}).PopulateAttributeStatus(), - "read_correction_algorithm_invocations": (&SmartScsiAttribute{AttributeId: "read_correction_algorithm_invocations", Name: "Read Correction Algorithm Invocations", Value: info.ScsiErrorCounterLog.Read.CorrectionAlgorithmInvocations, Threshold: -1}).PopulateAttributeStatus(), - "read_total_uncorrected_errors": (&SmartScsiAttribute{AttributeId: "read_total_uncorrected_errors", Name: "Read Total Uncorrected Errors", Value: info.ScsiErrorCounterLog.Read.TotalUncorrectedErrors, Threshold: 0}).PopulateAttributeStatus(), - "write_errors_corrected_by_eccfast": (&SmartScsiAttribute{AttributeId: "write_errors_corrected_by_eccfast", Name: "Write Errors Corrected by ECC Fast", Value: info.ScsiErrorCounterLog.Write.ErrorsCorrectedByEccfast, Threshold: -1}).PopulateAttributeStatus(), - "write_errors_corrected_by_eccdelayed": (&SmartScsiAttribute{AttributeId: "write_errors_corrected_by_eccdelayed", Name: "Write Errors Corrected by ECC Delayed", Value: info.ScsiErrorCounterLog.Write.ErrorsCorrectedByEccdelayed, Threshold: -1}).PopulateAttributeStatus(), - "write_errors_corrected_by_rereads_rewrites": (&SmartScsiAttribute{AttributeId: "write_errors_corrected_by_rereads_rewrites", Name: "Write Errors Corrected by ReReads/ReWrites", Value: info.ScsiErrorCounterLog.Write.ErrorsCorrectedByRereadsRewrites, Threshold: 0}).PopulateAttributeStatus(), - "write_total_errors_corrected": (&SmartScsiAttribute{AttributeId: "write_total_errors_corrected", Name: "Write Total Errors Corrected", Value: info.ScsiErrorCounterLog.Write.TotalErrorsCorrected, Threshold: -1}).PopulateAttributeStatus(), - "write_correction_algorithm_invocations": (&SmartScsiAttribute{AttributeId: "write_correction_algorithm_invocations", Name: "Write Correction Algorithm Invocations", Value: info.ScsiErrorCounterLog.Write.CorrectionAlgorithmInvocations, Threshold: -1}).PopulateAttributeStatus(), - "write_total_uncorrected_errors": (&SmartScsiAttribute{AttributeId: "write_total_uncorrected_errors", Name: "Write Total Uncorrected Errors", Value: info.ScsiErrorCounterLog.Write.TotalUncorrectedErrors, Threshold: 0}).PopulateAttributeStatus(), + "scsi_grown_defect_list": (&SmartScsiAttribute{AttributeId: "scsi_grown_defect_list", Value: info.ScsiGrownDefectList, Threshold: 0}).PopulateAttributeStatus(), + "read_errors_corrected_by_eccfast": (&SmartScsiAttribute{AttributeId: "read_errors_corrected_by_eccfast", Value: info.ScsiErrorCounterLog.Read.ErrorsCorrectedByEccfast, Threshold: -1}).PopulateAttributeStatus(), + "read_errors_corrected_by_eccdelayed": (&SmartScsiAttribute{AttributeId: "read_errors_corrected_by_eccdelayed", Value: info.ScsiErrorCounterLog.Read.ErrorsCorrectedByEccdelayed, Threshold: -1}).PopulateAttributeStatus(), + "read_errors_corrected_by_rereads_rewrites": (&SmartScsiAttribute{AttributeId: "read_errors_corrected_by_rereads_rewrites", Value: info.ScsiErrorCounterLog.Read.ErrorsCorrectedByRereadsRewrites, Threshold: 0}).PopulateAttributeStatus(), + "read_total_errors_corrected": (&SmartScsiAttribute{AttributeId: "read_total_errors_corrected", Value: info.ScsiErrorCounterLog.Read.TotalErrorsCorrected, Threshold: -1}).PopulateAttributeStatus(), + "read_correction_algorithm_invocations": (&SmartScsiAttribute{AttributeId: "read_correction_algorithm_invocations", Value: info.ScsiErrorCounterLog.Read.CorrectionAlgorithmInvocations, Threshold: -1}).PopulateAttributeStatus(), + "read_total_uncorrected_errors": (&SmartScsiAttribute{AttributeId: "read_total_uncorrected_errors", Value: info.ScsiErrorCounterLog.Read.TotalUncorrectedErrors, Threshold: 0}).PopulateAttributeStatus(), + "write_errors_corrected_by_eccfast": (&SmartScsiAttribute{AttributeId: "write_errors_corrected_by_eccfast", Value: info.ScsiErrorCounterLog.Write.ErrorsCorrectedByEccfast, Threshold: -1}).PopulateAttributeStatus(), + "write_errors_corrected_by_eccdelayed": (&SmartScsiAttribute{AttributeId: "write_errors_corrected_by_eccdelayed", Value: info.ScsiErrorCounterLog.Write.ErrorsCorrectedByEccdelayed, Threshold: -1}).PopulateAttributeStatus(), + "write_errors_corrected_by_rereads_rewrites": (&SmartScsiAttribute{AttributeId: "write_errors_corrected_by_rereads_rewrites", Value: info.ScsiErrorCounterLog.Write.ErrorsCorrectedByRereadsRewrites, Threshold: 0}).PopulateAttributeStatus(), + "write_total_errors_corrected": (&SmartScsiAttribute{AttributeId: "write_total_errors_corrected", Value: info.ScsiErrorCounterLog.Write.TotalErrorsCorrected, Threshold: -1}).PopulateAttributeStatus(), + "write_correction_algorithm_invocations": (&SmartScsiAttribute{AttributeId: "write_correction_algorithm_invocations", Value: info.ScsiErrorCounterLog.Write.CorrectionAlgorithmInvocations, Threshold: -1}).PopulateAttributeStatus(), + "write_total_uncorrected_errors": (&SmartScsiAttribute{AttributeId: "write_total_uncorrected_errors", Value: info.ScsiErrorCounterLog.Write.TotalUncorrectedErrors, Threshold: 0}).PopulateAttributeStatus(), } //find analyzed attribute status diff --git a/webapp/backend/pkg/models/measurements/smart_ata_attribute.go b/webapp/backend/pkg/models/measurements/smart_ata_attribute.go index 230c124..23f95fb 100644 --- a/webapp/backend/pkg/models/measurements/smart_ata_attribute.go +++ b/webapp/backend/pkg/models/measurements/smart_ata_attribute.go @@ -10,7 +10,6 @@ import ( type SmartAtaAttribute struct { AttributeId int `json:"attribute_id"` - Name string `json:"name"` Value int64 `json:"value"` Threshold int64 `json:"thresh"` Worst int64 `json:"worst"` @@ -20,12 +19,12 @@ type SmartAtaAttribute struct { //Generated data TransformedValue int64 `json:"transformed_value"` - Status string `json:"status,omitempty"` + Status int64 `json:"status,omitempty"` StatusReason string `json:"status_reason,omitempty"` FailureRate float64 `json:"failure_rate,omitempty"` } -func (sa *SmartAtaAttribute) GetStatus() string { +func (sa *SmartAtaAttribute) GetStatus() int64 { return sa.Status } @@ -35,7 +34,6 @@ func (sa *SmartAtaAttribute) Flatten() map[string]interface{} { return map[string]interface{}{ fmt.Sprintf("attr.%s.attribute_id", idString): idString, - fmt.Sprintf("attr.%s.name", idString): sa.Name, fmt.Sprintf("attr.%s.value", idString): sa.Value, fmt.Sprintf("attr.%s.worst", idString): sa.Worst, fmt.Sprintf("attr.%s.thresh", idString): sa.Threshold, @@ -62,8 +60,6 @@ func (sa *SmartAtaAttribute) Inflate(key string, val interface{}) { if err == nil { sa.AttributeId = attrId } - case "name": - sa.Name = val.(string) case "value": sa.Value = val.(int64) case "worst": @@ -81,7 +77,7 @@ func (sa *SmartAtaAttribute) Inflate(key string, val interface{}) { case "transformed_value": sa.TransformedValue = val.(int64) case "status": - sa.Status = val.(string) + sa.Status = val.(int64) case "status_reason": sa.StatusReason = val.(string) case "failure_rate": @@ -107,10 +103,6 @@ func (sa *SmartAtaAttribute) PopulateAttributeStatus() *SmartAtaAttribute { sa.ValidateThreshold(smartMetadata) } - //check if status is blank, set to "passed" - if len(sa.Status) == 0 { - sa.Status = pkg.SmartAttributeStatusPassed - } return sa } diff --git a/webapp/backend/pkg/models/measurements/smart_attribute.go b/webapp/backend/pkg/models/measurements/smart_attribute.go index a8aaea6..02a4fa7 100644 --- a/webapp/backend/pkg/models/measurements/smart_attribute.go +++ b/webapp/backend/pkg/models/measurements/smart_attribute.go @@ -3,5 +3,5 @@ package measurements type SmartAttribute interface { Flatten() (fields map[string]interface{}) Inflate(key string, val interface{}) - GetStatus() string + GetStatus() int64 } diff --git a/webapp/backend/pkg/models/measurements/smart_nvme_attribute.go b/webapp/backend/pkg/models/measurements/smart_nvme_attribute.go index 0320824..3996945 100644 --- a/webapp/backend/pkg/models/measurements/smart_nvme_attribute.go +++ b/webapp/backend/pkg/models/measurements/smart_nvme_attribute.go @@ -9,24 +9,22 @@ import ( type SmartNvmeAttribute struct { AttributeId string `json:"attribute_id"` //json string from smartctl - Name string `json:"name"` Value int64 `json:"value"` Threshold int64 `json:"thresh"` TransformedValue int64 `json:"transformed_value"` - Status string `json:"status,omitempty"` + Status int64 `json:"status,omitempty"` StatusReason string `json:"status_reason,omitempty"` FailureRate float64 `json:"failure_rate,omitempty"` } -func (sa *SmartNvmeAttribute) GetStatus() string { +func (sa *SmartNvmeAttribute) GetStatus() int64 { return sa.Status } func (sa *SmartNvmeAttribute) Flatten() map[string]interface{} { return map[string]interface{}{ fmt.Sprintf("attr.%s.attribute_id", sa.AttributeId): sa.AttributeId, - fmt.Sprintf("attr.%s.name", sa.AttributeId): sa.Name, fmt.Sprintf("attr.%s.value", sa.AttributeId): sa.Value, fmt.Sprintf("attr.%s.thresh", sa.AttributeId): sa.Threshold, @@ -47,8 +45,6 @@ func (sa *SmartNvmeAttribute) Inflate(key string, val interface{}) { switch keyParts[2] { case "attribute_id": sa.AttributeId = val.(string) - case "name": - sa.Name = val.(string) case "value": sa.Value = val.(int64) case "thresh": @@ -58,7 +54,7 @@ func (sa *SmartNvmeAttribute) Inflate(key string, val interface{}) { case "transformed_value": sa.TransformedValue = val.(int64) case "status": - sa.Status = val.(string) + sa.Status = val.(int64) case "status_reason": sa.StatusReason = val.(string) case "failure_rate": @@ -83,9 +79,5 @@ func (sa *SmartNvmeAttribute) PopulateAttributeStatus() *SmartNvmeAttribute { } //TODO: eventually figure out the critical_warning bits and determine correct error messages here. - //check if status is blank, set to "passed" - if len(sa.Status) == 0 { - sa.Status = pkg.SmartAttributeStatusPassed - } return sa } diff --git a/webapp/backend/pkg/models/measurements/smart_scsci_attribute.go b/webapp/backend/pkg/models/measurements/smart_scsci_attribute.go index a2f1ea1..d21229d 100644 --- a/webapp/backend/pkg/models/measurements/smart_scsci_attribute.go +++ b/webapp/backend/pkg/models/measurements/smart_scsci_attribute.go @@ -9,24 +9,22 @@ import ( type SmartScsiAttribute struct { AttributeId string `json:"attribute_id"` //json string from smartctl - Name string `json:"name"` Value int64 `json:"value"` Threshold int64 `json:"thresh"` TransformedValue int64 `json:"transformed_value"` - Status string `json:"status,omitempty"` + Status int64 `json:"status,omitempty"` StatusReason string `json:"status_reason,omitempty"` FailureRate float64 `json:"failure_rate,omitempty"` } -func (sa *SmartScsiAttribute) GetStatus() string { +func (sa *SmartScsiAttribute) GetStatus() int64 { return sa.Status } func (sa *SmartScsiAttribute) Flatten() map[string]interface{} { return map[string]interface{}{ fmt.Sprintf("attr.%s.attribute_id", sa.AttributeId): sa.AttributeId, - fmt.Sprintf("attr.%s.name", sa.AttributeId): sa.Name, fmt.Sprintf("attr.%s.value", sa.AttributeId): sa.Value, fmt.Sprintf("attr.%s.thresh", sa.AttributeId): sa.Threshold, @@ -47,8 +45,6 @@ func (sa *SmartScsiAttribute) Inflate(key string, val interface{}) { switch keyParts[2] { case "attribute_id": sa.AttributeId = val.(string) - case "name": - sa.Name = val.(string) case "value": sa.Value = val.(int64) case "thresh": @@ -58,7 +54,7 @@ func (sa *SmartScsiAttribute) Inflate(key string, val interface{}) { case "transformed_value": sa.TransformedValue = val.(int64) case "status": - sa.Status = val.(string) + sa.Status = val.(int64) case "status_reason": sa.StatusReason = val.(string) case "failure_rate": @@ -83,9 +79,5 @@ func (sa *SmartScsiAttribute) PopulateAttributeStatus() *SmartScsiAttribute { } } - //check if status is blank, set to "passed" - if len(sa.Status) == 0 { - sa.Status = pkg.SmartAttributeStatusPassed - } return sa } diff --git a/webapp/backend/pkg/thresholds/ata_attribute_metadata.go b/webapp/backend/pkg/thresholds/ata_attribute_metadata.go index 0628a18..3bd98eb 100644 --- a/webapp/backend/pkg/thresholds/ata_attribute_metadata.go +++ b/webapp/backend/pkg/thresholds/ata_attribute_metadata.go @@ -6,7 +6,7 @@ const AtaSmartAttributeDisplayTypeTransformed = "transformed" type AtaAttributeMetadata struct { ID int64 `json:"-"` - DisplayName string `json:"-"` + DisplayName string `json:"display_name"` Ideal string `json:"ideal"` Critical bool `json:"critical"` Description string `json:"description"`