428 lines
16 KiB
Python
428 lines
16 KiB
Python
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 <class 'datetime.timedelta'>
|
||
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) # <class 'float'> 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) # <class 'float'> 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()) # <class 'float'> 1651904713.9067075
|
||
os_boot_date = datetime.fromtimestamp(os_boot_timestamp) # <class 'datetime.datetime'>
|
||
os_run_date = datetime.now() - os_boot_date # <class 'datetime.timedelta'>
|
||
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
|