diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 848adf5..20075c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -163,7 +163,7 @@ docker cp scrutiny:/tmp/web.log web.log # Docker Development ``` -docker build -f docker/Dockerfile . -t chcr.io/analogj/scrutiny:master-omnibus +docker build -f docker/Dockerfile . -t ghcr.io/analogj/scrutiny:master-omnibus docker run -it --rm -p 8080:8080 \ -v /run/udev:/run/udev:ro \ --cap-add SYS_RAWIO \ diff --git a/webapp/backend/pkg/database/interface.go b/webapp/backend/pkg/database/interface.go index ee7066e..7e3dfcf 100644 --- a/webapp/backend/pkg/database/interface.go +++ b/webapp/backend/pkg/database/interface.go @@ -26,7 +26,7 @@ type DeviceRepo interface { SaveSmartAttributes(ctx context.Context, wwn string, collectorSmartData collector.SmartInfo) (measurements.Smart, error) GetSmartAttributeHistory(ctx context.Context, wwn string, durationKey string, selectEntries int, selectEntriesOffset int, attributes []string) ([]measurements.Smart, error) - SaveSmartTemperature(ctx context.Context, wwn string, deviceProtocol string, collectorSmartData collector.SmartInfo) error + SaveSmartTemperature(ctx context.Context, wwn string, deviceProtocol string, collectorSmartData collector.SmartInfo, discardSCTTempHistory bool) error GetSummary(ctx context.Context) (map[string]*models.DeviceSummary, error) GetSmartTemperatureHistory(ctx context.Context, durationKey string) (map[string][]measurements.SmartTemperature, error) diff --git a/webapp/backend/pkg/database/mock/mock_database.go b/webapp/backend/pkg/database/mock/mock_database.go index f5fefc2..bf7dd02 100644 --- a/webapp/backend/pkg/database/mock/mock_database.go +++ b/webapp/backend/pkg/database/mock/mock_database.go @@ -228,17 +228,17 @@ func (mr *MockDeviceRepoMockRecorder) SaveSmartAttributes(ctx, wwn, collectorSma } // SaveSmartTemperature mocks base method. -func (m *MockDeviceRepo) SaveSmartTemperature(ctx context.Context, wwn, deviceProtocol string, collectorSmartData collector.SmartInfo) error { +func (m *MockDeviceRepo) SaveSmartTemperature(ctx context.Context, wwn, deviceProtocol string, collectorSmartData collector.SmartInfo, discardSCTTempHistory bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveSmartTemperature", ctx, wwn, deviceProtocol, collectorSmartData) + ret := m.ctrl.Call(m, "SaveSmartTemperature", ctx, wwn, deviceProtocol, collectorSmartData, discardSCTTempHistory) ret0, _ := ret[0].(error) return ret0 } // SaveSmartTemperature indicates an expected call of SaveSmartTemperature. -func (mr *MockDeviceRepoMockRecorder) SaveSmartTemperature(ctx, wwn, deviceProtocol, collectorSmartData interface{}) *gomock.Call { +func (mr *MockDeviceRepoMockRecorder) SaveSmartTemperature(ctx, wwn, deviceProtocol, collectorSmartData, discardSCTTempHistory interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSmartTemperature", reflect.TypeOf((*MockDeviceRepo)(nil).SaveSmartTemperature), ctx, wwn, deviceProtocol, collectorSmartData) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSmartTemperature", reflect.TypeOf((*MockDeviceRepo)(nil).SaveSmartTemperature), ctx, wwn, deviceProtocol, collectorSmartData, discardSCTTempHistory) } // UpdateDevice mocks base method. diff --git a/webapp/backend/pkg/database/scrutiny_repository_migrations.go b/webapp/backend/pkg/database/scrutiny_repository_migrations.go index e12fae8..2e069e0 100644 --- a/webapp/backend/pkg/database/scrutiny_repository_migrations.go +++ b/webapp/backend/pkg/database/scrutiny_repository_migrations.go @@ -409,6 +409,21 @@ func (sr *scrutinyRepository) Migrate(ctx context.Context) error { return tx.AutoMigrate(m20250221084400.Device{}) }, }, + { + ID: "m20260105083200", // add discard_sct_temp_history setting. + Migrate: func(tx *gorm.DB) error { + //add discard_sct_temp_history setting default. + var defaultSettings = []m20220716214900.Setting{ + { + SettingKeyName: "collector.discard_sct_temp_history", + SettingKeyDescription: "Whether to discard SCT Temperature history (true | false)", + SettingDataType: "bool", + SettingValueBool: false, + }, + } + return tx.Create(&defaultSettings).Error + }, + }, }) if err := m.Migrate(); err != nil { diff --git a/webapp/backend/pkg/database/scrutiny_repository_temperature.go b/webapp/backend/pkg/database/scrutiny_repository_temperature.go index 9edec87..07caa0e 100644 --- a/webapp/backend/pkg/database/scrutiny_repository_temperature.go +++ b/webapp/backend/pkg/database/scrutiny_repository_temperature.go @@ -3,18 +3,19 @@ package database import ( "context" "fmt" + "strings" + "time" + "github.com/analogj/scrutiny/webapp/backend/pkg/models/collector" "github.com/analogj/scrutiny/webapp/backend/pkg/models/measurements" influxdb2 "github.com/influxdata/influxdb-client-go/v2" - "strings" - "time" ) -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Temperature Data -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -func (sr *scrutinyRepository) SaveSmartTemperature(ctx context.Context, wwn string, deviceProtocol string, collectorSmartData collector.SmartInfo) error { - if len(collectorSmartData.AtaSctTemperatureHistory.Table) > 0 { +// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +func (sr *scrutinyRepository) SaveSmartTemperature(ctx context.Context, wwn string, deviceProtocol string, collectorSmartData collector.SmartInfo, discardSCTTempHistory bool) error { + if len(collectorSmartData.AtaSctTemperatureHistory.Table) > 0 && !discardSCTTempHistory { for ndx, temp := range collectorSmartData.AtaSctTemperatureHistory.Table { //temp value may be null, we must skip/ignore them. See #393 diff --git a/webapp/backend/pkg/models/settings.go b/webapp/backend/pkg/models/settings.go index d40e3e9..a3a83ca 100644 --- a/webapp/backend/pkg/models/settings.go +++ b/webapp/backend/pkg/models/settings.go @@ -17,6 +17,10 @@ type Settings struct { LineStroke string `json:"line_stroke" mapstructure:"line_stroke"` PoweredOnHoursUnit string `json:"powered_on_hours_unit" mapstructure:"powered_on_hours_unit"` + Collector struct { + DiscardSCTTempHistory bool `json:"discard_sct_temp_history" mapstructure:"discard_sct_temp_history"` + } `json:"collector" mapstructure:"collector"` + Metrics struct { NotifyLevel int `json:"notify_level" mapstructure:"notify_level"` StatusFilterAttributes int `json:"status_filter_attributes" mapstructure:"status_filter_attributes"` diff --git a/webapp/backend/pkg/web/handler/upload_device_metrics.go b/webapp/backend/pkg/web/handler/upload_device_metrics.go index 0814433..7f4b0d7 100644 --- a/webapp/backend/pkg/web/handler/upload_device_metrics.go +++ b/webapp/backend/pkg/web/handler/upload_device_metrics.go @@ -61,7 +61,7 @@ func UploadDeviceMetrics(c *gin.Context) { } // save smart temperature data (ignore failures) - err = deviceRepo.SaveSmartTemperature(c, c.Param("wwn"), updatedDevice.DeviceProtocol, collectorSmartData) + err = deviceRepo.SaveSmartTemperature(c, c.Param("wwn"), updatedDevice.DeviceProtocol, collectorSmartData, appConfig.GetBool(fmt.Sprintf("%s.collector.discard_sct_temp_history", config.DB_USER_SETTINGS_SUBKEY))) if err != nil { logger.Errorln("An error occurred while saving smartctl temp data", err) c.JSON(http.StatusInternalServerError, gin.H{"success": false}) diff --git a/webapp/backend/pkg/web/server_test.go b/webapp/backend/pkg/web/server_test.go index bd2407c..081d9f6 100644 --- a/webapp/backend/pkg/web/server_test.go +++ b/webapp/backend/pkg/web/server_test.go @@ -191,6 +191,7 @@ func (suite *ServerTestSuite) TestUploadDeviceMetricsRoute() { fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes() fakeConfig.EXPECT().GetBool("user.metrics.repeat_notifications").Return(true).AnyTimes() + fakeConfig.EXPECT().GetBool("user.collector.discard_sct_temp_history").Return(false).AnyTimes() fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes() fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes() if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions { @@ -250,6 +251,7 @@ func (suite *ServerTestSuite) TestPopulateMultiple() { fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes() fakeConfig.EXPECT().GetBool("user.metrics.repeat_notifications").Return(true).AnyTimes() + fakeConfig.EXPECT().GetBool("user.collector.discard_sct_temp_history").Return(false).AnyTimes() fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes() fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes() if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions { @@ -533,6 +535,7 @@ func (suite *ServerTestSuite) TestGetDevicesSummaryRoute_Nvme() { fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes() fakeConfig.EXPECT().GetBool("user.metrics.repeat_notifications").Return(true).AnyTimes() + fakeConfig.EXPECT().GetBool("user.collector.discard_sct_temp_history").Return(false).AnyTimes() fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes() fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes() fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{}) diff --git a/webapp/frontend/src/app/core/config/app.config.ts b/webapp/frontend/src/app/core/config/app.config.ts index c5a1f6f..9172386 100644 --- a/webapp/frontend/src/app/core/config/app.config.ts +++ b/webapp/frontend/src/app/core/config/app.config.ts @@ -54,6 +54,10 @@ export interface AppConfig { line_stroke?: LineStroke; // Settings from Scrutiny API + + collector?: { + discard_sct_temp_history?: boolean + } metrics?: { notify_level?: MetricsNotifyLevel @@ -84,6 +88,10 @@ export const appConfig: AppConfig = { powered_on_hours_unit: 'humanize', line_stroke: 'smooth', + + collector: { + discard_sct_temp_history : false, + }, metrics: { notify_level: MetricsNotifyLevel.Fail, diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html index 970044c..c482506 100644 --- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html +++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html @@ -102,6 +102,23 @@ + +
+

