From 5a2381d9ddbf4c461e1c802f7fcf3926432f1b0a Mon Sep 17 00:00:00 2001 From: MacRimi Date: Tue, 7 Oct 2025 17:43:12 +0200 Subject: [PATCH] Update flask_server.py --- AppImage/scripts/flask_server.py | 211 +++++++++++++------------------ 1 file changed, 85 insertions(+), 126 deletions(-) diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 774c808..d06fb10 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -319,14 +319,6 @@ def get_system_info(): except Exception as e: print(f"Note: pveversion not available: {e}") - kernel_version = None - try: - result = subprocess.run(['uname', '-r'], capture_output=True, text=True, timeout=5) - if result.returncode == 0: - kernel_version = result.stdout.strip() - except Exception as e: - print(f"Note: uname not available: {e}") - cpu_cores = psutil.cpu_count(logical=False) # Physical cores only available_updates = 0 @@ -1572,6 +1564,63 @@ def identify_fan(fan_name, adapter): return fan_name +def get_gpu_info(): + """Get GPU information using various tools (nvidia-smi, intel_gpu_top, radeontop)""" + gpus = [] + try: + # Get basic GPU info from lspci first + result = subprocess.run(['lspci'], capture_output=True, text=True, timeout=5) + if result.returncode == 0: + for line in result.stdout.split('\n'): + if any(keyword in line for keyword in ['VGA compatible controller', '3D controller', 'Display controller']): + parts = line.split(':', 2) + if len(parts) >= 3: + gpu_name = parts[2].strip() + slot = parts[0] + + vendor = 'Unknown' + if 'NVIDIA' in gpu_name or 'nVidia' in gpu_name: + vendor = 'NVIDIA' + elif 'AMD' in gpu_name or 'ATI' in gpu_name or 'Radeon' in gpu_name: + vendor = 'AMD' + elif 'Intel' in gpu_name: + vendor = 'Intel' + + gpu_data = { + 'name': gpu_name, + 'vendor': vendor, + 'slot': slot + } + + # Attempt to get detailed info based on vendor + if vendor == 'NVIDIA': + detailed_info = get_detailed_gpu_info(gpu_data) + gpu_data.update(detailed_info) + elif vendor == 'Intel': + detailed_info = get_detailed_gpu_info(gpu_data) + gpu_data.update(detailed_info) + elif vendor == 'AMD': + # Placeholder for AMD monitoring if radeontop is available and integrated + try: + check_result = subprocess.run(['which', 'radeontop'], capture_output=True, timeout=1) + if check_result.returncode == 0: + gpu_data['has_monitoring_tool'] = True + else: + gpu_data['has_monitoring_tool'] = False + except Exception as e: + print(f"[v0] Error checking for radeontop: {e}") + gpu_data['has_monitoring_tool'] = False + + gpus.append(gpu_data) + print(f"[v0] Detected GPU: {gpu_name} ({vendor}) in slot {slot}") + print(f"[v0] Total GPUs detected: {len(gpus)}") + except Exception as e: + print(f"[v0] Error getting GPU info: {e}") + import traceback + traceback.print_exc() + + return gpus + def get_detailed_gpu_info(gpu): """Get detailed GPU information using nvidia-smi, intel_gpu_top, or radeontop""" detailed_info = {} @@ -1588,7 +1637,7 @@ def get_detailed_gpu_info(gpu): else: print(f"[v0] intel_gpu_top found for Intel GPU") - gpu_device = '/dev/dri/card0' + gpu_device = '/dev/dri/card0' # Assume first DRI card is the target if not os.path.exists(gpu_device): print(f"[v0] GPU device {gpu_device} not found - marking tool as unavailable") detailed_info['has_monitoring_tool'] = False @@ -1931,114 +1980,6 @@ def get_network_hardware_info(pci_slot): return net_info -def get_gpu_info(): - """Get GPU information from lspci and enrich with temperature/fan data from sensors""" - gpus = [] - - try: - result = subprocess.run(['lspci'], capture_output=True, text=True, timeout=5) - if result.returncode == 0: - for line in result.stdout.split('\n'): - # Match VGA, 3D, Display controllers - if any(keyword in line for keyword in ['VGA compatible controller', '3D controller', 'Display controller']): - # Line format: "00:02.0 VGA compatible controller: Intel Corporation ..." - # Split on first space to get slot, then split remaining on first colon to separate class from name - parts = line.split(' ', 1) - if len(parts) >= 2: - slot = parts[0].strip() # Now captures full slot like "00:02.0" - remaining = parts[1] - - # Split remaining part to get GPU name (after the class description) - if ':' in remaining: - class_and_name = remaining.split(':', 1) - gpu_name = class_and_name[1].strip() if len(class_and_name) > 1 else remaining.strip() - else: - gpu_name = remaining.strip() - - # Determine vendor - vendor = 'Unknown' - if 'NVIDIA' in gpu_name or 'nVidia' in gpu_name: - vendor = 'NVIDIA' - elif 'AMD' in gpu_name or 'ATI' in gpu_name or 'Radeon' in gpu_name: - vendor = 'AMD' - elif 'Intel' in gpu_name: - vendor = 'Intel' - - gpu = { - 'slot': slot, - 'name': gpu_name, - 'vendor': vendor, - 'type': 'Discrete' if vendor in ['NVIDIA', 'AMD'] else 'Integrated' - } - - pci_info = get_pci_device_info(slot) - if pci_info: - gpu['pci_class'] = pci_info.get('class', '') - gpu['pci_driver'] = pci_info.get('driver', '') - gpu['pci_kernel_module'] = pci_info.get('kernel_module', '') - - detailed_info = get_detailed_gpu_info(gpu) - gpu.update(detailed_info) - - gpus.append(gpu) - print(f"[v0] Found GPU: {gpu_name} ({vendor}) at slot {slot}") - - except Exception as e: - print(f"[v0] Error detecting GPUs from lspci: {e}") - - try: - result = subprocess.run(['sensors'], capture_output=True, text=True, timeout=5) - if result.returncode == 0: - current_adapter = None - - for line in result.stdout.split('\n'): - line = line.strip() - if not line: - continue - - # Detect adapter line - if line.startswith('Adapter:'): - current_adapter = line.replace('Adapter:', '').strip() - continue - - # Look for GPU-related sensors (nouveau, amdgpu, radeon, i915) - if ':' in line and not line.startswith(' '): - parts = line.split(':', 1) - sensor_name = parts[0].strip() - value_part = parts[1].strip() - - # Check if this is a GPU sensor - gpu_sensor_keywords = ['nouveau', 'amdgpu', 'radeon', 'i915'] - is_gpu_sensor = any(keyword in current_adapter.lower() if current_adapter else False for keyword in gpu_sensor_keywords) - - if is_gpu_sensor: - # Try to match this sensor to a GPU - for gpu in gpus: - # Match nouveau to NVIDIA, amdgpu/radeon to AMD, i915 to Intel - if (('nouveau' in current_adapter.lower() and gpu['vendor'] == 'NVIDIA') or - (('amdgpu' in current_adapter.lower() or 'radeon' in current_adapter.lower()) and gpu['vendor'] == 'AMD') or - ('i915' in current_adapter.lower() and gpu['vendor'] == 'Intel')): - - # Parse temperature (only if not already set by nvidia-smi) - if 'temperature' not in gpu or gpu['temperature'] is None: - if '°C' in value_part or 'C' in value_part: - temp_match = re.search(r'([+-]?[\d.]+)\s*°?C', value_part) - if temp_match: - gpu['temperature'] = float(temp_match.group(1)) - print(f"[v0] GPU {gpu['name']}: Temperature = {gpu['temperature']}°C") - - # Parse fan speed - elif 'RPM' in value_part: - rpm_match = re.search(r'([\d.]+)\s*RPM', value_part) - if rpm_match: - gpu['fan_speed'] = int(float(rpm_match.group(1))) - gpu['fan_unit'] = 'RPM' - print(f"[v0] GPU {gpu['name']}: Fan = {gpu['fan_speed']} RPM") - except Exception as e: - print(f"[v0] Error enriching GPU data from sensors: {e}") - - return gpus - def get_disk_hardware_info(disk_name): """Get detailed hardware information for a disk""" disk_info = {} @@ -2688,10 +2629,10 @@ def api_hardware(): # Format data for frontend formatted_data = { 'cpu': hardware_info.get('cpu', {}), - 'motherboard': hardware_info.get('motherboard', {}), + 'motherboard': hardware_data.get('motherboard', {}), 'memory_modules': hardware_info.get('memory_modules', []), 'storage_devices': hardware_info.get('storage_devices', []), - 'pci_devices': hardware_info.get('pci_devices', []), + 'pci_devices': hardware_info.get('pci_devices', []), # No filtering - include all PCI devices including GPUs 'temperatures': hardware_info.get('sensors', {}).get('temperatures', []), 'fans': all_fans, # Return combined fans (sensors + IPMI) 'power_supplies': hardware_info.get('ipmi_power', {}).get('power_supplies', []), @@ -2715,29 +2656,47 @@ def api_hardware(): def api_gpu_realtime(slot): """Get real-time GPU monitoring data for a specific GPU""" try: - # Find the GPU by slot - hardware_info = get_hardware_info() - gpus = hardware_info.get('gpus', []) + print(f"[v0] Getting real-time data for GPU slot {slot}") + gpus = get_gpu_info() gpu = None for g in gpus: - if g.get('slot') == slot or g.get('slot', '').startswith(slot): + if g.get('slot') == slot: gpu = g break if not gpu: + print(f"[v0] GPU not found for slot {slot}") return jsonify({'error': 'GPU not found'}), 404 - # Get real-time monitoring data - realtime_data = get_detailed_gpu_info(gpu) + # Extract only the monitoring-related fields + realtime_data = { + 'has_monitoring_tool': gpu.get('has_monitoring_tool', False) + } + + # Add monitoring data if available + monitoring_fields = [ + 'temperature', 'power_draw', 'power_limit', 'utilization_gpu', + 'utilization_memory', 'clock_graphics', 'clock_memory', 'clock_max', + 'memory_total', 'memory_used', 'memory_free', 'fan_speed', 'fan_unit', + 'driver_version', 'pcie_gen', 'pcie_width', 'power_state', 'irq_rate', + 'engine_render', 'engine_blitter', 'engine_video', 'engine_video_enhance', + 'processes' + ] + + for field in monitoring_fields: + if field in gpu and gpu[field] is not None: + realtime_data[field] = gpu[field] print(f"[v0] /api/gpu/{slot}/realtime returning data") print(f"[v0] - Vendor: {gpu.get('vendor')}") - print(f"[v0] - Realtime data: {realtime_data}") + print(f"[v0] - Has monitoring tool: {realtime_data.get('has_monitoring_tool')}") return jsonify(realtime_data) except Exception as e: print(f"[v0] Error getting real-time GPU data: {e}") + import traceback + traceback.print_exc() return jsonify({'error': str(e)}), 500 @app.route('/api/vms/', methods=['GET'])