Add persistence: install script, backup/restore, systemd service, docker-compose, docs

This commit is contained in:
devmatrix 2026-02-20 16:45:09 +00:00
parent 1d258875a8
commit 3dfdcd1865
6 changed files with 558 additions and 124 deletions

99
PERSISTENCE.md Normal file
View File

@ -0,0 +1,99 @@
# IPMI Controller - Persistence Setup
## Data Persistence
All configuration and user data is stored in the `data/` directory:
- `data/config.json` - All settings, fan curves, IPMI config
- `data/users.json` - User accounts and passwords
**IMPORTANT:** The `data/` directory is committed to git for version control of your settings.
## Backup Your Settings
```bash
# Create backup
cd ~/ipmi-controller
cp -r data data.backup.$(date +%Y%m%d)
# Or backup to external location
cp data/config.json /mnt/backup/ipmi-controller-config.json
```
## Auto-Start on Boot (systemd)
1. **Create service file:**
```bash
sudo tee /etc/systemd/system/ipmi-controller.service << 'EOF'
[Unit]
Description=IPMI Controller
After=network.target
[Service]
Type=simple
User=devmatrix
WorkingDirectory=/home/devmatrix/ipmi-controller
ExecStart=/usr/bin/python3 /home/devmatrix/ipmi-controller/web_server.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
```
2. **Enable and start:**
```bash
sudo systemctl daemon-reload
sudo systemctl enable ipmi-controller
sudo systemctl start ipmi-controller
```
3. **Check status:**
```bash
sudo systemctl status ipmi-controller
sudo journalctl -u ipmi-controller -f
```
## Docker Deployment (Persistent)
```bash
# Using docker-compose
cd ~/ipmi-controller
docker-compose up -d
# Data is persisted in ./data directory
```
## Updating Without Losing Settings
```bash
cd ~/ipmi-controller
# Backup first
cp -r data data.backup
# Pull updates
git pull
# Restart
sudo systemctl restart ipmi-controller
# OR if using docker:
docker-compose restart
```
## What Gets Persisted
✅ IPMI connection settings
✅ HTTP sensor configuration
✅ Fan curves (Balanced, Silent, Performance, etc.)
✅ User accounts and passwords
✅ Theme preference (dark/light)
✅ Fan groups and custom names
✅ All control settings (poll interval, panic temps, etc.)
## Migration to New Server
1. Copy `data/config.json` and `data/users.json` to new server
2. Install ipmitool: `sudo apt-get install ipmitool`
3. Install Python deps: `pip install -r requirements.txt`
4. Start server: `python3 web_server.py`

144
README.md
View File

