mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 04:06:21 +00:00
feat: Agent collects S.M.A.R.T. disk data via smartctl. Related to #907
- Add smartctl package to collect disk temperature and health data - Add SMART field to agent Sensors struct - Host agent now runs smartctl to collect disk temps when available - Backend processes agent SMART data for temperature display - Graceful fallback when smartctl not installed
This commit is contained in:
@@ -208,8 +208,29 @@ func convertHostSensorsToTemperature(sensors models.HostSensorSummary, lastSeen
|
||||
}
|
||||
}
|
||||
|
||||
// Convert S.M.A.R.T. data from host agent
|
||||
if len(sensors.SMART) > 0 {
|
||||
temp.SMART = make([]models.DiskTemp, 0, len(sensors.SMART))
|
||||
for _, disk := range sensors.SMART {
|
||||
// Skip disks in standby (no temperature data)
|
||||
if disk.Standby {
|
||||
continue
|
||||
}
|
||||
temp.SMART = append(temp.SMART, models.DiskTemp{
|
||||
Device: "/dev/" + disk.Device,
|
||||
Serial: disk.Serial,
|
||||
WWN: disk.WWN,
|
||||
Model: disk.Model,
|
||||
Type: disk.Type,
|
||||
Temperature: disk.Temperature,
|
||||
LastUpdated: lastSeen,
|
||||
})
|
||||
}
|
||||
temp.HasSMART = len(temp.SMART) > 0
|
||||
}
|
||||
|
||||
// Validate we have at least some data
|
||||
if !temp.HasCPU && !temp.HasGPU && !temp.HasNVMe {
|
||||
if !temp.HasCPU && !temp.HasGPU && !temp.HasNVMe && !temp.HasSMART {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -220,6 +241,7 @@ func convertHostSensorsToTemperature(sensors models.HostSensorSummary, lastSeen
|
||||
Int("coreCount", len(temp.Cores)).
|
||||
Int("nvmeCount", len(temp.NVMe)).
|
||||
Int("gpuCount", len(temp.GPU)).
|
||||
Int("smartCount", len(temp.SMART)).
|
||||
Msg("Converted host agent sensors to temperature data")
|
||||
|
||||
return temp
|
||||
@@ -258,7 +280,7 @@ func mergeTemperatureData(hostAgentTemp, proxyTemp *models.Temperature) *models.
|
||||
HasCPU: hostAgentTemp.HasCPU,
|
||||
HasGPU: hostAgentTemp.HasGPU,
|
||||
HasNVMe: hostAgentTemp.HasNVMe,
|
||||
HasSMART: proxyTemp.HasSMART, // SMART data only comes from proxy
|
||||
HasSMART: hostAgentTemp.HasSMART || proxyTemp.HasSMART,
|
||||
LastUpdate: hostAgentTemp.LastUpdate,
|
||||
}
|
||||
|
||||
@@ -270,8 +292,10 @@ func mergeTemperatureData(hostAgentTemp, proxyTemp *models.Temperature) *models.
|
||||
result.HasCPU = true
|
||||
}
|
||||
|
||||
// Keep proxy SMART data (host agent doesn't have smartctl access currently)
|
||||
if proxyTemp.HasSMART {
|
||||
// Merge SMART data - prefer host agent if available, fall back to proxy
|
||||
if hostAgentTemp.HasSMART {
|
||||
result.SMART = hostAgentTemp.SMART
|
||||
} else if proxyTemp.HasSMART {
|
||||
result.SMART = proxyTemp.SMART
|
||||
}
|
||||
|
||||
|
||||
@@ -2226,6 +2226,7 @@ func (m *Monitor) ApplyHostReport(report agentshost.Report, tokenRecord *config.
|
||||
TemperatureCelsius: cloneStringFloatMap(report.Sensors.TemperatureCelsius),
|
||||
FanRPM: cloneStringFloatMap(report.Sensors.FanRPM),
|
||||
Additional: cloneStringFloatMap(report.Sensors.Additional),
|
||||
SMART: convertAgentSMARTToModels(report.Sensors.SMART),
|
||||
},
|
||||
RAID: raid,
|
||||
Ceph: cephData,
|
||||
@@ -9605,6 +9606,27 @@ func isLegacyDockerAgent(agentType string) bool {
|
||||
return agentType != "unified"
|
||||
}
|
||||
|
||||
// convertAgentSMARTToModels converts agent report S.M.A.R.T. data to the models.HostDiskSMART format.
|
||||
func convertAgentSMARTToModels(smart []agentshost.DiskSMART) []models.HostDiskSMART {
|
||||
if len(smart) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]models.HostDiskSMART, 0, len(smart))
|
||||
for _, disk := range smart {
|
||||
result = append(result, models.HostDiskSMART{
|
||||
Device: disk.Device,
|
||||
Model: disk.Model,
|
||||
Serial: disk.Serial,
|
||||
WWN: disk.WWN,
|
||||
Type: disk.Type,
|
||||
Temperature: disk.Temperature,
|
||||
Health: disk.Health,
|
||||
Standby: disk.Standby,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// convertAgentCephToModels converts agent report Ceph data to the models.HostCephCluster format.
|
||||
func convertAgentCephToModels(ceph *agentshost.CephCluster) *models.HostCephCluster {
|
||||
if ceph == nil {
|
||||
|
||||
Reference in New Issue
Block a user