Files
diskmanager2/platform_compat.py
2026-02-10 02:54:13 +08:00

338 lines
12 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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}")