@@ -7,6 +7,8 @@ package diskhealthmetrics
77import (
88 "encoding/json"
99 "fmt"
10+ "os"
11+ "path/filepath"
1012 "time"
1113
1214 "github.com/nats-io/nats.go"
@@ -16,6 +18,11 @@ import (
1618func collectDiskHealthMetrics (cfg DiskHealthMetricsConfig ) []NormalizedSmartData {
1719 var allMetrics []NormalizedSmartData
1820
21+ // Check for test mode
22+ if cfg .TestMode {
23+ return collectTestDiskHealthMetrics (cfg )
24+ }
25+
1926 // Check if nvme-cli is available for enhanced NVMe support
2027 nvmeCliAvailable := checkNVMeCliInstalled ()
2128 if nvmeCliAvailable {
@@ -73,6 +80,59 @@ func collectDiskHealthMetrics(cfg DiskHealthMetricsConfig) []NormalizedSmartData
7380 return allMetrics
7481}
7582
83+ // collectTestDiskHealthMetrics collects metrics from test data files
84+ func collectTestDiskHealthMetrics (cfg DiskHealthMetricsConfig ) []NormalizedSmartData {
85+ var allMetrics []NormalizedSmartData
86+
87+ // Determine test data path
88+ scenarioPath := filepath .Join (cfg .TestDataPath , "scenarios" , cfg .TestScenario )
89+
90+ for _ , device := range cfg .Disks {
91+ jsonFile := filepath .Join (scenarioPath , device + ".json" )
92+
93+ // Check if file exists
94+ if _ , err := os .Stat (jsonFile ); os .IsNotExist (err ) {
95+ log .Warn ().Str ("device" , device ).Str ("file" , jsonFile ).Msg ("Test data file not found, skipping" )
96+ continue
97+ }
98+
99+ // Load test data
100+ rawData , err := collectSmartDataFromFile (jsonFile )
101+ if err != nil {
102+ log .Error ().Err (err ).Str ("file" , jsonFile ).Msg ("Error loading test data" )
103+ continue
104+ }
105+
106+ // Override device name to match test device
107+ rawData .Device .Name = "/dev/" + device
108+ rawData .Device .InfoName = "/dev/" + device
109+
110+ // Process as normal
111+ deviceInfo := & DeviceInfo {}
112+ FillDeviceInfoFromSmartData (deviceInfo , rawData )
113+ NormalizeVendor (deviceInfo )
114+ NormalizeDeviceInfo (deviceInfo )
115+
116+ smartAttrs := GetSmartAttributes ()
117+ ProcessAndUpdateSmartAttributes (smartAttrs , rawData )
118+ CleanupSmartAttributes (smartAttrs )
119+
120+ normalizedData := normalizeSmartData (rawData , deviceInfo , smartAttrs ,
121+ cfg .NodeName , cfg .InstanceID , cfg .CephOSDBasePath )
122+
123+ // Add a note in the log that this is test data
124+ log .Debug ().
125+ Str ("device" , device ).
126+ Str ("scenario" , cfg .TestScenario ).
127+ Interface ("attributes" , smartAttrs ).
128+ Msg ("Processed test device data" )
129+
130+ allMetrics = append (allMetrics , normalizedData )
131+ }
132+
133+ return allMetrics
134+ }
135+
76136// normalizeSmartData normalizes the raw SMART data
77137func normalizeSmartData (smartData * SmartCtlOutput , deviceInfo * DeviceInfo , attributes map [string ]SmartAttribute , nodeName , instanceID , basePath string ) NormalizedSmartData {
78138 var temperatureCelsius * int64
@@ -99,9 +159,15 @@ func normalizeSmartData(smartData *SmartCtlOutput, deviceInfo *DeviceInfo, attri
99159
100160 // Initialize device-specific attributes
101161 if smartData .Device .Protocol == "ATA" && smartData .ATASMARTAttributes != nil {
102- reallocatedSectors = & findSmartAttributeByID (smartData .ATASMARTAttributes .Table , 5 ).Raw .Value
103- pendingSectors = & findSmartAttributeByID (smartData .ATASMARTAttributes .Table , 197 ).Raw .Value
104- udmaCrcErrorCount = findSmartAttributeByID (smartData .ATASMARTAttributes .Table , 199 ).Raw .Value
162+ if attr := findSmartAttributeByID (smartData .ATASMARTAttributes .Table , 5 ); attr != nil {
163+ reallocatedSectors = & attr .Raw .Value
164+ }
165+ if attr := findSmartAttributeByID (smartData .ATASMARTAttributes .Table , 197 ); attr != nil {
166+ pendingSectors = & attr .Raw .Value
167+ }
168+ if attr := findSmartAttributeByID (smartData .ATASMARTAttributes .Table , 199 ); attr != nil {
169+ udmaCrcErrorCount = attr .Raw .Value
170+ }
105171 powerOnHours = & smartData .PowerOnTime .Hours
106172 } else if smartData .Device .Protocol == "SCSI" && smartData .SCSIStartStopCycleCounter != nil {
107173 powerOnHours = & smartData .PowerOnTime .Hours
@@ -141,20 +207,42 @@ func findSmartAttributeByID(attributes []SmartCtlATASMARTEntry, id int64) *Smart
141207}
142208
143209func StartMonitoring (cfg DiskHealthMetricsConfig ) {
144- if ! checkSmartctlInstalled () {
210+ // Skip smartctl check in test mode
211+ if ! cfg .TestMode && ! checkSmartctlInstalled () {
145212 log .Fatal ().Msg ("smartctl is not installed. please install smartmontools package." )
146213 }
147214
148- // Discover devices if wildcard (*) is used in the configuration.
149- if len ( cfg .Disks ) == 1 && cfg . Disks [ 0 ] == "*" {
150- devices , err := discoverDevices ()
151- if err != nil {
152- log . Fatal (). Err ( err ). Msg ( "Error discovering devices" )
215+ // Handle test mode setup
216+ if cfg .TestMode {
217+ // Use default test devices if none specified
218+ if len ( cfg . TestDevices ) == 0 {
219+ cfg . TestDevices = [] string { "nvme0" , "nvme1" , "sda" , "sdb" }
153220 }
221+ cfg .Disks = cfg .TestDevices
222+
223+ // Set default test data path if not specified
224+ if cfg .TestDataPath == "" {
225+ cfg .TestDataPath = "pkg/producers/diskhealthmetrics/testdata"
226+ }
227+
228+ log .Info ().
229+ Bool ("test_mode" , true ).
230+ Str ("test_scenario" , cfg .TestScenario ).
231+ Str ("test_data_path" , cfg .TestDataPath ).
232+ Strs ("test_devices" , cfg .TestDevices ).
233+ Msg ("Running in test mode with simulated data" )
234+ } else {
235+ // Discover devices if wildcard (*) is used in the configuration.
236+ if len (cfg .Disks ) == 1 && cfg .Disks [0 ] == "*" {
237+ devices , err := discoverDevices ()
238+ if err != nil {
239+ log .Fatal ().Err (err ).Msg ("Error discovering devices" )
240+ }
154241
155- cfg .Disks = make ([]string , len (devices .Devices ))
156- for i , device := range devices .Devices {
157- cfg .Disks [i ] = device .Name
242+ cfg .Disks = make ([]string , len (devices .Devices ))
243+ for i , device := range devices .Devices {
244+ cfg .Disks [i ] = device .Name
245+ }
158246 }
159247 }
160248
0 commit comments