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 # 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 ## Features
- 🌡️ **Temperature Monitoring** - IPMI and HTTP (lm-sensors) sensor support - 🌡️ **Dual Sensor Support** - IPMI + HTTP (lm-sensors from Proxmox/host)
- 🌬️ **Fan Control** - Automatic curves, manual control, panic mode - 🌬️ **Smart Fan Control** - Automatic curves, manual control, panic mode
- 👥 **Fan Groups** - Group fans with different curves - 📊 **3 Preset Curves** - Balanced (default), Silent, Performance
- 👥 **Fan Groups** - Organize and control fans individually or in groups
- 🔍 **Fan Identify** - Visual fan identification - 🔍 **Fan Identify** - Visual fan identification
- 🎨 **Dark/Light Mode** - Theme switching - 🎨 **Themes** - Dark and Light mode
- 📊 **Public API** - For external integrations - 📱 **Responsive Web UI** - Works on desktop and mobile
- 🔒 **Secure** - Password protected with JWT auth - 🔌 **Public API** - For external integrations
- 💾 **Persistent Settings** - Survives restarts and updates
## Quick Start ## Quick Start
### Requirements ### Automated Install (Recommended)
- Python 3.10+
- ipmitool
- Linux server (tested on Dell T710)
### Install
```bash ```bash
git clone https://github.com/yourusername/ipmi-controller.git git clone https://github.com/yourusername/ipmi-controller.git
cd ipmi-controller 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 ```bash
# Install dependencies
sudo apt-get install -y ipmitool python3-pip
pip3 install -r requirements.txt
# Run
python3 web_server.py 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 ```bash
docker build -t ipmi-controller . cd ipmi-controller
docker run -d -p 8000:8000 -v ./data:/app/data 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 ## 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 - [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 ## License
MIT 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 #!/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 set -e
echo "🌬️ IPMI Fan Controller v2 - Setup" INSTALL_DIR="${1:-/opt/ipmi-controller}"
echo "==================================" 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 # Check if running as root for system-wide install
if [ "$EUID" -eq 0 ]; then if [ "$EUID" -ne 0 ]; then
INSTALL_SYSTEM=true echo "⚠️ Not running as root. Installing to $HOME/ipmi-controller instead."
INSTALL_DIR="/opt/ipmi-fan-controller" INSTALL_DIR="$HOME/ipmi-controller"
CONFIG_DIR="/etc/ipmi-fan-controller" DATA_DIR="$INSTALL_DIR/data"
SYSTEM_INSTALL=false
else else
INSTALL_SYSTEM=false SYSTEM_INSTALL=true
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 ""
fi 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 # Create directories
echo ""
echo "📁 Creating directories..." echo "📁 Creating directories..."
mkdir -p "$INSTALL_DIR" mkdir -p "$INSTALL_DIR"
mkdir -p "$CONFIG_DIR" mkdir -p "$DATA_DIR"
mkdir -p "$INSTALL_DIR/logs"
# Copy files # Copy files
echo "" echo "📋 Copying files..."
echo "📋 Installing files..." cp -r . "$INSTALL_DIR/" 2>/dev/null || true
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/"
# Install Python dependencies # Ensure data directory exists with proper files
echo "" if [ ! -f "$DATA_DIR/config.json" ]; then
echo "🐍 Installing Python dependencies..." echo "⚙️ Creating default config..."
python3 -m pip install -q -r "$INSTALL_DIR/requirements.txt" cat > "$DATA_DIR/config.json" << 'EOF'
# Create default config if not exists
if [ ! -f "$CONFIG_DIR/config.json" ]; then
echo ""
echo "⚙️ Creating default configuration..."
cat > "$CONFIG_DIR/config.json" << 'EOF'
{ {
"host": "", "ipmi_host": "",
"username": "root", "ipmi_username": "",
"password": "", "ipmi_password": "",
"port": 623, "ipmi_port": 623,
"http_sensor_enabled": false,
"http_sensor_url": "",
"http_sensor_timeout": 10,
"enabled": false, "enabled": false,
"interval": 10, "poll_interval": 10,
"min_speed": 10, "min_speed": 10,
"max_speed": 100, "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_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 EOF
fi 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) # Create systemd service (system-wide only)
if [ "$INSTALL_SYSTEM" = true ]; then if [ "$SYSTEM_INSTALL" = true ]; then
echo "" echo "🔧 Creating systemd service..."
echo "🔧 Creating systemd service..." cat > "/etc/systemd/system/$SERVICE_NAME.service" << EOF
cat > /etc/systemd/system/ipmi-fan-controller.service << EOF
[Unit] [Unit]
Description=IPMI Fan Controller v2 Description=IPMI Controller
After=network.target After=network.target
[Service] [Service]
Type=simple Type=simple
User=root User=$USER
WorkingDirectory=$INSTALL_DIR 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 ExecStart=/usr/bin/python3 $INSTALL_DIR/web_server.py
ExecStop=/usr/bin/python3 -c "import requests; requests.post('http://localhost:8000/api/shutdown')" ExecStop=/bin/kill -TERM \$MAINPID
Restart=on-failure Restart=always
RestartSec=10 RestartSec=10
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
systemctl daemon-reload systemctl daemon-reload
systemctl enable ipmi-fan-controller.service systemctl enable "$SERVICE_NAME"
echo "" # Set proper ownership
echo "✅ Installation complete!" chown -R "$USER:$USER" "$INSTALL_DIR"
echo ""
echo "Next steps:" echo ""
echo " 1. Edit config: sudo nano $CONFIG_DIR/config.json" echo "✅ Installation complete!"
echo " 2. Start service: sudo systemctl start ipmi-fan-controller" echo ""
echo " 3. View status: sudo systemctl status ipmi-fan-controller" echo "Start the service:"
echo " 4. Open web UI: http://$(hostname -I | awk '{print $1}'):8000" echo " sudo systemctl start $SERVICE_NAME"
echo "" echo ""
echo "Or test from CLI:" echo "Check status:"
echo " python3 $INSTALL_DIR/fan_controller.py <host> <user> <pass>" 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 else
# User install - create a simple start script
echo "" cat > "$INSTALL_DIR/start.sh" << 'EOF'
echo "✅ User installation complete!" #!/bin/bash
echo "" cd "$(dirname "$0")"
echo "Next steps:" export DATA_DIR="./data"
echo " 1. Edit config: nano $CONFIG_DIR/config.json" export PYTHONUNBUFFERED=1
echo " 2. Start manually:" echo "Starting IPMI Controller..."
echo " CONFIG_PATH=$CONFIG_DIR/config.json python3 $INSTALL_DIR/web_server.py" echo "Data directory: $DATA_DIR"
echo " 3. Open web UI: http://localhost:8000" python3 web_server.py
echo "" EOF
echo "Or test from CLI:" chmod +x "$INSTALL_DIR/start.sh"
echo " python3 $INSTALL_DIR/fan_controller.py <host> <user> <pass>"
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 fi
echo "" 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