Quirks

+
+
+ +
+ + Discard SCT Temperature History + + Enabled + Disabled + + +
diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts index ec65157..e378448 100644 --- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts +++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts @@ -28,6 +28,7 @@ export class DashboardSettingsComponent implements OnInit { poweredOnHoursUnit: string; lineStroke: string; theme: string; + discardSCTTempHistory: boolean; statusThreshold: number; statusFilterAttributes: number; repeatNotifications: boolean; @@ -57,6 +58,8 @@ export class DashboardSettingsComponent implements OnInit { this.lineStroke = config.line_stroke; this.theme = config.theme; + this.discardSCTTempHistory = config.collector.discard_sct_temp_history; + this.statusFilterAttributes = config.metrics.status_filter_attributes; this.statusThreshold = config.metrics.status_threshold; this.repeatNotifications = config.metrics.repeat_notifications; @@ -74,6 +77,9 @@ export class DashboardSettingsComponent implements OnInit { powered_on_hours_unit: this.poweredOnHoursUnit as DevicePoweredOnUnit, line_stroke: this.lineStroke as LineStroke, theme: this.theme as Theme, + collector: { + discard_sct_temp_history: this.discardSCTTempHistory + }, metrics: { status_filter_attributes: this.statusFilterAttributes as MetricsStatusFilterAttributes, status_threshold: this.statusThreshold as MetricsStatusThreshold,