@ -1,50 +1,156 @@
# IPMI Controller
Advanced IPMI fan control for Dell servers with web interface, HTTP sensor support, fan groups, and multiple fan curves.
Advanced IPMI fan control for Dell servers with web interface, HTTP sensor support, multiple fan curves, and persistent configuration.
## Features
- 🌡️ **Temperature Monitoring** - IPMI and HTTP (lm-sensors) sensor support
- 🌬️ **Fan Control** - Automatic curves, manual control, panic mode
- 👥 **Fan Groups** - Group fans with different curves
- 🌡️ **Dual Sensor Support** - IPMI + HTTP (lm-sensors from Proxmox/host)
- 🌬️ **Smart Fan Control** - Automatic curves, manual control, panic mode
- 📊 **3 Preset Curves** - Balanced (default), Silent, Performance
- 👥 **Fan Groups** - Organize and control fans individually or in groups
- 🔍 **Fan Identify** - Visual fan identification
- 🎨 **Dark/Light Mode** - Theme switching
- 📊 **Public API** - For external integrations
- 🔒 **Secure** - Password protected with JWT auth
- 🎨 **Themes** - Dark and Light mode
- 📱 **Responsive Web UI** - Works on desktop and mobile
- 🔌 **Public API** - For external integrations
- 💾 **Persistent Settings** - Survives restarts and updates
## Quick Start
### Requirements
- Python 3.10+
- ipmitool
- Linux server (tested on Dell T710)
### Automated Install (Recommended)
### Install
```bash
git clone https://github.com/yourusername/ipmi-controller.git
cd ipmi-controller
pip install -r requirements.txt
chmod +x install.sh
sudo ./install.sh
```
### Run
This will:
- Install all dependencies
- Create systemd service for auto-start
- Set up persistent data directory
- Start the controller on boot
### Manual Install
```bash
# Install dependencies
sudo apt-get install -y ipmitool python3-pip
pip3 install -r requirements.txt
# Run
python3 web_server.py
```
Open `http://your-server:8000` and complete the setup wizard.
Access at `http://your-server:8000`
## Docker
## Initial Setup
1. Complete the setup wizard (create admin + IPMI config)
2. Login with your admin credentials
3. (Optional) Set up HTTP sensor on your Proxmox host:
```bash
# On Proxmox server
curl -O https://raw.githubusercontent.com/yourusername/ipmi-controller/main/setup-sensors-server.sh
sudo ./setup-sensors-server.sh
```
4. Enable auto control and enjoy automatic fan management!
## Persistence
All your settings are automatically saved to `data/config.json`:
✅ IPMI configuration
✅ HTTP sensor settings
✅ Fan curves (Balanced, Silent, Performance)
✅ User accounts
✅ Theme preference
✅ All control settings
**Backups:**
```bash
./backup.sh backup # Create backup
./backup.sh list # List backups
./backup.sh restore [file] # Restore from backup
```
**Auto-backup via cron:**
```bash
# Add to crontab (keeps 30 days of backups)
0 2 * * * /opt/ipmi-controller/backup.sh auto
```
## Updating
```bash
docker build -t ipmi-controller .
docker run -d -p 8000:8000 -v ./data:/app/data ipmi-controller
cd ipmi-controller
git pull
# Settings are preserved automatically
sudo systemctl restart ipmi-controller
```
## Fan Curves
**Balanced** (Default) - Best for most users:
```
30°C → 10% | 40°C → 15% | 50°C → 30% | 60°C → 55% | 70°C → 85% | 80°C → 100%
```
**Silent** - Noise-sensitive environments:
```
30°C → 5% | 40°C → 10% | 50°C → 15% | 60°C → 35% | 70°C → 70% | 80°C → 100%
```
**Performance** - Heavy workloads:
```
30°C → 20% | 40°C → 35% | 50°C → 55% | 60°C → 85% | 70°C → 100%
```
## Documentation
- [Setup Guide](SETUP.md) - Full installation and configuration
- [Setup Guide](SETUP.md) - Full installation instructions
- [Persistence Guide](PERSISTENCE.md) - Backup and restore
- [API Reference](API.md) - Public API documentation
## Docker
```bash
docker-compose up -d
```
Data persists in `./data` directory.
## Management Commands
```bash
# Status
sudo systemctl status ipmi-controller
# Logs
sudo journalctl -u ipmi-controller -f
# Restart
sudo systemctl restart ipmi-controller
# Stop
sudo systemctl stop ipmi-controller
```
## Troubleshooting
**IPMI Connection Failed:**
- Verify IPMI IP, username, password
- Test: `ipmitool -I lanplus -H <ip> -U <user> -P <pass> mc info`
**No Temperature Data:**
- Check HTTP sensor: `curl http://proxmox-ip:8888`
- Verify `sensor_preference` is set to "auto" or "http"
**Settings Lost After Update:**
- Ensure `data/` directory is not deleted
- Check file permissions: `ls -la data/`
## License
MIT License

141
backup.sh Executable file
View File

