You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

218 lines
7.5 KiB

"""
系统工具模块
"""
import platform
import subprocess
import re
from typing import Optional
from app.utils.structured_log import get_structured_logger
logger = get_structured_logger(__name__)
class SystemUtils:
"""系统工具类"""
@staticmethod
def get_system_type() -> str:
"""获取系统类型"""
return platform.system().lower()
@staticmethod
def get_machine_code() -> Optional[str]:
"""获取机器码
Windows: 使用 wmic csproduct get uuid
Linux: 使用 dmidecode -s system-serial-number 或 cat /sys/class/dmi/id/product_serial
"""
try:
system_type = SystemUtils.get_system_type()
logger.info(f"获取机器码,系统类型: {system_type}")
if system_type == "windows":
return SystemUtils._get_windows_machine_code()
elif system_type == "linux":
return SystemUtils._get_linux_machine_code()
else:
logger.warning(f"不支持的系统类型: {system_type}")
return None
except Exception as e:
logger.error(f"获取机器码失败: {e}")
return None
@staticmethod
def _get_windows_machine_code() -> Optional[str]:
"""获取Windows系统机器码"""
try:
# 使用 wmic csproduct get uuid 命令
result = subprocess.run(
["wmic", "csproduct", "get", "uuid"],
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
# 解析输出,跳过标题行
lines = result.stdout.strip().split('\n')
if len(lines) >= 2:
uuid = lines[1].strip()
if uuid and uuid != "UUID":
logger.info(f"Windows机器码获取成功: {uuid}")
return uuid
logger.warning("Windows机器码获取失败,尝试备用方法")
return SystemUtils._get_windows_machine_code_fallback()
except subprocess.TimeoutExpired:
logger.error("Windows机器码获取超时")
return None
except Exception as e:
logger.error(f"Windows机器码获取异常: {e}")
return None
@staticmethod
def _get_windows_machine_code_fallback() -> Optional[str]:
"""Windows机器码获取备用方法"""
try:
# 尝试使用 PowerShell 命令
result = subprocess.run(
["powershell", "-Command", "Get-WmiObject -Class Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID"],
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
uuid = result.stdout.strip()
if uuid and len(uuid) > 0:
logger.info(f"Windows机器码获取成功(备用方法): {uuid}")
return uuid
return None
except Exception as e:
logger.error(f"Windows机器码备用方法失败: {e}")
return None
@staticmethod
def _get_linux_machine_code() -> Optional[str]:
"""获取Linux系统机器码"""
try:
# 首先尝试 dmidecode 命令
serial_number = SystemUtils._get_linux_serial_via_dmidecode()
if serial_number:
return serial_number
# 备用方法:读取系统文件
serial_number = SystemUtils._get_linux_serial_via_file()
if serial_number:
return serial_number
# 最后尝试:使用 hostid 命令
return SystemUtils._get_linux_serial_via_hostid()
except Exception as e:
logger.error(f"Linux机器码获取异常: {e}")
return None
@staticmethod
def _get_linux_serial_via_dmidecode() -> Optional[str]:
"""通过 dmidecode 获取Linux序列号"""
try:
result = subprocess.run(
["dmidecode", "-s", "system-serial-number"],
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
serial = result.stdout.strip()
if serial and serial != "0" and len(serial) > 0:
logger.info(f"Linux序列号获取成功(dmidecode): {serial}")
return serial
return None
except (subprocess.TimeoutExpired, FileNotFoundError):
logger.warning("dmidecode命令不可用或超时")
return None
except Exception as e:
logger.error(f"dmidecode获取序列号失败: {e}")
return None
@staticmethod
def _get_linux_serial_via_file() -> Optional[str]:
"""通过读取系统文件获取Linux序列号"""
try:
# 尝试读取 /sys/class/dmi/id/product_serial
result = subprocess.run(
["cat", "/sys/class/dmi/id/product_serial"],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
serial = result.stdout.strip()
if serial and serial != "0" and len(serial) > 0:
logger.info(f"Linux序列号获取成功(文件): {serial}")
return serial
return None
except (subprocess.TimeoutExpired, FileNotFoundError):
logger.warning("系统文件读取失败")
return None
except Exception as e:
logger.error(f"文件读取序列号失败: {e}")
return None
@staticmethod
def _get_linux_serial_via_hostid() -> Optional[str]:
"""通过 hostid 命令获取Linux机器码"""
try:
result = subprocess.run(
["hostid"],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
hostid = result.stdout.strip()
if hostid and len(hostid) > 0:
logger.info(f"Linux机器码获取成功(hostid): {hostid}")
return hostid
return None
except (subprocess.TimeoutExpired, FileNotFoundError):
logger.warning("hostid命令不可用")
return None
except Exception as e:
logger.error(f"hostid获取机器码失败: {e}")
return None
@staticmethod
def get_system_info() -> dict:
"""获取系统信息"""
try:
system_type = SystemUtils.get_system_type()
machine_code = SystemUtils.get_machine_code()
return {
"system_type": system_type,
"platform": platform.platform(),
"architecture": platform.architecture()[0],
"processor": platform.processor(),
"machine_code": machine_code,
"python_version": platform.python_version()
}
except Exception as e:
logger.error(f"获取系统信息失败: {e}")
return {
"system_type": "unknown",
"error": str(e)
}