fix: improve turnkey temperature monitoring for standalone nodes

- Fix script input handling to work with standard curl | bash pattern by prioritizing /dev/tty
- Add Raspberry Pi temperature sensor support (cpu_thermal chip and generic temp sensors)
- Add comprehensive documentation for turnkey standalone node setup
- Fix printf formatting error in setup script
This commit is contained in:
Richard Courtman 2025-10-18 06:51:56 +00:00
parent 669d7dc05c
commit de3bb47930
3 changed files with 64 additions and 2 deletions

View file

@ -158,6 +158,40 @@ The auto-setup script (Settings → Nodes → Setup Script) will prompt you to c
If the node is part of a Proxmox cluster, the script will now detect the other members and offer to configure the same SSH/lm-sensors setup on each of them automatically—confirm when prompted to roll it out cluster-wide.
### Turnkey Setup for Standalone Nodes (v4.25.0+)
**For standalone nodes** (not in a Proxmox cluster) running **containerized Pulse**, the setup script now automatically configures temperature monitoring with zero manual steps:
1. The script detects the node is standalone (not in a cluster)
2. Automatically fetches the temperature proxy's SSH public key from your Pulse server via `/api/system/proxy-public-key`
3. Installs it with forced commands (`command="sensors -j"`) automatically
4. Temperature monitoring "just works" - no manual SSH key management needed!
**Example output:**
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Standalone Node Temperature Setup
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Detected: This is a standalone node (not in a Proxmox cluster)
Fetching temperature proxy public key...
✓ Retrieved proxy public key
✓ Temperature proxy key installed (restricted to sensors -j)
✓ Standalone node temperature monitoring configured
The Pulse temperature proxy can now collect temperature data
from this node using secure SSH with forced commands.
```
**Security:**
- Public keys are safe to expose (it's in the name!)
- Forced commands restrict the key to only `command="sensors -j"`
- All other SSH features disabled (no-port-forwarding, no-pty, etc.)
- Works exactly like cluster setups, but fully automated
**Note:** This only works for containerized Pulse deployments where the temperature proxy is running. For native (non-containerized) installs, you'll still need to provide your Pulse server's public key manually as described in step 3 above.
## Setup (Manual)
If you skipped SSH setup during auto-setup, you can configure it manually:

View file

@ -3847,8 +3847,11 @@ if [ "$SSH_ALREADY_CONFIGURED" = true ]; then
if [ -t 0 ]; then
read -p "> " -n 1 -r SSH_ACTION
else
# When stdin is not a terminal (e.g., curl | bash), try /dev/tty first, then stdin for piped input
if read -p "> " -n 1 -r SSH_ACTION </dev/tty 2>/dev/null; then
:
elif read -t 2 -n 1 -r SSH_ACTION 2>/dev/null && [ -n "$SSH_ACTION" ]; then
echo "$SSH_ACTION"
else
echo "(No terminal available - keeping existing configuration)"
SSH_ACTION="k"
@ -3909,8 +3912,11 @@ else
if [ -t 0 ]; then
read -n 1 -r SSH_REPLY
else
# When stdin is not a terminal (e.g., curl | bash), try /dev/tty first, then stdin for piped input
if read -n 1 -r SSH_REPLY </dev/tty 2>/dev/null; then
:
elif read -t 2 -n 1 -r SSH_REPLY 2>/dev/null && [ -n "$SSH_REPLY" ]; then
echo "$SSH_REPLY"
else
echo "(No terminal available - skipping temperature monitoring)"
SSH_REPLY="n"
@ -4247,12 +4253,12 @@ if [ "$AUTO_REG_SUCCESS" != true ]; then
else
echo " Token Value: [See token output above]"
fi
echo " Host URL: %s"
echo " Host URL: YOUR_PROXMOX_HOST:8006"
echo ""
fi
`, serverName, time.Now().Format("2006-01-02 15:04:05"), pulseIP,
tokenName, tokenName, tokenName, tokenName, tokenName, tokenName,
authToken, pulseURL, serverHost, tokenName, tokenName, storagePerms, sshPublicKey, pulseURL, pulseURL, pulseURL, pulseURL, authToken, pulseURL, tokenName, serverHost)
authToken, pulseURL, serverHost, tokenName, tokenName, storagePerms, sshPublicKey, pulseURL, pulseURL, pulseURL, pulseURL, authToken, pulseURL, pulseURL, tokenName, serverHost)
} else { // PBS
script = fmt.Sprintf(`#!/bin/bash

View file

@ -226,6 +226,8 @@ func (tc *TemperatureCollector) parseSensorsJSON(jsonStr string) (*models.Temper
// parseCPUTemps extracts CPU temperature data from a sensor chip
func (tc *TemperatureCollector) parseCPUTemps(chipMap map[string]interface{}, temp *models.Temperature) {
foundPackageTemp := false
for sensorName, sensorData := range chipMap {
sensorMap, ok := sensorData.(map[string]interface{})
if !ok {
@ -236,6 +238,7 @@ func (tc *TemperatureCollector) parseCPUTemps(chipMap map[string]interface{}, te
if strings.Contains(sensorName, "Package id") || strings.Contains(sensorName, "Tdie") {
if tempVal := extractTempInput(sensorMap); !math.IsNaN(tempVal) {
temp.CPUPackage = tempVal
foundPackageTemp = true
}
}
@ -253,6 +256,25 @@ func (tc *TemperatureCollector) parseCPUTemps(chipMap map[string]interface{}, te
}
}
}
// If no package temperature was found (e.g., Raspberry Pi), look for generic temp sensors
if !foundPackageTemp {
for sensorName, sensorData := range chipMap {
sensorMap, ok := sensorData.(map[string]interface{})
if !ok {
continue
}
// Look for generic temperature sensors (e.g., "temp1" on Raspberry Pi)
if strings.HasPrefix(sensorName, "temp") || strings.HasPrefix(sensorName, "Temp") {
if tempVal := extractTempInput(sensorMap); !math.IsNaN(tempVal) && tempVal > 0 {
temp.CPUPackage = tempVal
temp.CPUMax = tempVal
break // Use the first valid generic temp sensor
}
}
}
}
}
// parseNVMeTemps extracts NVMe temperature data from a sensor chip