fix bug
This commit is contained in:
337
platform_compat.py
Executable file
337
platform_compat.py
Executable 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}")
|
||||
Reference in New Issue
Block a user