构建
This commit is contained in:
@@ -22,6 +22,11 @@ from occupation_resolver_tkinter import OccupationResolver
|
||||
# 导入自定义对话框
|
||||
from dialogs_tkinter import (CreatePartitionDialog, MountDialog, CreateRaidDialog,
|
||||
CreatePvDialog, CreateVgDialog, CreateLvDialog)
|
||||
# 导入 SMART 对话框
|
||||
from dialogs_smart import SmartInfoDialog, SmartOverviewDialog
|
||||
from smart_monitor import SmartMonitor
|
||||
# 导入增强对话框
|
||||
from dialogs_enhanced import EnhancedFormatDialog, EnhancedRaidDeleteDialog, EnhancedPartitionDialog
|
||||
|
||||
|
||||
class MainWindow:
|
||||
@@ -71,9 +76,20 @@ class MainWindow:
|
||||
|
||||
def _create_widgets(self):
|
||||
"""创建界面元素"""
|
||||
# 顶部按钮栏
|
||||
btn_frame = ttk.Frame(self.main_frame)
|
||||
btn_frame.pack(fill=tk.X, pady=(0, 5))
|
||||
|
||||
# 刷新按钮
|
||||
self.refresh_btn = ttk.Button(self.main_frame, text="刷新数据", command=self.refresh_all_info)
|
||||
self.refresh_btn.pack(fill=tk.X, pady=(0, 5))
|
||||
self.refresh_btn = ttk.Button(btn_frame, text="刷新数据", command=self.refresh_all_info)
|
||||
self.refresh_btn.pack(side=tk.LEFT, padx=(0, 5))
|
||||
|
||||
# SMART 监控按钮
|
||||
self.smart_btn = ttk.Button(btn_frame, text="SMART 监控", command=self._show_smart_overview)
|
||||
self.smart_btn.pack(side=tk.LEFT, padx=(0, 5))
|
||||
|
||||
# 菜单栏
|
||||
self._create_menu()
|
||||
|
||||
# Notebook (Tab 控件)
|
||||
self.notebook = ttk.Notebook(self.main_frame)
|
||||
@@ -107,6 +123,38 @@ class MainWindow:
|
||||
self.log_text = ScrolledText(log_frame, height=8, state=tk.DISABLED)
|
||||
self.log_text.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
def _create_menu(self):
|
||||
"""创建菜单栏"""
|
||||
menubar = tk.Menu(self.root)
|
||||
self.root.config(menu=menubar)
|
||||
|
||||
# 工具菜单
|
||||
tools_menu = tk.Menu(menubar, tearoff=0)
|
||||
menubar.add_cascade(label="工具", menu=tools_menu)
|
||||
tools_menu.add_command(label="SMART 监控", command=self._show_smart_overview)
|
||||
tools_menu.add_separator()
|
||||
tools_menu.add_command(label="刷新数据", command=self.refresh_all_info)
|
||||
|
||||
# 帮助菜单
|
||||
help_menu = tk.Menu(menubar, tearoff=0)
|
||||
menubar.add_cascade(label="帮助", menu=help_menu)
|
||||
help_menu.add_command(label="关于", command=self._show_about)
|
||||
|
||||
def _show_about(self):
|
||||
"""显示关于对话框"""
|
||||
messagebox.showinfo(
|
||||
"关于",
|
||||
"Linux 存储管理工具\n\n"
|
||||
"版本: 2.0 (Tkinter 版)\n"
|
||||
"适配低版本系统 (CentOS 8+)\n\n"
|
||||
"功能:\n"
|
||||
"- 块设备管理\n"
|
||||
"- RAID 管理\n"
|
||||
"- LVM 管理\n"
|
||||
"- SMART 监控\n\n"
|
||||
"使用 Python 3.6+ 和 Tkinter 构建"
|
||||
)
|
||||
|
||||
def _create_block_devices_tree(self):
|
||||
"""创建块设备 Treeview"""
|
||||
# 创建框架
|
||||
@@ -359,6 +407,10 @@ class MainWindow:
|
||||
menu.add_command(label=f"擦除分区表 {device_path}...",
|
||||
command=lambda: self._handle_wipe_partition_table(device_path))
|
||||
menu.add_separator()
|
||||
# SMART 信息
|
||||
menu.add_command(label=f"查看 SMART 信息 {device_path}",
|
||||
command=lambda: self._handle_show_smart(device_path))
|
||||
menu.add_separator()
|
||||
|
||||
# 分区操作
|
||||
if device_type == 'part':
|
||||
@@ -373,7 +425,24 @@ class MainWindow:
|
||||
menu.add_command(label=f"删除分区 {device_path}",
|
||||
command=lambda: self._handle_delete_partition(device_path))
|
||||
menu.add_command(label=f"格式化分区 {device_path}...",
|
||||
command=lambda: self._handle_format_partition(device_path))
|
||||
command=lambda dp=device_path, dd=dev_data: self._handle_format_partition(dp, dd))
|
||||
|
||||
# 文件系统检查和修复
|
||||
fstype = dev_data.get('fstype', '')
|
||||
if fstype:
|
||||
fs_menu = tk.Menu(menu, tearoff=0)
|
||||
# 根据文件系统类型添加不同的检查/修复选项
|
||||
if fstype.startswith('ext'):
|
||||
fs_menu.add_command(label=f"检查文件系统 (fsck) {device_path}",
|
||||
command=lambda dp=device_path, ft=fstype: self._handle_fsck(dp, ft))
|
||||
fs_menu.add_command(label=f"调整文件系统大小 (resize2fs) {device_path}",
|
||||
command=lambda dp=device_path, ft=fstype: self._handle_resize2fs(dp, ft))
|
||||
elif fstype == 'xfs':
|
||||
fs_menu.add_command(label=f"修复文件系统 (xfs_repair) {device_path}",
|
||||
command=lambda dp=device_path: self._handle_xfs_repair(dp))
|
||||
if fs_menu.index(tk.END) is not None:
|
||||
menu.add_cascade(label=f"文件系统工具 {device_path}", menu=fs_menu)
|
||||
|
||||
menu.add_separator()
|
||||
|
||||
# Loop 设备操作
|
||||
@@ -459,9 +528,26 @@ class MainWindow:
|
||||
menu.add_command(label=f"停止阵列 {array_path}",
|
||||
command=lambda: self._handle_stop_raid_array(array_path))
|
||||
menu.add_command(label=f"删除阵列 {array_path}",
|
||||
command=lambda: self._handle_delete_active_raid_array(array_path, member_devices, array_uuid))
|
||||
command=lambda ap=array_path, md=member_devices, au=array_uuid, ad=array_data:
|
||||
self._handle_delete_active_raid_array(ap, md, au))
|
||||
menu.add_command(label=f"格式化阵列 {array_path}...",
|
||||
command=lambda: self._handle_format_raid_array(array_path))
|
||||
|
||||
# 文件系统检查和修复
|
||||
dev_details = self.system_manager.get_device_details_by_path(array_path)
|
||||
fstype = dev_details.get('fstype', '') if dev_details else ''
|
||||
if fstype:
|
||||
fs_menu = tk.Menu(menu, tearoff=0)
|
||||
if fstype.startswith('ext'):
|
||||
fs_menu.add_command(label=f"检查文件系统 (fsck) {array_path}",
|
||||
command=lambda dp=array_path, ft=fstype: self._handle_fsck(dp, ft))
|
||||
fs_menu.add_command(label=f"调整文件系统大小 (resize2fs) {array_path}",
|
||||
command=lambda dp=array_path, ft=fstype: self._handle_resize2fs(dp, ft))
|
||||
elif fstype == 'xfs':
|
||||
fs_menu.add_command(label=f"修复文件系统 (xfs_repair) {array_path}",
|
||||
command=lambda dp=array_path: self._handle_xfs_repair(dp))
|
||||
if fs_menu.index(tk.END) is not None:
|
||||
menu.add_cascade(label=f"文件系统工具 {array_path}", menu=fs_menu)
|
||||
|
||||
if menu.index(tk.END) is not None:
|
||||
self._show_context_menu(menu, event.x_root, event.y_root)
|
||||
@@ -535,7 +621,24 @@ class MainWindow:
|
||||
menu.add_command(label=f"删除逻辑卷 {lv_name}",
|
||||
command=lambda: self._handle_delete_lv(lv_name, vg_name))
|
||||
menu.add_command(label=f"格式化逻辑卷 {lv_name}...",
|
||||
command=lambda: self._handle_format_partition(lv_path))
|
||||
command=lambda lp=lv_path, ln=lv_name: self._handle_format_partition(
|
||||
lp, {'name': ln, 'path': lp}))
|
||||
|
||||
# 文件系统检查和修复
|
||||
dev_details = self.system_manager.get_device_details_by_path(lv_path)
|
||||
fstype = dev_details.get('fstype', '') if dev_details else ''
|
||||
if fstype:
|
||||
fs_menu = tk.Menu(menu, tearoff=0)
|
||||
if fstype.startswith('ext'):
|
||||
fs_menu.add_command(label=f"检查文件系统 (fsck) {lv_path}",
|
||||
command=lambda dp=lv_path, ft=fstype: self._handle_fsck(dp, ft))
|
||||
fs_menu.add_command(label=f"调整文件系统大小 (resize2fs) {lv_path}",
|
||||
command=lambda dp=lv_path, ft=fstype: self._handle_resize2fs(dp, ft))
|
||||
elif fstype == 'xfs':
|
||||
fs_menu.add_command(label=f"修复文件系统 (xfs_repair) {lv_path}",
|
||||
command=lambda dp=lv_path: self._handle_xfs_repair(dp))
|
||||
if fs_menu.index(tk.END) is not None:
|
||||
menu.add_cascade(label=f"文件系统工具 {lv_path}", menu=fs_menu)
|
||||
|
||||
if menu.index(tk.END) is not None:
|
||||
self._show_context_menu(menu, event.x_root, event.y_root)
|
||||
@@ -580,6 +683,41 @@ class MainWindow:
|
||||
"""处理卸载 loop 设备"""
|
||||
if self.disk_ops.unmount_loop_device(device_path):
|
||||
self.refresh_all_info()
|
||||
|
||||
def _handle_show_smart(self, device_path):
|
||||
"""显示指定设备的 SMART 信息"""
|
||||
monitor = SmartMonitor()
|
||||
if not monitor.is_available():
|
||||
messagebox.showwarning("警告",
|
||||
"smartctl 工具未安装。\n"
|
||||
"请安装 smartmontools:\n"
|
||||
" CentOS/RHEL: sudo yum install smartmontools\n"
|
||||
" Ubuntu/Debian: sudo apt-get install smartmontools")
|
||||
return
|
||||
|
||||
smart_info = monitor.get_disk_smart_info(device_path)
|
||||
if smart_info:
|
||||
SmartInfoDialog(self.root, device_path, smart_info)
|
||||
else:
|
||||
messagebox.showerror("错误",
|
||||
f"无法获取 {device_path} 的 SMART 信息。\n"
|
||||
f"可能的原因:\n"
|
||||
f"1. 设备不支持 SMART\n"
|
||||
f"2. 需要 root 权限\n"
|
||||
f"3. 设备未正确连接")
|
||||
|
||||
def _show_smart_overview(self):
|
||||
"""显示 SMART 总览"""
|
||||
monitor = SmartMonitor()
|
||||
if not monitor.is_available():
|
||||
messagebox.showwarning("警告",
|
||||
"smartctl 工具未安装。\n"
|
||||
"请安装 smartmontools:\n"
|
||||
" CentOS/RHEL: sudo yum install smartmontools\n"
|
||||
" Ubuntu/Debian: sudo apt-get install smartmontools")
|
||||
return
|
||||
|
||||
SmartOverviewDialog(self.root)
|
||||
|
||||
def _handle_wipe_partition_table(self, device_path):
|
||||
"""处理擦除物理盘分区表"""
|
||||
@@ -652,7 +790,8 @@ class MainWindow:
|
||||
max_available_mib = max(0.0, total_disk_mib - 1.0)
|
||||
start_position_mib = 1.0
|
||||
|
||||
dialog = CreatePartitionDialog(self.root, disk_path, total_disk_mib, max_available_mib)
|
||||
# 使用增强型分区创建对话框(带滑块可视化)
|
||||
dialog = EnhancedPartitionDialog(self.root, disk_path, total_disk_mib, max_available_mib)
|
||||
info = dialog.wait_for_result()
|
||||
|
||||
if info:
|
||||
@@ -684,9 +823,101 @@ class MainWindow:
|
||||
if self.disk_ops.delete_partition(device_path):
|
||||
self.refresh_all_info()
|
||||
|
||||
def _handle_format_partition(self, device_path):
|
||||
"""处理格式化分区"""
|
||||
self.disk_ops.format_partition(device_path)
|
||||
def _handle_format_partition(self, device_path, dev_data=None):
|
||||
"""处理格式化分区 - 使用增强型对话框"""
|
||||
# 获取设备信息
|
||||
if dev_data is None:
|
||||
dev_data = self.system_manager.get_device_details_by_path(device_path)
|
||||
|
||||
# 使用增强型格式化对话框
|
||||
dialog = EnhancedFormatDialog(self.root, device_path, dev_data or {})
|
||||
fs_type = dialog.wait_for_result()
|
||||
|
||||
if fs_type:
|
||||
# 执行格式化
|
||||
self.disk_ops.format_partition(device_path, fs_type)
|
||||
|
||||
def _handle_fsck(self, device_path, fstype):
|
||||
"""检查/修复 ext 文件系统"""
|
||||
# 检查设备是否已挂载
|
||||
mount_point = self.system_manager.get_mountpoint_for_device(device_path)
|
||||
if mount_point and mount_point != 'N/A':
|
||||
messagebox.showwarning("警告", f"设备 {device_path} 已挂载到 {mount_point}\n请先卸载后再执行文件系统检查。")
|
||||
return
|
||||
|
||||
if not messagebox.askyesno("确认",
|
||||
f"即将对 {device_path} ({fstype}) 执行文件系统检查。\n"
|
||||
f"命令: fsck -f -y {device_path}\n\n"
|
||||
f"注意:此操作可能需要较长时间,请勿中断。",
|
||||
default=messagebox.NO):
|
||||
return
|
||||
|
||||
logger.info(f"开始检查文件系统: {device_path} ({fstype})")
|
||||
success, stdout, stderr = self.lvm_ops._execute_shell_command(
|
||||
["fsck", "-f", "-y", device_path],
|
||||
f"检查文件系统 {device_path} 失败"
|
||||
)
|
||||
|
||||
if success:
|
||||
messagebox.showinfo("成功", f"文件系统检查完成:\n{stdout[:500] if stdout else '无输出'}")
|
||||
else:
|
||||
messagebox.showerror("错误", f"文件系统检查失败:\n{stderr[:500] if stderr else '未知错误'}")
|
||||
self.refresh_all_info()
|
||||
|
||||
def _handle_xfs_repair(self, device_path):
|
||||
"""修复 XFS 文件系统"""
|
||||
# 检查设备是否已挂载
|
||||
mount_point = self.system_manager.get_mountpoint_for_device(device_path)
|
||||
if mount_point and mount_point != 'N/A':
|
||||
messagebox.showwarning("警告", f"设备 {device_path} 已挂载到 {mount_point}\n请先卸载后再执行文件系统修复。")
|
||||
return
|
||||
|
||||
if not messagebox.askyesno("确认",
|
||||
f"即将对 {device_path} (XFS) 执行文件系统修复。\n"
|
||||
f"命令: xfs_repair {device_path}\n\n"
|
||||
f"警告:此操作会尝试修复文件系统错误,但可能无法恢复所有数据。\n"
|
||||
f"建议先备份重要数据。",
|
||||
default=messagebox.NO):
|
||||
return
|
||||
|
||||
logger.info(f"开始修复 XFS 文件系统: {device_path}")
|
||||
success, stdout, stderr = self.lvm_ops._execute_shell_command(
|
||||
["xfs_repair", device_path],
|
||||
f"修复 XFS 文件系统 {device_path} 失败"
|
||||
)
|
||||
|
||||
if success:
|
||||
messagebox.showinfo("成功", f"XFS 文件系统修复完成:\n{stdout[:500] if stdout else '无输出'}")
|
||||
else:
|
||||
messagebox.showerror("错误", f"XFS 文件系统修复失败:\n{stderr[:500] if stderr else '未知错误'}")
|
||||
self.refresh_all_info()
|
||||
|
||||
def _handle_resize2fs(self, device_path, fstype):
|
||||
"""调整 ext 文件系统大小"""
|
||||
# 检查设备是否已挂载
|
||||
mount_point = self.system_manager.get_mountpoint_for_device(device_path)
|
||||
if mount_point and mount_point != 'N/A':
|
||||
messagebox.showwarning("警告", f"设备 {device_path} 已挂载到 {mount_point}\n请先卸载后再执行调整大小操作。")
|
||||
return
|
||||
|
||||
if not messagebox.askyesno("确认",
|
||||
f"即将对 {device_path} ({fstype}) 强制调整文件系统大小以匹配分区大小。\n"
|
||||
f"命令: resize2fs -f {device_path}\n\n"
|
||||
f"注意:此操作会强制调整文件系统大小,请确保分区大小已正确设置。",
|
||||
default=messagebox.NO):
|
||||
return
|
||||
|
||||
logger.info(f"开始调整文件系统大小: {device_path} ({fstype})")
|
||||
success, stdout, stderr = self.lvm_ops._execute_shell_command(
|
||||
["resize2fs", "-f", device_path],
|
||||
f"调整文件系统大小 {device_path} 失败"
|
||||
)
|
||||
|
||||
if success:
|
||||
messagebox.showinfo("成功", f"文件系统大小调整完成:\n{stdout[:500] if stdout else '无输出'}")
|
||||
else:
|
||||
messagebox.showerror("错误", f"调整文件系统大小失败:\n{stderr[:500] if stderr else '未知错误'}")
|
||||
self.refresh_all_info()
|
||||
|
||||
def on_disk_formatting_finished(self, success, device_path, stdout, stderr):
|
||||
"""接收格式化完成回调并刷新界面"""
|
||||
@@ -791,10 +1022,29 @@ class MainWindow:
|
||||
if self.raid_ops.stop_raid_array(array_path):
|
||||
self.refresh_all_info()
|
||||
|
||||
def _handle_delete_active_raid_array(self, array_path, member_devices, uuid):
|
||||
"""处理删除活动的 RAID 阵列"""
|
||||
if self.raid_ops.delete_active_raid_array(array_path, member_devices, uuid):
|
||||
self.refresh_all_info()
|
||||
def _handle_delete_active_raid_array(self, array_path, member_devices, uuid, array_data=None):
|
||||
"""处理删除活动的 RAID 阵列 - 使用增强型对话框"""
|
||||
# 获取阵列详情(如果未提供)
|
||||
if array_data is None:
|
||||
array_data = {}
|
||||
try:
|
||||
raid_arrays = self.system_manager.get_mdadm_arrays()
|
||||
for array in raid_arrays:
|
||||
if array.get('device') == array_path:
|
||||
array_data = array
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
# 使用增强型删除对话框
|
||||
dialog = EnhancedRaidDeleteDialog(
|
||||
self.root, array_path, array_data, member_devices
|
||||
)
|
||||
|
||||
if dialog.wait_for_result():
|
||||
# 执行删除
|
||||
if self.raid_ops.delete_active_raid_array(array_path, member_devices, uuid):
|
||||
self.refresh_all_info()
|
||||
|
||||
def _handle_delete_configured_raid_array(self, uuid):
|
||||
"""处理删除停止状态 RAID 阵列的配置文件条目"""
|
||||
@@ -1051,3 +1301,10 @@ class MainWindow:
|
||||
self.refresh_all_info()
|
||||
else:
|
||||
messagebox.showerror("删除卷组失败", f"删除卷组 {vg_name} 失败,请检查日志。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 创建主窗口
|
||||
root = tk.Tk()
|
||||
app = MainWindow(root)
|
||||
root.mainloop()
|
||||
|
||||
Reference in New Issue
Block a user