import psutil import shutil from datetime import datetime import platform import subprocess import os class OSSystem(): def __init__(self): self.__system_name = platform.system() # 操作系统 self.__machine_node = str(platform.node()) # 机器名称 @staticmethod def getDateFmtStr(spend_date, spend_date_fmt="%d天%d小时%d分钟%d秒"): # type spend_day = spend_date.days # 已运行的天数 int spend_seconds = spend_date.seconds # 已运行的秒数 int spend_hour = int(spend_seconds / 60 / 60) # 已运行小时 int spend_seconds -= spend_hour * 60 * 60 # 已运行的秒数 int spend_minute = int(spend_seconds / 60) # 已运行的分钟 int spend_seconds -= spend_minute * 60 # 已运行的秒数 int spend_date_str = spend_date_fmt % (spend_day, spend_hour, spend_minute, spend_seconds) return spend_date_str def __byteFormat(self, bytes, suffix="B"): """ Scale bytes to its proper format e.g: 1253656 => '1.20MB' 1253656678 => '1.17GB' """ factor = 1024 for unit in ["", "K", "M", "G", "T", "P"]: if bytes < factor: return f"{bytes:.2f}{unit}{suffix}" bytes /= factor def getOSInfo(self, spend_date_fmt="%d天%d小时%d分钟%d秒", include_gpu=False): # 获取系统cpu比例 start os_cpu_used = psutil.cpu_percent() # os_cpu_physical_core = psutil.cpu_count(logical=False) # 物理核心数量 os_cpu_total_core = psutil.cpu_count(logical=True) # 逻辑核心数量 os_cpu_used_rate = round(os_cpu_used / 100, 3) # 0.125 # 获取系统cpu比例 end # 获取系统内存比例 start os_virtual_mem = psutil.virtual_memory() os_virtual_mem_total = os_virtual_mem.total if os_virtual_mem.total == 0: os_virtual_mem_used_rate = 0 else: os_virtual_mem_used_rate = os_virtual_mem.used / os_virtual_mem.total os_virtual_mem_used_rate = round(os_virtual_mem_used_rate, 3) # 0.635 # 获取系统内存比例 end # 获取系统磁盘比例 start os_disk_total = 0 os_disk_used = 0 os_disk_free = 0 os_disk_partitions = psutil.disk_partitions() for partition in os_disk_partitions: try: partition_usage = psutil.disk_usage(partition.mountpoint) os_disk_total += partition_usage.total os_disk_free += partition_usage.free os_disk_used += partition_usage.used except Exception as e: pass if os_disk_total == 0: os_disk_used_rate = 0 else: os_disk_used_rate = os_disk_used / os_disk_total os_disk_used_rate = round(os_disk_used_rate, 3) # 当前系统磁盘占用比例 # 获取系统磁盘比例 end # 获取系统开机时间 start os_boot_timestamp = int(psutil.boot_time()) # 1651904713.9067075 os_boot_date = datetime.fromtimestamp(os_boot_timestamp) # os_run_date = datetime.now() - os_boot_date # os_run_date_str = self.getDateFmtStr(os_run_date, spend_date_fmt=spend_date_fmt) # 获取系统开机时间 end os_gpus = [] if include_gpu: try: from app.utils.GpuInfo import get_gpu_info os_gpus = get_gpu_info() except Exception: os_gpus = [] os_info = { "machine_node": str(platform.node()), "system_name": self.getSystemName(), "os_cpu_used_rate": os_cpu_used_rate, # cpu总占比 "os_virtual_mem_used_rate": os_virtual_mem_used_rate, # 内存总占比 "os_disk_used_rate": os_disk_used_rate, "os_cpu_used_rate_str": str(round(os_cpu_used_rate * 100, 1)) + "% / " + str(os_cpu_total_core), "os_virtual_mem_used_rate_str": str(round(os_virtual_mem_used_rate * 100, 1)) + "% / " + str( self.__byteFormat(os_virtual_mem_total)), "os_disk_used_rate_str": str(round(os_disk_used_rate * 100, 1)) + "% / " + str( self.__byteFormat(os_disk_total)), "os_run_date_str": os_run_date_str, "os_gpus": os_gpus, } return os_info def getSystemName(self): return self.__system_name def getMachineNode(self): return self.__machine_node def getMachineOsRelease(self): # cat /etc/os-release if self.getSystemName() == "Windows": __str = "Windows" else: try: result = os.popen("cat /etc/os-release") __str = str(result.read()).strip() except: __str = "run error" return __str def getMachineLsCpu(self): # lscpu if self.getSystemName() == "Windows": __str = "Windows" else: try: result = os.popen("lscpu") __str = str(result.read()).strip() except: __str = "run error" return __str def getMachineUnameA(self): # uname -a if self.getSystemName() == "Windows": __str = "Windows" else: try: result = os.popen("uname -a") __str = str(result.read()).strip() except: __str = "run error" return __str def getMachineCpu(self): """ 获取系统 CPU 信息 兼容 Windows 11 (PowerShell) 和 Linux 系统 采用多种方法确保可靠获取 """ system_name = self.getSystemName() if system_name == "Windows": machine_cpu = None # --- 方案一:PowerShell Get-CimInstance (推荐,Win8+ 兼容) --- try: ps_command = [ "powershell", "-Command", "Get-CimInstance -ClassName Win32_Processor | Select-Object -ExpandProperty Name" ] output = subprocess.check_output( ps_command, stderr=subprocess.STDOUT, timeout=10, encoding='utf-8', creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0 ) machine_cpu = output.strip() # 验证结果是否有效 if machine_cpu and len(machine_cpu) > 3 and not machine_cpu.startswith("Get-CimInstance"): return machine_cpu except Exception as e: pass # --- 方案二:WMIC 命令 (旧版 Windows 回退方案) --- try: output = subprocess.check_output( "wmic cpu get Name", shell=True, timeout=10, encoding='utf-8', creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0 ) lines = [line.strip() for line in output.splitlines() if line.strip()] # 解析输出,跳过标题行 "Name" if len(lines) >= 2: # 第一行是 "Name",第二行开始是 CPU 名称 machine_cpu = lines[1].strip() if machine_cpu and len(machine_cpu) > 3: return machine_cpu except Exception as e: pass # --- 方案三:PowerShell Get-WmiObject (备选,兼容旧系统) --- try: ps_command = [ "powershell", "-Command", "Get-WmiObject -Class Win32_Processor | Select-Object -ExpandProperty Name" ] output = subprocess.check_output( ps_command, stderr=subprocess.STDOUT, timeout=10, encoding='utf-8', creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0 ) machine_cpu = output.strip() if machine_cpu and len(machine_cpu) > 3 and not machine_cpu.startswith("Get-WmiObject"): return machine_cpu except Exception as e: pass # --- 方案四:使用 psutil (如果可用) --- try: cpu_info = platform.processor() if cpu_info and len(cpu_info) > 3: return cpu_info except Exception: pass return "run error" else: # Linux / macOS / 其他 # --- 方案一:直接读取 /proc/cpuinfo (最可靠,不需要外部命令) --- try: with open('/proc/cpuinfo', 'r', encoding='utf-8') as f: for line in f: if line.startswith("model name"): machine_cpu = line.split(":", 1)[1].strip() if machine_cpu: return machine_cpu except Exception as e: pass # --- 方案二:使用 lscpu 命令 --- try: result = subprocess.run( ['lscpu'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10, universal_newlines=True ) if result.returncode == 0: for line in result.stdout.splitlines(): if line.startswith("Model name"): machine_cpu = line.split(":", 1)[1].strip() if machine_cpu: return machine_cpu except Exception as e: pass # --- 方案三:使用 uname -m 获取架构信息 --- try: result = subprocess.run( ['uname', '-m'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10, universal_newlines=True ) if result.returncode == 0: arch = result.stdout.strip() if arch: # 对于 ARM 设备,返回架构信息 return f"ARM Processor ({arch})" except Exception as e: pass # --- 方案四:尝试读取设备树信息 (适用于嵌入式 ARM 设备) --- try: dt_paths = [ '/proc/device-tree/model', '/proc/device-tree/compatible' ] for path in dt_paths: if os.path.exists(path): with open(path, 'rb') as f: value = f.read().rstrip(b'\x00').decode('utf-8', errors='ignore') if value and len(value) > 3: return value.replace('\n', ' ').strip() except Exception as e: pass return "run error" def getMachineNvidia(self): # nvidia-smi try: if shutil.which("nvidia-smi"): result = os.popen("nvidia-smi") __str = str(result.read()).strip() if len(__str) > 0: lines = __str.split("\n") lines_filter = [] for line in lines: if line.find("NVIDIA-SMI") > -1: lines_filter.append(line.strip()) elif line.find("Default") > -1: lines_filter.append(line.strip()) elif line.find("NVIDIA") > -1: lines_filter.append(line.strip()) __str = ",".join(lines_filter) else: __str = "" else: __str = "no" except: __str = "run error" return __str def getMachineAscend(self): # npu-smi info if self.getSystemName() == "Windows": return "" else: # 2. 定义可能的 npu-smi 路径 (防止 PATH 未配置) # 昇腾默认安装路径通常在 /usr/local/Ascend/driver/tools/ possible_paths = [ "npu-smi", "/usr/local/Ascend/driver/tools/npu-smi", "/usr/local/Ascend/bin/npu-smi" ] cmd_path = None # 优先查找环境变量中的命令,如果找不到则尝试硬编码路径 for path in possible_paths: if shutil.which(path): cmd_path = path break if not cmd_path: return "no_cmd_found" # 明确区分是找不到命令还是执行失败 try: # 3. 使用 subprocess.run 替代 os.popen # capture_output=True: 同时捕获 stdout 和 stderr # text=True: 直接返回字符串而非 bytes # timeout=10: 防止命令卡死导致程序挂起 result = subprocess.run( [cmd_path, "info"], capture_output=True, text=True, timeout=10, env=os.environ # 显式继承当前环境变量 ) # 4. 合并输出 (有些错误信息在 stderr,有些正常输出在 stdout) # 注意:npu-smi 正常时 stderr 通常为空,但为了调试建议都看看 output = result.stdout.strip() error = result.stderr.strip() # 如果返回码不为 0,说明执行出错 (可能是权限问题) if result.returncode != 0: # 如果是权限问题,npu-smi 通常会提示 "Permission denied" 或类似信息 return f"exec_error(code={result.returncode}): {error or output}" if not output: return "empty_output" # 5. 优化过滤逻辑 (保留原逻辑但增加容错) lines = output.split("\n") lines_filter = [] has_table = False for line in lines: # 只要包含 | 就认为是表格行 if "|" in line: lines_filter.append(line.strip()) has_table = True # 【改进】如果过滤后为空,但原始输出不为空,说明格式可能变了,不要直接丢弃,返回原始内容供调试 if not has_table: # 可以选择返回原始输出,或者标记为格式未知 # return output pass final_str = ",".join(lines_filter) return final_str if final_str else output except subprocess.TimeoutExpired: return "timeout" except PermissionError: return "permission_denied_need_root" except Exception as e: return f"run_exception: {str(e)}" def getMachineRknpu(self): # cat /sys/kernel/debug/rknpu/load if self.getSystemName() == "Windows": __str = "" else: try: if os.path.exists("/sys/kernel/debug/rknpu/load"): result = os.popen("cat /sys/kernel/debug/rknpu/load") __str = str(result.read()).strip() if len(__str) > 0: lines = __str.split("\n") __str = ",".join(lines) else: __str = "" else: __str = "no" except: __str = "run error" return __str