This commit is contained in:
root
2026-02-10 02:54:13 +08:00
parent 4a59323398
commit 64bfd85368
22 changed files with 2572 additions and 422 deletions

337
platform_compat.py Executable file
View File

@@ -0,0 +1,337 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
平台兼容性检测模块
自动检测操作系统和可用库,提供统一的兼容性接口
支持 Arch Linux (PySide6) 和 CentOS 8 (Tkinter)
"""
import sys
import os
import subprocess
import platform
import logging
logger = logging.getLogger(__name__)
class PlatformCompat:
"""平台兼容性检测类"""
# GUI 后端类型
GUI_PYSIDE6 = "pyside6"
GUI_TKINTER = "tkinter"
GUI_NONE = "none"
# 操作系统类型
OS_ARCH = "arch"
OS_CENTOS = "centos"
OS_RHEL = "rhel"
OS_UBUNTU = "ubuntu"
OS_DEBIAN = "debian"
OS_UNKNOWN = "unknown"
def __init__(self):
self._gui_backend = None
self._os_type = None
self._os_version = None
self._python_version = sys.version_info
self._detected = False
def detect(self):
"""执行平台检测"""
if self._detected:
return
logger.info(f"Python版本: {self._python_version.major}.{self._python_version.minor}.{self._python_version.micro}")
logger.info(f"平台: {platform.platform()}")
logger.info(f"系统: {platform.system()}")
logger.info(f"发行版: {platform.release()}")
# 检测操作系统
self._detect_os()
# 检测GUI后端
self._detect_gui_backend()
self._detected = True
logger.info(f"检测结果 - OS: {self._os_type}, GUI后端: {self._gui_backend}")
def _detect_os(self):
"""检测操作系统类型"""
try:
# 尝试读取 /etc/os-release
if os.path.exists('/etc/os-release'):
with open('/etc/os-release', 'r') as f:
content = f.read().lower()
if 'arch' in content:
self._os_type = self.OS_ARCH
elif 'centos' in content:
self._os_type = self.OS_CENTOS
elif 'rhel' in content or 'red hat' in content:
self._os_type = self.OS_RHEL
elif 'ubuntu' in content:
self._os_type = self.OS_UBUNTU
elif 'debian' in content:
self._os_type = self.OS_DEBIAN
else:
self._os_type = self.OS_UNKNOWN
else:
# 尝试使用 platform.linux_distribution() (Python 3.6 可用)
distro = platform.dist() if hasattr(platform, 'dist') else (None, None, None)
if distro[0]:
distro_name = distro[0].lower()
if 'centos' in distro_name or 'rhel' in distro_name:
self._os_type = self.OS_CENTOS
elif 'arch' in distro_name:
self._os_type = self.OS_ARCH
else:
self._os_type = self.OS_UNKNOWN
else:
self._os_type = self.OS_UNKNOWN
except Exception as e:
logger.warning(f"检测操作系统失败: {e}")
self._os_type = self.OS_UNKNOWN
def _detect_gui_backend(self):
"""检测可用的GUI后端"""
# 首先尝试 PySide6 (需要 Python 3.7+)
if self._python_version >= (3, 7):
try:
import PySide6
self._gui_backend = self.GUI_PYSIDE6
logger.info("检测到 PySide6 可用")
return
except ImportError:
logger.debug("PySide6 不可用")
else:
logger.debug(f"Python {self._python_version.major}.{self._python_version.minor} 不支持 PySide6 (需要 3.7+)")
# 尝试 PySide2 (CentOS 8 可能可用)
try:
import PySide2
self._gui_backend = "pyside2"
logger.info("检测到 PySide2 可用")
return
except ImportError:
logger.debug("PySide2 不可用")
# 最后尝试 Tkinter (Python 内置)
try:
import tkinter
self._gui_backend = self.GUI_TKINTER
logger.info("检测到 Tkinter 可用")
return
except ImportError:
logger.debug("Tkinter 不可用")
self._gui_backend = self.GUI_NONE
logger.error("没有检测到可用的GUI后端")
def get_gui_backend(self):
"""获取检测到的GUI后端"""
if not self._detected:
self.detect()
return self._gui_backend
def get_os_type(self):
"""获取检测到的操作系统类型"""
if not self._detected:
self.detect()
return self._os_type
def is_pyside6_available(self):
"""检查 PySide6 是否可用"""
return self.get_gui_backend() == self.GUI_PYSIDE6
def is_tkinter_available(self):
"""检查 Tkinter 是否可用"""
return self.get_gui_backend() == self.GUI_TKINTER
def get_recommended_entry_point(self):
"""
获取推荐的入口点模块名
统一使用 Tkinter 版本
返回: (模块名, 类名)
"""
return ("mainwindow_tkinter", "MainWindow")
def get_system_command_compat(self):
"""
获取系统命令兼容性配置
不同发行版的命令参数可能有差异
"""
os_type = self.get_os_type()
# 默认配置
config = {
'lsblk_json_available': True, # lsblk -J 支持
'parted_script_mode': True, # parted -s 支持
'lvm_json_report': True, # LVM JSON 报告格式
'mdadm_conf_paths': [
'/etc/mdadm/mdadm.conf',
'/etc/mdadm.conf'
],
}
# CentOS/RHEL 8 特定配置
if os_type in (self.OS_CENTOS, self.OS_RHEL):
# CentOS 8 的 lsblk 支持 -J但某些旧版本可能有问题
# 检查实际版本
try:
result = subprocess.run(['lsblk', '--version'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
version_output = result.stdout.decode('utf-8', errors='ignore') + result.stderr.decode('utf-8', errors='ignore')
# util-linux 2.23+ 支持 JSON
if '2.23' in version_output or '2.3' in version_output:
config['lsblk_json_available'] = True
else:
# 尝试解析版本号
import re
version_match = re.search(r'(\d+)\.(\d+)', version_output)
if version_match:
major = int(version_match.group(1))
minor = int(version_match.group(2))
config['lsblk_json_available'] = (major > 2) or (major == 2 and minor >= 23)
except Exception:
pass
# CentOS 8 的 LVM 版本支持 JSON 报告
try:
result = subprocess.run(['lvs', '--version'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# LVM 2.2.02.166+ 支持 JSON
version_output = result.stdout.decode('utf-8', errors='ignore') + result.stderr.decode('utf-8', errors='ignore')
import re
version_match = re.search(r'(\d+)\.(\d+)\.(\d+)\.(\d+)', version_output)
if version_match:
major = int(version_match.group(1))
minor = int(version_match.group(2))
config['lvm_json_report'] = (major >= 2) and (minor >= 2)
except Exception:
pass
return config
def get_installation_guide(self):
"""获取当前系统的安装指南"""
os_type = self.get_os_type()
backend = self.get_gui_backend()
guides = []
if os_type == self.OS_CENTOS:
guides.append("CentOS 8 安装命令:")
guides.append(" sudo yum install -y python3 python3-tkinter parted mdadm lvm2")
guides.append(" sudo yum install -y dosfstools e2fsprogs xfsprogs ntfs-3g")
if backend == self.GUI_TKINTER:
guides.append(" sudo pip3 install pexpect")
elif os_type == self.OS_ARCH:
guides.append("Arch Linux 安装命令:")
guides.append(" sudo pacman -S python python-pyside6 parted mdadm lvm2")
guides.append(" sudo pacman -S dosfstools e2fsprogs xfsprogs ntfs-3g")
guides.append(" pip install pexpect")
else:
guides.append("通用安装命令:")
guides.append(" 请安装: python3, tkinter, parted, mdadm, lvm2")
guides.append(" 以及文件系统工具: e2fsprogs, xfsprogs, dosfstools, ntfs-3g")
return "\n".join(guides)
# 全局实例
_platform_compat = None
def get_platform_compat():
"""获取全局平台兼容性实例"""
global _platform_compat
if _platform_compat is None:
_platform_compat = PlatformCompat()
_platform_compat.detect()
return _platform_compat
def check_prerequisites():
"""
检查系统前提条件
返回: (是否通过, 错误信息列表)
"""
compat = get_platform_compat()
errors = []
warnings = []
# 检查 GUI 后端
if compat.get_gui_backend() == PlatformCompat.GUI_NONE:
errors.append("没有检测到可用的GUI后端 (PySide6/PySide2/Tkinter)")
# 检查必要的系统命令
required_commands = [
('lsblk', '用于获取块设备信息'),
('parted', '用于分区操作'),
('mkfs.ext4', '用于创建ext4文件系统'),
('mount', '用于挂载操作'),
]
optional_commands = [
('mdadm', '用于RAID管理'),
('pvcreate', '用于LVM物理卷管理'),
('vgcreate', '用于LVM卷组管理'),
('lvcreate', '用于LVM逻辑卷管理'),
('mkfs.xfs', '用于创建XFS文件系统'),
('mkfs.ntfs', '用于创建NTFS文件系统'),
('mkfs.vfat', '用于创建FAT32文件系统'),
]
for cmd, desc in required_commands:
if not _command_exists(cmd):
errors.append(f"缺少必要命令: {cmd} ({desc})")
for cmd, desc in optional_commands:
if not _command_exists(cmd):
warnings.append(f"缺少可选命令: {cmd} ({desc})")
# 检查权限
if os.geteuid() != 0:
warnings.append("当前不是root用户部分功能可能需要sudo权限")
return (len(errors) == 0, errors, warnings)
def _command_exists(cmd):
"""检查命令是否存在"""
try:
# Python 3.6 兼容: 使用 stdout/stderr 参数代替 capture_output
result = subprocess.run(
['which', cmd],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True
)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
return False
if __name__ == "__main__":
# 测试模式
logging.basicConfig(level=logging.DEBUG)
compat = get_platform_compat()
print(f"操作系统类型: {compat.get_os_type()}")
print(f"GUI后端: {compat.get_gui_backend()}")
print(f"推荐入口点: {compat.get_recommended_entry_point()}")
print(f"系统命令兼容配置: {compat.get_system_command_compat()}")
print("\n" + compat.get_installation_guide())
print("\n前提条件检查:")
passed, errors, warnings = check_prerequisites()
if passed:
print("✓ 基本前提条件满足")
else:
print("✗ 前提条件不满足:")
for e in errors:
print(f" 错误: {e}")
for w in warnings:
print(f" 警告: {w}")