@ -0,0 +1,141 @@
#!/bin/bash
# IPMI Controller - Backup and Restore
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DATA_DIR="${DATA_DIR:-$SCRIPT_DIR/data}"
BACKUP_DIR="${BACKUP_DIR:-$SCRIPT_DIR/backups}"
show_help() {
echo "IPMI Controller - Backup/Restore Tool"
echo ""
echo "Usage:"
echo " $0 backup - Create backup"
echo " $0 restore [filename] - Restore from backup"
echo " $0 list - List available backups"
echo " $0 auto - Auto-backup (cron-friendly)"
echo ""
}
create_backup() {
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/ipmi-controller-backup-$TIMESTAMP.tar.gz"
echo "📦 Creating backup..."
tar -czf "$BACKUP_FILE" -C "$SCRIPT_DIR" data/ 2>/dev/null
if [ $? -eq 0 ]; then
echo "✅ Backup created: $BACKUP_FILE"
echo ""
ls -lh "$BACKUP_FILE"
else
echo "❌ Backup failed"
exit 1
fi
}
restore_backup() {
if [ -z "$1" ]; then
echo "❌ Please specify backup file"
list_backups
exit 1
fi
BACKUP_FILE="$BACKUP_DIR/$1"
if [ ! -f "$BACKUP_FILE" ]; then
BACKUP_FILE="$1"
fi
if [ ! -f "$BACKUP_FILE" ]; then
echo "❌ Backup file not found: $1"
exit 1
fi
echo "⚠️ This will overwrite current settings!"
read -p "Are you sure? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "Aborted"
exit 0
fi
# Create safety backup first
echo "📦 Creating safety backup..."
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
tar -czf "$BACKUP_DIR/safety-backup-before-restore-$TIMESTAMP.tar.gz" -C "$SCRIPT_DIR" data/ 2>/dev/null
# Stop service if running
if systemctl is-active --quiet ipmi-controller 2>/dev/null; then
echo "🛑 Stopping service..."
sudo systemctl stop ipmi-controller
WAS_RUNNING=true
else
WAS_RUNNING=false
fi
# Restore
echo "📥 Restoring from backup..."
tar -xzf "$BACKUP_FILE" -C "$SCRIPT_DIR"
if [ $? -eq 0 ]; then
echo "✅ Restore complete"
# Restart service if it was running
if [ "$WAS_RUNNING" = true ]; then
echo "🚀 Starting service..."
sudo systemctl start ipmi-controller
fi
else
echo "❌ Restore failed"
exit 1
fi
}
list_backups() {
echo "📂 Available backups:"
echo ""
if [ -d "$BACKUP_DIR" ] && [ "$(ls -A "$BACKUP_DIR")" ]; then
ls -lh "$BACKUP_DIR"/*.tar.gz 2>/dev/null | awk '{print " " $9 " (" $5 ")"}'
else
echo " No backups found"
fi
}
auto_backup() {
# This is cron-friendly - keeps only last 30 days
mkdir -p "$BACKUP_DIR"
# Create backup
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/auto-backup-$TIMESTAMP.tar.gz"
tar -czf "$BACKUP_FILE" -C "$SCRIPT_DIR" data/ 2>/dev/null
# Clean old backups (keep last 30 days)
find "$BACKUP_DIR" -name "auto-backup-*.tar.gz" -mtime +30 -delete 2>/dev/null
echo "Auto-backup complete: $BACKUP_FILE"
}
case "${1:-}" in
backup)
create_backup
;;
restore)
restore_backup "$2"
;;
list)
list_backups
;;
auto)
auto_backup
;;
help|--help|-h)
show_help
;;
*)
show_help
exit 1
;;
esac

20
docker-compose.yml Normal file
View File

@ -0,0 +1,20 @@
version: '3.8'
services:
ipmi-controller:
build: .
container_name: ipmi-controller
restart: unless-stopped
ports:
- "8000:8000"
volumes:
# Persist data directory
- ./data:/app/data
# Optional: mount ipmitool from host if needed
- /usr/bin/ipmitool:/usr/bin/ipmitool:ro
environment:
- PYTHONUNBUFFERED=1
- DATA_DIR=/app/data
# Required for ipmitool to work in container
privileged: true
network_mode: host

View File

@ -1,147 +1,215 @@
#!/bin/bash
# Setup script for IPMI Fan Controller v2
# IPMI Controller - Install Script with Persistence
# This sets up auto-start and ensures settings persist
set -e
echo "🌬️ IPMI Fan Controller v2 - Setup"
echo "=================================="
INSTALL_DIR="${1:-/opt/ipmi-controller}"
DATA_DIR="$INSTALL_DIR/data"
SERVICE_NAME="ipmi-controller"
USER="${SUDO_USER:-$USER}"
echo "🌡️ IPMI Controller Installation"
echo "================================"
echo "Install dir: $INSTALL_DIR"
echo "Data dir: $DATA_DIR"
echo "Service user: $USER"
echo ""
# Check if running as root for system-wide install
if [ "$EUID" -eq 0 ]; then
INSTALL_SYSTEM=true
INSTALL_DIR="/opt/ipmi-fan-controller"
CONFIG_DIR="/etc/ipmi-fan-controller"
if [ "$EUID" -ne 0 ]; then
echo "⚠️ Not running as root. Installing to $HOME/ipmi-controller instead."
INSTALL_DIR="$HOME/ipmi-controller"
DATA_DIR="$INSTALL_DIR/data"
SYSTEM_INSTALL=false
else
INSTALL_SYSTEM=false
INSTALL_DIR="$HOME/.local/ipmi-fan-controller"
CONFIG_DIR="$HOME/.config/ipmi-fan-controller"
echo "⚠️ Running as user - installing to $INSTALL_DIR"
echo " (Run with sudo for system-wide install)"
echo ""
SYSTEM_INSTALL=true
fi
# Check dependencies
echo "📦 Checking dependencies..."
if ! command -v python3 &> /dev/null; then
echo "❌ Python 3 is required but not installed"
exit 1
fi
if ! command -v ipmitool &> /dev/null; then
echo "⚠️ ipmitool not found. Installing..."
if [ "$INSTALL_SYSTEM" = true ]; then
apt-get update && apt-get install -y ipmitool
else
echo "❌ Please install ipmitool: sudo apt-get install ipmitool"
exit 1
fi
fi
echo "✓ Python 3: $(python3 --version)"
echo "✓ ipmitool: $(ipmitool -V)"
# Create directories
echo ""
echo "📁 Creating directories..."
mkdir -p "$INSTALL_DIR"
mkdir -p "$CONFIG_DIR"
mkdir -p "$INSTALL_DIR/logs"
mkdir -p "$DATA_DIR"
# Copy files
echo ""
echo "📋 Installing files..."
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cp "$SCRIPT_DIR/fan_controller.py" "$INSTALL_DIR/"
cp "$SCRIPT_DIR/web_server.py" "$INSTALL_DIR/"
cp "$SCRIPT_DIR/requirements.txt" "$INSTALL_DIR/"
echo "📋 Copying files..."
cp -r . "$INSTALL_DIR/" 2>/dev/null || true
# Install Python dependencies
echo ""
echo "🐍 Installing Python dependencies..."
python3 -m pip install -q -r "$INSTALL_DIR/requirements.txt"
# Create default config if not exists
if [ ! -f "$CONFIG_DIR/config.json" ]; then
echo ""
echo "⚙️ Creating default configuration..."
cat > "$CONFIG_DIR/config.json" << 'EOF'
# Ensure data directory exists with proper files
if [ ! -f "$DATA_DIR/config.json" ]; then
echo "⚙️ Creating default config..."
cat > "$DATA_DIR/config.json" << 'EOF'
{
"host": "",
"username": "root",
"password": "",
"port": 623,
"ipmi_host": "",
"ipmi_username": "",
"ipmi_password": "",
"ipmi_port": 623,
"http_sensor_enabled": false,
"http_sensor_url": "",
"http_sensor_timeout": 10,
"enabled": false,
"interval": 10,
"poll_interval": 10,
"min_speed": 10,
"max_speed": 100,
"fan_curve": [
{"temp": 30, "speed": 15},
{"temp": 40, "speed": 25},
{"temp": 50, "speed": 40},
{"temp": 60, "speed": 60},
{"temp": 70, "speed": 80},
{"temp": 80, "speed": 100}
],
"panic_temp": 85,
"panic_speed": 100
"panic_speed": 100,
"panic_on_no_data": true,
"no_data_timeout": 60,
"primary_sensor": "cpu",
"sensor_preference": "auto",
"fans": {},
"fan_groups": {},
"fan_curves": {
"Balanced": {
"points": [
{"temp": 30, "speed": 10},
{"temp": 35, "speed": 12},
{"temp": 40, "speed": 15},
{"temp": 45, "speed": 20},
{"temp": 50, "speed": 30},
{"temp": 55, "speed": 40},
{"temp": 60, "speed": 55},
{"temp": 65, "speed": 70},
{"temp": 70, "speed": 85},
{"temp": 75, "speed": 95},
{"temp": 80, "speed": 100}
],
"sensor_source": "cpu",
"applies_to": "all"
},
"Silent": {
"points": [
{"temp": 30, "speed": 5},
{"temp": 40, "speed": 10},
{"temp": 50, "speed": 15},
{"temp": 55, "speed": 25},
{"temp": 60, "speed": 35},
{"temp": 65, "speed": 50},
{"temp": 70, "speed": 70},
{"temp": 75, "speed": 85},
{"temp": 80, "speed": 100}
],
"sensor_source": "cpu",
"applies_to": "all"
},
"Performance": {
"points": [
{"temp": 30, "speed": 20},
{"temp": 35, "speed": 25},
{"temp": 40, "speed": 35},
{"temp": 45, "speed": 45},
{"temp": 50, "speed": 55},
{"temp": 55, "speed": 70},
{"temp": 60, "speed": 85},
{"temp": 65, "speed": 95},
{"temp": 70, "speed": 100}
],
"sensor_source": "cpu",
"applies_to": "all"
}
},
"active_curve": "Balanced",
"theme": "dark"
}
EOF
fi
if [ ! -f "$DATA_DIR/users.json" ]; then
echo "👤 Creating users file..."
echo '{"users": {}}' > "$DATA_DIR/users.json"
fi
# Install Python dependencies
echo "🐍 Installing dependencies..."
if [ "$SYSTEM_INSTALL" = true ]; then
pip3 install -q -r "$INSTALL_DIR/requirements.txt" || pip install -q -r "$INSTALL_DIR/requirements.txt"
else
pip3 install --user -q -r "$INSTALL_DIR/requirements.txt" 2>/dev/null || true
fi
# Install ipmitool if not present
if ! command -v ipmitool &> /dev/null; then
echo "📦 Installing ipmitool..."
if [ "$SYSTEM_INSTALL" = true ]; then
apt-get update -qq && apt-get install -y -qq ipmitool
else
echo "⚠️ Please install ipmitool manually: sudo apt-get install ipmitool"
fi
else
echo "✓ ipmitool already installed"
fi
# Create systemd service (system-wide only)
if [ "$INSTALL_SYSTEM" = true ]; then
echo ""
echo "🔧 Creating systemd service..."
cat > /etc/systemd/system/ipmi-fan-controller.service << EOF
if [ "$SYSTEM_INSTALL" = true ]; then
echo "🔧 Creating systemd service..."
cat > "/etc/systemd/system/$SERVICE_NAME.service" << EOF
[Unit]
Description=IPMI Fan Controller v2
Description=IPMI Controller
After=network.target
[Service]
Type=simple
User=root
User=$USER
WorkingDirectory=$INSTALL_DIR
Environment="CONFIG_PATH=$CONFIG_DIR/config.json"
Environment="PYTHONUNBUFFERED=1"
Environment="DATA_DIR=$DATA_DIR"
ExecStart=/usr/bin/python3 $INSTALL_DIR/web_server.py
ExecStop=/usr/bin/python3 -c "import requests; requests.post('http://localhost:8000/api/shutdown')"
Restart=on-failure
ExecStop=/bin/kill -TERM \$MAINPID
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable ipmi-fan-controller.service
echo ""
echo "✅ Installation complete!"
echo ""
echo "Next steps:"
echo " 1. Edit config: sudo nano $CONFIG_DIR/config.json"
echo " 2. Start service: sudo systemctl start ipmi-fan-controller"
echo " 3. View status: sudo systemctl status ipmi-fan-controller"
echo " 4. Open web UI: http://$(hostname -I | awk '{print $1}'):8000"
echo ""
echo "Or test from CLI:"
echo " python3 $INSTALL_DIR/fan_controller.py <host> <user> <pass>"
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
# Set proper ownership
chown -R "$USER:$USER" "$INSTALL_DIR"
echo ""
echo "✅ Installation complete!"
echo ""
echo "Start the service:"
echo " sudo systemctl start $SERVICE_NAME"
echo ""
echo "Check status:"
echo " sudo systemctl status $SERVICE_NAME"
echo ""
echo "View logs:"
echo " sudo journalctl -u $SERVICE_NAME -f"
echo ""
echo "Access: http://$(hostname -I | awk '{print $1}'):8000"
else
echo ""
echo "✅ User installation complete!"
echo ""
echo "Next steps:"
echo " 1. Edit config: nano $CONFIG_DIR/config.json"
echo " 2. Start manually:"
echo " CONFIG_PATH=$CONFIG_DIR/config.json python3 $INSTALL_DIR/web_server.py"
echo " 3. Open web UI: http://localhost:8000"
echo ""
echo "Or test from CLI:"
echo " python3 $INSTALL_DIR/fan_controller.py <host> <user> <pass>"
# User install - create a simple start script
cat > "$INSTALL_DIR/start.sh" << 'EOF'
#!/bin/bash
cd "$(dirname "$0")"
export DATA_DIR="./data"
export PYTHONUNBUFFERED=1
echo "Starting IPMI Controller..."
echo "Data directory: $DATA_DIR"
python3 web_server.py
EOF
chmod +x "$INSTALL_DIR/start.sh"
echo ""
echo "✅ User installation complete!"
echo ""
echo "Start manually:"
echo " cd $INSTALL_DIR && ./start.sh"
echo ""
echo "Or create a systemd service manually:"
echo " nano ~/.config/systemd/user/ipmi-controller.service"
echo ""
echo "Access: http://localhost:8000"
fi
echo ""
echo "📖 Configuration file: $CONFIG_DIR/config.json"
echo "📁 Your settings are stored in: $DATA_DIR"
echo " - config.json: All configuration"
echo " - users.json: User accounts"
echo ""
echo "💾 These files persist across restarts and updates!"

0
setup-sensors-server.sh Normal file → Executable file
View File