diff --git a/scripts/wk/hw/diags.py b/scripts/wk/hw/diags.py index daf4a7d5..f04ac04a 100644 --- a/scripts/wk/hw/diags.py +++ b/scripts/wk/hw/diags.py @@ -56,6 +56,7 @@ IO_SIZE_SKIP_NAME = ( TEST_GROUPS = { # Also used to build the menu options ## NOTE: This needs to be above MENU_SETS + 'System Info': 'system_info', 'CPU & Cooling': 'cpu_stress_tests', 'Disk Attributes': 'disk_attribute_check', 'Disk Self-Test': 'disk_self_test', @@ -385,6 +386,7 @@ def build_menu(cli_mode=False, quick_mode=False) -> std.Menu: # Skip CPU tests for TestStations if os.path.exists(cfg.hw.TESTSTATION_FILE): menu.options['CPU & Cooling']['Selected'] = False + menu.options['System Info']['Selected'] = False # Add CLI actions if necessary if cli_mode or 'DISPLAY' not in os.environ: @@ -885,6 +887,18 @@ def main() -> None: state.update_top_pane('Main Menu') +def post_system_info(state) -> None: + """Post system info to osTicket.""" + report = state.system.generate_full_report() + if state.disks: + report.append('\n[Disks]') + for disk in state.disks: + report.append(f'... {disk.description}') + + # Post to osTicket + state.ost.post_response('\n'.join(report)) + + def print_countdown(proc, seconds) -> None: """Print countdown to screen while proc is alive.""" seconds = int(seconds) @@ -939,13 +953,17 @@ def run_diags(state, menu, quick_mode=False, test_mode=False) -> None: # Just return if no tests were selected if not state.test_groups: - std.print_warning('No tests selected?') - std.pause() - return + if not menu.options['System Info']['Selected']: + std.print_warning('No tests selected?') + std.pause() + return # osTicket _init_osticket() + # Post system info + post_system_info(state) + # Run tests for group in state.test_groups: @@ -1017,7 +1035,7 @@ def show_results(state) -> None: ] if cpu_tests_enabled: std.print_success('CPU:') - std.print_report(state.system.generate_report()) + std.print_report(state.system.generate_cpu_ram_report()) std.print_standard(' ') # Drop Disk Utilization reports (only needed for OST) diff --git a/scripts/wk/hw/system.py b/scripts/wk/hw/system.py index d0f4fc02..5dd3c403 100644 --- a/scripts/wk/hw/system.py +++ b/scripts/wk/hw/system.py @@ -2,6 +2,7 @@ # vim: sts=2 sw=2 ts=2 import logging +import os import plistlib import re @@ -38,7 +39,7 @@ class System: self.set_cpu_description() self.get_ram_details() - def generate_report(self) -> list[str]: + def generate_cpu_ram_report(self) -> list[str]: """Generate CPU & RAM report, returns list.""" report = [] report.append(color_string('Device', 'BLUE')) @@ -54,6 +55,38 @@ class System: return report + def generate_full_report(self) -> list[str]: + """Generate full report, returns list.""" + report = ['[System]'] + report.extend([f'... {line}' for line in self.get_system_info()]) + report.append('\n[Motherboard]') + report.extend([f'... {line}' for line in self.get_mobo_info()]) + report.append('\n[BIOS]') + report.extend([f'... {line}' for line in self.get_bios_info()]) + report.append('\n[CPU]') + report.append(f'... {self.cpu_description}') + report.append('\n[RAM]') + report.append(f'... {self.ram_total} ({", ".join(self.ram_dimms)})') + report.append('\n[GPU]') + report.extend([f'... {line}' for line in self.get_gpu_info()]) + return report + + def get_bios_info(self) -> list[str]: + """Get BIOS details, returns list.""" + report = [] + + # Bail early + if PLATFORM != 'Linux': + # Only Linux is supported ATM + return report + + # Get details + report.append(f'Version: {get_dmi_info_linux("bios_version")}') + report.append(f'Released: {get_dmi_info_linux("bios_date")}') + + # Done + return report + def get_cpu_details(self) -> None: """Get CPU details using OS specific methods.""" cmd = ['lscpu', '--json'] @@ -73,6 +106,74 @@ class System: continue self.raw_details[_field] = _data + def get_gpu_info(self) -> list[str]: + """Get GPU details, returns list.""" + report = [] + + # Bail early + if PLATFORM != 'Linux': + # Only Linux is supported ATM + return report + + # Get PCI details + proc = run_program(['lspci']) + for line in proc.stdout.splitlines(): + if 'VGA' not in line: + continue + line = re.sub('^.*:', '', line) + line = re.sub('Integrated Graphics Controller.*', 'iGPU', line) + line = line.replace('Advanced Micro Devices, Inc.', 'AMD') + line = line.replace('Intel Corporation', 'Intel') + line = line.replace('Generation Core Processor Family', 'Gen') + report.append(f'{line.strip()}') + + # Get GLX info + if 'DISPLAY' in os.environ or 'WAYLAND_DISPLAY' in os.environ: + proc = run_program(['glxinfo']) + for line in proc.stdout.splitlines(): + if 'OpenGL renderer' in line: + line = re.sub('^.*:', '', line) + report.append(line.strip()) + break + + # Done + return report + + def get_mobo_info(self) -> list[str]: + """Get motherboard details, returns list.""" + report = [] + + # Bail early + if PLATFORM != 'Linux': + # Only Linux is supported ATM + return report + + # Get details + report.append(f'Vendor: {get_dmi_info_linux("board_vendor")}') + report.append(f'Name: {get_dmi_info_linux("board_name")}') + report.append(f'Version: {get_dmi_info_linux("board_version")}') + report.append(f'Serial: {get_dmi_info_linux("board_serial")}') + + # Done + return report + + def get_system_info(self) -> list[str]: + """Get system details, returns list.""" + report = [] + + # Bail early + if PLATFORM != 'Linux': + # Only Linux is supported ATM + return report + + # Get details + report.append(f'Vendor: {get_dmi_info_linux("sys_vendor")}') + report.append(f'Name: {get_dmi_info_linux("product_name")}') + report.append(f'Serial: {get_dmi_info_linux("product_serial")}') + + # Done + return report + def get_ram_details(self) -> None: """Get RAM details using OS specific methods.""" if PLATFORM == 'Darwin': @@ -110,11 +211,22 @@ class System: self.cpu_description = re.sub(r'\s+', ' ', proc.stdout.strip()) +def get_dmi_info_linux(value) -> str: + """Get DMI info, returns str.""" + dmi_path = '/sys/devices/virtual/dmi/id' + cmd = ['sudo', 'cat', f'{dmi_path}/{value}'] + proc = run_program(cmd, check=False) + if proc.returncode: + return '[???]' + return proc.stdout.strip() + + def get_ram_list_linux() -> list[list]: """Get RAM list using dmidecode.""" cmd = ['sudo', 'dmidecode', '--type', 'memory'] dimm_list = [] manufacturer = 'Unknown' + part_number = 'Unknown' size = 0 # Get DMI data @@ -125,8 +237,12 @@ def get_ram_list_linux() -> list[list]: for line in dmi_data: line = line.strip() if line == 'Memory Device': + # Add to list + if size and (manufacturer or part_number): + dimm_list.append([size, manufacturer, part_number]) # Reset vars manufacturer = 'Unknown' + part_number = 'Unknown' size = 0 elif line.startswith('Size:'): size = line.replace('Size: ', '') @@ -137,7 +253,16 @@ def get_ram_list_linux() -> list[list]: size = 0 elif line.startswith('Manufacturer:'): manufacturer = line.replace('Manufacturer: ', '') - dimm_list.append([size, manufacturer]) + elif line.startswith('Part Number: '): + part_number = line.replace('Part Number: ', '') + if size and (manufacturer or part_number): + dimm_list.append([size, manufacturer, part_number]) + + # Cleanup list + dimm_list = [ + [dimm[0], dimm[1] if dimm[1] != 'Unknown' else dimm[2]] + for dimm in dimm_list + ] # Save details return dimm_list diff --git a/setup/linux/packages/base b/setup/linux/packages/base index ebf6dfcb..e40bb93f 100644 --- a/setup/linux/packages/base +++ b/setup/linux/packages/base @@ -50,6 +50,7 @@ mdadm mediainfo memtest86+ memtest86-efi +mesa-utils mkinitcpio mkinitcpio-archiso mprime