Added pi optimizations

This commit is contained in:
Aodhan Collins
2025-07-27 02:03:58 +01:00
parent cf761decd8
commit c69b9ff817
3 changed files with 254 additions and 56 deletions

153
api.py
View File

@@ -136,27 +136,27 @@ class DockerService:
return networks
def get_system_info(self):
"""Get system information"""
"""Get system information optimized for Raspberry Pi"""
try:
# CPU usage
cpu_percent = psutil.cpu_percent(interval=1)
# CPU usage - reduced interval for Pi
cpu_percent = psutil.cpu_percent(interval=0.5)
# Memory usage
memory = psutil.virtual_memory()
# Disk usage - try multiple paths for different systems
# Disk usage - optimized for Pi filesystem
disk_info = None
# Try common Pi filesystem locations
disk_paths = ['/']
# Add common mount points for different Linux distributions
if os.path.exists('/home'):
disk_paths.append('/home')
if os.path.exists('/var'):
disk_paths.append('/var')
if os.path.exists('/opt'):
disk_paths.append('/opt')
# Check for common Pi mount points
pi_mounts = ['/boot', '/home', '/opt', '/var']
for mount in pi_mounts:
if os.path.exists(mount):
disk_paths.append(mount)
# Try each path until we find one that works
# Try to get SD card usage (rootfs)
for path in disk_paths:
try:
disk_info = psutil.disk_usage(path)
@@ -164,86 +164,107 @@ class DockerService:
except (OSError, IOError, PermissionError):
continue
# Fallback to root directory
# Fallback to root
if disk_info is None:
try:
disk_info = psutil.disk_usage('/')
except (OSError, IOError) as e:
logging.error(f"Cannot get disk usage: {e}")
# Return zero values as fallback
logging.warning(f"Cannot get disk usage on Pi: {e}")
disk_info = type('DiskInfo', (), {
'total': 0,
'used': 0,
'free': 0
})()
# Get load average (works on both Debian and Fedora)
# ARM-specific optimizations
try:
import os
load_avg = os.getloadavg()
except (AttributeError, OSError):
load_avg = (0.0, 0.0, 0.0)
# Check if we're on ARM (Pi)
with open('/proc/cpuinfo', 'r') as f:
cpu_info = f.read()
is_arm = 'ARM' in cpu_info or 'arm' in cpu_info
except:
is_arm = False
# Get swap memory
swap = psutil.swap_memory()
# Get temperature for Pi
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
def format_bytes(bytes_value):
"""Format bytes to human readable format"""
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes_value < 1024.0:
return f"{bytes_value:.1f} {unit}"
bytes_value /= 1024.0
return f"{bytes_value:.1f} PB"
# Memory optimization for Pi
def format_bytes_pi(bytes_value):
"""Format bytes optimized for Pi display"""
if bytes_value < 1024:
return f"{bytes_value} B"
elif bytes_value < 1024 * 1024:
return f"{bytes_value / 1024:.0f} KB"
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 {
'cpu': {
'percent': cpu_percent,
'cores': psutil.cpu_count(),
'load_avg': load_avg
'is_arm': is_arm,
'temperature': cpu_temp
},
'memory': {
'total': memory.total,
'available': memory.available,
'used': memory.used,
'percent': memory.percent,
'formatted_total': format_bytes(memory.total),
'formatted_used': format_bytes(memory.used),
'formatted_available': format_bytes(memory.available)
},
'swap': {
'total': swap.total,
'used': swap.used,
'percent': swap.percent
'formatted_total': format_bytes_pi(memory.total),
'formatted_used': format_bytes_pi(memory.used),
'formatted_available': format_bytes_pi(memory.available)
},
'disk': {
'total': disk_info.total,
'used': disk_info.used,
'free': disk_info.free,
'percent': (disk_info.used / disk_info.total) * 100 if disk_info.total > 0 else 0,
'formatted_total': format_bytes(disk_info.total),
'formatted_used': format_bytes(disk_info.used),
'formatted_free': format_bytes(disk_info.free)
'percent': round((disk_info.used / disk_info.total) * 100, 1) if disk_info.total > 0 else 0,
'formatted_total': format_bytes_pi(disk_info.total),
'formatted_used': format_bytes_pi(disk_info.used),
'formatted_free': format_bytes_pi(disk_info.free)
},
'uptime': self._get_system_uptime(),
'platform': {
'system': platform.system(),
'release': platform.release(),
'machine': platform.machine()
'machine': platform.machine(),
'is_raspberry_pi': self._is_raspberry_pi()
}
}
except Exception as e:
logging.error(f"Error getting system info: {e}")
logging.warning(f"System info error on Pi: {e}")
return {
'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},
'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):
"""Get system uptime"""
"""Get system uptime optimized for Pi"""
try:
with open('/proc/uptime', 'r') as f:
uptime_seconds = float(f.readline().split()[0])
@@ -252,10 +273,24 @@ class DockerService:
hours = int((uptime_seconds % 86400) // 3600)
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:
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):
"""Check if a service is responding"""
try:
@@ -267,11 +302,14 @@ class DockerService:
except Exception:
return False
# Configuration from environment variables
PORT = int(os.getenv('PORT', 5000))
# Configuration from environment variables - optimized for Raspberry Pi
PORT = int(os.getenv('PORT', 8080)) # Use 8080 as default for Pi
HOST = os.getenv('HOST', '0.0.0.0')
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
docker_service = DockerService()
@@ -526,4 +564,13 @@ def restart_container(container_id):
return jsonify({'success': False, 'error': str(e)}), 500
if __name__ == '__main__':
app.run(host=HOST, port=PORT, debug=DEBUG)
# 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)