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.
215 lines
7.5 KiB
215 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:
|
|
# 使用 PowerShell 命令获取 UUID,兼容 Windows 11
|
|
result = subprocess.run(
|
|
["powershell", "-command", "(Get-CimInstance Win32_ComputerSystemProduct).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
|
|
|
|
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)
|
|
}
|
|
|