Added pi optimizations
This commit is contained in:
@@ -2,18 +2,21 @@
|
|||||||
# Copy this file to .env and customize the values
|
# Copy this file to .env and customize the values
|
||||||
|
|
||||||
# Server Configuration
|
# Server Configuration
|
||||||
PORT=5000
|
PORT=5005
|
||||||
HOST=0.0.0.0
|
HOST=0.0.0.0
|
||||||
DEBUG=True
|
DEBUG=False
|
||||||
|
|
||||||
# Portainer Integration (Optional)
|
# Portainer Integration (Optional)
|
||||||
PORTAINER_URL=https://port.liveaodh.com
|
PORTAINER_URL=https://port.liveaodh.com
|
||||||
PORTAINER_USERNAME=aodhan
|
PORTAINER_USERNAME=aodhan
|
||||||
PORTAINER_PASSWORD=8yFdXkx274aSRX
|
PORTAINER_PASSWORD=8yFdXkx274aSRX
|
||||||
|
|
||||||
|
# Raspberry Pi Optimizations
|
||||||
|
PI_OPTIMIZATIONS=true
|
||||||
|
|
||||||
# Docker Configuration
|
# Docker Configuration
|
||||||
DOCKER_HOST=unix:///var/run/docker.sock
|
DOCKER_HOST=unix:///var/run/docker.sock
|
||||||
|
|
||||||
# Application Settings
|
# Application Settings
|
||||||
REFRESH_INTERVAL=30
|
REFRESH_INTERVAL=60
|
||||||
MAX_CONTAINERS=50
|
MAX_CONTAINERS=50
|
||||||
|
|||||||
151
api.py
151
api.py
@@ -136,27 +136,27 @@ class DockerService:
|
|||||||
return networks
|
return networks
|
||||||
|
|
||||||
def get_system_info(self):
|
def get_system_info(self):
|
||||||
"""Get system information"""
|
"""Get system information optimized for Raspberry Pi"""
|
||||||
try:
|
try:
|
||||||
# CPU usage
|
# CPU usage - reduced interval for Pi
|
||||||
cpu_percent = psutil.cpu_percent(interval=1)
|
cpu_percent = psutil.cpu_percent(interval=0.5)
|
||||||
|
|
||||||
# Memory usage
|
# Memory usage
|
||||||
memory = psutil.virtual_memory()
|
memory = psutil.virtual_memory()
|
||||||
|
|
||||||
# Disk usage - try multiple paths for different systems
|
# Disk usage - optimized for Pi filesystem
|
||||||
disk_info = None
|
disk_info = None
|
||||||
|
|
||||||
|
# Try common Pi filesystem locations
|
||||||
disk_paths = ['/']
|
disk_paths = ['/']
|
||||||
|
|
||||||
# Add common mount points for different Linux distributions
|
# Check for common Pi mount points
|
||||||
if os.path.exists('/home'):
|
pi_mounts = ['/boot', '/home', '/opt', '/var']
|
||||||
disk_paths.append('/home')
|
for mount in pi_mounts:
|
||||||
if os.path.exists('/var'):
|
if os.path.exists(mount):
|
||||||
disk_paths.append('/var')
|
disk_paths.append(mount)
|
||||||
if os.path.exists('/opt'):
|
|
||||||
disk_paths.append('/opt')
|
|
||||||
|
|
||||||
# Try each path until we find one that works
|
# Try to get SD card usage (rootfs)
|
||||||
for path in disk_paths:
|
for path in disk_paths:
|
||||||
try:
|
try:
|
||||||
disk_info = psutil.disk_usage(path)
|
disk_info = psutil.disk_usage(path)
|
||||||
@@ -164,86 +164,107 @@ class DockerService:
|
|||||||
except (OSError, IOError, PermissionError):
|
except (OSError, IOError, PermissionError):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Fallback to root directory
|
# Fallback to root
|
||||||
if disk_info is None:
|
if disk_info is None:
|
||||||
try:
|
try:
|
||||||
disk_info = psutil.disk_usage('/')
|
disk_info = psutil.disk_usage('/')
|
||||||
except (OSError, IOError) as e:
|
except (OSError, IOError) as e:
|
||||||
logging.error(f"Cannot get disk usage: {e}")
|
logging.warning(f"Cannot get disk usage on Pi: {e}")
|
||||||
# Return zero values as fallback
|
|
||||||
disk_info = type('DiskInfo', (), {
|
disk_info = type('DiskInfo', (), {
|
||||||
'total': 0,
|
'total': 0,
|
||||||
'used': 0,
|
'used': 0,
|
||||||
'free': 0
|
'free': 0
|
||||||
})()
|
})()
|
||||||
|
|
||||||
# Get load average (works on both Debian and Fedora)
|
# ARM-specific optimizations
|
||||||
try:
|
try:
|
||||||
import os
|
# Check if we're on ARM (Pi)
|
||||||
load_avg = os.getloadavg()
|
with open('/proc/cpuinfo', 'r') as f:
|
||||||
except (AttributeError, OSError):
|
cpu_info = f.read()
|
||||||
load_avg = (0.0, 0.0, 0.0)
|
is_arm = 'ARM' in cpu_info or 'arm' in cpu_info
|
||||||
|
except:
|
||||||
|
is_arm = False
|
||||||
|
|
||||||
# Get swap memory
|
# Get temperature for Pi
|
||||||
swap = psutil.swap_memory()
|
cpu_temp = None
|
||||||
|
try:
|
||||||
|
# Try Pi-specific temperature reading
|
||||||
|
temp_paths = [
|
||||||
|
'/sys/class/thermal/thermal_zone0/temp',
|
||||||
|
'/sys/class/hwmon/hwmon0/temp1_input',
|
||||||
|
'/sys/class/thermal/thermal_zone1/temp'
|
||||||
|
]
|
||||||
|
for temp_path in temp_paths:
|
||||||
|
try:
|
||||||
|
with open(temp_path, 'r') as f:
|
||||||
|
temp = int(f.read().strip())
|
||||||
|
if temp > 1000: # Convert millidegrees to degrees
|
||||||
|
temp = temp / 1000.0
|
||||||
|
cpu_temp = round(temp, 1)
|
||||||
|
break
|
||||||
|
except (IOError, OSError):
|
||||||
|
continue
|
||||||
|
except:
|
||||||
|
cpu_temp = None
|
||||||
|
|
||||||
# Format disk usage for display
|
# Memory optimization for Pi
|
||||||
def format_bytes(bytes_value):
|
def format_bytes_pi(bytes_value):
|
||||||
"""Format bytes to human readable format"""
|
"""Format bytes optimized for Pi display"""
|
||||||
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
if bytes_value < 1024:
|
||||||
if bytes_value < 1024.0:
|
return f"{bytes_value} B"
|
||||||
return f"{bytes_value:.1f} {unit}"
|
elif bytes_value < 1024 * 1024:
|
||||||
bytes_value /= 1024.0
|
return f"{bytes_value / 1024:.0f} KB"
|
||||||
return f"{bytes_value:.1f} PB"
|
elif bytes_value < 1024 * 1024 * 1024:
|
||||||
|
return f"{bytes_value / (1024 * 1024):.0f} MB"
|
||||||
|
else:
|
||||||
|
return f"{bytes_value / (1024 * 1024 * 1024):.1f} GB"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'cpu': {
|
'cpu': {
|
||||||
'percent': cpu_percent,
|
'percent': cpu_percent,
|
||||||
'cores': psutil.cpu_count(),
|
'cores': psutil.cpu_count(),
|
||||||
'load_avg': load_avg
|
'is_arm': is_arm,
|
||||||
|
'temperature': cpu_temp
|
||||||
},
|
},
|
||||||
'memory': {
|
'memory': {
|
||||||
'total': memory.total,
|
'total': memory.total,
|
||||||
'available': memory.available,
|
'available': memory.available,
|
||||||
'used': memory.used,
|
'used': memory.used,
|
||||||
'percent': memory.percent,
|
'percent': memory.percent,
|
||||||
'formatted_total': format_bytes(memory.total),
|
'formatted_total': format_bytes_pi(memory.total),
|
||||||
'formatted_used': format_bytes(memory.used),
|
'formatted_used': format_bytes_pi(memory.used),
|
||||||
'formatted_available': format_bytes(memory.available)
|
'formatted_available': format_bytes_pi(memory.available)
|
||||||
},
|
|
||||||
'swap': {
|
|
||||||
'total': swap.total,
|
|
||||||
'used': swap.used,
|
|
||||||
'percent': swap.percent
|
|
||||||
},
|
},
|
||||||
'disk': {
|
'disk': {
|
||||||
'total': disk_info.total,
|
'total': disk_info.total,
|
||||||
'used': disk_info.used,
|
'used': disk_info.used,
|
||||||
'free': disk_info.free,
|
'free': disk_info.free,
|
||||||
'percent': (disk_info.used / disk_info.total) * 100 if disk_info.total > 0 else 0,
|
'percent': round((disk_info.used / disk_info.total) * 100, 1) if disk_info.total > 0 else 0,
|
||||||
'formatted_total': format_bytes(disk_info.total),
|
'formatted_total': format_bytes_pi(disk_info.total),
|
||||||
'formatted_used': format_bytes(disk_info.used),
|
'formatted_used': format_bytes_pi(disk_info.used),
|
||||||
'formatted_free': format_bytes(disk_info.free)
|
'formatted_free': format_bytes_pi(disk_info.free)
|
||||||
},
|
},
|
||||||
'uptime': self._get_system_uptime(),
|
'uptime': self._get_system_uptime(),
|
||||||
'platform': {
|
'platform': {
|
||||||
'system': platform.system(),
|
'system': platform.system(),
|
||||||
'release': platform.release(),
|
'release': platform.release(),
|
||||||
'machine': platform.machine()
|
'machine': platform.machine(),
|
||||||
|
'is_raspberry_pi': self._is_raspberry_pi()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error getting system info: {e}")
|
logging.warning(f"System info error on Pi: {e}")
|
||||||
return {
|
return {
|
||||||
'error': str(e),
|
'error': str(e),
|
||||||
'cpu': {'percent': 0, 'cores': 1},
|
'cpu': {'percent': 0, 'cores': 1, 'is_arm': True},
|
||||||
'memory': {'total': 0, 'available': 0, 'used': 0, 'percent': 0},
|
'memory': {'total': 0, 'available': 0, 'used': 0, 'percent': 0},
|
||||||
'disk': {'total': 0, 'used': 0, 'free': 0, 'percent': 0},
|
'disk': {'total': 0, 'used': 0, 'free': 0, 'percent': 0},
|
||||||
'uptime': 'Unknown'
|
'uptime': 'Unknown',
|
||||||
|
'platform': {'system': 'Linux', 'is_raspberry_pi': True}
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_system_uptime(self):
|
def _get_system_uptime(self):
|
||||||
"""Get system uptime"""
|
"""Get system uptime optimized for Pi"""
|
||||||
try:
|
try:
|
||||||
with open('/proc/uptime', 'r') as f:
|
with open('/proc/uptime', 'r') as f:
|
||||||
uptime_seconds = float(f.readline().split()[0])
|
uptime_seconds = float(f.readline().split()[0])
|
||||||
@@ -252,10 +273,24 @@ class DockerService:
|
|||||||
hours = int((uptime_seconds % 86400) // 3600)
|
hours = int((uptime_seconds % 86400) // 3600)
|
||||||
minutes = int((uptime_seconds % 3600) // 60)
|
minutes = int((uptime_seconds % 3600) // 60)
|
||||||
|
|
||||||
return f"{days}d {hours}h {minutes}m"
|
if days > 0:
|
||||||
|
return f"{days}d {hours}h"
|
||||||
|
elif hours > 0:
|
||||||
|
return f"{hours}h {minutes}m"
|
||||||
|
else:
|
||||||
|
return f"{minutes}m"
|
||||||
except Exception:
|
except Exception:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
|
def _is_raspberry_pi(self):
|
||||||
|
"""Check if running on Raspberry Pi"""
|
||||||
|
try:
|
||||||
|
with open('/proc/cpuinfo', 'r') as f:
|
||||||
|
cpuinfo = f.read()
|
||||||
|
return any(x in cpuinfo.lower() for x in ['raspberry', 'bcm', 'broadcom'])
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
def check_service_health(self, url, port=None):
|
def check_service_health(self, url, port=None):
|
||||||
"""Check if a service is responding"""
|
"""Check if a service is responding"""
|
||||||
try:
|
try:
|
||||||
@@ -267,11 +302,14 @@ class DockerService:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Configuration from environment variables
|
# Configuration from environment variables - optimized for Raspberry Pi
|
||||||
PORT = int(os.getenv('PORT', 5000))
|
PORT = int(os.getenv('PORT', 8080)) # Use 8080 as default for Pi
|
||||||
HOST = os.getenv('HOST', '0.0.0.0')
|
HOST = os.getenv('HOST', '0.0.0.0')
|
||||||
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'
|
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'
|
||||||
REFRESH_INTERVAL = int(os.getenv('REFRESH_INTERVAL', 30))
|
REFRESH_INTERVAL = int(os.getenv('REFRESH_INTERVAL', 60)) # Longer refresh for Pi
|
||||||
|
|
||||||
|
# Raspberry Pi specific optimizations
|
||||||
|
PI_OPTIMIZATIONS = os.getenv('PI_OPTIMIZATIONS', 'True').lower() == 'true'
|
||||||
|
|
||||||
# Initialize Docker service
|
# Initialize Docker service
|
||||||
docker_service = DockerService()
|
docker_service = DockerService()
|
||||||
@@ -526,4 +564,13 @@ def restart_container(container_id):
|
|||||||
return jsonify({'success': False, 'error': str(e)}), 500
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
# Raspberry Pi optimizations
|
||||||
|
if PI_OPTIMIZATIONS:
|
||||||
|
# Reduce logging for production
|
||||||
|
logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
||||||
|
logging.getLogger('urllib3').setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
# Use threaded mode for better performance on Pi
|
||||||
|
app.run(host=HOST, port=PORT, debug=DEBUG, threaded=True)
|
||||||
|
else:
|
||||||
app.run(host=HOST, port=PORT, debug=DEBUG)
|
app.run(host=HOST, port=PORT, debug=DEBUG)
|
||||||
|
|||||||
148
setup-pi.sh
Normal file
148
setup-pi.sh
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Raspberry Pi Optimized Setup Script for Docker Dashboard
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🍓 Setting up Docker Dashboard for Raspberry Pi..."
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Detect Raspberry Pi
|
||||||
|
if [[ -f /proc/device-tree/model ]]; then
|
||||||
|
MODEL=$(tr -d '\0' < /proc/device-tree/model)
|
||||||
|
echo -e "${GREEN}Detected: $MODEL${NC}"
|
||||||
|
elif [[ -f /proc/cpuinfo ]] && grep -q "Raspberry Pi" /proc/cpuinfo; then
|
||||||
|
echo -e "${GREEN}Detected: Raspberry Pi${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: Not running on Raspberry Pi, continuing anyway...${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update system packages
|
||||||
|
echo -e "${YELLOW}Updating system packages...${NC}"
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
|
||||||
|
# Install Python3 and pip if not present
|
||||||
|
echo -e "${YELLOW}Installing Python3 and pip...${NC}"
|
||||||
|
sudo apt install -y python3 python3-pip python3-venv curl wget
|
||||||
|
|
||||||
|
# Install Docker if not present
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
echo -e "${YELLOW}Installing Docker...${NC}"
|
||||||
|
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||||
|
sudo sh get-docker.sh
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
rm get-docker.sh
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Docker already installed${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create virtual environment
|
||||||
|
echo -e "${YELLOW}Creating Python virtual environment...${NC}"
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# Install dependencies optimized for Pi
|
||||||
|
echo -e "${YELLOW}Installing Python dependencies...${NC}"
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
# Install packages with Pi optimizations
|
||||||
|
pip install flask==2.3.3
|
||||||
|
pip install flask-cors==4.0.0
|
||||||
|
pip install docker==6.1.3
|
||||||
|
pip install psutil==5.9.5
|
||||||
|
pip install requests==2.31.0
|
||||||
|
pip install python-dotenv==1.0.0
|
||||||
|
|
||||||
|
# Create .env file optimized for Pi
|
||||||
|
echo -e "${YELLOW}Creating Pi-optimized configuration...${NC}"
|
||||||
|
cat > .env << EOF
|
||||||
|
# Raspberry Pi Optimized Configuration
|
||||||
|
PORT=8080
|
||||||
|
HOST=0.0.0.0
|
||||||
|
DEBUG=false
|
||||||
|
REFRESH_INTERVAL=60
|
||||||
|
PI_OPTIMIZATIONS=true
|
||||||
|
|
||||||
|
# Optional: Portainer integration
|
||||||
|
# PORTAINER_URL=http://localhost:9000
|
||||||
|
# PORTAINER_USERNAME=admin
|
||||||
|
# PORTAINER_PASSWORD=yourpassword
|
||||||
|
|
||||||
|
# Docker socket path (Pi specific)
|
||||||
|
DOCKER_HOST=unix:///var/run/docker.sock
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Initialize database
|
||||||
|
echo -e "${YELLOW}Initializing database...${NC}"
|
||||||
|
python3 -c "
|
||||||
|
from database import db
|
||||||
|
db.init_database()
|
||||||
|
print('✅ Database initialized successfully')
|
||||||
|
"
|
||||||
|
|
||||||
|
# Create systemd service for auto-start
|
||||||
|
echo -e "${YELLOW}Creating systemd service...${NC}"
|
||||||
|
sudo tee /etc/systemd/system/docker-dashboard.service > /dev/null << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Docker Dashboard for Raspberry Pi
|
||||||
|
After=network.target docker.service
|
||||||
|
Requires=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=$USER
|
||||||
|
WorkingDirectory=$(pwd)
|
||||||
|
Environment=PATH=$(pwd)/venv/bin
|
||||||
|
ExecStart=$(pwd)/venv/bin/python3 api.py
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Set permissions
|
||||||
|
sudo chmod 644 /etc/systemd/system/docker-dashboard.service
|
||||||
|
|
||||||
|
# Create logs directory
|
||||||
|
mkdir -p logs
|
||||||
|
|
||||||
|
# Create simple start/stop scripts
|
||||||
|
cat > start-pi.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "Starting Docker Dashboard on Raspberry Pi..."
|
||||||
|
sudo systemctl start docker-dashboard
|
||||||
|
sudo systemctl status docker-dashboard --no-pager -l
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > stop-pi.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "Stopping Docker Dashboard..."
|
||||||
|
sudo systemctl stop docker-dashboard
|
||||||
|
sudo systemctl status docker-dashboard --no-pager -l
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x start-pi.sh stop-pi.sh
|
||||||
|
|
||||||
|
# Enable service
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable docker-dashboard
|
||||||
|
|
||||||
|
echo -e "${GREEN}✅ Setup complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Usage:"
|
||||||
|
echo " ./start-pi.sh - Start the dashboard"
|
||||||
|
echo " ./stop-pi.sh - Stop the dashboard"
|
||||||
|
echo " ./setup-pi.sh - Re-run this setup"
|
||||||
|
echo ""
|
||||||
|
echo "Access the dashboard at: http://$(hostname -I | awk '{print $1}'):8080"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}To start immediately:${NC}"
|
||||||
|
echo " sudo systemctl start docker-dashboard"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}To view logs:${NC}"
|
||||||
|
echo " sudo journalctl -u docker-dashboard -f"
|
||||||
Reference in New Issue
Block a user