fix: ensure proxmox nodes are displayed even if cluster endpoints are missing

Fixes #727. Previously, if temperature monitoring was enabled and a node wasn't found in ClusterEndpoints, the entire node processing was skipped. This change ensures we only skip temperature collection.
This commit is contained in:
courtmanr@gmail.com 2025-11-22 23:31:30 +00:00
parent f8647b53ff
commit d5fdf2f471

View file

@ -6165,6 +6165,7 @@ func (m *Monitor) pollPVEInstance(ctx context.Context, instanceName string, clie
// Prefer the resolved host for this node, with cluster overrides when available. // Prefer the resolved host for this node, with cluster overrides when available.
sshHost := modelNode.Host sshHost := modelNode.Host
foundNodeEndpoint := false foundNodeEndpoint := false
shouldCollect := true
if modelNode.IsClusterMember && instanceCfg.IsCluster { if modelNode.IsClusterMember && instanceCfg.IsCluster {
// For cluster members, wait until we have validated endpoints // For cluster members, wait until we have validated endpoints
@ -6175,107 +6176,109 @@ func (m *Monitor) pollPVEInstance(ctx context.Context, instanceName string, clie
Str("node", node.Node). Str("node", node.Node).
Str("instance", instanceCfg.Name). Str("instance", instanceCfg.Name).
Msg("Skipping temperature collection - cluster endpoints not yet validated") Msg("Skipping temperature collection - cluster endpoints not yet validated")
continue shouldCollect = false
} } else {
hasFingerprint := instanceCfg.Fingerprint != ""
hasFingerprint := instanceCfg.Fingerprint != "" for _, ep := range instanceCfg.ClusterEndpoints {
for _, ep := range instanceCfg.ClusterEndpoints { if strings.EqualFold(ep.NodeName, node.Node) {
if strings.EqualFold(ep.NodeName, node.Node) { if effective := clusterEndpointEffectiveURL(ep, instanceCfg.VerifySSL, hasFingerprint); effective != "" {
if effective := clusterEndpointEffectiveURL(ep, instanceCfg.VerifySSL, hasFingerprint); effective != "" { sshHost = effective
sshHost = effective foundNodeEndpoint = true
foundNodeEndpoint = true }
break
} }
break
} }
}
// If this node is a cluster member but we didn't find its specific endpoint, // If this node is a cluster member but we didn't find its specific endpoint,
// skip temperature collection to avoid using wrong endpoint // skip temperature collection to avoid using wrong endpoint
if !foundNodeEndpoint { if !foundNodeEndpoint {
tempCancel() tempCancel()
log.Debug(). log.Debug().
Str("node", node.Node). Str("node", node.Node).
Str("instance", instanceCfg.Name). Str("instance", instanceCfg.Name).
Int("endpointCount", len(instanceCfg.ClusterEndpoints)). Int("endpointCount", len(instanceCfg.ClusterEndpoints)).
Msg("Skipping temperature collection - node endpoint not found in cluster metadata") Msg("Skipping temperature collection - node endpoint not found in cluster metadata")
continue shouldCollect = false
}
} }
} }
if strings.TrimSpace(sshHost) == "" { if shouldCollect {
sshHost = node.Node if strings.TrimSpace(sshHost) == "" {
} sshHost = node.Node
// Use HTTP proxy if configured for this instance, otherwise fall back to socket/SSH
temp, err := m.tempCollector.CollectTemperatureWithProxy(tempCtx, sshHost, node.Node, instanceCfg.TemperatureProxyURL, instanceCfg.TemperatureProxyToken)
tempCancel()
if err == nil && temp != nil && temp.Available {
// Get the current CPU temperature (prefer package, fall back to max)
currentTemp := temp.CPUPackage
if currentTemp == 0 && temp.CPUMax > 0 {
currentTemp = temp.CPUMax
} }
// Find previous temperature data for this node to preserve min/max // Use HTTP proxy if configured for this instance, otherwise fall back to socket/SSH
var prevTemp *models.Temperature temp, err := m.tempCollector.CollectTemperatureWithProxy(tempCtx, sshHost, node.Node, instanceCfg.TemperatureProxyURL, instanceCfg.TemperatureProxyToken)
for _, prevNode := range prevInstanceNodes { tempCancel()
if prevNode.ID == modelNode.ID && prevNode.Temperature != nil {
prevTemp = prevNode.Temperature if err == nil && temp != nil && temp.Available {
break // Get the current CPU temperature (prefer package, fall back to max)
currentTemp := temp.CPUPackage
if currentTemp == 0 && temp.CPUMax > 0 {
currentTemp = temp.CPUMax
} }
}
// Initialize or update min/max tracking // Find previous temperature data for this node to preserve min/max
if prevTemp != nil && prevTemp.CPUMin > 0 { var prevTemp *models.Temperature
// Preserve existing min/max and update if necessary for _, prevNode := range prevInstanceNodes {
temp.CPUMin = prevTemp.CPUMin if prevNode.ID == modelNode.ID && prevNode.Temperature != nil {
temp.CPUMaxRecord = prevTemp.CPUMaxRecord prevTemp = prevNode.Temperature
temp.MinRecorded = prevTemp.MinRecorded break
temp.MaxRecorded = prevTemp.MaxRecorded }
}
// Update min if current is lower // Initialize or update min/max tracking
if currentTemp > 0 && currentTemp < temp.CPUMin { if prevTemp != nil && prevTemp.CPUMin > 0 {
// Preserve existing min/max and update if necessary
temp.CPUMin = prevTemp.CPUMin
temp.CPUMaxRecord = prevTemp.CPUMaxRecord
temp.MinRecorded = prevTemp.MinRecorded
temp.MaxRecorded = prevTemp.MaxRecorded
// Update min if current is lower
if currentTemp > 0 && currentTemp < temp.CPUMin {
temp.CPUMin = currentTemp
temp.MinRecorded = time.Now()
}
// Update max if current is higher
if currentTemp > temp.CPUMaxRecord {
temp.CPUMaxRecord = currentTemp
temp.MaxRecorded = time.Now()
}
} else if currentTemp > 0 {
// First reading - initialize min/max to current value
temp.CPUMin = currentTemp temp.CPUMin = currentTemp
temp.MinRecorded = time.Now()
}
// Update max if current is higher
if currentTemp > temp.CPUMaxRecord {
temp.CPUMaxRecord = currentTemp temp.CPUMaxRecord = currentTemp
temp.MinRecorded = time.Now()
temp.MaxRecorded = time.Now() temp.MaxRecorded = time.Now()
} }
} else if currentTemp > 0 {
// First reading - initialize min/max to current value
temp.CPUMin = currentTemp
temp.CPUMaxRecord = currentTemp
temp.MinRecorded = time.Now()
temp.MaxRecorded = time.Now()
}
modelNode.Temperature = temp modelNode.Temperature = temp
log.Debug(). log.Debug().
Str("node", node.Node). Str("node", node.Node).
Str("sshHost", sshHost). Str("sshHost", sshHost).
Float64("cpuPackage", temp.CPUPackage). Float64("cpuPackage", temp.CPUPackage).
Float64("cpuMax", temp.CPUMax). Float64("cpuMax", temp.CPUMax).
Float64("cpuMin", temp.CPUMin). Float64("cpuMin", temp.CPUMin).
Float64("cpuMaxRecord", temp.CPUMaxRecord). Float64("cpuMaxRecord", temp.CPUMaxRecord).
Int("nvmeCount", len(temp.NVMe)). Int("nvmeCount", len(temp.NVMe)).
Msg("Collected temperature data") Msg("Collected temperature data")
} else if err != nil { } else if err != nil {
log.Debug(). log.Debug().
Str("node", node.Node). Str("node", node.Node).
Str("sshHost", sshHost). Str("sshHost", sshHost).
Bool("isCluster", modelNode.IsClusterMember). Bool("isCluster", modelNode.IsClusterMember).
Int("endpointCount", len(instanceCfg.ClusterEndpoints)). Int("endpointCount", len(instanceCfg.ClusterEndpoints)).
Msg("Temperature collection failed - check SSH access") Msg("Temperature collection failed - check SSH access")
} else if temp != nil { } else if temp != nil {
log.Debug(). log.Debug().
Str("node", node.Node). Str("node", node.Node).
Str("sshHost", sshHost). Str("sshHost", sshHost).
Bool("available", temp.Available). Bool("available", temp.Available).
Msg("Temperature data unavailable after collection") Msg("Temperature data unavailable after collection")
}
} }
} }