fix bug45
This commit is contained in:
@@ -39,17 +39,18 @@ class RaidOperations:
|
||||
|
||||
return True, stdout, stderr
|
||||
except subprocess.CalledProcessError as e:
|
||||
stderr_output = e.stderr.strip() # 获取标准错误输出
|
||||
logger.error(f"{error_msg_prefix} 命令: {full_cmd_str}")
|
||||
logger.error(f"退出码: {e.returncode}")
|
||||
logger.error(f"标准输出: {e.stdout.strip()}")
|
||||
logger.error(f"标准错误: {e.stderr.strip()}")
|
||||
logger.error(f"标准错误: {stderr_output}")
|
||||
|
||||
# 根据 suppress_critical_dialog_on_stderr_match 参数决定是否弹出错误对话框
|
||||
if suppress_critical_dialog_on_stderr_match and suppress_critical_dialog_on_stderr_match in e.stderr.strip():
|
||||
if suppress_critical_dialog_on_stderr_match and suppress_critical_dialog_on_stderr_match in stderr_output:
|
||||
logger.info(f"特定错误 '{suppress_critical_dialog_on_stderr_match}' 匹配,已抑制错误对话框。")
|
||||
else:
|
||||
QMessageBox.critical(None, "错误", f"{error_msg_prefix}\n详细信息: {e.stderr.strip()}")
|
||||
return False, e.stdout, e.stderr
|
||||
QMessageBox.critical(None, "错误", f"{error_msg_prefix}\n详细信息: {stderr_output}")
|
||||
return False, e.stdout, stderr_output # 返回 stderr_output
|
||||
except FileNotFoundError:
|
||||
logger.error(f"命令 '{command_list[0]}' 未找到。请确保已安装相关工具。")
|
||||
QMessageBox.critical(None, "错误", f"命令 '{command_list[0]}' 未找到。请确保已安装相关工具。")
|
||||
@@ -59,13 +60,50 @@ class RaidOperations:
|
||||
QMessageBox.critical(None, "错误", f"{error_msg_prefix}\n未知错误: {e}")
|
||||
return False, "", str(e)
|
||||
|
||||
def _get_next_available_md_device_name(self):
|
||||
"""
|
||||
查找下一个可用的 /dev/mdX 设备名称(例如 /dev/md0, /dev/md1, ...)。
|
||||
"""
|
||||
existing_md_numbers = set()
|
||||
try:
|
||||
raid_arrays = self.system_manager.get_mdadm_arrays()
|
||||
for array in raid_arrays:
|
||||
device_path = array.get('device')
|
||||
if device_path and device_path.startswith('/dev/md'):
|
||||
# 匹配 /dev/mdX 形式的设备名
|
||||
match = re.match(r'/dev/md(\d+)', device_path)
|
||||
if match:
|
||||
existing_md_numbers.add(int(match.group(1)))
|
||||
except Exception as e:
|
||||
logger.warning(f"获取现有 RAID 阵列信息失败,可能无法找到最优的下一个设备名: {e}")
|
||||
|
||||
next_md_num = 0
|
||||
while next_md_num in existing_md_numbers:
|
||||
next_md_num += 1
|
||||
|
||||
return f"/dev/md{next_md_num}"
|
||||
|
||||
def create_raid_array(self, devices, level, chunk_size):
|
||||
"""
|
||||
创建 RAID 阵列。
|
||||
:param devices: 成员设备列表,例如 ['/dev/sdb1', '/dev/sdc1']
|
||||
:param level: RAID 级别,例如 'raid0', 'raid1', 'raid5'
|
||||
:param level: RAID 级别,例如 'raid0', 'raid1', 'raid5' (也可以是整数 0, 1, 5)
|
||||
:param chunk_size: Chunk 大小 (KB)
|
||||
"""
|
||||
# --- 标准化 RAID 级别输入 ---
|
||||
if isinstance(level, int):
|
||||
level_str = f"raid{level}"
|
||||
elif isinstance(level, str):
|
||||
if level in ["0", "1", "5", "6", "10"]: # 增加 "6", "10" 确保全面
|
||||
level_str = f"raid{level}"
|
||||
else:
|
||||
level_str = level
|
||||
else:
|
||||
QMessageBox.critical(None, "错误", f"不支持的 RAID 级别类型: {type(level)}")
|
||||
return False
|
||||
level = level_str
|
||||
# --- 标准化 RAID 级别输入结束 ---
|
||||
|
||||
if not devices or len(devices) < 2:
|
||||
QMessageBox.critical(None, "错误", "创建 RAID 阵列至少需要两个成员设备。")
|
||||
return False
|
||||
@@ -74,6 +112,17 @@ class RaidOperations:
|
||||
if level == "raid5" and len(devices) < 3:
|
||||
QMessageBox.critical(None, "错误", "RAID5 至少需要三个设备。")
|
||||
return False
|
||||
# 补充其他 RAID 级别设备数量检查,与 dialogs.py 保持一致
|
||||
if level == "raid1" and len(devices) < 2:
|
||||
QMessageBox.critical(None, "错误", "RAID1 至少需要两个设备。")
|
||||
return False
|
||||
if level == "raid6" and len(devices) < 4:
|
||||
QMessageBox.critical(None, "错误", "RAID6 至少需要四个设备。")
|
||||
return False
|
||||
if level == "raid10" and len(devices) < 2:
|
||||
QMessageBox.critical(None, "错误", "RAID10 至少需要两个设备。")
|
||||
return False
|
||||
|
||||
|
||||
# 确认操作
|
||||
reply = QMessageBox.question(None, "确认创建 RAID 阵列",
|
||||
@@ -90,69 +139,80 @@ class RaidOperations:
|
||||
for dev in devices:
|
||||
# 使用 --force 选项,避免交互式提示
|
||||
clear_cmd = ["mdadm", "--zero-superblock", "--force", dev]
|
||||
|
||||
# 定义表示设备上没有超级块的错误信息,根据日志调整
|
||||
no_superblock_error_match = "Unrecognised md component device"
|
||||
|
||||
success, _, stderr = self._execute_shell_command(
|
||||
clear_cmd,
|
||||
f"清除设备 {dev} 上的旧 RAID 超级块失败",
|
||||
suppress_critical_dialog_on_stderr_match="No superblocks" # 抑制“没有超级块”的错误提示
|
||||
suppress_critical_dialog_on_stderr_match=no_superblock_error_match
|
||||
)
|
||||
if success:
|
||||
|
||||
# 检查 stderr 是否包含“未识别的 MD 组件设备”信息
|
||||
if no_superblock_error_match in stderr:
|
||||
logger.info(f"设备 {dev} 上未找到旧 RAID 超级块,已确保其干净。")
|
||||
elif success:
|
||||
logger.info(f"已清除设备 {dev} 上的旧 RAID 超级块。")
|
||||
else:
|
||||
# 如果清除失败,但不是因为“没有超级块”,则可能需要进一步处理
|
||||
if "No superblocks" not in stderr:
|
||||
QMessageBox.warning(None, "警告", f"清除设备 {dev} 上的旧 RAID 超级块可能失败,但尝试继续。")
|
||||
logger.error(f"清除设备 {dev} 上的旧 RAID 超级块失败,中断 RAID 创建。")
|
||||
return False
|
||||
|
||||
# 2. 创建 RAID 阵列
|
||||
# 默认阵列名为 /dev/md/new_raid,可以考虑让用户输入
|
||||
array_name = "/dev/md/new_raid"
|
||||
# --- 修改点:自动生成唯一的阵列名称 ---
|
||||
array_name = self._get_next_available_md_device_name()
|
||||
logger.info(f"将使用自动生成的 RAID 阵列名称: {array_name}")
|
||||
# --- 修改点结束 ---
|
||||
|
||||
if level == "raid0":
|
||||
# RAID0 至少2个设备
|
||||
create_cmd = ["mdadm", "--create", array_name, "--level=raid0",
|
||||
f"--raid-devices={len(devices)}", f"--chunk={chunk_size}K"] + devices
|
||||
elif level == "raid1":
|
||||
# RAID1 至少2个设备
|
||||
create_cmd = ["mdadm", "--create", array_name, "--level=raid1",
|
||||
f"--raid-devices={len(devices)}"] + devices
|
||||
elif level == "raid5":
|
||||
# RAID5 至少3个设备
|
||||
create_cmd = ["mdadm", "--create", array_name, "--level=raid5",
|
||||
f"--raid-devices={len(devices)}"] + devices
|
||||
elif level == "raid6": # 增加 RAID6
|
||||
create_cmd = ["mdadm", "--create", array_name, "--level=raid6",
|
||||
f"--raid-devices={len(devices)}", f"--chunk={chunk_size}K"] + devices
|
||||
elif level == "raid10": # 增加 RAID10
|
||||
create_cmd = ["mdadm", "--create", array_name, "--level=raid10",
|
||||
f"--raid-devices={len(devices)}", f"--chunk={chunk_size}K"] + devices
|
||||
else:
|
||||
QMessageBox.critical(None, "错误", f"不支持的 RAID 级别: {level}")
|
||||
return False
|
||||
|
||||
# 在 --create 命令中添加 --force 选项
|
||||
create_cmd.insert(2, "--force") # 插入到 --create 后面
|
||||
create_cmd.insert(2, "--force")
|
||||
|
||||
# <--- 关键修改:通过 input_to_command 参数传入 'y\n' 来强制 mdadm 接受
|
||||
if not self._execute_shell_command(create_cmd, f"创建 RAID 阵列失败", input_to_command='y\n')[0]:
|
||||
success_create, stdout_create, stderr_create = self._execute_shell_command(
|
||||
create_cmd,
|
||||
f"创建 RAID 阵列失败",
|
||||
input_to_command='y\n' # 尝试通过 stdin 传递 'y'
|
||||
)
|
||||
if not success_create:
|
||||
# 检查是否是由于 "Array name ... is in use already." 导致的失败
|
||||
if "Array name" in stderr_create and "is in use already" in stderr_create:
|
||||
QMessageBox.critical(None, "错误", f"创建 RAID 阵列失败:阵列名称 {array_name} 已被占用。请尝试停止或删除现有阵列。")
|
||||
return False
|
||||
|
||||
logger.info(f"成功创建 RAID {level} 阵列 {array_name}。")
|
||||
QMessageBox.information(None, "成功", f"成功创建 RAID {level} 阵列 {array_name}。")
|
||||
|
||||
# 3. 刷新 mdadm 配置并等待阵列激活
|
||||
# 注意:这里使用 '>>' 重定向,subprocess.run 无法直接处理 shell 重定向符号
|
||||
# 需要改成先读取,再写入,或者使用 bash -c "..."
|
||||
# 暂时先用 bash -c 的方式,更简单
|
||||
# 旧代码:self._execute_shell_command(["mdadm", "--examine", "--scan", ">>", "/etc/mdadm/mdadm.conf"], "更新 mdadm.conf 失败")
|
||||
|
||||
# 获取新的 mdadm.conf 内容
|
||||
examine_scan_cmd = ["mdadm", "--examine", "--scan"]
|
||||
success_scan, scan_stdout, _ = self._execute_shell_command(examine_scan_cmd, "扫描 mdadm 配置失败")
|
||||
if success_scan:
|
||||
# 将扫描结果追加到 mdadm.conf
|
||||
append_to_conf_cmd = ["bash", "-c", f"echo '{scan_stdout.strip()}' >> /etc/mdadm/mdadm.conf"]
|
||||
if not self._execute_shell_command(append_to_conf_cmd, "更新 /etc/mdadm/mdadm.conf 失败")[0]:
|
||||
logger.warning("更新 /etc/mdadm/mdadm.conf 失败。")
|
||||
else:
|
||||
logger.warning("未能扫描到 mdadm 配置,跳过更新 mdadm.conf。")
|
||||
|
||||
|
||||
# self._execute_shell_command(["update-initramfs", "-u"], "更新 initramfs 失败")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def stop_raid_array(self, array_path):
|
||||
"""
|
||||
停止一个 RAID 阵列。
|
||||
@@ -173,7 +233,9 @@ class RaidOperations:
|
||||
# 暂时先直接调用 umount 命令,不处理 fstab
|
||||
# 或者,更好的方式是让 MainWindow 调用 disk_ops.unmount_partition
|
||||
# 此处简化处理,只执行 umount 命令
|
||||
self._execute_shell_command(["umount", array_path], f"尝试卸载 {array_path} 失败", suppress_critical_dialog_on_stderr_match="not mounted")
|
||||
# 这里的 suppress_critical_dialog_on_stderr_match 应该与 DiskOperations 中的定义保持一致
|
||||
self._execute_shell_command(["umount", array_path], f"尝试卸载 {array_path} 失败",
|
||||
suppress_critical_dialog_on_stderr_match="not mounted") # 仅抑制英文,因为这里没有访问 DiskOperations 的 already_unmounted_errors
|
||||
|
||||
if not self._execute_shell_command(["mdadm", "--stop", array_path], f"停止 RAID 阵列 {array_path} 失败")[0]:
|
||||
return False
|
||||
@@ -209,9 +271,24 @@ class RaidOperations:
|
||||
for dev in member_devices:
|
||||
# 使用 --force 选项,避免交互式提示
|
||||
clear_cmd = ["mdadm", "--zero-superblock", "--force", dev]
|
||||
if not self._execute_shell_command(clear_cmd, f"清除设备 {dev} 上的 RAID 超级块失败")[0]:
|
||||
|
||||
# 定义表示设备上没有超级块的错误信息,根据日志调整
|
||||
no_superblock_error_match = "Unrecognised md component device"
|
||||
|
||||
success, _, stderr = self._execute_shell_command(
|
||||
clear_cmd,
|
||||
f"清除设备 {dev} 上的 RAID 超级块失败",
|
||||
suppress_critical_dialog_on_stderr_match=no_superblock_error_match
|
||||
)
|
||||
|
||||
if no_superblock_error_match in stderr:
|
||||
logger.info(f"设备 {dev} 上未找到旧 RAID 超级块,已确保其干净。")
|
||||
elif success:
|
||||
logger.info(f"已清除设备 {dev} 上的旧 RAID 超级块。")
|
||||
else:
|
||||
success_all_cleared = False
|
||||
QMessageBox.warning(None, "警告", f"未能清除设备 {dev} 上的 RAID 超级块,请手动检查。")
|
||||
# 错误对话框已由 _execute_shell_command 弹出,这里只记录警告
|
||||
logger.warning(f"未能清除设备 {dev} 上的 RAID 超级块,请手动检查。")
|
||||
|
||||
if success_all_cleared:
|
||||
logger.info(f"成功删除 RAID 阵列 {array_path} 并清除了成员设备超级块。")
|
||||
|
||||
Reference in New Issue
Block a user