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:
parent
669d7dc05c
commit
de3bb47930
3 changed files with 64 additions and 2 deletions
|
|
@ -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.
|
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)
|
## Setup (Manual)
|
||||||
|
|
||||||
If you skipped SSH setup during auto-setup, you can configure it manually:
|
If you skipped SSH setup during auto-setup, you can configure it manually:
|
||||||
|
|
|
||||||
|
|
@ -3847,8 +3847,11 @@ if [ "$SSH_ALREADY_CONFIGURED" = true ]; then
|
||||||
if [ -t 0 ]; then
|
if [ -t 0 ]; then
|
||||||
read -p "> " -n 1 -r SSH_ACTION
|
read -p "> " -n 1 -r SSH_ACTION
|
||||||
else
|
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
|
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
|
else
|
||||||
echo "(No terminal available - keeping existing configuration)"
|
echo "(No terminal available - keeping existing configuration)"
|
||||||
SSH_ACTION="k"
|
SSH_ACTION="k"
|
||||||
|
|
@ -3909,8 +3912,11 @@ else
|
||||||
if [ -t 0 ]; then
|
if [ -t 0 ]; then
|
||||||
read -n 1 -r SSH_REPLY
|
read -n 1 -r SSH_REPLY
|
||||||
else
|
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
|
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
|
else
|
||||||
echo "(No terminal available - skipping temperature monitoring)"
|
echo "(No terminal available - skipping temperature monitoring)"
|
||||||
SSH_REPLY="n"
|
SSH_REPLY="n"
|
||||||
|
|
@ -4247,12 +4253,12 @@ if [ "$AUTO_REG_SUCCESS" != true ]; then
|
||||||
else
|
else
|
||||||
echo " Token Value: [See token output above]"
|
echo " Token Value: [See token output above]"
|
||||||
fi
|
fi
|
||||||
echo " Host URL: %s"
|
echo " Host URL: YOUR_PROXMOX_HOST:8006"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
`, serverName, time.Now().Format("2006-01-02 15:04:05"), pulseIP,
|
`, serverName, time.Now().Format("2006-01-02 15:04:05"), pulseIP,
|
||||||
tokenName, tokenName, tokenName, tokenName, tokenName, tokenName,
|
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
|
} else { // PBS
|
||||||
script = fmt.Sprintf(`#!/bin/bash
|
script = fmt.Sprintf(`#!/bin/bash
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,8 @@ func (tc *TemperatureCollector) parseSensorsJSON(jsonStr string) (*models.Temper
|
||||||
|
|
||||||
// parseCPUTemps extracts CPU temperature data from a sensor chip
|
// parseCPUTemps extracts CPU temperature data from a sensor chip
|
||||||
func (tc *TemperatureCollector) parseCPUTemps(chipMap map[string]interface{}, temp *models.Temperature) {
|
func (tc *TemperatureCollector) parseCPUTemps(chipMap map[string]interface{}, temp *models.Temperature) {
|
||||||
|
foundPackageTemp := false
|
||||||
|
|
||||||
for sensorName, sensorData := range chipMap {
|
for sensorName, sensorData := range chipMap {
|
||||||
sensorMap, ok := sensorData.(map[string]interface{})
|
sensorMap, ok := sensorData.(map[string]interface{})
|
||||||
if !ok {
|
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 strings.Contains(sensorName, "Package id") || strings.Contains(sensorName, "Tdie") {
|
||||||
if tempVal := extractTempInput(sensorMap); !math.IsNaN(tempVal) {
|
if tempVal := extractTempInput(sensorMap); !math.IsNaN(tempVal) {
|
||||||
temp.CPUPackage = 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
|
// parseNVMeTemps extracts NVMe temperature data from a sensor chip
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue